/*
* Licensed 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.
*/
package com.google.devtools.cyclefinder;
import com.google.common.collect.SetMultimap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An implementation of Tarjan's strongly connected components algorithm.
* http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
*/
class Tarjans {
private final SetMultimap<TypeNode, Edge> edges;
private final Set<TypeNode> seedTypes;
private int vIndex = 0;
// In case of performance issues, consider a data structure with faster .contains().
private ArrayList<Vertex> stack = new ArrayList<>();
private Map<TypeNode, Vertex> vertices = new HashMap<>();
private List<List<TypeNode>> stronglyConnectedComponents = new ArrayList<>();
private Tarjans(SetMultimap<TypeNode, Edge> edges, Set<TypeNode> seedTypes) {
this.edges = edges;
this.seedTypes = seedTypes;
}
public static List<List<TypeNode>> getStronglyConnectedComponents(
SetMultimap<TypeNode, Edge> edges, Set<TypeNode> seedTypes) {
Tarjans tarjans = new Tarjans(edges, seedTypes);
tarjans.run();
return tarjans.stronglyConnectedComponents;
}
private void run() {
for (TypeNode type : seedTypes) {
Vertex v = getVertex(type);
if (v.index == -1) {
visit(v);
}
}
}
private void visit(Vertex v) {
v.index = v.lowlink = vIndex++;
stack.add(v);
for (Edge edge : edges.get(v.type)) {
Vertex w = getVertex(edge.getTarget());
if (w.index == -1) {
visit(w);
v.lowlink = Math.min(v.lowlink, w.lowlink);
} else if (stack.contains(w)) {
v.lowlink = Math.min(v.lowlink, w.index);
}
}
if (v.lowlink == v.index) {
int idx = stack.indexOf(v);
assert idx >= 0;
List<Vertex> stronglyConnected = stack.subList(idx, stack.size());
if (stronglyConnected.size() > 1) {
List<TypeNode> stronglyConnectedTypes = new ArrayList<>(stronglyConnected.size());
for (Vertex ver : stronglyConnected) {
stronglyConnectedTypes.add(ver.type);
}
stronglyConnectedComponents.add(stronglyConnectedTypes);
}
stronglyConnected.clear(); // Removes the sublist from stack.
}
}
private Vertex getVertex(TypeNode type) {
Vertex v = vertices.get(type);
if (v == null) {
v = new Vertex(type);
vertices.put(type, v);
}
return v;
}
private static class Vertex {
private int index = -1;
private int lowlink = -1;
private TypeNode type;
private Vertex(TypeNode type) {
this.type = type;
}
public String toString() {
return type.toString();
}
}
}