Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ahFunctional.H
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#ifndef AH_FUNCTIONAL_H
33#define AH_FUNCTIONAL_H
34
60# include <stdexcept>
61# include <utility>
62# include <tuple>
63# include <functional>
64# include <algorithm>
65# include <concepts>
66# include <optional>
67# include <type_traits>
68# include <ah-errors.H>
69# include <ah-ranges.H>
70# include <ah-concepts.H>
71# include <hash-dry.H>
72
73namespace Aleph
74{
78 template <typename T> struct Found_Item
79 {
80 virtual T & get_item() = 0;
81 virtual const T & get_item() const = 0;
82 virtual bool is_found() const noexcept = 0;
84 };
85
88 {
89 [[noreturn]] T & get_item() override
90 {
91 ah_invalid_argument() << "Access from None type";
92 __builtin_unreachable(); // Satisfies compiler after throw
93 }
94
95 [[noreturn]] const T & get_item() const override
96 {
97 ah_invalid_argument() << "Access from None type";
99 }
100
101 [[nodiscard]] bool is_found() const noexcept override { return false; }
102 };
103
105 template <typename T> struct Some : public Found_Item<T>
106 {
108
109 Some(T & i) : item(i) {}
110
111 T & get_item() override { return item; }
112 const T & get_item() const override { return item; }
113 [[nodiscard]] bool is_found() const noexcept override { return true; }
114 };
115
117 template <typename tgtT, typename srcT>
119 {
120 tgtT operator () (const srcT & item) const noexcept { return static_cast<tgtT>(item); }
121 };
122
124 template <typename TR, typename TD>
126 {
127 TR operator () (const TR & /*acc */, const TD & /* val */)
128 const noexcept { return TR(); }
129 };
130
131 // Forward declaration to avoid pulling tpl_dynSetHash.H (it depends on
132 // ahFunctional.H through other Aleph headers).
133 template <typename Key, class Cmp>
135 struct DynSetLhash;
136
137 namespace functional_detail
138 {
139 template <typename T, class Eq, typename = void>
140 struct Dyn_Set_Lhash_Available : public std::false_type {};
141
142 template <typename T, class Eq>
144 std::void_t<decltype(sizeof(DynSetLhash<T, Eq>))>>
145 : public std::true_type {};
146
147 template <typename T, class Eq>
148 inline constexpr bool dyn_set_lhash_available =
150
151 template <AlephSequentialIterableContainer Container, class Equal>
152 [[nodiscard]] inline bool pairwise_all_unique(const Container & container,
153 Equal & eq)
154 {
155 for (auto it1 = container.get_it(); it1.has_curr(); it1.next_ne())
156 {
157 auto it2 = it1;
158 it2.next_ne();
159 for (; it2.has_curr(); it2.next_ne())
160 if (eq(it1.get_curr(), it2.get_curr()))
161 return false;
162 }
163 return true;
164 }
165
166 template <AlephSequentialIterableContainer Container, class Hash, class Equal>
167 requires std::copy_constructible<typename Container::Item_Type>
168 and requires(const typename Container::Item_Type & v, Hash h, Equal e)
169 {
170 { h(v) } -> std::convertible_to<size_t>;
171 { e(v, v) } -> std::convertible_to<bool>;
172 }
173 [[nodiscard]] inline bool all_unique_hash_path(const Container & container,
174 Hash & hash, Equal & eq)
175 {
176 using T = typename Container::Item_Type;
177 using Eq_Type = std::remove_cvref_t<Equal>;
178
179 if constexpr (dyn_set_lhash_available<T, Eq_Type>)
180 {
181 using Set_Type = DynSetLhash<T, Eq_Type>;
182 using Hash_Fct = typename Set_Type::Hash_Fct;
183
184 Hash_Fct hash_fct([&hash] (const T & value)
185 {
186 return static_cast<size_t>(hash(value));
187 });
188
189 Set_Type seen(17, hash_fct, eq,
191
192 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
193 if (seen.contains_or_insert(it.get_curr()).second)
194 return false;
195 return true;
196 }
197
198 return pairwise_all_unique(container, eq);
199 }
200 } // namespace functional_detail
201
202
203 template <typename T> class DynList;
204
214 template <typename T = int, template <typename> class Container = DynList>
215 [[nodiscard]] inline Container<T> range(const T start, const T end, const T step = 1)
216 {
218 for (T i = start; i <= end; i += step)
219 ret_val.append(i);
220 return ret_val;
221 }
222
233 template <typename T = int, template <typename> class Container = DynList>
234 [[nodiscard]] inline Container<T> nrange(const T start, const T end, const size_t n)
235 {
236 ah_domain_error_if(n == 0) << "nrange: n must be greater than 0";
237
239 if (n == 1)
240 {
241 ret_val.append(start);
242 return ret_val;
243 }
244
245 const auto step = static_cast<double>(end - start) / (n - 1);
246 for (size_t i = 0; i < n; ++i)
247 ret_val.append(static_cast<T>(start + i * step));
248
249 return ret_val;
250 }
251
263 template <typename T = int, template <typename> class Container = DynList,
264 class Op>
265 [[nodiscard]] inline auto set_range(const T start, const T end, const T step, Op & op)
266 -> Container<std::decay_t<decltype(op(start))>>
267 {
268 Container<std::decay_t<decltype(op(start))>> ret_val;
269 for (T i = start; i <= end; i += step)
270 ret_val.append(op(i));
271 return ret_val;
272 }
273
275 template <typename T = int, template <typename> class Container = DynList,
276 class Op>
277 [[nodiscard]] inline auto set_range(const T start, const T end,
278 const T step = 1, Op && op = Op())
279 -> Container<std::decay_t<decltype(op(start))>>
280 {
281 return set_range<T, Container, Op>(start, end, step, op);
282 }
283
303 template <typename T = int, template <typename> class Container = DynList>
304 [[nodiscard]] inline Container<T> contiguous_range(T start, const size_t n)
305 {
307 for (size_t i = 0; i < n; ++i)
308 ret_val.append(start++);
309 return ret_val;
310 }
311
312
320 template <typename T = int, template <typename> class Container = DynList>
322 {
324 for (T i = 0; i < n; ++i)
325 ret_val.append(i);
326 return ret_val;
327 }
328
329
338 template <typename T = int> [[nodiscard]] inline
339 DynList<T> rep(const size_t n, const T & item)
340 {
342 for (size_t i = 0; i < n; ++i)
343 ret_val.append(item);
344 return ret_val;
345 }
346
348 template <typename T = int> [[nodiscard]] inline
349 DynList<T> rep(const size_t n, T && item = T())
350 {
351 return rep<T>(n, item);
352 }
353
354 // ─── In-place fill operations ─────────────────────────────────────────────
355
386 template <AlephSequentialContainer C>
387 void fill(C & container, const typename C::Item_Type & value)
388 {
389 container.mutable_for_each([&value](typename C::Item_Type & item)
390 {
391 item = value;
392 });
393 }
394
421 template <AlephSequentialContainer C>
422 requires requires(typename C::Item_Type v) { ++v; }
423 void iota(C & container, typename C::Item_Type start)
424 {
425 container.mutable_for_each([&start](typename C::Item_Type & item)
426 {
427 item = start++;
428 });
429 }
430
456 template <AlephSequentialContainer C>
457 requires requires(typename C::Item_Type v, typename C::Item_Type s) { v += s; }
458 void iota(C & container,
459 typename C::Item_Type start,
460 const typename C::Item_Type step)
461 {
462 container.mutable_for_each([&start, &step](typename C::Item_Type & item)
463 {
464 item = start;
465 start += step;
466 });
467 }
468
491 template <class Container> [[nodiscard]]
493 {
495 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
496 ret.append(&it.get_curr());
497 return ret;
498 }
499
504 template <class Container> [[nodiscard]]
507 {
508 using T = const typename Container::Item_Type *;
510 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
511 ret.append(&it.get_curr());
512 return ret;
513 }
514
533 template <class Op>
534 void each(const size_t start, const size_t end, Op & op)
535 {
536 for (size_t i = start; i <= end; ++i)
537 op();
538 }
539
541 template <class Op>
542 void each(size_t start, size_t end, Op && op)
543 {
544 each(start, end, op);
545 }
546
548 template <class Op>
549 void each(const size_t n, Op & op)
550 {
551 if (n == 0) return;
552 each(0, n - 1, op);
553 }
554
556 template <class Op>
557 void each(size_t n, Op && op)
558 {
559 each(n, op);
560 }
561
569 template <class Container> [[nodiscard]]
571 sublist(const Container & c, size_t pos, const size_t stride)
572 {
574 try
575 {
576 for (auto it = c.get_it(pos); it.has_curr();
577 each(0, stride - 1, [&it] () { it.next(); }))
578 ret.append(it.get_curr());
579 }
580 catch (const std::overflow_error &) { /* end of container reached */ }
581
582 return ret;
583 }
584
586 template <class Container> [[nodiscard]]
588 sublist(const Container & c, size_t stride)
589 {
590 return sublist(c, 0, stride);
591 }
592
615 template <class Container, class Operation> inline
617 {
618 container.traverse([&operation] (const auto & item)
619 {
620 operation(item);
621 return true;
622 });
623 return container;
624 }
625
627 template <class Container, class Operation> inline
628 const Container & for_each(const Container & container, Operation & operation)
629 {
630 container.traverse([&operation] (const auto & item)
631 {
632 operation(item);
633 return true;
634 });
635 return container;
636 }
637
639 template <class Container, class Operation> inline
642 {
643 return for_each<Container, Operation>(container, operation);
644 }
645
647 template <class Container, class Operation> inline const Container &
648 for_each(const Container & container, Operation && operation = Operation())
649 {
650 return for_each<Container, Operation>(container, operation);
651 }
652
680 template <class Container, class Operation> inline
681 void enum_for_each(const Container & container, Operation & operation)
682 {
683 size_t i = 0;
684 for (auto it = container.get_it(); it.has_curr(); it.next_ne(), ++i)
685 operation(it.get_curr(), i);
686 }
687
689 template <class Container, class Operation> inline
690 void enum_for_each(const Container & container, Operation && operation)
691 {
692 enum_for_each(container, operation);
693 }
694
718 template <class Container, class Operation> inline
719 bool all(Container & container, Operation & operation)
720 {
721 return container.traverse(operation);
722 }
723
725 template <class Container, class Operation> inline
726 bool all(const Container & container, Operation & operation)
727 {
728 return container.template traverse<Operation>(operation);
729 }
730
732 template <class Container, class Operation> inline
733 bool all(Container & container, Operation && operation = Operation())
734 {
735 return all<Container, Operation>(container, operation);
736 }
737
739 template <class Container, class Operation> inline
740 bool all(const Container & container, Operation && operation = Operation())
741 {
742 return all<Container, Operation>(container, operation);
743 }
744
745
769 template <class Container, class Operation> inline
770 bool exists(Container & container, Operation & operation)
771 {
772 return not
773 container.traverse([&operation] (const auto & item)
774 {
775 return not operation(item);
776 });
777 }
778
780 template <class Container, class Operation> inline
781 bool exists(const Container & container, Operation & operation)
782 {
783 return not
784 container.traverse([&operation] (const auto & item)
785 {
786 return not operation(item);
787 });
788 }
789
791 template <class Container, class Operation> inline
792 bool exists(Container & container, Operation && operation = Operation())
793 {
794 return exists<Container, Operation>(container, operation);
795 }
796
798 template <class Container, class Operation> inline
799 bool exists(const Container & container, Operation && operation = Operation())
800 {
801 return exists<Container, Operation>(container, operation);
802 }
803
805 template <typename T>
807 {
808 bool operator () (const T &) const noexcept { return true; }
809 };
810
811
821 template <class Container1,
822 template <typename> class Container2 = Aleph::DynList,
826 {
828 container.
829 for_each([&ret_val, &operation] (const auto & item)
830 {
831 if (operation(item))
832 ret_val.append(item);
833 });
834 return ret_val;
835 }
836
838 template <class Container1,
839 template <typename> class Container2 = Aleph::DynList,
842 filter(const Container1 & container, Operation & operation)
843 {
845 container.for_each
846 ([&ret_val, &operation] (const auto & item)
847 {
848 if (operation(item))
849 ret_val.append(item);
850 });
851 return ret_val;
852 }
853
855 template <class Container1,
856 template <typename> class Container2 = Aleph::DynList,
860 {
862 }
863
864
866 template <class Container1,
867 template <typename> class Container2 = Aleph::DynList,
870 filter(const Container1 & container, Operation && operation)
871 {
873 }
874
883 template <typename T, class C, class Op>
884 [[nodiscard]] DynList<T> maps(const C & c, Op op)
885 {
887 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
888 ret.append(op(it.get_curr()));
889 return ret;
890 }
891
898 template < typename T, class Container, class Operation> [[nodiscard]] inline
899 T foldl(const Container & container, const T & init, Operation operation)
900 {
901#if ALEPH_HAS_RANGES
902 if constexpr (std::ranges::range<Container>)
903 return detail::ranges_fold_left(container, init, std::move(operation));
904 else
905#endif
906 {
907 T ret_val = init;
908 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
909 ret_val = operation(ret_val, it.get_curr());
910 return ret_val;
911 }
912 }
913
914
939 template <class Container1, class Container2> [[nodiscard]] inline
940 DynList<std::pair<typename Container1::Item_Type,
941 typename Container2::Item_Type>>
942 zip(const Container1 & a, const Container2 & b)
943 {
944 typedef typename Container1::Item_Type T1;
945 typedef typename Container2::Item_Type T2;
947
948 typename Container1::Iterator it1(a);
949 typename Container2::Iterator it2(b);
950 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
951 ret_val.append(std::pair<T1, T2>(it1.get_curr(), it2.get_curr()));
952
953 return ret_val;
954 }
955
982 template <class Container1, class Container2> [[nodiscard]] inline
983 DynList<std::tuple<typename Container1::Item_Type,
984 typename Container2::Item_Type>>
985 tzip(const Container1 & a, const Container2 & b)
986 {
987 typedef typename Container1::Item_Type T1;
988 typedef typename Container2::Item_Type T2;
989 using Tuple = std::tuple<T1, T2>;
991
992 typename Container1::Iterator it1(a);
993 typename Container2::Iterator it2(b);
994 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
995 ret_val.append(Tuple(it1.get_curr(), it2.get_curr()));
996
997 return ret_val;
998 }
999
1026 template <class Container1, class Container2> [[nodiscard]] inline
1027 DynList<std::pair<typename Container1::Item_Type,
1028 typename Container2::Item_Type>>
1029 zipEq(const Container1 & a, const Container2 & b)
1030 {
1031
1032 typedef typename Container1::Item_Type T1;
1033 typedef typename Container2::Item_Type T2;
1035
1036 auto it1 = a.get_it();
1037 auto it2 = b.get_it();
1038 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
1039 ret_val.append(std::pair<T1, T2>(it1.get_curr(), it2.get_curr()));
1040
1041 if (it1.has_curr() or it2.has_curr())
1042 ah_length_error() << "Container sizes mismatch";
1043
1044 return ret_val;
1045 }
1046
1063 template <class Container1, class Container2> [[nodiscard]] inline
1064 DynList<std::tuple<typename Container1::Item_Type,
1065 typename Container2::Item_Type>>
1066 tzipEq(const Container1 & a, const Container2 & b)
1067 {
1068 typedef typename Container1::Item_Type T1;
1069 typedef typename Container2::Item_Type T2;
1070 using Tuple = std::tuple<T1, T2>;
1072
1073 typename Container1::Iterator it1(a);
1074 typename Container2::Iterator it2(b);
1075 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
1076 ret_val.append(Tuple(it1.get_curr(), it2.get_curr()));
1077
1078 if (it1.has_curr() or it2.has_curr())
1079 ah_length_error() << "Container sizes mismatch";
1080
1081 return ret_val;
1082 }
1083
1109 template <class Container> [[nodiscard]]
1110 auto inline enumerate(const Container & c)
1111 {
1112 using Item = typename Container::Item_Type;
1113 using Pair = std::pair<Item, size_t>;
1115 size_t i = 0;
1116 c.for_each([&i, &ret] (const Item & item) { ret.append(Pair(item, i++)); });
1117 return ret;
1118 }
1119
1120
1149 template <class C1, class C2,
1150 class Eq = std::equal_to<typename C1::Item_Type>> [[nodiscard]] inline
1151 bool eq(const C1 & c1, const C2 & c2, Eq e = Eq())
1152 {
1153 auto it1 = c1.get_it();
1154 auto it2 = c2.get_it();
1155 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
1156 if (not (e(it1.get_curr(), it2.get_curr())))
1157 return false;
1158
1159 return not (it1.has_curr() or it2.has_curr());
1160 }
1161
1163 template <typename T> [[nodiscard]] inline
1164 bool operator == (const DynList<T> & l1, const DynList<T> & l2)
1165 {
1166 return eq(l1, l2);
1167 }
1168
1170 template <class C1, class C2, class Eq> [[nodiscard]] inline
1171 bool containers_eq(const C1 & c1, const C2 & c2, Eq e)
1172 {
1173 return eq(c1, c2, e);
1174 }
1175
1202 template <class C1, class C2,
1203 class Eq = std::equal_to<typename C1::Item_Type>> [[nodiscard]] inline
1204 std::tuple<bool, size_t, typename C1::Item_Type, typename C2::Item_Type>
1205 are_eq(const C1 & c1, const C2 & c2, Eq e = Eq())
1206 {
1207 using T = typename C1::Item_Type;
1208 auto it1 = c1.get_it();
1209 auto it2 = c2.get_it();
1210 size_t n = 0;
1211 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne(), n++)
1212 {
1213 auto & i1 = it1.get_curr();
1214 auto & i2 = it2.get_curr();
1215 if (not (e(i1, i2)))
1216 return std::make_tuple(false, n, i1, i2);
1217 }
1218
1219 return std::make_tuple(not (it1.has_curr() or it2.has_curr()), n, T(), T());
1220 }
1221
1248 template <class C1, class C2,
1249 class Cmp = std::less<typename C1::Item_Type>> [[nodiscard]] inline
1250 bool lesser(const C1 & c1, const C2 & c2, Cmp cmp = Cmp())
1251 {
1252 auto it1 = c1.get_it();
1253 auto it2 = c2.get_it();
1254 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
1255 {
1256 auto & curr1 = it1.get_curr();
1257 auto & curr2 = it2.get_curr();
1258 if (cmp(curr1, curr2))
1259 return true;
1260 else if (cmp(curr2, curr1))
1261 return false;
1262 }
1263
1264 if (not it1.has_curr() and not it2.has_curr())
1265 return false;
1266
1267 return it2.has_curr();
1268 }
1269
1286 template <class C1, class C2,
1287 class Eq = std::equal_to<typename C1::Item_Type>> [[nodiscard]] inline
1288 bool diff(const C1 & c1, const C2 & c2, Eq e = Eq())
1289 {
1290 return not eq(c1, c2, e);
1291 }
1292
1317 template <class Container> [[nodiscard]] inline
1318 auto unzip(const Container & l)
1319 {
1320 using T1 = std::decay_t<decltype(l.get_first().first)>;
1321 using T2 = std::decay_t<decltype(l.get_first().second)>;
1324 for (auto it = l.get_it(); it.has_curr(); it.next_ne())
1325 {
1326 auto & curr = it.get_curr();
1327 l1.append(curr.first);
1328 l2.append(curr.second);
1329 }
1330
1331 return std::make_pair(std::move(l1), std::move(l2));
1332 }
1333
1360 template <template <typename> class Container, typename T1, typename T2>
1361 [[nodiscard]] inline std::tuple<Container<T1>, Container<T2>>
1362 tunzip(const Container<std::tuple<T1, T2>> & l)
1363 {
1366 for (auto it = l.get_it(); it.has_curr(); it.next_ne())
1367 {
1368 auto & curr = it.get_curr();
1369 l1.append(std::get<0>(curr));
1370 l2.append(std::get<1>(curr));
1371 }
1372
1373 return std::make_tuple(std::move(l1), std::move(l2));
1374 }
1375
1376
1383 template <class SrcContainer,
1384 template <typename> class TgtContainer = Aleph::DynList>
1385 [[nodiscard]] inline std::pair<TgtContainer<typename SrcContainer::Item_Type>,
1387 (const SrcContainer & c,
1388 std::function<bool(const typename SrcContainer::Item_Type &)> operation)
1389 {
1390 typedef typename SrcContainer::Item_Type Type;
1391 typedef std::pair<TgtContainer<Type>, TgtContainer<Type>> Pair;
1392
1393 Pair ret_val;
1394 for_each(c, [&ret_val, &operation] (const Type & item)
1395 {
1396 if (operation(item))
1397 ret_val.first.append(item);
1398 else
1399 ret_val.second.append(item);
1400 });
1401 return ret_val;
1402 }
1403
1405 template <class Container> [[nodiscard]] inline
1408 {
1409 using T = typename Container::Key_Type;
1410 using Pair = std::pair<size_t, T>;
1411 size_t i = 0;
1412
1413 return c.Container:: template maps<Pair>([&i] (const T & d)
1414 { return Pair(i++, d); });
1415 }
1416
1417
1419 template <class Container> [[nodiscard]] inline
1422 {
1423 using T = typename Container::Key_Type;
1424 using Tuple = std::tuple<size_t, typename Container::Key_Type>;
1425 size_t i = 0;
1426 return c.Container::template maps<std::tuple<size_t, T>>([&i] (const T & d)
1427 {
1428 return Tuple(i++, d);
1429 });
1430 }
1431
1432
1453 template <typename T, template <typename> class Container>
1455 {
1457 l.for_each([&ret_val] (const T & item)
1458 {
1459 ret_val.insert(item);
1460 });
1461 return ret_val;
1462 }
1463
1485 template <class Container> [[nodiscard]]
1486 auto gen_seq_list_tuples(const Container & c, size_t n)
1487 {
1488 using T = typename Container::Item_Type;
1489 typename Container::Iterator it(c);
1490 DynList<T> l;
1491 for (size_t i = 0; i < n; ++i, it.next())
1492 l.append(it.get_curr());
1493
1495 ret.append(l);
1496 for (; it.has_curr(); it.next_ne())
1497 {
1498 l.remove_first();
1499 l.append(it.get_curr());
1500 ret.append(l);
1501 }
1502
1503 return ret;
1504 }
1505
1531 template <typename T, template <typename> class Container, class Equal>
1532 [[nodiscard]] std::pair<DynList<DynList<T>>, size_t>
1534 {
1535 using P = std::pair<DynList<DynList<T>>, size_t>;
1536 if (c.is_empty())
1537 return P(DynList<DynList<T> >(), 0);
1538
1539 DynList<DynList<T>> ret; // this will be the result
1540
1541 DynList<T> * group = &ret.append(DynList<T>()); // creates a first group
1542
1543 auto it = c.get_it(); // put the firstitem into the group
1544 auto curr_item = it.get_curr();
1545 group->append(curr_item);
1546
1547 size_t count = 1; // count the number of groups
1548
1549 for (it.next(); it.has_curr(); it.next_ne())
1550 {
1551 auto & curr = it.get_curr();
1552 if (not eq(curr, curr_item)) // group change?
1553 {
1554 curr_item = curr;
1555 group = &ret.append(DynList<T>()); // create new group and insert it
1556 ++count; // increase the number of groups
1557 }
1558
1559 group->append(curr);
1560 }
1561
1562 return P(ret, count);
1563 }
1564
1566 template <typename T, template <typename> class Container,
1567 class Equal = std::equal_to<T>>
1568 [[nodiscard]] std::pair<DynList<DynList<T>>, size_t>
1569 sequential_groups(const Container<T> & c, Equal && eq = Equal())
1570 {
1571 return sequential_groups(c, eq);
1572 }
1573
1601 template <typename T, template <typename> class Container, class Equal>
1602 [[nodiscard]] std::pair<DynList<T>, size_t>
1604 {
1605 using P = std::pair<DynList<T>, size_t>;
1606 if (c.is_empty())
1607 return P(DynList<T>(), 0);
1608
1610
1611 auto it = c.get_it(); // put the first item
1612 auto curr_item = it.get_curr();
1613 ret.append(curr_item);
1614
1615 size_t count = 1; // count the number of groups
1616
1617 for (it.next(); it.has_curr(); it.next_ne())
1618 {
1619 auto & curr = it.get_curr();
1620 if (not eq(curr, curr_item)) // group change?
1621 {
1622 curr_item = curr;
1623 ret.append(curr_item);
1624 ++count; // increase the number of groups
1625 }
1626 }
1627
1628 return P(ret, count);
1629 }
1630
1632 template <typename T, template <typename> class Container,
1633 class Equal = std::equal_to<T>>
1634 [[nodiscard]] std::pair<DynList<T>, size_t>
1635 unique_sequential(const Container<T> & c, Equal && eq = Equal())
1636 {
1637 return unique_sequential(c, eq);
1638 }
1639
1664 template <class Itor1, class Itor2 = Itor1>
1666 {
1667 Itor1 it1;
1668 Itor2 it2;
1669
1670 public:
1671
1673 Pair_Iterator(Itor1 i1, Itor2 i2) : it1(i1), it2(i2) {}
1674
1679 template <class C1, class C2>
1680 Pair_Iterator(const C1 & c1, const C2 & c2)
1681 : Pair_Iterator(c1.get_it(), c2.get_it()) {}
1682
1686 [[nodiscard]] bool has_curr() const noexcept { return it1.has_curr() and it2.has_curr(); }
1687
1689 [[nodiscard]] bool has_curr1() const noexcept { return it1.has_curr(); }
1690
1692 [[nodiscard]] bool has_curr2() const noexcept { return it2.has_curr(); }
1693
1698 auto get_curr() const
1699 {
1700 return std::make_pair(it1.get_curr(), it2.get_curr());
1701 }
1702
1707 auto get_curr_ne() const noexcept
1708 {
1709 return std::make_pair(it1.get_curr_ne(), it2.get_curr_ne());
1710 }
1711
1715 void next()
1716 {
1717 it1.next();
1718 it2.next();
1719 }
1720
1724 void next_ne() noexcept
1725 {
1726 it1.next_ne();
1727 it2.next_ne();
1728 }
1729
1734 [[nodiscard]] bool was_traversed() const noexcept
1735 {
1736 return not (it1.has_curr() or it2.has_curr());
1737 }
1738 };
1739
1754 template <class C1, class C2>
1756 get_pair_it(const C1 & c1, const C2 & c2)
1757 {
1758 using I1 = typename C1::Iterator;
1759 using I2 = typename C2::Iterator;
1760 I1 i1(c1);
1761 I2 i2(c2);
1762 return Pair_Iterator<I1, I2>(i1, i2);
1763 }
1764
1766 template <class C1, class C2>
1768 get_pair_it(const C1 & c1, const C2 & c2, size_t pos)
1769 {
1770 using I1 = typename C1::Iterator;
1771 using I2 = typename C2::Iterator;
1772 I1 i1(c1);
1773 I2 i2(c2);
1774 for (size_t i = 0; i < pos; ++i)
1775 {
1776 i1.next();
1777 i2.next();
1778 }
1779 return Pair_Iterator<I1, I2>(i1, i2);
1780 }
1781
1783 template <class C> inline void insert_in_container(C &, size_t&) {}
1784
1785 template <class C, typename T, typename ... Args> inline
1786 void insert_in_container(C & c, size_t & n, const T & item, Args & ... args)
1787 {
1788 c.insert(item);
1789 ++n;
1790 insert_in_container(c, n, args...);
1791 }
1793
1797 template <class C, typename ... Args> inline
1798 size_t insert_in_container(C & c, Args ... args)
1799 {
1800 size_t n = 0;
1801 insert_in_container(c, n, args...);
1802 return n;
1803 }
1804
1806 template <class C> inline void append_in_container(C &, size_t&) {}
1807
1808 template <class C, typename T, typename ... Args> inline
1809 void append_in_container(C & c, size_t & n, const T & item, Args & ... args)
1810 {
1811 c.append(item);
1812 ++n;
1813 append_in_container(c, n, args...);
1814 }
1816
1820 template <class C, typename ... Args> inline
1821 size_t append_in_container(C & c, Args ... args)
1822 {
1823 size_t n = 0;
1824 append_in_container(c, n, args...);
1825 return n;
1826 }
1827
1829 template <class C, typename ... Args>
1831 {
1832 C c;
1834 return c;
1835 }
1836
1838 template <class SrcC, class TgtC>
1840 {
1841 TgtC ret;
1842 for (auto it = srcc.get_it(); it.has_curr(); it.next_ne())
1843 ret.append(it.get_curr());
1844
1845 return ret;
1846 }
1847
1849 template <typename T, typename ... Args>
1854
1856 template <class C> inline void remove_from_container(C &, size_t&) {}
1857
1858 template <class C, typename T, typename ... Args> inline
1859 void remove_from_container(C & c, size_t & n, const T & item, Args & ... args)
1860 {
1861 c.remove(item);
1862 ++n;
1863 remove_from_container(c, n, args...);
1864 }
1866
1870 template <class C, typename ... Args> inline
1872 {
1873 size_t n = 0;
1874 remove_from_container(c, n, args...);
1875 return n;
1876 }
1877
1878 // These function are defined in tpl_dynSetHash.H
1879
1880 // union
1881 template <typename T, template <typename> class Container> [[nodiscard]] inline
1882 DynList<T> join(const Container<T> & c1, const Container<T> & c2);
1883
1884 template <typename T, template <typename> class Container> [[nodiscard]] inline
1885 DynList<T> intercept(const Container<T> & c1, const Container<T> & c2);
1886
1887 template <typename T, template <typename> class Container> [[nodiscard]] inline
1888 DynList<T> unique(const Container<T> & c);
1889
1890 template <typename T, template <typename> class Container> [[nodiscard]] inline
1891 DynList<T> repeated(const Container<T> & c);
1892
1893 template <typename T, template <typename> class Container> [[nodiscard]] inline
1895
1916 template <typename T,
1917 template <typename> class C1,
1918 template <typename> class C2>
1920 {
1922 for (auto it_c = c.get_it(); it_c.has_curr(); it_c.next_ne())
1923 {
1924 const auto & curr_c = it_c.get_curr();
1925 for (auto it = curr_c.get_it(); it.has_curr(); it.next_ne())
1926 ret.append(it.get_curr());
1927 }
1928 return ret;
1929 }
1930
1942 template <typename T,
1943 template <typename> class C1,
1944 template <typename> class C2,
1945 template <typename> class C3>
1947 {
1949 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1950 ret.append(flatten(it.get_curr()));
1951 return ret;
1952 }
1953
1965 template <typename T,
1966 template <typename> class C1,
1967 template <typename> class C2,
1968 template <typename> class C3,
1969 template <typename> class C4>
1971 {
1973 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1974 ret.append(flatten(it.get_curr()));
1975 return ret;
1976 }
1977
1989 template <typename T,
1990 template <typename> class C1,
1991 template <typename> class C2,
1992 template <typename> class C3,
1993 template <typename> class C4,
1994 template <typename> class C5>
1996 {
1998 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1999 ret.append(flatten(it.get_curr()));
2000 return ret;
2001 }
2002
2022 template <typename T> [[nodiscard]] inline
2023 bool is_inside(const T & val, const DynList<T> & values)
2024 {
2025 for (const auto & v : values)
2026 if (val == v)
2027 return true;
2028 return false;
2029 }
2030
2048 template <typename T> [[nodiscard]] inline
2049 bool is_equal(const T & val)
2050 {
2051 (void) val;
2052 return false;
2053 }
2054
2061 template <typename T, typename U, typename ... Args> [[nodiscard]] inline
2062 bool is_equal(const T & val, const U & rhs, const Args & ... args)
2063 {
2064 return (val == rhs) or is_equal(val, args...);
2065 }
2066
2073 template <class Container, class Operation> [[nodiscard]] inline
2074 bool none(const Container & container, Operation & operation)
2075 {
2076 return not exists(container, operation);
2077 }
2078
2080 template <class Container, class Operation> [[nodiscard]] inline
2081 bool none(const Container & container, Operation && operation = Operation())
2082 {
2083 return none<Container, Operation>(container, operation);
2084 }
2085
2113 template <class Container, class Pred> [[nodiscard]] inline
2114 typename Container::Item_Type *
2115 find_ptr(Container & container, Pred & pred)
2116 {
2117 typename Container::Item_Type * result = nullptr;
2118 container.traverse([&result, &pred] (auto & item)
2119 {
2120 if (pred(item))
2121 {
2122 result = &item;
2123 return false;
2124 }
2125 return true;
2126 });
2127 return result;
2128 }
2129
2131 template <class Container, class Pred> [[nodiscard]] inline
2132 const typename Container::Item_Type *
2133 find_ptr(const Container & container, Pred & pred)
2134 {
2135 const typename Container::Item_Type * result = nullptr;
2136 container.traverse([&result, &pred] (const auto & item)
2137 {
2138 if (pred(item))
2139 {
2140 result = &item;
2141 return false;
2142 }
2143 return true;
2144 });
2145 return result;
2146 }
2147
2149 template <class Container, class Pred> [[nodiscard]] inline
2150 typename Container::Item_Type *
2151 find_ptr(Container & container, Pred && pred)
2152 {
2153 return find_ptr<Container, Pred>(container, pred);
2154 }
2155
2157 template <class Container, class Pred> [[nodiscard]] inline
2158 const typename Container::Item_Type *
2159 find_ptr(const Container & container, Pred && pred)
2160 {
2161 return find_ptr<Container, Pred>(container, pred);
2162 }
2163
2186 template <class Container, class Pred> [[nodiscard]] inline
2187 std::optional<typename Container::Item_Type>
2188 find_opt(const Container & container, Pred & pred)
2189 {
2190 std::optional<typename Container::Item_Type> result;
2191 container.traverse([&result, &pred] (const auto & item)
2192 {
2193 if (pred(item))
2194 {
2195 result = item;
2196 return false;
2197 }
2198 return true;
2199 });
2200 return result;
2201 }
2202
2204 template <class Container, class Pred> [[nodiscard]] inline
2205 std::optional<typename Container::Item_Type>
2206 find_opt(const Container & container, Pred && pred)
2207 {
2208 return find_opt<Container, Pred>(container, pred);
2209 }
2210
2220 template <typename T, class Container, class Operation> [[nodiscard]] inline
2221 T foldr(const Container & container, const T & init, Operation operation)
2222 {
2223 DynList<T> reversed;
2224 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2225 reversed.insert(it.get_curr());
2226
2227 T ret_val = init;
2228 for (auto it = reversed.get_it(); it.has_curr(); it.next_ne())
2229 ret_val = operation(it.get_curr(), ret_val);
2230 return ret_val;
2231 }
2232
2240 template <class Container, typename T = typename Container::Item_Type>
2241 [[nodiscard]] inline T sum(const Container & container, const T & init = T{})
2242 {
2243#if ALEPH_HAS_RANGES
2244 if constexpr (std::ranges::range<Container>)
2245 return detail::ranges_fold_left(container, init, std::plus<>{});
2246 else
2247#endif
2248 {
2249 T result = init;
2250 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2251 result = result + it.get_curr();
2252 return result;
2253 }
2254 }
2255
2263 template <class Container, typename T = typename Container::Item_Type>
2264 [[nodiscard]] inline T product(const Container & container, const T & init = T{1})
2265 {
2266#if ALEPH_HAS_RANGES
2267 if constexpr (std::ranges::range<Container>)
2268 return detail::ranges_fold_left(container, init, std::multiplies<>{});
2269 else
2270#endif
2271 {
2272 T result = init;
2273 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2274 result = result * it.get_curr();
2275 return result;
2276 }
2277 }
2278
2286 template <class C1, class C2> [[nodiscard]] inline
2288 concat(const C1 & c1, const C2 & c2)
2289 {
2291 for (auto it = c1.get_it(); it.has_curr(); it.next_ne())
2292 result.append(it.get_curr());
2293 for (auto it = c2.get_it(); it.has_curr(); it.next_ne())
2294 result.append(it.get_curr());
2295 return result;
2296 }
2297
2305 template <class Container, class Pred> [[nodiscard]] inline
2308 {
2310 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
2311 {
2312 const auto & item = it.get_curr();
2313 if (not pred(item))
2314 break;
2315 result.append(item);
2316 }
2317 return result;
2318 }
2319
2327 template <class Container, class Pred> [[nodiscard]] inline
2330 {
2332 bool dropping = true;
2333 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
2334 {
2335 const auto & item = it.get_curr();
2336 if (dropping and pred(item))
2337 continue;
2338 dropping = false;
2339 result.append(item);
2340 }
2341 return result;
2342 }
2343
2353 template <class Container, class Op> [[nodiscard]] inline
2354 auto flat_map(const Container & container, Op op)
2356 {
2357 using ResultItemType = typename std::decay_t<decltype(op(std::declval<typename Container::Item_Type>()))>::Item_Type;
2359 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2360 {
2361 auto sub = op(it.get_curr());
2362 for (auto sit = sub.get_it(); sit.has_curr(); sit.next_ne())
2363 result.append(sit.get_curr());
2364 }
2365 return result;
2366 }
2367
2377 template <typename T, class Container> [[nodiscard]] inline
2378 DynList<T> scanl_sum(const Container & container, const T & init)
2379 {
2380 DynList<T> result;
2381 T acc = init;
2382 result.append(acc);
2383 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2384 {
2385 acc = acc + it.get_curr();
2386 result.append(acc);
2387 }
2388 return result;
2389 }
2390
2401 template <typename T, class Container, class Op> [[nodiscard]] inline
2402 DynList<T> scanl(const Container & container, const T & init, Op op)
2403 {
2404 DynList<T> result;
2405 T acc = init;
2406 result.append(acc);
2407 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2408 {
2409 acc = op(acc, it.get_curr());
2410 result.append(acc);
2411 }
2412 return result;
2413 }
2414
2422 template <class Container,
2423 class Cmp = std::less<typename Container::Item_Type>>
2424 [[nodiscard]] inline const typename Container::Item_Type *
2425 min_ptr(const Container & container, Cmp cmp = Cmp())
2426 {
2427 auto it = container.get_it();
2428 if (not it.has_curr())
2429 return nullptr;
2430
2431 const typename Container::Item_Type * min_elem = &it.get_curr();
2432 for (it.next_ne(); it.has_curr(); it.next_ne())
2433 {
2434 const auto & curr = it.get_curr();
2435 if (cmp(curr, *min_elem))
2436 min_elem = &curr;
2437 }
2438 return min_elem;
2439 }
2440
2448 template <class Container,
2449 class Cmp = std::less<typename Container::Item_Type>>
2450 [[nodiscard]] inline const typename Container::Item_Type *
2451 max_ptr(const Container & container, Cmp cmp = Cmp())
2452 {
2453 auto it = container.get_it();
2454 if (not it.has_curr())
2455 return nullptr;
2456
2457 const typename Container::Item_Type * max_elem = &it.get_curr();
2458 for (it.next_ne(); it.has_curr(); it.next_ne())
2459 {
2460 const auto & curr = it.get_curr();
2461 if (cmp(*max_elem, curr))
2462 max_elem = &curr;
2463 }
2464 return max_elem;
2465 }
2466
2474 template <class Container,
2475 class Cmp = std::less<typename Container::Item_Type>>
2476 [[nodiscard]] inline std::pair<const typename Container::Item_Type *,
2477 const typename Container::Item_Type *>
2478 minmax_ptr(const Container & container, Cmp cmp = Cmp())
2479 {
2480 using T = typename Container::Item_Type;
2481 auto it = container.get_it();
2482 if (not it.has_curr())
2483 return {nullptr, nullptr};
2484
2485 const T * min_elem = &it.get_curr();
2486 const T * max_elem = min_elem;
2487 for (it.next_ne(); it.has_curr(); it.next_ne())
2488 {
2489 const auto & curr = it.get_curr();
2490 if (cmp(curr, *min_elem))
2491 min_elem = &curr;
2492 if (cmp(*max_elem, curr))
2493 max_elem = &curr;
2494 }
2495 return {min_elem, max_elem};
2496 }
2497
2505 template <class Container, class Pred> [[nodiscard]] inline
2506 size_t count_if(const Container & container, Pred pred)
2507 {
2508 size_t count = 0;
2509 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2510 if (pred(it.get_curr()))
2511 ++count;
2512 return count;
2513 }
2514
2531 template <AlephSequentialIterableContainer Container, class Equal>
2532 [[nodiscard]] inline bool all_unique(const Container & container, Equal & eq)
2533 {
2534 using T = typename Container::Item_Type;
2535 using Eq = std::remove_cvref_t<Equal>;
2536
2537 if constexpr (std::is_same_v<Eq, std::equal_to<T>>
2538 and std::copy_constructible<T>
2539 and requires(const T & v)
2540 {
2541 { std::hash<T>{}(v) } -> std::convertible_to<size_t>;
2542 })
2543 {
2544 auto hash = [] (const T & value) -> size_t
2545 {
2546 return std::hash<T>{}(value);
2547 };
2548 return functional_detail::all_unique_hash_path(container, hash, eq);
2549 }
2550
2551 return functional_detail::pairwise_all_unique(container, eq);
2552 }
2553
2568 template <AlephSequentialIterableContainer Container,
2569 class Equal = std::equal_to<typename Container::Item_Type>>
2570 [[nodiscard]] inline bool all_unique(const Container & container,
2571 Equal && eq = Equal())
2572 {
2573 return all_unique<Container, Equal>(container, eq);
2574 }
2575
2591 template <AlephSequentialIterableContainer Container, class Hash, class Equal>
2592 requires std::copy_constructible<typename Container::Item_Type>
2593 and requires(const typename Container::Item_Type & v, Hash h, Equal e)
2594 {
2595 { h(v) } -> std::convertible_to<size_t>;
2596 { e(v, v) } -> std::convertible_to<bool>;
2597 }
2598 [[nodiscard]] inline bool all_unique(const Container & container,
2599 Hash & hash, Equal & eq)
2600 {
2601 return functional_detail::all_unique_hash_path(container, hash, eq);
2602 }
2603
2605 template <AlephSequentialIterableContainer Container, class Hash, class Equal>
2606 requires std::copy_constructible<typename Container::Item_Type>
2607 and requires(const typename Container::Item_Type & v, Hash h, Equal e)
2608 {
2609 { h(v) } -> std::convertible_to<size_t>;
2610 { e(v, v) } -> std::convertible_to<bool>;
2611 }
2612 [[nodiscard]] inline bool all_unique(const Container & container,
2613 Hash && hash, Equal && eq)
2614 {
2615 using Hash_Type = std::remove_reference_t<Hash>;
2616 using Eq_Type = std::remove_reference_t<Equal>;
2617
2618 Hash_Type hash_obj = std::forward<Hash>(hash);
2619 Eq_Type eq_obj = std::forward<Equal>(eq);
2620 return all_unique(container, hash_obj, eq_obj);
2621 }
2622
2630 template <class Container> [[nodiscard]] inline
2631 bool contains(const Container & container,
2632 const typename Container::Item_Type & value)
2633 {
2634 for (auto it = container.get_it(); it.has_curr(); it.next_ne())
2635 if (it.get_curr() == value)
2636 return true;
2637 return false;
2638 }
2639
2648 template <class Container> [[nodiscard]] inline
2650 enumerate_tuple(const Container & container)
2651 {
2652 using T = typename Container::Item_Type;
2653 using Tuple = std::tuple<size_t, T>;
2654 DynList<Tuple> result;
2655 size_t i = 0;
2656 for (auto it = container.get_it(); it.has_curr(); it.next_ne(), ++i)
2657 result.append(Tuple(i, it.get_curr()));
2658 return result;
2659 }
2660
2661
2696 template <class Container1, class Container2> [[nodiscard]] inline
2697 DynList<std::pair<typename Container1::Item_Type,
2698 typename Container2::Item_Type>>
2699 zip_longest(const Container1 & a, const Container2 & b,
2700 const typename Container1::Item_Type & fill_a = typename Container1::Item_Type(),
2701 const typename Container2::Item_Type & fill_b = typename Container2::Item_Type())
2702 {
2703 using T1 = typename Container1::Item_Type;
2704 using T2 = typename Container2::Item_Type;
2706
2707 auto it1 = a.get_it();
2708 auto it2 = b.get_it();
2709
2710 // Process while both have elements
2711 while (it1.has_curr() and it2.has_curr())
2712 {
2713 ret_val.append(std::pair<T1, T2>(it1.get_curr(), it2.get_curr()));
2714 it1.next_ne();
2715 it2.next_ne();
2716 }
2717
2718 // Process remaining elements from the first container
2719 while (it1.has_curr())
2720 {
2721 ret_val.append(std::pair<T1, T2>(it1.get_curr(), fill_b));
2722 it1.next_ne();
2723 }
2724
2725 // Process remaining elements from the second container
2726 while (it2.has_curr())
2727 {
2728 ret_val.append(std::pair<T1, T2>(fill_a, it2.get_curr()));
2729 it2.next_ne();
2730 }
2731
2732 return ret_val;
2733 }
2734
2735
2767 template <class Container1, class Container2> [[nodiscard]] inline
2768 DynList<std::tuple<typename Container1::Item_Type,
2769 typename Container2::Item_Type>>
2770 tzip_longest(const Container1 & a, const Container2 & b,
2771 const typename Container1::Item_Type & fill_a = typename Container1::Item_Type(),
2772 const typename Container2::Item_Type & fill_b = typename Container2::Item_Type())
2773 {
2774 using T1 = typename Container1::Item_Type;
2775 using T2 = typename Container2::Item_Type;
2776 using Tuple = std::tuple<T1, T2>;
2778
2779 auto it1 = a.get_it();
2780 auto it2 = b.get_it();
2781
2782 while (it1.has_curr() and it2.has_curr())
2783 {
2784 ret_val.append(Tuple(it1.get_curr(), it2.get_curr()));
2785 it1.next_ne();
2786 it2.next_ne();
2787 }
2788
2789 while (it1.has_curr())
2790 {
2791 ret_val.append(Tuple(it1.get_curr(), fill_b));
2792 it1.next_ne();
2793 }
2794
2795 while (it2.has_curr())
2796 {
2797 ret_val.append(Tuple(fill_a, it2.get_curr()));
2798 it2.next_ne();
2799 }
2800
2801 return ret_val;
2802 }
2803
2804
2840 template <class Container1, class Container2> [[nodiscard]] inline
2842 std::optional<typename Container2::Item_Type>>>
2844 {
2845 using T1 = typename Container1::Item_Type;
2846 using T2 = typename Container2::Item_Type;
2847 using Opt1 = std::optional<T1>;
2848 using Opt2 = std::optional<T2>;
2850
2851 auto it1 = a.get_it();
2852 auto it2 = b.get_it();
2853
2854 while (it1.has_curr() and it2.has_curr())
2855 {
2856 ret_val.append(std::make_pair(Opt1(it1.get_curr()), Opt2(it2.get_curr())));
2857 it1.next_ne();
2858 it2.next_ne();
2859 }
2860
2861 while (it1.has_curr())
2862 {
2863 ret_val.append(std::make_pair(Opt1(it1.get_curr()), std::nullopt));
2864 it1.next_ne();
2865 }
2866
2867 while (it2.has_curr())
2868 {
2869 ret_val.append(std::make_pair(std::nullopt, Opt2(it2.get_curr())));
2870 it2.next_ne();
2871 }
2872
2873 return ret_val;
2874 }
2875
2876
2925 template <typename T, template <typename> class Container, class KeyFunc>
2926 [[nodiscard]]
2929 {
2930 using Key = std::invoke_result_t<KeyFunc, const T&>;
2931 using Group = DynList<T>;
2932 using Result = DynList<std::pair<Key, Group>>;
2933
2934 Result ret;
2935 if (c.is_empty())
2936 return ret;
2937
2938 auto it = c.get_it();
2939 Key curr_key = key_func(it.get_curr());
2940 Group * curr_group = &ret.append(std::make_pair(curr_key, Group())).second;
2941 curr_group->append(it.get_curr());
2942
2943 for (it.next_ne(); it.has_curr(); it.next_ne())
2944 {
2945 const auto & item = it.get_curr();
2946 Key new_key = key_func(item);
2947
2948 if (new_key != curr_key)
2949 {
2950 curr_key = new_key;
2951 curr_group = &ret.append(std::make_pair(curr_key, Group())).second;
2952 }
2953
2954 curr_group->append(item);
2955 }
2956
2957 return ret;
2958 }
2959
2960
2992 template <typename T, template <typename> class Container,
2993 class KeyFunc, class KeyEqual>
2994 [[nodiscard]]
2997 {
2998 using Key = std::invoke_result_t<KeyFunc, const T&>;
2999 using Group = DynList<T>;
3000 using Result = DynList<std::pair<Key, Group>>;
3001
3002 Result ret;
3003 if (c.is_empty())
3004 return ret;
3005
3006 auto it = c.get_it();
3007 Key curr_key = key_func(it.get_curr());
3008 Group * curr_group = &ret.append(std::make_pair(curr_key, Group())).second;
3009 curr_group->append(it.get_curr());
3010
3011 for (it.next_ne(); it.has_curr(); it.next_ne())
3012 {
3013 const auto & item = it.get_curr();
3014 Key new_key = key_func(item);
3015
3017 {
3018 curr_key = new_key;
3019 curr_group = &ret.append(std::make_pair(curr_key, Group())).second;
3020 }
3021
3022 curr_group->append(item);
3023 }
3024
3025 return ret;
3026 }
3027
3028
3066 template <typename T, template <typename> class Container,
3067 class KeyFunc, class Reducer>
3068 [[nodiscard]]
3071 std::invoke_result_t<Reducer, const DynList<T>&>>>
3072 {
3073 using Key = std::invoke_result_t<KeyFunc, const T&>;
3074 using ReducedType = std::invoke_result_t<Reducer, const DynList<T>&>;
3076
3077 auto groups = group_by(c, key_func);
3078 Result ret;
3079
3080 for (auto it = groups.get_it(); it.has_curr(); it.next_ne())
3081 {
3082 auto & [key, group] = it.get_curr();
3083 ret.append(std::make_pair(key, reducer(group)));
3084 }
3085
3086 return ret;
3087 }
3088
3089} // end namespace Aleph
3090
3091#endif // AH_FUNCTIONAL_H
C++20 concepts for constraining comparison functors.
Exception handling system with formatted messages for Aleph-w.
#define ah_length_error()
Throws std::length_error unconditionally.
Definition ah-errors.H:730
#define ah_invalid_argument()
Throws std::invalid_argument unconditionally.
Definition ah-errors.H:671
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
C++20 Ranges support and adaptors for Aleph-w containers.
long double h
Definition btreepic.C:154
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & insert(const T &item)
Definition htlist.H:1220
T & append(const T &item)
Definition htlist.H:1271
T & get_first() const
Return the first item of the list.
Definition htlist.H:1375
Iterator that zips two other iterators.
auto get_curr_ne() const noexcept
Get current pair (no bounds check).
bool has_curr1() const noexcept
Check if first iterator has current element.
Pair_Iterator(const C1 &c1, const C2 &c2)
Construct from two containers.
Pair_Iterator(Itor1 i1, Itor2 i2)
Construct from two iterators.
bool has_curr() const noexcept
Check if both iterators have current elements.
bool was_traversed() const noexcept
Check if both iterators were completely traversed.
bool has_curr2() const noexcept
Check if second iterator has current element.
void next()
Advance both iterators (bounds-checked).
auto get_curr() const
Get current pair (bounds-checked).
void next_ne() noexcept
Advance both iterators (no bounds check).
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:779
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:222
pair< size_t, string > P
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4118
Common hash table utilities and base classes.
Freq_Node * pred
Predecessor node in level-order traversal.
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 pairwise_all_unique(const Container &container, Equal &eq)
bool all_unique_hash_path(const Container &container, Hash &hash, Equal &eq)
constexpr bool dyn_set_lhash_available
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::pair< DynList< T >, size_t > unique_sequential(const Container< T > &c, Equal &eq)
Extract unique consecutive items.
and
Check uniqueness with explicit hash + equality functors.
std::tuple< bool, size_t, typename C1::Item_Type, typename C2::Item_Type > are_eq(const C1 &c1, const C2 &c2, Eq e=Eq())
Detailed equality check returning mismatch position and values.
DynList< T > flatten(const C2< C1< T > > &c)
Flatten a nested container of one level.
Container< T > nrange(const T start, const T end, const size_t n)
Generate exactly n values evenly spaced between [start, end].
auto group_by_reduce(const Container< T > &c, KeyFunc key_func, Reducer reducer) -> DynList< std::pair< std::invoke_result_t< KeyFunc, const T & >, std::invoke_result_t< Reducer, const DynList< T > & > > >
Group consecutive elements and apply a reducer to each group.
std::pair< const typename Container::Item_Type *, const typename Container::Item_Type * > minmax_ptr(const Container &container, Cmp cmp=Cmp())
Find both min and max elements in a single pass.
const Container::Item_Type * min_ptr(const Container &container, Cmp cmp=Cmp())
Find the minimum element in a container.
DynList< std::pair< std::optional< typename Container1::Item_Type >, std::optional< typename Container2::Item_Type > > > zip_longest_opt(const Container1 &a, const Container2 &b)
Zip two containers using optionals for missing values.
auto unzip(const Container &l)
Separate a list of pairs into two lists.
bool all(Container &container, Operation &operation)
Return true if all elements satisfy a predicate.
void reverse(Itor beg, Itor end)
Reverse elements in a range.
Definition ahAlgo.H:1094
bool eq(const C1 &c1, const C2 &c2, Eq e=Eq())
Check equality of two containers using a predicate.
DynList< T > scanl(const Container &container, const T &init, Op op)
Prefix scan with custom operation.
Itor unique(Itor __first, Itor __last, BinaryPredicate __binary_pred=BinaryPredicate())
Remove consecutive duplicates in place.
Definition ahAlgo.H:1058
T Item_Type
Type of elements from the first container.
Definition ah-zip.H:114
bool operator==(const DynList< T > &l1, const DynList< T > &l2)
Equality operator for DynList.
DynList< typename Container::Item_Type > drop_while(const Container &c, Pred pred)
Skip elements while predicate is true (drop_while).
auto set_range(const T start, const T end, const T step, Op &op) -> Container< std::decay_t< decltype(op(start))> >
Generate a range [start, end] and apply an operation to each value.
DynList< std::pair< typename Container1::Item_Type, typename Container2::Item_Type > > zipEq(const Container1 &a, const Container2 &b)
Zip two containers; throw if lengths differ.
DynList< T > intercept(const Container< T > &c1, const Container< T > &c2)
std::pair< TgtContainer< typename SrcContainer::Item_Type >, TgtContainer< typename SrcContainer::Item_Type > > partition(const SrcContainer &c, std::function< bool(const typename SrcContainer::Item_Type &)> operation)
Partition a container into two based on a predicate.
bool containers_eq(const C1 &c1, const C2 &c2, Eq e)
bool all_unique(const Container &container, Equal &eq)
Check if all elements in a container are unique using a comparator.
DynList< std::pair< T, size_t > > repeated_with_index(const Container< T > &c)
Container2< typename Container1::Item_Type > filter(Container1 &container, Operation &operation)
Filter elements that satisfy operation.
bool is_inside(const T &val, const DynList< T > &values)
Check if a value is present in a list.
DynList< T > repeated(const Container< T > &c)
const float hash_default_upper_alpha
Definition hash-dry.C:40
DynList< typename Container::Item_Type > take_while(const Container &c, Pred pred)
Return elements while predicate is true (take_while).
DynList< std::pair< typename Container1::Item_Type, typename Container2::Item_Type > > zip(const Container1 &a, const Container2 &b)
Zip two containers into a list of pairs.
Pair_Iterator< typename C1::Iterator, typename C2::Iterator > get_pair_it(const C1 &c1, const C2 &c2)
Create a Pair_Iterator for two containers.
void fill(Itor beg, const Itor &end, const T &value)
Fill a range with a value.
Definition ahAlgo.H:707
T foldr(const Container &container, const T &init, Operation operation)
Right fold (reduce).
auto enumerate(const Container &c)
Return pairs of (element, index).
size_t append_in_container(C &c, Args ... args)
Append multiple items into a container.
bool is_equal(const T &val)
Variadic check for equality against multiple values.
T foldl(const Container &container, const T &init, Operation operation)
Classic left fold (reduce).
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
bool exists(Container &container, Operation &operation)
Return true if at least one element satisfies a predicate.
auto gen_seq_list_tuples(const Container &c, size_t n)
Generate all sequential tuples (sliding windows) of size n.
bool contains(const std::string_view &str, const std::string_view &substr)
Check if substr appears inside str.
DynList< T > build_dynlist(Args ... args)
Build a DynList with the given items.
void iota(C &container, typename C::Item_Type start)
Fill all elements of a container with unit-step sequential values.
std::optional< typename Container::Item_Type > find_opt(const Container &container, Pred &pred)
Find the first element satisfying pred (safe version).
void each(const size_t start, const size_t end, Op &op)
Execute an operation repeatedly over a range of indices.
DynList< T > rep(const size_t n, const T &item)
Create a sequence of repeated items.
auto flat_map(const Container &container, Op op) -> DynList< typename std::decay_t< decltype(op(std::declval< typename Container::Item_Type >()))>::Item_Type >
Apply operation and flatten results (flatMap/concatMap).
DynList< std::tuple< typename Container1::Item_Type, typename Container2::Item_Type > > tzip(const Container1 &a, const Container2 &b)
Zip two containers into a list of tuples.
T product(const Container &container, const T &init=T{1})
Compute product of all elements.
bool diff(const C1 &c1, const C2 &c2, Eq e=Eq())
Check if two containers differ.
DynList< typename Container::Item_Type * > pointers_list(Container &c)
Create a list of pointers to items in a container.
DynList< std::pair< size_t, typename Container::Key_Type > > indexes(const Container &c)
Return pairs of (index, key).
bool lesser(const C1 &c1, const C2 &c2, Cmp cmp=Cmp())
Lexicographical comparison between two containers.
TgtC assign_container(const SrcC &srcc)
Convert one container type to another.
DynList< std::tuple< size_t, typename Container::Item_Type > > enumerate_tuple(const Container &container)
Zip containers with an index (enumerate with zip).
const Container::Item_Type * max_ptr(const Container &container, Cmp cmp=Cmp())
Find the maximum element in a container.
DynList< std::tuple< typename Container1::Item_Type, typename Container2::Item_Type > > tzipEq(const Container1 &a, const Container2 &b)
Zip two containers into tuples; throw if lengths differ.
Operation for_each(Itor beg, const Itor &end, Operation op)
Apply an operation to each element in a range.
Definition ahAlgo.H:76
std::pair< DynList< DynList< T > >, size_t > sequential_groups(const Container< T > &c, Equal &eq)
Group consecutive equal elements together.
size_t insert_in_container(C &c, Args ... args)
Insert multiple items into a container.
DynList< std::tuple< size_t, typename Container::Key_Type > > tindexes(const Container &c)
Return tuples of (index, key).
Itor::difference_type count_if(Itor beg, const Itor &end, Operation op)
Count elements satisfying a predicate.
Definition ahAlgo.H:100
DynList< T > scanl_sum(const Container &container, const T &init)
Compute running sums (scanl with addition).
DynList< std::tuple< typename Container1::Item_Type, typename Container2::Item_Type > > tzip_longest(const Container1 &a, const Container2 &b, const typename Container1::Item_Type &fill_a=typename Container1::Item_Type(), const typename Container2::Item_Type &fill_b=typename Container2::Item_Type())
Zip two containers into tuples, padding the shorter one.
DynList< std::pair< std::invoke_result_t< KeyFunc, const T & >, DynList< T > > > group_by_eq(const Container< T > &c, KeyFunc key_func, KeyEqual key_equal)
Group consecutive elements by a key function with custom equality.
std::string concat(const Args &... args)
Concatenate multiple arguments into a single std::string.
DynList< std::pair< typename Container1::Item_Type, typename Container2::Item_Type > > zip_longest(const Container1 &a, const Container2 &b, const typename Container1::Item_Type &fill_a=typename Container1::Item_Type(), const typename Container2::Item_Type &fill_b=typename Container2::Item_Type())
Zip two containers, padding the shorter one with a fill value.
std::ostream & join(const C &c, const std::string &sep, std::ostream &out)
Join elements of an Aleph-style container into a stream.
DynList< typename Container::Item_Type > sublist(const Container &c, size_t pos, const size_t stride)
Extract a sublist using a stride.
Container::Item_Type * find_ptr(Container &container, Pred &pred)
Find the first element satisfying pred.
Container< T > range(const T start, const T end, const T step=1)
Generate a range of values [start, end] with a given step.
void enum_for_each(const Container &container, Operation &operation)
Apply an operation to each element and its index.
C build_container(Args ... args)
Build a container with the given items.
DynList< std::pair< std::invoke_result_t< KeyFunc, const T & >, DynList< T > > > group_by(const Container< T > &c, KeyFunc key_func)
Group consecutive elements by a key function.
Container< T > contiguous_range(T start, const size_t n)
Generate n contiguous values starting from start.
const float hash_default_lower_alpha
Definition hash-dry.C:38
bool none(const Container &container, Operation &operation)
Return true if no element satisfies operation.
static std::atomic< bool > init
Definition hash-fct.C:53
size_t remove_from_container(C &c, Args ... args)
Remove multiple items from a container.
std::tuple< Container< T1 >, Container< T2 > > tunzip(const Container< std::tuple< T1, T2 > > &l)
Separate a list of tuples into two containers.
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.
STL namespace.
Default filter operation (always true).
Default folding operation (returns default-constructed accumulator).
Default mapping operation (identity).
Abstract base class for optional-like results.
virtual bool is_found() const noexcept=0
virtual T & get_item()=0
virtual const T & get_item() const =0
Represents a missing value.
const T & get_item() const override
T & get_item() override
bool is_found() const noexcept override
Represents a found value (stored by reference).
const T & get_item() const override
bool is_found() const noexcept override
T & get_item() override
DynList< int > l1
DynList< int > l2
DynList< int > l