github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/digraph/util.go (about) 1 package digraph 2 3 // DepthFirstWalk performs a depth-first traversal of the nodes 4 // that can be reached from the initial input set. The callback is 5 // invoked for each visited node, and may return false to prevent 6 // vising any children of the current node 7 func DepthFirstWalk(node Node, cb func(n Node) bool) { 8 frontier := []Node{node} 9 seen := make(map[Node]struct{}) 10 for len(frontier) > 0 { 11 // Pop the current node 12 n := len(frontier) 13 current := frontier[n-1] 14 frontier = frontier[:n-1] 15 16 // Check for potential cycle 17 if _, ok := seen[current]; ok { 18 continue 19 } 20 seen[current] = struct{}{} 21 22 // Visit with the callback 23 if !cb(current) { 24 continue 25 } 26 27 // Add any new edges to visit, in reverse order 28 edges := current.Edges() 29 for i := len(edges) - 1; i >= 0; i-- { 30 frontier = append(frontier, edges[i].Tail()) 31 } 32 } 33 } 34 35 // FilterDegree returns only the nodes with the desired 36 // degree. This can be used with OutDegree or InDegree 37 func FilterDegree(degree int, degrees map[Node]int) []Node { 38 var matching []Node 39 for n, d := range degrees { 40 if d == degree { 41 matching = append(matching, n) 42 } 43 } 44 return matching 45 } 46 47 // InDegree is used to compute the in-degree of nodes 48 func InDegree(nodes []Node) map[Node]int { 49 degree := make(map[Node]int, len(nodes)) 50 for _, n := range nodes { 51 if _, ok := degree[n]; !ok { 52 degree[n] = 0 53 } 54 for _, e := range n.Edges() { 55 degree[e.Tail()]++ 56 } 57 } 58 return degree 59 } 60 61 // OutDegree is used to compute the in-degree of nodes 62 func OutDegree(nodes []Node) map[Node]int { 63 degree := make(map[Node]int, len(nodes)) 64 for _, n := range nodes { 65 degree[n] = len(n.Edges()) 66 } 67 return degree 68 } 69 70 // Sinks is used to get the nodes with out-degree of 0 71 func Sinks(nodes []Node) []Node { 72 return FilterDegree(0, OutDegree(nodes)) 73 } 74 75 // Sources is used to get the nodes with in-degree of 0 76 func Sources(nodes []Node) []Node { 77 return FilterDegree(0, InDegree(nodes)) 78 } 79 80 // Unreachable starts at a given start node, performs 81 // a DFS from there, and returns the set of unreachable nodes. 82 func Unreachable(start Node, nodes []Node) []Node { 83 // DFS from the start ndoe 84 frontier := []Node{start} 85 seen := make(map[Node]struct{}) 86 for len(frontier) > 0 { 87 // Pop the current node 88 n := len(frontier) 89 current := frontier[n-1] 90 frontier = frontier[:n-1] 91 92 // Check for potential cycle 93 if _, ok := seen[current]; ok { 94 continue 95 } 96 seen[current] = struct{}{} 97 98 // Add any new edges to visit, in reverse order 99 edges := current.Edges() 100 for i := len(edges) - 1; i >= 0; i-- { 101 frontier = append(frontier, edges[i].Tail()) 102 } 103 } 104 105 // Check for any unseen nodes 106 var unseen []Node 107 for _, node := range nodes { 108 if _, ok := seen[node]; !ok { 109 unseen = append(unseen, node) 110 } 111 } 112 return unseen 113 }