Difference between revisions of "User:Chase-san/Kd-Tree"

From Robowiki
Jump to navigation Jump to search
(Reduced the source some.)
(Updated, now with nearest N neighbors)
Line 1: Line 1:
 
[[Category:Code Snippets|Kd-Tree]]
 
[[Category:Code Snippets|Kd-Tree]]
This is my version of the [[Kd-Tree]], I admit it has gotten a bit large, and it could be cut down a great deal, I will add a K nearest neighbors implimentation soon enough. however it does have a singular copy, which will find the single closest neighbor (not as useful).
+
Everyone and their brother has one of these now, me and Simonton started it, but I was to inexperienced to get anything written, I took an hour or two to rewrite it today, because I am no longer completely terrible at these things. So here is mine if you care to see it.
 
 
[[User:Simonton|Simonton]] and I worked on kd-trees at about the same time, however I was less experienced and it turns out the whole time I had nothing but a very elusive off by 1 problem. I recently rebuilt my kd-tree into this. Which unhooks the points from the branches to speed things up, it also uses buckets, which are great fun.
 
 
 
Also unlike most others mine is fairly modular and includes a range search (good for those old fashioned [[Pattern Matching|pattern matchers]]!!).
 
 
 
Please forgive the incomplete documentation. I have compacted the source down to just 3 files instead of 6 (I just internalized the Node,Branch, and Bucket classes into KDTreeB). I am adding that multiple Nearest Neightbor function later on, since it seems to be how most people use it.
 
  
 
=== KDTreeB ===
 
=== KDTreeB ===
 
<pre>
 
<pre>
 
package org.csdgn.util;
 
package org.csdgn.util;
 +
 +
