/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.indexStructures;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import xxl.core.collections.ReversedList;
import xxl.core.collections.containers.Container;
import xxl.core.comparators.ComparableComparator;
import xxl.core.comparators.FeatureComparator;
import xxl.core.cursors.Cursors;
import xxl.core.cursors.filters.Filter;
import xxl.core.cursors.identities.TeeCursor;
import xxl.core.cursors.mappers.Mapper;
import xxl.core.cursors.sources.ArrayCursor;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Constant;
import xxl.core.functions.Function;
import xxl.core.io.converters.ConvertableConverter;
import xxl.core.io.converters.Converter;
import xxl.core.io.converters.IntegerConverter;
import xxl.core.io.converters.LongConverter;
import xxl.core.io.converters.ShortConverter;
import xxl.core.predicates.AbstractPredicate;
import xxl.core.spatial.rectangles.DoublePointRectangle;
import xxl.core.spatial.rectangles.Rectangle;
import xxl.core.util.BitSet;
/** The <tt>XTree</tt> is a variant of the {@link RTree RTree} for
* high dimensional data which avoids splits which would result in
* high overlap.
*
* For a detailed discussion see
* Stefan Berchtold, Daniel A. Keim and Hans-Peter Kriegel:
* "The {X}-Tree: An Index Structure for High-Dimensional Data",
* VLDB (1996), 28-39.
*
* @see Tree
* @see ORTree
* @see RTree
*/
public class XTree extends RTree {
/** Threshold overlap value. Explained in detail in the article about the X-Tree.
*/
protected double maxOverlap = 0.2;
/** Minimum fanout. Explained in detail in the article about the X-Tree.
*/
protected double minFanout = 0.2;
/** the number of nodes in the tree
*/
protected int numOfNode = 0;
/** the number of dimensions
*/
protected int numberOfDimensions;
/** Initializes a created XTree.
*
* @param rootEntry the new {@link Tree#rootEntry}
* @param rootDescriptor the new {@link Tree#rootDescriptor}
* @param getDescriptor the new {@link Tree#getDescriptor}
* @param container container used for as constant for {@link Tree#getContainer}
* and {@link Tree#determineContainer}
* @param minCapacity is used to define {@link Tree#underflows}, {@link Tree#getSplitMinRatio}
* and {@link Tree#getSplitMaxRatio}.
* @param maxCapacity is used to define {@link Tree#underflows} and {@link Tree#overflows}
* @param maxOverlap the new {@link XTree#maxOverlap}
* @param minFanout the new {@link XTree#minFanout}
* @param numberOfDimensions {@link XTree#numberOfDimensions}
* @return the initialized <tt>XTree</tt>
*/
public XTree initialize (IndexEntry rootEntry, Descriptor rootDescriptor, Function getDescriptor, Container container, final int minCapacity, final int maxCapacity,double maxOverlap,double minFanout,int numberOfDimensions) {
this.numberOfDimensions = numberOfDimensions;
this.maxOverlap = maxOverlap;
this.minFanout = minFanout;
return (XTree)super.initialize(
rootEntry,
rootDescriptor,
getDescriptor,
new Constant(container),
new Constant(container),
new AbstractPredicate() {
public boolean invoke (Object node) {
return ((Tree.Node)node).number()<(maxCapacity*(((XTree.Node)node).getNodeSize()-1)+minCapacity);
}
},
new AbstractPredicate() {
public boolean invoke (Object node) {
return ((Tree.Node)node).number()>(maxCapacity*((XTree.Node)node).getNodeSize());
}
},
new AbstractFunction() {
public Object invoke (Object node) {
return new Double(minCapacity*((XTree.Node)node).getNodeSize()/(double)((Tree.Node)node).number());
}
},
new AbstractFunction() {
public Object invoke (Object node) {
return new Double(1.0-minCapacity*((XTree.Node)node).getNodeSize()/(double)((Tree.Node)node).number());
}
}
);
}
/** Initializes a created XTree qithoit root entry.
*
* @param getDescriptor the new {@link Tree#getDescriptor}
* @param container container used for as constant for {@link Tree#getContainer}
* and {@link Tree#determineContainer}
* @param minCapacity is used to define {@link Tree#underflows}, {@link Tree#getSplitMinRatio}
* and {@link Tree#getSplitMaxRatio}.
* @param maxCapacity is used to define {@link Tree#underflows} and {@link Tree#overflows}
* @param maxOverlap the new {@link XTree#maxOverlap}
* @param minFanout the new {@link XTree#minFanout}
* @param numberOfDimensions {@link XTree#numberOfDimensions}
* @return the initialized <tt>XTree</tt>
*/
public Tree initialize (Function getDescriptor, Container container, int minCapacity, int maxCapacity, double maxOverlap, double minFanout, int numberOfDimensions) {
return this.initialize (null,null,getDescriptor,container,minCapacity,maxCapacity,maxOverlap,minFanout,numberOfDimensions);
}
/** Initializes a created XTree without root entry and default values for
* XTree parameters.
*
* @param getDescriptor the new {@link Tree#getDescriptor}
* @param container container used for as constant for {@link Tree#getContainer}
* and {@link Tree#determineContainer}
* @param minCapacity is used to define {@link Tree#underflows}, {@link Tree#getSplitMinRatio}
* and {@link Tree#getSplitMaxRatio}.
* @param maxCapacity is used to define {@link Tree#underflows} and {@link Tree#overflows}
* @param numberOfDimensions {@link XTree#numberOfDimensions}
* @return the initialized <tt>XTree</tt>
*/
public Tree initialize (Function getDescriptor, Container container, final int minCapacity, final int maxCapacity, int numberOfDimensions) {
return this.initialize (null,null,getDescriptor,container,minCapacity,maxCapacity,this.maxOverlap,this.minFanout,numberOfDimensions);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createNode(int)
*/
public Tree.Node createNode(int level){
return new Node().initialize(level, new LinkedList(),1);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createIndexEntry(int)
*/
public Tree.IndexEntry createIndexEntry (int parentLevel) {
return new IndexEntry(parentLevel);
}
/** Counts the number of normal directory nodes per level in the X-tree.
*
* @param noOfDNodePerLevel array with a counter for each level
* @param bufferSize array containing the buffersize at index 0
*/
public void getNoOfDNode(int[] noOfDNodePerLevel,int[] bufferSize){
((XTree.Node)rootEntry().get()).getNoOfDNode(noOfDNodePerLevel,bufferSize);
}
/** Counts the number of supernodes per level in the X-tree.
*
* @param noOfSDNodePerLevel array with a counter for each level
* @param bufferSize array containing the buffersize at index 0
*/
public void getNoOfSDNode(int[] noOfSDNodePerLevel,int[] bufferSize){
((XTree.Node)rootEntry().get()).getNoOfSDNode(noOfSDNodePerLevel,bufferSize);
}
/** This class describes the index entries (i.e. the entries of the non-leaf nodes)
* of a x-tree. Each index entry refers to a {@link Tree.Node node} which is the root
* of the subtree. We call this node the subnode of the index entry.
*
* @see ORTree.IndexEntry
*/
public class IndexEntry extends ORTree.IndexEntry {
/** The split history of the son.
*/
protected BitSet splitHistory = new BitSet(numberOfDimensions);
/** Creates a new <tt>IndexEntry</tt> with a given {@link Tree.IndexEntry#parentLevel parent level}.
* @param parentLevel the parent level of the new <tt>IndexEntry</tt>
*/
public IndexEntry(int parentLevel) {
super(parentLevel);
}
/** This method is used to initialize a new <tt>IndexEntry</tt> with
* a split-history.
*
* @param splitHistory the new {@link XTree.IndexEntry#splitHistory}
* @return the <tt>IndexEntry</tt>, i.e. <tt>this</tt>
*/
public IndexEntry initialize(BitSet splitHistory) {
this.splitHistory = splitHistory;
return this;
}
/** Returns the split-history of the node.
*
* @return the split-history of the node
*/
public BitSet getHistory() {
return splitHistory;
}
/** Sets the split-history of the node
*
* @param splitHistory the new {@link XTree.IndexEntry#splitHistory}
*/
public void setHistory(BitSet splitHistory) {
this.splitHistory = splitHistory;
}
/** Updates the split-history of this node by setting the bit at dimension
* <tt>dim</tt>.
*
* @param dim the dimension at which the split-history is updated
*/
public void updateHistory(int dim) {
BitSet set = new BitSet(numberOfDimensions);
set.set(dim);
updateHistory(set);
}
/** Updates the split-history of this node by setting all bits set in
* <tt>bitSet</tt>.
*
* @param bitSet the bits to set in the split-history
*/
public void updateHistory(BitSet bitSet) {
splitHistory.or(bitSet);
}
} //IndexEntry
/** <tt>Node</tt> is the class used to represent leaf- and non-leaf
* nodes of <tt>XTree</tt>. Nodes are stored in containers.
*
* @see Tree.Node
* @see ORTree.Node
* @see RTree.Node
*/
public class Node extends RTree.Node {
/* (non-Javadoc)
* @see xxl.core.indexStructures.ORTree.Node#chooseSubtree(xxl.core.indexStructures.Descriptor, java.util.Iterator)
*/
protected ORTree.IndexEntry chooseSubtree (Descriptor descriptor, Iterator minima) {
final Rectangle dataRectangle = (Rectangle)descriptor;
TeeCursor validEntries = new TeeCursor(minima);
minima = new Filter(validEntries,
new AbstractPredicate() {
public boolean invoke (Object object) {
return rectangle(object).contains(dataRectangle);
}
}
);
if (!minima.hasNext()) {
minima = Cursors.minima(validEntries.cursor(),
new AbstractFunction() {
public Object invoke (Object object) {
Rectangle oldRectangle = rectangle(object);
Rectangle newRectangle = Descriptors.union(oldRectangle, dataRectangle);
return new Double(newRectangle.area()-oldRectangle.area());
}
}
).iterator();
}
minima = Cursors.minima(minima,
new AbstractFunction() {
public Object invoke (Object object) {
return new Double(rectangle(object).area());
}
}
).iterator();
validEntries.close();
return (IndexEntry)minima.next();
}
/** SplitInfo contains information about a split. The enclosing
* Object of this SplitInfo-Object (i.e. Node.this) is the new node
* that was created by the split.
*/
public class SplitInfo extends RTree.Node.SplitInfo {
/** Flag indicating if the split succeeded.
*/
protected boolean splitSuccess;
/** Creates a new <tt>SplitInfo</tt> with a given path.
* @param path the path from the root to the splitted node
*/
public SplitInfo (Stack path) {
super(path);
}
/** Initializes a new <tt>SplitInfo</tt> by setting <tt>splitSeccess</tt>.
* @param splitSuccess the new {@link XTree.Node.SplitInfo#splitSuccess}
* @return the initialized <tt>Splitinfo</tt>, i.e. <tt>this</tt>
*/
public SplitInfo initialize (boolean splitSuccess) {
this.splitSuccess = splitSuccess;
return this;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.RTree.Node.SplitInfo#initialize(xxl.core.indexStructures.RTree.Node.Distribution)
*/
public ORTree.Node.SplitInfo initialize( RTree.Node.Distribution distribution) {
return super.initialize(distribution);
}
/** Returns if the split succeeded.
* @return <tt>true</tt> if the split succeeded
*/
public boolean isSucceeded() {
return splitSuccess;
}
/** Sets the <tt>splitSuccess</tt> flag.
* @param splitSuccess the new {@link XTree.Node.SplitInfo#splitSuccess}
*/
public void setSuccess(boolean splitSuccess) {
this.splitSuccess = splitSuccess;
}
}
/** The size if this node. If nodeSize>1, this is a supernode.
*/
protected int nodeSize;
/** Initializes a created node by setting its level, entries and size.
*
* @param level the node's level
* @param entries the node's entries
* @param nodeSize the size of the node
* @return the initialized node
*/
public Node initialize(int level, Collection entries, int nodeSize) {
super.initialize(level,entries);
this.nodeSize = nodeSize;
return this;
}
/** Gets the size of the node.
*
* @return the size of the node
*/
public int getNodeSize() {
return nodeSize;
}
/** Sets the size of the node
*
* @param nodeSize the new {@link XTree.Node#nodeSize}
*/
public void setNodeSize(int nodeSize) {
this.nodeSize = nodeSize;
}
/** Adapts {@link XTree.Node#nodeSize} to a suitable value by
* checking for under- and overflows.
*/
public void adaptNodeSize() {
while (underflows()) {
if (getNodeSize()==1)
break;
setNodeSize(getNodeSize()-1);
}
while (overflows())
setNodeSize(getNodeSize()+1);
}
/** Adapts {@link XTree.Node#nodeSize} by setting it to <tt>mult</tt>.
*
* @param mult the new {@link XTree.Node#nodeSize}
*/
public void adaptNodeSize(int mult) {
setNodeSize(mult);
}
/** Counts the number of normal directory nodes per level.
*
* @param noOfDNodePerLevel array with a counter for each level
* @param bufferSize array containing the buffersize at index 0
*/
public void getNoOfDNode(int[] noOfDNodePerLevel, int[] bufferSize) {
if (getNodeSize()==1)
noOfDNodePerLevel[level]++;
if (level>1) {
Object [] entryArray = entries.toArray();
for (int i=0; i<entryArray.length && bufferSize[0]>0; i++) {
(((IndexEntry)entryArray[i]).container()).get(((IndexEntry)entryArray[i]).id(),false);
bufferSize[0]--;
}
for (int i=0; i<entryArray.length; i++) {
((Node)((IndexEntry)entryArray[i]).get()).getNoOfDNode(noOfDNodePerLevel,bufferSize);
}
}
}
/** Counts the number of supernodes per level.
*
* @param noOfSDNodePerLevel array with a counter for each level
* @param bufferSize array containing the buffersize at index 0
*/
public void getNoOfSDNode(int[] noOfSDNodePerLevel, int[] bufferSize) {
if (getNodeSize()>1)
noOfSDNodePerLevel[level]++;
if (level>1) {
Object [] entryArray = entries.toArray();
for (int i =0; i<entryArray.length && bufferSize[0]>0; i++) {
if (((Node)((IndexEntry)entryArray[i]).get()).getNodeSize()>1) {
(((IndexEntry)entryArray[i]).container()).get(((IndexEntry)entryArray[i]).id(),false);
bufferSize[0]--;
}
}
for (int i =0; i<entryArray.length; i++) {
((Node)((IndexEntry)entryArray[i]).get()).getNoOfSDNode(noOfSDNodePerLevel,bufferSize);
}
}
}
/** Treats overflows which occur during an insertion.
* It tries to split the overflowing node in new nodes. If a split occurs
* it creates new index-entries for the new Nodes. These
* index-entries will be added to the given <tt>List</tt>.
* The method {@link Tree.Node#post(Tree.Node.SplitInfo, Tree.IndexEntry)}
* will be used to post the created index-entries to
* the parent Node. If no split occurs the node is added to a supernode.
*
* @param path the path from the root to the overflowing node
* @param parentNode the parent node of the overflowing node
* @param newIndexEntries a <tt>List</tt> to carry the new index-entries created by the split
* @return splitinfo for the split
*/
protected Tree.Node.SplitInfo redressOverflow (Stack path, Tree.Node parentNode, List newIndexEntries) {
IndexEntry indexEntry = (IndexEntry)indexEntry(path);
SplitInfo splitInfo = (SplitInfo)createNode(level).split(path);
if(splitInfo.isSucceeded()){
int dim = splitInfo.distribution().getDim();
IndexEntry newIndexEntry =(IndexEntry) createIndexEntry(parentNode.level).initialize(splitInfo);
Node newNode = (Node)splitInfo.newNode();
// update histories of both IndexEntries
//old node
indexEntry.updateHistory(dim);
//newNode
newIndexEntry.updateHistory(indexEntry.getHistory());
Container container = splitInfo.determineContainer();
// adds new newIndexentry in entries f parentNode
parentNode.post(splitInfo, newIndexEntry);
if(level !=0){
// old node
if (overflows()){
adaptNodeSize();
indexEntry.update(this);
}
if (underflows()){
adaptNodeSize();
indexEntry.update(this);
}
//new node
if (newNode.overflows())
newNode.adaptNodeSize();
}
//new Node is inserted in container
((Tree.IndexEntry)newIndexEntry).initialize(container, container.insert(newNode));
numOfNode++;
newIndexEntries.add(newIndexEntry);
}//endif
else{
//extend old node to supernode,note: this method (redressover..) is called from old node
// here implicit that level !=0, because for level = 0 topospilt aways OK
adaptNodeSize();
// update old node in Container
indexEntry.update(this);
}
return splitInfo;
}
/** Split the node in the case of an overflow. First, the split method
* of the r*-tree is tried. If this produces too much overlap, the
* overlap minimal split is used instead. If this produces an unbalanced
* node, a supernode is created and no split is performed.
*
* @param path the nodes already visited during this insert
* @return a SplitInfo containig all needed information about the split
*/
protected Tree.Node.SplitInfo split (Stack path) {
// node = node before split , this = new node)
final Node node = (Node)node(path);
// try topological split, i.e. use split method of R-Tree
// Note : minEntries and maxEntries in RTree.split( Stack path) method are constants ?!
Tree.Node.SplitInfo splitInfoTopo = topologicalsplit(path);
RTree.Node.Distribution distribution = ((RTree.Node.SplitInfo)splitInfoTopo).distribution();
// only change the type of SplitInfo
SplitInfo splitInfo =new SplitInfo(path);
splitInfo.initialize(distribution);
// if node is not data node, test further, else return, i.e. only use the topological split(RTree split)
if (level() != 0) {
double overlap = distribution.overlapValue();
double area = distribution.areaValue();
double relOverlap = overlap/(area-overlap);
if (relOverlap > maxOverlap) {
// topological split fails -> try overlap minimal split
// but first we have to merge both new node and old node
// it is because of the design of split of RTree and we have to use it !!
// if we can change the end of the method split in RTree then we do not need to merge the nodes
//i.e one must not .....
node.entries.clear();
node.entries.addAll(Arrays.asList(distribution.entries));
(((IndexEntry)indexEntry(path)).descriptor).union(distribution.descriptor(true));
// try overlap minimal split
splitInfo = this.overlapminimalsplit(path);
// if the split has not been successful, then use the topological
// split from above instead.
if (!splitInfo.isSucceeded())
return splitInfo;
// test for unbalanced nodes
double minF = ((node.splitMinNumber()+node.splitMaxNumber()-1)/node.getNodeSize())*minFanout;
Node.Distribution distr = splitInfo.distribution();
overlap = distr.overlapValue();
area =distr.areaValue();
if (distr.entries(true).size()<minF||distr.entries(false).size()<minF||overlap/(area-overlap) > maxOverlap) {
//overvalminimalsplit is not succesfull
//extend old node to supernode, it is carried out in redressoverflow
//node.adaptNodeSize();
// we donot need to reunite the node , because overlap split do not divide old node into two nodes
// it gives only splitInfo
//System.out.println(" Ovlmsplit fails size of node "+splitInfo.distribution().entries(true).size()+ " and "+splitInfo.distribution().entries(false).size()+" required not < "+minF);
splitInfo.setSuccess(false);
return splitInfo;
}
// overlapminimal split is successful
distribution = splitInfo.distribution();
//old node
((IndexEntry)indexEntry(path)).descriptor = distribution.descriptor(false);
//new node
//entries.addAll(distribution.entries(true));
// note : descriptor of new node is in splitInfo and will be set for new Indexentry in redressoverflow
}//if
}// end if level !=0
//update new node
entries.clear();
entries.addAll(distribution.entries(true));
//old node was updated in toplogicalsplit
node.entries.clear();
node.entries.addAll(distribution.entries(false));
splitInfo.setSuccess(true);
return splitInfo;
}//split
/** Performs the normal split of the superclass <tt>RTree</tt>.
*
* @param path the nodes already visited during this insert
* @return a SplitInfo containig all needed information about the split
*
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected Tree.Node.SplitInfo topologicalsplit(Stack path){
return super.split(path);
}
/** Performs a node split minimizing the overlap of the nodes.
*
* @param path the nodes already visited during this insert
* @return a SplitInfo containig all needed information about the split
*
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected SplitInfo overlapminimalsplit (Stack path) {
int numberOfDimensions = ((Rectangle)rootDescriptor()).dimensions();
// the node to be split
final Node node = (Node)node(path);
Distribution distribution;
final int minEntries = 1;
final int maxEntries = node.splitMaxNumber()+node.splitMinNumber()- minEntries;//node.number()+1-minEntries;//node.number()=maxCapacity
Iterator entries = node.entries();
BitSet splitSet = ((IndexEntry)entries.next()).getHistory();
for(; entries.hasNext();)
splitSet.and(((IndexEntry)entries.next()).getHistory() );
if (splitSet.equals(new BitSet(numberOfDimensions))) {
SplitInfo splitInfo = new SplitInfo(path);
splitInfo.setSuccess(false);
return splitInfo;
}
int numberOfCandidate = 0;
Integer [] candidate = new Integer[splitSet.size()];
for (int i =0; i<splitSet.size(); i++) {
if( splitSet.get(i)){
candidate[numberOfCandidate] =new Integer(i);
numberOfCandidate++;
}
}
Integer [] candidate2 = new Integer[numberOfCandidate];
for (int j=0; j<numberOfCandidate; j++)
candidate2[j]=candidate[j];
Iterator<List<Distribution>> distributionLists = new Mapper<Integer, List<Distribution>>(
new AbstractFunction<Integer, List<Distribution>>() {
public List<Distribution> invoke(final Integer dim) {
// Liste der Distributionen fuer diese Dimension
ArrayList<Distribution> distributionList = new ArrayList<Distribution>(2*(maxEntries-minEntries+1));
Rectangle [] [] rectangles = new Rectangle[2] [];
// Betrachte pro Dimension die bzgl. linken bzw. rechten Raendern geordneten Eintraege
for (int i=0; i<2; i++) {
// Gewuenschte minimale Anzahl an Eintraegen im alten bzw. neuen Knoten
Object [] entryArray = node.entries.toArray();
final boolean right = i==1;
// Sortierung der Eintraege nach linkem bzw. rechtem Rand der aktuellen Dimension
Arrays.sort(entryArray, new FeatureComparator(new ComparableComparator(),
new AbstractFunction() {
public Object invoke (Object entry) {
return new Double(rectangle(entry).getCorner(right).getValue(dim));
}
}
));
// Monotone Berechnung der Deskriptoren fuer alle Distributionen (linear!)
for (int k = 0; k<2; k++) {
List entryList = Arrays.asList(entryArray);
Iterator entries2 = (k==0? entryList: new ReversedList(entryList)).iterator();
Rectangle rectangle = new DoublePointRectangle(rectangle(entries2.next()));
for (int l = (k==0? minEntries: node.number()-maxEntries); --l>0;)
rectangle.union(rectangle(entries2.next()));
(rectangles[k] = new Rectangle[maxEntries-minEntries+1])[0] = rectangle;
for (int j=1; j<=maxEntries-minEntries; rectangles[k][j++] = rectangle)
rectangle = Descriptors.union(rectangle, (rectangle(entries2.next())));
}
// Erzeugen der Distributionen dieser Dimension
for (int j = minEntries; j<=maxEntries; j++)
distributionList.add(new Distribution(entryArray, j, rectangles[0][j-minEntries], rectangles[1][maxEntries-j], dim));
}
return distributionList;
}
},
new ArrayCursor<Integer>(candidate2)
);
// Gib die Distributionsliste einer der Dimensionen zurueck, fuer die die Summe der Margin-Werte
// aller ihrer Distributionen minimal ist
List<Distribution> distributionList = Cursors.minima(
distributionLists,
new AbstractFunction<Collection<Distribution>, Double> () {
public Double invoke (Collection<Distribution> list) {
double marginValue = 0.0;
for (Distribution distribution : list)
marginValue += distribution.marginValue();
return marginValue;
}
}
).getFirst();
// Waehle die Distributionen der gewaehlten Dimension mit minimalem Overlap aus
distributionList = Cursors.minima(
distributionList.iterator(),
new AbstractFunction<Distribution, Double>() {
public Double invoke(Distribution distribution) {
return distribution.overlapValue();
}
}
);
// Falls mehrere Distributionen in Frage kommen: Waehle eine mit minimalem Area-Wert aus
distributionList = Cursors.minima(
distributionList.iterator(),
new AbstractFunction<Distribution, Double>() {
public Double invoke(Distribution distribution) {
return distribution.areaValue();
}
}
);
distribution = distributionList.get(0);
return (XTree.Node.SplitInfo)((new SplitInfo(path)).initialize(true).initialize(distribution));
// end choose one dimension
}//overlapminimalsplit
}//Node
/* (non-Javadoc)
* @see xxl.core.indexStructures.ORTree#indexEntryConverter(xxl.core.io.converters.Converter)
*/
public Converter indexEntryConverter (Converter descriptorConverter) {
return new IndexEntryConverter(descriptorConverter);
}
/** The instances of this class are converters to write index entries to the
* external storage (or any other {@link java.io.DataOutput DataOutput}) or read
* them from it (or any other {@link java.io.DataInput DataInput}).
* @see xxl.core.io.converters.Converter
*/
public class IndexEntryConverter extends ORTree.IndexEntryConverter {
/**
* @param descriptorConverter a converter for descriptors
*/
public IndexEntryConverter (Converter descriptorConverter) {
super(descriptorConverter);
}
/* (non-Javadoc)
* @see xxl.core.io.converters.Converter#read(java.io.DataInput, java.lang.Object)
*/
public Object read (DataInput dataInput, Object object) throws IOException {
IndexEntry indexEntry = (IndexEntry)object;
indexEntry.container();
indexEntry.id = LongConverter.DEFAULT_INSTANCE.read(dataInput, null);
indexEntry.splitHistory.read(dataInput);
indexEntry.descriptor = (Descriptor)descriptorConverter.read(dataInput, null);
return indexEntry;
}
/* (non-Javadoc)
* @see xxl.core.io.converters.Converter#write(java.io.DataOutput, java.lang.Object)
*/
public void write (DataOutput dataOutput, Object object) throws IOException {
IndexEntry indexEntry = (IndexEntry)object;
indexEntry.container();
LongConverter.DEFAULT_INSTANCE.write(dataOutput, (Long)indexEntry.id());
indexEntry.splitHistory.write(dataOutput);
descriptorConverter.write(dataOutput, indexEntry.descriptor());
}
}//IndexEntryConverter
/* (non-Javadoc)
* @see xxl.core.indexStructures.ORTree#nodeConverter(xxl.core.io.converters.Converter, xxl.core.io.converters.Converter)
*/
public Converter nodeConverter (Converter objectConverter, Converter indexEntryConverter) {
return new NodeConverter(objectConverter, indexEntryConverter);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.RTree#nodeConverter(xxl.core.io.converters.Converter, int)
*/
public Converter nodeConverter (Converter objectConverter, final int dimensions) {
return nodeConverter(objectConverter,indexEntryConverter(
new ConvertableConverter(
new AbstractFunction() {
public Object invoke () {
return new DoublePointRectangle(dimensions);
}
}
)
));
}
/** The instances of this class are converters to write nodes to the
* external storage (or any other {@link java.io.DataOutput DataOutput}) or read
* them from it (or any other {@link java.io.DataInput DataInput}).
* @see xxl.core.io.converters.Converter
*/
public class NodeConverter extends ORTree.NodeConverter {
/**
* @param objectConverter
* @param indexEntryConverter
*/
public NodeConverter (Converter objectConverter, Converter indexEntryConverter) {
super(objectConverter,indexEntryConverter);
}
/* (non-Javadoc)
* @see xxl.core.io.converters.Converter#read(java.io.DataInput, java.lang.Object)
*/
public Object read (DataInput dataInput, Object object) throws IOException {
Node node = (Node)createNode(dataInput.readShort());
int nodeSize = dataInput.readShort();
node.setNodeSize(nodeSize);
for (int i=dataInput.readInt(); --i>=0;)
node.entries.add(node.level==0?
objectConverter.read(dataInput, null):
indexEntryConverter.read(dataInput, createIndexEntry(node.level))
);
return node;
}
/* (non-Javadoc)
* @see xxl.core.io.converters.Converter#write(java.io.DataOutput, java.lang.Object)
*/
public void write (DataOutput dataOutput, Object object) throws IOException {
Node node = (Node)object;
Converter converter = node.level==0? objectConverter: indexEntryConverter;
ShortConverter.DEFAULT_INSTANCE.write(dataOutput, new Short((short)node.level));
ShortConverter.DEFAULT_INSTANCE.write(dataOutput, new Short((short)node.nodeSize));
IntegerConverter.DEFAULT_INSTANCE.write(dataOutput, new Integer(node.number()));
for (Iterator entries = node.entries(); entries.hasNext();)
converter.write(dataOutput, entries.next());
}
}//NodeConverter
}//XTree