Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
sparse_table_test.cc
Go to the documentation of this file.
1/*
2Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
59# include <cstdio>
60# include <iostream>
61# include <iomanip>
62# include <chrono>
63# include <cassert>
64# include <cmath>
65# include <string>
66# include <sstream>
67# include <random>
68# include <numeric>
69# include <climits>
70# include <cfloat>
71# include <functional>
72# include <algorithm>
73# include <vector>
74
75# include <tpl_sparse_table.H>
76# include <tpl_array.H>
77# include <tpl_dynList.H>
78
79using namespace std;
80using namespace Aleph;
81
82// ============================================================================
83// Test Infrastructure
84// ============================================================================
85
86static int tests_passed = 0;
87static int tests_failed = 0;
88static int total_tests = 0;
89
90# define TEST(name) \
91 do { \
92 total_tests++; \
93 cout << " Testing: " << name << "... " << flush; \
94 } while(0)
95
96# define PASS() \
97 do { \
98 tests_passed++; \
99 cout << "\033[32mPASS\033[0m\n"; \
100 } while(0)
101
102# define FAIL(msg) \
103 do { \
104 tests_failed++; \
105 cout << "\033[31mFAIL\033[0m (" << msg << ")\n"; \
106 } while(0)
107
108# define CHECK(cond, msg) \
109 do { \
110 if (!(cond)) { FAIL(msg); return; } \
111 } while(0)
112
113# define CHECK_EQ(a, b, msg) \
114 do { \
115 if ((a) != (b)) { \
116 stringstream ss; \
117 ss << msg << " (expected " << (b) << ", got " << (a) << ")"; \
118 FAIL(ss.str()); \
119 return; \
120 } \
121 } while(0)
122
123# define CHECK_THROWS(ExType, expr, msg) \
124 do { \
125 bool caught = false; \
126 try { expr; } catch (const ExType &) { caught = true; } \
127 if (!caught) { FAIL(msg); return; } \
128 } while(0)
129
130class Timer
131{
132 chrono::high_resolution_clock::time_point start;
133public:
135 double elapsed_ms() const
136 {
137 auto end = chrono::high_resolution_clock::now();
138 return chrono::duration<double, milli>(end - start).count();
139 }
140};
141
142// Global RNG — seeded from main()
144
145// ============================================================================
146// Brute-force baselines
147// ============================================================================
148
149template <typename T>
150T brute_min(const vector<T> & v, size_t l, size_t r)
151{
152 T m = v[l];
153 for (size_t i = l + 1; i <= r; ++i)
154 m = min(m, v[i]);
155 return m;
156}
157
158template <typename T>
159T brute_max(const vector<T> & v, size_t l, size_t r)
160{
161 T m = v[l];
162 for (size_t i = l + 1; i <= r; ++i)
163 m = max(m, v[i]);
164 return m;
165}
166
167int brute_gcd(const vector<int> & v, size_t l, size_t r)
168{
169 int g = v[l];
170 for (size_t i = l + 1; i <= r; ++i)
171 g = gcd(g, v[i]);
172 return g;
173}
174
175int brute_and(const vector<int> & v, size_t l, size_t r)
176{
177 int a = v[l];
178 for (size_t i = l + 1; i <= r; ++i)
179 a &= v[i];
180 return a;
181}
182
183int brute_or(const vector<int> & v, size_t l, size_t r)
184{
185 int a = v[l];
186 for (size_t i = l + 1; i <= r; ++i)
187 a |= v[i];
188 return a;
189}
190
191// ============================================================================
192// 1. Edge Cases
193// ============================================================================
194
196{
197 TEST("empty table");
198
199 Sparse_Table<int> st(std::vector<int>{});
200 CHECK_EQ(st.size(), 0u, "size");
201 CHECK(st.is_empty(), "is_empty");
202 CHECK_EQ(st.num_levels(), 0u, "levels");
203
204 CHECK_THROWS(std::out_of_range, st.get(0),
205 "get(0) on empty should throw");
206 CHECK_THROWS(std::out_of_range, st.query(0, 0),
207 "query(0,0) on empty should throw");
208
209 PASS();
210}
211
213{
214 TEST("single element — min");
215 Sparse_Table<int> st = {42};
216 CHECK_EQ(st.size(), 1u, "size");
217 CHECK_EQ(st.num_levels(), 1u, "levels");
218 CHECK_EQ(st.get(0), 42, "get(0)");
219 CHECK_EQ(st.query(0, 0), 42, "query(0,0)");
220 CHECK(!st.is_empty(), "not empty");
221 PASS();
222}
223
225{
226 TEST("single element — max");
227 Max_Sparse_Table<int> st = {-7};
228 CHECK_EQ(st.query(0, 0), -7, "query(0,0)");
229 PASS();
230}
231
233{
234 TEST("two elements — min/max");
235 Sparse_Table<int> mn = {10, 3};
236 Max_Sparse_Table<int> mx = {10, 3};
237 CHECK_EQ(mn.query(0, 1), 3, "min[0,1]");
238 CHECK_EQ(mn.query(0, 0), 10, "min[0,0]");
239 CHECK_EQ(mn.query(1, 1), 3, "min[1,1]");
240 CHECK_EQ(mx.query(0, 1), 10, "max[0,1]");
241 CHECK_EQ(mx.query(0, 0), 10, "max[0,0]");
242 CHECK_EQ(mx.query(1, 1), 3, "max[1,1]");
243 PASS();
244}
245
247{
248 TEST("all-equal array (n=100)");
249 vector<int> v(100, 77);
250 Sparse_Table<int> st(v);
251 for (size_t l = 0; l < 100; l += 13)
252 for (size_t r = l; r < 100; r += 17)
253 CHECK_EQ(st.query(l, r), 77, "all-equal query");
254 PASS();
255}
256
258{
259 TEST("sorted ascending (min = leftmost, max = rightmost)");
260 vector<int> v(50);
261 iota(v.begin(), v.end(), 1); // 1, 2, ..., 50
264 CHECK_EQ(mn.query(0, 49), 1, "min entire");
265 CHECK_EQ(mx.query(0, 49), 50, "max entire");
266 CHECK_EQ(mn.query(10, 30), 11, "min sub");
267 CHECK_EQ(mx.query(10, 30), 31, "max sub");
268 PASS();
269}
270
272{
273 TEST("sorted descending");
274 vector<int> v(50);
275 for (int i = 0; i < 50; ++i) v[i] = 50 - i;
278 CHECK_EQ(mn.query(0, 49), 1, "min entire");
279 CHECK_EQ(mx.query(0, 49), 50, "max entire");
280 CHECK_EQ(mn.query(0, 0), 50, "min first");
281 CHECK_EQ(mx.query(49, 49), 1, "max last");
282 PASS();
283}
284
286{
287 TEST("power-of-two sizes (1, 2, 4, 8, 16, 32, 64)");
288 for (size_t sz : {1u, 2u, 4u, 8u, 16u, 32u, 64u})
289 {
290 vector<int> v(sz);
291 for (size_t i = 0; i < sz; ++i)
292 v[i] = static_cast<int>(rng() % 10000);
293 Sparse_Table<int> st(v);
294 CHECK_EQ(st.size(), sz, "size mismatch");
295 int bmin = *min_element(v.begin(), v.end());
296 CHECK_EQ(st.query(0, sz - 1), bmin, "full-range min");
297 }
298 PASS();
299}
300
302{
303 TEST("non-power-of-two sizes (3, 5, 7, 10, 13, 17, 31, 33, 63, 65, 100)");
304 for (size_t sz : {3u, 5u, 7u, 10u, 13u, 17u, 31u, 33u, 63u, 65u, 100u})
305 {
306 vector<int> v(sz);
307 for (size_t i = 0; i < sz; ++i)
308 v[i] = static_cast<int>(rng() % 10000);
309 Sparse_Table<int> st(v);
310 CHECK_EQ(st.size(), sz, "size mismatch");
311 int bmin = *min_element(v.begin(), v.end());
312 CHECK_EQ(st.query(0, sz - 1), bmin, "full-range min");
313 }
314 PASS();
315}
316
317// ============================================================================
318// 2. Basic Correctness — known arrays
319// ============================================================================
320
322{
323 TEST("known array min queries");
324 // 0 1 2 3 4 5 6 7 8 9
325 Sparse_Table<int> st = {5, 2, 4, 7, 1, 3, 6, 8, 0, 9};
326
327 CHECK_EQ(st.query(0, 0), 5, "[0,0]");
328 CHECK_EQ(st.query(0, 1), 2, "[0,1]");
329 CHECK_EQ(st.query(0, 9), 0, "[0,9]");
330 CHECK_EQ(st.query(4, 4), 1, "[4,4]");
331 CHECK_EQ(st.query(3, 5), 1, "[3,5]");
332 CHECK_EQ(st.query(6, 8), 0, "[6,8]");
333 CHECK_EQ(st.query(8, 9), 0, "[8,9]");
334 CHECK_EQ(st.query(1, 3), 2, "[1,3]");
335 CHECK_EQ(st.query(5, 7), 3, "[5,7]");
336 CHECK_EQ(st.query(0, 4), 1, "[0,4]");
337 PASS();
338}
339
341{
342 TEST("known array max queries");
343 Max_Sparse_Table<int> st = {5, 2, 4, 7, 1, 3, 6, 8, 0, 9};
344
345 CHECK_EQ(st.query(0, 9), 9, "[0,9]");
346 CHECK_EQ(st.query(0, 3), 7, "[0,3]");
347 CHECK_EQ(st.query(4, 7), 8, "[4,7]");
348 CHECK_EQ(st.query(6, 8), 8, "[6,8]");
349 CHECK_EQ(st.query(8, 9), 9, "[8,9]");
350 CHECK_EQ(st.query(2, 2), 4, "[2,2]");
351 PASS();
352}
353
355{
356 TEST("get() returns correct element for all positions");
357 vector<int> v = {10, -3, 42, 0, 7, -99, 88, 1};
358 Sparse_Table<int> st(v);
359 for (size_t i = 0; i < v.size(); ++i)
360 CHECK_EQ(st.get(i), v[i], "get mismatch");
361 PASS();
362}
363
365{
366 TEST("values() reconstructs original array");
367 vector<int> v = {10, -3, 42, 0, 7, -99, 88, 1, 55, -20};
368 Sparse_Table<int> st(v);
369 auto vals = st.values();
370 CHECK_EQ(vals.size(), v.size(), "values size");
371 for (size_t i = 0; i < v.size(); ++i)
372 CHECK_EQ(vals(i), v[i], "values mismatch");
373 PASS();
374}
375
376// ============================================================================
377// 3. Brute-force stress tests — random data
378// ============================================================================
379
381{
382 TEST("stress: Sparse_Table (min) n=200, 5000 random queries");
383 const size_t N = 200;
384 const int Q = 5000;
385
386 vector<int> v(N);
387 for (size_t i = 0; i < N; ++i)
388 v[i] = static_cast<int>(rng() % 200001) - 100000;
389
390 Sparse_Table<int> st(v);
391
393 for (int q = 0; q < Q; ++q)
394 {
395 size_t a = dist(rng), b = dist(rng);
396 if (a > b) swap(a, b);
397 int expected = brute_min(v, a, b);
398 int got = st.query(a, b);
399 if (got != expected)
400 {
401 stringstream ss;
402 ss << "query(" << a << "," << b << "): expected " << expected
403 << ", got " << got << " (q=" << q << ")";
404 FAIL(ss.str());
405 return;
406 }
407 }
408 PASS();
409}
410
412{
413 TEST("stress: Max_Sparse_Table (max) n=200, 5000 random queries");
414 const size_t N = 200;
415 const int Q = 5000;
416
417 vector<int> v(N);
418 for (size_t i = 0; i < N; ++i)
419 v[i] = static_cast<int>(rng() % 200001) - 100000;
420
422
424 for (int q = 0; q < Q; ++q)
425 {
426 size_t a = dist(rng), b = dist(rng);
427 if (a > b) swap(a, b);
428 int expected = brute_max(v, a, b);
429 int got = st.query(a, b);
430 if (got != expected)
431 {
432 stringstream ss;
433 ss << "query(" << a << "," << b << "): expected " << expected
434 << ", got " << got;
435 FAIL(ss.str());
436 return;
437 }
438 }
439 PASS();
440}
441
443{
444 TEST("stress: Sparse_Table (min) n=10000, 50000 random queries");
445 const size_t N = 10000;
446 const int Q = 50000;
447
448 vector<int> v(N);
449 for (size_t i = 0; i < N; ++i)
450 v[i] = static_cast<int>(rng());
451
452 Sparse_Table<int> st(v);
453
455 for (int q = 0; q < Q; ++q)
456 {
457 size_t a = dist(rng), b = dist(rng);
458 if (a > b) swap(a, b);
459 int expected = brute_min(v, a, b);
460 int got = st.query(a, b);
461 if (got != expected)
462 {
463 stringstream ss;
464 ss << "query(" << a << "," << b << "): expected " << expected
465 << ", got " << got;
466 FAIL(ss.str());
467 return;
468 }
469 }
470 PASS();
471}
472
474{
475 TEST("stress: all point queries match original (n=500)");
476 const size_t N = 500;
477 vector<int> v(N);
478 for (size_t i = 0; i < N; ++i)
479 v[i] = static_cast<int>(rng());
480
481 Sparse_Table<int> st(v);
482 for (size_t i = 0; i < N; ++i)
483 {
484 CHECK_EQ(st.query(i, i), v[i], "point query mismatch");
485 CHECK_EQ(st.get(i), v[i], "get mismatch");
486 }
487 PASS();
488}
489
491{
492 TEST("stress: ALL (l,r) pairs for n=80 — exhaustive");
493 const size_t N = 80;
494 vector<int> v(N);
495 for (size_t i = 0; i < N; ++i)
496 v[i] = static_cast<int>(rng() % 1000) - 500;
497
500
501 for (size_t l = 0; l < N; ++l)
502 for (size_t r = l; r < N; ++r)
503 {
504 int exp_min = brute_min(v, l, r);
505 int exp_max = brute_max(v, l, r);
506 if (mn.query(l, r) != exp_min)
507 {
508 stringstream ss;
509 ss << "min query(" << l << "," << r << "): expected "
510 << exp_min << ", got " << mn.query(l, r);
511 FAIL(ss.str());
512 return;
513 }
514 if (mx.query(l, r) != exp_max)
515 {
516 stringstream ss;
517 ss << "max query(" << l << "," << r << "): expected "
518 << exp_max << ", got " << mx.query(l, r);
519 FAIL(ss.str());
520 return;
521 }
522 }
523 PASS();
524}
525
526// ============================================================================
527// 4. Custom idempotent operations (GCD, AND, OR)
528// ============================================================================
529
530struct Gcd_Op
531{
532 int operator()(const int & a, const int & b) const noexcept
533 {
534 return gcd(a, b);
535 }
536};
537
538struct And_Op
539{
540 int operator()(const int & a, const int & b) const noexcept
541 {
542 return a & b;
543 }
544};
545
546struct Or_Op
547{
548 int operator()(const int & a, const int & b) const noexcept
549 {
550 return a | b;
551 }
552};
553
555{
556 TEST("GCD sparse table — known values");
557 Gen_Sparse_Table<int, Gcd_Op> st = {12, 18, 24, 36, 60, 48};
558 CHECK_EQ(st.query(0, 1), 6, "gcd(12,18)");
559 CHECK_EQ(st.query(0, 5), 6, "gcd(all)");
560 CHECK_EQ(st.query(2, 4), 12, "gcd(24,36,60)");
561 CHECK_EQ(st.query(3, 5), 12, "gcd(36,60,48)");
562 CHECK_EQ(st.query(4, 4), 60, "gcd(60)");
563 PASS();
564}
565
567{
568 TEST("GCD sparse table — random stress n=300, 10000 queries");
569 const size_t N = 300;
570 const int Q = 10000;
571
572 vector<int> v(N);
573 uniform_int_distribution<int> dist(1, 100000);
574 for (size_t i = 0; i < N; ++i)
575 v[i] = dist(rng);
576
578
580 for (int q = 0; q < Q; ++q)
581 {
582 size_t a = qdist(rng), b = qdist(rng);
583 if (a > b) swap(a, b);
584 int expected = brute_gcd(v, a, b);
585 int got = st.query(a, b);
586 if (got != expected)
587 {
588 stringstream ss;
589 ss << "gcd query(" << a << "," << b << "): expected "
590 << expected << ", got " << got;
591 FAIL(ss.str());
592 return;
593 }
594 }
595 PASS();
596}
597
599{
600 TEST("AND sparse table — random stress n=200, 5000 queries");
601 const size_t N = 200;
602 const int Q = 5000;
603
604 vector<int> v(N);
605 for (size_t i = 0; i < N; ++i)
606 v[i] = static_cast<int>(rng() & 0x7FFFFFFF);
607
609
611 for (int q = 0; q < Q; ++q)
612 {
613 size_t a = dist(rng), b = dist(rng);
614 if (a > b) swap(a, b);
615 int expected = brute_and(v, a, b);
616 int got = st.query(a, b);
617 if (got != expected)
618 {
619 stringstream ss;
620 ss << "AND query(" << a << "," << b << "): expected "
621 << expected << ", got " << got;
622 FAIL(ss.str());
623 return;
624 }
625 }
626 PASS();
627}
628
630{
631 TEST("OR sparse table — random stress n=200, 5000 queries");
632 const size_t N = 200;
633 const int Q = 5000;
634
635 vector<int> v(N);
636 for (size_t i = 0; i < N; ++i)
637 v[i] = static_cast<int>(rng() & 0x7FFFFFFF);
638
640
642 for (int q = 0; q < Q; ++q)
643 {
644 size_t a = dist(rng), b = dist(rng);
645 if (a > b) swap(a, b);
646 int expected = brute_or(v, a, b);
647 int got = st.query(a, b);
648 if (got != expected)
649 {
650 stringstream ss;
651 ss << "OR query(" << a << "," << b << "): expected "
652 << expected << ", got " << got;
653 FAIL(ss.str());
654 return;
655 }
656 }
657 PASS();
658}
659
660// ============================================================================
661// 5. Construction from all container types
662// ============================================================================
663
665{
666 TEST("construct from Array<int>");
667 Array<int> arr = {9, 1, 7, 3, 5};
668 Sparse_Table<int> st(arr);
669 CHECK_EQ(st.size(), 5u, "size");
670 CHECK_EQ(st.query(0, 4), 1, "min");
671 CHECK_EQ(st.get(2), 7, "get(2)");
672 PASS();
673}
674
676{
677 TEST("construct from std::vector<int>");
678 vector<int> v = {9, 1, 7, 3, 5};
679 Sparse_Table<int> st(v);
680 CHECK_EQ(st.size(), 5u, "size");
681 CHECK_EQ(st.query(0, 4), 1, "min");
682 PASS();
683}
684
686{
687 TEST("construct from DynList<int>");
688 DynList<int> dl = {9, 1, 7, 3, 5};
690 CHECK_EQ(st.size(), 5u, "size");
691 CHECK_EQ(st.query(0, 4), 1, "min");
692 PASS();
693}
694
696{
697 TEST("construct from initializer_list");
698 Sparse_Table<int> st = {9, 1, 7, 3, 5};
699 CHECK_EQ(st.size(), 5u, "size");
700 CHECK_EQ(st.query(0, 4), 1, "min");
701 PASS();
702}
703
705{
706 TEST("construct with uniform init_val (n=50, val=-5)");
708 CHECK_EQ(st.size(), 50u, "size");
709 CHECK_EQ(st.query(0, 49), -5, "min");
710 CHECK_EQ(st.query(20, 30), -5, "sub min");
711 PASS();
712}
713
715{
716 TEST("all constructors produce identical query results");
717 vector<int> raw = {15, 8, 23, 4, 42, 1, 17, 9, 30, 6};
718
719 Array<int> arr(raw.size());
720 for (size_t i = 0; i < raw.size(); ++i)
721 arr.append(raw[i]);
722
724 for (size_t i = 0; i < raw.size(); ++i)
725 dl.append(raw[i]);
726
730 Sparse_Table<int> st_il = {15, 8, 23, 4, 42, 1, 17, 9, 30, 6};
731
733 for (int q = 0; q < 200; ++q)
734 {
735 size_t a = dist(rng), b = dist(rng);
736 if (a > b) swap(a, b);
737 int v0 = st_vec.query(a, b);
738 CHECK_EQ(st_arr.query(a, b), v0, "arr disagrees");
739 CHECK_EQ(st_dl.query(a, b), v0, "dl disagrees");
740 CHECK_EQ(st_il.query(a, b), v0, "il disagrees");
741 }
742 PASS();
743}
744
745// ============================================================================
746// 6. Copy, move, and swap
747// ============================================================================
748
750{
751 TEST("copy constructor");
752 Sparse_Table<int> orig = {5, 2, 8, 1, 9};
754 CHECK_EQ(copy.size(), orig.size(), "size");
755 CHECK_EQ(copy.query(0, 4), orig.query(0, 4), "min");
756 CHECK_EQ(copy.get(3), 1, "get(3)");
757 // Verify independence: original still works
758 CHECK_EQ(orig.query(1, 3), 1, "orig query");
759 PASS();
760}
761
763{
764 TEST("move constructor");
765 Sparse_Table<int> orig = {5, 2, 8, 1, 9};
766 size_t orig_sz = orig.size();
767 int orig_min = orig.query(0, 4);
768 Sparse_Table<int> moved(std::move(orig));
769 CHECK_EQ(moved.size(), orig_sz, "size");
770 CHECK_EQ(moved.query(0, 4), orig_min, "min");
771 PASS();
772}
773
775{
776 TEST("copy assignment");
777 Sparse_Table<int> a = {5, 2, 8, 1, 9};
778 Sparse_Table<int> b = {100, 200};
779 b = a;
780 CHECK_EQ(b.size(), 5u, "size");
781 CHECK_EQ(b.query(0, 4), 1, "min");
782 CHECK_EQ(a.query(0, 4), 1, "orig still works");
783 PASS();
784}
785
787{
788 TEST("move assignment");
789 Sparse_Table<int> a = {5, 2, 8, 1, 9};
790 Sparse_Table<int> b = {100, 200};
791 b = std::move(a);
792 CHECK_EQ(b.size(), 5u, "size");
793 CHECK_EQ(b.query(0, 4), 1, "min");
794 PASS();
795}
796
798{
799 TEST("swap");
800 Sparse_Table<int> a = {1, 2, 3};
801 Sparse_Table<int> b = {10, 20, 30, 40};
802 a.swap(b);
803 CHECK_EQ(a.size(), 4u, "a size");
804 CHECK_EQ(b.size(), 3u, "b size");
805 CHECK_EQ(a.query(0, 3), 10, "a min");
806 CHECK_EQ(b.query(0, 2), 1, "b min");
807 PASS();
808}
809
810// ============================================================================
811// 7. Exception safety
812// ============================================================================
813
815{
816 TEST("query throws out_of_range when r >= n");
817 Sparse_Table<int> st = {1, 2, 3, 4, 5};
818 CHECK_THROWS(std::out_of_range, st.query(0, 5),
819 "should throw for r=5, n=5");
820 CHECK_THROWS(std::out_of_range, st.query(0, 100),
821 "should throw for r=100");
822 PASS();
823}
824
826{
827 TEST("query throws out_of_range when l > r");
828 Sparse_Table<int> st = {1, 2, 3, 4, 5};
829 CHECK_THROWS(std::out_of_range, st.query(3, 2),
830 "should throw for l=3, r=2");
831 PASS();
832}
833
835{
836 TEST("get throws out_of_range when i >= n");
837 Sparse_Table<int> st = {1, 2, 3};
838 CHECK_THROWS(std::out_of_range, st.get(3),
839 "should throw for i=3, n=3");
840 CHECK_THROWS(std::out_of_range, st.get(1000),
841 "should throw for i=1000");
842 PASS();
843}
844
846{
847 TEST("boundary queries that should NOT throw");
848 Sparse_Table<int> st = {1, 2, 3, 4, 5};
849 // These should all succeed
850 int v;
851 v = st.query(0, 0); CHECK_EQ(v, 1, "q(0,0)");
852 v = st.query(4, 4); CHECK_EQ(v, 5, "q(4,4)");
853 v = st.query(0, 4); CHECK_EQ(v, 1, "q(0,4)");
854 v = st.get(0); CHECK_EQ(v, 1, "get(0)");
855 v = st.get(4); CHECK_EQ(v, 5, "get(4)");
856 PASS();
857}
858
859// ============================================================================
860// 8. Numerical edge cases
861// ============================================================================
862
864{
865 TEST("negative values");
866 Sparse_Table<int> st = {-5, -2, -8, -1, -9};
867 CHECK_EQ(st.query(0, 4), -9, "min all");
868 CHECK_EQ(st.query(0, 2), -8, "min [0,2]");
869 Max_Sparse_Table<int> mx = {-5, -2, -8, -1, -9};
870 CHECK_EQ(mx.query(0, 4), -1, "max all");
871 PASS();
872}
873
875{
876 TEST("INT_MIN/INT_MAX values");
878 CHECK_EQ(st.query(0, 4), INT_MIN, "min with INT_MIN");
879 CHECK_EQ(st.query(0, 1), 0, "min [0,1]");
880 CHECK_EQ(st.query(3, 4), 42, "min [3,4]");
882 CHECK_EQ(mx.query(0, 4), INT_MAX, "max with INT_MAX");
883 CHECK_EQ(mx.query(1, 3), 42, "max [1,3]");
884 PASS();
885}
886
888{
889 TEST("double values (including negative and close values)");
890 Sparse_Table<double> st = {3.14, 2.71, 1.41, 1.73, 0.577};
891 CHECK(st.query(0, 4) == 0.577, "min double");
892 CHECK(st.query(0, 1) == 2.71, "min [0,1]");
893 CHECK(st.query(2, 3) == 1.41, "min [2,3]");
894 Max_Sparse_Table<double> mx = {3.14, 2.71, 1.41, 1.73, 0.577};
895 CHECK(mx.query(0, 4) == 3.14, "max double");
896 PASS();
897}
898
900{
901 TEST("stress: double min/max n=500, 5000 queries");
902 const size_t N = 500;
903 const int Q = 5000;
904
905 vector<double> v(N);
907 for (size_t i = 0; i < N; ++i)
908 v[i] = dist(rng);
909
912
914 for (int q = 0; q < Q; ++q)
915 {
916 size_t a = qdist(rng), b = qdist(rng);
917 if (a > b) swap(a, b);
918 double exp_min = brute_min(v, a, b);
919 double exp_max = brute_max(v, a, b);
920 if (mn.query(a, b) != exp_min || mx.query(a, b) != exp_max)
921 {
922 stringstream ss;
923 ss << "double query(" << a << "," << b << ") mismatch";
924 FAIL(ss.str());
925 return;
926 }
927 }
928 PASS();
929}
930
932{
933 TEST("long long values");
935 1LL << 60, -(1LL << 59), 0LL, 1LL << 50, -(1LL << 62)
936 };
937 CHECK_EQ(st.query(0, 4), -(1LL << 62), "min ll");
938 CHECK_EQ(st.query(0, 0), 1LL << 60, "get(0)");
940 1LL << 60, -(1LL << 59), 0LL, 1LL << 50, -(1LL << 62)
941 };
942 CHECK_EQ(mx.query(0, 4), 1LL << 60, "max ll");
943 PASS();
944}
945
946// ============================================================================
947// 9. Performance tests
948// ============================================================================
949
951{
952 TEST("performance: build n=1,000,000");
953 const size_t N = 1000000;
954 vector<int> v(N);
955 for (size_t i = 0; i < N; ++i)
956 v[i] = static_cast<int>(rng());
957
958 Timer timer;
959 Sparse_Table<int> st(v);
960 double ms = timer.elapsed_ms();
961
962 CHECK_EQ(st.size(), N, "size");
963 cout << "[" << fixed << setprecision(1) << ms << " ms] " << flush;
964 CHECK(ms < 5000.0, "build should complete in < 5s");
965 PASS();
966}
967
969{
970 TEST("performance: 1,000,000 queries on n=100,000");
971 const size_t N = 100000;
972 const int Q = 1000000;
973
974 vector<int> v(N);
975 for (size_t i = 0; i < N; ++i)
976 v[i] = static_cast<int>(rng());
977
978 Sparse_Table<int> st(v);
979
981 volatile int sink = 0; // prevent optimization
982
983 Timer timer;
984 for (int q = 0; q < Q; ++q)
985 {
986 size_t a = dist(rng), b = dist(rng);
987 if (a > b) swap(a, b);
988 sink = st.query(a, b);
989 }
990 double ms = timer.elapsed_ms();
991
992 cout << "[" << fixed << setprecision(1) << ms << " ms] " << flush;
993 CHECK(ms < 10000.0, "1M queries should complete in < 10s");
994 (void)sink;
995 PASS();
996}
997
999{
1000 TEST("performance: build n=5,000,000");
1001 const size_t N = 5000000;
1002 vector<int> v(N);
1003 for (size_t i = 0; i < N; ++i)
1004 v[i] = static_cast<int>(rng());
1005
1006 Timer timer;
1007 Sparse_Table<int> st(v);
1008 double ms = timer.elapsed_ms();
1009
1010 CHECK_EQ(st.size(), N, "size");
1011 // Spot-check a random query
1012 size_t a = 1000, b = 4999000;
1013 int got = st.query(a, b);
1014 int expected = *min_element(v.begin() + a, v.begin() + b + 1);
1015 CHECK_EQ(got, expected, "spot-check query");
1016
1017 cout << "[" << fixed << setprecision(1) << ms << " ms] " << flush;
1018 CHECK(ms < 20000.0, "build should complete in < 20s");
1019 PASS();
1020}
1021
1022// ============================================================================
1023// 10. Idempotency and overlap correctness
1024// ============================================================================
1025
1027{
1028 TEST("overlapping sub-ranges give correct result (idempotency)");
1029 // The key property: Op(table[k][l], table[k][r-2^k+1]) is correct
1030 // because Op(x, x) = x for idempotent operations.
1031 // This test specifically exercises the overlap.
1032 vector<int> v = {10, 3, 7, 1, 8, 5, 2, 9, 4, 6};
1033 Sparse_Table<int> st(v);
1034
1035 // Range [0, 5] has length 6. k = floor(log2(6)) = 2, 2^2 = 4.
1036 // So we combine table[2][0] (covers [0,3]) and table[2][2] (covers [2,5]).
1037 // Overlap is [2,3]. The idempotency of min ensures correctness.
1038 CHECK_EQ(st.query(0, 5), 1, "min [0,5] with overlap");
1039
1040 // Range [2, 8] has length 7. k = floor(log2(7)) = 2, 2^2 = 4.
1041 // Combines table[2][2] (covers [2,5]) and table[2][5] (covers [5,8]).
1042 // Overlap is [5,5].
1043 CHECK_EQ(st.query(2, 8), 1, "min [2,8]");
1044
1045 // Range [3, 9] — length 7.
1046 CHECK_EQ(st.query(3, 9), 1, "min [3,9]");
1047
1048 // Range [4, 9] — length 6. min is 2 (position 6).
1049 CHECK_EQ(st.query(4, 9), 2, "min [4,9]");
1050
1051 PASS();
1052}
1053
1055{
1056 TEST("num_levels() = floor(log2(n)) + 1");
1057 for (size_t n : {1u, 2u, 3u, 4u, 5u, 7u, 8u, 15u, 16u, 17u, 100u, 1024u})
1058 {
1059 vector<int> v(n, 0);
1060 Sparse_Table<int> st(v);
1061 size_t expected = static_cast<size_t>(std::bit_width(n));
1062 if (st.num_levels() != expected)
1063 {
1064 stringstream ss;
1065 ss << "n=" << n << ": expected " << expected
1066 << " levels, got " << st.num_levels();
1067 FAIL(ss.str());
1068 return;
1069 }
1070 }
1071 PASS();
1072}
1073
1074// ============================================================================
1075// 11. Robustness & Algebraic Properties
1076// ============================================================================
1077
1079{
1080 TEST("self copy-assignment (st = st)");
1081 Sparse_Table<int> st = {5, 3, 8, 1, 7, 2, 9};
1082 int orig_min = st.query(0, 6);
1083 size_t orig_sz = st.size();
1084
1085 auto & ref = st;
1086 st = ref;
1087
1088 CHECK_EQ(st.size(), orig_sz, "size after self-assign");
1089 CHECK_EQ(st.query(0, 6), orig_min, "query after self-assign");
1090 for (size_t i = 0; i < st.size(); ++i)
1091 CHECK_EQ(st.get(i), st.get(i), "element intact");
1092 PASS();
1093}
1094
1096{
1097 TEST("self swap (st.swap(st))");
1098 Sparse_Table<int> st = {5, 3, 8, 1, 7, 2, 9};
1099 int orig_min = st.query(0, 6);
1100 size_t orig_sz = st.size();
1101 st.swap(st);
1102 CHECK_EQ(st.size(), orig_sz, "size after self-swap");
1103 CHECK_EQ(st.query(0, 6), orig_min, "query after self-swap");
1104 PASS();
1105}
1106
1108{
1109 TEST("get(i) == query(i,i) for all i (n=200)");
1110 const size_t n = 200;
1111 vector<int> v(n);
1112 for (auto & x : v) x = static_cast<int>(rng() % 10000);
1113 Sparse_Table<int> st(v);
1114 for (size_t i = 0; i < n; ++i)
1115 CHECK_EQ(st.get(i), st.query(i, i), "get != point query");
1116 PASS();
1117}
1118
1120{
1121 TEST("idempotent overlap split: min(query(l,m), query(m',r)) == query(l,r)");
1122 const size_t n = 60;
1123 vector<int> v(n);
1124 for (auto & x : v) x = static_cast<int>(rng() % 1000);
1125 Sparse_Table<int> st(v);
1126
1127 for (size_t l = 0; l < n; ++l)
1128 for (size_t r = l; r < n; ++r)
1129 {
1130 int expected = st.query(l, r);
1131 // Try every overlapping split point
1132 for (size_t m = l; m < r; ++m)
1133 {
1134 int combined = min(st.query(l, m), st.query(m, r));
1135 CHECK_EQ(combined, expected, "overlap split mismatch");
1136 }
1137 }
1138 PASS();
1139}
1140
1142{
1143 TEST("disjoint split: min(query(l,m), query(m+1,r)) == query(l,r)");
1144 const size_t n = 80;
1145 vector<int> v(n);
1146 for (auto & x : v) x = static_cast<int>(rng() % 1000);
1147 Sparse_Table<int> st(v);
1148
1149 for (size_t l = 0; l < n; ++l)
1150 for (size_t r = l + 1; r < n; ++r)
1151 {
1152 int expected = st.query(l, r);
1153 for (size_t m = l; m < r; ++m)
1154 {
1155 int combined = min(st.query(l, m), st.query(m + 1, r));
1156 CHECK_EQ(combined, expected, "disjoint split mismatch");
1157 }
1158 }
1159 PASS();
1160}
1161
1163{
1164 TEST("adversarial: zigzag pattern (alternating high/low)");
1165 // 1000, 1, 1000, 1, 1000, 1, ...
1166 const size_t n = 100;
1167 vector<int> v(n);
1168 for (size_t i = 0; i < n; ++i)
1169 v[i] = (i % 2 == 0) ? 1000 : 1;
1170
1171 Sparse_Table<int> st(v);
1172 for (size_t l = 0; l < n; ++l)
1173 for (size_t r = l; r < n; r += 7)
1174 CHECK_EQ(st.query(l, r), brute_min(v, l, r), "zigzag mismatch");
1175 PASS();
1176}
1177
1179{
1180 TEST("adversarial: single outlier among equals");
1181 const size_t n = 128;
1182 for (size_t outlier_pos = 0; outlier_pos < n; outlier_pos += 11)
1183 {
1184 vector<int> v(n, 100);
1185 v[outlier_pos] = -1;
1186 Sparse_Table<int> st(v);
1187
1188 for (size_t l = 0; l < n; ++l)
1189 for (size_t r = l; r < n; r += 13)
1190 {
1191 int expected = (l <= outlier_pos && outlier_pos <= r) ? -1 : 100;
1192 CHECK_EQ(st.query(l, r), expected, "outlier mismatch");
1193 }
1194 }
1195 PASS();
1196}
1197
1199{
1200 TEST("adversarial: plateau with single dip at every position");
1201 const size_t n = 50;
1202 for (size_t dip = 0; dip < n; ++dip)
1203 {
1204 vector<int> v(n, 999);
1205 v[dip] = 0;
1206 Sparse_Table<int> st(v);
1207
1208 // Full range must find the dip
1209 CHECK_EQ(st.query(0, n - 1), 0, "full range must find dip");
1210 // Range excluding dip must not find 0
1211 if (dip > 0)
1212 CHECK_EQ(st.query(0, dip - 1), 999, "before dip");
1213 if (dip < n - 1)
1214 CHECK_EQ(st.query(dip + 1, n - 1), 999, "after dip");
1215 }
1216 PASS();
1217}
1218
1220{
1221 TEST("sliding window queries (width=10) across n=200");
1222 const size_t n = 200;
1223 const size_t w = 10;
1224 vector<int> v(n);
1225 for (auto & x : v) x = static_cast<int>(rng() % 1000);
1226 Sparse_Table<int> st(v);
1227
1228 for (size_t l = 0; l + w - 1 < n; ++l)
1229 CHECK_EQ(st.query(l, l + w - 1), brute_min(v, l, l + w - 1),
1230 "sliding window mismatch");
1231 PASS();
1232}
1233
1234// ============================================================================
1235// Main
1236// ============================================================================
1237
1238int main(int argc, char *argv[])
1239{
1240 unsigned seed;
1241 if (argc > 1)
1242 seed = static_cast<unsigned>(atoi(argv[1]));
1243 else
1244 seed = static_cast<unsigned>(
1245 chrono::high_resolution_clock::now().time_since_epoch().count());
1246
1247 rng.seed(seed);
1248
1249 cout << "╔══════════════════════════════════════════════════════════════╗\n";
1250 cout << "║ Sparse Table Test Suite ║\n";
1251 cout << "║ Testing Gen_Sparse_Table, Sparse_Table, Max_Sparse ║\n";
1252 cout << "╚══════════════════════════════════════════════════════════════╝\n";
1253 cout << " Seed: " << seed << "\n\n";
1254
1255 cout << "=== 1. Edge Cases ===\n";
1265
1266 cout << "\n=== 2. Basic Correctness ===\n";
1271
1272 cout << "\n=== 3. Brute-Force Stress Tests ===\n";
1278
1279 cout << "\n=== 4. Custom Idempotent Operations ===\n";
1284
1285 cout << "\n=== 5. Construction from All Container Types ===\n";
1292
1293 cout << "\n=== 6. Copy, Move, Swap ===\n";
1298 test_swap();
1299
1300 cout << "\n=== 7. Exception Safety ===\n";
1305
1306 cout << "\n=== 8. Numerical Edge Cases ===\n";
1312
1313 cout << "\n=== 9. Performance ===\n";
1317
1318 cout << "\n=== 10. Idempotency & Structure ===\n";
1321
1322 cout << "\n=== 11. Robustness & Algebraic Properties ===\n";
1332
1333 cout << "\n";
1334 cout << "══════════════════════════════════════════════════════════════\n";
1335 cout << " RESULTS: " << tests_passed << "/" << total_tests << " passed";
1336 if (tests_failed > 0)
1337 cout << ", \033[31m" << tests_failed << " FAILED\033[0m";
1338 else
1339 cout << " — \033[32mALL PASS\033[0m";
1340 cout << "\n";
1341 cout << "══════════════════════════════════════════════════════════════\n";
1342
1343 return tests_failed > 0 ? 1 : 0;
1344}
int main()
long double w
Definition btreepic.C:153
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:239
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
Sparse Table over an arbitrary associative and idempotent binary operation.
T get(const size_t i) const
Retrieve the value a[i] in O(1).
constexpr size_t num_levels() const noexcept
Number of precomputed levels (floor(log2(n)) + 1).
Array< T > values() const
Reconstruct all original values into an Array.
constexpr size_t size() const noexcept
Number of logical elements.
constexpr bool is_empty() const noexcept
True if the table contains no elements.
T query(const size_t l, const size_t r) const
Range query over [l, r] in O(1).
void swap(Gen_Sparse_Table &other) noexcept
Swap this table with other in O(1).
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
double elapsed_ms() const
chrono::high_resolution_clock::time_point start
#define N
Definition fib.C:294
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_max_function > > max(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4110
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_min_function > > min(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4111
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
T & swap(T &t1, T &t2)
Generic swap using object's swap method.
Definition ahTypes.H:121
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
Itor min_element(Itor beg, const Itor &end, CompFunc op=CompFunc())
Find the minimum element in a range.
Definition ahAlgo.H:148
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
#define LL
Definition ran_array.c:24
void test_stress_max_small()
void test_adversarial_plateau_with_dip()
void test_known_max_array()
void test_values_reconstruction()
void test_adversarial_single_outlier()
void test_overlapping_ranges_idempotent()
#define CHECK(cond, msg)
#define FAIL(msg)
void test_query_l_greater_than_r()
void test_get_out_of_range()
void test_double_stress()
void test_stress_min_medium()
void test_performance_build_large()
#define TEST(name)
#define CHECK_EQ(a, b, msg)
void test_bitwise_or_stress()
void test_empty_table()
T brute_max(const vector< T > &v, size_t l, size_t r)
void test_move_constructor()
void test_single_element_max()
void test_double_values()
void test_disjoint_split_min()
void test_two_elements()
#define CHECK_THROWS(ExType, expr, msg)
void test_known_min_array()
void test_stress_all_pairs_small()
void test_construct_uniform_value()
int brute_and(const vector< int > &v, size_t l, size_t r)
void test_construct_from_array()
void test_performance_build()
void test_move_assignment()
int brute_or(const vector< int > &v, size_t l, size_t r)
void test_long_long_values()
void test_power_of_two_sizes()
void test_all_equal()
void test_gcd_stress()
void test_stress_all_point_queries()
void test_copy_constructor()
void test_sliding_window()
void test_swap()
#define PASS()
void test_negative_values()
static mt19937 rng
void test_non_power_of_two_sizes()
static int total_tests
void test_construct_from_vector()
void test_stress_min_small()
static int tests_passed
void test_query_r_out_of_range()
void test_all_constructors_agree()
static int tests_failed
void test_copy_assignment()
void test_bitwise_and_stress()
void test_sorted_ascending()
void test_single_element()
void test_self_swap()
void test_get_equals_point_query()
void test_self_copy_assignment()
void test_performance_queries()
T brute_min(const vector< T > &v, size_t l, size_t r)
void test_construct_from_initlist()
void test_sorted_descending()
void test_get_all_elements()
void test_int_extremes()
void test_boundary_queries_valid()
void test_gcd_known()
void test_construct_from_dynlist()
void test_adversarial_zigzag()
int brute_gcd(const vector< int > &v, size_t l, size_t r)
void test_num_levels_correctness()
void test_idempotent_overlap_split()
Sparse Table for range maximum queries.
Sparse Table for range minimum queries.
int operator()(const int &a, const int &b) const noexcept
int operator()(const int &a, const int &b) const noexcept
int operator()(const int &a, const int &b) const noexcept
Dynamic array container with automatic resizing.
Alias for htlist.H (DynList implementation).
Sparse Table for static range queries in O(1).
DynList< int > l