-
Notifications
You must be signed in to change notification settings - Fork 0
/
depgraph.go
195 lines (164 loc) · 4.5 KB
/
depgraph.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package depgraph
import (
"errors"
"fmt"
)
// Our type consists of only a map of Nodes, indexed by strings. The graph's
// edge data is stored in the Nodes themselves, which makes cycle detection a
// bit easier.
type DependencyGraph struct {
NodeMap map[string]*Node
}
// A Node of a directed graph, with incoming and outgoing edges.
type Node struct {
Value Keyer
EdgesOut map[string]*Node
EdgesIn map[string]*Node
}
func (n *Node) Key() string {
return n.Value.Key()
}
type Keyer interface {
Key() string
}
type StringNode string
func (s StringNode) Key() string {
return string(s)
}
// Add an incoming edge. This needs to be paired with addEdgeOut.
func (n *Node) addEdgeIn(edgeNode *Node) {
n.EdgesIn[edgeNode.Key()] = edgeNode
}
// Add an outgoing edge. This needs to be paired with addEdgeIn.
func (n *Node) addEdgeOut(edgeNode *Node) {
n.EdgesOut[edgeNode.Key()] = edgeNode
}
// Remove an incoming edge. This needs to be paired with removeEdgeOut.
func (n *Node) removeEdgeIn(edgeNode *Node) {
delete(n.EdgesIn, edgeNode.Key())
}
/*
// Remove an outgoing edge. This needs to be paired with removeEdgeIn.
func (n *Node) removeEdgeOut(edgeNode *Node) {
delete(n.EdgesOut, edgeNode.Key())
}
*/
// TopSort creates a topological sort of the Nodes of a Graph. If there is a
// cycle, an error is returned, otherwise the topological sort is returned as a
// list of node names.
func (dg *DependencyGraph) TopSort() ([]string, error) {
sorted := make([]string, 0)
copy := dg.copy()
// Initially, add all nodes without dependencies
empty := make([]*Node, 0)
for _, node := range copy.NodeMap {
if len(node.EdgesIn) == 0 {
empty = append(empty, node)
}
}
for len(empty) > 0 {
node := empty[0]
sorted = append(sorted, node.Key())
empty = empty[1:]
for _, outgoing := range node.EdgesOut {
// delete the edge from node -> outgoing
outgoing.removeEdgeIn(node)
if len(outgoing.EdgesIn) == 0 {
empty = append(empty, outgoing)
}
}
node.EdgesOut = nil
}
// if there are any edges left, we have a cycle
for _, n := range copy.NodeMap {
if len(n.EdgesIn) > 0 || len(n.EdgesOut) > 0 {
return nil, errors.New("Cycle!")
}
}
return sorted, nil
}
// Copy an existing graph into an independent structure
// (i.e. new nodes/edges are created - pointers aren't copied)
func (dg *DependencyGraph) copy() *DependencyGraph {
// Copy nodes
nodes := make([]Keyer, 0, len(dg.NodeMap))
for _, node := range dg.NodeMap {
nodes = append(nodes, node.Value)
}
other := New(nodes)
// Copy edges
for fromId, node := range dg.NodeMap {
for toId := range node.EdgesOut {
other.addEdge(other.NodeMap[fromId], other.NodeMap[toId])
}
}
return other
}
// Create a new graph consisting of a set of nodes with no edges.
func New(nodes []Keyer) *DependencyGraph {
dg := &DependencyGraph{}
dg.NodeMap = make(map[string]*Node)
for _, s := range nodes {
dg.AddNode(s)
}
return dg
}
func (dg *DependencyGraph) AddNode(node Keyer) {
dg.NodeMap[node.Key()] = &Node{
Value: node,
EdgesIn: make(map[string]*Node),
EdgesOut: make(map[string]*Node),
}
}
func (dg *DependencyGraph) addEdge(from *Node, to *Node) {
from.addEdgeOut(to)
to.addEdgeIn(from)
}
// Add an edge from <from> to <to>
func (g *DependencyGraph) AddEdge(from Keyer, to Keyer) error {
if from == to {
return errors.New("From node cannot be the same as To node")
}
fromNode, ok1 := g.NodeMap[from.Key()]
toNode, ok2 := g.NodeMap[to.Key()]
if !ok1 {
return errors.New("from node not found")
} else if !ok2 {
return errors.New("to node not found")
} else {
g.addEdge(fromNode, toNode)
return nil
}
}
// A Dependency is a Dependent node that is DependentOn on another node.
type Dependency struct {
Dependent string
DependentOn string
}
func (dg *DependencyGraph) AddDependency(dep *Dependency) error {
from, ok := dg.NodeMap[dep.Dependent]
if !ok {
return fmt.Errorf("Cannot find dependent node '%v'", dep.Dependent)
}
to, ok := dg.NodeMap[dep.DependentOn]
if !ok {
return fmt.Errorf("Cannot find dependency node '%v'", dep.DependentOn)
}
return dg.AddEdge(from.Value, to.Value)
}
func (dg *DependencyGraph) AddDependencies(deps []*Dependency) error {
for _, dep := range deps {
if err := dg.AddDependency(dep); err != nil {
return err
}
}
return nil
}
func (dg *DependencyGraph) AddDependenciesForNode(dependentKey string, dependencyKeys []string) error {
for _, dep := range dependencyKeys {
if err := dg.AddDependency(&Dependency{dependentKey, dep}); err != nil {
return err
}
}
return nil
}