From 0a604491ee7e07d42a3f06e70ec1af2a1b7b40ae Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Mon, 2 Aug 2021 13:47:02 +0200 Subject: [PATCH] Create edges using -> operator, to make direction of edge explicit --- abc/dag/edgeset.nim | 13 ++++++----- abc/dag/sorteddag.nim | 4 +++- tests/abc/testEdgeSet.nim | 24 ++++++++++---------- tests/abc/testSortedDag.nim | 45 +++++++++++++++++-------------------- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/abc/dag/edgeset.nim b/abc/dag/edgeset.nim index 951cc65..5018b15 100644 --- a/abc/dag/edgeset.nim +++ b/abc/dag/edgeset.nim @@ -6,18 +6,18 @@ type # invariant: (∀ x->y ∈ outgoing: y<-x ∈ incoming) outgoing: Table[Vertex, HashSet[Vertex]] incoming: Table[Vertex, HashSet[Vertex]] - Edge*[Vertex] = (Vertex, Vertex) + Edge*[Vertex] = object + x*, y* : Vertex func init*[V](_: type EdgeSet[V]): EdgeSet[V] = discard func incl*[V](edges: var EdgeSet[V], edge: Edge[V]) = - let (x, y) = edge - edges.outgoing.mgetOrPut(x, HashSet[V].default).incl(y) - edges.incoming.mgetOrPut(y, HashSet[V].default).incl(x) + edges.outgoing.mgetOrPut(edge.x, HashSet[V].default).incl(edge.y) + edges.incoming.mgetOrPut(edge.y, HashSet[V].default).incl(edge.x) func contains*[V](edges: EdgeSet[V], edge: Edge[V]): bool = - edge[1] in edges.outgoing.getOrDefault(edge[0]) + edge.y in edges.outgoing.getOrDefault(edge.x) iterator outgoing*[V](edges: EdgeSet[V], vertex: V): V = for v in edges.outgoing.getOrDefault(vertex).items: @@ -26,3 +26,6 @@ iterator outgoing*[V](edges: EdgeSet[V], vertex: V): V = iterator incoming*[V](edges: EdgeSet[V], vertex: V): V = for v in edges.incoming.getOrDefault(vertex).items: yield v + +func `->`*[V](x, y: V): Edge[V] = + Edge[V](x: x, y: y) diff --git a/abc/dag/sorteddag.nim b/abc/dag/sorteddag.nim index 48ec634..69fff25 100644 --- a/abc/dag/sorteddag.nim +++ b/abc/dag/sorteddag.nim @@ -13,6 +13,8 @@ import ./merge ## Uses the dynamic topological sort algorithm by ## [Pearce and Kelly](https://www.doc.ic.ac.uk/~phjk/Publications/DynamicTopoSortAlg-JEA-07.pdf). +export `->` + type SortedDag*[Vertex] = ref object ## A DAG whose vertices are kept in topological order @@ -92,7 +94,7 @@ func add[V](dag: SortedDag[V], vertex: V) = if vertex notin dag: dag.order[vertex] = -(dag.order.len) -func add*[V](dag: SortedDag[V], edge: tuple[x, y: V]) = +func add*[V](dag: SortedDag[V], edge: Edge[V]) = ## Adds an edge x -> y to the DAG dag.add(edge.y) dag.add(edge.x) diff --git a/tests/abc/testEdgeSet.nim b/tests/abc/testEdgeSet.nim index 052d889..3e6d023 100644 --- a/tests/abc/testEdgeSet.nim +++ b/tests/abc/testEdgeSet.nim @@ -11,15 +11,15 @@ suite "DAG edge set": edges = EdgeSet[string].init() test "contains edges": - edges.incl( ("a", "b") ) - check ("a", "b") in edges - check not ( ("a", "c") in edges ) - check not ( ("b", "a") in edges ) + edges.incl("a"->"b") + check ("a"->"b") in edges + check ("a"->"c") notin edges + check ("b"->"a") notin edges test "iterates over outgoing edges": - edges.incl( ("a", "b") ) - edges.incl( ("b", "c") ) - edges.incl( ("a", "c") ) + edges.incl("a"->"b") + edges.incl("b"->"c") + edges.incl("a"->"c") let neighboursA = toSeq(edges.outgoing("a")) let neighboursB = toSeq(edges.outgoing("b")) let neighboursC = toSeq(edges.outgoing("c")) @@ -30,9 +30,9 @@ suite "DAG edge set": check "c" in neighboursB test "iterates over incoming edges": - edges.incl( ("a", "b") ) - edges.incl( ("b", "c") ) - edges.incl( ("a", "c") ) + edges.incl("a"->"b") + edges.incl("b"->"c") + edges.incl("a"->"c") let neighboursA = toSeq(edges.incoming("a")) let neighboursB = toSeq(edges.incoming("b")) let neighboursC = toSeq(edges.incoming("c")) @@ -48,7 +48,7 @@ suite "DAG edge set": let x, y = rand(100) if x != y: let (x, y) = (min(x,y), max(x,y)) - large.incl((x,y)) - check (x, y) in large + large.incl(x->y) + check (x->y) in large check y in toSeq large.outgoing(x) check x in toSeq large.incoming(y) diff --git a/tests/abc/testSortedDag.nim b/tests/abc/testSortedDag.nim index 0d7582d..fd1c76a 100644 --- a/tests/abc/testSortedDag.nim +++ b/tests/abc/testSortedDag.nim @@ -8,20 +8,20 @@ suite "Sorted DAG": test "contains vertices": var dag = SortedDag[int].new - dag.add( (1, 2) ) + dag.add(1->2) check 1 in dag check 2 in dag check 42 notin dag - dag.add( (2, 42) ) + dag.add(2->42) check 42 in dag test "contains edges": var dag = SortedDag[int].new - dag.add( (1, 2) ) - check (1, 2) in dag - check (2, 3) notin dag - dag.add( (2, 3) ) - check (2, 3) in dag + dag.add(1->2) + check (1->2) in dag + check (2->3) notin dag + dag.add(2->3) + check (2->3) in dag test "visits reachable vertices, nearest first": @@ -30,7 +30,7 @@ suite "Sorted DAG": # ② var dag = SortedDag[int].new - for edge in [ (0, 1), (1, 2), (0, 2) ]: + for edge in [0->1, 1->2, 0->2]: dag.add(edge) check toSeq(dag.visit(0)) == @[1, 2] @@ -46,7 +46,7 @@ suite "Sorted DAG": # ③ var dag = SortedDag[int].new - for edge in [ (5, 2), (5, 0), (4, 0), (4, 1), (2, 3), (3, 1) ]: + for edge in [5->2, 5->0, 4->0, 4->1, 2->3, 3->1]: dag.add(edge) let reachableFrom5 = toSeq(dag.visit(5)) @@ -65,11 +65,11 @@ suite "Sorted DAG": # gain ← spend var dag = SortedDag[string].new - for edge in [("acks", "ack1"), - ("acks", "ack2"), - ("ack1", "gain"), - ("ack2", "spend"), - ("spend", "gain")]: + for edge in ["acks"->"ack1", + "acks"->"ack2", + "ack1"->"gain", + "ack2"->"spend", + "spend"->"gain"]: dag.add(edge) let walk = toSeq dag.visit("acks") @@ -91,28 +91,25 @@ suite "Sorted DAG": var dag = SortedDag[int].new for vertex in [1,6]: - dag.add((0, vertex)) + dag.add(0->vertex) for vertex in 1..<5: - dag.add((vertex, vertex + 1)) + dag.add(vertex->vertex + 1) for vertex in 6..<10: - dag.add((vertex, vertex + 1)) + dag.add(vertex->vertex + 1) for vertex in [1, 3, 5]: - dag.add((vertex, vertex + 5)) + dag.add(vertex->vertex + 5) for vertex in [2, 4]: - dag.add((vertex+5, vertex)) + dag.add(vertex+5->vertex) check toSeq(dag.visit(0)) == @[1, 6, 7, 2, 3, 8, 9, 4, 5, 10] test "handles DAGs with many edges": var dag = SortedDag[int].new - - var vertices: seq[int] - for _ in 0..10_000: let x, y = rand(100) if x != y: - dag.add((min(x,y), max(x,y))) + dag.add(min(x,y)->max(x,y)) var latest = -1 for vertex in dag.visit(0): @@ -125,7 +122,7 @@ suite "Sorted DAG": var dag = SortedDag[int].new for i in 1..10_000: - dag.add((i, i-1)) + dag.add(i->i-1) var latest = 10_000 for vertex in dag.visit(10_000):