github.com/Kevinklinger/open_terraform@v0.11.12-beta1/digraph/util.go (about)

     1  package digraph
     2  
     3  // DepthFirstWalk performs a depth-first traversal of the nodes
     4  // that can be reached from the initial input set. The callback is
     5  // invoked for each visited node, and may return false to prevent
     6  // vising any children of the current node
     7  func DepthFirstWalk(node Node, cb func(n Node) bool) {
     8  	frontier := []Node{node}
     9  	seen := make(map[Node]struct{})
    10  	for len(frontier) > 0 {
    11  		// Pop the current node
    12  		n := len(frontier)
    13  		current := frontier[n-1]
    14  		frontier = frontier[:n-1]
    15  
    16  		// Check for potential cycle
    17  		if _, ok := seen[current]; ok {
    18  			continue
    19  		}
    20  		seen[current] = struct{}{}
    21  
    22  		// Visit with the callback
    23  		if !cb(current) {
    24  			continue
    25  		}
    26  
    27  		// Add any new edges to visit, in reverse order
    28  		edges := current.Edges()
    29  		for i := len(edges) - 1; i >= 0; i-- {
    30  			frontier = append(frontier, edges[i].Tail())
    31  		}
    32  	}
    33  }
    34  
    35  // FilterDegree returns only the nodes with the desired
    36  // degree. This can be used with OutDegree or InDegree
    37  func FilterDegree(degree int, degrees map[Node]int) []Node {
    38  	var matching []Node
    39  	for n, d := range degrees {
    40  		if d == degree {
    41  			matching = append(matching, n)
    42  		}
    43  	}
    44  	return matching
    45  }
    46  
    47  // InDegree is used to compute the in-degree of nodes
    48  func InDegree(nodes []Node) map[Node]int {
    49  	degree := make(map[Node]int, len(nodes))
    50  	for _, n := range nodes {
    51  		if _, ok := degree[n]; !ok {
    52  			degree[n] = 0
    53  		}
    54  		for _, e := range n.Edges() {
    55  			degree[e.Tail()]++
    56  		}
    57  	}
    58  	return degree
    59  }
    60  
    61  // OutDegree is used to compute the in-degree of nodes
    62  func OutDegree(nodes []Node) map[Node]int {
    63  	degree := make(map[Node]int, len(nodes))
    64  	for _, n := range nodes {
    65  		degree[n] = len(n.Edges())
    66  	}
    67  	return degree
    68  }
    69  
    70  // Sinks is used to get the nodes with out-degree of 0
    71  func Sinks(nodes []Node) []Node {
    72  	return FilterDegree(0, OutDegree(nodes))
    73  }
    74  
    75  // Sources is used to get the nodes with in-degree of 0
    76  func Sources(nodes []Node) []Node {
    77  	return FilterDegree(0, InDegree(nodes))
    78  }
    79  
    80  // Unreachable starts at a given start node, performs
    81  // a DFS from there, and returns the set of unreachable nodes.
    82  func Unreachable(start Node, nodes []Node) []Node {
    83  	// DFS from the start ndoe
    84  	frontier := []Node{start}
    85  	seen := make(map[Node]struct{})
    86  	for len(frontier) > 0 {
    87  		// Pop the current node
    88  		n := len(frontier)
    89  		current := frontier[n-1]
    90  		frontier = frontier[:n-1]
    91  
    92  		// Check for potential cycle
    93  		if _, ok := seen[current]; ok {
    94  			continue
    95  		}
    96  		seen[current] = struct{}{}
    97  
    98  		// Add any new edges to visit, in reverse order
    99  		edges := current.Edges()
   100  		for i := len(edges) - 1; i >= 0; i-- {
   101  			frontier = append(frontier, edges[i].Tail())
   102  		}
   103  	}
   104  
   105  	// Check for any unseen nodes
   106  	var unseen []Node
   107  	for _, node := range nodes {
   108  		if _, ok := seen[node]; !ok {
   109  			unseen = append(unseen, node)
   110  		}
   111  	}
   112  	return unseen
   113  }