import java.util.Comparator;
  
 
public class KDTreeB {
 
public class KDTreeB {
public final static int DEFAULT_BUCKET_SIZE = 100;
+
public final static int DEFAULT_BUCKET_SIZE = 200;
protected int bucketSize;
+
protected NodeKD root;
 
protected int dimensions;
 
protected int dimensions;
protected NodeKD root;
+
protected int bucket_size;
protected PointKD keys[];
+
protected long size = 0;
protected int size;
+
 
+
/**
 +
* This creates a KDTreeB with the given number of dimensions and
 +
* the default bucket size.
 +
* @param dimensions
 +
*/
 +
public KDTreeB(int dimensions) {
 +
this(dimensions,DEFAULT_BUCKET_SIZE);
 +
}
 +
/**
 +
* This creates a KDTreeB with the given number of dimensions and
 +
* the given bucket size.
 +
* @param dimensions
 +
* @param bucket_size
 +
*/
 +
public KDTreeB(int dimensions, int bucket_size) {
 +
this.dimensions = dimensions;
 +
this.bucket_size = bucket_size;
 +
root = new BucketKD(this);
 +
}
 +
 
/**
 
/**
* Initializes a new KDTreeB with a number of dimensions.
+
* Adds the given point to the tree. Uses a recursive algorithm.
*
+
* @param point
* @param dim
 
*            - Dimensions
 
 
*/
 
*/
public KDTreeB(int dim) {
+
public void add(PointKD point) {
this(dim, DEFAULT_BUCKET_SIZE);
+
root.add(point);
 
}
 
}
 
+
 
/**
 
/**
* Initializes a new KDTreeB with a number of dimensions and bucket size
+
* Returns the nearest neighbor to the given point.
*
+
* @param point - PointKD to find the nearest to.
* @param dim
+
* @return - The nearest PointKD to
*            - Dimensions
 
* @param buckets
 
*            - BucketKD Size
 
 
*/
 
*/
public KDTreeB(int dim, int buckets) {
+
public PointKD getNearestNeighbor(PointKD point) {
if(dim < 1)
+
return root.nearest(point);
System.err.println("Dimensions < 1: Undefined Behavior may occur.");
 
if(buckets < 2)
 
System.err.println("Bucket Size < 2: Undefined Behavior may occur.");
 
 
bucketSize = buckets;
 
dimensions = dim;
 
keys = new PointKD[buckets];
 
size = 0;
 
 
}
 
}
 
+
 
/**
 
/**
* Adds a new PointKD to the Tree, uses a recursive algorithm.
+
* Returns the nearest num neighbors to the given point.
*  
+
* @param point - PointKD to find the nearest to.
* @param k
+
* @param num - The number of points to find.
*           - Point to add
+
* @return - The nearest PointKD's to point.
 
*/
 
*/
public void add(PointKD k) {
+
public PointKD[] getNearestNeighbors(final PointKD point, int num) {
if (null == root) {
+
if(num == 1) {
root = new BucketKD(this);
+
return new PointKD[] { getNearestNeighbor(point) };
 
}
 
}
if (size >= keys.length) {
+
PriorityDeque<PointKD> queue = new PriorityDeque<PointKD>(
// this is basically what a vector does, I am
+
new Comparator<PointKD>(){
// just removing the overhead of using a vector
+
@Override
PointKD tmp[] = new PointKD[keys.length * 2];
+
public int compare(PointKD o1, PointKD o2) {
System.arraycopy(keys, 0, tmp, 0, size);
+
double dist0 = point.distance(o1);
keys = tmp;
+
double dist1 = point.distance(o2);
 +
return (dist0-dist1 < 0) ? -1 : 1;
 +
}
 +
},num);
 +
 +
root.nearestn(queue,point);
 +
 +
PointKD[] array = new PointKD[num];
 +
Object[] obj = queue.toArray();
 +
for(int i=0; i<num; ++i) {
 +
array[i] = (PointKD)obj[i];
 
}
 
}
keys[size++] = k;
+
root.add(k);
+
return array;
 
}
 
}
 
+
 
/**
 
/**
* Finds the approximate nearest neighbor to PointKD k. This uses a
+
* Returns all PointKD within a certain RectangleKD.
* recursive algorithm.
+
* @param rect - area to get PointKD from
*
+
* @return - All PointKD within rect.
* @param k
 
* @return
 
 
*/
 
*/
public PointKD getApproxNN(PointKD k) {
+
public PointKD[] getRange(RectangleKD rect) {
if (null == root)
+
return root.range(rect);
return null;
 
return root.approx(k);
 
 
}
 
}
  
 
/**
 
/**
* Finds the nearest neighbor to PointKD k. Uses an exhastive search
+
* Returns all PointKD within a certain range defined by an upper and lower PointKD.
* method.
+
* @param low - lower bounds of area
* @param k -  
+
* @param high - upper bounds of area
* @return
+
* @return - All PointKD between low and high.
 
*/
 
*/
public PointKD getNN(PointKD k) {
 
if (null == root)
 
return null;
 
return root.nearest(k);
 
}
 
 
public PointKD[] getRange(RectKD r) {
 
if (null == root)
 
return new PointKD[0];
 
return root.range(r);
 
}
 
 
 
public PointKD[] getRange(PointKD low, PointKD high) {
 
public PointKD[] getRange(PointKD low, PointKD high) {
return this.getRange(new RectKD(low, high));
+
return this.getRange(new RectangleKD(low, high));
 
}
 
}
 
 
 
public abstract class NodeKD {
+
protected abstract class NodeKD {
protected KDTreeB ref;
+
protected KDTreeB owner;
 
protected BranchKD parent;
 
protected BranchKD parent;
protected int depth;
+
protected RectangleKD rect;
protected RectKD rect;
+
protected int depth = 0;
 
+
 
protected abstract void add(PointKD k);
 
protected abstract void add(PointKD k);
protected abstract PointKD approx(PointKD k);
 
 
protected abstract PointKD nearest(PointKD k);
 
protected abstract PointKD nearest(PointKD k);
protected abstract PointKD[] range(RectKD r);
+
protected abstract void nearestn(PriorityDeque<PointKD> queue, PointKD k);
 +
protected abstract PointKD[] range(RectangleKD r);
 
}
 
}
+
protected class BucketKD extends NodeKD {
public class BucketKD extends NodeKD {
 
 
protected PointKD bucket[];
 
protected PointKD bucket[];
 
protected int current;
 
protected int current;
 
+
protected BucketKD(KDTreeB reference) {
+
public BucketKD(KDTreeB owner) {
ref = reference;
+
this.owner = owner;
ref.root = this;
+
bucket = new PointKD[owner.bucket_size];
bucket = new PointKD[ref.bucketSize];
+
rect = new RectangleKD();
 
parent = null;
 
parent = null;
rect = new RectKD();
+
}
depth = 0;
+
public BucketKD(BranchKD branch) {
 +
this(branch.owner);
 +
parent = branch;
 +
depth = branch.depth + 1;
 
}
 
}
 
 
protected BucketKD(BranchKD p) {
+
@Override
ref = p.ref;
+
protected void add(PointKD point) {
bucket = new PointKD[ref.bucketSize];
+
if(current >= bucket.length) {
parent = p;
+
//Split the bucket into a branch
rect = new RectKD();
+
BranchKD branch = new BranchKD(this);
depth = p.depth + 1;
+
if(parent == null) {
 +
owner.root = branch;
 +
} else {
 +
if(parent.isLeft(this)) {
 +
parent.left = branch;
 +
} else {
 +
parent.right = branch;
 +
}
 +
}
 +
branch.add(point);
 +
bucket = null;
 +
current = 0;
 +
return;
 +
}
 +
bucket[current++] = point;
 +
rect.expand(point);
 
}
 
}
  
protected PointKD approx(PointKD k) {
+
@Override
 +
protected PointKD nearest(PointKD k) {
 
double nearestDist = Double.POSITIVE_INFINITY;
 
double nearestDist = Double.POSITIVE_INFINITY;
 
int nearest = 0;
 
int nearest = 0;
Line 152: Line 165:
 
return bucket[nearest];
 
return bucket[nearest];
 
}
 
}
 
+
protected PointKD nearest(PointKD k) {
+
@Override
return approx(k);
+
protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) {
}
+
for(int i = 0; i < current; i++) {
 
+
queue.offer(bucket[i]);
protected void add(PointKD k) {
 
if (current >= ref.bucketSize) {
 
BranchKD b = null;
 
if (null == parent)
 
b = new BranchKD(ref);
 
else
 
b = parent.extend(this);
 
 
 
int dim = b.depth % ref.dimensions;
 
double total = 0;
 
for (int i = 0; i < current; i++) {
 
total += bucket[i].array[dim];
 
}
 
b.slice = total / current;
 
for (int i = 0; i < current; i++) {
 
b.add(bucket[i]);
 
}
 
b.add(k);
 
bucket = null;
 
parent = null;
 
current = 0;
 
return;
 
 
}
 
}
rect.add(k);
 
bucket[current++] = k;
 
 
}
 
}
  
protected PointKD[] range(RectKD r) {
+
@Override
 +
protected PointKD[] range(RectangleKD r) {
 
PointKD[] tmp = new PointKD[current];
 
PointKD[] tmp = new PointKD[current];
 
int n = 0;
 
int n = 0;
Line 196: Line 186:
 
return tmp2;
 
return tmp2;
 
}
 
}
 +
 
}
 
}
+
protected class BranchKD extends NodeKD {
public class BranchKD extends NodeKD {
 
 
protected NodeKD left, right;
 
protected NodeKD left, right;
 
protected double slice;
 
protected double slice;
 
+
protected int dim;
protected BranchKD(KDTreeB reference) {
+
 +
public BranchKD(BucketKD k) {
 +
owner = k.owner;
 +
parent = k.parent;
 
slice = 0;
 
slice = 0;
ref = reference;
+
rect = k.rect;
rect = new RectKD();
+
depth = k.depth;
depth = 0;
 
ref.root = this;
 
 
left = new BucketKD(this);
 
left = new BucketKD(this);
 
right = new BucketKD(this);
 
right = new BucketKD(this);
 +
 +
dim = depth % owner.dimensions;
 +
double total = 0;
 +
for (int i = 0; i < k.current; i++) {
 +
total += k.bucket[i].internal[dim];
 +
}
 +
slice = total / k.current;
 +
for (int i = 0; i < k.current; i++)
 +
add(k.bucket[i]);
 
}
 
}
protected BranchKD(BranchKD b) {
+
ref = b.ref;
+
@Override
parent = b;
 
slice = 0;
 
rect = new RectKD();
 
depth = b.depth + 1;
 
left = new BucketKD(this);
 
right = new BucketKD(this);
 
}
 
 
 
 
protected void add(PointKD k) {
 
protected void add(PointKD k) {
rect.add(k);
+
if(k.internal[dim] > slice) {
int dim = depth % ref.dimensions;
 
if (k.array[dim] > slice) {
 
 
right.add(k);
 
right.add(k);
 
} else {
 
} else {
Line 231: Line 221:
 
}
 
}
  
protected PointKD approx(PointKD k) {
+
@Override
int dim = depth % ref.dimensions;
 
if (k.array[dim] > slice)
 
return right.approx(k);
 
return left.approx(k);
 
}
 
 
 
 
protected PointKD nearest(PointKD k) {
 
protected PointKD nearest(PointKD k) {
int dim = depth % ref.dimensions;
 
 
PointKD near = null;
 
PointKD near = null;
if (k.array[dim] > slice) {
+
if (k.internal[dim] > slice) {
 
near = right.nearest(k);
 
near = right.nearest(k);
 
double t = near.distanceSq(k);
 
double t = near.distanceSq(k);
Line 260: Line 243:
 
}
 
}
 
}
 
}
 
 
return near;
 
return near;
 +
}
 +
 +
@Override
 +
protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) {
 +
//TODO
 +
if(k.internal[dim] > slice) {
 +
right.nearestn(queue, k);
 +
double t = queue.peekBottom().distanceSq(k);
 +
if(k.distanceSq(left.rect.getNearest(k)) < t) {
 +
left.nearestn(queue, k);
 +
}
 +
} else {
 +
left.nearestn(queue, k);
 +
double t = queue.peekBottom().distanceSq(k);
 +
if(k.distanceSq(right.rect.getNearest(k)) < t) {
 +
right.nearestn(queue, k);
 +
}
 +
}
 
}
 
}
  
protected PointKD[] range(RectKD r) {
+
@Override
 +
protected PointKD[] range(RectangleKD r) {
 
PointKD[] tmp = new PointKD[0];
 
PointKD[] tmp = new PointKD[0];
 
if (r.intersects(left.rect)) {
 
if (r.intersects(left.rect)) {
Line 284: Line 285:
 
return tmp;
 
return tmp;
 
}
 
}
 
+
protected BranchKD extend(BucketKD b) {
+
protected boolean isLeft(BucketKD kd) {
if (b == left) {
+
if(left == kd) return true;
left = null;
+
return false;
left = new BranchKD(this);
 
return (BranchKD) left;
 
} else if (b == right) {
 
right = null;
 
right = new BranchKD(this);
 
return (BranchKD) right;
 
}
 
return null;
 
 
}
 
}
 
}
 
}
Line 304: Line 297:
 
<pre>
 
<pre>
 
package org.csdgn.util;
 
package org.csdgn.util;
 +
 +
import java.io.Serializable;
  
 
/**
 
/**
  * A K-Dimensional Point, used in conjunction with the KD-Tree multidimensional
+
  * PointKD class is a class that wraps a double array, for use in K-Dimensional structures.
  * data structure.
+
  * This wrapping is done to improve readability and modularity. Often used to define a
  *  
+
  * point in k-dimensional space.
* @author Robert Maupin
 
 
  *  
 
  *  
 +
* @author Chase
 
  */
 
