Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah-uni-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_UNI_FUNCTIONAL_H
33# define AH_UNI_FUNCTIONAL_H
34
71# include <type_traits>
72# include <vector>
73# include <optional>
74# include <functional>
75# include <algorithm>
76# include <utility>
77# include <tuple>
78# include <iterator>
79
80namespace Aleph
81{
82 // ============================================================================
83 // Type Traits for Container Detection
84 // ============================================================================
85
86 namespace uni_functional_detail
87 {
88 // Detect if type has STL-style begin()/end()
89 template <typename T, typename = void>
90 struct has_stl_iterator : std::false_type
91 {};
92
93 template <typename T>
95 decltype(std::declval<const T &>().begin()),
96 decltype(std::declval<const T &>().end()),
97 typename T::value_type
98 >> : std::true_type
99 {};
100
101 // Detect if type has Aleph-style Iterator with has_curr()/get_curr()/next()
102 template <typename T, typename = void>
103 struct has_aleph_iterator : std::false_type
104 {};
105
106 template <typename T>
108 typename T::Iterator,
109 typename T::Item_Type,
110 decltype(std::declval<typename T::Iterator>().has_curr()),
111 decltype(std::declval<typename T::Iterator>().get_curr()),
112 decltype(std::declval<typename T::Iterator>().next())
113 >> : std::true_type
114 {};
115
116 // Detect if type has reverse iterators (rbegin/rend)
117 template <typename T, typename = void>
118 struct has_reverse_iterator : std::false_type
119 {};
120
121 template <typename T>
123 decltype(std::declval<const T &>().rbegin()),
124 decltype(std::declval<const T &>().rend())
125 >> : std::true_type
126 {};
127
128 // Get value type from container
129 template <typename T, typename = void>
131
132 template <typename T>
133 struct container_value_type<T, std::enable_if_t<has_stl_iterator<T>::value>>
134 {
135 using type = typename T::value_type;
136 };
137
138 template <typename T>
140 has_aleph_iterator<T>::value and not has_stl_iterator<T>::value>>
141 {
142 using type = std::decay_t<typename T::Item_Type>;
143 };
144
145 template <typename T>
147
148 // Check if container is STL-style
149 template <typename T>
151
152 // Check if container is Aleph-only
153 template <typename T>
154 constexpr bool is_aleph_only_v =
157 } // namespace uni_functional_detail
158
159 // ============================================================================
160 // Core Functional Operations
161 // ============================================================================
162
173 template <typename Op, typename Container>
174 void uni_for_each(Op && op, const Container & c)
175 {
176 auto & op_ref = op;
177 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
178 for (const auto & item: c)
179 op_ref(item);
180 else
181 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
182 op_ref(it.get_curr());
183 }
184
193 template <typename Op, typename Container>
194 void uni_for_each_indexed(Op && op, const Container & c)
195 {
196 auto & op_ref = op;
197 size_t i = 0;
198 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
199 for (const auto & item: c)
200 op_ref(i++, item);
201 else
202 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++i)
203 op_ref(i, it.get_curr());
204 }
205
215 template <typename Op, typename Container>
216 [[nodiscard]] auto uni_map(Op && op, const Container & c)
217 {
219 using R = std::decay_t<decltype(std::forward<Op>(op)(std::declval<T>()))>;
220
221 std::vector<R> result;
222 auto & op_ref = op;
223
224 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
225 {
226 result.reserve(c.size());
227 for (const auto & item: c)
228 result.push_back(op_ref(item));
229 }
230 else
231 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
232 result.push_back(op_ref(it.get_curr()));
233
234 return result;
235 }
236
246 template <typename Op, typename Container>
247 [[nodiscard]] auto uni_mapi(Op && op, const Container & c)
248 {
250 using R = std::decay_t<decltype(std::forward<Op>(op)(size_t{}, std::declval<T>()))>;
251
252 std::vector<R> result;
253 auto & op_ref = op;
254 size_t i = 0;
255
256 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
257 {
258 result.reserve(c.size());
259 for (const auto & item: c)
260 result.push_back(op_ref(i++, item));
261 }
262 else
263 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++i)
264 result.push_back(op_ref(i, it.get_curr()));
265
266 return result;
267 }
268
278 template <typename Pred, typename Container>
279 [[nodiscard]] auto uni_filter(Pred && pred, const Container & c)
280 {
282
283 std::vector<T> result;
284 auto & pred_ref = pred;
285
286 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
287 {
288 for (const auto & item: c)
289 if (pred_ref(item))
290 result.push_back(item);
291 }
292 else
293 {
294 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
295 if (const auto & item = it.get_curr(); pred_ref(item))
296 result.push_back(item);
297 }
298 return result;
299 }
300
310 template <typename Pred, typename Container>
311 [[nodiscard]] auto uni_filteri(Pred && pred, const Container & c)
312 {
314
315 std::vector<T> result;
316 auto & pred_ref = pred;
317 size_t i = 0;
318
319 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
320 for (const auto & item: c)
321 {
322 if (pred_ref(i, item))
323 result.push_back(item);
324 ++i;
325 }
326 else
327 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++i)
328 if (const auto & item = it.get_curr(); pred_ref(i, item))
329 result.push_back(item);
330
331 return result;
332 }
333
344 template <typename T, typename Op, typename Container>
345 [[nodiscard]] T uni_foldl(T init, Op && op, const Container & c)
346 {
347 T acc = std::move(init);
348 auto & op_ref = op;
349
350 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
351 for (const auto & item: c)
352 acc = op_ref(std::move(acc), item);
353 else
354 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
355 acc = op_ref(std::move(acc), it.get_curr());
356 return acc;
357 }
358
360 template <typename T, typename Op, typename Container>
361 [[nodiscard]] T uni_reduce(T init, Op && op, const Container & c)
362 {
363 return uni_foldl(std::move(init), std::forward<Op>(op), c);
364 }
365
376 template <typename T, typename Op, typename Container>
377 [[nodiscard]] std::vector<T> uni_scan_left(T init, Op && op, const Container & c)
378 {
379 std::vector<T> result;
380 result.push_back(init);
381
382 T acc = std::move(init);
383 auto & op_ref = op;
384
385 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
386 for (const auto & item: c)
387 {
388 acc = op_ref(std::move(acc), item);
389 result.push_back(acc);
390 }
391 else
392 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
393 {
394 acc = op_ref(std::move(acc), it.get_curr());
395 result.push_back(acc);
396 }
397 return result;
398 }
399
400 // ============================================================================
401 // Predicates
402 // ============================================================================
403
413 template <typename Pred, typename Container>
414 [[nodiscard]] bool uni_all(Pred && pred, const Container & c)
415 {
416 auto & pred_ref = pred;
417 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
418 {
419 for (const auto & item: c)
420 if (not pred_ref(item))
421 return false;
422 }
423 else
424 {
425 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
426 if (not pred_ref(it.get_curr()))
427 return false;
428 }
429 return true;
430 }
431
441 template <typename Pred, typename Container>
442 [[nodiscard]] bool uni_exists(Pred && pred, const Container & c)
443 {
444 auto & pred_ref = pred;
445 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
446 {
447 for (const auto & item: c)
448 if (pred_ref(item))
449 return true;
450 }
451 else
452 {
453 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
454 if (pred_ref(it.get_curr()))
455 return true;
456 }
457 return false;
458 }
459
461 template <typename Pred, typename Container>
462 [[nodiscard]] bool uni_any(Pred && pred, const Container & c)
463 {
464 return uni_exists(std::forward<Pred>(pred), c);
465 }
466
472 template <typename Pred, typename Container>
473 [[nodiscard]] bool uni_none(Pred && pred, const Container & c)
474 {
475 return not uni_exists(std::forward<Pred>(pred), c);
476 }
477
478 // ============================================================================
479 // Finding Elements
480 // ============================================================================
481
491 template <typename Pred, typename Container>
492 [[nodiscard]] auto uni_find(Pred && pred, const Container & c)
493 {
495
496 auto & pred_ref = pred;
497 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
498 {
499 for (const auto & item: c)
500 if (pred_ref(item))
501 return std::optional<T>(item);
502 }
503 else
504 {
505 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
506 if (const auto & item = it.get_curr(); pred_ref(item))
507 return std::optional<T>(item);
508 }
509 return std::optional<T>{};
510 }
511
521 template <typename Pred, typename Container>
522 [[nodiscard]] std::optional<size_t> uni_find_index(Pred && pred, const Container & c)
523 {
524 auto & pred_ref = pred;
525 size_t i = 0;
526
527 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
528 {
529 for (const auto & item: c)
530 {
531 if (pred_ref(item))
532 return i;
533 ++i;
534 }
535 }
536 else
537 {
538 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++i)
539 if (pred_ref(it.get_curr()))
540 return i;
541 }
542 return std::nullopt;
543 }
544
554 template <typename Op, typename Container>
555 [[nodiscard]] auto uni_find_mapi(Op && op, const Container & c)
556 {
558 using OptType = std::decay_t<decltype(std::forward<Op>(op)(size_t{}, std::declval<T>()))>;
559
560 auto & op_ref = op;
561 size_t i = 0;
562
563 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
564 {
565 for (const auto & item: c)
566 if (auto result = op_ref(i++, item))
567 return result;
568 }
569 else
570 {
571 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++i)
572 if (auto result = op_ref(i, it.get_curr()))
573 return result;
574 }
575 return OptType{};
576 }
577
587 template <typename T, typename Container>
588 [[nodiscard]] bool uni_mem(const T & target, const Container & c)
589 {
590 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
591 {
592 for (const auto & item: c)
593 if (item == target)
594 return true;
595 }
596 else
597 {
598 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
599 if (it.get_curr() == target)
600 return true;
601 }
602 return false;
603 }
604
605 // ============================================================================
606 // Counting
607 // ============================================================================
608
618 template <typename Pred, typename Container>
619 [[nodiscard]] size_t uni_count(Pred && pred, const Container & c)
620 {
621 auto & pred_ref = pred;
622 size_t count = 0;
623
624 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
625 {
626 for (const auto & item: c)
627 if (pred_ref(item))
628 ++count;
629 }
630 else
631 {
632 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
633 if (pred_ref(it.get_curr()))
634 ++count;
635 }
636 return count;
637 }
638
647 template <typename Container>
648 [[nodiscard]] size_t uni_length(const Container & c)
649 {
650 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
651 {
652 return c.size();
653 }
654 else
655 {
656 size_t count = 0;
657 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
658 ++count;
659 return count;
660 }
661 }
662
663 // ============================================================================
664 // Taking and Dropping
665 // ============================================================================
666
676 template <typename Container>
677 [[nodiscard]] auto uni_take(size_t n, const Container & c)
678 {
680
681 std::vector<T> result;
682 result.reserve(n);
683 size_t count = 0;
684
685 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
686 {
687 for (const auto & item: c)
688 {
689 if (count >= n) break;
690 result.push_back(item);
691 ++count;
692 }
693 }
694 else
695 {
696 for (auto it = c.get_it(); it.has_curr() and count < n; it.next_ne(), ++count)
697 result.push_back(it.get_curr());
698 }
699 return result;
700 }
701
711 template <typename Container>
712 [[nodiscard]] auto uni_drop(size_t n, const Container & c)
713 {
715
716 std::vector<T> result;
717 size_t count = 0;
718
719 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
720 {
721 for (const auto & item: c)
722 {
723 if (count >= n)
724 result.push_back(item);
725 ++count;
726 }
727 }
728 else
729 {
730 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++count)
731 if (count >= n)
732 result.push_back(it.get_curr());
733 }
734 return result;
735 }
736
746 template <typename Pred, typename Container>
748 {
750
751 std::vector<T> result;
752 auto & pred_ref = pred;
753
754 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
755 {
756 for (const auto & item: c)
757 {
758 if (not pred_ref(item))
759 break;
760 result.push_back(item);
761 }
762 }
763 else
764 {
765 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
766 {
767 const auto & item = it.get_curr();
768 if (not pred_ref(item))
769 break;
770 result.push_back(item);
771 }
772 }
773 return result;
774 }
775
785 template <typename Pred, typename Container>
787 {
789
790 std::vector<T> result;
791 auto & pred_ref = pred;
792 bool dropping = true;
793
794 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
795 {
796 for (const auto & item: c)
797 {
798 if (dropping and pred_ref(item))
799 continue;
800 dropping = false;
801 result.push_back(item);
802 }
803 }
804 else
805 {
806 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
807 {
808 const auto & item = it.get_curr();
809 if (dropping and pred_ref(item))
810 continue;
811 dropping = false;
812 result.push_back(item);
813 }
814 }
815 return result;
816 }
817
818 // ============================================================================
819 // Accessing Elements
820 // ============================================================================
821
830 template <typename Container>
831 [[nodiscard]] auto uni_first(const Container & c)
832 {
834
835 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
836 {
837 auto it = c.begin();
838 if (it == c.end())
839 return std::optional<T>{};
840 return std::optional<T>(*it);
841 }
842 else
843 {
844 auto it = c.get_it();
845 if (not it.has_curr())
846 return std::optional<T>{};
847 return std::optional<T>(it.get_curr());
848 }
849 }
850
859 template <typename Container>
860 [[nodiscard]] auto uni_last(const Container & c)
861 {
863
864 std::optional<T> result;
865
866 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
867 {
868 for (const auto & item: c)
869 result = item;
870 }
871 else
872 {
873 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
874 result = it.get_curr();
875 }
876 return result;
877 }
878
888 template <typename Container>
889 [[nodiscard]] auto uni_nth(size_t n, const Container & c)
890 {
892
893 size_t i = 0;
894
895 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
896 {
897 for (const auto & item: c)
898 {
899 if (i == n)
900 return std::optional<T>(item);
901 ++i;
902 }
903 }
904 else
905 {
906 for (auto it = c.get_it(); it.has_curr(); it.next_ne(), ++i)
907 if (i == n)
908 return std::optional<T>(it.get_curr());
909 }
910 return std::optional<T>{};
911 }
912
913 // ============================================================================
914 // Min/Max
915 // ============================================================================
916
925 template <typename Container>
926 [[nodiscard]] auto uni_min(const Container & c)
927 {
929
930 std::optional<T> result;
931
932 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
933 {
934 auto it = c.begin();
935 if (it == c.end())
936 return result;
937
938 T min_val = *it;
939 ++it;
940 for (; it != c.end(); ++it)
941 if (*it < min_val)
942 min_val = *it;
943 return std::optional<T>(min_val);
944 }
945 else
946 {
947 auto it = c.get_it();
948 if (not it.has_curr())
949 return result;
950
951 T min_val = it.get_curr();
952 it.next_ne();
953 for (; it.has_curr(); it.next_ne())
954 {
955 const auto & item = it.get_curr();
956 if (item < min_val)
957 min_val = item;
958 }
959 return std::optional<T>(min_val);
960 }
961 }
962
971 template <typename Container>
972 [[nodiscard]] auto uni_max(const Container & c)
973 {
975
976 std::optional<T> result;
977
978 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
979 {
980 auto it = c.begin();
981 if (it == c.end())
982 return result;
983
984 T max_val = *it;
985 ++it;
986 for (; it != c.end(); ++it)
987 if (*it > max_val)
988 max_val = *it;
989 return std::optional<T>(max_val);
990 }
991 else
992 {
993 auto it = c.get_it();
994 if (not it.has_curr())
995 return result;
996
997 T max_val = it.get_curr();
998 it.next_ne();
999 for (; it.has_curr(); it.next_ne())
1000 {
1001 const auto & item = it.get_curr();
1002 if (item > max_val)
1003 max_val = item;
1004 }
1005 return std::optional<T>(max_val);
1006 }
1007 }
1008
1017 template <typename Container>
1018 [[nodiscard]] auto uni_min_max(const Container & c)
1019 {
1021 using ResultType = std::optional<std::pair<T, T>>;
1022
1023 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1024 {
1025 auto it = c.begin();
1026 if (it == c.end())
1027 return ResultType{};
1028
1029 T min_val = *it;
1030 T max_val = *it;
1031 ++it;
1032
1033 for (; it != c.end(); ++it)
1034 {
1035 if (*it < min_val) min_val = *it;
1036 if (*it > max_val) max_val = *it;
1037 }
1038 return ResultType(std::make_pair(min_val, max_val));
1039 }
1040 else
1041 {
1042 auto it = c.get_it();
1043 if (not it.has_curr())
1044 return ResultType{};
1045
1046 T min_val = it.get_curr();
1047 T max_val = min_val;
1048 it.next_ne();
1049
1050 for (; it.has_curr(); it.next_ne())
1051 {
1052 const auto & item = it.get_curr();
1053 if (item < min_val) min_val = item;
1054 if (item > max_val) max_val = item;
1055 }
1056 return ResultType(std::make_pair(min_val, max_val));
1057 }
1058 }
1059
1060 // ============================================================================
1061 // Sum and Product
1062 // ============================================================================
1063
1072 template <typename Container>
1073 [[nodiscard]] auto uni_sum(const Container & c)
1074 {
1076 T sum{};
1077
1078 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1079 {
1080 for (const auto & item: c)
1081 sum = sum + item;
1082 }
1083 else
1084 {
1085 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1086 sum = sum + it.get_curr();
1087 }
1088 return sum;
1089 }
1090
1099 template <typename Container>
1100 [[nodiscard]] auto uni_product(const Container & c)
1101 {
1103
1104 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1105 {
1106 auto it = c.begin();
1107 if (it == c.end()) return T{};
1108
1109 T prod = *it;
1110 ++it;
1111 for (; it != c.end(); ++it)
1112 prod = prod * (*it);
1113 return prod;
1114 }
1115 else
1116 {
1117 auto it = c.get_it();
1118 if (not it.has_curr()) return T{};
1119
1120 T prod = it.get_curr();
1121 it.next_ne();
1122 for (; it.has_curr(); it.next_ne())
1123 prod = prod * it.get_curr();
1124 return prod;
1125 }
1126 }
1127
1128 // ============================================================================
1129 // Partitioning
1130 // ============================================================================
1131
1141 template <typename Pred, typename Container>
1143 {
1145
1146 std::vector<T> matching, non_matching;
1147 auto & pred_ref = pred;
1148
1149 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1150 {
1151 for (const auto & item: c)
1152 {
1153 if (pred_ref(item))
1154 matching.push_back(item);
1155 else
1156 non_matching.push_back(item);
1157 }
1158 }
1159 else
1160 {
1161 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1162 {
1163 const auto & item = it.get_curr();
1164 if (pred_ref(item))
1165 matching.push_back(item);
1166 else
1167 non_matching.push_back(item);
1168 }
1169 }
1170 return std::make_pair(std::move(matching), std::move(non_matching));
1171 }
1172
1173 // ============================================================================
1174 // Concatenation and Flattening
1175 // ============================================================================
1176
1186 template <typename Container1, typename Container2>
1187 [[nodiscard]] auto uni_concat(const Container1 & c1, const Container2 & c2)
1188 {
1190
1191 std::vector<T> result;
1192
1193 if constexpr (uni_functional_detail::is_stl_container_v<Container1>)
1194 for (const auto & item: c1)
1195 result.push_back(item);
1196 else
1197 for (auto it = c1.get_it(); it.has_curr(); it.next_ne())
1198 result.push_back(it.get_curr());
1199
1200 if constexpr (uni_functional_detail::is_stl_container_v<Container2>)
1201 for (const auto & item: c2)
1202 result.push_back(item);
1203 else
1204 for (auto it = c2.get_it(); it.has_curr(); it.next_ne())
1205 result.push_back(it.get_curr());
1206
1207 return result;
1208 }
1209
1218 template <typename Container>
1219 [[nodiscard]] auto uni_flatten(const Container & c)
1220 {
1223
1224 std::vector<T> result;
1225
1226 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1227 {
1228 for (const auto & inner: c)
1229 {
1230 if constexpr (uni_functional_detail::is_stl_container_v<InnerContainer>)
1231 for (const auto & item: inner)
1232 result.push_back(item);
1233 else
1234 for (auto it = inner.get_it(); it.has_curr(); it.next_ne())
1235 result.push_back(it.get_curr());
1236 }
1237 }
1238 else
1239 {
1240 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1241 {
1242 const auto & inner = it.get_curr();
1243 if constexpr (uni_functional_detail::is_stl_container_v<InnerContainer>)
1244 for (const auto & item: inner)
1245 result.push_back(item);
1246 else
1247 for (auto it2 = inner.get_it(); it2.has_curr(); it2.next_ne())
1248 result.push_back(it2.get_curr());
1249 }
1250 }
1251
1252 return result;
1253 }
1254
1264 template <typename Op, typename Container>
1265 [[nodiscard]] auto uni_flat_map(Op && op, const Container & c)
1266 {
1268 using InnerContainer = std::decay_t<decltype(std::forward<Op>(op)(std::declval<T>()))>;
1270
1271 std::vector<R> result;
1272 auto & op_ref = op;
1273
1274 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1275 {
1276 for (const auto & item: c)
1277 {
1278 auto inner = op_ref(item);
1279 if constexpr (uni_functional_detail::is_stl_container_v<InnerContainer>)
1280 for (const auto & inner_item: inner)
1281 result.push_back(inner_item);
1282 else
1283 for (auto it = inner.get_it(); it.has_curr(); it.next_ne())
1284 result.push_back(it.get_curr());
1285 }
1286 }
1287 else
1288 {
1289 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1290 {
1291 auto inner = op_ref(it.get_curr());
1292 if constexpr (uni_functional_detail::is_stl_container_v<InnerContainer>)
1293 for (const auto & inner_item: inner)
1294 result.push_back(inner_item);
1295 else
1296 for (auto it2 = inner.get_it(); it2.has_curr(); it2.next_ne())
1297 result.push_back(it2.get_curr());
1298 }
1299 }
1300
1301 return result;
1302 }
1303
1304 // ============================================================================
1305 // Uniqueness and Grouping
1306 // ============================================================================
1307
1319 template <typename Container>
1321 {
1323
1324 std::vector<T> result;
1325
1326 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1327 {
1328 for (const auto & item: c)
1329 if (std::find(result.begin(), result.end(), item) == result.end())
1330 result.push_back(item);
1331 }
1332 else
1333 {
1334 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1335 if (const auto & item = it.get_curr(); std::find(result.begin(), result.end(), item) == result.end())
1336 result.push_back(item);
1337 }
1338
1339 return result;
1340 }
1341
1351 template <typename Key, typename Container>
1352 [[nodiscard]] auto uni_group_by(Key && key, const Container & c)
1353 {
1355 using K = std::decay_t<decltype(std::forward<Key>(key)(std::declval<T>()))>;
1356
1357 std::vector<std::pair<K, std::vector<T>>> result;
1358 auto & key_ref = key;
1359
1360 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1361 {
1362 for (const auto & item: c)
1363 {
1364 auto k = key_ref(item);
1365 auto it = std::find_if(result.begin(), result.end(),
1366 [&k](const auto & p) { return p.first == k; });
1367 if (it != result.end())
1368 it->second.push_back(item);
1369 else
1370 result.emplace_back(std::move(k), std::vector<T>{item});
1371 }
1372 }
1373 else
1374 {
1375 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1376 {
1377 const auto & item = it.get_curr();
1378 auto k = key_ref(item);
1379 auto res_it = std::find_if(result.begin(), result.end(),
1380 [&k](const auto & p) { return p.first == k; });
1381 if (res_it != result.end())
1382 res_it->second.push_back(item);
1383 else
1384 result.emplace_back(std::move(k), std::vector<T>{item});
1385 }
1386 }
1387
1388 return result;
1389 }
1390
1399 template <typename Container>
1400 [[nodiscard]] auto uni_tally(const Container & c)
1401 {
1403
1404 std::vector<std::pair<T, size_t>> result;
1405
1406 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1407 {
1408 for (const auto & item: c)
1409 {
1410 auto it = std::find_if(result.begin(), result.end(),
1411 [&item](const auto & p) { return p.first == item; });
1412 if (it != result.end())
1413 ++it->second;
1414 else
1415 result.emplace_back(item, 1);
1416 }
1417 }
1418 else
1419 {
1420 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1421 {
1422 const auto & item = it.get_curr();
1423 auto res_it = std::find_if(result.begin(), result.end(),
1424 [&item](const auto & p) { return p.first == item; });
1425 if (res_it != result.end())
1426 ++res_it->second;
1427 else
1428 result.emplace_back(item, 1);
1429 }
1430 }
1431
1432 return result;
1433 }
1434
1435 // ============================================================================
1436 // Conversion
1437 // ============================================================================
1438
1447 template <typename Container>
1449 {
1451
1452 std::vector<T> result;
1453
1454 if constexpr (uni_functional_detail::is_stl_container_v<Container>)
1455 {
1456 result.reserve(c.size());
1457 for (const auto & item: c)
1458 result.push_back(item);
1459 }
1460 else
1461 {
1462 for (auto it = c.get_it(); it.has_curr(); it.next_ne())
1463 result.push_back(it.get_curr());
1464 }
1465 return result;
1466 }
1467
1468 // ============================================================================
1469 // Comparison
1470 // ============================================================================
1471
1484 template <typename Container1, typename Container2>
1485 [[nodiscard]] bool uni_equal(const Container1 & c1, const Container2 & c2)
1486 {
1487 constexpr bool c1_is_stl = uni_functional_detail::is_stl_container_v<Container1>;
1488 constexpr bool c2_is_stl = uni_functional_detail::is_stl_container_v<Container2>;
1489
1490 if constexpr (c1_is_stl and c2_is_stl)
1491 {
1492 // Both STL: use standard iterators
1493 auto it1 = c1.begin();
1494 auto it2 = c2.begin();
1495 for (; it1 != c1.end() and it2 != c2.end(); ++it1, ++it2)
1496 if (not (*it1 == *it2))
1497 return false;
1498 return it1 == c1.end() and it2 == c2.end();
1499 }
1500 else if constexpr (c1_is_stl and not c2_is_stl)
1501 {
1502 // c1 STL, c2 Aleph
1503 auto it1 = c1.begin();
1504 auto it2 = c2.get_it();
1505 for (; it1 != c1.end() and it2.has_curr(); ++it1, it2.next_ne())
1506 if (not (*it1 == it2.get_curr()))
1507 return false;
1508 return it1 == c1.end() and not it2.has_curr();
1509 }
1510 else if constexpr (not c1_is_stl and c2_is_stl)
1511 {
1512 // c1 Aleph, c2 STL
1513 auto it1 = c1.get_it();
1514 auto it2 = c2.begin();
1515 for (; it1.has_curr() and it2 != c2.end(); it1.next_ne(), ++it2)
1516 if (not (it1.get_curr() == *it2))
1517 return false;
1518 return not it1.has_curr() and it2 == c2.end();
1519 }
1520 else
1521 {
1522 // Both Aleph
1523 auto it1 = c1.get_it();
1524 auto it2 = c2.get_it();
1525 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
1526 if (not (it1.get_curr() == it2.get_curr()))
1527 return false;
1528 return not it1.has_curr() and not it2.has_curr();
1529 }
1530 }
1531
1544 template <typename Container1, typename Container2>
1546 {
1547 constexpr bool c1_is_stl = uni_functional_detail::is_stl_container_v<Container1>;
1548 constexpr bool c2_is_stl = uni_functional_detail::is_stl_container_v<Container2>;
1549
1550 if constexpr (c1_is_stl and c2_is_stl)
1551 {
1552 // Both STL: use standard iterators
1553 auto it1 = c1.begin();
1554 auto it2 = c2.begin();
1555 for (; it1 != c1.end() and it2 != c2.end(); ++it1, ++it2)
1556 {
1557 if (*it1 < *it2) return -1;
1558 if (*it2 < *it1) return 1;
1559 }
1560 if (it1 == c1.end() and it2 == c2.end()) return 0;
1561 return (it1 == c1.end()) ? -1 : 1;
1562 }
1563 else if constexpr (c1_is_stl and not c2_is_stl)
1564 {
1565 // c1 STL, c2 Aleph
1566 auto it1 = c1.begin();
1567 auto it2 = c2.get_it();
1568 for (; it1 != c1.end() and it2.has_curr(); ++it1, it2.next_ne())
1569 {
1570 if (*it1 < it2.get_curr()) return -1;
1571 if (it2.get_curr() < *it1) return 1;
1572 }
1573 if (it1 == c1.end() and not it2.has_curr()) return 0;
1574 return (it1 == c1.end()) ? -1 : 1;
1575 }
1576 else if constexpr (not c1_is_stl and c2_is_stl)
1577 {
1578 // c1 Aleph, c2 STL
1579 auto it1 = c1.get_it();
1580 auto it2 = c2.begin();
1581 for (; it1.has_curr() and it2 != c2.end(); it1.next_ne(), ++it2)
1582 {
1583 if (it1.get_curr() < *it2) return -1;
1584 if (*it2 < it1.get_curr()) return 1;
1585 }
1586 if (not it1.has_curr() and it2 == c2.end()) return 0;
1587 return (not it1.has_curr()) ? -1 : 1;
1588 }
1589 else
1590 {
1591 // Both Aleph
1592 auto it1 = c1.get_it();
1593 auto it2 = c2.get_it();
1594 for (; it1.has_curr() and it2.has_curr(); it1.next_ne(), it2.next_ne())
1595 {
1596 if (it1.get_curr() < it2.get_curr()) return -1;
1597 if (it2.get_curr() < it1.get_curr()) return 1;
1598 }
1599 if (not it1.has_curr() and not it2.has_curr()) return 0;
1600 return (not it1.has_curr()) ? -1 : 1;
1601 }
1602 }
1603} // end namespace Aleph
1604
1605# endif // AH_UNI_FUNCTIONAL_H
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
Freq_Node * pred
Predecessor node in level-order traversal.
typename container_value_type< std::decay_t< T > >::type value_t
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
T uni_reduce(T init, Op &&op, const Container &c)
Alias for uni_foldl.
auto uni_distinct(const Container &c)
Remove all duplicates (keeps first occurrence).
bool uni_none(Pred &&pred, const Container &c)
Check if no element satisfies predicate.
auto uni_filter(Pred &&pred, const Container &c)
Filter elements satisfying predicate.
auto uni_nth(size_t n, const Container &c)
Get n-th element.
int uni_compare(const Container1 &c1, const Container2 &c2)
Compare two containers lexicographically.
bool uni_equal(const Container1 &c1, const Container2 &c2)
Check equality of two containers.
std::vector< T > uni_scan_left(T init, Op &&op, const Container &c)
Scan left - fold with all intermediate results.
size_t uni_length(const Container &c)
Get container length.
auto uni_tally(const Container &c)
Count occurrences of each element (frequency count).
auto uni_flatten(const Container &c)
Flatten a container of containers.
auto uni_flat_map(Op &&op, const Container &c)
Flat map - map then flatten.
auto uni_drop_while(Pred &&pred, const Container &c)
Drop elements while predicate is true, return the rest.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
auto uni_product(const Container &c)
Product of all elements.
auto uni_find(Pred &&pred, const Container &c)
Find first element satisfying predicate.
bool uni_mem(const T &target, const Container &c)
Check if element exists in container (mem in ML).
auto uni_group_by(Key &&key, const Container &c)
Group elements by key function.
auto uni_filteri(Pred &&pred, const Container &c)
Filter with index (filteri in ML).
auto uni_map(Op &&op, const Container &c)
Map operation - transform each element.
auto uni_concat(const Container1 &c1, const Container2 &c2)
Concatenate two containers.
auto uni_last(const Container &c)
Get last element.
auto uni_min_max(const Container &c)
Get both min and max in a single pass.
auto uni_min(const Container &c)
Get minimum element.
size_t uni_count(Pred &&pred, const Container &c)
Count elements satisfying predicate.
static bool init
Definition hash-fct.C:47
void uni_for_each_indexed(Op &&op, const Container &c)
Apply operation to each element with index.
T uni_foldl(T init, Op &&op, const Container &c)
Left fold (foldl) - reduce from left to right.
auto uni_drop(size_t n, const Container &c)
Drop first n elements, return the rest.
auto uni_max(const Container &c)
Get maximum element.
void uni_for_each(Op &&op, const Container &c)
Apply operation to each element (for_each).
auto uni_find_mapi(Op &&op, const Container &c)
Find and map with index (find_mapi in ML).
auto uni_take_while(Pred &&pred, const Container &c)
Take elements while predicate is true.
bool uni_all(Pred &&pred, const Container &c)
Check if all elements satisfy predicate.
auto uni_first(const Container &c)
Get first element.
auto uni_partition(Pred &&pred, const Container &c)
Partition elements by predicate.
auto uni_to_vector(const Container &c)
Convert container to std::vector.
auto uni_mapi(Op &&op, const Container &c)
Map with index (mapi in ML).
bool uni_any(Pred &&pred, const Container &c)
Alias for uni_exists.
auto uni_take(size_t n, const Container &c)
Take first n elements.
auto uni_sum(const Container &c)
Sum all elements.
bool uni_exists(Pred &&pred, const Container &c)
Check if any element satisfies predicate.
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.
std::optional< size_t > uni_find_index(Pred &&pred, const Container &c)
Find index of first element satisfying predicate.
STL namespace.