Skip to content

Commit

Permalink
feat: Add graph, node interfaces. Add BF DF iterators.
Browse files Browse the repository at this point in the history
  • Loading branch information
vantreeseba committed Jun 7, 2024
1 parent 61b4699 commit 2af5219
Show file tree
Hide file tree
Showing 27 changed files with 509 additions and 626 deletions.
3 changes: 3 additions & 0 deletions .vim/coc-settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"haxe.hxml": "test.hxml"
}
8 changes: 4 additions & 4 deletions src/dropecho/ds/BSPNode.hx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class BSPNode<TNodeData> extends GraphNode<TNodeData, String> {
this.left = node;
node.parent = this;
graph.addNode(node);
graph.addUniEdge(this.id, node.id, "left");
graph.addUniEdge(node.id, this.id, "parent");
graph.addUniEdge(this.label, node.label, "left");
graph.addUniEdge(node.label, this.label, "parent");

return node;
}
Expand All @@ -57,8 +57,8 @@ class BSPNode<TNodeData> extends GraphNode<TNodeData, String> {
this.right = node;
node.parent = this;
graph.addNode(node);
graph.addUniEdge(this.id, node.id, "right");
graph.addUniEdge(node.id, this.id, "parent");
graph.addUniEdge(this.label, node.label, "right");
graph.addUniEdge(node.label, this.label, "parent");

return node;
}
Expand Down
201 changes: 66 additions & 135 deletions src/dropecho/ds/Graph.hx
Original file line number Diff line number Diff line change
@@ -1,201 +1,134 @@
package dropecho.ds;

import dropecho.ds.IGraph;
import dropecho.interop.AbstractMap;

