Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
quadtree_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 <quadtree.H>
41#include <random>
42#include <algorithm>
43#include <unordered_set>
44#include <sstream>
45
46// ============================================================================
47// Basic Functionality Tests
48// ============================================================================
49
51{
52 QuadTree tree(0, 100, 0, 100, 4);
53
54 EXPECT_NE(tree.get_root(), nullptr);
56}
57
59{
60 QuadTree tree(0, 100, 0, 100, 4);
61
62 Point * inserted = tree.insert(Point(50, 50));
63
64 ASSERT_NE(inserted, nullptr);
65 EXPECT_EQ(inserted->get_x(), 50);
66 EXPECT_EQ(inserted->get_y(), 50);
67}
68
70{
71 QuadTree tree(0, 100, 0, 100, 4);
72
73 Point * result1 = tree.insert(Point(-10, 50));
74 Point * result2 = tree.insert(Point(50, 150));
75 Point * result3 = tree.insert(Point(150, 50));
76 Point * result4 = tree.insert(Point(50, -10));
77
78 EXPECT_EQ(result1, nullptr);
79 EXPECT_EQ(result2, nullptr);
80 EXPECT_EQ(result3, nullptr);
81 EXPECT_EQ(result4, nullptr);
82}
83
85{
86 QuadTree tree(0, 100, 0, 100, 4);
87
88 tree.insert(Point(25, 25));
89 tree.insert(Point(75, 75));
90
91 EXPECT_TRUE(tree.contains(Point(25, 25)));
92 EXPECT_TRUE(tree.contains(Point(75, 75)));
93 EXPECT_TRUE(tree.contains(Point(50, 50))); // In bounds but not inserted
94 EXPECT_FALSE(tree.contains(Point(-10, 50))); // Out of bounds
95 EXPECT_FALSE(tree.contains(Point(150, 50))); // Out of bounds
96}
97
99{
100 QuadTree tree(0, 100, 0, 100, 4);
101
102 tree.insert(Point(30, 40));
103 tree.insert(Point(70, 60));
104
105 Point * found1 = tree.search(Point(30, 40));
106 Point * found2 = tree.search(Point(70, 60));
107
108 ASSERT_NE(found1, nullptr);
109 ASSERT_NE(found2, nullptr);
110 EXPECT_EQ(found1->get_x(), 30);
111 EXPECT_EQ(found1->get_y(), 40);
112 EXPECT_EQ(found2->get_x(), 70);
113 EXPECT_EQ(found2->get_y(), 60);
114}
115
117{
118 QuadTree tree(0, 100, 0, 100, 4);
119
120 tree.insert(Point(30, 40));
121
122 Point * found = tree.search(Point(50, 50));
123
124 EXPECT_EQ(found, nullptr);
125}
126
128{
129 QuadTree tree(0, 100, 0, 100, 4);
130
131 tree.insert(Point(25, 25));
132
133 QuadNode * node = tree.search_container_node(Point(25, 25));
134
135 ASSERT_NE(node, nullptr);
136 EXPECT_TRUE(node->is_leaf());
137 EXPECT_NE(node->search_point(Point(25, 25)), nullptr);
138}
139
141{
142 QuadTree tree(0, 100, 0, 100, 4);
143
144 tree.insert(Point(50, 50));
145 EXPECT_NE(tree.search(Point(50, 50)), nullptr);
146
147 tree.remove(Point(50, 50));
148 EXPECT_EQ(tree.search(Point(50, 50)), nullptr);
149}
150
152{
153 QuadTree tree(0, 100, 0, 100, 4);
154
155 tree.insert(Point(50, 50));
156
157 // Should not crash or cause errors
158 tree.remove(Point(30, 30));
159
160 EXPECT_NE(tree.search(Point(50, 50)), nullptr);
161}
162
164{
165 QuadTree tree(0, 100, 0, 100, 4);
166
167 tree.insert(Point(25, 25));
168 tree.insert(Point(75, 75));
169 tree.insert(Point(50, 50));
170
171 tree.empty();
172
173 EXPECT_EQ(tree.search(Point(25, 25)), nullptr);
174 EXPECT_EQ(tree.search(Point(75, 75)), nullptr);
175 EXPECT_EQ(tree.search(Point(50, 50)), nullptr);
176}
177
178// ============================================================================
179// Subdivision and Merging Tests
180// ============================================================================
181
183{
184 QuadTree tree(0, 100, 0, 100, 2); // Max 2 points per node
185
186 tree.insert(Point(25, 25));
187 tree.insert(Point(30, 30));
188
189 // Root should still be a leaf
190 EXPECT_TRUE(tree.get_root()->is_leaf());
191
192 // Third point should trigger split
193 tree.insert(Point(35, 35));
194
195 // Root should now be internal (Gray)
196 EXPECT_FALSE(tree.get_root()->is_leaf());
197 EXPECT_EQ(COLOR(tree.get_root()), QuadNode::Color::Gray);
198}
199
201{
202 QuadTree tree(0, 100, 0, 100, 2);
203
204 // Insert many points in one quadrant to force deep splits
205 for (int i = 1; i <= 10; ++i)
206 {
207 tree.insert(Point(10 + i, 10 + i));
208 }
209
210 // Verify root is internal
211 EXPECT_FALSE(tree.get_root()->is_leaf());
212
213 // Verify tree has multiple levels
214 QuadNode * nw = NW_CHILD(tree.get_root());
215 ASSERT_NE(nw, nullptr);
216
217 // At least one child should be further subdivided
218 bool has_deep_child = false;
219 if (not nw->is_leaf() || (NE_CHILD(tree.get_root()) != nullptr and not NE_CHILD(tree.get_root())->is_leaf()))
220 has_deep_child = true;
221
223}
224
226{
227 QuadTree tree(0, 100, 0, 100, 1);
228
229 // Insert one point in each quadrant
230 tree.insert(Point(25, 25)); // SW
231 tree.insert(Point(75, 25)); // SE
232 tree.insert(Point(25, 75)); // NW
233 tree.insert(Point(75, 75)); // NE
234
235 QuadNode * root = tree.get_root();
236 EXPECT_FALSE(root->is_leaf());
237
238 // All four children should exist and be leaves
239 ASSERT_NE(NW_CHILD(root), nullptr);
240 ASSERT_NE(NE_CHILD(root), nullptr);
241 ASSERT_NE(SW_CHILD(root), nullptr);
242 ASSERT_NE(SE_CHILD(root), nullptr);
243
248}
249
251{
252 QuadTree tree(0, 100, 0, 100, 2);
253
254 // Insert 3 points in DIFFERENT quadrants to trigger split
255 // Points must be in different quadrants to avoid nested splits
256 tree.insert(Point(25, 25)); // SW quadrant
257 tree.insert(Point(75, 25)); // SE quadrant
258 tree.insert(Point(25, 75)); // NW quadrant
259
260 EXPECT_FALSE(tree.get_root()->is_leaf());
261
262 // Remove one point to go below threshold
263 tree.remove(Point(25, 75));
264
265 // Root should merge back to leaf (only 2 points left, threshold is 2)
266 EXPECT_TRUE(tree.get_root()->is_leaf());
267}
268
270{
271 QuadTree tree(0, 100, 0, 100, 2);
272
273 // Insert many points
274 std::vector<Point> points = {
275 Point(10, 10), Point(15, 15), Point(20, 20),
276 Point(80, 80), Point(85, 85), Point(90, 90)
277 };
278
279 for (const auto & p : points)
280 tree.insert(p);
281
282 // Tree should be subdivided
283 EXPECT_FALSE(tree.get_root()->is_leaf());
284
285 // Remove points one by one
286 for (const auto & p : points)
287 {
288 tree.remove(p);
289 }
290
291 // After all removals, root should be a leaf again
292 EXPECT_TRUE(tree.get_root()->is_leaf());
293 EXPECT_EQ(COLOR(tree.get_root()), QuadNode::Color::White);
294}
295
296// ============================================================================
297// Copy Constructor and Assignment Tests
298// ============================================================================
299
301{
302 QuadTree tree1(0, 100, 0, 100, 3);
303
304 tree1.insert(Point(25, 25));
305 tree1.insert(Point(75, 75));
306 tree1.insert(Point(50, 50));
307
309
310 // Verify all points are in the copy
311 EXPECT_NE(tree2.search(Point(25, 25)), nullptr);
312 EXPECT_NE(tree2.search(Point(75, 75)), nullptr);
313 EXPECT_NE(tree2.search(Point(50, 50)), nullptr);
314
315 // Verify they are independent
316 tree1.insert(Point(10, 10));
317 EXPECT_EQ(tree2.search(Point(10, 10)), nullptr);
318}
319
321{
322 QuadTree tree1(0, 100, 0, 100, 3);
323 tree1.insert(Point(25, 25));
324 tree1.insert(Point(75, 75));
325
326 QuadTree tree2(0, 200, 0, 200, 5);
327 tree2.insert(Point(150, 150));
328
329 tree2 = tree1;
330
331 // tree2 should now have tree1's data
332 EXPECT_NE(tree2.search(Point(25, 25)), nullptr);
333 EXPECT_NE(tree2.search(Point(75, 75)), nullptr);
334 EXPECT_EQ(tree2.search(Point(150, 150)), nullptr);
335
336 // Configuration should also be copied
337 EXPECT_EQ(tree2.get_max_num_points_per_node(), 3);
338}
339
341{
342 QuadTree tree(0, 100, 0, 100, 3);
343 tree.insert(Point(50, 50));
344
345 tree = tree;
346
347 // Should still work
348 EXPECT_NE(tree.search(Point(50, 50)), nullptr);
349}
350
351// ============================================================================
352// Stress Tests
353// ============================================================================
354
356{
357 QuadTree tree(0, 1000, 0, 1000, 4);
358
359 std::mt19937 gen(12345);
360 std::uniform_real_distribution<> dis(0, 1000);
361
362 const size_t num_points = 10000;
363 std::vector<Point> points;
364
365 for (size_t i = 0; i < num_points; ++i)
366 {
367 Point p(dis(gen), dis(gen));
368 points.push_back(p);
369 Point * inserted = tree.insert(p);
370 ASSERT_NE(inserted, nullptr);
371 }
372
373 // Verify all points can be found
374 for (const auto & p : points)
375 {
376 EXPECT_NE(tree.search(p), nullptr);
377 }
378}
379
381{
382 QuadTree tree(0, 100, 0, 100, 4);
383
384 std::mt19937 gen(54321);
385 std::uniform_real_distribution<> dis(0, 100);
386
387 const size_t cycles = 100;
388 const size_t points_per_cycle = 50;
389
390 for (size_t cycle = 0; cycle < cycles; ++cycle)
391 {
392 std::vector<Point> points;
393
394 // Insert points
395 for (size_t i = 0; i < points_per_cycle; ++i)
396 {
397 Point p(dis(gen), dis(gen));
398 points.push_back(p);
399 tree.insert(p);
400 }
401
402 // Remove half of them
403 for (size_t i = 0; i < points_per_cycle / 2; ++i)
404 {
405 tree.remove(points[i]);
406 }
407 }
408
409 // Tree should still be functional
410 Point * test = tree.insert(Point(50, 50));
411 EXPECT_NE(test, nullptr);
412}
413
415{
416 QuadTree tree(0, 100, 0, 100, 2);
417
418 // Insert many points in a small region
419 for (int x = 40; x <= 60; ++x)
420 {
421 for (int y = 40; y <= 60; ++y)
422 {
423 tree.insert(Point(x, y));
424 }
425 }
426
427 // Verify all can be found
428 for (int x = 40; x <= 60; ++x)
429 {
430 for (int y = 40; y <= 60; ++y)
431 {
432 EXPECT_NE(tree.search(Point(x, y)), nullptr);
433 }
434 }
435}
436
437// ============================================================================
438// Edge Cases
439// ============================================================================
440
442{
443 QuadTree tree(0, 100, 0, 100, 4);
444
445 // Test exact boundary points
446 Point * p1 = tree.insert(Point(0, 0));
447 Point * p2 = tree.insert(Point(0, 100)); // Upper bound, should fail
448 Point * p3 = tree.insert(Point(100, 0)); // Upper bound, should fail
449 Point * p4 = tree.insert(Point(100, 100)); // Upper bounds, should fail
450
451 EXPECT_NE(p1, nullptr);
452 EXPECT_EQ(p2, nullptr); // Upper bounds are exclusive
453 EXPECT_EQ(p3, nullptr);
454 EXPECT_EQ(p4, nullptr);
455}
456
458{
459 QuadTree tree(0, 100, 0, 100, 1);
460
461 // Insert point exactly at midpoint
462 tree.insert(Point(50, 50));
463
464 // Insert more to trigger split
465 tree.insert(Point(25, 25));
466
467 // Midpoint should be in one of the quadrants
468 EXPECT_NE(tree.search(Point(50, 50)), nullptr);
469}
470
472{
473 QuadTree tree(0, 100, 0, 100, 1);
474
475 tree.insert(Point(25, 25));
476 tree.insert(Point(30, 30));
477
478 // Should trigger immediate split
479 EXPECT_FALSE(tree.get_root()->is_leaf());
480}
481
483{
484 QuadTree tree(0, 1, 0, 1, 2);
485
486 tree.insert(Point(0.1, 0.1));
487 tree.insert(Point(0.9, 0.9));
488
489 EXPECT_NE(tree.search(Point(0.1, 0.1)), nullptr);
490 EXPECT_NE(tree.search(Point(0.9, 0.9)), nullptr);
491}
492
494{
495 QuadTree tree(-1e9, 1e9, -1e9, 1e9, 4);
496
497 tree.insert(Point(0, 0));
498 tree.insert(Point(1e8, 1e8));
499 tree.insert(Point(-5e8, -5e8));
500
501 EXPECT_NE(tree.search(Point(0, 0)), nullptr);
502 EXPECT_NE(tree.search(Point(1e8, 1e8)), nullptr);
503 EXPECT_NE(tree.search(Point(-5e8, -5e8)), nullptr);
504}
505
506// ============================================================================
507// Traversal Tests
508// ============================================================================
509
511{
512 QuadTree tree(0, 100, 0, 100, 2);
513
514 for (int i = 0; i < 10; ++i)
515 {
516 tree.insert(Point(10 * i, 10 * i));
517 }
518
519 size_t node_count = 0;
520 tree.for_each([&node_count](QuadNode * node) {
521 ++node_count;
522 EXPECT_NE(node, nullptr);
523 });
524
525 EXPECT_GT(node_count, 0);
526}
527
529{
530 QuadTree tree(0, 100, 0, 100, 2);
531
532 tree.insert(Point(10, 10));
533 tree.insert(Point(20, 20));
534 tree.insert(Point(80, 80));
535 tree.insert(Point(90, 90));
536
537 size_t leaf_count = 0;
538 tree.for_each([&leaf_count](QuadNode * node) {
539 if (node->is_leaf())
540 ++leaf_count;
541 });
542
544}
545
546// ============================================================================
547// Fuzz Tests
548// ============================================================================
549
551{
552 QuadTree tree(0, 1000, 0, 1000, 4);
553
554 std::mt19937 gen(99999);
555 // Use integer coordinates to avoid mpq_class precision issues with double conversion
556 std::uniform_int_distribution<int> coord_dis(0, 999);
557 std::uniform_int_distribution<> op_dis(0, 2);
558
559 // Store points directly to avoid string conversion precision issues
560 std::vector<Point> inserted_points;
561
562 for (int i = 0; i < 1000; ++i)
563 {
564 int op = op_dis(gen);
565
566 if (op == 0 || op == 1) // Insert
567 {
569 Point * result = tree.insert(p);
570 if (result != nullptr)
571 inserted_points.push_back(p);
572 }
573 else if (op == 2 && not inserted_points.empty()) // Remove
574 {
575 // Pick a random inserted point
576 size_t idx = gen() % inserted_points.size();
578
579 tree.remove(to_remove);
581 }
582 }
583
584 // Verify consistency
585 for (const auto & p : inserted_points)
586 EXPECT_NE(tree.search(p), nullptr)
587 << "Point (" << p.get_x() << ", " << p.get_y() << ") should be in tree";
588}
589
590// ============================================================================
591// Main
592// ============================================================================
593
594int main(int argc, char **argv)
595{
596 ::testing::InitGoogleTest(&argc, argv);
597 return RUN_ALL_TESTS();
598}
599
int main()
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
void empty() noexcept
empty the list
Definition htlist.H:1689
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Rectangular point in the plane.
Definition point.H:156
Node for QuadTree spatial data structure.
Definition quadnode.H:94
Point * search_point(const Point &p) noexcept
Search for a point in this node.
Definition quadnode.H:508
bool is_leaf() const noexcept
Check if this node is a leaf (has no children).
Definition quadnode.H:375
QuadTree - Hierarchical spatial index for 2D points.
Definition quadtree.H:126
void empty(Node *&r) noexcept
Recursively delete all nodes.
Definition quadtree.H:255
void for_each(Op &op)
Apply an operation to each node in the tree.
Definition quadtree.H:542
void remove(const Point &p)
Remove a point from the tree.
Definition quadtree.H:502
Point * search(const Point &p) noexcept
Search for a point in the tree.
Definition quadtree.H:461
Node * search_container_node(const Point &p) noexcept
Find the leaf node containing a point.
Definition quadtree.H:479
bool contains(const Point &p) const noexcept
Check if a point is within the tree's region.
Definition quadtree.H:424
Point * insert(Node *&r, const Point &p)
Recursive insert helper.
Definition quadtree.H:237
size_t get_max_num_points_per_node() const noexcept
Get the maximum points per leaf node.
Definition quadtree.H:414
Node * get_root() noexcept
Get the root node.
Definition quadtree.H:396
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
#define TEST(name)
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
static mpfr_t y
Definition mpfr_mul_d.c:3
static bool is_leaf(BinNode< std::string > *p) noexcept
Definition Huffman.H:111
DynList< T > maps(const C &c, Op op)
Classic map operation.
#define COLOR(p)
Definition quadnode.H:58
#define SE_CHILD(p)
Definition quadnode.H:57
#define NE_CHILD(p)
Definition quadnode.H:55
#define NW_CHILD(p)
Definition quadnode.H:54
#define SW_CHILD(p)
Definition quadnode.H:56
QuadTree spatial data structure for efficient 2D point indexing.
void test(unsigned long n, gsl_rng *r)