1 module dstruct.graph;
2 
3 import core.stdc..string: memmove;
4 
5 import dstruct.support;
6 import dstruct.map;
7 
8 /// The directionality of a graph.
9 enum EdgeDirection : bool {
10     /// Undirected, meaning edges face either direction.
11     undirected,
12     /// Directed, meaning edges face one direction.
13     directed,
14 }
15 
16 @trusted
17 private bool findAndRemove(T)(ref T[] arr, ref T needle) {
18     foreach(index; 0 .. arr.length) {
19         if (cast() arr[index] == cast() needle) {
20             // When the index we find is right at the end, we shouldn't
21             // move anything, just reduce the slice on the right by one.
22             if (index != arr.length - 1) {
23                 // Shift all of the array elements back.
24                 memmove(
25                     cast(void*) &arr[index],
26                     cast(void*) &arr[index + 1],
27                     T.sizeof * (arr.length - index)
28                 );
29             }
30 
31             // Now shrink the slice by one element.
32             arr = arr[0 .. $ - 1];
33 
34             return true;
35         }
36     }
37 
38     return false;
39 }
40 
41 private void addIfMissing(T)(ref T[] arr, ref T value) {
42     bool found = false;
43 
44     foreach(ref otherValue; arr) {
45         if (cast() value == cast() otherValue) {
46             found = true;
47             break;
48         }
49     }
50 
51     if (!found) {
52         arr ~= value;
53     }
54 }
55 
56 /**
57  * This struct represents a graph type as a reference type.
58  *
59  * Graphs types have a type of vertex and a direction.
60  *
61  * The graphs are represented by adjacency lists, which have good
62  * all-around performance characteristics for sparse graphs.
63  */
64 struct BasicGraph(Vertex, EdgeDirection edgeDirection) {
65 private:
66     HashMap!(Vertex, Vertex[]) adjacencyMap;
67 public:
68     /// true if this graph is a directed graph.
69     enum bool isDirected = edgeDirection == EdgeDirection.directed;
70 
71     /**
72      * Add a vertex to the graph.
73      */
74     void addVertex(ref Vertex vertex) {
75         adjacencyMap.setDefault(vertex);
76     }
77 
78     /// ditto
79     void addVertex(Vertex vertex) {
80         addVertex(vertex);
81     }
82 
83     /**
84      * Remove the given vertex from this graph.
85      *
86      * Any edges to the given vertex will be removed.
87      *
88      * Returns: true if a vertex was removed.
89      */
90     bool removeVertex(ref Vertex vertex) {
91         // Try to remove the vertex's adjacency mapping first.
92         if (!adjacencyMap.remove(vertex)) {
93             return false;
94         }
95 
96         foreach(ref list; adjacencyMap.byValue) {
97             findAndRemove(list, vertex);
98         }
99 
100         return true;
101     }
102 
103     /// ditto
104     bool removeVertex(Vertex vertex) {
105         return removeVertex(vertex);
106     }
107 
108     /**
109      * Returns: true if the vertex is in the graph.
110      */
111     bool hasVertex(ref Vertex vertex) const {
112         return (vertex in adjacencyMap) !is null;
113     }
114 
115     /// ditto
116     bool hasVertex(Vertex vertex) const {
117         return hasVertex(vertex);
118     }
119 
120     /**
121      * Add an edge to the graph.
122      *
123      * New vertices will be added to the graph automatically.
124      */
125     void addEdge(ref Vertex left, ref Vertex right) {
126         adjacencyMap.setDefault(left).addIfMissing(right);
127 
128         static if (!isDirected) {
129             adjacencyMap.setDefault(right).addIfMissing(left);
130         } else {
131             addVertex(right);
132         }
133     }
134 
135     /// ditto
136     void addEdge(ref Vertex left, Vertex right) {
137         addEdge(left, right);
138     }
139 
140     /// ditto
141     void addEdge(Vertex left, ref Vertex right) {
142         addEdge(left, right);
143     }
144 
145     /// ditto
146     void addEdge(Vertex left, Vertex right) {
147         addEdge(left, right);
148     }
149 
150     /**
151      * Remove an edge from the graph.
152      *
153      * Vertices in the edge will not be removed.
154      *
155      * Returns: true if an edge was removed.
156      */
157     bool removeEdge(ref Vertex left, ref Vertex right) {
158         auto listPtr = left in adjacencyMap;
159 
160         if (listPtr is null) {
161             return false;
162         }
163 
164         if (!findAndRemove(*listPtr, right)) {
165             return false;
166         }
167 
168         static if (!isDirected) {
169             findAndRemove(adjacencyMap[right], left);
170         }
171 
172         return true;
173     }
174 
175     /// ditto
176     bool removeEdge(ref Vertex left, Vertex right) {
177         return removeEdge(left, right);
178     }
179 
180     /// ditto
181     bool removeEdge(Vertex left, ref Vertex right) {
182         return removeEdge(left, right);
183     }
184 
185     /// ditto
186     bool removeEdge(Vertex left, Vertex right) {
187         return removeEdge(left, right);
188     }
189 
190     /**
191      * Check if an edge exists in the graph.
192      *
193      * Returns: true if the edge exists in the graph.
194      */
195     bool hasEdge(ref Vertex left, ref Vertex right) const {
196         if (auto listPtr = left in adjacencyMap) {
197             foreach(ref outgoingVertex; *listPtr) {
198                 if (cast() right == cast() outgoingVertex) {
199                     return true;
200                 }
201             }
202 
203             return false;
204         }
205 
206         return false;
207     }
208 
209     /// ditto
210     bool hasEdge(ref Vertex left, Vertex right) const {
211         return hasEdge(left, right);
212     }
213 
214     /// ditto
215     bool hasEdge(Vertex left, ref Vertex right) const {
216         return hasEdge(left, right);
217     }
218 
219     /// ditto
220     bool hasEdge(Vertex left, Vertex right) const {
221         return hasEdge(left, right);
222     }
223 
224     /**
225      * Return the number of vertices in this graph
226      * in constant time.
227      *
228      * Returns: The number of vertices in this graph.
229      */
230     @property
231     size_t vertexCount() const {
232         return adjacencyMap.length;
233     }
234 
235     /**
236      * Return the number of directed edges in this graph
237      * in linear time. If this graph is a graph with undirected
238      * edges, this will always be double the undirected
239      * edge count.
240      *
241      * Returns: The number of directed edges in this graph.
242      */
243     size_t directedEdgeCount() const {
244         size_t count = 0;
245 
246         foreach(ref list; adjacencyMap.byValue()) {
247             count += list.length;
248         }
249 
250         return count;
251     }
252 
253     /**
254      * Return the number of edges in this graph
255      * in linear time.
256      *
257      * Returns: The number of edges in this graph.
258      */
259     size_t edgeCount() const {
260         static if (!isDirected) {
261             return directedEdgeCount() / 2;
262         } else {
263             return directedEdgeCount();
264         }
265     }
266 }
267 
268 /**
269  * An edge in a graph.
270  */
271 struct Edge(V) {
272     V from;
273     V to;
274 }
275 
276 /// A shorthand for an undirected graph.
277 alias Graph(Vertex) = BasicGraph!(Vertex, EdgeDirection.undirected);
278 
279 /// A shorthand for a directed graph.
280 alias Digraph(Vertex) = BasicGraph!(Vertex, EdgeDirection.directed);
281 
282 /// Test if a type T is an undirected graph type with a particular vertex type.
283 enum isUndirectedGraph(T, Vertex) = is(T == BasicGraph!(Vertex, EdgeDirection.undirected));
284 
285 /// Test if a type T is a directed graph type with a particular vertex type.
286 enum isDirectedGraph(T, Vertex) = is(T == BasicGraph!(Vertex, EdgeDirection.directed));
287 
288 /// Test if a type T is any graph type with a particular vertex type.
289 enum isGraph(T, Vertex) = isUndirectedGraph!(T, Vertex) || isDirectedGraph!(T, Vertex);
290 
291 // Test the templates.
292 unittest {
293     Graph!int graph;
294 
295     assert(isUndirectedGraph!(typeof(graph), int));
296     assert(isGraph!(typeof(graph), int));
297     assert(!isGraph!(typeof(graph), short));
298 
299     Digraph!int digraph;
300 
301     assert(isDirectedGraph!(typeof(digraph), int));
302     assert(isGraph!(typeof(digraph), int));
303     assert(!isGraph!(typeof(digraph), short));
304 }
305 
306 // Test adding vertices and the vertex count on graphs
307 unittest {
308     @safe pure nothrow
309     void runTest() {
310         Graph!string graph;
311 
312         foreach(symbol; ["a", "b", "c", "d", "a"]) {
313             graph.addVertex(symbol);
314         }
315 
316         assert(graph.vertexCount == 4);
317     }
318 
319     runTest();
320 }
321 
322 unittest {
323     @safe pure nothrow
324     void runTest() {
325         Digraph!string digraph;
326 
327         foreach(symbol; ["a", "b", "c", "d", "a"]) {
328             digraph.addVertex(symbol);
329         }
330 
331         // Test that @nogc works.
332         @nogc @safe pure nothrow
333         void runNoGCPart(typeof(digraph) digraph) {
334             assert(digraph.vertexCount == 4);
335         }
336 
337         runNoGCPart(digraph);
338     }
339 
340     runTest();
341 }
342 
343 // Test adding edges and the edge count.
344 unittest {
345     static assert(isUndirectedGraph!(Graph!byte, byte));
346 
347     @safe pure nothrow
348     void runTest() {
349         Graph!byte graph;
350 
351         byte[2][] edgeList = [[1, 2], [2, 1], [3, 4], [5, 6]];
352 
353         foreach(edge; edgeList) {
354             graph.addEdge(edge[0], edge[1]);
355         }
356 
357         @nogc @safe pure nothrow
358         void runNoGCPart(typeof(graph) graph) {
359             assert(graph.directedEdgeCount == 6);
360             assert(graph.edgeCount == 3);
361             assert(graph.hasVertex(1));
362         }
363 
364         runNoGCPart(graph);
365     }
366 
367     runTest();
368 }
369 
370 // Test adding edges and the edge count.
371 unittest {
372     @safe pure nothrow
373     void runTest() {
374         Digraph!byte graph;
375 
376         byte[2][] edgeList = [[1, 2], [2, 1], [3, 4], [5, 6]];
377 
378         foreach(edge; edgeList) {
379             graph.addEdge(edge[0], edge[1]);
380         }
381 
382         @nogc @safe pure nothrow
383         void runNoGCPart(typeof(graph) graph) {
384             assert(graph.directedEdgeCount == 4);
385             assert(graph.edgeCount == 4);
386             assert(graph.hasVertex(1));
387         }
388 
389         runNoGCPart(graph);
390     }
391 
392     runTest();
393 }
394 
395 // Test adding one undirected graph edge implies the reverse.
396 unittest {
397     @safe pure nothrow
398     void runTest() {
399         Graph!byte graph;
400 
401         byte[2][] edgeList = [[1, 2], [3, 4]];
402 
403         foreach(edge; edgeList) {
404             graph.addEdge(edge[0], edge[1]);
405         }
406 
407         @nogc @safe pure nothrow
408         void runNoGCPart(typeof(graph) graph) {
409             assert(graph.edgeCount == 2);
410             assert(graph.hasEdge(2, 1));
411             assert(graph.hasEdge(4, 3));
412         }
413 
414         runNoGCPart(graph);
415     }
416 
417     runTest();
418 }
419 
420 // Test that removing a vertex also removes the edges.
421 unittest {
422     @safe pure nothrow
423     void runTest() {
424         Digraph!byte graph;
425 
426         byte[2][] edgeList = [[1, 2], [3, 1], [2, 3]];
427 
428         foreach(edge; edgeList) {
429             graph.addEdge(edge[0], edge[1]);
430         }
431 
432         @nogc @safe pure nothrow
433         void runNoGCPart(typeof(graph) graph) {
434             assert(graph.removeVertex(1));
435             assert(graph.edgeCount == 1);
436             assert(graph.hasEdge(2, 3));
437         }
438 
439         runNoGCPart(graph);
440     }
441 
442     runTest();
443 }
444 
445 // Test adding and removing vertices for immutable objects
446 unittest {
447     struct SomeType {
448         int x;
449     }
450 
451     @safe pure nothrow
452     void runTest() {
453         Digraph!(immutable(SomeType)) graph;
454 
455         graph.addVertex(immutable(SomeType)(3));
456         graph.addVertex(immutable(SomeType)(4));
457 
458         @nogc @safe pure nothrow
459         void runNoGCPart(typeof(graph) graph) {
460             assert(graph.removeVertex(immutable(SomeType)(3)));
461             assert(graph.removeVertex(immutable(SomeType)(4)));
462         }
463 
464         runNoGCPart(graph);
465     }
466 
467     runTest();
468 }
469 
470 unittest {
471     @safe pure nothrow
472     void runTest() {
473         Graph!byte graph;
474 
475         byte[2][] edgeList = [[1, 2], [3, 1], [2, 3]];
476 
477         foreach(edge; edgeList) {
478             graph.addEdge(edge[0], edge[1]);
479         }
480 
481         @nogc @safe pure nothrow
482         void runNoGCPart(typeof(graph) graph) {
483             assert(graph.removeVertex(1));
484             assert(graph.edgeCount == 1);
485             assert(graph.hasEdge(2, 3));
486         }
487 
488         runNoGCPart(graph);
489     }
490 
491     runTest();
492 }
493 
494 /**
495  * Given any type of graph, produce a range through the vertices of the graph.
496  *
497  * Params:
498  *     graph = A graph.
499  *
500  * Returns:
501  *     A ForwardRange through the vertices of the graph.
502  */
503 @nogc @safe pure nothrow
504 auto vertices(V, EdgeDirection edgeDirection)
505 (auto ref BasicGraph!(V, edgeDirection) graph) {
506     return graph.adjacencyMap.byKey;
507 }
508 
509 /// ditto
510 @nogc @safe pure nothrow
511 auto vertices(V, EdgeDirection edgeDirection)
512 (auto ref const(BasicGraph!(V, edgeDirection)) graph) {
513     return graph.adjacencyMap.byKey;
514 }
515 
516 /// ditto
517 @nogc @safe pure nothrow
518 auto vertices(V, EdgeDirection edgeDirection)
519 (auto ref immutable(BasicGraph!(V, edgeDirection)) graph) {
520     return graph.adjacencyMap.byKey;
521 }
522 
523 unittest {
524     @safe pure nothrow
525     void runTest() {
526         Digraph!string graph;
527 
528         graph.addEdge("a", "b");
529         graph.addEdge("a", "c");
530         graph.addEdge("a", "d");
531         graph.addEdge("b", "e");
532         graph.addEdge("b", "f");
533         graph.addEdge("b", "g");
534 
535         @nogc @safe pure nothrow
536         void runNoGCPart(typeof(graph) graph) {
537             foreach(vertex; graph.vertices) {}
538         }
539 
540         runNoGCPart(graph);
541 
542         string[] vertexList;
543 
544         foreach(vertex; graph.vertices) {
545             vertexList ~= vertex;
546         }
547 
548         // We know we will get this order from how the hashing works.
549         assert(vertexList == ["a", "b", "c", "d", "e", "f", "g"]);
550     }
551 
552     runTest();
553 }
554 
555 unittest {
556     Graph!string mGraph;
557     const(Graph!string) cGraph;
558     immutable(Graph!string) iGraph;
559 
560     auto mVertices = mGraph.vertices();
561     auto cVertices = cGraph.vertices();
562     auto iVertices = iGraph.vertices();
563 
564     assert(is(typeof(mVertices.front) == string));
565     assert(is(typeof(cVertices.front) == const(string)));
566     assert(is(typeof(iVertices.front) == immutable(string)));
567 }
568 
569 /**
570  * A range through the edges of a graph.
571  */
572 struct EdgeRange(V, VArr) {
573 private:
574     KeyValueRange!(V, VArr) _keyValueRange;
575     size_t _outgoingIndex;
576 
577     @nogc @safe pure nothrow
578     this (typeof(_keyValueRange) keyValueRange) {
579         _keyValueRange = keyValueRange;
580 
581         // Advance until we find an edge.
582         while (!_keyValueRange.empty
583         && _keyValueRange.front.value.length == 0) {
584             _keyValueRange.popFront;
585         }
586     }
587 public:
588     ///
589     @nogc @safe pure nothrow
590     inout(typeof(this)) save() inout {
591         return this;
592     }
593 
594     ///
595     @nogc @safe pure nothrow
596     @property
597     bool empty() const {
598         return _keyValueRange.empty;
599     }
600 
601     ///
602     @nogc @safe pure nothrow
603     @property
604     Edge!V front() const {
605         auto item = _keyValueRange.front;
606 
607         return Edge!V(item.key, item.value[_outgoingIndex]);
608     }
609 
610     ///
611     @nogc @safe pure nothrow
612     void popFront() {
613         if (++_outgoingIndex < _keyValueRange.front.value.length) {
614             // There's another outgoing edge in the list, so move to that.
615             return;
616         }
617 
618         // We have to find the next vertex with a non-empty adjacency list.
619         _outgoingIndex = 0;
620 
621         do {
622             _keyValueRange.popFront;
623         } while (!_keyValueRange.empty
624         && _keyValueRange.front.value.length == 0);
625     }
626 }
627 
628 /**
629  * Given any type of graph, produce a range through the edges of the graph.
630  *
631  * Params:
632  *     graph = A graph.
633  *
634  * Returns:
635  *     A ForwardRange through the edges of the graph.
636  */
637 @nogc @safe pure nothrow
638 auto edges(V, EdgeDirection edgeDirection)
639 (auto ref BasicGraph!(V, edgeDirection) graph) {
640     return EdgeRange!(V, V[])(graph.adjacencyMap.byKeyValue);
641 }
642 
643 /// ditto
644 @nogc @trusted pure nothrow
645 auto edges(V, EdgeDirection edgeDirection)
646 (auto ref const(BasicGraph!(V, edgeDirection)) graph) {
647     return EdgeRange!(const(V), const(V[]))(
648         cast(KeyValueRange!(const(V), const(V[])))
649         graph.adjacencyMap.byKeyValue
650     );
651 }
652 
653 /// ditto
654 @nogc @trusted pure nothrow
655 auto edges(V, EdgeDirection edgeDirection)
656 (auto ref immutable(BasicGraph!(V, edgeDirection)) graph) {
657     return EdgeRange!(immutable(V), immutable(V[]))(
658         cast(KeyValueRange!(immutable(V), immutable(V[])))
659         graph.adjacencyMap.byKeyValue
660     );
661 }
662 
663 unittest {
664     @safe pure nothrow
665     void runTest() {
666         Digraph!string graph;
667 
668         graph.addEdge("a", "b");
669         graph.addEdge("a", "c");
670         graph.addEdge("a", "d");
671         graph.addEdge("b", "e");
672         graph.addEdge("b", "f");
673         graph.addEdge("b", "g");
674 
675         @nogc @safe pure nothrow
676         void runNoGCPart(typeof(graph) graph) {
677             foreach(vertex; graph.edges) {}
678         }
679 
680         runNoGCPart(graph);
681 
682         Edge!string[] edgeList;
683 
684         foreach(edge; graph.edges) {
685             edgeList ~= edge;
686         }
687 
688         // We know we will get this order from how the hashing works.
689         assert(edgeList.length);
690         assert(edgeList[0].from == "a");
691         assert(edgeList[0].to == "b");
692         assert(edgeList[1].from == "a");
693         assert(edgeList[1].to == "c");
694         assert(edgeList[2].from == "a");
695         assert(edgeList[2].to == "d");
696         assert(edgeList[3].from == "b");
697         assert(edgeList[3].to == "e");
698         assert(edgeList[4].from == "b");
699         assert(edgeList[4].to == "f");
700         assert(edgeList[5].from == "b");
701         assert(edgeList[5].to == "g");
702     }
703 
704     runTest();
705 }
706 
707 unittest {
708     Graph!string mGraph;
709     const(Graph!string) cGraph;
710     immutable(Graph!string) iGraph;
711 
712     auto mVertices = mGraph.edges();
713     auto cVertices = cGraph.edges();
714     auto iVertices = iGraph.edges();
715 
716     assert(is(typeof(mVertices.front) == Edge!string));
717     assert(is(typeof(cVertices.front) == Edge!(const(string))));
718     assert(is(typeof(iVertices.front) == Edge!(immutable(string))));
719 }