  */
public class PointKD {
+
public class PointKD implements Serializable {
protected double[] array;
+
private static final long serialVersionUID = -841162798668123755L;
 
+
protected double[] internal;
 +
 
/**
 
/**
* Constructor defining the number of dimensions to use in this Point;
+
* Constructor
*
+
* @param dimensions - Number of dimensions for this PointKD.
* @param dimensions
 
*            - The number of dimensions in this point
 
 
*/
 
*/
 
public PointKD(int dimensions) {
 
public PointKD(int dimensions) {
array = new double[dimensions];
+
internal = new double[dimensions];
 +
}
 +
 +
/**
 +
* Constructor
 +
* @param array - An array to use for the internal array of this PointKD.
 +
*/
 +
public PointKD(double[] array) {
 +
internal = array.clone();
 
}
 
}
 
+
 
/**
 
/**
* Creates a PointKD from an array.
+
* Constructor
*
+
* @param point - PointKD to clone.
* @param pos
 
*            - An array of Numbers
 
 
*/
 
*/
public PointKD(double[] pos) {
+
public PointKD(PointKD point) {
array = pos.clone();
+
this.internal = point.internal.clone();
// array = new double[pos.length];
 
// System.arraycopy(pos, 0, array, 0, array.length);
 
 
}
 
}
 
+
 +
 
/**
 
/**
* Creates a copy of the selected point.
+
* Returns array.
*
+
* @return The internal array of this pointKD, changes made to this will effect this PointKD.
* @param p
 
*            - A point to copy
 
 
*/
 
*/
public PointKD(PointKD p) {
+
public double[] get() {
// array = new double[p.array.length];
+
return internal;
setPoint(p);
 
 
}
 
}
 
+
 
/**
 
/**
* Sets the coordinates of this point to be the same as the selected point.
+
* Sets the location of this point.
*
+
* @param point - PointKD to copy.
* @param p
 
*            - A point to copy
 
 
*/
 
*/
public void setPoint(PointKD p) {
+
public void set(PointKD point) {
this.array = p.array.clone();
+
this.internal = point.internal.clone();
// System.arraycopy(p.array, 0, array, 0, array.length);
 
 
}
 
}
 
+
 
/**
 
/**
* Returns the number of dimensions this point has.
+
* Sets the location of this point.
*
+
* @param array - Array to copy.
* @return the dimensions
 
 
*/
 
*/
public int getDimensions() {
+
public void set(double[] array) {
return array.length;
+
this.internal = array.clone();
 
}
 
}
 
+
 
/**
 
/**
* Returns the coordinate at dimension <b>i</b>.
+
* Sets the value at dimension index
*  
+
* @param index - Index
* @param i
+
* @param value - Value to set
*            - The dimension to retrieve.
 
* @return The coordinate at i.
 
 
*/
 
*/
public double getCoordinate(int i) {
+
public void set(int index, double value) {
return array[i];
+
internal[index] = value;
 
}
 
}
 
+
 
/**
 
/**
* Sets the coordinate to <b>k</b> at dimension <b>i</b>.
+
* @return number of dimensions
*
 
* @param i
 
*            - the dimension to set
 
* @param k
 
*            - the value to set the dimension to
 
 
*/
 
*/
public void setCoordinate(int i, double k) {
+
public int size() {
array[i] = k;
+
return internal.length;
 
}
 
}
 
+
 
/**
 
/**
 
* Compares this to a selected point and returns the euclidean distance
 
* Compares this to a selected point and returns the euclidean distance
Line 400: Line 385:
 
*/
 
*/
 
public double distance(PointKD p) {
 
public double distance(PointKD p) {
return distance(this, p);
+
return distance(this.internal, p.internal);
 
}
 
}
  
Line 412: Line 397:
 
*/
 
*/
 
public double distanceSq(PointKD p) {
 
public double distanceSq(PointKD p) {
return distanceSq(this, p);
+
return distanceSq(this.internal, p.internal);
 +
}
 +
 +
/**
 +
* Clones this point.
 +
*/
 +
public PointKD clone() {
 +
return new PointKD(internal);
 
}
 
}
 
+
 
/**
 
/**
 
* Prints the class name and the point coordinates.
 
* Prints the class name and the point coordinates.
 
*/
 
*/
 
public String toString() {
 
public String toString() {
String output = getClass().getName() + "[";
+
String output = getClass().getSimpleName() + "[";
for (int i = 0; i < array.length; i++) {
+
for (int i = 0; i < internal.length; ++i) {
 
if (0 != i)
 
if (0 != i)
 
output += ",";
 
output += ",";
output += array[i];
+
output += internal[i];
 
}
 
}
 
return output + "]";
 
return output + "]";
 
}
 
}
 
+
 
/**
 
/**
* @return a distinct copy of this object
+
* Compares arrays of double and returns the euclidean distance
*/
+
* between them.
public Object clone() {
 
return new PointKD(this);
 
}
 
 
 
/**
 
* Compares two Points and returns the euclidean distance between them.
 
 
*  
 
*  
* @param a
+
* @param a - The first set of numbers
*            - The first set of numbers
+
* @param b - The second set of numbers
* @param b
+
* @return The distance squared between <b>a</b> and <b>b</b>.
*            - The second set of numbers
 
* @return The distance between <b>a</b> and <b>b</b>.
 
*/
 
public static final double distance(PointKD a, PointKD b) {
 
return distance(a.array, b.array);
 
}
 
 
 
/**
 
* Compares two Points and returns the squared euclidean distance between
 
* them.
 
*
 
* @param a
 
*            - The first set of numbers
 
* @param b
 
*            - The second set of numbers
 
* @return The distance between <b>a</b> and <b>b</b>.
 
*/
 
public static final double distanceSq(PointKD a, PointKD b) {
 
return distanceSq(a.array, b.array);
 
}
 
 
 
/**
 
* Compares arrays of double and returns the euclidean distance between
 
* them.
 
*
 
* @param a
 
*            - The first set of numbers
 
* @param b
 
*            - The second set of numbers
 
* @return The distance between <b>a</b> and <b>b</b>.
 
 
*/
 
*/
 
public static final double distance(double[] a, double[] b) {
 
public static final double distance(double[] a, double[] b) {
return Math.sqrt(distanceSq(a, b));
+
double total = 0;
 +
for (int i = 0; i < a.length; ++i)
 +
total += (b[i] - a[i]) * (b[i] - a[i]);
 +
return Math.sqrt(total);
 
}
 
}
 
+
 
/**
 
/**
 
* Compares arrays of double and returns the squared euclidean distance
 
* Compares arrays of double and returns the squared euclidean distance
 
* between them.
 
* between them.
 
*  
 
*  
* @param a
+
* @param a - The first set of numbers
*            - The first set of numbers
+
* @param b - The second set of numbers
* @param b
 
*            - The second set of numbers
 
 
* @return The distance squared between <b>a</b> and <b>b</b>.
 
* @return The distance squared between <b>a</b> and <b>b</b>.
 
*/
 
*/
 
public static final double distanceSq(double[] a, double[] b) {
 
public static final double distanceSq(double[] a, double[] b) {
if (a.length != b.length || a.length < 1)
 
return -1;
 
 
double total = 0;
 
double total = 0;
for (int i = 0; i < a.length; i++)
+
for (int i = 0; i < a.length; ++i)
 
total += (b[i] - a[i]) * (b[i] - a[i]);
 
total += (b[i] - a[i]) * (b[i] - a[i]);
 
return total;
 
return total;
Line 497: Line 452:
 
</pre>
 
</pre>
  
=== RectKD ===
+
=== RectangleKD ===
 
<pre>
 
<pre>
 
package org.csdgn.util;
 
package org.csdgn.util;
  
/**
+
import java.io.Serializable;
* A K-Dimensional Hyper Rectangle, used in conjunction with the KD-Tree
 
* multidimensional data structure.
 
*
 
* @author Robert Maupin
 
*
 
*/
 
public class RectKD {
 
PointKD upper;
 
PointKD lower;
 
  
 +
public class RectangleKD implements Serializable {
 +
private static final long serialVersionUID = 524388821816648020L;
 +
protected PointKD upper, lower;
 
/**
 
/**
* Creates an empty RectKD
+
* Creates an empty RectangleKD
 
*/
 
*/
public RectKD() {
+
public RectangleKD() {
 
upper = null;
 
upper = null;
 
lower = null;
 
lower = null;
 
}
 
}
 
+
/**
public RectKD(PointKD s) {
+
* Creates a RectangleKD from two points.
this(s, s);
+
* @param lower
 +
* @param upper
 +
*/
 +
public RectangleKD(PointKD lower, PointKD upper) {
 +
this.lower = lower.clone();
 +
this.upper = upper.clone();
 
}
 
}
 
+
public RectKD(PointKD l, PointKD u) {
+
/**
this();
+
* Expands this RectangleKD to include point.
if (l.array.length == u.array.length) {
+
* @param point - Point used to expand this Rectangle
upper = new PointKD(u);
+
*/
lower = new PointKD(l);
+
public void expand(PointKD point) {
 +
if(upper == null) {
 +
upper = point.clone();
 +
lower = point.clone();
 +
return;
 +
}
 +
for(int i=0; i<point.size(); ++i) {
 +
if(upper.internal[i] < point.internal[i])
 +
upper.internal[i] = point.internal[i];
 +
if(lower.internal[i] > point.internal[i])
 +
lower.internal[i] = point.internal[i];
 
}
 
}
 
}
 
}
 
+
public void add(PointKD p) {
+
/**
if (upper == null) {
+
* Checks if this RectangleKD contains the given point.
upper = new PointKD(p);
+
* @param point - PointKD to check.
lower = new PointKD(p);
+
* @return true - if this rectangle contains the given point.
return;
+
*/
 +
public boolean contains(PointKD point) {
 +
if(upper == null) return false;
 +
for(int i=0; i<point.size(); ++i) {
 +
if(point.internal[i] > upper.internal[i] ||
 +
point.internal[i] < lower.internal[i])  
 +
return false;
 
}
 
}
 
+
return true;
for (int i = 0; i < upper.array.length; i++) {
+
}
if (p.array[i] > upper.array[i])
+
upper.array[i] = p.array[i];
+
/**
if (p.array[i] < lower.array[i])
+
* Checks if this table intersects the given table.
lower.array[i] = p.array[i];
+
* @param rectangle - The rectangle to check intersection with.
 +
* @return true - if this rectangle intersects the given rectangle.
 +
*/
 +
public boolean intersects(RectangleKD rectangle) {
 +
if(rectangle.upper == null || upper == null) return false;
 +
for(int i=0; i<upper.size(); ++i) {
 +
if(rectangle.upper.internal[i] < lower.internal[i]
 +
|| rectangle.lower.internal[i] > upper.internal[i]) return false;
 
}
 
}
 +
return true;
 
}
 
}
 
+
public boolean contains(PointKD p) {
+
/**
if (upper == null)
+
* Gets the nearest point in this RectangleKD to the given point
return false;
+
* @param point - PointKD to get the nearest point to.
if (upper.array.length != p.array.length)
+
* @return the nearest point in this RectangleKD to the given point.
return false;
+
*/
boolean inside = true;
+
public PointKD getNearest(PointKD point) {
for (int i = 0; i < upper.array.length; i++) {
+
if(upper == null) return null;
if (p.array[i] > upper.array[i])
+
if(contains(point)) return point; //This check may not be needed.
inside = false;
+
PointKD nearest = new PointKD(point);
if (p.array[i] < lower.array[i])
+
for(int i = 0; i < upper.size(); ++i) {
inside = false;
+
if(nearest.internal[i] > upper.internal[i])
 +
nearest.internal[i] = upper.internal[i];
 +
if(nearest.internal[i] < lower.internal[i])
 +
nearest.internal[i] = lower.internal[i];
 
}
 
}
return inside;
+
return nearest;
 
}
 
}
 +
}
 +
</pre>
  
public boolean intersects(RectKD r) {
+
=== PriorityDeque ===
boolean check = false;
+
<pre>
 +
package org.csdgn.util;
  
if (null == r)
+
import java.util.Comparator;
return false;
 
if (null == r.lower)
 
return false;
 
if (null == r.upper)
 
return false;
 
if (r.upper.array.length != upper.array.length)
 
return false;
 
  
int len = upper.array.length;
+
/**
for (int i = 0; i < len; i++) {
+
* This is a home made PriorityDeque with maximum size limiter, and
if (upper.array[i] < r.lower.array[i])
+
* comparator or natural ordering selection.
check = true;
+
*
if (lower.array[i] > r.upper.array[i])
+
* @author Chase
check = true;
+
* @param <E>
 +
*/
 +
public class PriorityDeque<E> {
 +
private class Item {
 +
public Item down, up;
 +
public E obj;
 +
public Item(E item) {
 +
obj = item;
 +
down = up = null;
 +
}
 +
}
 +
private final Comparator<? super E> comparator;
 +
private Item bottom, top;
 +
private int maximum_size;
 +
private int size;
 +
 +
/**
 +
* Generic Constructor
 +
*/
 +
public PriorityDeque() {
 +
this(null,-1);
 +
}
 +
/**
 +
* Constructor with a defined maximum number of entries
 +
* @param maximum
 +
*/
 +
public PriorityDeque(int maximum) {
 +
this(null,maximum);
 +
}
 +
/**
 +
* Constructor with a defined comparator and an unlimited number of items.
 +
* @param comp
 +
*/
 +
public PriorityDeque(Comparator<? super E> comp) {
 +
this(comp,-1);
 +
}
 +
/**
 +
* Constructor with a defined conparator and limited number of items.
 +
* @param comp
 +
* @param maximum
 +
*/
 +
public PriorityDeque(Comparator<? super E> comp, int maximum) {
 +
comparator = comp;
 +
maximum_size = maximum;
 +
bottom = top = null;
 +
size = 0;
 +
}
 +
/**
 +
* Adds an item to this deque.
 +
* @param value - item to add
 +
*/
 +
public void offer(E value) {
 +
//System.err.println("Offering: " + value.toString());
 +
if(bottom == null) {
 +
//System.err.println("-Is first item.");
 +
bottom = top = new Item(value);
 +
return;
 +
}
 +
//do ordering etc
 +
if(comparator != null) {
 +
//System.err.println("-Comparator.");
 +
offerComparator(value);
 +
} else {
 +
//System.err.println("-Natural.");
 +
offerNatural(value);
 +
}
 +
}
 +
 +
/**
 +
* Removes and returns an item from the bottom of the list (lowest value)
 +
* @return the lowest value (bottom)
 +
*/
 +
public E pollBottom() {
 +
if(bottom == null) return null;
 +
Item tmp = bottom;
 +
if(bottom == top) bottom = top = null;
 +
else {
 +
bottom = bottom.up;
 +
bottom.down = null;
 +
tmp.up = null;
 +
}
 +
--size;
 +
return tmp.obj;
 +
}
 +
/**
 +
* Returns but does not remove the item from the bottom of the list.
 +
* @return the lowest value (bottom)
 +
*/
 +
public E peekBottom() {
 +
if(bottom == null) return null;
 +
return bottom.obj;
 +
}
 +
/**
 +
* Removes and returns an item from the top of the list (highest value).
 +
* @return the highest value (top)
 +
*/
 +
public E pollTop() {
 +
if(top == null) return null;
 +
Item tmp = top;
 +
if(bottom == top) bottom = top = null;
 +
else {
 +
top = top.down;
 +
top.up = null;
 +
tmp.down = null;
 +
}
 +
--size;
 +
return tmp.obj;
 +
}
 +
/**
 +
* Returns but does not remove the item from the top of the list (highest value).
 +
* @return the highest value (top)
 +
*/
 +
public E peekTop() {
 +
if(top == null) return null;
 +
return top.obj;
 +
}
 +
/**
 +
* This is a generic toArray, returns a non-castable Object
 +
* array with all the items in this deque.
 +
* @return an Object array with everything in this deque.
 +
*/
 +
public Object[] toArray() {
 +
Object array[] = new Object[size+1];
 +
Item current = bottom;
 +
int i = 0;
 +
while(current != null) {
 +
array[i++] = current.obj;
 +
current = current.up;
 +
}
 +
return array;
 +
}
 +
/**
 +
* Add/sorts a value using the comparator
 +
* @param value
 +
*/
 +
private void offerComparator(E value) {
 +
if(maximum_size > 0 && size >= maximum_size) {
 +
if(comparator.compare(value, top.obj) >= 0) {
 +
return;
 +
}
 +
}
 +
Item nItem = new Item(value);
 +
if(comparator.compare(value,bottom.obj) < 0) {
 +
//less than the bottom, put on the bottom
 +
nItem.up = bottom;
 +
bottom.down = nItem;
 +
bottom = nItem;
 +
++size;
 +
if(maximum_size > 0 && size > maximum_size) {
 +
pollTop();
 +
}
 +
return;
 +
}
 +
 +
//start at the bottom
 +
Item current = bottom;
 +
while(current != null) {
 +
if(comparator.compare(value,current.obj) < 0) {
 +
nItem.up = current;
 +
nItem.down = current.down;
 +
current.down.up = nItem;
 +
current.down = nItem;
 +
++size;
 +
if(maximum_size > 0 && size > maximum_size) {
 +
pollTop();
 +
}
 +
return;
 +
}
 +
current = current.up;
 +
}
 +
 +
//else put it on top
 +
nItem.down = top;
 +
top.up = nItem;
 +
top = nItem;
 +
++size;
 +
if(maximum_size > 0 && size > maximum_size) {
 +
pollTop();
 
}
 
}
 
return !check;
 
 
}
 
}
 
+
public PointKD getNearest(PointKD p) {
+
/**
if (upper == null)
+
* Add/sorts a value using the natural order
return null;
+
* @param value
if (upper.array.length != p.array.length)
+
*/
return null;
+
@SuppressWarnings("unchecked")
PointKD near = new PointKD(p);
+
private void offerNatural(E value) {
for (int i = 0; i < upper.array.length; i++) {
+
Comparable<? super E> key = (Comparable<? super E>)value;
near.array[i] = p.array[i];
+
if(maximum_size > 0 && size >= maximum_size) {
if (p.array[i] > upper.array[i])
+
if(key.compareTo(top.obj) >= 0) {
near.array[i] = upper.array[i];
+
return;
if (p.array[i] < lower.array[i])
+
}
near.array[i] = lower.array[i];
+
}
 +
Item nItem = new Item(value);
 +
if(key.compareTo(bottom.obj) < 0) {
 +
//less than the bottom, put on the bottom
 +
nItem.up = bottom;
 +
bottom.down = nItem;
 +
bottom = nItem;
 +
++size;
 +
if(maximum_size > 0 && size > maximum_size) {
 +
pollTop();
 +
}
 +
return;
 +
}
 +
 +
//start at the bottom
 +
Item current = bottom;
 +
while(current != null) {
 +
if(key.compareTo(current.obj) < 0) {
 +
nItem.up = current;
 +
nItem.down = current.down;
 +
current.down.up = nItem;
 +
current.down = nItem;
 +
++size;
 +
if(maximum_size > 0 && size > maximum_size) {
 +
pollTop();
 +
}
 +
return;
 +
}
 +
current = current.up;
 
}
 
}
return near;
+
 +
//else put it on top
 +
nItem.down = top;
 +
top.up = nItem;
 +
top = nItem;
 +
++size;
 +
if(maximum_size > 0 && size > maximum_size) {
 +
pollTop();
 +
}
 
}
 
}
 
}
 
}
 +
 
</pre>
 
</pre>

Revision as of 23:39, 1 March 2010

Everyone and their brother has one of these now, me and Simonton started it, but I was to inexperienced to get anything written, I took an hour or two to rewrite it today, because I am no longer completely terrible at these things. So here is mine if you care to see it.

KDTreeB

package org.csdgn.util;

import java.util.Comparator;

public class KDTreeB {
	public final static int DEFAULT_BUCKET_SIZE = 200;
	protected NodeKD root;
	protected int dimensions;
	protected int bucket_size;
	protected long size = 0;
	
