Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit dd3ba8d

Browse files
committedJun 5, 2016
add project for SSSP with Bellman-Ford implementation
1 parent 495303b commit dd3ba8d

File tree

15 files changed

+1059
-4
lines changed

15 files changed

+1059
-4
lines changed
 

‎.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,26 @@ osx_image: xcode7.3
33

44
script:
55

6+
- xcodebuild test -project ./All-Pairs\ Shortest\ Paths/APSP/APSP.xcodeproj -scheme APSPTests
67
- xcodebuild test -project ./Array2D/Tests/Tests.xcodeproj -scheme Tests
78
- xcodebuild test -project ./AVL\ Tree/Tests/Tests.xcodeproj -scheme Tests
89
- xcodebuild test -project ./Binary\ Search/Tests/Tests.xcodeproj -scheme Tests
910
- xcodebuild test -project ./Binary\ Search\ Tree/Solution\ 1/Tests/Tests.xcodeproj -scheme Tests
1011
- xcodebuild test -project ./Bloom\ Filter/Tests/Tests.xcodeproj -scheme Tests
1112
- xcodebuild test -project ./Breadth-First\ Search/Tests/Tests.xcodeproj -scheme Tests
13+
- xcodebuild test -project ./Bucket\ Sort/Tests/Tests.xcodeproj -scheme Tests
1214
- xcodebuild test -project ./Heap/Tests/Tests.xcodeproj -scheme Tests
1315
- xcodebuild test -project ./Heap\ Sort/Tests/Tests.xcodeproj -scheme Tests
1416
- xcodebuild test -project ./Insertion\ Sort/Tests/Tests.xcodeproj -scheme Tests
1517
- xcodebuild test -project ./K-Means/Tests/Tests.xcodeproj -scheme Tests
1618
- xcodebuild test -project ./Linked\ List/Tests/Tests.xcodeproj -scheme Tests
19+
- xcodebuild test -project ./Longest\ Common\ Subsequence/Tests/Tests.xcodeproj -scheme Tests
1720
- xcodebuild test -project ./Priority\ Queue/Tests/Tests.xcodeproj -scheme Tests
1821
- xcodebuild test -project ./Queue/Tests/Tests.xcodeproj -scheme Tests
1922
- xcodebuild test -project ./Quicksort/Tests/Tests.xcodeproj -scheme Tests
2023
- xcodebuild test -project ./Run-Length\ Encoding/Tests/Tests.xcodeproj -scheme Tests
2124
- xcodebuild test -project ./Select\ Minimum\ Maximum/Tests/Tests.xcodeproj -scheme Tests
2225
- xcodebuild test -project ./Selection\ Sort/Tests/Tests.xcodeproj -scheme Tests
2326
- xcodebuild test -project ./Shell\ Sort/Tests/Tests.xcodeproj -scheme Tests
24-
- xcodebuild test -project ./Stack/Tests/Tests.xcodeproj -scheme Tests
25-
- xcodebuild test -project ./Longest\ Common\ Subsequence/Tests/Tests.xcodeproj -scheme Tests
26-
- xcodebuild test -project ./Bucket\ Sort/Tests/Tests.xcodeproj -scheme Tests
27-
- xcodebuild test -project ./All-Pairs\ Shortest\ Paths/APSP/APSP.xcodeproj -scheme APSPTests
27+
- xcodebuild test -project ./Single-Source\ Shortest\ Paths\ \(Weighted\)/SSSP.xcodeproj -scheme SSSPTests
28+
- xcodebuild test -project ./Stack/Tests/Tests.xcodeproj -scheme Tests
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Graph
2+
import SSSP
3+
4+
let graph = AdjacencyMatrixGraph<String>()
5+
let s = graph.createVertex("s")
6+
let t = graph.createVertex("t")
7+
let x = graph.createVertex("x")
8+
let y = graph.createVertex("y")
9+
let z = graph.createVertex("z")
10+
11+
graph.addDirectedEdge(s, to: t, withWeight: 6)
12+
graph.addDirectedEdge(s, to: y, withWeight: 7)
13+
14+
graph.addDirectedEdge(t, to: x, withWeight: 5)
15+
graph.addDirectedEdge(t, to: y, withWeight: 8)
16+
graph.addDirectedEdge(t, to: z, withWeight: -4)
17+
18+
graph.addDirectedEdge(x, to: t, withWeight: -2)
19+
20+
graph.addDirectedEdge(y, to: x, withWeight: -3)
21+
graph.addDirectedEdge(y, to: z, withWeight: 9)
22+
23+
graph.addDirectedEdge(z, to: s, withWeight: 2)
24+
graph.addDirectedEdge(z, to: x, withWeight: 7)
25+
26+
let result = BellmanFord<String>.apply(graph, source: s)!
27+
28+
let path = result.path(z, inGraph: graph)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='osx'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

