Skip to content

Commit c629316

Browse files
authored
Merge pull request #74 from ferranpujolcamins/recursive-init
Add recursion inits
2 parents 8882f7b + 9024eb8 commit c629316

File tree

3 files changed

+140
-1
lines changed

3 files changed

+140
-1
lines changed

Sources/SwiftGraph/Queue.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
// limitations under the License.
1818

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

@@ -52,7 +52,9 @@ public class Queue<T: Equatable> {
5252
public var count: Int {
5353
return container.count - head
5454
}
55+
}
5556

57+
extension Queue where T: Equatable {
5658
public func contains(_ thing: T) -> Bool {
5759
let content = container.dropFirst(head)
5860
if content.firstIndex(of: thing) != nil {

Sources/SwiftGraph/UniqueElementsGraph.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,60 @@ extension UniqueElementsGraph where E == UnweightedEdge {
132132
return g
133133
}
134134

135+
private struct QueueElement<V> {
136+
let v: V
137+
let previousIndex: Int
138+
}
139+
140+
/// Construct a UniqueElementsGraph by repeatedly applying a recursion function to a vertex and adding them to the graph.
141+
///
142+
/// The recursion function is only called on a vertex when visited for the first time.
143+
///
144+
/// - Parameter recursion: A function that returns the neighbouring vertices for a given visited vertex.
145+
/// - Parameter initialVertex: The first vertex to which the recursion function is applied.
146+
public static func fromRecursion(_ recursion: (V) -> [V], startingWith initialVertex: V) -> UniqueElementsGraph {
147+
return self.fromRecursion(recursion, selectingVertex: { $0 }, startingWith: initialVertex)
148+
}
149+
150+
/// Construct a UniqueElementsGraph by repeatedly applying a recursion function to some elements and adding the corresponding vertex to the graph.
151+
///
152+
/// The recursion function is only called on an element when visited for the first time.
153+
///
154+
/// - Parameter recursion: A function that returns the neighbouring elements for a given visited element.
155+
/// - Parameter vertexFor: A function that returns the vertex that will be added to the graph for each visited element.
156+
/// - Parameter initialElement: The first element to which the recursion function is applied.
157+
public static func fromRecursion<T>(_ recursion: (T) -> [T], selectingVertex vertexFor: (T) -> V, startingWith initialElement: T) -> UniqueElementsGraph {
158+
let g = UniqueElementsGraph(vertices: [])
159+
160+
let queue = Queue<QueueElement<T>>()
161+
162+
g.vertices.append(vertexFor(initialElement))
163+
g.edges.append([E]())
164+
recursion(initialElement).forEach { v in
165+
queue.push(QueueElement(v: v, previousIndex: 0))
166+
}
167+
168+
while !queue.isEmpty {
169+
let element = queue.pop()
170+
let (e, previousIndex) = (element.v, element.previousIndex)
171+
let u = vertexFor(e)
172+
let uIndex = g.indexOfVertex(u) ?? {
173+
g.vertices.append(u)
174+
g.edges.append([E]())
175+
176+
let uIndex = g.vertices.count - 1
177+
178+
recursion(e).forEach { v in
179+
queue.push(QueueElement(v: v, previousIndex: uIndex))
180+
}
181+
return uIndex
182+
}()
183+
184+
g.addEdge(fromIndex: previousIndex, toIndex: uIndex, directed: true)
185+
}
186+
187+
return g
188+
}
135189
}
136190

137191
extension UniqueElementsGraph where V: Hashable, E == UnweightedEdge {

Tests/SwiftGraphTests/UniqueElementsGraph/UniqueElementsGraphInitTests.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,5 +216,88 @@ class UnweightedUniqueElementsGraphInitTests: XCTestCase {
216216
XCTAssertTrue(g6Cycle.edgeExists(from: "Boston", to: "Eugene"), "g6Cycle: Expected an edge from Boston to Eugene")
217217
XCTAssertTrue(g6Cycle.edgeExists(from: "Eugene", to: "Atlanta"), "g6Cycle: Expected an edge from Eugene to Atlanta")
218218
}
219+
220+
func testRecursionInitializerWithDictionary() {
221+
let structure = [
222+
0: [1, 2, 3],
223+
1: [2, 3],
224+
2: [0, 1]
225+
]
226+
227+
func next(_ i: Int) -> [Int] {
228+
return structure[i] ?? []
229+
}
230+
231+
let g = UniqueElementsGraph.fromRecursion(next, startingWith: 0)
232+
XCTAssertTrue(g.edgeExists(from: 0, to: 1))
233+
XCTAssertTrue(g.edgeExists(from: 0, to: 2))
234+
XCTAssertTrue(g.edgeExists(from: 0, to: 3))
235+
236+
XCTAssertTrue(g.edgeExists(from: 1, to: 2))
237+
XCTAssertTrue(g.edgeExists(from: 1, to: 3))
238+
239+
XCTAssertTrue(g.edgeExists(from: 2, to: 0))
240+
XCTAssertTrue(g.edgeExists(from: 2, to: 1))
241+
242+
XCTAssertEqual(g.edgeCount, 7)
243+
XCTAssertEqual(g.vertexCount, 4)
244+
}
245+
246+
func testRecursionInitializerWithRecursiveEnum() {
247+
indirect enum Tree {
248+
case node(Int, Tree, Tree)
249+
case leaf(Int)
250+
}
251+
252+
func next(_ tree: Tree) -> [Tree] {
253+
switch tree {
254+
case .node(_, let lhs, let rhs):
255+
return [lhs, rhs]
256+
case .leaf:
257+
return []
258+
}
259+
}
260+
261+
func select(_ tree: Tree) -> Int {
262+
switch tree {
263+
case .node(let i, _, _),
264+
.leaf(let i):
265+
return i
266+
}
267+
}
268+
269+
let tree = Tree.node(
270+
0,
271+
.node(
272+
1,
273+
.leaf(2),
274+
.node(3, .leaf(4), .leaf(5))
275+
),
276+
.node(
277+
6,
278+
.node(7, .leaf(8), .leaf(9)),
279+
.leaf(10)
280+
)
281+
)
282+
283+
let g = UniqueElementsGraph.fromRecursion(next, selectingVertex: select, startingWith: tree)
284+
XCTAssertTrue(g.edgeExists(from: 0, to: 1))
285+
XCTAssertTrue(g.edgeExists(from: 0, to: 6))
286+
287+
XCTAssertTrue(g.edgeExists(from: 1, to: 2))
288+
XCTAssertTrue(g.edgeExists(from: 1, to: 3))
289+
290+
XCTAssertTrue(g.edgeExists(from: 3, to: 4))
291+
XCTAssertTrue(g.edgeExists(from: 3, to: 5))
292+
293+
XCTAssertTrue(g.edgeExists(from: 6, to: 7))
294+
XCTAssertTrue(g.edgeExists(from: 6, to: 10))
295+
296+
XCTAssertTrue(g.edgeExists(from: 7, to: 8))
297+
XCTAssertTrue(g.edgeExists(from: 7, to: 9))
298+
299+
XCTAssertEqual(g.edgeCount, 10)
300+
XCTAssertEqual(g.vertexCount, 11)
301+
}
219302
}
220303

0 commit comments

Comments
 (0)