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