             self.addNeighbor($0)
         }
     }
+    var component: PathComponent {
+        var end = self.next!.componentLocation
+        if end == self.pathComponent.startingIndexedLocation {
+            end = self.pathComponent.endingIndexedLocation
+        }
+        return self.pathComponent.split(from: self.componentLocation, to: end)
+    }
+    var backComponent: PathComponent {
+        var end = self.next!.componentLocation
+        if self.componentLocation == self.pathComponent.startingIndexedLocation {
+            return self.pathComponent.split(from: self.pathComponent.endingIndexedLocation, to: end)
+        }
+        return self.pathComponent.split(from: self.componentLocation, to: end)
+    }
 }
 
 private class Edge {
     var visited: Bool = false
     var inSolution: Bool = false
-    let component: PathComponent
-    init(component: PathComponent) {
-        self.component = component
+    init() {
+        self.visited = false
+        self.inSolution = false
     }
 }
 

        nodes = duplicatesRemoved
    }
    func createComponent(from startingNode: Node) -> PathComponent {
        let firstPoint = startingNode.edge!.component.startingPoint
        var points: [CGPoint] = [firstPoint]
        var orders: [Int] = []
        func visit(from node: Node) -> Node? {
            guard let forwardEdge = node.edge else {
                assertionFailure("consistency failure")
                return nil
            }
            guard let backwardsEdge = node.previous?.edge else {
                assertionFailure("consistency failure")
                return nil
            }
            if forwardEdge.inSolution, forwardEdge.visited == false {
                visit(forwardEdge.component)
                forwardEdge.visited = true
                return node.next
            } else if backwardsEdge.inSolution, backwardsEdge.visited == false {
                visit(backwardsEdge.component.reversed())
                backwardsEdge.visited = true
                return node.previous
            } else {
                return nil
            }
        }
        func visit(_ component: PathComponent) {
            points += component.points[1..<component.points.count]

    case removeCrossings
}

private class PathComponentGraph {
    /// Create the graph representing the component and return the first node (which represents the start of the first edge)
    private let nodes: [Node]
    init(for component: PathComponent, componentIndex: Int, using intersections: [Node]) {
        var endCappedIntersections = intersections
        let startingLocation = IndexedPathLocation(componentIndex: componentIndex, elementIndex: component.startingIndexedLocation.elementIndex, t: component.startingIndexedLocation.t)
        let endingLocation = IndexedPathLocation(componentIndex: componentIndex, elementIndex: component.endingIndexedLocation.elementIndex, t: component.endingIndexedLocation.t)
        if endCappedIntersections.first?.location != startingLocation {
            endCappedIntersections.insert(Node(location: startingLocation, pathComponent: component), at: 0)
        }
        if endCappedIntersections.last?.location != endingLocation {
            endCappedIntersections.append(Node(location: endingLocation, pathComponent: component))
        }
        for i in 0..<endCappedIntersections.count {
            if i > 0 {
                endCappedIntersections[i].previous = endCappedIntersections[i-1]
            }
            if i < endCappedIntersections.count-1 {
                endCappedIntersections[i].next = endCappedIntersections[i+1]
            }
        }
        // loop back the end to the start (if needed)
        if component.isClosed {
            // if the component is closed then the first and last nodes are really the same node
            let last = endCappedIntersections.last!
            let first = endCappedIntersections.first!
            first.previous = last.previous
            last.previous?.next = first
            first.mergeNeighbors(of: last)
            endCappedIntersections.removeLast()
        }
        self.nodes = endCappedIntersections
    }
    func forEachNode(callback: (_ node: Node, _ stop: inout Bool) -> Void) {
        var stop = false
        for node in self.nodes {
            callback(node, &stop)
            guard stop == false else { return }
        }
    }
}

private class PathGraph {
    let path: Path
    let components: [PathComponentGraph]
    init(for path: Path, using intersections: [Node]) {
        // first file each intersection by component index
        self.path = path
        let intersectionsByComponent = { () -> [[Node]] in
            var temp = [[Node]](repeating: [], count: path.components.count)
            intersections.forEach {
                temp[$0.location.componentIndex].append($0)
            }
            return temp
        }()
        self.components = (0..<path.components.count).map {
            PathComponentGraph(for: path.components[$0], componentIndex: $0, using: intersectionsByComponent[$0])
        }
    }
}

// TODO: revert public scope
public final class AugmentedGraph {
    private let operation: BooleanPathOperation
    private let graph1: PathGraph
    private let graph2: PathGraph
    public init(path1: Path, path2: Path, intersections: [PathIntersection], operation: BooleanPathOperation) {
        // take the pairwise intersections and make two mutually linked lists of intersections, one for each path
        self.operation = operation
        var path1Intersections: [Node] = []
        var path2Intersections: [Node] = []
        intersections.forEach {
            let node1 = Node(location: $0.indexedPathLocation1, pathComponent: path1.components[$0.indexedPathLocation1.componentIndex])
            let node2 = Node(location: $0.indexedPathLocation2, pathComponent: path2.components[$0.indexedPathLocation2.componentIndex])
            node1.addNeighbor(node2)
            node2.addNeighbor(node1)
            path1Intersections.append(node1)
