/*
* This file is part of the Trickl Open Source Libraries.
*
* Trickl Open Source Libraries - http://open.trickl.com/
*
* Copyright (C) 2007 Aaron Windsor (part of the C++ Boost Graph Library)
* Copyright (C) 2011 Tim Gee (ported to Java).
*
* Trickl Open Source Libraries are free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Trickl Open Source Libraries are distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this project. If not, see <http://www.gnu.org/licenses/>.
*/
package com.trickl.graph;
import java.util.*;
import org.jgrapht.EdgeFactory;
import org.jgrapht.Graph;
public class Biconnectivity<V, E> {
private static class BiconnectedComponentIndexer<V, E> implements SpanningSearchVisitor<V, E> {
private static class Detail<V> {
int discoverTime;
int lowPoint;
V predecessor;
}
private int index = 0;
private int dfsTime = 0;
private Graph<V, E> graph;
private Map<V, Detail<V> > vertexDetails;
private Map<E, Integer> edgeComponent;
private List<V> articulationPoints;
private Stack<E> stack;
BiconnectedComponentIndexer(Graph<V, E> graph) {
this.graph = graph;
this.edgeComponent = new HashMap<E, Integer>();
this.vertexDetails = new HashMap<V, Detail<V> >();
this.articulationPoints = new LinkedList<V>();
this.stack = new Stack<E>();
}
@Override
public void startVertex(V u) {
Detail<V> detail = vertexDetails.get(u);
if (detail == null)
{
detail = new Detail<V>();
vertexDetails.put(u, detail);
}
detail.predecessor = u;
}
@Override
public void discoverVertex(V u) {
Detail<V> detail = vertexDetails.get(u);
if (detail == null)
{
detail = new Detail<V>();
vertexDetails.put(u, detail);
}
detail.discoverTime = ++dfsTime;
detail.lowPoint = detail.discoverTime;
}
@Override
public void treeEdge(V source, V target) {
stack.push(graph.getEdge(source, target));
Detail<V> targetDetail = vertexDetails.get(target);
targetDetail.predecessor = source;
}
@Override
public void backEdge(V source, V target) {
Detail<V> sourceDetail = vertexDetails.get(source);
Detail<V> targetDetail = vertexDetails.get(target);
if (!target.equals(sourceDetail.predecessor)) {
E edge = graph.getEdge(source, target);
stack.push(edge);
sourceDetail.lowPoint =
Math.min(sourceDetail.lowPoint,
targetDetail.discoverTime);
}
}
@Override
public void finishVertex(V u) {
Detail<V> detail = vertexDetails.get(u);
V parent = detail.predecessor;
Detail<V> parentDetail = vertexDetails.get(parent);
int dtmOfDubiousParent = parentDetail.discoverTime;
boolean isArticulationPoint = false;
if (dtmOfDubiousParent > detail.discoverTime) {
parent = parentDetail.predecessor;
isArticulationPoint = true;
vertexDetails.get(detail.predecessor).predecessor = u;
detail.predecessor = u;
}
if (parent.equals(u)) { // at top
if (detail.discoverTime + 1 == dtmOfDubiousParent) {
isArticulationPoint = false;
}
} else {
parentDetail.lowPoint =
Math.min(parentDetail.lowPoint, detail.lowPoint);
if (detail.lowPoint >= parentDetail.discoverTime) {
if (parentDetail.discoverTime > vertexDetails.get(parentDetail.predecessor).discoverTime) {
detail.predecessor = parentDetail.predecessor;
parentDetail.predecessor = u;
}
while (vertexDetails.get(graph.getEdgeSource(stack.peek())).discoverTime >= detail.discoverTime) {
edgeComponent.put(stack.peek(), index);
stack.pop();
}
edgeComponent.put(stack.peek(), index);
stack.pop();
++index;
if (stack.empty()) {
detail.predecessor = parent;
parentDetail.predecessor = u;
}
}
}
if (isArticulationPoint) {
articulationPoints.add(u);
}
}
@Override
public void initializeVertex(V u) {
}
@Override
public void examineEdge(V source, V target) {
}
@Override
public void forwardOrCrossEdge(V source, V target) {
}
};
private Graph<V, E> graph;
private BiconnectedComponentIndexer<V, E> componentVisitor;
private DepthFirstSearch<V, E> depthFirstSearch;
public Biconnectivity(Graph<V, E> graph) {
this.graph = graph;
depthFirstSearch = new DepthFirstSearch<V, E>(graph);
}
private void lazyImpl() {
if (componentVisitor == null) {
this.componentVisitor = new BiconnectedComponentIndexer<V, E>(graph);
depthFirstSearch.traverse(componentVisitor);
}
}
public int getComponents() {
if (graph.vertexSet().isEmpty()) {
return 0;
}
lazyImpl();
return componentVisitor.index;
}
public int getComponent(E edge) {
lazyImpl();
return componentVisitor.edgeComponent.get(edge);
}
public boolean isBiconnected()
{
return getComponents() == 1;
}
public List<V> getArticulationPoints() {
return componentVisitor.articulationPoints;
}
public void makeBiconnectedPlanar(EdgeFactory<V, E> edgeFactory) {
int componentCount = getComponents();
if (componentCount < 2) {
return;
}
for (V articulationPoint : getArticulationPoints()) {
int previous_component = Integer.MAX_VALUE;
V previous_vertex = null;
for (E e : graph.edgesOf(articulationPoint)) {
V e_source = graph.getEdgeSource(e);
V e_target = graph.getEdgeTarget(e);
//Skip self-loops and parallel edges
if (e_source.equals(e_target) || previous_vertex.equals(e_target)) {
continue;
}
V current_vertex = e_source.equals(articulationPoint) ? e_target : e_source;
int current_component = getComponent(e);
if (previous_vertex != null
&& current_component != previous_component) {
edgeFactory.createEdge(current_vertex, previous_vertex);
}
previous_vertex = current_vertex;
previous_component = current_component;
}
}
}
public void makeBiconnectedPlanar() {
makeBiconnectedPlanar(graph.getEdgeFactory());
}
}