1236 lines
41 KiB
Java
1236 lines
41 KiB
Java
|
|
/*
|
||
|
|
* Graph.java 2005-05-12
|
||
|
|
*
|
||
|
|
* Dirk Ansorge, FernUniversitaet Hagen
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
|
||
|
|
package twodsack.util.graph;
|
||
|
|
|
||
|
|
import java.io.*;
|
||
|
|
import twodsack.io.*;
|
||
|
|
import twodsack.operation.setoperation.*;
|
||
|
|
import twodsack.set.*;
|
||
|
|
import twodsack.setelement.*;
|
||
|
|
import twodsack.setelement.datatype.*;
|
||
|
|
import twodsack.setelement.datatype.basicdatatype.*;
|
||
|
|
import twodsack.util.*;
|
||
|
|
import twodsack.util.collection.*;
|
||
|
|
import twodsack.util.collectiontype.*;
|
||
|
|
import twodsack.util.comparator.*;
|
||
|
|
import java.util.Enumeration;
|
||
|
|
import java.util.Hashtable;
|
||
|
|
import java.util.Iterator;
|
||
|
|
import java.util.LinkedList;
|
||
|
|
import java.util.ListIterator;
|
||
|
|
import java.util.NoSuchElementException;
|
||
|
|
import java.util.Stack;
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This class implements an undirected graph. The graph's vertices are of type {@link twodsack.setelement.Element}. The set of vertices <i>V</i>
|
||
|
|
* is of type {@link twodsack.set.ElemMultiSet}. Additionally, the set of vertices is stored as Vertex array. The successors for vertices
|
||
|
|
* are stored in adjacency lists, which are implemented as {@link java.util.LinkedList}s, here.<p>
|
||
|
|
* The most interesting methods of this class are {@link #computeFaces()}, which computes a set of faces for a graph, and {@link #connectedComponents()}.
|
||
|
|
* The latter computes the connected components of a graph and stores them in a {@link ConnectedComponentsPair}.<p>
|
||
|
|
* Note, that the vertices for this graph can hold any type of objects that extends the Element class.
|
||
|
|
*/
|
||
|
|
public class Graph {
|
||
|
|
/*
|
||
|
|
* fields
|
||
|
|
*/
|
||
|
|
static final int NUMBER_OF_BUCKETS = 499;
|
||
|
|
|
||
|
|
private ElemMultiSet v; //vertices
|
||
|
|
private Vertex[] vArr; //array of vertices
|
||
|
|
private LinkedList[] succLists; //lists of successors for every vertex
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stored in this array is the same set of vertices as in vArr. Unlike <tt>vArr</tt>, the vertices are sorted in ascending order.
|
||
|
|
* The array has size n. This array is used to support the <tt>getVertex</tt> method, particularly it allows binary search.
|
||
|
|
*/
|
||
|
|
private Vertex[] sortedVertices;
|
||
|
|
/**
|
||
|
|
* This array stores the indices which 'translate' from <tt>sortedVertices</tt> to <tt>vArr</tt>, i.e. the vertex V with with
|
||
|
|
* sortedVertices[i] = V can be found in vArr[indexArray[i]].
|
||
|
|
*/
|
||
|
|
private int[] indexArray;
|
||
|
|
/**
|
||
|
|
* This flag is true, if <tt>sortedVertices</tt> is defined.
|
||
|
|
*/
|
||
|
|
private boolean sortedVerticesValid = false;
|
||
|
|
|
||
|
|
/*
|
||
|
|
* constructors
|
||
|
|
*/
|
||
|
|
/**
|
||
|
|
* Constructs an 'empty' graph.
|
||
|
|
* All fields are initialized.
|
||
|
|
*/
|
||
|
|
public Graph() {
|
||
|
|
v = new ElemMultiSet(new ElemComparator());
|
||
|
|
vArr = new Vertex[0];
|
||
|
|
succLists = new LinkedList[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Constructs a graph from a set of edges.
|
||
|
|
* When using this constructor, the internal fields are set including the <tt>succLists</tt> field. The <tt>succLists</tt> are the list of
|
||
|
|
* successors for each vertex. They are computed using a hash table.
|
||
|
|
*
|
||
|
|
* @param edges the set of edges
|
||
|
|
*/
|
||
|
|
public Graph(SegMultiSet edges) {
|
||
|
|
ElemMultiSet cleanList = makeCleanList(edges);
|
||
|
|
|
||
|
|
int initialCapacity = NUMBER_OF_BUCKETS;
|
||
|
|
Hashtable pointsHT = new Hashtable(initialCapacity);
|
||
|
|
|
||
|
|
//fill in the segments points as pointers to original points
|
||
|
|
Iterator eit = cleanList.iterator();
|
||
|
|
PointLink htEntry;
|
||
|
|
Segment actSeg;
|
||
|
|
int counter = 0;
|
||
|
|
int count2 = 0;
|
||
|
|
while (eit.hasNext()) {
|
||
|
|
actSeg = (Segment)((MultiSetEntry)eit.next()).value;
|
||
|
|
htEntry = new PointLink(actSeg.getStartpoint());
|
||
|
|
//check whether htEntry already exists in hash table
|
||
|
|
//if not, give it a new number and insert it
|
||
|
|
if (!pointsHT.containsKey(htEntry.linkedPoint)) {
|
||
|
|
htEntry.number = counter;
|
||
|
|
counter++;
|
||
|
|
pointsHT.put(htEntry.linkedPoint,htEntry);
|
||
|
|
|
||
|
|
PointLink pl = (PointLink)pointsHT.get(actSeg.getStartpoint());
|
||
|
|
}//if
|
||
|
|
|
||
|
|
//do the same with endpoint
|
||
|
|
htEntry = new PointLink(actSeg.getEndpoint());
|
||
|
|
|
||
|
|
if (!pointsHT.containsKey(htEntry.linkedPoint)) {
|
||
|
|
htEntry.number = counter;
|
||
|
|
counter++;
|
||
|
|
pointsHT.put(htEntry.linkedPoint,htEntry);
|
||
|
|
|
||
|
|
PointLink pl2 = (PointLink)pointsHT.get(actSeg.getEndpoint());
|
||
|
|
}//if
|
||
|
|
count2++;
|
||
|
|
}//while eit
|
||
|
|
|
||
|
|
//construct list of vertices from hashtable
|
||
|
|
Enumeration enumr = pointsHT.elements();
|
||
|
|
vArr = new Vertex[counter];
|
||
|
|
Object elem;
|
||
|
|
while (enumr.hasMoreElements()) {
|
||
|
|
elem = enumr.nextElement();
|
||
|
|
vArr[((PointLink)elem).number] = new Vertex(((PointLink)elem).linkedPoint,((PointLink)elem).number);
|
||
|
|
}//while enumr
|
||
|
|
|
||
|
|
//construct succLists
|
||
|
|
count2 = 0;
|
||
|
|
succLists = new LinkedList[vArr.length];
|
||
|
|
for (int i = 0; i < succLists.length; i++) succLists[i] = new LinkedList();
|
||
|
|
eit = cleanList.iterator();
|
||
|
|
int pointPosS;
|
||
|
|
int pointPosE;
|
||
|
|
while (eit.hasNext()) {
|
||
|
|
actSeg = (Segment)((MultiSetEntry)eit.next()).value;
|
||
|
|
//get the numbers of the segments' endpoints
|
||
|
|
pointPosS = ((PointLink)pointsHT.get(actSeg.getStartpoint())).number;
|
||
|
|
pointPosE = ((PointLink)pointsHT.get(actSeg.getEndpoint())).number;
|
||
|
|
//set the proper vertices in succLists
|
||
|
|
succLists[pointPosS].add(vArr[pointPosE]);
|
||
|
|
succLists[pointPosE].add(vArr[pointPosS]);
|
||
|
|
count2++;
|
||
|
|
}//while eit
|
||
|
|
}//end constructor
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Constructs a graph from sets of vertices and edges.
|
||
|
|
* When using this constructor, the internal fields are set including the <tt>succLists</tt> field. The <tt>succLists</tt> are the
|
||
|
|
* lists of successors for each vertex. They are computed using a hash table.<p>
|
||
|
|
* Make sure, that the types of the objects stored in the vertices match the object types in the edges. Otherwise a
|
||
|
|
* {@link twodsack.setelement.datatype.WrongTypeException} will be thrown.
|
||
|
|
*
|
||
|
|
* @param vertices the set of vertices
|
||
|
|
* @param edges the set of edges
|
||
|
|
* @throws WrongTypeException if the object types of the vertices/edges doesn't match
|
||
|
|
*/
|
||
|
|
public Graph(ElemMultiSet vertices,PairMultiSet edges) throws WrongTypeException {
|
||
|
|
v = makeCleanList(vertices.copy());
|
||
|
|
PairMultiSet cleanEdges = makeCleanList(edges.copy());
|
||
|
|
|
||
|
|
int initialCapacity = NUMBER_OF_BUCKETS;
|
||
|
|
Hashtable vertexHT = new Hashtable(initialCapacity);
|
||
|
|
|
||
|
|
//put vertices in hashtable
|
||
|
|
Iterator it = v.iterator();
|
||
|
|
ObjectLink htEntry;
|
||
|
|
Element actElem;
|
||
|
|
int counter = 0;
|
||
|
|
while (it.hasNext()) {
|
||
|
|
actElem = (Element)((MultiSetEntry)it.next()).value;
|
||
|
|
htEntry = new ObjectLink(actElem);
|
||
|
|
//check whether htEntry already exists in hashtable
|
||
|
|
//if not, give it a new number and insert it
|
||
|
|
if (!vertexHT.containsKey(htEntry.linkedObject)) {
|
||
|
|
htEntry.number = counter;
|
||
|
|
counter++;
|
||
|
|
vertexHT.put(htEntry.linkedObject,htEntry);
|
||
|
|
}//if
|
||
|
|
}//while it
|
||
|
|
|
||
|
|
//construct list of vertices from hashtable
|
||
|
|
Enumeration enumr = vertexHT.elements();
|
||
|
|
vArr = new Vertex[counter];
|
||
|
|
ObjectLink elem;
|
||
|
|
while (enumr.hasMoreElements()) {
|
||
|
|
elem = (ObjectLink)enumr.nextElement();
|
||
|
|
vArr[((ObjectLink)elem).number] = new Vertex((Element)elem.linkedObject,((ObjectLink)elem).number);
|
||
|
|
}//while enumr
|
||
|
|
|
||
|
|
//construct succLists
|
||
|
|
succLists = new LinkedList[vArr.length];
|
||
|
|
for (int i = 0; i < succLists.length; i++) succLists[i] = new LinkedList();
|
||
|
|
it = cleanEdges.iterator();
|
||
|
|
int pos1 = 0;
|
||
|
|
int pos2 = 0;
|
||
|
|
ElemPair actPair;
|
||
|
|
while (it.hasNext()) {
|
||
|
|
actPair = (ElemPair)((MultiSetEntry)it.next()).value;
|
||
|
|
//get the numbers for both objects in actPair
|
||
|
|
try {
|
||
|
|
pos1 = ((ObjectLink)vertexHT.get(actPair.first)).number;
|
||
|
|
pos2 = ((ObjectLink)vertexHT.get(actPair.second)).number;
|
||
|
|
} catch (Exception e) {
|
||
|
|
throw new WrongTypeException("Graph.constructor(...): An error occured while constructing the graph. Edges don't match to vertices.");
|
||
|
|
}//catch
|
||
|
|
|
||
|
|
//set the proper vertices in succLists
|
||
|
|
succLists[pos1].add(vArr[pos2]);
|
||
|
|
succLists[pos2].add(vArr[pos1]);
|
||
|
|
}//while it
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Constructs a subgraph from vertices and edges.
|
||
|
|
*
|
||
|
|
* @param msV the set of vertices
|
||
|
|
* @param msE the set of edges
|
||
|
|
*/
|
||
|
|
protected Graph (MultiSet msV, MultiSet msE) {
|
||
|
|
//extract elements from msV and msE
|
||
|
|
ElemMultiSet extractV = new ElemMultiSet(new ElemComparator());
|
||
|
|
Iterator it = msV.iterator();
|
||
|
|
while (it.hasNext())
|
||
|
|
extractV.add(((Vertex)((MultiSetEntry)it.next()).value).value);
|
||
|
|
PairMultiSet extractE = new PairMultiSet(new ElemPairComparator());
|
||
|
|
it = msE.iterator();
|
||
|
|
while (it.hasNext())
|
||
|
|
extractE.add(new ElemPair((Element)(((Edge)((MultiSetEntry)it.next()).value).first).value,
|
||
|
|
(Element)(((Edge)((MultiSetEntry)it.next()).value).second).value));
|
||
|
|
|
||
|
|
ElemMultiSet vertices = extractV;
|
||
|
|
PairMultiSet edges = extractE;
|
||
|
|
|
||
|
|
/* COPY from constructor above */
|
||
|
|
v = makeCleanList(vertices.copy());
|
||
|
|
|
||
|
|
PairMultiSet cleanEdges = makeCleanList(edges.copy());
|
||
|
|
|
||
|
|
int initialCapacity = NUMBER_OF_BUCKETS;
|
||
|
|
Hashtable vertexHT = new Hashtable(initialCapacity);
|
||
|
|
|
||
|
|
//put vertices in hashtable
|
||
|
|
it = v.iterator();
|
||
|
|
ObjectLink htEntry;
|
||
|
|
Element actElem;
|
||
|
|
int counter = 0;
|
||
|
|
while (it.hasNext()) {
|
||
|
|
actElem = (Element)((MultiSetEntry)it.next()).value;
|
||
|
|
htEntry = new ObjectLink(actElem);
|
||
|
|
//check whether htEntry already exists in hashtable
|
||
|
|
//if not, give it a new number and insert it
|
||
|
|
if (!vertexHT.containsKey(htEntry.linkedObject)) {
|
||
|
|
htEntry.number = counter;
|
||
|
|
counter++;
|
||
|
|
vertexHT.put(htEntry.linkedObject,htEntry);
|
||
|
|
}//if
|
||
|
|
}//while it
|
||
|
|
|
||
|
|
//construct list of vertices from hashtable
|
||
|
|
Enumeration enumr = vertexHT.elements();
|
||
|
|
vArr = new Vertex[counter];
|
||
|
|
ObjectLink elem;
|
||
|
|
while (enumr.hasMoreElements()) {
|
||
|
|
elem = (ObjectLink)enumr.nextElement();
|
||
|
|
vArr[((ObjectLink)elem).number] = new Vertex((Element)elem.linkedObject,((ObjectLink)elem).number);
|
||
|
|
}//while enumr
|
||
|
|
|
||
|
|
//construct succLists
|
||
|
|
succLists = new LinkedList[vArr.length];
|
||
|
|
for (int i = 0; i < succLists.length; i++) succLists[i] = new LinkedList();
|
||
|
|
it = cleanEdges.iterator();
|
||
|
|
int pos1;
|
||
|
|
int pos2;
|
||
|
|
ElemPair actPair;
|
||
|
|
while (it.hasNext()) {
|
||
|
|
actPair = (ElemPair)((MultiSetEntry)it.next()).value;
|
||
|
|
//get the numbers for both objects in actPair
|
||
|
|
pos1 = ((ObjectLink)vertexHT.get(actPair.first)).number;
|
||
|
|
pos2 = ((ObjectLink)vertexHT.get(actPair.second)).number;
|
||
|
|
//set the proper vertices in succLists
|
||
|
|
succLists[pos1].add(vArr[pos2]);
|
||
|
|
succLists[pos2].add(vArr[pos1]);
|
||
|
|
}//while it
|
||
|
|
|
||
|
|
//now replace the ordinary vertices with ElemMultiSets
|
||
|
|
ElemComparator ec = new ElemComparator();
|
||
|
|
for (int i = 0; i < vArr.length; i++) {
|
||
|
|
Vertex oldV = vArr[i];
|
||
|
|
ElemMultiSet newV = new ElemMultiSet(ec);
|
||
|
|
newV.add(oldV.value);
|
||
|
|
oldV.value = newV;
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
* methods
|
||
|
|
*/
|
||
|
|
/**
|
||
|
|
* Returns the connected components of <i>this</i>.
|
||
|
|
* The result of this method is stored in a {@link ConnectedComponentsPair}.
|
||
|
|
*
|
||
|
|
* @return the connected components as a ConnectedComponentsPair
|
||
|
|
*/
|
||
|
|
public ConnectedComponentsPair connectedComponents() {
|
||
|
|
boolean [] marks = new boolean [vArr.length];
|
||
|
|
for (int i = 0; i < marks.length; i++) {
|
||
|
|
marks[i] = false;
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
int actElem = 0;
|
||
|
|
LinkedList compListV = new LinkedList();
|
||
|
|
LinkedList compListE = new LinkedList();
|
||
|
|
|
||
|
|
if (!(v.size() == 0)) {
|
||
|
|
//compute depth-first spanning trees
|
||
|
|
while (hasUnvisitedVertices(marks)) {
|
||
|
|
MultiSet compV = new MultiSet();
|
||
|
|
MultiSet compE = new MultiSet();
|
||
|
|
actElem = getUnvisitedVertex(marks);
|
||
|
|
depthFirst(actElem,marks,compV,compE);
|
||
|
|
compListV.add(compV);
|
||
|
|
compListE.add(compE);
|
||
|
|
}//while
|
||
|
|
}//if
|
||
|
|
else {
|
||
|
|
//System.out.println("Graph has no vertices.");
|
||
|
|
}//else
|
||
|
|
|
||
|
|
ConnectedComponentsPair ccp = new ConnectedComponentsPair(compListV,compListE);
|
||
|
|
|
||
|
|
return ccp;
|
||
|
|
}//end method connectedComponentsV
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Prints the data of <i>this</i> to standard output.
|
||
|
|
*/
|
||
|
|
public void print () {
|
||
|
|
System.out.println("vertices: ");
|
||
|
|
for (int i = 0; i < vArr.length; i++) vArr[i].print();
|
||
|
|
System.out.println("\nedges: ");
|
||
|
|
printSuccLists();
|
||
|
|
}//end method print
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Prints the adjacency lists to standard output.
|
||
|
|
*/
|
||
|
|
public void printSuccLists() {
|
||
|
|
for (int i = 0; i < vArr.length; i++) {
|
||
|
|
System.out.println("\nvertex: ");
|
||
|
|
vArr[i].print();
|
||
|
|
System.out.println("successors: ");
|
||
|
|
for (int j = 0; j < succLists[i].size(); j++) {
|
||
|
|
((Vertex)succLists[i].get(j)).print(); }
|
||
|
|
}//for i
|
||
|
|
}//end method printSuccLists
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Computes a depth first search on the graph and stores the result in the parameters.
|
||
|
|
*
|
||
|
|
* @param actElem the index of the actually visited vertex
|
||
|
|
* @param marks an array which indicates which vertices were already visited
|
||
|
|
* @param compV represents the actual connected component; stores the vertices of it
|
||
|
|
* @param compE represents the actual connected component; stores the edges of it
|
||
|
|
*/
|
||
|
|
private void depthFirst (int actElem, boolean[] marks, MultiSet compV, MultiSet compE) {
|
||
|
|
marks[actElem] = true;
|
||
|
|
|
||
|
|
//check, whether a vertex has a self-edge.
|
||
|
|
//though this is not relevant for the connected component algo
|
||
|
|
//it is needed for some algorithms in SetOps, e.g. overlapReduce.
|
||
|
|
ListIterator lit = succLists[actElem].listIterator(0);
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
|
||
|
|
if (((Vertex)lit.next()).equal(vArr[actElem])) {
|
||
|
|
compE.add(new Edge(vArr[actElem],vArr[actElem]));
|
||
|
|
}//if
|
||
|
|
}//while
|
||
|
|
|
||
|
|
compV.add(vArr[actElem].copy());
|
||
|
|
while (hasUnvisitedSon(actElem,marks)) {
|
||
|
|
int next = nextUnvisitedSon(actElem,marks);
|
||
|
|
compE.add(new Edge(vArr[actElem],vArr[next]));
|
||
|
|
depthFirst(next,marks,compV,compE);
|
||
|
|
}//while
|
||
|
|
}//end method depthFirst
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns true, if the given array has still FALSE buckets.
|
||
|
|
*
|
||
|
|
* @param marks an array which indicates which vertices were already visited
|
||
|
|
*/
|
||
|
|
private boolean hasUnvisitedVertices (boolean [] marks) {
|
||
|
|
for (int i = 0; i < marks.length; i++) {
|
||
|
|
if (!marks[i]) { return true; }
|
||
|
|
}//for i
|
||
|
|
return false;
|
||
|
|
}//end method hasUnvisitedVertices
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the array index of the first FALSE bucket.
|
||
|
|
* @param marks an array which indicates which vertices were already visited
|
||
|
|
*/
|
||
|
|
private int getUnvisitedVertex(boolean [] marks) {
|
||
|
|
for (int i = 0; i < marks.length; i++) {
|
||
|
|
if (!marks[i]) { return i; }
|
||
|
|
}//for i
|
||
|
|
return -1;
|
||
|
|
}//end method getUnvisitedVertex
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns true, if a vertex has a successor which wasn't visited yet.
|
||
|
|
*
|
||
|
|
* @param actElem the index of the vertex
|
||
|
|
* @param marks an array which indicates which vertices were already visited
|
||
|
|
*/
|
||
|
|
private boolean hasUnvisitedSon(int actElem, boolean[] marks) {
|
||
|
|
ListIterator lit = succLists[actElem].listIterator(0);
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
Vertex actSucc = (Vertex)lit.next();
|
||
|
|
if (!marks[actSucc.number]) return true;
|
||
|
|
}//while
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}//end method hasUnvisitedSon
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the next index of a vertex which wasn't visited yet.
|
||
|
|
*
|
||
|
|
* @param actElem the index of the vertex
|
||
|
|
* @param marks an array which indicates which vertices were already visited
|
||
|
|
*/
|
||
|
|
private int nextUnvisitedSon (int actElem, boolean[] marks) {
|
||
|
|
ListIterator lit = succLists[actElem].listIterator(0);
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
Vertex actSucc = (Vertex)lit.next();
|
||
|
|
if (!marks[actSucc.number]) return actSucc.number;
|
||
|
|
}//while
|
||
|
|
|
||
|
|
return -1;
|
||
|
|
}//end method nextUnvisitedSon
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Removes all duplicates from the given set.
|
||
|
|
* The PairMultiSet that is passed to the Graph constructor may have duplicates. That would result in duplicate edges.
|
||
|
|
* Since no duplicate edges are allowed in the graph representation, they are removed here.
|
||
|
|
*
|
||
|
|
* @param plIn the set of edges as PairMultiSet
|
||
|
|
* @return the 'clean' set
|
||
|
|
* @throws WrongTypeException is thrown by the compare() method for Element
|
||
|
|
*/
|
||
|
|
private static PairMultiSet makeCleanList (PairMultiSet plIn) throws WrongTypeException {
|
||
|
|
//first, traverse Pairlist and twist all Pairs that way, that the smaller
|
||
|
|
//Element is at first position
|
||
|
|
Iterator lit = plIn.iterator();
|
||
|
|
boolean isSegment = false;
|
||
|
|
|
||
|
|
if (!plIn.isEmpty() &&
|
||
|
|
((((ElemPair)plIn.first()).first) instanceof Segment)) isSegment = true;
|
||
|
|
|
||
|
|
ElemPair actPair;
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
actPair = (ElemPair)((MultiSetEntry)lit.next()).value;
|
||
|
|
if (isSegment) {
|
||
|
|
((Segment)actPair.first).align();
|
||
|
|
((Segment)actPair.second).align();
|
||
|
|
}//if
|
||
|
|
if (actPair.first.compare(actPair.second) == 1) actPair.twist();
|
||
|
|
}//while
|
||
|
|
|
||
|
|
PairMultiSet retSet = new PairMultiSet(new ElemPairComparator());
|
||
|
|
retSet.addAll(plIn);
|
||
|
|
//remove duplicates manually
|
||
|
|
Iterator it = retSet.iterator();
|
||
|
|
while (it.hasNext())
|
||
|
|
((MultiSetEntry)it.next()).number = 1;
|
||
|
|
|
||
|
|
return retSet;
|
||
|
|
}//end method makeCleanList
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Removes all duplicates from the given set.
|
||
|
|
* The ElemMultiSet that is passed to the Graph constructor may have duplicates. That would result in duplicate vertices. Since no duplicates
|
||
|
|
* are allowed in the graph representation, they are removed here.
|
||
|
|
*
|
||
|
|
* @param elIn the set of vertices
|
||
|
|
* @return the 'clean' set
|
||
|
|
* @throws WrongTypeException is thrown by the compare() method of Element
|
||
|
|
*/
|
||
|
|
private static ElemMultiSet makeCleanList (ElemMultiSet elIn) throws WrongTypeException {
|
||
|
|
//check for segments and align them if necessary (needed for compare)
|
||
|
|
if (!elIn.isEmpty() &&
|
||
|
|
(((Element)(elIn.first()) instanceof Segment))) {
|
||
|
|
Iterator lit = elIn.iterator();
|
||
|
|
Element actEl;
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
actEl = (Element)((MultiSetEntry)lit.next()).value;
|
||
|
|
((Segment)actEl).align();
|
||
|
|
}//while
|
||
|
|
}//if
|
||
|
|
|
||
|
|
ElemMultiSet retSet = new ElemMultiSet(new ElemComparator());
|
||
|
|
retSet.addAll(elIn);
|
||
|
|
retSet = SetOps.rdup(retSet);
|
||
|
|
|
||
|
|
return retSet;
|
||
|
|
}//end method makeCleanList
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Computes a reduced pair for a connected components.
|
||
|
|
* For every component, the vertices of <tt>compVertices</tt> may occur in more than one edge in the appropriate <tt>compEdges</tt>.
|
||
|
|
* In the result of this method
|
||
|
|
* this is no longer true. Example: A ConnectedComponentsPair could be:<p>
|
||
|
|
* <tt>compVertices: (A,B,C,D)<br>
|
||
|
|
* compEdges: (A-B, B-C, C-D)</tt><p>
|
||
|
|
* As you can see, some of the vertices of <tt>compVertices</tt> occur more than once in <tt>compEdges</tt>. Now, a reduced pair is
|
||
|
|
* computed. One possible result could be:<p>
|
||
|
|
* <tt>compVertices: ()<br>
|
||
|
|
* compEdges: (A-B, C-D)</tt><p>
|
||
|
|
* Another result is:<p>
|
||
|
|
* <tt>compVertices: (A,D)<br>
|
||
|
|
* compEdges: (B-C)</tt><p>
|
||
|
|
* All of the vertices that are not used in the reduced set of edges are stored in <tt>compVertices</tt>.
|
||
|
|
*
|
||
|
|
* @param ccp the 'in' pair
|
||
|
|
* @return the reduced pair
|
||
|
|
*/
|
||
|
|
public ConnectedComponentsPair computeReducedPair (ConnectedComponentsPair ccp) {
|
||
|
|
//make a copy of succLists
|
||
|
|
LinkedList[] slCopy = new LinkedList[succLists.length];
|
||
|
|
for (int i = 0; i < succLists.length; i++) {
|
||
|
|
slCopy[i] = (LinkedList)succLists[i].clone(); }
|
||
|
|
|
||
|
|
ConnectedComponentsPair retCCP = new ConnectedComponentsPair();
|
||
|
|
|
||
|
|
//traverse ccp.compEdges
|
||
|
|
ListIterator litE = ccp.compEdges.listIterator(0);
|
||
|
|
MultiSet actEL;
|
||
|
|
Edge actEdge;
|
||
|
|
//scan every component
|
||
|
|
while (litE.hasNext()) {
|
||
|
|
MultiSet elList = new MultiSet();
|
||
|
|
//set actEL as one component
|
||
|
|
actEL = (MultiSet)litE.next();
|
||
|
|
//traverse component
|
||
|
|
if (!actEL.isEmpty()) {
|
||
|
|
Iterator litE2 = actEL.iterator();
|
||
|
|
while (litE2.hasNext()) {
|
||
|
|
//get edge from component
|
||
|
|
actEdge = (Edge)((MultiSetEntry)litE2.next()).value;
|
||
|
|
//scan slCopy for both vertices in actEdge and
|
||
|
|
//remove _all_ successors.
|
||
|
|
//if one of the vertices has no successors,
|
||
|
|
//the edge may not be added
|
||
|
|
|
||
|
|
boolean wasEmpty1 = false;
|
||
|
|
boolean wasEmpty2 = false;
|
||
|
|
Vertex vertex1 = actEdge.first;
|
||
|
|
Vertex vertex2 = actEdge.second;
|
||
|
|
|
||
|
|
if (slCopy[vertex1.number].isEmpty()) wasEmpty1 = true;
|
||
|
|
if (slCopy[vertex2.number].isEmpty()) wasEmpty2 = true;
|
||
|
|
|
||
|
|
//If both of the vertices still had successors,
|
||
|
|
//now remove all successors from slCopy and add
|
||
|
|
//the found edge to the resulting elList.
|
||
|
|
if (!(wasEmpty1 || wasEmpty2)) {
|
||
|
|
//delete succLists
|
||
|
|
slCopy[vertex1.number].clear();
|
||
|
|
slCopy[vertex2.number].clear();
|
||
|
|
//add actPair to retList
|
||
|
|
elList.add(actEdge); }
|
||
|
|
}//while
|
||
|
|
}//if
|
||
|
|
//add the newly computed list of edges for the actual component to
|
||
|
|
//the resulting list retCCP
|
||
|
|
retCCP.compEdges.add(elList);
|
||
|
|
}//while
|
||
|
|
|
||
|
|
//now compute the new compV
|
||
|
|
//extract used elements for every compE and compute the difference
|
||
|
|
//with old compV
|
||
|
|
ListIterator litV;
|
||
|
|
MultiSet actVL;
|
||
|
|
litE = retCCP.compEdges.listIterator(0);
|
||
|
|
litV = ccp.compVertices.listIterator(0);
|
||
|
|
if (!ccp.compEdges.isEmpty()) {
|
||
|
|
while (litE.hasNext()) {
|
||
|
|
actVL = (MultiSet)litV.next();
|
||
|
|
actEL = (MultiSet)litE.next();
|
||
|
|
//to be able to use the SetOps operations, the vertices
|
||
|
|
//must be extracted from list of edges (litE) and list of
|
||
|
|
//vertices (litV). After the first extraction, the elements must
|
||
|
|
//be extracted from the vertices.
|
||
|
|
|
||
|
|
//extract elements from list of edges
|
||
|
|
ElemMultiSet elementsInEdges = new ElemMultiSet(new ElemComparator());
|
||
|
|
Iterator tempLIT = actEL.iterator();
|
||
|
|
actEdge = null;
|
||
|
|
while (tempLIT.hasNext()) {
|
||
|
|
actEdge = (Edge)((MultiSetEntry)tempLIT.next()).value;
|
||
|
|
elementsInEdges.add(actEdge.first.value);
|
||
|
|
elementsInEdges.add(actEdge.second.value);
|
||
|
|
}//while
|
||
|
|
|
||
|
|
//extract elements from list of vertices
|
||
|
|
ElemMultiSet elementsInVertices = new ElemMultiSet(new ElemComparator());
|
||
|
|
tempLIT = actVL.iterator();
|
||
|
|
Vertex actVertex;
|
||
|
|
while (tempLIT.hasNext()) {
|
||
|
|
actVertex = (Vertex)((MultiSetEntry)tempLIT.next()).value;
|
||
|
|
elementsInVertices.add(actVertex.value);
|
||
|
|
}//while
|
||
|
|
|
||
|
|
ElemMultiSet diff = SetOps.difference(elementsInVertices,elementsInEdges);
|
||
|
|
|
||
|
|
//now transform the ElemList back to a list of vertices
|
||
|
|
tempLIT = diff.iterator();
|
||
|
|
MultiSet vertexList = new MultiSet();
|
||
|
|
while (tempLIT.hasNext()) {
|
||
|
|
vertexList.add(new Vertex((Element)((MultiSetEntry)tempLIT.next()).value,-1)); }
|
||
|
|
retCCP.compVertices.add(vertexList);
|
||
|
|
}//while
|
||
|
|
}//if
|
||
|
|
else { retCCP.compVertices.addAll(ccp.compVertices); }
|
||
|
|
|
||
|
|
return retCCP;
|
||
|
|
}//end method computeReducedPair
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the faces of this graph.
|
||
|
|
* Here, the edges of the graph are interpreted as segments which bound a certain area. The graph's vertices are assumed to be points.
|
||
|
|
* Then, the graph is a representation of a polygon which has faces. The face cycles of this polygon are stored as sets of segments
|
||
|
|
* in {@link twodsack.set.ElemMultiSet}s.
|
||
|
|
* For each face, such a ElemMultiSet exist. Since the faces are computed beginning from the outside, the outmost face cycle is the
|
||
|
|
* first cycle in the result list.
|
||
|
|
*
|
||
|
|
* @return the set of cycles representing the faces
|
||
|
|
*/
|
||
|
|
public ElemMultiSetList computeFaces(){
|
||
|
|
ElemMultiSetList retList = new ElemMultiSetList();
|
||
|
|
CycleList cl = computeFaceCycles();
|
||
|
|
|
||
|
|
//store all cycles of cl in retList
|
||
|
|
for (int i = 0; i < cl.size(); i++) {
|
||
|
|
SegMultiSet sms = SegMultiSet.convert(SupportOps.convert(((LinkedList)cl.get(i))));
|
||
|
|
//retList.add(SegMultiSet.convert((LinkedList)cl.get(i)));
|
||
|
|
retList.add(sms);
|
||
|
|
}//for i
|
||
|
|
return retList;
|
||
|
|
}//end method computeFaces
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the faces of this graph.
|
||
|
|
* Here, the edges of the graph are interpreted as segments which bound a certain area. The graph's vertices are assumed to be points.
|
||
|
|
* Then, the graph is a representation of a polygon which has faces. The face cycles of this polygon are stored as sets of segments
|
||
|
|
* in {@link twodsack.util.collection.CycleList}s.<p>
|
||
|
|
* Since the faces are computed beginning from the outside, the outmost face cycle is the
|
||
|
|
* first cycle in the result list.
|
||
|
|
*
|
||
|
|
* @return the set of cycles representing the faces
|
||
|
|
*/
|
||
|
|
public CycleList computeFaceCycles(){
|
||
|
|
//first, sort the vertices in succLists such that
|
||
|
|
//the segments (which are formed by the pairs of vertices)
|
||
|
|
//are sorted as described in the ROSE implementation paper
|
||
|
|
//-> sorting of halfsegments.
|
||
|
|
//here, only the vertices are sorted. More is not needed
|
||
|
|
//(especially no construction of halfsegments).
|
||
|
|
|
||
|
|
//System.out.println("\nEntering Graph.computeFaceCycles.");
|
||
|
|
|
||
|
|
CycleList retList = new CycleList();
|
||
|
|
boolean isNotEmpty = false;
|
||
|
|
for (int i = 0; i < succLists.length; i++) {
|
||
|
|
if (succLists[i].size() > 0) {
|
||
|
|
isNotEmpty = true;
|
||
|
|
break;
|
||
|
|
}//if
|
||
|
|
}//while it
|
||
|
|
if (isNotEmpty == false) return retList;
|
||
|
|
|
||
|
|
//make working-copy of succLists
|
||
|
|
LinkedList[] succListsCOP = (LinkedList[])this.succLists.clone();
|
||
|
|
|
||
|
|
//sorting
|
||
|
|
for (int i = 0; i < succListsCOP.length; i++) {
|
||
|
|
//generate edges from pairs of vertices
|
||
|
|
LinkedList edges = new LinkedList();
|
||
|
|
ListIterator lit = succListsCOP[i].listIterator(0);
|
||
|
|
while (lit.hasNext()) edges.add(new Edge(vArr[i],(Vertex)lit.next()));
|
||
|
|
edges = sortEdgeListWithRespectToHalfSegmentsOrder(edges);
|
||
|
|
//rebuild succListsCOP
|
||
|
|
succListsCOP[i] = extractVerticesOtherThanX(edges,vArr[i]);
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
/* find cycles */
|
||
|
|
//loop while edges exist
|
||
|
|
while (thereAreEdgesLeft(succListsCOP)) {
|
||
|
|
//find starting point: Select leftmost and downmost point
|
||
|
|
Vertex startVertex = getStartVertex(succListsCOP);
|
||
|
|
|
||
|
|
//System.out.println("startVertex: "+startVertex);
|
||
|
|
|
||
|
|
//call sub-routine findCycles
|
||
|
|
findCycles(succListsCOP,startVertex,retList);
|
||
|
|
}//while
|
||
|
|
|
||
|
|
return retList;
|
||
|
|
}//end method computeFaceCycles
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the postion of vert in vertList.
|
||
|
|
*
|
||
|
|
* @param vert a vertex
|
||
|
|
* @param vertList a list of vertices
|
||
|
|
* @return the positon as int
|
||
|
|
* @throws NoSuchElementException if the vertex cannot be found
|
||
|
|
*/
|
||
|
|
private static int getPosOfVertex(Vertex vert, LinkedList vertList) throws NoSuchElementException {
|
||
|
|
ListIterator lit = vertList.listIterator(0);
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
Vertex actV = (Vertex)lit.next();
|
||
|
|
if (actV.equal(vert)) return lit.nextIndex()-1;
|
||
|
|
}//while
|
||
|
|
//an error ocurred, print error messages
|
||
|
|
System.out.println("\nVertex list: ");
|
||
|
|
lit = vertList.listIterator(0);
|
||
|
|
if (vertList.isEmpty())
|
||
|
|
System.out.println("Graph.getPosOfVertex: vertList is empty!");
|
||
|
|
else {
|
||
|
|
while (lit.hasNext())
|
||
|
|
((Element)((Vertex)lit.next()).value).print();
|
||
|
|
}//else
|
||
|
|
throw new NoSuchElementException(vert+" cannot be found in vertex list. Typically, this error is a precision error. Change the value for DeviationDouble in Algebra Initialization.");
|
||
|
|
}//end getPosOfVertex
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Computes a set of Segments from an object cycle.
|
||
|
|
* Works only for vertices of type Point.
|
||
|
|
*
|
||
|
|
* @param inCycle a cycle with vertices of type Point
|
||
|
|
* @return the cycle converted to a SegMultiSet
|
||
|
|
*/
|
||
|
|
private static SegMultiSet computeSMSFromCycle(LinkedList inCycle) {
|
||
|
|
SegMultiSet retList = new SegMultiSet(new SegmentComparator());
|
||
|
|
|
||
|
|
if (inCycle.size() < 3) {
|
||
|
|
System.out.println("ERROR: found bad cycle in Graph.computeSMSFromCycle.");
|
||
|
|
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
|
||
|
|
}//if
|
||
|
|
|
||
|
|
for (int i = 0; i < inCycle.size()-1; i++) {
|
||
|
|
retList.add(new Segment((Point)((Vertex)inCycle.get(i)).value,
|
||
|
|
(Point)((Vertex)inCycle.get(i+1)).value));
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
return retList;
|
||
|
|
}//end method computeSMSFromCycle
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Computes a set of segments from a cycle of Point(s).
|
||
|
|
* If inCycle is a list of the form (A-B-C-D-A), new segments are constructed such, that the result is
|
||
|
|
* (A-B)(B-C)(C-D)(D-A).
|
||
|
|
*
|
||
|
|
* @param inCycle the cycle of Point(s)
|
||
|
|
* @return the cycle of segments
|
||
|
|
*/
|
||
|
|
private static LinkedList computeSegListFromCycle(LinkedList inCycle) {
|
||
|
|
LinkedList retList = new LinkedList();
|
||
|
|
|
||
|
|
if (inCycle.size() < 3) {
|
||
|
|
System.out.println("ERROR: found bad cycle in Graph.computeSegListFromCycle.");
|
||
|
|
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
|
||
|
|
}//if
|
||
|
|
|
||
|
|
for (int i = 0; i < inCycle.size()-1; i++) {
|
||
|
|
retList.add(new Segment((Point)((Vertex)inCycle.get(i)).value,
|
||
|
|
(Point)((Vertex)inCycle.get(i+1)).value));
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
return retList;
|
||
|
|
}//end method computeSegListFromCycle
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the leftmost and downmost vertex (w.r.t. the coordinates).
|
||
|
|
*
|
||
|
|
* @param sL the adjacencyc list of <i>this</i>
|
||
|
|
*/
|
||
|
|
private Vertex getStartVertex(LinkedList[] sL) {
|
||
|
|
//since vArr is sorted originally, we can take the first
|
||
|
|
//vertex which has successors, i.e. outgoing edges.
|
||
|
|
|
||
|
|
for (int i = 0; i < sL.length; i++) {
|
||
|
|
if (!sL[i].isEmpty()) { return vArr[i]; }
|
||
|
|
}//for i
|
||
|
|
return null;
|
||
|
|
}//end method getStartVertex
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns true, if there is at least one vertex in sL which has a successor, i.e. an outgoing edge.
|
||
|
|
*
|
||
|
|
* @param sL the adjacency lists
|
||
|
|
* @return true, if there is such a vertex
|
||
|
|
*/
|
||
|
|
private static boolean thereAreEdgesLeft(LinkedList[] sL) {
|
||
|
|
for (int i = 0; i < sL.length; i++) {
|
||
|
|
if (!sL[i].isEmpty()) { return true; }
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}//end method thereAreEdgesLeft
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns a list with all (other) vertices of elements of <i>edges</i>.
|
||
|
|
* Example: edges ((A-B)(B-C)(B-D)) x = B. Then, the result is (A,C,D)
|
||
|
|
* @param edges the list of edges
|
||
|
|
* @param x the vertex x
|
||
|
|
* @return the list of 'other' vertices
|
||
|
|
*/
|
||
|
|
private static LinkedList extractVerticesOtherThanX(LinkedList edges, Vertex x) {
|
||
|
|
LinkedList retList = new LinkedList();
|
||
|
|
|
||
|
|
ListIterator lit = edges.listIterator(0);
|
||
|
|
while (lit.hasNext()) {
|
||
|
|
retList.add(((Edge)lit.next()).theOtherOne(x));
|
||
|
|
}//while
|
||
|
|
|
||
|
|
return retList;
|
||
|
|
}//end method extractVerticesOtherThanX
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the sorted paramter list.
|
||
|
|
* The elements are sorted using the order for halfsegments (described in the ROSE implementation paper).
|
||
|
|
*
|
||
|
|
* @param edges the list of edges
|
||
|
|
* @return the sorted list
|
||
|
|
*/
|
||
|
|
private static LinkedList sortEdgeListWithRespectToHalfSegmentsOrder(LinkedList edges) {
|
||
|
|
LinkedList retList = edges;
|
||
|
|
|
||
|
|
Edge min = null;
|
||
|
|
int minPos = 0;
|
||
|
|
boolean found = false;
|
||
|
|
int edgessize = edges.size();
|
||
|
|
|
||
|
|
for (int i = 0; i < edgessize-1; i++) {
|
||
|
|
min = (Edge)edges.get(i);
|
||
|
|
found = false;
|
||
|
|
for (int j = i+1; j < edgessize; j++) {
|
||
|
|
Edge actJ = (Edge)edges.get(j);
|
||
|
|
int cmp = min.compare(actJ);
|
||
|
|
if (cmp == 1) {
|
||
|
|
min = (Edge)edges.get(j);
|
||
|
|
minPos = j;
|
||
|
|
found = true;
|
||
|
|
}//if
|
||
|
|
}//for j
|
||
|
|
if (found) {
|
||
|
|
Edge tmp = (Edge)edges.get(i);
|
||
|
|
edges.set(i,min);
|
||
|
|
edges.set(minPos,tmp);
|
||
|
|
}//if
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
return retList;
|
||
|
|
}//end method sortEdgeListWithRespectToHalfSegmentsOrder
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Supportive method for computeFaceCycles. Computes all cycles connected to the starting point.
|
||
|
|
* At least one new cycle is added to <tt>retlist</tt> for every call of <tt>findCycles</tt>.
|
||
|
|
*
|
||
|
|
* @param succLists is a copy of the adjacency lists for the graph; may be modified
|
||
|
|
* @param startVertex the point from where the search for cycles begins
|
||
|
|
* @param retList all found cycles are added to this list
|
||
|
|
*/
|
||
|
|
private void findCycles (LinkedList[] succLists, Vertex startVertex, CycleList retList) {
|
||
|
|
//The algorithm for finding cycles works as follows: First, find a starting vertex. This is the
|
||
|
|
//bottom left vertex. Then, follow the edges from there. The next edge is given by chosing the
|
||
|
|
//_predecessor_ in the succLists (or the next vertex resp.). Remove the edge from succLists and
|
||
|
|
//mark the vertex as visited (in the visited array). Push all visited vertices on the stack.
|
||
|
|
//When meeting a vertex again (entry in visited array is TRUE), remove all entries from the stack
|
||
|
|
//until the actual vertex is found. Store all these vertices as new cycle. Then continue with
|
||
|
|
//following edges.
|
||
|
|
//Note: All edges must be removed twice, since every edge has two entries in succLists.
|
||
|
|
|
||
|
|
Stack stack = new Stack();
|
||
|
|
//initialize visited
|
||
|
|
boolean[] visited = new boolean[succLists.length];
|
||
|
|
for (int i = 0; i < visited.length; i++)
|
||
|
|
visited[i] = false;
|
||
|
|
|
||
|
|
//push startVertex on stack, set visited
|
||
|
|
|
||
|
|
stack.push(startVertex);
|
||
|
|
visited[startVertex.number] = true;
|
||
|
|
|
||
|
|
Vertex stackVertex = null;
|
||
|
|
|
||
|
|
//follow edges until no more edges exist
|
||
|
|
Vertex actVertex = startVertex;
|
||
|
|
Vertex nextVertex = null;
|
||
|
|
Vertex prevVertex = null;
|
||
|
|
boolean alreadySet = false;
|
||
|
|
do {
|
||
|
|
//get next Vertex
|
||
|
|
if (!alreadySet)
|
||
|
|
nextVertex = getPredecessor(succLists,actVertex,prevVertex);
|
||
|
|
else
|
||
|
|
alreadySet = false;
|
||
|
|
|
||
|
|
//remove edge by removing both involved vertices from succLists
|
||
|
|
//...
|
||
|
|
if (visited[nextVertex.number]) {
|
||
|
|
//finished another cycle, remove it from stack and store it
|
||
|
|
LinkedList newCycle = new LinkedList();
|
||
|
|
newCycle.add(nextVertex);
|
||
|
|
|
||
|
|
//find next vertex before the cycle vertices are deleted
|
||
|
|
Vertex nextVertexS = getPredecessor(succLists,nextVertex,actVertex);
|
||
|
|
|
||
|
|
alreadySet = true;
|
||
|
|
|
||
|
|
//pop elements form stack until element is equal to nextVertex
|
||
|
|
do {
|
||
|
|
stackVertex = (Vertex)stack.pop();
|
||
|
|
newCycle.add(stackVertex);
|
||
|
|
} while (!stackVertex.equal(nextVertex));
|
||
|
|
|
||
|
|
//push nextVertex on stack again
|
||
|
|
stack.push(nextVertex);
|
||
|
|
|
||
|
|
//add new cycle to retList
|
||
|
|
retList.add(computeSegListFromCycle(newCycle));
|
||
|
|
|
||
|
|
//remove edges from succLists
|
||
|
|
for (int i = 0; i < newCycle.size()-1; i++)
|
||
|
|
removeVertices(succLists,(Vertex)newCycle.get(i),(Vertex)newCycle.get(i+1));
|
||
|
|
|
||
|
|
actVertex = nextVertex;
|
||
|
|
nextVertex = nextVertexS;
|
||
|
|
|
||
|
|
} else {
|
||
|
|
//set visited for nextVertex
|
||
|
|
visited[nextVertex.number] = true;
|
||
|
|
//push nextVertex on stack
|
||
|
|
stack.push(nextVertex);
|
||
|
|
|
||
|
|
prevVertex = actVertex;
|
||
|
|
actVertex = nextVertex;
|
||
|
|
}//else
|
||
|
|
} while (succLists[actVertex.number].size() > 0);
|
||
|
|
}//end method findCycles
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Supportive method for findCycles. Removes vertices from succList.
|
||
|
|
* Given, the succLists and two vertices, this method removes the first vertex from the successor list of the other
|
||
|
|
* one and vice versa.
|
||
|
|
*
|
||
|
|
* @param succLists the successor lists
|
||
|
|
* @param firstVertex one of the vertices
|
||
|
|
* @param secondVertex the other vertex
|
||
|
|
*/
|
||
|
|
private void removeVertices (LinkedList[] succLists, Vertex firstVertex, Vertex secondVertex) {
|
||
|
|
Iterator it;
|
||
|
|
Vertex actVertex;
|
||
|
|
//remove firstVertex from list of secondVertex
|
||
|
|
it = succLists[secondVertex.number].iterator();
|
||
|
|
while (it.hasNext()) {
|
||
|
|
actVertex = (Vertex)it.next();
|
||
|
|
if (actVertex.equal(firstVertex)) it.remove();
|
||
|
|
}//while
|
||
|
|
|
||
|
|
//remove secondVertex from list of firstVertex
|
||
|
|
it = succLists[firstVertex.number].iterator();
|
||
|
|
while (it.hasNext()) {
|
||
|
|
actVertex = (Vertex)it.next();
|
||
|
|
if (actVertex.equal(secondVertex)) it.remove();
|
||
|
|
}//while
|
||
|
|
}//end method removeVertices
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Supportive method for findCycles. Returns the vertex which is the predecessor of a certain vertex from <tt>succLists</tt>.
|
||
|
|
* Note: A special case occurs, if <tt>prevVertex</tt> = null. Then, the first vertex of the appropriate <tt>succLists</tt> is returned.
|
||
|
|
* @param succLists the successor lists
|
||
|
|
* @param actVertex the actual vertex
|
||
|
|
* @param prevVertex the previous vertex
|
||
|
|
* @return the vertex which is the predecessor of <tt>actVertex</tt>
|
||
|
|
*/
|
||
|
|
private Vertex getPredecessor(LinkedList[] succLists, Vertex actVertex, Vertex prevVertex) {
|
||
|
|
int sLaV = succLists[actVertex.number].size();
|
||
|
|
//special case:
|
||
|
|
if ((prevVertex == null) || (sLaV == 1))
|
||
|
|
return (Vertex)succLists[actVertex.number].getFirst();
|
||
|
|
|
||
|
|
//find actVertex in list
|
||
|
|
int pos = -1;
|
||
|
|
Vertex sVertex;
|
||
|
|
//get position of prevVertex
|
||
|
|
for (int i = 0; i < sLaV; i++) {
|
||
|
|
sVertex = (Vertex)succLists[actVertex.number].get(i);
|
||
|
|
if (sVertex.equal(prevVertex)) pos = i;
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
//return predecessor
|
||
|
|
return (Vertex)succLists[actVertex.number].get((sLaV+pos-1)%sLaV);
|
||
|
|
}//end method getPredecessor
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* For a given Point instance, this method returns the Vertex of the graph.
|
||
|
|
* Note: Works only, if the vertices of <tt>this</tt> store Element types.
|
||
|
|
*
|
||
|
|
* @param queryElement the element
|
||
|
|
* @return the vertex for the element
|
||
|
|
* @throws WrongTypeException if vertices don't store Element types
|
||
|
|
*/
|
||
|
|
public Vertex getVertex(Element queryElement) throws WrongTypeException {
|
||
|
|
//check for correct type
|
||
|
|
if (!(vArr[0].value instanceof Element))
|
||
|
|
throw new WrongTypeException("Graph.getVertex: Graph has vertex types "+vArr[0].value.getClass()+". Class Element is needed instead.");
|
||
|
|
|
||
|
|
//look whether sortedVertices already exists
|
||
|
|
if (!sortedVerticesValid) {
|
||
|
|
//sort!
|
||
|
|
sortVertices();
|
||
|
|
this.sortedVerticesValid = true;
|
||
|
|
}//if
|
||
|
|
//binary search
|
||
|
|
int pos = binarySearch(sortedVertices,0,vArr.length,queryElement);
|
||
|
|
if (pos > -1) return vArr[indexArray[pos]];
|
||
|
|
else {
|
||
|
|
System.out.println("Vertex wasn't found in vertex array.");
|
||
|
|
for (int i = 0; i < vArr.length; i++)
|
||
|
|
System.out.println("["+i+"] "+((Element)vArr[i].value));
|
||
|
|
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
|
||
|
|
//return null;
|
||
|
|
}//else
|
||
|
|
}//end method getVertex
|
||
|
|
|
||
|
|
|
||
|
|
private int binarySearch(Vertex[] arr, int low, int high, Element el) {
|
||
|
|
if (low > high)
|
||
|
|
return -1;
|
||
|
|
else {
|
||
|
|
int m = (int)((low+high) / 2);
|
||
|
|
int comp = ((Element)arr[m].value).compare(el);
|
||
|
|
if (comp == 0) {
|
||
|
|
return m;
|
||
|
|
}//if
|
||
|
|
else {
|
||
|
|
if (comp == -1)
|
||
|
|
return binarySearch(arr,m+1,high,el);
|
||
|
|
else
|
||
|
|
return binarySearch(arr,low,m-1,el);
|
||
|
|
}//else
|
||
|
|
}//else
|
||
|
|
}//end method binarySearch
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns a sorted version of vArr.
|
||
|
|
* Uses quicksort to sort the vertices. Stored in the second fields are the indexes for the vertices in vArr.
|
||
|
|
*/
|
||
|
|
private void sortVertices() {
|
||
|
|
//construct new sortedVertices array
|
||
|
|
this.sortedVertices = new Vertex[vArr.length];
|
||
|
|
this.indexArray = new int[vArr.length];
|
||
|
|
for (int i = 0; i < vArr.length; i++) {
|
||
|
|
sortedVertices[i] = (Vertex)(vArr[i].copy());
|
||
|
|
indexArray[i] = i;
|
||
|
|
}//for i
|
||
|
|
|
||
|
|
//sort array
|
||
|
|
quicksort(sortedVertices,0,sortedVertices.length-1,this.indexArray);
|
||
|
|
}//end method sortVertices
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This is the standard quicksort algorithm
|
||
|
|
*/
|
||
|
|
private static void quicksort(Vertex[] arr, int i, int j,int[] idxArr) {
|
||
|
|
int k, xIndex;
|
||
|
|
if (i < j) {
|
||
|
|
xIndex = findX(arr,i,j);
|
||
|
|
if (xIndex != -1) {
|
||
|
|
//DIVIDE
|
||
|
|
k = partition(arr,i,j,(Element)arr[xIndex].value,idxArr);
|
||
|
|
//CONQUER
|
||
|
|
quicksort(arr,i,k-1,idxArr);
|
||
|
|
quicksort(arr,k,j,idxArr);
|
||
|
|
//MERGE - nothing
|
||
|
|
}//if
|
||
|
|
}//if
|
||
|
|
}//end method quicksort
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This method is part of the quicksort algorithm.
|
||
|
|
*/
|
||
|
|
private static int partition (Vertex[] arr, int i, int j, Element x, int[] idxArr) {
|
||
|
|
int l = i;
|
||
|
|
int r = j;
|
||
|
|
while (l < r) {
|
||
|
|
while (((Element)arr[l].value).compare(x) == -1) l++;
|
||
|
|
while (x.compare((Element)arr[r].value) == -1) r--;
|
||
|
|
if (l < r) swap(arr,l,r,idxArr);
|
||
|
|
}//while
|
||
|
|
return l;
|
||
|
|
}//end method partition
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This method is part of the quicksort algorithm.
|
||
|
|
*/
|
||
|
|
private static int findX(Vertex[] arr, int i, int j) {
|
||
|
|
int k = i+1;
|
||
|
|
while (k <= j && ((Element)arr[k].value).equal((Element)arr[k-1].value)) k++;
|
||
|
|
if (k > j) return -1;
|
||
|
|
else if (((Element)arr[k-1].value).compare((Element)arr[k].value) == -1)
|
||
|
|
return k;
|
||
|
|
else return k-1;
|
||
|
|
}//end method findX
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This method is part of the quicksort algorithm.
|
||
|
|
*/
|
||
|
|
private static void swap(Vertex[] arr, int l, int r, int[] idxArr) {
|
||
|
|
Vertex tmpV = (Vertex)arr[l].copy();
|
||
|
|
int tmpI = idxArr[l];
|
||
|
|
arr[l] = arr[r];
|
||
|
|
idxArr[l] = idxArr[r];
|
||
|
|
arr[r] = tmpV;
|
||
|
|
idxArr[r] = tmpI;
|
||
|
|
}//end method swap
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* For a pair of vertices, this method returns another vertex, which is the 'successor' of these vertices.
|
||
|
|
* For three vertices <tt>a,b,c</tt> with <tt>b,c</tt> neighbours of <tt>a</tt>, this method returns <tt>c</tt>
|
||
|
|
* when called with <tt>b</tt> as queryVertex and <tt>a</tt> as prevVertex.<p>
|
||
|
|
* Note: It is assumed, that a vertex has the degree 2.
|
||
|
|
*
|
||
|
|
* @param queryVertex the returned vertex is a neighbour of this vertex
|
||
|
|
* @param prevVertex this vertex is another neighbour of queryVertex
|
||
|
|
* @return the vertex which is a neighbour of queryVertex but which is not equal to prevVertex
|
||
|
|
*/
|
||
|
|
public Vertex getNextVertex(Vertex queryVertex, Vertex prevVertex) {
|
||
|
|
if (succLists[queryVertex.number].size() != 2) {
|
||
|
|
System.out.println("Graph.getNextVertex: Too many neighbour vertices. May have only two neighbours.");
|
||
|
|
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
|
||
|
|
}//if
|
||
|
|
//get both neighbour vertices from succLists
|
||
|
|
Vertex v0 = (Vertex)succLists[queryVertex.number].get(0);
|
||
|
|
Vertex v1 = (Vertex)succLists[queryVertex.number].get(1);
|
||
|
|
|
||
|
|
//return correct vertex
|
||
|
|
if (((Element)v0.value).equal((Element)prevVertex.value))
|
||
|
|
return v1;
|
||
|
|
else
|
||
|
|
return v0;
|
||
|
|
}//end method getNextVertex
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* For a queryVertex this method returns an array of all neighbours of this vertex.
|
||
|
|
*
|
||
|
|
* @param queryVertex the vertex for which the neighbours are demanded
|
||
|
|
* @return an array with the neighbour vertices
|
||
|
|
*/
|
||
|
|
public Vertex[] getNeighbours(Vertex queryVertex) {
|
||
|
|
//construct new array
|
||
|
|
Vertex[] resArr = new Vertex[succLists[queryVertex.number].size()];
|
||
|
|
|
||
|
|
//store vertices in resArr
|
||
|
|
for (int i = 0; i < resArr.length; i++)
|
||
|
|
resArr[i] = (Vertex)succLists[queryVertex.number].get(i);
|
||
|
|
|
||
|
|
return resArr;
|
||
|
|
}//end method getNeighbours
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the vertex array.
|
||
|
|
* Note: Don't change any entries in this array, since it is NO copy but the original vertex array.
|
||
|
|
*
|
||
|
|
* @return the verrex array
|
||
|
|
*/
|
||
|
|
public Vertex[] getVertexArray() {
|
||
|
|
return vArr;
|
||
|
|
}//end method getVertexArray
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns a sorted version of the vertex array.
|
||
|
|
*
|
||
|
|
* @return the verrex array
|
||
|
|
*/
|
||
|
|
public Vertex[] getSortedVertexArray() {
|
||
|
|
if (!sortedVerticesValid) {
|
||
|
|
sortVertices();
|
||
|
|
this.sortedVerticesValid = true;
|
||
|
|
}//if
|
||
|
|
return sortedVertices;
|
||
|
|
}//end method getVertexArray
|
||
|
|
|
||
|
|
}//end class Graph
|