	/**
	 * This creates a KDTreeB with the given number of dimensions and
	 * the default bucket size.
	 * @param dimensions
	 */
	public KDTreeB(int dimensions) {
		this(dimensions,DEFAULT_BUCKET_SIZE);
	}
	/**
	 * This creates a KDTreeB with the given number of dimensions and
	 * the given bucket size.
	 * @param dimensions
	 * @param bucket_size
	 */
	public KDTreeB(int dimensions, int bucket_size) {
		this.dimensions = dimensions;
		this.bucket_size = bucket_size;
		root = new BucketKD(this);
	}
	
	/**
	 * Adds the given point to the tree. Uses a recursive algorithm.
	 * @param point
	 */
	public void add(PointKD point) {
		root.add(point);
	}
	
	/**
	 * Returns the nearest neighbor to the given point.
	 * @param point - PointKD to find the nearest to.
	 * @return - The nearest PointKD to 
	 */
	public PointKD getNearestNeighbor(PointKD point) {
		return root.nearest(point);
	}
	
	/**
	 * Returns the nearest num neighbors to the given point.
	 * @param point - PointKD to find the nearest to.
	 * @param num - The number of points to find.
	 * @return - The nearest PointKD's to point.
	 */
	public PointKD[] getNearestNeighbors(final PointKD point, int num) {
		if(num == 1) {
			return new PointKD[] { getNearestNeighbor(point) };
		}
		PriorityDeque<PointKD> queue = new PriorityDeque<PointKD>(
				new Comparator<PointKD>(){
					@Override
					public int compare(PointKD o1, PointKD o2) {
						double dist0 = point.distance(o1);
						double dist1 = point.distance(o2);
						return (dist0-dist1 < 0) ? -1 : 1;
					}
				},num);
		
		root.nearestn(queue,point);
		
		PointKD[] array = new PointKD[num];
		Object[] obj = queue.toArray();
		for(int i=0; i<num; ++i) {
			array[i] = (PointKD)obj[i];
		}
		
		return array;
	}
	
