github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/dag/tarjan.go (about) 1 package dag 2 3 // StronglyConnected returns the list of strongly connected components 4 // within the Graph g. This information is primarily used by this package 5 // for cycle detection, but strongly connected components have widespread 6 // use. 7 func StronglyConnected(g *Graph) [][]Vertex { 8 vs := g.Vertices() 9 acct := sccAcct{ 10 NextIndex: 1, 11 VertexIndex: make(map[Vertex]int, len(vs)), 12 } 13 for _, v := range vs { 14 // Recurse on any non-visited nodes 15 if acct.VertexIndex[v] == 0 { 16 stronglyConnected(&acct, g, v) 17 } 18 } 19 return acct.SCC 20 } 21 22 func stronglyConnected(acct *sccAcct, g *Graph, v Vertex) int { 23 // Initial vertex visit 24 index := acct.visit(v) 25 minIdx := index 26 27 for _, raw := range g.DownEdges(v).List() { 28 target := raw.(Vertex) 29 targetIdx := acct.VertexIndex[target] 30 31 // Recurse on successor if not yet visited 32 if targetIdx == 0 { 33 minIdx = min(minIdx, stronglyConnected(acct, g, target)) 34 } else if acct.inStack(target) { 35 // Check if the vertex is in the stack 36 minIdx = min(minIdx, targetIdx) 37 } 38 } 39 40 // Pop the strongly connected components off the stack if 41 // this is a root vertex 42 if index == minIdx { 43 var scc []Vertex 44 for { 45 v2 := acct.pop() 46 scc = append(scc, v2) 47 if v2 == v { 48 break 49 } 50 } 51 52 acct.SCC = append(acct.SCC, scc) 53 } 54 55 return minIdx 56 } 57 58 func min(a, b int) int { 59 if a <= b { 60 return a 61 } 62 return b 63 } 64 65 // sccAcct is used ot pass around accounting information for 66 // the StronglyConnectedComponents algorithm 67 type sccAcct struct { 68 NextIndex int 69 VertexIndex map[Vertex]int 70 Stack []Vertex 71 SCC [][]Vertex 72 } 73 74 // visit assigns an index and pushes a vertex onto the stack 75 func (s *sccAcct) visit(v Vertex) int { 76 idx := s.NextIndex 77 s.VertexIndex[v] = idx 78 s.NextIndex++ 79 s.push(v) 80 return idx 81 } 82 83 // push adds a vertex to the stack 84 func (s *sccAcct) push(n Vertex) { 85 s.Stack = append(s.Stack, n) 86 } 87 88 // pop removes a vertex from the stack 89 func (s *sccAcct) pop() Vertex { 90 n := len(s.Stack) 91 if n == 0 { 92 return nil 93 } 94 vertex := s.Stack[n-1] 95 s.Stack = s.Stack[:n-1] 96 return vertex 97 } 98 99 // inStack checks if a vertex is in the stack 100 func (s *sccAcct) inStack(needle Vertex) bool { 101 for _, n := range s.Stack { 102 if n == needle { 103 return true 104 } 105 } 106 return false 107 }