1 /**
2     Some additional mathematical functions.
3 
4     Copyright: © 2019 Arne Ludwig <arne.ludwig@posteo.de>
5     License: Subject to the terms of the MIT license, as written in the
6              included LICENSE file.
7     Authors: Arne Ludwig <arne.ludwig@posteo.de>
8 */
9 module dalicious.math;
10 
11 import dalicious.algorithm : cmpLexicographically, sliceBy;
12 import std.algorithm :
13     all,
14     among,
15     copy,
16     countUntil,
17     cumulativeFold,
18     filter,
19     map,
20     max,
21     min,
22     maxElement,
23     sort,
24     sum,
25     swap,
26     uniq;
27 import std.array : appender, Appender, array;
28 import std.conv : to;
29 import std.exception : assertThrown;
30 import std.format : format;
31 import std.functional : binaryFun, unaryFun;
32 import std.range :
33     assumeSorted,
34     chain,
35     ElementType,
36     enumerate,
37     isForwardRange,
38     isInputRange,
39     isRandomAccessRange,
40     retro,
41     save,
42     slide,
43     StoppingPolicy,
44     walkLength,
45     zip;
46 import std.traits :
47     isCallable,
48     isIntegral,
49     isNumeric;
50 import std.typecons :
51     Flag,
52     No,
53     Yes;
54 
55 debug import std.stdio : writeln;
56 
57 /// Calculate the mean of range.
58 ElementType!Range mean(Range)(Range values) if (isForwardRange!Range)
59 {
60     auto sum = values.save.sum;
61     auto length = values.walkLength.to!(ElementType!Range);
62 
63     return sum / length;
64 }
65 
66 unittest
67 {
68     {
69         auto values = [2, 4, 8];
70         assert(values.mean == 4);
71     }
72     {
73         auto values = [1.0, 2.0, 3.0, 4.0];
74         assert(values.mean == 2.5);
75     }
76 }
77 
78 /// Calculate the weighted mean of values.
79 double mean(Values, Weights)(Values values, Weights weights)
80     if (isInputRange!Values && isForwardRange!Weights)
81 {
82     enum zeroWeight = cast(ElementType!Weights) 0;
83 
84     auto weightedSum = zip(StoppingPolicy.requireSameLength, values, weights)
85         .map!(pair => (pair[0] * pair[1]).to!double)
86         .sum;
87     auto totalWeight = weights.sum;
88 
89     return weightedSum / totalWeight;
90 }
91 
92 unittest
93 {
94     {
95         auto values = [2, 4, 6];
96         auto equalWeights = [1, 1, 1];
97         auto weights = [3, 4, 1];
98 
99         assert(mean(values, equalWeights) == mean(values));
100         assert(mean(values, weights) == 3.5);
101     }
102     {
103         auto values = [1.0, 2.0, 3.0, 4.0];
104         auto weights = [4.0, 3.0, 2.0, 1.0];
105 
106         assert(mean(values, weights) == 2.0);
107     }
108 }
109 
110 /// Calculate the median of range.
111 ElementType!Range median(Range)(Range values) if (__traits(compiles, sort(values)))
112 {
113     assert(values.length > 0, "median is undefined for empty set");
114     auto middleIdx = values.length / 2;
115     auto useAverage = values.length > 1 && values.length % 2 == 0;
116     auto sortedValues = values.sort;
117 
118     if (useAverage)
119         return (sortedValues[middleIdx - 1] + sortedValues[middleIdx]) / 2;
120     else
121         return values[middleIdx];
122 }
123 
124 unittest
125 {
126     {
127         auto values = [4, 2, 8];
128         assert(values.median == 4);
129     }
130     {
131         auto values = [4, 3, 2, 8];
132         assert(values.median == 3);
133     }
134     {
135         auto values = [4, 6, 2, 8];
136         assert(values.median == 5);
137     }
138     {
139         auto values = [2, 1, 3, 0, 4, 9, 8, 5, 6, 3, 9];
140         assert(values.median == 4);
141     }
142     {
143         auto values = [2.0, 1.0, 4.0, 3.0];
144         assert(values.median == 2.5);
145     }
146     {
147         auto values = [2.0, 1.0, 4.0, 3.0, 5.0];
148         assert(values.median == 3.0);
149     }
150 }
151 
152 /// Calculate the Nxx (e.g. N50) of values.
153 ElementType!Range N(real xx, Range, Num)(Range values, Num totalSize) if (__traits(compiles, sort(values)))
154 {
155     static assert(0 < xx && xx < 100, "N" ~ xx.to!string ~ " is undefined");
156     assert(values.length > 0, "N" ~ xx.to!string ~ " is undefined for empty set");
157     auto xxPercentile = xx/100.0 * totalSize;
158     auto sortedValues = values.sort;
159     auto targetIndex = sortedValues
160         .retro
161         .cumulativeFold!"a + b"(cast(ElementType!Range) 0)
162         .countUntil!"a >= b"(xxPercentile);
163 
164     if (targetIndex.among(-1, values.length))
165         return 0;
166     else
167         return sortedValues[$ - targetIndex - 1];
168 }
169 
170 unittest
171 {
172     {
173         auto totalSize = 54;
174         auto values = [2, 3, 4, 5, 6, 7, 8, 9, 10];
175         enum N50 = 8;
176         enum N10 = 10;
177 
178         assert(N!50(values, totalSize) == N50);
179         assert(N!10(values, totalSize) == N10);
180     }
181     {
182         auto totalSize = 32;
183         auto values = [2, 2, 2, 3, 3, 4, 8, 8];
184         enum N50 = 8;
185 
186         assert(N!50(values, totalSize) == N50);
187     }
188 }
189 
190 enum RoundingMode : byte
191 {
192     floor,
193     round,
194     ceil,
195 }
196 
197 /**
198     Round x upward according to base, ie. returns the next integer larger or
199     equal to x which is divisible by base.
200 
201     Returns: x rounded upward according to base.
202 */
203 Integer ceil(Integer)(in Integer x, in Integer base) pure nothrow
204         if (isIntegral!Integer)
205 {
206     return x % base == 0
207         ? x
208         : (x / base + 1) * base;
209 }
210 
211 ///
212 unittest
213 {
214     assert(ceil(8, 10) == 10);
215     assert(ceil(32, 16) == 32);
216     assert(ceil(101, 100) == 200);
217 }
218 
219 /**
220     Round x downward according to base, ie. returns the next integer smaller or
221     equal to x which is divisible by base.
222 
223     Returns: x rounded downward according to base.
224 */
225 Integer floor(Integer)(in Integer x, in Integer base) pure nothrow
226         if (isIntegral!Integer)
227 {
228     return (x / base) * base;
229 }
230 
231 ///
232 unittest
233 {
234     assert(floor(8, 10) == 0);
235     assert(floor(32, 16) == 32);
236     assert(floor(101, 100) == 100);
237 }
238 
239 /// Returns the absolute difference between two numbers.
240 Num absdiff(Num)(in Num a, in Num b) pure nothrow if (isNumeric!Num)
241 {
242     return a > b
243         ? a - b
244         : b - a;
245 }
246 
247 ///
248 unittest
249 {
250     assert(absdiff(2UL, 3UL) == 1UL);
251     assert(absdiff(-42, 13) == 55);
252     assert(absdiff(2.5, 5) == 2.5);
253 }
254 
255 /// Returns the result of `ceil(a / b)` but uses integer arithmetic only.
256 Integer ceildiv(Integer)(in Integer a, in Integer b) pure nothrow if (isIntegral!Integer)
257 {
258     Integer resultSign = (a < 0) ^ (b < 0) ? -1 : 1;
259 
260     return resultSign < 0 || a % b == 0
261         ? a / b
262         : a / b + resultSign;
263 }
264 
265 ///
266 unittest
267 {
268     assert(ceildiv(0, 3) == 0);
269     assert(ceildiv(1UL, 3UL) == 1UL);
270     assert(ceildiv(2L, 3L) == 1L);
271     assert(ceildiv(3U, 3U) == 1U);
272     assert(ceildiv(4, 3) == 2);
273     assert(ceildiv(-4, 4) == -1);
274     assert(ceildiv(-4, 3) == -1);
275 }
276 
277 class EdgeExistsException : Exception
278 {
279     pure nothrow @nogc @safe this(
280         string file = __FILE__,
281         size_t line = __LINE__,
282         Throwable nextInChain = null,
283     )
284     {
285         super("edge cannot be inserted: edge already exists", file, line, nextInChain);
286     }
287 }
288 
289 class MissingEdgeException : Exception
290 {
291     pure nothrow @nogc @safe this(
292         string file = __FILE__,
293         size_t line = __LINE__,
294         Throwable nextInChain = null,
295     )
296     {
297         super("edge not found", file, line, nextInChain);
298     }
299 }
300 
301 class MissingNodeException : Exception
302 {
303     pure nothrow @nogc @safe this(
304         string file = __FILE__,
305         size_t line = __LINE__,
306         Throwable nextInChain = null,
307     )
308     {
309         super("node not found", file, line, nextInChain);
310     }
311 }
312 
313 /// This structure represents a graph with optional edge
314 /// payloads. The graph is represented as a list of edges which is
315 /// particularly suited for sparse graphs. While the set of nodes is fixed the
316 /// set of edges is mutable.
317 struct Graph(Node, Weight = void, Flag!"isDirected" isDirected = No.isDirected, EdgePayload = void)
318 {
319     static enum isWeighted = !is(Weight == void);
320     static enum hasEdgePayload = !is(EdgePayload == void);
321 
322     static struct Edge
323     {
324         protected Node _start;
325         protected Node _end;
326 
327         static if (isWeighted)
328             Weight weight;
329 
330         static if (hasEdgePayload)
331             EdgePayload payload;
332 
333         /// Construct an edge.
334         this(Node start, Node end)
335         {
336             this._start = start;
337             this._end = end;
338 
339             static if (!isDirected)
340             {
341                 if (end < start)
342                 {
343                     swap(this._start, this._end);
344                 }
345             }
346         }
347 
348         static if (isWeighted)
349         {
350             /// ditto
351             this(Node start, Node end, Weight weight)
352             {
353                 this(start, end);
354                 this.weight = weight;
355             }
356         }
357 
358         static if (hasEdgePayload && !is(EdgePayload : Weight))
359         {
360             /// ditto
361             this(Node start, Node end, EdgePayload payload)
362             {
363                 this(start, end);
364                 this.payload = payload;
365             }
366         }
367 
368         static if (isWeighted && hasEdgePayload)
369         {
370             /// ditto
371             this(Node start, Node end, Weight weight, EdgePayload payload)
372             {
373                 this(start, end);
374                 this.weight = weight;
375                 this.payload = payload;
376             }
377         }
378 
379         /// Get the start of this edge. For undirected graphs this is the
380         /// smaller of both incident nodes.
381         @property Node start() const pure nothrow
382         {
383             return _start;
384         }
385 
386         /// Get the end of this edge. For undirected graphs this is the
387         /// larger of both incident nodes.
388         @property Node end() const pure nothrow
389         {
390             return _end;
391         }
392 
393         /**
394             Get target of this edge beginning at node `from`. For undirected
395             graphs returns the other node of this edge.
396 
397             Throws: MissingNodeException if this edge does not start in node `from`.
398         */
399         Node target(Node from) const
400         {
401             static if (isDirected)
402             {
403                 if (start == from)
404                 {
405                     return end;
406                 }
407                 else
408                 {
409                     throw new MissingNodeException();
410                 }
411             }
412             else
413             {
414                 if (start == from)
415                 {
416                     return end;
417                 }
418                 else if (end == from)
419                 {
420                     return start;
421                 }
422                 else
423                 {
424                     throw new MissingNodeException();
425                 }
426             }
427         }
428 
429         /**
430             Get source of this edge beginning at node `from`. For undirected
431             graphs returns the other node of this edge.
432 
433             Throws: MissingNodeException if this edge does not end in node `from`.
434         */
435         static if (isDirected)
436         {
437             Node source(Node from) const
438             {
439                 if (end == from)
440                 {
441                     return start;
442                 }
443                 else
444                 {
445                     throw new MissingNodeException();
446                 }
447             }
448         }
449         else
450         {
451             alias source = target;
452         }
453 
454         /// Two edges are equal iff their incident nodes (and weight) are the
455         /// same.
456         bool opEquals(in Edge other) const pure nothrow
457         {
458             static if (isWeighted)
459             {
460                 return this.start == other.start && this.end == other.end
461                     && this.weight == other.weight;
462             }
463             else
464             {
465                 return this.start == other.start && this.end == other.end;
466             }
467         }
468 
469         /// Orders edge lexicographically by `start`, `end`(, `weight`).
470         int opCmp(in Edge other) const pure nothrow
471         {
472             static if (isWeighted)
473             {
474                 return cmpLexicographically!(
475                     typeof(this),
476                     "a.start",
477                     "a.end",
478                     "a.weight",
479                 )(this, other);
480             }
481             else
482             {
483                 return cmpLexicographically!(
484                     typeof(this),
485                     "a.start",
486                     "a.end",
487                 )(this, other);
488             }
489         }
490 
491         private int compareNodes(in Edge other) const pure nothrow
492         {
493             return cmpLexicographically!(
494                 typeof(this),
495                 "a.start",
496                 "a.end",
497             )(this, other);
498         }
499 
500         /**
501             Returns the node that is connects this edge with other edge. In
502             case of undirected graphs this is just the common node of both
503             edges; in directed case this is the end node of this edge if it
504             matches the start node of other edge.
505 
506             Throws: MissingNodeException if the connecting node is undefined.
507         */
508         Node getConnectingNode(in Edge other) const
509         {
510             static if (isDirected)
511             {
512                 if (this.end == other.start)
513                 {
514                     return this.end;
515                 }
516             }
517             else
518             {
519                 if (this.end == other.start || this.end == other.end)
520                 {
521                     return this.end;
522                 }
523                 else if (this.start == other.start || this.start == other.end)
524                 {
525                     return this.start;
526                 }
527             }
528 
529             throw new MissingNodeException();
530         }
531     }
532 
533     static bool orderByNodes(in Edge a, in Edge b) nothrow pure
534     {
535         return a.compareNodes(b) < 0;
536     }
537 
538     static bool groupByNodes(in Edge a, in Edge b) nothrow pure
539     {
540         return a.compareNodes(b) == 0;
541     }
542 
543     /// Construct an edge for this graph.
544     static Edge edge(T...)(T args)
545     {
546         return Edge(args);
547     }
548 
549     protected Node[] _nodes;
550     protected Appender!(Edge[]) _edges;
551 
552     /// The set (ordered list) of nodes.
553     @property const(Node[]) nodes() const nothrow pure
554     {
555         return _nodes;
556     }
557 
558     private @property void nodes(Node[] nodes)
559     {
560         nodes.sort();
561 
562         this._nodes = nodes.uniq.array;
563     }
564 
565     /// Get the set (ordered list) of edges in this graph.
566     @property auto edges() nothrow pure
567     {
568         return chain(_edges.data);
569     }
570 
571     /// ditto
572     @property auto edges() const nothrow pure
573     {
574         return chain(_edges.data);
575     }
576 
577     /**
578         Construct a graph from a set of nodes (and edges). Modifies `nodes`
579         while sorting but releases it after construction.
580 
581         Throws: MissingNodeException if an edge has a node that is not present
582                 in this graph .
583         Throws: EdgeExistsException if an edge already exists when trying
584                 inserting it.
585     */
586     this(Node[] nodes)
587     {
588         this.nodes = nodes;
589     }
590 
591     /// ditto
592     this(Node[] nodes, Edge[] edges)
593     {
594         this(nodes);
595 
596         _edges.reserve(edges.length);
597         foreach (edge; edges)
598         {
599             add(this, edge);
600         }
601     }
602 
603     this(this)
604     {
605         _nodes = _nodes.dup;
606     }
607 
608     /// Add a set of edges to this graph without any checks.
609     void bulkAddForce(R)(R edges) if (isForwardRange!R && is(ElementType!R == Edge))
610     {
611         this._edges ~= edges;
612         _edges.data.sort;
613     }
614 
615     /// Add an edge to this graph.
616     /// See_Also: `Edge add(Graph, Edge)`
617     void opOpAssign(string op)(Edge edge) if (op == "~")
618     {
619         add(this, edge);
620     }
621 
622     /// Some pre-defined conflict handlers for `add`.
623     static struct ConflictStrategy
624     {
625         static if (isWeighted)
626         {
627             /// Return an edge with sum of both weights. If given payload will be
628             /// kept from existingEdge .
629             static Edge sumWeights(Edge existingEdge, Edge newEdge)
630             {
631                 existingEdge.weight += newEdge.weight;
632 
633                 return existingEdge;
634             }
635 
636             ///
637             unittest
638             {
639                 auto g1 = Graph!(int, int)([1, 2]);
640                 alias CS = g1.ConflictStrategy;
641 
642                 g1 ~= g1.edge(1, 2, 1);
643 
644                 auto addedEdge = g1.add!(CS.sumWeights)(g1.edge(1, 2, 1));
645 
646                 assert(addedEdge.weight == 2);
647             }
648         }
649 
650         /// Throw `EdgeExistsException`.
651         static inout(Edge) error(inout(Edge) existingEdge, inout(Edge) newEdge)
652         {
653             throw new EdgeExistsException();
654         }
655 
656         ///
657         unittest
658         {
659             auto g1 = Graph!int([1, 2]);
660             alias CS = g1.ConflictStrategy;
661 
662             g1 ~= g1.edge(1, 2);
663 
664             assertThrown!EdgeExistsException(g1.add!(CS.error)(g1.edge(1, 2)));
665         }
666 
667         /// Replace the existingEdge by newEdge.
668         static inout(Edge) replace(inout(Edge) existingEdge, inout(Edge) newEdge)
669         {
670             return newEdge;
671         }
672 
673         ///
674         unittest
675         {
676             auto g1 = Graph!(int, int)([1, 2]);
677             alias CS = g1.ConflictStrategy;
678 
679             g1 ~= g1.edge(1, 2, 1);
680 
681             auto addedEdge = g1.add!(CS.replace)(g1.edge(1, 2, 2));
682 
683             assert(addedEdge.weight == 2);
684         }
685 
686         /// Keep existingEdge – discard newEdge.
687         static inout(Edge) keep(inout(Edge) existingEdge, inout(Edge) newEdge)
688         {
689             return existingEdge;
690         }
691 
692         ///
693         unittest
694         {
695             auto g1 = Graph!(int, int)([1, 2]);
696             alias CS = g1.ConflictStrategy;
697 
698             g1 ~= g1.edge(1, 2, 1);
699 
700             auto addedEdge = g1.add!(CS.keep)(g1.edge(1, 2, 2));
701 
702             assert(addedEdge.weight == 1);
703         }
704     }
705 
706     /// Forcibly add an edge to this graph.
707     protected Edge forceAdd(Edge edge)
708     {
709         _edges ~= edge;
710         _edges.data.sort;
711 
712         return edge;
713     }
714 
715     /// Replace an edge in this graph.
716     protected Edge replaceEdge(in size_t edgeIdx, Edge newEdge)
717     {
718         auto shouldSort = _edges.data[edgeIdx] != newEdge;
719 
720         _edges.data[edgeIdx] = newEdge;
721 
722         if (shouldSort)
723         {
724             _edges.data.sort;
725         }
726 
727         return newEdge;
728     }
729 
730     /// Check if edge/node exists in this graph. Ignores the weight if weighted.
731     bool opBinaryRight(string op)(in Node node) const pure nothrow if (op == "in")
732     {
733         auto sortedNodes = assumeSorted(nodes);
734 
735         return sortedNodes.contains(node);
736     }
737 
738     /// ditto
739     bool has(in Node node) const pure nothrow
740     {
741         return node in this;
742     }
743 
744     /// Check if edge exists in this graph. Only the `start` and `end` node
745     /// will be compared.
746     bool opBinaryRight(string op)(in Edge edge) const pure nothrow if (op == "in")
747     {
748         auto sortedEdges = assumeSorted!orderByNodes(edges);
749 
750         return sortedEdges.contains(edge);
751     }
752 
753     /// ditto
754     bool has(in Edge edge) const pure nothrow
755     {
756         return edge in this;
757     }
758 
759     /// Get the designated edge from this graph. Only the `start` and `end`
760     /// node will be compared.
761     auto ref get(in Edge edge)
762     {
763         auto sortedEdges = assumeSorted!orderByNodes(edges);
764         auto existingEdges = sortedEdges.equalRange(edge);
765 
766         if (existingEdges.empty)
767         {
768             throw new MissingEdgeException();
769         }
770         else
771         {
772             return existingEdges.front;
773         }
774     }
775 
776     ///
777     unittest
778     {
779         auto g1 = Graph!(int, int)([1, 2]);
780 
781         auto e1 = g1.edge(1, 2, 1);
782 
783         g1 ~= e1;
784 
785         assert(g1.get(g1.edge(1, 2)) == e1);
786         assertThrown!MissingEdgeException(g1.get(g1.edge(1, 1)));
787     }
788 
789     /// Returns the index of node `n` in the list of nodes.
790     size_t indexOf(in Node n) const
791     {
792         auto sortedNodes = assumeSorted(nodes);
793         auto tristectedNodes = sortedNodes.trisect(n);
794 
795         if (tristectedNodes[1].empty)
796         {
797             throw new MissingNodeException();
798         }
799 
800         return tristectedNodes[0].length;
801     }
802 
803     ///
804     unittest
805     {
806         auto g1 = Graph!(int, int)([1, 2]);
807 
808         assert(g1.indexOf(1) == 0);
809         assert(g1.indexOf(2) == 1);
810         assertThrown!MissingNodeException(g1.indexOf(3));
811     }
812 
813     /// Returns the index of node `n` in the list of nodes.
814     size_t indexOf(in Edge edge) const
815     {
816         auto sortedEdges = assumeSorted!orderByNodes(edges);
817         auto trisectedEdges = sortedEdges.trisect(edge);
818 
819         if (trisectedEdges[1].empty)
820         {
821             throw new MissingEdgeException();
822         }
823 
824         return trisectedEdges[0].length;
825     }
826 
827     ///
828     unittest
829     {
830         auto g1 = Graph!(int, int)([1, 2]);
831 
832         auto e1 = g1.edge(1, 2, 1);
833 
834         g1 ~= e1;
835 
836         assert(g1.indexOf(g1.edge(1, 2)) == 0);
837         assertThrown!MissingEdgeException(g1.indexOf(g1.edge(1, 1)));
838     }
839 
840     static if (isDirected)
841     {
842         /// Returns a range of in/outgoing edges of node `n`.
843         auto inEdges(Node n) nothrow pure
844         {
845             return _edges.data[].filter!(e => e.end == n);
846         }
847 
848         /// ditto
849         auto inEdges(Node n) const nothrow pure
850         {
851             return edges[].filter!(e => e.end == n);
852         }
853 
854         /// ditto
855         auto outEdges(Node n) nothrow pure
856         {
857             return _edges.data[].filter!(e => e.start == n);
858         }
859 
860         /// ditto
861         auto outEdges(Node n) const nothrow pure
862         {
863             return edges[].filter!(e => e.start == n);
864         }
865 
866         ///
867         unittest
868         {
869             import std.algorithm : equal;
870 
871             auto g1 = Graph!(int, void, Yes.isDirected)([1, 2, 3]);
872 
873             g1 ~= g1.edge(1, 1);
874             g1 ~= g1.edge(1, 2);
875             g1 ~= g1.edge(2, 2);
876             g1 ~= g1.edge(2, 3);
877 
878             assert(g1.inEdges(1).equal([
879                 g1.edge(1, 1),
880             ]));
881             assert(g1.outEdges(1).equal([
882                 g1.edge(1, 1),
883                 g1.edge(1, 2),
884             ]));
885             assert(g1.inEdges(2).equal([
886                 g1.edge(1, 2),
887                 g1.edge(2, 2),
888             ]));
889             assert(g1.outEdges(2).equal([
890                 g1.edge(2, 2),
891                 g1.edge(2, 3),
892             ]));
893             assert(g1.inEdges(3).equal([
894                 g1.edge(2, 3),
895             ]));
896             assert(g1.outEdges(3).empty);
897         }
898 
899         /// Get the in/out degree of node `n`.
900         size_t inDegree(Node n) const nothrow pure
901         {
902             return inEdges(n).walkLength;
903         }
904 
905         /// ditto
906         size_t outDegree(Node n) const nothrow pure
907         {
908             return outEdges(n).walkLength;
909         }
910 
911         ///
912         unittest
913         {
914             auto g1 = Graph!(int, void, Yes.isDirected)([1, 2, 3]);
915 
916             g1 ~= g1.edge(1, 1);
917             g1 ~= g1.edge(1, 2);
918             g1 ~= g1.edge(2, 2);
919             g1 ~= g1.edge(2, 3);
920 
921             assert(g1.inDegree(1) == 1);
922             assert(g1.outDegree(1) == 2);
923             assert(g1.inDegree(2) == 2);
924             assert(g1.outDegree(2) == 2);
925             assert(g1.inDegree(3) == 1);
926             assert(g1.outDegree(3) == 0);
927         }
928     }
929     else
930     {
931         /// Returns a range of all edges incident to node `n`.
932         auto incidentEdges(Node n) nothrow pure
933         {
934             return _edges.data[].filter!(e => e.start == n || e.end == n);
935         }
936 
937         /// ditto
938         auto incidentEdges(Node n) const nothrow pure
939         {
940             return edges[].filter!(e => e.start == n || e.end == n);
941         }
942 
943         /// ditto
944         alias inEdges = incidentEdges;
945 
946         /// ditto
947         alias outEdges = incidentEdges;
948 
949         ///
950         unittest
951         {
952             import std.algorithm : equal;
953 
954             auto g1 = Graph!int([1, 2, 3]);
955 
956             g1 ~= g1.edge(1, 1);
957             g1 ~= g1.edge(1, 2);
958             g1 ~= g1.edge(2, 2);
959             g1 ~= g1.edge(2, 3);
960 
961             assert(g1.incidentEdges(1).equal([
962                 g1.edge(1, 1),
963                 g1.edge(1, 2),
964             ]));
965             assert(g1.incidentEdges(2).equal([
966                 g1.edge(1, 2),
967                 g1.edge(2, 2),
968                 g1.edge(2, 3),
969             ]));
970             assert(g1.incidentEdges(3).equal([
971                 g1.edge(2, 3),
972             ]));
973         }
974 
975         IncidentEdgesCache allIncidentEdges()
976         {
977             return IncidentEdgesCache(this);
978         }
979 
980         static struct IncidentEdgesCache
981         {
982             alias G = Graph!(Node, Weight, isDirected, EdgePayload);
983             G graph;
984             Edge[][] incidentEdges;
985 
986             this(G graph)
987             {
988                 this.graph = graph;
989                 collectAllIncidentEdges();
990             }
991 
992             private void collectAllIncidentEdges()
993             {
994                 preallocateMemory();
995 
996                 size_t startIdx;
997                 size_t endIdx;
998                 foreach (edge; graph._edges.data)
999                 {
1000                     if (graph._nodes[startIdx] < edge.start)
1001                         endIdx = startIdx;
1002                     while (graph._nodes[startIdx] < edge.start)
1003                         ++startIdx;
1004                     if (endIdx < startIdx)
1005                         endIdx = startIdx;
1006                     while (graph._nodes[endIdx] < edge.end)
1007                         ++endIdx;
1008 
1009                     incidentEdges[startIdx] ~= edge;
1010                     // Avoid double-counting of loops
1011                     if (startIdx != endIdx)
1012                         incidentEdges[endIdx] ~= edge;
1013                 }
1014             }
1015 
1016             void preallocateMemory()
1017             {
1018                 auto degreesCache = graph.allDegrees();
1019                 Edge[] buffer;
1020                 buffer.length = degreesCache.degrees.sum;
1021                 incidentEdges.length = degreesCache.degrees.length;
1022 
1023                 size_t sliceBegin;
1024                 size_t startIdx;
1025                 foreach (degree; degreesCache)
1026                 {
1027                     incidentEdges[startIdx] = buffer[sliceBegin .. sliceBegin + degree];
1028                     incidentEdges[startIdx].length = 0;
1029 
1030                     sliceBegin += degree;
1031                     ++startIdx;
1032                 }
1033             }
1034 
1035             ref inout(Edge[]) opIndex(in Node node) inout
1036             {
1037                 return incidentEdges[graph.indexOf(node)];
1038             }
1039 
1040             ref inout(Edge[]) opIndex(in size_t nodeIdx) inout
1041             {
1042                 return incidentEdges[nodeIdx];
1043             }
1044 
1045             int opApply(scope int delegate(Edge[]) yield)
1046             {
1047                 int result = 0;
1048 
1049                 foreach (currentIncidentEdges; incidentEdges)
1050                 {
1051                     result = yield(currentIncidentEdges);
1052                     if (result)
1053                         break;
1054                 }
1055 
1056                 return result;
1057             }
1058 
1059             int opApply(scope int delegate(Node, Edge[]) yield)
1060             {
1061                 int result = 0;
1062 
1063                 foreach (i, currentIncidentEdges; incidentEdges)
1064                 {
1065                     result = yield(graph._nodes[i], currentIncidentEdges);
1066                     if (result)
1067                         break;
1068                 }
1069 
1070                 return result;
1071             }
1072         }
1073 
1074         /// Get the `adjacencyList` of this graph where nodes are represented
1075         /// by their index in the nodes list.
1076         size_t[][] adjacencyList() const
1077         {
1078             size_t[][] _adjacencyList;
1079             _adjacencyList.length = nodes.length;
1080             size_t[] targetsBuffer;
1081             targetsBuffer.length = 2 * edges.length;
1082 
1083             foreach (i, node; _nodes)
1084             {
1085                 auto bufferRest = edges
1086                     .filter!(e => e.start == node || e.end == node)
1087                     .map!(edge => indexOf(edge.target(node)))
1088                     .copy(targetsBuffer);
1089                 _adjacencyList[i] = targetsBuffer[0 .. $ - bufferRest.length];
1090                 _adjacencyList[i].sort;
1091                 targetsBuffer = bufferRest;
1092             }
1093 
1094             return _adjacencyList;
1095         }
1096 
1097         ///
1098         unittest
1099         {
1100             auto g1 = Graph!int([1, 2, 3, 4]);
1101 
1102             g1 ~= g1.edge(1, 1);
1103             g1 ~= g1.edge(1, 2);
1104             g1 ~= g1.edge(2, 2);
1105             g1 ~= g1.edge(2, 3);
1106             g1 ~= g1.edge(2, 4);
1107             g1 ~= g1.edge(3, 4);
1108 
1109             assert(g1.adjacencyList() == [
1110                 [0, 1],
1111                 [0, 1, 2, 3],
1112                 [1, 3],
1113                 [1, 2],
1114             ]);
1115         }
1116 
1117         /// Get the degree of node `n`.
1118         size_t degree(Node n) const nothrow pure
1119         {
1120             return incidentEdges(n).walkLength;
1121         }
1122 
1123         /// ditto
1124         alias inDegree = degree;
1125 
1126         /// ditto
1127         alias outDegree = degree;
1128 
1129         DegreesCache allDegrees() const
1130         {
1131             return DegreesCache(this);
1132         }
1133 
1134         static struct DegreesCache
1135         {
1136             alias G = Graph!(Node, Weight, isDirected, EdgePayload);
1137             const(G) graph;
1138             size_t[] degrees;
1139 
1140             this(in G graph)
1141             {
1142                 this.graph = graph;
1143                 collectAllDegrees();
1144             }
1145 
1146             private void collectAllDegrees()
1147             {
1148                 degrees.length = graph._nodes.length;
1149 
1150                 size_t startIdx;
1151                 size_t endIdx;
1152                 foreach (edge; graph._edges.data)
1153                 {
1154                     if (graph._nodes[startIdx] < edge.start)
1155                         endIdx = startIdx;
1156                     while (graph._nodes[startIdx] < edge.start)
1157                         ++startIdx;
1158                     if (endIdx < startIdx)
1159                         endIdx = startIdx;
1160                     while (graph._nodes[endIdx] < edge.end)
1161                         ++endIdx;
1162 
1163                     ++degrees[startIdx];
1164                     // Avoid double-counting of loops
1165                     if (startIdx != endIdx)
1166                         ++degrees[endIdx];
1167                 }
1168             }
1169 
1170             size_t opIndex(in Node node) const
1171             {
1172                 return degrees[graph.indexOf(node)];
1173             }
1174 
1175             size_t opIndex(in size_t nodeIdx) const
1176             {
1177                 return degrees[nodeIdx];
1178             }
1179 
1180             int opApply(scope int delegate(size_t) yield) const
1181             {
1182                 int result = 0;
1183 
1184                 foreach (degree; degrees)
1185                 {
1186                     result = yield(degree);
1187                     if (result)
1188                         break;
1189                 }
1190 
1191                 return result;
1192             }
1193 
1194             int opApply(scope int delegate(Node, size_t) yield) const
1195             {
1196                 int result = 0;
1197 
1198                 foreach (i, degree; degrees)
1199                 {
1200                     result = yield(graph._nodes[i], degree);
1201                     if (result)
1202                         break;
1203                 }
1204 
1205                 return result;
1206             }
1207         }
1208     }
1209 }
1210 
1211 ///
1212 unittest
1213 {
1214     //   +-+  +-+
1215     //   \ /  \ /
1216     //   (1)--(2)
1217     auto g1 = Graph!int([1, 2]);
1218 
1219     g1 ~= g1.edge(1, 1);
1220     g1 ~= g1.edge(1, 2);
1221     g1.add(g1.edge(2, 2));
1222 
1223     assert(g1.edge(1, 1) in g1);
1224     assert(g1.edge(1, 2) in g1);
1225     assert(g1.edge(2, 1) in g1);
1226     assert(g1.has(g1.edge(2, 2)));
1227     assert(g1.allDegrees().degrees == [2, 2]);
1228     assert(g1.allIncidentEdges().incidentEdges == [
1229         [g1.edge(1, 1), g1.edge(1, 2)],
1230         [g1.edge(1, 2), g1.edge(2, 2)],
1231     ]);
1232 
1233     //   0.5     0.5
1234     //   +-+     +-+
1235     //   \ /     \ /
1236     //   (1)-----(2)
1237     //       1.0
1238     auto g2 = Graph!(int, double)([1, 2]);
1239 
1240     g2 ~= g2.edge(1, 1, 0.5);
1241     g2 ~= g2.edge(1, 2, 1.0);
1242     g2.add(g2.edge(2, 2, 0.5));
1243 
1244     assert(g2.edge(1, 1) in g2);
1245     assert(g2.edge(1, 2) in g2);
1246     assert(g2.edge(2, 1) in g2);
1247     assert(g2.has(g2.edge(2, 2)));
1248     assert(g2.allDegrees().degrees == [2, 2]);
1249     assert(g2.allIncidentEdges().incidentEdges == [
1250         [g2.edge(1, 1, 0.5), g2.edge(1, 2, 1.0)],
1251         [g2.edge(1, 2, 1.0), g2.edge(2, 2, 0.5)],
1252     ]);
1253 
1254     //   0.5     0.5
1255     //   +-+     +-+
1256     //   \ v     v /
1257     //   (1)---->(2)
1258     //       1.0
1259     auto g3 = Graph!(int, double, Yes.isDirected)([1, 2]);
1260 
1261     g3 ~= g3.edge(1, 1, 0.5);
1262     g3 ~= g3.edge(1, 2, 1.0);
1263     g3.add(g3.edge(2, 2, 0.5));
1264 
1265     assert(g3.edge(1, 1) in g3);
1266     assert(g3.edge(1, 2) in g3);
1267     assert(!(g3.edge(2, 1) in g3));
1268     assert(g3.has(g3.edge(2, 2)));
1269 
1270     //   +-+   +-+
1271     //   \ v   v /
1272     //   (1)-->(2)
1273     auto g4 = Graph!(int, void, Yes.isDirected)([1, 2]);
1274 
1275     g4 ~= g4.edge(1, 1);
1276     g4 ~= g4.edge(1, 2);
1277     g4.add(g4.edge(2, 2));
1278 
1279     assert(g4.edge(1, 1) in g4);
1280     assert(g4.edge(1, 2) in g4);
1281     assert(!(g4.edge(2, 1) in g4));
1282     assert(g4.has(g4.edge(2, 2)));
1283 
1284     //   +-+  +-+
1285     //   \ /  \ /
1286     //   (1)--(2)
1287     //
1288     // payload(1, 1) = [1];
1289     // payload(1, 2) = [2];
1290     // payload(2, 2) = [3];
1291     auto g5 = Graph!(int, void, No.isDirected, int[])([1, 2]);
1292 
1293     g5 ~= g5.edge(1, 1, [1]);
1294     g5 ~= g5.edge(1, 2, [2]);
1295     g5.add(g5.edge(2, 2, [3]));
1296 
1297     assert(g5.edge(1, 1) in g5);
1298     assert(g5.get(g5.edge(1, 1)).payload == [1]);
1299     assert(g5.edge(1, 2) in g5);
1300     assert(g5.get(g5.edge(1, 2)).payload == [2]);
1301     assert(g5.edge(2, 1) in g5);
1302     assert(g5.get(g5.edge(2, 1)).payload == [2]);
1303     assert(g5.has(g5.edge(2, 2)));
1304     assert(g5.get(g5.edge(2, 2)).payload == [3]);
1305     assert(g5.allDegrees().degrees == [2, 2]);
1306     assert(g5.allIncidentEdges().incidentEdges == [
1307         [g5.edge(1, 1), g5.edge(1, 2)],
1308         [g5.edge(1, 2), g5.edge(2, 2)],
1309     ]);
1310 }
1311 
1312 ///
1313 unittest
1314 {
1315     //     -1     1         1
1316     // (1)----(2)---(3) (4)---(5) (6)
1317     size_t[] contigs = [1, 2, 3, 4, 5, 6];
1318     auto contigGraph = Graph!(size_t, int)([1, 2, 3, 4, 5, 6]);
1319 
1320     contigGraph.add(contigGraph.edge(1, 2, -1));
1321     contigGraph.add(contigGraph.edge(2, 3, 1));
1322     contigGraph.add(contigGraph.edge(4, 5, 1));
1323 
1324     foreach (contig; contigs)
1325     {
1326         assert(contigGraph.degree(contig) <= 2);
1327     }
1328     assert(contigGraph.allDegrees().degrees == [1, 2, 1, 1, 1, 0]);
1329     assert(contigGraph.allIncidentEdges().incidentEdges == [
1330         [contigGraph.edge(1, 2, -1)],
1331         [contigGraph.edge(1, 2, -1), contigGraph.edge(2, 3, 1)],
1332         [contigGraph.edge(2, 3, 1)],
1333         [contigGraph.edge(4, 5, 1)],
1334         [contigGraph.edge(4, 5, 1)],
1335         [],
1336     ]);
1337 }
1338 
1339 /// Add a set of edges to this graph and merge mutli-edges using `merge`.
1340 void bulkAdd(alias merge, G, R)(ref G graph, R edges)
1341         if (is(G : Graph!Params, Params...) && isForwardRange!R && is(ElementType!R == G.Edge))
1342 {
1343     alias Edge = G.Edge;
1344     alias ReturnTypeMerge = typeof(merge(new Edge[0]));
1345     static assert(is(ReturnTypeMerge == Edge), "expected `Edge merge(Edge[] multiEdge)`");
1346 
1347     graph.bulkAddForce(edges);
1348 
1349     auto bufferRest = graph
1350         ._edges
1351         .data
1352         .sliceBy!(G.groupByNodes)
1353         .map!(unaryFun!merge)
1354         .copy(graph._edges.data);
1355     graph._edges.shrinkTo(graph._edges.data.length - bufferRest.length);
1356 }
1357 
1358 ///
1359 unittest
1360 {
1361     auto g1 = Graph!(int, int)([1, 2]);
1362 
1363     static g1.Edge sumWeights(g1.Edge[] multiEdge)
1364     {
1365         auto sumOfWeights = multiEdge.map!"a.weight".sum;
1366         auto mergedEdge = multiEdge[0];
1367         mergedEdge.weight = sumOfWeights;
1368 
1369         return mergedEdge;
1370     }
1371 
1372     auto edges = [
1373         g1.edge(1, 2, 1),
1374         g1.edge(1, 2, 1),
1375         g1.edge(1, 2, 1),
1376         g1.edge(2, 3, 2),
1377         g1.edge(2, 3, 2),
1378         g1.edge(3, 4, 3),
1379     ];
1380     g1.bulkAdd!sumWeights(edges);
1381     assert(g1.edges == [
1382         g1.edge(1, 2, 3),
1383         g1.edge(2, 3, 4),
1384         g1.edge(3, 4, 3),
1385     ]);
1386 }
1387 
1388 /// Add an edge to this graph and handle existing edges with `handleConflict`.
1389 /// The handler must have this signature `Edge handleConflict(Edge, Edge)`.
1390 G.Edge add(alias handleConflict = 1337, G)(ref G graph, G.Edge edge)
1391         if (is(G : Graph!Params, Params...))
1392 {
1393     static if (isCallable!handleConflict)
1394         alias handleConflict_ = binaryFun!handleConflict;
1395     else
1396         alias handleConflict_ = binaryFun!(G.ConflictStrategy.error);
1397 
1398     if (!graph.has(edge.start) || !graph.has(edge.end))
1399     {
1400         throw new MissingNodeException();
1401     }
1402 
1403     auto sortedEdges = assumeSorted!(G.orderByNodes)(graph._edges.data);
1404     auto trisectedEdges = sortedEdges.trisect(edge);
1405     auto existingEdges = trisectedEdges[1];
1406     auto existingEdgeIdx = trisectedEdges[0].length;
1407 
1408     if (existingEdges.empty)
1409     {
1410         return graph.forceAdd(edge);
1411     }
1412     else
1413     {
1414         auto newEdge = handleConflict_(existingEdges.front, edge);
1415 
1416         return graph.replaceEdge(existingEdgeIdx, newEdge);
1417     }
1418 }
1419 
1420 ///
1421 unittest
1422 {
1423     auto g1 = Graph!(int, int)([1, 2]);
1424 
1425     auto e1 = g1.edge(1, 2, 1);
1426     auto e2 = g1.edge(1, 2, 2);
1427 
1428     g1 ~= e1;
1429 
1430     assertThrown!EdgeExistsException(g1.add(e2));
1431 
1432     with (g1.ConflictStrategy)
1433     {
1434         g1.add!replace(e2);
1435 
1436         assert(g1.get(g1.edge(1, 2)) == e2);
1437 
1438         g1.add!keep(e1);
1439 
1440         assert(g1.get(g1.edge(1, 2)) == e2);
1441 
1442         g1.add!sumWeights(e2);
1443 
1444         assert(g1.get(g1.edge(1, 2)).weight == 2 * e2.weight);
1445     }
1446 }
1447 
1448 void filterEdges(alias pred, G)(ref G graph) if (is(G : Graph!Params, Params...))
1449 {
1450     auto bufferRest = graph
1451         ._edges
1452         .data
1453         .filter!pred
1454         .copy(graph._edges.data);
1455     graph._edges.shrinkTo(graph._edges.data.length - bufferRest.length);
1456 }
1457 
1458 void mapEdges(alias fun, G)(ref G graph) if (is(G : Graph!Params, Params...))
1459 {
1460     foreach (ref edge; graph._edges.data)
1461         edge = unaryFun!fun(edge);
1462 
1463     graph._edges.data.sort();
1464 }
1465 
1466 class EmptySetException : Exception
1467 {
1468     this(string msg)
1469     {
1470         super(msg);
1471     }
1472 }
1473 
1474 struct NaturalNumberSet
1475 {
1476     private static enum partSize = 8 * size_t.sizeof;
1477     private static enum size_t firstBit = 1;
1478     private static enum size_t lastBit = firstBit << (partSize - 1);
1479     private static enum size_t emptyPart = 0;
1480     private static enum size_t fullPart = ~emptyPart;
1481 
1482     private size_t[] parts;
1483     private size_t nMax;
1484 
1485     this(size_t initialNumElements, Flag!"addAll" addAll = No.addAll)
1486     {
1487         reserveFor(initialNumElements);
1488 
1489         if (addAll)
1490         {
1491             foreach (i; 0 .. initialNumElements / partSize)
1492                 parts[i] = fullPart;
1493             foreach (i; initialNumElements / partSize .. initialNumElements)
1494                 add(i);
1495         }
1496     }
1497 
1498     static NaturalNumberSet create(size_t[] initialElements...)
1499     {
1500         if (initialElements.length == 0)
1501             return NaturalNumberSet();
1502 
1503         auto set = NaturalNumberSet(initialElements.maxElement);
1504 
1505         foreach (i; initialElements)
1506             set.add(i);
1507 
1508         return set;
1509     }
1510 
1511     this(this)
1512     {
1513         parts = parts.dup;
1514     }
1515 
1516     private this(size_t[] parts)
1517     {
1518         this.parts = parts;
1519     }
1520 
1521     private bool inBounds(in size_t n) const pure nothrow
1522     {
1523         return n < nMax;
1524     }
1525 
1526     void reserveFor(in size_t n)
1527     {
1528         if (parts.length == 0)
1529         {
1530             parts.length = max(1, ceildiv(n, partSize));
1531             nMax = parts.length * partSize;
1532         }
1533 
1534         while (!inBounds(n))
1535         {
1536             parts.length *= 2;
1537             nMax = parts.length * partSize;
1538         }
1539     }
1540 
1541     @property size_t capacity() pure const nothrow
1542     {
1543         return nMax;
1544     }
1545 
1546     private size_t partIdx(in size_t n) const pure nothrow
1547     {
1548         return n / partSize;
1549     }
1550 
1551     private size_t idxInPart(in size_t n) const pure nothrow
1552     {
1553         return n % partSize;
1554     }
1555 
1556     private size_t itemMask(in size_t n) const pure nothrow
1557     {
1558         return firstBit << idxInPart(n);
1559     }
1560 
1561     static size_t inverse(in size_t n) pure nothrow
1562     {
1563         return n ^ fullPart;
1564     }
1565 
1566     void add(in size_t n)
1567     {
1568         reserveFor(n);
1569 
1570         parts[partIdx(n)] |= itemMask(n);
1571     }
1572 
1573     void remove(in size_t n)
1574     {
1575         if (!inBounds(n))
1576         {
1577             return;
1578         }
1579 
1580         parts[partIdx(n)] &= inverse(itemMask(n));
1581     }
1582 
1583     bool has(in size_t n) const pure nothrow
1584     {
1585         if (!inBounds(n))
1586         {
1587             return false;
1588         }
1589 
1590         return (parts[partIdx(n)] & itemMask(n)) != emptyPart;
1591     }
1592 
1593     bool opBinaryRight(string op)(in size_t n) const pure nothrow if (op == "in")
1594     {
1595         return this.has(n);
1596     }
1597 
1598     bool empty() const pure nothrow
1599     {
1600         return parts.all!(part => part == emptyPart);
1601     }
1602 
1603     void clear() pure nothrow
1604     {
1605         foreach (ref part; parts)
1606             part = emptyPart;
1607     }
1608 
1609     bool opBinary(string op)(in NaturalNumberSet other) const pure nothrow if (op == "==")
1610     {
1611         auto numCommonParts = min(this.parts.length, other.parts.length);
1612 
1613         foreach (i; 0 .. numCommonParts)
1614         {
1615             if (this.parts[i] != other.parts[i])
1616                 return false;
1617         }
1618 
1619         static bool hasEmptyTail(ref in NaturalNumberSet set, in size_t tailStart)
1620         {
1621             foreach (i; tailStart .. set.parts.length)
1622                 if (set.parts[i] != emptyPart)
1623                     return false;
1624 
1625             return true;
1626         }
1627 
1628         if (this.parts.length > numCommonParts)
1629             return hasEmptyTail(this, numCommonParts);
1630         if (other.parts.length > numCommonParts)
1631             return hasEmptyTail(other, numCommonParts);
1632 
1633         return true;
1634     }
1635 
1636     bool opBinary(string op)(in NaturalNumberSet other) const pure nothrow if (op == "in")
1637     {
1638         auto numCommonParts = min(this.parts.length, other.parts.length);
1639 
1640         foreach (i; 0 .. numCommonParts)
1641             if ((this.parts[i] & other.parts[i]) != this.parts[i])
1642                 return false;
1643 
1644         static bool hasEmptyTail(ref in NaturalNumberSet set, in size_t tailStart)
1645         {
1646             foreach (i; tailStart .. set.parts.length)
1647                 if (set.parts[i] != emptyPart)
1648                     return false;
1649 
1650             return true;
1651         }
1652 
1653         if (this.parts.length > numCommonParts)
1654             return hasEmptyTail(this, numCommonParts);
1655 
1656         return true;
1657     }
1658 
1659     NaturalNumberSet opBinary(string op)(in NaturalNumberSet other) const pure nothrow if (op.among("|", "^", "&"))
1660     {
1661         NaturalNumberSet result;
1662         result.parts.length = max(this.parts.length, other.parts.length);
1663         result.nMax = max(this.nMax, other.nMax);
1664 
1665         auto numCommonParts = min(this.parts.length, other.parts.length);
1666 
1667         foreach (i; 0 .. numCommonParts)
1668             result.parts[i] = mixin("this.parts[i] " ~ op ~ " other.parts[i]");
1669 
1670         static if (op.among("|", "^"))
1671         {
1672             if (this.parts.length > numCommonParts)
1673                 result.parts[numCommonParts .. $] = this.parts[numCommonParts .. $];
1674             if (other.parts.length > numCommonParts)
1675                 result.parts[numCommonParts .. $] = other.parts[numCommonParts .. $];
1676         }
1677 
1678         return result;
1679     }
1680 
1681     bool intersects(in NaturalNumberSet other) const pure nothrow
1682     {
1683         auto numCommonParts = min(this.parts.length, other.parts.length);
1684 
1685         foreach (i; 0 .. numCommonParts)
1686         {
1687             if ((this.parts[i] & other.parts[i]) != emptyPart)
1688                 return true;
1689         }
1690 
1691         return false;
1692     }
1693 
1694     @property size_t size() const pure nothrow
1695     {
1696         size_t numSetBits;
1697 
1698         foreach (i, part; parts)
1699         {
1700             size_t j = 0;
1701 
1702             while ((part >> j) != emptyPart && j < partSize)
1703             {
1704                 while (((part >> j) & firstBit) != firstBit)
1705                     ++j;
1706                 ++numSetBits;
1707                 ++j;
1708             }
1709         }
1710 
1711         return numSetBits;
1712     }
1713 
1714     size_t minElement() const
1715     {
1716         foreach (i, part; parts)
1717         {
1718             if (part != emptyPart)
1719             {
1720                 size_t j = 0;
1721 
1722                 while (((part >> j) & firstBit) != firstBit)
1723                 {
1724                     ++j;
1725                 }
1726 
1727                 return i * partSize + j;
1728             }
1729         }
1730 
1731         throw new EmptySetException("empty set has no minElement");
1732     }
1733 
1734     size_t maxElement() const
1735     {
1736         foreach (i, part; parts.retro.enumerate)
1737         {
1738             if (part != emptyPart)
1739             {
1740                 size_t j = 0;
1741 
1742                 while (((part << j) & lastBit) != lastBit)
1743                 {
1744                     ++j;
1745                 }
1746 
1747                 return (parts.length - i - 1) * partSize + (partSize - j - 1);
1748             }
1749         }
1750 
1751         throw new EmptySetException("empty set has no maxElement");
1752     }
1753 
1754     unittest
1755     {
1756         foreach (i; 0 .. 2 * NaturalNumberSet.partSize)
1757         {
1758             NaturalNumberSet set;
1759 
1760             set.add(i + 5);
1761             set.add(i + 7);
1762 
1763             assert(set.minElement() == i + 5);
1764             assert(set.maxElement() == i + 7);
1765         }
1766     }
1767 
1768     /// Returns a range of the elements in this set. The elements are ordered
1769     /// ascending.
1770     @property auto elements() const pure nothrow
1771     {
1772         static struct ElementsRange
1773         {
1774             const NaturalNumberSet* set;
1775             bool _empty = false;
1776             size_t i = 0;
1777             size_t part;
1778             size_t j = 0;
1779 
1780             this(const NaturalNumberSet* set) pure nothrow
1781             {
1782                 this.set = set;
1783                 this._empty = set.empty;
1784 
1785                 if (!this.empty)
1786                 {
1787                     this.part = set.parts[i];
1788                     if (!set.has(front))
1789                     {
1790                         popFront();
1791                     }
1792                 }
1793             }
1794 
1795             @property ElementsRange save() const pure nothrow
1796             {
1797                 return this;
1798             }
1799 
1800             void popFront() pure nothrow
1801             {
1802                 assert(!empty, "Attempting to popFront an empty elements range");
1803                 ++j;
1804 
1805                 while (shiftedPartEmpty)
1806                 {
1807                     nextPart();
1808 
1809                     if (empty)
1810                     {
1811                         return;
1812                     }
1813                 }
1814 
1815                 while (((part >> j) & firstBit) != firstBit && !shiftedPartEmpty)
1816                 {
1817                     ++j;
1818                 }
1819 
1820                 if (shiftedPartEmpty)
1821                 {
1822                     popFront();
1823                 }
1824             }
1825 
1826             @property size_t front() const pure nothrow
1827             {
1828                 assert(!empty, "Attempting to fetch the front of an empty elements range");
1829                 return i * partSize + j;
1830             }
1831 
1832             @property bool empty() const pure nothrow
1833             {
1834                 return _empty;
1835             }
1836 
1837             private @property bool shiftedPartEmpty() const pure nothrow
1838             {
1839                 return (part >> j) == emptyPart || j >= partSize;
1840             }
1841 
1842             private void nextPart() pure nothrow
1843             {
1844                 // move to start of next part
1845                 ++i;
1846                 j = 0;
1847 
1848                 if (i < set.parts.length)
1849                 {
1850                     part = set.parts[i];
1851                 }
1852                 else
1853                 {
1854                     _empty = true;
1855                 }
1856             }
1857         }
1858 
1859         return ElementsRange(&this);
1860     }
1861 
1862     ///
1863     unittest
1864     {
1865         import std.algorithm : equal;
1866         import std.range : iota;
1867 
1868         NaturalNumberSet set;
1869         auto someNumbers = iota(set.partSize).filter!"a % 3 == 0";
1870 
1871         foreach (i; someNumbers)
1872         {
1873             set.add(i);
1874         }
1875 
1876         assert(equal(someNumbers, set.elements));
1877     }
1878 
1879     /// The set may be modified while iterating:
1880     unittest
1881     {
1882         import std.algorithm : equal;
1883         import std.range : iota;
1884 
1885         enum numElements = 64;
1886         auto set = NaturalNumberSet(numElements, Yes.addAll);
1887 
1888         foreach (i; set.elements)
1889         {
1890             if (i % 10 == 0)
1891                 set.remove(i + 1);
1892         }
1893 
1894         auto expectedNumbers = iota(numElements).filter!"a == 0 || !((a - 1) % 10 == 0)";
1895         assert(equal(expectedNumbers, set.elements));
1896     }
1897 
1898     string toString() const pure
1899     {
1900         return format("[%(%d,%)]", this.elements);
1901     }
1902 }
1903 
1904 unittest
1905 {
1906     NaturalNumberSet set;
1907 
1908     // add some numbers
1909     foreach (i; 0 .. set.partSize)
1910     {
1911         if (i % 2 == 0)
1912         {
1913             set.add(i);
1914         }
1915     }
1916 
1917     // force extension of set
1918     foreach (i; set.partSize .. 2 * set.partSize)
1919     {
1920         if (i % 3 == 0)
1921         {
1922             set.add(i);
1923         }
1924     }
1925 
1926     // validate presence
1927     foreach (i; 0 .. 2 * set.partSize)
1928     {
1929         if (i / set.partSize == 0 && i % 2 == 0)
1930         {
1931             assert(set.has(i));
1932         }
1933         else if (i / set.partSize == 1 && i % 3 == 0)
1934         {
1935             assert(set.has(i));
1936         }
1937         else
1938         {
1939             assert(!set.has(i));
1940         }
1941     }
1942 }
1943 
1944 /**
1945     Find all maximal connected components of a graph-like structure. The
1946     predicate `isConnected` will be evaluated `O(n^^2)` times in the
1947     worst-case and `Ω(n)` in the best case. In expectation it will be
1948     evaluated `θ(n*log(n))`.
1949 
1950     Params:
1951         isConnected =   binary predicate that evaluates to true iff two nodes,
1952                         represented as indices, are connected
1953         numNodes    =   total number of nodes in the graph
1954 
1955     Returns:    range of maxmimally connected components represented as
1956                 `NaturalNumberSet`s
1957 */
1958 auto findMaximallyConnectedComponents(alias isConnected)(in size_t numNodes)
1959 {
1960     return MaximalConnectedComponents!(binaryFun!isConnected)(numNodes);
1961 }
1962 
1963 ///
1964 unittest
1965 {
1966     import std.algorithm : equal;
1967     import std.range : only;
1968 
1969     alias modEqv(size_t m) = (a, b) => (a % m) == (b % m);
1970     alias clusterByThreshold(size_t t) = (a, b) => (a < t) == (b < t);
1971 
1972     assert(equal(
1973         findMaximallyConnectedComponents!(modEqv!5)(15),
1974         only(
1975             NaturalNumberSet.create(0, 5, 10),
1976             NaturalNumberSet.create(1, 6, 11),
1977             NaturalNumberSet.create(2, 7, 12),
1978             NaturalNumberSet.create(3, 8, 13),
1979             NaturalNumberSet.create(4, 9, 14),
1980         ),
1981     ));
1982     assert(equal(
1983         findMaximallyConnectedComponents!(modEqv!3)(15),
1984         only(
1985             NaturalNumberSet.create(0, 3, 6, 9, 12),
1986             NaturalNumberSet.create(1, 4, 7, 10, 13),
1987             NaturalNumberSet.create(2, 5, 8, 11, 14),
1988         ),
1989     ));
1990     assert(equal(
1991         findMaximallyConnectedComponents!(clusterByThreshold!10)(15),
1992         only(
1993             NaturalNumberSet.create(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
1994             NaturalNumberSet.create(10, 11, 12, 13, 14),
1995         ),
1996     ));
1997 }
1998 
1999 ///
2000 unittest
2001 {
2002     import std.algorithm : equal;
2003     import std.range : only;
2004 
2005     auto connectivity = [
2006         [false, false, false, true ],
2007         [false, false, true , false],
2008         [false, true , false, false],
2009         [true , false, false, false],
2010     ];
2011     alias isConnected = (i, j) => connectivity[i][j];
2012 
2013     assert(equal(
2014         findMaximallyConnectedComponents!isConnected(4),
2015         only(
2016             NaturalNumberSet.create(0, 3),
2017             NaturalNumberSet.create(1, 2),
2018         ),
2019     ));
2020 }
2021 
2022 private struct MaximalConnectedComponents(alias isConnected)
2023 {
2024 
2025     const(size_t) numNodes;
2026     NaturalNumberSet unvisited;
2027     NaturalNumberSet currentComponent;
2028 
2029     this(in size_t numNodes)
2030     {
2031         this.numNodes = numNodes;
2032         this.unvisited = NaturalNumberSet(numNodes, Yes.addAll);
2033         this.currentComponent = NaturalNumberSet(numNodes);
2034 
2035         if (!empty)
2036             popFront();
2037     }
2038 
2039     void popFront()
2040     {
2041         assert(!empty, "Attempting to popFront an empty " ~ typeof(this).stringof);
2042 
2043         currentComponent.clear();
2044 
2045         if (unvisited.empty)
2046             return;
2047 
2048         auto seedNode = unvisited.minElement;
2049 
2050         maximizeConnectedComponent(seedNode);
2051     }
2052 
2053     private void maximizeConnectedComponent(size_t node)
2054     {
2055         currentComponent.add(node);
2056         unvisited.remove(node);
2057 
2058         foreach (nextNode; unvisited.elements)
2059             if (isConnected(node, nextNode))
2060                 maximizeConnectedComponent(nextNode);
2061     }
2062 
2063     @property NaturalNumberSet front()
2064     {
2065         assert(!empty, "Attempting to fetch the front an empty " ~ typeof(this).stringof);
2066 
2067         return currentComponent;
2068     }
2069 
2070     @property bool empty() const pure nothrow
2071     {
2072         return unvisited.empty && currentComponent.empty;
2073     }
2074 }
2075 
2076 /**
2077     Find a cycle base of an undirected graph using the Paton's
2078     algorithm.
2079 
2080     The algorithm is described in
2081 
2082     > K. Paton, An algorithm for finding a fundamental set of cycles
2083     > for an undirected linear graph, Comm. ACM 12 (1969), pp. 514-518.
2084 
2085     and the implementation is adapted from the [Java implementation][1] of
2086     K. Paton originally licensed under [Apache License 2.0][2].
2087 
2088     [1]: https://code.google.com/archive/p/niographs/
2089     [2]: http://www.apache.org/licenses/LICENSE-2.0
2090 
2091     Returns: range of cycles in the graph represented as arrays of node indices
2092 */
2093 auto findCyclicSubgraphs(G)(
2094     G graph,
2095     G.IncidentEdgesCache incidentEdgesCache = G.IncidentEdgesCache.init,
2096 )
2097     if (is(G : Graph!Params, Params...))
2098 {
2099     auto node(in size_t idx)
2100     {
2101         return graph.nodes[idx];
2102     }
2103 
2104     version(assert) void assertValidCycle(in size_t[] cycle)
2105     {
2106         enum errorMsg = "not a cycle";
2107 
2108         assert(
2109             cycle.length > 0 && graph.edge(node(cycle[0]), node(cycle[$ - 1])) in graph,
2110             errorMsg
2111         );
2112 
2113         foreach (pair; cycle.slide!(No.withPartial)(2))
2114             assert(graph.edge(node(pair[0]), node(pair[1])) in graph, errorMsg);
2115     }
2116 
2117     auto numNodes = graph.nodes.length;
2118 
2119     NaturalNumberSet[] used;
2120     used.length = numNodes;
2121 
2122     long[] parent;
2123     parent.length = numNodes;
2124     parent[] = -1;
2125 
2126     size_t[] stack;
2127     stack.reserve(numNodes);
2128 
2129     auto cycles = appender!(size_t[][]);
2130 
2131     if (incidentEdgesCache == G.IncidentEdgesCache.init)
2132         incidentEdgesCache = graph.allIncidentEdges();
2133 
2134     foreach (rootIdx, root; graph.nodes)
2135     {
2136         // Loop over the connected
2137         // components of the graph.
2138         if (parent[rootIdx] >= 0)
2139             continue;
2140 
2141         // Prepare to walk the spanning tree.
2142         parent[rootIdx] = rootIdx;
2143         used[rootIdx].reserveFor(numNodes);
2144         used[rootIdx].add(rootIdx);
2145         stack ~= rootIdx;
2146 
2147         // Do the walk. It is a BFS with
2148         // a LIFO instead of the usual
2149         // FIFO. Thus it is easier to
2150         // find the cycles in the tree.
2151         while (stack.length > 0)
2152         {
2153             auto currentIdx = stack[$ - 1];
2154             --stack.length;
2155             auto current = node(currentIdx);
2156             auto currentUsed = &used[currentIdx];
2157 
2158             foreach (edge; incidentEdgesCache[currentIdx])
2159             {
2160                 auto neighbour = edge.target(current);
2161                 auto neighbourIdx = graph.indexOf(neighbour);
2162                 auto neighbourUsed = &used[neighbourIdx];
2163 
2164                 if (neighbourUsed.empty)
2165                 {
2166                     // found a new node
2167                     parent[neighbourIdx] = currentIdx;
2168                     neighbourUsed.reserveFor(numNodes);
2169                     neighbourUsed.add(currentIdx);
2170 
2171                     stack ~= neighbourIdx;
2172                 }
2173                 else if (neighbourIdx == currentIdx)
2174                 {
2175                     // found a self loop
2176                     auto cycle = [currentIdx];
2177                     cycles ~= cycle;
2178                     version(assert) assertValidCycle(cycle);
2179                 }
2180                 else if (!currentUsed.has(neighbourIdx))
2181                 {
2182                     // found a cycle
2183                     auto cycle = appender!(size_t[]);
2184                     cycle ~= neighbourIdx;
2185                     cycle ~= currentIdx;
2186 
2187                     auto p = parent[currentIdx];
2188                     for (; !neighbourUsed.has(p); p = parent[p])
2189                         cycle ~= p;
2190 
2191                     cycle ~= p;
2192                     cycles ~= cycle.data;
2193                     version(assert) assertValidCycle(cycle.data);
2194                     neighbourUsed.add(currentIdx);
2195                 }
2196             }
2197         }
2198     }
2199 
2200     return cycles.data;
2201 }
2202 
2203 ///
2204 unittest
2205 {
2206     alias G = Graph!int;
2207 
2208     //   __
2209     //   \ \
2210     //    `-0 -- 1 -- 2 -- 3
2211     //      |       / |    |
2212     //      |      /  |    |
2213     //      4 -- 5 -- 6    7
2214     auto g = G([0, 1, 2, 3, 4, 5, 6, 7], [
2215         G.edge(0, 0),
2216         G.edge(0, 1),
2217         G.edge(0, 4),
2218         G.edge(1, 2),
2219         G.edge(2, 3),
2220         G.edge(2, 5),
2221         G.edge(2, 6),
2222         G.edge(3, 7),
2223         G.edge(4, 5),
2224         G.edge(5, 6),
2225     ]);
2226     auto cycles = g.findCyclicSubgraphs();
2227 
2228     import std.algorithm : equal;
2229 
2230     assert(cycles.equal([
2231         [0],
2232         [2, 6, 5],
2233         [1, 2, 5, 4, 0],
2234     ]));
2235 }
2236 
2237 /**
2238     Find all maximal cliques in a graph represented by `adjacencyList`.
2239     The implementation is based on version 1 of the Bron-Kerbosch algorithm [1].
2240 
2241     [1]: Bron, C.; Kerbosch, J. (1973), "Algorithm 457: finding all cliques
2242          of an undirected graph", Communications of the ACM, 16 (9): 575–577,
2243          doi:10.1145/362342.362367.
2244 
2245     Returns: list of sets of nodes each representing a maximal clique
2246 */
2247 auto findAllCliques(in size_t[][] adjacencyList)
2248 {
2249     return BronKerboschVersion1(adjacencyList);
2250 }
2251 
2252 ///
2253 unittest
2254 {
2255     auto g = Graph!int([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
2256     g.add(g.edge(0, 1));
2257     g.add(g.edge(0, 2));
2258     g.add(g.edge(1, 2));
2259     g.add(g.edge(1, 7));
2260     g.add(g.edge(1, 8));
2261     g.add(g.edge(2, 3));
2262     g.add(g.edge(3, 4));
2263     g.add(g.edge(3, 5));
2264     g.add(g.edge(3, 6));
2265     g.add(g.edge(4, 5));
2266     g.add(g.edge(4, 6));
2267     g.add(g.edge(5, 6));
2268     g.add(g.edge(6, 7));
2269     g.add(g.edge(7, 8));
2270 
2271     auto cliques = array(findAllCliques(g.adjacencyList()));
2272 
2273     assert(cliques == [
2274         [0, 1, 2],
2275         [1, 7, 8],
2276         [2, 3],
2277         [3, 4, 5, 6],
2278         [6, 7],
2279         [9],
2280     ]);
2281 }
2282 
2283 private struct BronKerboschVersion1
2284 {
2285     const size_t[][] adjacencyList;
2286 
2287     int opApply(scope int delegate(size_t[]) yield)
2288     {
2289         size_t[] clique;
2290         clique.reserve(adjacencyList.length);
2291 
2292         auto candidates = NaturalNumberSet(adjacencyList.length, Yes.addAll);
2293         auto not = NaturalNumberSet(adjacencyList.length);
2294 
2295         return extendClique(clique, candidates, not, yield);
2296     }
2297 
2298     private int extendClique(
2299         size_t[] clique,
2300         NaturalNumberSet candidates,
2301         NaturalNumberSet not,
2302         scope int delegate(size_t[]) yield,
2303     )
2304     {
2305         import std.stdio;
2306 
2307         if (not.empty && candidates.empty)
2308             return clique.length == 0 ? 0 : yield(clique);
2309 
2310         int result;
2311 
2312         foreach (candidate; candidates.elements)
2313         {
2314             clique ~= candidate;
2315 
2316             auto reducedCandidates = NaturalNumberSet(adjacencyList.length);
2317             auto reducedNot = NaturalNumberSet(adjacencyList.length);
2318 
2319             foreach (neighbourNode; adjacencyList[candidate])
2320             {
2321                 if (candidates.has(neighbourNode))
2322                     reducedCandidates.add(neighbourNode);
2323                 if (not.has(neighbourNode))
2324                     reducedNot.add(neighbourNode);
2325             }
2326 
2327             result = extendClique(clique, reducedCandidates, reducedNot, yield);
2328 
2329             if (result)
2330                 return result;
2331 
2332             candidates.remove(candidate);
2333             not.add(candidate);
2334             --clique.length;
2335         }
2336 
2337         return result;
2338     }
2339 }
2340 
2341 /**
2342     Calculate a longest increasing subsequence of `sequence`. This subsequence
2343     is not necessarily contiguous, or unique. Given a `sequence` of `n`
2344     elements the algorithm uses `O(n log n)` evaluation of `pred`.
2345 
2346     See_Also: https://en.wikipedia.org/wiki/Longest_increasing_subsequence
2347 */
2348 auto longestIncreasingSubsequence(alias pred = "a < b", Range)(Range sequence)
2349         if (isRandomAccessRange!Range)
2350 {
2351     alias lessThan = binaryFun!pred;
2352 
2353     size_t[] subseqEnds;
2354     subseqEnds.length = sequence.length;
2355     size_t[] predecessors;
2356     predecessors.length = sequence.length;
2357     size_t subseqLength;
2358 
2359     foreach (i; 0 .. sequence.length)
2360     {
2361         // Binary search for the largest positive j < subseqLength
2362         // such that sequence[subseqEnds[j]] < sequence[i]
2363         long lo = 0;
2364         long hi = subseqLength - 1;
2365         auto pivot = sequence[i];
2366         assert(!lessThan(pivot, pivot), "`pred` is not anti-symmetric");
2367 
2368         while (lo <= hi)
2369         {
2370             auto mid = ceildiv(lo + hi, 2);
2371 
2372             if (lessThan(sequence[subseqEnds[mid]], pivot))
2373                 lo = mid + 1;
2374             else
2375                 hi = mid - 1;
2376         }
2377 
2378         // After searching, lo + 1 is the length of the longest prefix of
2379         // sequence[i]
2380         auto newSubseqLength = lo + 1;
2381 
2382         // The predecessor of sequence[i] is the last index of
2383         // the subsequence of length newSubseqLength - 1
2384         subseqEnds[lo] = i;
2385         if (lo > 0)
2386             predecessors[i] = subseqEnds[lo - 1];
2387 
2388         if (newSubseqLength > subseqLength)
2389             // If we found a subsequence longer than any we've
2390             // found yet, update subseqLength
2391             subseqLength = newSubseqLength;
2392     }
2393 
2394     auto subsequenceResult = subseqEnds[0 .. subseqLength];
2395 
2396     if (subseqLength > 0)
2397     {
2398         // Reconstruct the longest increasing subsequence
2399         // Note: reusing memory from now unused subseqEnds
2400         auto k = subseqEnds[subseqLength - 1];
2401         foreach_reverse (i; 0 .. subseqLength)
2402         {
2403             subsequenceResult[i] = k;
2404             k = predecessors[k];
2405         }
2406     }
2407 
2408     return subsequenceResult.map!(i => sequence[i]);
2409 }
2410 
2411 /// Example from Wikipedia
2412 unittest
2413 {
2414     import std.algorithm : equal;
2415 
2416     auto inputSequence = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
2417     auto expectedOutput = [0, 2, 6, 9, 11, 15];
2418 
2419     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2420 }
2421 
2422 /// Example using a different `pred`
2423 unittest
2424 {
2425     import std.algorithm : equal;
2426     import std.range : retro;
2427 
2428     auto inputSequence = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
2429     auto expectedOutput = [12, 10, 9, 5, 3];
2430 
2431     assert(inputSequence.longestIncreasingSubsequence!"a > b".equal(expectedOutput));
2432 }
2433 
2434 unittest
2435 {
2436     import std.algorithm : equal;
2437 
2438     int[] inputSequence = [];
2439     int[] expectedOutput = [];
2440 
2441     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2442 }
2443 
2444 unittest
2445 {
2446     import std.algorithm : equal;
2447 
2448     auto inputSequence = [1, 2, 3, 4, 5];
2449     auto expectedOutput = [1, 2, 3, 4, 5];
2450 
2451     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2452 }
2453 
2454 unittest
2455 {
2456     import std.algorithm : equal;
2457 
2458     auto inputSequence = [2, 1, 3, 4, 5];
2459     auto expectedOutput = [1, 3, 4, 5];
2460 
2461     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2462 }
2463 
2464 unittest
2465 {
2466     import std.algorithm : equal;
2467 
2468     auto inputSequence = [1, 2, 3, 5, 4];
2469     auto expectedOutput = [1, 2, 3, 4];
2470 
2471     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2472 }