diff --git a/Sources/TSCBasic/GraphAlgorithms.swift b/Sources/TSCBasic/GraphAlgorithms.swift index 02ac880e..55df8ac4 100644 --- a/Sources/TSCBasic/GraphAlgorithms.swift +++ b/Sources/TSCBasic/GraphAlgorithms.swift @@ -115,10 +115,13 @@ public func findCycle( ) rethrows -> (path: [T], cycle: [T])? { // Ordered set to hold the current traversed path. var path = OrderedSet() + var validNodes = Set() // Function to visit nodes recursively. // FIXME: Convert to stack. func visit(_ node: T, _ successors: (T) throws -> [T]) rethrows -> (path: [T], cycle: [T])? { + if validNodes.contains(node) { return nil } + // If this node is already in the current path then we have found a cycle. if !path.append(node) { let index = path.firstIndex(of: node)! @@ -133,6 +136,7 @@ public func findCycle( // No cycle found for this node, remove it from the path. let item = path.removeLast() assert(item == node) + validNodes.insert(node) return nil } diff --git a/Tests/TSCBasicTests/GraphAlgorithmsTests.swift b/Tests/TSCBasicTests/GraphAlgorithmsTests.swift index 69169365..61466f48 100644 --- a/Tests/TSCBasicTests/GraphAlgorithmsTests.swift +++ b/Tests/TSCBasicTests/GraphAlgorithmsTests.swift @@ -54,6 +54,23 @@ class GraphAlgorithmsTests: XCTestCase { XCTAssertEqual([1, 2], transitiveClosure(1, [1: [2], 2: [1]])) } + func testLayeredAllToAllGraph() throws { + let count = 100 + let items = Array(0...count) + let layers = (0...count).flatMap { layerNumber in + items.map { + ( + $0 + 1000 * layerNumber, + layerNumber == count ? [] : items.map { + $0 + 1000 * (layerNumber + 1) + } + ) + } + } + + XCTAssertNotCycle(findCycle(1, Dictionary(uniqueKeysWithValues: layers))) + } + func testTopologicalSort() throws { // A trival graph. XCTAssertEqual([1, 2], try topologicalSort(1, [1: [2]]))