/* * 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.
* 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: (Element x ElemMultiSet). Finally, a LinkedList type
* only appears in some sorting algorithm in this class.
* The operations in this class can be roughly divided in four groups:
* 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 map(...). The operations in the other * groups are quite similar.
* In this class, different implementations can be found for similar operations like join(...) and overlapJoin(...) or * group(...) * and overlapGroup. The overlap prefix indicates a method as using a special mechanism for speeding up * the operation. Whereas a simple join takes quite a long time, the overlapJoin 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 inside of bounding boxes. 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 ems1,ems2 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 predicate and method.
* This is another variant of the normal {@link #reduce(ElemMultiSet,Method,Method)} operation. For the functionality of reduce itself, * have a look at that method.
* Here, a plane sweep algorithm is used for the computation of the reduced set:
* Note, that the signature of all predicates and methods must be
* Element x Element -> boolean and Element x Element -> Element, 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 true, also objects of ems 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 Element x Element -> ElemMultiSet.
* 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 true, also objects of ems 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 ljpMS consists of a pair (Element x ElemMultiSet). This method traverses the set and
* stores the Element in the result set if Element != NULL. If Element == NULL, the ElemMultiSet is stored
* in the result set instead.
*
* @param ljpMS the 'in' set
* @return the collected elements of ljpMS stored in an ElemMultiSet
*/
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 only the elements of the ElemMultiSets of every entry of ljpMS.
* The first entries, i.e. the Element 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 (Element x ElemMultiSet).
* For every pair Element x ElementOfEMS of this set element, the predicate holds. This set is computed as follows:
* 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.
* If, in a special case, for one element ex of el1 no element ey of el2 is found, the entry constructed in the * resulting set is (ex x NULL), i.e. the ElemMultiSet is not initialized.
* The predicate must have the signature Element x Element -> boolean.
*
* @param el1 the first set of elements. These elements can be found again in the resulting LeftJoinPairMultiSet
* as the first entry of every (Element x ElemMultiSet). In particular, this means that if el1 is (e1, e2, e3...),
* the resulting set is ( (e1 x ems1), (e2 x ems2), (e3 x ems3) ...).
* @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 overlappingPairs(); if true objects whith adjacent bounding boxes are
* reported, too
* @param bboxFilter this flag is passed to overlappingPairs(); if true, in a pre-processing step the number of
* candidates is reduced by removing objects of el1,el2 which don't overlap the unified bounding box of el1,
* el2 resp.
* @param earlyExit if true, an {@link EarlyExit} exception is thrown immediately when the first object of elN is found which
* doesn't have a partner in elM, i.e. at least one element of elM which has a bounding box that overlaps the
* bounding box of the element of elN. N is the passed setNumber. earlyExit is evaluated in bboxFilter, so if shall
* be used, be sure that bboxFilter = true
* @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 ljpl.
* An entry of the passed ljpl has the form (Element x ElemMultiSet). Now, let the ElemMultiSet be (l1, l2 ... ln).
* Then, using the passed method subtractSets computes from such a pair (Element x (l1, l2 ... ln)) a new pair
* (element x (method(Element,l1), method(Element,l2) ... method(Element,ln))).
* The method must have the signature Element x Element -> Element or
* Element x Element -> ElemMultiSet.
*
* @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 reduce itself,
* have a look at that method.
* 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 * true, 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 bigger than the original two * objects.)
* overlapReduce(el,PRED,METH,...) works as follows:
* The predicate must have the signature Element x Element -> boolean and method must have the signature
* Element x Element -> ElemMultiSet
*
* @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 overlappingPairs(); if true, objects with adjacent bounding boxes are
* reported, too
* @param bboxFilter this flag is passed to overlappingPairs(); if true, in a pre-processing step the number of
* candidates is reduced by removing objects of el1,el2 which don't overlap the unified bounding box of el1,el2 resp.
* @param earlyExit if true, an EarlyExit exception is thrown immediately when the first object of elN is found which
* doesn't have a partner in elM, i.e. at least one element of elM which has a bounding box that overlaps the
* bounding box of the element of elN. N is the passed setNumber. earlyExit is evaluated in bboxFilter, so if shall
* be used, be sure that bboxFilter = true
* @param setNumber must be 1 or 2; specifies the set for earlyExit
* @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 keep = true, a pair (x,y) is kept if predicate(x,y) = true. All other pairs are deleted. If keep = false,
* the inverse set is returned.
* The predicate must have the signature Element x Element -> boolean.
*
* @param plIn the set of pairs
* @param predicate the predicate that is used to filter the pairs
* @param keep if true, 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.
* This is the 'overlap' variant of the ordinary {@link #group(ElemMultiSet,Method)}. The result of this method is a list of ElemMultiSet(s). * For every pair of elements (x,y) of such a group (ElemMultiSet), predicate(x,y) holds.
* 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.
* This method works as follows:
* The signature of the predicate must be Element x Element -> boolean.
*
* @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 overlappingPairs(); if true, 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.
* This is a variant of the normal join 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.
* That 'overlap' predicate is also the join predicate. For all pairs (x,y) in the resulting set, that predicate holds.
* The join is computed by first calling overlappingPairs(ems1,ems2,...). After that, the resulting set is filtered * using predicate.
* The predicate must have the signature Element x Element -> boolean.
*
* @param ems1 the first set
* @param ems2 the second set
* @param predicate the join predicate
* @param ovLapPairsMeet this flag is passed to overlappingPairs(); if true, objects with adjacent bounding boxes are
* reported, too
* @param bboxFilter this flag is passed to overlappingPairs(); if true, in a pre-processing step the number of
* candidates is reduced by removing objects of el1,el2 which don't overlap the unified bounding box of el1,el2 resp.
* @param earlyExit if true, an EarlyExit exception is thrown immediately when the first object of elN is found which
* doesn't have a partner in elM, i.e. at least one element of elM which has a bounding box that overlaps the
* bounding box of the element of elN. N is the passed setNumber. earlyExit is evaluated in bboxFilter, so if shall
* be used, be sure that bboxFilter = true
* @param setNumber must be 1 or 2; specifies the set for earlyExit
* @return the PairMultiSet as join of ems1 and ems2
* @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 e in a group another element f can be found with predicate(e,f) = true.
* Example: Let the original set be {a,b,c,d}. Assume, that the predicate p holds for the pairs (a,b),(b,c). * Then, the resulting groups are {a,b,c}, {d}.
* The predicate's signature must be: Element x Element -> boolean * * @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 secMethod on pairs of elements of ljpl for which the predicate holds.
* The mainMethod itself must be a set operation with the signature ElemMultiSet x ElemMultiSet x predicate
* x secMethod -> ElemMultiSet. Such a method can be found in this class.
* Then, this map operation only splits every entry of ljpl and calls
* mainMethod with the two objects of the entry and the parameters predicate and secMethod.
* The signatures for predicate and secMethod have to be Element x Element -> boolean and
* Element x Element -> Element, resp.
* 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 ljpl * @param predicate the predicate is passed to mainMethod * @param secMethod this method is passed to mainMethod * @return the union of the results of mainMethod */ 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 true, if both sets are disjoint. * Takes use of the intersects() method which must be implemented for each type which implements the {@link Element} interface. * An overlapJoin() is computed using that intersects() method. If the result is empty, true is returned. * false otherwise. * * @param ems1 the first set * @param ems2 the second set * @return true, if ems1,ems2 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.
* Uses the compare() method which must be implemented for every type which implements the {@link Element} interface.
* Throws a WrongTypeException, if the sets are not of the same type.
* Note, that equal((a,a,b), (a,b)) returns false. * * @param el1 the first set * @param el2 the second set * @return true, 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 E,F and a predicate p this method returns a set of {@link LeftJoinPair}s where the first elements of the pairs are elements of E and their partners are elements of F. * Each element of E appears exactly once in the resulting {@link LeftJoinPairMultiSet}. For all of the partners of such an * element e, p(e,g), g element of the partner set of e, holds.
* Given an example, if E,F are sets of triangles and p is a predicate overlap: Triangle x Triangle -> boolean. * E = {a,b,c,d}, F = {e,f,g}. Now, e overlaps a,b and g overlaps a,b,c. Then the result would be:
* {
* (a x {e,g}),
* (b x {e,g}),
* (c x {g}),
* (d x {})
* }
* The predicate must have the signature: Element x Element -> boolean. * * @param ems1 the first set * @param ems2 the second set * @param predicate the join predicate used to construct the LeftJoinPairs * @return the resulting LeftJoinPairMultiSet * @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 (Element x ElemMultiSet) of ljpMS the method is invoked on that pair if the predicate holds. * The predicate may be NULL. If so, it is assumed, that the predicate holds for all pairs of ljpMS. Then, the * method is invoked on all entries with no further checks.
* For every {@link LeftJoinPair}, the method is invoked on that pair, if the predicate holds (if not NULL). Then, the * result of the method invocation is stored in LeftJoinPair.elemSet, i.e. it replaces the original * ElemMultiSet. The first argument of the method (the Element) remains unchanged.
* The allowed signature for predicate is Element x ElemMultiSet -> boolean. The method must have
* the signature Element x ElemMultiSet -> ElemMultiSet.
*
* @param ljpMS the 'in' set
* @param predicate it checks whether the method may be invoked on a pair; may be NULL
* @param method is invoked on each pair for which the predicate holds
* @return the changed leftJoinPairMultiSet
*/
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 e1 of el1 x e2 of el2 exists which predicate(e1,e2) == true, replace e1 in el with method(e1,e2).
* The predicate and method must have the signature Element x Element -> boolean and
* Element x Element -> Element (of the same type as el1), resp.
* 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 method * @param method is invoked on candidates selected by predicate * @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 ems1,ems2 using method.
* All pairs of e1 of ems1 and e2 of ems2 are checked for their return value of * method(e1,e2). That pair, which returns * the maximum value of all pairs, is returned.
* The passed method must have the signature Element x Element -> Rational.
*
* @param ems1 the first set
* @param ems2 the second set
* @param method the method that is used to compute the values for ElemPair(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 ems1,ems2 using method.
* All pairs of e1 of ems1 and e2 of ems2 are checked for their return value of * method(e1,e2). That pair, which returns * the minimum value of all pairs, is returned.
* The passed method must have the signature Element x Element -> Rational.
*
* @param el1 the first set
* @param el2 the second set
* @param method the method that is used to compute the values for ElemPair(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 e1,e2 of el exists with predicate(e1,e2) == true, replace both elements by method(e1,e2).
* The reduce operation traverses the set el and searches for pairs of elements for which the predicate holds. * If such a pair is found, both elements are removed from the set and the result of the method 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.
* The predicate must have the signature Element x Element -> boolean. The method must have the signature
* Element x Element -> Element (of the same type).
* This method is very time consuming. Some other implementations of reduce 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 E,F and a predicate p this method computes a set of pairs (e,f) with e element of * E, f element of F and p(e,f) == true.
* The predicate's signature must be Element x Element -> boolean. 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.
* The method must have the signature Element x Element -> Element or
* Element x Element -> ElemMultiSet.
* 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.
* Duplicates are removed afterwards.
* The signature of the method m must be one of
* Element -> Element,
* Element -> ElemMultiSet or
* Element -> Element[].
*
* @param el the 'in' set
* @param m the method that is invoked on the elements of el
* @return the changed el
*/
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 disjointUnion((a,b),(b,c)) -> (a,b,b,c).
*
* @param el1 the first set
* @param el2 the second set
* @return the disjoint union of el1,el2
*/
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.
* This means that intersection((a,b),(b,c)) -> (b).
* This method uses the compare() 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 el1In,el2In * @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.
* This means that difference((a,b,c),(a,b)) -> (c).
* This method uses the compare() 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 el1 the first set * @param el2 the second set * @return the difference el - el2 * @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. * rdup((a,b,b,c)) -> (a,b,c). * * @param elIn the 'in' set * @return elIn 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 rdup2((a,b,b,c)) -> (a,c). * * @param elIn the 'in' set * @return elIn 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.
* This means that union((a,b),(b,c)) -> (a,b,c). The duplicates are removed.
* A WrongTypeException 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 el1,el2
* @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 m invoked on every element of the set.
* Method m must have the signature Element -> double.
*
* @param ems the 'in' set
* @param m the method that is invoked on the elements of ems
* @return the sum of the results of m'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.
* This method implements a pretty complex DAC algorithm which is not described here.
* In general, pairs are computed for objects of two sets (which don't have to be of the same type). But, when using the * sameSet 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 overlappingPairs is invoked on (e1,e2,e3...) x (f1,f2,f3) one would get (at least) * the pairs (e1,f2) etc. are identical and therefore have overlapping bounding boxes. * If the sameSet flag is set, these pairs are not found in the resulting set.
* Using the meet 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.
* If the bboxFilter flag is set, first the bounding box of the complete sets el1,el2 is computed, i.e. that complete bounding box * includes all of the set's objects. Then, all object's bounding boxes of el1 are checked against the el2 complete box.* * Objects with boxes that don't intersect the set's box are removed. The same is done for the elements of el2 and the box of el1. *
* When using the earlyExit flag, the setNumber is evaluated. This number must be 1 or 2 and specifies one of the two sets. When, * during the execution of bboxFilter one object of that set is removed for the reason that it doesn't overlap the big box * of the other set, a NoOverlappingBoxFoundException is thrown.
* Note, that the earlyExit flag can only be used together with the bboxFilter flag and the setNumber. * * @param el1 the first set * @param el2 the second set * @param sameSet choose true, if identical pairs should not be reported in the case that el1 == el2 * @param meet set to true, if adjancency for bounding boxed should suffice instead of overlap * @param bboxFilter use true, if a filter step should be used to reduce the number of objects * @param earlyExit if set to true, an NoOverlappingboxException will be thrown, if one element of the set specified by * setNumber finds no partner * @param setNumber specifies the set for earlyExit * @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 real 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 ivlArr * @param idx2 defines the acutal right index on ivlArr * @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 el1's and el2's objects. * This is a supportive method for the overlappingPairs method. From the elements of el1,el2, 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 el2 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 true, 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 compareX() method, then using their compareY() method.
* This method can only be used for Element 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 to hi */ 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.
* 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 earlyExit can be used to stop the execution of the calling algorithm. Therefore,
* a setNumber is passed together with earlyExit. If true,
* bboxFilter throws a {@link NoOverlappingBoxFoundException} if the bounding box of an element of the
* set specified by setNumber 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 true, if execution shall be stopped
* @param setNumber the number of the set which is examined if earlyExit == true
* @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