diff --git a/CHANGELOG.md b/CHANGELOG.md index a83d5fa..719fdb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ ## Unreleased +- Fixed ClassCastExceptionin HD tree. + [#40](https://github.com/tzaeschke/phtree/pull/40) - getStats() for empty trees fails. [#36](https://github.com/tzaeschke/phtree/pull/36) - Fix some warnings. [#37](https://github.com/tzaeschke/phtree/pull/37) -- UPdated some dependencies. [#38](https://github.com/tzaeschke/phtree/pull/38) +- Updated some dependencies. [#38](https://github.com/tzaeschke/phtree/pull/38) ## 2.8.0 - 2023-07-29 diff --git a/src/main/java/ch/ethz/globis/phtree/PhDistanceL.java b/src/main/java/ch/ethz/globis/phtree/PhDistanceL.java index 300e705..b39c022 100644 --- a/src/main/java/ch/ethz/globis/phtree/PhDistanceL.java +++ b/src/main/java/ch/ethz/globis/phtree/PhDistanceL.java @@ -37,11 +37,11 @@ public double dist(long[] v1, long[] v2) { // would be absolutely precise and unlikely to overflow. //The dl*dl can be done as 'double', which is always safe. for (int i = 0; i < v1.length; i++) { - //double dl = (double)v1[i] - (double)v2[i]; - long dl = Math.subtractExact(v1[i], v2[i]); - d += Math.multiplyExact(dl, dl); -// double dl = Math.subtractExact(v1[i], v2[i]); -// d += dl*dl; + double dl = (double)v1[i] - (double)v2[i]; + // long dl = Math.subtractExact(v1[i], v2[i]); + // d += Math.multiplyExact(dl, dl); + // double dl = Math.subtractExact(v1[i], v2[i]); + d += dl*dl; } return Math.sqrt(d); } diff --git a/src/main/java/ch/ethz/globis/phtree/util/Refs.java b/src/main/java/ch/ethz/globis/phtree/util/Refs.java index efc4157..8e7046a 100644 --- a/src/main/java/ch/ethz/globis/phtree/util/Refs.java +++ b/src/main/java/ch/ethz/globis/phtree/util/Refs.java @@ -302,11 +302,6 @@ public static T[] read(ObjectInput in) throws IOException { return ret; } - @SuppressWarnings("unchecked") - public static T[] newArray(int size) { - return (T[]) new Object[size]; - } - @SuppressWarnings("unchecked") public static T[] newArray(Class c, int size) { return (T[]) Array.newInstance(c, size); diff --git a/src/main/java/ch/ethz/globis/phtree/v13SynchedPool/PhQueryKnnMbbPPList.java b/src/main/java/ch/ethz/globis/phtree/v13SynchedPool/PhQueryKnnMbbPPList.java index c7f2eea..71b3549 100644 --- a/src/main/java/ch/ethz/globis/phtree/v13SynchedPool/PhQueryKnnMbbPPList.java +++ b/src/main/java/ch/ethz/globis/phtree/v13SynchedPool/PhQueryKnnMbbPPList.java @@ -14,6 +14,7 @@ import ch.ethz.globis.phtree.PhFilterDistance; import ch.ethz.globis.phtree.PhTree.PhExtent; import ch.ethz.globis.phtree.PhTree.PhKnnQuery; +import ch.ethz.globis.phtree.util.Refs; import java.util.Arrays; import java.util.NoSuchElementException; @@ -304,7 +305,7 @@ void reset(int newSize, long[] center) { this.center = center; maxDistance = Double.MAX_VALUE; if (data == null) { - data = (PhEntryDist[]) new Object[newSize]; + data = Refs.newArray(PhEntryDist.class, newSize); distData = new double[newSize]; for (int i = 0; i < data.length; i++) { data[i] = createEntry(); diff --git a/src/main/java/ch/ethz/globis/phtree/v16hd/NodeIteratorListReuse.java b/src/main/java/ch/ethz/globis/phtree/v16hd/NodeIteratorListReuse.java index 4fe03b0..24157bf 100644 --- a/src/main/java/ch/ethz/globis/phtree/v16hd/NodeIteratorListReuse.java +++ b/src/main/java/ch/ethz/globis/phtree/v16hd/NodeIteratorListReuse.java @@ -21,6 +21,7 @@ import java.util.List; import ch.ethz.globis.phtree.PhEntry; +import ch.ethz.globis.phtree.util.Refs; import ch.ethz.globis.phtree.v16hd.Node.BSTEntry; import ch.ethz.globis.phtree.v16hd.bst.BSTIteratorMask; @@ -41,8 +42,7 @@ public class NodeIteratorListReuse { private class PhIteratorStack { - @SuppressWarnings("unchecked") - private final NodeIterator[] stack = (NodeIteratorListReuse.NodeIterator[]) new Object[64]; + private final NodeIterator[] stack = Refs.newArray(NodeIteratorListReuse.NodeIterator.class, 64); private int size = 0; diff --git a/src/main/java/ch/ethz/globis/phtree/v8/Node.java b/src/main/java/ch/ethz/globis/phtree/v8/Node.java index a79ae6a..0610d73 100644 --- a/src/main/java/ch/ethz/globis/phtree/v8/Node.java +++ b/src/main/java/ch/ethz/globis/phtree/v8/Node.java @@ -78,7 +78,7 @@ private static final boolean NI_THRESHOLD(int subCnt, int postCnt) { protected Node(Node original, int dim) { if (original.subNRef != null) { int size = original.subNRef.length; - this.subNRef = (Node[]) new Object[size]; + this.subNRef = Refs.newArray(Node.class, size); System.arraycopy(original.subNRef, 0, this.subNRef, 0, size); } if (original.values != null) { diff --git a/src/test/java/ch/ethz/globis/phtree/hd/TestHighDimensions.java b/src/test/java/ch/ethz/globis/phtree/hd/TestHighDimensions.java index 4186dc8..00f8429 100644 --- a/src/test/java/ch/ethz/globis/phtree/hd/TestHighDimensions.java +++ b/src/test/java/ch/ethz/globis/phtree/hd/TestHighDimensions.java @@ -13,8 +13,11 @@ import static org.junit.Assert.assertTrue; import java.util.Iterator; +import java.util.List; import java.util.Random; +import ch.ethz.globis.phtree.PhEntry; +import ch.ethz.globis.phtree.PhEntryDist; import org.junit.Test; import ch.ethz.globis.phtree.PhTree; @@ -34,6 +37,9 @@ public class TestHighDimensions { private PhTree create(int dim) { return TestUtil.newTreeHD(dim); } + private PhTree createInt(int dim) { + return TestUtil.newTreeHD(dim); + } @Test public void testHighD64Neg() { @@ -248,4 +254,96 @@ public void testQueryHighD64Neg() { assertTrue(n2 < n); } } + + @Test + public void smokeTest() { + final int N = 1000; + for (int DIM : DIMS) { + smokeTest(N, DIM, 0); + } + } + + private void smokeTest(int N, int DIM, long SEED) { + Random R = new Random(SEED); + PhTree ind = createInt(DIM); + long[][] keys = new long[N][DIM]; + for (int i = 0; i < N; i++) { + for (int d = 0; d < DIM; d++) { + keys[i][d] = R.nextInt(); //INT! + } + if (ind.contains(keys[i])) { + i--; + continue; + } + //build + assertNull(ind.put(keys[i], i)); + //System.out.println("key=" + Bits.toBinary(keys[i], 64)); + //System.out.println(ind); + assertTrue("i="+ i, ind.contains(keys[i])); + assertEquals(i, (int)ind.get(keys[i])); + } + + + //first check + for (int i = 0; i < N; i++) { + assertTrue(ind.contains(keys[i])); + assertEquals(i, (int)ind.get(keys[i])); + } + + //update + for (int i = 0; i < N; i++) { + assertEquals(i, (int)ind.put(keys[i], -i)); + assertTrue(ind.contains(keys[i])); + assertEquals(-i, (int)ind.get(keys[i])); + } + + //check again + for (int i = 0; i < N; i++) { + assertTrue(ind.contains(keys[i])); + assertEquals(-i, (int)ind.get(keys[i])); + } + + // query + for (int i = 0; i < N; i++) { + PhTree.PhQuery q = ind.query(keys[i], keys[i]); + assertTrue(q.hasNext()); + assertEquals(-i, (int)q.next()); + assertFalse(q.hasNext()); + } + + // query all + for (int i = 0; i < N; i++) { + List> q = ind.queryAll(keys[i], keys[i]); + assertEquals(1, q.size()); + assertEquals(-i, (int)q.get(0).getValue()); + } + + // extent + PhTree.PhExtent extent = ind.queryExtent(); + int nExtent = 0; + while (extent.hasNext()) { + extent.next(); + nExtent++; + } + + // query kNN + for (int i = 0; i < N; i++) { + PhTree.PhKnnQuery q = ind.nearestNeighbour(1, keys[i]); + assertTrue(q.hasNext()); + PhEntryDist e = q.nextEntryReuse(); + assertEquals(-i, (int)e.getValue()); + assertEquals(0, e.dist(), 0); + } + + //delete + for (int i = 0; i < N; i++) { + //System.out.println("Removing: " + Bits.toBinary(keys[i], 64)); + //System.out.println("Tree: \n" + ind); + assertEquals(-i, (int)ind.remove(keys[i])); + assertFalse(ind.contains(keys[i])); + assertNull(ind.get(keys[i])); + } + + assertEquals(0, ind.size()); + } } diff --git a/src/test/java/ch/ethz/globis/phtree/test/TestHighDimensions.java b/src/test/java/ch/ethz/globis/phtree/test/TestHighDimensions.java deleted file mode 100644 index f955be8..0000000 --- a/src/test/java/ch/ethz/globis/phtree/test/TestHighDimensions.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2011-2016 ETH Zurich. All Rights Reserved. - * - * This software is the proprietary information of ETH Zurich. - * Use is subject to license terms. - */ -package ch.ethz.globis.phtree.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Iterator; -import java.util.Random; - -import org.junit.Test; - -import ch.ethz.globis.phtree.PhTree; -import ch.ethz.globis.phtree.test.util.TestUtil; -import ch.ethz.globis.phtree.util.Bits; - -/** - * This test harness contains all of the tests for trees whose number of dimensions k will - * be higher than 31. - * - */ -public class TestHighDimensions { - - private PhTree create(int dim) { - return TestUtil.newTree(dim); - } - - @Test - public void testHighD64Neg() { - final int MAX_DIM = 60; - final int N = 1000; - Random R = new Random(0); - - for (int DIM = 32; DIM <= MAX_DIM; DIM++) { - //System.out.println("d="+ DIM); - long[][] vals = new long[N][]; - PhTree ind = create(DIM); - for (int i = 0; i < N; i++) { - long[] v = new long[DIM]; - for (int j = 0; j < DIM; j++) { - v[j] = R.nextLong(); - } - vals[i] = v; - assertNull(Bits.toBinary(v), ind.put(v, v)); - } - - //delete all - for (long[] v: vals) { - assertTrue("DIM=" + DIM + " v=" + Bits.toBinary(v), ind.contains(v)); - assertNotNull(ind.remove(v)); - } - - //check empty result - long[] min = new long[DIM]; - long[] max = new long[DIM]; - for (int i = 0; i < DIM; i++) { - min[i] = Long.MIN_VALUE; - max[i] = Long.MAX_VALUE; - } - Iterator it = ind.query(min, max); - assertFalse(it.hasNext()); - - assertEquals(0, ind.size()); - } - } - - @Test - public void testQueryHighD() { - final int MAX_DIM = 60; - final int N = 2000; - final long mask = ~(1L<<(63)); //only positive numbers! - Random R = new Random(0); - - for (int DIM = 3; DIM <= MAX_DIM; DIM++) { - //System.out.println("d="+ DIM); - PhTree ind = create(DIM); - for (int i = 0; i < N; i++) { - long[] v = new long[DIM]; - for (int j = 0; j < DIM; j++) { - v[j] = R.nextLong() & mask; - } - assertNull(Bits.toBinary(v), ind.put(v, v)); - } - - //check empty result - Iterator it; - int n = 0; - long[] min = new long[DIM]; - long[] max = new long[DIM]; - it = ind.query(min, max); - while(it.hasNext()) { - long[] v = it.next(); - assertEquals(v[0], 0); - n++; - } - assertFalse(it.hasNext()); - assertEquals(0, n); - - //check full result - for (int i = 0; i < DIM; i++) { - min[i] = 0; - max[i] = Long.MAX_VALUE & mask; - } - it = ind.query(min, max); - for (int i = 0; i < N; i++) { - n++; - long[] v = it.next(); - assertNotNull(v); - } - assertFalse(it.hasNext()); - - //check partial result - int n2 = 0; - //0, 0, 0, 50, 0, 50, 0, 50, 0, 50 - max[0] = 0; - it = ind.query(min, max); - while (it.hasNext()) { - n2++; - long[] v = it.next(); - assertEquals(0, v[0]); - } - assertTrue(n2 < n); - } - } - - @Test - public void testQueryHighD64() { - final int MAX_DIM = 60; - final int N = 1000; - final long mask = Long.MAX_VALUE; - Random R = new Random(0); - - for (int DIM = 3; DIM <= MAX_DIM; DIM++) { - //System.out.println("d="+ DIM); - PhTree ind = create(DIM); - for (int i = 0; i < N; i++) { - long[] v = new long[DIM]; - for (int j = 0; j < DIM; j++) { - v[j] = R.nextLong() & mask; - } - assertNull(Bits.toBinary(v), ind.put(v, v)); - } - - //check empty result - Iterator it; - int n = 0; - long[] min = new long[DIM]; - long[] max = new long[DIM]; - it = ind.query(min, max); - while(it.hasNext()) { - long[] v = it.next(); - assertEquals(v[0], 0); - n++; - } - assertFalse(it.hasNext()); - assertEquals(0, n); - - //check full result - for (int i = 0; i < DIM; i++) { - min[i] = 0; - max[i] = Long.MAX_VALUE & mask; - } - it = ind.query(min, max); - for (int i = 0; i < N; i++) { - n++; - long[] v = it.next(); - assertNotNull(v); - } - assertFalse(it.hasNext()); - - //check partial result - int n2 = 0; - //0, 0, 0, 50, 0, 50, 0, 50, 0, 50 - max[0] = 0; - it = ind.query(min, max); - while (it.hasNext()) { - n2++; - long[] v = it.next(); - assertEquals(0, v[0]); - } - assertTrue(n2 < n); - } - } - - @Test - public void testQueryHighD64Neg() { - final int MAX_DIM = 60; - final int N = 1000; - Random R = new Random(0); - - for (int DIM = 3; DIM <= MAX_DIM; DIM++) { - //System.out.println("d="+ DIM); - PhTree ind = create(DIM); - for (int i = 0; i < N; i++) { - long[] v = new long[DIM]; - for (int j = 0; j < DIM; j++) { - v[j] = R.nextLong(); - } - assertNull(Bits.toBinary(v), ind.put(v, v)); - } - - //check empty result - Iterator it; - int n = 0; - long[] min = new long[DIM]; - long[] max = new long[DIM]; - it = ind.query(min, max); - while(it.hasNext()) { - long[] v = it.next(); - assertEquals(v[0], 0); - n++; - } - assertFalse(it.hasNext()); - assertEquals(0, n); - - //check full result - for (int i = 0; i < DIM; i++) { - min[i] = Long.MIN_VALUE; - max[i] = Long.MAX_VALUE; - } - it = ind.query(min, max); - for (int i = 0; i < N; i++) { - n++; - assertTrue("DIM=" + DIM + " i=" + i, it.hasNext()); - long[] v = it.next(); - assertNotNull(v); - } - assertFalse(it.hasNext()); - assertEquals(N, n); - - //check partial result - int n2 = 0; - //0, 0, ... - min[0] = 0; - max[0] = 0; - it = ind.query(min, max); - while (it.hasNext()) { - n2++; - long[] v = it.next(); - assertEquals(0, v[0]); - } - assertTrue(n2 < n); - } - } -} diff --git a/src/test/java/ch/ethz/globis/phtree/test/util/TestSuite.java b/src/test/java/ch/ethz/globis/phtree/test/util/TestSuite.java index e770de0..fb40113 100644 --- a/src/test/java/ch/ethz/globis/phtree/test/util/TestSuite.java +++ b/src/test/java/ch/ethz/globis/phtree/test/util/TestSuite.java @@ -18,6 +18,7 @@ import ch.ethz.globis.phtree.bits.TestIncSuccessor; import ch.ethz.globis.phtree.bits.TestIncrementor; import ch.ethz.globis.phtree.bits.TestTranspose; +import ch.ethz.globis.phtree.hd.TestHighDimensions; import ch.ethz.globis.phtree.test.*; import org.junit.AfterClass;