package org.khelekore.prtree.junit;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.khelekore.prtree.MBR;
import org.khelekore.prtree.MBRConverter;
import org.khelekore.prtree.PRTree;
import org.khelekore.prtree.SimpleMBR;
import static org.junit.Assert.*;
public class TestRTree {
private Rectangle2DConverter converter = new Rectangle2DConverter ();
private PRTree<Rectangle2D> tree;
@Before
public void setUp() {
tree = new PRTree<Rectangle2D> (converter, 10);
}
private class Rectangle2DConverter implements MBRConverter<Rectangle2D> {
public double getMinX (Rectangle2D t) {
return t.getMinX ();
}
public double getMinY (Rectangle2D t) {
return t.getMinY ();
}
public double getMaxX (Rectangle2D t) {
return t.getMaxX ();
}
public double getMaxY (Rectangle2D t) {
return t.getMaxY ();
}
}
@Test
public void testEmpty () {
tree.load (Collections.<Rectangle2D>emptyList ());
for (Rectangle2D r : tree.find (0, 0, 1, 1))
fail ("should not get any results");
assertNull ("mbr of empty tress should be null", tree.getMBR ());
assertEquals ("height of empty tree", 1, tree.getHeight ());
}
@Test
public void testSingle () {
Rectangle2D rx = new Rectangle2D.Double (0, 0, 1, 1);
tree.load (Collections.singletonList (rx));
MBR mbr = tree.getMBR ();
assertEquals ("odd min for mbr", 0, mbr.getMinX (), 0);
assertEquals ("odd min for mbr", 0, mbr.getMinY (), 0);
assertEquals ("odd max for mbr", 1, mbr.getMaxX (), 0);
assertEquals ("odd max for mbr", 1, mbr.getMaxY (), 0);
assertEquals ("height of tree with one entry", 1, tree.getHeight ());
int count = 0;
for (Rectangle2D r : tree.find (0, 0, 1, 1)) {
assertEquals ("odd rectangle returned", rx, r);
count++;
}
assertEquals ("odd number of rectangles returned", 1, count);
for (Rectangle2D r : tree.find (5, 5, 6, 7))
fail ("should not find any rectangle");
for (Rectangle2D r : tree.find (-5, -5, -2, -4))
fail ("should not find any rectangle");
}
@Test(expected = IllegalArgumentException.class)
public void testBadQueryRectX () {
tree.find (0, 0, -1, 1);
}
@Test(expected = IllegalArgumentException.class)
public void testBadQueryRectY () {
tree.find (0, 0, 1, -1);
}
@Test(expected = IllegalStateException.class)
public void testMultiLoad () {
Rectangle2D rx = new Rectangle2D.Double (0, 0, 1, 1);
tree.load (Collections.singletonList (rx));
tree.load (Collections.singletonList (rx));
}
@Test
public void testHeight () {
int numRects = 11; // root and below it we have two leaf nodes
List<Rectangle2D> rects = new ArrayList<Rectangle2D> (numRects);
for (int i = 0; i < numRects; i++) {
rects.add (new Rectangle2D.Double (i, i, 10, 10));
}
tree.load (rects);
assertEquals ("height of tree", 2, tree.getHeight ());
}
@Test
public void testMany () {
int numRects = 1000000 / 2;
MBR queryInside = new SimpleMBR (495, 495, 504.9, 504.9);
MBR queryOutside = new SimpleMBR (1495, 495, 1504.9, 504.9);
int shouldFindInside = 0;
int shouldFindOutside = 0;
List<Rectangle2D> rects = new ArrayList<Rectangle2D> (numRects * 2);
// build an "X"
System.err.println ("building rects");
for (int i = 0; i < numRects; i++) {
Rectangle2D r1 = new Rectangle2D.Double (i, i, 10, 10);
Rectangle2D r2 = new Rectangle2D.Double (i, numRects - i, 10, 10);
if (queryInside.intersects (r1, converter))
shouldFindInside++;
if (queryOutside.intersects (r1, converter))
shouldFindOutside++;
if (queryInside.intersects (r2, converter))
shouldFindInside++;
if (queryOutside.intersects (r2, converter))
shouldFindOutside++;
rects.add (r1);
rects.add (r2);
}
System.err.println ("shuffling rects");
// shuffle, but make sure the shuffle is the same every time
Random random = new Random (4711);
Collections.shuffle (rects, random);
System.err.println ("loading tree");
tree.load (rects);
System.err.println ("tree loaded");
int count = 0;
// dx = 10, each rect is 10 so 20 in total
for (Rectangle2D r : tree.find (queryInside))
count++;
assertEquals ("should find some rectangles", shouldFindInside, count);
count = 0;
for (Rectangle2D r : tree.find (queryOutside))
count++;
assertEquals ("should not find rectangles", shouldFindOutside, count);
}
private static final double RANGE = 100000;
private double getRandomRectangleSize (Random random) {
return random.nextDouble () * RANGE - RANGE / 2;
}
@Test
public void testRandom () {
System.err.println ("testRandom");
int numRects = 100000;
int numRounds = 100;
Random random = new Random (1234); // same random every time
for (int round = 0; round < numRounds; round++) {
tree = new PRTree<Rectangle2D> (converter, 10);
List<Rectangle2D> rects = new ArrayList<Rectangle2D> (numRects);
for (int i = 0; i < numRects; i++) {
Rectangle2D r =
new Rectangle2D.Double (getRandomRectangleSize (random),
getRandomRectangleSize (random),
getRandomRectangleSize (random),
getRandomRectangleSize (random));
rects.add (r);
}
tree.load (rects);
double x1 = getRandomRectangleSize (random);
double y1 = getRandomRectangleSize (random);
double x2 = getRandomRectangleSize (random);
double y2 = getRandomRectangleSize (random);
MBR query = new SimpleMBR (Math.min (x1, x2), Math.min (y1, y2),
Math.max (x1, x2), Math.min (y1, y2));
int countSimple = 0;
for (Rectangle2D r : rects) {
if (query.intersects (r, converter))
countSimple++;
}
int countTree = 0;
for (Rectangle2D r : tree.find (query))
countTree++;
assertEquals (round + ": should find same number of rectangles",
countSimple, countTree);
}
}
@Test
public void testFindSpeed () {
System.err.println ("testFindSpeed");
int numRects = 100000;
List<Rectangle2D> rects = new ArrayList<Rectangle2D> (numRects);
for (int i = 0; i < numRects; i++)
rects.add (new Rectangle2D.Double (i, i, 10, 10));
System.out.println ("running speed test");
tree.load (rects);
testFindSpeedIterator ();
testFindSpeedArray ();
}
private void testFindSpeedIterator () {
int count = 0;
int numRounds = 100000;
long start = System.nanoTime ();
for (int i = 0; i < numRounds; i++) {
for (Rectangle2D r : tree.find (295, 295, 1504.9, 5504.9))
count++;
}
long end = System.nanoTime ();
long diff = end - start;
System.out.println ("finding " + count + " took: " + (diff / 1000000) +
" millis, average: " + (diff / numRounds) +
" nanos");
}
private void testFindSpeedArray () {
int count = 0;
int numRounds = 100000;
long start = System.nanoTime ();
for (int i = 0; i < numRounds; i++) {
List<Rectangle2D> result = new ArrayList<Rectangle2D> (150);
tree.find (295, 295, 1504.9, 5504.9, result);
for (Rectangle2D r : result)
count++;
}
long end = System.nanoTime ();
long diff = end - start;
System.out.println ("finding " + count + " took: " + (diff / 1000000) +
" millis, average: " + (diff / numRounds) +
" nanos");
}
public static void main (String args[]) {
JUnitCore.main (TestRTree.class.getName ());
}
}