go-hep.org/x/hep@v0.38.1/fwk/utils/tarjan/tarjan.go (about) 1 // Copyright ©2017 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Copyright (c) 2013 - Max Persson <max@looplab.se> 6 // Copyright (c) 2010-2013 - Gustavo Niemeyer <gustavo@niemeyer.net> 7 // 8 // Licensed under the Apache License, Version 2.0 (the "License"); 9 // you may not use this file except in compliance with the License. 10 // You may obtain a copy of the License at 11 // 12 // http://www.apache.org/licenses/LICENSE-2.0 13 // 14 // Unless required by applicable law or agreed to in writing, software 15 // distributed under the License is distributed on an "AS IS" BASIS, 16 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 // See the License for the specific language governing permissions and 18 // limitations under the License. 19 20 // package tarjan implements a graph loop detection algorithm called Tarjan's algorithm. 21 // 22 // The algorithm takes a input graph and produces a slice where each item is a 23 // slice of strongly connected vertices. The input graph is in form of a map 24 // where the key is a graph vertex and the value is the edges in for of a slice 25 // of vertices. 26 // 27 // Algorithm description: 28 // http://en.wikipedia.org/wiki/Tarjan’s_strongly_connected_components_algorithm 29 // 30 // Based on an implementation by Gustavo Niemeyer (in mgo/txn): 31 // http://bazaar.launchpad.net/+branch/mgo/v2/view/head:/txn/tarjan.go 32 package tarjan // import "go-hep.org/x/hep/fwk/utils/tarjan" 33 34 // Connections creates a slice where each item is a slice of strongly connected vertices. 35 // 36 // If a slice item contains only one vertex there are no loops. A loop on the 37 // vertex itself is also a connected group. 38 // 39 // The example shows the same graph as in the Wikipedia article. 40 func Connections(graph map[any][]any) [][]any { 41 g := &data{ 42 graph: graph, 43 nodes: make([]node, 0, len(graph)), 44 index: make(map[any]int, len(graph)), 45 } 46 for v := range g.graph { 47 if _, ok := g.index[v]; !ok { 48 g.strongConnect(v) 49 } 50 } 51 return g.output 52 } 53 54 // data contains all common data for a single operation. 55 type data struct { 56 graph map[any][]any 57 nodes []node 58 stack []any 59 index map[any]int 60 output [][]any 61 } 62 63 // node stores data for a single vertex in the connection process. 64 type node struct { 65 lowlink int 66 stacked bool 67 } 68 69 // strongConnect runs Tarjan's algorithm recursivley and outputs a grouping of 70 // strongly connected vertices. 71 func (data *data) strongConnect(v any) *node { 72 index := len(data.nodes) 73 data.index[v] = index 74 data.stack = append(data.stack, v) 75 data.nodes = append(data.nodes, node{lowlink: index, stacked: true}) 76 node := &data.nodes[index] 77 78 for _, w := range data.graph[v] { 79 i, seen := data.index[w] 80 if !seen { 81 n := data.strongConnect(w) 82 if n.lowlink < node.lowlink { 83 node.lowlink = n.lowlink 84 } 85 } else if data.nodes[i].stacked { 86 if i < node.lowlink { 87 node.lowlink = i 88 } 89 } 90 } 91 92 if node.lowlink == index { 93 var vertices []any 94 i := len(data.stack) - 1 95 for { 96 w := data.stack[i] 97 stackIndex := data.index[w] 98 data.nodes[stackIndex].stacked = false 99 vertices = append(vertices, w) 100 if stackIndex == index { 101 break 102 } 103 i-- 104 } 105 data.stack = data.stack[:i] 106 data.output = append(data.output, vertices) 107 } 108 109 return node 110 }