
org.khelekore.prtree.LeafBuilder Maven / Gradle / Ivy
package org.khelekore.prtree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/** A builder of internal nodes used during bulk loading of a PR-Tree.
* A PR-Tree is build by building a pseudo R-Tree and grabbing the
* leaf nodes (and then repeating until you have just one root node).
* This class creates the leaf nodes without building the full pseudo tree.
*/
class LeafBuilder {
private final int dimensions;
private final int branchFactor;
public LeafBuilder (int dimensions, int branchFactor) {
this.dimensions = dimensions;
this.branchFactor = branchFactor;
}
public void buildLeafs (Collection extends T> ls,
NodeComparators comparators,
NodeFactory nf,
List leafNodes) {
List> nodes = new ArrayList<> (ls.size ());
for (T t : ls)
nodes.add (new NodeUsage<> (t, 1));
Circle> getters = new Circle<> (dimensions * 2);
for (int i = 0; i < dimensions; i++)
addGetterAndSplitter (nodes, comparators.getMinComparator (i),
getters);
for (int i = 0; i < dimensions; i++)
addGetterAndSplitter (nodes, comparators.getMaxComparator (i),
getters);
getLeafs (1, ls.size (), getters, nf, leafNodes);
}
private void addGetterAndSplitter (List> nodes,
Comparator tcomp,
Circle> getters) {
Comparator> comp = new NodeUsageComparator<> (tcomp);
Collections.sort (nodes, comp);
List> sortedNodes = new ArrayList<> (nodes);
getters.add (new Noder (sortedNodes));
}
private void getLeafs (int id, int totalNumberOfElements,
Circle> getters,
NodeFactory nf, List leafNodes) {
List partitionsToExpand = new ArrayList<> ();
int[] pos = new int[2 * dimensions];
partitionsToExpand.add (new Partition (id, totalNumberOfElements, pos));
while (!partitionsToExpand.isEmpty ()) {
Partition p = partitionsToExpand.remove (0);
// Get the extreme nodes
getters.reset ();
for (int i = 0; i < getters.getNumElements (); i++) {
int nodesToGet = Math.min (p.numElementsLeft, branchFactor);
if (nodesToGet == 0)
break;
Noder noder = getters.getNext ();
leafNodes.add (noder.getNextNode (p, i, nodesToGet, nf));
p.numElementsLeft -= nodesToGet;
}
// Split the rest of the elements
if (p.numElementsLeft > 0) {
int splitPos = getSplitPos (p.id) % getters.getNumElements ();
Noder s = getters.get (splitPos);
s.split (p, splitPos, p.numElementsLeft,
p.id, 2 * p.id, 2 * p.id + 1,
partitionsToExpand);
}
}
}
private int getSplitPos (int n) {
// id is generated by the power of twos so get biggest n where 2 ^ n < id
// id: 1 -> splitPos 0, id: 2 -> 1, 3 -> 1, 4 -> 2, 5 -> 2, 7 -> 2, 8 -> 3
int splitPos = 0;
while (n >= 2) {
n >>= 1;
splitPos++;
}
return splitPos;
}
private static class NodeUsageComparator
implements Comparator> {
private Comparator sorter;
public NodeUsageComparator (Comparator sorter) {
this.sorter = sorter;
}
public int compare (NodeUsage n1, NodeUsage n2) {
return sorter.compare (n1.getData (), n2.getData ());
}
}
private static class Noder {
private final List> data;
private Noder (List> data) {
this.data = data;
}
/** Get the next node.
* @param p the Partition to get a node from
* @param gi the current getter index
* @param maxObjects use at most this many objects
* @param nf the NodeFactory used to create the nodes
* @return the next node
*/
private N getNextNode (Partition p, int gi, int maxObjects,
NodeFactory nf) {
Object nodeData[] = new Object[maxObjects];
int s = data.size ();
for (int i = 0; i < maxObjects; i++) {
while (p.currentPositions[gi] < s &&
isUsedNode (p, p.currentPositions[gi])) {
p.currentPositions[gi]++;
}
NodeUsage nu = data.set (p.currentPositions[gi], null);
nodeData[i] = nu.getData ();
nu.use ();
}
for (int i = 0; i < nodeData.length; i++) {
if (nodeData[i] == null) {
for (int j = 0; j < data.size (); j++)
System.err.println (j + ": " + data.get (j));
throw new NullPointerException ("Null data found at: " + i);
}
}
return nf.create (nodeData);
}
private boolean isUsedNode (Partition p, int pos) {
NodeUsage nu = data.get (pos);
return nu == null || nu.isUsed () || nu.getOwner () != p.id;
}
private void split (Partition p, int gi,
int nodesToMark, int fromId, int toId1, int toId2,
List partitionsToExpand) {
int sizePart2 = nodesToMark / 2;
int sizePart1 = nodesToMark - sizePart2;
int startPos = p.currentPositions[gi];
int startPos2 = markPart (sizePart1, fromId, toId1, startPos);
markPart (sizePart2, fromId, toId2, startPos2);
partitionsToExpand.add (0, new Partition (toId1, sizePart1,
p.currentPositions));
int[] pos = p.currentPositions.clone ();
pos[gi] = startPos2;
partitionsToExpand.add (1, new Partition (toId2, sizePart2, pos));
}
private int markPart (int numToMark, int fromId, int toId, int startPos) {
NodeUsage nu;
while (numToMark > 0) {
while ((nu = data.get (startPos)) == null ||
nu.getOwner () != fromId)
startPos++;
nu.changeOwner (toId);
numToMark--;
}
return startPos;
}
}
private static class Partition {
private final int id;
private int numElementsLeft;
private int[] currentPositions;
public Partition (int id, int numElementsLeft, int[] currentPositions) {
this.id = id;
this.numElementsLeft = numElementsLeft;
this.currentPositions = currentPositions;
}
@Override public String toString () {
return getClass ().getSimpleName () + "{id: " + id +
", numElementsLeft: " + numElementsLeft +
", currentPositions: " + Arrays.toString (currentPositions) +
"}";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy