Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah-dry-mixin_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
40#include <vector>
41#include <string>
42#include <htlist.H>
43#include <ah-dry-mixin.H>
44
45using namespace std;
46using namespace Aleph;
47
48// =============================================================================
49// Test Container using CRTP Mixins
50// =============================================================================
51
53template <typename T>
54class MixinVector : public TraverseMixin<MixinVector<T>, T>,
55 public LocateMixin<MixinVector<T>, T>,
56 public FunctionalMixin<MixinVector<T>, T>,
57 public KeysMixin<MixinVector<T>, T>
58{
59 std::vector<T> data;
60
61public:
62 using Item_Type = T;
63
64 MixinVector() = default;
65 MixinVector(std::initializer_list<T> init) : data(init) {}
66
67 void append(const T & item) { data.push_back(item); }
68 void insert(const T & item) { data.insert(data.begin(), item); }
69 size_t size() const noexcept { return data.size(); }
70 bool is_empty() const noexcept { return data.empty(); }
71
72 // Iterator compatible with Aleph requirements
74 {
76 size_t pos;
77
78 public:
80 Iterator(const MixinVector & c) : container(&c), pos(0) {}
81
82 bool has_curr() const noexcept { return pos < container->data.size(); }
83 T & get_curr() const { return const_cast<T&>(container->data[pos]); }
84 void next() { ++pos; }
85 void next_ne() noexcept { ++pos; }
86 };
87
88 Iterator get_it() const { return Iterator(*this); }
89
90 // For testing equality
91 bool operator==(const MixinVector & other) const { return data == other.data; }
92};
93
94
95// =============================================================================
96// TraverseMixin Tests
97// =============================================================================
98
100{
101 MixinVector<int> v = {1, 2, 3, 4, 5};
102
103 int sum = 0;
104 bool completed = v.traverse([&sum](int x) {
105 sum += x;
106 return true;
107 });
108
110 EXPECT_EQ(sum, 15);
111}
112
114{
115 MixinVector<int> v = {1, 2, 3, 4, 5};
116
117 int sum = 0;
118 bool completed = v.traverse([&sum](int x) {
119 if (x > 3) return false;
120 sum += x;
121 return true;
122 });
123
125 EXPECT_EQ(sum, 6); // 1 + 2 + 3
126}
127
129{
131
132 bool completed = v.traverse([](int) { return true; });
134}
135
136
137// =============================================================================
138// FunctionalMixin Tests
139// =============================================================================
140
142{
143 MixinVector<int> v = {1, 2, 3};
144
145 int sum = 0;
146 v.for_each([&sum](int x) { sum += x; });
147
148 EXPECT_EQ(sum, 6);
149}
150
152{
153 MixinVector<int> v = {1, 2, 3};
154
155 v.mutable_for_each([](int& x) { x *= 2; });
156
157 int sum = 0;
158 v.for_each([&sum](int x) { sum += x; });
159 EXPECT_EQ(sum, 12); // 2 + 4 + 6
160}
161
163{
164 MixinVector<int> v = {2, 4, 6, 8};
165
166 EXPECT_TRUE(v.all([](int x) { return x % 2 == 0; }));
167 EXPECT_FALSE(v.all([](int x) { return x > 5; }));
168}
169
171{
173 // Vacuous truth
174 EXPECT_TRUE(v.all([](int) { return false; }));
175}
176
178{
179 MixinVector<int> v = {1, 3, 5, 6, 7};
180
181 EXPECT_TRUE(v.exists([](int x) { return x % 2 == 0; })); // 6
182 EXPECT_FALSE(v.exists([](int x) { return x > 100; }));
183}
184
186{
188 EXPECT_FALSE(v.exists([](int) { return true; }));
189}
190
192{
193 MixinVector<int> v = {1, 2, 3};
194
195 auto squared = v.maps<int>([](int x) { return x * x; });
196
197 EXPECT_EQ(squared.size(), 3);
198
199 int expected[] = {1, 4, 9};
200 int i = 0;
201 squared.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
202}
203
205{
206 MixinVector<int> v = {1, 2, 3};
207
208 auto strs = v.maps<string>([](int x) { return to_string(x); });
209
210 EXPECT_EQ(strs.size(), 3);
211
212 string expected[] = {"1", "2", "3"};
213 int i = 0;
214 strs.for_each([&](const string& s) { EXPECT_EQ(s, expected[i++]); });
215}
216
218{
219 MixinVector<int> v = {1, 2, 3, 4, 5, 6};
220
221 auto evens = v.filter([](int x) { return x % 2 == 0; });
222
223 EXPECT_EQ(evens.size(), 3);
224
225 int expected[] = {2, 4, 6};
226 int i = 0;
227 evens.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
228}
229
231{
232 MixinVector<int> v = {1, 3, 5, 7};
233
234 auto evens = v.filter([](int x) { return x % 2 == 0; });
236}
237
239{
240 MixinVector<int> v = {1, 2, 3, 4, 5};
241
242 int sum = v.foldl<int>(0, [](int acc, int x) { return acc + x; });
243 EXPECT_EQ(sum, 15);
244
245 int product = v.foldl<int>(1, [](int acc, int x) { return acc * x; });
246 EXPECT_EQ(product, 120);
247}
248
250{
252
253 int result = v.foldl<int>(42, [](int acc, int x) { return acc + x; });
254 EXPECT_EQ(result, 42);
255}
256
258{
259 MixinVector<int> v = {1, 2, 3, 4, 5, 6};
260
261 auto [evens, odds] = v.partition([](int x) { return x % 2 == 0; });
262
263 EXPECT_EQ(evens.size(), 3);
264 EXPECT_EQ(odds.size(), 3);
265
266 int expected_evens[] = {2, 4, 6};
267 int expected_odds[] = {1, 3, 5};
268
269 int i = 0;
270 evens.for_each([&](int x) { EXPECT_EQ(x, expected_evens[i++]); });
271
272 i = 0;
273 odds.for_each([&](int x) { EXPECT_EQ(x, expected_odds[i++]); });
274}
275
277{
278 MixinVector<int> v = {1, 2, 3, 4, 5};
279 EXPECT_EQ(v.length(), 5);
280
281 MixinVector<int> empty;
282 EXPECT_EQ(empty.length(), 0);
283}
284
286{
287 MixinVector<int> v = {1, 2, 3, 4, 5};
288
289 auto reversed = v.rev();
290
291 EXPECT_EQ(reversed.size(), 5);
292
293 int expected[] = {5, 4, 3, 2, 1};
294 int i = 0;
295 reversed.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
296}
297
299{
300 MixinVector<int> v = {1, 2, 3, 4, 5};
301
302 auto first3 = v.take(3);
303 EXPECT_EQ(first3.size(), 3);
304
305 int expected[] = {1, 2, 3};
306 int i = 0;
307 first3.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
308
309 auto all = v.take(100);
310 EXPECT_EQ(all.size(), 5);
311}
312
314{
315 MixinVector<int> v = {1, 2, 3, 4, 5};
316
317 auto last3 = v.drop(2);
318 EXPECT_EQ(last3.size(), 3);
319
320 int expected[] = {3, 4, 5};
321 int i = 0;
322 last3.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
323
324 auto empty = v.drop(100);
325 EXPECT_TRUE(empty.is_empty());
326}
327
328
329// =============================================================================
330// LocateMixin Tests
331// =============================================================================
332
334{
335 MixinVector<int> v = {10, 20, 30, 40, 50};
336
337 EXPECT_EQ(v.nth(0), 10);
338 EXPECT_EQ(v.nth(2), 30);
339 EXPECT_EQ(v.nth(4), 50);
340
341 EXPECT_THROW(v.nth(5), std::out_of_range);
342 EXPECT_THROW(v.nth(100), std::out_of_range);
343}
344
346{
347 MixinVector<int> v = {10, 20, 30};
348
349 EXPECT_EQ(v.nth_ne(0), 10);
350 EXPECT_EQ(v.nth_ne(1), 20);
351 EXPECT_EQ(v.nth_ne(2), 30);
352}
353
355{
356 MixinVector<int> v = {10, 20, 30};
357
358 v.nth(1) = 25;
359 EXPECT_EQ(v.nth(1), 25);
360}
361
363{
364 MixinVector<int> v = {1, 2, 3, 4, 5};
365
366 int* p = v.find_ptr([](int x) { return x == 3; });
367 ASSERT_NE(p, nullptr);
368 EXPECT_EQ(*p, 3);
369
370 int* not_found = v.find_ptr([](int x) { return x == 100; });
371 EXPECT_EQ(not_found, nullptr);
372}
373
375{
376 MixinVector<int> v = {1, 2, 3, 4, 5};
377
378 int* p = v.find_ptr([](int x) { return x == 3; });
379 ASSERT_NE(p, nullptr);
380 *p = 30;
381
382 EXPECT_EQ(v.nth(2), 30);
383}
384
386{
387 MixinVector<int> v = {1, 2, 3, 4, 5};
388
389 auto [found, item] = v.find_item([](int x) { return x > 3; });
391 EXPECT_EQ(item, 4);
392
393 auto [not_found, default_val] = v.find_item([](int x) { return x > 100; });
395}
396
397
398// =============================================================================
399// KeysMixin Tests
400// =============================================================================
401
403{
404 MixinVector<int> v = {1, 2, 3};
405
406 auto k = v.keys();
407 EXPECT_EQ(k.size(), 3);
408}
409
411{
412 MixinVector<string> v = {"a", "b", "c"};
413
414 auto items = v.items();
415 EXPECT_EQ(items.size(), 3);
416}
417
418
419// =============================================================================
420// Chained Operations Tests
421// =============================================================================
422
424{
425 MixinVector<int> v = {1, 2, 3, 4, 5, 6};
426
427 // Filter evens, then square them
428 auto evens = v.filter([](int x) { return x % 2 == 0; });
429 auto squared = evens.maps<int>([](int x) { return x * x; });
430
431 EXPECT_EQ(squared.size(), 3);
432
433 int expected[] = {4, 16, 36};
434 int i = 0;
435 squared.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
436}
437
439{
440 MixinVector<int> v = {1, 2, 3, 4, 5};
441
442 // Square then sum
443 auto squared = v.maps<int>([](int x) { return x * x; });
444 int sum = squared.foldl<int>(0, [](int acc, int x) { return acc + x; });
445
446 EXPECT_EQ(sum, 1 + 4 + 9 + 16 + 25);
447}
448
449
450// =============================================================================
451// Complex Type Tests
452// =============================================================================
453
454struct Person
455{
456 string name;
457 int age;
458};
459
461{
463 people.append({"Alice", 30});
464 people.append({"Bob", 25});
465 people.append({"Charlie", 35});
466
467 // Find oldest
468 Person* oldest = people.find_ptr([](const Person& p) {
469 return p.age >= 35;
470 });
471 ASSERT_NE(oldest, nullptr);
472 EXPECT_EQ(oldest->name, "Charlie");
473
474 // Get ages
475 auto ages = people.maps<int>([](const Person& p) { return p.age; });
476 int sum = ages.foldl<int>(0, [](int acc, int a) { return acc + a; });
477 EXPECT_EQ(sum, 90);
478
479 // Filter by age
480 auto over27 = people.filter([](const Person& p) { return p.age > 27; });
481 EXPECT_EQ(over27.size(), 2);
482}
483
484
485// =============================================================================
486// Aggregation Tests (sum, product, min, max)
487// =============================================================================
488
490{
491 MixinVector<int> v = {1, 2, 3, 4, 5};
492 EXPECT_EQ(v.sum(), 15);
493 EXPECT_EQ(v.sum(10), 25); // with initial value
494}
495
497{
499 EXPECT_EQ(v.sum(), 0);
500 EXPECT_EQ(v.sum(100), 100);
501}
502
504{
505 MixinVector<string> v = {"Hello", " ", "World"};
506 EXPECT_EQ(v.sum(), "Hello World");
507 EXPECT_EQ(v.sum(string("Prefix: ")), "Prefix: Hello World");
508}
509
511{
512 MixinVector<int> v = {1, 2, 3, 4, 5};
513 EXPECT_EQ(v.product(1), 120);
514 EXPECT_EQ(v.product(2), 240);
515}
516
523
525{
526 MixinVector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
527
528 const int* min_ptr = v.min();
529 ASSERT_NE(min_ptr, nullptr);
530 EXPECT_EQ(*min_ptr, 1);
531}
532
534{
536 EXPECT_EQ(v.min(), nullptr);
537}
538
540{
541 MixinVector<int> v = {42};
542 ASSERT_NE(v.min(), nullptr);
543 EXPECT_EQ(*v.min(), 42);
544}
545
547{
548 MixinVector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
549
550 const int* max_ptr = v.max();
551 ASSERT_NE(max_ptr, nullptr);
552 EXPECT_EQ(*max_ptr, 9);
553}
554
556{
558 EXPECT_EQ(v.max(), nullptr);
559}
560
562{
563 MixinVector<int> v = {42};
564 ASSERT_NE(v.max(), nullptr);
565 EXPECT_EQ(*v.max(), 42);
566}
567
569{
570 MixinVector<string> v = {"apple", "pie", "a", "banana"};
571
572 const string* shortest = v.min_by([](const string& a, const string& b) {
573 return a.length() < b.length();
574 });
575
576 ASSERT_NE(shortest, nullptr);
577 EXPECT_EQ(*shortest, "a");
578}
579
581{
582 MixinVector<string> v = {"apple", "pie", "a", "banana"};
583
584 const string* longest = v.max_by([](const string& a, const string& b) {
585 return a.length() < b.length();
586 });
587
588 ASSERT_NE(longest, nullptr);
589 EXPECT_EQ(*longest, "banana");
590}
591
593{
594 MixinVector<int> v = {-5, 3, -10, 7, 0};
595
596 EXPECT_EQ(*v.min(), -10);
597 EXPECT_EQ(*v.max(), 7);
598}
599
600
601// =============================================================================
602// Search and Counting Tests (contains, none, count_if)
603// =============================================================================
604
606{
607 MixinVector<int> v = {1, 2, 3, 4, 5};
608
614 EXPECT_FALSE(v.has_value(100));
615}
616
622
624{
625 MixinVector<string> v = {"apple", "banana", "cherry"};
626
627 EXPECT_TRUE(v.has_value("banana"));
628 EXPECT_FALSE(v.has_value("Banana")); // case sensitive
629 EXPECT_FALSE(v.has_value("grape"));
630}
631
633{
634 MixinVector<int> v = {2, 4, 6, 8, 10};
635
636 EXPECT_TRUE(v.none([](int x) { return x % 2 != 0; })); // no odds
637 EXPECT_FALSE(v.none([](int x) { return x > 5; })); // some > 5
638}
639
641{
643 EXPECT_TRUE(v.none([](int) { return true; })); // vacuously true
644}
645
647{
648 MixinVector<int> v = {2, 4, 6};
649
650 auto is_odd = [](int x) { return x % 2 != 0; };
652}
653
655{
656 MixinVector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
657
658 EXPECT_EQ(v.count_if([](int x) { return x % 2 == 0; }), 5); // evens
659 EXPECT_EQ(v.count_if([](int x) { return x > 5; }), 5); // > 5
660 EXPECT_EQ(v.count_if([](int x) { return x > 100; }), 0); // none
661 EXPECT_EQ(v.count_if([](int) { return true; }), 10); // all
662}
663
665{
667 EXPECT_EQ(v.count_if([](int) { return true; }), 0);
668}
669
670
671// =============================================================================
672// First/Last Element Tests
673// =============================================================================
674
676{
677 MixinVector<int> v = {10, 20, 30};
678
679 const int* ptr = v.first();
680 ASSERT_NE(ptr, nullptr);
681 EXPECT_EQ(*ptr, 10);
682}
683
685{
687 EXPECT_EQ(v.first(), nullptr);
688}
689
691{
692 MixinVector<int> v = {10, 20, 30};
693 EXPECT_EQ(v.first_or(-1), 10);
694
695 MixinVector<int> empty;
696 EXPECT_EQ(empty.first_or(-1), -1);
697}
698
700{
701 MixinVector<int> v = {10, 20, 30};
702
703 const int* ptr = v.last();
704 ASSERT_NE(ptr, nullptr);
705 EXPECT_EQ(*ptr, 30);
706}
707
709{
711 EXPECT_EQ(v.last(), nullptr);
712}
713
715{
716 MixinVector<int> v = {10, 20, 30};
717 EXPECT_EQ(v.last_or(-1), 30);
718
719 MixinVector<int> empty;
720 EXPECT_EQ(empty.last_or(-1), -1);
721}
722
724{
725 MixinVector<int> v = {42};
726
727 EXPECT_EQ(*v.first(), 42);
728 EXPECT_EQ(*v.last(), 42);
729 EXPECT_EQ(v.first_or(-1), 42);
730 EXPECT_EQ(v.last_or(-1), 42);
731}
732
733
734// =============================================================================
735// Enumeration and Indexing Tests
736// =============================================================================
737
739{
740 MixinVector<string> v = {"a", "b", "c"};
741
742 auto enumerated = v.enumerate();
744
745 size_t expected_idx = 0;
746 string expected_vals[] = {"a", "b", "c"};
747 enumerated.for_each([&](const std::pair<size_t, string>& p) {
748 EXPECT_EQ(p.first, expected_idx);
750 ++expected_idx;
751 });
752}
753
760
762{
763 MixinVector<int> v = {10, 20, 30, 40, 50};
764
765 EXPECT_EQ(v.find_index([](int x) { return x == 30; }), 2);
766 EXPECT_EQ(v.find_index([](int x) { return x > 35; }), 3); // first > 35 is 40
767 EXPECT_EQ(v.find_index([](int x) { return x > 100; }), static_cast<size_t>(-1));
768}
769
771{
773 EXPECT_EQ(v.find_index([](int) { return true; }), static_cast<size_t>(-1));
774}
775
777{
778 MixinVector<int> v = {10, 20, 30, 40, 50};
779
780 EXPECT_EQ(v.index_of(10), 0);
781 EXPECT_EQ(v.index_of(30), 2);
782 EXPECT_EQ(v.index_of(50), 4);
783 EXPECT_EQ(v.index_of(99), static_cast<size_t>(-1));
784}
785
787{
788 MixinVector<string> v = {"apple", "banana", "cherry"};
789
790 EXPECT_EQ(v.index_of("banana"), 1);
791 EXPECT_EQ(v.index_of("grape"), static_cast<size_t>(-1));
792}
793
794
795// =============================================================================
796// Combined Operations with New Methods
797// =============================================================================
798
800{
801 MixinVector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
802
803 // Sum of evens using filter + foldl (since filter returns DynList)
804 auto evens = v.filter([](int x) { return x % 2 == 0; });
805 int sum = evens.foldl<int>(0, [](int acc, int x) { return acc + x; });
806 EXPECT_EQ(sum, 2 + 4 + 6 + 8 + 10);
807
808 // Direct sum on MixinVector
809 EXPECT_EQ(v.sum(), 55);
810}
811
813{
814 MixinVector<int> v = {1, 3, 5, 7, 9}; // odds only
815
816 EXPECT_EQ(*v.min(), 1);
817 EXPECT_EQ(*v.max(), 9);
818}
819
821{
822 MixinVector<string> v = {"a", "bb", "ccc", "dd"};
823
824 auto enumerated = v.enumerate();
826
827 // Count even indices using the mixin's count_if
828 // (enumerated is DynList, so we verify via foldl)
829 int even_count = enumerated.foldl<int>(0, [](int acc, const std::pair<size_t, string>& p) {
830 return acc + (p.first % 2 == 0 ? 1 : 0);
831 });
832 EXPECT_EQ(even_count, 2); // indices 0 and 2
833}
834
835
836// =============================================================================
837// Stress Tests
838// =============================================================================
839
841{
842 constexpr size_t N = 10000;
844
845 for (size_t i = 0; i < N; ++i)
846 v.append(i);
847
848 // Sum using foldl
849 size_t sum = v.foldl<size_t>(0, [](size_t acc, size_t x) { return acc + x; });
850 EXPECT_EQ(sum, N * (N - 1) / 2);
851
852 // Sum using sum()
853 EXPECT_EQ(v.sum(), N * (N - 1) / 2);
854
855 // Count evens
856 auto evens = v.filter([](size_t x) { return x % 2 == 0; });
857 EXPECT_EQ(evens.size(), N / 2);
858
859 // count_if
860 EXPECT_EQ(v.count_if([](size_t x) { return x % 2 == 0; }), N / 2);
861
862 // All positive
863 EXPECT_TRUE(v.all([](size_t x) { return x < N; }));
864
865 // Min/Max
866 EXPECT_EQ(*v.min(), 0);
867 EXPECT_EQ(*v.max(), N - 1);
868
869 // has_value
870 EXPECT_TRUE(v.has_value(N / 2));
872
873 // first/last
874 EXPECT_EQ(*v.first(), 0);
875 EXPECT_EQ(*v.last(), N - 1);
876}
877
879{
880 constexpr size_t N = 1000;
882
883 for (size_t i = 0; i < N; ++i)
884 v.append(static_cast<int>(i * 2));
885
886 auto enumerated = v.enumerate();
888
889 // Verify all indices are correct
890 size_t idx = 0;
891 enumerated.for_each([&idx](const std::pair<size_t, int>& p) {
892 EXPECT_EQ(p.first, idx);
893 EXPECT_EQ(p.second, static_cast<int>(idx * 2));
894 ++idx;
895 });
896}
897
898
899// =============================================================================
900// Constraint Tests - verify methods are properly constrained
901// =============================================================================
902
903// Type without arithmetic operators
905{
906 int value;
907 // No operator+, operator*, operator<, operator==
908};
909
911{
913 v.append({1});
914 v.append({2});
915 v.append({3});
916
917 // These should compile - they don't require operators
918 EXPECT_EQ(v.length(), 3);
919 EXPECT_NE(v.first(), nullptr);
920 EXPECT_NE(v.last(), nullptr);
921 EXPECT_EQ(v.first()->value, 1);
922 EXPECT_EQ(v.last()->value, 3);
923
924 auto enumerated = v.enumerate();
926
927 // count_if with custom predicate works
928 EXPECT_EQ(v.count_if([](const NoOpType& x) { return x.value > 1; }), 2);
929
930 // find_index with custom predicate works
931 EXPECT_EQ(v.find_index([](const NoOpType& x) { return x.value == 2; }), 1);
932
933 // The following would NOT compile (correctly!) due to requires constraints:
934 // v.sum(); // requires operator+
935 // v.product({0}); // requires operator*
936 // v.min(); // requires operator<
937 // v.max(); // requires operator<
938 // v.has_value({1}); // requires operator==
939 // v.index_of({1}); // requires operator==
940}
941
942// Type with only equality
944{
945 int value;
946 bool operator==(const EqOnlyType& other) const { return value == other.value; }
947};
948
950{
952 v.append({1});
953 v.append({2});
954 v.append({3});
955
956 // has_value and index_of work (require operator==)
957 EXPECT_TRUE(v.has_value({2}));
958 EXPECT_FALSE(v.has_value({99}));
959 EXPECT_EQ(v.index_of({2}), 1);
960
961 // The following would NOT compile:
962 // v.sum(); // requires operator+
963 // v.min(); // requires operator<
964}
965
966// Type with only less-than
968{
969 int value;
970 bool operator<(const LtOnlyType& other) const { return value < other.value; }
971};
972
974{
976 v.append({3});
977 v.append({1});
978 v.append({2});
979
980 // min and max work (require operator<)
981 EXPECT_EQ(v.min()->value, 1);
982 EXPECT_EQ(v.max()->value, 3);
983
984 // The following would NOT compile:
985 // v.sum(); // requires operator+
986 // v.has_value({1}); // requires operator==
987}
988
989
990// =============================================================================
991// Advanced Transformation Tests (unique, intersperse)
992// =============================================================================
993
995{
996 MixinVector<int> v = {1, 1, 2, 2, 2, 3, 1, 1};
997
998 auto u = v.unique();
999 EXPECT_EQ(u.size(), 4);
1000
1001 int expected[] = {1, 2, 3, 1};
1002 int i = 0;
1003 u.for_each([&](int x) { EXPECT_EQ(x, expected[i++]); });
1004}
1005
1007{
1009 auto u = v.unique();
1010 EXPECT_TRUE(u.is_empty());
1011}
1012
1014{
1015 MixinVector<int> v = {42};
1016 auto u = v.unique();
1017 EXPECT_EQ(u.size(), 1);
1018
1019 // Verify first element via iteration
1020 int first_val = 0;
1021 u.traverse([&first_val](int x) { first_val = x; return false; });
1022 EXPECT_EQ(first_val, 42);
1023}
1024
1026{
1027 MixinVector<int> v = {1, 2, 3, 4, 5};
1028 auto u = v.unique();
1029 EXPECT_EQ(u.size(), 5);
1030}
1031
1033{
1034 MixinVector<string> v = {"a", "A", "b", "B", "c"};
1035
1036 auto u = v.unique_by([](const string& a, const string& b) {
1037 return tolower(a[0]) == tolower(b[0]);
1038 });
1039
1040 EXPECT_EQ(u.size(), 3); // "a", "b", "c"
1041}
1042
1044{
1045 MixinVector<int> v = {1, 2, 3};
1046
1047 auto i = v.intersperse(0);
1048 EXPECT_EQ(i.size(), 5); // 1, 0, 2, 0, 3
1049
1050 int expected[] = {1, 0, 2, 0, 3};
1051 int idx = 0;
1052 i.for_each([&](int x) { EXPECT_EQ(x, expected[idx++]); });
1053}
1054
1056{
1058 auto i = v.intersperse(0);
1059 EXPECT_TRUE(i.is_empty());
1060}
1061
1063{
1064 MixinVector<int> v = {42};
1065 auto i = v.intersperse(0);
1066 EXPECT_EQ(i.size(), 1);
1067
1068 // Verify first element via iteration
1069 int first_val = 0;
1070 i.traverse([&first_val](int x) { first_val = x; return false; });
1071 EXPECT_EQ(first_val, 42);
1072}
1073
1075{
1076 MixinVector<string> v = {"a", "b", "c"};
1077 auto i = v.intersperse(string("-"));
1078 EXPECT_EQ(i.size(), 5);
1079}
1080
1081
1082// =============================================================================
1083// Chunking and Windowing Tests
1084// =============================================================================
1085
1087{
1088 MixinVector<int> v = {1, 2, 3, 4, 5};
1089
1090 auto chunks = v.chunk(2);
1091 EXPECT_EQ(chunks.size(), 3); // {1,2}, {3,4}, {5}
1092
1093 // Verify first chunk size via iteration
1094 size_t first_chunk_size = 0;
1095 chunks.traverse([&first_chunk_size](const auto& c) {
1096 first_chunk_size = c.size();
1097 return false;
1098 });
1100}
1101
1103{
1104 MixinVector<int> v = {1, 2, 3, 4, 5, 6};
1105
1106 auto chunks = v.chunk(2);
1107 EXPECT_EQ(chunks.size(), 3); // {1,2}, {3,4}, {5,6}
1108}
1109
1111{
1113 auto chunks = v.chunk(2);
1115}
1116
1118{
1119 MixinVector<int> v = {1, 2, 3};
1120 auto chunks = v.chunk(0);
1122}
1123
1125{
1126 MixinVector<int> v = {1, 2};
1127 auto chunks = v.chunk(10);
1128 EXPECT_EQ(chunks.size(), 1); // single chunk with all elements
1129}
1130
1132{
1133 MixinVector<int> v = {1, 2, 3, 4, 5};
1134
1135 auto windows = v.sliding(3);
1136 EXPECT_EQ(windows.size(), 3); // {1,2,3}, {2,3,4}, {3,4,5}
1137
1138 // Verify each window has 3 elements
1139 windows.for_each([](const auto& w) { EXPECT_EQ(w.size(), 3); });
1140}
1141
1143{
1144 MixinVector<int> v = {1, 2, 3, 4, 5, 6};
1145
1146 auto windows = v.sliding(2, 2);
1147 EXPECT_EQ(windows.size(), 3); // {1,2}, {3,4}, {5,6}
1148}
1149
1156
1158{
1159 MixinVector<int> v = {1, 2, 3};
1160 auto windows = v.sliding(0);
1162}
1163
1170
1171
1172// =============================================================================
1173// Conversion Tests (to_vector, join)
1174// =============================================================================
1175
1177{
1178 MixinVector<int> v = {1, 2, 3, 4, 5};
1179
1180 std::vector<int> vec = v.to_vector();
1181 EXPECT_EQ(vec.size(), 5);
1182 EXPECT_EQ(vec[0], 1);
1183 EXPECT_EQ(vec[4], 5);
1184}
1185
1187{
1189 std::vector<int> vec = v.to_vector();
1191}
1192
1194{
1195 MixinVector<int> v = {1, 2, 3, 4, 5};
1196
1197 DynList<int> list = v.to_dynlist();
1198 EXPECT_EQ(list.size(), 5);
1199
1200 // Verify order is preserved
1201 auto it = list.get_it();
1202 EXPECT_EQ(it.get_curr(), 1);
1203 it.next_ne();
1204 EXPECT_EQ(it.get_curr(), 2);
1205}
1206
1213
1215{
1216 // to_vector -> to_dynlist -> verify same content
1217 MixinVector<int> v = {10, 20, 30};
1218
1219 std::vector<int> vec = v.to_vector();
1220 DynList<int> list = v.to_dynlist();
1221
1222 // Both should have same size and content
1223 EXPECT_EQ(vec.size(), list.size());
1224
1225 size_t i = 0;
1226 list.for_each([&vec, &i](int x) {
1227 EXPECT_EQ(x, vec[i++]);
1228 });
1229}
1230
1232{
1233 MixinVector<int> v = {1, 2, 3};
1234
1235 string s = v.join(string(", "));
1236 EXPECT_EQ(s, "1, 2, 3");
1237}
1238
1240{
1241 MixinVector<int> v = {42};
1242 string s = v.join(string("-"));
1243 EXPECT_EQ(s, "42");
1244}
1245
1247{
1249 string s = v.join(string(", "));
1250 EXPECT_EQ(s, "");
1251}
1252
1254{
1255 MixinVector<string> v = {"a", "b", "c"};
1256
1257 string s = v.join_str("-");
1258 EXPECT_EQ(s, "a-b-c");
1259}
1260
1262{
1264 string s = v.join_str("-");
1265 EXPECT_EQ(s, "");
1266}
1267
1268
1269// =============================================================================
1270// Zip Tests
1271// =============================================================================
1272
1274{
1275 MixinVector<int> a = {1, 2, 3};
1277 b.append("a");
1278 b.append("b");
1279 b.append("c");
1280
1281 auto zipped = a.zip_with(b);
1282 EXPECT_EQ(zipped.size(), 3);
1283
1284 // Verify pairs
1285 auto it = zipped.get_it();
1286 EXPECT_EQ(it.get_curr().first, 1);
1287 EXPECT_EQ(it.get_curr().second, "a");
1288}
1289
1291{
1292 MixinVector<int> a = {1, 2, 3, 4, 5};
1293 MixinVector<int> b = {10, 20};
1294
1295 auto zipped = a.zip_with(b);
1296 EXPECT_EQ(zipped.size(), 2); // stops at shorter
1297}
1298
1300{
1301 MixinVector<int> a = {1, 2, 3};
1303
1304 auto zipped = a.zip_with(b);
1306}
1307
1316
1317
1318// =============================================================================
1319// Combined Advanced Operations
1320// =============================================================================
1321
1323{
1324 MixinVector<int> v = {1, 1, 2, 2, 3};
1325
1326 auto unique_result = v.unique();
1327 EXPECT_EQ(unique_result.size(), 3); // 1, 2, 3
1328
1329 // Verify values via iteration
1330 int expected[] = {1, 2, 3};
1331 int idx = 0;
1332 unique_result.for_each([&](int x) { EXPECT_EQ(x, expected[idx++]); });
1333}
1334
1336{
1337 MixinVector<int> v = {1, 2, 3, 4, 5, 6};
1338
1339 auto chunks = v.chunk(2);
1340 // Each chunk: {1,2}, {3,4}, {5,6} -> sums: 3, 7, 11
1341
1342 int total = 0;
1343 chunks.for_each([&total](const Aleph::DynList<int>& chunk) {
1344 total += chunk.foldl<int>(0, [](int acc, int x) { return acc + x; });
1345 });
1346 EXPECT_EQ(total, 21);
1347}
1348
1349
1350// =============================================================================
1351// Tests with DynList (Real Aleph Container) - uses existing methods
1352// =============================================================================
1353
1355{
1356 // DynList uses legacy ahDry.H macros, so only test existing methods
1358 for (int i = 1; i <= 5; ++i)
1359 list.append(i);
1360
1361 // foldl exists in DynList
1362 int sum = list.foldl<int>(0, [](int acc, int x) { return acc + x; });
1363 EXPECT_EQ(sum, 15);
1364
1365 // filter exists
1366 auto evens = list.filter([](int x) { return x % 2 == 0; });
1367 EXPECT_EQ(evens.size(), 2);
1368
1369 // all exists
1370 EXPECT_TRUE(list.all([](int x) { return x <= 5; }));
1371
1372 // exists works
1373 EXPECT_TRUE(list.exists([](int x) { return x == 3; }));
1374}
1375
CRTP Mixins for container functionality (DRY principle).
long double w
Definition btreepic.C:153
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
void empty() noexcept
empty the list
Definition htlist.H:1689
CRTP Mixin providing functional programming operations.
size_t find_index(Predicate pred) const
Find the index of the first element satisfying a predicate.
bool all(Operation &operation) const
Test if all elements satisfy a predicate.
const Type * min_by(Compare cmp) const
Find the minimum element using a custom comparator.
auto mutable_for_each(Operation &operation) -> decltype(self())
Apply an operation to each element (mutable).
Container< Type > drop(const size_t n) const
Skip the first n elements.
Container< std::pair< size_t, Type > > enumerate() const
Enumerate elements with their indices.
Container< Container< Type > > chunk(size_t n) const
Split into chunks of fixed size.
size_t count_if(Predicate pred) const
Count elements satisfying a predicate.
DynListType to_dynlist() const
Convert container to DynList.
DynList< Type > filter(Operation &operation) const
Filter elements by a predicate.
const Type * max() const
Find the maximum element.
Container< Type > rev() const
Create a reversed copy.
Container< __Type > maps(Operation &operation) const
Transform elements using a mapping function.
Container< Container< Type > > sliding(size_t size, size_t step=1) const
Create sliding windows of fixed size.
Container< Type > take(const size_t n) const
Take the first n elements.
std::string join_str(const std::string &sep=", ") const
Join string elements with separator.
Container< Type > unique() const
Remove consecutive duplicate elements.
const Type * first() const
Get the first element.
Container< std::pair< Type, typename Other::Item_Type > > zip_with(const Other &other) const
Zip with another container.
bool none(Predicate &pred) const
Check if no element satisfies a predicate.
Type last_or(const Type &default_val) const
Get the last element or a default value.
bool has_value(const Type &val) const
Check if container has a value.
StringType join(const StringType &sep=StringType{", "}) const
Join elements into a string with separator.
bool exists(Operation &operation) const
Test if any element satisfies a predicate.
size_t index_of(const Type &val) const
Find the index of a specific value.
__Type foldl(const __Type &init, std::function< __Type(const __Type &, const Type &)> operation) const
Left fold (reduce) with initial value.
auto for_each(Operation &operation) const -> decltype(self())
Apply an operation to each element (read-only).
Type product(const Type &init) const
Compute the product of all elements.
size_t length() const noexcept
Count the number of elements.
Container< Type > unique_by(EqPred eq) const
Remove consecutive duplicates using a custom equality predicate.
Type sum(const Type &init=Type{}) const
Compute the sum of all elements.
const Type * max_by(Compare cmp) const
Find the maximum element using a custom comparator.
std::pair< DynList< Type >, DynList< Type > > partition(Operation &op) const
Partition elements by a predicate.
std::vector< Type > to_vector() const
Convert to std::vector.
Container< Type > intersperse(const Type &sep) const
Intersperse a separator between elements.
Type first_or(const Type &default_val) const
Get the first element or a default value.
const Type * last() const
Get the last element.
const Type * min() const
Find the minimum element.
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
CRTP Mixin for extracting keys from set-like containers.
Container< Type > items() const
Alias for keys().
Container< Type > keys() const
Extract all keys as a list.
CRTP Mixin providing element location operations.
std::tuple< bool, Type > find_item(Operation &operation)
Find element with success flag.
Type * find_ptr(Operation &operation)
Find the first element satisfying a predicate.
Type & nth_ne(const size_t n) const noexcept
Access the n-th element (unchecked).
Type & nth(const size_t n) const
Access the n-th element (bounds-checked).
CRTP Mixin providing traversal operations.
bool traverse(Operation &operation) const
Traverse all elements, applying an operation to each.
__T foldl(const __T &init, Op &op) const
Fold the elements of the container to a specific result.
Definition ah-dry.H:1034
Aleph::DynList< T > filter(Operation &operation) const
Filter the elements of a container according to a matching criteria.
Definition ah-dry.H:1135
bool exists(Operation &op) const
Test for existence in the container of an element satisfying a criteria.
Definition ah-dry.H:846
bool all(Operation &operation) const
Check if all the elements of container satisfy a condition.
Definition ah-dry.H:816
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:685
Aleph::DynList< __T > maps(Operation &op) const
Map the elements of the container.
Definition ah-dry.H:904
Type * find_ptr(Operation &operation) noexcept(operation_is_noexcept< Operation >())
Find a pointer to an item in the container according to a searching criteria.
Definition ah-dry.H:318
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
Iterator(const MixinVector &c)
const MixinVector * container
bool has_curr() const noexcept
Simple test container using std::vector internally but with Aleph mixins.
void insert(const T &item)
Iterator get_it() const
bool operator==(const MixinVector &other) const
void append(const T &item)
std::vector< T > data
MixinVector()=default
MixinVector(std::initializer_list< T > init)
size_t size() const noexcept
bool is_empty() const noexcept
#define TEST(name)
#define N
Definition fib.C:294
Singly linked list implementations with head-tail access.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::string tolower(const char *str)
Convert a C std::string to lower-case.
const Container::Item_Type * min_ptr(const Container &container, Cmp cmp=Cmp())
Find the minimum element in a container.
bool all(Container &container, Operation &operation)
Return true if all elements satisfy a predicate.
bool completed() const noexcept
Return true if all underlying iterators are finished.
Definition ah-zip.H:140
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
T product(const Container &container, const T &init=T{1})
Compute product of all elements.
const Container::Item_Type * max_ptr(const Container &container, Cmp cmp=Cmp())
Find the maximum element in a container.
static bool init
Definition hash-fct.C:47
std::string to_string(const time_t t, const std::string &format)
Format a time_t value into a string using format.
Definition ah-date.H:140
DynList< T > maps(const C &c, Op op)
Classic map operation.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
STL namespace.
#define is_odd(x)
Definition ran_array.c:30
Represents a missing value.
bool operator==(const EqOnlyType &other) const
bool traverse(Operation &operation) noexcept(traverse_is_noexcept< Operation >())
Traverse the container via its iterator and performs a conditioned operation on each item.
Definition ah-dry.H:95
bool operator<(const LtOnlyType &other) const