Files
secondo/Algebras/Rose/RoseJava/twodsack/operation/setoperation/SetOps.java
2026-01-23 17:03:45 +08:00

2866 lines
109 KiB
Java

/*
* SetOps.java 2005-05-02
*
* Dirk Ansorge, FernUniversitaet Hagen
*
*/
package twodsack.operation.setoperation;
import twodsack.io.*;
import twodsack.set.*;
import twodsack.setelement.*;
import twodsack.setelement.datatype.*;
import twodsack.setelement.datatype.basicdatatype.*;
import twodsack.util.collection.*;
import twodsack.util.collectiontype.*;
import twodsack.util.comparator.*;
import twodsack.util.graph.*;
import twodsack.util.iterator.*;
import twodsack.util.number.*;
import java.util.*;
import java.lang.reflect.*;
import java.io.*;
/**
* The SetOps class is one of the central classes of the whole 2D-SACK package.
* Collected in this class are all
* of the genereric set operations the package offers. Sets in 2D-SACK are commonly {@link ElemMultiSet}, i.e. they
* are one of {{@link PointMultiSet}, {@link SegMultiSet}, {@link TriMultiSet}}. Most of the operations provided here are made for this
* type. Other types are {@link PairMultiSet}, {@link LeftJoinPairMultiSet} and, in one special case a LinkedList.<p>
* A PairMultiSet is the result type of a join performed on two sets. Each element of such a set is an {@link ElemPair}
* which holds two elements. A LeftJoinPairMultiSet is the result of an {@link #leftOuterJoin(ElemMultiSet,ElemMultiSet,Method)} and also has pairs
* as elements. Such a pair looks like this: <code>(Element x ElemMultiSet)</code>. Finally, a LinkedList type
* only appears in some sorting algorithm in this class.<p>
* The operations in this class can be roughly divided in four groups:<ol>
* <li> set operations for ElemMultiSet(s)
* <li> set operations for PairMultiSet(s)
* <li> set operations for LeftJoinPairMultiSet(s)
* <li> supporting operations
* </ol><p>
* E.g. the group for set operations for PairMultiSet(s) holds several join operations to construct PairMultiSets
* from pairs of ElemMultiSet(s). Then, some other operations can be used for further processing, like {@link #filter(PairMultiSet,Method,boolean)},
* {@link #proj1(PairMultiSet)}, {@link #proj2(PairMultiSet)}, and four different versions of <tt>map(...)</tt>. The operations in the other
* groups are quite similar.<p>
* In this class, different implementations can be found for similar operations like <tt>join(...)</tt> and <tt>overlapJoin(...)</tt> or
* <tt>group(...)</tt>
* and <tt>overlapGroup</tt>. The <i>overlap</i> prefix indicates a method as using a special mechanism for speeding up
* the operation. Whereas a simple <tt>join</tt> takes quite a long time, the <tt>overlapJoin</tt> uses a filter&refine technique
* to reduce the number of possibly interesting pairs of elements before executing (possibly) expensive following
* operations. The only constraint for these overlap versions of methods is, that the passed predicate and operation
* for this operation may work only <i>inside of bounding boxes</i>. This means for predicates, that it only holds for
* two elements, if their bounding boxes overlap or are adjacent at least. For operations, the result of the operation must
* lie inside of the bounding box of the two bounding boxes involved.
*/
public class SetOps {
/*
* fields
*/
static final ElemComparator ELEM_COMPARATOR = new ElemComparator();
static final LeftJoinPairComparator LEFTJOINPAIR_COMPARATOR = new LeftJoinPairComparator();
static final ElemPairComparator ELEMPAIR_COMPARATOR = new ElemPairComparator();
/*
* constructors
*/
/**
* The standard constructor.
*/
public SetOps(){}
/*
* methods
*/
/**
* Separates the passed PairMultiSet and returns the elements in the two passed ElemMultiSet(s).
* Both passed sets <tt>ems1,ems2</tt> are cleared before adding new elements.
*
* @param pms the set that is separated
* @param ems1 all first elements are stored in this set
* @param ems2 all second elements are stored in this set
*/
static public void separateSets (PairMultiSet pms, ElemMultiSet ems1, ElemMultiSet ems2) {
if (ems1 == null) ems1 = new ElemMultiSet(ELEM_COMPARATOR);
if (ems2 == null) ems2 = new ElemMultiSet(ELEM_COMPARATOR);
ems1.clear();
ems2.clear();
Iterator it = pms.iterator();
ElemPair actPair;
while (it.hasNext()) {
actPair = (ElemPair)((MultiSetEntry)it.next()).value;
ems1.add(actPair.first);
ems2.add(actPair.second);
}//while it
}//end method separateSets
/**
* Returns a set of elements which is 'reduced' using the passed <tt>predicate</tt> and <tt>method</tt>.<p>
* This is another variant of the normal {@link #reduce(ElemMultiSet,Method,Method)} operation. For the functionality of <tt>reduce</tt> itself,
* have a look at that method. <p>
* Here, a plane sweep algorithm is used for the computation of the reduced set:<ul>
* <li> construct the sweep event structure (ses) from the bounding boxes of the elements in ems; store the right and
* left interval borders
* <li> sort the intervals by their x-coordinate, lower y-coordinate, upper y-coordinate, in that order
* <li> construct a sweep status structure (sss) which is initially empty but will hold intervals later on
* <li> traverse the ses; for every element do:<ul>
* <li> if it is a left interval: check whether it overlaps any of the intervals in sss. If it does:<ul>
* <li> evaluate the <tt>predicate</tt> for the two involved objects
* <li> evaluate the operation if the <tt>predicate</tt> yields <tt>true</tt>
* <li> if the result of the operation is empty, delete the left and right intervals from ses and sss,
* otherwise adjust the intervals in ses and sss</ul>
* <li> if it is a left interval and the intervals don't overlap, add the interval to sss
* <li> if it is a right interval: add the object to the result set and delete the interval from sss</ul>
* </ul>
* This method is successfully used in the <tt>SupportOps.minimal()</tt> method. There, it works for sets of segments,
* the predicate <tt>adjacent()</tt> and the method <tt>concat()</tt>. No other implementations were tested, yet.<p>
* Note, that the signature of all predicates and methods must be<br>
* <code>Element x Element -> boolean</code> and <code>Element x Element -> Element</code>, resp.
*
* @param ems the input set of elements
* @param predicate the method that is used to identify candidates for the second method
* @param method the method that is invoked on pairs identified by the predicate
* @param meet if <tt>true</tt>, also objects of <tt>ems</tt> which have only adjacent bounding boxes are tested with predicate
* @return the 'reduced' set of objects
*/
public static ElemMultiSet overlapReduceSweep (ElemMultiSet ems, Method predicate, Method method, boolean meet) {
//construct sweep event structure from ems
//the left and right intervals of the bounding boxes of all elements in ems are stored
if (ems.isEmpty()) return ems;
//get some information about the passed methods
int paramTypeCountP = Array.getLength(predicate.getParameterTypes());
int paramTypeCountM = Array.getLength(method.getParameterTypes());
Element[] paramListP = new Element[paramTypeCountP];
Element[] paramListM = new Element[paramTypeCountM];
//the elements of ems are stored in an array for better accessibility
Object[] elemStore = ems.toArray();
//declare the sweep event structure (ses) and fill it with the left and right borders of the
//bounding boxes of all elements in ems.
MultiSet ses = new MultiSet(new IvlComparator(meet));
Object[] sesArr;
Element actEl;
Rect actRect;
boolean buddy;
for (int i = 0; i < elemStore.length; i++) {
actEl = (Element)elemStore[i];
actRect = actEl.rect();
buddy = actRect.ulx.equal(actRect.urx);
ses.add(new Interval(actRect.lly,actRect.uly,"blueleft",actRect.ulx,null,i,buddy));
ses.add(new Interval(actRect.lry,actRect.ury,"blueright",actRect.urx,null,i,buddy));
}//for i
//declare the sweep status structure
MultiSet sss = new MultiSet(new IvlComparator(meet));
Interval actIvl,sesIvl;
Interval sssIvl = null;
Iterator it,it2,it3;
boolean predTrue,resultIsEmpty,removedIvl,ivlOverlap = false;
ElemMultiSet resultSet = new ElemMultiSet(ELEM_COMPARATOR);
Element resultElement = null;
sesArr = ses.toArray();
//start the sweep
for (int sesIdx = 0; sesIdx < sesArr.length; sesIdx++) {
//while there are elements left in ses, continue with the sweep
while (sesArr[sesIdx] == null) {
sesIdx++;
}//while
actIvl = (Interval)sesArr[sesIdx];
//the actually visited interval can be marked with blueleft or blueright
//the first case handled is blueleft
if (actIvl.mark == "blueleft") {
resultIsEmpty = false;
it2 = sss.iterator();
//now, traverse the intervals already stored in sss.
while (!resultIsEmpty && it2.hasNext()) {
sssIvl = (Interval)(((MultiSetEntry)it2.next()).value);
predTrue = false;
if (actIvl.right.less(sssIvl.left)) {
//break;
}
else {
//define ivlOverlap = true, if actIvl and sssIvl overlap
//in case flag meet is true, ivlOverlap = true if the intervals are adjacent, too
if (!meet)
ivlOverlap =
(sssIvl.left.less(actIvl.left) && sssIvl.right.greaterOrEqual(actIvl.left)) ||
(sssIvl.left.equal(actIvl.left) && sssIvl.right.greater(actIvl.left)) ||
(sssIvl.left.greater(actIvl.left) && sssIvl.left.less(actIvl.right));
else
ivlOverlap =
(sssIvl.left.less(actIvl.left) && sssIvl.right.greaterOrEqual(actIvl.left)) ||
(sssIvl.left.equal(actIvl.left) && sssIvl.right.greater(actIvl.left)) ||
(sssIvl.left.greater(actIvl.left) && sssIvl.left.less(actIvl.right)) ||
sssIvl.right.equal(actIvl.left) || sssIvl.left.equal(actIvl.right);;
}//else
//if both intervals overlap check the predicate
if (ivlOverlap) {
//now check for predicate
//if predicate has one argument
if (paramTypeCountP == 1) paramListP[0] = (Element)elemStore[actIvl.number];
//if predicate has two arguments
else {
paramListP[0] = (Element)elemStore[sssIvl.number];
paramListP[1] = (Element)elemStore[actIvl.number];
}//else
//evaluate predicate
try {
predTrue = ((Boolean)predicate.invoke((Element)elemStore[sssIvl.number],paramListP)).booleanValue();
} catch (Exception e) {
System.out.println("Exception in SetOps.overlapReduceSweep when evaluating predicate.");
System.out.println("elemStore[sssIvl.number]: "+(Element)elemStore[sssIvl.number]);
System.out.println("elemStore[actIvl.number]: "+(Element)elemStore[actIvl.number]);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//invoke method on the elements linked to the intervals, if the predicate yields true
if (predTrue) {
//evaluate method, note that result may be empty
if (paramTypeCountM == 1) paramListM[0] = (Element)elemStore[actIvl.number];
else {
paramListM[0] = (Element)elemStore[sssIvl.number];
paramListM[1] = (Element)elemStore[actIvl.number];
}//else
//evaluate method
try {
resultElement = (Element)method.invoke((Element)elemStore[sssIvl.number],paramListM);
} catch (Exception e) {
System.out.println("Exception in SetOps.overlapReduceSweep when evaluating method.");
System.out.println("elemStore[sssIvl.number]: "+(Element)elemStore[sssIvl.number]);
System.out.println("elemStore[actIvl.number]: "+(Element)elemStore[actIvl.number]);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (resultElement == null) resultIsEmpty = true;
//update ref in actEl, update interval borders in actEl
elemStore[actIvl.number] = resultElement;
actIvl.left = resultElement.rect().lly;
actIvl.right = resultElement.rect().uly;
//remove the sssIvl from sss and remove right interval with number = sssIvl.number from ses,
//i.e. remove the interval also from ses, where its "blueright" interval still exists
it2.remove();
removedIvl = false;
for (int i = sesIdx; i < sesArr.length; i++) {
if ((sesArr[i] != null) &&((Interval)sesArr[i]).number == sssIvl.number) {
sesArr[i] = null;
removedIvl = true;
break;
}//if
}//for i
}//if predTrue
}//if ivlOverlap
}//while it2
//if the result of the method invocation is not empty, add the interval of the new/changed
//object to the sss
//otherwise delete the right partner of the left interval from sss
if (!resultIsEmpty)
sss.add(actIvl);
else {
//remove the right interval from ses
removedIvl = false;
for (int i = sesIdx; i < sesArr.length; i++) {
if ((sesArr[i] != null) && ((Interval)sesArr[i]).number == sssIvl.number) {
sesArr[i] = null;
removedIvl = true;
break;
}//if
}//for i
}//else
}//if blueleft
//the actually visited interval is marked with blueright
else {
//store the object linked to the interval in the result set
resultSet.add(elemStore[actIvl.number]);
//remove left interval from sss
it2 = sss.iterator();
while (it2.hasNext()) {
sesIvl = (Interval)(((MultiSetEntry)it2.next()).value);
if (sesIvl.number == actIvl.number) {
it2.remove();
break;
}//if
}//while it2
}//else if blueright
}//while it
return resultSet;
}//end method overlapReduceSweep
/**
* This method is a variant of overlapReduceSweep for methods of type <tt>Element x Element -> ElemMultiSet</tt>.
* More information about this method can be found there.
*
* @param ems the input set of elements
* @param predicate the method that is used to identify candidates for the second method
* @param method the method that is invoked on pairs identified by the predicate
* @param meet if <tt>true</tt>, also objects of <tt>ems</tt> which have only adjacent bounding boxes are tested with predicate
* @return the 'reduced' set of objects
*/
public static ElemMultiSet overlapReduceSweep2 (ElemMultiSet ems, Method predicate, Method method, boolean meet) {
//construct sweep event structure (ses) from ems;
//the left an right intervals of the bounding boxes of all elements in ems are stored in ses
if (ems.isEmpty()) return ems;
//get some information about the passed methods
int paramTypeCountP = Array.getLength(predicate.getParameterTypes());
int paramTypeCountM = Array.getLength(method.getParameterTypes());
Element[] paramListP = new Element[paramTypeCountP];
Element[] paramListM = new Element[paramTypeCountM];
//the elements of ems are stored in a Vector we need a dynamic structure here in contrast the
//ordinary overlapReduceSweep, since the SES is changed during the sweep
Vector elemStore = ems.toVector();
int number = ems.size();
//declare the sweep event structure (SES) and fill it with the left and right borders of the
//bounding boxes of all elements of ems
IvlComparator ivlComp = new IvlComparator(meet);
MultiSet ses = new MultiSet(ivlComp);
ProLinkedList sesList;
Element actEl,actEl2;
Rect actRect;
boolean buddy;
for (int i = 0; i < elemStore.size(); i++) {
actEl = (Element)elemStore.get(i);
actRect = actEl.rect();
buddy = actRect.ulx.equal(actRect.urx);
ses.add(new Interval(actRect.lly,actRect.uly,"blueleft",actRect.ulx,null,i,buddy));
ses.add(new Interval(actRect.lry,actRect.ury,"blueright",actRect.urx,null,i,buddy));
}//for i
//declare the sweep status structure SSS
MultiSet sss = new MultiSet(new IvlComparator(meet));
Interval actIvl,actIvl2,sesIvl;
Interval sssIvl = null;
Iterator it2,it4;
ProListIterator it,it3;
boolean predTrue = false;
boolean resultIsEmpty,removedIvl,ivlOverlap = false;
ElemMultiSet resultSet = new ElemMultiSet(ELEM_COMPARATOR);
ElemMultiSet resultEMS = null;
int sweepPointer;
sesList = new ProLinkedList(ses,new IvlComparatorSimpleType(meet));
//start the sweep
it = sesList.listIterator(0);
sweepPointer = -1;
//while there are elements left in SES, continue with the sweep
while (it.hasNext()) {
actIvl = (Interval)it.next();
sweepPointer++;
//the actually visited interval is marked with either "blueleft" or "blueright";
//handle "blueleft" first
if (actIvl.mark == "blueleft") {
resultIsEmpty = false;
predTrue = false;
it2 = sss.iterator();
//now, traverse the intervals already stored in SSS
while (it2.hasNext()) {
sssIvl = (Interval)(((MultiSetEntry)it2.next()).value);
predTrue = false;
if (actIvl.right.less(sssIvl.left)) {
//break;
}
else {
//define ivlOverlap = true, if actIvl and sssIvl overlap
//in case flag meet is true, ivlOverlap = true if the intervals are adjacent, too
if (!meet)
ivlOverlap =
(sssIvl.left.less(actIvl.left) && sssIvl.right.greaterOrEqual(actIvl.left)) ||
(sssIvl.left.equal(actIvl.left) && sssIvl.right.greater(actIvl.left)) ||
(sssIvl.left.greater(actIvl.left) && sssIvl.left.less(actIvl.right));
else
ivlOverlap =
(sssIvl.left.less(actIvl.left) && sssIvl.right.greaterOrEqual(actIvl.left)) ||
(sssIvl.left.equal(actIvl.left) && sssIvl.right.greater(actIvl.left)) ||
(sssIvl.left.greater(actIvl.left) && sssIvl.left.less(actIvl.right)) ||
sssIvl.right.equal(actIvl.left) || sssIvl.left.equal(actIvl.right);;
}//else
//if both intervals overlap check the predicate
if (ivlOverlap) {
//now check for predicate
//if predicate has one argument
if (paramTypeCountP == 1) paramListP[0] = (Element)elemStore.get(actIvl.number);
//if predicate has two arguments
else {
paramListP[0] = (Element)elemStore.get(sssIvl.number);
paramListP[1] = (Element)elemStore.get(actIvl.number);
}//else
//evaluate predicate
try {
predTrue = ((Boolean)predicate.invoke((Element)elemStore.get(sssIvl.number),paramListP)).booleanValue();
} catch (Exception e) {
System.out.println("Exception in SetOps.overlapReduceSweep2 when evaluating predicate.");
System.out.println("elemStore.get(sssIvl.number): "+(Element)elemStore.get(sssIvl.number));
System.out.println("elemStore.get(actIvl.number): "+(Element)elemStore.get(actIvl.number));
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//invoke method on the elements linked to the intervals, if the predicate yields true
if (predTrue) {
//evaluate method, note that result may be empty
if (paramTypeCountM == 1) paramListM[0] = (Element)elemStore.get(actIvl.number);
else {
paramListM[0] = (Element)elemStore.get(sssIvl.number);
paramListM[1] = (Element)elemStore.get(actIvl.number);
}//else
//evaluate method
try {
resultEMS = (ElemMultiSet)method.invoke((Element)elemStore.get(sssIvl.number),paramListM);
} catch (Exception e) {
System.out.println("Exception in SetOps.overlapReduceSweep when evaluating method.");
System.out.println("elemStore.get(sssIvl.number): "+(Element)elemStore.get(sssIvl.number));
System.out.println("elemStore.get(actIvl.number): "+(Element)elemStore.get(actIvl.number));
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if ((resultEMS == null) || resultEMS.isEmpty()) resultIsEmpty = true;
else resultIsEmpty = false;
//remove the sssIvl from sss and remove both intervals marked with sssIvl.number from SES
//System.out.println("\nremove sssIvl and its partner from sss and ses. sssIvl.number: "+sssIvl.number);
it2.remove();
it3 = sesList.listIterator(0);
int ct = 0;
while (it3.hasNext()) {
actIvl2 = (Interval)it3.next();
if (actIvl2.number == sssIvl.number) {
it3.remove();
ct++;
}//if
if (ct == 2) break;
}//while it3
sweepPointer--;
}//if predTrue
}//if ivlOverlap
//if method was invoked, break
if (predTrue) break;
}//while it
if (predTrue) {
//delete actIvl and its partner from SES
it3 = sesList.listIterator(0);
int ct = 0;
int ct1 = 0;
while (it3.hasNext()) {
actIvl2 = (Interval)it3.next();
ct1++;
if (actIvl2.number == actIvl.number) {
it3.remove();
ct++;
}//if
if (ct == 2) break;
}//while it
if (ct != 2) {
System.out.println("\nct = "+ct);
System.out.println("\ndelete actIvl and its partner from SES. actIvl.nr: "+actIvl.number);
System.out.println("\n********************************");
System.out.println("SESLIST:");
for (int i = 0; i < sesList.size(); i++) {
System.out.print("["+i+"] ");((Interval)sesList.get(i)).print();
}
System.out.println("********************************");
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}
sweepPointer--;
//if the result of the method invocation is not empty, add all new objects to SES and elemStore;
int pos;
if (!resultIsEmpty) {
it4 = resultEMS.iterator();
while (it4.hasNext()) {
actEl2 = (Element)((MultiSetEntry)it4.next()).value;
elemStore.add(actEl2);
actRect = actEl2.rect();
buddy = actRect.ulx.equal(actRect.urx);
pos = sesList.addSorted(new Interval(actRect.lly,actRect.uly,"blueleft",actRect.ulx,null,number,buddy));
//one or none of the new intervals may have been added BEFORE the actual x;
//set pointer to pos-1 or pos-2, resp.
if (pos <= sweepPointer) {
sweepPointer++;
}//if
pos = sesList.addSorted(new Interval(actRect.lry,actRect.ury,"blueright",actRect.urx,null,number,buddy));
if (pos <= sweepPointer) {
sweepPointer++;
}//if
number++;
}//while it4
it.reset();
for (int i = 0; i < sweepPointer+1; i++) it.next();
} else {
//if result is empty, two intervals were deleted, that have positions < X;
//decrease sweepPointer by 2
it.reset();
for (int i = 0; i < sweepPointer+1; i++) it.next();
}//else
} else {
//if predTrue = false;
//simply add actIvl to sss
sss.add(actIvl);
}//else
}//if blueleft
//the actually visited interval is marked "blueright"
else {
//store the object linked to the interval in the result set
resultSet.add(elemStore.get(actIvl.number));
//remove left interval from sss
it2 = sss.iterator();
while (it2.hasNext()) {
sesIvl = (Interval)(((MultiSetEntry)it2.next()).value);
if (sesIvl.number == actIvl.number) {
it2.remove();
break;
}//if
}//while it2
//remove both intervals from ses
it.remove(); //removes right interval
it.reset();
while (it.hasNext()) {
sesIvl = (Interval)(it.next());
if (sesIvl.number == actIvl.number) {
it.remove();
break;
}//if
}//while it
sweepPointer = sweepPointer-2;
it.reset();
for (int i = 0; i < sweepPointer+1; i++)
if (it.hasNext()) it.next();
}//if blueright
}//while it
return resultSet;
}//end method overlapReduceSweep2
/**
* Collects elements from the parameter set and stores them in the resulting set.
* Every element of <tt>ljpMS</tt> consists of a pair <tt>(Element x ElemMultiSet)</tt>. This method traverses the set and
* stores the <tt>Element</tt> in the result set if <tt>Element != NULL</tt>. If <tt>Element == NULL</tt>, the <tt>ElemMultiSet</tt> is stored
* in the result set instead.
*
* @param ljpMS the 'in' set
* @return the collected elements of <tt>ljpMS</tt> stored in an <tt>ElemMultiSet</tt>
*/
public static ElemMultiSet collect (LeftJoinPairMultiSet ljpMS) {
ElemMultiSet result = new ElemMultiSet(ELEM_COMPARATOR);
if (ljpMS.isEmpty()) return result;
Iterator it = ljpMS.iterator();
LeftJoinPair actPair;
while (it.hasNext()) {
actPair = (LeftJoinPair)((MultiSetEntry)it.next()).value;
if (actPair.elemSet == null) {
result.add(actPair.element); }
else result.addAll(actPair.elemSet);
}//while it.hasNext
return result;
}//end method collect
/**
* Collects elements from the parameter set and stores them in the resulting set.
* This method collects <i>only</i> the elements of the <tt>ElemMultiSet</tt>s of every entry of <tt>ljpMS</tt>.
* The first entries, i.e. the <tt>Element</tt> part of every pair, is ignored.
*
* @param ljpMS the 'in' set
* @return the collected elements stored in a ElemMultiSet
*/
public static ElemMultiSet collect2nd (LeftJoinPairMultiSet ljpMS) {
ElemMultiSet result = new ElemMultiSet(ELEM_COMPARATOR);
if (ljpMS.isEmpty()) return result;
Iterator it = ljpMS.iterator();
LeftJoinPair actPair;
while (it.hasNext()) {
actPair = (LeftJoinPair)((MultiSetEntry)it.next()).value;
if (actPair.elemSet != null) {
result.addAll(actPair.elemSet);
}//if
}//while
return result;
}//end method collect2nd
/**
* For the two passed sets, a LeftJoinpPairMultiSet is computed where every element of the set has the form <tt>(Element x ElemMultiSet)</tt>.
* For every pair <tt>Element x ElementOfEMS</tt> of this set element, the <tt>predicate</tt> holds. This set is computed as follows:<ul>
* <li> call <tt>overlappingPairs()</tt> to find all the pairs <tt>e1 x e2</tt>, <tt>e1</tt> element of <tt>el1</tt> and
* <tt>e2</tt> element of <tt>el2</tt>, which have
* overlapping bounding boxes; an {@link EarlyExit} exception is thrown when the <tt>earlyExit</tt> flag is set
* <li> in a filter step, the number of pairs is reduced using the passed <tt>predicate</tt>
* <li> while the set of pairs is traversed, all pairs which have the same first element, are collected and all of them are
* stored in one single entry of the resulting LeftJoinPairMultiSet
* </ul>
* The new LeftJoinPairMultiSet is returned, then.<p>
* This is the 'overlap' version of the normal {@link #leftOuterJoin(ElemMultiSet,ElemMultiSet,Method)}. It only works for predicates which solely hold
* for elements with overlapping bounding boxes.<p>
* If, in a special case, for one element <tt>ex</tt> of <tt>el1</tt> no element <tt>ey</tt> of <tt>el2</tt> is found, the entry constructed in the
* resulting set is <tt>(ex x NULL)</tt>, i.e. the <tt>ElemMultiSet</tt> is <u>not</u> <tt>initialized</tt>.<p>
* The <tt>predicate</tt> must have the signature <code>Element x Element -> boolean</code>.
*
* @param el1 the first set of elements. These elements can be found again in the resulting LeftJoinPairMultiSet
* as the first entry of every <tt>(Element x ElemMultiSet)</tt>. In particular, this means that if <tt>el1</tt> is <tt>(e1, e2, e3...)</tt>,
* the resulting set is <tt>( (e1 x ems1), (e2 x ems2), (e3 x ems3) ...)</tt>.
* @param el2 the second set of elements. The elements of this set are found in the second entries (i.e. in the sets)
* of the resulting LeftJoinPairMultiSet
* @param predicate according to this predicate, the ElemMultiSet(s) in the LeftJoinPairMultiSet's entries are built
* @param useOvLapPairsMeet this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt> objects whith adjacent bounding boxes are
* reported, too
* @param bboxFilter this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt>, in a pre-processing step the number of
* candidates is reduced by removing objects of <tt>el1</tt>,</tt>el2</tt> which don't overlap the unified bounding box of <tt>el1</tt>,
* <tt>el2</tt> resp.
* @param earlyExit if <tt>true</tt>, an {@link EarlyExit} exception is thrown immediately when the first object of <tt>elN</tt> is found which
* doesn't have a partner in <tt>elM</tt>, i.e. at least one element of <tt>elM</tt> which has a bounding box that overlaps the
* bounding box of the element of <tt>elN</tt>. <tt>N</tt> is the passed <tt>setNumber</tt>. earlyExit is evaluated in <tt>bboxFilter</tt>, so if shall
* be used, be sure that <tt>bboxFilter = true</tt>
* @param setNumber must be 1 or 2; specifies the set for earlyExit
* @return the resulting LeftJoinPairMultiSet
* @throws EarlyExit
* @see #leftOuterJoin(ElemMultiSet,ElemMultiSet,Method)
*/
public static LeftJoinPairMultiSet overlapLeftOuterJoin (ElemMultiSet el1, ElemMultiSet el2, Method predicate, boolean useOvLapPairsMeet, boolean bboxFilter, boolean earlyExit, int setNumber)
throws EarlyExit {
//compute overlapping pairs
PairMultiSet pl;
try {
pl = overlappingPairs(el1,el2,false,useOvLapPairsMeet,bboxFilter,earlyExit,setNumber);
} catch (NoOverlappingBoxFoundException nop) {
throw new EarlyExit();
}//catch
//filter set
pl = filter(pl,predicate,true);
//make final set; note, that the elements in pl are already sorted
LeftJoinPairMultiSet retList = new LeftJoinPairMultiSet(LEFTJOINPAIR_COMPARATOR);
Iterator lit1 = el1.iterator();
Iterator lit2;
Element act1;
ElemPair act2;
LeftJoinPair ljp;
boolean stillTrue;
boolean constructedEMS;
int num;
while (lit1.hasNext()) {
act1 = (Element)((MultiSetEntry)lit1.next()).value;
ljp = new LeftJoinPair();
ljp.element = act1;
constructedEMS = false;
while (pl.size() > 0) {
act2 = (ElemPair)pl.first();
num = pl.firstMSE().number;
if (act2.first.equal(act1)) {
if (!constructedEMS) {
ljp.elemSet = new ElemMultiSet(ELEM_COMPARATOR);
constructedEMS = true;
}//if
ljp.elemSet.add(act2.second);
pl.removeAllOfThisKind(act2,num);
}//if
else break;
}//while pl.size > 0
retList.add(ljp);
}//while
return retList;
}//end method overlapLeftOuterJoin
/**
* Invokes the passed method on each pair of elements in every entry of <tt>ljpl</tt>.
* An entry of the passed <tt>ljpl</tt> has the form <tt>(Element x ElemMultiSet)</tt>. Now, let the ElemMultiSet be <tt>(l1, l2 ... ln)</tt>.
* Then, using the passed method <tt>subtractSets</tt> computes from such a pair <tt>(Element x (l1, l2 ... ln))</tt> a new pair
* <tt>(element x (method(Element,l1), method(Element,l2) ... method(Element,ln)))</tt>.<p>
* The method must have the signature <code>Element x Element -> Element</code> or
* <code>Element x Element -> ElemMultiSet</code>.
*
* @param ljpl the passed LeftJoinPairMultiSet
* @param method the method that is invoked on the elements
* @return the result set with the changed ElemMultiSet(s)
*/
public static LeftJoinPairMultiSet subtractSets (LeftJoinPairMultiSet ljpl, Method method) {
Iterator lit1 = ljpl.iterator();
Iterator lit2;
LeftJoinPair actLjp;
ElemMultiSet actList;
Element actElem1;
Element actElem2;
ElemMultiSet subList;
int paramTypeCount = Array.getLength(method.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
boolean metTypeElement = false;
boolean metTypeElemMultiSet = false;
//examine passed method
try {
if (method.getReturnType().isInstance(Class.forName("twodsack.setelement.Element")) ||
method.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.setelement.Element"))) {
metTypeElement = true; }
if (method.getReturnType().isInstance(Class.forName("twodsack.set.ElemMultiSet")) ||
method.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.set.ElemMultiSet"))) {
metTypeElemMultiSet = true; }
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.subtractSets: can't examine method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//traverse element sets
while (lit1.hasNext()) {
actLjp = (LeftJoinPair)((MultiSetEntry)lit1.next()).value;
actElem1 = actLjp.element;
actList = actLjp.elemSet;
if (!(actList == null)) {
lit2 = actList.iterator();
subList = new ElemMultiSet(ELEM_COMPARATOR);
while (lit2.hasNext()) {
actElem2 = (Element)((MultiSetEntry)lit2.next()).value;
//if method has one argument
if (paramTypeCount == 1) {
paramList[0] = actElem2; }
//if method has two arguments
else {
paramList[0] = actElem1;
paramList[1] = actElem2; }
try {
if (metTypeElement) {
subList.add((Element)(method.invoke(actElem1,paramList))); }
else if (metTypeElemMultiSet) {
subList.addAll((ElemMultiSet)(method.invoke(actElem1,paramList))); }
else {
System.out.println("Error in SetOps.subtractSets: can't invoke method");
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.subtractSets: Problem with using method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
}//while
//substitute original elemSet with subList
actLjp.elemSet = subList;
}//if != null
}//while
return ljpl;
}//end method subtractSets
/**
* Returns a set of elements which is 'reduced' using the passed predicate and method.
* This is a variant of the normal {@link #reduce(ElemMultiSet,Method,Method)} operation. For the fuctionality of <tt>reduce</tt> itself,
* have a look at that method.<p>
* This is a recursive algorithm which uses graph algorithms to find groups of candidates for the invocation
* of the passed method. First note, that the predicate must be an 'overlap' predicate, i.e. is may only yield
* <tt>true</tt>, if the bounding boxes of both elements overlap or are adjacent at least. Additionally, the method
* may only return an object which bounding box lies inside of the bounding box of the bounding box of the
* objects it was constructed from (for clarification: the result may not be <i>bigger</i> than the original two
* objects.)<p>
* <tt>overlapReduce(el,PRED,METH,...)</tt> works as follows:<ol>
* <li> perform <tt>overlappingPairs</tt> on <tt>el</tt> to find all pairs of candidates, result is called <tt>PL</tt>; if <tt>earlyExit</tt>
* flag is set,
* throw an <tt>EarlyExit</tt> exception if neccessary
* <li> filter <tt>PL</tt> using <tt>PRED</tt>
* <li> if then, no pairs still exist, return <tt>el</tt>
* <li> build a graph with vertices (all elements of <tt>el</tt>) and edges; an edge exists between <tt>x,y</tt> (of <tt>el</tt>) if a pair
* <tt>(x,y)</tt> exists in <tt>PL</tt>
* <li> compute the connected components of that graph
* <li> for every component <tt>COMP</tt> of the connected components, do the following <ul>
* <li> compute a set of (independent) pairs of that component, such that no element is part of two pairs
* <li> invoke the method <tt>METH</tt> on all those pairs
* <li> replace every pair <tt>(x,y)</tt> with <tt>METH(x,y)</tt>
* <li> call <tt>overlapReduce(COMP,...)</tt>
* </ul></ol><p>
* The predicate must have the signature <code>Element x Element -> boolean</code> and method must have the signature
* <code>Element x Element -> ElemMultiSet</code>
*
* @param el the set that shall be reduced
* @param predicate according to this predicate, candidates are filtered
* @param method this method is invoked on the pairs of elements that were found
* @param ovLapPairsMeet this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt>, objects with adjacent bounding boxes are
* reported, too
* @param bboxFilter this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt>, in a pre-processing step the number of
* candidates is reduced by removing objects of <tt>el1,el2</tt> which don't overlap the unified bounding box of <tt>el1,el2</tt> resp.
* @param earlyExit if <tt>true</tt>, an <tt>EarlyExit</tt> exception is thrown immediately when the first object of <tt>elN</tt> is found which
* doesn't have a partner in <tt>elM</tt>, i.e. at least one element of <tt>elM</tt> which has a bounding box that overlaps the
* bounding box of the element of <tt>elN</tt>. <tt>N</tt> is the passed setNumber. <tt>earlyExit</tt> is evaluated in <tt>bboxFilter</tt>, so if shall
* be used, be sure that <tt>bboxFilter = true</tt>
* @param setNumber must be 1 or 2; specifies the set for <tt>earlyExit</tt>
* @return the 'reduced' set of elements
* @throws EarlyExit
*/
public static ElemMultiSet overlapReduce (ElemMultiSet el, Method predicate, Method method, boolean ovLapPairsMeet, boolean bboxFilter, boolean earlyExit, int setNumber)
throws EarlyExit {
ElemMultiSet retList = new ElemMultiSet(ELEM_COMPARATOR);
PairMultiSet pl;
try {
pl = overlappingPairs(el,el,true,ovLapPairsMeet,bboxFilter,earlyExit,setNumber);
} catch (NoOverlappingBoxFoundException nop) {
throw new EarlyExit();
}//catch
pl = filter(pl,predicate,true);
if (pl.isEmpty()) {
return el; }
Graph g = new Graph(el,pl);
ConnectedComponentsPair ccp = g.connectedComponents();
//the pairs from ccE must not be computed all at once
//so compute the set of pairs that may be computed
ccp = g.computeReducedPair(ccp);
ElemMultiSetList vertices = ccp.verticesToEMSList();
PairMultiSetList edges = ccp.edgesToPairListList();
ListIterator litE = edges.listIterator(0);
ListIterator litV = vertices.listIterator(0);
PairMultiSet actPL;
ElemMultiSet actEL;
ElemMultiSet ml = new ElemMultiSet(ELEM_COMPARATOR);
while (litE.hasNext()) {
actPL = (PairMultiSet)litE.next();
actEL = (ElemMultiSet)litV.next();
//if there are no pairs, get the isolated elements from actEL
if (!actPL.isEmpty()) {
ml = map(actPL,method); }
else { ml = new ElemMultiSet(ELEM_COMPARATOR); }
retList.addAll(overlapReduce(disjointUnion(ml,actEL),predicate,method,ovLapPairsMeet,bboxFilter,earlyExit,setNumber));
}//for i
rdup(retList);
return retList;
}//end method overlapReduce
/**
* Lets through pairs of the set depending on the predicate and the 'keep' flag.
* If <tt>keep = true</tt>, a pair <tt>(x,y)</tt> is kept if <tt>predicate(x,y) = true</tt>. All other pairs are deleted. If <tt>keep = false</tt>,
* the inverse set is returned.<p>
* The predicate must have the signature <code>Element x Element -> boolean</code>.
*
* @param plIn the set of pairs
* @param predicate the predicate that is used to filter the pairs
* @param keep if <tt>true</tt>, pairs are kept for which the predicate holds
* @return the filtered set
*/
public static PairMultiSet filter (PairMultiSet plIn, Method predicate, boolean keep) {
int paramTypeCount = Array.getLength(predicate.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
boolean predHolds = false;
Iterator it = plIn.iterator();
ElemPair actElem;
MultiSetEntry mse;
int number;
while (it.hasNext()) {
actElem = (ElemPair)((MultiSetEntry)it.next()).value;
//if predicate is non-static
if (paramTypeCount == 1) {
paramList[0] = actElem.second;
}//if
//if predicate is static
else {
paramList[0] = actElem.first;
paramList[1] = actElem.second;
}//else
try {
predHolds = (((Boolean)predicate.invoke(actElem.first,paramList)).booleanValue());
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" in SetOps.filter(PairList,Method,boolean). Can't invoke method '"+predicate+"' on");
actElem.first.print();
actElem.second.print();
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if ((predHolds && !keep) ||
(!predHolds && keep)) { it.remove(); }
}//while
plIn.recomputeSize();
return plIn;
}//end method filter
/**
* Divides the elements of the passed set in groups according to the parameter predicate.<p>
* This is the 'overlap' variant of the ordinary {@link #group(ElemMultiSet,Method)}. The result of this method is a list of <tt>ElemMultiSet</tt>(s).
* For every pair of elements <tt>(x,y)</tt> of such a group (<tt>ElemMultiSet</tt>), <tt>predicate(x,y)</tt> holds.<p>
* First note, that the passed predicate must be an 'overlap' predicate. Such a predicate may only hold, if
* the bounding box of its parameter objects overlap or are adjacent at least.<p>
* This method works as follows:<ul>
* <li> call <tt>overlappingPairs</tt> to compute the candidate pairs; store the result in <tt>PL</tt>
* <li> filter <tt>PL</tt> using <tt>predicate</tt>
* <li> construct a graph from <tt>PL</tt> using <tt>ems</tt> as vertices and <tt>PL</tt> as edges
* <li> compute the connected components for that graph
* <li> store all elements of such a component in a single <tt>ElemMultiSet</tt>; store all <tt>ElemMultiSet</tt>(s) in
* a <tt>ElemMultiSetList</tt>
* <li> return that <tt>ElemMultiSetList</tt><p>
* The signature of the predicate must be <code>Element x Element -> boolean</code>.
*
* @param ems the set of elements that shall be divided in groups
* @param predicate the predicate that is used to filter the candidate pairs
* @param ovLapPairsMeet this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt>, objects with adjacent bounding boxes are
* reported, too
* @return the groups of elements
*/
public static ElemMultiSetList overlapGroup (ElemMultiSet ems, Method predicate, boolean ovLapPairsMeet) {
//find the overlapping pairs of elements
boolean sameSet = true;
boolean bboxFilter = false;
boolean earlyExit = false;
PairMultiSet pl = null;
double tt01 = System.currentTimeMillis();
try {
pl = overlappingPairs(ems,ems,sameSet,ovLapPairsMeet,bboxFilter,earlyExit,0);
} catch (Exception e) {
System.out.println("Unexpected error in SetOps.overlapGroup during execution of overlappingPairs.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
double tt02 = System.currentTimeMillis();
//filter method: remove pairs for which predicate doesn't hold
pl = filter(pl,predicate,true);
double tt03 = System.currentTimeMillis();
//construct a graph with vertices: elements and edges exist for pairs of elements
Graph g = new Graph(ems,pl);
double tt04 = System.currentTimeMillis();
//compute the connected components
ConnectedComponentsPair ccp = g.connectedComponents();
double tt05 = System.currentTimeMillis();
ElemMultiSetList retList = ccp.verticesToEMSList();
double tt06 = System.currentTimeMillis();
/*
System.out.println("\n+++++++++++++++++++++++++++++++++++++");
System.out.println("costs for overlapGroup (in detail):");
System.out.println("overlappingPairs: "+(tt02-tt01)+" ms");
System.out.println("filter: "+(tt03-tt02)+" ms");
System.out.println("construct graph: "+(tt04-tt03)+" ms");
System.out.println("connectedComponents: "+(tt05-tt04)+" ms");
System.out.println("construct result list: "+(tt06-tt05)+" ms");
System.out.println("+++++++++++++++++++++++++++++++++++++");
*/
return retList;
}//end method overlapGroup
/**
* Computes a join on two element sets.<p>
* This is a variant of the normal <tt>join</tt> operation which uses 'overlap' predicates.
* First note, that the passed predicate must be an 'overlap' predicate. Such a predicate may only hold, if
* the bounding box of its parameter objects overlap or are adjacent at least.<p>
* That 'overlap' predicate is also the join predicate. For all pairs <tt>(x,y)</tt> in the resulting set, that predicate holds.<p>
* The join is computed by first calling <tt>overlappingPairs(ems1,ems2,...)</tt>. After that, the resulting set is filtered
* using predicate.<p>
* The predicate must have the signature <code>Element x Element -> boolean</code>.
*
* @param ems1 the first set
* @param ems2 the second set
* @param predicate the join predicate
* @param ovLapPairsMeet this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt>, objects with adjacent bounding boxes are
* reported, too
* @param bboxFilter this flag is passed to <tt>overlappingPairs()</tt>; if <tt>true</tt>, in a pre-processing step the number of
* candidates is reduced by removing objects of <tt>el1,el2</tt> which don't overlap the unified bounding box of <tt>el1,el2</tt> resp.
* @param earlyExit if <tt>true</tt>, an <tt>EarlyExit</tt> exception is thrown immediately when the first object of <tt>elN</tt> is found which
* doesn't have a partner in <tt>elM</tt>, i.e. at least one element of <tt>elM</tt> which has a bounding box that overlaps the
* bounding box of the element of <tt>elN</tt>. <tt>N</tt> is the passed setNumber. <tt>earlyExit</tt> is evaluated in <tt>bboxFilter</tt>, so if shall
* be used, be sure that <tt>bboxFilter = true</tt>
* @param setNumber must be 1 or 2; specifies the set for <tt>earlyExit</tt>
* @return the PairMultiSet as join of <tt>ems1</tt> and <tt>ems2</tt>
* @throws EarlyExit
*/
public static PairMultiSet overlapJoin (ElemMultiSet ems1, ElemMultiSet ems2, Method predicate, boolean ovLapPairsMeet, boolean bboxFilter, boolean earlyExit, int setNumber)
throws EarlyExit {
PairMultiSet retSet;
try {
retSet = overlappingPairs(ems1,ems2,false,ovLapPairsMeet,bboxFilter,earlyExit,setNumber);
} catch (NoOverlappingBoxFoundException nop) {
throw new EarlyExit();
}//catch
retSet = filter(retSet,predicate,true);
return retSet;
}//end method overlapJoin
/**
* This method constructs groups from the passed set such that for every element <tt>e</tt> in a group another element <tt>f</tt> can be found with <tt>predicate(e,f) = true</tt>.<p>
* Example: Let the original set be <tt>{a,b,c,d}</tt>. Assume, that the predicate <tt>p</tt> holds for the pairs <tt>(a,b),(b,c)</tt>.
* Then, the resulting groups are <tt>{a,b,c}, {d}</tt>.<p>
* The predicate's signature must be: <tt>Element x Element -> boolean</tt>
*
* @param ems the set that shall be divided in groups
* @param predicate the predicate that is responsible for the division into groups
* @return the list of groups
* @see #overlapGroup(ElemMultiSet,Method,boolean)
*/
public static ElemMultiSetList group (ElemMultiSet ems, Method predicate) {
/*
* This method works as follows: It traverses ems and for each element E it checks all already existing groups.
* It traverses each group. For each element F of such group it invokes predicate(E,F) until the predicate holds.
* Then, it stores the group in a special list. This is done for each group. The reason why this complicated
* computation is needed is that a new element E can _combine_ two or more groups. Therefore, all groups must be visited.
* After all groups were checked, all groups in the special list are joined. If there is no entry in the special list
* a new group is build for E.
*/
ElemMultiSetList retList = new ElemMultiSetList();
if (ems == null || ems.isEmpty()) return retList;
int paramTypeCount = Array.getLength(predicate.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
Iterator it = ems.iterator();
Iterator itRL, it2;
Element actElem1, actElem2;
ElemMultiSet actGroup;
boolean belongsToGroup = false;
LinkedList idxList = new LinkedList();
//traverse elements of ems
while (it.hasNext()) {
actElem1 = (Element)((MultiSetEntry)it.next()).value;
belongsToGroup = false;
idxList.clear();
if (retList.isEmpty()) {
//build new group and add it to retList
ElemMultiSet group = new ElemMultiSet(ELEM_COMPARATOR);
group.add(actElem1);
retList.add(group);
} else {
//other groups already exist
itRL = retList.listIterator(0);
while (itRL.hasNext()) {
actGroup = (ElemMultiSet)itRL.next();
it2 = actGroup.iterator();
while (it2.hasNext()) {
actElem2 = (Element)((MultiSetEntry)it2.next()).value;
if (paramTypeCount == 1)
//predicate is non-static
paramList[0] = actElem2;
else {
//predicate is static
paramList[0] = actElem1;
paramList[1] = actElem2;
}//else
//evaluate predicate
try {
belongsToGroup = ((Boolean)predicate.invoke(actElem1,paramList)).booleanValue();
} catch (Exception e) {
System.out.println("Error in SetOps.group: Wasn't able to invoke predicate.");
e.printStackTrace();
}//catch
System.out.println(belongsToGroup);
if (belongsToGroup) {
//actGroup.add(actElem2);
idxList.add(actGroup);
break;
}//if
}//while it2
}//while itRL
if (belongsToGroup || !idxList.isEmpty()) {
//merge all groups for which belongsToGroup was true
while (idxList.size() > 1) {
((ElemMultiSet)idxList.get(idxList.size()-2)).addAll((ElemMultiSet)idxList.get(idxList.size()-1));
//clear and remove second set
((ElemMultiSet)idxList.get(idxList.size()-1)).clear();
idxList.remove(idxList.size()-1);
}//while
//now, add the new element
((ElemMultiSet)idxList.get(0)).add(actElem1);
//remove all empty groups from retList
itRL = retList.listIterator(0);
while (itRL.hasNext()) {
actGroup = (ElemMultiSet)itRL.next();
if (actGroup.isEmpty()) itRL.remove();
}//while
} else {
//actElem doesn't belong to any group
//build new group and add it to retList
ElemMultiSet group = new ElemMultiSet(ELEM_COMPARATOR);
group.add(actElem1);
retList.add(group);
}//else
}//else other groups exist
}//while it hasNext
return retList;
}//end method group
/**
* This map method invokes the <tt>secMethod</tt> on pairs of elements of <tt>ljpl</tt> for which the <tt>predicate</tt> holds.<p>
* The <tt>mainMethod</tt> itself must be a set operation with the signature <code>ElemMultiSet x ElemMultiSet x predicate
* x secMethod -> ElemMultiSet</code>. Such a method can be found in this class.
* Then, this map operation only splits every entry of <tt>ljpl</tt> and calls
* <tt>mainMethod</tt> with the two objects of the entry and the parameters <tt>predicate</tt> and <tt>secMethod</tt>.<p>
* The signatures for predicate and secMethod have to be <code>Element x Element -> boolean</code> and
* <code>Element x Element -> Element</code>, resp.<p>
* The resulting sets of mainMethods are collected and stored in the result set.
* @param ljpl the 'in' set
* @param mainMethod this method is called for every entry of <tt>ljpl</tt>
* @param predicate the predicate is passed to <tt>mainMethod</tt>
* @param secMethod this method is passed to <tt>mainMethod</tt>
* @return the union of the results of <tt>mainMethod</tt>
*/
public static ElemMultiSet map (LeftJoinPairMultiSet ljpl, Method mainMethod, Method predicate, Method secMethod) {
ElemMultiSet retList = new ElemMultiSet(ELEM_COMPARATOR);
Object[] paramListMM = new Object[4];
ElemMultiSet paramList1 = new ElemMultiSet(ELEM_COMPARATOR);
Iterator lit = ljpl.iterator();
LeftJoinPair actLjp;
while (lit.hasNext()) {
actLjp = (LeftJoinPair)((MultiSetEntry)lit.next()).value;
//if second list is not empty do the computation
if (!(actLjp.elemSet == null) && !actLjp.elemSet.isEmpty()) {
try {
paramList1.clear();
paramList1.add(actLjp.element);
paramListMM[0] = paramList1;
paramListMM[1] = actLjp.elemSet;
paramListMM[2] = predicate;
paramListMM[3] = secMethod;
retList.addAll((ElemMultiSet)mainMethod.invoke(null,paramListMM));
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.map(ljpl,m,m,m). Can't invoke method "+mainMethod);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
}//if
else { retList.add(actLjp.element); }
}//for i
return retList;
}//end method map
/**
* Perfoms a projection on the first element.
* The first partner of every pair is stored in the result set.
*
* @param pl the 'in' set
* @return the projection on the first element
*/
public static ElemMultiSet proj1 (PairMultiSet pl) {
ElemMultiSet retSet = new ElemMultiSet(ELEM_COMPARATOR);
Iterator it = pl.iterator();
while (it.hasNext()) retSet.add(((ElemPair)((MultiSetEntry)it.next()).value).first);
return retSet;
}//end method proj1
/**
* Performs a projection on the second element.
* The second partner of every pair is stored in the result set.
*
* @param pl the 'in' set
* @return the projection on the second element
*/
public static ElemMultiSet proj2 (PairMultiSet pl) {
ElemMultiSet retSet = new ElemMultiSet(ELEM_COMPARATOR);
Iterator it = pl.iterator();
while (it.hasNext()) retSet.add(((ElemPair)((MultiSetEntry)it.next()).value).second);
return retSet;
}//end method proj2
/**
* Returns <tt>true</tt>, if both sets are disjoint.
* Takes use of the <tt>intersects()</tt> method which must be implemented for each type which implements the {@link Element} interface.
* An <tt>overlapJoin()</tt> is computed using that <tt>intersects()</tt> method. If the result is empty, <tt>true</tt> is returned.
* <tt>false</tt> otherwise.
*
* @param ems1 the first set
* @param ems2 the second set
* @return <tt>true</tt>, if <tt>ems1,ems2</tt> are disjoint
*/
public static boolean disjoint (ElemMultiSet ems1, ElemMultiSet ems2) {
if (ems1 == null || ems1.isEmpty() ||
ems2 == null || ems2.isEmpty()) return true;
Class [] paramList = new Class [1];
PairMultiSet rms = new PairMultiSet(ELEMPAIR_COMPARATOR);
Class c = ems1.first().getClass();
try {
paramList[0] = Class.forName("twodsack.setelement.Element");
Method intersectsM = c.getMethod("intersects",paramList);
rms = overlapJoin(ems1,ems2,intersectsM,true,true,false,0);
} catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.disjoint. Can't get Method intersects.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (rms.isEmpty()) { return true; }
else { return false; }
}//end method disjoint
/**
* Returns true, if both sets are equal.<p>
* Uses the <tt>compare()</tt> method which must be implemented for every type which implements the {@link Element} interface.<p>
* Throws a <tt>WrongTypeException</tt>, if the sets are not of the same type.<p>
* Note, that <tt>equal((a,a,b), (a,b))</tt> returns <tt>false</tt>.
*
* @param el1 the first set
* @param el2 the second set
* @return <tt>true</tt>, if both sets are equal
*/
public static boolean equal (ElemMultiSet el1, ElemMultiSet el2) throws WrongTypeException {
if (el1.size() != el2.size()) return false;
Iterator it1 = el1.iterator();
Iterator it2 = el2.iterator();
ElemComparator ec = ELEM_COMPARATOR;
while (it1.hasNext())
if (ec.compare(it1.next(),it2.next()) != 0)
return false;
return true;
}//end method equal
/**
* For two sets <tt>E,F</tt> and a predicate <tt>p</tt> this method returns a set of {@link LeftJoinPair}s where the first elements of the pairs are elements of <tt>E</tt> and their partners are elements of <tt>F</tt>.
* Each element of <tt>E</tt> appears exactly once in the resulting {@link LeftJoinPairMultiSet}. For all of the partners of such an
* element <tt>e</tt>, <tt>p(e,g)</tt>, <tt>g</tt> element of the partner set of <tt>e</tt>, holds.<p>
* Given an example, if <tt>E,F</tt> are sets of triangles and <tt>p</tt> is a predicate <tt>overlap: Triangle x Triangle -> boolean</tt>.
* <tt>E = {a,b,c,d}, F = {e,f,g}</tt>. Now, <tt>e</tt> overlaps <tt>a,b</tt> and <tt>g</tt> overlaps <tt>a,b,c</tt>. Then the result would be:<p>
* <tt>{<br>
* (a x {e,g}),<br>
* (b x {e,g}),<br>
* (c x {g}),<br>
* (d x {})<br>
* }</tt><p>
* The predicate must have the signature: <tt>Element x Element -> boolean</tt>.
*
* @param ems1 the first set
* @param ems2 the second set
* @param predicate the join predicate used to construct the <tt>LeftJoinPair</tt>s
* @return the resulting <tt>LeftJoinPairMultiSet</tt>
* @see #overlapLeftOuterJoin(ElemMultiSet,ElemMultiSet,Method,boolean,boolean,boolean,int)
*/
public static LeftJoinPairMultiSet leftOuterJoin (ElemMultiSet ems1, ElemMultiSet ems2, Method predicate) {
LeftJoinPairMultiSet retSet = new LeftJoinPairMultiSet(LEFTJOINPAIR_COMPARATOR);
int paramTypeCount = Array.getLength(predicate.getParameterTypes());
boolean predHolds = false;
Element[] paramList = new Element[paramTypeCount];
Iterator it1 = ems1.iterator();
Iterator it2;
Element actElem1,actElem2;
//traverse the first set
while (it1.hasNext()) {
actElem1 = (Element)((MultiSetEntry)it1.next()).value;
//construct new LeftJoinPair and initialize values
LeftJoinPair newLjp = new LeftJoinPair();
newLjp.element = actElem1;
newLjp.elemSet = new ElemMultiSet(ELEM_COMPARATOR);
//traverse the second set
it2 = ems2.iterator();
while (it2.hasNext()) {
actElem2 = (Element)((MultiSetEntry)it2.next()).value;
//set paramList
if (paramTypeCount == 1)
//predicate is non-static
paramList[0] = actElem2;
else {
//predicate is static
paramList[0] = actElem1;
paramList[1] = actElem2;
}//else
//invoke predicate
try {
predHolds = ((Boolean)predicate.invoke(actElem1,paramList)).booleanValue();
} catch (Exception e) {
System.out.println("Error in SetOps.leftOuterJoin: Can't invoke predicate.");
e.printStackTrace();
}//catch
//add element to elemSet if predHolds == true
if (predHolds)
newLjp.elemSet.add(actElem2);
}//while it2
//add new LeftJoinPair to retSet
retSet.add(newLjp);
}//while it1
return retSet;
}//end method leftOuterJoin
/**
* For every pair <tt>(Element x ElemMultiSet)</tt> of <tt>ljpMS</tt> the method is invoked on that pair if the predicate holds.
* The predicate may be <tt>NULL</tt>. If so, it is assumed, that the predicate holds for all pairs of <tt>ljpMS</tt>. Then, the
* method is invoked on all entries with no further checks.<p>
* For every {@link LeftJoinPair}, the <tt>method</tt> is invoked on that pair, if the <tt>predicate</tt> holds (if not <tt>NULL</tt>). Then, the
* result of the method invocation is stored in <tt>LeftJoinPair.elemSet</tt>, i.e. it replaces the original
* <tt>ElemMultiSet</tt>. The first argument of the method (the <tt>Element</tt>) remains unchanged.<p>
* The allowed signature for predicate is <code>Element x ElemMultiSet -> boolean</code>. The method must have
* the signature <code>Element x ElemMultiSet -> ElemMultiSet</code>.
*
* @param ljpMS the 'in' set
* @param predicate it checks whether the <tt>method</tt> may be invoked on a pair; may be <tt>NULL</tt>
* @param method is invoked on each pair for which the predicate holds
* @return the changed <tt>leftJoinPairMultiSet</tt>
*/
public static LeftJoinPairMultiSet map (LeftJoinPairMultiSet ljpMS, Method predicate, Method method) {
boolean predOkay = (predicate != null);
int paramTypeCountP = 0;
Object[] paramListP = null;
if (predOkay) {
paramTypeCountP = Array.getLength(predicate.getParameterTypes());
paramListP = new Object[paramTypeCountP];
}//if
int paramTypeCountM = Array.getLength(method.getParameterTypes());
Object[] paramListM = new Object[paramTypeCountM];
ElemMultiSet returnSet = new ElemMultiSet(ELEM_COMPARATOR);
Iterator it = ljpMS.iterator();
LeftJoinPair actLJP;
Element actElem;
ElemMultiSet actSet;
boolean isTrue = false;
ElemMultiSet mResult = null;
while (it.hasNext()) {
actLJP = (LeftJoinPair)((MultiSetEntry)it.next()).value;
actElem = actLJP.element;
actSet = actLJP.elemSet;
//if predicate is okay and non-static
if (predOkay) {
if (paramTypeCountP == 1) {
paramListP[0] = actSet; }
else {
paramListP[0] = actElem;
paramListP[1] = actSet;
}//else
//compute boolean value for predicate
try {
isTrue = ((Boolean)predicate.invoke(actElem,paramListP)).booleanValue();
} catch (Exception e) {
System.out.println("\nException in SetOps.map(ljpMS,pred,met) when computing boolean value.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
}//if
else isTrue = true;
if (isTrue) {
//if method has one argument
if (paramTypeCountM == 1)
paramListM[0] = actSet;
else {
paramListM[0] = actElem;
paramListM[1] = actSet;
}//else
//invoke method
try {
mResult = (ElemMultiSet)method.invoke(actElem,paramListM);
} catch (Exception e) {
System.out.println("Exception in SetOps.map(ljpMS,pred,met) when invoking method.");
System.out.println("\nmethod: "+method);
System.out.println("\nactElem: "+actElem);
System.out.println("\nparamListM[0]: "+paramListM[0]);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//set result
actLJP.elemSet = mResult;
}//if isTrue
}//while it.hasNext
return ljpMS;
}//end method map
/**
* As long as a pair <tt>e1</tt> of <tt>el1 x e2</tt> of <tt>el2</tt> exists which <tt>predicate(e1,e2) == true</tt>, replace <tt>e1</tt> in <tt>el</tt> with <tt>method(e1,e2)</tt>.
* The <tt>predicate</tt> and <tt>method</tt> must have the signature <code>Element x Element -> boolean</code> and
* <code>Element x Element -> Element</code> (of the same type as <tt>el1</tt>), resp.<p>
* This is a very expensive operation w.r.t. time consumption!
*
* @param el1 the first set
* @param el2 the second set
* @param predicate chooses the candidates for <tt>method</tt>
* @param method is invoked on candidates selected by <tt>predicate</tt>
* @return the 'subtracted' set
*/
public static ElemMultiSet subtract (ElemMultiSet el1, ElemMultiSet el2, Method predicate, Method method) {
int paramTypeCountPred = Array.getLength(predicate.getParameterTypes());
int paramTypeCountMet = Array.getLength(method.getParameterTypes());
ElemMultiSet retSet = (ElemMultiSet)el1.clone();
ElemMultiSet paramList = (ElemMultiSet)el2.clone();
boolean stillExist = false;
boolean isTrue = false;
Element[] paramListP = new Element[paramTypeCountPred];
Element[] paramListM = new Element[paramTypeCountMet];
ElemMultiSet mreturn = new ElemMultiSet(ELEM_COMPARATOR);
Iterator lit1;
Iterator lit2;
Element actEl1;
Element actEl2;
ElemMultiSet finalSet = new ElemMultiSet(ELEM_COMPARATOR);
MultiSetEntry mse1;
do {
stillExist = false;
lit1 = retSet.iterator();
while (lit1.hasNext()) {
mse1 = (MultiSetEntry)lit1.next();
actEl1 = (Element)mse1.value;
isTrue = false;
lit2 = paramList.iterator();
while (lit2.hasNext()) {
actEl2 = (Element)((MultiSetEntry)lit2.next()).value;
//if predicate has one argument
if (paramTypeCountPred == 1) {
paramListP[0] = actEl2;
}//if
//if predicate has two arguments
else {
paramListP[0] = actEl1;
paramListP[1] = actEl2;
}//else
try {
isTrue = ((Boolean)predicate.invoke(actEl1,paramListP)).booleanValue();
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.subtract(el,el,pr,m).");
System.out.println("Cause for Exception: "+e.getCause());
System.out.println("Exception String: "+e.toString());
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (isTrue) {
//if method has one argument
if (paramTypeCountMet == 1) {
paramListM[0] = actEl2;
}//if
//if method has two arguments
else {
paramListM[0] = actEl1;
paramListM[1] = actEl2;
}//else
try {
mreturn = (ElemMultiSet)(method.invoke(actEl1,paramListM));
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.subtract(el,el,pr,m). Can't invoke method "+method);
System.out.println("Cause for Exception: "+e.getCause());
System.out.println("Exception String: "+e.toString());
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (isTrue) {
//copy first elements of retSet to finalSet
//these elements are not needed anymore
SortedSet ss = retSet.treeSet().headSet(mse1);
Iterator tempIt = ss.iterator();
while (tempIt.hasNext()) {
finalSet.add((Element)((MultiSetEntry)tempIt.next()).value); }
retSet.removeAllOfThisKind(actEl1);
retSet.addAll(mreturn);
mreturn = null;
stillExist = true;
lit1 = retSet.iterator();
break;
}//if
if (retSet.isEmpty()) { break; }
}//if
}//for j
if (retSet.isEmpty()) {
break; }
}//for i
}//do
while (stillExist);
//copy all elements of retSet to finalSet
Iterator tmpIt = retSet.iterator();
while (tmpIt.hasNext()) {
finalSet.add((Element)((MultiSetEntry)tmpIt.next()).value); }
return finalSet;
}//end method subtract
/**
* Returns a pair of elements which returns the maximum value of all possible pairs of <tt>ems1,ems2</tt> using method.<p>
* All pairs of <tt>e1</tt> of <tt>ems1</tt> and <tt>e2</tt> of <tt>ems2</tt> are checked for their return value of
* <tt>method(e1,e2)</tt>. That pair, which returns
* the maximum value of all pairs, is returned.<p>
* The passed method must have the signature <code>Element x Element -> Rational</code>.
*
* @param ems1 the first set
* @param ems2 the second set
* @param method the method that is used to compute the values for <tt>ElemPair</tt>(s)
* @return the pair with the maximum value
*/
public static ElemPair max (ElemMultiSet ems1, ElemMultiSet ems2, Method method) {
Element elem1 = (Element)ems1.first();
Element elem2 = (Element)ems2.first();
Rational maximum = RationalFactory.constRational(0); // just for initialization
Rational value = RationalFactory.constRational(0); //dto.
int paramTypeCount = Array.getLength(method.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
//initialization
try {
//if method has one argument
if (paramTypeCount ==1) paramList[0] = (Element)ems2.first();
//if method has two arguments (is static)
else {
paramList[0] = (Element)ems1.first();
paramList[1] = (Element)ems2.first();
}//else
maximum = ((Rational)method.invoke(elem1,paramList));
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.max. Can't invoke method "+method);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
Iterator it1 = ems1.iterator();
Iterator it2;
Element actElem1;
Element actElem2;
while (it1.hasNext()) {
actElem1 = (Element)((MultiSetEntry)it1.next()).value;
it2 = ems2.iterator();
while (it2.hasNext()) {
actElem2 = (Element)((MultiSetEntry)it2.next()).value;
try {
//if method has one argument
if (paramTypeCount == 1) {
paramList[0] = actElem2;
}//if
//if method has two arguments (is static)
else {
paramList[0] = actElem1;
paramList[1] = actElem2;
}//else
value = ((Rational)method.invoke(actElem1,paramList));
} catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.max. Can't invoke method "+method);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (value.greater(maximum)) {
maximum = value.copy();
elem1 = actElem1;
elem2 = actElem2;
}//if
}//while it2
}//while it1
return new ElemPair(elem1,elem2);
}//end method max
/**
* Returns a pair of elements which returns the minimum value of all possible pairs of <tt>ems1,ems2</tt> using method.<p>
* All pairs of <tt>e1</tt> of <tt>ems1</tt> and <tt>e2</tt> of <tt>ems2</tt> are checked for their return value of
* <tt>method(e1,e2)</tt>. That pair, which returns
* the minimum value of all pairs, is returned.<p>
* The passed method must have the signature <code>Element x Element -> Rational</code>.
*
* @param el1 the first set
* @param el2 the second set
* @param method the method that is used to compute the values for <tt>ElemPair</tt>(s)
* @return the pair with the maximum value
*/
public static ElemPair min (ElemMultiSet el1, ElemMultiSet el2, Method method) {
if (el1 == null || el1.isEmpty() || el2 == null || el2.isEmpty()) return null;
Element elem1 = (Element)el1.first();
Element elem2 = (Element)el2.first();;
Rational value = null;
int paramTypeCount = Array.getLength(method.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
Rational minimum = null;
//initialization
try {
//if method has one argument
if (paramTypeCount == 1) {
paramList[0] = (Element)el2.first();
}//if
//if method has two arguments (is static)
else {
paramList[0] = (Element)el1.first();
paramList[1] = (Element)el2.first();
}//else
minimum = ((Rational)method.invoke(el1.first(),paramList));
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.min. Can't invoke method "+method);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
Iterator it1 = el1.iterator();
Iterator it2;
Element actElem1;
Element actElem2;
while (it1.hasNext()) {
actElem1 = (Element)((MultiSetEntry)it1.next()).value;
it2 = el2.iterator();
while (it2.hasNext()) {
actElem2 = (Element)((MultiSetEntry)it2.next()).value;
//if method has one argument
if (paramTypeCount == 1) {
paramList[0] = actElem2;
}//if
//if method has two arguments (is static)
else {
paramList[0] = actElem1;
paramList[1] = actElem2;
}//else
try {
value = ((Rational)method.invoke(actElem1,paramList));
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.min. Can't invoke method "+method);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (value.equal(0)) return new ElemPair(actElem1,actElem2);
if (value.less(minimum)) {
minimum = value.copy();
elem1 = actElem1;
elem2 = actElem2;
}//if
}//while j
}//while i
return new ElemPair(elem1,elem2);
}//end method min
/**
* As long as a pair <tt>e1,e2</tt> of <tt>el</tt> exists with <tt>predicate(e1,e2) == true</tt>, replace both elements by <tt>method(e1,e2)</tt>.<p>
* The reduce operation traverses the set <tt>el</tt> and searches for pairs of elements for which the <tt>predicate</tt> holds.
* If such a pair is found, both elements are removed from the set and the result of the <tt>method</tt> invoked on that
* pair is added to the set. If, at some point, no such pair can be found, reduce exits and returns the 'reduced' set.<p>
* The <tt>predicate</tt> must have the signature <code>Element x Element -> boolean</code>. The <tt>method</tt> must have the signature
* <code>Element x Element -> Element</code> (of the same type).<p>
* This method is very time consuming. Some other implementations of <tt>reduce</tt> exist and can be found in this class.
*
* @param el the 'in' set that shall be reduced
* @param predicate checks for candidates for the method
* @param method is invoked on the candidates found by the predicate
* @return the 'reduced' set
* @see #overlapReduce(ElemMultiSet,Method,Method,boolean,boolean,boolean,int)
* @see #overlapReduceSweep(ElemMultiSet,Method,Method,boolean)
*/
public static ElemMultiSet reduce (ElemMultiSet el, Method predicate, Method method) {
ElemMultiSet retSet = el.copy();
boolean stillExist;
boolean isTrue;
int paramTypeCountPred = Array.getLength(predicate.getParameterTypes());
Element[] paramList = new Element[paramTypeCountPred];
Element[] paramListMet = new Element[2];
Element actElem1;
Element actElem2;
//these variables are used to determine wether the method
//has return type Element or ElemList
boolean metTypeElement = false;
boolean metTypeElemMultiSet = false;
try {
if (method.getReturnType().isInstance(Class.forName("twodsack.setelement.Element")) ||
method.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.setelement.Element"))) {
metTypeElement = true; }
if (method.getReturnType().isInstance(Class.forName("twodsack.set.ElemMultiSet")) ||
method.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.set.ElemMultiSet"))) {
metTypeElemMultiSet = true; }
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.reduce: can't examine method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//NEW CODE WITH ITERATORS AND MULTISETS
Iterator it1;
Iterator it2;
MultiSetEntry mse1;
int number1;
MultiSetEntry mse2;
int number2;
//first, handle multiple elements:
//the set is traversed and for all duplicates, the entries are replaced by the method result,
//if the predicate holds
it1 = retSet.iterator();
while(it1.hasNext()) {
mse1 = (MultiSetEntry)it1.next();
if (mse1.number > 1) {
//check for predicate
//if predicate has one argument
if (paramTypeCountPred == 1)
paramList[0] = (Element)mse1.value;
//if predicate has two arguments (is static)
else {
paramList[0] = (Element)mse1.value;
paramList[1] = (Element)mse1.value;
}//else
isTrue = false;
try {
isTrue = ((Boolean)predicate.invoke(mse1.value,paramList)).booleanValue();
} catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.reduce: Problem with using predicate.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (isTrue) {
paramListMet[0] = (Element)mse1.value;
paramListMet[1] = (Element)mse1.value;
try {
//if method has returnType Element
if (metTypeElement)
retSet.add((Element)(method.invoke(null,paramListMet)));
else {
if (metTypeElemMultiSet)
retSet.addAll((ElemMultiSet)(method.invoke(null,paramListMet)));
else {
System.out.println("ERROR in SO.reduce(): can't invoke method");
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//else
}//else
} catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.reduce: Problem with using method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//remove element from set
it1.remove();
}//if isTrue
}//if mse1.number > 1
}//while it.hasNext
//now, all duplicates should be removed
//start processing the remaining elements
do {
//reset loop variables
stillExist = false;
isTrue = false;
it1 = retSet.iterator();
while (it1.hasNext()) {
//get element from first set
mse1 = (MultiSetEntry)it1.next();
actElem1 = (Element)mse1.value;
isTrue = false;
it2 = retSet.iterator();
while (it2.hasNext()) {
//get element from second set
mse2 = (MultiSetEntry)it2.next();
if (((Object)mse2).equals((Object)mse1))
if (it2.hasNext()) mse2 = (MultiSetEntry)it2.next();
else break;
number2 = mse2.number;
actElem2 = (Element)mse2.value;
//if predicate has one argument
if (paramTypeCountPred == 1)
paramList[0] = actElem2;
//if predicate has two arguments (is static)
else {
paramList[0] = actElem1;
paramList[1] = actElem2;
}//else
isTrue = false;
try {
isTrue = ((Boolean)predicate.invoke(actElem1,paramList)).booleanValue();
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.reduce: Problem with using predicate.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
if (isTrue) {
paramListMet[0] = actElem1;
paramListMet[1] = actElem2;
try {
//if method has returnType Element
if (metTypeElement)
retSet.add((Element)(method.invoke(null,paramListMet)));
else {
if (metTypeElemMultiSet)
retSet.addAll((ElemMultiSet)(method.invoke(null,paramListMet)));
else {
System.out.println("ERROR in SO.reduce(): can't invoke method");
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//else
}//else
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.reduce: Problem with using method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
//now remove elements from retSet
retSet.removeOneEntry(actElem1);
retSet.removeOneEntry(actElem2);
stillExist = true;
isTrue = false;
if (retSet.isEmpty())
stillExist = false;
}//if isTrue
if (stillExist) break;
}//while it2
if (stillExist) break;
}//while it1
} while (stillExist);
return retSet;
}//end method reduce
/**
* From two sets <tt>E,F</tt> and a predicate <tt>p</tt> this method computes a set of pairs <tt>(e,f)</tt> with <tt>e</tt> element of
* <tt>E</tt>, <tt>f</tt> element of <tt>F</tt> and <tt>p(e,f) == true</tt>.<p>
* The predicate's signature must be <tt>Element x Element -> boolean</tt>. This method uses a O(n*n) algorithm.
*
* @param ems1 the first set
* @param ems2 the second set
* @param predicate the join predicate
* @return the set of pairs
* @see #overlapJoin(ElemMultiSet,ElemMultiSet,Method,boolean,boolean,boolean,int)
*/
public static PairMultiSet join (ElemMultiSet ems1, ElemMultiSet ems2, Method predicate) {
PairMultiSet retSet = new PairMultiSet(new ElemPairComparator());
if (ems1 == null || ems2 == null || ems1.isEmpty() || ems2.isEmpty()) return retSet;
int paramTypeCount = Array.getLength(predicate.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
Iterator it1 = ems1.iterator();
Iterator it2;
Element actElem1, actElem2;
while (it1.hasNext()) {
actElem1 = (Element)((MultiSetEntry)it1.next()).value;
it2 = ems2.iterator();
while (it2.hasNext()) {
actElem2 = (Element)((MultiSetEntry)it2.next()).value;
if (paramTypeCount == 1)
//predicate is non-static
paramList[0] = actElem2;
else {
//predicate is static
paramList[0] = actElem1;
paramList[1] = actElem2;
}//else
//invoke predicate
try {
if (((Boolean)predicate.invoke(actElem1,paramList)).booleanValue())
retSet.add(new ElemPair(actElem1,actElem2));
} catch (Exception e) {
System.out.println("Error in SetOps.join: Wasn't able to invoke predicate.");
e.printStackTrace();
}//catch
}//while it2
}//while it1
return retSet;
}//end method join
/**
* Invokes the passed method on each pair of the passed PairMultiSet. The result is collected and returned.<p>
* The method must have the signature <code>Element x Element -> Element</code> or
* <tt>Element x Element -> ElemMultiSet</tt>.<p>
* At the end, duplicates are removed.
*
* @param pl the set of ElemPair(s)
* @param method the method that is invoked on the ElemPair(s)
* @return the set of results collected in an ElemMultiSet
*/
public static ElemMultiSet map (PairMultiSet pl, Method method) {
ElemMultiSet retSet = new ElemMultiSet(ELEM_COMPARATOR);
int paramTypeCount = Array.getLength(method.getParameterTypes());
Element[] paramList = new Element[paramTypeCount];
Iterator lit = pl.iterator();
ElemPair actPair;
boolean metTypeElement = false;
boolean metTypeElemList = false;
Element result;
try {
if (method.getReturnType().isInstance(Class.forName("twodsack.setelement.Element")) ||
method.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.setelement.Element"))) {
metTypeElement = true; }
if (method.getReturnType().isInstance(Class.forName("twodsack.set.ElemMultiSet")) ||
method.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.set.ElemMultiSet"))) {
metTypeElemList = true; }
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.reduce: can't examine method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
MultiSetEntry mse;
int number;
while (lit.hasNext()) {
mse = (MultiSetEntry)lit.next();
number = mse.number;
actPair = (ElemPair)mse.value;
//muliset handling
for (int msenum = 0; msenum < number; msenum++) {
try {
//if method has one argument
if (paramTypeCount == 1) {
paramList[0] = actPair.second;
}//if
//if method has two arguments (is static)
else {
paramList[0] = actPair.first;
paramList[1] = actPair.second;
}//else
//if returntype of m is Element
if (metTypeElement) {
result = (Element)method.invoke(actPair.first,paramList);
if (result != null)
retSet.add(result);
//retSet.add((Element)(method.invoke(actPair.first,paramList)));
}//if
//if returntype of m is ElemList
else {
if (metTypeElemList) {
retSet.addAll((ElemMultiSet)(method.invoke(actPair.first,paramList)));
}//if
else {
System.out.println("Error in SetOps.map: can't invoke method");
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//else
}//else
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.map(pl,m). Can't invoke method "+method);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
}//for
}//while
return retSet;
}//end method map
/**
* Invokes the method on every element of the set and returns the resulting set.<p>
* Duplicates are removed afterwards.<p>
* The signature of the method <tt>m</tt> must be one of
* <code>Element -> Element</code>,<p>
* <code>Element -> ElemMultiSet</code> or<p>
* <code>Element -> Element[]</code>.
*
* @param el the 'in' set
* @param m the method that is invoked on the elements of <tt>el</tt>
* @return the changed <tt>el</tt>
*/
public static ElemMultiSet map (ElemMultiSet el, Method m) {
ElemMultiSet retList = new ElemMultiSet(ELEM_COMPARATOR);
boolean mRetTypeElem = false;
boolean mRetTypeElemMultiSet = false;
boolean mRetTypeElemArray = false;
try {
mRetTypeElemArray = m.getReturnType().isArray();
if (!mRetTypeElemArray) {
mRetTypeElem = (m.getReturnType().isInstance(Class.forName("twodsack.setelement.Element")) ||
m.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.setelement.Element")));
if (!mRetTypeElem)
mRetTypeElemMultiSet = (m.getReturnType().isInstance(Class.forName("twodsack.set.ElemMultiSet")) ||
m.getReturnType().getSuperclass().isAssignableFrom(Class.forName("twodsack.set.ElemMultiSet")));
}//if
} catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.map: can't examine method.");
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
Iterator lit = el.iterator();
Element actEl;
MultiSetEntry mse;
int numOfmse;
while (lit.hasNext()) {
mse = (MultiSetEntry)lit.next();
actEl = (Element)mse.value;
numOfmse = mse.number;
//handling multiple entries
for (int nom = 0; nom < numOfmse; nom++) {
try {
//if returntype of m is Element
if (mRetTypeElem) {
retList.add(m.invoke(actEl,null));
}//if
//if returntype of m is ElemList
else {
if (mRetTypeElemMultiSet) {
retList.addAll((ElemMultiSet)m.invoke(actEl,null));
}//if
else {
if (mRetTypeElemArray) {
retList.add((Element[])m.invoke(actEl,null));
}//if
else {
System.out.println("ERROR (SetOps.map): can't invoke method");
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//else
}//else
}//else
}//try
catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.map. Can't invoke method "+m);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
}//for nom
}//while
return retList;
}//end method map
/**
* Returns the disjoint union of both sets.
* This means that <tt>disjointUnion((a,b),(b,c)) -> (a,b,b,c)</tt>.
*
* @param el1 the first set
* @param el2 the second set
* @return the disjoint union of <tt>el1,el2</tt>
*/
public static ElemMultiSet disjointUnion (ElemMultiSet el1, ElemMultiSet el2) {
ElemMultiSet eUnion = el1.copy();
eUnion.addAll(el2);
return eUnion;
}//end method disjointUnion
/**
* Returns the intersecion of both sets.<p>
* This means that <tt>intersection((a,b),(b,c)) -> (b)</tt>.<p>
* This method uses the <tt>compare()</tt> method which must be implemented for any type that implements the Element interface and
* throws a WrongTypeException if the sets are not of the same type.
*
* @param el1In the first set
* @param el2In the second set
* @return the intersection of <tt>el1In,el2In</tt>
* @throws WrongTypeException
*/
public static ElemMultiSet intersection (ElemMultiSet el1In, ElemMultiSet el2In) throws WrongTypeException {
ElemMultiSet eRet = new ElemMultiSet(ELEM_COMPARATOR);
if ((el1In.size() == 0) ||
(el2In.size() == 0))
return eRet;
ElemMultiSet ems1 = el1In;
ElemMultiSet ems2 = el2In;
Iterator it1 = ems1.iterator();
Iterator it2 = ems2.iterator();
boolean getNextE1 = true;
boolean getNextE2 = true;
MultiSetEntry mse1 = null;
MultiSetEntry mse2 = null;
Element e1,e2;
while (true) {
if (getNextE1) { mse1 = (MultiSetEntry)it1.next(); }
if (getNextE2) { mse2 = (MultiSetEntry)it2.next(); }
e1 = (Element)mse1.value;
e2 = (Element)mse2.value;
int cmp = e1.compare(e2);
if (cmp == 0) {
eRet.add(e1);
getNextE1 = true;
getNextE2 = true;
}//if
else if (cmp == -1) {
getNextE1 = true;
getNextE2 = false;
}//if
else {
getNextE1 = false;
getNextE2 = true;
}//else
if ((getNextE1 && !it1.hasNext()) ||
(getNextE2 && !it2.hasNext()))
break;
}//while
return eRet;
}//end method intersection
/**
* Returns the difference of the two sets.<p>
* This means that <tt>difference((a,b,c),(a,b)) -> (c)</tt>.<p>
* This method uses the <tt>compare()</tt> method which must be implemented for any type that implements the <tt>Element</tt> interface and
* throws a WrongTypeException if the sets are not of the same type.
*
* @param el1 the first set
* @param el2 the second set
* @return the difference <tt>el - el2</tt>
* @throws WrongTypeException
*/
public static ElemMultiSet difference (ElemMultiSet el1, ElemMultiSet el2) throws WrongTypeException {
ElemMultiSet eDiff = new ElemMultiSet(ELEM_COMPARATOR);
int el1size = el1.size();
int el2size= el2.size();
el1 = rdup(el1);
el2 = rdup(el2);
if (el2.isEmpty()) { return el1; }
Iterator it1 = el1.iterator();
Iterator it2 = el2.iterator();
boolean next1 = true;
boolean next2 = true;
Element elem1 = null;//just for initialization
Element elem2 = null;//dito
int comp;
while ((!next1 || it1.hasNext()) &&
(!next2 || it2.hasNext())) {
if (next1) elem1 = (Element)((MultiSetEntry)it1.next()).value;
if (next2) elem2 = (Element)((MultiSetEntry)it2.next()).value;
next1 = false;
next2 = false;
if (elem1.equal(elem2)) {
next1 = true;
next2 = true;
}//if
else {
comp = elem1.compare(elem2);
switch (comp) {
case -1 : {
eDiff.add(elem1.copy());
next1 = true;
break; }
case 1 : { next2 = true; }
}//switch
}//else
}//while
if (!next1) { eDiff.add(elem1.copy()); }
while (it1.hasNext()) {
eDiff.add(((Element)((MultiSetEntry)it1.next()).value).copy());
}//while
return eDiff;
}//end method difference
/**
* Removes the duplicates from the (multi-)set.
* As an ElemMultiSet is a set type that allows duplicates, this method removes all duplicates, i.e.
* <tt>rdup((a,b,b,c)) -> (a,b,c)</tt>.
*
* @param elIn the 'in' set
* @return <tt>elIn</tt> without duplicates
* @throws WrongTypeException
*/
public static ElemMultiSet rdup (ElemMultiSet elIn) throws WrongTypeException {
Iterator it = elIn.iterator();
MultiSetEntry mse;
int num;
while (it.hasNext()) {
mse = (MultiSetEntry)it.next();
num = mse.number;
elIn.size = elIn.size - num + 1;
mse.number = 1;
}//while
return elIn;
}//end method rdup
/**
* Removes from the set all elements which are found more than once.
* This means that <tt>rdup2((a,b,b,c)) -> (a,c)</tt>.
*
* @param elIn the 'in' set
* @return <tt>elIn</tt> without any duplicates
*/
public static ElemMultiSet rdup2 (ElemMultiSet elIn) {
Iterator it = elIn.iterator();
while (it.hasNext()) {
MultiSetEntry mse = (MultiSetEntry)it.next();
if (mse.number > 1)
it.remove();
}//while
elIn.recomputeSize();
return elIn;
}//end method rdup2
/**
* Computes the union of both sets.<p>
* This means that <tt>union((a,b),(b,c)) -> (a,b,c)</tt>. The duplicates are removed.
* A <tt>WrongTypeException</tt> is thrown, if the sets are not of the same type.
*
* @param el1 the first set
* @param el2 the second set
* @return the union of <tt>el1,el2</tt>
* @throws WrongTypeException
*/
public static ElemMultiSet union (ElemMultiSet el1, ElemMultiSet el2) throws WrongTypeException {
ElemMultiSet eRet = new ElemMultiSet(ELEM_COMPARATOR);
eRet = rdup(disjointUnion(el1,el2));
return eRet;
}//end method union
/**
* Sums up the results of method <tt>m</tt> invoked on every element of the set.
* Method <tt>m</tt> must have the signature <code>Element -> double</code>.
*
* @param ems the 'in' set
* @param m the method that is invoked on the elements of ems
* @return the sum of the results of <tt>m</tt>'s invocation
*/
public static double sum (ElemMultiSet ems, Method m) {
double retSum = 0;
Iterator it = ems.iterator();
MultiSetEntry mse;
try {
while (it.hasNext()) {
mse = (MultiSetEntry)it.next();
for (int count = 0; count < mse.number; count++) {
retSum = retSum + ((Double)m.invoke((Element)mse.value,null)).doubleValue();
}//for count
}//while
} catch (Exception e) {
System.out.println("Exception: "+e.getClass()+" --- "+e.getMessage());
System.out.println("Error in SetOps.sum. Can't invoke method "+m);
e.printStackTrace();
throw new RuntimeException("An error occurred in the ROSEAlgebra.");
}//catch
return retSum;
}//end method sum
/**
* Computes for two sets of geometrical objects a set of object pairs for which holds, that their bounding boxes overlap.<p>
* This method implements a pretty complex DAC algorithm which is not described here.<p>
* In general, pairs are computed for objects of two sets (which don't have to be of the same type). But, when using the
* <tt>sameSet</tt> flag, one may indicate that the sets passed are actually the same object. In that case, all pairs of identical
* objects are removed. To clarify this: If <tt>overlappingPairs</tt> is invoked on <tt>(e1,e2,e3...) x (f1,f2,f3)</tt> one would get (at least)
* the pairs <tt<(e1,f1),(e2,f2),(e3,f3)</tt< because <tt>(e1,f2)</tt> etc. are identical and therefore have overlapping bounding boxes.
* If the <tt>sameSet</tt> flag is set, these pairs are not found in the resulting set.<p>
* Using the <tt>meet</tt> flag, an overlap of the object's bounding boxes is not required. Now, adjacency of bounding boxes is
* sufficient for an object pair to be stored in the result set.<p>
* If the <tt>bboxFilter</tt> flag is set, first the bounding box of the complete sets <tt>el1,el2</tt> is computed, i.e. that complete bounding box
* includes <u>all</u> of the set's objects. Then, all object's bounding boxes of <tt>el1</tt> are checked against the <tt>el2</tt> complete box.*
* Objects with boxes that don't intersect the set's box are removed. The same is done for the elements of <tt>el2</tt> and the box of <tt>el1</tt>.
* <p>
* When using the <tt>earlyExit</tt> flag, the <tt>setNumber</tt> is evaluated. This number must be 1 or 2 and specifies one of the two sets. When,
* during the execution of <tt>bboxFilter</tt> one object of that set is removed for the reason that it doesn't overlap the big box
* of the other set, a <tt>NoOverlappingBoxFoundException</tt> is thrown.<p>
* Note, that the <tt>earlyExit</tt> flag can <u>only</u> be used together with the <tt>bboxFilter</tt> flag and the <tt>setNumber</tt>.
*
* @param el1 the first set
* @param el2 the second set
* @param sameSet choose <tt>true</tt>, if identical pairs should not be reported in the case that <tt>el1 == el2</tt>
* @param meet set to <tt>true</tt>, if adjancency for bounding boxed should suffice instead of overlap
* @param bboxFilter use <tt>true</tt>, if a filter step should be used to reduce the number of objects
* @param earlyExit if set to <tt>true</tt>, an <tt>NoOverlappingboxException</tt> will be thrown, if one element of the set specified by
* setNumber finds no partner
* @param setNumber specifies the set for <tt>earlyExit</tt>
* @return the set of object pairs which all have overlapping (or adjacent) bounding boxes
* @throws NoOverlappingBoxFoundException
*/
public static PairMultiSet overlappingPairs(ElemMultiSet el1, ElemMultiSet el2, boolean sameSet, boolean meet, boolean bboxFilter, boolean earlyExit, int setNumber) throws NoOverlappingBoxFoundException {
PairMultiSet pairs = new PairMultiSet(new ElemPairComparator());
if (el1.isEmpty() || el2.isEmpty()) return pairs;
Object[] ivlArr;
if (bboxFilter) {
//Use bboxFilter to reduce number of elements.
ElemMultiSet[] newSets = bboxFilter(el1,el2,earlyExit,setNumber);
if (newSets[0].isEmpty() || newSets[1].isEmpty()) return pairs;
//generate interval list which stores left,right vertical
//intervals of the elements bboxes of el1,el2
MultiSet ems = generateIntervalList(newSets[0],newSets[1],meet);
ivlArr = ems.toArray();
}//if bboxFilter
else {
//generate interval list which stores left,right vertical
//intervals of the elements bboxes of el1,el2
//Store elements in an array.
MultiSet ems = generateIntervalList(el1,el2,meet);
ivlArr = ems.toArray();
}//else
//everything is done so start the DAC
IvlList mtList = new IvlList();
ResultList inlist = new ResultList(mtList,mtList,mtList,mtList,mtList,mtList,mtList);
//initialize a structure for storing the already found intersections
ProLinkedList[] intStore = new ProLinkedList[el1.size()+el2.size()];
for (int i = 0; i < intStore.length; i++) {
intStore[i] = new ProLinkedList(); }
PairMultiSet pairList = new PairMultiSet(new ElemPairComparator());
int idx1 = 0;
int idx2 = ivlArr.length-1;
ResultList rl = computeOverlaps(intStore,inlist,sameSet,el1.size(),pairList,ivlArr,idx1,idx2);
return pairList;
}//end method overlappingPairs
/**
* This method does the <i>real</i> work for the DAC.
* Comments are found in the code.
*
* @param intStore this array stores for every object of the first set a list with objects of the second set which have overlapping
* or adjacent bounding boxes; in fact, not the objects themselves are stored in that array, but the object IDs
* @param il in this structure, all left,right,blue,green etc. intervals are stored; additionally, all information which is passed
* through the several recursive steps of this algorithm are stored in here
* @param sameSet the flag which indicates whether both (initial) sets are equal or not
* @param size the size of the first (initial)set
* @param resultingPairs the set of pairs which has to be computed
* @param ivlArr stores the left and right border intervals of the objects bounding boxes
* @param idx1 defines the actual left index on <tt>ivlArr</tt>
* @param idx2 defines the acutal right index on <tt>ivlArr</tt>
* @return the new ResulList structure
*/
private static ResultList computeOverlaps(ProLinkedList[] intStore, ResultList il, boolean sameSet, int size, PairMultiSet resultingPairs,Object[] ivlArr, int idx1, int idx2) {
//initially il.m is the full intervall list
ResultList rl;
//is m small enough?
//if (il.m.size() == 1) {
if (idx2 == idx1) {
rl = new ResultList(il.m,null,null,null,null,null,null);
//dependent on the only element in m compute the sets
//blue, green, blueLeft, blueRight, greenLeft, greenRight
//Interval actInt = (Interval)il.m.getFirst();
Interval actInt = (Interval)ivlArr[idx1];
if (actInt.mark == "blueleft") {
if (rl.blue == null) rl.blue = new IvlList();
rl.blue.add(actInt);
rl.green = il.green;
if (rl.blueLeft == null) rl.blueLeft = new IvlList();
rl.blueLeft.add(actInt);
rl.blueRight = il.blueRight;
rl.greenLeft = il.greenLeft;
rl.greenRight = il.greenRight;
}//if
else if (actInt.mark == "blueright") {
if (rl.blue == null) rl.blue = new IvlList();
rl.blue.add(actInt);
rl.green = il.green;
rl.blueLeft = il.blueLeft;
if (rl.blueRight == null) rl.blueRight = new IvlList();
rl.blueRight.add(actInt);
rl.greenLeft = il.greenLeft;
rl.greenRight = il.greenRight;
}//if
else if (actInt.mark == "greenleft") {
rl.blue = il.blue;
if (rl.green == null) rl.green = new IvlList();
rl.green.add(actInt);;
rl.blueLeft = il.blueLeft;
rl.blueRight = il.blueRight;
if (rl.greenLeft == null) rl.greenLeft = new IvlList();
rl.greenLeft.add(actInt);
rl.greenRight = il.greenRight;
}//if
else if (actInt.mark == "greenright") {
rl.blue = il.blue;
if (rl.green == null) rl.green = new IvlList();
rl.green.add(actInt);
rl.blueLeft = il.blueLeft;
rl.blueRight = il.blueRight;
rl.greenLeft = il.greenLeft;
if (rl.greenRight == null) rl.greenRight = new IvlList();
rl.greenRight.add(actInt);
}//if
}//if size() == 1
else {
//DIVIDE
int half = (idx2-idx1)/2;
int m1idx1 = idx1;
int m1idx2 = idx1+half;
int m2idx1 = m1idx2+1;
int m2idx2 = idx2;
//CONQUER
ResultList rl1 =
computeOverlaps(intStore,
new ResultList(null,il.blue,il.green,il.blueLeft,il.blueRight,il.greenLeft,il.greenRight),
sameSet,size,resultingPairs,
ivlArr,m1idx1,m1idx2);
ResultList rl2 =
computeOverlaps(intStore,
new ResultList(null,il.blue,il.green,il.blueLeft,il.blueRight,il.greenLeft,il.greenRight),
sameSet,size,resultingPairs,
ivlArr,m2idx1,m2idx2);
//MERGE
IvlList fullBlue;
IvlList fullGreen;
fullBlue = IvlList.intersect(rl1.blueLeft,rl2.blueRight);
fullGreen = IvlList.intersect(rl1.greenLeft,rl2.greenRight);
rl = new ResultList(null,null,null,null,null,null,null);
rl.blue = IvlList.merge(rl1.blue,rl2.blue,true);
rl.green = IvlList.merge(rl1.green,rl2.green,true);
IvlList rl1BlueLeftMINUSfullBlue = IvlList.minus(rl1.blueLeft,fullBlue);
IvlList rl1GreenLeftMINUSfullGreen = IvlList.minus(rl1.greenLeft,fullGreen);
IvlList rl2BlueRightMINUSfullBlue = IvlList.minus(rl2.blueRight,fullBlue);
IvlList rl2GreenRightMINUSfullGreen = IvlList.minus(rl2.greenRight,fullGreen);
rl.blueLeft = IvlList.merge(rl1BlueLeftMINUSfullBlue,rl2.blueLeft,false);
rl.blueRight = IvlList.merge(rl2BlueRightMINUSfullBlue,rl1.blueRight,false);
rl.greenLeft = IvlList.merge(rl1GreenLeftMINUSfullGreen,rl2.greenLeft,false);
rl.greenRight = IvlList.merge(rl2GreenRightMINUSfullGreen,rl1.greenRight,false);
//compute pairs
resultingPairs = IvlList.overlappingIntervals(intStore,sameSet,size,
rl1BlueLeftMINUSfullBlue,
rl2.green, resultingPairs);
resultingPairs = IvlList.overlappingIntervals(intStore,sameSet,size,rl2.blue,
rl1GreenLeftMINUSfullGreen,
resultingPairs);
resultingPairs = IvlList.overlappingIntervals(intStore,sameSet,size,
rl2BlueRightMINUSfullBlue,
rl1.green,resultingPairs);
resultingPairs = IvlList.overlappingIntervals(intStore,sameSet,size,rl1.blue,
rl2GreenRightMINUSfullGreen,
resultingPairs);
}//else
return rl;
}//end method compute overlaps
/**
* Constructs a set of intervals which are the border intervals form the bounding boxes of <tt>el1</tt>'s and <tt>el2</tt>'s objects.
* This is a supportive method for the <tt>overlappingPairs</tt> method. From the elements of <tt>el1,el2</tt>, the vertical intervals
* of the bounding boxes are taken and stored as intervals in the resulting MultiSet. All elements of el1 are marked
* with "blueleft" or "blueright" and elements of <tt>el2</tt> are marked with "greenleft" and "greenright". Additionally,
* all bounding boxes get a number which is assigned to the intervals. A flag is set for every interval, whether
* the partner of an inerval is located at the same x-coordinate or not.
*
* @param el1 the first set
* @param el2 the second set
* @param meet <tt>true</tt>, if adjacency of bounding boxes suffices for reporting it in the result set
* @return the MultiSet with the intervals
*/
static private MultiSet generateIntervalList (ElemMultiSet el1, ElemMultiSet el2, boolean meet) {
MultiSet retSet = new MultiSet(new IvlComparator(meet));
int counter = 0;
Iterator it1 = el1.iterator();
Iterator it2 = el2.iterator();
Element actEl;
Rect actRect;
MultiSetEntry mse;
boolean buddy;
while (it1.hasNext()) {
mse = (MultiSetEntry)it1.next();
actEl = (Element)mse.value;
actRect = actEl.rect();
buddy = actRect.ulx.equal(actRect.urx);
//multiset handling
for (int msenum = 0; msenum < mse.number; msenum++) {
retSet.add(new Interval(
actRect.lly,
actRect.uly,
"blueleft",
actRect.ulx,
actEl,
counter,
buddy));
retSet.add(new Interval(
actRect.lry,
actRect.ury,
"blueright",
actRect.urx,
actEl,
counter,
buddy));
counter++;
}//for
}//while
while (it2.hasNext()) {
mse = (MultiSetEntry)it2.next();
actEl = (Element)mse.value;
actRect = actEl.rect();
buddy = actRect.ulx.equal(actRect.urx);
for (int msenum = 0; msenum < mse.number; msenum++) {
retSet.add(new Interval(
actRect.lly,
actRect.uly,
"greenleft",
actRect.ulx,
actEl,
counter,
buddy));
retSet.add(new Interval(
actRect.lry,
actRect.ury,
"greenright",
actRect.urx,
actEl,
counter,
buddy));
counter++;
}//for
}//while
return retSet;
}//end method generateIntervalList
/**
* Sorts the input list using mergesort.
* First, elements are sorted using their <tt>compareX()</tt> method, then using their <tt>compareY()</tt> method.<p>
* This method can <i>only</i> be used for <tt>Element</tt> lists.
*
* @param el the unsorted list
* @return the sorted list
*/
public static void mergesortXY (LinkedList el) {
mergesXY(el,0,el.size()-1);
}//end method mergesortXY
/**
* This is a supportive method for {@link #mergesortXY(LinkedList)}.
*
* @param list the list to be sorted
* @param lo the bottom index
* @param hi the top index
* @return a list that is sorted form lo to hi
*/
private static void mergesXY (LinkedList list, int lo, int hi) {
//lo,hi are indices to determine which part of the list shoult be sorted
int m;
if (lo < hi) {
m = (lo + hi) / 2;
mergesXY (list,lo,m);
mergesXY (list,m+1,hi);
mergeXY (list,lo,hi);
}//if
}//end method mergesXY
/**
* Supportive method for {@link #mergesXY(LinkedList,int,int)}.
*
* @param list the list to be sorted
* @param lo the bottom index
* @param hi the top index
* @return the list that is sorted from <tt<lo</tt> to <tt>hi</tt>
*/
private static void mergeXY(LinkedList list, int lo, int hi) {
int i,j,k,m,n = hi-lo+1;
Element[] tmp = new Element[n];//temporary array
byte erg;
k = 0;
m = (lo+hi)/2;
//copy lower half to array b
for (i = lo; i <= m; i++)
tmp[k++] = (Element)list.get(i);
//copy upper half in reverse order to array b
for (j = hi; j >= m+1; j--)
tmp[k++] = (Element)list.get(j);
i = 0;
j = n-1;
k = lo;
//copy the next greatest element back until i,j meet
while (i <= j) {
erg = tmp[i].compX(tmp[j]);
if (erg == 0) {
erg = tmp[i].compY(tmp[j]);
switch (erg) {
case -1 : { erg = 1; break; }
case 1 : { erg = -1; break; }
case 0 : erg = 0;
}//switch
}//if
switch (erg) {
case -1 : { list.set(k++,(Element)tmp[i++]); break; }
case 0 : { list.set(k++,(Element)tmp[i++]); break; }
case 1 : list.set(k++,(Element)tmp[j--]);
}//switch
}//while
}//end method mergeXY
/**
* Returns the subsets of elements which bounding boxes overlap the bounding box of the element set of the other set.<p>
* The bounding boxes for every passed set is computed first. Then, the intersection of these bounding boxes,
* which is again a rectangle and can be described as the "convex hull rectangle", is computed.
* Afterwards, the elements of both sets are compared to this convex hull rectangle. If the bounding box of such
* an element has at least one common point with the hull, it belongs to the subset for a set. Subsets are computed
* for both sets and are returned in an array.
* The parameter <code>earlyExit</code> can be used to stop the execution of the calling algorithm. Therefore,
* a <code>setNumber</code> is passed together with <code>earlyExit</code>. If <code>true</code>,
* <code>bboxFilter</code> throws a {@link NoOverlappingBoxFoundException} if the bounding box of an element of the
* set specified by <code>setNumber</code> doesn't overlap the hull of the other set.
*
* @param ems1 the first set of elements
* @param ems2 the second set of elements
* @param earlyExit must be <tt>true</tt>, if execution shall be stopped
* @param setNumber the number of the set which is examined if <tt>earlyExit == true</tt>
* @return the subsets
*/
public static ElemMultiSet[] bboxFilter (ElemMultiSet ems1, ElemMultiSet ems2, boolean earlyExit, int setNumber)
throws NoOverlappingBoxFoundException {
if (earlyExit && (setNumber < 1 || setNumber > 2)) throw new InternalError("SetOps.bboxFilter: setNumber must be 1 or 2.");
ElemMultiSet[] retArr = new ElemMultiSet[2];
if (ems1.size() == 0 || ems2.size() == 0) return retArr;
ElemMultiSet ems1Clone = (ElemMultiSet)ems1.clone();
ElemMultiSet ems2Clone = (ElemMultiSet)ems2.clone();
Rect ems1Rect = ems1Clone.rect();
Rect ems2Rect = ems2Clone.rect();
Rect combinedRect = ems1Rect.intersection(ems2Rect);
Element actElem;
Iterator it;
it = ems1Clone.iterator();
while (it.hasNext()) {
actElem = (Element)((MultiSetEntry)it.next()).value;
if (!actElem.rect().hasCommonPoints(combinedRect)) {
if (earlyExit && setNumber == 1) throw new NoOverlappingBoxFoundException();
it.remove();
}//if
}//while
ems1Clone.recomputeSize();
retArr[0] = ems1Clone;
it = ems2Clone.iterator();
while (it.hasNext()) {
actElem = (Element)((MultiSetEntry)it.next()).value;
if (!actElem.rect().hasCommonPoints(combinedRect)) {
if (earlyExit && setNumber == 2) throw new NoOverlappingBoxFoundException();
it.remove();
}//if
}//while
ems2Clone.recomputeSize();
retArr[1] = ems2Clone;
return retArr;
}//end method bboxFilter
}//class SetOps