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