/* 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.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import xxl.core.collections.Lists;
import xxl.core.collections.MapEntry;
import xxl.core.cursors.AbstractCursor;
import xxl.core.cursors.Cursor;
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.EmptyCursor;
import xxl.core.cursors.sources.SingleObjectCursor;
import xxl.core.cursors.unions.Sequentializer;
import xxl.core.cursors.wrappers.IteratorCursor;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.indexStructures.ORTree.IndexEntry;
import xxl.core.io.converters.Converter;
import xxl.core.io.converters.DoubleConverter;
import xxl.core.io.converters.IntegerConverter;
import xxl.core.io.converters.ShortConverter;
import xxl.core.predicates.AbstractPredicate;
import xxl.core.spatial.LpMetric;
import xxl.core.util.Distance;
/** An <tt>ORTree</tt> for objects with spheres as regions.
*
* For a detailed discussion see
* P. Ciaccia, M. Patella, P. Zezula:
* "M-tree: An Efficient Access Method for Similarity Search in Metric Spaces",
* VLDB (1997) 426-435.
*
* @see Tree
* @see ORTree
* @see SlimTree
*/
public class MTree extends ORTree {
/** Hyperplane split strategy
*/
public static final int HYPERPLANE_SPLIT = 0;
/** Balanced split strategy
*/
public static final int BALANCED_SPLIT = 1;
/** The metric distance function for points.
*/
protected Distance pointDistance = LpMetric.EUCLIDEAN;
/** The metric distance function for spheres.
*/
protected Distance sphereDistance = Sphere.DEFAULT_SPHERE_DISTANCE;
/** A flag indicating if a split is in progress.
*/
protected boolean split = false;
/** The split strategy used in this <tt>MTree</tt>.
*/
protected int splitMode = HYPERPLANE_SPLIT;
/** <tt>Node</tt> is the class used to represent leaf- and non-leaf nodes
* of <tt>MTree</tt>. Nodes are stored in containers.
* @see Tree.Node
* @see ORTree.Node
*/
public class Node extends ORTree.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 Sphere sphere = (Sphere)descriptor;
TeeCursor entries = new TeeCursor(new IteratorCursor(minima));
Iterator containing = new Filter(
entries,
new AbstractPredicate() {
public boolean invoke (Object object) {
return sphere(object).containsPD(sphere);
}
}
);
MapEntry entry = (MapEntry)(containing.hasNext() ?
Cursors.minimize(containing,
new AbstractFunction() {
public Object invoke (Object object) {
return new Double(sphere(object).centerDistance(sphere));
}
}
) :
Cursors.minimize(entries.cursor(),
new AbstractFunction() {
public Object invoke (Object object) {
return new Double(sphere(object).centerDistance(sphere) - sphere(object).radius());
}
}
)
);
IndexEntry indexEntry = (IndexEntry)((LinkedList)entry.getValue()).getFirst();
sphere.setDistanceToParent(sphere.centerDistance((Sphere)indexEntry.descriptor()));
entries.close();
return indexEntry;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected Tree.Node.SplitInfo split (Stack path) {
final Node node = (Node)node(path);
// promote
Iterator seeds = new Sequentializer(
new Mapper(
new AbstractFunction() {
int index = 0;
public Object invoke (final Object entry1) {
return new Mapper(
new AbstractFunction() {
public Object invoke (Object entry2) {
return new Object[] {entry1, entry2};
}
}
,((ArrayList)node.entries).listIterator(++index) );
}
}
, node.entries())
);
final Object [] seed = (Object[])Cursors.maxima(seeds,
new AbstractFunction() {
public Object invoke (Object seed) {
double dist0 = sphere(((Object[])seed)[0]).getDistanceToParent();
double dist1 = sphere(((Object[])seed)[1]).getDistanceToParent();
if (dist0 == -1 || dist1 == -1)
throw new IllegalArgumentException("Undefined 'distanceToParent' entry in a sphere detected.");
return new Double(Math.abs(dist0 - dist1));
}
}
).getFirst();
// reference spheres
final Sphere sphere0 = sphere(seed[0]), sphere1 = sphere(seed[1]);
final ArrayList entries0 = new ArrayList(node.number()/2);
switch (splitMode) {
case HYPERPLANE_SPLIT : { /* modified generalized hyperplane decomposition ensuring minCapacity */
final int maxCapacity = node.splitMaxNumber();
Cursors.consume(new Mapper(
new AbstractFunction() {
Collection insertTo = null;
Collection[] collections = new Collection []{entries0, entries};
public Object invoke (Object o) {
if (insertTo != null) {
insertTo.add(o);
return o;
}
Sphere sphere = sphere(o);
if (o != seed[0] && (o == seed[1] || sphere.centerDistance(sphere0) > sphere.centerDistance(sphere1)))
collections[1].add(o);
else
collections[0].add(o);
for (int i = 0; i < collections.length; i++)
if (collections[i].size() == maxCapacity) {
insertTo = collections[(i+1)%2];
return o;
}
return o;
}
},node.entries())
);
break;
}
case BALANCED_SPLIT : { /* balanced distribution */
ArrayList NNSphere0 = (ArrayList)node.entries;
Lists.quickSort(NNSphere0, getDistanceBasedComparator(sphere0));
ArrayList NNSphere1 = (ArrayList)((ArrayList)node.entries).clone();
Lists.quickSort(NNSphere1, getDistanceBasedComparator(sphere1));
while(!(NNSphere0.isEmpty() && NNSphere1.isEmpty())) {
Object next;
if (!NNSphere0.isEmpty()) {
next = NNSphere0.get(0);
entries0.add(next);
NNSphere0.remove(0);
NNSphere1.remove(next);
}
if (!NNSphere1.isEmpty()) {
next = NNSphere1.get(0);
entries.add(next);
NNSphere1.remove(0);
NNSphere0.remove(next);
}
}
break;
}
default : throw new IllegalArgumentException ("Undefined split mode.");
}
Sphere newSphere = (Sphere)computeDescriptor(node.entries = entries0);
if (!path.isEmpty()) {
Object top = path.pop();
newSphere.setDistanceToParent(newSphere.centerDistance((Sphere)((IndexEntry)indexEntry(path)).descriptor()));
path.push(top);
}
((IndexEntry)indexEntry(path)).descriptor = newSphere;
split = true;
return new SplitInfo(path).initialize(computeDescriptor(entries));
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#grow(java.lang.Object, java.util.Stack)
*/
protected void grow (Object data, Stack path) {
Sphere child;
if (!(data instanceof IndexEntry)) {
LeafEntry entry = !(data instanceof LeafEntry) ? new LeafEntry(data) : (LeafEntry)data;
child = (Sphere)entry.descriptor();
super.grow(entry, path);
}
else {
child = (Sphere)((IndexEntry)data).descriptor();
super.grow(data, path);
}
Stack s = (Stack)path.clone();
if (split && !s.isEmpty()) s.pop();
split = false;
postDistanceToParent(child, s);
}
/** Updates the {@link Sphere#distanceToParent} used for optimization.
*
* @param entry the sphere to be updated
* @param path the path from the root to <tt>entry</tt>
*/
protected void postDistanceToParent (Sphere entry, Stack path) {
Sphere child = entry;
double distToParent = child.getDistanceToParent();
Sphere parent = path.isEmpty() ? (Sphere)rootDescriptor : (Sphere)((IndexEntry)indexEntry(path)).descriptor();
child.setDistanceToParent(child.centerDistance(parent));
if (distToParent == child.getDistanceToParent())
return;
if (!path.isEmpty()) {
path.pop();
postDistanceToParent(parent, path);
}
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#descriptors(xxl.core.indexStructures.Descriptor)
*/
public Iterator descriptors (Descriptor nodeDescriptor) {
return new Mapper(
level == 0 ?
(Function)new AbstractFunction() {
public Object invoke (Object entry) {
return ((LeafEntry)entry).descriptor;
}
}
:
(Function)new AbstractFunction() {
public Object invoke (Object entry) {
return ((IndexEntry)entry).descriptor;
}
}
,entries());
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#query(xxl.core.indexStructures.Descriptor)
*/
public Iterator query (final Descriptor queryDescriptor) {
final Sphere querySphere = (Sphere)queryDescriptor;
final double querySphereDist = querySphere.getDistanceToParent();
return new Filter(entries(),
new AbstractPredicate() {
public boolean invoke (Object object) {
querySphere.setDistanceToParent(querySphereDist);
return sphere(object).overlapsPD(querySphere);
}
}
);
}
} // end of class Node
/** <tt>LeafEntry</tt> is the class used to represent entries of leafnodes
* of <tt>MTree</tt>.
*/
public class LeafEntry {
/** The sphere containing <tt>data</tt>.
*/
protected Sphere descriptor;
/** The data object stored in this leafentry.
*/
protected Object data;
/** Creates a new leafentry without data.
*
*/
public LeafEntry () {
data = descriptor = null;
}
/** Creates a new leafentry containing <tt>data</tt>.
*
* @param data the object to store in this leafentry.
*/
public LeafEntry (Object data) {
this.data = data;
descriptor = (Sphere)getDescriptor.invoke(data);
}
/** Returns the descriptor of this leafentry.
*
* @return the descriptor of this leafentry
*/
public Descriptor descriptor () {
return descriptor;
}
/** Returns the data object stored in this leafentry
*
* @return data object of this leafentry
*/
public Object getData() {
return this.data;
}
/** Compairs this leafentry to other objects. Returns <tt>true</tt>
* if <tt>obj</tt> is another instance of <tt>LeafEntry</tt> containing
* the same data or if <tt>obj</tt> itself equals the data object stored
* in this leafentry.
*
* @param obj the object to be compared for equality with this LeafEntry
* @return <tt>true</tt> if <tt>obj</tt> equals the data in this entry or
* is a <tt>LeafNode</tt> containing the same data as this entry
*/
public boolean equals (Object obj) {
return data.equals(obj instanceof LeafEntry ? ((LeafEntry)obj).data : obj);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return data.hashCode();
}
} // end of class LeafEntry
/** Creates a new Mtree using the spezified distance functions and the
* spezified split mode.
*
* @param pointDistance the new {@link #pointDistance}
* @param sphereDistance the new {@link #sphereDistance}
* @param splitMode the new {@link #splitMode}
*/
public MTree (Distance pointDistance, Distance sphereDistance, int splitMode) {
this.pointDistance = pointDistance;
this.sphereDistance = sphereDistance;
this.splitMode = splitMode;
}
/** Creates a new Mtree using the spezified distance functions and the
* default split mode.
*
* @param pointDistance the new {@link #pointDistance}
* @param sphereDistance the new {@link #sphereDistance}
*/
public MTree (Distance pointDistance, Distance sphereDistance) {
this.pointDistance = pointDistance;
this.sphereDistance = sphereDistance;
}
/** Creates a new Mtree using the spezified distance function for points
* and the spezified split mode.
*
* @param pointDistance the new {@link #pointDistance}
* @param splitMode the new {@link #splitMode}
*/
public MTree (Distance pointDistance, int splitMode) {
this.pointDistance = pointDistance;
this.splitMode = splitMode;
}
/** Creates a new Mtree using the spezified distance function for points and the
* default split mode.
*
* @param pointDistance the new {@link #pointDistance}
*/
public MTree (Distance pointDistance) {
this(pointDistance, HYPERPLANE_SPLIT);
}
/** Creates a new Mtree using the spezified split mode.
*
* @param splitMode the new {@link #splitMode}
*/
public MTree (int splitMode) {
this.splitMode = splitMode;
}
/** Creates a new Mtree using the default parameters.
*/
public MTree () {}
/** Returns a comparator comparing spheres with respect to their distance
* to the given <tt>referenceSphere</tt>.
*
* @param referenceSphere the rererence for the comparison of the comparator
* @return a comparator returning 1, 0 or -1 if its first parameter has
* smaller, equal or greater distance to <tt>referenceSphere</tt> than the second one
*/
protected Comparator getDistanceBasedComparator (final Sphere referenceSphere) {
return new Comparator () {
public int compare (Object o1, Object o2) {
double dist1 = referenceSphere.centerDistance(sphere(o1));
double dist2 = referenceSphere.centerDistance(sphere(o2));
return dist1 < dist2 ? 1 : dist1 > dist2 ? -1 : 0;
}
};
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.ORTree#computeDescriptor(java.util.Collection)
*/
public Descriptor computeDescriptor (Collection collection) {
ArrayList entries = (ArrayList)Cursors.toList(collection.iterator(), new ArrayList(collection.size()));
double[][] distance = new double[entries.size()][entries.size()];
int i_min = 0;
double minValue = 0;
for (int i = 0; i < entries.size(); i++) {
double maxValue = 0, dist = 0;
for (int j = i+1; j < entries.size(); j++) {
Sphere sphere0 = sphere(entries.get(i));
Sphere sphere1 = sphere(entries.get(j));
dist = (distance[i][j] = sphere0.centerDistance(sphere1)) + sphere1.radius();
if (dist > maxValue)
maxValue = dist;
}
if (i == 0 || maxValue < minValue) {
i_min = i;
minValue = maxValue;
}
}
Sphere descriptor = (Sphere)sphere(entries.get(i_min)).clone();
for (int i = 0; i < entries.size(); i++) {
Sphere next = sphere(entries.get(i));
next.setDistanceToParent(distance[i][i_min]);
descriptor.union(next);
}
return descriptor;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createNode(int)
*/
public Tree.Node createNode (int level) {
return new Node().initialize(level, new ArrayList(20));
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#descriptor(java.lang.Object)
*/
public Descriptor descriptor (Object entry) {
return (entry instanceof IndexEntry) ?
((IndexEntry)entry).descriptor() :
(entry instanceof LeafEntry) ?
((LeafEntry)entry).descriptor() :
(Descriptor)getDescriptor.invoke(entry);
}
/** Returns the descriptor of <tt>entry</tt> as a sphere.
*
* @param entry the entry whos descriptor sphere is demanded.
* @return the sphere which is the descriptor of <tt>entry</tt>
*/
public Sphere sphere (Object entry) {
return (Sphere)descriptor(entry);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#query(xxl.core.indexStructures.Descriptor, int)
*/
public Cursor query (final Descriptor queryDescriptor, final int targetLevel) {
final Iterator [] iterators = new Iterator[height()+1];
final Sphere querySphere = (Sphere)queryDescriptor;
final double[] parentDistance = new double[height()+1];
Arrays.fill(parentDistance, -1);
Arrays.fill(iterators, EmptyCursor.DEFAULT_INSTANCE);
if (height()>0 && querySphere.overlapsPD(rootDescriptor())) {
iterators[height()] = new SingleObjectCursor(rootEntry());
parentDistance[height()] = querySphere.centerDistance((Sphere)rootDescriptor());
}
return new AbstractCursor() {
int queryAllLevel = 0;
Object toRemove = null;
Stack path = new Stack();
public boolean hasNextObject() {
for (int parentLevel = targetLevel;;)
if (iterators[parentLevel].hasNext())
if (parentLevel==targetLevel)
return true;
else {
IndexEntry indexEntry = (IndexEntry)iterators[parentLevel].next();
if (indexEntry.level()>=targetLevel) {
Node node = (Node)indexEntry.get(true);
Sphere sphere = (Sphere)indexEntry.descriptor();
if (parentDistance[node.level] == -1)
parentDistance[node.level] = querySphere.centerDistance(sphere);
if (parentDistance[parentLevel] != -1)
querySphere.setDistanceToParent(parentDistance[parentLevel]);
Iterator queryIterator;
if (parentLevel<=queryAllLevel || querySphere.containsPD(sphere)) {
queryIterator = node.entries();
if (parentLevel>queryAllLevel && !iterators[node.level].hasNext())
queryAllLevel = node.level;
}
else {
querySphere.setDistanceToParent(parentDistance[node.level]);
queryIterator = node.query(querySphere);
}
iterators[parentLevel = node.level] =
iterators[parentLevel].hasNext()?
new Sequentializer(queryIterator, iterators[parentLevel]):
queryIterator;
path.push(new MapEntry(indexEntry, node));
}
}
else
if (parentLevel==height())
return false;
else {
if (parentLevel==queryAllLevel)
queryAllLevel = 0;
if (level(path)==parentLevel) {
path.pop();
parentDistance[parentLevel] = -1;
}
iterators[parentLevel++] = EmptyCursor.DEFAULT_INSTANCE;
}
}
public Object nextObject() {
Object next = toRemove = iterators[targetLevel].next();
return next instanceof LeafEntry ? ((LeafEntry)next).data : next;
}
public void update (Object object) throws UnsupportedOperationException, IllegalStateException, IllegalArgumentException {
super.update(object);
if (targetLevel>0)
throw new IllegalStateException();
else
if (targetLevel!=0 || !descriptor(object).equals(descriptor(toRemove)))
throw new IllegalArgumentException();
else {
IndexEntry indexEntry = (IndexEntry)indexEntry(path);
Node node = (Node)node(path);
iterators[0].remove();
node.grow(object, path);
indexEntry.update(node, true);
}
}
public boolean supportsUpdate() {
return true;
}
public void remove () throws UnsupportedOperationException, IllegalStateException {
super.remove();
if (targetLevel<height()) {
IndexEntry indexEntry = (IndexEntry)indexEntry(path);
Node node = (Node)node(path);
iterators[node.level].remove();
for (;;) {
if (indexEntry==rootEntry() && node.level>0 && node.number()==1) {
rootEntry = (IndexEntry)node.entries().next();
rootDescriptor = ((IndexEntry)rootEntry()).descriptor();
indexEntry.remove();
break;
}
if (node.number()==0) {
up(path);
indexEntry.remove();
if (height()==1) {
rootEntry = null;
rootDescriptor = null;
break;
}
else {
indexEntry = (IndexEntry)indexEntry(path);
node = (Node)node(path);
iterators[node.level].remove();
}
}
else if (indexEntry!=rootEntry() && node.underflows()) {
Iterator entries = node.entries();
indexEntry.descriptor = computeDescriptor(node.entries);
up(path);
indexEntry.remove();
iterators[level(path)].remove();
((Sphere)indexEntry.descriptor).setDistanceToParent(-1);
indexEntry = (IndexEntry)node(path).chooseSubtree(indexEntry.descriptor, path);
MTree.this.update(path);
node = (Node)down(path, indexEntry);
while (entries.hasNext())
node.grow(entries.next(), path);
if (node.overflows())
node.redressOverflow(path);
else {
MTree.this.update(path);
up(path);
}
indexEntry = (IndexEntry)indexEntry(path);
node = (Node)node(path);
}
else {
MTree.this.update(path);
while (up(path)!=rootEntry()) {
MTree.this.update(path);
indexEntry = (IndexEntry)indexEntry(path);
node = (Node)node(path);
}
((IndexEntry)rootEntry).descriptor = rootDescriptor = computeDescriptor(node.entries);
break;
}
}
}
else {
rootEntry = null;
rootDescriptor = null;
}
if (targetLevel>0) {
IndexEntry indexEntry = (IndexEntry)toRemove;
indexEntry.removeAll();
}
}
public boolean supportsRemove() {
return true;
}
};
}
/*********************************************************************/
/* CONVERTER */
/*********************************************************************/
/** This class acts as a converter to serialize the tree's leaf-entries.
*/
public class LeafEntryConverter extends Converter {
/** A suitable Converter to serialize the objects stored in the leaf-entries.
*/
protected Converter objectConverter;
/** Creates a new <tt>LeafEntryConverter</tt>.
*
* @param objectConverter a <tt>Converter</tt> to serialize objects stored in the leaf-entries
*/
public LeafEntryConverter (Converter objectConverter) {
this.objectConverter = objectConverter;
}
/* (non-Javadoc)
* @see xxl.core.io.converters.Converter#read(java.io.DataInput, java.lang.Object)
*/
public Object read (DataInput dataInput, Object object) throws IOException {
LeafEntry leafEntry = (LeafEntry)object;
leafEntry.data = objectConverter.read(dataInput, null);
leafEntry.descriptor = (Sphere)getDescriptor.invoke(leafEntry.data);
leafEntry.descriptor.setDistanceToParent(DoubleConverter.DEFAULT_INSTANCE.readDouble(dataInput));
return leafEntry;
}
/* (non-Javadoc)
* @see xxl.core.io.converters.Converter#write(java.io.DataOutput, java.lang.Object)
*/
public void write (DataOutput dataOutput, Object object) throws IOException {
LeafEntry leafEntry = (LeafEntry)object;
objectConverter.write(dataOutput, leafEntry.data);
DoubleConverter.DEFAULT_INSTANCE.writeDouble(dataOutput, leafEntry.descriptor.getDistanceToParent());
}
}
/** Creates a new Converter for leaf-entries using the given Converter
* for objects stored within the entries.
*
* @param objectConverter a Converter to serialize objects stored in the leaf-entries
* @return a new Converter for leaf-entries
*/
public Converter leafEntryConverter (Converter objectConverter) {
return new LeafEntryConverter(objectConverter);
}
/** This class acts as a converter to serialize the tree's nodes.
*/
public class NodeConverter extends Converter {
/** A converter for index-entries.
*/
protected Converter indexEntryConverter;
/** A converter for leaf-entries.
*/
protected Converter leafEntryConverter;
/** Creates a new <tt>NodeConverter</tt> using the given converters
* for leaf- and index-entries.
*
* @param leafEntryConverter a converter for leaf-entries
* @param indexEntryConverter a converter for index-entries
*/
public NodeConverter (Converter leafEntryConverter, Converter indexEntryConverter) {
this.indexEntryConverter = indexEntryConverter;
this.leafEntryConverter = leafEntryConverter;
}
/* (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());
for (int i=dataInput.readInt(); --i>=0;)
node.entries.add(node.level==0?
leafEntryConverter.read(dataInput, new LeafEntry()):
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 ? leafEntryConverter: indexEntryConverter;
ShortConverter.DEFAULT_INSTANCE.write(dataOutput, new Short((short)node.level));
IntegerConverter.DEFAULT_INSTANCE.write(dataOutput, new Integer(node.number()));
for (Iterator entries = node.entries(); entries.hasNext();)
converter.write(dataOutput, entries.next());
}
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.ORTree#nodeConverter(xxl.core.io.converters.Converter, xxl.core.io.converters.Converter)
*/
public Converter nodeConverter (Converter leafEntryConverter, Converter indexEntryConverter) {
return new NodeConverter(leafEntryConverter, indexEntryConverter);
}
/*********************************************************************/
/* DEBUG FUNCTIONALITY */
/*********************************************************************/
/** Checks if the distances to parents are valid.
*
* @param indexEntry IndexEntry to start at
* @param descriptorList list collecting descriptors
*/
public void checkDistanceToParent (IndexEntry indexEntry, List descriptorList) {
Node parent = (Node)indexEntry.get(true);
Sphere parentSphere = (Sphere)indexEntry.descriptor();
if (parent.level > 0)
for (Iterator entries = parent.entries(); entries.hasNext();) {
IndexEntry next = (IndexEntry)entries.next();
Sphere descriptor = (Sphere)next.descriptor();
descriptorList.add(next.descriptor);
if (descriptor.getDistanceToParent() != parentSphere.centerDistance(descriptor))
System.out.println("Error occured: wrong distance to parent! \n"
+"\tchild: "+descriptor
+"\n\tparent: "+parentSphere
+"\n\tchild.distanceToParent: "+descriptor.getDistanceToParent()
+"\n\tparent.centerDistance(child): "+parentSphere.centerDistance(descriptor)+"\n"
);
checkDistanceToParent(next, descriptorList);
descriptorList.remove(next.descriptor);
}
else {
Iterator entries = parent.entries();
while (entries.hasNext()) {
Sphere descriptor = (Sphere)((LeafEntry)entries.next()).descriptor();
if (descriptor.getDistanceToParent() != parentSphere.centerDistance(descriptor))
System.out.println("Error occured: wrong distance to parent! \n"
+"\tchild: "+descriptor
+"\n\tparent: "+parentSphere
+"\n\tchild.distanceToParent: "+descriptor.getDistanceToParent()
+"\n\tparent.centerDistance(child): "+parentSphere.centerDistance(descriptor)+"\n"
);
}
}
}
/** Checks if the distances to parents are valid in the tree.
*/
public void checkDistanceToParent () {
if (height() > 0)
checkDistanceToParent((IndexEntry)rootEntry(), new LinkedList());
}
/** Returns the Sphere from the given IndexEntry or LeafEntry
*
* @param o IndexEntry or LeafEntry
* @return Sphere
*/
public static Sphere sphereFrom(Object o) {
if(o instanceof MTree.IndexEntry) {
return (Sphere)((IndexEntry)o).descriptor();
} else if(o instanceof MTree.LeafEntry) {
return (Sphere)((LeafEntry)o).descriptor();
}
throw new RuntimeException("Invalid argument");
}
}