	/**
	 * Returns all PointKD within a certain RectangleKD.
	 * @param rect - area to get PointKD from
	 * @return - All PointKD within rect.
	 */
	public PointKD[] getRange(RectangleKD rect) {
		return root.range(rect);
	}

	/**
	 * Returns all PointKD within a certain range defined by an upper and lower PointKD.
	 * @param low - lower bounds of area
	 * @param high - upper bounds of area
	 * @return - All PointKD between low and high.
	 */
	public PointKD[] getRange(PointKD low, PointKD high) {
		return this.getRange(new RectangleKD(low, high));
	}
	
	protected abstract class NodeKD {
		protected KDTreeB owner;
		protected BranchKD parent;
		protected RectangleKD rect;
		protected int depth = 0;
		
		protected abstract void add(PointKD k);
		protected abstract PointKD nearest(PointKD k);
		protected abstract void nearestn(PriorityDeque<PointKD> queue, PointKD k);
		protected abstract PointKD[] range(RectangleKD r);
	}
	protected class BucketKD extends NodeKD {
		protected PointKD bucket[];
		protected int current;
		
		public BucketKD(KDTreeB owner) {
			this.owner = owner;
			bucket = new PointKD[owner.bucket_size];
			rect = new RectangleKD();
			parent = null;
		}
		public BucketKD(BranchKD branch) {
			this(branch.owner);
			parent = branch;
			depth = branch.depth + 1;
		}
		
