Skip to content

Commit

Permalink
Add recursion inits
Browse files Browse the repository at this point in the history
  • Loading branch information
ferranpujolcamins committed Aug 22, 2019
1 parent 8882f7b commit 91f03f4
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
4 changes: 3 additions & 1 deletion Sources/SwiftGraph/Queue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// limitations under the License.

/// Implements a queue - helper class that uses an array internally.
public class Queue<T: Equatable> {
public class Queue<T> {
private var container = [T]()
private var head = 0

Expand Down Expand Up @@ -52,7 +52,9 @@ public class Queue<T: Equatable> {
public var count: Int {
return container.count - head
}
}

extension Queue where T: Equatable {
public func contains(_ thing: T) -> Bool {
let content = container.dropFirst(head)
if content.firstIndex(of: thing) != nil {
Expand Down
52 changes: 52 additions & 0 deletions Sources/SwiftGraph/UniqueElementsGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,58 @@ extension UniqueElementsGraph where E == UnweightedEdge {
return g
}

private struct QueueElement<V> {
let v: V
let previousIndex: Int
}

/// Construct a UniqueElementsGraph by repeatedly applying a recursion function to a vertex and adding them to the graph.
///
/// The recursion function is only called on a vertex when visited for the first time.
///
/// - Parameter recursion: A function that returns the neighbouring vertices for a given visited vertex.
/// - Parameter initialVertex: The first vertex to which the recursion function is applied.
public convenience init(fromRecursion recursion: (V) -> [V], startingWith initialVertex: V) {
self.init(fromRecursion: recursion, selectingVertex: { $0 }, startingWith: initialVertex)
}

/// Construct a UniqueElementsGraph by repeatedly applying a recursion function to some elements and adding the corresponding vertex to the graph.
///
/// The recursion function is only called on an element when visited for the first time.
///
/// - Parameter recursion: A function that returns the neighbouring elements for a given visited element.
/// - Parameter vertexFor: A function that returns the vertex that will be added to the graph for each visited element.
/// - Parameter initialElement: The first element to which the recursion function is applied.
public convenience init<T>(fromRecursion recursion: (T) -> [T], selectingVertex vertexFor: (T) -> V, startingWith initialElement: T) {
self.init()

let queue = Queue<QueueElement<T>>()

vertices.append(vertexFor(initialElement))
edges.append([E]())
recursion(initialElement).forEach { v in
queue.push(QueueElement(v: v, previousIndex: 0))
}

while !queue.isEmpty {
let element = queue.pop()
let (e, previousIndex) = (element.v, element.previousIndex)
let u = vertexFor(e)
let uIndex = indexOfVertex(u) ?? {
vertices.append(u)
edges.append([E]())

let uIndex = vertices.count - 1

recursion(e).forEach { v in
queue.push(QueueElement(v: v, previousIndex: uIndex))
}
return uIndex
}()

addEdge(fromIndex: previousIndex, toIndex: uIndex, directed: true)
}
}
}

extension UniqueElementsGraph where V: Hashable, E == UnweightedEdge {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,5 +216,88 @@ class UnweightedUniqueElementsGraphInitTests: XCTestCase {
XCTAssertTrue(g6Cycle.edgeExists(from: "Boston", to: "Eugene"), "g6Cycle: Expected an edge from Boston to Eugene")
XCTAssertTrue(g6Cycle.edgeExists(from: "Eugene", to: "Atlanta"), "g6Cycle: Expected an edge from Eugene to Atlanta")
}

func testRecursionInitializerWithDictionary() {
let structure = [
0: [1, 2, 3],
1: [2, 3],
2: [0, 1]
]

func next(_ i: Int) -> [Int] {
return structure[i] ?? []
}

let g = UniqueElementsGraph(fromRecursion: next, startingWith: 0)
XCTAssertTrue(g.edgeExists(from: 0, to: 1))
XCTAssertTrue(g.edgeExists(from: 0, to: 2))
XCTAssertTrue(g.edgeExists(from: 0, to: 3))

XCTAssertTrue(g.edgeExists(from: 1, to: 2))
XCTAssertTrue(g.edgeExists(from: 1, to: 3))

XCTAssertTrue(g.edgeExists(from: 2, to: 0))
XCTAssertTrue(g.edgeExists(from: 2, to: 1))

XCTAssertEqual(g.edgeCount, 7)
XCTAssertEqual(g.vertexCount, 4)
}

func testRecursionInitializerWithRecursiveEnum() {
indirect enum Tree {
case node(Int, Tree, Tree)
case leaf(Int)
}

func next(_ tree: Tree) -> [Tree] {
switch tree {
case .node(_, let lhs, let rhs):
return [lhs, rhs]
case .leaf:
return []
}
}

func select(_ tree: Tree) -> Int {
switch tree {
case .node(let i, _, _),
.leaf(let i):
return i
}
}

let tree = Tree.node(
0,
.node(
1,
.leaf(2),
.node(3, .leaf(4), .leaf(5))
),
.node(
6,
.node(7, .leaf(8), .leaf(9)),
.leaf(10)
)
)

let g = UniqueElementsGraph(fromRecursion: next, selectingVertex: select, startingWith: tree)
XCTAssertTrue(g.edgeExists(from: 0, to: 1))
XCTAssertTrue(g.edgeExists(from: 0, to: 6))

XCTAssertTrue(g.edgeExists(from: 1, to: 2))
XCTAssertTrue(g.edgeExists(from: 1, to: 3))

XCTAssertTrue(g.edgeExists(from: 3, to: 4))
XCTAssertTrue(g.edgeExists(from: 3, to: 5))

XCTAssertTrue(g.edgeExists(from: 6, to: 7))
XCTAssertTrue(g.edgeExists(from: 6, to: 10))

XCTAssertTrue(g.edgeExists(from: 7, to: 8))
XCTAssertTrue(g.edgeExists(from: 7, to: 9))

XCTAssertEqual(g.edgeCount, 10)
XCTAssertEqual(g.vertexCount, 11)
}
}

0 comments on commit 91f03f4

Please sign in to comment.