‎Single-Source Shortest Paths (Weighted)/SSSP.playground/playground.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Single-Source Shortest Paths (Weighted)/SSSP.xcodeproj/project.pbxproj

Lines changed: 472 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "0730"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "49BFA2921CDF86E100522D66"
18+
BuildableName = "SSSP.framework"
19+
BlueprintName = "SSSP"
20+
ReferencedContainer = "container:SSSP.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
<TestableReference
32+
skipped = "NO">
33+
<BuildableReference
34+
BuildableIdentifier = "primary"
35+
BlueprintIdentifier = "49BFA29C1CDF86E100522D66"
36+
BuildableName = "SSSPTests.xctest"
37+
BlueprintName = "SSSPTests"
38+
ReferencedContainer = "container:SSSP.xcodeproj">
39+
</BuildableReference>
40+
</TestableReference>
41+
</Testables>
42+
<MacroExpansion>
43+
<BuildableReference
44+
BuildableIdentifier = "primary"
45+
BlueprintIdentifier = "49BFA2921CDF86E100522D66"
46+
BuildableName = "SSSP.framework"
47+
BlueprintName = "SSSP"
48+
ReferencedContainer = "container:SSSP.xcodeproj">
49+
</BuildableReference>
50+
</MacroExpansion>
51+
<AdditionalOptions>
52+
</AdditionalOptions>
53+
</TestAction>
54+
<LaunchAction
55+
buildConfiguration = "Debug"
56+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
57+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
58+
launchStyle = "0"
59+
useCustomWorkingDirectory = "NO"
60+
ignoresPersistentStateOnLaunch = "NO"
61+
debugDocumentVersioning = "YES"
62+
debugServiceExtension = "internal"
63+
allowLocationSimulation = "YES">
64+
<MacroExpansion>
65+
<BuildableReference
66+
BuildableIdentifier = "primary"
67+
BlueprintIdentifier = "49BFA2921CDF86E100522D66"
68+
BuildableName = "SSSP.framework"
69+
BlueprintName = "SSSP"
70+
ReferencedContainer = "container:SSSP.xcodeproj">
71+
</BuildableReference>
72+
</MacroExpansion>
73+
<AdditionalOptions>
74+
</AdditionalOptions>
75+
</LaunchAction>
76+
<ProfileAction
77+
buildConfiguration = "Release"
78+
shouldUseLaunchSchemeArgsEnv = "YES"
79+
savedToolIdentifier = ""
80+
useCustomWorkingDirectory = "NO"
81+
debugDocumentVersioning = "YES">
82+
<MacroExpansion>
83+
<BuildableReference
84+
BuildableIdentifier = "primary"
85+
BlueprintIdentifier = "49BFA2921CDF86E100522D66"
86+
BuildableName = "SSSP.framework"
87+
BlueprintName = "SSSP"
88+
ReferencedContainer = "container:SSSP.xcodeproj">
89+
</BuildableReference>
90+
</MacroExpansion>
91+
</ProfileAction>
92+
<AnalyzeAction
93+
buildConfiguration = "Debug">
94+
</AnalyzeAction>
95+
<ArchiveAction
96+
buildConfiguration = "Release"
97+
revealArchiveInOrganizer = "YES">
98+
</ArchiveAction>
99+
</Scheme>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "0730"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
</BuildAction>
9+
<TestAction
10+
buildConfiguration = "Debug"
11+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
12+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
13+
shouldUseLaunchSchemeArgsEnv = "YES">
14+
<Testables>
15+
<TestableReference
16+
skipped = "NO">
17+
<BuildableReference
18+
BuildableIdentifier = "primary"
19+
BlueprintIdentifier = "49BFA29C1CDF86E100522D66"
20+
BuildableName = "SSSPTests.xctest"
21+
BlueprintName = "SSSPTests"
22+
ReferencedContainer = "container:SSSP.xcodeproj">
23+
</BuildableReference>
24+
</TestableReference>
25+
</Testables>
26+
<AdditionalOptions>
27+
</AdditionalOptions>
28+
</TestAction>
29+
<LaunchAction
30+
buildConfiguration = "Debug"
31+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
32+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
33+
launchStyle = "0"
34+
useCustomWorkingDirectory = "NO"
35+
ignoresPersistentStateOnLaunch = "NO"
36+
debugDocumentVersioning = "YES"
37+
debugServiceExtension = "internal"
38+
allowLocationSimulation = "YES">
39+
<AdditionalOptions>
40+
</AdditionalOptions>
41+
</LaunchAction>
42+
<ProfileAction
43+
buildConfiguration = "Release"
44+
shouldUseLaunchSchemeArgsEnv = "YES"
45+
savedToolIdentifier = ""
46+
useCustomWorkingDirectory = "NO"
47+
debugDocumentVersioning = "YES">
48+
</ProfileAction>
49+
<AnalyzeAction
50+
buildConfiguration = "Debug">
51+
</AnalyzeAction>
52+
<ArchiveAction
53+
buildConfiguration = "Release"
54+
revealArchiveInOrganizer = "YES">
55+
</ArchiveAction>
56+
</Scheme>