		@Override
		protected void add(PointKD point) {
			if(current >= bucket.length) {
				//Split the bucket into a branch
				BranchKD branch = new BranchKD(this);
				if(parent == null) {
					owner.root = branch;
				} else {
					if(parent.isLeft(this)) {
						parent.left = branch;
					} else {
						parent.right = branch;
					}
				}
				branch.add(point);
				bucket = null;
				current = 0;
				return;
			}
			bucket[current++] = point;
			rect.expand(point);
		}

		@Override
		protected PointKD nearest(PointKD k) {
			double nearestDist = Double.POSITIVE_INFINITY;
			int nearest = 0;
			for (int i = 0; i < current; i++) {
				double distance = k.distanceSq(bucket[i]);
				if (distance < nearestDist) {
					nearestDist = distance;
					nearest = i;
				}
			}
			return bucket[nearest];
		}
		
		@Override
		protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) {
			for(int i = 0; i < current; i++) {
				queue.offer(bucket[i]);
			}
		}

		@Override
		protected PointKD[] range(RectangleKD r) {
			PointKD[] tmp = new PointKD[current];
			int n = 0;
			for (int i = 0; i < current; i++) {
				if (r.contains(bucket[i])) {
					tmp[n++] = bucket[i];
				}
			}
			PointKD[] tmp2 = new PointKD[n];
			System.arraycopy(tmp, 0, tmp2, 0, n);
			return tmp2;
		}
		
	}
	protected class BranchKD extends NodeKD {
		protected NodeKD left, right;
		protected double slice;
		protected int dim;
		
		public BranchKD(BucketKD k) {
			owner = k.owner;
			parent = k.parent;
			slice = 0;
			rect = k.rect;
			depth = k.depth;
			left = new BucketKD(this);
			right = new BucketKD(this);
			
			dim = depth % owner.dimensions;
			double total = 0;
			for (int i = 0; i < k.current; i++) {
				total += k.bucket[i].internal[dim];
			}
			slice = total / k.current;
			for (int i = 0; i < k.current; i++)
				add(k.bucket[i]);
		}
		
		@Override
		protected void add(PointKD k) {
			if(k.internal[dim] > slice) {
				right.add(k);
			} else {
				left.add(k);
			}
		}

