mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
70eebe3521
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
package dag
|
|
|
|
// StronglyConnected returns the list of strongly connected components
|
|
// within the Graph g. This information is primarily used by this package
|
|
// for cycle detection, but strongly connected components have widespread
|
|
// use.
|
|
func StronglyConnected(g *Graph) [][]Vertex {
|
|
vs := g.Vertices()
|
|
acct := sccAcct{
|
|
NextIndex: 1,
|
|
VertexIndex: make(map[Vertex]int, len(vs)),
|
|
}
|
|
for _, v := range vs {
|
|
// Recurse on any non-visited nodes
|
|
if acct.VertexIndex[v] == 0 {
|
|
stronglyConnected(&acct, g, v)
|
|
}
|
|
}
|
|
return acct.SCC
|
|
}
|
|
|
|
func stronglyConnected(acct *sccAcct, g *Graph, v Vertex) int {
|
|
// Initial vertex visit
|
|
index := acct.visit(v)
|
|
minIdx := index
|
|
|
|
for _, raw := range g.downEdgesNoCopy(v) {
|
|
target := raw.(Vertex)
|
|
targetIdx := acct.VertexIndex[target]
|
|
|
|
// Recurse on successor if not yet visited
|
|
if targetIdx == 0 {
|
|
minIdx = min(minIdx, stronglyConnected(acct, g, target))
|
|
} else if acct.inStack(target) {
|
|
// Check if the vertex is in the stack
|
|
minIdx = min(minIdx, targetIdx)
|
|
}
|
|
}
|
|
|
|
// Pop the strongly connected components off the stack if
|
|
// this is a root vertex
|
|
if index == minIdx {
|
|
var scc []Vertex
|
|
for {
|
|
v2 := acct.pop()
|
|
scc = append(scc, v2)
|
|
if v2 == v {
|
|
break
|
|
}
|
|
}
|
|
|
|
acct.SCC = append(acct.SCC, scc)
|
|
}
|
|
|
|
return minIdx
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a <= b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// sccAcct is used ot pass around accounting information for
|
|
// the StronglyConnectedComponents algorithm
|
|
type sccAcct struct {
|
|
NextIndex int
|
|
VertexIndex map[Vertex]int
|
|
Stack []Vertex
|
|
SCC [][]Vertex
|
|
}
|
|
|
|
// visit assigns an index and pushes a vertex onto the stack
|
|
func (s *sccAcct) visit(v Vertex) int {
|
|
idx := s.NextIndex
|
|
s.VertexIndex[v] = idx
|
|
s.NextIndex++
|
|
s.push(v)
|
|
return idx
|
|
}
|
|
|
|
// push adds a vertex to the stack
|
|
func (s *sccAcct) push(n Vertex) {
|
|
s.Stack = append(s.Stack, n)
|
|
}
|
|
|
|
// pop removes a vertex from the stack
|
|
func (s *sccAcct) pop() Vertex {
|
|
n := len(s.Stack)
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
vertex := s.Stack[n-1]
|
|
s.Stack = s.Stack[:n-1]
|
|
return vertex
|
|
}
|
|
|
|
// inStack checks if a vertex is in the stack
|
|
func (s *sccAcct) inStack(needle Vertex) bool {
|
|
for _, n := range s.Stack {
|
|
if n == needle {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|