-
Notifications
You must be signed in to change notification settings - Fork 173
/
dag.go
152 lines (141 loc) · 5.31 KB
/
dag.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
package ffmpeg_go
import (
"errors"
)
// Node in a directed-acyclic graph (DAG).
//
// Edges:
// DagNodes are connected by edges. An edge connects two nodes with a label for each side:
// - ``upstream_node``: upstream/parent node
// - ``upstream_label``: label on the outgoing side of the upstream node
// - ``downstream_node``: downstream/child node
// - ``downstream_label``: label on the incoming side of the downstream node
//
// For example, DagNode A may be connected to DagNode B with an edge labelled "foo" on A's side, and "bar" on B's
// side:
//
// _____ _____
// | | | |
// | A >[foo]---[bar]> B |
// |_____| |_____|
//
// Edge labels may be integers or strings, and nodes cannot have more than one incoming edge with the same label.
//
// DagNodes may have any number of incoming edges and any number of outgoing edges. DagNodes keep track only of
// their incoming edges, but the entire graph structure can be inferred by looking at the furthest downstream
// nodes and working backwards.
//
// Hashing:
// DagNodes must be hashable, and two nodes are considered to be equivalent if they have the same hash value.
//
// Nodes are immutable, and the hash should remain constant as a result. If a node with new contents is required,
// create a new node and throw the old one away.
//
// String representation:
// In order for graph visualization tools to show useful information, nodes must be representable as strings. The
// ``String`` operator should provide a more or less "full" representation of the node, and the ``ShortRepr``
// property should be a shortened, concise representation.
//
// Again, because nodes are immutable, the string representations should remain constant.
type DagNode interface {
Hash() int
// Compare two nodes
Equal(other DagNode) bool
// Return a full string representation of the node.
String() string
// Return a partial/concise representation of the node
ShortRepr() string
// Provides information about all incoming edges that connect to this node.
//
// The edge map is a dictionary that maps an ``incoming_label`` to ``(outgoing_node, outgoing_label)``. Note that
// implicity, ``incoming_node`` is ``self``. See "Edges" section above.
IncomingEdgeMap() map[Label]NodeInfo
}
type Label string
type NodeInfo struct {
Node DagNode
Label Label
Selector Selector
}
type Selector string
type DagEdge struct {
DownStreamNode DagNode
DownStreamLabel Label
UpStreamNode DagNode
UpStreamLabel Label
UpStreamSelector Selector
}
func GetInComingEdges(downStreamNode DagNode, inComingEdgeMap map[Label]NodeInfo) []DagEdge {
var edges []DagEdge
for _, downStreamLabel := range _getAllLabelsInSorted(inComingEdgeMap) {
upStreamInfo := inComingEdgeMap[downStreamLabel]
edges = append(edges, DagEdge{
DownStreamNode: downStreamNode,
DownStreamLabel: downStreamLabel,
UpStreamNode: upStreamInfo.Node,
UpStreamLabel: upStreamInfo.Label,
UpStreamSelector: upStreamInfo.Selector,
})
}
return edges
}
func GetOutGoingEdges(upStreamNode DagNode, outOutingEdgeMap map[Label][]NodeInfo) []DagEdge {
var edges []DagEdge
for _, upStreamLabel := range _getAllLabelsSorted(outOutingEdgeMap) {
downStreamInfos := outOutingEdgeMap[upStreamLabel]
for _, downStreamInfo := range downStreamInfos {
edges = append(edges, DagEdge{
DownStreamNode: downStreamInfo.Node,
DownStreamLabel: downStreamInfo.Label,
UpStreamNode: upStreamNode,
UpStreamLabel: upStreamLabel,
UpStreamSelector: downStreamInfo.Selector,
})
}
}
return edges
}
func TopSort(downStreamNodes []DagNode) (sortedNodes []DagNode, outOutingEdgeMaps map[int]map[Label][]NodeInfo, err error) {
markedNodes := map[int]struct{}{}
markedSortedNodes := map[int]struct{}{}
outOutingEdgeMaps = map[int]map[Label][]NodeInfo{}
var visit func(upStreamNode, downstreamNode DagNode, upStreamLabel, downStreamLabel Label, downStreamSelector Selector) error
visit = func(upStreamNode, downstreamNode DagNode, upStreamLabel, downStreamLabel Label, downStreamSelector Selector) error {
if _, ok := markedNodes[upStreamNode.Hash()]; ok {
return errors.New("graph if not DAG")
}
if downstreamNode != nil {
if a, ok := outOutingEdgeMaps[upStreamNode.Hash()]; !ok || a == nil {
outOutingEdgeMaps[upStreamNode.Hash()] = map[Label][]NodeInfo{}
}
outgoingEdgeMap := outOutingEdgeMaps[upStreamNode.Hash()]
outgoingEdgeMap[upStreamLabel] = append(outgoingEdgeMap[upStreamLabel], NodeInfo{
Node: downstreamNode,
Label: downStreamLabel,
Selector: downStreamSelector,
})
}
if _, ok := markedSortedNodes[upStreamNode.Hash()]; !ok {
markedNodes[upStreamNode.Hash()] = struct{}{}
for _, edge := range GetInComingEdges(upStreamNode, upStreamNode.IncomingEdgeMap()) {
err := visit(edge.UpStreamNode, edge.DownStreamNode, edge.UpStreamLabel, edge.DownStreamLabel, edge.UpStreamSelector)
if err != nil {
return err
}
}
delete(markedNodes, upStreamNode.Hash())
sortedNodes = append(sortedNodes, upStreamNode)
markedSortedNodes[upStreamNode.Hash()] = struct{}{}
}
return nil
}
for len(downStreamNodes) > 0 {
node := downStreamNodes[len(downStreamNodes)-1]
downStreamNodes = downStreamNodes[:len(downStreamNodes)-1]
err = visit(node, nil, "", "", "")
if err != nil {
return
}
}
return
}