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;
12 import std.algorithm;
13 import std.algorithm : stdMean = mean;
14 import std.array;
15 import std.conv;
16 import std.exception;
17 import std.format;
18 import std.functional;
19 import std.math;
20 import std.math : stdFloor = floor;
21 import std.range;
22 import std..string;
23 import std.traits;
24 import std.typecons;
25 
26 debug import std.stdio : writeln;
27 
28 /// Calculate the mean of range.
29 ElementType!Range mean(Range)(Range values) if (isForwardRange!Range)
30 {
31     auto sum = values.save.sum;
32     auto length = values.walkLength.to!(ElementType!Range);
33 
34     return cast(typeof(return)) (sum / length);
35 }
36 
37 unittest
38 {
39     {
40         auto values = [2, 4, 8];
41         assert(values.mean == 4);
42     }
43     {
44         auto values = [1.0, 2.0, 3.0, 4.0];
45         assert(values.mean == 2.5);
46     }
47 }
48 
49 /// Calculate the weighted mean of values.
50 double mean(Values, Weights)(Values values, Weights weights)
51     if (isInputRange!Values && isForwardRange!Weights)
52 {
53     enum zeroWeight = cast(ElementType!Weights) 0;
54 
55     auto weightedSum = zip(StoppingPolicy.requireSameLength, values, weights)
56         .map!(pair => (pair[0] * pair[1]).to!double)
57         .sum;
58     auto totalWeight = weights.sum;
59 
60     return weightedSum / totalWeight;
61 }
62 
63 unittest
64 {
65     {
66         auto values = [2, 4, 6];
67         auto equalWeights = [1, 1, 1];
68         auto weights = [3, 4, 1];
69 
70         assert(mean(values, equalWeights) == mean(values));
71         assert(mean(values, weights) == 3.5);
72     }
73     {
74         auto values = [1.0, 2.0, 3.0, 4.0];
75         auto weights = [4.0, 3.0, 2.0, 1.0];
76 
77         assert(mean(values, weights) == 2.0);
78     }
79 }
80 
81 /// Calculate the median of range.
82 ElementType!Range median(Range)(Range values) if (__traits(compiles, sort(values)))
83 {
84     assert(values.length > 0, "median is undefined for empty set");
85     auto middleIdx = values.length / 2;
86     auto useAverage = values.length > 1 && values.length % 2 == 0;
87     auto sortedValues = values.sort;
88 
89     if (useAverage)
90         return (sortedValues[middleIdx - 1] + sortedValues[middleIdx]) / 2;
91     else
92         return values[middleIdx];
93 }
94 
95 unittest
96 {
97     {
98         auto values = [4, 2, 8];
99         assert(values.median == 4);
100     }
101     {
102         auto values = [4, 3, 2, 8];
103         assert(values.median == 3);
104     }
105     {
106         auto values = [4, 6, 2, 8];
107         assert(values.median == 5);
108     }
109     {
110         auto values = [2, 1, 3, 0, 4, 9, 8, 5, 6, 3, 9];
111         assert(values.median == 4);
112     }
113     {
114         auto values = [2.0, 1.0, 4.0, 3.0];
115         assert(values.median == 2.5);
116     }
117     {
118         auto values = [2.0, 1.0, 4.0, 3.0, 5.0];
119         assert(values.median == 3.0);
120     }
121 }
122 
123 /// Calculate the Nxx (e.g. N50) of values.
124 ElementType!Range N(real xx, Range, Num)(Range values, Num totalSize) if (__traits(compiles, sort(values)))
125 {
126     static assert(0 < xx && xx < 100, "N" ~ xx.to!string ~ " is undefined");
127     assert(values.length > 0, "N" ~ xx.to!string ~ " is undefined for empty set");
128     auto xxPercentile = xx/100.0 * totalSize;
129     auto sortedValues = values.sort;
130     auto targetIndex = sortedValues
131         .retro
132         .cumulativeFold!"a + b"(cast(ElementType!Range) 0)
133         .countUntil!"a >= b"(xxPercentile);
134 
135     if (targetIndex.among(-1, values.length))
136         return 0;
137     else
138         return sortedValues[$ - targetIndex - 1];
139 }
140 
141 unittest
142 {
143     {
144         auto totalSize = 54;
145         auto values = [2, 3, 4, 5, 6, 7, 8, 9, 10];
146         enum N50 = 8;
147         enum N10 = 10;
148 
149         assert(N!50(values, totalSize) == N50);
150         assert(N!10(values, totalSize) == N10);
151     }
152     {
153         auto totalSize = 32;
154         auto values = [2, 2, 2, 3, 3, 4, 8, 8];
155         enum N50 = 8;
156 
157         assert(N!50(values, totalSize) == N50);
158     }
159 }
160 
161 enum RoundingMode : byte
162 {
163     floor,
164     round,
165     ceil,
166 }
167 
168 /**
169     Round x upward according to base, ie. returns the next integer larger or
170     equal to x which is divisible by base.
171 
172     Returns: x rounded upward according to base.
173 */
174 Integer ceil(Integer)(in Integer x, in Integer base) pure nothrow
175         if (isIntegral!Integer)
176 {
177     return x % base == 0
178         ? x
179         : (x / base + 1) * base;
180 }
181 
182 ///
183 unittest
184 {
185     assert(ceil(8, 10) == 10);
186     assert(ceil(32, 16) == 32);
187     assert(ceil(101, 100) == 200);
188 }
189 
190 /**
191     Round x downward according to base, ie. returns the next integer smaller or
192     equal to x which is divisible by base.
193 
194     Returns: x rounded downward according to base.
195 */
196 Integer floor(Integer)(in Integer x, in Integer base) pure nothrow
197         if (isIntegral!Integer)
198 {
199     return (x / base) * base;
200 }
201 
202 ///
203 unittest
204 {
205     assert(floor(8, 10) == 0);
206     assert(floor(32, 16) == 32);
207     assert(floor(101, 100) == 100);
208 }
209 
210 /// Returns the absolute difference between two numbers.
211 Num absdiff(Num)(in Num a, in Num b) pure nothrow if (isNumeric!Num)
212 {
213     return a > b
214         ? a - b
215         : b - a;
216 }
217 
218 ///
219 unittest
220 {
221     assert(absdiff(2UL, 3UL) == 1UL);
222     assert(absdiff(-42, 13) == 55);
223     assert(absdiff(2.5, 5) == 2.5);
224 }
225 
226 /// Returns the result of `ceil(a / b)` but uses integer arithmetic only.
227 Integer ceildiv(Integer)(in Integer a, in Integer b) pure nothrow if (isIntegral!Integer)
228 {
229     Integer resultSign = (a < 0) ^ (b < 0) ? -1 : 1;
230 
231     return resultSign < 0 || a % b == 0
232         ? a / b
233         : a / b + resultSign;
234 }
235 
236 ///
237 unittest
238 {
239     assert(ceildiv(0, 3) == 0);
240     assert(ceildiv(1UL, 3UL) == 1UL);
241     assert(ceildiv(2L, 3L) == 1L);
242     assert(ceildiv(3U, 3U) == 1U);
243     assert(ceildiv(4, 3) == 2);
244     assert(ceildiv(-4, 4) == -1);
245     assert(ceildiv(-4, 3) == -1);
246 }
247 
248 class EdgeExistsException : Exception
249 {
250     pure nothrow @nogc @safe this(
251         string file = __FILE__,
252         size_t line = __LINE__,
253         Throwable nextInChain = null,
254     )
255     {
256         super("edge cannot be inserted: edge already exists", file, line, nextInChain);
257     }
258 }
259 
260 class NodeExistsException : Exception
261 {
262     pure nothrow @nogc @safe this(
263         string file = __FILE__,
264         size_t line = __LINE__,
265         Throwable nextInChain = null,
266     )
267     {
268         super("node cannot be inserted: node already exists", file, line, nextInChain);
269     }
270 }
271 
272 class MissingEdgeException : Exception
273 {
274     pure nothrow @nogc @safe this(
275         string file = __FILE__,
276         size_t line = __LINE__,
277         Throwable nextInChain = null,
278     )
279     {
280         super("edge not found", file, line, nextInChain);
281     }
282 }
283 
284 class MissingNodeException : Exception
285 {
286     pure nothrow @nogc @safe this(
287         string file = __FILE__,
288         size_t line = __LINE__,
289         Throwable nextInChain = null,
290     )
291     {
292         super("node not found", file, line, nextInChain);
293     }
294 }
295 
296 /// This structure represents a graph with optional edge
297 /// payloads. The graph is represented as a list of edges which is
298 /// particularly suited for sparse graphs. While the set of nodes is fixed the
299 /// set of edges is mutable.
300 struct Graph(Node, Weight = void, Flag!"isDirected" isDirected = No.isDirected, EdgePayload = void)
301 {
302     static enum isWeighted = !is(Weight == void);
303     static enum hasEdgePayload = !is(EdgePayload == void);
304 
305     static struct Edge
306     {
307         protected Node _start;
308         protected Node _end;
309 
310         static if (isWeighted)
311             Weight weight;
312 
313         static if (hasEdgePayload)
314             EdgePayload payload;
315 
316         /// Construct an edge.
317         this(Node start, Node end)
318         {
319             this._start = start;
320             this._end = end;
321 
322             static if (!isDirected)
323             {
324                 if (end < start)
325                 {
326                     swap(this._start, this._end);
327                 }
328             }
329         }
330 
331         static if (isWeighted)
332         {
333             /// ditto
334             this(Node start, Node end, Weight weight)
335             {
336                 this(start, end);
337                 this.weight = weight;
338             }
339         }
340 
341         static if (hasEdgePayload && !is(EdgePayload : Weight))
342         {
343             /// ditto
344             this(Node start, Node end, EdgePayload payload)
345             {
346                 this(start, end);
347                 this.payload = payload;
348             }
349         }
350 
351         static if (isWeighted && hasEdgePayload)
352         {
353             /// ditto
354             this(Node start, Node end, Weight weight, EdgePayload payload)
355             {
356                 this(start, end);
357                 this.weight = weight;
358                 this.payload = payload;
359             }
360         }
361 
362         /// Get the start of this edge. For undirected graphs this is the
363         /// smaller of both incident nodes.
364         @property Node start() const pure nothrow
365         {
366             return _start;
367         }
368 
369         /// Get the end of this edge. For undirected graphs this is the
370         /// larger of both incident nodes.
371         @property Node end() const pure nothrow
372         {
373             return _end;
374         }
375 
376         /**
377             Get target of this edge beginning at node `from`. For undirected
378             graphs returns the other node of this edge.
379 
380             Throws: MissingNodeException if this edge does not start in node `from`.
381         */
382         Node target(Node from) const
383         {
384             static if (isDirected)
385             {
386                 if (start == from)
387                 {
388                     return end;
389                 }
390                 else
391                 {
392                     throw new MissingNodeException();
393                 }
394             }
395             else
396             {
397                 if (start == from)
398                 {
399                     return end;
400                 }
401                 else if (end == from)
402                 {
403                     return start;
404                 }
405                 else
406                 {
407                     throw new MissingNodeException();
408                 }
409             }
410         }
411 
412         /**
413             Get source of this edge beginning at node `from`. For undirected
414             graphs returns the other node of this edge.
415 
416             Throws: MissingNodeException if this edge does not end in node `from`.
417         */
418         static if (isDirected)
419         {
420             Node source(Node from) const
421             {
422                 if (end == from)
423                 {
424                     return start;
425                 }
426                 else
427                 {
428                     throw new MissingNodeException();
429                 }
430             }
431         }
432         else
433         {
434             alias source = target;
435         }
436 
437         /// Two edges are equal iff their incident nodes (and weight) are the
438         /// same.
439         bool opEquals(in Edge other) const pure nothrow
440         {
441             static if (isWeighted)
442             {
443                 return this.start == other.start && this.end == other.end
444                     && this.weight == other.weight;
445             }
446             else
447             {
448                 return this.start == other.start && this.end == other.end;
449             }
450         }
451 
452         /// Orders edge lexicographically by `start`, `end`(, `weight`).
453         int opCmp(in Edge other) const pure nothrow
454         {
455             static if (isWeighted)
456             {
457                 return cmpLexicographically!(
458                     typeof(this),
459                     "a.start",
460                     "a.end",
461                     "a.weight",
462                 )(this, other);
463             }
464             else
465             {
466                 return cmpLexicographically!(
467                     typeof(this),
468                     "a.start",
469                     "a.end",
470                 )(this, other);
471             }
472         }
473 
474         private int compareNodes(in Edge other) const pure nothrow
475         {
476             return cmpLexicographically!(
477                 typeof(this),
478                 "a.start",
479                 "a.end",
480             )(this, other);
481         }
482 
483         /**
484             Returns the node that is connects this edge with other edge. In
485             case of undirected graphs this is just the common node of both
486             edges; in directed case this is the end node of this edge if it
487             matches the start node of other edge.
488 
489             Throws: MissingNodeException if the connecting node is undefined.
490         */
491         Node getConnectingNode(in Edge other) const
492         {
493             static if (isDirected)
494             {
495                 if (this.end == other.start)
496                 {
497                     return this.end;
498                 }
499             }
500             else
501             {
502                 if (this.end == other.start || this.end == other.end)
503                 {
504                     return this.end;
505                 }
506                 else if (this.start == other.start || this.start == other.end)
507                 {
508                     return this.start;
509                 }
510             }
511 
512             throw new MissingNodeException();
513         }
514     }
515 
516     static bool orderByNodes(in Edge a, in Edge b) nothrow pure
517     {
518         return a.compareNodes(b) < 0;
519     }
520 
521     static bool groupByNodes(in Edge a, in Edge b) nothrow pure
522     {
523         return a.compareNodes(b) == 0;
524     }
525 
526     /// Construct an edge for this graph.
527     static Edge edge(T...)(T args)
528     {
529         return Edge(args);
530     }
531 
532     protected Node[] _nodes;
533     protected Appender!(Edge[]) _edges;
534 
535     /// The set (ordered list) of nodes.
536     @property const(Node[]) nodes() const nothrow pure
537     {
538         return _nodes;
539     }
540 
541     private @property void nodes(Node[] nodes)
542     {
543         nodes.sort();
544 
545         this._nodes = nodes.uniq.array;
546     }
547 
548     /// Get the set (ordered list) of edges in this graph.
549     @property auto edges() nothrow pure
550     {
551         return chain(_edges.data);
552     }
553 
554     /// ditto
555     @property auto edges() const nothrow pure
556     {
557         return chain(_edges.data);
558     }
559 
560     /**
561         Construct a graph from a set of nodes (and edges). Modifies `nodes`
562         while sorting but releases it after construction.
563 
564         Throws: MissingNodeException if an edge has a node that is not present
565                 in this graph .
566         Throws: EdgeExistsException if an edge already exists when trying
567                 inserting it.
568     */
569     this(Node[] nodes)
570     {
571         this.nodes = nodes;
572     }
573 
574     /// ditto
575     this(Node[] nodes, Edge[] edges)
576     {
577         this(nodes);
578 
579         _edges.reserve(edges.length);
580         foreach (edge; edges)
581         {
582             add(this, edge);
583         }
584     }
585 
586     this(this)
587     {
588         _nodes = _nodes.dup;
589     }
590 
591     /// Add a set of edges to this graph without any checks.
592     void bulkAddForce(R)(R edges) if (isForwardRange!R && is(ElementType!R == Edge))
593     {
594         this._edges ~= edges;
595         _edges.data.sort;
596     }
597 
598     /// Add an edge to this graph.
599     /// See_Also: `Edge add(Graph, Edge)`
600     void opOpAssign(string op)(Edge edge) if (op == "~")
601     {
602         add(this, edge);
603     }
604 
605     /// Some pre-defined conflict handlers for `add`.
606     static struct ConflictStrategy
607     {
608         static if (isWeighted)
609         {
610             /// Return an edge with sum of both weights. If given payload will be
611             /// kept from existingEdge .
612             static Edge sumWeights(Edge existingEdge, Edge newEdge)
613             {
614                 existingEdge.weight += newEdge.weight;
615 
616                 return existingEdge;
617             }
618 
619             ///
620             unittest
621             {
622                 auto g1 = Graph!(int, int)([1, 2]);
623                 alias CS = g1.ConflictStrategy;
624 
625                 g1 ~= g1.edge(1, 2, 1);
626 
627                 auto addedEdge = g1.add!(CS.sumWeights)(g1.edge(1, 2, 1));
628 
629                 assert(addedEdge.weight == 2);
630             }
631         }
632 
633         /// Throw `EdgeExistsException`.
634         static inout(Edge) error(inout(Edge) existingEdge, inout(Edge) newEdge)
635         {
636             throw new EdgeExistsException();
637         }
638 
639         ///
640         unittest
641         {
642             auto g1 = Graph!int([1, 2]);
643             alias CS = g1.ConflictStrategy;
644 
645             g1 ~= g1.edge(1, 2);
646 
647             assertThrown!EdgeExistsException(g1.add!(CS.error)(g1.edge(1, 2)));
648         }
649 
650         /// Replace the existingEdge by newEdge.
651         static inout(Edge) replace(inout(Edge) existingEdge, inout(Edge) newEdge)
652         {
653             return newEdge;
654         }
655 
656         ///
657         unittest
658         {
659             auto g1 = Graph!(int, int)([1, 2]);
660             alias CS = g1.ConflictStrategy;
661 
662             g1 ~= g1.edge(1, 2, 1);
663 
664             auto addedEdge = g1.add!(CS.replace)(g1.edge(1, 2, 2));
665 
666             assert(addedEdge.weight == 2);
667         }
668 
669         /// Keep existingEdge – discard newEdge.
670         static inout(Edge) keep(inout(Edge) existingEdge, inout(Edge) newEdge)
671         {
672             return existingEdge;
673         }
674 
675         ///
676         unittest
677         {
678             auto g1 = Graph!(int, int)([1, 2]);
679             alias CS = g1.ConflictStrategy;
680 
681             g1 ~= g1.edge(1, 2, 1);
682 
683             auto addedEdge = g1.add!(CS.keep)(g1.edge(1, 2, 2));
684 
685             assert(addedEdge.weight == 1);
686         }
687     }
688 
689     /// Forcibly add an edge to this graph.
690     protected Edge forceAdd(Edge edge)
691     {
692         _edges ~= edge;
693         _edges.data.sort;
694 
695         return edge;
696     }
697 
698     /// Replace an edge in this graph.
699     protected Edge replaceEdge(in size_t edgeIdx, Edge newEdge)
700     {
701         auto shouldSort = _edges.data[edgeIdx] != newEdge;
702 
703         _edges.data[edgeIdx] = newEdge;
704 
705         if (shouldSort)
706         {
707             _edges.data.sort;
708         }
709 
710         return newEdge;
711     }
712 
713     /// Check if edge/node exists in this graph. Ignores the weight if weighted.
714     bool opBinaryRight(string op)(in Node node) const pure nothrow if (op == "in")
715     {
716         auto sortedNodes = assumeSorted(nodes);
717 
718         return sortedNodes.contains(node);
719     }
720 
721     /// ditto
722     bool has(in Node node) const pure nothrow
723     {
724         return node in this;
725     }
726 
727     /// Check if edge exists in this graph. Only the `start` and `end` node
728     /// will be compared.
729     bool opBinaryRight(string op)(in Edge edge) const pure nothrow if (op == "in")
730     {
731         auto sortedEdges = assumeSorted!orderByNodes(edges);
732 
733         return sortedEdges.contains(edge);
734     }
735 
736     /// ditto
737     bool has(in Edge edge) const pure nothrow
738     {
739         return edge in this;
740     }
741 
742     /// Get the designated edge from this graph. Only the `start` and `end`
743     /// node will be compared.
744     auto ref get(in Edge edge)
745     {
746         auto sortedEdges = assumeSorted!orderByNodes(edges);
747         auto existingEdges = sortedEdges.equalRange(edge);
748 
749         if (existingEdges.empty)
750         {
751             throw new MissingEdgeException();
752         }
753         else
754         {
755             return existingEdges.front;
756         }
757     }
758 
759     ///
760     unittest
761     {
762         auto g1 = Graph!(int, int)([1, 2]);
763 
764         auto e1 = g1.edge(1, 2, 1);
765 
766         g1 ~= e1;
767 
768         assert(g1.get(g1.edge(1, 2)) == e1);
769         assertThrown!MissingEdgeException(g1.get(g1.edge(1, 1)));
770     }
771 
772     /// Returns the index of node `n` in the list of nodes.
773     size_t indexOf(in Node n) const
774     {
775         auto sortedNodes = assumeSorted(nodes);
776         auto tristectedNodes = sortedNodes.trisect(n);
777 
778         if (tristectedNodes[1].empty)
779         {
780             throw new MissingNodeException();
781         }
782 
783         return tristectedNodes[0].length;
784     }
785 
786     ///
787     unittest
788     {
789         auto g1 = Graph!(int, int)([1, 2]);
790 
791         assert(g1.indexOf(1) == 0);
792         assert(g1.indexOf(2) == 1);
793         assertThrown!MissingNodeException(g1.indexOf(3));
794     }
795 
796     /// Returns the index of node `n` in the list of nodes.
797     size_t indexOf(in Edge edge) const
798     {
799         auto sortedEdges = assumeSorted!orderByNodes(edges);
800         auto trisectedEdges = sortedEdges.trisect(edge);
801 
802         if (trisectedEdges[1].empty)
803         {
804             throw new MissingEdgeException();
805         }
806 
807         return trisectedEdges[0].length;
808     }
809 
810     ///
811     unittest
812     {
813         auto g1 = Graph!(int, int)([1, 2]);
814 
815         auto e1 = g1.edge(1, 2, 1);
816 
817         g1 ~= e1;
818 
819         assert(g1.indexOf(g1.edge(1, 2)) == 0);
820         assertThrown!MissingEdgeException(g1.indexOf(g1.edge(1, 1)));
821     }
822 
823     static if (isDirected)
824     {
825         /// Returns a range of in/outgoing edges of node `n`.
826         auto inEdges(Node n) nothrow pure
827         {
828             return _edges.data[].filter!(e => e.end == n);
829         }
830 
831         /// ditto
832         auto inEdges(Node n) const nothrow pure
833         {
834             return edges[].filter!(e => e.end == n);
835         }
836 
837         /// ditto
838         auto outEdges(Node n) nothrow pure
839         {
840             return _edges.data[].filter!(e => e.start == n);
841         }
842 
843         /// ditto
844         auto outEdges(Node n) const nothrow pure
845         {
846             return edges[].filter!(e => e.start == n);
847         }
848 
849         ///
850         unittest
851         {
852             import std.algorithm : equal;
853 
854             auto g1 = Graph!(int, void, Yes.isDirected)([1, 2, 3]);
855 
856             g1 ~= g1.edge(1, 1);
857             g1 ~= g1.edge(1, 2);
858             g1 ~= g1.edge(2, 2);
859             g1 ~= g1.edge(2, 3);
860 
861             assert(g1.inEdges(1).equal([
862                 g1.edge(1, 1),
863             ]));
864             assert(g1.outEdges(1).equal([
865                 g1.edge(1, 1),
866                 g1.edge(1, 2),
867             ]));
868             assert(g1.inEdges(2).equal([
869                 g1.edge(1, 2),
870                 g1.edge(2, 2),
871             ]));
872             assert(g1.outEdges(2).equal([
873                 g1.edge(2, 2),
874                 g1.edge(2, 3),
875             ]));
876             assert(g1.inEdges(3).equal([
877                 g1.edge(2, 3),
878             ]));
879             assert(g1.outEdges(3).empty);
880         }
881 
882         /// Get the in/out degree of node `n`.
883         size_t inDegree(Node n) const nothrow pure
884         {
885             return inEdges(n).walkLength;
886         }
887 
888         /// ditto
889         size_t outDegree(Node n) const nothrow pure
890         {
891             return outEdges(n).walkLength;
892         }
893 
894         ///
895         unittest
896         {
897             auto g1 = Graph!(int, void, Yes.isDirected)([1, 2, 3]);
898 
899             g1 ~= g1.edge(1, 1);
900             g1 ~= g1.edge(1, 2);
901             g1 ~= g1.edge(2, 2);
902             g1 ~= g1.edge(2, 3);
903 
904             assert(g1.inDegree(1) == 1);
905             assert(g1.outDegree(1) == 2);
906             assert(g1.inDegree(2) == 2);
907             assert(g1.outDegree(2) == 2);
908             assert(g1.inDegree(3) == 1);
909             assert(g1.outDegree(3) == 0);
910         }
911     }
912     else
913     {
914         /// Returns a range of all edges incident to node `n`.
915         auto incidentEdges(Node n) nothrow pure
916         {
917             return _edges.data[].filter!(e => e.start == n || e.end == n);
918         }
919 
920         /// ditto
921         auto incidentEdges(Node n) const nothrow pure
922         {
923             return edges[].filter!(e => e.start == n || e.end == n);
924         }
925 
926         /// ditto
927         alias inEdges = incidentEdges;
928 
929         /// ditto
930         alias outEdges = incidentEdges;
931 
932         ///
933         unittest
934         {
935             import std.algorithm : equal;
936 
937             auto g1 = Graph!int([1, 2, 3]);
938 
939             g1 ~= g1.edge(1, 1);
940             g1 ~= g1.edge(1, 2);
941             g1 ~= g1.edge(2, 2);
942             g1 ~= g1.edge(2, 3);
943 
944             assert(g1.incidentEdges(1).equal([
945                 g1.edge(1, 1),
946                 g1.edge(1, 2),
947             ]));
948             assert(g1.incidentEdges(2).equal([
949                 g1.edge(1, 2),
950                 g1.edge(2, 2),
951                 g1.edge(2, 3),
952             ]));
953             assert(g1.incidentEdges(3).equal([
954                 g1.edge(2, 3),
955             ]));
956         }
957 
958         IncidentEdgesCache allIncidentEdges()
959         {
960             return IncidentEdgesCache(this);
961         }
962 
963         static struct IncidentEdgesCache
964         {
965             alias G = Graph!(Node, Weight, isDirected, EdgePayload);
966             G graph;
967             Edge[][] incidentEdges;
968 
969             this(G graph)
970             {
971                 this.graph = graph;
972                 collectAllIncidentEdges();
973             }
974 
975             private void collectAllIncidentEdges()
976             {
977                 preallocateMemory();
978 
979                 size_t startIdx;
980                 size_t endIdx;
981                 foreach (edge; graph._edges.data)
982                 {
983                     if (graph._nodes[startIdx] < edge.start)
984                         endIdx = startIdx;
985                     while (graph._nodes[startIdx] < edge.start)
986                         ++startIdx;
987                     if (endIdx < startIdx)
988                         endIdx = startIdx;
989                     while (graph._nodes[endIdx] < edge.end)
990                         ++endIdx;
991 
992                     incidentEdges[startIdx] ~= edge;
993                     // Avoid double-counting of loops
994                     if (startIdx != endIdx)
995                         incidentEdges[endIdx] ~= edge;
996                 }
997             }
998 
999             void preallocateMemory()
1000             {
1001                 auto degreesCache = graph.allDegrees();
1002                 Edge[] buffer;
1003                 buffer.length = degreesCache.degrees.sum;
1004                 incidentEdges.length = degreesCache.degrees.length;
1005 
1006                 size_t sliceBegin;
1007                 size_t startIdx;
1008                 foreach (degree; degreesCache)
1009                 {
1010                     incidentEdges[startIdx] = buffer[sliceBegin .. sliceBegin + degree];
1011                     incidentEdges[startIdx].length = 0;
1012 
1013                     sliceBegin += degree;
1014                     ++startIdx;
1015                 }
1016             }
1017 
1018             ref inout(Edge[]) opIndex(in Node node) inout
1019             {
1020                 return incidentEdges[graph.indexOf(node)];
1021             }
1022 
1023             ref inout(Edge[]) opIndex(in size_t nodeIdx) inout
1024             {
1025                 return incidentEdges[nodeIdx];
1026             }
1027 
1028             int opApply(scope int delegate(Edge[]) yield)
1029             {
1030                 int result = 0;
1031 
1032                 foreach (currentIncidentEdges; incidentEdges)
1033                 {
1034                     result = yield(currentIncidentEdges);
1035                     if (result)
1036                         break;
1037                 }
1038 
1039                 return result;
1040             }
1041 
1042             int opApply(scope int delegate(Node, Edge[]) yield)
1043             {
1044                 int result = 0;
1045 
1046                 foreach (i, currentIncidentEdges; incidentEdges)
1047                 {
1048                     result = yield(graph._nodes[i], currentIncidentEdges);
1049                     if (result)
1050                         break;
1051                 }
1052 
1053                 return result;
1054             }
1055         }
1056 
1057         /// Get the `adjacencyList` of this graph where nodes are represented
1058         /// by their index in the nodes list.
1059         size_t[][] adjacencyList() const
1060         {
1061             size_t[][] _adjacencyList;
1062             _adjacencyList.length = nodes.length;
1063             size_t[] targetsBuffer;
1064             targetsBuffer.length = 2 * edges.length;
1065 
1066             foreach (i, node; _nodes)
1067             {
1068                 auto bufferRest = edges
1069                     .filter!(e => e.start == node || e.end == node)
1070                     .map!(edge => indexOf(edge.target(node)))
1071                     .copy(targetsBuffer);
1072                 _adjacencyList[i] = targetsBuffer[0 .. $ - bufferRest.length];
1073                 _adjacencyList[i].sort;
1074                 targetsBuffer = bufferRest;
1075             }
1076 
1077             return _adjacencyList;
1078         }
1079 
1080         ///
1081         unittest
1082         {
1083             auto g1 = Graph!int([1, 2, 3, 4]);
1084 
1085             g1 ~= g1.edge(1, 1);
1086             g1 ~= g1.edge(1, 2);
1087             g1 ~= g1.edge(2, 2);
1088             g1 ~= g1.edge(2, 3);
1089             g1 ~= g1.edge(2, 4);
1090             g1 ~= g1.edge(3, 4);
1091 
1092             assert(g1.adjacencyList() == [
1093                 [0, 1],
1094                 [0, 1, 2, 3],
1095                 [1, 3],
1096                 [1, 2],
1097             ]);
1098         }
1099 
1100         /// Get the degree of node `n`.
1101         size_t degree(Node n) const nothrow pure
1102         {
1103             return incidentEdges(n).walkLength;
1104         }
1105 
1106         /// ditto
1107         alias inDegree = degree;
1108 
1109         /// ditto
1110         alias outDegree = degree;
1111 
1112         DegreesCache allDegrees() const
1113         {
1114             return DegreesCache(this);
1115         }
1116 
1117         static struct DegreesCache
1118         {
1119             alias G = Graph!(Node, Weight, isDirected, EdgePayload);
1120             const(G) graph;
1121             size_t[] degrees;
1122 
1123             this(in G graph)
1124             {
1125                 this.graph = graph;
1126                 collectAllDegrees();
1127             }
1128 
1129             private void collectAllDegrees()
1130             {
1131                 degrees.length = graph._nodes.length;
1132 
1133                 size_t startIdx;
1134                 size_t endIdx;
1135                 foreach (edge; graph._edges.data)
1136                 {
1137                     if (graph._nodes[startIdx] < edge.start)
1138                         endIdx = startIdx;
1139                     while (graph._nodes[startIdx] < edge.start)
1140                         ++startIdx;
1141                     if (endIdx < startIdx)
1142                         endIdx = startIdx;
1143                     while (graph._nodes[endIdx] < edge.end)
1144                         ++endIdx;
1145 
1146                     ++degrees[startIdx];
1147                     // Avoid double-counting of loops
1148                     if (startIdx != endIdx)
1149                         ++degrees[endIdx];
1150                 }
1151             }
1152 
1153             size_t opIndex(in Node node) const
1154             {
1155                 return degrees[graph.indexOf(node)];
1156             }
1157 
1158             size_t opIndex(in size_t nodeIdx) const
1159             {
1160                 return degrees[nodeIdx];
1161             }
1162 
1163             int opApply(scope int delegate(size_t) yield) const
1164             {
1165                 int result = 0;
1166 
1167                 foreach (degree; degrees)
1168                 {
1169                     result = yield(degree);
1170                     if (result)
1171                         break;
1172                 }
1173 
1174                 return result;
1175             }
1176 
1177             int opApply(scope int delegate(Node, size_t) yield) const
1178             {
1179                 int result = 0;
1180 
1181                 foreach (i, degree; degrees)
1182                 {
1183                     result = yield(graph._nodes[i], degree);
1184                     if (result)
1185                         break;
1186                 }
1187 
1188                 return result;
1189             }
1190         }
1191     }
1192 }
1193 
1194 ///
1195 unittest
1196 {
1197     //   +-+  +-+
1198     //   \ /  \ /
1199     //   (1)--(2)
1200     auto g1 = Graph!int([1, 2]);
1201 
1202     g1 ~= g1.edge(1, 1);
1203     g1 ~= g1.edge(1, 2);
1204     g1.add(g1.edge(2, 2));
1205 
1206     assert(g1.edge(1, 1) in g1);
1207     assert(g1.edge(1, 2) in g1);
1208     assert(g1.edge(2, 1) in g1);
1209     assert(g1.has(g1.edge(2, 2)));
1210     assert(g1.allDegrees().degrees == [2, 2]);
1211     assert(g1.allIncidentEdges().incidentEdges == [
1212         [g1.edge(1, 1), g1.edge(1, 2)],
1213         [g1.edge(1, 2), g1.edge(2, 2)],
1214     ]);
1215 
1216     //   0.5     0.5
1217     //   +-+     +-+
1218     //   \ /     \ /
1219     //   (1)-----(2)
1220     //       1.0
1221     auto g2 = Graph!(int, double)([1, 2]);
1222 
1223     g2 ~= g2.edge(1, 1, 0.5);
1224     g2 ~= g2.edge(1, 2, 1.0);
1225     g2.add(g2.edge(2, 2, 0.5));
1226 
1227     assert(g2.edge(1, 1) in g2);
1228     assert(g2.edge(1, 2) in g2);
1229     assert(g2.edge(2, 1) in g2);
1230     assert(g2.has(g2.edge(2, 2)));
1231     assert(g2.allDegrees().degrees == [2, 2]);
1232     assert(g2.allIncidentEdges().incidentEdges == [
1233         [g2.edge(1, 1, 0.5), g2.edge(1, 2, 1.0)],
1234         [g2.edge(1, 2, 1.0), g2.edge(2, 2, 0.5)],
1235     ]);
1236 
1237     //   0.5     0.5
1238     //   +-+     +-+
1239     //   \ v     v /
1240     //   (1)---->(2)
1241     //       1.0
1242     auto g3 = Graph!(int, double, Yes.isDirected)([1, 2]);
1243 
1244     g3 ~= g3.edge(1, 1, 0.5);
1245     g3 ~= g3.edge(1, 2, 1.0);
1246     g3.add(g3.edge(2, 2, 0.5));
1247 
1248     assert(g3.edge(1, 1) in g3);
1249     assert(g3.edge(1, 2) in g3);
1250     assert(!(g3.edge(2, 1) in g3));
1251     assert(g3.has(g3.edge(2, 2)));
1252 
1253     //   +-+   +-+
1254     //   \ v   v /
1255     //   (1)-->(2)
1256     auto g4 = Graph!(int, void, Yes.isDirected)([1, 2]);
1257 
1258     g4 ~= g4.edge(1, 1);
1259     g4 ~= g4.edge(1, 2);
1260     g4.add(g4.edge(2, 2));
1261 
1262     assert(g4.edge(1, 1) in g4);
1263     assert(g4.edge(1, 2) in g4);
1264     assert(!(g4.edge(2, 1) in g4));
1265     assert(g4.has(g4.edge(2, 2)));
1266 
1267     //   +-+  +-+
1268     //   \ /  \ /
1269     //   (1)--(2)
1270     //
1271     // payload(1, 1) = [1];
1272     // payload(1, 2) = [2];
1273     // payload(2, 2) = [3];
1274     auto g5 = Graph!(int, void, No.isDirected, int[])([1, 2]);
1275 
1276     g5 ~= g5.edge(1, 1, [1]);
1277     g5 ~= g5.edge(1, 2, [2]);
1278     g5.add(g5.edge(2, 2, [3]));
1279 
1280     assert(g5.edge(1, 1) in g5);
1281     assert(g5.get(g5.edge(1, 1)).payload == [1]);
1282     assert(g5.edge(1, 2) in g5);
1283     assert(g5.get(g5.edge(1, 2)).payload == [2]);
1284     assert(g5.edge(2, 1) in g5);
1285     assert(g5.get(g5.edge(2, 1)).payload == [2]);
1286     assert(g5.has(g5.edge(2, 2)));
1287     assert(g5.get(g5.edge(2, 2)).payload == [3]);
1288     assert(g5.allDegrees().degrees == [2, 2]);
1289     assert(g5.allIncidentEdges().incidentEdges == [
1290         [g5.edge(1, 1), g5.edge(1, 2)],
1291         [g5.edge(1, 2), g5.edge(2, 2)],
1292     ]);
1293 }
1294 
1295 ///
1296 unittest
1297 {
1298     //     -1     1         1
1299     // (1)----(2)---(3) (4)---(5) (6)
1300     size_t[] contigs = [1, 2, 3, 4, 5, 6];
1301     auto contigGraph = Graph!(size_t, int)([1, 2, 3, 4, 5, 6]);
1302 
1303     contigGraph.add(contigGraph.edge(1, 2, -1));
1304     contigGraph.add(contigGraph.edge(2, 3, 1));
1305     contigGraph.add(contigGraph.edge(4, 5, 1));
1306 
1307     foreach (contig; contigs)
1308     {
1309         assert(contigGraph.degree(contig) <= 2);
1310     }
1311     assert(contigGraph.allDegrees().degrees == [1, 2, 1, 1, 1, 0]);
1312     assert(contigGraph.allIncidentEdges().incidentEdges == [
1313         [contigGraph.edge(1, 2, -1)],
1314         [contigGraph.edge(1, 2, -1), contigGraph.edge(2, 3, 1)],
1315         [contigGraph.edge(2, 3, 1)],
1316         [contigGraph.edge(4, 5, 1)],
1317         [contigGraph.edge(4, 5, 1)],
1318         [],
1319     ]);
1320 }
1321 
1322 /// Add a set of edges to this graph and merge mutli-edges using `merge`.
1323 void bulkAdd(alias merge, G, R)(ref G graph, R edges)
1324         if (is(G : Graph!Params, Params...) && isForwardRange!R && is(ElementType!R == G.Edge))
1325 {
1326     alias Edge = G.Edge;
1327     alias ReturnTypeMerge = typeof(merge(new Edge[0]));
1328     static assert(is(ReturnTypeMerge == Edge), "expected `Edge merge(Edge[] multiEdge)`");
1329 
1330     graph.bulkAddForce(edges);
1331 
1332     auto bufferRest = graph
1333         ._edges
1334         .data
1335         .sliceBy!(G.groupByNodes)
1336         .map!(unaryFun!merge)
1337         .copy(graph._edges.data);
1338     graph._edges.shrinkTo(graph._edges.data.length - bufferRest.length);
1339 }
1340 
1341 ///
1342 unittest
1343 {
1344     auto g1 = Graph!(int, int)([1, 2]);
1345 
1346     static g1.Edge sumWeights(g1.Edge[] multiEdge)
1347     {
1348         auto sumOfWeights = multiEdge.map!"a.weight".sum;
1349         auto mergedEdge = multiEdge[0];
1350         mergedEdge.weight = sumOfWeights;
1351 
1352         return mergedEdge;
1353     }
1354 
1355     auto edges = [
1356         g1.edge(1, 2, 1),
1357         g1.edge(1, 2, 1),
1358         g1.edge(1, 2, 1),
1359         g1.edge(2, 3, 2),
1360         g1.edge(2, 3, 2),
1361         g1.edge(3, 4, 3),
1362     ];
1363     g1.bulkAdd!sumWeights(edges);
1364     assert(g1.edges == [
1365         g1.edge(1, 2, 3),
1366         g1.edge(2, 3, 4),
1367         g1.edge(3, 4, 3),
1368     ]);
1369 }
1370 
1371 /// Add an edge to this graph and handle existing edges with `handleConflict`.
1372 /// The handler must have this signature `Edge handleConflict(Edge, Edge)`.
1373 G.Edge add(alias handleConflict = 1337, G)(ref G graph, G.Edge edge)
1374         if (is(G : Graph!Params, Params...))
1375 {
1376     static if (isCallable!handleConflict)
1377         alias handleConflict_ = binaryFun!handleConflict;
1378     else
1379         alias handleConflict_ = binaryFun!(G.ConflictStrategy.error);
1380 
1381     if (!graph.has(edge.start) || !graph.has(edge.end))
1382     {
1383         throw new MissingNodeException();
1384     }
1385 
1386     auto sortedEdges = assumeSorted!(G.orderByNodes)(graph._edges.data);
1387     auto trisectedEdges = sortedEdges.trisect(edge);
1388     auto existingEdges = trisectedEdges[1];
1389     auto existingEdgeIdx = trisectedEdges[0].length;
1390 
1391     if (existingEdges.empty)
1392     {
1393         return graph.forceAdd(edge);
1394     }
1395     else
1396     {
1397         auto newEdge = handleConflict_(existingEdges.front, edge);
1398 
1399         return graph.replaceEdge(existingEdgeIdx, newEdge);
1400     }
1401 }
1402 
1403 ///
1404 unittest
1405 {
1406     auto g1 = Graph!(int, int)([1, 2]);
1407 
1408     auto e1 = g1.edge(1, 2, 1);
1409     auto e2 = g1.edge(1, 2, 2);
1410 
1411     g1 ~= e1;
1412 
1413     assertThrown!EdgeExistsException(g1.add(e2));
1414 
1415     with (g1.ConflictStrategy)
1416     {
1417         g1.add!replace(e2);
1418 
1419         assert(g1.get(g1.edge(1, 2)) == e2);
1420 
1421         g1.add!keep(e1);
1422 
1423         assert(g1.get(g1.edge(1, 2)) == e2);
1424 
1425         g1.add!sumWeights(e2);
1426 
1427         assert(g1.get(g1.edge(1, 2)).weight == 2 * e2.weight);
1428     }
1429 }
1430 
1431 void filterEdges(alias pred, G)(ref G graph) if (is(G : Graph!Params, Params...))
1432 {
1433     auto bufferRest = graph
1434         ._edges
1435         .data
1436         .filter!pred
1437         .copy(graph._edges.data);
1438     graph._edges.shrinkTo(graph._edges.data.length - bufferRest.length);
1439 }
1440 
1441 void mapEdges(alias fun, G)(ref G graph) if (is(G : Graph!Params, Params...))
1442 {
1443     foreach (ref edge; graph._edges.data)
1444         edge = unaryFun!fun(edge);
1445 
1446     graph._edges.data.sort();
1447 }
1448 
1449 
1450 struct UndirectedGraph(Node, Weight = void)
1451 {
1452     static enum isWeighted = !is(Weight == void);
1453 
1454     static if (isWeighted)
1455         alias adjacency_t = Weight;
1456     else
1457         alias adjacency_t = bool;
1458 
1459 
1460     static struct Edge
1461     {
1462         protected Node _start;
1463         protected Node _end;
1464 
1465         static if (isWeighted)
1466             Weight weight;
1467 
1468         /// Construct an edge.
1469         this(Node start, Node end) pure nothrow @safe
1470         {
1471             this._start = start;
1472             this._end = end;
1473 
1474             if (end < start)
1475                 swap(this._start, this._end);
1476         }
1477 
1478         static if (isWeighted)
1479         {
1480             /// ditto
1481             this(Node start, Node end, Weight weight) pure nothrow @safe
1482             {
1483                 this(start, end);
1484                 this.weight = weight;
1485             }
1486         }
1487 
1488 
1489         private @property adjacency_t adjacencyValue() pure nothrow @safe @nogc
1490         {
1491             static if (isWeighted)
1492                 return weight;
1493             else
1494                 return true;
1495         }
1496 
1497 
1498         /// Get the start of this edge, i.e. the smaller of both incident
1499         /// nodes.
1500         @property Node start() const pure nothrow @safe
1501         {
1502             return _start;
1503         }
1504 
1505         /// Get the end of this edge, i.e. the larger of both incident nodes.
1506         @property Node end() const pure nothrow @safe
1507         {
1508             return _end;
1509         }
1510 
1511         /**
1512             Returns the other node of this edge.
1513 
1514             Throws: MissingNodeException if this edge does not coincide with `from`.
1515         */
1516         Node target(Node from) const
1517         {
1518             if (from == start)
1519                 return end;
1520             else if (from == end)
1521                 return start;
1522             else
1523                 throw new MissingNodeException();
1524         }
1525 
1526         /// ditto
1527         alias source = target;
1528 
1529         /// Two edges are equal iff their incident nodes are the same.
1530         bool opEquals(in Edge other) const pure nothrow
1531         {
1532             return this.start == other.start && this.end == other.end;
1533         }
1534 
1535         /// Orders edge lexicographically by `start`, `end`.
1536         int opCmp(in Edge other) const pure nothrow
1537         {
1538             return cmpLexicographically!(
1539                 typeof(this),
1540                 "a.start",
1541                 "a.end",
1542             )(this, other);
1543         }
1544 
1545         /**
1546             Returns the node that is common to this and other.
1547 
1548             Throws: MissingNodeException if there this and other do not share
1549                 a common node.
1550         */
1551         Node getCommonNode(in Edge other) const
1552         {
1553             import std.algorithm : among;
1554 
1555             if (this.end.among(other.start, other.end))
1556                 return this.end;
1557             else if (this.start.among(other.start, other.end))
1558                 return this.start;
1559             else
1560                 throw new MissingNodeException();
1561         }
1562     }
1563 
1564     /// Construct an edge for this graph.
1565     static Edge edge(T...)(T args)
1566     {
1567         return Edge(args);
1568     }
1569 
1570 
1571     protected bool[Node] _nodes;
1572     protected adjacency_t[Node][Node] _adjacency;
1573 
1574 
1575     /// Returns a list of the nodes in this graph.
1576     @property Node[] nodes() const nothrow pure
1577     {
1578         return _nodes.keys;
1579     }
1580 
1581 
1582     /// Returns a list of all edges in this graph.
1583     @property auto edges() nothrow pure
1584     {
1585         static if (isWeighted)
1586             alias buildEdge = (start, end, weight) => Edge(start, end, weight);
1587         else
1588             alias buildEdge = (start, end, weight) => Edge(start, end);
1589 
1590         return _adjacency
1591             .keys()
1592             .map!(start => _adjacency[start]
1593                 .keys()
1594                 .map!(end => buildEdge(start, end, _adjacency[start][end]))
1595             )
1596             .joiner;
1597     }
1598 
1599     /// ditto
1600     @property auto edges() const nothrow pure
1601     {
1602         static if (isWeighted)
1603             alias buildEdge = (start, end, weight) => cast(const) Edge(start, end, cast() weight);
1604         else
1605             alias buildEdge = (start, end, weight) => cast(const) Edge(start, end);
1606 
1607         return _adjacency
1608             .keys()
1609             .map!(start => _adjacency[start]
1610                 .keys()
1611                 .map!(end => buildEdge(start, end, _adjacency[start][end]))
1612             )
1613             .joiner;
1614     }
1615 
1616 
1617     /**
1618         Construct a graph from a set of nodes (and edges).
1619 
1620         Throws: NodeExistsException if a node already exists when trying
1621                 to insert it.
1622         Throws: EdgeExistsException if an edge already exists when trying
1623                 to insert it.
1624     */
1625     this(Node[] nodes)
1626     {
1627         foreach (node; nodes)
1628             this.addNode(node);
1629     }
1630 
1631     /// ditto
1632     this(Node[] nodes, Edge[] edges)
1633     {
1634         this(nodes);
1635 
1636         foreach (edge; edges)
1637             this.addEdge(edge);
1638     }
1639 
1640 
1641     /// Inserts node into the node list if not yet present.
1642     void requireNode(Node node) nothrow @safe
1643     {
1644         requireNode(node);
1645     }
1646 
1647     /// ditto
1648     void requireNode(ref Node node) nothrow @safe
1649     {
1650         this._nodes[node] = true;
1651     }
1652 
1653 
1654     private static T _throw(T, E)(ref T _)
1655     {
1656         throw new E();
1657     }
1658 
1659 
1660     /**
1661         Inserts node into the node list throwing an exception if already
1662         present.
1663 
1664         Throws: NodeExistsException if node already present.
1665     */
1666     void addNode(Node node) @safe
1667     {
1668         addNode(node);
1669     }
1670 
1671     /// ditto
1672     void addNode(ref Node node) @safe
1673     {
1674         this._nodes.update(
1675             node,
1676             { return true; },
1677             &_throw!(bool, NodeExistsException),
1678         );
1679     }
1680 
1681 
1682     /**
1683         Inserts edge into the graph thrwoing an exception if already present.
1684 
1685         Throws: EdgeExistsException if edge already present.
1686     */
1687     void addEdge(Edge edge) @safe
1688     {
1689         addEdge(edge._start, edge._end, edge.adjacencyValue);
1690         addEdge(edge._end, edge._start, edge.adjacencyValue);
1691         requireNode(edge._start);
1692         requireNode(edge._end);
1693     }
1694 
1695 
1696     private void addEdge(ref Node start, ref Node end, adjacency_t adjacencyValue) @safe
1697     {
1698         import std.exception : enforce;
1699 
1700         _adjacency.update(start,
1701             {
1702                 adjacency_t[Node] secondLevelAdjacency;
1703 
1704                 secondLevelAdjacency[end] = adjacencyValue;
1705 
1706                 return secondLevelAdjacency;
1707             },
1708             (ref adjacency_t[Node] secondLevelAdjacency) {
1709                 secondLevelAdjacency.update(
1710                     end,
1711                     delegate () { return adjacencyValue; },
1712                     &_throw!(adjacency_t, EdgeExistsException),
1713                 );
1714 
1715                 return secondLevelAdjacency;
1716             },
1717         );
1718 
1719         requireNode(start);
1720         requireNode(end);
1721     }
1722 }
1723 
1724 
1725 class EmptySetException : Exception
1726 {
1727     this(string msg)
1728     {
1729         super(msg);
1730     }
1731 }
1732 
1733 struct NaturalNumberSet
1734 {
1735     private static enum partSize = 8 * size_t.sizeof;
1736     private static enum size_t firstBit = 1;
1737     private static enum size_t lastBit = firstBit << (partSize - 1);
1738     private static enum size_t emptyPart = 0;
1739     private static enum size_t fullPart = ~emptyPart;
1740 
1741     private size_t[] parts;
1742     private size_t nMax;
1743 
1744     this(size_t initialNumElements, Flag!"addAll" addAll = No.addAll)
1745     {
1746         reserveFor(initialNumElements);
1747 
1748         if (addAll)
1749         {
1750             foreach (i; 0 .. initialNumElements / partSize)
1751                 parts[i] = fullPart;
1752             foreach (i; initialNumElements / partSize .. initialNumElements)
1753                 add(i);
1754         }
1755     }
1756 
1757     static NaturalNumberSet create(size_t[] initialElements...)
1758     {
1759         if (initialElements.length == 0)
1760             return NaturalNumberSet();
1761 
1762         auto set = NaturalNumberSet(initialElements.maxElement);
1763 
1764         foreach (i; initialElements)
1765             set.add(i);
1766 
1767         return set;
1768     }
1769 
1770     this(this)
1771     {
1772         parts = parts.dup;
1773     }
1774 
1775     private this(size_t[] parts)
1776     {
1777         this.parts = parts;
1778     }
1779 
1780     private bool inBounds(in size_t n) const pure nothrow
1781     {
1782         return n < nMax;
1783     }
1784 
1785     void reserveFor(in size_t n)
1786     {
1787         if (parts.length == 0)
1788         {
1789             parts.length = max(1, ceildiv(n, partSize));
1790             nMax = parts.length * partSize;
1791         }
1792 
1793         while (!inBounds(n))
1794         {
1795             parts.length *= 2;
1796             nMax = parts.length * partSize;
1797         }
1798     }
1799 
1800     @property size_t capacity() pure const nothrow
1801     {
1802         return nMax;
1803     }
1804 
1805     private size_t partIdx(in size_t n) const pure nothrow
1806     {
1807         return n / partSize;
1808     }
1809 
1810     private size_t idxInPart(in size_t n) const pure nothrow
1811     {
1812         return n % partSize;
1813     }
1814 
1815     private size_t itemMask(in size_t n) const pure nothrow
1816     {
1817         return firstBit << idxInPart(n);
1818     }
1819 
1820     static size_t inverse(in size_t n) pure nothrow
1821     {
1822         return n ^ fullPart;
1823     }
1824 
1825     void add(in size_t n)
1826     {
1827         reserveFor(n);
1828 
1829         parts[partIdx(n)] |= itemMask(n);
1830     }
1831 
1832     void remove(in size_t n)
1833     {
1834         if (!inBounds(n))
1835         {
1836             return;
1837         }
1838 
1839         parts[partIdx(n)] &= inverse(itemMask(n));
1840     }
1841 
1842     bool has(in size_t n) const pure nothrow
1843     {
1844         if (!inBounds(n))
1845         {
1846             return false;
1847         }
1848 
1849         return (parts[partIdx(n)] & itemMask(n)) != emptyPart;
1850     }
1851 
1852     bool opBinaryRight(string op)(in size_t n) const pure nothrow if (op == "in")
1853     {
1854         return this.has(n);
1855     }
1856 
1857     bool empty() const pure nothrow
1858     {
1859         return parts.all!(part => part == emptyPart);
1860     }
1861 
1862     void clear() pure nothrow
1863     {
1864         foreach (ref part; parts)
1865             part = emptyPart;
1866     }
1867 
1868     bool opBinary(string op)(in NaturalNumberSet other) const pure nothrow if (op == "==")
1869     {
1870         auto numCommonParts = min(this.parts.length, other.parts.length);
1871 
1872         foreach (i; 0 .. numCommonParts)
1873         {
1874             if (this.parts[i] != other.parts[i])
1875                 return false;
1876         }
1877 
1878         static bool hasEmptyTail(ref in NaturalNumberSet set, in size_t tailStart)
1879         {
1880             foreach (i; tailStart .. set.parts.length)
1881                 if (set.parts[i] != emptyPart)
1882                     return false;
1883 
1884             return true;
1885         }
1886 
1887         if (this.parts.length > numCommonParts)
1888             return hasEmptyTail(this, numCommonParts);
1889         if (other.parts.length > numCommonParts)
1890             return hasEmptyTail(other, numCommonParts);
1891 
1892         return true;
1893     }
1894 
1895     bool opBinary(string op)(in NaturalNumberSet other) const pure nothrow if (op == "in")
1896     {
1897         auto numCommonParts = min(this.parts.length, other.parts.length);
1898 
1899         foreach (i; 0 .. numCommonParts)
1900             if ((this.parts[i] & other.parts[i]) != this.parts[i])
1901                 return false;
1902 
1903         static bool hasEmptyTail(ref in NaturalNumberSet set, in size_t tailStart)
1904         {
1905             foreach (i; tailStart .. set.parts.length)
1906                 if (set.parts[i] != emptyPart)
1907                     return false;
1908 
1909             return true;
1910         }
1911 
1912         if (this.parts.length > numCommonParts)
1913             return hasEmptyTail(this, numCommonParts);
1914 
1915         return true;
1916     }
1917 
1918     NaturalNumberSet opBinary(string op)(in NaturalNumberSet other) const pure nothrow if (op.among("|", "^", "&"))
1919     {
1920         NaturalNumberSet result;
1921         result.parts.length = max(this.parts.length, other.parts.length);
1922         result.nMax = max(this.nMax, other.nMax);
1923 
1924         auto numCommonParts = min(this.parts.length, other.parts.length);
1925 
1926         foreach (i; 0 .. numCommonParts)
1927             result.parts[i] = mixin("this.parts[i] " ~ op ~ " other.parts[i]");
1928 
1929         static if (op.among("|", "^"))
1930         {
1931             if (this.parts.length > numCommonParts)
1932                 result.parts[numCommonParts .. $] = this.parts[numCommonParts .. $];
1933             if (other.parts.length > numCommonParts)
1934                 result.parts[numCommonParts .. $] = other.parts[numCommonParts .. $];
1935         }
1936 
1937         return result;
1938     }
1939 
1940     bool intersects(in NaturalNumberSet other) const pure nothrow
1941     {
1942         auto numCommonParts = min(this.parts.length, other.parts.length);
1943 
1944         foreach (i; 0 .. numCommonParts)
1945         {
1946             if ((this.parts[i] & other.parts[i]) != emptyPart)
1947                 return true;
1948         }
1949 
1950         return false;
1951     }
1952 
1953     @property size_t size() const pure nothrow
1954     {
1955         size_t numSetBits;
1956 
1957         foreach (i, part; parts)
1958         {
1959             size_t j = 0;
1960 
1961             while ((part >> j) != emptyPart && j < partSize)
1962             {
1963                 while (((part >> j) & firstBit) != firstBit)
1964                     ++j;
1965                 ++numSetBits;
1966                 ++j;
1967             }
1968         }
1969 
1970         return numSetBits;
1971     }
1972 
1973     size_t minElement() const
1974     {
1975         foreach (i, part; parts)
1976         {
1977             if (part != emptyPart)
1978             {
1979                 size_t j = 0;
1980 
1981                 while (((part >> j) & firstBit) != firstBit)
1982                 {
1983                     ++j;
1984                 }
1985 
1986                 return i * partSize + j;
1987             }
1988         }
1989 
1990         throw new EmptySetException("empty set has no minElement");
1991     }
1992 
1993     size_t maxElement() const
1994     {
1995         foreach (i, part; parts.retro.enumerate)
1996         {
1997             if (part != emptyPart)
1998             {
1999                 size_t j = 0;
2000 
2001                 while (((part << j) & lastBit) != lastBit)
2002                 {
2003                     ++j;
2004                 }
2005 
2006                 return (parts.length - i - 1) * partSize + (partSize - j - 1);
2007             }
2008         }
2009 
2010         throw new EmptySetException("empty set has no maxElement");
2011     }
2012 
2013     unittest
2014     {
2015         foreach (i; 0 .. 2 * NaturalNumberSet.partSize)
2016         {
2017             NaturalNumberSet set;
2018 
2019             set.add(i + 5);
2020             set.add(i + 7);
2021 
2022             assert(set.minElement() == i + 5);
2023             assert(set.maxElement() == i + 7);
2024         }
2025     }
2026 
2027     /// Returns a range of the elements in this set. The elements are ordered
2028     /// ascending.
2029     @property auto elements() const pure nothrow
2030     {
2031         static struct ElementsRange
2032         {
2033             const NaturalNumberSet* set;
2034             bool _empty = false;
2035             size_t i = 0;
2036             size_t part;
2037             size_t j = 0;
2038 
2039             this(const NaturalNumberSet* set) pure nothrow
2040             {
2041                 this.set = set;
2042                 this._empty = set.empty;
2043 
2044                 if (!this.empty)
2045                 {
2046                     this.part = set.parts[i];
2047                     if (!set.has(front))
2048                     {
2049                         popFront();
2050                     }
2051                 }
2052             }
2053 
2054             @property ElementsRange save() const pure nothrow
2055             {
2056                 return this;
2057             }
2058 
2059             void popFront() pure nothrow
2060             {
2061                 assert(!empty, "Attempting to popFront an empty elements range");
2062                 ++j;
2063 
2064                 while (shiftedPartEmpty)
2065                 {
2066                     nextPart();
2067 
2068                     if (empty)
2069                     {
2070                         return;
2071                     }
2072                 }
2073 
2074                 while (((part >> j) & firstBit) != firstBit && !shiftedPartEmpty)
2075                 {
2076                     ++j;
2077                 }
2078 
2079                 if (shiftedPartEmpty)
2080                 {
2081                     popFront();
2082                 }
2083             }
2084 
2085             @property size_t front() const pure nothrow
2086             {
2087                 assert(!empty, "Attempting to fetch the front of an empty elements range");
2088                 return i * partSize + j;
2089             }
2090 
2091             @property bool empty() const pure nothrow
2092             {
2093                 return _empty;
2094             }
2095 
2096             private @property bool shiftedPartEmpty() const pure nothrow
2097             {
2098                 return (part >> j) == emptyPart || j >= partSize;
2099             }
2100 
2101             private void nextPart() pure nothrow
2102             {
2103                 // move to start of next part
2104                 ++i;
2105                 j = 0;
2106 
2107                 if (i < set.parts.length)
2108                 {
2109                     part = set.parts[i];
2110                 }
2111                 else
2112                 {
2113                     _empty = true;
2114                 }
2115             }
2116         }
2117 
2118         return ElementsRange(&this);
2119     }
2120 
2121     ///
2122     unittest
2123     {
2124         import std.algorithm : equal;
2125         import std.range : iota;
2126 
2127         NaturalNumberSet set;
2128         auto someNumbers = iota(set.partSize).filter!"a % 3 == 0";
2129 
2130         foreach (i; someNumbers)
2131         {
2132             set.add(i);
2133         }
2134 
2135         assert(equal(someNumbers, set.elements));
2136     }
2137 
2138     /// The set may be modified while iterating:
2139     unittest
2140     {
2141         import std.algorithm : equal;
2142         import std.range : iota;
2143 
2144         enum numElements = 64;
2145         auto set = NaturalNumberSet(numElements, Yes.addAll);
2146 
2147         foreach (i; set.elements)
2148         {
2149             if (i % 10 == 0)
2150                 set.remove(i + 1);
2151         }
2152 
2153         auto expectedNumbers = iota(numElements).filter!"a == 0 || !((a - 1) % 10 == 0)";
2154         assert(equal(expectedNumbers, set.elements));
2155     }
2156 
2157     string toString() const pure
2158     {
2159         return format("[%(%d,%)]", this.elements);
2160     }
2161 }
2162 
2163 unittest
2164 {
2165     NaturalNumberSet set;
2166 
2167     // add some numbers
2168     foreach (i; 0 .. set.partSize)
2169     {
2170         if (i % 2 == 0)
2171         {
2172             set.add(i);
2173         }
2174     }
2175 
2176     // force extension of set
2177     foreach (i; set.partSize .. 2 * set.partSize)
2178     {
2179         if (i % 3 == 0)
2180         {
2181             set.add(i);
2182         }
2183     }
2184 
2185     // validate presence
2186     foreach (i; 0 .. 2 * set.partSize)
2187     {
2188         if (i / set.partSize == 0 && i % 2 == 0)
2189         {
2190             assert(set.has(i));
2191         }
2192         else if (i / set.partSize == 1 && i % 3 == 0)
2193         {
2194             assert(set.has(i));
2195         }
2196         else
2197         {
2198             assert(!set.has(i));
2199         }
2200     }
2201 }
2202 
2203 /**
2204     Find all maximal connected components of a graph-like structure. The
2205     predicate `isConnected` will be evaluated `O(n^^2)` times in the
2206     worst-case and `Ω(n)` in the best case. In expectation it will be
2207     evaluated `θ(n*log(n))`.
2208 
2209     Params:
2210         isConnected =   binary predicate that evaluates to true iff two nodes,
2211                         represented as indices, are connected
2212         numNodes    =   total number of nodes in the graph
2213 
2214     Returns:    range of maxmimally connected components represented as
2215                 `NaturalNumberSet`s
2216 */
2217 auto findMaximallyConnectedComponents(alias isConnected)(in size_t numNodes)
2218 {
2219     return MaximalConnectedComponents!(binaryFun!isConnected)(numNodes);
2220 }
2221 
2222 ///
2223 unittest
2224 {
2225     import std.algorithm : equal;
2226     import std.range : only;
2227 
2228     alias modEqv(size_t m) = (a, b) => (a % m) == (b % m);
2229     alias clusterByThreshold(size_t t) = (a, b) => (a < t) == (b < t);
2230 
2231     assert(equal(
2232         findMaximallyConnectedComponents!(modEqv!5)(15),
2233         only(
2234             NaturalNumberSet.create(0, 5, 10),
2235             NaturalNumberSet.create(1, 6, 11),
2236             NaturalNumberSet.create(2, 7, 12),
2237             NaturalNumberSet.create(3, 8, 13),
2238             NaturalNumberSet.create(4, 9, 14),
2239         ),
2240     ));
2241     assert(equal(
2242         findMaximallyConnectedComponents!(modEqv!3)(15),
2243         only(
2244             NaturalNumberSet.create(0, 3, 6, 9, 12),
2245             NaturalNumberSet.create(1, 4, 7, 10, 13),
2246             NaturalNumberSet.create(2, 5, 8, 11, 14),
2247         ),
2248     ));
2249     assert(equal(
2250         findMaximallyConnectedComponents!(clusterByThreshold!10)(15),
2251         only(
2252             NaturalNumberSet.create(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
2253             NaturalNumberSet.create(10, 11, 12, 13, 14),
2254         ),
2255     ));
2256 }
2257 
2258 ///
2259 unittest
2260 {
2261     import std.algorithm : equal;
2262     import std.range : only;
2263 
2264     auto connectivity = [
2265         [false, false, false, true ],
2266         [false, false, true , false],
2267         [false, true , false, false],
2268         [true , false, false, false],
2269     ];
2270     alias isConnected = (i, j) => connectivity[i][j];
2271 
2272     assert(equal(
2273         findMaximallyConnectedComponents!isConnected(4),
2274         only(
2275             NaturalNumberSet.create(0, 3),
2276             NaturalNumberSet.create(1, 2),
2277         ),
2278     ));
2279 }
2280 
2281 private struct MaximalConnectedComponents(alias isConnected)
2282 {
2283 
2284     const(size_t) numNodes;
2285     NaturalNumberSet unvisited;
2286     NaturalNumberSet currentComponent;
2287 
2288     this(in size_t numNodes)
2289     {
2290         this.numNodes = numNodes;
2291         this.unvisited = NaturalNumberSet(numNodes, Yes.addAll);
2292         this.currentComponent = NaturalNumberSet(numNodes);
2293 
2294         if (!empty)
2295             popFront();
2296     }
2297 
2298     void popFront()
2299     {
2300         assert(!empty, "Attempting to popFront an empty " ~ typeof(this).stringof);
2301 
2302         currentComponent.clear();
2303 
2304         if (unvisited.empty)
2305             return;
2306 
2307         auto seedNode = unvisited.minElement;
2308 
2309         maximizeConnectedComponent(seedNode);
2310     }
2311 
2312     private void maximizeConnectedComponent(size_t node)
2313     {
2314         currentComponent.add(node);
2315         unvisited.remove(node);
2316 
2317         foreach (nextNode; unvisited.elements)
2318             if (isConnected(node, nextNode))
2319                 maximizeConnectedComponent(nextNode);
2320     }
2321 
2322     @property NaturalNumberSet front()
2323     {
2324         assert(!empty, "Attempting to fetch the front an empty " ~ typeof(this).stringof);
2325 
2326         return currentComponent;
2327     }
2328 
2329     @property bool empty() const pure nothrow
2330     {
2331         return unvisited.empty && currentComponent.empty;
2332     }
2333 }
2334 
2335 /**
2336     Find a cycle base of an undirected graph using the Paton's
2337     algorithm.
2338 
2339     The algorithm is described in
2340 
2341     > K. Paton, An algorithm for finding a fundamental set of cycles
2342     > for an undirected linear graph, Comm. ACM 12 (1969), pp. 514-518.
2343 
2344     and the implementation is adapted from the [Java implementation][1] of
2345     K. Paton originally licensed under [Apache License 2.0][2].
2346 
2347     [1]: https://code.google.com/archive/p/niographs/
2348     [2]: http://www.apache.org/licenses/LICENSE-2.0
2349 
2350     Returns: range of cycles in the graph represented as arrays of node indices
2351 */
2352 auto findCyclicSubgraphs(G)(
2353     G graph,
2354     G.IncidentEdgesCache incidentEdgesCache = G.IncidentEdgesCache.init,
2355 )
2356     if (is(G : Graph!Params, Params...))
2357 {
2358     auto node(in size_t idx)
2359     {
2360         return graph.nodes[idx];
2361     }
2362 
2363     version(assert) void assertValidCycle(in size_t[] cycle)
2364     {
2365         enum errorMsg = "not a cycle";
2366 
2367         assert(
2368             cycle.length > 0 && graph.edge(node(cycle[0]), node(cycle[$ - 1])) in graph,
2369             errorMsg
2370         );
2371 
2372         foreach (pair; cycle.slide!(No.withPartial)(2))
2373             assert(graph.edge(node(pair[0]), node(pair[1])) in graph, errorMsg);
2374     }
2375 
2376     auto numNodes = graph.nodes.length;
2377 
2378     NaturalNumberSet[] used;
2379     used.length = numNodes;
2380 
2381     long[] parent;
2382     parent.length = numNodes;
2383     parent[] = -1;
2384 
2385     size_t[] stack;
2386     stack.reserve(numNodes);
2387 
2388     auto cycles = appender!(size_t[][]);
2389 
2390     if (incidentEdgesCache == G.IncidentEdgesCache.init)
2391         incidentEdgesCache = graph.allIncidentEdges();
2392 
2393     foreach (rootIdx, root; graph.nodes)
2394     {
2395         // Loop over the connected
2396         // components of the graph.
2397         if (parent[rootIdx] >= 0)
2398             continue;
2399 
2400         // Prepare to walk the spanning tree.
2401         parent[rootIdx] = rootIdx;
2402         used[rootIdx].reserveFor(numNodes);
2403         used[rootIdx].add(rootIdx);
2404         stack ~= rootIdx;
2405 
2406         // Do the walk. It is a BFS with
2407         // a LIFO instead of the usual
2408         // FIFO. Thus it is easier to
2409         // find the cycles in the tree.
2410         while (stack.length > 0)
2411         {
2412             auto currentIdx = stack[$ - 1];
2413             --stack.length;
2414             auto current = node(currentIdx);
2415             auto currentUsed = &used[currentIdx];
2416 
2417             foreach (edge; incidentEdgesCache[currentIdx])
2418             {
2419                 auto neighbour = edge.target(current);
2420                 auto neighbourIdx = graph.indexOf(neighbour);
2421                 auto neighbourUsed = &used[neighbourIdx];
2422 
2423                 if (neighbourUsed.empty)
2424                 {
2425                     // found a new node
2426                     parent[neighbourIdx] = currentIdx;
2427                     neighbourUsed.reserveFor(numNodes);
2428                     neighbourUsed.add(currentIdx);
2429 
2430                     stack ~= neighbourIdx;
2431                 }
2432                 else if (neighbourIdx == currentIdx)
2433                 {
2434                     // found a self loop
2435                     auto cycle = [currentIdx];
2436                     cycles ~= cycle;
2437                     version(assert) assertValidCycle(cycle);
2438                 }
2439                 else if (!currentUsed.has(neighbourIdx))
2440                 {
2441                     // found a cycle
2442                     auto cycle = appender!(size_t[]);
2443                     cycle ~= neighbourIdx;
2444                     cycle ~= currentIdx;
2445 
2446                     auto p = parent[currentIdx];
2447                     for (; !neighbourUsed.has(p); p = parent[p])
2448                         cycle ~= p;
2449 
2450                     cycle ~= p;
2451                     cycles ~= cycle.data;
2452                     version(assert) assertValidCycle(cycle.data);
2453                     neighbourUsed.add(currentIdx);
2454                 }
2455             }
2456         }
2457     }
2458 
2459     return cycles.data;
2460 }
2461 
2462 ///
2463 unittest
2464 {
2465     alias G = Graph!int;
2466 
2467     //   __
2468     //   \ \
2469     //    `-0 -- 1 -- 2 -- 3
2470     //      |       / |    |
2471     //      |      /  |    |
2472     //      4 -- 5 -- 6    7
2473     auto g = G([0, 1, 2, 3, 4, 5, 6, 7], [
2474         G.edge(0, 0),
2475         G.edge(0, 1),
2476         G.edge(0, 4),
2477         G.edge(1, 2),
2478         G.edge(2, 3),
2479         G.edge(2, 5),
2480         G.edge(2, 6),
2481         G.edge(3, 7),
2482         G.edge(4, 5),
2483         G.edge(5, 6),
2484     ]);
2485     auto cycles = g.findCyclicSubgraphs();
2486 
2487     import std.algorithm : equal;
2488 
2489     assert(cycles.equal([
2490         [0],
2491         [2, 6, 5],
2492         [1, 2, 5, 4, 0],
2493     ]));
2494 }
2495 
2496 /**
2497     Find all maximal cliques in a graph represented by `adjacencyList`.
2498     The implementation is based on version 1 of the Bron-Kerbosch algorithm [1].
2499 
2500     [1]: Bron, C.; Kerbosch, J. (1973), "Algorithm 457: finding all cliques
2501          of an undirected graph", Communications of the ACM, 16 (9): 575–577,
2502          doi:10.1145/362342.362367.
2503 
2504     Returns: list of sets of nodes each representing a maximal clique
2505 */
2506 auto findAllCliques(in size_t[][] adjacencyList)
2507 {
2508     return BronKerboschVersion1(adjacencyList);
2509 }
2510 
2511 ///
2512 unittest
2513 {
2514     auto g = Graph!int([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
2515     g.add(g.edge(0, 1));
2516     g.add(g.edge(0, 2));
2517     g.add(g.edge(1, 2));
2518     g.add(g.edge(1, 7));
2519     g.add(g.edge(1, 8));
2520     g.add(g.edge(2, 3));
2521     g.add(g.edge(3, 4));
2522     g.add(g.edge(3, 5));
2523     g.add(g.edge(3, 6));
2524     g.add(g.edge(4, 5));
2525     g.add(g.edge(4, 6));
2526     g.add(g.edge(5, 6));
2527     g.add(g.edge(6, 7));
2528     g.add(g.edge(7, 8));
2529 
2530     auto cliques = array(findAllCliques(g.adjacencyList()));
2531 
2532     assert(cliques == [
2533         [0, 1, 2],
2534         [1, 7, 8],
2535         [2, 3],
2536         [3, 4, 5, 6],
2537         [6, 7],
2538         [9],
2539     ]);
2540 }
2541 
2542 private struct BronKerboschVersion1
2543 {
2544     const size_t[][] adjacencyList;
2545 
2546     int opApply(scope int delegate(size_t[]) yield)
2547     {
2548         size_t[] clique;
2549         clique.reserve(adjacencyList.length);
2550 
2551         auto candidates = NaturalNumberSet(adjacencyList.length, Yes.addAll);
2552         auto not = NaturalNumberSet(adjacencyList.length);
2553 
2554         return extendClique(clique, candidates, not, yield);
2555     }
2556 
2557     private int extendClique(
2558         size_t[] clique,
2559         NaturalNumberSet candidates,
2560         NaturalNumberSet not,
2561         scope int delegate(size_t[]) yield,
2562     )
2563     {
2564         import std.stdio;
2565 
2566         if (not.empty && candidates.empty)
2567             return clique.length == 0 ? 0 : yield(clique);
2568 
2569         int result;
2570 
2571         foreach (candidate; candidates.elements)
2572         {
2573             clique ~= candidate;
2574 
2575             auto reducedCandidates = NaturalNumberSet(adjacencyList.length);
2576             auto reducedNot = NaturalNumberSet(adjacencyList.length);
2577 
2578             foreach (neighbourNode; adjacencyList[candidate])
2579             {
2580                 if (candidates.has(neighbourNode))
2581                     reducedCandidates.add(neighbourNode);
2582                 if (not.has(neighbourNode))
2583                     reducedNot.add(neighbourNode);
2584             }
2585 
2586             result = extendClique(clique, reducedCandidates, reducedNot, yield);
2587 
2588             if (result)
2589                 return result;
2590 
2591             candidates.remove(candidate);
2592             not.add(candidate);
2593             --clique.length;
2594         }
2595 
2596         return result;
2597     }
2598 }
2599 
2600 /**
2601     Calculate a longest increasing subsequence of `sequence`. This subsequence
2602     is not necessarily contiguous, or unique. Given a `sequence` of `n`
2603     elements the algorithm uses `O(n log n)` evaluation of `pred`.
2604 
2605     See_Also: https://en.wikipedia.org/wiki/Longest_increasing_subsequence
2606 */
2607 auto longestIncreasingSubsequence(alias pred = "a < b", Range)(Range sequence)
2608         if (isRandomAccessRange!Range)
2609 {
2610     alias lessThan = binaryFun!pred;
2611 
2612     size_t[] subseqEnds;
2613     subseqEnds.length = sequence.length;
2614     size_t[] predecessors;
2615     predecessors.length = sequence.length;
2616     size_t subseqLength;
2617 
2618     foreach (i; 0 .. sequence.length)
2619     {
2620         // Binary search for the largest positive j < subseqLength
2621         // such that sequence[subseqEnds[j]] < sequence[i]
2622         long lo = 0;
2623         long hi = subseqLength - 1;
2624         auto pivot = sequence[i];
2625         assert(!lessThan(pivot, pivot), "`pred` is not anti-symmetric");
2626 
2627         while (lo <= hi)
2628         {
2629             auto mid = ceildiv(lo + hi, 2);
2630 
2631             if (lessThan(sequence[subseqEnds[mid]], pivot))
2632                 lo = mid + 1;
2633             else
2634                 hi = mid - 1;
2635         }
2636 
2637         // After searching, lo + 1 is the length of the longest prefix of
2638         // sequence[i]
2639         auto newSubseqLength = lo + 1;
2640 
2641         // The predecessor of sequence[i] is the last index of
2642         // the subsequence of length newSubseqLength - 1
2643         subseqEnds[lo] = i;
2644         if (lo > 0)
2645             predecessors[i] = subseqEnds[lo - 1];
2646 
2647         if (newSubseqLength > subseqLength)
2648             // If we found a subsequence longer than any we've
2649             // found yet, update subseqLength
2650             subseqLength = newSubseqLength;
2651     }
2652 
2653     auto subsequenceResult = subseqEnds[0 .. subseqLength];
2654 
2655     if (subseqLength > 0)
2656     {
2657         // Reconstruct the longest increasing subsequence
2658         // Note: reusing memory from now unused subseqEnds
2659         auto k = subseqEnds[subseqLength - 1];
2660         foreach_reverse (i; 0 .. subseqLength)
2661         {
2662             subsequenceResult[i] = k;
2663             k = predecessors[k];
2664         }
2665     }
2666 
2667     return subsequenceResult.map!(i => sequence[i]);
2668 }
2669 
2670 /// Example from Wikipedia
2671 unittest
2672 {
2673     import std.algorithm : equal;
2674 
2675     auto inputSequence = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
2676     auto expectedOutput = [0, 2, 6, 9, 11, 15];
2677 
2678     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2679 }
2680 
2681 /// Example using a different `pred`
2682 unittest
2683 {
2684     import std.algorithm : equal;
2685     import std.range : retro;
2686 
2687     auto inputSequence = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15];
2688     auto expectedOutput = [12, 10, 9, 5, 3];
2689 
2690     assert(inputSequence.longestIncreasingSubsequence!"a > b".equal(expectedOutput));
2691 }
2692 
2693 unittest
2694 {
2695     import std.algorithm : equal;
2696 
2697     int[] inputSequence = [];
2698     int[] expectedOutput = [];
2699 
2700     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2701 }
2702 
2703 unittest
2704 {
2705     import std.algorithm : equal;
2706 
2707     auto inputSequence = [1, 2, 3, 4, 5];
2708     auto expectedOutput = [1, 2, 3, 4, 5];
2709 
2710     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2711 }
2712 
2713 unittest
2714 {
2715     import std.algorithm : equal;
2716 
2717     auto inputSequence = [2, 1, 3, 4, 5];
2718     auto expectedOutput = [1, 3, 4, 5];
2719 
2720     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2721 }
2722 
2723 unittest
2724 {
2725     import std.algorithm : equal;
2726 
2727     auto inputSequence = [1, 2, 3, 5, 4];
2728     auto expectedOutput = [1, 2, 3, 4];
2729 
2730     assert(inputSequence.longestIncreasingSubsequence.equal(expectedOutput));
2731 }
2732 
2733 
2734 /**
2735     Compute a logarithmic index to `base` of `value` and vice versa.
2736     The function is piecewise linear for each interval of `base` indices.
2737     For interger values, the functions are mathematically equivalent to:
2738 
2739         logIndex(x, b) = (b - 1) ⌊log_b(x)⌋ + x / b^^⌊log_b(x)⌋
2740 
2741         inverseLogIndex(y, b) = (m + 1) * b^^d
2742         where
2743             m = (y - 1) mod (b - 1)
2744             d = ⌊(y - 1) / (b - 1)⌋
2745 */
2746 size_t logIndex(size_t value, size_t base) pure nothrow @safe
2747 {
2748     size_t nDigits;
2749 
2750     while (value >= base)
2751     {
2752         value /= base;
2753         ++nDigits;
2754     }
2755 
2756     return (base - 1) * nDigits + value;
2757 }
2758 
2759 ///
2760 unittest
2761 {
2762     enum base = 10;
2763     auto testValues = [
2764         0: 0,
2765         1: 1,
2766         2: 2,
2767         9: 9,
2768         10: 10,
2769         11: 10,
2770         19: 10,
2771         20: 11,
2772         21: 11,
2773         29: 11,
2774         99: 18,
2775         100: 19,
2776         101: 19,
2777         199: 19,
2778         200: 20,
2779         1000: 28,
2780     ];
2781 
2782     foreach (value, result; testValues)
2783         assert(
2784             logIndex(value, base) == result,
2785             format!"%d != %d"(logIndex(value, base), result),
2786         );
2787 }
2788 
2789 ///
2790 unittest
2791 {
2792     enum base = 16;
2793     auto testValues = [
2794         0x0000: 0,
2795         0x0001: 0x1,
2796         0x0002: 0x2,
2797         0x000f: 0xf,
2798         0x0010: 0x10,
2799         0x0011: 0x10,
2800         0x001f: 0x10,
2801         0x0020: 0x11,
2802         0x0021: 0x11,
2803         0x002f: 0x11,
2804         0x00ff: 0x1e,
2805         0x0100: 0x1f,
2806         0x0101: 0x1f,
2807         0x01ff: 0x1f,
2808         0x0200: 0x20,
2809         0x1000: 0x2e
2810     ];
2811 
2812     foreach (value, result; testValues)
2813         assert(
2814             logIndex(value, base) == result,
2815             format!"0x%x != 0x%x"(logIndex(value, base), result),
2816         );
2817 }
2818 
2819 /// ditto
2820 size_t inverseLogIndex(size_t value, size_t base) pure nothrow @safe
2821 {
2822     if (value == 0)
2823         return 0;
2824 
2825     auto nDigits = (value - 1) / (base - 1);
2826     auto rem = (value - 1) % (base - 1) + 1;
2827 
2828     return rem * base^^nDigits;
2829 }
2830 
2831 ///
2832 unittest
2833 {
2834     enum base = 10;
2835     auto testValues = [
2836         0: 0,
2837         1: 1,
2838         2: 2,
2839         9: 9,
2840         10: 10,
2841         10: 10,
2842         11: 20,
2843         11: 20,
2844         18: 90,
2845         19: 100,
2846         20: 200,
2847         28: 1000,
2848     ];
2849 
2850     foreach (value, result; testValues)
2851         assert(
2852             inverseLogIndex(value, base) == result,
2853             format!"%d != %d"(inverseLogIndex(value, base), result),
2854         );
2855 }
2856 
2857 ///
2858 unittest
2859 {
2860     enum base = 16;
2861     auto testValues = [
2862         0x00: 0x0,
2863         0x01: 0x1,
2864         0x02: 0x2,
2865         0x0f: 0xf,
2866         0x10: 0x10,
2867         0x10: 0x10,
2868         0x10: 0x10,
2869         0x11: 0x20,
2870         0x1e: 0xf0,
2871         0x1f: 0x100,
2872         0x20: 0x200,
2873         0x2e: 0x1000
2874     ];
2875 
2876     foreach (value, result; testValues)
2877         assert(
2878             inverseLogIndex(value, base) == result,
2879             format!"0x%x != 0x%x"(inverseLogIndex(value, base), result),
2880         );
2881 }
2882 
2883 unittest
2884 {
2885     auto testValues = [
2886         0: 0,
2887         1: 1,
2888         2: 2,
2889         3: 3,
2890         4: 4,
2891         5: 5,
2892         6: 6,
2893         7: 7,
2894         8: 8,
2895         9: 9,
2896         10: 10,
2897         11: 20,
2898         12: 30,
2899         13: 40,
2900         14: 50,
2901         15: 60,
2902         16: 70,
2903         17: 80,
2904         18: 90,
2905         19: 100,
2906         20: 200,
2907         21: 300,
2908         22: 400,
2909         23: 500,
2910         24: 600,
2911         25: 700,
2912         26: 800,
2913         27: 900,
2914         28: 1000,
2915         29: 2000,
2916         30: 3000,
2917     ];
2918 
2919     foreach (value, result; testValues)
2920         assert(
2921             inverseLogIndex(value, 10) == result,
2922             format!"%d != %d"(inverseLogIndex(value, 10), result),
2923         );
2924 }
2925 
2926 
2927 struct Histogram(T, Flag!"logIndex" logIndex = No.logIndex)
2928 {
2929     static assert(isNumeric!T, "currently only built-in numeric types are supported");
2930 
2931     static if (logIndex)
2932     {
2933         alias bin_size_t = size_t;
2934         alias indexBase = _binSize;
2935     }
2936     else
2937     {
2938         alias bin_size_t = T;
2939     }
2940 
2941 
2942     static if (isFloatingPoint!T)
2943     {
2944         enum valueInf = -T.infinity;
2945         enum valueSup = T.infinity;
2946     }
2947     else
2948     {
2949         enum valueInf = T.min;
2950         enum valueSup = T.max;
2951     }
2952 
2953 
2954     private
2955     {
2956         T _histMin;
2957         T _histMax;
2958         bin_size_t _binSize;
2959         enum size_t underflowIdx = 0;
2960         size_t overflowIdx;
2961         size_t[] _counts;
2962         size_t _totalCount;
2963         T _minValue;
2964         T _maxValue;
2965         T _sum;
2966     }
2967 
2968 
2969     this(T histMin, T histMax, bin_size_t binSize)
2970     {
2971         static if (isFloatingPoint!T)
2972             assert(
2973                 -T.infinity < histMin && histMax < T.infinity,
2974                 "histMin and histMax must be finite",
2975             );
2976         assert(histMin < histMax, "histMin should be less than histMax");
2977         assert(binSize > 0, "binSize/indexBase must be positive");
2978 
2979         if (histMin > histMax)
2980             swap(histMin, histMax);
2981 
2982         this._histMin = histMin;
2983         this._histMax = histMax;
2984         this._binSize = binSize;
2985         this.overflowIdx = rawBinIdx(histMax);
2986         this._counts = new size_t[overflowIdx + 1];
2987         this._minValue = valueSup;
2988         this._maxValue = valueInf;
2989         this._sum = 0;
2990 
2991         debug assert(binCoord(overflowIdx) <= histMax && histMax <= valueSup);
2992     }
2993 
2994 
2995     @property bool hasUnderflowBin() const pure nothrow @safe
2996     {
2997         static if (isFloatingPoint!T)
2998             return true;
2999         else
3000             return valueInf < histMin;
3001     }
3002 
3003 
3004     @property bool hasOverflowBin() const pure nothrow @safe
3005     {
3006         static if (isFloatingPoint!T)
3007             return true;
3008         else
3009             return histMax < valueSup;
3010     }
3011 
3012 
3013     @property inout(size_t[]) countsWithoutOutliers() inout pure nothrow @safe
3014     {
3015         size_t begin = cast(size_t) hasUnderflowBin;
3016         size_t end = _counts.length - 1 - cast(size_t) hasOverflowBin;
3017 
3018         return _counts[begin .. end];
3019     }
3020 
3021 
3022     @property size_t numUnderflows() const pure nothrow @safe
3023     {
3024         if (hasUnderflowBin)
3025             return _counts[underflowIdx];
3026         else
3027             return 0;
3028     }
3029 
3030 
3031     @property size_t numOverflows() const pure nothrow @safe
3032     {
3033         if (hasOverflowBin)
3034             return _counts[overflowIdx];
3035         else
3036             return 0;
3037     }
3038 
3039 
3040     /// Insert value into this histogram.
3041     void insert(T value)
3042     {
3043         ++_counts[binIdx(value)];
3044         ++_totalCount;
3045         _minValue = min(_minValue, value);
3046         _maxValue = max(_maxValue, value);
3047         _sum += value;
3048     }
3049 
3050 
3051     /// Insert a range of values into this histogram. This is equivalent to
3052     ///
3053     ///     foreach (value; values)
3054     ///         insert(value);
3055     void insert(R)(R values) if (isInputRange!R && is(ElementType!R == T))
3056     {
3057         foreach (value; values)
3058             insert(value);
3059     }
3060 
3061 
3062     /// Values smaller than this value are stored in the lower overflow bin.
3063     @property const(T) histMin() const pure nothrow @safe { return _histMin; }
3064 
3065     /// Values larger than this value are stored in the lower overflow bin.
3066     @property const(T) histMax() const pure nothrow @safe { return _histMax; }
3067 
3068     /// Total number of values stored in this histogram.
3069     @property const(size_t) totalCount() const pure nothrow @safe { return _totalCount; }
3070 
3071     /// Smallest value stored in this histogram. This is not subject to
3072     /// `histMin` and `histMax`.
3073     @property const(T) minValue() const pure nothrow @safe
3074     in (_totalCount > 0, "undefined for empty histogram")
3075     {
3076         return _minValue;
3077     }
3078 
3079     /// Largest value stored in this histogram. This is not subject to
3080     /// `histMin` and `histMax`.
3081     @property const(T) maxValue() const pure nothrow @safe
3082     in (_totalCount > 0, "undefined for empty histogram")
3083     {
3084         return _maxValue;
3085     }
3086 
3087     /// Sum of all values stored in this histogram. This is not subject to
3088     /// `histMin` and `histMax`.
3089     @property const(T) sum() const pure nothrow @safe
3090     in (_totalCount > 0, "undefined for empty histogram")
3091     {
3092         return _sum;
3093     }
3094 
3095 
3096     /// Returns a value such that roughly `percent` values in the histogram
3097     /// are smaller than value. The value is linearly interpolated between
3098     /// bin coordinates. The second form stores the bin index such that no
3099     /// more the `percent` of the values in the histrogram are in the bins
3100     /// up to `index` (inclusive).
3101     double percentile(
3102         double percent,
3103         Flag!"excludeOutliers" excludeOutliers = No.excludeOutliers,
3104     ) const pure
3105     {
3106         size_t index;
3107 
3108         return percentile(percent, index, excludeOutliers);
3109     }
3110 
3111     /// ditto
3112     double percentile(
3113         double percent,
3114         out size_t index,
3115         Flag!"excludeOutliers" excludeOutliers = No.excludeOutliers,
3116     ) const pure
3117     {
3118         assert(0.0 < percent && percent < 1.0, "percent must be between 0 and 1");
3119 
3120         if (totalCount == 0)
3121             return double.nan;
3122 
3123         auto threshold = excludeOutliers
3124             ? percent * (totalCount - (numUnderflows + numOverflows))
3125             : percent * totalCount;
3126 
3127         size_t partialSum;
3128         foreach (i, ref count; excludeOutliers ? countsWithoutOutliers : _counts)
3129         {
3130             if (partialSum + count >= threshold)
3131             {
3132                 index = i;
3133 
3134                 return binCoord(i) + binSize(i) * (threshold - partialSum) / count;
3135             }
3136 
3137             partialSum += count;
3138         }
3139 
3140         assert(0, "unreachable");
3141     }
3142 
3143 
3144     /// Returns the mean of the inserted values.
3145     @property double mean() const pure nothrow
3146     {
3147         return cast(double) sum / totalCount;
3148     }
3149 
3150 
3151     /// Iterates over the histogram bins enumerating the probability
3152     /// densities `density`.
3153     int opApply(scope int delegate(T coord, double density) yield)
3154     {
3155         int result;
3156 
3157         foreach (size_t idx, T coord, double density; this)
3158         {
3159             result = yield(coord, density);
3160 
3161             if (result)
3162                 break;
3163         }
3164 
3165         return result;
3166     }
3167 
3168     /// ditto
3169     int opApply(scope int delegate(size_t index, T coord, double density) yield)
3170     {
3171         int result;
3172 
3173         foreach (i, count; _counts)
3174         {
3175             result = yield(
3176                 i,
3177                 binCoord(i),
3178                 cast(double) count / (totalCount * binSize(i)),
3179             );
3180 
3181             if (result)
3182                 break;
3183         }
3184 
3185         return result;
3186     }
3187 
3188     /// ditto
3189     int opApplyReverse(scope int delegate(T coord, double density) yield)
3190     {
3191         int result;
3192 
3193         foreach_reverse (size_t idx, T coord, double density; this)
3194         {
3195             result = yield(coord, density);
3196 
3197             if (result)
3198                 break;
3199         }
3200 
3201         return result;
3202     }
3203 
3204     /// ditto
3205     int opApplyReverse(scope int delegate(size_t index, T coord, double density) yield)
3206     {
3207         int result;
3208 
3209         foreach_reverse (i, count; _counts)
3210         {
3211             result = yield(
3212                 i,
3213                 binCoord(i),
3214                 cast(double) count / (totalCount * binSize(i)),
3215             );
3216 
3217             if (result)
3218                 break;
3219         }
3220 
3221         return result;
3222     }
3223 
3224     /// ditto
3225     auto densities() const pure nothrow @safe
3226     {
3227         return _counts
3228             .enumerate
3229             .map!(enumValue => tuple!(
3230                 "index",
3231                 "coord",
3232                 "density",
3233             )(
3234                 enumValue.index,
3235                 binCoord(enumValue.index),
3236                 cast(double) enumValue.value / (totalCount * binSize(enumValue.index)),
3237             ));
3238     }
3239 
3240 
3241     /// Iterates over the histogram bins enumerating the counts.
3242     int opApply(scope int delegate(T coord, size_t count) yield)
3243     {
3244         int result;
3245 
3246         foreach (size_t idx, T coord, size_t count; this)
3247         {
3248             result = yield(coord, count);
3249 
3250             if (result)
3251                 break;
3252         }
3253 
3254         return result;
3255     }
3256 
3257     /// ditto
3258     int opApply(scope int delegate(size_t index, T coord, size_t count) yield)
3259     {
3260         int result;
3261 
3262         foreach (i, count; _counts)
3263         {
3264             result = yield(
3265                 i,
3266                 binCoord(i),
3267                 count,
3268             );
3269 
3270             if (result)
3271                 break;
3272         }
3273 
3274         return result;
3275     }
3276 
3277     /// ditto
3278     int opApplyReverse(scope int delegate(T coord, size_t count) yield)
3279     {
3280         int result;
3281 
3282         foreach_reverse (size_t idx, T coord, size_t count; this)
3283         {
3284             result = yield(coord, count);
3285 
3286             if (result)
3287                 break;
3288         }
3289 
3290         return result;
3291     }
3292 
3293     /// ditto
3294     int opApplyReverse(scope int delegate(size_t index, T coord, size_t count) yield)
3295     {
3296         int result;
3297 
3298         foreach_reverse (i, count; _counts)
3299         {
3300             result = yield(
3301                 i,
3302                 binCoord(i),
3303                 count,
3304             );
3305 
3306             if (result)
3307                 break;
3308         }
3309 
3310         return result;
3311     }
3312 
3313 
3314     /// Calculate the bin index corresponding to `value`.
3315     size_t binIdx(T value) const pure @safe
3316     {
3317         if (value < histMin)
3318             return underflowIdx;
3319         else if (histMax <= value)
3320             return overflowIdx;
3321         else
3322             return rawBinIdx(value);
3323     }
3324 
3325     /// ditto
3326     auto counts() const pure nothrow @safe
3327     {
3328         return _counts
3329             .enumerate
3330             .map!(enumValue => tuple!(
3331                 "index",
3332                 "coord",
3333                 "count",
3334             )(
3335                 enumValue.index,
3336                 binCoord(enumValue.index),
3337                 enumValue.value,
3338             ));
3339     }
3340 
3341 
3342     private size_t rawBinIdx(T value) const pure @safe
3343     {
3344         size_t idx;
3345 
3346         static if (logIndex)
3347         {
3348             static if (isFloatingPoint!T)
3349                 idx = dalicious.math.logIndex(stdFloor(normalizedValue(value)).to!size_t, indexBase);
3350             else
3351                 idx = dalicious.math.logIndex(normalizedValue(value).to!size_t, indexBase);
3352         }
3353         else
3354         {
3355             static if (isFloatingPoint!T)
3356                 idx = stdFloor(normalizedValue(value) / binSize).to!size_t;
3357             else
3358                 idx = (normalizedValue(value) / binSize).to!size_t;
3359         }
3360 
3361         return idx + cast(size_t) hasUnderflowBin;
3362     }
3363 
3364 
3365     /// Calculate the bin size at of bin `idx`.
3366     T binSize(size_t idx) const pure @safe
3367     {
3368         if (hasUnderflowBin && idx == underflowIdx)
3369             return minValue - valueInf;
3370         else if (hasOverflowBin && idx == overflowIdx)
3371             return valueSup - histMax;
3372 
3373         static if (logIndex)
3374         {
3375             idx -= cast(size_t) hasUnderflowBin;
3376 
3377             return to!T(
3378                 dalicious.math.inverseLogIndex(idx + 1, indexBase)
3379                 -
3380                 dalicious.math.inverseLogIndex(idx, indexBase)
3381             );
3382         }
3383         else
3384         {
3385             return binSize;
3386         }
3387     }
3388 
3389     /// ditto
3390     static if (!logIndex)
3391         T binSize() const pure @safe
3392         {
3393             return _binSize;
3394         }
3395 
3396 
3397     /// Calculate the value corresponding to the bin index.
3398     T binCoord(size_t idx) const pure @safe
3399     {
3400         if (hasUnderflowBin && idx == underflowIdx)
3401             return valueInf;
3402         else if (hasOverflowBin && idx == overflowIdx)
3403             return histMax;
3404 
3405         idx -= cast(size_t) hasUnderflowBin;
3406 
3407         static if (logIndex)
3408             return primalValue(to!T(dalicious.math.inverseLogIndex(idx, indexBase)));
3409         else
3410             return primalValue(to!T(idx * binSize));
3411     }
3412 
3413 
3414     private T normalizedValue(T value) const pure nothrow @safe
3415     out (nValue; nValue >= 0, "normalizedValue must be non-negative")
3416     {
3417         static if (isUnsigned!T)
3418             assert(histMin <= value, "subtraction overflow");
3419 
3420         return cast(T) (value - histMin);
3421     }
3422 
3423 
3424     private T primalValue(T nValue) const pure nothrow @safe
3425     in (nValue >= 0, "nValue must be non-negative")
3426     {
3427         return cast(T) (nValue + histMin);
3428     }
3429 
3430 
3431     /// Returns a space-separated table of the histogram bins and header lines
3432     /// with `totalCount`, `minValue`, `maxValue` and `sum`. The header lines
3433     /// begin with a has sign (`#`) so they may be treated as comments
3434     /// by other programs.
3435     string toString() const
3436     {
3437         enum histFmt = format!(`
3438             # totalCount=%%d
3439             # minValue=%%%1$c
3440             # maxValue=%%%1$c
3441             # sum=%%%1$c
3442             # mean=%%g
3443             %%-(%%s\%%)
3444         `.outdent.strip.tr(`\`, "\n"))(isFloatingPoint!T ? 'g' : 'd');
3445         enum histLineFmt = isFloatingPoint!T
3446             ? "%g %g"
3447             : "%d %g";
3448 
3449         return format!histFmt(
3450             totalCount,
3451             minValue,
3452             maxValue,
3453             sum,
3454             mean,
3455             densities.map!(v => format!histLineFmt(v.coord, v.density)),
3456         );
3457     }
3458 }
3459 
3460 unittest
3461 {
3462     auto h = logHistogram(0U, 10000U, 10U);
3463 
3464     with (h)
3465     {
3466         assert(binIdx(0) == 0);
3467         assert(binIdx(1) == 1);
3468         assert(binIdx(2) == 2);
3469         assert(binIdx(9) == 9);
3470         assert(binIdx(10) == 10);
3471         assert(binIdx(11) == 10);
3472         assert(binIdx(19) == 10);
3473         assert(binIdx(20) == 11);
3474         assert(binIdx(21) == 11);
3475         assert(binIdx(29) == 11);
3476         assert(binIdx(99) == 18);
3477         assert(binIdx(100) == 19);
3478         assert(binIdx(101) == 19);
3479         assert(binIdx(199) == 19);
3480         assert(binIdx(200) == 20);
3481         assert(binIdx(1000) == 28);
3482 
3483         assert(binSize(0) == 1);
3484         assert(binSize(1) == 1);
3485         assert(binSize(2) == 1);
3486         assert(binSize(3) == 1);
3487         assert(binSize(4) == 1);
3488         assert(binSize(5) == 1);
3489         assert(binSize(6) == 1);
3490         assert(binSize(7) == 1);
3491         assert(binSize(8) == 1);
3492         assert(binSize(9) == 1);
3493         assert(binSize(10) == 10);
3494         assert(binSize(11) == 10);
3495         assert(binSize(12) == 10);
3496         assert(binSize(13) == 10);
3497         assert(binSize(14) == 10);
3498         assert(binSize(15) == 10);
3499         assert(binSize(16) == 10);
3500         assert(binSize(17) == 10);
3501         assert(binSize(18) == 10);
3502         assert(binSize(19) == 100);
3503         assert(binSize(20) == 100);
3504         assert(binSize(21) == 100);
3505         assert(binSize(22) == 100);
3506         assert(binSize(23) == 100);
3507         assert(binSize(24) == 100);
3508         assert(binSize(25) == 100);
3509         assert(binSize(26) == 100);
3510         assert(binSize(27) == 100);
3511         assert(binSize(28) == 1000);
3512         assert(binSize(29) == 1000);
3513         assert(binSize(30) == 1000);
3514 
3515         assert(binCoord(0) == 0);
3516         assert(binCoord(1) == 1);
3517         assert(binCoord(2) == 2);
3518         assert(binCoord(3) == 3);
3519         assert(binCoord(4) == 4);
3520         assert(binCoord(5) == 5);
3521         assert(binCoord(6) == 6);
3522         assert(binCoord(7) == 7);
3523         assert(binCoord(8) == 8);
3524         assert(binCoord(9) == 9);
3525         assert(binCoord(10) == 10);
3526         assert(binCoord(11) == 20);
3527         assert(binCoord(12) == 30);
3528         assert(binCoord(13) == 40);
3529         assert(binCoord(14) == 50);
3530         assert(binCoord(15) == 60);
3531         assert(binCoord(16) == 70);
3532         assert(binCoord(17) == 80);
3533         assert(binCoord(18) == 90);
3534         assert(binCoord(19) == 100);
3535         assert(binCoord(20) == 200);
3536         assert(binCoord(21) == 300);
3537         assert(binCoord(22) == 400);
3538         assert(binCoord(23) == 500);
3539         assert(binCoord(24) == 600);
3540         assert(binCoord(25) == 700);
3541         assert(binCoord(26) == 800);
3542         assert(binCoord(27) == 900);
3543         assert(binCoord(28) == 1000);
3544         assert(binCoord(29) == 2000);
3545         assert(binCoord(30) == 3000);
3546     }
3547 }
3548 
3549 
3550 /**
3551     Creates a histogram of values. Additional values can be inserted into the
3552     histogram using the `insert` method. The second form `logHistogram`
3553     creates a histogram with logarithmic bin sizes.
3554 
3555     See_also:
3556         Histogram,
3557         dalicious.math.logIndex
3558 */
3559 Histogram!T histogram(R, T = ElementType!R)(T histMin, T histMax, T binSize, R values) if (isInputRange!R)
3560 {
3561     auto hist = typeof(return)(histMin, histMax, binSize);
3562 
3563     hist.insert(values);
3564 
3565     return hist;
3566 }
3567 
3568 /// ditto
3569 Histogram!T histogram(T)(T histMin, T histMax, T binSize)
3570 {
3571     return typeof(return)(histMin, histMax, binSize);
3572 }
3573 
3574 /// ditto
3575 Histogram!(T, Yes.logIndex) logHistogram(R, T = ElementType!R)(T histMin, T histMax, size_t indexBase, R values) if (isInputRange!R)
3576 {
3577     auto hist = typeof(return)(histMin, histMax, indexBase);
3578 
3579     hist.insert(values);
3580 
3581     return hist;
3582 }
3583 
3584 /// ditto
3585 Histogram!(T, Yes.logIndex) logHistogram(T)(T histMin, T histMax, size_t indexBase)
3586 {
3587     return typeof(return)(histMin, histMax, indexBase);
3588 }
3589 
3590 ///
3591 unittest
3592 {
3593     // Generate a histogram of standard-normal-distributed numbers
3594     // with 7+2 bins from -2.0 to 2.0. The additional two bins are
3595     // the overflow bins.
3596     auto h = histogram(-2.0, 2.0, 0.5, [
3597         0.697108, 0.019264, -1.838430, 1.831528, -0.804880, -1.558828,
3598         -0.131643, -0.306090, -0.397831, 0.037725, 0.328819, -0.640064,
3599         0.664097, 1.156503, -0.837012, -0.969499, -1.410276, 0.501637,
3600         1.521720, 1.392988, -0.619393, -0.039576, 1.937708, -1.325983,
3601         -0.677214, 1.390584, 1.798133, -1.094093, 2.263360, -0.462949,
3602         1.993554, 2.243889, 1.606391, 0.153866, 1.945514, 1.007849,
3603         -0.663765, -0.304843, 0.617464, 0.674804, 0.038555, 1.696985,
3604         1.473917, -0.244211, -1.410381, 0.201184, -0.923119, -0.220677,
3605         0.045521, -1.966340,
3606     ]);
3607 
3608     assert(h.totalCount == 50);
3609     assert(approxEqual(h.minValue, -1.96634));
3610     assert(approxEqual(h.maxValue, 2.26336));
3611     assert(approxEqual(h.sum, 10.3936));
3612     assert(approxEqual(h.mean, 0.2079));
3613     assert(approxEqual(h.percentile(0.5), 0.1429));
3614 
3615     enum inf = double.infinity;
3616     auto expectedHist = [
3617         [-inf, 0.00],
3618         [-2.0, 0.12],
3619         [-1.5, 0.16],
3620         [-1.0, 0.32],
3621         [-0.5, 0.32],
3622         [ 0.0, 0.28],
3623         [ 0.5, 0.20],
3624         [ 1.0, 0.20],
3625         [ 1.5, 0.32],
3626         [ 2.0, 0.00],
3627     ];
3628 
3629     foreach (idx, coord, double density; h)
3630     {
3631         assert(approxEqual(expectedHist[idx][0], coord));
3632         assert(approxEqual(expectedHist[idx][1], density));
3633     }
3634 }
3635 
3636 ///
3637 unittest
3638 {
3639     // Generate a histogram of geometric-distributed numbers.
3640     auto h = logHistogram(0U, 50U, 10U, [
3641         3U, 4U, 23U, 2U, 0U, 2U, 9U, 0U, 17U, 2U, 0U, 5U, 5U, 35U, 0U, 16U,
3642         17U, 3U, 7U, 14U, 3U, 9U, 1U, 17U, 13U, 10U, 38U, 2U, 1U, 29U, 1U,
3643         5U, 49U, 40U, 2U, 1U, 13U, 5U, 1U, 1U, 2U, 4U, 1U, 0U, 0U, 7U, 7U,
3644         34U, 3U, 2U,
3645     ]);
3646 
3647     assert(h.totalCount == 50);
3648     assert(h.minValue == 0);
3649     assert(h.maxValue == 49);
3650     assert(h.sum == 465);
3651     assert(approxEqual(h.mean, 9.3));
3652     assert(approxEqual(h.percentile(0.5), 4.5));
3653 
3654     auto expectedHist = [
3655         [ 0, 0.120],
3656         [ 1, 0.140],
3657         [ 2, 0.140],
3658         [ 3, 0.080],
3659         [ 4, 0.040],
3660         [ 5, 0.080],
3661         [ 6, 0.000],
3662         [ 7, 0.060],
3663         [ 8, 0.000],
3664         [ 9, 0.040],
3665         [10, 0.016],
3666         [20, 0.004],
3667         [30, 0.006],
3668         [40, 0.004],
3669         [50, 0.000],
3670     ];
3671 
3672     foreach (idx, coord, double density; h)
3673     {
3674         assert(approxEqual(expectedHist[idx][0], coord));
3675         assert(approxEqual(expectedHist[idx][1], density));
3676     }
3677 }
3678 
3679 unittest
3680 {
3681     auto h = histogram(0U, 50U, 1U, [
3682         3U, 4U, 23U, 2U, 0U, 2U, 9U, 0U, 17U, 2U, 0U, 5U, 5U, 35U, 0U, 16U,
3683         17U, 3U, 7U, 14U, 3U, 9U, 1U, 17U, 13U, 10U, 38U, 2U, 1U, 29U, 1U,
3684         5U, 49U, 40U, 2U, 1U, 13U, 5U, 1U, 1U, 2U, 4U, 1U, 0U, 0U, 7U, 7U,
3685         34U, 3U, 2U,
3686     ]);
3687 
3688     assert(h.totalCount == 50);
3689     assert(h.minValue == 0);
3690     assert(h.maxValue == 49);
3691     assert(h.sum == 465);
3692     assert(approxEqual(h.mean, 9.3));
3693     assert(approxEqual(h.percentile(0.5), 4.5));
3694 
3695     assert(equal(h.counts.map!"a.count", [
3696         6, 7, 7, 4, 2, 4, 0, 3, 0, 2, 1, 0, 0, 2, 1, 0, 1, 3, 0, 0, 0, 0, 0,
3697         1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
3698         0, 0, 0, 1, 0,
3699     ]));
3700 }