/* 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.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import xxl.core.collections.queues.Heap;
import xxl.core.comparators.ComparableComparator;
import xxl.core.comparators.FeatureComparator;
import xxl.core.functions.AbstractFunction;
import xxl.core.util.Distance;
/** A dynamic tree for organizing metric datasets.
*
* For a detailed discussion see
* Caetano Traina Jr., Agma Traina, Bernhard Seeger, Christos Faloutsos:
* "Slim-trees: High Performance Metric Trees Minimizing Overlap Between Nodes",
* EDBT 2000, pp. 51--65, Konstanz, Germany, Mar. 2000
*
* @see Tree
* @see ORTree
* @see MTree
*/
public class SlimTree extends MTree {
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createNode(int)
*/
public Tree.Node createNode (int level) {
return new Node().initialize(level, new ArrayList(20));
}
/** <tt>Node</tt> is the class used to represent leaf- and non-leaf
* nodes of <tt>SlimTree</tt>. Nodes are stored in containers.
*
* @see Tree.Node
* @see ORTree.Node
* @see MTree.Node
*/
public class Node extends MTree.Node {
/** Returns the partition for <tt>i</tt>.
*
* @param i index
* @param partitions partitions to search in
* @return partition for i
*/
protected int getPartition (int i, int [] partitions) {
while (partitions[i]!=i)
i = partitions[i];
return i;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected Tree.Node.SplitInfo split (Stack path) {
SlimTree.Node node = (SlimTree.Node)node(path);
ArrayList nodeEntries = (ArrayList)node.entries;
int minCapacity = node.splitMinNumber();
Object [] edges = new Object[nodeEntries.size()*(nodeEntries.size()-1)/2];
int [] partitions = new int[nodeEntries.size()];
int [] sizes = new int [nodeEntries.size()];
int partitionNumber = nodeEntries.size();
for (int i = 0, index = 0; i<nodeEntries.size(); i++) {
Sphere sphere = sphere(nodeEntries.get(i));
partitions[i] = i;
sizes[i] = 1;
for (int j = 0; j<i; j++)
edges[index++] = new Object[]{new Double(sphere.centerDistance(sphere(nodeEntries.get(j)))), new Integer(i), new Integer(j)};
}
Heap heap = new Heap(edges, new FeatureComparator(new ComparableComparator(),
new AbstractFunction() {
public Object invoke (Object object) {
return ((Object[])object)[0];
}
}
));
heap.open();
while (partitionNumber>2) {
Object [] edge = (Object[])heap.dequeue();
int i = getPartition(((Integer)edge[1]).intValue(), partitions);
int j = getPartition(((Integer)edge[2]).intValue(), partitions);
if (i!=j) {
if (sizes[i]>sizes[j]) {
sizes[i] += sizes[j];
partitions[j] = i;
}
else {
sizes[j] += sizes[i];
partitions[i] = j;
}
partitionNumber--;
}
}
heap.close();
int firstPartition = getPartition(0, partitions), i = 0;
for (Iterator entries = node.entries(); entries.hasNext(); i++) {
Object entry = entries.next();
if (getPartition(i, partitions)!=firstPartition) {
this.entries.add(entry);
entries.remove();
}
}
// ensure that each node contains enough elements
if (node.entries.size() < minCapacity || entries.size() < minCapacity) {
node.entries.addAll(entries);
entries.clear();
return super.split(path);
}
else {
Sphere newSphere = (Sphere)computeDescriptor(node.entries);
if (!path.isEmpty()) {
Object top = path.pop();
newSphere.distanceToParent = newSphere.centerDistance(sphere(indexEntry(path)));
path.push(top);
}
((IndexEntry)indexEntry(path)).descriptor = newSphere;
split = true;
return new SplitInfo(path).initialize(computeDescriptor(entries));
}
}
}
/** Creates a new <tt>SlimTree</tt> using the given distance functions.
*
* @param pointDistance distance function for points
* @param sphereDistance distance function for spheres
*/
public SlimTree (Distance pointDistance, Distance sphereDistance) {
super(pointDistance, sphereDistance);
}
/** Creates a new <tt>SlimTree</tt> using the given distance function for points.
*
* @param pointDistance distance function for points
*/
public SlimTree (Distance pointDistance) {
super(pointDistance);
}
/** Creates a new <tt>SlimTree</tt> using default distance functions.
*/
public SlimTree () {
super();
}
}