Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
r_star_tree_test.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 https://github.com/lrleon/Aleph-w
6
7 This file is part of Aleph-w library
8
9 Copyright (c) 2002-2026 Leandro Rabindranath Leon
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28*/
29
35#include <gtest/gtest.h>
36
37#include <algorithm>
38#include <memory>
39#include <random>
40#include <utility>
41
42#include <ahSort.H>
43#include <tpl_r_star_tree.H>
44
46
47using namespace Aleph;
48using namespace Aleph::test_helpers;
49
50namespace
51{
52 Rectangle rect(const int x1, const int y1, const int x2, const int y2)
53 {
55 Geom_Number(x2), Geom_Number(y2));
56 }
57
58 Point pt(const int x, const int y)
59 {
60 return Point(Geom_Number(x), Geom_Number(y));
61 }
62
63 template <typename Tree>
64 Array<int> sorted_intersects(const Tree &tree, const Rectangle &q)
65 {
66 Array<int> hits = tree.search_intersects(q);
68 return hits;
69 }
70
71 Array<int> brute_intersects(const Array<std::pair<Rectangle, int>> &ref,
72 const Rectangle &q)
73 {
75 for (const auto &[b, id] : ref)
76 if (b.intersects(q))
77 out.append(id);
79 return out;
80 }
81
82 Array<int> brute_contains(const Array<std::pair<Rectangle, int>> &ref,
83 const Point &p)
84 {
86 for (const auto &[b, id] : ref)
87 if (b.contains(p))
88 out.append(id);
90 return out;
91 }
92
93 template <typename Tree>
94 Array<int> sorted_contains(const Tree &tree, const Point &p)
95 {
96 Array<int> hits = tree.search_contains(p);
98 return hits;
99 }
100
101 // Randomized parity + per-step invariant check for a given fanout.
102 template <size_t Max, size_t Min>
103 void randomized_parity(const unsigned seed, const int iters, const int space,
104 const int ext)
105 {
106 std::mt19937 rng(seed);
107 std::uniform_int_distribution<int> op_dist(0, 2);
108 std::uniform_int_distribution<int> coord(0, space);
109 std::uniform_int_distribution<int> extent(0, ext);
110
113 int next_id = 0;
114
115 auto random_rect = [&]()
116 {
117 const int x1 = coord(rng);
118 const int y1 = coord(rng);
119 return rect(x1, y1, x1 + extent(rng), y1 + extent(rng));
120 };
121
122 for (int iter = 0; iter < iters; ++iter)
123 {
124 const int op = ref.is_empty() ? 0 : op_dist(rng);
125 if (op == 0)
126 {
127 const Rectangle b = random_rect();
128 const int id = next_id++;
129 tree.insert(b, id);
130 ref.append(std::make_pair(b, id));
131 }
132 else if (op == 1)
133 {
134 std::uniform_int_distribution<size_t> pick(0, ref.size() - 1);
135 const size_t k = pick(rng);
136 ASSERT_TRUE(tree.erase(ref(k).first, ref(k).second));
137 std::swap(ref(k), ref(ref.size() - 1)); // swap-and-pop; order is irrelevant here
138 [[maybe_unused]] const auto popped = ref.remove_last();
139 }
140 else
141 {
142 const Rectangle q = random_rect();
144 const Point p = pt(coord(rng), coord(rng));
145 EXPECT_EQ(sorted_contains(tree, p), brute_contains(ref, p));
146 }
147
148 ASSERT_TRUE(tree.verify()) << "invariant broken at iter " << iter;
149 ASSERT_EQ(tree.size(), ref.size());
150 }
151
152 for (int x = 0; x <= space; x += 9)
153 for (int y = 0; y <= space; y += 9)
154 {
155 const Rectangle q = rect(x, y, x + ext, y + ext);
157 ASSERT_EQ(sorted_contains(tree, pt(x, y)), brute_contains(ref, pt(x, y)));
158 }
159 }
160}
161
163{
164 RStarTree<int> tree;
165 EXPECT_TRUE(tree.is_empty());
166 EXPECT_EQ(tree.size(), 0u);
167 EXPECT_EQ(tree.height(), 0u);
168 EXPECT_TRUE(tree.verify());
169 EXPECT_FALSE(tree.erase(rect(0, 0, 1, 1), 5));
170}
171
173{
174 RStarTree<int> tree;
175 tree.insert(rect(0, 0, 2, 2), 42);
176 EXPECT_EQ(tree.size(), 1u);
177 EXPECT_EQ(sorted_intersects(tree, rect(1, 1, 5, 5)), build_array<int>(42));
178 EXPECT_TRUE(tree.erase(rect(0, 0, 2, 2), 42));
179 EXPECT_TRUE(tree.is_empty());
180 EXPECT_EQ(tree.height(), 0u);
181 EXPECT_TRUE(tree.verify());
182}
183
185{
186 // Default fanout (16/8): forced reinsertion of ~30% of a leaf triggers on the
187 // first leaf overflow of each insert once the tree has depth >= 2.
188 RStarTree<int> tree; // Max=16, Min=8
189 for (int i = 0; i < 500; ++i)
190 {
191 tree.insert(rect(i % 50, i / 50, i % 50 + 3, i / 50 + 3), i);
192 ASSERT_TRUE(tree.verify());
193 }
194 EXPECT_EQ(tree.size(), 500u);
195 EXPECT_GT(tree.height(), 1u);
196
197 // Every inserted box must still be found by an intersecting query.
198 for (int i = 0; i < 500; ++i)
199 {
200 const Rectangle b = rect(i % 50, i / 50, i % 50 + 3, i / 50 + 3);
201 const Array<int> hits = sorted_intersects(tree, b);
202 EXPECT_TRUE(std::find(hits.begin(), hits.end(), i) != hits.end());
203 }
204}
205
207{
208 RStarTree<int> tree; // Max=16, Min=8; same insert pattern as forced reinsertion
209 for (int i = 0; i < 500; ++i)
210 tree.insert(rect(i % 50, i / 50, i % 50 + 3, i / 50 + 3), i);
211
212 const auto snap = tree.debug_snapshot();
213 ASSERT_LT(snap.root, snap.nodes.size());
214 const size_t total_entries = check_snapshot_node(snap, snap.root, 0);
216}
217
219{
221 const Rectangle box = rect(3, 3, 6, 6);
222 for (int i = 0; i < 25; ++i)
223 tree.insert(box, i);
224 ASSERT_TRUE(tree.verify());
225 EXPECT_EQ(sorted_intersects(tree, rect(4, 4, 5, 5)).size(), 25u);
226
227 ASSERT_TRUE(tree.erase(box, 10));
228 ASSERT_TRUE(tree.verify());
229 EXPECT_EQ(sorted_intersects(tree, box).size(), 24u);
230}
231
233{
235 for (int i = 0; i < 40; ++i)
236 tree.insert(rect(i, 5, i, 5), i); // points
237 for (int i = 0; i < 20; ++i)
238 tree.insert(rect(0, i, 40, i), 100 + i); // horizontal segments
239 ASSERT_TRUE(tree.verify());
240 EXPECT_EQ(tree.size(), 60u);
241
242 const Array<int> hits = sorted_contains(tree, pt(10, 5));
243 EXPECT_TRUE(std::find(hits.begin(), hits.end(), 10) != hits.end());
244 EXPECT_TRUE(std::find(hits.begin(), hits.end(), 105) != hits.end());
245}
246
248{
250 for (int i = 0; i < 60; ++i)
251 original.insert(rect(i, i, i + 2, i + 2), i);
252
254 ASSERT_TRUE(copy.verify());
255 EXPECT_EQ(copy.size(), original.size());
256 for (int i = 0; i < 30; ++i)
257 ASSERT_TRUE(original.erase(rect(i, i, i + 2, i + 2), i));
258 EXPECT_EQ(original.size(), 30u);
259 EXPECT_EQ(copy.size(), 60u);
260 ASSERT_TRUE(original.verify());
261 ASSERT_TRUE(copy.verify());
262
263 RStarTree<int, 6, 3> moved = std::move(copy);
264 EXPECT_EQ(moved.size(), 60u);
265 EXPECT_TRUE(moved.verify());
266 EXPECT_TRUE(copy.is_empty()); // NOLINT(bugprone-use-after-move)
267 EXPECT_TRUE(copy.verify());
268
269 copy.insert(rect(-5, -5, -4, -4), -5); // NOLINT(bugprone-use-after-move)
270 EXPECT_EQ(copy.size(), 1u);
271 EXPECT_TRUE(copy.verify());
272}
273
275{
277 for (int i = 0; i < 40; ++i)
278 tree.insert(rect(i, i, i + 1, i + 1), std::make_unique<int>(i));
279 ASSERT_TRUE(tree.verify());
280 EXPECT_EQ(tree.size(), 40u);
281
282 int seen = 0;
283 tree.for_each_intersecting(rect(0, 0, 100, 100),
284 [&](const Rectangle &, const std::unique_ptr<int> &) { ++seen; });
285 EXPECT_EQ(seen, 40);
286}
287
289{
292 for (int i = 0; i < 100; ++i)
293 {
294 const Rectangle b = rect(i % 20, i / 20, i % 20 + 3, i / 20 + 2);
295 source.insert(b, i);
296 ref.append(std::make_pair(b, i));
297 }
298 ASSERT_TRUE(source.verify());
299
301 assigned = source;
302 EXPECT_EQ(assigned.size(), source.size());
303 EXPECT_TRUE(assigned.verify());
304
306 moved = std::move(assigned);
307 EXPECT_EQ(moved.size(), 100u);
308 EXPECT_TRUE(moved.verify());
309 EXPECT_TRUE(assigned.is_empty()); // NOLINT(bugprone-use-after-move)
310 EXPECT_TRUE(assigned.verify());
311
312 assigned.insert(rect(-10, -10, -9, -9), -10); // NOLINT(bugprone-use-after-move)
313 EXPECT_EQ(assigned.size(), 1u);
314 EXPECT_TRUE(assigned.verify());
315 assigned.clear();
316
317 for (const auto &[b, id] : ref)
318 {
319 ASSERT_TRUE(moved.erase(b, id));
320 ASSERT_TRUE(moved.verify());
321 }
322 EXPECT_TRUE(moved.is_empty());
323 EXPECT_EQ(moved.height(), 0u);
324
325 source.clear();
326 EXPECT_TRUE(source.is_empty());
327 EXPECT_TRUE(source.verify());
328 source.insert(rect(-20, -20, -19, -19), -20);
329 EXPECT_EQ(source.size(), 1u);
330 EXPECT_TRUE(source.verify());
331}
332
337
High-level sorting functions for Aleph containers.
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
constexpr bool is_empty() const noexcept
Checks if the container is empty.
Definition tpl_array.H:359
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:221
Dynamic R-tree indexing axis-aligned rectangles by payload.
Definition tpl_r_tree.H:118
DebugSnapshot debug_snapshot() const
Capture the full tree structure for visualization/debugging.
void insert(const Rectangle &bbox, const Payload &value)
Insert a (bbox, value) entry, copying value.
Definition tpl_r_tree.H:969
bool erase(const Rectangle &bbox, const Payload &value)
Remove one entry equal to (bbox, value).
size_t height() const noexcept
Return the number of node levels (0 when empty, 1 for a lone leaf).
Definition tpl_r_tree.H:946
bool is_empty() const noexcept
Return true when the tree has no entries.
Definition tpl_r_tree.H:928
size_t size() const noexcept
Return the number of stored entries.
Definition tpl_r_tree.H:937
void for_each_intersecting(const Rectangle &rect, F &&f) const
Invoke f for every entry whose bbox intersects rect.
bool verify() const
Verify the R-tree structural invariants.
void clear() noexcept
Remove all entries.
Definition tpl_r_tree.H:954
An axis-aligned rectangle.
Definition point.H:1789
QuadTree - Hierarchical spatial index for 2D points.
Definition quadtree.H:126
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
#define TEST(name)
static mt19937 rng
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4114
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
static mpfr_t y
Definition mpfr_mul_d.c:3
size_t check_snapshot_node(const Snapshot &snap, const size_t idx, const size_t expected_depth)
Recursively validates the structural invariants of a DebugSnapshot produced by RTree::debug_snapshot(...
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
size_t size(Node *root) noexcept
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
bool contains(const std::string_view &str, const std::string_view &substr)
Check if substr appears inside str.
DynArray< T > & in_place_sort(DynArray< T > &c, Cmp cmp=Cmp())
Sorts a DynArray in place.
Definition ahSort.H:328
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:113
Shared DebugSnapshot structural-invariant checker for RTree and RStarTree unit tests.
static Rectangle box(const int x1, const int y1, const int x2, const int y2)
ValueArg< size_t > seed
Definition testHash.C:53
static int * k
R*-tree: an R-tree tuned with the Beckmann-Kriegel-Schneider-Seeger heuristics.