Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah-comb.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
38# include <gtest/gtest.h>
39
40# include <ah-comb.H>
41# include <ahFunctional.H>
42
43# include <set>
44# include <sstream>
45
46using namespace Aleph;
47
48namespace
49{
50std::string list_to_string(const DynList<int> & l)
51{
52 std::ostringstream oss;
53 bool first = true;
54 l.for_each([&](int v)
55 {
56 if (!first)
57 oss << ',';
58 first = false;
59 oss << v;
60 });
61 return oss.str();
62}
63}
64
73
75{
76 DynList<int> r1 = {1, 2, 3};
77 DynList<int> r2 = {4, 5, 6};
78 DynList<DynList<int>> m = {r1, r2};
79
80 DynList<int> c1 = {1, 4};
81 DynList<int> c2 = {2, 5};
82 DynList<int> c3 = {3, 6};
84
86
87 auto m2 = m;
90}
91
93{
95
97 r1.reserve(3);
98 r1(0) = 1;
99 r1(1) = 2;
100 r1(2) = 3;
101
102 DynArray<int> r2;
103 r2.reserve(3);
104 r2(0) = 4;
105 r2(1) = 5;
106 r2(2) = 6;
107
108 m.reserve(2);
109 m(0) = r1;
110 m(1) = r2;
111
113
114 ASSERT_EQ(m.size(), 3u);
115 ASSERT_EQ(m(0).size(), 2u);
116 EXPECT_EQ(m(0)(0), 1);
117 EXPECT_EQ(m(0)(1), 4);
118 EXPECT_EQ(m(1)(0), 2);
119 EXPECT_EQ(m(1)(1), 5);
120 EXPECT_EQ(m(2)(0), 3);
121 EXPECT_EQ(m(2)(1), 6);
122}
123
125{
126 DynList<int> l1 = {1, 2};
127 DynList<int> l2 = {10, 20};
128 DynList<DynList<int>> l = {l1, l2};
129
130 std::set<std::string> perms;
131 bool ok = traverse_perm(l, [&](const DynList<int> & p)
132 {
134 return true;
135 });
136
138 EXPECT_EQ(perms.size(), 4u);
139 EXPECT_TRUE(perms.contains("1,10"));
140 EXPECT_TRUE(perms.contains("2,10"));
141 EXPECT_TRUE(perms.contains("1,20"));
142 EXPECT_TRUE(perms.contains("2,20"));
143}
144
146{
147 DynList<int> l1 = {1, 2, 3};
148 DynList<int> l2 = {10, 20};
149 DynList<DynList<int>> l = {l1, l2};
150
151 size_t seen = 0;
152 bool ok = traverse_perm(l, [&](const DynList<int> &)
153 {
154 ++seen;
155 return seen < 3; // stop on third call
156 });
157
159 EXPECT_EQ(seen, 3u);
160}
161
163{
164 DynList<int> l1 = {1, 2};
165 DynList<int> l2 = {10};
166 DynList<DynList<int>> l = {l1, l2};
167
168 auto perms = build_perms(l);
169 EXPECT_EQ(perms.size(), 2u);
170
171 std::set<std::string> s;
172 perms.for_each([&](const DynList<int> & p) { s.insert(list_to_string(p)); });
173 EXPECT_TRUE(s.contains("1,10"));
174 EXPECT_TRUE(s.contains("2,10"));
175}
176
178{
179 DynList<int> l1 = {1, 2};
180 DynList<int> l2 = {2, 1};
181 DynList<DynList<int>> l = {l1, l2};
182
183 auto combs = build_combs(l);
184
185 // Possible permutations are (1,2), (1,1), (2,2), (2,1).
186 // After sorting: (1,2), (1,1), (2,2), (1,2) => unique: 3
187 EXPECT_EQ(combs.size(), 3u);
188
189 std::set<std::string> s;
190 combs.for_each([&](const DynList<int> & c) { s.insert(list_to_string(c)); });
191 EXPECT_TRUE(s.contains("1,1"));
192 EXPECT_TRUE(s.contains("1,2"));
193 EXPECT_TRUE(s.contains("2,2"));
194}
195
197{
198 DynList<int> l1 = {1, 2};
199 DynList<int> l2 = {10, 20};
200 DynList<DynList<int>> l = {l1, l2};
201
202 // Sum of first elements across permutations:
203 // (1,10), (2,10), (1,20), (2,20) => sum = 6
204 auto sum = fold_perm<size_t, int>(0u, l, [](size_t a, const DynList<int> & p)
205 {
206 return a + p.get_first();
207 });
208
209 EXPECT_EQ(sum, 6u);
210}
211
213{
215 size_t calls = 0;
216 bool ok = traverse_perm(l, [&](const DynList<int> & p)
217 {
218 ++calls;
220 return true;
221 });
223 EXPECT_EQ(calls, 1u);
224}
225
233
241
243{
245 DynList<int> r2;
246 DynList<DynList<int>> m = {r1, r2};
247
248 EXPECT_TRUE(transpose(m).is_empty());
249
250 auto m2 = m;
253}
254
255#ifndef NDEBUG
257{
258 DynList<int> r1 = {1, 2};
259 DynList<int> r2 = {3};
260 DynList<DynList<int>> m = {r1, r2};
261
262 EXPECT_DEATH((void)transpose(m), "");
263
264 auto m2 = m;
266}
267#endif
268
269// ==== New tests for additional functions ====
270
272{
273 DynList<int> l1 = {1, 2};
274 DynList<int> l2 = {10, 20, 30};
275 DynList<DynList<int>> l = {l1, l2};
276
277 EXPECT_EQ(perm_count(l), 6u); // 2 * 3
278}
279
281{
283 EXPECT_EQ(perm_count(l), 1u); // One empty permutation
284}
285
287{
288 DynList<int> l1 = {1, 2};
289 DynList<int> l2; // empty
290 DynList<DynList<int>> l = {l1, l2};
291
292 EXPECT_EQ(perm_count(l), 0u); // Any empty list = 0 permutations
293}
294
296{
297 DynList<int> l1 = {1, 2, 3, 4, 5};
298 DynList<DynList<int>> l = {l1};
299
300 EXPECT_EQ(perm_count(l), 5u);
301}
302
304{
305 DynList<int> l1 = {1, 2, 3};
306 DynList<int> l2 = {10, 20};
307 DynList<DynList<int>> l = {l1, l2};
308
309 bool found = exists_perm(l, [](const DynList<int> & p)
310 {
311 return p.get_first() == 2 && p.get_last() == 20;
312 });
313
315}
316
318{
319 DynList<int> l1 = {1, 2};
320 DynList<int> l2 = {10, 20};
321 DynList<DynList<int>> l = {l1, l2};
322
323 bool found = exists_perm(l, [](const DynList<int> & p)
324 {
325 return p.get_first() == 100; // Never true
326 });
327
329}
330
332{
333 DynList<int> l1 = {1, 2, 3};
334 DynList<int> l2 = {10, 20, 30};
335 DynList<DynList<int>> l = {l1, l2};
336
337 size_t calls = 0;
338 (void)exists_perm(l, [&calls](const DynList<int> & p)
339 {
340 ++calls;
341 return p.get_first() == 1 && p.get_last() == 10; // First one matches
342 });
343
344 EXPECT_EQ(calls, 1u); // Should stop after first match
345}
346
348{
349 DynList<int> l1 = {2, 4};
350 DynList<int> l2 = {10, 20};
351 DynList<DynList<int>> l = {l1, l2};
352
353 // All sums are >= 12
354 bool all = all_perm(l, [](const DynList<int> & p)
355 {
356 int sum = 0;
357 p.for_each([&sum](int v) { sum += v; });
358 return sum >= 12;
359 });
360
362}
363
365{
366 DynList<int> l1 = {1, 2};
367 DynList<int> l2 = {10, 20};
368 DynList<DynList<int>> l = {l1, l2};
369
370 bool all = all_perm(l, [](const DynList<int> & p)
371 {
372 return p.get_first() == 2; // Only half satisfy this
373 });
374
376}
377
379{
380 DynList<int> l1 = {1, 2, 3, 4};
381 DynList<int> l2 = {10, 20, 30, 40};
382 DynList<DynList<int>> l = {l1, l2};
383
384 size_t calls = 0;
385 (void)all_perm(l, [&calls](const DynList<int> & p)
386 {
387 ++calls;
388 return p.get_first() != 1; // First permutation fails
389 });
390
391 EXPECT_EQ(calls, 1u); // Should stop after first failure
392}
393
395{
396 DynList<int> l1 = {1, 2};
397 DynList<int> l2 = {10, 20};
398 DynList<DynList<int>> l = {l1, l2};
399
400 bool none = none_perm(l, [](const DynList<int> & p)
401 {
402 return p.get_first() == 100; // Never true
403 });
404
406}
407
409{
410 DynList<int> l1 = {1, 2};
411 DynList<int> l2 = {10, 20};
412 DynList<DynList<int>> l = {l1, l2};
413
414 bool none = none_perm(l, [](const DynList<int> & p)
415 {
416 return p.get_first() == 1; // Some satisfy
417 });
418
420}
421
423{
424 DynList<int> l1 = {1, 2, 3};
425 DynList<int> l2 = {10, 20};
426 DynList<DynList<int>> l = {l1, l2};
427
428 auto filtered = filter_perm(l, [](const DynList<int> & p)
429 {
430 return p.get_first() >= 2; // Only 2,10 2,20 3,10 3,20
431 });
432
433 EXPECT_EQ(filtered.size(), 4u);
434}
435
437{
438 DynList<int> l1 = {1, 2};
439 DynList<int> l2 = {10, 20};
440 DynList<DynList<int>> l = {l1, l2};
441
442 auto filtered = filter_perm(l, [](const DynList<int> &)
443 {
444 return false; // None pass
445 });
446
448}
449
451{
452 DynList<int> l1 = {1, 2};
453 DynList<int> l2 = {10};
454 DynList<DynList<int>> l = {l1, l2};
455
456 auto filtered = filter_perm(l, [](const DynList<int> &)
457 {
458 return true; // All pass
459 });
460
461 EXPECT_EQ(filtered.size(), 2u);
462}
463
465{
466 DynList<int> l1 = {1, 2};
467 DynList<int> l2 = {10, 20};
468 DynList<DynList<int>> l = {l1, l2};
469
470 auto sums = map_perm<int>(l, [](const DynList<int> & p)
471 {
472 int sum = 0;
473 p.for_each([&sum](int v) { sum += v; });
474 return sum;
475 });
476
477 EXPECT_EQ(sums.size(), 4u);
478
479 std::set<int> s;
480 sums.for_each([&s](int v) { s.insert(v); });
481 // (1,10)=11, (2,10)=12, (1,20)=21, (2,20)=22
482 EXPECT_TRUE(s.contains(11));
483 EXPECT_TRUE(s.contains(12));
484 EXPECT_TRUE(s.contains(21));
485 EXPECT_TRUE(s.contains(22));
486}
487
489{
490 DynList<int> l1 = {1, 2};
491 DynList<int> l2 = {10};
492 DynList<DynList<int>> l = {l1, l2};
493
494 auto strs = map_perm<std::string>(l, [](const DynList<int> & p)
495 {
496 return list_to_string(p);
497 });
498
499 EXPECT_EQ(strs.size(), 2u);
500
501 std::set<std::string> s;
502 strs.for_each([&s](const std::string & v) { s.insert(v); });
503 EXPECT_TRUE(s.contains("1,10"));
504 EXPECT_TRUE(s.contains("2,10"));
505}
506
508{
509 DynList<int> l1 = {1, 2};
510 DynList<int> l2 = {10};
511 DynList<DynList<int>> l = {l1, l2};
512
513 std::set<std::string> perms;
514 for_each_perm(l, [&perms](const DynList<int> & p)
515 {
517 });
518
519 EXPECT_EQ(perms.size(), 2u);
520 EXPECT_TRUE(perms.contains("1,10"));
521 EXPECT_TRUE(perms.contains("2,10"));
522}
523
525{
527
528 size_t calls = 0;
529 for_each_perm(l, [&calls](const DynList<int> & p)
530 {
532 ++calls;
533 });
534
535 EXPECT_EQ(calls, 1u);
536}
537
539{
540 DynList<int> l1 = {1, 2};
541 DynList<int> l2 = {10, 20};
542 DynList<int> l3 = {100, 200};
543 DynList<DynList<int>> l = {l1, l2, l3};
544
545 std::set<std::string> perms;
546 traverse_perm(l, [&perms](const DynList<int> & p)
547 {
549 return true;
550 });
551
552 EXPECT_EQ(perms.size(), 8u); // 2 * 2 * 2
553}
554
556{
557 DynList<int> l1 = {42};
558 DynList<DynList<int>> l = {l1};
559
560 std::set<std::string> perms;
561 traverse_perm(l, [&perms](const DynList<int> & p)
562 {
564 return true;
565 });
566
567 EXPECT_EQ(perms.size(), 1u);
568 EXPECT_TRUE(perms.contains("42"));
569}
570
572{
573 DynList<int> r1 = {1, 2, 3};
575
576 auto t = transpose(m);
577 EXPECT_EQ(t.size(), 3u); // 3 columns => 3 rows
578
579 size_t idx = 1;
580 for (auto it = t.get_it(); it.has_curr(); it.next_ne(), ++idx)
581 {
582 EXPECT_EQ(it.get_curr().size(), 1u);
583 EXPECT_EQ(it.get_curr().get_first(), static_cast<int>(idx));
584 }
585}
586
588{
589 DynList<int> r1 = {1};
590 DynList<int> r2 = {2};
591 DynList<int> r3 = {3};
592 DynList<DynList<int>> m = {r1, r2, r3};
593
594 auto t = transpose(m);
595 EXPECT_EQ(t.size(), 1u); // 1 column => 1 row
596 EXPECT_EQ(t.get_first().size(), 3u); // 3 rows => 3 columns
597
598 DynList<int> expected = {1, 2, 3};
599 EXPECT_EQ(t.get_first(), expected);
600}
601
603{
604 DynList<int> r1 = {1, 2, 3};
606
608 EXPECT_EQ(m.size(), 3u);
609
610 auto it = m.get_it();
611 EXPECT_EQ(it.get_curr().get_first(), 1);
612 it.next_ne();
613 EXPECT_EQ(it.get_curr().get_first(), 2);
614 it.next_ne();
615 EXPECT_EQ(it.get_curr().get_first(), 3);
616}
617
619{
620 DynList<int> r1 = {1, 2};
621 DynList<int> r2 = {3, 4};
622 DynList<DynList<int>> m = {r1, r2};
623
624 auto t = transpose(m);
625
626 DynList<int> c1 = {1, 3};
627 DynList<int> c2 = {2, 4};
629
631}
632
634{
635 DynList<int> l1 = {1};
636 DynList<int> l2 = {1};
637 DynList<DynList<int>> l = {l1, l2};
638
639 auto combs = build_combs(l);
640 EXPECT_EQ(combs.size(), 1u);
641
642 DynList<int> expected = {1, 1};
644}
645
647{
648 DynList<int> l1 = {1, 2};
649 DynList<int> l2 = {2, 1};
650 DynList<int> l3 = {1, 2};
651 DynList<DynList<int>> l = {l1, l2, l3};
652
653 auto combs = build_combs(l);
654
655 // Possible combinations after sorting:
656 // (1,1,1), (1,1,2), (1,2,2), (2,2,2)
657 EXPECT_EQ(combs.size(), 4u);
658}
659
661{
662 DynList<int> l1 = {2, 3};
663 DynList<int> l2 = {10};
664 DynList<DynList<int>> l = {l1, l2};
665
666 // (2,10) and (3,10) => product of first elements = 2 * 3 = 6
667 auto product = fold_perm<int, int>(1, l, [](int acu, const DynList<int> & p)
668 {
669 return acu * p.get_first();
670 });
671
672 EXPECT_EQ(product, 6);
673}
674
676{
677 // 3^5 = 243 permutations
678 DynList<int> l1 = {1, 2, 3};
679 DynList<int> l2 = {10, 20, 30};
680 DynList<int> l3 = {100, 200, 300};
681 DynList<int> l4 = {1000, 2000, 3000};
682 DynList<int> l5 = {10000, 20000, 30000};
683 DynList<DynList<int>> l = {l1, l2, l3, l4, l5};
684
685 EXPECT_EQ(perm_count(l), 243u);
686
687 size_t count = 0;
688 traverse_perm(l, [&count](const DynList<int> & p)
689 {
690 EXPECT_EQ(p.size(), 5u);
691 ++count;
692 return true;
693 });
694
695 EXPECT_EQ(count, 243u);
696}
697
699{
700 DynList<int> l1 = {1, 2};
701 DynList<DynList<int>> l = {l1};
702
703 // Should not trigger nodiscard warning - use result
704 auto perms = build_perms(l);
705 EXPECT_EQ(perms.size(), 2u);
706}
707
709{
710 DynList<int> l1 = {1, 2};
711 DynList<DynList<int>> l = {l1};
712
713 // Should not trigger nodiscard warning - use result
714 auto combs = build_combs(l);
715 EXPECT_EQ(combs.size(), 2u);
716}
717
719{
720 DynList<int> r1 = {1, 2};
722
723 // Should not trigger nodiscard warning - use result
724 auto t = transpose(m);
725 EXPECT_EQ(t.size(), 2u);
726}
727
729{
730 DynList<int> l1 = {1, 2};
731 DynList<DynList<int>> l = {l1};
732
733 // Should not trigger nodiscard warning - use result
734 auto sum = fold_perm<int, int>(0, l, [](int a, const DynList<int> & p)
735 {
736 return a + p.get_first();
737 });
738 EXPECT_EQ(sum, 3);
739}
740
742{
743 DynList<int> l1 = {1, 2};
744 DynList<DynList<int>> l = {l1};
745
746 // Should not trigger nodiscard warning - use result
747 auto count = perm_count(l);
748 EXPECT_EQ(count, 2u);
749}
750
752{
753 DynList<int> l1 = {1, 2};
754 DynList<DynList<int>> l = {l1};
755
756 // Should not trigger nodiscard warning - use result
757 auto exists = exists_perm(l, [](const DynList<int> &) { return true; });
759}
760
762{
763 DynList<int> l1 = {1, 2};
764 DynList<DynList<int>> l = {l1};
765
766 // Should not trigger nodiscard warning - use result
767 auto all = all_perm(l, [](const DynList<int> &) { return true; });
769}
770
772{
773 DynList<int> l1 = {1, 2};
774 DynList<DynList<int>> l = {l1};
775
776 // Should not trigger nodiscard warning - use result
777 auto none = none_perm(l, [](const DynList<int> &) { return false; });
779}
780
782{
783 DynList<int> l1 = {1, 2};
784 DynList<DynList<int>> l = {l1};
785
786 // Should not trigger nodiscard warning - use result
787 auto filtered = filter_perm(l, [](const DynList<int> &) { return true; });
788 EXPECT_EQ(filtered.size(), 2u);
789}
790
792{
793 DynList<int> l1 = {1, 2};
794 DynList<DynList<int>> l = {l1};
795
796 // Should not trigger nodiscard warning - use result
797 auto mapped = map_perm<int>(l, [](const DynList<int> & p) { return p.get_first(); });
798 EXPECT_EQ(mapped.size(), 2u);
799}
Combinatorics utilities: permutations, combinations and matrix transposition.
Functional programming utilities for Aleph-w containers.
size_t size() const noexcept
Return the current dimension of array.
void reserve(const size_t l, const size_t r)
Allocate a range of entries.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
T & get_last() const
Return the last item of the list.
Definition htlist.H:1663
T & get_first() const
Return the first item of the list.
Definition htlist.H:1675
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
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
#define TEST(name)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool none_perm(const DynList< DynList< T > > &l, Pred &pred)
Check if no permutation satisfies a predicate.
Definition ah-comb.H:611
DynList< DynList< T > > build_combs(const DynList< DynList< T > > &l)
Build the set of unique combinations from a list of lists.
Definition ah-comb.H:448
bool all(Container &container, Operation &operation)
Return true if all elements satisfy a predicate.
size_t size(Node *root) noexcept
size_t perm_count(const DynList< DynList< T > > &l)
Count the total number of permutations from a list of lists.
Definition ah-comb.H:514
bool traverse_perm(const DynList< DynList< T > > &l, Op &op)
Traverse all the possible permutations that can be done of a list of lists and on each permutation pe...
Definition ah-comb.H:355
DynList< DynList< T > > filter_perm(const DynList< DynList< T > > &l, Pred &pred)
Filter permutations that satisfy a predicate.
Definition ah-comb.H:639
bool exists(Container &container, Operation &operation)
Return true if at least one element satisfies a predicate.
DynArray< T > sort(const DynArray< T > &a, Cmp &&cmp=Cmp())
Returns a sorted copy of a DynArray.
Definition ahSort.H:227
void for_each_perm(const DynList< DynList< T > > &l, Op &op)
Apply a procedure to every permutation produced by traverse_perm.
Definition ah-comb.H:395
DynList< DynList< T > > transpose(const DynList< DynList< T > > &l)
Transpose a matrix represented as a list of lists.
Definition ah-comb.H:140
T product(const Container &container, const T &init=T{1})
Compute product of all elements.
DynList< DynList< T > > build_perms(const DynList< DynList< T > > &l)
Materialize all permutations from a list of lists.
Definition ah-comb.H:427
void in_place_transpose(C< C< T > > &l)
In-place transpose of a rectangular matrix stored as a nested container.
Definition ah-comb.H:191
bool exists_perm(const DynList< DynList< T > > &l, Pred &pred)
Check if any permutation satisfies a predicate.
Definition ah-comb.H:541
bool all_perm(const DynList< DynList< T > > &l, Pred &pred)
Check if all permutations satisfy a predicate.
Definition ah-comb.H:581
bool none(const Container &container, Operation &operation)
Return true if no element satisfies operation.
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.
DynList< int > l