Skip to content

Commit e915be3

Browse files
authored
Improve speed of finding cycles (#332)
1 parent b484fbe commit e915be3

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed

Sources/TSCBasic/GraphAlgorithms.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,13 @@ public func findCycle<T: Hashable>(
115115
) rethrows -> (path: [T], cycle: [T])? {
116116
// Ordered set to hold the current traversed path.
117117
var path = OrderedSet<T>()
118+
var validNodes = Set<T>()
118119

119120
// Function to visit nodes recursively.
120121
// FIXME: Convert to stack.
121122
func visit(_ node: T, _ successors: (T) throws -> [T]) rethrows -> (path: [T], cycle: [T])? {
123+
if validNodes.contains(node) { return nil }
124+
122125
// If this node is already in the current path then we have found a cycle.
123126
if !path.append(node) {
124127
let index = path.firstIndex(of: node)!
@@ -133,6 +136,7 @@ public func findCycle<T: Hashable>(
133136
// No cycle found for this node, remove it from the path.
134137
let item = path.removeLast()
135138
assert(item == node)
139+
validNodes.insert(node)
136140
return nil
137141
}
138142

Tests/TSCBasicTests/GraphAlgorithmsTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ class GraphAlgorithmsTests: XCTestCase {
5454
XCTAssertEqual([1, 2], transitiveClosure(1, [1: [2], 2: [1]]))
5555
}
5656

57+
func testLayeredAllToAllGraph() throws {
58+
let count = 100
59+
let items = Array(0...count)
60+
let layers = (0...count).flatMap { layerNumber in
61+
items.map {
62+
(
63+
$0 + 1000 * layerNumber,
64+
layerNumber == count ? [] : items.map {
65+
$0 + 1000 * (layerNumber + 1)
66+
}
67+
)
68+
}
69+
}
70+
71+
XCTAssertNotCycle(findCycle(1, Dictionary(uniqueKeysWithValues: layers)))
72+
}
73+
5774
func testTopologicalSort() throws {
5875
// A trival graph.
5976
XCTAssertEqual([1, 2], try topologicalSort(1, [1: [2]]))

0 commit comments

Comments
 (0)