mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 18:01:01 -06:00
268959f4be
The public functions for the graph UpEdges and DownEdges is returning the internal Set from the graph, meaning that callers could inadvertently corrupt the graph structure by editing the returned Sets. Make UpEdges and DownEdges return a copy of the set, while retaining the efficient no-copy behavior for internal callers.
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
|
|
}
|