Skip to content

Commit 2dd1fad

Browse files
authored
Pull down Graph from dn-m/NotationModel/SpelledPitch (#173)
1 parent 655a419 commit 2dd1fad

26 files changed

+1086
-21
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ os:
44
language: generic
55
sudo: required
66
dist: trusty
7-
osx_image: xcode9
7+
osx_image: xcode10
88
install:
99
- eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
1010
env:
11-
- SWIFT_VERSION=DEVELOPMENT-SNAPSHOT-2018-05-30-a
11+
- SWIFT_VERSION=4.2
1212
script:
1313
- swift package update
1414
- swift test
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// DirectedGraph.swift
3+
// DataStructures
4+
//
5+
// Created by James Bean on 9/27/18.
6+
//
7+
8+
/// Unweighted, directed graph.
9+
public struct DirectedGraph <Node: Hashable>: UnweightedGraphProtocol, DirectedGraphProtocol {
10+
11+
// MARK: - Instance Properties
12+
13+
/// All of the nodes contained herein.
14+
///
15+
/// A `Node` is any `Hashable`-conforming value.
16+
public var nodes: Set<Node>
17+
18+
/// All of the edges contained herein.
19+
///
20+
/// An `Edge` is an `OrderedPair` of `Node` values.
21+
public var edges: Set<Edge>
22+
}
23+
24+
extension DirectedGraph {
25+
26+
// MARK: - Type Aliases
27+
28+
/// The type of edges which connect nodes.
29+
public typealias Edge = OrderedPair<Node>
30+
}
31+
32+
extension DirectedGraph {
33+
34+
// MARK: - Initializers
35+
36+
/// Creates a `DirectedGraph` with the given set of nodes, with no edges between the nodes.
37+
@inlinable
38+
public init(_ nodes: Set<Node> = []) {
39+
self.nodes = nodes
40+
self.edges = []
41+
}
42+
43+
/// Creates a `DirectedGraph` with the given set of nodes and the given set of edges connecting the
44+
/// nodes.
45+
@inlinable
46+
public init(_ nodes: Set<Node> = [], _ edges: Set<Edge> = []) {
47+
self.init(nodes)
48+
self.edges = edges
49+
}
50+
51+
/// Creates a `DirectedGraph` which is composed a path of nodes.
52+
@inlinable
53+
public init <C> (path: C) where C: Collection, C.Element == Node {
54+
self.nodes = Set(path)
55+
self.edges = Set(nodes.pairs.map(OrderedPair.init))
56+
}
57+
}
58+
59+
extension DirectedGraph: Equatable { }
60+
extension DirectedGraph: Hashable { }
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// Graph.swift
3+
// DataStructures
4+
//
5+
// Created by James Bean on 9/27/18.
6+
//
7+
8+
/// Unweighted, undirected graph.
9+
public struct Graph <Node: Hashable>: UnweightedGraphProtocol, UndirectedGraphProtocol {
10+
11+
// MARK: - Instance Properties
12+
13+
/// All of the nodes contained herein.
14+
///
15+
/// A `Node` is any `Hashable`-conforming value.
16+
public var nodes: Set<Node>
17+
18+
/// All of the edges contained herein.
19+
///
20+
/// An `Edge` is an `UnorderedPair` of `Node` values.
21+
public var edges: Set<Edge>
22+
}
23+
24+
extension Graph {
25+
26+
// MARK: - Type Aliases
27+
28+
/// The type of edges which connect nodes.
29+
public typealias Edge = UnorderedPair<Node>
30+
}
31+
32+
extension Graph {
33+
34+
// MARK: - Initializers
35+
36+
/// Creates a `Graph` with the given set of nodes, with no edges between the nodes.
37+
@inlinable
38+
public init(_ nodes: Set<Node> = []) {
39+
self.nodes = nodes
40+
self.edges = []
41+
}
42+
43+
/// Creates a `Graph` with the given set of nodes and the given set of edges connecting the
44+
/// nodes.
45+
@inlinable
46+
public init(_ nodes: Set<Node> = [], _ edges: Set<Edge> = []) {
47+
self.init(nodes)
48+
self.edges = edges
49+
}
50+
}
51+
52+
extension Graph: Equatable { }
53+
extension Graph: Hashable { }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//
2+
// DirectedGraphProtocol.swift
3+
// DataStructures
4+
//
5+
// Created by James Bean on 9/27/18.
6+
//
7+
8+
/// Interface for directed graphs.
9+
public protocol DirectedGraphProtocol: GraphProtocol where Edge == OrderedPair<Node> { }
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//
2+
// GraphProtocol.swift
3+
// DataStructures
4+
//
5+
// Created by James Bean on 9/27/18.
6+
//
7+
8+
/// Interface for graph-like tyes.
9+
public protocol GraphProtocol {
10+
11+
// MARK: - Associated Types
12+
13+
/// The type of nodes contained herein.
14+
associatedtype Node: Hashable
15+
16+
/// The type of edges contained herein.
17+
associatedtype Edge: SymmetricPair & Hashable where Edge.A == Node
18+
19+
// MARK: - Instance Properties
20+
21+
/// All of the nodes contained herein.
22+
var nodes: Set<Node> { get set }
23+
24+
/// All of the edges contained herein.
25+
var edges: Set<Edge> { get }
26+
27+
// MARK: - Initializers
28+
29+
/// Creates a `GraphProtocol`-conforming type value with the given set of `nodes`.
30+
init(_ nodes: Set<Node>)
31+
32+
// MARK: - Instance Methods
33+
34+
/// Removes the edge from the given `source` to the given `destination`.
35+
mutating func removeEdge(from source: Node, to destination: Node)
36+
}
37+
38+
extension GraphProtocol {
39+
40+
/// Inserts the given `node` without making any connections to other nodes contained herein.
41+
@inlinable
42+
public mutating func insert(_ node: Node) {
43+
nodes.insert(node)
44+
}
45+
46+
/// - Returns: `true` if the `GraphProtocol`-conforming type value contains the given `node`.
47+
/// Otherwise, `false`.
48+
@inlinable
49+
public func contains(_ node: Node) -> Bool {
50+
return nodes.contains(node)
51+
}
52+
53+
/// - Returns: `true` if this graph contains an edge between given `source` and `destination`.
54+
/// Otherwise, `false`.
55+
@inlinable
56+
public func containsEdge(from source: Node, to destination: Node) -> Bool {
57+
return contains(Edge(source,destination))
58+
}
59+
60+
/// Returns: `true` if the `GraphProtocol`-conforming type value contains the given `edge`.
61+
/// Otherwise, `false`.
62+
@inlinable
63+
public func contains(_ edge: Edge) -> Bool {
64+
return edges.contains(edge)
65+
}
66+
67+
/// - Returns: A set of nodes connected to the given `source`, in the given set of
68+
/// `nodes`.
69+
///
70+
/// If `nodes` is empty, then any nodes contained herein are able to be included in the
71+
/// resultant set.
72+
@inlinable
73+
public func neighbors(of source: Node, in nodes: Set<Node>? = nil) -> Set<Node> {
74+
return (nodes ?? self.nodes).filter { edges.contains(Edge(source,$0)) }
75+
}
76+
77+
/// - Returns: A set of edges outgoing from the given `source`.
78+
@inlinable
79+
public func edges(from source: Node) -> Set<Edge> {
80+
return Set(neighbors(of: source).map { Edge(source, $0) })
81+
}
82+
83+
/// - Returns: A set of edges incident to the given `destination`.
84+
@inlinable
85+
public func edges(to destination: Node) -> Set<Edge> {
86+
return Set(nodes.lazy.map { Edge($0, destination) }.filter(edges.contains))
87+
}
88+
89+
/// - Returns: An array of `Node` values in breadth first order.
90+
@inlinable
91+
public func breadthFirstSearch(from source: Node, to destination: Node? = nil) -> [Node] {
92+
var visited: [Node] = []
93+
var queue = Queue<Node>()
94+
queue.enqueue(source)
95+
visited.append(source)
96+
while !queue.isEmpty {
97+
let node = queue.dequeue()
98+
for neighbor in neighbors(of: node) where !visited.contains(neighbor) {
99+
queue.enqueue(neighbor)
100+
visited.append(neighbor)
101+
if neighbor == destination { return visited }
102+
}
103+
}
104+
return visited
105+
}
106+
107+
/// - Returns: An array of `Node` values from the given `source` to the given `destination`.
108+
///
109+
/// In the case that there are more than one paths from the given `source` to the given
110+
/// `destination`, the path with the fewest edges is returned.
111+
@inlinable
112+
public func shortestUnweightedPath(from source: Node, to destination: Node) -> [Node]? {
113+
var breadcrumbs: [Node: Node] = [:]
114+
func backtrace() -> [Node] {
115+
var path = [destination]
116+
var cursor = destination
117+
while cursor != source {
118+
path.insert(breadcrumbs[cursor]!, at: 0)
119+
cursor = breadcrumbs[cursor]!
120+
}
121+
return path
122+
}
123+
if source == destination { return .init([source]) }
124+
var unvisited = nodes
125+
var queue = Queue<Node>()
126+
queue.enqueue(source)
127+
while !queue.isEmpty {
128+
let node = queue.dequeue()
129+
for neighbor in neighbors(of: node, in: unvisited) {
130+
queue.enqueue(neighbor)
131+
unvisited.remove(neighbor)
132+
breadcrumbs[neighbor] = node
133+
if neighbor == destination { return backtrace() }
134+
}
135+
}
136+
return nil
137+
}
138+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//
2+
// UndirectedGraphProtocol.swift
3+
// DataStructures
4+
//
5+
// Created by James Bean on 9/27/18.
6+
//
7+
8+
/// Interface for undirected graphs.
9+
public protocol UndirectedGraphProtocol: GraphProtocol where Edge == UnorderedPair<Node> { }
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// UnweightedGraphProtocol.swift
3+
// DataStructures
4+
//
5+
// Created by James Bean on 9/27/18.
6+
//
7+
8+
/// Interface for unweighted graphs.
9+
public protocol UnweightedGraphProtocol: GraphProtocol {
10+
11+
// MARK: - Instance Properties
12+
13+
/// All of the edges contained herein.
14+
var edges: Set<Edge> { get set }
15+
16+
// MARK: - Initializers
17+
18+
/// Creates an `UnweightedGraphProtocol`-conforming type value with the given set of `nodes`
19+
/// and the given set of `edges`.
20+
init(_ nodes: Set<Node>, _ edges: Set<Edge>)
21+
}
22+
23+
extension UnweightedGraphProtocol {
24+
25+
/// Creates an `UnweightedGraphProtocol`-conforming type value which is composed a path of
26+
/// nodes.
27+
@inlinable
28+
public init <S> (path: S) where S: Sequence, S.Element == Node {
29+
self.init(Set(path), Set(path.pairs.map(Edge.init)))
30+
}
31+
}
32+
33+
extension UnweightedGraphProtocol {
34+
35+
// MARK: - Modifying
36+
37+
/// Inserts an edge between the given `source` and `destination` nodes.
38+
///
39+
/// If the `source` or `destination` nodes are not yet contained herein, they are inserted.
40+
@inlinable
41+
public mutating func insertEdge(from source: Node, to destination: Node) {
42+
insert(source)
43+
insert(destination)
44+
edges.insert(Edge(source,destination))
45+
}
46+
47+
/// Removes the edge between the given `source` and `destination` nodes.
48+
@inlinable
49+
public mutating func removeEdge(from source: Node, to destination: Node) {
50+
edges.remove(Edge(source,destination))
51+
}
52+
}
53+
54+
extension UnweightedGraphProtocol {
55+
56+
/// - Returns: A new graph with the union of the nodes and edges of the two given graphs.
57+
@inlinable
58+
public static func + (lhs: Self, rhs: Self) -> Self {
59+
return .init(lhs.nodes.union(rhs.nodes), lhs.edges.union(rhs.edges))
60+
}
61+
}

0 commit comments

Comments
 (0)