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 }