Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
disjoint_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
62# include <cstdio>
63# include <iostream>
64# include <iomanip>
65# include <chrono>
66# include <cassert>
67# include <cmath>
68# include <string>
69# include <sstream>
70# include <random>
71# include <numeric>
72# include <climits>
73# include <cfloat>
74# include <functional>
75# include <algorithm>
76# include <vector>
77
79# include <tpl_sparse_table.H>
80# include <tpl_array.H>
81# include <tpl_dynList.H>
82# include <string>
83
84using namespace std;
85using namespace Aleph;
86
87// ============================================================================
88// Test Infrastructure
89// ============================================================================
90
91static int tests_passed = 0;
92static int tests_failed = 0;
93static int total_tests = 0;
94
95# define TEST(name) \
96 do { \
97 total_tests++; \
98 cout << " Testing: " << name << "... " << flush; \
99 } while(0)
100
101# define PASS() \
102 do { \
103 tests_passed++; \
104 cout << "\033[32mPASS\033[0m\n"; \
105 } while(0)
106
107# define FAIL(msg) \
108 do { \
109 tests_failed++; \
110 cout << "\033[31mFAIL\033[0m (" << msg << ")\n"; \
111 } while(0)
112
113# define CHECK(cond, msg) \
114 do { \
115 if (!(cond)) { FAIL(msg); return; } \
116 } while(0)
117
118# define CHECK_EQ(a, b, msg) \
119 do { \
120 if ((a) != (b)) { \
121 stringstream ss; \
122 ss << msg << " (expected " << (b) << ", got " << (a) << ")"; \
123 FAIL(ss.str()); \
124 return; \
125 } \
126 } while(0)
127
128# define CHECK_THROWS(ExType, expr, msg) \
129 do { \
130 bool caught = false; \
131 try { expr; } catch (const ExType &) { caught = true; } \
132 if (!caught) { FAIL(msg); return; } \
133 } while(0)
134
135class Timer
136{
137 chrono::high_resolution_clock::time_point start;
138public:
140 double elapsed_ms() const
141 {
142 auto end = chrono::high_resolution_clock::now();
143 return chrono::duration<double, milli>(end - start).count();
144 }
145};
146
147// Global RNG — seeded from main()
149
150// ============================================================================
151// Brute-force baselines
152// ============================================================================
153
154template <typename T>
155T brute_sum(const vector<T> & v, size_t l, size_t r)
156{
157 T s = v[l];
158 for (size_t i = l + 1; i <= r; ++i)
159 s += v[i];
160 return s;
161}
162
163template <typename T>
164T brute_product(const vector<T> & v, size_t l, size_t r)
165{
166 T p = v[l];
167 for (size_t i = l + 1; i <= r; ++i)
168 p *= v[i];
169 return p;
170}
171
172template <typename T>
173T brute_min(const vector<T> & v, size_t l, size_t r)
174{
175 T m = v[l];
176 for (size_t i = l + 1; i <= r; ++i)
177 m = min(m, v[i]);
178 return m;
179}
180
181unsigned brute_xor(const vector<unsigned> & v, size_t l, size_t r)
182{
183 unsigned x = v[l];
184 for (size_t i = l + 1; i <= r; ++i)
185 x ^= v[i];
186 return x;
187}
188
189// ============================================================================
190// Custom functors
191// ============================================================================
192
193struct Xor_Op
194{
195 constexpr unsigned operator()(unsigned a, unsigned b) const noexcept
196 {
197 return a ^ b;
198 }
199};
200
201// ============================================================================
202// 1. Edge Cases
203// ============================================================================
204
206{
207 TEST("empty table");
208
209 Sum_Disjoint_Sparse_Table<int> st(std::vector<int>{});
210 CHECK_EQ(st.size(), 0u, "size");
211 CHECK(st.is_empty(), "is_empty");
212 CHECK_EQ(st.num_levels(), 0u, "levels");
213
214 CHECK_THROWS(std::out_of_range, st.get(0),
215 "get(0) on empty should throw");
216 CHECK_THROWS(std::out_of_range, st.query(0, 0),
217 "query(0,0) on empty should throw");
218
219 PASS();
220}
221
223{
224 TEST("single element — sum");
226 CHECK_EQ(st.size(), 1u, "size");
227 CHECK_EQ(st.num_levels(), 0u, "levels");
228 CHECK_EQ(st.get(0), 42, "get(0)");
229 CHECK_EQ(st.query(0, 0), 42, "query(0,0)");
230 CHECK(!st.is_empty(), "not empty");
231 PASS();
232}
233
235{
236 TEST("single element — product");
238 CHECK_EQ(st.query(0, 0), 7LL, "query(0,0)");
239 PASS();
240}
241
243{
244 TEST("two elements — sum and product");
247 CHECK_EQ(s.query(0, 1), 13, "sum[0,1]");
248 CHECK_EQ(s.query(0, 0), 10, "sum[0,0]");
249 CHECK_EQ(s.query(1, 1), 3, "sum[1,1]");
250 CHECK_EQ(p.query(0, 1), 30, "prod[0,1]");
251 CHECK_EQ(p.query(0, 0), 10, "prod[0,0]");
252 CHECK_EQ(p.query(1, 1), 3, "prod[1,1]");
253 PASS();
254}
255
257{
258 TEST("all-equal array (n=100, val=5)");
259 vector<int> v(100, 5);
261 for (size_t l = 0; l < 100; l += 13)
262 for (size_t r = l; r < 100; r += 17)
263 CHECK_EQ(st.query(l, r), static_cast<int>(r - l + 1) * 5,
264 "all-equal sum query");
265 PASS();
266}
267
269{
270 TEST("sorted ascending — sum");
271 vector<int> v(50);
272 iota(v.begin(), v.end(), 1); // 1, 2, ..., 50
274 // sum(1..50) = 50*51/2 = 1275
275 CHECK_EQ(st.query(0, 49), 1275, "sum entire");
276 // sum(11..31) = sum(1..31) - sum(1..10) = 31*32/2 - 10*11/2 = 496 - 55 = 441
277 CHECK_EQ(st.query(10, 30), 441, "sum sub");
278 PASS();
279}
280
282{
283 TEST("sorted descending — product (small)");
284 // 5, 4, 3, 2, 1
285 Product_Disjoint_Sparse_Table<long long> st = {5LL, 4LL, 3LL, 2LL, 1LL};
286 CHECK_EQ(st.query(0, 4), 120LL, "prod entire = 5!");
287 CHECK_EQ(st.query(0, 2), 60LL, "prod[0,2] = 5*4*3");
288 CHECK_EQ(st.query(2, 4), 6LL, "prod[2,4] = 3*2*1");
289 CHECK_EQ(st.query(1, 3), 24LL, "prod[1,3] = 4*3*2");
290 PASS();
291}
292
294{
295 TEST("power-of-two sizes (1, 2, 4, 8, 16, 32, 64)");
296 for (size_t sz : {1u, 2u, 4u, 8u, 16u, 32u, 64u})
297 {
298 vector<int> v(sz);
299 for (size_t i = 0; i < sz; ++i)
300 v[i] = static_cast<int>(rng() % 100);
302 CHECK_EQ(st.size(), sz, "size mismatch");
303 int bf = brute_sum(v, 0, sz - 1);
304 CHECK_EQ(st.query(0, sz - 1), bf, "full-range sum");
305 }
306 PASS();
307}
308
310{
311 TEST("non-power-of-two sizes (3, 5, 7, 10, 13, 17, 31, 33, 63, 65, 100)");
312 for (size_t sz : {3u, 5u, 7u, 10u, 13u, 17u, 31u, 33u, 63u, 65u, 100u})
313 {
314 vector<int> v(sz);
315 for (size_t i = 0; i < sz; ++i)
316 v[i] = static_cast<int>(rng() % 100);
318 CHECK_EQ(st.size(), sz, "size mismatch");
319 int bf = brute_sum(v, 0, sz - 1);
320 CHECK_EQ(st.query(0, sz - 1), bf, "full-range sum");
321 }
322 PASS();
323}
324
325// ============================================================================
326// 2. Basic Correctness — known arrays
327// ============================================================================
328
330{
331 TEST("known array sum queries");
332 // 0 1 2 3 4 5 6 7 8 9
333 Sum_Disjoint_Sparse_Table<int> st = {5, 2, 4, 7, 1, 3, 6, 8, 0, 9};
334
335 CHECK_EQ(st.query(0, 0), 5, "[0,0]");
336 CHECK_EQ(st.query(0, 1), 7, "[0,1]");
337 CHECK_EQ(st.query(0, 9), 45, "[0,9]");
338 CHECK_EQ(st.query(4, 4), 1, "[4,4]");
339 CHECK_EQ(st.query(3, 5), 11, "[3,5]");
340 CHECK_EQ(st.query(6, 8), 14, "[6,8]");
341 CHECK_EQ(st.query(8, 9), 9, "[8,9]");
342 CHECK_EQ(st.query(1, 3), 13, "[1,3]");
343 CHECK_EQ(st.query(5, 7), 17, "[5,7]");
344 CHECK_EQ(st.query(0, 4), 19, "[0,4]");
345 PASS();
346}
347
349{
350 TEST("known array product queries");
352 {2LL, 3LL, 5LL, 7LL, 11LL};
353
354 CHECK_EQ(st.query(0, 4), 2310LL, "[0,4]");
355 CHECK_EQ(st.query(0, 0), 2LL, "[0,0]");
356 CHECK_EQ(st.query(1, 3), 105LL, "[1,3]");
357 CHECK_EQ(st.query(2, 4), 385LL, "[2,4]");
358 CHECK_EQ(st.query(0, 2), 30LL, "[0,2]");
359 CHECK_EQ(st.query(3, 4), 77LL, "[3,4]");
360 PASS();
361}
362
364{
365 TEST("get() returns correct element for all positions");
366 vector<int> v = {10, 20, 30, 40, 50};
368 for (size_t i = 0; i < v.size(); ++i)
369 CHECK_EQ(st.get(i), v[i], "get mismatch");
370 PASS();
371}
372
374{
375 TEST("values() reconstructs original array");
376 vector<int> v = {7, 3, 9, 1, 5, 8, 2};
378 auto vals = st.values();
379 CHECK_EQ(vals.size(), v.size(), "size mismatch");
380 for (size_t i = 0; i < v.size(); ++i)
381 CHECK_EQ(vals(i), v[i], "value mismatch");
382 PASS();
383}
384
385// ============================================================================
386// 3. Brute-Force Stress Tests
387// ============================================================================
388
390{
391 TEST("stress: sum n=200, 5000 random queries");
392 const size_t n = 200;
393 vector<int> v(n);
394 for (auto & x : v) x = static_cast<int>(rng() % 1000) - 500;
396
397 for (int q = 0; q < 5000; ++q)
398 {
399 size_t l = rng() % n, r = rng() % n;
400 if (l > r) swap(l, r);
401 CHECK_EQ(st.query(l, r), brute_sum(v, l, r), "stress sum mismatch");
402 }
403 PASS();
404}
405
407{
408 TEST("stress: product n=50, 3000 random queries (small values)");
409 const size_t n = 50;
411 for (auto & x : v) x = static_cast<long long>(rng() % 5) + 1; // 1..5
413
414 for (int q = 0; q < 3000; ++q)
415 {
416 size_t l = rng() % n, r = rng() % n;
417 if (l > r) swap(l, r);
418 CHECK_EQ(st.query(l, r), brute_product(v, l, r),
419 "stress product mismatch");
420 }
421 PASS();
422}
423
425{
426 TEST("stress: sum n=10000, 50000 random queries");
427 const size_t n = 10000;
428 vector<int> v(n);
429 for (auto & x : v) x = static_cast<int>(rng() % 200) - 100;
431
432 for (int q = 0; q < 50000; ++q)
433 {
434 size_t l = rng() % n, r = rng() % n;
435 if (l > r) swap(l, r);
436 CHECK_EQ(st.query(l, r), brute_sum(v, l, r), "stress large mismatch");
437 }
438 PASS();
439}
440
442{
443 TEST("stress: all point queries match original (n=500)");
444 const size_t n = 500;
445 vector<int> v(n);
446 for (auto & x : v) x = static_cast<int>(rng() % 10000);
448
449 for (size_t i = 0; i < n; ++i)
450 {
451 CHECK_EQ(st.query(i, i), v[i], "point query mismatch");
452 CHECK_EQ(st.get(i), v[i], "get mismatch");
453 }
454 PASS();
455}
456
458{
459 TEST("stress: ALL (l,r) pairs for n=80 — exhaustive sum");
460 const size_t n = 80;
461 vector<int> v(n);
462 for (auto & x : v) x = static_cast<int>(rng() % 100) - 50;
464
465 for (size_t l = 0; l < n; ++l)
466 for (size_t r = l; r < n; ++r)
467 CHECK_EQ(st.query(l, r), brute_sum(v, l, r), "exhaustive mismatch");
468 PASS();
469}
470
471// ============================================================================
472// 4. Custom Operations
473// ============================================================================
474
476{
477 TEST("XOR disjoint sparse table — known values");
479 {0xA3u, 0x5Fu, 0x12u, 0xB7u, 0x8Cu};
480
481 CHECK_EQ(st.query(0, 0), 0xA3u, "[0,0]");
482 CHECK_EQ(st.query(0, 1), 0xA3u ^ 0x5Fu, "[0,1]");
483 CHECK_EQ(st.query(0, 4), 0xA3u ^ 0x5Fu ^ 0x12u ^ 0xB7u ^ 0x8Cu, "[0,4]");
484 CHECK_EQ(st.query(1, 3), 0x5Fu ^ 0x12u ^ 0xB7u, "[1,3]");
485 CHECK_EQ(st.query(2, 4), 0x12u ^ 0xB7u ^ 0x8Cu, "[2,4]");
486 PASS();
487}
488
490{
491 TEST("XOR stress n=300, 10000 queries");
492 const size_t n = 300;
493 vector<unsigned> v(n);
494 for (auto & x : v) x = static_cast<unsigned>(rng());
496
497 for (int q = 0; q < 10000; ++q)
498 {
499 size_t l = rng() % n, r = rng() % n;
500 if (l > r) swap(l, r);
501 CHECK_EQ(st.query(l, r), brute_xor(v, l, r), "xor mismatch");
502 }
503 PASS();
504}
505
507{
508 TEST("min via DST cross-validated with Sparse_Table");
509 const size_t n = 200;
510 vector<int> v(n);
511 for (auto & x : v) x = static_cast<int>(rng() % 10000);
512
514 Sparse_Table<int> st(v);
515
516 for (int q = 0; q < 5000; ++q)
517 {
518 size_t l = rng() % n, r = rng() % n;
519 if (l > r) swap(l, r);
520 CHECK_EQ(dst.query(l, r), st.query(l, r),
521 "min cross-validation mismatch");
522 }
523 PASS();
524}
525
527{
528 TEST("max via DST cross-validated with Max_Sparse_Table");
529 const size_t n = 200;
530 vector<int> v(n);
531 for (auto & x : v) x = static_cast<int>(rng() % 10000);
532
535
536 for (int q = 0; q < 5000; ++q)
537 {
538 size_t l = rng() % n, r = rng() % n;
539 if (l > r) swap(l, r);
540 CHECK_EQ(dst.query(l, r), st.query(l, r),
541 "max cross-validation mismatch");
542 }
543 PASS();
544}
545
546// ============================================================================
547// 5. Construction from All Container Types
548// ============================================================================
549
551{
552 TEST("construct from Array<int>");
553 vector<int> raw = {3, 1, 4, 1, 5, 9, 2, 6};
554 Array<int> arr(raw.size());
555 for (size_t i = 0; i < raw.size(); ++i)
556 arr.append(raw[i]);
558 CHECK_EQ(st.query(0, 7), brute_sum(raw, 0, 7), "sum mismatch");
559 PASS();
560}
561
563{
564 TEST("construct from std::vector<int>");
565 vector<int> raw = {3, 1, 4, 1, 5, 9, 2, 6};
567 CHECK_EQ(st.query(0, 7), brute_sum(raw, 0, 7), "sum mismatch");
568 PASS();
569}
570
572{
573 TEST("construct from DynList<int>");
574 vector<int> raw = {3, 1, 4, 1, 5, 9, 2, 6};
576 for (size_t i = 0; i < raw.size(); ++i)
577 dl.append(raw[i]);
579 CHECK_EQ(st.query(0, 7), brute_sum(raw, 0, 7), "sum mismatch");
580 PASS();
581}
582
584{
585 TEST("construct from initializer_list");
586 Sum_Disjoint_Sparse_Table<int> st = {3, 1, 4, 1, 5, 9, 2, 6};
587 CHECK_EQ(st.query(0, 7), 31, "sum mismatch");
588 PASS();
589}
590
592{
593 TEST("construct with uniform init_val (n=50, val=-5)");
595 CHECK_EQ(st.query(0, 49), -250, "sum of 50 * -5");
596 CHECK_EQ(st.query(10, 19), -50, "sum of 10 * -5");
597 CHECK_EQ(st.query(25, 25), -5, "single element");
598 PASS();
599}
600
602{
603 TEST("all constructors produce identical query results");
604 vector<int> raw = {7, 2, 9, 4, 6, 1, 8, 3, 5};
605
606 Array<int> arr(raw.size());
607 for (size_t i = 0; i < raw.size(); ++i) arr.append(raw[i]);
609 for (size_t i = 0; i < raw.size(); ++i) dl.append(raw[i]);
610
614 Sum_Disjoint_Sparse_Table<int> from_il = {7, 2, 9, 4, 6, 1, 8, 3, 5};
615
616 for (size_t l = 0; l < raw.size(); ++l)
617 for (size_t r = l; r < raw.size(); ++r)
618 {
619 int expected = from_vec.query(l, r);
620 CHECK_EQ(from_arr.query(l, r), expected, "arr != vec");
621 CHECK_EQ(from_dl.query(l, r), expected, "dl != vec");
622 CHECK_EQ(from_il.query(l, r), expected, "il != vec");
623 }
624 PASS();
625}
626
627// ============================================================================
628// 6. Copy, Move, Swap
629// ============================================================================
630
632{
633 TEST("copy constructor");
634 vector<int> v = {10, 20, 30, 40, 50};
637
638 CHECK_EQ(copy.size(), orig.size(), "size");
639 for (size_t l = 0; l < v.size(); ++l)
640 for (size_t r = l; r < v.size(); ++r)
641 CHECK_EQ(copy.query(l, r), orig.query(l, r), "query mismatch");
642 PASS();
643}
644
646{
647 TEST("move constructor");
648 vector<int> v = {10, 20, 30, 40, 50};
650 int full_sum = orig.query(0, 4);
651
653 CHECK_EQ(moved.size(), 5u, "size");
654 CHECK_EQ(moved.query(0, 4), full_sum, "query after move");
655 PASS();
656}
657
659{
660 TEST("copy assignment");
661 vector<int> v1 = {1, 2, 3};
662 vector<int> v2 = {10, 20, 30, 40, 50};
665
666 a = b;
667 CHECK_EQ(a.size(), 5u, "size");
668 CHECK_EQ(a.query(0, 4), b.query(0, 4), "query");
669 PASS();
670}
671
673{
674 TEST("move assignment");
675 vector<int> v1 = {1, 2, 3};
676 vector<int> v2 = {10, 20, 30, 40, 50};
679 int expected = b.query(0, 4);
680
681 a = std::move(b);
682 CHECK_EQ(a.size(), 5u, "size");
683 CHECK_EQ(a.query(0, 4), expected, "query");
684 PASS();
685}
686
688{
689 TEST("swap");
690 Sum_Disjoint_Sparse_Table<int> a = {1, 2, 3};
691 Sum_Disjoint_Sparse_Table<int> b = {10, 20, 30, 40};
692
693 int a_sum = a.query(0, 2);
694 int b_sum = b.query(0, 3);
695 size_t a_sz = a.size();
696 size_t b_sz = b.size();
697
698 a.swap(b);
699 CHECK_EQ(a.size(), b_sz, "a.size after swap");
700 CHECK_EQ(b.size(), a_sz, "b.size after swap");
701 CHECK_EQ(a.query(0, 3), b_sum, "a.query after swap");
702 CHECK_EQ(b.query(0, 2), a_sum, "b.query after swap");
703 PASS();
704}
705
706// ============================================================================
707// 7. Exception Safety
708// ============================================================================
709
711{
712 TEST("query throws out_of_range when r >= n");
713 Sum_Disjoint_Sparse_Table<int> st = {1, 2, 3, 4, 5};
714 CHECK_THROWS(std::out_of_range, st.query(0, 5),
715 "r=5 >= n=5 should throw");
716 CHECK_THROWS(std::out_of_range, st.query(0, 100),
717 "r=100 should throw");
718 PASS();
719}
720
722{
723 TEST("query throws out_of_range when l > r");
724 Sum_Disjoint_Sparse_Table<int> st = {1, 2, 3, 4, 5};
725 CHECK_THROWS(std::out_of_range, st.query(3, 2),
726 "l=3 > r=2 should throw");
727 PASS();
728}
729
731{
732 TEST("get throws out_of_range when i >= n");
733 Sum_Disjoint_Sparse_Table<int> st = {1, 2, 3};
734 CHECK_THROWS(std::out_of_range, st.get(3), "i=3 >= n=3 should throw");
735 CHECK_THROWS(std::out_of_range, st.get(100), "i=100 should throw");
736 PASS();
737}
738
740{
741 TEST("boundary queries that should NOT throw");
742 Sum_Disjoint_Sparse_Table<int> st = {1, 2, 3, 4, 5};
743 bool ok = true;
744 try
745 {
746 st.query(0, 0);
747 st.query(4, 4);
748 st.query(0, 4);
749 st.get(0);
750 st.get(4);
751 }
752 catch (...)
753 {
754 ok = false;
755 }
756 CHECK(ok, "boundary queries should not throw");
757 PASS();
758}
759
760// ============================================================================
761// 8. Numerical Edge Cases
762// ============================================================================
763
765{
766 TEST("negative values — sum");
767 Sum_Disjoint_Sparse_Table<int> st = {-10, -5, -20, -1, -15};
768 CHECK_EQ(st.query(0, 4), -51, "sum of negatives");
769 CHECK_EQ(st.query(1, 3), -26, "partial sum of negatives");
770 PASS();
771}
772
774{
775 TEST("INT_MAX values — sum (beware overflow)");
776 // Use long long to avoid overflow
778 {1000000000LL, 2000000000LL, 3000000000LL, 4000000000LL};
779 CHECK_EQ(st.query(0, 3), 10000000000LL, "large sum");
780 CHECK_EQ(st.query(1, 2), 5000000000LL, "partial large sum");
781 PASS();
782}
783
785{
786 TEST("double values — sum");
787 vector<double> v = {1.5, 2.3, -0.8, 4.1, 3.7};
789
790 double expected = 1.5 + 2.3 + (-0.8) + 4.1 + 3.7;
791 double result = st.query(0, 4);
792 CHECK(abs(result - expected) < 1e-10, "double sum mismatch");
793 PASS();
794}
795
797{
798 TEST("double values — product");
799 vector<double> v = {0.5, 2.0, 3.0, 0.1, 10.0};
801
802 double expected = 0.5 * 2.0 * 3.0 * 0.1 * 10.0;
803 double result = st.query(0, 4);
804 CHECK(abs(result - expected) < 1e-10, "double product mismatch");
805
806 // Partial product
807 double partial = 2.0 * 3.0 * 0.1;
808 CHECK(abs(st.query(1, 3) - partial) < 1e-10, "partial product mismatch");
809 PASS();
810}
811
813{
814 TEST("stress: double sum n=500, 5000 queries");
815 const size_t n = 500;
816 vector<double> v(n);
817 uniform_real_distribution<double> dist(-100.0, 100.0);
818 for (auto & x : v) x = dist(rng);
820
821 for (int q = 0; q < 5000; ++q)
822 {
823 size_t l = rng() % n, r = rng() % n;
824 if (l > r) swap(l, r);
825 double bf = 0;
826 for (size_t i = l; i <= r; ++i) bf += v[i];
827 double result = st.query(l, r);
828 CHECK(abs(result - bf) < 1e-6 * max(1.0, abs(bf)),
829 "double stress mismatch");
830 }
831 PASS();
832}
833
834// ============================================================================
835// 9. Performance
836// ============================================================================
837
839{
840 TEST("performance: build n=1,000,000");
841 const size_t n = 1000000;
842 vector<int> v(n);
843 for (auto & x : v) x = static_cast<int>(rng() % 1000000);
844
845 Timer t;
847 double ms = t.elapsed_ms();
848
849 // Sanity check
850 CHECK_EQ(st.size(), n, "size");
851 cout << "[" << fixed << setprecision(1) << ms << " ms] ";
852 PASS();
853}
854
856{
857 TEST("performance: 1,000,000 queries on n=100,000");
858 const size_t n = 100000;
859 vector<int> v(n);
860 for (auto & x : v) x = static_cast<int>(rng() % 1000);
862
863 volatile long long sink = 0;
864 Timer t;
865 for (int q = 0; q < 1000000; ++q)
866 {
867 size_t l = rng() % n, r = rng() % n;
868 if (l > r) swap(l, r);
869 sink += st.query(l, r);
870 }
871 double ms = t.elapsed_ms();
872 cout << "[" << fixed << setprecision(1) << ms << " ms] ";
873 PASS();
874}
875
877{
878 TEST("performance: build n=5,000,000");
879 const size_t n = 5000000;
880 vector<int> v(n);
881 for (auto & x : v) x = static_cast<int>(rng() % 1000000);
882
883 Timer t;
885 double ms = t.elapsed_ms();
886
887 CHECK_EQ(st.size(), n, "size");
888 cout << "[" << fixed << setprecision(1) << ms << " ms] ";
889 PASS();
890}
891
892// ============================================================================
893// 10. Associativity & Disjointness Verification
894// ============================================================================
895
897{
898 TEST("sum is non-idempotent: overlapping would be wrong");
899 // This test verifies that the DST works correctly for sum,
900 // which a classical Sparse Table cannot handle.
901 // sum(a, a) = 2a != a, so overlapping sub-ranges give wrong results.
902 vector<int> v = {10, 20, 30, 40, 50};
904
905 // Verify all ranges against brute-force sum
906 for (size_t l = 0; l < v.size(); ++l)
907 for (size_t r = l; r < v.size(); ++r)
908 CHECK_EQ(dst.query(l, r), brute_sum(v, l, r),
909 "non-idempotent correctness");
910 PASS();
911}
912
914{
915 TEST("num_levels() matches expected formula");
916 // For n <= 1: levels = 0
917 // For n >= 2: levels = bit_width(n - 1)
918 Sum_Disjoint_Sparse_Table<int> s0(std::vector<int>{});
919 CHECK_EQ(s0.num_levels(), 0u, "n=0");
920
922 CHECK_EQ(s1.num_levels(), 0u, "n=1");
923
925 CHECK_EQ(s2.num_levels(), 1u, "n=2");
926
927 Sum_Disjoint_Sparse_Table<int> s4 = {1, 2, 3, 4};
928 CHECK_EQ(s4.num_levels(), 2u, "n=4");
929
930 Sum_Disjoint_Sparse_Table<int> s5 = {1, 2, 3, 4, 5};
931 CHECK_EQ(s5.num_levels(), 3u, "n=5");
932
933 Sum_Disjoint_Sparse_Table<int> s8 = {1, 2, 3, 4, 5, 6, 7, 8};
934 CHECK_EQ(s8.num_levels(), 3u, "n=8");
935
936 vector<int> v9(9, 1);
938 CHECK_EQ(s9.num_levels(), 4u, "n=9");
939 PASS();
940}
941
942// ============================================================================
943// 11. Robustness & Algebraic Properties
944// ============================================================================
945
947{
948 TEST("self copy-assignment (st = st)");
949 Sum_Disjoint_Sparse_Table<int> st = {5, 3, 8, 1, 7, 2, 9};
950 int orig = st.query(0, 6);
951 size_t orig_sz = st.size();
952
953 auto & ref = st;
954 st = ref;
955
956 CHECK_EQ(st.size(), orig_sz, "size after self-assign");
957 CHECK_EQ(st.query(0, 6), orig, "query after self-assign");
958 for (size_t i = 0; i < st.size(); ++i)
959 CHECK_EQ(st.get(i), st.get(i), "element intact");
960 PASS();
961}
962
964{
965 TEST("self swap (st.swap(st))");
966 Sum_Disjoint_Sparse_Table<int> st = {5, 3, 8, 1, 7, 2, 9};
967 int orig = st.query(0, 6);
968 size_t orig_sz = st.size();
969 st.swap(st);
970 CHECK_EQ(st.size(), orig_sz, "size after self-swap");
971 CHECK_EQ(st.query(0, 6), orig, "query after self-swap");
972 PASS();
973}
974
976{
977 TEST("get(i) == query(i,i) for all i (n=300)");
978 const size_t n = 300;
979 vector<int> v(n);
980 for (auto & x : v) x = static_cast<int>(rng() % 10000) - 5000;
982 for (size_t i = 0; i < n; ++i)
983 CHECK_EQ(st.get(i), st.query(i, i), "get != point query");
984 PASS();
985}
986
988{
989 TEST("splitting: query(l,r) == op(query(l,m), query(m+1,r))");
990 const size_t n = 60;
991 vector<int> v(n);
992 for (auto & x : v) x = static_cast<int>(rng() % 100) - 50;
994
995 for (size_t l = 0; l < n; ++l)
996 for (size_t r = l + 1; r < n; ++r)
997 {
998 int expected = st.query(l, r);
999 // Every valid split must produce the same result
1000 for (size_t m = l; m < r; ++m)
1001 {
1002 int combined = st.query(l, m) + st.query(m + 1, r);
1003 CHECK_EQ(combined, expected, "split composability mismatch");
1004 }
1005 }
1006 PASS();
1007}
1008
1010{
1011 TEST("splitting: product query(l,r) == query(l,m) * query(m+1,r)");
1012 const size_t n = 30;
1013 vector<long long> v(n);
1014 for (auto & x : v) x = static_cast<long long>(rng() % 4) + 1; // 1..4
1016
1017 for (size_t l = 0; l < n; ++l)
1018 for (size_t r = l + 1; r < n; ++r)
1019 {
1020 long long expected = st.query(l, r);
1021 for (size_t m = l; m < r; ++m)
1022 {
1023 long long combined = st.query(l, m) * st.query(m + 1, r);
1024 CHECK_EQ(combined, expected, "product split mismatch");
1025 }
1026 }
1027 PASS();
1028}
1029
1031{
1032 TEST("prefix sum: query(0,r) == sum of get(i) for i in [0,r]");
1033 const size_t n = 200;
1034 vector<int> v(n);
1035 for (auto & x : v) x = static_cast<int>(rng() % 200) - 100;
1037
1038 int running = 0;
1039 for (size_t r = 0; r < n; ++r)
1040 {
1041 running += st.get(r);
1042 CHECK_EQ(st.query(0, r), running, "prefix sum mismatch");
1043 }
1044 PASS();
1045}
1046
1048{
1049 TEST("adversarial: zigzag pattern (alternating high/low)");
1050 const size_t n = 100;
1051 vector<int> v(n);
1052 for (size_t i = 0; i < n; ++i)
1053 v[i] = (i % 2 == 0) ? 1000 : -1000;
1054
1056 for (size_t l = 0; l < n; ++l)
1057 for (size_t r = l; r < n; r += 7)
1058 CHECK_EQ(st.query(l, r), brute_sum(v, l, r), "zigzag mismatch");
1059 PASS();
1060}
1061
1063{
1064 TEST("adversarial: single non-zero among zeros");
1065 const size_t n = 128;
1066 for (size_t outlier_pos = 0; outlier_pos < n; outlier_pos += 11)
1067 {
1068 vector<int> v(n, 0);
1069 v[outlier_pos] = 42;
1071
1072 for (size_t l = 0; l < n; ++l)
1073 for (size_t r = l; r < n; r += 13)
1074 {
1075 int expected = (l <= outlier_pos && outlier_pos <= r) ? 42 : 0;
1076 CHECK_EQ(st.query(l, r), expected, "outlier mismatch");
1077 }
1078 }
1079 PASS();
1080}
1081
1083{
1084 TEST("adversarial: plateau with single spike at every position");
1085 const size_t n = 50;
1086 for (size_t spike = 0; spike < n; ++spike)
1087 {
1088 vector<int> v(n, 1);
1089 v[spike] = 1000;
1091
1092 CHECK_EQ(st.query(0, n - 1),
1093 static_cast<int>(n - 1) + 1000, "full range with spike");
1094 if (spike > 0)
1095 CHECK_EQ(st.query(0, spike - 1),
1096 static_cast<int>(spike), "before spike");
1097 if (spike < n - 1)
1098 CHECK_EQ(st.query(spike + 1, n - 1),
1099 static_cast<int>(n - 1 - spike), "after spike");
1100 }
1101 PASS();
1102}
1103
1105{
1106 TEST("sliding window queries (width=10) across n=200");
1107 const size_t n = 200;
1108 const size_t w = 10;
1109 vector<int> v(n);
1110 for (auto & x : v) x = static_cast<int>(rng() % 1000) - 500;
1112
1113 for (size_t l = 0; l + w - 1 < n; ++l)
1114 CHECK_EQ(st.query(l, l + w - 1), brute_sum(v, l, l + w - 1),
1115 "sliding window mismatch");
1116 PASS();
1117}
1118
1119// String concatenation: non-numeric, non-idempotent associative op
1121{
1122 string operator()(const string & a, const string & b) const
1123 {
1124 return a + b;
1125 }
1126};
1127
1129{
1130 TEST("string concatenation (non-numeric associative op)");
1131 vector<string> words = {"the", " ", "quick", " ", "brown", " ", "fox"};
1133
1134 CHECK_EQ(st.query(0, 6), string("the quick brown fox"), "[0,6]");
1135 CHECK_EQ(st.query(0, 0), string("the"), "[0,0]");
1136 CHECK_EQ(st.query(2, 4), string("quick brown"), "[2,4]");
1137 CHECK_EQ(st.query(4, 6), string("brown fox"), "[4,6]");
1138 CHECK_EQ(st.query(0, 2), string("the quick"), "[0,2]");
1139 CHECK_EQ(st.get(0), string("the"), "get(0)");
1140 CHECK_EQ(st.get(6), string("fox"), "get(6)");
1141
1142 // Splitting composability for strings
1143 for (size_t l = 0; l < words.size(); ++l)
1144 for (size_t r = l + 1; r < words.size(); ++r)
1145 {
1146 string expected = st.query(l, r);
1147 for (size_t m = l; m < r; ++m)
1148 {
1149 string combined = st.query(l, m) + st.query(m + 1, r);
1150 CHECK_EQ(combined, expected, "string split mismatch");
1151 }
1152 }
1153 PASS();
1154}
1155
1157{
1158 TEST("string concat stress (n=100, all pairs)");
1159 const size_t n = 100;
1160 vector<string> v(n);
1161 for (size_t i = 0; i < n; ++i)
1162 v[i] = string(1, 'a' + static_cast<char>(i % 26));
1164
1165 for (size_t l = 0; l < n; ++l)
1166 for (size_t r = l; r < n; ++r)
1167 {
1168 string bf;
1169 for (size_t i = l; i <= r; ++i) bf += v[i];
1170 CHECK_EQ(st.query(l, r), bf, "string stress mismatch");
1171 }
1172 PASS();
1173}
1174
1175// ============================================================================
1176// main
1177// ============================================================================
1178
1179int main(int argc, char * argv[])
1180{
1181 unsigned seed;
1182 if (argc > 1)
1183 seed = static_cast<unsigned>(atoi(argv[1]));
1184 else
1185 seed = static_cast<unsigned>(
1186 chrono::system_clock::now().time_since_epoch().count());
1187
1188 rng.seed(seed);
1189
1190 cout << "╔══════════════════════════════════════════════════════════════╗\n";
1191 cout << "║ Disjoint Sparse Table Test Suite ║\n";
1192 cout << "║ Testing Gen_Disjoint_Sparse_Table, Sum, Product ║\n";
1193 cout << "╚══════════════════════════════════════════════════════════════╝\n";
1194 cout << " Seed: " << seed << "\n\n";
1195
1196 cout << "=== 1. Edge Cases ===\n";
1206
1207 cout << "\n=== 2. Basic Correctness ===\n";
1212
1213 cout << "\n=== 3. Brute-Force Stress Tests ===\n";
1219
1220 cout << "\n=== 4. Custom Operations & Cross-Validation ===\n";
1225
1226 cout << "\n=== 5. Construction from All Container Types ===\n";
1233
1234 cout << "\n=== 6. Copy, Move, Swap ===\n";
1239 test_swap();
1240
1241 cout << "\n=== 7. Exception Safety ===\n";
1246
1247 cout << "\n=== 8. Numerical Edge Cases ===\n";
1253
1254 cout << "\n=== 9. Performance ===\n";
1258
1259 cout << "\n=== 10. Associativity & Disjointness ===\n";
1262
1263 cout << "\n=== 11. Robustness & Algebraic Properties ===\n";
1276
1277 cout << "\n══════════════════════════════════════════════════════════════\n";
1278 cout << " RESULTS: " << tests_passed << "/" << total_tests << " passed";
1279 if (tests_failed == 0)
1280 cout << " — \033[32mALL PASS\033[0m\n";
1281 else
1282 cout << " — \033[31m" << tests_failed << " FAILED\033[0m\n";
1283 cout << "══════════════════════════════════════════════════════════════\n";
1284
1285 return tests_failed == 0 ? 0 : 1;
1286}
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
Disjoint Sparse Table over an arbitrary associative binary operation.
void swap(Gen_Disjoint_Sparse_Table &other) noexcept
Swap this table with other in O(1).
constexpr bool is_empty() const noexcept
True if the table contains no elements.
Array< T > values() const
Reconstruct all original values into an Array.
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.
T query(const size_t l, const size_t r) const
Range query over [l, r] in O(1).
constexpr size_t size() const noexcept
Number of logical elements.
T query(const size_t l, const size_t r) const
Range query over [l, r] 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
void test_min_cross_validation()
void test_stress_sum_large()
void test_prefix_sum_consistency()
void test_max_cross_validation()
unsigned brute_xor(const vector< unsigned > &v, size_t l, size_t r)
void test_adversarial_single_outlier()
void test_perf_build_large()
void test_num_levels()
void test_adversarial_plateau_with_spike()
#define CHECK(cond, msg)
void test_string_concatenation()
void test_stress_double_sum()
void test_exception_l_greater_than_r()
void test_single_element_sum()
#define TEST(name)
#define CHECK_EQ(a, b, msg)
void test_construct_from_init_list()
void test_known_sum_array()
void test_exception_r_out_of_range()
void test_empty_table()
void test_construct_all_identical()
void test_perf_build()
void test_string_stress()
void test_stress_product_small()
void test_splitting_composability()
void test_move_constructor()
void test_stress_point_queries()
void test_stress_sum_small()
void test_splitting_product()
void test_exception_get_out_of_range()
void test_int_limits()
void test_boundary_queries_no_throw()
void test_xor_stress()
void test_two_elements()
#define CHECK_THROWS(ExType, expr, msg)
void test_construct_from_array()
void test_known_product_array()
T brute_sum(const vector< T > &v, size_t l, size_t r)
void test_move_assignment()
void test_construct_uniform()
void test_power_of_two_sizes()
void test_all_equal()
void test_copy_constructor()
void test_sliding_window()
#define PASS()
void test_negative_values()
void test_non_idempotent_correctness()
static mt19937 rng
void test_non_power_of_two_sizes()
static int total_tests
void test_construct_from_vector()
void test_perf_queries()
static int tests_passed
static int tests_failed
void test_copy_assignment()
void test_sorted_ascending()
void test_self_swap()
void test_double_product()
void test_get_equals_point_query()
void test_self_copy_assignment()
void test_values_reconstruct()
T brute_min(const vector< T > &v, size_t l, size_t r)
void test_double_sum()
void test_sorted_descending()
void test_get_all_elements()
T brute_product(const vector< T > &v, size_t l, size_t r)
void test_stress_exhaustive_small()
void test_construct_from_dynlist()
void test_adversarial_zigzag()
void test_xor_known()
void test_single_element_product()
__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< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_abs_function > > abs(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4051
__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
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Sparse Table for range maximum queries.
Disjoint Sparse Table for range product queries.
Sparse Table for range minimum queries.
Disjoint Sparse Table for range sum queries.
string operator()(const string &a, const string &b) const
constexpr unsigned operator()(unsigned a, unsigned b) const noexcept
Dynamic array container with automatic resizing.
Disjoint Sparse Table for static range queries in O(1).
Alias for htlist.H (DynList implementation).
Sparse Table for static range queries in O(1).
DynList< int > l