Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah-stl-functional.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
32# ifndef AH_STL_FUNCTIONAL_H
33# define AH_STL_FUNCTIONAL_H
34
74# include <type_traits>
75# include <vector>
76# include <optional>
77# include <functional>
78# include <algorithm>
79# include <numeric>
80# include <utility>
81# include <tuple>
82# include <iterator>
83# include <stdexcept>
84# include <unordered_set>
85# include <unordered_map>
86# include <ah-ranges.H>
87
88namespace Aleph
89{
90 // ============================================================================
91 // Type Traits and Concepts
92 // ============================================================================
93
94 namespace stl_detail
95 {
97 inline constexpr size_t ADAPTIVE_THRESHOLD = 64;
98
100 template <typename T, typename = void>
101 struct has_size : std::false_type {};
102
103 template <typename T>
104 struct has_size<T, std::void_t<decltype(std::declval<T>().size())>>
105 : std::true_type {};
106
107 template <typename T>
108 inline constexpr bool has_size_v = has_size<T>::value;
109
111 template <typename T, typename = void>
112 struct is_std_hashable : std::false_type {};
113
114 template <typename T>
116 std::void_t<decltype(std::hash<T>{}(std::declval<T>()))>>
117 : std::true_type {};
118
119 template <typename T>
121
123 template <typename T, typename = void>
124 struct has_reverse_iterators : std::false_type {};
125
126 template <typename T>
128 std::void_t<decltype(std::declval<T>().rbegin()),
129 decltype(std::declval<T>().rend())>>
130 : std::true_type {};
131
132 template <typename T>
134
136 template <typename Container>
137 [[nodiscard]] size_t safe_size(const Container & c)
138 {
139 if constexpr (has_size_v<Container>)
140 return c.size();
141 else
142 return 0; // Cannot determine size efficiently
143 }
144
146 template <typename Result, typename Container>
147 void try_reserve(Result & result, const Container & c)
148 {
149 if constexpr (has_size_v<Container>)
150 result.reserve(c.size());
151 }
152
154 template <typename Result>
155 void try_reserve_n(Result & result, size_t n)
156 {
157 if constexpr (requires { result.reserve(n); })
158 result.reserve(n);
159 }
160 } // namespace stl_detail
161
162 // ============================================================================
163 // Range Generation
164 // ============================================================================
165
184 template <typename T = int>
185 [[nodiscard]] std::vector<T> stl_range(T start, T end, T step = 1)
186 {
187 std::vector<T> result;
188 if (step > 0 and start <= end)
189 {
190 result.reserve(static_cast<size_t>((end - start) / step + 1));
191 for (T i = start; i <= end; i += step)
192 result.push_back(i);
193 }
194 else if (step < 0 and start >= end)
195 {
196 result.reserve(static_cast<size_t>((start - end) / (-step) + 1));
197 for (T i = start; i >= end; i += step)
198 result.push_back(i);
199 }
200 return result;
201 }
202
212 template <typename T = int>
213 [[nodiscard]] std::vector<T> stl_range(T n)
214 {
215 std::vector<T> result;
216 result.reserve(static_cast<size_t>(n));
217 for (T i = 0; i < n; ++i)
218 result.push_back(i);
219 return result;
220 }
221
233 template <typename T = double>
234 [[nodiscard]] std::vector<T> stl_linspace(T start, T end, size_t n)
235 {
236 std::vector<T> result;
237 if (n == 0) return result;
238 result.reserve(n);
239 if (n == 1)
240 {
241 result.push_back(start);
242 return result;
243 }
244
245 result.reserve(n);
246 const auto step = (end - start) / static_cast<T>(n - 1);
247 for (size_t i = 0; i < n; ++i)
248 result.push_back(start + static_cast<T>(i) * step);
249 return result;
250 }
251
262 template <typename T>
263 [[nodiscard]] std::vector<T> stl_rep(size_t n, const T & value)
264 {
265 return std::vector<T>(n, value);
266 }
267
278 template <typename Gen>
279 [[nodiscard]] auto stl_generate(size_t n, Gen && gen)
280 {
281 using T = std::decay_t<decltype(gen(size_t{}))>;
282 std::vector<T> result;
283 result.reserve(n);
284 auto & gen_ref = gen; // Preserve as lvalue to avoid moving in loop
285 for (size_t i = 0; i < n; ++i)
286 result.push_back(gen_ref(i));
287 return result;
288 }
289
290 // ============================================================================
291 // Core Functional Operations
292 // ============================================================================
293
303 template <typename Op, typename Container>
304 void stl_for_each(Op && op, const Container & c)
305 {
306 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
307 for (const auto & item: c)
308 op_ref(item);
309 }
310
320 template <typename Op, typename Container>
321 void stl_for_each_indexed(Op && op, const Container & c)
322 {
323 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
324 size_t i = 0;
325 for (const auto & item: c)
326 op_ref(i++, item);
327 }
328
344 template <typename Op, typename Container>
345 [[nodiscard]] auto stl_map(Op && op, const Container & c)
346 {
347 using T = typename Container::value_type;
348 using R = std::decay_t<decltype(op(std::declval<T>()))>;
349
350 std::vector<R> result;
351 stl_detail::try_reserve(result, c);
352 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
353 for (const auto & item: c)
354 result.push_back(op_ref(item));
355 return result;
356 }
357
368 template <typename Op, typename Container>
369 [[nodiscard]] auto stl_mapi(Op && op, const Container & c)
370 {
371 using T = typename Container::value_type;
372 using R = std::decay_t<decltype(op(size_t{}, std::declval<T>()))>;
373
374 std::vector<R> result;
375 stl_detail::try_reserve(result, c);
376 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
377 size_t i = 0;
378 for (const auto & item: c)
379 result.push_back(op_ref(i++, item));
380 return result;
381 }
382
393 template <typename Pred, typename Container>
394 [[nodiscard]] auto stl_filter(Pred && pred, const Container & c)
395 {
396 using T = typename Container::value_type;
397
398 std::vector<T> result;
399 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
400 for (const auto & item: c)
401 if (pred_ref(item))
402 result.push_back(item);
403 return result;
404 }
405
416 template <typename Pred, typename Container>
417 [[nodiscard]] auto stl_filteri(Pred && pred, const Container & c)
418 {
419 using T = typename Container::value_type;
420
421 std::vector<T> result;
422 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
423 size_t i = 0;
424 for (const auto & item: c)
425 {
426 if (pred_ref(i, item))
427 result.push_back(item);
428 ++i;
429 }
430 return result;
431 }
432
451 template <typename T, typename Op, typename Container>
452 [[nodiscard]] T stl_foldl(T init, Op && op, const Container & c)
453 {
454#if ALEPH_HAS_RANGES
455 if constexpr (std::ranges::range<Container>)
456 return detail::ranges_fold_left(c, std::move(init), std::forward<Op>(op));
457 else
458#endif
459 {
460 T acc = std::move(init);
461 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
462 for (const auto & item: c)
463 acc = op_ref(std::move(acc), item);
464 return acc;
465 }
466 }
467
489 template <typename T, typename Op, typename Container>
490 [[nodiscard]] T stl_foldr(T init, Op && op, const Container & c)
491 {
492 static_assert(stl_detail::has_reverse_iterators_v<Container>,
493 "stl_foldr requires a container with reverse iterators (rbegin/rend)");
494 T acc = std::move(init);
495 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
496 for (auto it = c.rbegin(); it != c.rend(); ++it)
497 acc = op_ref(*it, std::move(acc));
498 return acc;
499 }
500
518 template <typename T, typename Op, typename Container>
519 [[nodiscard]] std::vector<T> stl_scan_left(T init, Op && op, const Container & c)
520 {
521 std::vector<T> result;
522 if constexpr (stl_detail::has_size_v<Container>)
523 result.reserve(c.size() + 1);
524 result.push_back(init);
525
526 T acc = std::move(init);
527 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
528 for (const auto & item: c)
529 {
530 acc = op_ref(std::move(acc), item);
531 result.push_back(acc);
532 }
533 return result;
534 }
535
547 template <typename T, typename Op, typename Container>
548 [[nodiscard]] std::vector<T> stl_scan_right(T init, Op && op, const Container & c)
549 {
550 static_assert(stl_detail::has_reverse_iterators_v<Container>,
551 "stl_scan_right requires a container with reverse iterators (rbegin/rend)");
552 std::vector<T> result;
553 if constexpr (stl_detail::has_size_v<Container>)
554 result.reserve(c.size() + 1);
555
556 T acc = std::move(init);
557 result.push_back(acc);
558
559 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
560 for (auto it = c.rbegin(); it != c.rend(); ++it)
561 {
562 acc = op_ref(*it, std::move(acc));
563 result.push_back(acc);
564 }
565
566 std::reverse(result.begin(), result.end());
567 return result;
568 }
569
571 template <typename T, typename Op, typename Container>
572 [[nodiscard]] T stl_reduce(T init, Op && op, const Container & c)
573 {
574 return stl_foldl(std::move(init), std::forward<Op>(op), c);
575 }
576
577 // ============================================================================
578 // Predicates
579 // ============================================================================
580
591 template <typename Pred, typename Container>
592 [[nodiscard]] bool stl_all(Pred && pred, const Container & c)
593 {
594#if ALEPH_HAS_RANGES
595 if constexpr (std::ranges::range<Container>)
596 return detail::ranges_all_of(c, std::forward<Pred>(pred));
597 else
598#endif
599 {
600 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
601 for (const auto & item: c)
602 if (not pred_ref(item))
603 return false;
604 return true;
605 }
606 }
607
618 template <typename Pred, typename Container>
619 [[nodiscard]] bool stl_exists(Pred && pred, const Container & c)
620 {
621#if ALEPH_HAS_RANGES
622 if constexpr (std::ranges::range<Container>)
623 return detail::ranges_any_of(c, std::forward<Pred>(pred));
624 else
625#endif
626 {
627 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
628 for (const auto & item: c)
629 if (pred_ref(item))
630 return true;
631 return false;
632 }
633 }
634
636 template <typename Pred, typename Container>
637 [[nodiscard]] bool stl_any(Pred && pred, const Container & c)
638 {
639 return stl_exists(std::forward<Pred>(pred), c);
640 }
641
648 template <typename Pred, typename Container>
649 [[nodiscard]] bool stl_none(Pred && pred, const Container & c)
650 {
651 return not stl_exists(std::forward<Pred>(pred), c);
652 }
653
654 // ============================================================================
655 // Finding Elements
656 // ============================================================================
657
668 template <typename Pred, typename Container>
669 [[nodiscard]] auto stl_find(Pred && pred, const Container & c)
670 {
671 using T = typename Container::value_type;
672
673 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
674 for (const auto & item: c)
675 if (pred_ref(item))
676 return std::optional<T>(item);
677 return std::optional<T>{};
678 }
679
690 template <typename Pred, typename Container>
691 [[nodiscard]] auto stl_find_last(Pred && pred, const Container & c)
692 {
693 using T = typename Container::value_type;
694
695 std::optional<T> result;
696 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
697 for (const auto & item: c)
698 if (pred_ref(item))
699 result = item;
700 return result;
701 }
702
713 template <typename Pred, typename Container>
714 [[nodiscard]] std::optional<size_t> stl_find_index(Pred && pred, const Container & c)
715 {
716 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
717 size_t i = 0;
718 for (const auto & item: c)
719 {
720 if (pred_ref(item))
721 return i;
722 ++i;
723 }
724 return std::nullopt;
725 }
726
737 template <typename Op, typename Container>
738 [[nodiscard]] auto stl_find_mapi(Op && op, const Container & c)
739 {
740 using T = typename Container::value_type;
741 using OptType = std::decay_t<decltype(op(size_t{}, std::declval<T>()))>;
742 static_assert(std::is_default_constructible_v<OptType>,
743 "stl_find_mapi requires the return type to be default-constructible (like std::optional)");
744
745 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
746 size_t i = 0;
747 for (const auto & item: c)
748 if (auto result = op_ref(i++, item))
749 return result;
750 return OptType{};
751 }
752
763 template <typename T, typename Container>
764 [[nodiscard]] bool stl_mem(const T & target, const Container & c)
765 {
766 for (const auto & item: c)
767 if (item == target)
768 return true;
769 return false;
770 }
771
772 // ============================================================================
773 // Counting
774 // ============================================================================
775
786 template <typename Pred, typename Container>
787 [[nodiscard]] size_t stl_count(Pred && pred, const Container & c)
788 {
789 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
790 size_t count = 0;
791 for (const auto & item: c)
792 if (pred_ref(item))
793 ++count;
794 return count;
795 }
796
807 template <typename T, typename Container>
808 [[nodiscard]] size_t stl_count_value(const T & target, const Container & c)
809 {
810 size_t count = 0;
811 for (const auto & item: c)
812 if (item == target)
813 ++count;
814 return count;
815 }
816
817 // ============================================================================
818 // Taking and Dropping
819 // ============================================================================
820
831 template <typename Container>
832 [[nodiscard]] auto stl_take(size_t n, const Container & c)
833 {
834 using T = typename Container::value_type;
835
836 std::vector<T> result;
837 result.reserve(n);
838 size_t count = 0;
839 for (const auto & item: c)
840 {
841 if (count >= n) break;
842 result.push_back(item);
843 ++count;
844 }
845 return result;
846 }
847
858 template <typename Container>
859 [[nodiscard]] auto stl_drop(size_t n, const Container & c)
860 {
861 using T = typename Container::value_type;
862
863 std::vector<T> result;
864 if constexpr (stl_detail::has_size_v<Container>)
865 {
866 if (const size_t size = c.size(); size > n)
867 result.reserve(size - n);
868 }
869 size_t count = 0;
870 for (const auto & item: c)
871 {
872 if (count >= n)
873 result.push_back(item);
874 ++count;
875 }
876 return result;
877 }
878
889 template <typename Container>
890 [[nodiscard]] auto stl_take_last(size_t n, const Container & c)
891 {
892 using T = typename Container::value_type;
893
894 std::vector<T> result;
895 size_t size = stl_detail::safe_size(c);
896
897 // If container doesn't have size(), we need to iterate twice or use a different approach
898 if constexpr (!stl_detail::has_size_v<Container>)
899 {
900 // For containers without size(), collect all then take last n
901 std::vector<T> all(c.begin(), c.end());
902 size = all.size();
903 const size_t skip = size > n ? (size - n) : 0;
904 result.reserve(size > n ? n : size);
905 for (size_t i = skip; i < size; ++i)
906 result.push_back(std::move(all[i]));
907 return result;
908 }
909 else
910 {
911 size_t actual_n = size > n ? n : size;
912 result.reserve(actual_n);
913 const size_t skip = size > n ? (size - n) : 0;
914
915 size_t count = 0;
916 for (const auto & item: c)
917 {
918 if (count >= skip)
919 result.push_back(item);
920 ++count;
921 }
922 return result;
923 }
924 }
925
936 template <typename Pred, typename Container>
938 {
939 using T = typename Container::value_type;
940
941 std::vector<T> result;
942 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
943 for (const auto & item: c)
944 {
945 if (not pred_ref(item))
946 break;
947 result.push_back(item);
948 }
949 return result;
950 }
951
962 template <typename Pred, typename Container>
964 {
965 using T = typename Container::value_type;
966
967 std::vector<T> result;
968 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
969 bool dropping = true;
970 for (const auto & item: c)
971 {
972 if (dropping and pred_ref(item))
973 continue;
974 dropping = false;
975 result.push_back(item);
976 }
977 return result;
978 }
979
980 // ============================================================================
981 // Accessing Elements
982 // ============================================================================
983
993 template <typename Container>
994 [[nodiscard]] auto stl_first(const Container & c)
995 {
996 using T = typename Container::value_type;
997
998 auto it = c.begin();
999 if (it == c.end())
1000 return std::optional<T>{};
1001 return std::optional<T>(*it);
1002 }
1003
1013 template <typename Container>
1014 [[nodiscard]] auto stl_last(const Container & c)
1015 {
1016 using T = typename Container::value_type;
1017
1018 auto it = c.begin();
1019 if (it == c.end())
1020 return std::optional<T>{};
1021
1022 // Use reverse iterators if available (more efficient)
1023 if constexpr (stl_detail::has_reverse_iterators_v<Container>)
1024 {
1025 return std::optional<T>(*c.rbegin());
1026 }
1027 else
1028 {
1029 // Fallback: iterate through entire container (for forward_list, etc.)
1030 T last = *it;
1031 ++it;
1032 for (; it != c.end(); ++it)
1033 last = *it;
1034 return std::optional<T>(last);
1035 }
1036 }
1037
1048 template <typename Container>
1049 [[nodiscard]] auto stl_nth(const size_t n, const Container & c)
1050 {
1051 using T = typename Container::value_type;
1052
1053 size_t i = 0;
1054 for (const auto & item: c)
1055 {
1056 if (i == n)
1057 return std::optional<T>(item);
1058 ++i;
1059 }
1060 return std::optional<T>{};
1061 }
1062
1063 // ============================================================================
1064 // Min/Max
1065 // ============================================================================
1066
1076 template <typename Container>
1077 [[nodiscard]] auto stl_min(const Container & c)
1078 {
1079 using T = typename Container::value_type;
1080
1081 auto it = c.begin();
1082 if (it == c.end())
1083 return std::optional<T>{};
1084
1085 T min_val = *it;
1086 ++it;
1087 for (; it != c.end(); ++it)
1088 if (*it < min_val)
1089 min_val = *it;
1090 return std::optional<T>(min_val);
1091 }
1092
1102 template <typename Container>
1103 [[nodiscard]] auto stl_max(const Container & c)
1104 {
1105 using T = typename Container::value_type;
1106
1107 auto it = c.begin();
1108 if (it == c.end())
1109 return std::optional<T>{};
1110
1111 T max_val = *it;
1112 ++it;
1113 for (; it != c.end(); ++it)
1114 if (*it > max_val)
1115 max_val = *it;
1116 return std::optional<T>(max_val);
1117 }
1118
1128 template <typename Container>
1129 [[nodiscard]] auto stl_min_max(const Container & c)
1130 {
1131 using T = typename Container::value_type;
1132 using ResultType = std::optional<std::pair<T, T>>;
1133
1134 auto it = c.begin();
1135 if (it == c.end())
1136 return ResultType{};
1137
1138 T min_val = *it;
1139 T max_val = *it;
1140 ++it;
1141
1142 for (; it != c.end(); ++it)
1143 {
1144 if (*it < min_val) min_val = *it;
1145 if (*it > max_val) max_val = *it;
1146 }
1147 return ResultType(std::make_pair(min_val, max_val));
1148 }
1149
1160 template <typename Key, typename Container>
1161 [[nodiscard]] auto stl_min_by(Key && key, const Container & c)
1162 {
1163 using T = typename Container::value_type;
1164
1165 auto it = c.begin();
1166 if (it == c.end())
1167 return std::optional<T>{};
1168
1169 auto & key_ref = key; // Preserve as lvalue to avoid moving in loop
1170 T min_elem = *it;
1171 auto min_key = key_ref(min_elem);
1172 ++it;
1173
1174 for (; it != c.end(); ++it)
1175 {
1176 auto k = key_ref(*it);
1177 if (k < min_key)
1178 {
1179 min_key = k;
1180 min_elem = *it;
1181 }
1182 }
1183 return std::optional<T>(min_elem);
1184 }
1185
1196 template <typename Key, typename Container>
1197 [[nodiscard]] auto stl_max_by(Key && key, const Container & c)
1198 {
1199 using T = typename Container::value_type;
1200
1201 auto it = c.begin();
1202 if (it == c.end())
1203 return std::optional<T>{};
1204
1205 auto & key_ref = key; // Preserve as lvalue to avoid moving in loop
1206 T max_elem = *it;
1207 auto max_key = key_ref(max_elem);
1208 ++it;
1209
1210 for (; it != c.end(); ++it)
1211 {
1212 auto k = key_ref(*it);
1213 if (k > max_key)
1214 {
1215 max_key = k;
1216 max_elem = *it;
1217 }
1218 }
1219 return std::optional<T>(max_elem);
1220 }
1221
1222 // ============================================================================
1223 // Sum and Product
1224 // ============================================================================
1225
1235 template <typename Container>
1236 [[nodiscard]] auto stl_sum(const Container & c)
1237 {
1238 using T = typename Container::value_type;
1239 T sum{};
1240 for (const auto & item: c)
1241 sum = sum + item;
1242 return sum;
1243 }
1244
1254 template <typename Container>
1255 [[nodiscard]] auto stl_product(const Container & c)
1256 {
1257 using T = typename Container::value_type;
1258 if (c.empty()) return T{};
1259
1260 auto it = c.begin();
1261 T prod = *it;
1262 ++it;
1263 for (; it != c.end(); ++it)
1264 prod = prod * (*it);
1265 return prod;
1266 }
1267
1268 // ============================================================================
1269 // Partitioning
1270 // ============================================================================
1271
1282 template <typename Pred, typename Container>
1284 {
1285 using T = typename Container::value_type;
1286
1287 std::vector<T> matching, non_matching;
1288 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
1289 for (const auto & item: c)
1290 if (pred_ref(item))
1291 matching.push_back(item);
1292 else
1293 non_matching.push_back(item);
1294 return std::make_pair(std::move(matching), std::move(non_matching));
1295 }
1296
1297 // ============================================================================
1298 // Zipping and Pairing
1299 // ============================================================================
1300
1311 template <typename Container1, typename Container2>
1313 {
1314 using T1 = typename Container1::value_type;
1315 using T2 = typename Container2::value_type;
1316
1317 std::vector<std::pair<T1, T2>> result;
1318 if constexpr (stl_detail::has_size_v<Container1> && stl_detail::has_size_v<Container2>)
1319 result.reserve(std::min(c1.size(), c2.size()));
1320 auto it1 = c1.begin();
1321 auto it2 = c2.begin();
1322 for (; it1 != c1.end() and it2 != c2.end(); ++it1, ++it2)
1323 result.emplace_back(*it1, *it2);
1324 return result;
1325 }
1326
1336 template <typename Container>
1338 {
1339 using PairType = typename Container::value_type;
1340 using T1 = std::decay_t<decltype(std::declval<PairType>().first)>;
1341 using T2 = std::decay_t<decltype(std::declval<PairType>().second)>;
1342
1343 std::vector<T1> v1;
1344 std::vector<T2> v2;
1347
1348 for (const auto & p: c)
1349 {
1350 v1.push_back(p.first);
1351 v2.push_back(p.second);
1352 }
1353 return std::make_pair(std::move(v1), std::move(v2));
1354 }
1355
1365 template <typename Container>
1367 {
1368 using T = typename Container::value_type;
1369
1370 std::vector<std::pair<size_t, T>> result;
1371 stl_detail::try_reserve(result, c);
1372 size_t i = 0;
1373 for (const auto & item: c)
1374 result.emplace_back(i++, item);
1375 return result;
1376 }
1377
1378 // ============================================================================
1379 // Comparison
1380 // ============================================================================
1381
1392 template <typename Container1, typename Container2>
1393 [[nodiscard]] bool stl_equal(const Container1 & c1, const Container2 & c2)
1394 {
1395 auto it1 = c1.begin();
1396 auto it2 = c2.begin();
1397 for (; it1 != c1.end() and it2 != c2.end(); ++it1, ++it2)
1398 if (not (*it1 == *it2))
1399 return false;
1400 return it1 == c1.end() and it2 == c2.end();
1401 }
1402
1413 template <typename Container1, typename Container2>
1415 {
1416 auto it1 = c1.begin();
1417 auto it2 = c2.begin();
1418
1419 for (; it1 != c1.end() and it2 != c2.end(); ++it1, ++it2)
1420 {
1421 if (*it1 < *it2) return -1;
1422 if (*it2 < *it1) return 1;
1423 }
1424
1425 if (it1 == c1.end() and it2 == c2.end()) return 0;
1426 return (it1 == c1.end()) ? -1 : 1;
1427 }
1428
1429 // ============================================================================
1430 // Reversing and Sorting
1431 // ============================================================================
1432
1442 template <typename Container>
1443 [[nodiscard]] auto stl_reverse(const Container & c)
1444 {
1445 using T = typename Container::value_type;
1446
1447 std::vector<T> result(c.begin(), c.end());
1448 std::reverse(result.begin(), result.end());
1449 return result;
1450 }
1451
1461 template <typename Container>
1462 [[nodiscard]] auto stl_sort(const Container & c)
1463 {
1464 using T = typename Container::value_type;
1465
1466 std::vector<T> result(c.begin(), c.end());
1467 std::sort(result.begin(), result.end());
1468 return result;
1469 }
1470
1481 template <typename Cmp, typename Container>
1482 [[nodiscard]] auto stl_sort_by(Cmp && cmp, const Container & c)
1483 {
1484 using T = typename Container::value_type;
1485
1486 std::vector<T> result(c.begin(), c.end());
1487 // std::sort only calls comparator, doesn't need forwarding protection
1488 // as it makes copies/moves internally, but for consistency:
1489 std::sort(result.begin(), result.end(), std::forward<Cmp>(cmp));
1490 return result;
1491 }
1492
1493 // ============================================================================
1494 // Uniqueness
1495 // ============================================================================
1496
1506 template <typename Container>
1507 [[nodiscard]] auto stl_unique(const Container & c)
1508 {
1509 using T = typename Container::value_type;
1510
1511 std::vector<T> result;
1512 for (const auto & item: c)
1513 if (result.empty() or not (result.back() == item))
1514 result.push_back(item);
1515 return result;
1516 }
1517
1544 template <typename Container>
1546 {
1547 using T = typename Container::value_type;
1548
1549 std::vector<T> result;
1550 const size_t size = stl_detail::safe_size(c);
1551
1552 // Use linear search for small containers or if type is not hashable
1553 if constexpr (!stl_detail::is_std_hashable_v<T>)
1554 {
1555 // Linear search only (type not hashable)
1556 for (const auto & item : c)
1557 if (std::find(result.begin(), result.end(), item) == result.end())
1558 result.push_back(item);
1559 }
1561 {
1562 // Linear search for small containers (better cache, no hash overhead)
1563 for (const auto & item : c)
1564 if (std::find(result.begin(), result.end(), item) == result.end())
1565 result.push_back(item);
1566 }
1567 else
1568 {
1569 // Hash-based for large containers
1570 std::unordered_set<T> seen;
1571 seen.reserve(size);
1572 for (const auto & item : c)
1573 if (seen.insert(item).second)
1574 result.push_back(item);
1575 }
1576
1577 return result;
1578 }
1579
1580 // ============================================================================
1581 // Concatenation and Flattening
1582 // ============================================================================
1583
1594 template <typename Container1, typename Container2>
1595 [[nodiscard]] auto stl_concat(const Container1 & c1, const Container2 & c2)
1596 {
1597 using T = typename Container1::value_type;
1598
1599 std::vector<T> result;
1600 if constexpr (stl_detail::has_size_v<Container1> && stl_detail::has_size_v<Container2>)
1601 result.reserve(c1.size() + c2.size());
1602 for (const auto & item: c1)
1603 result.push_back(item);
1604 for (const auto & item: c2)
1605 result.push_back(item);
1606 return result;
1607 }
1608
1618 template <typename Container>
1619 [[nodiscard]] auto stl_flatten(const Container & c)
1620 {
1621 using InnerContainer = typename Container::value_type;
1622 using T = typename InnerContainer::value_type;
1623
1624 std::vector<T> result;
1625 for (const auto & inner: c)
1626 for (const auto & item: inner)
1627 result.push_back(item);
1628 return result;
1629 }
1630
1641 template <typename Op, typename Container>
1642 [[nodiscard]] auto stl_flat_map(Op && op, const Container & c)
1643 {
1644 using T = typename Container::value_type;
1645 using InnerContainer = std::decay_t<decltype(op(std::declval<T>()))>;
1646 using R = typename InnerContainer::value_type;
1647
1648 std::vector<R> result;
1649 auto & op_ref = op; // Preserve as lvalue to avoid moving in loop
1650 for (const auto & item: c)
1651 for (const auto & inner_item: op_ref(item))
1652 result.push_back(inner_item);
1653 return result;
1654 }
1655
1656 // ============================================================================
1657 // Grouping
1658 // ============================================================================
1659
1669 template <typename Container>
1670 [[nodiscard]] auto stl_group(const Container & c)
1671 {
1672 using T = typename Container::value_type;
1673
1674 std::vector<std::vector<T>> result;
1675 if (c.empty()) return result;
1676
1677 auto it = c.begin();
1678 result.emplace_back();
1679 result.back().push_back(*it);
1680 T current = *it;
1681 ++it;
1682
1683 for (; it != c.end(); ++it)
1684 {
1685 if (not (*it == current))
1686 {
1687 result.emplace_back();
1688 current = *it;
1689 }
1690 result.back().push_back(*it);
1691 }
1692 return result;
1693 }
1694
1724 template <typename Key, typename Container>
1725 [[nodiscard]] auto stl_group_by(Key && key, const Container & c)
1726 {
1727 using T = typename Container::value_type;
1728 using K = std::decay_t<decltype(key(std::declval<T>()))>;
1729
1730 std::vector<std::pair<K, std::vector<T>>> result;
1731 auto & key_ref = key; // Preserve as lvalue to avoid moving in loop
1732 const size_t size = stl_detail::safe_size(c);
1733
1734 // Use linear search for small containers or if key type is not hashable
1735 if constexpr (!stl_detail::is_std_hashable_v<K>)
1736 {
1737 // Linear search only (key type not hashable)
1738 for (const auto & item : c)
1739 {
1740 auto k = key_ref(item);
1741 auto it = std::find_if(result.begin(), result.end(),
1742 [&k](const auto & p) { return p.first == k; });
1743 if (it != result.end())
1744 it->second.push_back(item);
1745 else
1746 result.emplace_back(std::move(k), std::vector<T>{item});
1747 }
1748 }
1750 {
1751 // Linear search for small containers
1752 for (const auto & item : c)
1753 {
1754 auto k = key_ref(item);
1755 auto it = std::find_if(result.begin(), result.end(),
1756 [&k](const auto & p) { return p.first == k; });
1757 if (it != result.end())
1758 it->second.push_back(item);
1759 else
1760 result.emplace_back(std::move(k), std::vector<T>{item});
1761 }
1762 }
1763 else
1764 {
1765 // Hash-based for large containers
1766 std::unordered_map<K, size_t> key_to_index;
1767 key_to_index.reserve(size);
1768
1769 for (const auto & item : c)
1770 {
1771 auto k = key_ref(item);
1772 auto [it, inserted] = key_to_index.try_emplace(k, result.size());
1773 if (inserted)
1774 result.emplace_back(std::move(k), std::vector<T>{item});
1775 else
1776 result[it->second].second.push_back(item);
1777 }
1778 }
1779
1780 return result;
1781 }
1782
1783 // ============================================================================
1784 // Combinatorics: Permutations, Combinations, Arrangements
1785 // ============================================================================
1786
1787 namespace stl_comb_detail
1788 {
1789 template <typename T, typename Op>
1790 bool permutations_impl(std::vector<T> & arr, size_t start, Op & op_ref)
1791 {
1792 if (start >= arr.size())
1793 return op_ref(arr);
1794
1795 for (size_t i = start; i < arr.size(); ++i)
1796 {
1797 std::swap(arr[start], arr[i]);
1798 if (not permutations_impl(arr, start + 1, op_ref))
1799 {
1800 std::swap(arr[start], arr[i]);
1801 return false;
1802 }
1803 std::swap(arr[start], arr[i]);
1804 }
1805 return true;
1806 }
1807
1808 template <typename T, typename Op>
1809 bool combinations_impl(const std::vector<T> & arr, size_t k, size_t start,
1810 std::vector<T> & current, Op & op_ref)
1811 {
1812 if (current.size() == k)
1813 return op_ref(current);
1814
1815 for (size_t i = start; i <= arr.size() - (k - current.size()); ++i)
1816 {
1817 current.push_back(arr[i]);
1818 if (not combinations_impl(arr, k, i + 1, current, op_ref))
1819 {
1820 current.pop_back();
1821 return false;
1822 }
1823 current.pop_back();
1824 }
1825 return true;
1826 }
1827
1828 template <typename T, typename Op>
1829 bool arrangements_impl(const std::vector<T> & arr, size_t k,
1830 std::vector<T> & current, std::vector<bool> & used, Op & op_ref)
1831 {
1832 if (current.size() == k)
1833 return op_ref(current);
1834
1835 for (size_t i = 0; i < arr.size(); ++i)
1836 {
1837 if (used[i]) continue;
1838 used[i] = true;
1839 current.push_back(arr[i]);
1840 if (not arrangements_impl(arr, k, current, used, op_ref))
1841 {
1842 current.pop_back();
1843 used[i] = false;
1844 return false;
1845 }
1846 current.pop_back();
1847 used[i] = false;
1848 }
1849 return true;
1850 }
1851 } // namespace stl_comb_detail
1852
1876 template <typename Op, typename Container>
1878 {
1879 using T = typename Container::value_type;
1880 std::vector<T> arr(c.begin(), c.end());
1881 auto & op_ref = op; // Preserve as lvalue for recursive calls
1883 }
1884
1896 template <typename Container>
1898 {
1899 using T = typename Container::value_type;
1900 std::vector<std::vector<T>> result;
1901
1902 stl_traverse_permutations([&result](const std::vector<T> & p)
1903 {
1904 result.push_back(p);
1905 return true;
1906 }, c);
1907
1908 return result;
1909 }
1910
1935 template <typename Op, typename Container>
1936 bool stl_traverse_combinations(size_t k, Op && op, const Container & c)
1937 {
1938 using T = typename Container::value_type;
1939 std::vector<T> arr(c.begin(), c.end());
1940
1941 if (k > arr.size())
1942 return true;
1943
1944 std::vector<T> current;
1945 current.reserve(k);
1946 auto & op_ref = op; // Preserve as lvalue for recursive calls
1947 return stl_comb_detail::combinations_impl(arr, k, 0, current, op_ref);
1948 }
1949
1960 template <typename Container>
1961 [[nodiscard]] auto stl_combinations(size_t k, const Container & c)
1962 {
1963 using T = typename Container::value_type;
1964 std::vector<std::vector<T>> result;
1965
1966 stl_traverse_combinations(k, [&result](const std::vector<T> & combo)
1967 {
1968 result.push_back(combo);
1969 return true;
1970 }, c);
1971
1972 return result;
1973 }
1974
1999 template <typename Op, typename Container>
2000 bool stl_traverse_arrangements(size_t k, Op && op, const Container & c)
2001 {
2002 using T = typename Container::value_type;
2003 std::vector<T> arr(c.begin(), c.end());
2004
2005 if (k > arr.size())
2006 return true;
2007
2008 std::vector<T> current;
2009 current.reserve(k);
2010 std::vector<bool> used(arr.size(), false);
2011 auto & op_ref = op; // Preserve as lvalue for recursive calls
2012 return stl_comb_detail::arrangements_impl(arr, k, current, used, op_ref);
2013 }
2014
2025 template <typename Container>
2026 [[nodiscard]] auto stl_arrangements(size_t k, const Container & c)
2027 {
2028 using T = typename Container::value_type;
2029 std::vector<std::vector<T>> result;
2030
2031 stl_traverse_arrangements(k, [&result](const std::vector<T> & arr)
2032 {
2033 result.push_back(arr);
2034 return true;
2035 }, c);
2036
2037 return result;
2038 }
2039
2056 template <typename T>
2057 [[nodiscard]] auto stl_cartesian_product(const std::vector<std::vector<T>> & containers)
2058 {
2059 std::vector<std::vector<T>> result;
2060
2061 if (containers.empty())
2062 return result;
2063
2064 // Start with first container
2065 for (const auto & elem: containers[0])
2066 result.push_back({elem});
2067
2068 // Extend with each subsequent container
2069 for (size_t i = 1; i < containers.size(); ++i)
2070 {
2071 std::vector<std::vector<T>> new_result;
2072 for (const auto & partial: result)
2073 for (const auto & elem: containers[i])
2074 {
2075 auto extended = partial;
2076 extended.push_back(elem);
2077 new_result.push_back(std::move(extended));
2078 }
2079 result = std::move(new_result);
2080 }
2081
2082 return result;
2083 }
2084
2094 template <typename Container>
2095 [[nodiscard]] auto stl_power_set(const Container & c)
2096 {
2097 using T = typename Container::value_type;
2098 std::vector<T> arr(c.begin(), c.end());
2099 std::vector<std::vector<T>> result;
2100
2101 const size_t n = arr.size();
2102
2103 // Protect against overflow: 2^n overflows for n >= 64
2104 if (n >= 63)
2105 throw std::overflow_error("stl_power_set: container too large (n >= 63 would overflow)");
2106
2107 const size_t total = 1ULL << n; // 2^n
2108 result.reserve(total);
2109
2110 for (size_t mask = 0; mask < total; ++mask)
2111 {
2112 std::vector<T> subset;
2113 for (size_t i = 0; i < n; ++i)
2114 if (mask & (1ULL << i))
2115 subset.push_back(arr[i]);
2116 result.push_back(std::move(subset));
2117 }
2118
2119 return result;
2120 }
2121
2122 // ============================================================================
2123 // Additional Ruby/ML-style Operations
2124 // ============================================================================
2125
2142 template <typename Container>
2143 [[nodiscard]] auto stl_sliding_window(size_t n, const Container & c)
2144 {
2145 using T = typename Container::value_type;
2146 std::vector<std::vector<T>> result;
2147
2148 if (n == 0) return result;
2149
2150 std::vector<T> arr(c.begin(), c.end());
2151 if (arr.size() < n) return result;
2152
2153 for (size_t i = 0; i <= arr.size() - n; ++i)
2154 result.emplace_back(arr.begin() + i, arr.begin() + i + n);
2155
2156 return result;
2157 }
2158
2175 template <typename Container>
2176 [[nodiscard]] auto stl_chunks(size_t n, const Container & c)
2177 {
2178 using T = typename Container::value_type;
2179 std::vector<std::vector<T>> result;
2180
2181 if (n == 0) return result;
2182
2183 std::vector<T> arr(c.begin(), c.end());
2184
2185 for (size_t i = 0; i < arr.size(); i += n)
2186 {
2187 size_t end = std::min(i + n, arr.size());
2188 result.emplace_back(arr.begin() + i, arr.begin() + end);
2189 }
2190
2191 return result;
2192 }
2193
2210 template <typename T, typename Container>
2211 [[nodiscard]] auto stl_intersperse(const T & sep, const Container & c)
2212 {
2213 std::vector<T> result;
2214
2215 bool first = true;
2216 for (const auto & item: c)
2217 {
2218 if (not first)
2219 result.push_back(sep);
2220 result.push_back(item);
2221 first = false;
2222 }
2223
2224 return result;
2225 }
2226
2237 template <typename Container>
2238 [[nodiscard]] auto stl_split_at(size_t n, const Container & c)
2239 {
2240 using T = typename Container::value_type;
2241
2242 std::vector<T> first, second;
2243 size_t i = 0;
2244 for (const auto & item: c)
2245 {
2246 if (i < n)
2247 first.push_back(item);
2248 else
2249 second.push_back(item);
2250 ++i;
2251 }
2252
2253 return std::make_pair(std::move(first), std::move(second));
2254 }
2255
2268 template <typename Pred, typename Container>
2269 [[nodiscard]] auto stl_span(Pred && pred, const Container & c)
2270 {
2271 using T = typename Container::value_type;
2272
2273 std::vector<T> first, second;
2274 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
2275 bool taking = true;
2276
2277 for (const auto & item: c)
2278 if (taking and pred_ref(item))
2279 first.push_back(item);
2280 else
2281 {
2282 taking = false;
2283 second.push_back(item);
2284 }
2285
2286 return std::make_pair(std::move(first), std::move(second));
2287 }
2288
2298 template <typename Container>
2299 [[nodiscard]] auto stl_init(const Container & c)
2300 {
2301 using T = typename Container::value_type;
2302 std::vector<T> arr(c.begin(), c.end());
2303
2304 if (not arr.empty())
2305 arr.pop_back();
2306
2307 return arr;
2308 }
2309
2319 template <typename Container>
2320 [[nodiscard]] auto stl_tail(const Container & c)
2321 {
2322 using T = typename Container::value_type;
2323 std::vector<T> result;
2324
2325 bool first = true;
2326 for (const auto & item: c)
2327 {
2328 if (first)
2329 first = false;
2330 else
2331 result.push_back(item);
2332 }
2333
2334 return result;
2335 }
2336
2361 template <typename Container>
2362 [[nodiscard]] auto stl_tally(const Container & c)
2363 {
2364 using T = typename Container::value_type;
2365
2366 std::vector<std::pair<T, size_t>> result;
2367 const size_t size = stl_detail::safe_size(c);
2368
2369 // Use linear search for small containers or if type is not hashable
2370 if constexpr (!stl_detail::is_std_hashable_v<T>)
2371 {
2372 // Linear search only (type not hashable)
2373 for (const auto & item : c)
2374 {
2375 auto it = std::find_if(result.begin(), result.end(),
2376 [&item](const auto & p) { return p.first == item; });
2377 if (it != result.end())
2378 ++it->second;
2379 else
2380 result.emplace_back(item, 1);
2381 }
2382 }
2383 else if (size <= stl_detail::ADAPTIVE_THRESHOLD)
2384 {
2385 // Linear search for small containers
2386 for (const auto & item : c)
2387 {
2388 auto it = std::find_if(result.begin(), result.end(),
2389 [&item](const auto & p) { return p.first == item; });
2390 if (it != result.end())
2391 ++it->second;
2392 else
2393 result.emplace_back(item, 1);
2394 }
2395 }
2396 else
2397 {
2398 // Hash-based for large containers
2399 std::unordered_map<T, size_t> counts;
2400 counts.reserve(size);
2401 std::vector<T> order; // Track insertion order
2402 order.reserve(size);
2403
2404 for (const auto & item : c)
2405 {
2406 auto [it, inserted] = counts.try_emplace(item, 0);
2407 ++it->second;
2408 if (inserted)
2409 order.push_back(item);
2410 }
2411
2412 result.reserve(order.size());
2413 for (const auto & item : order)
2414 result.emplace_back(item, counts[item]);
2415 }
2416
2417 return result;
2418 }
2419
2430 template <typename Pred, typename Container>
2431 [[nodiscard]] auto stl_reject(Pred && pred, const Container & c)
2432 {
2433 using T = typename Container::value_type;
2434
2435 std::vector<T> result;
2436 auto & pred_ref = pred; // Preserve as lvalue to avoid moving in loop
2437 for (const auto & item: c)
2438 if (not pred_ref(item))
2439 result.push_back(item);
2440 return result;
2441 }
2442} // end namespace Aleph
2443
2444# endif // AH_STL_FUNCTIONAL_H
C++20 Ranges support and adaptors for Aleph-w containers.
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
void empty() noexcept
empty the list
Definition htlist.H:1689
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4118
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 ranges_any_of(const Container &c, Pred &&pred)
Fallback any_of using range-based for loop.
Definition ah-ranges.H:847
bool ranges_all_of(const Container &c, Pred &&pred)
Fallback all_of using range-based for loop.
Definition ah-ranges.H:835
bool arrangements_impl(const std::vector< T > &arr, size_t k, std::vector< T > &current, std::vector< bool > &used, Op &op_ref)
bool permutations_impl(std::vector< T > &arr, size_t start, Op &op_ref)
bool combinations_impl(const std::vector< T > &arr, size_t k, size_t start, std::vector< T > &current, Op &op_ref)
constexpr size_t ADAPTIVE_THRESHOLD
Threshold for switching from linear to hash-based algorithms.
constexpr bool is_std_hashable_v
void try_reserve_n(Result &result, size_t n)
Helper to reserve with a specific size if the result supports it.
size_t safe_size(const Container &c)
Helper to get container size safely (returns 0 for containers without size())
void try_reserve(Result &result, const Container &c)
Helper to reserve if possible.
constexpr bool has_reverse_iterators_v
constexpr bool has_size_v
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
auto stl_first(const Container &c)
Get first element.
T stl_reduce(T init, Op &&op, const Container &c)
Alias for stl_foldl.
std::vector< T > stl_scan_left(T init, Op &&op, const Container &c)
Scan left - fold with all intermediate results.
bool stl_any(Pred &&pred, const Container &c)
Alias for stl_exists.
auto stl_span(Pred &&pred, const Container &c)
Split at predicate boundary (span in Haskell).
auto stl_take_last(size_t n, const Container &c)
Take last n elements.
bool all(Container &container, Operation &operation)
Return true if all elements satisfy a predicate.
auto stl_chunks(size_t n, const Container &c)
Split container into chunks of size n (each_slice in Ruby).
int stl_compare(const Container1 &c1, const Container2 &c2)
Compare two containers lexicographically.
auto stl_min_max(const Container &c)
Get both min and max in a single pass.
auto stl_last(const Container &c)
Get last element.
bool stl_traverse_arrangements(size_t k, Op &&op, const Container &c)
Traverse all k-arrangements (k-permutations) of a container.
auto stl_distinct(const Container &c)
Remove all duplicates (keeps first occurrence).
auto stl_max(const Container &c)
Get maximum element.
size_t size(Node *root) noexcept
auto stl_flatten(const Container &c)
Flatten a container of containers.
auto stl_reject(Pred &&pred, const Container &c)
Filter out elements (reject in Ruby, opposite of filter).
auto stl_filter(Pred &&pred, const Container &c)
Filter elements satisfying predicate.
auto stl_arrangements(size_t k, const Container &c)
Generate all k-arrangements (k-permutations) of a container.
auto stl_drop(size_t n, const Container &c)
Drop first n elements, return the rest.
auto stl_enumerate_to_pairs(const Container &c)
Enumerate container (return pairs of index and element).
bool stl_equal(const Container1 &c1, const Container2 &c2)
Check equality of two containers.
auto stl_concat(const Container1 &c1, const Container2 &c2)
Concatenate two containers.
auto stl_combinations(size_t k, const Container &c)
Generate all k-combinations of a container.
auto stl_take_while(Pred &&pred, const Container &c)
Take elements while predicate is true.
auto stl_find_mapi(Op &&op, const Container &c)
Find and map with index (find_mapi in ML).
auto stl_nth(const size_t n, const Container &c)
Get n-th element.
auto stl_map(Op &&op, const Container &c)
Map operation - transform each element.
bool stl_mem(const T &target, const Container &c)
Check if element exists in container (mem in ML).
std::optional< size_t > stl_find_index(Pred &&pred, const Container &c)
Find index of first element satisfying predicate.
T stl_foldl(T init, Op &&op, const Container &c)
Left fold (foldl) - reduce from left to right.
auto stl_min_by(Key &&key, const Container &c)
Get minimum element by key function.
std::vector< T > stl_linspace(T start, T end, size_t n)
Generate n evenly spaced values between start and end.
bool stl_exists(Pred &&pred, const Container &c)
Check if any element satisfies predicate.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
auto stl_take(size_t n, const Container &c)
Take first n elements.
auto stl_generate(size_t n, Gen &&gen)
Generate a vector using a generator function.
std::vector< T > stl_range(T start, T end, T step=1)
Generate a range of values [start, end] with given step.
auto stl_init(const Container &c)
Get all elements except the last (init in Haskell).
bool stl_traverse_permutations(Op &&op, const Container &c)
Traverse all permutations of a container.
size_t stl_count_value(const T &target, const Container &c)
Count occurrences of a value.
auto stl_sort(const Container &c)
Return sorted copy of container.
auto stl_permutations(const Container &c)
Generate all permutations of a container.
auto stl_tally(const Container &c)
Count occurrences of each element (tally in Ruby, frequencies).
bool stl_all(Pred &&pred, const Container &c)
Check if all elements satisfy predicate.
auto stl_partition(Pred &&pred, const Container &c)
Partition elements by predicate.
auto stl_unique(const Container &c)
Remove consecutive duplicates.
auto stl_mapi(Op &&op, const Container &c)
Map with index (mapi in ML).
size_t stl_count(Pred &&pred, const Container &c)
Count elements satisfying predicate.
void stl_for_each_indexed(Op &&op, const Container &c)
Apply operation to each element with index.
auto stl_sum(const Container &c)
Sum all elements.
auto stl_sliding_window(size_t n, const Container &c)
Sliding window of size n over container (each_cons in Ruby).
auto stl_max_by(Key &&key, const Container &c)
Get maximum element by key function.
static bool init
Definition hash-fct.C:47
auto stl_group_by(Key &&key, const Container &c)
Group elements by key function.
auto stl_min(const Container &c)
Get minimum element.
bool stl_none(Pred &&pred, const Container &c)
Check if no element satisfies predicate.
auto stl_reverse(const Container &c)
Return reversed copy of container.
auto stl_flat_map(Op &&op, const Container &c)
Flat map - map then flatten.
std::vector< T > stl_rep(size_t n, const T &value)
Generate a vector of n repeated values.
bool stl_traverse_combinations(size_t k, Op &&op, const Container &c)
Traverse all k-combinations of a container.
T stl_foldr(T init, Op &&op, const Container &c)
Right fold (foldr) - reduce from right to left.
std::vector< T > stl_scan_right(T init, Op &&op, const Container &c)
Scan right - right fold with all intermediate results.
auto stl_unzip_pairs(const Container &c)
Unzip pairs into two vectors.
auto stl_drop_while(Pred &&pred, const Container &c)
Drop elements while predicate is true, return the rest.
auto stl_intersperse(const T &sep, const Container &c)
Insert element between each pair (intersperse in Haskell).
auto stl_cartesian_product(const std::vector< std::vector< T > > &containers)
Generate cartesian product of multiple containers.
auto stl_product(const Container &c)
Product of all elements.
auto stl_tail(const Container &c)
Get all elements except the first (tail in Haskell).
auto stl_filteri(Pred &&pred, const Container &c)
Filter with index (filteri in ML).
auto stl_find(Pred &&pred, const Container &c)
Find first element satisfying predicate.
void stl_for_each(Op &&op, const Container &c)
Apply operation to each element (for_each).
auto stl_split_at(size_t n, const Container &c)
Split at position n, returning (take n, drop n) in one pass.
auto stl_sort_by(Cmp &&cmp, const Container &c)
Return sorted copy using custom comparator.
auto stl_power_set(const Container &c)
Generate power set (all subsets) of a container.
auto stl_group(const Container &c)
Group consecutive equal elements.
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
auto stl_zip_to_pairs(const Container1 &c1, const Container2 &c2)
Zip two containers into pairs.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
auto stl_find_last(Pred &&pred, const Container &c)
Find last element satisfying predicate.
STL namespace.
Detect if a container has reverse iterators.
Detect if a type has a size() method.
Detect if a type is hashable via std::hash.