blob: 2344bf0ae65a326f6e37fc4345876f731bfab974 [file] [log] [blame]
package topology
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import (
"github.com/apache/trafficcontrol/lib/go-tc"
"math"
)
type TarjanNode struct {
tc.TopologyNode
Index *int
LowLink *int
OnStack *bool
}
type NodeStack []*TarjanNode
type Graph []*TarjanNode
type Component []tc.TopologyNode
type Tarjan struct {
Graph *Graph
Stack *NodeStack
Components []Component
Index int
}
func (stack *NodeStack) push(node *TarjanNode) {
*stack = append(append([]*TarjanNode{}, *stack...), node)
}
func (stack *NodeStack) pop() *TarjanNode {
length := len(*stack)
node := (*stack)[length-1]
*stack = (*stack)[:length-1]
return node
}
func tarjan(nodes []tc.TopologyNode) [][]tc.TopologyNode {
structs := Tarjan{
Graph: &Graph{},
Stack: &NodeStack{},
Components: []Component{},
Index: 0,
}
for _, node := range nodes {
tarjanNode := TarjanNode{TopologyNode: node, LowLink: new(int)}
*tarjanNode.LowLink = 500
*structs.Graph = append(*structs.Graph, &tarjanNode)
}
structs.Stack = &NodeStack{}
structs.Index = 0
for _, vertex := range *structs.Graph {
if vertex.Index == nil {
structs.strongConnect(vertex)
}
}
var components [][]tc.TopologyNode
for _, component := range structs.Components {
var componentArray []tc.TopologyNode
for _, node := range component {
componentArray = append(componentArray, node)
}
components = append(components, componentArray)
}
return components
}
func (structs *Tarjan) nextComponent() (Component, int) {
var component = Component{}
index := len(structs.Components)
structs.Components = append(structs.Components, component)
return component, index
}
func (structs *Tarjan) strongConnect(node *TarjanNode) {
stack := structs.Stack
node.Index = new(int)
*node.Index = structs.Index
node.LowLink = new(int)
*node.LowLink = structs.Index
structs.Index++
stack.push(node)
node.OnStack = new(bool)
*node.OnStack = true
for _, parentIndex := range node.Parents {
parent := (*structs.Graph)[parentIndex]
if parent.Index == nil {
structs.strongConnect(parent)
*(*parent).LowLink = int(math.Min(float64(*node.LowLink), float64(*parent.LowLink)))
} else if *parent.OnStack {
*node.LowLink = int(math.Min(float64(*node.LowLink), float64(*parent.Index)))
}
}
if *node.LowLink == *node.Index {
component, index := structs.nextComponent()
var otherNode *TarjanNode
for node != otherNode {
otherNode = stack.pop()
*otherNode.OnStack = false
component = append(component, otherNode.TopologyNode)
}
structs.Components[index] = component
}
}