All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.khelekore.prtree.LeafBuilder Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
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 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