		@Override
		protected PointKD nearest(PointKD k) {
			PointKD near = null;
			if (k.internal[dim] > slice) {
				near = right.nearest(k);
				double t = near.distanceSq(k);
				if (k.distanceSq(left.rect.getNearest(k)) < t) {
					PointKD tmp = left.nearest(k);
					if (tmp.distanceSq(k) < t) {
						near = tmp;
					}
				}
			} else {
				near = left.nearest(k);
				double t = near.distanceSq(k);
				if (k.distanceSq(right.rect.getNearest(k)) < t) {
					PointKD tmp = right.nearest(k);
					if (tmp.distanceSq(k) < t) {
						near = tmp;
					}
				}
			}
			return near;
		}
		
		@Override
		protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) {
			//TODO
			if(k.internal[dim] > slice) {
				right.nearestn(queue, k);
				double t = queue.peekBottom().distanceSq(k);
				if(k.distanceSq(left.rect.getNearest(k)) < t) {
					left.nearestn(queue, k);
				}
			} else {
				left.nearestn(queue, k);
				double t = queue.peekBottom().distanceSq(k);
				if(k.distanceSq(right.rect.getNearest(k)) < t) {
					right.nearestn(queue, k);
				}
			}
		}

		@Override
		protected PointKD[] range(RectangleKD r) {
			PointKD[] tmp = new PointKD[0];
			if (r.intersects(left.rect)) {
				PointKD[] tmpl = left.range(r);
				if(0 == tmp.length)
					tmp = tmpl;
			}
			if (r.intersects(right.rect)) {
				PointKD[] tmpr = right.range(r);
				if (0 == tmp.length)
					tmp = tmpr;
				else if (0 < tmpr.length) {
					PointKD[] tmp2 = new PointKD[tmp.length + tmpr.length];
					System.arraycopy(tmp, 0, tmp2, 0, tmp.length);
					System.arraycopy(tmpr, 0, tmp2, tmp.length, tmpr.length);
					tmp = tmp2;
				}
			}
			return tmp;
		}
		
		protected boolean isLeft(BucketKD kd) {
			if(left == kd) return true;
			return false;
		}
	}
}

PointKD

package org.csdgn.util;

import java.io.Serializable;

/**
 * PointKD class is a class that wraps a double array, for use in K-Dimensional structures.
 * This wrapping is done to improve readability and modularity. Often used to define a
 * point in k-dimensional space.
 * 
 * @author Chase
 */
public class PointKD implements Serializable {
	private static final long serialVersionUID = -841162798668123755L;
	protected double[] internal;
	
	/**
	 * Constructor
	 * @param dimensions - Number of dimensions for this PointKD.
	 */
	public PointKD(int dimensions) {
		internal = new double[dimensions];
	}
	
	/**
	 * Constructor
	 * @param array - An array to use for the internal array of this PointKD.
	 */
	public PointKD(double[] array) {
		internal = array.clone();
	}
	
	/**
	 * Constructor
	 * @param point - PointKD to clone.
	 */
	public PointKD(PointKD point) {
		this.internal = point.internal.clone();
	}
	
	
	/**
	 * Returns array.
	 * @return The internal array of this pointKD, changes made to this will effect this PointKD.
	 */
	public double[] get() {
		return internal;
	}
	
	/**
	 * Sets the location of this point.
	 * @param point - PointKD to copy.
	 */
	public void set(PointKD point) {
		this.internal = point.internal.clone();
	}
	
	/**
	 * Sets the location of this point.
	 * @param array - Array to copy.
	 */
	public void set(double[] array) {
		this.internal = array.clone();
	}
	
	/**
	 * Sets the value at dimension index
	 * @param index - Index
	 * @param value - Value to set
	 */
	public void set(int index, double value) {
		internal[index] = value;
	}
	
	/**
	 * @return number of dimensions
	 */
	public int size() {
		return internal.length;
	}
	
	/**
	 * Compares this to a selected point and returns the euclidean distance
	 * between them.
	 * 
	 * @param p
	 *            - The Point to get the distance to.
	 * @return The distance between this and <b>p</b>.
	 */
	public double distance(PointKD p) {
		return distance(this.internal, p.internal);
	}

	/**
	 * Compares this to a selected point and returns the squared euclidean
	 * distance between them.
	 * 
	 * @param p
	 *            - The Point to get the distance to.
	 * @return The distance between this and <b>p</b>.
	 */
	public double distanceSq(PointKD p) {
		return distanceSq(this.internal, p.internal);
	}
	
	/**
	 * Clones this point.
	 */
	public PointKD clone() {
		return new PointKD(internal);
	}
	
	/**
	 * Prints the class name and the point coordinates.
	 */
	public String toString() {
		String output = getClass().getSimpleName() + "[";
		for (int i = 0; i < internal.length; ++i) {
			if (0 != i)
				output += ",";
			output += internal[i];
		}
		return output + "]";
	}
	
	/**
	 * Compares arrays of double and returns the euclidean distance
	 * between them.
	 * 
	 * @param a - The first set of numbers
	 * @param b - The second set of numbers
	 * @return The distance squared between <b>a</b> and <b>b</b>.
	 */
	public static final double distance(double[] a, double[] b) {
		double total = 0;
		for (int i = 0; i < a.length; ++i)
			total += (b[i] - a[i]) * (b[i] - a[i]);
		return Math.sqrt(total);
	}
	
	/**
	 * Compares arrays of double and returns the squared euclidean distance
	 * between them.
	 * 
	 * @param a - The first set of numbers
	 * @param b - The second set of numbers
	 * @return The distance squared between <b>a</b> and <b>b</b>.
	 */
	public static final double distanceSq(double[] a, double[] b) {
		double total = 0;
		for (int i = 0; i < a.length; ++i)
			total += (b[i] - a[i]) * (b[i] - a[i]);
		return total;
	}
}

RectangleKD

package org.csdgn.util;

import java.io.Serializable;

public class RectangleKD implements Serializable {
	private static final long serialVersionUID = 524388821816648020L;
	protected PointKD upper, lower;
	/**
	 * Creates an empty RectangleKD
	 */
	public RectangleKD() {
		upper = null;
		lower = null;
	}
	/**
	 * Creates a RectangleKD from two points.
	 * @param lower
	 * @param upper
	 */
	public RectangleKD(PointKD lower, PointKD upper) {
		this.lower = lower.clone();
		this.upper = upper.clone();
	}
	
	/**
	 * Expands this RectangleKD to include point.
	 * @param point - Point used to expand this Rectangle
	 */
	public void expand(PointKD point) {
		if(upper == null) {
			upper = point.clone();
			lower = point.clone();
			return;
		}
		for(int i=0; i<point.size(); ++i) {
			if(upper.internal[i] < point.internal[i])
				upper.internal[i] = point.internal[i];
			if(lower.internal[i] > point.internal[i])
				lower.internal[i] = point.internal[i];
		}
	}
	
	/**
	 * Checks if this RectangleKD contains the given point.
	 * @param point - PointKD to check.
	 * @return true - if this rectangle contains the given point.
	 */
	public boolean contains(PointKD point) {
		if(upper == null) return false;
		for(int i=0; i<point.size(); ++i) {
			if(point.internal[i] > upper.internal[i] ||
				point.internal[i] < lower.internal[i]) 
					return false;
		}
		return true;
	}
	