/**
* A graph data structure.
*
* Depending on methods used, can represent both a directed and non-directed graph.
* @param T - The node data type (stored within nodes).
* @param U - The edge data type (stored within edges).
*/
@:nativeGen
@:expose("Graph")
class Graph<T, U> {
@:inheritDoc(IGraph)
class Graph<T, U> implements IGraph<T, U> {
/** The nodes or vertices of the graph. */
public var nodes:AbstractMap<String, GraphNode<T, U>>;
public var nodes:AbstractMap<String, IGraphNode<T, U>>;

/** The edges of the graph. */
public var edges:AbstractMap<String, AbstractMap<String, U>>;

public function new() {
nodes = new AbstractMap<String, GraphNode<T, U>>();
nodes = new AbstractMap<String, IGraphNode<T, U>>();
edges = new AbstractMap<String, AbstractMap<String, U>>();
}

/**
* Creates a new node from a given value
* @param value The value to assign to the new node.
* @return The new node.
*/
public function createNode(value:T, ?id:String):GraphNode<T, U> {
return addNode(new GraphNode<T, U>(value, id));
@:inheritDoc(IGraph.createNode)
public function createNode(value:T, ?label:String) {
return addNode(new GraphNode<T, U>(value, label));
}

/**
* Add an existing node to this graph.
* This will set the internal graph property on the node to this.
* @param node The Node to add.
* @return The added graph node.
*/
public function addNode(node:GraphNode<T, U>):GraphNode<T, U> {
@:inheritDoc(IGraph.addNode)
public function addNode(node:IGraphNode<T, U>) {
node.graph = this;
return nodes.set(node.id, node);
return nodes.set(node.label, node);
}

/**
* Add a unidirectional edge from node to another.
*
* @param nodeId The start node of the edge.
* @param otherId The end node of the edge.
* @param data The data to assign to the edge.
*/
public function addUniEdge(fromId:String, toId:String, ?data:U):Void {
if (!edges.exists(fromId)) {
edges.set(fromId, new AbstractMap<String, U>());
@:inheritDoc(IGraph.addUniEdge)
public function addUniEdge(fromLabel:String, toLabel:String, ?data:U):Void {
if (!edges.exists(fromLabel)) {
edges.set(fromLabel, new AbstractMap<String, U>());
}

edges.get(fromId).set(toId, data);
edges.get(fromLabel).set(toLabel, data);
}

/**
* Add a bidirectional edge from node to another.
* @param nodeId - One node of the edge.
* @param otherId - The other node of the edge.
* @param data - The data to assign to the edge.
*/
public function addBiEdge(nodeId:String, otherId:String, ?data:U):Void {
addUniEdge(nodeId, otherId, data);
addUniEdge(otherId, nodeId, data);
@:inheritDoc(IGraph.addBiEdge)
public function addBiEdge(nodeLabel:String, otherLabel:String, ?data:U):Void {
addUniEdge(nodeLabel, otherLabel, data);
addUniEdge(otherLabel, nodeLabel, data);
}

/**
* Removes a node (and it's edges) from the graph.
*
* @param id - The id of the node to remove from the graph.
*/
public function remove(id:String):Void {
for (n in inNeighborIds(nodes.get(id))) {
edges.get(n).remove(id);
@:inheritDoc(IGraph.remove)
public function remove(label:String):Void {
for (n in inNeighborLabels(nodes.get(label))) {
edges.get(n).remove(label);
}
edges.remove(id);
nodes.remove(id);
edges.remove(label);
nodes.remove(label);
}

/**
* Get the in neighbors of the given node, filtering by the edge data.
* @param node - The node to find the in neighbors of.
* @param filter - The edge data filter.
* @return The list of in neighbor nodes.
*/
public function inNeighbors(node:GraphNode<T, U>, ?filter:(String, U) -> Bool):Array<GraphNode<T, U>> {
return inNeighborIds(node, filter).map(id -> nodes.get(id));
@:inheritDoc(IGraph.remove)
public function inNeighbors(node:IGraphNode<T, U>, ?filter:(String, U) -> Bool):Array<IGraphNode<T, U>> {
return inNeighborLabels(node, filter).map(label -> nodes.get(label));
}

/**
* Get the in neighbor ids of the given node, filtering by the edge data.
* @param node - The node to find the in neighbors of.
* @param filter - The edge data filter.
* @return The list of in neighbor node ids.
*/
public function inNeighborIds(node:GraphNode<T, U>, ?filter:(String, U) -> Bool):Array<String> {
var ids = [];
public function inNeighborLabels(node:IGraphNode<T, U>, ?filter:(String, U) -> Bool):Array<String> {
var labels = [];

// Get all edges as id, nodeEdges pairs
for (id => nodeEdges in edges) {
for (label => nodeEdges in edges) {
// If an edge exists from some other node to this node, add it to list.
if (nodeEdges.exists(node.id)) {
ids.push(id);
if (nodeEdges.exists(node.label)) {
labels.push(label);
}
}

// TODO: Add back in filter.
// if (edges.exists(node.id) && (filter == null || filter(id, edge.get(node.id)))) {
return ids;
return labels;
}

/**
* Get the out-neighbors of the node.
* @param node - The node to get out-neighbors of.
* @param filter - A filter that sorts by either id or edge data.
* @return The list of out-neighbor nodes.
*/
public function outNeighbors(node:GraphNode<T, U>, ?filter:(String, U) -> Bool):Array<GraphNode<T, U>> {
return outNeighborIds(node, filter).map(id -> nodes.get(id));
@:inheritDoc(IGraph.outNeighbors)
public function outNeighbors(node:IGraphNode<T, U>, ?filter:(String, U) -> Bool):Array<IGraphNode<T, U>> {
return outNeighborLabels(node, filter).map(label -> nodes.get(label));
}

/**
* Get the out-neighbors ids of the node.
* @param node - The node to get out-neighbors of.
* @param filter - A filter that sorts by either id or edge data.
* @return The list of out-neighbor node ids.
*/
public function outNeighborIds(node:GraphNode<T, U>, ?filter:(String, U) -> Bool):Array<String> {
if (!edges.exists(node.id)) {
@:inheritDoc(IGraph.outNeighborLabels)
public function outNeighborLabels(node:IGraphNode<T, U>, ?filter:(String, U) -> Bool):Array<String> {
if (!edges.exists(node.label)) {
return [];
}

var ids = [
for (id => data in edges.get(node.id)) {
if (filter == null || filter(id, data)) {
id;
var labels = [
for (label => data in edges.get(node.label)) {
if (filter == null || filter(label, data)) {
label;
}
}
];

haxe.ds.ArraySort.sort(ids, Reflect.compare);
return ids;
haxe.ds.ArraySort.sort(labels, Reflect.compare);
return labels;
}

/**
* Get the in and out neighbor ids of the node.
* @param node - The node to get neighbors of.
* @param filter - A filter that sorts by either id or edge data.
* @return The list of neighbor node ids.
*/
public function neighborIds(node:GraphNode<T, U>, ?filter:(String, U) -> Bool):Array<String> {
return outNeighborIds(node, filter).concat(inNeighborIds(node, filter));
@:inheritDoc(IGraph.neighborLabels)
public function neighborLabels(node:IGraphNode<T, U>, ?filter:(String, U) -> Bool):Array<String> {
return outNeighborLabels(node, filter).concat(inNeighborLabels(node, filter));
}

/**
* Get the in and out neighbor nodes of the node.
* @param node - The node to get neighbors of.
* @param filter - A filter that sorts by either id or edge data.
* @return The list of neighbor nodes.
*/
public function neighbors(node:GraphNode<T, U>, ?filter:(String, U) -> Bool):Array<GraphNode<T, U>> {
@:inheritDoc(IGraph.neighbors)
public function neighbors(node:IGraphNode<T, U>, ?filter:(String, U) -> Bool):Array<IGraphNode<T, U>> {
return outNeighbors(node, filter).concat(inNeighbors(node, filter));
}

/**
* Get the data from the edge between From and To.
* @param fromId - The start node of the edge.
* @param toId - The end node of the edge.
* @return The edge data.
*/
public function edgeData(fromId:String, toId:String):Null<U> {
if (edges.exists(fromId)) {
var edgefrom = edges.get(fromId);
if (edgefrom.exists(toId)) {
return edgefrom.get(toId);
@:inheritDoc(IGraph.edgeData)
public function edgeData(fromLabel:String, toLabel:String):Null<U> {
if (edges.exists(fromLabel)) {
var edgefrom = edges.get(fromLabel);
if (edgefrom.exists(toLabel)) {
return edgefrom.get(toLabel);
}
}

return null;
}

/**
* Outputs the graph as a string, represented by an adjacency list.
*/
@:inheritDoc(IGraph.toString)
public function toString() {
var adjList = "\nGraph:\n";
adjList += "out-Neighbors:\n";
for (node in nodes) {
adjList += node.id;
adjList += node.label;
adjList += "\t-> ";
var neighbors = outNeighbors(node);
for (node in neighbors) {
adjList += node.id;
adjList += node.label;
if (neighbors.indexOf(node) != neighbors.length - 1) {
adjList += ",";
}
Expand All @@ -205,11 +138,11 @@ class Graph<T, U> {

adjList += "in-Neighbors:\n";
for (node in nodes) {
adjList += node.id;
adjList += node.label;
adjList += "\t-> ";
var neighbors = inNeighbors(node);
for (node in neighbors) {
adjList += node.id;
adjList += node.label;
if (neighbors.indexOf(node) != neighbors.length - 1) {
adjList += ",";
}
Expand All @@ -220,20 +153,18 @@ class Graph<T, U> {
return adjList;
}

/**
* Outputs the graph as a string in graph-viz dot format.
*/
@:inheritDoc(IGraph.toDot)
public function toDot() {
var dot = "digraph {\n";

for (node in nodes) {
dot += '\t${node.id}\n';
dot += '\t${node.label}\n';
}

for (node in nodes) {
var neighbors = outNeighbors(node);
for (n in neighbors) {
dot += '\t${node.id} -> ${n.id}\n';
dot += '\t${node.label} -> ${n.label}\n';
}
}

Expand Down
28 changes: 14 additions & 14 deletions src/dropecho/ds/GraphNode.hx
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,42 @@ package dropecho.ds;
*/
@:expose("GraphNode")
@:nativeGen
class GraphNode<T, U> {
public var id:String;
class GraphNode<T, U> implements IGraphNode<T, U> {
public var label:String;
public var value:T;
public var graph:Graph<T, U>;

public function new(?value:T, ?id:String) {
this.id = id != null ? id : Std.string(Std.random(10000000));
public function new(?value:T, ?label:String) {
this.label = label != null ? label : Std.string(Std.random(10000000));
this.value = value;
}

/**
* Add a unidirectional edge from node to another.
*
* @param otherId - The end node of the edge.
* @param otherLabel - The end node of the edge.
* @param data - The data to assign to the edge.
*/
public function addUniEdge(to:GraphNode<T, U>, ?data:U) {
graph.addUniEdge(id, to.id);
public function addUniEdge(to:IGraphNode<T, U>, ?data:U) {
graph.addUniEdge(label, to.label);
}

/**
* Add a bidirectional edge from node to another.
*
* @param otherId - The end node of the edge.
* @param otherLabel - The end node of the edge.
* @param data - The data to assign to the edge.
*/
public function addBiEdge(to:GraphNode<T, U>, ?data:U) {
graph.addBiEdge(id, to.id);
public function addBiEdge(to:IGraphNode<T, U>, ?data:U) {
graph.addBiEdge(label, to.label);
}

/**
* Get the in and out neighbor ids of the node.
* @return The list of neighbor node ids.
* Get the in and out neighbor labels of the node.
* @return The list of neighbor node labels.
*/
public function neighborIds() {
return graph.neighborIds(this);
public function neighborLabels() {
return graph.neighborLabels(this);
}

/**
Expand Down
Loading

0 comments on commit 2af5219

Please sign in to comment.