/* $RCSfile$ * $Author$ * $Date$ * $Revision$ * * Copyright (C) 2004-2007 Ulrich Bauer <ulrich.bauer@alumni.tum.de> * * Contact: cdk-devel@lists.sourceforge.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * All we ask is that proper credit is given for our work, which includes * - but is not limited to - adding the above copyright notice to the beginning * of your source code files, and to any copyright notice that you may distribute * with programs based on this work. * * This program is 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * */ package org.openscience.cdk.graph; import org._3pq.jgrapht.Edge; import org._3pq.jgrapht.Graph; import org._3pq.jgrapht.UndirectedGraph; import org._3pq.jgrapht.alg.ConnectivityInspector; import org._3pq.jgrapht.event.GraphEdgeChangeEvent; import org._3pq.jgrapht.event.GraphVertexChangeEvent; import org._3pq.jgrapht.graph.SimpleGraph; import org._3pq.jgrapht.graph.Subgraph; import org.openscience.cdk.annotations.TestClass; import org.openscience.cdk.annotations.TestMethod; import java.util.*; /** * Finds the biconnected components of a graph. * Two edges belong to the same biconnected component if and only if they are * identical or both belong to a simple cycle. * * @author Ulrich Bauer <ulrich.bauer@alumni.tum.de> * * @cdk.module standard * @cdk.githash * * @cdk.builddepends jgrapht-0.5.3.jar * @cdk.depends jgrapht-0.5.3.jar */ @TestClass("org.openscience.cdk.graph.BiconnectivityInspectorTest") public class BiconnectivityInspector { private List biconnectedSets; private UndirectedGraph graph; /** * Creates a biconnectivity inspector for the specified undirected graph. * @param g the specified graph */ public BiconnectivityInspector(UndirectedGraph g) { graph = g; } private List lazyFindBiconnectedSets( ) { if( biconnectedSets == null ) { biconnectedSets = new ArrayList(); Iterator connectedSets = new ConnectivityInspector(graph).connectedSets().iterator(); while (connectedSets.hasNext()) { Set connectedSet = (Set) connectedSets.next(); if (connectedSet.size() == 1) { continue; } Graph subgraph = new Subgraph(graph, connectedSet, null); // do DFS // Stack for the DFS Stack vertexStack = new Stack(); Set visitedVertices = new HashSet(); Map parent = new HashMap(); List dfsVertices = new ArrayList(); Set treeEdges = new HashSet(); Object currentVertex = subgraph.vertexSet().toArray()[0]; vertexStack.push(currentVertex); visitedVertices.add(currentVertex); while (!vertexStack.isEmpty()) { currentVertex = vertexStack.pop(); Object parentVertex = parent.get(currentVertex); if (parentVertex != null) { Edge edge = subgraph.getEdge(parentVertex, currentVertex); // tree edge treeEdges.add(edge); } visitedVertices.add(currentVertex); dfsVertices.add(currentVertex); Iterator edges = subgraph.edgesOf(currentVertex).iterator(); while (edges.hasNext()) { // find a neighbour vertex of the current vertex Edge edge = (Edge)edges.next(); if (!treeEdges.contains(edge)) { Object nextVertex = edge.oppositeVertex(currentVertex); if (!visitedVertices.contains(nextVertex)) { vertexStack.push(nextVertex); parent.put(nextVertex, currentVertex); } else { // non-tree edge } } } } // DFS is finished. Now create the auxiliary graph h // Add all the tree edges as vertices in h SimpleGraph h = new SimpleGraph(); h.addAllVertices(treeEdges); visitedVertices.clear(); Set connected = new HashSet(); for (Iterator it = dfsVertices.iterator(); it.hasNext();) { Object v = it.next(); visitedVertices.add(v); // find all adjacent non-tree edges for (Iterator adjacentEdges = subgraph.edgesOf(v).iterator(); adjacentEdges.hasNext();) { Edge l = (Edge) adjacentEdges.next(); if (!treeEdges.contains(l)) { h.addVertex(l); Object u = l.oppositeVertex(v); // we need to check if (u,v) is a back-edge if (!visitedVertices.contains(u)) { while (u != v) { Object pu = parent.get(u); Edge f = subgraph.getEdge(u, pu); h.addEdge(f, l); if (!connected.contains(f)) { connected.add(f); u = pu; } else { u = v; } } } } } } ConnectivityInspector connectivityInspector = new ConnectivityInspector(h); biconnectedSets.addAll(connectivityInspector.connectedSets()); } } return biconnectedSets; } /** * Returns a list of <code>Set</code>s, where each set contains all edge that are * in the same biconnected component. All graph edges occur in exactly one set. * * @return a list of <code>Set</code>s, where each set contains all edge that are * in the same biconnected component */ @TestMethod("testBiconnectedSets") public List biconnectedSets( ) { return lazyFindBiconnectedSets( ); } /* public List hopcroftTarjanKnuthFindBiconnectedSets() { Map rank; Map parent; Map untagged; Map link; Stack activeStack; Map min; int nn; return biconnectedSets; } */ private void init() { biconnectedSets = null; } /** * @see org._3pq.jgrapht.event.GraphListener#edgeAdded(GraphEdgeChangeEvent) */ public void edgeAdded( GraphEdgeChangeEvent e ) { init( ); } /** * @see org._3pq.jgrapht.event.GraphListener#edgeRemoved(GraphEdgeChangeEvent) */ public void edgeRemoved( GraphEdgeChangeEvent e ) { init( ); } /** * @see org._3pq.jgrapht.event.VertexSetListener#vertexAdded(GraphVertexChangeEvent) */ public void vertexAdded( GraphVertexChangeEvent e ) { init( ); } /** * @see org._3pq.jgrapht.event.VertexSetListener#vertexRemoved(GraphVertexChangeEvent) */ public void vertexRemoved( GraphVertexChangeEvent e ) { init( ); } }