/*
* This file is part of the Trickl Open Source Libraries.
*
* Trickl Open Source Libraries - http://open.trickl.com/
*
* Copyright (C) 2011 Tim Gee.
*
* 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.planar;
import com.trickl.graph.edges.DirectedEdge;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/* This is an implemention of
* "Leftist Canonical Ordering"
* Melanie Badent, Micael Baur, Ulrik Brandes, Sabine Cornelsen
* Department of Computer and Information Science
* University of Konstanz (2009)
* http://www.informatik.uni-konstanz.de/~cornelse/Papers/bbbc-lco-gd09.pdf
*/
public class LeftistPlanarCanonicalOrdering<V, E> implements PlanarCanonicalOrdering<V, E> {
private static class BeltCutFace<V> {
public BeltCutFace(LinkedList<DirectedEdge<V>> edges) {
this(edges, null);
}
public BeltCutFace(LinkedList<DirectedEdge<V>> edgeChain, V rightmostStopper) {
this.edgeChain = edgeChain;
this.rightmostStopper = rightmostStopper;
}
@Override
public String toString() {
return edgeChain.toString();
}
LinkedList<DirectedEdge<V>> edgeChain;
V rightmostStopper;
}
PlanarGraph<V, E> graph;
List<BeltCutFace<V>> belt;
Set<DirectedEdge<V>> markedEdges;
Map<V, Integer> vertexCutFaces;
Map<V, Integer> vertexCutEdges;
int candidateIndex;
/**
* Create an instance of the canonical ordering solver
* @param graph A triconnected graph
*/
public LeftistPlanarCanonicalOrdering() {
}
/**
* Get the canonical ordering of the graph vertices
* (See Algorithm 2 in the cited paper.)
* @param graph A triconnected graph
* @param first The first vertex in the ordering (must be on the outside of the graph)
* @return The vertices in leftist canonical order
*/
@Override
public List<V> getOrder(PlanarGraph<V, E> graph, V first) {
if (graph == null) {
throw new NullPointerException();
}
Set<V> vertices = graph.vertexSet();
List<V> ordering = new ArrayList<V>(vertices.size());
if (first == null) {
first = graph.getBoundary().getSource();
}
if (vertices.isEmpty()) {
return ordering;
}
else if (vertices.size() == 1) {
ordering.add(first);
return ordering;
}
this.graph = graph;
belt = new LinkedList<BeltCutFace<V>>();
markedEdges = new HashSet<DirectedEdge<V>>();
vertexCutFaces = new HashMap<V, Integer>();
vertexCutEdges = new HashMap<V, Integer>();
V second = PlanarGraphs.getPrevVertexOnBoundary(graph, first);
V last = PlanarGraphs.getNextVertexOnBoundary(graph, first);
for (V vertex : vertices) {
vertexCutFaces.put(vertex, 0);
vertexCutEdges.put(vertex, 0);
}
vertexCutFaces.put(last, 1);
markedEdges.add(new DirectedEdge<V>(first, second));
markedEdges.add(new DirectedEdge<V>(second, first));
LinkedList<DirectedEdge<V>> initialBeltEdgeChain = new LinkedList<DirectedEdge<V>>();
initialBeltEdgeChain.add(new DirectedEdge<V>(second, first));
initialBeltEdgeChain.add(new DirectedEdge<V>(first, second));
initialBeltEdgeChain.add(new DirectedEdge<V>(second, first));
belt.add(new BeltCutFace<V>(initialBeltEdgeChain, null));
candidateIndex = 0;
while (!belt.isEmpty()) {
ordering.addAll(getLeftmostFeasibleCandidate());
updateBelt();
}
return ordering;
}
/**
* If the vertex appears non-consecutively in the belt
* @param vertex
* @return
*/
private boolean isForbidden(V vertex) {
return vertexCutFaces.get(vertex) > (vertexCutEdges.get(vertex) + 1);
}
/**
* If the vertex is consecutive and it appears more than twice in the belt
* @param vertex
* @return
*/
private boolean isSingular(V vertex) {
return 2 < vertexCutFaces.get(vertex)
&& vertexCutFaces.get(vertex) == vertexCutEdges.get(vertex) + 1;
}
/**
* (See Algorithm 3 in the cited paper.)
*/
private List<V> getLeftmostFeasibleCandidate() {
boolean found = false;
BeltCutFace<V> candidate = null;
do {
candidate = belt.get(candidateIndex);
List<DirectedEdge<V>> edgeChain = candidate.edgeChain;
int j = edgeChain.size() - 1;
if (!edgeChain.get(0).getSource().equals(
edgeChain.get(j).getTarget()))
{
// Look from right to left until a stopper is found
while (j > 0 &&
!(isForbidden(edgeChain.get(j).getSource())
|| isSingular(edgeChain.get(j).getSource()))) {
--j;
}
if (j > 0) {
candidate.rightmostStopper = edgeChain.get(j).getSource();
}
// If the candidate contains no stopper or it is a singular singleton
// then it is the next locally feasible candidate
if (j == 0
|| (isSingular(candidate.rightmostStopper)
&& edgeChain.size() == 2)) {
found = true;
for (DirectedEdge<V> edge : candidate.edgeChain) {
markedEdges.add(new DirectedEdge<V>(edge.getTarget(),
edge.getSource()));
}
}
}
if (!found) {
candidateIndex++;
if (candidateIndex == belt.size()) {
throw new IllegalArgumentException("Supplied graph must be planar and triconnected.");
}
}
}
while (!found);
List<V> path = new LinkedList<V>();
for (int i = 0; i < candidate.edgeChain.size() - 1; ++i) {
path.add(candidate.edgeChain.get(i).getTarget());
}
return path;
}
/**
* (See Algorithm 4 in the cited paper.)
*
*/
private List<BeltCutFace<V>> getBeltExtension(BeltCutFace<V> candidate) {
List<BeltCutFace<V>> extension = new LinkedList<BeltCutFace<V>>();
for (int j = 1; j < candidate.edgeChain.size(); ++j) {
DirectedEdge<V> first = candidate.edgeChain.get(j);
V start = first.getSource();
V end = first.getTarget();
do {
first = new DirectedEdge<V>(first.getSource(),
graph.getNextVertex(first.getTarget(),
first.getSource()));
vertexCutEdges.put(first.getTarget(),
vertexCutEdges.get(first.getTarget()) + 1);
if (!markedEdges.contains(first)) {
// New cut face
LinkedList<DirectedEdge<V>> chain = new LinkedList<DirectedEdge<V>>();
DirectedEdge<V> edge = first;
do {
markedEdges.add(edge);
chain.add(edge);
vertexCutFaces.put(edge.getTarget(),
vertexCutFaces.get(edge.getTarget()) + 1);
edge = new DirectedEdge<V>(edge.getTarget(),
graph.getPrevVertex(edge.getTarget(),
edge.getSource()));
}
while (!edge.getTarget().equals(start) &&
!edge.getTarget().equals(end));
markedEdges.add(edge);
chain.add(edge);
extension.add(new BeltCutFace(chain, null));
}
}
while(!first.getTarget().equals(end));
}
return extension;
}
/**
* (See Algorithm 5 in the cited paper.)
*
*/
private void updateBelt() {
BeltCutFace<V> candidate = belt.get(candidateIndex);
if (candidate.rightmostStopper != null &&
isSingular(candidate.rightmostStopper)) {
// Remove neighbouring items with the same singleton
for (int j = candidateIndex - 1; j >= 0; belt.remove(j), j--, candidateIndex--)
{
BeltCutFace<V> neighbour = belt.get(j);
if (neighbour.edgeChain.size() != 2) {
break;
}
}
for (int j = candidateIndex + 1; j < belt.size(); belt.remove(j))
{
BeltCutFace<V> neighbour = belt.get(j);
if (neighbour.edgeChain.size() != 2) {
break;
}
}
}
BeltCutFace<V> predecessor = candidateIndex > 0 ?
belt.get(candidateIndex - 1) : null;
BeltCutFace<V> successor = candidateIndex < (belt.size() - 1) ?
belt.get(candidateIndex + 1) : null;
if (successor != null) {
successor.edgeChain.remove(0);
/*
if (successor.edgeChain.isEmpty()) {
belt.remove(candidateIndex + 1);
}
*
*/
}
List<BeltCutFace<V>> extension = getBeltExtension(candidate);
belt.remove(candidateIndex);
belt.addAll(candidateIndex, extension);
if (predecessor != null) {
DirectedEdge<V> firstEdge = predecessor.edgeChain.get(0);
DirectedEdge<V> lastEdge = predecessor.edgeChain.remove(predecessor.edgeChain.size() - 1);
if (lastEdge.getSource().equals(predecessor.rightmostStopper)
|| lastEdge.getTarget().equals(firstEdge.getSource())) {
predecessor.rightmostStopper = null;
--candidateIndex;
}
}
}
}