terraform/dag/tarjan.go

108 lines
2.2 KiB
Go
Raw Normal View History

2015-01-24 01:42:06 +01:00
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()
2015-02-05 01:38:38 +01:00
acct := sccAcct{
NextIndex: 1,
VertexIndex: make(map[Vertex]int, len(vs)),
2015-01-24 01:42:06 +01:00
}
for _, v := range vs {
2015-02-05 01:38:38 +01:00
// Recurse on any non-visited nodes
if acct.VertexIndex[v] == 0 {
stronglyConnected(&acct, g, v)
2015-01-24 01:42:06 +01:00
}
}
2015-02-05 01:38:38 +01:00
return acct.SCC
2015-01-24 01:42:06 +01:00
}
2015-02-05 01:38:38 +01:00
func stronglyConnected(acct *sccAcct, g *Graph, v Vertex) int {
// Initial vertex visit
index := acct.visit(v)
minIdx := index
2015-01-24 01:42:06 +01:00
for _, raw := range g.downEdgesNoCopy(v) {
2015-01-24 01:42:06 +01:00
target := raw.(Vertex)
2015-02-05 01:38:38 +01:00
targetIdx := acct.VertexIndex[target]
2015-01-24 01:42:06 +01:00
2015-02-05 01:38:38 +01:00
// 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)
2015-01-24 01:42:06 +01:00
}
}
2015-02-05 01:38:38 +01:00
// 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 {
2015-01-24 01:42:06 +01:00
break
}
}
2015-02-05 01:38:38 +01:00
acct.SCC = append(acct.SCC, scc)
}
return minIdx
}
func min(a, b int) int {
if a <= b {
return a
2015-01-24 01:42:06 +01:00
}
2015-02-05 01:38:38 +01:00
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
}
2015-01-24 01:42:06 +01:00
2015-02-05 01:38:38 +01:00
// 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
2015-01-24 01:42:06 +01:00
}