Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah_ranges_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
33
38#include <gtest/gtest.h>
39#include <vector>
40#include <string>
41#include <type_traits>
42
43#include <ah-ranges.H>
44#include <htlist.H>
45#include <tpl_dynArray.H>
46#include <tpl_dynDlist.H>
47#include <tpl_dynListStack.H>
48#include <tpl_dynListQueue.H>
49#include <tpl_dynSetTree.H>
50
51// Regression guard for the portability escape hatch (feat/ranges-isolation):
52// when the library is built with ranges forced off (-DALEPH_DISABLE_RANGES=ON,
53// which defines ALEPH_NO_RANGES), the auto-detection must resolve to 0 so the
54// ranges-off code path is exercised. The CI `ranges-off` job builds this file
55// with ALEPH_NO_RANGES defined, so this assertion locks the override contract.
56#ifdef ALEPH_NO_RANGES
57static_assert(ALEPH_HAS_RANGES == 0,
58 "ALEPH_NO_RANGES must force ALEPH_HAS_RANGES to 0");
59#endif
60
61using namespace Aleph;
62
63// ============================================================================
64// Feature Detection Tests
65// ============================================================================
66
68 #if ALEPH_HAS_RANGES
69 SUCCEED() << "C++20 ranges support is available";
70 #else
71 GTEST_SKIP() << "std::ranges not fully supported on this platform (libc++ version)";
72 #endif
73}
74
75#if ALEPH_HAS_RANGES
76
78 #ifdef ALEPH_HAS_RANGES
79 SUCCEED();
80 #else
81 FAIL() << "ALEPH_HAS_RANGES should be defined";
82 #endif
83
84 #ifdef ALEPH_HAS_STRIDE
85 SUCCEED();
86 #else
87 FAIL() << "ALEPH_HAS_STRIDE should be defined";
88 #endif
89
90 #ifdef ALEPH_HAS_ENUMERATE
91 SUCCEED();
92 #else
93 FAIL() << "ALEPH_HAS_ENUMERATE should be defined";
94 #endif
95}
96
97// ============================================================================
98// Pipe Adaptor Tests - to_dynlist_v
99// ============================================================================
100
102 auto list = std::views::iota(1, 6) | to_dynlist_v;
103
104 ASSERT_EQ(list.size(), 5);
105
106 int expected = 1;
107 for (auto x : list) {
108 EXPECT_EQ(x, expected++);
109 }
110}
111
113 auto evens = std::views::iota(1, 11)
114 | std::views::filter([](int x) { return x % 2 == 0; })
115 | to_dynlist_v;
116
117 ASSERT_EQ(evens.size(), 5);
118
119 int expected = 2;
120 for (auto x : evens) {
122 expected += 2;
123 }
124}
125
127 auto squares = std::views::iota(1, 6)
128 | std::views::transform([](int x) { return x * x; })
129 | to_dynlist_v;
130
131 ASSERT_EQ(squares.size(), 5);
132
133 DynList<int> expected = {1, 4, 9, 16, 25};
134 auto it1 = squares.get_it();
135 auto it2 = expected.get_it();
136 while (it1.has_curr() && it2.has_curr()) {
137 EXPECT_EQ(it1.get_curr(), it2.get_curr());
138 it1.next();
139 it2.next();
140 }
141}
142
144 std::vector<int> vec = {10, 20, 30, 40, 50};
145 auto list = vec | std::views::all | to_dynlist_v;
146
147 ASSERT_EQ(list.size(), 5);
148
149 size_t i = 0;
150 for (auto x : list) {
151 EXPECT_EQ(x, vec[i++]);
152 }
153}
154
155// ============================================================================
156// Pipe Adaptor Tests - to_dynarray_v
157// ============================================================================
158
160 auto arr = std::views::iota(1, 6) | to_dynarray_v;
161
162 ASSERT_EQ(arr.size(), 5);
163
164 for (int i = 0; i < 5; ++i) {
165 EXPECT_EQ(int(arr[i]), i + 1);
166 }
167}
168
170 auto odds = std::views::iota(1, 11)
171 | std::views::filter([](int x) { return x % 2 == 1; })
173
174 ASSERT_EQ(odds.size(), 5);
175 EXPECT_EQ(int(odds[0]), 1);
176 EXPECT_EQ(int(odds[1]), 3);
177 EXPECT_EQ(int(odds[2]), 5);
178 EXPECT_EQ(int(odds[3]), 7);
179 EXPECT_EQ(int(odds[4]), 9);
180}
181
183 // Filter -> Transform -> Take
184 auto result = std::views::iota(1, 100)
185 | std::views::filter([](int x) { return x % 3 == 0; })
186 | std::views::transform([](int x) { return x * 2; })
187 | std::views::take(5)
189
190 ASSERT_EQ(result.size(), 5);
191 EXPECT_EQ(int(result[0]), 6); // 3 * 2
192 EXPECT_EQ(int(result[1]), 12); // 6 * 2
193 EXPECT_EQ(int(result[2]), 18); // 9 * 2
194 EXPECT_EQ(int(result[3]), 24); // 12 * 2
195 EXPECT_EQ(int(result[4]), 30); // 15 * 2
196}
197
198// ============================================================================
199// Pipe Adaptor Tests - to_dyndlist_v
200// ============================================================================
201
203 auto dlist = std::views::iota(1, 6) | to_dyndlist_v;
204
205 ASSERT_EQ(dlist.size(), 5);
206
207 int expected = 1;
208 for (auto x : dlist) {
209 EXPECT_EQ(x, expected++);
210 }
211}
212
213// ============================================================================
214// Pipe Adaptor Tests - Stack and Queue
215// ============================================================================
216
218 auto stack = std::views::iota(1, 6) | to_dynliststack_v;
219
220 EXPECT_EQ(stack.size(), 5);
221
222 // Stack: last pushed is on top (5 is top)
223 EXPECT_EQ(stack.top(), 5);
224}
225
227 auto queue = std::views::iota(1, 6) | to_dynlistqueue_v;
228
229 EXPECT_EQ(queue.size(), 5);
230
231 // Queue: first put is at front
232 EXPECT_EQ(queue.front(), 1);
233 EXPECT_EQ(queue.rear(), 5);
234}
235
236// ============================================================================
237// Generic to<Container>() Adaptor Tests
238// ============================================================================
239
241 auto list = std::views::iota(1, 6) | to<DynList<int>>();
242
243 ASSERT_EQ(list.size(), 5);
244
245 int expected = 1;
246 for (auto x : list) {
247 EXPECT_EQ(x, expected++);
248 }
249}
250
252 auto arr = std::views::iota(10, 15) | to<DynArray<int>>();
253
254 ASSERT_EQ(arr.size(), 5);
255 EXPECT_EQ(int(arr[0]), 10);
256 EXPECT_EQ(int(arr[4]), 14);
257}
258
260 auto set = std::views::iota(1, 11)
261 | std::views::filter([](int x) { return x % 2 == 0; })
262 | to<DynSetRbTree<int>>();
263
264 EXPECT_EQ(set.size(), 5);
265 EXPECT_TRUE(set.has(2));
266 EXPECT_TRUE(set.has(4));
267 EXPECT_TRUE(set.has(6));
268 EXPECT_TRUE(set.has(8));
269 EXPECT_TRUE(set.has(10));
270 EXPECT_FALSE(set.has(1));
271 EXPECT_FALSE(set.has(3));
272}
273
274// ============================================================================
275// Internal Range Functions Tests (using std::vector which is std::ranges::range)
276// ============================================================================
277
279 std::vector<int> all_positive = {1, 2, 3, 4, 5};
280 std::vector<int> has_negative = {1, 2, -3, 4, 5};
281
282 EXPECT_TRUE(detail::ranges_all_of(all_positive, [](int x) { return x > 0; }));
283 EXPECT_FALSE(detail::ranges_all_of(has_negative, [](int x) { return x > 0; }));
284}
285
287 std::vector<int> no_even = {1, 3, 5, 7, 9};
288 std::vector<int> has_even = {1, 2, 3, 4, 5};
289
290 EXPECT_FALSE(detail::ranges_any_of(no_even, [](int x) { return x % 2 == 0; }));
291 EXPECT_TRUE(detail::ranges_any_of(has_even, [](int x) { return x % 2 == 0; }));
292}
293
295 std::vector<int> all_positive = {1, 2, 3, 4, 5};
296 std::vector<int> has_negative = {1, 2, -3, 4, 5};
297
298 EXPECT_TRUE(detail::ranges_none_of(all_positive, [](int x) { return x < 0; }));
299 EXPECT_FALSE(detail::ranges_none_of(has_negative, [](int x) { return x < 0; }));
300}
301
303 std::vector<int> vec = {1, 2, 3, 4, 5};
304
305 auto it = detail::ranges_find_if(vec, [](int x) { return x > 3; });
306 ASSERT_NE(it, vec.end());
307 EXPECT_EQ(*it, 4);
308
309 auto not_found = detail::ranges_find_if(vec, [](int x) { return x > 10; });
310 EXPECT_EQ(not_found, vec.end());
311}
312
314 std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
315
316 auto even_count = detail::ranges_count_if(vec, [](int x) { return x % 2 == 0; });
318
319 auto gt_five = detail::ranges_count_if(vec, [](int x) { return x > 5; });
320 EXPECT_EQ(gt_five, 5);
321}
322
324 std::vector<int> vec = {1, 2, 3, 4, 5};
325
326 // Sum
327 auto sum = detail::ranges_fold_left(vec, 0, std::plus<>{});
328 EXPECT_EQ(sum, 15);
329
330 // Product
331 auto product = detail::ranges_fold_left(vec, 1, std::multiplies<>{});
332 EXPECT_EQ(product, 120);
333
334 // String concatenation
335 std::vector<std::string> strs = {"Hello", " ", "World"};
336 auto concat = detail::ranges_fold_left(strs, std::string{}, std::plus<>{});
337 EXPECT_EQ(concat, "Hello World");
338}
339
341 std::vector<int> vec = {1, 2, 3, 4, 5};
342
343 auto sum = detail::ranges_sum(vec);
344 EXPECT_EQ(sum, 15);
345}
346
348 std::vector<int> vec = {1, 2, 3, 4, 5};
349
350 auto prod = detail::ranges_product(vec);
351 EXPECT_EQ(prod, 120);
352}
353
354// ============================================================================
355// Lazy Range Generation Tests
356// ============================================================================
357
359 auto range = lazy_range(0, 5);
360
361 int count = 0;
362 for (auto x : range) {
363 EXPECT_EQ(x, count++);
364 }
365 EXPECT_EQ(count, 5);
366}
367
369 auto range = lazy_range(1, 6);
370
371 std::vector<int> result;
372 for (auto x : range) {
373 result.push_back(x);
374 }
375
376 ASSERT_EQ(result.size(), 5);
377 for (int i = 0; i < 5; ++i) {
378 EXPECT_EQ(result[i], i + 1);
379 }
380}
381
383 auto first_10 = lazy_iota(1) | std::views::take(10) | to_dynarray_v;
384
385 ASSERT_EQ(first_10.size(), 10);
386 for (int i = 0; i < 10; ++i) {
387 EXPECT_EQ(int(first_10[i]), i + 1);
388 }
389}
390
391// ============================================================================
392// RangeLike Concept Tests
393// ============================================================================
394
396 static_assert(RangeLike<std::vector<int>>);
397 static_assert(RangeLike<std::string>);
398# if !(defined(_MSC_VER) && defined(__clang__))
399 // clang-cl + MSVC STL: std::ranges::range<std::array> fails at the CPO
400 // level (_Begin::_Cpo) — compiler/library bug, not a standard violation.
401 static_assert(RangeLike<std::array<int, 5>>);
402# endif
403}
404
406 using IotaView = decltype(std::views::iota(1, 10));
407 static_assert(RangeLike<IotaView>);
408
409 std::vector<int> v = {1, 2, 3};
410 using FilteredView = decltype(v | std::views::filter([](int x) { return x > 0; }));
411 static_assert(RangeLike<FilteredView>);
412}
413
414// ============================================================================
415// Edge Cases
416// ============================================================================
417
419 auto empty = std::views::iota(1, 1) | to_dynlist_v; // [1, 1) is empty
420 EXPECT_EQ(empty.size(), 0);
421}
422
424 auto empty = std::views::iota(5, 5) | to_dynarray_v;
425 EXPECT_EQ(empty.size(), 0);
426}
427
429 auto single = std::views::iota(42, 43) | to_dynlist_v;
430 ASSERT_EQ(single.size(), 1);
431 EXPECT_EQ(single.get_first(), 42);
432}
433
435 const int N = 10000;
436 auto large = std::views::iota(1, N + 1) | to_dynarray_v;
437
438 ASSERT_EQ(large.size(), N);
439 EXPECT_EQ(int(large[0]), 1);
440 EXPECT_EQ(int(large[N - 1]), N);
441
442 // Verify sum using fold
443 long long sum = detail::ranges_fold_left(large, 0LL, std::plus<>{});
444 EXPECT_EQ(sum, static_cast<long long>(N) * (N + 1) / 2);
445}
446
447// ============================================================================
448// String Type Tests
449// ============================================================================
450
452 std::vector<std::string> strs = {"hello", "world", "test"};
453 auto list = strs | std::views::all | to_dynlist_v;
454
455 ASSERT_EQ(list.size(), 3);
456
457 auto it = list.get_it();
458 EXPECT_EQ(it.get_curr(), "hello");
459 it.next();
460 EXPECT_EQ(it.get_curr(), "world");
461 it.next();
462 EXPECT_EQ(it.get_curr(), "test");
463}
464
466 std::vector<std::string> strs = {"a", "bb", "ccc"};
467 auto lengths = strs
468 | std::views::transform([](const std::string& s) { return s.length(); })
470
471 ASSERT_EQ(lengths.size(), 3);
472 EXPECT_EQ(size_t(lengths[0]), 1);
473 EXPECT_EQ(size_t(lengths[1]), 2);
474 EXPECT_EQ(size_t(lengths[2]), 3);
475}
476
477// ============================================================================
478// Stress Tests
479// ============================================================================
480
482 const int N = 1000;
483
484 auto result = std::views::iota(1, N + 1)
485 | std::views::filter([](int x) { return x % 2 == 0; }) // 500 evens
486 | std::views::transform([](int x) { return x * 3; }) // multiply by 3
487 | std::views::filter([](int x) { return x % 6 == 0; }) // divisible by 6
488 | std::views::take(100)
489 | to_dynlist_v;
490
491 ASSERT_EQ(result.size(), 100);
492
493 // All elements should be divisible by 6
494 for (auto x : result) {
495 EXPECT_EQ(x % 6, 0);
496 }
497}
498
500 // Build list from iota view
501 auto list1 = std::views::iota(1, 101) | to_dynlist_v;
502 EXPECT_EQ(list1.size(), 100);
503
504 // Build set from filtered iota view
505 auto set = std::views::iota(1, 51)
506 | std::views::filter([](int x) { return x > 40; })
507 | to<DynSetRbTree<int>>();
508 EXPECT_EQ(set.size(), 10); // 41..50
509}
510
511// ============================================================================
512// Tests for Aleph Container Iteration (using range-based for)
513// ============================================================================
514
516 DynList<int> list;
517 for (int i = 1; i <= 5; ++i)
518 list.append(i);
519
520 int expected = 1;
521 for (auto x : list) {
522 EXPECT_EQ(x, expected++);
523 }
524}
525
527 DynArray<int> arr;
528 for (int i = 1; i <= 5; ++i)
529 arr.append(i);
530
531 int expected = 1;
532 for (auto x : arr) {
533 EXPECT_EQ(x, expected++);
534 }
535}
536
539 for (int i = 1; i <= 5; ++i)
540 set.insert(i);
541
542 int count = 0;
543 for (auto x : set) {
544 EXPECT_GE(x, 1);
545 EXPECT_LE(x, 5);
546 ++count;
547 }
548 EXPECT_EQ(count, 5);
549}
550
551// ============================================================================
552// Note on std::ranges compatibility
553// ============================================================================
554
555// Aleph iterators currently don't satisfy the full requirements of
556// std::ranges::input_iterator because:
557// - iter_reference_t<const I> != iter_reference_t<I>
558//
559// This means std::ranges algorithms like std::ranges::all_of, std::ranges::find,
560// std::ranges::min_element cannot be used directly with Aleph containers.
561//
562// The workarounds are:
563// 1. Use the pipe adaptors (to_dynlist_v, to_dynarray_v, etc.) to convert
564// views to Aleph containers
565// 2. Use the internal detail::ranges_* functions which work with std::vector
566// 3. Use the traditional Aleph algorithms from ahFunctional.H
567
568// Test that std::ranges works with std::vector (as a sanity check)
570 std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
571
572 EXPECT_TRUE(std::ranges::any_of(vec, [](int x) { return x > 5; }));
573 EXPECT_FALSE(std::ranges::all_of(vec, [](int x) { return x < 5; }));
574 EXPECT_TRUE(std::ranges::none_of(vec, [](int x) { return x > 10; }));
575
576 auto it = std::ranges::find(vec, 5);
577 ASSERT_NE(it, vec.end());
578 EXPECT_EQ(*it, 5);
579
580 auto min_it = std::ranges::min_element(vec);
581 ASSERT_NE(min_it, vec.end());
582 EXPECT_EQ(*min_it, 1);
583
584 auto max_it = std::ranges::max_element(vec);
585 ASSERT_NE(max_it, vec.end());
586 EXPECT_EQ(*max_it, 9);
587}
588
589// ============================================================================
590// Additional Pipe Adaptor Tests - ArrayStack, ArrayQueue, Random_Set
591// ============================================================================
592
593#include <tpl_arrayStack.H>
594#include <tpl_arrayQueue.H>
595#include <tpl_random_queue.H> // Contains Random_Set
596
598 auto stack = std::views::iota(1, 6) | to_arraystack_v;
599
600 EXPECT_EQ(stack.size(), 5);
601 // Stack: last pushed is on top
602 EXPECT_EQ(stack.top(), 5);
603
604 // Pop in LIFO order
605 EXPECT_EQ(stack.pop(), 5);
606 EXPECT_EQ(stack.pop(), 4);
607 EXPECT_EQ(stack.pop(), 3);
608}
609
611 auto queue = std::views::iota(10, 15) | to_arrayqueue_v;
612
613 EXPECT_EQ(queue.size(), 5);
614 // Queue: first put is at front
615 EXPECT_EQ(queue.front(), 10);
616 EXPECT_EQ(queue.rear(), 14);
617
618 // Pop in FIFO order
619 EXPECT_EQ(queue.get(), 10);
620 EXPECT_EQ(queue.get(), 11);
621}
622
624 auto set = std::views::iota(1, 11) | to_randomset_v;
625
626 EXPECT_EQ(set.size(), 10);
627
628 // Random_Set uses for_each for iteration
629 int sum = 0;
630 set.for_each([&sum](int x) { sum += x; });
631
632 // Sum of 1 to 10 is 55
633 EXPECT_EQ(sum, 55);
634}
635
637 // Random_Set allows duplicates (it's more like a random queue)
638 std::vector<int> vec = {1, 2, 2, 3, 3, 3};
639 auto set = vec | std::views::all | to_randomset_v;
640
641 // All elements are appended (duplicates allowed)
642 EXPECT_EQ(set.size(), 6);
643}
644
645// ============================================================================
646// Detail Range Functions - Transform, Filter, Take, Drop
647// ============================================================================
648
650 std::vector<int> vec = {1, 2, 3, 4, 5};
651
652 auto doubled = detail::ranges_transform(vec, [](int x) { return x * 2; });
653
654 // Materialize to check results
655 std::vector<int> result;
656 for (auto x : doubled) {
657 result.push_back(x);
658 }
659
660 ASSERT_EQ(result.size(), 5);
661 EXPECT_EQ(result[0], 2);
662 EXPECT_EQ(result[1], 4);
663 EXPECT_EQ(result[2], 6);
664 EXPECT_EQ(result[3], 8);
665 EXPECT_EQ(result[4], 10);
666}
667
669 std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
670
671 auto evens = detail::ranges_filter(vec, [](int x) { return x % 2 == 0; });
672
673 std::vector<int> result;
674 for (auto x : evens) {
675 result.push_back(x);
676 }
677
678 ASSERT_EQ(result.size(), 5);
679 EXPECT_EQ(result[0], 2);
680 EXPECT_EQ(result[1], 4);
681 EXPECT_EQ(result[2], 6);
682 EXPECT_EQ(result[3], 8);
683 EXPECT_EQ(result[4], 10);
684}
685
687 std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
688
689 auto first_three = detail::ranges_take(vec, 3);
690
691 std::vector<int> result;
692 for (auto x : first_three) {
693 result.push_back(x);
694 }
695
696 ASSERT_EQ(result.size(), 3);
697 EXPECT_EQ(result[0], 1);
698 EXPECT_EQ(result[1], 2);
699 EXPECT_EQ(result[2], 3);
700}
701
703 std::vector<int> vec = {1, 2, 3, 4, 5};
704
705 auto last_three = detail::ranges_drop(vec, 2);
706
707 std::vector<int> result;
708 for (auto x : last_three) {
709 result.push_back(x);
710 }
711
712 ASSERT_EQ(result.size(), 3);
713 EXPECT_EQ(result[0], 3);
714 EXPECT_EQ(result[1], 4);
715 EXPECT_EQ(result[2], 5);
716}
717
719 std::vector<int> vec = {1, 2, 3, 4, 5};
720 auto empty = detail::ranges_take(vec, 0);
721
722 int count = 0;
723 for ([[maybe_unused]] auto x : empty) {
724 ++count;
725 }
726 EXPECT_EQ(count, 0);
727}
728
730 std::vector<int> vec = {1, 2, 3};
731 auto empty = detail::ranges_drop(vec, 10); // More than size
732
733 int count = 0;
734 for ([[maybe_unused]] auto x : empty) {
735 ++count;
736 }
737 EXPECT_EQ(count, 0);
738}
739
740// ============================================================================
741// Detail Range Functions - Reverse, Min, Max, Sort
742// ============================================================================
743
745 std::vector<int> vec = {1, 2, 3, 4, 5};
746
747 auto reversed = detail::ranges_reverse(vec);
748
749 std::vector<int> result;
750 for (auto x : reversed) {
751 result.push_back(x);
752 }
753
754 ASSERT_EQ(result.size(), 5);
755 EXPECT_EQ(result[0], 5);
756 EXPECT_EQ(result[1], 4);
757 EXPECT_EQ(result[2], 3);
758 EXPECT_EQ(result[3], 2);
759 EXPECT_EQ(result[4], 1);
760}
761
763 std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
764
765 auto min_it = detail::ranges_min(vec);
766 ASSERT_NE(min_it, vec.end());
767 EXPECT_EQ(*min_it, 1);
768}
769
771 std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
772
773 auto max_it = detail::ranges_max(vec);
774 ASSERT_NE(max_it, vec.end());
775 EXPECT_EQ(*max_it, 9);
776}
777
779 std::vector<int> vec = {42};
780
781 auto min_it = detail::ranges_min(vec);
782 ASSERT_NE(min_it, vec.end());
783 EXPECT_EQ(*min_it, 42);
784}
785
787 std::vector<int> vec = {5, 2, 8, 1, 9, 3, 7, 4, 6};
788
789 detail::ranges_sort(vec);
790
791 ASSERT_EQ(vec.size(), 9);
792 for (int i = 0; i < 9; ++i) {
793 EXPECT_EQ(vec[i], i + 1);
794 }
795}
796
798 std::vector<int> vec = {5, 2, 8, 1, 9, 3, 7, 4, 6};
799
800 detail::ranges_sort(vec, std::greater<>{});
801
802 ASSERT_EQ(vec.size(), 9);
803 for (int i = 0; i < 9; ++i) {
804 EXPECT_EQ(vec[i], 9 - i);
805 }
806}
807
809 std::vector<std::string> vec = {"banana", "apple", "cherry", "date"};
810
811 detail::ranges_sort(vec);
812
813 EXPECT_EQ(vec[0], "apple");
814 EXPECT_EQ(vec[1], "banana");
815 EXPECT_EQ(vec[2], "cherry");
816 EXPECT_EQ(vec[3], "date");
817}
818
819// ============================================================================
820// Detail Range Functions - Flatten (Join)
821// ============================================================================
822
824 std::vector<std::vector<int>> nested = {{1, 2}, {3}, {4, 5, 6}};
825
826 auto flat = detail::ranges_flatten(nested);
827
828 std::vector<int> result;
829 for (auto x : flat) {
830 result.push_back(x);
831 }
832
833 ASSERT_EQ(result.size(), 6);
834 EXPECT_EQ(result[0], 1);
835 EXPECT_EQ(result[1], 2);
836 EXPECT_EQ(result[2], 3);
837 EXPECT_EQ(result[3], 4);
838 EXPECT_EQ(result[4], 5);
839 EXPECT_EQ(result[5], 6);
840}
841
843 std::vector<std::vector<int>> nested = {{}, {}, {}};
844
845 auto flat = detail::ranges_flatten(nested);
846
847 int count = 0;
848 for ([[maybe_unused]] auto x : flat) {
849 ++count;
850 }
851 EXPECT_EQ(count, 0);
852}
853
855 std::vector<std::vector<int>> nested = {{1}, {}, {2, 3}, {}};
856
857 auto flat = detail::ranges_flatten(nested);
858
859 std::vector<int> result;
860 for (auto x : flat) {
861 result.push_back(x);
862 }
863
864 ASSERT_EQ(result.size(), 3);
865 EXPECT_EQ(result[0], 1);
866 EXPECT_EQ(result[1], 2);
867 EXPECT_EQ(result[2], 3);
868}
869
870// ============================================================================
871// Collect Function Tests
872// ============================================================================
873
875 auto list = collect<DynList<int>>(std::views::iota(1, 6));
876
877 ASSERT_EQ(list.size(), 5);
878 int expected = 1;
879 for (auto x : list) {
880 EXPECT_EQ(x, expected++);
881 }
882}
883
885 auto arr = collect<DynArray<int>>(std::views::iota(10, 15));
886
887 ASSERT_EQ(arr.size(), 5);
888 EXPECT_EQ(int(arr[0]), 10);
889 EXPECT_EQ(int(arr[4]), 14);
890}
891
893 auto set = collect<DynSetRbTree<int>>(std::views::iota(1, 11));
894
895 EXPECT_EQ(set.size(), 10);
896 for (int i = 1; i <= 10; ++i) {
897 EXPECT_TRUE(set.has(i));
898 }
899}
900
903 std::views::iota(1, 6) | std::views::transform([](int x) { return x * x * x; })
904 );
905
906 ASSERT_EQ(cubes.size(), 5);
907
908 DynList<int> expected = {1, 8, 27, 64, 125};
909 auto it1 = cubes.get_it();
910 auto it2 = expected.get_it();
911 while (it1.has_curr()) {
912 EXPECT_EQ(it1.get_curr(), it2.get_curr());
913 it1.next();
914 it2.next();
915 }
916}
917
918// ============================================================================
919// Chained Operations with Multiple Views
920// ============================================================================
921
923 auto result = std::views::iota(1, 100)
924 | std::views::filter([](int x) { return x % 2 == 0; })
925 | std::views::transform([](int x) { return x * x; })
926 | std::views::take(5)
927 | to_dynlist_v;
928
929 ASSERT_EQ(result.size(), 5);
930 // 2² = 4, 4² = 16, 6² = 36, 8² = 64, 10² = 100
931 DynList<int> expected = {4, 16, 36, 64, 100};
932
933 auto it1 = result.get_it();
934 auto it2 = expected.get_it();
935 while (it1.has_curr()) {
936 EXPECT_EQ(it1.get_curr(), it2.get_curr());
937 it1.next();
938 it2.next();
939 }
940}
941
943 auto result = std::views::iota(1, 11)
944 | std::views::transform([](int x) { return x * 10; })
945 | std::views::filter([](int x) { return x % 30 != 0; })
946 | std::views::drop(2)
948
949 // 10, 20, 40, 50, 70, 80, 100 (drop first 2) -> 40, 50, 70, 80, 100
950 ASSERT_EQ(result.size(), 5);
951 EXPECT_EQ(int(result[0]), 40);
952 EXPECT_EQ(int(result[1]), 50);
953 EXPECT_EQ(int(result[2]), 70);
954}
955
957 std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
958
959 auto result = detail::ranges_reverse(vec);
960 std::vector<int> rev;
961 for (auto x : result) {
962 if (x % 2 == 1) rev.push_back(x);
963 }
964
965 // Reversed odds: 9, 7, 5, 3, 1
966 ASSERT_EQ(rev.size(), 5);
967 EXPECT_EQ(rev[0], 9);
968 EXPECT_EQ(rev[1], 7);
969 EXPECT_EQ(rev[2], 5);
970 EXPECT_EQ(rev[3], 3);
971 EXPECT_EQ(rev[4], 1);
972}
973
974// ============================================================================
975// Edge Cases - More Comprehensive
976// ============================================================================
977
979 const int N = 100000;
981 std::views::iota(1, N + 1),
982 0LL,
983 std::plus<>{}
984 );
985
986 EXPECT_EQ(sum, static_cast<long long>(N) * (N + 1) / 2);
987}
988
990 std::vector<int> empty;
991
992 EXPECT_TRUE(detail::ranges_all_of(empty, [](int x) { return x > 0; }));
993 EXPECT_FALSE(detail::ranges_any_of(empty, [](int x) { return x > 0; }));
994 EXPECT_TRUE(detail::ranges_none_of(empty, [](int x) { return x > 0; }));
995 EXPECT_EQ(detail::ranges_count_if(empty, [](int x) { return x > 0; }), 0);
996}
997
999 std::vector<int> single = {42};
1000
1001 EXPECT_TRUE(detail::ranges_all_of(single, [](int x) { return x == 42; }));
1002 EXPECT_TRUE(detail::ranges_any_of(single, [](int x) { return x == 42; }));
1003 EXPECT_TRUE(detail::ranges_none_of(single, [](int x) { return x != 42; }));
1004 EXPECT_EQ(detail::ranges_count_if(single, [](int x) { return x == 42; }), 1);
1005 EXPECT_EQ(detail::ranges_sum(single), 42);
1006 EXPECT_EQ(detail::ranges_product(single), 42);
1007}
1008
1010 std::vector<int> neg = {-5, -3, -1, 0, 1, 3, 5};
1011
1012 EXPECT_EQ(detail::ranges_sum(neg), 0);
1013 EXPECT_EQ(detail::ranges_count_if(neg, [](int x) { return x < 0; }), 3);
1014
1015 auto min_it = detail::ranges_min(neg);
1016 EXPECT_EQ(*min_it, -5);
1017
1018 auto max_it = detail::ranges_max(neg);
1019 EXPECT_EQ(*max_it, 5);
1020}
1021
1023 std::vector<double> floats = {1.5, 2.5, 3.5, 4.5};
1024
1025 double sum = detail::ranges_fold_left(floats, 0.0, std::plus<>{});
1026 EXPECT_DOUBLE_EQ(sum, 12.0);
1027
1028 double product = detail::ranges_fold_left(floats, 1.0, std::multiplies<>{});
1029 EXPECT_DOUBLE_EQ(product, 1.5 * 2.5 * 3.5 * 4.5);
1030}
1031
1032// ============================================================================
1033// Complex Type Tests
1034// ============================================================================
1035
1036struct Point {
1037 int x, y;
1038 bool operator==(const Point& other) const { return x == other.x && y == other.y; }
1039 bool operator<(const Point& other) const {
1040 return x < other.x || (x == other.x && y < other.y);
1041 }
1042};
1043
1045 std::vector<Point> points = {{1, 2}, {3, 4}, {5, 6}};
1046
1047 auto distances = detail::ranges_transform(points, [](const Point& p) {
1048 return p.x * p.x + p.y * p.y; // squared distance from origin
1049 });
1050
1051 std::vector<int> result;
1052 for (auto d : distances) {
1053 result.push_back(d);
1054 }
1055
1056 ASSERT_EQ(result.size(), 3);
1057 EXPECT_EQ(result[0], 5); // 1² + 2²
1058 EXPECT_EQ(result[1], 25); // 3² + 4²
1059 EXPECT_EQ(result[2], 61); // 5² + 6²
1060}
1061
1063 std::vector<Point> points = {{0, 0}, {1, 1}, {2, 0}, {0, 2}, {3, 3}};
1064
1065 auto on_diagonal = detail::ranges_filter(points, [](const Point& p) {
1066 return p.x == p.y;
1067 });
1068
1069 int count = 0;
1070 for ([[maybe_unused]] auto& p : on_diagonal) {
1071 ++count;
1072 }
1073
1074 EXPECT_EQ(count, 3); // (0,0), (1,1), (3,3)
1075}
1076
1077// ============================================================================
1078// Lazy Range with Complex Pipelines
1079// ============================================================================
1080
1082 // Generate first 10 Fibonacci-like numbers using lazy evaluation
1083 auto fib = std::views::iota(0)
1084 | std::views::transform([](int n) {
1085 // Using formula for testing purposes
1086 int a = 0, b = 1;
1087 for (int i = 0; i < n; ++i) {
1088 int temp = a + b;
1089 a = b;
1090 b = temp;
1091 }
1092 return a;
1093 })
1094 | std::views::take(10)
1095 | to_dynarray_v;
1096
1097 ASSERT_EQ(fib.size(), 10);
1098 EXPECT_EQ(int(fib[0]), 0);
1099 EXPECT_EQ(int(fib[1]), 1);
1100 EXPECT_EQ(int(fib[2]), 1);
1101 EXPECT_EQ(int(fib[3]), 2);
1102 EXPECT_EQ(int(fib[4]), 3);
1103 EXPECT_EQ(int(fib[5]), 5);
1104 EXPECT_EQ(int(fib[6]), 8);
1105}
1106
1108 // Find first 10 primes using lazy evaluation
1109 auto is_prime = [](int n) {
1110 if (n < 2) return false;
1111 if (n == 2) return true;
1112 if (n % 2 == 0) return false;
1113 for (int i = 3; i * i <= n; i += 2)
1114 if (n % i == 0) return false;
1115 return true;
1116 };
1117
1118 auto primes = lazy_iota(2)
1119 | std::views::filter(is_prime)
1120 | std::views::take(10)
1121 | to_dynlist_v;
1122
1123 ASSERT_EQ(primes.size(), 10);
1124
1125 DynList<int> expected = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
1126 auto it1 = primes.get_it();
1127 auto it2 = expected.get_it();
1128 while (it1.has_curr()) {
1129 EXPECT_EQ(it1.get_curr(), it2.get_curr());
1130 it1.next();
1131 it2.next();
1132 }
1133}
1134
1135// ============================================================================
1136// Aleph Container with std::ranges Algorithms (via conversion)
1137// ============================================================================
1138
1141 for (int i = 1; i <= 5; ++i)
1142 original.append(i);
1143
1144 // Convert to vector for std::ranges operations
1145 std::vector<int> vec(original.begin(), original.end());
1146
1147 // Use std::ranges
1148 EXPECT_TRUE(std::ranges::all_of(vec, [](int x) { return x > 0; }));
1149 EXPECT_TRUE(std::ranges::any_of(vec, [](int x) { return x == 3; }));
1150
1151 // Transform and convert back
1152 auto doubled = vec
1153 | std::views::transform([](int x) { return x * 2; })
1154 | to_dynlist_v;
1155
1156 ASSERT_EQ(doubled.size(), 5);
1157
1158 DynList<int> expected = {2, 4, 6, 8, 10};
1159 auto it1 = doubled.get_it();
1160 auto it2 = expected.get_it();
1161 while (it1.has_curr()) {
1162 EXPECT_EQ(it1.get_curr(), it2.get_curr());
1163 it1.next();
1164 it2.next();
1165 }
1166}
1167
1169 DynArray<int> arr;
1170 for (int i = 1; i <= 10; ++i)
1171 arr.append(i);
1172
1173 // DynArray should work with range-based for
1174 std::vector<int> vec(arr.begin(), arr.end());
1175
1176 auto evens = vec
1177 | std::views::filter([](int x) { return x % 2 == 0; })
1178 | to_dynarray_v;
1179
1180 ASSERT_EQ(evens.size(), 5);
1181 EXPECT_EQ(int(evens[0]), 2);
1182 EXPECT_EQ(int(evens[4]), 10);
1183}
1184
1185// ============================================================================
1186// Stress Tests - Performance and Correctness
1187// ============================================================================
1188
1190 const int N = 50000;
1191
1192 auto result = std::views::iota(1, N + 1)
1193 | std::views::filter([](int x) { return x % 3 == 0; })
1194 | std::views::transform([](int x) { return x * 2; })
1195 | std::views::filter([](int x) { return x % 4 == 0; })
1196 | std::views::take(1000)
1197 | to_dynlist_v;
1198
1199 EXPECT_LE(result.size(), 1000);
1200
1201 // All elements should be divisible by 4
1202 for (auto x : result) {
1203 EXPECT_EQ(x % 4, 0);
1204 }
1205}
1206
1208 // Build from range -> DynList -> vector -> DynArray -> set
1209 auto list = std::views::iota(1, 101) | to_dynlist_v;
1210 EXPECT_EQ(list.size(), 100);
1211
1212 std::vector<int> vec(list.begin(), list.end());
1213 EXPECT_EQ(vec.size(), 100);
1214
1215 auto arr = vec | std::views::filter([](int x) { return x > 50; }) | to_dynarray_v;
1216 EXPECT_EQ(arr.size(), 50);
1217
1218 std::vector<int> arr_vec(arr.begin(), arr.end());
1219 auto set = arr_vec | std::views::all | to<DynSetRbTree<int>>();
1220 EXPECT_EQ(set.size(), 50);
1221
1222 EXPECT_TRUE(set.has(51));
1223 EXPECT_TRUE(set.has(100));
1224 EXPECT_FALSE(set.has(50));
1225}
1226
1228 const long long N = 100000;
1229
1231 std::views::iota(1LL, N + 1),
1232 0LL,
1233 std::plus<>{}
1234 );
1235
1236 EXPECT_EQ(sum, N * (N + 1) / 2);
1237}
1238
1239#endif // ALEPH_HAS_RANGES
1240
1241int main(int argc, char** argv) {
1242 ::testing::InitGoogleTest(&argc, argv);
1243 return RUN_ALL_TESTS();
1244}
C++20 Ranges support and adaptors for Aleph-w containers.
#define ALEPH_HAS_RANGES
Definition ah-ranges.H:153
static size_t primes[]
int main()
size_t size() const noexcept
Return the current dimension of array.
T & append()
Allocate a new entry to the end of array.
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Dynamic set implemented using Red-Black binary search trees of type Rb_Tree<Key> (bottom-up implement...
const size_t & size() const
Returns the cardinality of the set.
Key * insert(const Key &key)
Inserts a key into the dynamic set.
bool has(const Key &key) const
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1065
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:221
bool operator==(const Point &point) const noexcept
Checks for exact equality between two points.
Definition point.H:259
bool operator<(const Point &point) const noexcept
Defines a strict lexicographical ordering for points.
Definition point.H:279
Minimal std::expected-style result type for C++20.
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:796
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
#define FAIL(msg)
#define TEST(name)
#define N
Definition fib.C:294
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
Singly linked list implementations with head-tail access.
static mpfr_t y
Definition mpfr_mul_d.c:3
auto ranges_count_if(const Container &c, Pred &&pred)
Fallback count_if using range-based for loop.
Definition ah-ranges.H:957
constexpr T ranges_fold_left(Container &&c, T init, BinaryOp &&op)
Fallback fold_left using range-based for loop.
Definition ah-ranges.H:913
bool ranges_none_of(const Container &c, Pred &&pred)
Fallback none_of using range-based for loop.
Definition ah-ranges.H:948
bool ranges_any_of(const Container &c, Pred &&pred)
Fallback any_of using range-based for loop.
Definition ah-ranges.H:936
bool ranges_all_of(const Container &c, Pred &&pred)
Fallback all_of using range-based for loop.
Definition ah-ranges.H:924
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::string concat(const Args &...args)
Concatenate multiple arguments into a single std::string.
T product(const Container &container, const T &init=T{1})
Compute product of all elements.
Container< T > range(const T start, const T end, const T step=1)
Generate a range of values [start, end] with a given step.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
#define LL
Definition ran_array.c:24
bool is_prime(int n)
Circular queue implementations backed by arrays.
Stack implementations backed by dynamic or fixed arrays.
Lazy and scalable dynamic array implementation.
Dynamic doubly linked list implementation.
Dynamic queue implementation based on linked lists.
Dynamic stack implementation based on linked lists.
Dynamic set implementations based on balanced binary search trees.
Random access queue (bag) with O(1) random pop.