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