Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/SwiftGraph/Union.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public extension UniqueElementsGraph {

// We know vertices in lhs are unique, so we call Graph.addVertex to avoid the uniqueness check of UniqueElementsGraph.addVertex.
for vertex in firstGraph.vertices {
_ = super.addVertex(vertex)
_ = addVertex(vertex)
}

// When vertices are removed from Graph, edges might mutate,
Expand Down
138 changes: 129 additions & 9 deletions Sources/SwiftGraph/UniqueElementsGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
// limitations under the License.

/// A subclass of UnweightedGraph that ensures there are no pairs of equal vertices and no repeated edges.
open class UniqueElementsGraph<V: Equatable>: UnweightedGraph<V> {
open class UniqueElementsGraph<V: Equatable>: Graph {
public var vertices: [V] = [V]()
public var edges: [[UnweightedEdge]] = [[UnweightedEdge]]() //adjacency lists

public override init() {
super.init()
public init() {
}

/// Init the Graph with vertices, but removes duplicates. O(n^2)
public override init(vertices: [V]) {
super.init()
public init(vertices: [V]) {
for vertex in vertices {
_ = self.addVertex(vertex) // make sure to call our version
}
Expand All @@ -39,13 +39,15 @@ open class UniqueElementsGraph<V: Equatable>: UnweightedGraph<V> {
if let equalVertexIndex = indexOfVertex(v) {
return equalVertexIndex
}
return super.addVertex(v)
vertices.append(v)
edges.append([E]())
return vertices.count - 1
}

/// Only allow the edge to be added once
///
/// - parameter e: The edge to add.
public func addEdge(_ e: E) {
public func addEdge(_ e: UnweightedEdge) {
if !edgeExists(from: e.u, to: e.v) {
edges[e.u].append(e)
}
Expand All @@ -56,7 +58,7 @@ open class UniqueElementsGraph<V: Equatable>: UnweightedGraph<V> {
/// - parameter from: The starting vertex's index.
/// - parameter to: The ending vertex's index.
/// - parameter directed: Is the edge directed? (default `false`)
public override func addEdge(fromIndex u: Int, toIndex v: Int, directed: Bool = false) {
public func addEdge(fromIndex u: Int, toIndex v: Int, directed: Bool = false) {
if !edgeExists(from: u, to: v) {
addEdge(UnweightedEdge(u: u, v: v))
if !directed && !edgeExists(from: v, to: u) {
Expand All @@ -70,9 +72,127 @@ open class UniqueElementsGraph<V: Equatable>: UnweightedGraph<V> {
/// - parameter from: The starting vertex.
/// - parameter to: The ending vertex.
/// - parameter directed: Is the edge directed? (default `false`)
public override func addEdge(from: V, to: V, directed: Bool = false) {
public func addEdge(from: V, to: V, directed: Bool = false) {
if let u = indexOfVertex(from), let v = indexOfVertex(to) {
addEdge(fromIndex: u, toIndex: v, directed: directed)
}
}
}

extension UniqueElementsGraph {

private func addEdgesForPath(withIndices indices: [Int], directed: Bool) {
for i in 0..<indices.count - 1 {
addEdge(fromIndex: indices[i], toIndex: indices[i+1], directed: directed)
}
}

/// Initialize an UniqueElementsGraph consisting of path.
///
/// The resulting graph has the vertices in path and an edge between
/// each pair of consecutive vertices in path.
///
/// If path is an empty array, the resulting graph is the empty graph.
/// If path is an array with a single vertex, the resulting graph has that vertex and no edges.
///
/// - Parameters:
/// - path: An array of vertices representing a path.
/// - directed: If false, undirected edges are created.
/// If true, edges are directed from vertex i to vertex i+1 in path.
/// Default is false.
public convenience init(withPath path: [V], directed: Bool = false) {
self.init(vertices: path)

guard path.count >= 2 else {
if let v = path.first {
_ = addVertex(v)
}
return
}

let indices = path.map({ indexOfVertex($0)! })
addEdgesForPath(withIndices: indices, directed: directed)
}

/// Initialize an UniqueElementsGraph consisting of cycle.
///
/// The resulting graph has the vertices in cycle and an edge between
/// each pair of consecutive vertices in cycle,
/// plus an edge between the last and the first vertices.
///
/// If path is an empty array, the resulting graph is the empty graph.
/// If path is an array with a single vertex, the resulting graph has the vertex
/// and a single edge to itself if directed is true.
/// If directed is false the resulting graph has the vertex and two edges to itself.
///
/// - Parameters:
/// - cycle: An array of vertices representing a cycle.
/// - directed: If false, undirected edges are created.
/// If true, edges are directed from vertex i to vertex i+1 in cycle.
/// Default is false.
public convenience init(withCycle cycle: [V], directed: Bool = false) {
self.init(vertices: cycle)

guard cycle.count >= 2 else {
if let v = cycle.first {
let index = addVertex(v)
addEdge(fromIndex: index, toIndex: index)
}
return
}

let indices = cycle.map({ indexOfVertex($0)! })
addEdgesForPath(withIndices: indices, directed: directed)
addEdge(fromIndex: indices.last!, toIndex: indices.first!, directed: directed)
}

}

extension UniqueElementsGraph where V: Hashable {
public convenience init(withPath path: [V], directed: Bool = false) {
self.init()

guard path.count >= 2 else {
if let v = path.first {
_ = addVertex(v)
}
return
}

let indices = indicesForPath(path)
addEdgesForPath(withIndices: indices, directed: directed)
}


public convenience init(withCycle cycle: [V], directed: Bool = false) {
self.init()

guard cycle.count >= 2 else {
if let v = cycle.first {
let index = addVertex(v)
addEdge(fromIndex: index, toIndex: index)
}
return
}

let indices = indicesForPath(cycle)
addEdgesForPath(withIndices: indices, directed: directed)
addEdge(fromIndex: indices.last!, toIndex: indices.first!, directed: directed)
}

private func indicesForPath(_ path: [V]) -> [Int] {
var indices: [Int] = []
var indexForVertex: Dictionary<V, Int> = [:]

for v in path {
if let index = indexForVertex[v] {
indices.append(index)
} else {
let index = addVertex(v)
indices.append(index)
indexForVertex[v] = index
}
}
return indices
}
}
5 changes: 2 additions & 3 deletions Sources/SwiftGraph/UnweightedGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ open class UnweightedGraph<V: Equatable>: Graph {
}

for i in 0..<path.count - 1 {
let vertices = path[i...i+1]
self.addEdge(from: vertices.first!, to: vertices.last!, directed: directed)
self.addEdge(fromIndex: i, toIndex: i+1, directed: directed)
}
}

Expand All @@ -75,7 +74,7 @@ open class UnweightedGraph<V: Equatable>: Graph {
public convenience init(withCycle cycle: [V], directed: Bool = false) {
self.init(withPath: cycle, directed: directed)
if cycle.count > 0 {
self.addEdge(from: cycle.last!, to: cycle.first!, directed: directed)
self.addEdge(fromIndex: cycle.count-1, toIndex: 0, directed: directed)
}
}

Expand Down
28 changes: 24 additions & 4 deletions SwiftGraph.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
7985B92B1E5A503200C100E7 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5589B47A1C0E75A700D6664E /* Queue.swift */; };
B5100A4D208B97AA00C7A73A /* UnweightedGraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5100A4B208B97A800C7A73A /* UnweightedGraphTests.swift */; };
B51B460B2083E14200CD0463 /* Union.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51B460A2083E14200CD0463 /* Union.swift */; };
B523F2EA2094F0E2006587ED /* UniqueElementsGraphInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523F2E92094F0E2006587ED /* UniqueElementsGraphInitTests.swift */; };
B523F2EA2094F0E2006587ED /* UniqueElementsGraphHashableInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523F2E92094F0E2006587ED /* UniqueElementsGraphHashableInitTests.swift */; };
B52ABD39208955BD00FBF10C /* UnionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52ABD37208955B500FBF10C /* UnionTests.swift */; };
B54FDA6E21729EFF00057C51 /* Constructors.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54FDA6D21729EFF00057C51 /* Constructors.swift */; };
B54FDA712172A34D00057C51 /* ConstructorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54FDA702172A34D00057C51 /* ConstructorsTests.swift */; };
Expand All @@ -45,6 +45,9 @@
B5D229BF207BF3A800151820 /* UniqueElementsGraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D229BD207BF36900151820 /* UniqueElementsGraphTests.swift */; };
B5EACB1D2172315E00E527BD /* SwiftGraph.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7985B8FB1E5A4FB800C100E7 /* SwiftGraph.framework */; };
B5EACB292172336900E527BD /* SearchPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DD6DA42171EEE1007EFF44 /* SearchPerformanceTests.swift */; };
B5EF143121791009008FCC5C /* UniqueElementsGraphInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EF143021791009008FCC5C /* UniqueElementsGraphInitTests.swift */; };
B5EF143321791348008FCC5C /* UniqueElementsGraphHashableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EF143221791348008FCC5C /* UniqueElementsGraphHashableTests.swift */; };
B5EF1437217913F1008FCC5C /* EquatableTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EF1436217913F1008FCC5C /* EquatableTypes.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -118,7 +121,7 @@
7985B91C1E5A4FCB00C100E7 /* SwiftGraphTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftGraphTests.swift; sourceTree = "<group>"; };
B5100A4B208B97A800C7A73A /* UnweightedGraphTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnweightedGraphTests.swift; sourceTree = "<group>"; };
B51B460A2083E14200CD0463 /* Union.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Union.swift; path = ../Sources/SwiftGraph/Union.swift; sourceTree = "<group>"; };
B523F2E92094F0E2006587ED /* UniqueElementsGraphInitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueElementsGraphInitTests.swift; sourceTree = "<group>"; };
B523F2E92094F0E2006587ED /* UniqueElementsGraphHashableInitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueElementsGraphHashableInitTests.swift; sourceTree = "<group>"; };
B52ABD37208955B500FBF10C /* UnionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnionTests.swift; sourceTree = "<group>"; };
B54FDA6D21729EFF00057C51 /* Constructors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constructors.swift; path = ../Sources/SwiftGraph/Constructors.swift; sourceTree = "<group>"; };
B54FDA702172A34D00057C51 /* ConstructorsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstructorsTests.swift; sourceTree = "<group>"; };
Expand All @@ -127,6 +130,9 @@
B5D229BD207BF36900151820 /* UniqueElementsGraphTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueElementsGraphTests.swift; sourceTree = "<group>"; };
B5DD6DA42171EEE1007EFF44 /* SearchPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchPerformanceTests.swift; sourceTree = "<group>"; };
B5EACB222172315E00E527BD /* SwiftGraphPerformanceTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftGraphPerformanceTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B5EF143021791009008FCC5C /* UniqueElementsGraphInitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueElementsGraphInitTests.swift; sourceTree = "<group>"; };
B5EF143221791348008FCC5C /* UniqueElementsGraphHashableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueElementsGraphHashableTests.swift; sourceTree = "<group>"; };
B5EF1436217913F1008FCC5C /* EquatableTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EquatableTypes.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -260,6 +266,7 @@
7985B9091E5A4FB900C100E7 /* SwiftGraphTests */ = {
isa = PBXGroup;
children = (
B5EF1435217913E0008FCC5C /* Utils */,
B54FDA6F2172A30600057C51 /* Constructors */,
B523F2E82094F0A1006587ED /* UniqueElementsGraph */,
557F55E91F8AB247002AF0BF /* Info.plist */,
Expand All @@ -281,7 +288,9 @@
isa = PBXGroup;
children = (
B5D229BD207BF36900151820 /* UniqueElementsGraphTests.swift */,
B523F2E92094F0E2006587ED /* UniqueElementsGraphInitTests.swift */,
B5EF143221791348008FCC5C /* UniqueElementsGraphHashableTests.swift */,
B5EF143021791009008FCC5C /* UniqueElementsGraphInitTests.swift */,
B523F2E92094F0E2006587ED /* UniqueElementsGraphHashableInitTests.swift */,
);
path = UniqueElementsGraph;
sourceTree = "<group>";
Expand Down Expand Up @@ -312,6 +321,14 @@
path = Tests/SwiftGraphPerformanceTests;
sourceTree = "<group>";
};
B5EF1435217913E0008FCC5C /* Utils */ = {
isa = PBXGroup;
children = (
B5EF1436217913F1008FCC5C /* EquatableTypes.swift */,
);
path = Utils;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -522,10 +539,13 @@
B52ABD39208955BD00FBF10C /* UnionTests.swift in Sources */,
55E784281ED2971E003899D0 /* MSTTests.swift in Sources */,
B5100A4D208B97AA00C7A73A /* UnweightedGraphTests.swift in Sources */,
B5EF1437217913F1008FCC5C /* EquatableTypes.swift in Sources */,
7985B91D1E5A4FCB00C100E7 /* DijkstraGraphTests.swift in Sources */,
7985B91F1E5A4FCB00C100E7 /* SwiftGraphSortTests.swift in Sources */,
B5EF143321791348008FCC5C /* UniqueElementsGraphHashableTests.swift in Sources */,
55F5EA5B2151E33100DFC301 /* SwiftGraphCodableTests.swift in Sources */,
B523F2EA2094F0E2006587ED /* UniqueElementsGraphInitTests.swift in Sources */,
B523F2EA2094F0E2006587ED /* UniqueElementsGraphHashableInitTests.swift in Sources */,
B5EF143121791009008FCC5C /* UniqueElementsGraphInitTests.swift in Sources */,
B54FDA712172A34D00057C51 /* ConstructorsTests.swift in Sources */,
B5D229BF207BF3A800151820 /* UniqueElementsGraphTests.swift in Sources */,
);
Expand Down
Loading