blob: 26fdfa9ba974f7a82f15eba7bdc446717a1e037b [file] [log] [blame]
Todd Wangc2cba882014-10-14 10:59:56 -07001// Package toposort implements topological sort. For details see:
2// http://en.wikipedia.org/wiki/Topological_sorting
Jiri Simsa5293dcb2014-05-10 09:56:38 -07003package toposort
4
Todd Wangc2cba882014-10-14 10:59:56 -07005// Sorter implements a topological sorter. Add nodes and edges to the sorter to
6// describe the graph, and call Sort to retrieve topologically-sorted nodes.
7// The zero Sorter describes an empty graph.
8type Sorter struct {
9 values map[interface{}]int // maps from user-provided value to index in nodes
10 nodes []*node // the graph to sort
Jiri Simsa5293dcb2014-05-10 09:56:38 -070011}
12
Todd Wangc2cba882014-10-14 10:59:56 -070013// node represents a node in the graph.
14type node struct {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070015 value interface{}
Todd Wangc2cba882014-10-14 10:59:56 -070016 children []*node
Jiri Simsa5293dcb2014-05-10 09:56:38 -070017}
18
Todd Wangc2cba882014-10-14 10:59:56 -070019func (s *Sorter) getOrAddNode(value interface{}) *node {
20 if s.values == nil {
21 s.values = make(map[interface{}]int)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070022 }
Todd Wangc2cba882014-10-14 10:59:56 -070023 if index, ok := s.values[value]; ok {
24 return s.nodes[index]
Jiri Simsa5293dcb2014-05-10 09:56:38 -070025 }
Todd Wangc2cba882014-10-14 10:59:56 -070026 s.values[value] = len(s.nodes)
27 newNode := &node{value: value}
28 s.nodes = append(s.nodes, newNode)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070029 return newNode
30}
31
Todd Wangc2cba882014-10-14 10:59:56 -070032// AddNode adds a node. Arbitrary value types are supported, but the values
33// must be comparable; they'll be used as map keys. Typically this is only used
34// to add nodes with no incoming or outgoing edges.
35func (s *Sorter) AddNode(value interface{}) {
36 s.getOrAddNode(value)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070037}
38
Todd Wangc2cba882014-10-14 10:59:56 -070039// AddEdge adds nodes from and to, and adds an edge from -> to. You don't need
40// to call AddNode first; the nodes will be implicitly added if they don't
41// already exist. The direction means that from depends on to; i.e. to will
42// appear before from in the sorted output. Cycles are allowed.
43func (s *Sorter) AddEdge(from interface{}, to interface{}) {
44 fromN, toN := s.getOrAddNode(from), s.getOrAddNode(to)
45 fromN.children = append(fromN.children, toN)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070046}
47
Todd Wangc2cba882014-10-14 10:59:56 -070048// Sort returns the topologically sorted nodes, along with some of the cycles
49// (if any) that were encountered. You're guaranteed that len(cycles)==0 iff
50// there are no cycles in the graph, otherwise an arbitrary (but non-empty) list
51// of cycles is returned.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070052//
53// If there are cycles the sorting is best-effort; portions of the graph that
54// are acyclic will still be ordered correctly, and the cyclic portions have an
55// arbitrary ordering.
56//
Todd Wangc2cba882014-10-14 10:59:56 -070057// Sort is deterministic; given the same sequence of inputs it always returns
58// the same output, even if the inputs are only partially ordered.
59func (s *Sorter) Sort() (sorted []interface{}, cycles [][]interface{}) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070060 // The strategy is the standard simple approach of performing DFS on the
61 // graph. Details are outlined in the above wikipedia article.
Todd Wangc2cba882014-10-14 10:59:56 -070062 done := make(map[*node]bool)
63 for _, n := range s.nodes {
64 cycles = appendCycles(cycles, n.visit(done, make(map[*node]bool), &sorted))
Jiri Simsa5293dcb2014-05-10 09:56:38 -070065 }
66 return
67}
68
Jiri Simsa5293dcb2014-05-10 09:56:38 -070069// visit performs DFS on the graph, and fills in sorted and cycles as it
70// traverses. We use done to indicate a node has been fully explored, and
71// visiting to indicate a node is currently being explored.
72//
73// The cycle collection strategy is to wait until we've hit a repeated node in
74// visiting, and add that node to cycles and return. Thereafter as the
75// recursive stack is unwound, nodes append themselves to the end of each cycle,
76// until we're back at the repeated node. This guarantees that if the graph is
77// cyclic we'll return at least one of the cycles.
Todd Wangc2cba882014-10-14 10:59:56 -070078func (n *node) visit(done, visiting map[*node]bool, sorted *[]interface{}) (cycles [][]interface{}) {
79 if done[n] {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070080 return
81 }
Todd Wangc2cba882014-10-14 10:59:56 -070082 if visiting[n] {
83 cycles = [][]interface{}{{n.value}}
Jiri Simsa5293dcb2014-05-10 09:56:38 -070084 return
85 }
Todd Wangc2cba882014-10-14 10:59:56 -070086 visiting[n] = true
87 for _, child := range n.children {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070088 cycles = appendCycles(cycles, child.visit(done, visiting, sorted))
89 }
Todd Wangc2cba882014-10-14 10:59:56 -070090 done[n] = true
91 *sorted = append(*sorted, n.value)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070092 // Update cycles. If it's empty none of our children detected any cycles, and
93 // there's nothing to do. Otherwise we append ourselves to the cycle, iff the
94 // cycle hasn't completed yet. We know the cycle has completed if the first
95 // and last item in the cycle are the same, with an exception for the single
96 // item case; self-cycles are represented as the same node appearing twice.
97 for cx := range cycles {
98 len := len(cycles[cx])
99 if len == 1 || cycles[cx][0] != cycles[cx][len-1] {
Todd Wangc2cba882014-10-14 10:59:56 -0700100 cycles[cx] = append(cycles[cx], n.value)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700101 }
102 }
103 return
104}
105
106// appendCycles returns the combined cycles in a and b.
107func appendCycles(a [][]interface{}, b [][]interface{}) [][]interface{} {
108 for _, bcycle := range b {
109 a = append(a, bcycle)
110 }
111 return a
112}
113
Todd Wangc2cba882014-10-14 10:59:56 -0700114// DumpCycles dumps the cycles returned from Sorter.Sort, using toString to
115// convert each node into a string.
116func DumpCycles(cycles [][]interface{}, toString func(n interface{}) string) string {
117 var str string
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700118 for cyclex, cycle := range cycles {
119 if cyclex > 0 {
120 str += " "
121 }
122 str += "["
123 for nodex, node := range cycle {
124 if nodex > 0 {
125 str += " <= "
126 }
Todd Wangc2cba882014-10-14 10:59:56 -0700127 str += toString(node)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700128 }
129 str += "]"
130 }
Todd Wangc2cba882014-10-14 10:59:56 -0700131 return str
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700132}