	/**
	 * Checks if this table intersects the given table.
	 * @param rectangle - The rectangle to check intersection with.
	 * @return true - if this rectangle intersects the given rectangle.
	 */
	public boolean intersects(RectangleKD rectangle) {
		if(rectangle.upper == null || upper == null) return false;
		for(int i=0; i<upper.size(); ++i) {
			if(rectangle.upper.internal[i] < lower.internal[i]
			|| rectangle.lower.internal[i] > upper.internal[i]) return false;
		}
		return true;
	}
	
	/**
	 * Gets the nearest point in this RectangleKD to the given point
	 * @param point - PointKD to get the nearest point to.
	 * @return the nearest point in this RectangleKD to the given point.
	 */
	public PointKD getNearest(PointKD point) {
		if(upper == null) return null;
		if(contains(point)) return point; //This check may not be needed.
		PointKD nearest = new PointKD(point);
		for(int i = 0; i < upper.size(); ++i) {
			if(nearest.internal[i] > upper.internal[i])
				nearest.internal[i] = upper.internal[i];
			if(nearest.internal[i] < lower.internal[i])
				nearest.internal[i] = lower.internal[i];
		}
		return nearest;
	}
}

PriorityDeque

package org.csdgn.util;

import java.util.Comparator;

/**
 * This is a home made PriorityDeque with maximum size limiter, and
 * comparator or natural ordering selection.
 * 
 * @author Chase
 * @param <E>
 */
public class PriorityDeque<E> {
	private class Item {
		public Item down, up;
		public E obj;
		public Item(E item) { 
			obj = item;
			down = up = null;
		}
	}
	private final Comparator<? super E> comparator;
	private Item bottom, top;
	private int maximum_size;
	private int size;
	
	/**
	 * Generic Constructor
	 */
	public PriorityDeque() {
		this(null,-1);
	}
	/**
	 * Constructor with a defined maximum number of entries
	 * @param maximum
	 */
	public PriorityDeque(int maximum) {
		this(null,maximum);
	}
	/**
	 * Constructor with a defined comparator and an unlimited number of items.
	 * @param comp
	 */
	public PriorityDeque(Comparator<? super E> comp) {
		this(comp,-1);
	}
	/**
	 * Constructor with a defined conparator and limited number of items.
	 * @param comp
	 * @param maximum
	 */
	public PriorityDeque(Comparator<? super E> comp, int maximum) {
		comparator = comp;
		maximum_size = maximum;
		bottom = top = null;
		size = 0;
	}
	/**
	 * Adds an item to this deque.
	 * @param value - item to add
	 */
	public void offer(E value) {
		//System.err.println("Offering: " + value.toString());
		if(bottom == null) {
			//System.err.println("-Is first item.");
			bottom = top = new Item(value);
			return;
		}
		//do ordering etc
		if(comparator != null) {
			//System.err.println("-Comparator.");
			offerComparator(value);
		} else {
			//System.err.println("-Natural.");
			offerNatural(value);
		}
	}
	
	/**
	 * Removes and returns an item from the bottom of the list (lowest value)
	 * @return the lowest value (bottom)
	 */
	public E pollBottom() {
		if(bottom == null) return null;
		Item tmp = bottom;
		if(bottom == top) bottom = top = null;
		else {
			bottom = bottom.up;
			bottom.down = null;
			tmp.up = null;
		}
		--size;
		return tmp.obj;
	}
	/**
	 * Returns but does not remove the item from the bottom of the list.
	 * @return the lowest value (bottom)
	 */
	public E peekBottom() {
		if(bottom == null) return null;
		return bottom.obj;
	}
	/**
	 * Removes and returns an item from the top of the list (highest value).
	 * @return the highest value (top)
	 */
	public E pollTop() {
		if(top == null) return null;
		Item tmp = top;
		if(bottom == top) bottom = top = null;
		else {
			top = top.down;
			top.up = null;
			tmp.down = null;
		}
		--size;
		return tmp.obj;
	}
	/**
	 * Returns but does not remove the item from the top of the list (highest value).
	 * @return the highest value (top)
	 */
	public E peekTop() {
		if(top == null) return null;
		return top.obj;
	}
	/**
	 * This is a generic toArray, returns a non-castable Object
	 * array with all the items in this deque.
	 * @return an Object array with everything in this deque.
	 */
	public Object[] toArray() {
		Object array[] = new Object[size+1];
		Item current = bottom;
		int i = 0;
		while(current != null) {
			array[i++] = current.obj;
			current = current.up;
		}
		return array;
	}
	/**
	 * Add/sorts a value using the comparator
	 * @param value
	 */
	private void offerComparator(E value) {
		if(maximum_size > 0 && size >= maximum_size) {
			if(comparator.compare(value, top.obj) >= 0) {
				return;
			}
		}
		Item nItem = new Item(value);
		if(comparator.compare(value,bottom.obj) < 0) {
			//less than the bottom, put on the bottom
			nItem.up = bottom;
			bottom.down = nItem;
			bottom = nItem;
			++size;
			if(maximum_size > 0 && size > maximum_size) {
				pollTop();
			}
			return;
		}
		
		//start at the bottom
		Item current = bottom;
		while(current != null) {
			if(comparator.compare(value,current.obj) < 0) {
				nItem.up = current;
				nItem.down = current.down;
				current.down.up = nItem;
				current.down = nItem;
				++size;
				if(maximum_size > 0 && size > maximum_size) {
					pollTop();
				}
				return;
			}
			current = current.up;
		}
		
		//else put it on top
		nItem.down = top;
		top.up = nItem;
		top = nItem;
		++size;
		if(maximum_size > 0 && size > maximum_size) {
			pollTop();
		}
	}
	
	/**
	 * Add/sorts a value using the natural order
	 * @param value
	 */
	@SuppressWarnings("unchecked")
	private void offerNatural(E value) {
		Comparable<? super E> key = (Comparable<? super E>)value;
		if(maximum_size > 0 && size >= maximum_size) {
			if(key.compareTo(top.obj) >= 0) {
				return;
			}
		}
		Item nItem = new Item(value);
		if(key.compareTo(bottom.obj) < 0) {
			//less than the bottom, put on the bottom
			nItem.up = bottom;
			bottom.down = nItem;
			bottom = nItem;
			++size;
			if(maximum_size > 0 && size > maximum_size) {
				pollTop();
			}
			return;
		}
		
		//start at the bottom
		Item current = bottom;
		while(current != null) {
			if(key.compareTo(current.obj) < 0) {
				nItem.up = current;
				nItem.down = current.down;
				current.down.up = nItem;
				current.down = nItem;
				++size;
				if(maximum_size > 0 && size > maximum_size) {
					pollTop();
				}
				return;
			}
			current = current.up;
		}
		
		//else put it on top
		nItem.down = top;
		top.up = nItem;
		top = nItem;
		++size;
		if(maximum_size > 0 && size > maximum_size) {
			pollTop();
		}		
	}
}