Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
k2tree_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
39#include <gtest/gtest.h>
40#include <tpl_2dtree.H>
41#include <random>
42#include <algorithm>
43#include <vector>
44#include <set>
45#include <cmath>
46
48
49// ============================================================================
50// Basic Functionality Tests
51// ============================================================================
52
54{
55 K2TreeInt tree(0, 0, 100, 100);
56
57 EXPECT_TRUE(tree.is_empty());
58 EXPECT_EQ(tree.size(), 0);
59}
60
62{
63 K2TreeInt tree(0, 0, 100, 100);
64
65 Point * inserted = tree.insert(Point(50, 50));
66
67 ASSERT_NE(inserted, nullptr);
68 EXPECT_EQ(tree.size(), 1);
69 EXPECT_FALSE(tree.is_empty());
70}
71
73{
74 K2TreeInt tree(0, 0, 100, 100);
75
76 tree.insert(Point(25, 25));
77 tree.insert(Point(75, 75));
78 tree.insert(Point(50, 50));
79
80 EXPECT_EQ(tree.size(), 3);
81}
82
84{
85 K2TreeInt tree(0, 0, 100, 100);
86
87 Point * first = tree.insert(Point(50, 50));
88 Point * second = tree.insert(Point(50, 50));
89
90 ASSERT_NE(first, nullptr);
91 EXPECT_EQ(second, nullptr); // Duplicates not allowed
92 EXPECT_EQ(tree.size(), 1);
93}
94
96{
97 K2TreeInt tree(0, 0, 100, 100);
98
99 tree.insert(Point(30, 40));
100 tree.insert(Point(70, 60));
101
102 EXPECT_TRUE(tree.contains(Point(30, 40)));
103 EXPECT_TRUE(tree.contains(Point(70, 60)));
104}
105
107{
108 K2TreeInt tree(0, 0, 100, 100);
109
110 tree.insert(Point(30, 40));
111
112 EXPECT_FALSE(tree.contains(Point(50, 50)));
113 EXPECT_FALSE(tree.contains(Point(70, 60)));
114}
115
117{
118 K2TreeInt tree(0, 0, 100, 100);
119
120 EXPECT_FALSE(tree.contains(Point(50, 50)));
121}
122
123// ============================================================================
124// Insertion Patterns Tests
125// ============================================================================
126
128{
129 K2TreeInt tree(0, 0, 100, 100);
130
131 for (int i = 0; i < 10; ++i)
132 {
133 for (int j = 0; j < 10; ++j)
134 {
135 tree.insert(Point(i * 10, j * 10));
136 }
137 }
138
139 EXPECT_EQ(tree.size(), 100);
140
141 // Verify all points
142 for (int i = 0; i < 10; ++i)
143 {
144 for (int j = 0; j < 10; ++j)
145 {
146 EXPECT_TRUE(tree.contains(Point(i * 10, j * 10)));
147 }
148 }
149}
150
152{
153 K2TreeInt tree(0, 0, 100, 100);
154
155 for (int i = 9; i >= 0; --i)
156 {
157 for (int j = 9; j >= 0; --j)
158 {
159 tree.insert(Point(i * 10, j * 10));
160 }
161 }
162
163 EXPECT_EQ(tree.size(), 100);
164}
165
167{
168 K2TreeInt tree(0, 0, 1000, 1000);
169
170 std::mt19937 gen(12345);
171 std::uniform_real_distribution<> dis(0, 1000);
172
173 std::vector<Point> points;
174 for (int i = 0; i < 100; ++i)
175 {
176 Point p(dis(gen), dis(gen));
177 points.push_back(p);
178 tree.insert(p);
179 }
180
181 EXPECT_EQ(tree.size(), points.size());
182
183 for (const auto & p : points)
184 {
185 EXPECT_TRUE(tree.contains(p));
186 }
187}
188
190{
191 K2TreeInt tree(0, 0, 1000, 1000);
192
193 // Insert cluster in one region
194 for (int i = 0; i < 20; ++i)
195 {
196 tree.insert(Point(10 + i, 10 + i));
197 }
198
199 // Insert cluster in another region
200 for (int i = 0; i < 20; ++i)
201 {
202 tree.insert(Point(900 + i, 900 + i));
203 }
204
205 EXPECT_EQ(tree.size(), 40);
206}
207
208// ============================================================================
209// Nearest Neighbor Tests
210// ============================================================================
211
213{
214 K2TreeInt tree(0, 0, 100, 100);
215
216 tree.insert(Point(50, 50));
217
218 Point nearest = tree.nearest(Point(55, 55));
219
220 EXPECT_EQ(nearest.get_x(), 50);
221 EXPECT_EQ(nearest.get_y(), 50);
222}
223
225{
226 K2TreeInt tree(0, 0, 100, 100);
227
228 tree.insert(Point(10, 10));
229 tree.insert(Point(50, 50));
230 tree.insert(Point(90, 90));
231
232 // Query near (50, 50)
233 Point nearest = tree.nearest(Point(52, 48));
234 EXPECT_EQ(nearest.get_x(), 50);
235 EXPECT_EQ(nearest.get_y(), 50);
236
237 // Query near (10, 10)
238 nearest = tree.nearest(Point(12, 12));
239 EXPECT_EQ(nearest.get_x(), 10);
240 EXPECT_EQ(nearest.get_y(), 10);
241
242 // Query near (90, 90)
243 nearest = tree.nearest(Point(88, 92));
244 EXPECT_EQ(nearest.get_x(), 90);
245 EXPECT_EQ(nearest.get_y(), 90);
246}
247
249{
250 K2TreeInt tree(0, 0, 100, 100);
251
252 Point nearest = tree.nearest(Point(50, 50));
253
254 // Should return NullPoint
255 EXPECT_EQ(nearest, NullPoint);
256}
257
259{
260 K2TreeInt tree(0, 0, 100, 100);
261
262 tree.insert(Point(50, 50));
263 tree.insert(Point(75, 75));
264
265 Point nearest = tree.nearest(Point(50, 50));
266
267 EXPECT_EQ(nearest.get_x(), 50);
268 EXPECT_EQ(nearest.get_y(), 50);
269}
270
272{
273 K2TreeInt tree(0, 0, 100, 100);
274
275 // Insert grid
276 for (int i = 0; i <= 100; i += 10)
277 {
278 for (int j = 0; j <= 100; j += 10)
279 {
280 tree.insert(Point(i, j));
281 }
282 }
283
284 // Query between grid points
285 Point nearest = tree.nearest(Point(43, 57));
286
287 // Should find (40, 60) or (40, 50) or (50, 60) or (50, 50)
288 Geom_Number dist = nearest.distance_with(Point(43, 57));
289 EXPECT_LT(dist, 10); // Should be within one grid cell
290}
291
292// ============================================================================
293// Range Query Tests
294// ============================================================================
295
297{
298 K2TreeInt tree(0, 0, 100, 100);
299
300 Rectangle rect(20, 20, 80, 80);
301 DynList<Point> result;
302 tree.range(rect, &result);
303
304 EXPECT_TRUE(result.is_empty());
305}
306
308{
309 K2TreeInt tree(0, 0, 100, 100);
310
311 tree.insert(Point(30, 30));
312 tree.insert(Point(50, 50));
313 tree.insert(Point(70, 70));
314
315 Rectangle rect(0, 0, 100, 100);
316 DynList<Point> result;
317 tree.range(rect, &result);
318
319 EXPECT_EQ(result.size(), 3);
320}
321
323{
324 K2TreeInt tree(0, 0, 100, 100);
325
326 tree.insert(Point(10, 10));
327 tree.insert(Point(90, 90));
328
329 Rectangle rect(40, 40, 60, 60);
330 DynList<Point> result;
331 tree.range(rect, &result);
332
333 EXPECT_TRUE(result.is_empty());
334}
335
337{
338 K2TreeInt tree(0, 0, 100, 100);
339
340 tree.insert(Point(25, 25)); // Inside
341 tree.insert(Point(50, 50)); // Inside
342 tree.insert(Point(75, 75)); // Inside
343 tree.insert(Point(10, 10)); // Outside
344 tree.insert(Point(90, 90)); // Outside
345
346 Rectangle rect(20, 20, 80, 80);
347 DynList<Point> result;
348 tree.range(rect, &result);
349
350 EXPECT_EQ(result.size(), 3);
351
352 // Verify the right points are in the result
353 bool found_25 = false, found_50 = false, found_75 = false;
354 for (const auto & p : result)
355 {
356 if (p == Point(25, 25)) found_25 = true;
357 if (p == Point(50, 50)) found_50 = true;
358 if (p == Point(75, 75)) found_75 = true;
359 }
360
364}
365
367{
368 K2TreeInt tree(0, 0, 100, 100);
369
370 tree.insert(Point(20, 20)); // On boundary
371 tree.insert(Point(80, 80)); // On boundary
372 tree.insert(Point(50, 50)); // Inside
373
374 Rectangle rect(20, 20, 80, 80);
375 DynList<Point> result;
376 tree.range(rect, &result);
377
378 // Boundary behavior depends on Rectangle::contains implementation
379 EXPECT_GE(result.size(), 1); // At least the inside point
380 EXPECT_LE(result.size(), 3);
381}
382
383// ============================================================================
384// Stress Tests
385// ============================================================================
386
388{
389 K2TreeInt tree(0, 0, 10000, 10000);
390
391 std::mt19937 gen(54321);
392 std::uniform_real_distribution<> dis(0, 10000);
393
394 const size_t num_points = 10000;
395 std::vector<Point> points;
396
397 for (size_t i = 0; i < num_points; ++i)
398 {
399 Point p(dis(gen), dis(gen));
400 points.push_back(p);
401 tree.insert(p);
402 // May return nullptr for duplicates
403 }
404
405 // Size should be <= num_points due to possible duplicates
406 EXPECT_LE(tree.size(), num_points);
407 EXPECT_GT(tree.size(), num_points * 0.9); // Expect most to be unique
408}
409
411{
412 K2TreeInt tree(0, 0, 1000, 1000);
413
414 std::mt19937 gen(99999);
415 std::uniform_real_distribution<> dis(0, 1000);
416
417 // Insert points
418 for (int i = 0; i < 1000; ++i)
419 {
420 tree.insert(Point(dis(gen), dis(gen)));
421 }
422
423 // Perform many nearest queries
424 for (int i = 0; i < 1000; ++i)
425 {
426 Point query(dis(gen), dis(gen));
427 Point nearest = tree.nearest(query);
428
429 EXPECT_NE(nearest, NullPoint);
430 }
431}
432
434{
435 K2TreeInt tree(0, 0, 1000, 1000);
436
437 std::mt19937 gen(11111);
438 std::uniform_real_distribution<> dis(0, 1000);
439
440 // Insert points
441 for (int i = 0; i < 1000; ++i)
442 {
443 tree.insert(Point(dis(gen), dis(gen)));
444 }
445
446 // Perform many range queries
447 for (int i = 0; i < 100; ++i)
448 {
449 double x1 = dis(gen), y1 = dis(gen);
450 double x2 = dis(gen), y2 = dis(gen);
451
452 if (x1 > x2) std::swap(x1, x2);
453 if (y1 > y2) std::swap(y1, y2);
454
455 Rectangle rect(x1, y1, x2, y2);
456 DynList<Point> result;
457 tree.range(rect, &result);
458
459 // Just verify it doesn't crash
460 EXPECT_GE(result.size(), 0);
461 }
462}
463
465{
466 K2TreeInt tree(0, 0, 1000, 1000);
467
468 // Dense region: [100, 200] × [100, 200]
469 for (int i = 100; i <= 200; i += 2)
470 {
471 for (int j = 100; j <= 200; j += 2)
472 {
473 tree.insert(Point(i, j));
474 }
475 }
476
477 // Sparse region: [700, 900] × [700, 900]
478 for (int i = 700; i <= 900; i += 50)
479 {
480 for (int j = 700; j <= 900; j += 50)
481 {
482 tree.insert(Point(i, j));
483 }
484 }
485
486 // Query dense region
487 Rectangle dense_rect(100, 100, 200, 200);
491
492 // Query sparse region
493 Rectangle sparse_rect(700, 700, 900, 900);
498}
499
500// ============================================================================
501// Edge Cases
502// ============================================================================
503
505{
506 K2TreeInt tree(0, 0, 100, 100);
507
508 // Insert points along a line
509 for (int i = 0; i <= 100; i += 10)
510 {
511 tree.insert(Point(i, i)); // Diagonal line
512 }
513
514 EXPECT_EQ(tree.size(), 11);
515
516 // Verify all can be found
517 for (int i = 0; i <= 100; i += 10)
518 {
519 EXPECT_TRUE(tree.contains(Point(i, i)));
520 }
521}
522
524{
525 K2TreeInt tree(0, 0, 100, 100);
526
527 for (int i = 0; i <= 100; i += 10)
528 {
529 tree.insert(Point(50, i));
530 }
531
532 EXPECT_EQ(tree.size(), 11);
533}
534
536{
537 K2TreeInt tree(0, 0, 100, 100);
538
539 for (int i = 0; i <= 100; i += 10)
540 {
541 tree.insert(Point(i, 50));
542 }
543
544 EXPECT_EQ(tree.size(), 11);
545}
546
548{
549 K2TreeInt tree(0, 0, 1, 1);
550
551 tree.insert(Point(0.1, 0.1));
552 tree.insert(Point(0.100001, 0.100001));
553 tree.insert(Point(0.9, 0.9));
554
555 EXPECT_EQ(tree.size(), 3);
556}
557
559{
560 K2TreeInt tree(-100, -100, 100, 100);
561
562 tree.insert(Point(-50, -50));
563 tree.insert(Point(0, 0));
564 tree.insert(Point(50, 50));
565
566 EXPECT_EQ(tree.size(), 3);
567 EXPECT_TRUE(tree.contains(Point(-50, -50)));
568 EXPECT_TRUE(tree.contains(Point(0, 0)));
569 EXPECT_TRUE(tree.contains(Point(50, 50)));
570}
571
572// ============================================================================
573// Correctness Verification Tests
574// ============================================================================
575
577{
578 K2TreeInt tree(0, 0, 100, 100);
579
580 std::vector<Point> points = {
581 Point(10, 10),
582 Point(50, 50),
583 Point(90, 90),
584 Point(30, 70),
585 Point(70, 30)
586 };
587
588 for (const auto & p : points)
589 tree.insert(p);
590
591 Point query(45, 45);
592 Point nearest = tree.nearest(query);
593
594 // Verify it's actually the nearest
595 Geom_Number min_dist = nearest.distance_with(query);
596 for (const auto & p : points)
597 {
598 Geom_Number dist = p.distance_with(query);
599 EXPECT_GE(dist, min_dist - 1e-9); // Allow tiny floating point error
600 }
601}
602
604{
605 K2TreeInt tree(0, 0, 100, 100);
606
607 std::mt19937 gen(77777);
608 std::uniform_real_distribution<> dis(0, 100);
609
610 std::vector<Point> all_points;
611 for (int i = 0; i < 100; ++i)
612 {
613 Point p(dis(gen), dis(gen));
614 all_points.push_back(p);
615 tree.insert(p);
616 }
617
618 Rectangle rect(25, 25, 75, 75);
619 DynList<Point> result;
620 tree.range(rect, &result);
621
622 // Verify all points in result are actually in rectangle
623 for (const auto & p : result)
624 {
625 EXPECT_TRUE(rect.contains(p));
626 }
627
628 // Verify all points in rectangle are in result
629 for (const auto & p : all_points)
630 {
631 if (rect.contains(p))
632 {
633 bool found = false;
634 for (const auto & r : result)
635 {
636 if (r == p)
637 {
638 found = true;
639 break;
640 }
641 }
642 EXPECT_TRUE(found) << "Point in rectangle not found in result";
643 }
644 }
645}
646
647// ============================================================================
648// Fuzz Testing
649// ============================================================================
650
652{
653 K2TreeInt tree(0, 0, 10000, 10000);
654
655 std::mt19937 gen(31415);
656 std::uniform_real_distribution<> coord_dis(0, 10000);
657 std::uniform_int_distribution<> op_dis(0, 2);
658
659 std::set<std::pair<double, double>> inserted;
660
661 for (int i = 0; i < 1000; ++i)
662 {
663 int op = op_dis(gen);
664
665 if (op == 0) // Insert
666 {
668 Point * result = tree.insert(p);
669 if (result != nullptr)
670 {
671 inserted.insert(std::make_pair(static_cast<double>(p.get_x().get_d()),
672 static_cast<double>(p.get_y().get_d())));
673 }
674 }
675 else if (op == 1) // Nearest
676 {
677 Point query(coord_dis(gen), coord_dis(gen));
678 Point nearest = tree.nearest(query);
679
680 if (not tree.is_empty())
681 EXPECT_NE(nearest, NullPoint);
682 }
683 else // Range
684 {
685 double x1 = coord_dis(gen), y1 = coord_dis(gen);
686 double x2 = coord_dis(gen), y2 = coord_dis(gen);
687 if (x1 > x2) std::swap(x1, x2);
688 if (y1 > y2) std::swap(y1, y2);
689
690 Rectangle rect(x1, y1, x2, y2);
691 DynList<Point> result;
692 tree.range(rect, &result);
693 }
694 }
695
696 // Verify all inserted points can be found
697 for (const auto & [x, y] : inserted)
698 {
699 EXPECT_TRUE(tree.contains(Point(x, y)));
700 }
701}
702
703// ============================================================================
704// Main
705// ============================================================================
706
707int main(int argc, char **argv)
708{
709 ::testing::InitGoogleTest(&argc, argv);
710 return RUN_ALL_TESTS();
711}
712
int main()
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
2D k-d tree for efficient spatial point operations.
Definition tpl_2dtree.H:132
constexpr size_t size() const noexcept
Get the number of points in the tree.
Definition tpl_2dtree.H:214
static void range(Node *root, const Rectangle &rect, DynList< Point > *q)
Recursively find points within a rectangle.
Definition tpl_2dtree.H:416
Point nearest(const Point &p) noexcept
Find the nearest point to a query point.
Definition tpl_2dtree.H:523
bool contains(const Point &p) const noexcept
Check if the tree contains a point.
Definition tpl_2dtree.H:404
constexpr bool is_empty() const noexcept
Check if the tree is empty.
Definition tpl_2dtree.H:211
Point * insert(const Point &p)
Insert a point into the tree.
Definition tpl_2dtree.H:343
Rectangular point in the plane.
Definition point.H:156
const Geom_Number & get_y() const
Returns y value.
Definition point.H:226
Geom_Number distance_with(const Point &p) const
Returns the Euclidean distance between this and p.
Definition point.H:880
const Geom_Number & get_x() const
Returns x value.
Definition point.H:221
bool contains(const Point &p) const
Definition point.H:1069
#define TEST(name)
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4103
static mpfr_t y
Definition mpfr_mul_d.c:3
DynList< T > maps(const C &c, Op op)
Classic map operation.
const Point NullPoint
Definition point.C:41
2D k-d tree implementation for spatial point indexing.