diff --git a/CHANGELOG.md b/CHANGELOG.md index e421343..0f2d6f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added `lower_bounds(key)` to API. [#126](https://github.com/tzaeschke/phtree-cpp/issues/126) - Added `bpt_fixed_vector`, a fixed size flat vector for future use. It can be dropped in for an std::vector. [#124](https://github.com/tzaeschke/phtree-cpp/pull/124) +- Added Chebyshev distance metric `DIstanceChebyshev`. [#129](https://github.com/tzaeschke/phtree-cpp/pull/139) ### Changed - Changed `bpt_vectot` to use `std::destroy` i.o. default dstr. [#132](https://github.com/tzaeschke/phtree-cpp/pull/132) diff --git a/README.md b/README.md index 412e257..36246eb 100644 --- a/README.md +++ b/README.md @@ -250,8 +250,8 @@ struct FilterMultiMapByValueId { ##### Distance function -Nearest neighbor queries can also use custom distance metrics, such as L1 distance. Note that this returns a special -iterator that provides a function to get the distance of the current entry: +Nearest neighbor queries can also use other distance metrics, such as L1 or Chebyshev distance. Note that the query +returns a special iterator that provides a function to get the distance of the current entry: ```C++ #include "phtree/phtree.h" diff --git a/include/phtree/distance.h b/include/phtree/distance.h index 039b2fd..cbaebe9 100644 --- a/include/phtree/distance.h +++ b/include/phtree/distance.h @@ -38,7 +38,7 @@ namespace improbable::phtree { template struct DistanceEuclidean { - double operator()(const PhPoint& v1, const PhPoint& v2) const { + double operator()(const PhPoint& v1, const PhPoint& v2) const noexcept { double sum2 = 0; for (dimension_t i = 0; i < DIM; ++i) { assert( @@ -51,7 +51,7 @@ struct DistanceEuclidean { return sqrt(sum2); }; - double operator()(const PhPointD& p1, const PhPointD& p2) const { + double operator()(const PhPointD& p1, const PhPointD& p2) const noexcept { double sum2 = 0; for (dimension_t i = 0; i < DIM; ++i) { double d2 = p1[i] - p2[i]; @@ -60,7 +60,7 @@ struct DistanceEuclidean { return sqrt(sum2); }; - double operator()(const PhPointF& v1, const PhPointF& v2) const { + double operator()(const PhPointF& v1, const PhPointF& v2) const noexcept { double sum2 = 0; for (dimension_t i = 0; i < DIM; i++) { double d2 = double(v1[i] - v2[i]); @@ -72,7 +72,7 @@ struct DistanceEuclidean { template struct DistanceL1 { - double operator()(const PhPoint& v1, const PhPoint& v2) const { + double operator()(const PhPoint& v1, const PhPoint& v2) const noexcept { double sum = 0; for (dimension_t i = 0; i < DIM; ++i) { assert( @@ -84,13 +84,52 @@ struct DistanceL1 { return sum; }; - double operator()(const PhPointD& v1, const PhPointD& v2) const { + double operator()(const PhPointD& v1, const PhPointD& v2) const noexcept { double sum = 0; for (dimension_t i = 0; i < DIM; ++i) { sum += std::abs(v1[i] - v2[i]); } return sum; }; + + float operator()(const PhPointF& v1, const PhPointF& v2) const noexcept { + float sum = 0; + for (dimension_t i = 0; i < DIM; ++i) { + sum += std::abs(v1[i] - v2[i]); + } + return sum; + }; +}; + +template +struct DistanceChebyshev { + double operator()(const PhPoint& v1, const PhPoint& v2) const noexcept { + double sum = 0; + for (dimension_t i = 0; i < DIM; ++i) { + assert( + (v1[i] >= 0) != (v2[i] >= 0) || + double(v1[i]) - double(v2[i]) < + double(std::numeric_limits::max())); + sum = std::max(sum, std::abs(double(v1[i] - v2[i]))); + } + return sum; + }; + + double operator()(const PhPointD& v1, const PhPointD& v2) const noexcept { + double sum = 0; + for (dimension_t i = 0; i < DIM; ++i) { + sum = std::max(sum, std::abs(v1[i] - v2[i])); + } + return sum; + }; + + float operator()(const PhPointF& v1, const PhPointF& v2) const noexcept { + float sum = 0; + for (dimension_t i = 0; i < DIM; ++i) { + sum = std::max(sum, std::abs(v1[i] - v2[i])); + } + return sum; + }; }; } // namespace improbable::phtree diff --git a/test/distance_test.cc b/test/distance_test.cc index d065293..63287fc 100644 --- a/test/distance_test.cc +++ b/test/distance_test.cc @@ -20,6 +20,8 @@ using namespace improbable::phtree; +// NOTE: These are very superficial tests. Proper testing is done in the respective PhTree tests. + TEST(PhTreeDistanceTest, DoubleEuclidean) { auto distance = DistanceEuclidean<2>(); ASSERT_DOUBLE_EQ(5, distance(PhPointD<2>{-1, -1}, PhPointD<2>{2, 3})); @@ -30,6 +32,26 @@ TEST(PhTreeDistanceTest, DoubleL1) { ASSERT_DOUBLE_EQ(7, distance(PhPointD<2>{-1, -1}, PhPointD<2>{2, 3})); } +TEST(PhTreeDistanceTest, DoubleChebyshev) { + auto distance = DistanceChebyshev<2>(); + ASSERT_DOUBLE_EQ(4, distance(PhPointD<2>{-1, -1}, PhPointD<2>{2, 3})); +} + +TEST(PhTreeDistanceTest, FloatEuclidean) { + auto distance = DistanceEuclidean<2>(); + ASSERT_DOUBLE_EQ(5, distance(PhPointF<2>{-1, -1}, PhPointF<2>{2, 3})); +} + +TEST(PhTreeDistanceTest, FloatL1) { + auto distance = DistanceL1<2>(); + ASSERT_DOUBLE_EQ(7, distance(PhPointF<2>{-1, -1}, PhPointF<2>{2, 3})); +} + +TEST(PhTreeDistanceTest, FloatChebyshev) { + auto distance = DistanceChebyshev<2>(); + ASSERT_DOUBLE_EQ(4, distance(PhPointF<2>{-1, -1}, PhPointF<2>{2, 3})); +} + TEST(PhTreeDistanceTest, LongEuclidean) { auto distance = DistanceEuclidean<2>(); ASSERT_DOUBLE_EQ(5, distance(PhPoint<2>{-1, -1}, PhPoint<2>{2, 3})); @@ -39,3 +61,9 @@ TEST(PhTreeDistanceTest, LongL1) { auto distance = DistanceL1<2>(); ASSERT_DOUBLE_EQ(7, distance(PhPoint<2>{-1, -1}, PhPoint<2>{2, 3})); } + +TEST(PhTreeDistanceTest, LongChebyshev) { + auto distance = DistanceChebyshev<2>(); + ASSERT_DOUBLE_EQ(4, distance(PhPoint<2>{-1, -1}, PhPoint<2>{2, 3})); +} + diff --git a/test/phtree_d_test.cc b/test/phtree_d_test.cc index f847b66..a64b596 100644 --- a/test/phtree_d_test.cc +++ b/test/phtree_d_test.cc @@ -82,7 +82,7 @@ double distance(const TestPoint& p1, const TestPoint& p2) { } template -double distanceL1(const TestPoint& p1, const TestPoint& p2) { +double distance_L1(const TestPoint& p1, const TestPoint& p2) { double sum = 0; for (dimension_t i = 0; i < DIM; i++) { sum += std::abs(p1[i] - p2[i]); @@ -90,6 +90,15 @@ double distanceL1(const TestPoint& p1, const TestPoint& p2) { return sum; } +template +double distance_chebyshev(const TestPoint& p1, const TestPoint& p2) { + double sum = 0; + for (dimension_t i = 0; i < DIM; i++) { + sum = std::max(sum, std::abs(p1[i] - p2[i])); + } + return sum; +} + template void generateCube(std::vector>& points, size_t N) { DoubleRng rng(-1000, 1000); @@ -1051,7 +1060,8 @@ TEST(PhTreeDTest, TestWindowQueryFilter) { ASSERT_GE(50, num_e); } -TEST(PhTreeDTest, TestKnnQuery) { +template +void test_knn_query(DIST_TEST dist_fn, DIST_REF dist_fn_reference) { // deliberately allowing outside of main points range DoubleRng rng(-1500, 1500); const dimension_t dim = 3; @@ -1068,18 +1078,18 @@ TEST(PhTreeDTest, TestKnnQuery) { // sort points manually std::vector sorted_data; for (size_t i = 0; i < points.size(); i++) { - double dist = distance(center, points[i]); + double dist = dist_fn_reference(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistance); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, DistanceEuclidean<3>()); + auto q = tree.begin_knn_query(Nq, center, dist_fn); while (q != tree.end()) { // just read the entry auto& e = *q; - ASSERT_EQ(sorted_data[n]._distance, q.distance()); + ASSERT_DOUBLE_EQ(sorted_data[n]._distance, q.distance()); ASSERT_EQ(sorted_data[n]._id, e._i); ASSERT_EQ(points[sorted_data[n]._id], q.first()); ASSERT_EQ(sorted_data[n]._id, q.second()._i); @@ -1092,18 +1102,35 @@ TEST(PhTreeDTest, TestKnnQuery) { } } +TEST(PhTreeDTest, TestKnnQuery_Euclidean) { + const dimension_t DIM = 3; + test_knn_query(DistanceEuclidean<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance(v1, v2); + }); +} + +TEST(PhTreeDTest, TestKnnQuery_L1) { + const dimension_t DIM = 3; + test_knn_query(DistanceL1<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_L1(v1, v2); + }); +} + +TEST(PhTreeDTest, TestKnnQuery_Chebyshev) { + const dimension_t DIM = 3; + test_knn_query(DistanceChebyshev<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_chebyshev(v1, v2); + }); +} + template -struct PhDistanceLongL1 { +struct MyDistance { double operator()(const TestPoint& v1, const TestPoint& v2) const { - double sum = 0; - for (dimension_t i = 0; i < DIM; i++) { - sum += std::abs(v1[i] - v2[i]); - } - return sum; + return distance_L1(v1, v2); }; }; -TEST(PhTreeDTest, TestKnnQueryFilterAndDistanceL1) { +TEST(PhTreeDTest, TestKnnQueryFilterAndCustomDistance) { // deliberately allowing outside of main points range DoubleRng rng(-1500, 1500); const dimension_t dim = 3; @@ -1120,14 +1147,14 @@ TEST(PhTreeDTest, TestKnnQueryFilterAndDistanceL1) { // sort points manually by L1; skip every 2nd point std::vector sorted_data; for (size_t i = 0; i < points.size(); i += 2) { - double dist = distanceL1(center, points[i]); + double dist = MyDistance{}(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistance); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, PhDistanceLongL1(), FilterEvenId()); + auto q = tree.begin_knn_query(Nq, center, MyDistance(), FilterEvenId()); while (q != tree.end()) { // just read the entry auto& e = *q; diff --git a/test/phtree_f_test.cc b/test/phtree_f_test.cc index 02d4896..b73d47a 100644 --- a/test/phtree_f_test.cc +++ b/test/phtree_f_test.cc @@ -78,7 +78,7 @@ double distance(const TestPoint& p1, const TestPoint& p2) { } template -double distanceL1(const TestPoint& p1, const TestPoint& p2) { +double distance_L1(const TestPoint& p1, const TestPoint& p2) { double sum = 0; for (dimension_t i = 0; i < DIM; i++) { sum += std::abs(p1[i] - p2[i]); @@ -86,6 +86,15 @@ double distanceL1(const TestPoint& p1, const TestPoint& p2) { return sum; } +template +double distance_chebyshev(const TestPoint& p1, const TestPoint& p2) { + float sum = 0; + for (dimension_t i = 0; i < DIM; i++) { + sum = std::max(sum, std::abs(p1[i] - p2[i])); + } + return sum; +} + template void generateCube(std::vector>& points, size_t N) { FloatRng rng(-1000, 1000); @@ -782,7 +791,8 @@ TEST(PhTreeFTest, TestWindowQueryFilter) { ASSERT_GE(50, num_e); } -TEST(PhTreeFTest, TestKnnQuery) { +template +void test_knn_query(DIST_TEST dist_fn, DIST_REF dist_fn_reference) { // deliberately allowing outside of main points range FloatRng rng(-1500, 1500); const dimension_t dim = 3; @@ -799,18 +809,18 @@ TEST(PhTreeFTest, TestKnnQuery) { // sort points manually std::vector sorted_data; for (size_t i = 0; i < points.size(); i++) { - double dist = distance(center, points[i]); + double dist = dist_fn_reference(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistance); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, DistanceEuclidean<3>()); + auto q = tree.begin_knn_query(Nq, center, dist_fn); while (q != tree.end()) { // just read the entry auto& e = *q; - ASSERT_EQ(sorted_data[n]._distance, q.distance()); + ASSERT_FLOAT_EQ(sorted_data[n]._distance, q.distance()); ASSERT_EQ(sorted_data[n]._id, e._i); ASSERT_EQ(points[sorted_data[n]._id], q.first()); ASSERT_EQ(sorted_data[n]._id, q.second()._i); @@ -823,18 +833,35 @@ TEST(PhTreeFTest, TestKnnQuery) { } } +TEST(PhTreeFTest, TestKnnQuery_Euclidean) { + const dimension_t DIM = 3; + test_knn_query(DistanceEuclidean<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance(v1, v2); + }); +} + +TEST(PhTreeFTest, TestKnnQuery_L1) { + const dimension_t DIM = 3; + test_knn_query(DistanceL1<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_L1(v1, v2); + }); +} + +TEST(PhTreeFTest, TestKnnQuery_Chebyshev) { + const dimension_t DIM = 3; + test_knn_query(DistanceChebyshev<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_chebyshev(v1, v2); + }); +} + template -struct PhDistanceLongL1 { +struct MyDistance { double operator()(const TestPoint& v1, const TestPoint& v2) const { - double sum = 0; - for (dimension_t i = 0; i < DIM; i++) { - sum += std::abs(v1[i] - v2[i]); - } - return sum; + return distance_L1(v1, v2); }; }; -TEST(PhTreeFTest, TestKnnQueryFilterAndDistanceL1) { +TEST(PhTreeFTest, TestKnnQueryFilterAndCustomDistance) { // deliberately allowing outside of main points range FloatRng rng(-1500, 1500); const dimension_t dim = 3; @@ -851,14 +878,14 @@ TEST(PhTreeFTest, TestKnnQueryFilterAndDistanceL1) { // sort points manually by L1; skip every 2nd point std::vector sorted_data; for (size_t i = 0; i < points.size(); i += 2) { - double dist = distanceL1(center, points[i]); + double dist = MyDistance{}(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistance); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, PhDistanceLongL1(), FilterEvenId()); + auto q = tree.begin_knn_query(Nq, center, MyDistance(), FilterEvenId()); while (q != tree.end()) { // just read the entry auto& e = *q; diff --git a/test/phtree_multimap_d_test.cc b/test/phtree_multimap_d_test.cc index 8c87efb..c06552a 100644 --- a/test/phtree_multimap_d_test.cc +++ b/test/phtree_multimap_d_test.cc @@ -94,7 +94,7 @@ double distance(const TestPoint& p1, const TestPoint& p2) { } template -double distanceL1(const TestPoint& p1, const TestPoint& p2) { +double distance_L1(const TestPoint& p1, const TestPoint& p2) { double sum = 0; for (dimension_t i = 0; i < DIM; i++) { sum += std::abs(p1[i] - p2[i]); @@ -102,6 +102,15 @@ double distanceL1(const TestPoint& p1, const TestPoint& p2) { return sum; } +template +double distance_chebyshev(const TestPoint& p1, const TestPoint& p2) { + double sum = 0; + for (dimension_t i = 0; i < DIM; i++) { + sum = std::max(sum, std::abs(p1[i] - p2[i])); + } + return sum; +} + template void generateCube(std::vector>& points, size_t N) { assert(N % NUM_DUPL == 0); @@ -1068,7 +1077,8 @@ TEST(PhTreeMMDTest, TestWindowQueryFilter) { ASSERT_GE(50, num_e); } -TEST(PhTreeMMDTest, TestKnnQuery) { +template +void test_knn_query(DIST_TEST dist_fn, DIST_REF dist_fn_reference) { // deliberately allowing outside of main points range DoubleRng rng(-1500, 1500); const dimension_t dim = 3; @@ -1085,14 +1095,14 @@ TEST(PhTreeMMDTest, TestKnnQuery) { // sort points manually std::vector sorted_data; for (size_t i = 0; i < points.size(); i++) { - double dist = distance(center, points[i]); + double dist = dist_fn_reference(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistanceAndId); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, DistanceEuclidean<3>()); + auto q = tree.begin_knn_query(Nq, center, dist_fn); while (q != tree.end()) { // just read the entry auto& e = *q; @@ -1109,18 +1119,35 @@ TEST(PhTreeMMDTest, TestKnnQuery) { } } +TEST(PhTreeDTest, TestKnnQuery_Euclidean) { + const dimension_t DIM = 3; + test_knn_query(DistanceEuclidean<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance(v1, v2); + }); +} + +TEST(PhTreeDTest, TestKnnQuery_L1) { + const dimension_t DIM = 3; + test_knn_query(DistanceL1<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_L1(v1, v2); + }); +} + +TEST(PhTreeDTest, TestKnnQuery_Chebyshev) { + const dimension_t DIM = 3; + test_knn_query(DistanceChebyshev<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_chebyshev(v1, v2); + }); +} + template -struct PhDistanceLongL1 { +struct MyDistance { double operator()(const TestPoint& v1, const TestPoint& v2) const { - double sum = 0; - for (dimension_t i = 0; i < DIM; i++) { - sum += std::abs(v1[i] - v2[i]); - } - return sum; + return distance_L1(v1, v2); }; }; -TEST(PhTreeMMDTest, TestKnnQueryFilterAndDistanceL1) { +TEST(PhTreeMMDTest, TestKnnQueryFilterAndCustomDistance) { // deliberately allowing outside of main points range DoubleRng rng(-1500, 1500); const dimension_t dim = 3; @@ -1137,7 +1164,7 @@ TEST(PhTreeMMDTest, TestKnnQueryFilterAndDistanceL1) { // sort points manually by L1; skip every 2nd point std::vector sorted_data; for (size_t i = 0; i < points.size(); i += 2) { - double dist = distanceL1(center, points[i]); + double dist = MyDistance{}(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistanceAndId); @@ -1145,7 +1172,7 @@ TEST(PhTreeMMDTest, TestKnnQueryFilterAndDistanceL1) { std::vector sorted_results; size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, PhDistanceLongL1(), FilterEvenId()); + auto q = tree.begin_knn_query(Nq, center, MyDistance(), FilterEvenId()); while (q != tree.end()) { // just read the entry auto& e = *q; diff --git a/test/phtree_test.cc b/test/phtree_test.cc index e03aae8..095eb52 100644 --- a/test/phtree_test.cc +++ b/test/phtree_test.cc @@ -129,7 +129,7 @@ double distance(const TestPoint& p1, const TestPoint& p2) { } template -double distanceL1(const TestPoint& p1, const TestPoint& p2) { +double distance_L1(const TestPoint& p1, const TestPoint& p2) { double sum = 0; for (dimension_t i = 0; i < DIM; i++) { sum += std::abs(p1[i] - p2[i]); @@ -137,6 +137,15 @@ double distanceL1(const TestPoint& p1, const TestPoint& p2) { return sum; } +template +double distance_chebyshev(const TestPoint& p1, const TestPoint& p2) { + double sum = 0; + for (dimension_t i = 0; i < DIM; i++) { + sum = std::max(sum, (double)std::abs(p1[i] - p2[i])); + } + return sum; +} + template void generateCube(std::vector>& points, size_t N) { IntRng rng(-1000, 1000); @@ -1178,7 +1187,8 @@ TEST(PhTreeTest, TestWindowQueryFilter) { ASSERT_GE(50, num_e); } -TEST(PhTreeTest, TestKnnQuery) { +template +void test_knn_query(DIST_TEST dist_fn, DIST_REF dist_fn_reference, bool check_id = false) { // deliberately allowing outside of main points range IntRng rng(-1500, 1500); const dimension_t dim = 3; @@ -1195,21 +1205,23 @@ TEST(PhTreeTest, TestKnnQuery) { // sort points manually std::vector sorted_data; for (size_t i = 0; i < points.size(); i++) { - double dist = distance(center, points[i]); + double dist = dist_fn_reference(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistance); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, DistanceEuclidean<3>()); + auto q = tree.begin_knn_query(Nq, center, dist_fn); while (q != tree.end()) { - // just read the entry - auto& e = *q; ASSERT_EQ(sorted_data[n]._distance, q.distance()); - ASSERT_EQ(sorted_data[n]._id, e._i); - ASSERT_EQ(points[sorted_data[n]._id], q.first()); - ASSERT_EQ(sorted_data[n]._id, q.second()._i); + if (check_id) { + // With L1 and Chebyshev distances are not unique and IDs may be reordered + auto& e = *q; + ASSERT_EQ(sorted_data[n]._id, e._i); + ASSERT_EQ(points[sorted_data[n]._id], q.first()); + ASSERT_EQ(sorted_data[n]._id, q.second()._i); + } ASSERT_GE(q.distance(), prevDist); prevDist = q.distance(); ++q; @@ -1219,18 +1231,36 @@ TEST(PhTreeTest, TestKnnQuery) { } } +TEST(PhTreeTest, TestKnnQuery_Euclidean) { + const dimension_t DIM = 3; + test_knn_query( + DistanceEuclidean<3>(), + [](const TestPoint& v1, const TestPoint& v2) { return distance(v1, v2); }, + true); +} + +TEST(PhTreeTest, TestKnnQuery_L1) { + const dimension_t DIM = 3; + test_knn_query(DistanceL1<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_L1(v1, v2); + }); +} + +TEST(PhTreeTest, TestKnnQuery_Chebyshev) { + const dimension_t DIM = 3; + test_knn_query(DistanceChebyshev<3>(), [](const TestPoint& v1, const TestPoint& v2) { + return distance_chebyshev(v1, v2); + }); +} + template -struct PhDistanceLongL1 { +struct MyDistance { double operator()(const TestPoint& v1, const TestPoint& v2) const { - double sum = 0; - for (dimension_t i = 0; i < DIM; i++) { - sum += std::abs(v1[i] - v2[i]); - } - return sum; + return distance_L1(v1, v2); }; }; -TEST(PhTreeTest, TestKnnQueryFilterAndDistanceL1) { +TEST(PhTreeTest, TestKnnQueryFilterAndCustomDistance) { // deliberately allowing outside of main points range IntRng rng(-1500, 1500); const dimension_t dim = 3; @@ -1247,14 +1277,14 @@ TEST(PhTreeTest, TestKnnQueryFilterAndDistanceL1) { // sort points manually by L1; skip every 2nd point std::vector sorted_data; for (size_t i = 0; i < points.size(); i += 2) { - double dist = distanceL1(center, points[i]); + double dist = MyDistance{}(center, points[i]); sorted_data.emplace_back(dist, i); } std::sort(sorted_data.begin(), sorted_data.end(), comparePointDistance); size_t n = 0; double prevDist = -1; - auto q = tree.begin_knn_query(Nq, center, PhDistanceLongL1(), FilterEvenId()); + auto q = tree.begin_knn_query(Nq, center, MyDistance(), FilterEvenId()); while (q != tree.end()) { // just read the entry ASSERT_EQ(sorted_data[n]._distance, q.distance());