‎Single-Source Shortest Paths (Weighted)/SSSP.xcworkspace/contents.xcworkspacedata

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
6+
<false/>
7+
</dict>
8+
</plist>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//
2+
// BellmanFord.swift
3+
// SSSP
4+
//
5+
// Created by Andrew McKnight on 5/8/16.
6+
//
7+
8+
import Foundation
9+
import Graph
10+
11+
public struct BellmanFord<T where T: Hashable> {
12+
public typealias Q = T
13+
}
14+
15+
/**
16+
Encapsulation of the Bellman-Ford Single-Source Shortest Paths algorithm, which operates on a general directed graph that may contain negative edge weights.
17+
18+
- note: In all complexity bounds, `V` is the number of vertices in the graph, and `E` is the number of edges.
19+
*/
20+
extension BellmanFord: SSSPAlgorithm {
21+
22+
/**
23+
Compute the shortest path from `source` to each other vertex in `graph`, if such paths exist. Also report negative weight cycles reachable from `source`, which are cycles whose sum of edge weights is negative.
24+
25+
- precondition: `graph` must have no negative weight cycles
26+
- complexity: `O(VE)` time, `Θ(V)` space
27+
- returns a `BellmanFordResult` struct which can be queried for shortest paths and their total weights, or `nil` if a negative weight cycle exists
28+
*/
29+
public static func apply(graph: AbstractGraph<T>, source: Vertex<Q>) -> BellmanFordResult<T>? {
30+
let vertices = graph.vertices
31+
let edges = graph.edges
32+
33+
var predecessors = Array<Int?>(count: vertices.count, repeatedValue: nil)
34+
var weights = Array(count: vertices.count, repeatedValue: Double.infinity)
35+
predecessors[source.index] = source.index
36+
weights[source.index] = 0
37+
38+
for _ in 0 ..< vertices.count - 1 {
39+
var weightsUpdated = false
40+
edges.forEach() { edge in
41+
let weight = edge.weight!
42+
let relaxedDistance = weights[edge.from.index] + weight
43+
let nextVertexIdx = edge.to.index
44+
if relaxedDistance < weights[nextVertexIdx] {
45+
predecessors[nextVertexIdx] = edge.from.index
46+
weights[nextVertexIdx] = relaxedDistance
47+
weightsUpdated = true
48+
}
49+
}
50+
if !weightsUpdated {
51+
break
52+
}
53+
}
54+
55+
// check for negative weight cycles reachable from the source vertex
56+
// TODO: modify to incorporate solution to 24.1-4, pg 654, to set the weight of a path containing a negative weight cycle to -∞, instead of returning nil for the entire result
57+
for edge in edges {
58+
if weights[edge.to.index] > weights[edge.from.index] + edge.weight! {
59+
return nil
60+
}
61+
}
62+
63+
return BellmanFordResult(predecessors: predecessors, weights: weights)
64+
}
65+
66+
}
67+
68+
/**
69+
`BellmanFordResult` encapsulates the result of the computation, namely the minimized distances, and the predecessor indices.
70+
71+
It conforms to the `SSSPResult` procotol which provides methods to retrieve distances and paths between given pairs of start and end nodes.
72+
*/
73+
public struct BellmanFordResult<T where T: Hashable> {
74+
75+
private var predecessors: [Int?]
76+
private var weights: [Double]
77+
78+
}
79+
80+
extension BellmanFordResult: SSSPResult {
81+
82+
/**
83+
- returns: the total weight of the path from the source vertex to a destination. This value is the minimal connected weight between the two vertices, or `nil` if no path exists
84+
- complexity: `Θ(1)` time/space
85+
*/
86+
public func distance(to: Vertex<T>) -> Double? {
87+
let distance = weights[to.index]
88+
89+
guard distance != Double.infinity else {
90+
return nil
91+
}
92+
93+
return distance
94+
}
95+
96+
/**
97+
- returns: the reconstructed path from the source vertex to a destination, as an array containing the data property of each vertex, or `nil` if no path exists
98+
- complexity: `Θ(V)` time, `Θ(V^2)` space
99+
*/
100+
public func path(to: Vertex<T>, inGraph graph: AbstractGraph<T>) -> [T]? {
101+
guard weights[to.index] != Double.infinity else {
102+
return nil
103+
}
104+
105+
guard let path = recursePath(to, inGraph: graph, path: [to]) else {
106+
return nil
107+
}
108+
109+
return path.map() { vertex in
110+
return vertex.data
111+
}
112+
}
113+
114+
/**
115+
The recursive component to rebuilding the shortest path between two vertices using predecessors.
116+
117+
- returns: the list of predecessors discovered so far, or `nil` if the next vertex has no predecessor
118+
*/
119+
private func recursePath(to: Vertex<T>, inGraph graph: AbstractGraph<T>, path: [Vertex<T>]) -> [Vertex<T>]? {
120+
guard let predecessorIdx = predecessors[to.index] else {
121+
return nil
122+
}
123+
124+
let predecessor = graph.vertices[predecessorIdx]
125+
if predecessor.index == to.index {
126+
return [ to ]
127+
}
128+
129+
guard let buildPath = recursePath(predecessor, inGraph: graph, path: path) else {
130+
return nil
131+
}
132+
133+
return buildPath + [ to ]
134+
}
135+
136+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>FMWK</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
19+
<key>CFBundleSignature</key>
20+
<string>????</string>
21+
<key>CFBundleVersion</key>
22+
<string>$(CURRENT_PROJECT_VERSION)</string>
23+
<key>NSPrincipalClass</key>
24+
<string></string>
25+
</dict>
26+
</plist>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// SSSP.h
3+
// SSSP
4+
//
5+
// Created by Andrew McKnight on 5/8/16.
6+
//
7+
8+
#import <Cocoa/Cocoa.h>
9+
10+
//! Project version number for SSSP.
11+
FOUNDATION_EXPORT double SSSPVersionNumber;
12+
13+
//! Project version string for SSSP.
14+
FOUNDATION_EXPORT const unsigned char SSSPVersionString[];
15+
16+
// In this header, you should import all the public headers of your framework using statements like #import <SSSP/PublicHeader.h>
17+
18+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// SSSP.swift
3+
// SSSP
4+
//
5+
// Created by Andrew McKnight on 5/8/16.
6+
//
7+
8+
import Foundation
9+
import Graph
10+
11+
/**
12+
`SSSPAlgorithm` is a protocol for encapsulating a Single-Source Shortest Path algorithm. It provides a single function `apply` that accepts a subclass of `AbstractGraph` and returns an object conforming to `SSSPResult`.
13+
*/
14+
protocol SSSPAlgorithm {
15+
16+
associatedtype Q: Equatable, Hashable
17+
associatedtype P: SSSPResult
18+
19+
static func apply(graph: AbstractGraph<Q>, source: Vertex<Q>) -> P?
20+
21+
}
22+
23+
/**
24+
`SSSPResult` is a protocol defining functions `distance` and `path`, allowing for opaque queries into the actual data structures that represent the SSSP solution according to the algorithm used.
25+
*/
26+
protocol SSSPResult {
27+
28+
associatedtype T: Equatable, Hashable
29+
30+
func distance(to: Vertex<T>) -> Double?
31+
func path(to: Vertex<T>, inGraph graph: AbstractGraph<T>) -> [T]?
32+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>BNDL</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
19+
<key>CFBundleSignature</key>
20+
<string>????</string>
21+
<key>CFBundleVersion</key>
22+
<string>1</string>
23+
</dict>
24+
</plist>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//
2+
// SSSPTests.swift
3+
// SSSPTests
4+
//
5+
// Created by Andrew McKnight on 5/8/16.
6+
//
7+
8+
import Graph
9+
import XCTest
10+
@testable import SSSP
11+
12+
class SSSPTests: XCTestCase {
13+
14+
/**
15+
See Figure 24.4 of “Introduction to Algorithms” by Cormen et al, 3rd ed., pg 652
16+
*/
17+
func testExampleFromBook() {
18+
let graph = AdjacencyMatrixGraph<String>()
19+
let s = graph.createVertex("s")
20+
let t = graph.createVertex("t")
21+
let x = graph.createVertex("x")
22+
let y = graph.createVertex("y")
23+
let z = graph.createVertex("z")
24+
25+
graph.addDirectedEdge(s, to: t, withWeight: 6)
26+
graph.addDirectedEdge(s, to: y, withWeight: 7)
27+
28+
graph.addDirectedEdge(t, to: x, withWeight: 5)
29+
graph.addDirectedEdge(t, to: y, withWeight: 8)
30+
graph.addDirectedEdge(t, to: z, withWeight: -4)
31+
32+
graph.addDirectedEdge(x, to: t, withWeight: -2)
33+
34+
graph.addDirectedEdge(y, to: x, withWeight: -3)
35+
graph.addDirectedEdge(y, to: z, withWeight: 9)
36+
37+
graph.addDirectedEdge(z, to: s, withWeight: 2)
38+
graph.addDirectedEdge(z, to: x, withWeight: 7)
39+
40+
let result = BellmanFord<String>.apply(graph, source: s)!
41+
42+
let expectedPath = ["s", "y", "x", "t", "z"]
43+
let computedPath = result.path(z, inGraph: graph)!
44+
XCTAssertEqual(expectedPath, computedPath, "expected path of \(expectedPath) but got \(computedPath)")
45+
46+
let expectedWeight = -2.0
47+
let computedWeight = result.distance(z)
48+
XCTAssertEqual(expectedWeight, computedWeight, "expected weight of \(expectedWeight) but got \(computedWeight)")
49+
}
50+
51+
func testSimpleExample() {
52+
let graph = AdjacencyMatrixGraph<String>()
53+
let a = graph.createVertex("a")
54+
let b = graph.createVertex("b")
55+
let c = graph.createVertex("c")
56+
let d = graph.createVertex("d")
57+
let e = graph.createVertex("e")
58+
59+
graph.addDirectedEdge(a, to: b, withWeight: 4)
60+
graph.addDirectedEdge(a, to: c, withWeight: 5)
61+
graph.addDirectedEdge(a, to: d, withWeight: 8)
62+
63+
graph.addDirectedEdge(b, to: c, withWeight: -2)
64+
65+
graph.addDirectedEdge(c, to: e, withWeight: 4)
66+
67+
graph.addDirectedEdge(d, to: e, withWeight: 2)
68+
69+
graph.addDirectedEdge(e, to: d, withWeight: 1)
70+
71+
let result = BellmanFord<String>.apply(graph, source: a)!
72+
73+
let expectedPath = ["a", "b", "c", "e", "d"]
74+
let computedPath = result.path(d, inGraph: graph)!
75+
XCTAssertEqual(expectedPath, computedPath, "expected path of \(expectedPath) but got \(computedPath)")
76+
77+
let expectedWeight = 7.0
78+
let computedWeight = result.distance(d)
79+
XCTAssertEqual(expectedWeight, computedWeight, "expected weight of \(expectedWeight) but got \(computedWeight)")
80+
}
81+
82+
/**
83+
Construct a nearly bipartite graph, except one vertex only has out-edges. Then no path should exist from the source to that vertex.
84+
*/
85+
func testGraphWithUnreachableVertex() {
86+
let graph = AdjacencyMatrixGraph<String>()
87+
let a = graph.createVertex("a")
88+
let b = graph.createVertex("b")
89+
let s = graph.createVertex("s")
90+
let t = graph.createVertex("t")
91+
92+
graph.addDirectedEdge(a, to: s, withWeight: 9)
93+
graph.addDirectedEdge(a, to: t, withWeight: 2)
94+
95+
graph.addDirectedEdge(b, to: a, withWeight: 1)
96+
graph.addDirectedEdge(b, to: s, withWeight: 2)
97+
graph.addDirectedEdge(b, to: t, withWeight: 3)
98+
99+
graph.addDirectedEdge(s, to: a, withWeight: 4)
100+
graph.addDirectedEdge(s, to: t, withWeight: 1)
101+
102+
graph.addDirectedEdge(t, to: a, withWeight: 7)
103+
graph.addDirectedEdge(t, to: s, withWeight: 5)
104+
105+
let result = BellmanFord<String>.apply(graph, source: a)!
106+
107+
XCTAssertNil(result.path(b, inGraph: graph), "a path should not be defined from a ~> b")
108+
XCTAssertNil(result.distance(b), "a path should not be defined from a ~> b")
109+
XCTAssertNotNil(result.path(s, inGraph: graph), "a path should be defined from a ~> s")
110+
XCTAssertNotNil(result.distance(s), "a path should be defined from a ~> s")
111+
XCTAssertNotNil(result.path(t, inGraph: graph), "a path should be defined from a ~> t")
112+
XCTAssertNotNil(result.distance(t), "a path should be defined from a ~> t")
113+
}
114+
115+
func testNegativeWeightCycle() {
116+
let graph = AdjacencyMatrixGraph<String>()
117+
let a = graph.createVertex("a")
118+
let b = graph.createVertex("b")
119+
let s = graph.createVertex("s")
120+
let t = graph.createVertex("t")
121+
122+
graph.addDirectedEdge(a, to: t, withWeight: 2)
123+
124+
graph.addDirectedEdge(b, to: t, withWeight: 5)
125+
126+
graph.addDirectedEdge(s, to: a, withWeight: 4)
127+
graph.addDirectedEdge(s, to: b, withWeight: 4)
128+
129+
graph.addDirectedEdge(t, to: s, withWeight: -15)
130+
131+
XCTAssertNil(BellmanFord<String>.apply(graph, source: s), "negative weight cycle not reported")
132+
}
133+
134+
}

0 commit comments

Comments
 (0)
Please sign in to comment.