Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_sort_utils.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
94#ifndef TPL_SORT_UTILS_H
95#define TPL_SORT_UTILS_H
96
97#include <ahUtils.H>
98#include <ahFunctional.H>
99#include <ah-concepts.H>
100#include <ah-errors.H>
101#include <tpl_arrayStack.H>
102#include <tpl_array.H>
103#include <tpl_dynArray.H>
104#include <tpl_dynDlist.H>
105#include <tpl_arrayHeap.H>
106#include <htlist.H>
107#include <limits>
108#include <type_traits>
109#include <utility>
110#include <cmath>
111#include <algorithm>
112
113namespace Aleph {
124extern size_t Insertion_Threshold;
125
132extern size_t Quicksort_Threshold;
133
140extern const int Not_Found;
141
162template <template <typename> class Container, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
163[[nodiscard]] bool is_sorted(const Container<T> &cont, const Compare &cmp = Compare())
164{
165 if (cont.is_empty())
166 return true;
167
168 typename Container<T>::Iterator it(cont);
169 const T &first = it.get_curr();
170 const T *prev = &first;
171 it.next_ne();
172 for (; it.has_curr(); it.next_ne())
173 {
174 const T &curr = it.get_curr();
175 if (cmp(curr, *prev))
176 return false;
177 prev = &curr;
178 }
179 return true;
180}
181
204template <template <typename> class Container, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
205[[nodiscard]] std::pair<bool, size_t> search_inversion(const Container<T> &cont,
206 const Compare &cmp = Compare())
207{
208 if (cont.is_empty())
209 return std::make_pair(true, 0);
210
211 typename Container<T>::Iterator it(cont);
212 const T &first = it.get_curr();
213 const T *prev = &first;
214 size_t i = 1;
215 it.next_ne();
216 for (; it.has_curr(); it.next_ne(), ++i)
217 {
218 const T &curr = it.get_curr();
219 if (cmp(curr, *prev))
220 return std::make_pair(false, i);
221 prev = &curr;
222 }
223
224 return std::make_pair(true, i);
225}
226
246template <template <typename> class Container, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
247[[nodiscard]] bool is_inversely_sorted(const Container<T> &cont, const Compare &cmp = Compare())
248{
249 if (cont.is_empty())
250 return true;
251
252 typename Container<T>::Iterator it(cont);
253 const T &first = it.get_curr();
254 const T *prev = &first;
255 it.next_ne();
256 for (; it.has_curr(); it.next_ne())
257 {
258 const T &curr = it.get_curr();
259 if (cmp(*prev, curr))
260 return false;
261 prev = &curr;
262 }
263 return true;
264}
265
288template <template <typename> class Container, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
289[[nodiscard]] std::pair<bool, size_t> test_sorted(const Container<T> &cont,
290 const Compare &cmp = Compare())
291{
292 if (cont.is_empty())
293 return {true, 0};
294
295 typename Container<T>::Iterator it(cont);
296 const T &first = it.get_curr();
297 const T *prev = &first;
298 size_t i = 1;
299 it.next_ne();
300 for (; it.has_curr(); it.next_ne(), ++i)
301 {
302 const T &curr = it.get_curr();
303 if (cmp(curr, *prev))
304 return {false, i};
305 prev = &curr;
306 }
307 return {true, i};
308}
309
343template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
344inline void selection_sort(T *a, const size_t n,
345 const Compare &cmp = Compare()) noexcept(noexcept(cmp(a[0], a[0])) &&
347{
348 if (n < 2)
349 return;
350
351 for (size_t i = 0, min, j; i < n - 1; ++i)
352 {
353 for (min = i, j = i + 1; j < n; ++j)
354 if (cmp(a[j], a[min]))
355 min = j;
356
357 if (cmp(a[min], a[i]))
358 std::swap(a[min], a[i]);
359 }
360}
361
384template <class Link, class Compare>
385inline Link *search_extreme(const Link &list, const Compare &cmp)
386{
389 typename Link::Iterator it(const_cast<Link &>(list));
390 Link *extreme = it.get_curr();
391
392 for (it.next(); it.has_curr(); it.next_ne())
393 {
394 Link *curr = it.get_curr();
395 if (cmp(curr, extreme))
396 extreme = curr;
397 }
398
399 return extreme;
400}
401
402template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
403inline const T *search_extreme(const DynList<T> &list, const Compare &cmp = Compare())
404{
405 if (list.is_empty())
406 return nullptr;
407
409 typename DynList<T>::Iterator it(list);
410 const T *extreme = &it.get_curr();
411 it.next_ne();
412 for (; it.has_curr(); it.next_ne())
413 {
414 const T &curr = it.get_curr();
415 if (cmp(curr, *extreme))
416 extreme = &curr;
417 }
418
419 return extreme;
420}
421
422template <class Compare>
423inline Dlink *search_extreme(const Dlink &list, const Compare &cmp = Compare())
424{
426}
427
428template <class Compare>
429inline Slinknc *search_extreme(const Slinknc &list, const Compare &cmp = Compare())
430{
432}
433
443template <class Compare>
444inline void selection_sort(Dlink &list, Compare cmp)
445{
446 Dlink aux;
447 while (not list.is_empty())
448 {
450 extreme->del(); // remove extreme from list
451 aux.append(extreme); // insert it ordered into aux
452 }
453
454 list.swap(&aux);
455}
456
466template <typename Tlink, template <class> class Tnode, typename T, class Compare>
468{
469 Compare cmp;
470
471public:
474 : cmp(cmp_fct)
475 { /* empty */
476 }
477
479 bool operator () (Tlink *l1, Tlink *l2) const
480 noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
481 std::declval<const T &>())))
482 {
483 auto *n1 = static_cast<Tnode<T> *>(l1);
484 auto *n2 = static_cast<Tnode<T> *>(l2);
485
486 assert(n1 == l1 and n2 == l2);
487
488 return cmp(n1->get_data(), n2->get_data());
489 }
490
492 bool operator () (Tlink *l, const T &x) const
493 noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
494 std::declval<const T &>())))
495 {
496 auto *n = static_cast<Tnode<T> *>(l);
497
498 assert(n == l);
499
500 return cmp(n->get_data(), x);
501 }
502};
503
507template <typename T, class Compare>
508struct Compare_Dnode : public Compare_Tnode<Dlink, Dnode, T, Compare>
509{
510 Compare_Dnode(const Compare &cmp = Compare()) noexcept(std::is_nothrow_copy_constructible_v<Compare>)
511 : Compare_Tnode<Dlink, Dnode, T, Compare>(cmp)
512 { /* empty */
513 }
514};
515
519template <typename T, class Compare>
521
529template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
534
556template <typename T, class Equal = Aleph::equal_to<T>>
557[[nodiscard]] inline long sequential_search(T *a, const T &x, const long l, const long r,
558 Equal eq = Equal())
559{
560 for (long i = l; i <= r; ++i)
561 if (eq(a[i], x))
562 return i;
563
564 return Not_Found;
565}
566
584template <typename T, class Equal = Aleph::equal_to<T>>
585inline long sequential_search(const DynArray<T> &a, const T &x, const long l, const long r,
586 Equal eq = Equal())
587{
588 for (long i = l; i <= r; ++i)
589 if (a.exist(i))
590 if (eq(a(i), x))
591 return i;
592
593 return Not_Found;
594}
595
608template <class Link, typename T, class Equal>
609Link *sequential_search(const Link &list, const T &x, Equal &eq)
610{
611 for (typename Link::Iterator it(const_cast<Link &>(list)); it.has_curr(); it.next_ne())
612 if (Link *curr = it.get_curr(); eq(curr, x))
613 return curr;
614
615 return nullptr;
616}
617
624template <typename T, class Equal = Aleph::equal_to<T>>
625Dlink *sequential_search(const Dlink &list, const T &x, Equal eq = Equal())
626{
630}
631
638template <typename T, class Equal = Aleph::equal_to<T>>
639Slinknc *sequential_search(const Slinknc &list, const T &x, Equal eq = Equal())
640{
644}
645
652template <typename T, class Equal = Aleph::equal_to<T>>
653inline Dnode<T> *sequential_search(Dnode<T> &list, const T &x, Equal &eq)
654{
658
659 return ret == nullptr ? nullptr : static_cast<Dnode<T> *>(ret);
660}
661
663template <typename T, class Equal = Aleph::equal_to<T>>
664inline Dnode<T> *sequential_search(Dnode<T> &list, const T &x, Equal &&eq = Equal())
665{
666 return sequential_search<T, Equal>(list, x, eq);
667}
668
675template <typename T, class Equal = Aleph::equal_to<T>>
676inline const Dnode<T> *sequential_search(const Dnode<T> &list, const T &x, Equal &eq)
677{
680 const auto &base = static_cast<const Dlink &>(list);
682
683 return ret == nullptr ? nullptr : static_cast<const Dnode<T> *>(ret);
684}
685
687template <typename T, class Equal = Aleph::equal_to<T>>
688inline const Dnode<T> *sequential_search(const Dnode<T> &list, const T &x, Equal &&eq = Equal())
689{
690 return sequential_search<T, Equal>(list, x, eq);
691}
692
699template <typename T, class Equal = Aleph::equal_to<T>>
700inline T *sequential_search(DynDlist<T> &list, const T &x, Equal eq = Equal())
701{
702 Dnode<T> *ret = sequential_search<T, Equal>(static_cast<Dnode<T> &>(list), x, eq);
703 return ret != nullptr ? &ret->get_data() : nullptr;
704}
705
712template <typename T, class Equal = Aleph::equal_to<T>>
713inline const T *sequential_search(const DynDlist<T> &list, const T &x, Equal eq = Equal())
714{
715 const Dnode<T> *ret = sequential_search<T, Equal>(static_cast<const Dnode<T> &>(list), x, eq);
716 return ret != nullptr ? &ret->get_data() : nullptr;
717}
718
725template <typename T, class Equal = Aleph::equal_to<T>>
726inline T *sequential_search(DynList<T> &list, const T &x, Equal eq = Equal())
727{
728 for (typename DynList<T>::Iterator it(list); it.has_curr(); it.next_ne())
729 {
730 T &curr = it.get_curr_ne();
731 if (eq(curr, x))
732 return &curr;
733 }
734 return nullptr;
735}
736
743template <typename T, class Equal = Aleph::equal_to<T>>
744inline const T *sequential_search(const DynList<T> &list, const T &x, Equal eq = Equal())
745{
746 for (typename DynList<T>::Iterator it(list); it.has_curr(); it.next_ne())
747 {
748 const T &curr = it.get_curr_ne();
749 if (eq(curr, x))
750 return &curr;
751 }
752 return nullptr;
753}
754
769template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
770inline long search_extreme(T *a, const long l, const long r, const Compare &cmp = Compare())
771{
772 long extreme_index = l;
773 for (long i = l + 1; i <= r; ++i)
774 if (cmp(a[i], a[extreme_index])) // is there a new minimum?
775 extreme_index = i; // yes
776
777 return extreme_index;
778}
779
784template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
785inline long search_min(T *a, const long l, const long r, const Compare &cmp = Compare())
786{
787 return search_extreme<T, Compare>(a, l, r, cmp);
788}
789
794template <typename T, StrictWeakOrder<T> Compare = Aleph::greater<T>>
795inline long search_max(T *a, const long l, const long r, const Compare &cmp = Compare())
796{
797 return search_extreme<T, Compare>(a, l, r, cmp);
798}
799
803template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
804inline Dnode<T> *search_extreme(const Dnode<T> &list, const Compare &cmp = Compare())
805{
808 const_cast<Dlink &>(static_cast<const Dlink &>(list)), cmp_dnode);
809
810 return static_cast<Dnode<T> *>(ret);
811}
812
823template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
824inline T *search_extreme(DynDlist<T> &list, const Compare &cmp = Compare())
825{
826 const auto &base = static_cast<const Dnode<T> &>(list);
827 Dnode<T> *ret = search_extreme<T, Compare>(const_cast<Dnode<T> &>(base), cmp);
828
829 return ret != nullptr ? &(ret->get_data()) : nullptr;
830}
831
833template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
834inline const T *search_extreme(const DynDlist<T> &list, const Compare &cmp = Compare())
835{
836 const auto &base = static_cast<const Dnode<T> &>(list);
837 Dnode<T> *ret = search_extreme<T, Compare>(const_cast<Dnode<T> &>(base), cmp);
838
839 return ret != nullptr ? &(ret->get_data()) : nullptr;
840}
841
847template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
848inline T *search_extreme(DynList<T> &list, const Compare &cmp = Compare())
849{
850 if (list.is_empty())
851 return nullptr;
852
853 typename DynList<T>::Iterator it(list);
854 T *extreme = &it.get_curr_ne();
855 it.next_ne();
856 for (; it.has_curr(); it.next_ne())
857 {
858 T &curr = it.get_curr_ne();
859 if (cmp(curr, *extreme))
860 extreme = &curr;
861 }
862
863 return extreme;
864}
865
871template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
872inline T *search_min(DynDlist<T> &list, const Compare &cmp = Compare())
873{
874 return search_extreme<T, Compare>(list, cmp);
875}
876
878template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
879inline const T *search_min(const DynDlist<T> &list, const Compare &cmp = Compare())
880{
881 return search_extreme<T, Compare>(list, cmp);
882}
883
889template <typename T, StrictWeakOrder<T> Compare = Aleph::greater<T>>
890inline T *search_max(DynDlist<T> &list, const Compare &cmp = Compare())
891{
892 return search_extreme<T, Compare>(list, cmp);
893}
894
896template <typename T, StrictWeakOrder<T> Compare = Aleph::greater<T>>
897inline const T *search_max(const DynDlist<T> &list, const Compare &cmp = Compare())
898{
899 return search_extreme<T, Compare>(list, cmp);
900}
901
937template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
938inline void insertion_sort(T *a, const long l, const long r, const Compare &cmp = Compare()) noexcept(
939 noexcept(cmp(std::declval<T &>(), std::declval<T &>())) and
941{
942 if (l >= r)
943 return;
944 for (long i = l + 1, j; i <= r; ++i)
945 {
946 T tmp = std::move(a[i]); // store a[i], since it will be overwritten
947 for (j = i; j > l and cmp(tmp, a[j - 1]); --j)
948 a[j] = std::move(a[j - 1]); // shift to the right
949
950 a[j] = std::move(tmp); // insert tmp into the gap
951 }
952}
953
967template <class Compare>
968inline void insert_sorted(Dlink &list, Dlink *p, const Compare &cmp)
969{
970 Dlink::Iterator it(list);
971 while (it.has_curr() and cmp(it.get_curr(), p))
972 it.next_ne();
973
974 if (it.has_curr())
975 it.get_curr()->append(p); // insert before current
976 else
977 list.append(p);
978}
979
980template <class Compare>
981inline void insert_sorted(HTList &list, Slinknc *p, const Compare &cmp)
982{
983 // Handle empty list case
984 if (list.is_empty())
985 {
986 list.insert(p);
987 return;
988 }
989
990 if (Slinknc *first = list.get_first(); cmp(p, first) or not cmp(first, p)) // p <= first?
991 {
992 list.insert(p);
993 return;
994 }
995
996 if (Slinknc *last = list.get_last(); cmp(last, p) or not cmp(p, last)) // p >= last?
997 {
998 list.append(p);
999 return;
1000 }
1001
1002 Slinknc *prev = list.get_first();
1003 HTList::Iterator it(list);
1004 for (it.next(); it.has_curr(); it.next_ne())
1005 {
1006 Slinknc *curr = it.get_curr();
1007 if (cmp(p, curr)) // p < curr
1008 {
1009 prev->insert(p);
1010 return;
1011 }
1012 prev = curr;
1013 }
1014 ah_logic_error() << "insert_sorted(): reached end of list traversal without inserting";
1015}
1016
1024template <class ListType, class Compare>
1025inline void list_insertion_sort(ListType &list, const Compare &cmp)
1026{
1027 if (list.is_empty())
1028 return;
1029
1030 ListType aux;
1031 aux.append(list.remove_first());
1032 while (not list.is_empty())
1033 insert_sorted<Compare>(aux, list.remove_first(), cmp);
1034
1035 list.swap(aux);
1036}
1037
1045template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1046inline void insertion_sort(DynList<T> &l, const Compare &cmp = Compare())
1047{
1049 Cmp c(cmp);
1051}
1052
1064template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1065inline DynList<T> insertion_sort(DynList<T> &&l, const Compare &cmp = Compare())
1066{
1068 Cmp c(cmp);
1070 return std::move(l);
1071}
1072
1080template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1081inline void insertion_sort(Dnode<T> &list, const Compare &cmp = Compare())
1082{
1084 Cmp c(cmp);
1086}
1087
1116template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1117inline void merge(T *a, const long l, const long m, const long r, Array<T> &buf,
1118 const Compare &cmp = Compare())
1119{
1120 const long s = r - l + 1;
1121
1122 // Grow the buffer's logical size once instead of multiple push_back
1123 // calls. Array::putn() is additive (adds to the current logical size)
1124 // and only reallocates past the current capacity, so once `buf` has
1125 // grown to the largest `s` needed across the whole sort, every further
1126 // call here is O(1): just bumping the size counter back down would be
1127 // wrong (contents get fully overwritten below regardless), so the
1128 // buffer is deliberately never shrunk between merges.
1129 if (buf.size() < static_cast<size_t>(s))
1130 buf.putn(static_cast<size_t>(s) - buf.size());
1131
1132 // Copy left partition [l..m] to buffer start
1133 long buf_idx = 0;
1134 for (long i = l; i <= m; ++i)
1135 buf[buf_idx++] = std::move(a[i]);
1136
1137 // Copy right partition [m+1..r] in reverse to buffer end
1138 buf_idx = s - 1;
1139 for (long j = m + 1; j <= r; ++j)
1140 buf[buf_idx--] = std::move(a[j]);
1141
1142 // Merge from both ends toward the middle
1143 long i = 0;
1144 long j = s - 1;
1145
1146 for (long k = l; k <= r; ++k)
1147 if (not cmp(buf[j], buf[i]))
1148 a[k] = std::move(buf[i++]);
1149 else
1150 a[k] = std::move(buf[j--]);
1151}
1152
1167template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1168inline void merge(T *a, const long l, const long m, const long r, const Compare &cmp = Compare())
1169{
1170 Array<T> buf(static_cast<size_t>(r - l + 1));
1171 merge(a, l, m, r, buf, cmp);
1172}
1173
1181template <typename T, class Compare>
1182void mergesort(T *a, const long l, const long r, Array<T> &buf, Compare cmp)
1183{
1184 if (l >= r)
1185 return;
1186
1187 const long m = l + (r - l) / 2;
1188
1189 mergesort<T, Compare>(a, l, m, buf, cmp);
1190 mergesort<T, Compare>(a, m + 1, r, buf, cmp);
1191
1192 if (not cmp(a[m + 1], a[m]))
1193 return;
1194
1195 merge<T, Compare>(a, l, m, r, buf, cmp);
1196}
1197
1232template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1233inline void mergesort(T *a, const long l, const long r, const Compare &cmp = Compare())
1234{
1235 if (l >= r)
1236 return;
1237
1238 Array<T> buf(static_cast<size_t>(r - l + 1));
1239 mergesort(a, l, r, buf, cmp);
1240}
1241
1267template <typename Tlist, class Compare>
1268inline void merge_lists(Tlist &l1, Tlist &l2, Tlist &result, const Compare &cmp = Compare())
1269{
1270 assert(result.is_empty());
1271
1272 while (not l1.is_empty() and not l2.is_empty())
1273 if (cmp(l1.get_first_ne(), l2.get_first_ne()))
1274 result.append(l1.remove_first_ne());
1275 else
1276 result.append(l2.remove_first_ne());
1277
1278 if (l1.is_empty())
1279 result.concat_list(l2);
1280 else
1281 result.concat_list(l1);
1282
1284}
1285
1295template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1296inline void merge_lists(Dnode<T> &l1, Dnode<T> &l2, Dnode<T> &result, const Compare &cmp = Compare())
1297{
1299}
1300
1331template <typename Tlist, class Compare>
1332inline void mergesort(Tlist &list, const Compare &cmp = Compare())
1333{
1334 if (list.is_unitarian_or_empty())
1335 return;
1336
1337 Tlist l, r;
1338 list.split_list_ne(l, r); // split into two lists
1339
1342
1343 merge_lists<Tlist, Compare>(l, r, list, cmp); // merge them
1344}
1345
1364template <template <typename> class Tlist, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1365inline void mergeinsertsort(Tlist<T> &list, const Compare &cmp = Compare(),
1366 const size_t lsz = Aleph::Insertion_Threshold)
1367{
1368 if (list.is_unitarian_or_empty())
1369 return;
1370
1371 if (list.size() <= lsz)
1372 {
1374 return;
1375 }
1376
1377 Tlist<T> l, r;
1378 list.split_list(l, r); // split into two lists
1379
1382
1383 merge_lists<Tlist<T>, Compare>(l, r, list, cmp); // merge them
1384}
1385
1394template <template <typename> class Tlist, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1395inline void mergesort(Tlist<T> &list, const Compare &cmp = Compare())
1396{
1397 if (list.is_unitarian_or_empty())
1398 return;
1399
1400 Tlist<T> l, r;
1401 list.split_list(l, r); // split into two lists
1402
1405
1406 merge_lists<Tlist<T>, Compare>(l, r, list, cmp); // merge them
1407}
1408
1416template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1417inline void mergesort(Dnode<T> &list, const Compare &cmp = Compare())
1418{
1420}
1421
1429template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1430inline void mergesort(DynDlist<T> &list, const Compare &cmp = Compare())
1431{
1433}
1434
1442template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1443inline void mergesort(DynList<T> &list, const Compare &cmp = Compare())
1444{
1445 mergesort<DynList<T>, Compare>(list, cmp);
1446}
1447
1458template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1459inline DynList<T> mergesort(DynList<T> &&list, const Compare &cmp = Compare())
1460{
1461 mergesort<DynList<T>, Compare>(list, cmp);
1462 return std::move(list);
1463}
1464
1472template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1473inline long select_pivot(T *a, long l, long r,
1474 const Compare &cmp = Compare()) noexcept(noexcept(cmp(a[0], a[0])) &&
1476
1494template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1495inline long partition(T *a, const long l, const long r,
1496 const Compare &cmp = Compare()) noexcept(noexcept(cmp(a[0], a[0])) &&
1498{
1499 if (l >= r)
1500 return l;
1501
1502 const long p = select_pivot<T, Compare>(a, l, r, cmp);
1504 std::swap(a[p], a[r]);
1505
1506 // Note: pivot stays at a[r] throughout the loop - no copy needed
1507 // This saves a potentially expensive copy for large T types
1508 long i = l - 1; // index first element to the left > pivot
1509 long j = r; // index first element to the right < pivot
1510 while (true)
1511 {
1512 // advance while a[i] < pivot (pivot is at a[r])
1513 while (cmp(a[++i], a[r]))
1514 ;
1515
1516 while (cmp(a[r], a[--j])) // advance while pivot < a[j]
1517 if (j == l) // Has the left edge been reached?
1518 break; // yes ==> the iteration must be finished
1519
1520 if (i >= j)
1521 break;
1522
1524 std::swap(a[i], a[j]); // Eliminate inversion
1525 }
1526
1528 std::swap(a[i], a[r]); // Place pivot in its final position
1529
1530 return i;
1531}
1532
1551template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1552inline void quicksort_rec(T *a, const long l, const long r, const Compare &cmp = Compare())
1553{
1554 if (l >= r)
1555 return;
1556
1557 const long pivot = partition<T, Compare>(a, l, r, cmp);
1558
1561}
1562
1576template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1577inline void quicksort_no_tail(T *a, long l, long r, const Compare &cmp = Compare())
1578{
1579 while (l < r)
1580 {
1581 const long pivot = partition<T, Compare>(a, l, r, cmp);
1582 const long left_size = pivot - l;
1583 const long right_size = r - pivot;
1584
1585 // Recurse on the smaller partition to ensure O(log n) stack depth.
1586 if (left_size < right_size)
1587 {
1589 l = pivot + 1; // tail recurse on the right by looping
1590 }
1591 else
1592 {
1594 r = pivot - 1; // tail recurse on the left by looping
1595 }
1596 }
1597}
1598
1615template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1616inline void quicksort_rec_min(T *a, const long l, const long r, const Compare &cmp = Compare())
1617{
1618 if (r <= l)
1619 return;
1620
1621 if (const long pivot = partition<T, Compare>(a, l, r, cmp); pivot - l < r - pivot)
1622 // which partition is smaller?
1623 { // left partition is smaller
1626 }
1627 else
1628 { // right partition is smaller
1631 }
1632}
1633
1634template <typename T, StrictWeakOrder<T> Compare>
1635inline long select_pivot(T *a, const long l, const long r,
1636 const Compare &cmp) noexcept(noexcept(cmp(a[0], a[0])) &&
1637 std::is_nothrow_swappable_v<T>)
1638{
1639 const long m = l + (r - l) / 2;
1640
1643 if (cmp(a[r], a[l]))
1644 std::swap(a[l], a[r]);
1646 if (cmp(a[m], a[l]))
1647 std::swap(a[m], a[l]);
1649 if (cmp(a[r], a[m]))
1650 std::swap(a[r], a[m]);
1651
1652 return m;
1653}
1654
1656inline size_t introsort_depth_limit(size_t n) noexcept;
1657
1695template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1696inline void quicksort(T *a, const long l, const long r, const Compare &cmp = Compare())
1697{
1698 if (r - l < Quicksort_Threshold)
1699 {
1700 insertion_sort(a, l, r, cmp);
1701 return;
1702 }
1703
1704 const auto n = static_cast<size_t>(r - l + 1);
1705
1706 typedef std::pair<long, long> Partition;
1709 stack.push(Partition(l, r));
1710
1711 while (stack.size() > 0)
1712 {
1713 auto [fst, snd] = stack.pop();
1714 const long diff = snd - fst;
1715
1716 if (diff <= 0)
1717 continue;
1718
1720 {
1721 insertion_sort(a, fst, snd, cmp);
1722 continue;
1723 }
1724
1725 if (const long pivot = partition<T, Compare>(a, fst, snd, cmp); pivot - fst < snd - pivot)
1726 // which one is smaller?
1727 { // the left partition is smaller
1728 stack.push(Partition(pivot + 1, snd));
1729 stack.push(Partition(fst, pivot - 1));
1730 }
1731 else
1732 { // the right partition is smaller
1733 stack.push(Partition(fst, pivot - 1));
1734 stack.push(Partition(pivot + 1, snd));
1735 }
1736 }
1737}
1738
1747inline size_t introsort_depth_limit(size_t n) noexcept
1748{
1749 size_t depth = 0;
1750 while (n > 1)
1751 {
1752 n >>= 1;
1753 ++depth;
1754 }
1755 return depth * 2;
1756}
1757
1762template <typename T, class Compare>
1763void introsort_loop(T *a, long l, long r, size_t depth_limit, const Compare &cmp)
1764{
1765 while (r - l >= static_cast<long>(Quicksort_Threshold))
1766 {
1767 if (depth_limit == 0) [[unlikely]]
1768 {
1769 // Depth limit reached: switch to heapsort for guaranteed O(n log n)
1770 faster_heapsort(a + l, static_cast<size_t>(r - l + 1), cmp);
1771 return;
1772 }
1773
1774 --depth_limit;
1775
1776 // Median-of-three pivot selection
1777 // Recurse on smaller partition, iterate on larger (tail call elimination)
1778 if (const long pivot = partition<T, Compare>(a, l, r, cmp); pivot - l < r - pivot)
1779 {
1781 l = pivot + 1;
1782 }
1783 else
1784 {
1786 r = pivot - 1;
1787 }
1788 }
1789}
1790
1838template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1839inline void introsort(T *a, const long l, const long r, const Compare &cmp = Compare())
1840{
1841 if (r <= l)
1842 return;
1843
1844 const auto n = static_cast<size_t>(r - l + 1);
1845
1846 if (n < Quicksort_Threshold)
1847 {
1848 insertion_sort(a, l, r, cmp);
1849 return;
1850 }
1851
1854
1855 // Final insertion sort pass for small unsorted subarrays
1856 insertion_sort(a, l, r, cmp);
1857}
1858
1870template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1871inline void introsort(T *a, const size_t n, const Compare &cmp = Compare())
1872{
1873 if (n <= 1)
1874 return;
1875 introsort(a, 0L, static_cast<long>(n - 1), cmp);
1876}
1877
1905template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1906inline void introsort(T *begin, T *end, const Compare &cmp = Compare())
1907{
1908 if (begin >= end)
1909 return;
1910 const auto n = static_cast<size_t>(end - begin);
1911 introsort(begin, n, cmp);
1912}
1913
1921template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1922inline void introsort(Array<T> &arr, const Compare &cmp = Compare())
1923{
1924 const size_t n = arr.size();
1925 if (n <= 1)
1926 return;
1927 // Array uses contiguous memory via MemArray, so we can use pointer arithmetic
1928 introsort(&arr(0), n, cmp);
1929}
1930
1944template <class Compare>
1945void quicksort(Dlink &list, const Compare &cmp = Compare())
1946{
1947 if (list.is_unitarian_or_empty())
1948 return;
1949
1950 Dlink *pivot = list.remove_next();
1951 Dlink smaller, bigger; // lists of smaller and larger than pivot
1952
1953 while (not list.is_empty())
1954 if (Dlink *p = list.remove_next(); cmp(p, pivot))
1955 smaller.append(p);
1956 else
1957 bigger.append(p);
1958
1960 quicksort<Compare>(smaller, cmp);
1961
1962 list.concat_list(&smaller); // restore sorted lists into list
1963 list.append(pivot);
1964 list.concat_list(&bigger);
1965}
1966
1988template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
1989void quicksort(Dnode<T> &list, const Compare &cmp = Compare())
1990{
1992}
1993
1998template <typename T, class Compare>
1999void quicksort(HTList &list, const Compare &cmp = Compare())
2000{
2001 if (list.is_unitarian_or_empty())
2002 return;
2003
2004 auto *pivot = static_cast<Snodenc<T> *>(list.remove_first_ne());
2005 HTList smaller, bigger; // lists of smaller and larger than pivot
2006
2007 while (not list.is_empty())
2008 if (auto *p = static_cast<Snodenc<T> *>(list.remove_first_ne());
2009 cmp(p->get_data(), pivot->get_data()))
2010 smaller.append(p);
2011 else
2012 bigger.append(p);
2013
2015 quicksort<T, Compare>(smaller, cmp);
2016
2017 list.concat_list(smaller); // restore sorted lists into list
2018 list.append(pivot);
2019 list.concat_list(bigger);
2020}
2021
2026template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2027void quicksort(DynList<T> &list, const Compare &cmp = Compare())
2028{
2029 quicksort<T, Compare>(static_cast<HTList &>(list), cmp);
2030}
2031
2070template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2071inline void quicksort_insertion(T *a, const long l, const long r, const Compare &cmp = Compare())
2072{
2073 if (r <= l)
2074 return;
2075
2076 const long pivot = partition<T, Compare>(a, l, r, cmp);
2077
2078 const long l_size = pivot - l; // left partition size
2079 const long r_size = r - pivot; // right partition size
2080 bool left_done = false; // true if left partition is sorted
2081 bool right_done = false; // true if der partition is sorted
2082
2084 { // sort left partition by insertion
2086 left_done = true;
2087 }
2088
2090 { // sort der partition by insertion
2092 right_done = true;
2093 }
2094
2096 return; // both partitions sorted by insertion
2097
2098 if (left_done) // left partition sorted by insertion?
2099 { // Yeah; It only remains to recursively sort the right partition
2101 return;
2102 }
2103
2104 if (right_done) // der partition sorted by insertion?
2105 { // Yeah; It only remains to recursively sort the left partition
2107 return;
2108 }
2109
2110 // here, both partitions were not insertion sorted
2111 if (l_size < r_size) // sort smallest partition first
2112 { // smaller left partition
2115 }
2116 else
2117 { // smaller right partition
2120 }
2121}
2122
2156template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2157inline long random_search(T *a, const T &x, const long l, const long r,
2158 const Compare &cmp = Compare())
2159{
2160 if (l > r)
2161 return Not_Found;
2162
2163 const long pivot = partition<T, Compare>(a, l, r, cmp);
2164
2165 if (cmp(x, a[pivot]))
2166 return random_search<T, Compare>(a, x, l, pivot - 1, cmp);
2167 if (cmp(a[pivot], x))
2168 return random_search<T, Compare>(a, x, pivot + 1, r, cmp);
2169
2170 return pivot; // element found at index x
2171}
2172
2181template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2182inline long random_search(DynArray<T> &a, const T &x, const long l, const long r,
2183 const Compare &cmp = Compare())
2184{
2185 if (l > r)
2186 return Not_Found;
2187
2188 const long pivot = partition(a, l, r, cmp);
2189
2190 if (cmp(x, a(pivot)))
2191 return random_search<T, Compare>(a, x, l, pivot - 1, cmp);
2192 if (cmp(a(pivot), x))
2193 return random_search<T, Compare>(a, x, pivot + 1, r, cmp);
2194
2195 return pivot; // element found at index x
2196}
2197
2224template <typename T, class Compare>
2225inline Dnode<T> *dlink_random_search(Dlink &list, const T &x, const Compare &cmp = Compare())
2226{
2227 if (list.is_empty())
2228 return nullptr;
2229
2232 Dnode<T> item(x);
2233 Dnode<T> *item_ptr = &item; // pointer to the cell containing x
2234
2235 Dlink smaller; // list of those smaller than pivot
2236 Dlink bigger; // list of those greater than pivot
2237
2238 auto *pivot = static_cast<Dnode<T> *>(list.remove_next());
2239
2240 while (not list.is_empty())
2241 if (Dlink *p = list.remove_next(); cmp(p, pivot))
2242 smaller.append(p);
2243 else
2244 bigger.append(p);
2245
2246 Dnode<T> *ret_val = nullptr;
2247 if (cmp(item_ptr, pivot))
2249 else if (cmp(pivot, item_ptr))
2251 else
2252 ret_val = pivot;
2253
2254 assert(list.is_empty());
2255
2256 list.swap(&smaller);
2257 list.append(pivot);
2258 list.concat_list(&bigger);
2259
2260 return ret_val;
2261}
2262
2292template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2293Dnode<T> *random_search(Dlink &list, const T &x, const Compare &cmp = Compare())
2294{
2296}
2297
2327template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2328inline T *random_search(DynDlist<T> &list, const T &x, const Compare &cmp = Compare())
2329{
2331
2332 return p == nullptr ? nullptr : &(p->get_data());
2333}
2334
2335template <typename T, class Compare>
2336static inline std::pair<long, long> partition_three_way(T *a, long l, long r, const Compare &cmp)
2337{
2338 const long pivot_idx = select_pivot<T, Compare>(a, l, r, cmp);
2340 std::swap(a[pivot_idx], a[r]);
2341
2342 long lt = l;
2343 long gt = r - 1;
2344 long current = l;
2345
2346 while (current <= gt)
2347 if (cmp(a[current], a[r]))
2348 {
2349 std::swap(a[lt], a[current]);
2350 lt++;
2351 current++;
2352 }
2353 else if (cmp(a[r], a[current]))
2354 {
2355 std::swap(a[current], a[gt]);
2356 gt--;
2357 }
2358 else
2359 current++;
2360
2362 std::swap(a[current], a[r]);
2363 return {lt, current};
2364}
2365
2366template <class Container, typename T, class Compare>
2367static inline std::pair<long, long> partition_three_way_op(Container &a, long l, long r,
2368 const Compare &cmp)
2369{
2370 const long pivot_idx = select_pivot_op<T, Compare>(a, l, r, cmp);
2372 std::swap(a(pivot_idx), a(r));
2373
2374 long lt = l;
2375 long gt = r - 1;
2376 long current = l;
2377
2378 while (current <= gt)
2379 if (cmp(a(current), a(r)))
2380 {
2381 std::swap(a(lt), a(current));
2382 lt++;
2383 current++;
2384 }
2385 else if (cmp(a(r), a(current)))
2386 {
2387 std::swap(a(current), a(gt));
2388 gt--;
2389 }
2390 else
2391 current++;
2392
2394 std::swap(a(current), a(r));
2395 return {lt, current};
2396}
2397
2398template <typename T, class Compare>
2399static inline const T &__random_select(T *a, const long i, long l, long r, const Compare &cmp)
2400{
2401 while (l < r)
2402 {
2403 if (r - l < 10)
2404 {
2406 return a[i];
2407 }
2408
2409 const auto [lt, current] = partition_three_way<T, Compare>(a, l, r, cmp);
2410
2411 if (i >= lt and i <= current)
2412 return a[i];
2413
2414 if (i < lt)
2415 r = lt - 1;
2416 else
2417 l = current + 1;
2418 }
2419
2420 return a[l];
2421}
2422
2431template <typename T, class Compare, typename Container>
2432inline long select_pivot_op_impl(const Container &a, const long l, const long r, const Compare &cmp)
2433{
2434 assert(l <= r);
2435
2436 if (r - l <= 5)
2437 return r;
2438
2439 const long m = l + (r - l) / 2; // central element
2440
2441 // Access array entries only once
2442 const T &la = a(l);
2443 const T &ra = a(r);
2444 const T &ma = a(m);
2445
2447
2448 if (med_ptr == &la)
2449 return l;
2450
2451 if (med_ptr == &ma)
2452 return m;
2453
2454 assert(med_ptr == &ra);
2455
2456 return r;
2457}
2458
2464template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2465inline long select_pivot_op(const DynArray<T> &a, const long l, const long r,
2466 const Compare &cmp = Compare())
2467{
2469}
2470
2476template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2477inline long select_pivot_op(const Array<T> &a, const long l, const long r,
2478 const Compare &cmp = Compare())
2479{
2481}
2482
2491template <typename T, class Compare, typename Container>
2492inline long partition_op_impl(Container &a, const long l, const long r, const Compare &cmp)
2493{
2494 if (l == r)
2495 return l;
2496
2497 long i = l - 1;
2498 long j = r;
2499
2500 // Move the selected pivot to position r
2501 if (const long pivot_idx = select_pivot_op<T, Compare>(a, l, r, cmp); pivot_idx != r)
2503 std::swap(a(pivot_idx), a(r));
2504
2505 // Now pivot value is at a(r) - use direct access instead of reference
2506 while (true)
2507 {
2508 while (cmp(a(++i), a(r)))
2509 ;
2510
2511 while (cmp(a(r), a(--j)))
2512 if (j == l)
2513 break;
2514
2515 if (i >= j)
2516 break;
2517
2518 std::swap(a(i), a(j));
2519 }
2520
2522 std::swap(a(i), a(r));
2523
2524 return i;
2525}
2526
2534template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2535inline long partition_op(DynArray<T> &a, const long l, const long r, const Compare &cmp = Compare())
2536{
2537 return partition_op_impl<T, Compare>(a, l, r, cmp);
2538}
2539
2547template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2548inline long partition_op(Array<T> &a, const long l, const long r, const Compare &cmp = Compare())
2549{
2550 return partition_op_impl<T, Compare>(a, l, r, cmp);
2551}
2552
2553template <typename Container, class Compare>
2554static inline void insertion_sort_range(Container &a, long l, long r, const Compare &cmp)
2555{
2556 using T = typename Container::Item_Type;
2557 for (long p = l + 1; p <= r; ++p)
2558 {
2559 T key = std::move(a(p));
2560 long j = p - 1;
2561 while (j >= l and cmp(key, a(j)))
2562 {
2563 a(j + 1) = std::move(a(j));
2564 j--;
2565 }
2566 a(j + 1) = std::move(key);
2567 }
2568}
2569
2570template <typename T, class Compare>
2571static inline const T &__random_select(DynArray<T> &a, const long i, long l, long r,
2572 const Compare &cmp = Compare())
2573{
2574 assert(i >= l and i <= r);
2575
2576 while (l < r)
2577 {
2578 if (r - l < 10)
2579 {
2581 return a(i);
2582 }
2583
2584 const auto [lt, current] = partition_three_way_op<DynArray<T>, T, Compare>(a, l, r, cmp);
2585
2586 if (i >= lt and i <= current)
2587 return a(i);
2588
2589 if (i < lt)
2590 r = lt - 1;
2591 else
2592 l = current + 1;
2593 }
2594
2595 return a(l);
2596}
2597
2598template <typename T, class Compare>
2599static inline const T &__random_select(Array<T> &a, const long i, long l, long r,
2600 const Compare &cmp = Compare())
2601{
2602 assert(i >= l and i <= r);
2603
2604 while (l < r)
2605 {
2606 if (r - l < 10)
2607 {
2609 return a(i);
2610 }
2611
2612 const auto [lt, current] = partition_three_way_op<Array<T>, T, Compare>(a, l, r, cmp);
2613
2614 if (i >= lt and i <= current)
2615 return a(i);
2616
2617 if (i < lt)
2618 r = lt - 1;
2619 else
2620 l = current + 1;
2621 }
2622
2623 return a(l);
2624}
2625
2633template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2634const T &random_select(DynArray<T> &a, const long i, const Compare &cmp = Compare())
2635{
2636 const auto n_sz = a.size();
2637 ah_out_of_range_error_if(n_sz == 0 or i < 0) << "index out of range";
2638
2639 const long n = static_cast<long>(n_sz) - 1;
2640 ah_out_of_range_error_if(i > n) << "index out of range";
2641
2642 return __random_select<T, Compare>(a, i, 0, n, cmp);
2643}
2644
2652template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2653const T &random_select(Array<T> &a, const long i, const Compare &cmp = Compare())
2654{
2655 const auto n_sz = a.size();
2656 ah_out_of_range_error_if(n_sz == 0 or i < 0) << "index out of range";
2657
2658 const long n = static_cast<long>(n_sz) - 1;
2659 ah_out_of_range_error_if(i > n) << "index out of range";
2660
2661 return __random_select<T, Compare>(a, i, 0, n, cmp);
2662}
2663
2704template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2705const T &random_select(T *a, const long i, const long n, const Compare &cmp = Compare())
2706{
2707 ah_invalid_argument_if(a == nullptr and n > 0) << "null pointer";
2708
2709 ah_out_of_range_error_if(n <= 0 or i < 0 or i >= n) << "index out of range";
2710
2711 return __random_select<T, Compare>(a, i, 0, n - 1, cmp);
2712}
2713
2721template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2722const T &random_select(T *a, const long i, const long n, Compare &&cmp = Compare())
2723{
2724 return random_select<T, Compare>(a, i, n, cmp);
2725}
2726
2749template <class Compare>
2750Dlink *dlink_random_select(Dlink &list, const size_t i, const Compare &cmp = Compare())
2751{
2752 if (list.is_empty())
2753 return nullptr;
2754
2755 Dlink smaller; // list of minors than pivot
2756 Dlink bigger; // list of majors than pivot
2757
2758 size_t smaller_count = 0, // number of elements in smaller
2759 bigger_count = 0; // number of elements in bigger
2760
2761 Dlink *pivot = list.remove_next();
2762
2763 while (not list.is_empty())
2764 if (Dlink *p = list.remove_next(); cmp(p, pivot)) // p < pivot?
2765 {
2766 smaller.append(p);
2767 ++smaller_count;
2768 }
2769 else
2770 {
2771 bigger.append(p);
2772 ++bigger_count;
2773 }
2774
2775 if (i >= smaller_count + bigger_count + 1)
2776 {
2777 list.concat_list(&smaller);
2778 list.append(pivot);
2779 list.concat_list(&bigger);
2780 ah_out_of_range_error() << "index of selection greater than list's size";
2781 }
2782
2783 Dlink *ret_val = nullptr;
2784 if (i == smaller_count)
2785 ret_val = pivot;
2786 else if (i < smaller_count)
2788 else
2790
2791 list.concat_list(&smaller);
2792 list.append(pivot);
2793 list.concat_list(&bigger);
2794
2795 return ret_val;
2796}
2797
2823template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2824Dnode<T> *random_select(Dlink &list, const size_t i, const Compare &cmp = Compare())
2825{
2826 return static_cast<Dnode<T> *>(dlink_random_select<Compare_Dnode<T, Compare>>(list, i, cmp));
2827}
2828
2854template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2855T *random_select(DynDlist<T> &list, const size_t i, const Compare &cmp = Compare())
2856{
2858
2859 auto *p = static_cast<Dnode<T> *>(link);
2860
2861 return p != nullptr ? &(p->get_data()) : nullptr;
2862}
2863
2883template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2884inline void selection_sort(DynArray<T> &a, const Compare &cmp = Compare())
2885{
2886 const long n = static_cast<long>(a.size());
2887 if (n < 2)
2888 return;
2889
2890 for (long i = 0; i < n - 1; ++i)
2891 {
2892 long min = i;
2893
2894 for (long j = i + 1; j < n; ++j)
2895 if (cmp(a(j), a(min)))
2896 min = j;
2897
2898 if (cmp(a(min), a(i)))
2899 std::swap(a(min), a(i));
2900 }
2901}
2902
2933template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2934inline void bubble_sort(DynArray<T> &a, const Compare &cmp = Compare())
2935{
2936 const long n = static_cast<long>(a.size());
2937 if (n < 2)
2938 return;
2939
2940 for (long i = 0; i < n - 1; ++i)
2941 for (long j = n - 1; j > i; --j)
2942 if (cmp(a(j), a(j - 1)))
2944 std::swap(a(j - 1), a(j));
2945}
2946
2973template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2974inline void insertion_sort(C<T> &a, const long l, const long r, const Compare &cmp = Compare())
2975{
2976 for (long i = l + 1; i <= r; i++)
2977 {
2978 T tmp = std::move(a(i));
2979 long j = i;
2980 for (/* nothing */; j > l and cmp(tmp, a(j - 1)); --j)
2981 a(j) = std::move(a(j - 1));
2982
2983 a(j) = std::move(tmp);
2984 }
2985}
2986
2991template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
2992inline void insertion_sort(C<T> &a, const Compare &cmp = Compare())
2993{
2994 const auto n = a.size();
2995 if (n <= 1)
2996 return;
2997 insertion_sort(a, 0, static_cast<long>(n) - 1, cmp);
2998}
2999
3034template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3035inline void shellsort(DynArray<T> &a, const Compare &cmp = Compare())
3036{
3037 const long n = a.size();
3038 if (n <= 1)
3039 return;
3040
3041 // Ciura's gap sequence - empirically optimal for most data distributions
3042 // Extended with ~2.25x multiplier for larger arrays
3043 static constexpr long ciura_gaps[] = {1, 4, 10, 23, 57, 132,
3044 301, 701, 1750, 4024, 9233, 21223,
3045 48805, 112217, 258100, 593630, 1365513, 3140680};
3046 static constexpr int num_gaps = std::size(ciura_gaps);
3047
3048 // Find the first gap smaller than the array size
3049 int gap_idx = num_gaps - 1;
3050 while (gap_idx >= 0 && ciura_gaps[gap_idx] >= n)
3051 --gap_idx;
3052
3053 // Sort with decreasing gaps
3054 for (; gap_idx >= 0; --gap_idx)
3055 {
3056 const long h = ciura_gaps[gap_idx];
3057 for (long i = h; i < n; i++)
3058 {
3059 T tmp = std::move(a(i));
3060 long j = i;
3061
3062 while (j >= h and cmp(tmp, a(j - h)))
3063 {
3064 a(j) = std::move(a(j - h));
3065 j -= h;
3066 }
3067
3068 a(j) = std::move(tmp);
3069 }
3070 }
3071}
3072
3074inline static long back_index(const long i) noexcept
3075{
3076 return i - 1;
3077}
3078
3085template <typename T, class Compare>
3086inline void sift_up(DynArray<T> &table, const long n, const Compare &cmp)
3087{
3088 long p;
3089 for (long i = n; i > 1; i = p)
3090 {
3091 p = i >> 1; // c = i/2
3092
3093 if (cmp(table(back_index(p)), table(back_index(i))))
3094 return;
3095
3096 std::swap(table(back_index(p)), table(back_index(i)));
3097 }
3098}
3099
3107template <typename T, class Compare>
3108inline void sift_down(DynArray<T> &table, const long start, const long n, const Compare &cmp)
3109{
3110 long i = start;
3111
3112 while (true)
3113 {
3114 long c = i << 1; // c = 2*i (left child)
3115
3116 if (c > n)
3117 return;
3118
3119 // Select the smaller/larger child depending on comparator
3120 if (c + 1 <= n)
3121 if (cmp(table(back_index(c + 1)), table(back_index(c))))
3122 c++;
3123
3124 if (cmp(table(back_index(i)), table(back_index(c))))
3125 return;
3126
3127 std::swap(table(back_index(c)), table(back_index(i)));
3128 i = c;
3129 }
3130}
3131
3133template <typename T, class Compare>
3134inline void sift_down(DynArray<T> &table, const long n, const Compare &cmp)
3135{
3136 sift_down<T, Compare>(table, 1, n, cmp);
3137}
3138
3151template <typename T, class Compare>
3153{
3154 Compare cmp;
3155
3156public:
3159 : cmp(__cmp)
3160 { /* Empty */
3161 }
3162
3164 bool operator () (const T &e1, const T &e2) const
3165 noexcept(noexcept(std::declval<const Compare &>()(e2, e1)))
3166 {
3167 return cmp(e2, e1);
3168 }
3169};
3170
3211template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3212inline void heapsort(DynArray<T> &a, const Compare &cmp = Compare())
3213{
3214 const long n = a.size();
3215 if (n <= 1)
3216 return;
3217
3219
3220 // Floyd's heap construction: O(n) instead of O(n log n)
3221 // Start from the last non-leaf node and sift down each node
3222 for (long i = n / 2; i >= 1; --i)
3224
3225 // Extract elements from the heap one by one
3226 for (long i = n; i >= 2; --i)
3227 {
3228 std::swap(a(0), a(i - 1));
3230 }
3231}
3232
3241template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3242inline long partition(C<T> &a, const long l, const long r, const Compare &cmp = Compare())
3243{
3244 if (l == r)
3245 return l;
3246
3247 long i = l - 1;
3248 long j = r;
3249 const T &pivot = a(r);
3250
3251 while (true)
3252 {
3253 while (cmp(a(++i), pivot))
3254 ; /* Nothing else */
3255
3256 while (cmp(pivot, a(--j)))
3257 if (j == l)
3258 break;
3259
3260 if (i >= j)
3261 break;
3262
3263 std::swap(a(i), a(j));
3264 }
3265
3266 std::swap(a(i), a(r));
3267
3268 return i;
3269}
3270
3277template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3278inline void quicksort_rec(DynArray<T> &a, const long l, const long r, const Compare &cmp = Compare())
3279{
3280 if (r <= l)
3281 return;
3282
3283 if (const long i = partition(a, l, r, cmp); i - l < r - i)
3284 {
3285 quicksort_rec<T, Compare>(a, l, i - 1, cmp);
3286 quicksort_rec<T, Compare>(a, i + 1, r, cmp);
3287 }
3288 else
3289 {
3290 quicksort_rec<T, Compare>(a, i + 1, r, cmp);
3291 quicksort_rec<T, Compare>(a, l, i - 1, cmp);
3292 }
3293}
3294
3306template <class Stack, class A, class B>
3307inline void push2(Stack &stack, const A &a, const B &b)
3308{
3309 stack.push(b);
3310 stack.push(a);
3311}
3312
3341template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3342inline void quicksort(DynArray<T> &a, const Compare &cmp = Compare())
3343{
3344 long l = 0, r = a.size() - 1;
3345
3346 const size_t n = a.size();
3348 FixedStack<long> stack((introsort_depth_limit(n) + 2) * 2);
3349
3350 push2(stack, l, r);
3351
3352 while (not stack.is_empty())
3353 {
3354 l = stack.pop();
3355 r = stack.pop();
3356
3357 if (r <= l)
3358 continue;
3359
3360 if (const long i = partition(a, l, r, cmp); i - l > r - i)
3361 {
3362 push2(stack, l, i - 1);
3363 push2(stack, i + 1, r);
3364 }
3365 else
3366 {
3367 push2(stack, i + 1, r);
3368 push2(stack, l, i - 1);
3369 }
3370 }
3371}
3372
3382template <typename T, class Compare>
3383inline void sift_down_subrange(DynArray<T> &a, long start, const long n, const long offset,
3384 const Compare &cmp)
3385{
3386 while (true)
3387 {
3388 long c = start << 1; // c = 2*start (left child)
3389 if (c > n)
3390 return;
3391
3392 // Select the child with higher priority
3393 if (c + 1 <= n)
3394 if (cmp(a(offset + c), a(offset + c - 1)))
3395 c++;
3396
3397 if (cmp(a(offset + start - 1), a(offset + c - 1)))
3398 return;
3399
3400 std::swap(a(offset + start - 1), a(offset + c - 1));
3401 start = c;
3402 }
3403}
3404
3421template <typename T, class Compare>
3422inline void heapsort_subrange(DynArray<T> &a, const long l, const long r, const Compare &cmp)
3423{
3424 const long n = r - l + 1;
3425 if (n <= 1)
3426 return;
3427
3429
3430 // Floyd's heap construction: O(n)
3431 for (long i = n / 2; i >= 1; --i)
3433
3434 // Extract elements
3435 for (long i = n; i >= 2; --i)
3436 {
3437 std::swap(a(l), a(l + i - 1));
3439 }
3440}
3441
3446template <typename T, class Compare>
3447void introsort_loop(DynArray<T> &a, long l, long r, size_t depth_limit, const Compare &cmp)
3448{
3449 while (r - l >= static_cast<long>(Quicksort_Threshold))
3450 {
3451 if (depth_limit == 0) [[unlikely]]
3452 {
3453 // Depth limit reached: switch to heapsort for guaranteed O(n log n)
3455 return;
3456 }
3457
3458 --depth_limit;
3459
3460 // Partition and continue
3461
3462 // Recurse on smaller partition, iterate on larger (tail call elimination)
3463 if (const long pivot = partition(a, l, r, cmp); pivot - l < r - pivot)
3464 {
3466 l = pivot + 1;
3467 }
3468 else
3469 {
3471 r = pivot - 1;
3472 }
3473 }
3474}
3475
3506template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3507inline void introsort(DynArray<T> &a, const Compare &cmp = Compare())
3508{
3509 const long n = a.size();
3510 if (n <= 1)
3511 return;
3512
3513 if (n < static_cast<long>(Quicksort_Threshold))
3514 {
3515 insertion_sort(a, 0, n - 1, cmp);
3516 return;
3517 }
3518
3521
3522 // Final insertion sort pass for small unsorted subarrays
3523 insertion_sort(a, 0, n - 1, cmp);
3524}
3525
3540template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3541inline long search_extreme(const DynArray<T> &a, const long l, const long r,
3542 const Compare &cmp = Compare())
3543{
3544 long extreme_index = l;
3545
3546 for (long i = l + 1; i <= r; i++)
3547 if (cmp(a(i), a(extreme_index)))
3548 extreme_index = i;
3549
3550 return extreme_index;
3551}
3552
3557template <typename T, StrictWeakOrder<T> Compare = Aleph::greater<T>>
3558inline long search_max(const DynArray<T> &a, const long l, const long r,
3559 const Compare &cmp = Compare())
3560{
3561 return search_extreme<T, Compare>(a, l, r, cmp);
3562}
3563
3609template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3610inline long binary_search(const C<T> &a, const T &x, long l, long r, const Compare &cmp = Compare())
3611{
3612 if (l > r)
3613 return l;
3614
3615 while (l <= r)
3616 if (long m = l + (r - l) / 2; cmp(x, a(m)))
3617 r = m - 1;
3618 else if (cmp(a(m), x))
3619 l = m + 1;
3620 else
3621 return m; // key found
3622
3623 return l;
3624}
3625
3643template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3644inline long binary_search(const C<T *> &a, const T &x, long l, long r, const Compare &cmp = Compare())
3645{
3646 if (l > r)
3647 return l;
3648
3649 while (l <= r)
3650 if (long m = l + (r - l) / 2; cmp(x, *a(m)))
3651 r = m - 1;
3652 else if (cmp(*a(m), x))
3653 l = m + 1;
3654 else
3655 return m; // found key
3656
3657 return l;
3658}
3659
3671template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3672inline long binary_search(const C<T *> &a, const T &x, Compare &&cmp = Compare())
3673{
3674 const auto n = a.size();
3675 if (n == 0)
3676 return 0;
3677
3678 return binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3679}
3680
3707template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3708inline long binary_search(const C<T> &a, const T &x, const Compare &cmp = Compare())
3709{
3710 const auto n = a.size();
3711 if (n == 0)
3712 return 0;
3713
3714 return binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3715}
3716
3730template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3731inline DynList<size_t> binary_search_dup(const C<T> &a, const T &x, const Compare &cmp = Compare())
3732{
3734 const auto n = a.size();
3735 if (n == 0)
3736 return ret;
3737
3738 long idx = binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3739 if (idx < 0)
3740 return ret;
3741
3742 if (not are_equals(a(idx), x, cmp))
3743 return ret;
3744
3745 ret.append(idx);
3746 for (long i = idx - 1; i >= 0; --i)
3747 {
3748 if (not are_equals(a(i), x, cmp))
3749 break;
3750 ret.insert(i);
3751 }
3752
3753 for (long i = idx + 1; i < static_cast<long>(n); ++i)
3754 {
3755 if (not are_equals(a(i), x, cmp))
3756 break;
3757 ret.append(i);
3758 }
3759
3760 return ret;
3761}
3762
3783template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3784inline Array<const T *> build_index_ptr(const C<T> &a, const Compare &cmp = Compare())
3785{
3786 const size_t n = a.size();
3788 for (size_t i = 0; i < n; ++i)
3789 ret(i) = &a(i);
3790
3791 quicksort_op(ret, [&cmp](const T *ptr1, const T *ptr2)
3792 {
3793 return cmp(*ptr1, *ptr2);
3794 });
3795
3796 return ret;
3797}
3798
3813template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3814inline DynList<const T *> bsearch_dup(const C<T> &a, const T &x, const Compare &cmp = Compare())
3815{
3817 long idx = binary_search(a, x, cmp);
3818 if (idx < 0)
3819 return ret;
3820
3821 const T *found_ptr = &a(idx);
3822 if (not are_equals(*found_ptr, x, cmp))
3823 return ret;
3824
3825 for (long i = idx - 1; i >= 0; --i)
3826 {
3827 const T *ptr = &a(i);
3828 if (not are_equals(*ptr, x, cmp))
3829 break;
3830 ret.insert(ptr);
3831 }
3832
3833 ret.append(found_ptr);
3834
3835 for (long i = idx + 1, n = a.size(); i < n; ++i)
3836 {
3837 const T *ptr = &a(i);
3838 if (not are_equals(*ptr, x, cmp))
3839 break;
3840 ret.append(ptr);
3841 }
3842
3843 return ret;
3844}
3845
3857template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3858inline T *bsearch(C<T> &a, const T &x, const Compare &cmp = Compare())
3859{
3860 long i = binary_search(a, x, cmp);
3861 if (i < 0)
3862 return nullptr;
3863 T *ptr = &a(i);
3864 return are_equals(*ptr, x, cmp) ? ptr : nullptr;
3865}
3866
3868template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3869inline const T *bsearch(const C<T> &a, const T &x, const Compare &cmp = Compare())
3870{
3871 long i = binary_search(a, x, cmp);
3872 if (i < 0)
3873 return nullptr;
3874 const T *ptr = &a(i);
3875 return are_equals(*ptr, x, cmp) ? ptr : nullptr;
3876}
3877
3889template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3890inline T *bsearch(const C<T *> &a, const T &x, const Compare &cmp = Compare())
3891{
3892 long i = binary_search(a, x, cmp);
3893 if (i < 0)
3894 return nullptr;
3895 T *ptr = a(i);
3896 return are_equals(*ptr, x, cmp) ? ptr : nullptr;
3897}
3898
3910template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3911inline DynList<T *> bsearch_dup(C<T> &a, const T &x, const Compare &cmp = Compare())
3912{
3914 long idx = binary_search(a, x, cmp);
3915 if (idx < 0)
3916 return ret;
3917
3918 T *found_ptr = &a(idx);
3919 if (not are_equals(*found_ptr, x, cmp))
3920 return ret;
3921
3922 for (long i = idx - 1; i >= 0; --i)
3923 {
3924 T *ptr = &a(i);
3925 if (not are_equals(*ptr, x, cmp))
3926 break;
3927 ret.insert(ptr);
3928 }
3929
3930 ret.append(found_ptr);
3931
3932 for (long i = idx + 1, n = a.size(); i < n; ++i)
3933 {
3934 T *ptr = &a(i);
3935 if (not are_equals(*ptr, x, cmp))
3936 break;
3937 ret.append(ptr);
3938 }
3939
3940 return ret;
3941}
3942
3954template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
3955inline DynList<T *> bsearch_dup(const C<T *> &a, const T &x, const Compare &cmp = Compare())
3956{
3958 const auto n = a.size();
3959 if (n == 0)
3960 return ret;
3961
3962 long idx = binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3963 if (idx < 0)
3964 return ret;
3965
3966 T *found_ptr = a(idx);
3967 if (not are_equals(*found_ptr, x, cmp))
3968 return ret;
3969
3970 for (long i = idx - 1; i >= 0; --i)
3971 {
3972 T *ptr = a(i);
3973 if (not are_equals(*ptr, x, cmp))
3974 break;
3975 ret.insert(ptr);
3976 }
3977
3978 ret.append(found_ptr);
3979
3980 for (long i = idx + 1, n = a.size(); i < n; ++i)
3981 {
3982 T *ptr = a(i);
3983 if (not are_equals(*ptr, x, cmp))
3984 break;
3985 ret.append(ptr);
3986 }
3987
3988 return ret;
3989}
3990
4024template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4025inline long binindex(const C<T> &a, const T &x, const Compare &cmp = Compare())
4026{
4027 return binary_search(a, x, cmp);
4028}
4029
4059template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4060inline DynList<long> binindex_dup(const C<T> &a, const T &x, const Compare &cmp = Compare())
4061{
4063 long idx = binary_search(a, x, cmp);
4064 if (idx < 0)
4065 return ret;
4066
4067 if (not are_equals(a(idx), x, cmp))
4068 return ret;
4069
4070 const long mid = idx;
4071
4072 for (long i = idx - 1; i >= 0; --i)
4073 {
4074 if (not are_equals(a(i), x, cmp))
4075 break;
4076 ret.insert(i);
4077 }
4078
4079 ret.append(mid);
4080
4081 for (long i = idx + 1, n = a.size(); i < n; ++i)
4082 {
4083 if (not are_equals(a(i), x, cmp))
4084 break;
4085 ret.append(i);
4086 }
4087
4088 return ret;
4089}
4090
4102template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4103inline DynList<long> binindex_dup(const C<T *> &a, const T &x, const Compare &cmp = Compare())
4104{
4106 long idx = binary_search(a, x, cmp);
4107 if (idx < 0)
4108 return ret;
4109
4110 T *ptr = a(idx);
4111 if (not are_equals(*ptr, x, cmp))
4112 return ret;
4113
4114 for (long i = idx - 1; i >= 0; --i)
4115 {
4116 ptr = a(i);
4117 if (not are_equals(*ptr, x, cmp))
4118 break;
4119 ret.insert(i);
4120 }
4121
4122 ret.append(idx);
4123
4124 for (long i = idx + 1, n = a.size(); i < n; ++i)
4125 {
4126 T *ptr = a(i);
4127 if (not are_equals(*ptr, x, cmp))
4128 break;
4129 ret.append(i);
4130 }
4131
4132 return ret;
4133}
4134
4136
4143namespace sort_utils_detail {
4156template <class C>
4157decltype(auto) indexed_value(const C &c, const size_t i)
4158{
4159 if constexpr (requires { c(i); })
4160 return c(i);
4161 else
4162 return c[i];
4163}
4164
4173template <class C>
4174using Indexed_Value_Type =
4175 std::remove_cvref_t<decltype(indexed_value(std::declval<const C &>(), size_t{}))>;
4176
4192template <class C, class Compare>
4193bool stable_index_less(const C &c, const size_t i, const size_t j, const Compare &cmp)
4194{
4195 auto &&lhs = indexed_value(c, i);
4196 auto &&rhs = indexed_value(c, j);
4197
4198 if (cmp(lhs, rhs))
4199 return true;
4200
4201 if (cmp(rhs, lhs))
4202 return false;
4203
4204 return i < j;
4205}
4206} // namespace sort_utils_detail
4208
4253template <class C, StrictWeakOrder<sort_utils_detail::Indexed_Value_Type<C>> Compare = Aleph::less<sort_utils_detail::Indexed_Value_Type<C>>>
4254inline Array<size_t> build_index(const C &a, const Compare &cmp = Compare())
4255{
4256 const size_t n = a.size();
4258 for (size_t i = 0; i < n; ++i)
4259 ret(i) = i;
4260
4261 quicksort_op(ret, [&a, &cmp](const size_t i, const size_t j)
4262 {
4263 return cmp(sort_utils_detail::indexed_value(a, i), sort_utils_detail::indexed_value(a, j));
4264 });
4265
4266 return ret;
4267}
4268
4308template <class C, StrictWeakOrder<sort_utils_detail::Indexed_Value_Type<C>> Compare = Aleph::less<sort_utils_detail::Indexed_Value_Type<C>>>
4309inline Array<size_t> stable_build_index(const C &a, const Compare &cmp = Compare())
4310{
4311 const size_t n = a.size();
4313 for (size_t i = 0; i < n; ++i)
4314 ret(i) = i;
4315
4316 quicksort_op(ret, [&a, &cmp](const size_t i, const size_t j)
4317 {
4318 return sort_utils_detail::stable_index_less(a, i, j, cmp);
4319 });
4320
4321 return ret;
4322}
4323
4356template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4357inline Array<T *> build_index_ptr(C<T> &a, const Compare &cmp = Compare())
4358{
4359 const size_t n = a.size();
4361 for (size_t i = 0; i < n; ++i)
4362 ret(i) = &a(i);
4363
4364 quicksort_op(ret, [&cmp](const T *ptr1, const T *ptr2)
4365 {
4366 return cmp(*ptr1, *ptr2);
4367 });
4368
4369 return ret;
4370}
4371
4400template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4401inline Array<const T *> stable_build_index_ptr(const C<T> &a, const Compare &cmp = Compare())
4402{
4403 const size_t n = a.size();
4405 for (size_t i = 0; i < n; ++i)
4406 idx(i) = i;
4407
4408 quicksort_op(idx, [&a, &cmp](const size_t i, const size_t j)
4409 {
4410 return sort_utils_detail::stable_index_less(a, i, j, cmp);
4411 });
4412
4414 for (size_t i = 0; i < n; ++i)
4415 ret(i) = &a(idx(i));
4416
4417 return ret;
4418}
4419
4438template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4439inline Array<T *> stable_build_index_ptr(C<T> &a, const Compare &cmp = Compare())
4440{
4441 const size_t n = a.size();
4443 for (size_t i = 0; i < n; ++i)
4444 idx(i) = i;
4445
4446 quicksort_op(idx, [&a, &cmp](const size_t i, const size_t j)
4447 {
4448 return sort_utils_detail::stable_index_less(a, i, j, cmp);
4449 });
4450
4452 for (size_t i = 0; i < n; ++i)
4453 ret(i) = &a(idx(i));
4454
4455 return ret;
4456}
4457
4485template <template <typename> class C, typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4486inline void quicksort_op(C<T> &a, const Compare &cmp = Compare(),
4487 const size_t threshold = Quicksort_Threshold)
4488{
4489 const size_t n = a.size();
4490 if (n <= 1)
4491 return;
4492
4493 size_t l = 0, r = n - 1;
4494
4495 FixedStack<size_t> stack((introsort_depth_limit(n) + 2) * 2);
4496
4497 push2(stack, l, r);
4498
4499 while (not stack.is_empty())
4500 {
4501 l = stack.pop();
4502 r = stack.pop();
4503
4504 const size_t partition_size = r - l + 1;
4505 if (partition_size <= 1)
4506 continue;
4507
4508 if (partition_size <= threshold)
4509 {
4510 insertion_sort(a, l, r, cmp);
4511 continue;
4512 }
4513
4514 const auto i = static_cast<size_t>(
4515 partition_op<T, Compare>(a, static_cast<long>(l), static_cast<long>(r), cmp));
4516
4517 const size_t left_size = i > l ? i - l : 0;
4518 const size_t right_size = r > i ? r - i : 0;
4519
4520 if (left_size > right_size)
4521 {
4522 if (left_size > 0)
4523 push2(stack, l, i - 1);
4524 if (right_size > 0)
4525 push2(stack, i + 1, r);
4526 }
4527 else
4528 {
4529 if (right_size > 0)
4530 push2(stack, i + 1, r);
4531 if (left_size > 0)
4532 push2(stack, l, i - 1);
4533 }
4534 }
4535}
4536
4545template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4546inline long binary_search_rec(T *a, const T &x, const long l, const long r,
4547 const Compare &cmp = Compare())
4548{
4549 if (l > r)
4550 return l;
4551
4552 const long m = l + (r - l) / 2;
4553
4554 if (cmp(x, a[m]))
4555 return binary_search_rec<T, Compare>(a, x, l, m - 1, cmp);
4556 if (cmp(a[m], x))
4557 return binary_search_rec<T, Compare>(a, x, m + 1, r, cmp);
4558
4559 return m;
4560}
4561
4570template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
4571inline long binary_search(T *a, const T &x, long l, long r, const Compare &cmp = Compare())
4572{
4573 while (l <= r)
4574 {
4575 long m = l + (r - l) / 2;
4576 if (cmp(x, a[m]))
4577 r = m - 1;
4578 else if (cmp(a[m], x))
4579 l = m + 1;
4580 else
4581 return m;
4582 }
4583 return l;
4584}
4585
4612template <typename KeyFn>
4613 requires std::invocable<const KeyFn &, size_t> and
4614 std::convertible_to<std::invoke_result_t<const KeyFn &, size_t>, int>
4615void counting_sort_indices(Array<size_t> &sa, Array<size_t> &tmp, const size_t n, const int min_key,
4616 const int max_key, const KeyFn &key_of)
4617{
4618 ah_domain_error_if(max_key < min_key) << "counting_sort_indices(): max_key < min_key";
4619
4620 if (n == 0)
4621 return;
4622
4623 ah_out_of_range_error_if(sa.size() < n or tmp.size() < n)
4624 << "counting_sort_indices(): sa/tmp size is smaller than n";
4625
4626 const auto range_minus_one = static_cast<unsigned long long>(static_cast<long long>(max_key) -
4627 static_cast<long long>(min_key));
4629 constexpr auto max_count = static_cast<unsigned long long>(std::numeric_limits<size_t>::max() - size_t(1));
4631 << "counting_sort_indices(): key range is too large";
4632
4633 const auto k = static_cast<size_t>(range_minus_one) + 1;
4635 for (size_t i = 0; i < k; ++i)
4636 count[i] = 0;
4637
4638 for (size_t i = 0; i < n; ++i)
4639 {
4640 const int key = key_of(sa[i]);
4642 << "counting_sort_indices(): key out of expected range";
4643 const auto bucket = static_cast<size_t>(static_cast<unsigned long long>(
4644 static_cast<long long>(key) - static_cast<long long>(min_key)));
4645 ++count[bucket];
4646 }
4647
4648 for (size_t j = 1; j < k; ++j)
4649 count[j] += count[j - 1];
4650
4651 for (size_t i = n; i > 0; --i)
4652 {
4653 const int key = key_of(sa[i - 1]);
4654 const auto idx = static_cast<size_t>(static_cast<unsigned long long>(
4655 static_cast<long long>(key) - static_cast<long long>(min_key)));
4656 tmp[--count[idx]] = sa[i - 1];
4657 }
4658
4659 for (size_t i = 0; i < n; ++i)
4660 sa[i] = tmp[i];
4661}
4662
4663namespace sort_utils_detail {
4672template <typename IntT>
4673using counting_unsigned_t = std::make_unsigned_t<std::remove_cv_t<IntT>>;
4674
4683template <typename IntT>
4684using radix_unsigned_t = std::make_unsigned_t<std::remove_cv_t<IntT>>;
4685
4695template <typename IntT>
4696[[nodiscard]] size_t counting_bucket_count(const IntT min_key, const IntT max_key)
4697{
4698 ah_domain_error_if(max_key < min_key) << "counting_sort(): max_key < min_key";
4699
4701 const U range_minus_one = static_cast<U>(max_key) - static_cast<U>(min_key);
4702
4703 if constexpr (sizeof(U) >= sizeof(size_t))
4704 {
4705 constexpr size_t max_count = std::numeric_limits<size_t>::max() - static_cast<size_t>(1);
4707 << "counting_sort(): key range is too large";
4708 }
4709
4710 return static_cast<size_t>(range_minus_one) + 1;
4711}
4712
4720template <typename IntT>
4721[[nodiscard]] inline size_t counting_bucket(const IntT value, const IntT min_key) noexcept
4722{
4723 return static_cast<size_t>(static_cast<counting_unsigned_t<IntT>>(value) -
4724 static_cast<counting_unsigned_t<IntT>>(min_key));
4725}
4726
4734template <typename IntT>
4735[[nodiscard]] inline IntT counting_value_from_bucket(const size_t bucket, const IntT min_key) noexcept
4736{
4738 return static_cast<IntT>(static_cast<U>(min_key) + static_cast<U>(bucket));
4739}
4740
4747template <template <typename> class C, typename IntT>
4750{
4751 const size_t n = a.size();
4752 if (n < 2)
4753 return;
4754
4755 IntT min_key = a(0), max_key = a(0);
4756 for (size_t i = 1; i < n; ++i)
4757 {
4758 if (a(i) < min_key)
4759 min_key = a(i);
4760 if (a(i) > max_key)
4761 max_key = a(i);
4762 }
4763
4764 const size_t k = counting_bucket_count(min_key, max_key);
4766 for (size_t i = 0; i < k; ++i)
4767 count[i] = 0;
4768
4769 for (size_t i = 0; i < n; ++i)
4770 ++count[counting_bucket(a(i), min_key)];
4771
4772 for (size_t i = 1; i < k; ++i)
4773 count[i] += count[i - 1];
4774
4776 for (size_t i = n; i > 0; --i)
4777 {
4778 const auto bucket = counting_bucket(a(i - 1), min_key);
4779 tmp[--count[bucket]] = a(i - 1);
4780 }
4781
4782 for (size_t i = 0; i < n; ++i)
4783 a(i) = tmp[i];
4784}
4785
4795template <typename IntT>
4797void counting_sort_impl(IntT *a, const size_t n)
4798{
4799 if (n < 2)
4800 return;
4801
4802 IntT min_key = a[0], max_key = a[0];
4803 for (size_t i = 1; i < n; ++i)
4804 {
4805 if (a[i] < min_key)
4806 min_key = a[i];
4807 if (a[i] > max_key)
4808 max_key = a[i];
4809 }
4810
4811 const size_t k = counting_bucket_count(min_key, max_key);
4813 for (size_t i = 0; i < k; ++i)
4814 count[i] = 0;
4815
4816 for (size_t i = 0; i < n; ++i)
4817 ++count[counting_bucket(a[i], min_key)];
4818
4819 for (size_t i = 1; i < k; ++i)
4820 count[i] += count[i - 1];
4821
4823 for (size_t i = n; i > 0; --i)
4824 {
4825 const auto bucket = counting_bucket(a[i - 1], min_key);
4826 tmp[--count[bucket]] = a[i - 1];
4827 }
4828
4829 for (size_t i = 0; i < n; ++i)
4830 a[i] = tmp[i];
4831}
4832
4841template <typename IntT>
4844{
4845 const size_t n = list.size();
4846 if (n < 2)
4847 return;
4848
4849 typename DynList<IntT>::Iterator it(list);
4850 IntT min_key = it.get_curr_ne();
4851 IntT max_key = min_key;
4852 for (it.next_ne(); it.has_curr(); it.next_ne())
4853 {
4854 const IntT value = it.get_curr_ne();
4855 if (value < min_key)
4856 min_key = value;
4857 if (value > max_key)
4858 max_key = value;
4859 }
4860
4861 const size_t k = counting_bucket_count(min_key, max_key);
4863 for (size_t i = 0; i < k; ++i)
4864 count[i] = 0;
4865
4866 for (typename DynList<IntT>::Iterator scan(list); scan.has_curr(); scan.next_ne())
4867 ++count[counting_bucket(scan.get_curr_ne(), min_key)];
4868
4869 typename DynList<IntT>::Iterator out(list);
4870 for (size_t bucket = 0; bucket < k; ++bucket)
4871 {
4872 const size_t times = count[bucket];
4873 if (times == 0)
4874 continue;
4875
4876 const IntT value = counting_value_from_bucket(bucket, min_key);
4877 for (size_t c = 0; c < times; ++c)
4878 {
4879 out.get_curr_ne() = value;
4880 out.next_ne();
4881 }
4882 }
4883}
4884
4893template <typename IntT>
4896{
4897 const size_t n = list.size();
4898 if (n < 2)
4899 return;
4900
4901 typename DynDlist<IntT>::Iterator it(list);
4902 IntT min_key = it.get_curr_ne();
4903 IntT max_key = min_key;
4904 for (it.next_ne(); it.has_curr(); it.next_ne())
4905 {
4906 const IntT value = it.get_curr_ne();
4907 if (value < min_key)
4908 min_key = value;
4909 if (value > max_key)
4910 max_key = value;
4911 }
4912
4913 const size_t k = counting_bucket_count(min_key, max_key);
4915 for (size_t i = 0; i < k; ++i)
4916 count[i] = 0;
4917
4918 for (typename DynDlist<IntT>::Iterator scan(list); scan.has_curr(); scan.next_ne())
4919 ++count[counting_bucket(scan.get_curr_ne(), min_key)];
4920
4921 typename DynDlist<IntT>::Iterator out(list);
4922 for (size_t bucket = 0; bucket < k; ++bucket)
4923 {
4924 const size_t times = count[bucket];
4925 if (times == 0)
4926 continue;
4927
4928 const IntT value = counting_value_from_bucket(bucket, min_key);
4929 for (size_t c = 0; c < times; ++c)
4930 {
4931 out.get_curr_ne() = value;
4932 out.next_ne();
4933 }
4934 }
4935}
4936
4947template <typename IntT>
4949{
4950 using U = radix_unsigned_t<IntT>;
4951 if constexpr (std::is_signed_v<std::remove_cv_t<IntT>>)
4952 return static_cast<U>(value) ^ (U{1} << (std::numeric_limits<U>::digits - 1));
4953 else
4954 return static_cast<U>(value);
4955}
4956
4966template <template <typename> class C, typename IntT>
4969{
4970 using U = radix_unsigned_t<IntT>;
4971
4972 const size_t n = a.size();
4973 if (n < 2)
4974 return;
4975
4978
4979 for (size_t pass = 0; pass < sizeof(U); ++pass)
4980 {
4981 for (size_t i = 0; i < 256; ++i)
4982 count[i] = 0;
4983
4984 const size_t shift = pass * 8;
4985 for (size_t i = 0; i < n; ++i)
4986 {
4987 const U key = radix_key<IntT>(a(i));
4988 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4989 ++count[bucket];
4990 }
4991
4992 for (size_t i = 1; i < 256; ++i)
4993 count[i] += count[i - 1];
4994
4995 for (size_t i = n; i > 0; --i)
4996 {
4997 const U key = radix_key<IntT>(a(i - 1));
4998 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4999 tmp[--count[bucket]] = a(i - 1);
5000 }
5001
5002 for (size_t i = 0; i < n; ++i)
5003 a(i) = tmp[i];
5004 }
5005}
5006
5015template <typename IntT>
5017void radix_sort_impl(IntT *a, const size_t n)
5018{
5019 using U = radix_unsigned_t<IntT>;
5020
5021 if (n < 2)
5022 return;
5023
5026
5027 for (size_t pass = 0; pass < sizeof(U); ++pass)
5028 {
5029 for (size_t i = 0; i < 256; ++i)
5030 count[i] = 0;
5031
5032 const size_t shift = pass * 8;
5033 for (size_t i = 0; i < n; ++i)
5034 {
5035 const U key = radix_key<IntT>(a[i]);
5036 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
5037 ++count[bucket];
5038 }
5039
5040 for (size_t i = 1; i < 256; ++i)
5041 count[i] += count[i - 1];
5042
5043 for (size_t i = n; i > 0; --i)
5044 {
5045 const U key = radix_key<IntT>(a[i - 1]);
5046 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
5047 tmp[--count[bucket]] = a[i - 1];
5048 }
5049
5050 for (size_t i = 0; i < n; ++i)
5051 a[i] = tmp[i];
5052 }
5053}
5054
5063template <typename IntT>
5066{
5067 using U = radix_unsigned_t<IntT>;
5068
5069 if (const size_t n = list.size(); n < 2)
5070 return;
5071
5072 Array<HTList> buckets;
5073 buckets.reserve(256);
5074 for (size_t i = 0; i < 256; ++i)
5075 buckets.append(HTList());
5076
5077 for (size_t pass = 0; pass < sizeof(U); ++pass)
5078 {
5079 const size_t shift = pass * 8;
5080
5081 while (not list.is_empty())
5082 {
5083 Slinknc *link = list.HTList::remove_first_ne();
5084 auto *node = static_cast<Snodenc<IntT> *>(link);
5085 const U key = radix_key<IntT>(node->get_data());
5086 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
5087 buckets[bucket].append(link);
5088 }
5089
5090 for (auto &bucket : buckets)
5091 list.HTList::append(bucket);
5092 }
5093}
5094
5102template <typename IntT>
5105{
5106 using U = radix_unsigned_t<IntT>;
5107
5108 if (const size_t n = list.size(); n < 2)
5109 return;
5110
5111 Array<Dnode<IntT>> buckets;
5112 buckets.reserve(256);
5113 for (size_t i = 0; i < 256; ++i)
5114 buckets.append(Dnode<IntT>());
5115
5116 for (size_t pass = 0; pass < sizeof(U); ++pass)
5117 {
5118 const size_t shift = pass * 8;
5119
5120 while (not list.template Dnode<IntT>::is_empty())
5121 {
5122 Dnode<IntT> *node = list.template Dnode<IntT>::remove_first_ne();
5123 const U key = radix_key<IntT>(node->get_data());
5124 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
5125 buckets[bucket].append(node);
5126 }
5127
5128 for (auto &bucket : buckets)
5129 list.template Dnode<IntT>::append_list(&bucket);
5130 }
5131}
5132} // namespace sort_utils_detail
5133
5138template <typename T>
5140
5145template <typename T>
5147
5176template <typename T>
5177 requires CountingSortable<T>
5183
5199template <typename T>
5200 requires CountingSortable<T>
5206
5226template <typename T>
5227 requires CountingSortable<T>
5233
5253template <typename T>
5254 requires CountingSortable<T>
5260
5275template <typename T>
5276 requires CountingSortable<T>
5277void counting_sort(T *a, const size_t n)
5278{
5279 ah_invalid_argument_if(a == nullptr and n > 0)
5280 << "counting_sort(): null pointer with non-zero length";
5283}
5284
5294template <typename T, size_t N>
5295 requires CountingSortable<T>
5296void counting_sort(T (&a)[N])
5297{
5298 counting_sort(a, N);
5299}
5300
5331template <typename T>
5332 requires RadixSortable<T>
5338
5353template <typename T>
5354 requires RadixSortable<T>
5360
5371template <typename T>
5372 requires RadixSortable<T>
5378
5389template <typename T>
5390 requires RadixSortable<T>
5396
5409template <typename T>
5410 requires RadixSortable<T>
5411void radix_sort(T *a, const size_t n)
5412{
5413 ah_invalid_argument_if(a == nullptr and n > 0)
5414 << "radix_sort(): null pointer with non-zero length";
5417}
5418
5428template <typename T, size_t N>
5429 requires RadixSortable<T>
5430void radix_sort(T (&a)[N])
5431{
5432 radix_sort(a, N);
5433}
5434
5435// ================================================================
5436// Bucket Sort
5437// ================================================================
5438
5439namespace bucket_sort_detail {
5443template <typename T, class Compare>
5444void insertion_sort_range(T *a, const size_t lo, const size_t hi, const Compare &cmp) noexcept(
5445 std::is_nothrow_move_constructible_v<T> and std::is_nothrow_move_assignable_v<T> and
5446 noexcept(std::declval<const Compare &>()(std::declval<const T &>(), std::declval<const T &>())))
5447{
5448 for (size_t i = lo + 1; i < hi; ++i)
5449 {
5450 T tmp = std::move(a[i]);
5451 size_t j = i;
5452 while (j > lo and cmp(tmp, a[j - 1]))
5453 {
5454 a[j] = std::move(a[j - 1]);
5455 --j;
5456 }
5457 a[j] = std::move(tmp);
5458 }
5459}
5460
5471template <typename T, class Compare, class BucketKey>
5472void bucket_sort_impl(T *a, const size_t n, const size_t num_buckets, const BucketKey &bucket_key,
5473 const Compare &cmp)
5474{
5475 if (n < 2)
5476 return;
5477
5478 ah_domain_error_if(num_buckets == 0) << "bucket_sort: num_buckets must be > 0";
5479
5480 // Count elements per bucket
5481 Array<size_t> counts = Array<size_t>::create(num_buckets);
5482 for (size_t i = 0; i < num_buckets; ++i)
5483 counts[i] = 0;
5484
5485 for (size_t i = 0; i < n; ++i)
5486 {
5487 const size_t b = bucket_key(a[i]);
5488 ah_domain_error_if(b >= num_buckets) << "bucket_sort: bucket_key returned " << b
5489 << " which is >= num_buckets (" << num_buckets << ")";
5490 ++counts[b];
5491 }
5492
5493 // Compute bucket start offsets (prefix sum)
5494 Array<size_t> offsets = Array<size_t>::create(num_buckets);
5495 offsets[0] = 0;
5496 for (size_t i = 1; i < num_buckets; ++i)
5497 offsets[i] = offsets[i - 1] + counts[i - 1];
5498
5499 // Distribute elements into a temporary array (stable)
5501 // Reset counts for placement
5502 for (size_t i = 0; i < num_buckets; ++i)
5503 counts[i] = 0;
5504
5505 for (size_t i = 0; i < n; ++i)
5506 {
5507 const size_t b = bucket_key(a[i]);
5508 tmp[offsets[b] + counts[b]] = std::move(a[i]);
5509 ++counts[b];
5510 }
5511
5512 // Sort each bucket with insertion sort
5513 for (size_t b = 0; b < num_buckets; ++b)
5514 if (counts[b] > 1)
5515 insertion_sort_range(&tmp[0], offsets[b], offsets[b] + counts[b], cmp);
5516
5517 // Copy back
5518 for (size_t i = 0; i < n; ++i)
5519 a[i] = std::move(tmp[i]);
5520}
5521
5530template <typename T, class Container, class SortFn, class Compare>
5532{
5533 const size_t n = a.size();
5534 if (n < 2)
5535 return;
5536
5538 size_t i = 0;
5539 for (auto it = a.get_it(); it.has_curr(); it.next_ne())
5540 tmp[i++] = std::move(it.get_curr_ne());
5541
5542 sort_fn(&tmp[0], n, cmp);
5543
5544 i = 0;
5545 for (auto it = a.get_it(); it.has_curr(); it.next_ne())
5546 it.get_curr_ne() = std::move(tmp[i++]);
5547}
5548
5550template <auto SortFn>
5552{
5553 template <class Compare>
5554 auto operator () (const Compare &) const
5555 {
5556 return [](auto *p, size_t n, const Compare &c)
5557 {
5558 SortFn(p, n, c);
5559 };
5560 }
5561};
5562
5564template <class BucketKey>
5566{
5569
5570 template <class Compare>
5571 auto operator () (const Compare &) const
5572 {
5573 return [this](auto *p, size_t n, const Compare &c)
5574 {
5576 };
5577 }
5578};
5579} // namespace bucket_sort_detail
5580
5629template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>, class BucketKey>
5630void bucket_sort(T *a, const size_t n, const size_t num_buckets, const BucketKey &bucket_key,
5631 const Compare &cmp = Compare())
5632{
5633 ah_invalid_argument_if(a == nullptr and n > 0)
5634 << "bucket_sort(): null pointer with non-zero length";
5636 bucket_sort_detail::bucket_sort_impl(a, n, num_buckets, bucket_key, cmp);
5637}
5638
5672template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
5673 requires std::floating_point<T>
5674void bucket_sort(T *a, const size_t n, const Compare &cmp = Compare())
5675{
5676 ah_invalid_argument_if(a == nullptr and n > 0)
5677 << "bucket_sort(): null pointer with non-zero length";
5678
5679 if (n < 2)
5680 return;
5681
5682 T min_val = a[0], max_val = a[0];
5683 for (size_t i = 0; i < n; ++i)
5684 {
5685 if (not std::isfinite(a[i])) [[unlikely]]
5686 {
5687 timsort(a, n, cmp);
5688 return;
5689 }
5690 if (a[i] < min_val)
5691 min_val = a[i];
5692 if (a[i] > max_val)
5693 max_val = a[i];
5694 }
5695
5696 const T range = max_val - min_val;
5697 if (range == T{0})
5698 return; // all equal
5699
5700 // Numeric interpolation is only correct for the default arithmetic
5701 // comparators (Aleph::less / Aleph::greater). For any other comparator
5702 // the mapping from value to bucket index is undefined, so fall back to
5703 // the stable timsort which respects the caller's comparator directly.
5704 if constexpr (std::is_same_v<Compare, Aleph::less<T>> or std::is_same_v<Compare, Aleph::greater<T>>)
5705 {
5706 const bool descending = cmp(max_val, min_val) and not cmp(min_val, max_val);
5707 const size_t num_buckets = n;
5708 auto key = [min_val, range, num_buckets, descending](const T &val) -> size_t
5709 {
5710 auto b = static_cast<size_t>((val - min_val) / range * static_cast<T>(num_buckets - 1));
5711 if (b >= num_buckets)
5712 b = num_buckets - 1;
5713
5714 if (descending)
5715 b = (num_buckets - 1) - b;
5716
5717 return b;
5718 };
5719
5720 bucket_sort_detail::bucket_sort_impl(a, n, num_buckets, key, cmp);
5721 }
5722 else
5723 {
5724 // Custom comparator: bucket interpolation is not order-preserving;
5725 // delegate to timsort which respects any strict-weak-ordering comparator.
5726 timsort(a, n, cmp);
5727 }
5728}
5729
5742template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
5743 requires std::floating_point<T>
5744void bucket_sort(DynArray<T> &a, const Compare &cmp = Compare())
5745{
5746 bucket_sort_detail::apply_sort_with_temp<T>(
5747 a,
5748 bucket_sort_detail::sort_invoker_factory<static_cast<void (*)(T *, size_t, const Compare &)>(
5749 bucket_sort)>{}(cmp),
5750 cmp);
5751}
5752
5762template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
5763 requires std::floating_point<T>
5764void bucket_sort(Array<T> &a, const Compare &cmp = Compare())
5765{
5766 if (a.size() < 2)
5767 return;
5768 bucket_sort(&a[0], a.size(), cmp);
5769}
5770
5781template <typename T, size_t N, StrictWeakOrder<T> Compare = Aleph::less<T>>
5782 requires std::floating_point<T>
5783void bucket_sort(T (&a)[N], const Compare &cmp = Compare())
5784{
5785 bucket_sort(static_cast<T *>(a), N, cmp);
5786}
5787
5800template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
5801 requires std::floating_point<T>
5802void bucket_sort(DynList<T> &list, const Compare &cmp = Compare())
5803{
5804 bucket_sort_detail::apply_sort_with_temp<T>(
5805 list,
5806 bucket_sort_detail::sort_invoker_factory<static_cast<void (*)(T *, size_t, const Compare &)>(
5807 bucket_sort)>{}(cmp),
5808 cmp);
5809}
5810
5823template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>, class BucketKey>
5824void bucket_sort(DynArray<T> &a, const size_t num_buckets, const BucketKey &bucket_key,
5825 const Compare &cmp = Compare())
5826{
5827 bucket_sort_detail::apply_sort_with_temp<T>(
5829}
5830
5843template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>, class BucketKey>
5844void bucket_sort(Array<T> &a, const size_t num_buckets, const BucketKey &bucket_key,
5845 const Compare &cmp = Compare())
5846{
5847 if (a.size() < 2)
5848 return;
5849 bucket_sort(&a[0], a.size(), num_buckets, bucket_key, cmp);
5850}
5851
5852// ================================================================
5853// Timsort
5854// ================================================================
5855
5856namespace timsort_detail {
5863[[nodiscard]] constexpr size_t compute_minrun(size_t n) noexcept
5864{
5865 size_t r = 0;
5866 while (n >= 64)
5867 {
5868 r |= n & 1;
5869 n >>= 1;
5870 }
5871 return n + r;
5872}
5873
5877template <typename T>
5878void reverse_range(T *a, size_t lo, size_t hi) noexcept(std::is_nothrow_swappable_v<T>)
5879{
5880 while (lo + 1 < hi)
5881 {
5882 std::swap(a[lo], a[hi - 1]);
5883 ++lo;
5884 --hi;
5885 }
5886}
5887
5896template <typename T, class Compare>
5897size_t count_run_and_make_ascending(T *a, const size_t lo, const size_t hi, const Compare &cmp)
5898{
5899 if (hi - lo <= 1)
5900 return hi - lo;
5901
5902 size_t run_hi = lo + 1;
5903 if (cmp(a[run_hi], a[lo])) // descending
5904 {
5905 while (run_hi < hi and cmp(a[run_hi], a[run_hi - 1]))
5906 ++run_hi;
5907 reverse_range(a, lo, run_hi);
5908 }
5909 else // ascending
5910 while (run_hi < hi and not cmp(a[run_hi], a[run_hi - 1]))
5911 ++run_hi;
5912 return run_hi - lo;
5913}
5914
5923template <typename T, class Compare>
5925 T *a, const size_t lo, const size_t hi, const size_t start,
5926 const Compare &cmp) noexcept(std::is_nothrow_move_constructible_v<T> and
5927 std::is_nothrow_move_assignable_v<T> and
5928 noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
5929 std::declval<const T &>())))
5930
5931{
5932 size_t i = (start <= lo) ? lo + 1 : start;
5933 for (; i < hi; ++i)
5934 {
5935 T pivot = std::move(a[i]);
5936
5937 // Binary search for insertion point in [lo, i)
5938 size_t left = lo;
5939 size_t right = i;
5940 while (left < right)
5941 {
5942 const size_t mid = left + (right - left) / 2;
5943 if (cmp(pivot, a[mid]))
5944 right = mid;
5945 else
5946 left = mid + 1;
5947 }
5948
5949 // Shift elements [left, i) right by one
5950 for (size_t p = i; p > left; --p)
5951 a[p] = std::move(a[p - 1]);
5952 a[left] = std::move(pivot);
5953 }
5954}
5955
5973template <typename T, class Compare>
5975 const T &key, const T *a, const size_t len, const size_t hint,
5976 const Compare &cmp) noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
5977 std::declval<const T &>())))
5978{
5979 size_t last_ofs = 0;
5980 size_t ofs = 1;
5981
5982 if (cmp(a[hint], key)) // key > a[hint]: search right
5983 {
5984 const size_t max_ofs = len - hint;
5985 while (ofs < max_ofs and cmp(a[hint + ofs], key))
5986 {
5987 last_ofs = ofs;
5988 ofs = (ofs << 1) + 1;
5989 if (ofs > max_ofs)
5990 ofs = max_ofs;
5991 }
5992 last_ofs += hint;
5993 ofs += hint;
5994 }
5995 else // key <= a[hint]: search left
5996 {
5997 const size_t max_ofs = hint + 1;
5998 while (ofs < max_ofs and not cmp(a[hint - ofs], key))
5999 {
6000 last_ofs = ofs;
6001 ofs = (ofs << 1) + 1;
6002 if (ofs > max_ofs)
6003 ofs = max_ofs;
6004 }
6005 const size_t tmp = last_ofs;
6006 // Note: last_ofs may intentionally underflow if hint < ofs.
6007 // This is compensated by the subsequent ++last_ofs to effectively
6008 // start the binary search at index 0. This is safe for unsigned types.
6009 last_ofs = hint - ofs;
6010 ofs = hint - tmp;
6011 }
6012
6013 // Binary search in (last_ofs, ofs]
6014 ++last_ofs;
6015 while (last_ofs < ofs)
6016 {
6017 const size_t mid = last_ofs + (ofs - last_ofs) / 2;
6018 if (cmp(a[mid], key))
6019 last_ofs = mid + 1;
6020 else
6021 ofs = mid;
6022 }
6023 return ofs;
6024}
6025
6042template <typename T, class Compare>
6044 const T &key, const T *a, const size_t len, const size_t hint,
6045 const Compare &cmp) noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
6046 std::declval<const T &>())))
6047{
6048 size_t last_ofs = 0;
6049 size_t ofs = 1;
6050
6051 if (cmp(key, a[hint])) // key < a[hint]: search left
6052 {
6053 const size_t max_ofs = hint + 1;
6054 while (ofs < max_ofs and cmp(key, a[hint - ofs]))
6055 {
6056 last_ofs = ofs;
6057 ofs = (ofs << 1) + 1;
6058 if (ofs > max_ofs)
6059 ofs = max_ofs;
6060 }
6061 const size_t tmp = last_ofs;
6062 // Note: last_ofs may intentionally underflow if hint < ofs.
6063 // This is compensated by the subsequent ++last_ofs to effectively
6064 // start the binary search at index 0. This is safe for unsigned types.
6065 last_ofs = hint - ofs;
6066 ofs = hint - tmp;
6067 }
6068 else // key >= a[hint]: search right
6069 {
6070 const size_t max_ofs = len - hint;
6071 while (ofs < max_ofs and not cmp(key, a[hint + ofs]))
6072 {
6073 last_ofs = ofs;
6074 ofs = (ofs << 1) + 1;
6075 if (ofs > max_ofs)
6076 ofs = max_ofs;
6077 }
6078 last_ofs += hint;
6079 ofs += hint;
6080 }
6081
6082 ++last_ofs;
6083 while (last_ofs < ofs)
6084 {
6085 const size_t mid = last_ofs + (ofs - last_ofs) / 2;
6086 if (cmp(key, a[mid]))
6087 ofs = mid;
6088 else
6089 last_ofs = mid + 1;
6090 }
6091 return ofs;
6092}
6093
6100template <typename T, class Compare>
6101void merge_lo(T *a, const size_t base1, const size_t len1, const size_t base2, const size_t len2,
6102 T *tmp, const Compare &cmp)
6103{
6104 // Copy run1 into tmp
6105 for (size_t i = 0; i < len1; ++i)
6106 tmp[i] = std::move(a[base1 + i]);
6107
6108 size_t c1 = 0; // cursor into tmp (run1 copy)
6109 size_t c2 = base2; // cursor into a (run2)
6110 size_t dest = base1; // write cursor into a
6111 const size_t end1 = len1;
6112 const size_t end2 = base2 + len2;
6113
6114 while (c1 < end1 and c2 < end2)
6115 if (cmp(a[c2], tmp[c1]))
6116 a[dest++] = std::move(a[c2++]);
6117 else
6118 a[dest++] = std::move(tmp[c1++]);
6119
6120 // Copy remaining from tmp (run1)
6121 while (c1 < end1)
6122 a[dest++] = std::move(tmp[c1++]);
6123 // Remaining run2 elements are already in place
6124}
6125
6130template <typename T, class Compare>
6131void merge_hi(T *a, const size_t base1, const size_t len1, const size_t base2, const size_t len2,
6132 T *tmp, const Compare &cmp)
6133{
6134 // Copy run2 into tmp
6135 for (size_t i = 0; i < len2; ++i)
6136 tmp[i] = std::move(a[base2 + i]);
6137
6138 // Merge from the right (highest to lowest)
6139 size_t dest = base2 + len2; // one past end
6140 size_t c1 = base1 + len1; // one past end of run1 in a
6141 size_t c2 = len2; // one past end of run2 in tmp
6142
6143 while (c1 > base1 and c2 > 0)
6144 if (cmp(tmp[c2 - 1], a[c1 - 1]))
6145 a[--dest] = std::move(a[--c1]);
6146 else
6147 a[--dest] = std::move(tmp[--c2]);
6148
6149 // Copy remaining from tmp (run2)
6150 while (c2 > 0)
6151 a[--dest] = std::move(tmp[--c2]);
6152 // Remaining run1 elements are already in place
6153}
6154
6160template <typename T, class Compare>
6161void merge_at(T *a, size_t *run_base, size_t *run_len, size_t &stack_size, const size_t at, T *tmp,
6162 const Compare &cmp)
6163{
6164 size_t base1 = run_base[at];
6165 size_t len1 = run_len[at];
6166 size_t base2 = run_base[at + 1];
6167 size_t len2 = run_len[at + 1];
6168
6169 // Record merged run
6170 run_len[at] = len1 + len2;
6171 if (at == stack_size - 3)
6172 {
6173 run_base[at + 1] = run_base[at + 2];
6174 run_len[at + 1] = run_len[at + 2];
6175 }
6176 --stack_size;
6177
6178 // Where does the first element of run2 go in run1?
6179 size_t k = gallop_right(a[base2], a + base1, len1, 0, cmp);
6180 base1 += k;
6181 len1 -= k;
6182 if (len1 == 0)
6183 return;
6184
6185 // Where does the last element of run1 go in run2?
6186 len2 = gallop_left(a[base1 + len1 - 1], a + base2, len2, len2 - 1, cmp);
6187 if (len2 == 0)
6188 return;
6189
6190 if (len1 <= len2)
6191 merge_lo(a, base1, len1, base2, len2, tmp, cmp);
6192 else
6193 merge_hi(a, base1, len1, base2, len2, tmp, cmp);
6194}
6195
6203template <typename T, class Compare>
6204void merge_collapse(T *a, size_t *run_base, size_t *run_len, size_t &stack_size, T *tmp,
6205 const Compare &cmp)
6206{
6207 while (stack_size > 1)
6208 {
6209 long n = static_cast<long>(stack_size) - 2;
6210 if ((n > 0 and run_len[n - 1] <= run_len[n] + run_len[n + 1]) or
6211 (n > 1 and run_len[n - 2] <= run_len[n - 1] + run_len[n]))
6212 {
6213 if (run_len[n - 1] < run_len[n + 1])
6214 --n;
6216 }
6217 else if (run_len[n] <= run_len[n + 1])
6219 else
6220 break;
6221 }
6222}
6223
6225template <typename T, class Compare>
6226void merge_force_collapse(T *a, size_t *run_base, size_t *run_len, size_t &stack_size, T *tmp,
6227 const Compare &cmp)
6228{
6229 while (stack_size > 1)
6230 {
6231 long n = static_cast<long>(stack_size) - 2;
6232 if (n > 0 and run_len[n - 1] < run_len[n + 1])
6233 --n;
6235 }
6236}
6237
6252template <typename T, class Compare>
6253void timsort_impl(T *a, const size_t n, const Compare &cmp)
6254{
6255 if (n < 2)
6256 return;
6257
6258 // For very small arrays, just use binary insertion sort
6259 if (n < 64)
6260 {
6261 const size_t run_len = count_run_and_make_ascending(a, 0, n, cmp);
6263 return;
6264 }
6265
6266 const size_t minrun = compute_minrun(n);
6267
6268 // Run stack — log2(n) + 1 entries is sufficient
6269 // (timsort invariant guarantees Fibonacci-like growth)
6270 constexpr size_t MAX_STACK = 85; // enough for 2^64 elements
6271 size_t run_base_arr[MAX_STACK];
6272 size_t run_len_arr[MAX_STACK];
6273 size_t stack_size = 0;
6274
6275 // Temporary merge buffer — allocated once, sized to n/2
6276 // (merge_lo/merge_hi copy the smaller run)
6277 Array<T> tmp_buf = Array<T>::create(n / 2 + 1);
6278 T *tmp = &tmp_buf[0];
6279
6280 size_t lo = 0;
6281 size_t remaining = n;
6282
6283 while (remaining > 0)
6284 {
6285 // Find the next natural run
6286 size_t run_len = count_run_and_make_ascending(a, lo, lo + remaining, cmp);
6287
6288 // If the run is too short, extend it with insertion sort
6289 if (run_len < minrun)
6290 {
6291 const size_t force = remaining < minrun ? remaining : minrun;
6292 binary_insertion_sort(a, lo, lo + force, lo + run_len, cmp);
6293 run_len = force;
6294 }
6295
6296 // Push this run onto the stack
6298 << "timsort: run stack overflow (stack_size=" << stack_size << " >= MAX_STACK=" << MAX_STACK
6299 << "); this should never happen";
6302 ++stack_size;
6303
6304 // Maintain the stack invariants
6306
6307 lo += run_len;
6308 remaining -= run_len;
6309 }
6310
6311 // Force-merge all remaining runs
6313}
6314} // namespace timsort_detail
6315
6343template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
6344void timsort(T *a, const size_t n, const Compare &cmp = Compare())
6345{
6346 if (n == 0)
6347 return;
6348
6349 ah_invalid_argument_if(a == nullptr) << "timsort(): null pointer with non-zero length";
6350
6351 if (n < 2)
6352 return;
6353
6356}
6357
6370template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
6371void timsort(T *a, const long l, const long r, const Compare &cmp = Compare())
6372{
6373 ah_range_error_if(l < 0 or r < 0)
6374 << "timsort(): negative bounds [" << l << ", " << r << "] are not allowed";
6375
6376 if (l >= r)
6377 return;
6378
6379 ah_invalid_argument_if(a == nullptr)
6380 << "timsort(): null pointer contract violation for range [" << l << ", " << r
6381 << "]; cannot call timsort_detail::timsort_impl()";
6382
6384 timsort_detail::timsort_impl(a + l, static_cast<size_t>(r - l + 1), cmp);
6385}
6386
6396template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
6397void timsort(DynArray<T> &a, const Compare &cmp = Compare())
6398{
6399 bucket_sort_detail::apply_sort_with_temp<T>(
6400 a,
6401 bucket_sort_detail::sort_invoker_factory<static_cast<void (*)(T *, size_t, const Compare &)>(
6402 timsort)>{}(cmp),
6403 cmp);
6404}
6405
6415template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
6416void timsort(Array<T> &a, const Compare &cmp = Compare())
6417{
6418 if (a.size() < 2)
6419 return;
6420 timsort(&a[0], a.size(), cmp);
6421}
6422
6433template <typename T, size_t N, StrictWeakOrder<T> Compare = Aleph::less<T>>
6434 requires std::is_invocable_r_v<bool, Compare, const T &, const T &>
6435void timsort(T (&a)[N], const Compare &cmp = Compare())
6436{
6437 timsort(static_cast<T *>(a), N, cmp);
6438}
6439
6452template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
6453void timsort(DynList<T> &list, const Compare &cmp = Compare())
6454{
6455 bucket_sort_detail::apply_sort_with_temp<T>(
6456 list,
6457 bucket_sort_detail::sort_invoker_factory<static_cast<void (*)(T *, size_t, const Compare &)>(
6458 timsort)>{}(cmp),
6459 cmp);
6460}
6461
6474template <typename T, StrictWeakOrder<T> Compare = Aleph::less<T>>
6475void timsort(DynDlist<T> &list, const Compare &cmp = Compare())
6476{
6477 bucket_sort_detail::apply_sort_with_temp<T>(
6478 list,
6479 bucket_sort_detail::sort_invoker_factory<static_cast<void (*)(T *, size_t, const Compare &)>(
6480 timsort)>{}(cmp),
6481 cmp);
6482}
6483} // namespace Aleph
6484
6485#endif // TPL_SORT_UTILS_H
C++20 concepts hub: comparison, BST policy, and Aleph container concepts.
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
Definition ah-errors.H:255
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
#define ah_logic_error()
Throws std::logic_error unconditionally.
Definition ah-errors.H:346
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:644
#define ah_range_error_if(C)
Throws std::range_error if condition holds.
Definition ah-errors.H:212
#define ah_out_of_range_error()
Throws std::out_of_range unconditionally.
Definition ah-errors.H:616
Functional programming utilities for Aleph-w containers.
General utility functions and helpers.
long double h
Definition btreepic.C:154
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:196
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
void putn(const size_t n)
Reserve n additional logical slots in the array without value-initializing them.
Definition tpl_array.H:310
Helper class to compare nodes of a linked list.
bool operator()(Tlink *l1, Tlink *l2) const noexcept(noexcept(std::declval< const Compare & >()(std::declval< const T & >(), std::declval< const T & >())))
Compares two nodes based on their data.
Compare_Tnode(Compare cmp_fct=Compare()) noexcept(std::is_nothrow_copy_constructible_v< Compare >)
Construct from a comparison functor.
Node belonging to a double circular linked list with header node.
Definition tpl_dnode.H:106
Dnode< T > * remove_first_ne() noexcept
Remove the first node and return its address.
Definition tpl_dnode.H:152
T & get_data() noexcept
Return a modifiable reference to the data contained in the node.
Definition tpl_dnode.H:232
size_t size() const noexcept
Return the current dimension of array.
bool exist(const size_t i) const
Return true if the i-th entry is accessible.
Iterator dynamic list.
void next_ne() noexcept
Move the iterator one position forward guaranteeing no exception.
T & get_curr_ne() const noexcept
Dynamic doubly linked list with O(1) size and bidirectional access.
const size_t & size() const noexcept
Return the number of elements (constant time)
Iterator on the items of list.
Definition htlist.H:1420
T & get_curr() const
Return the current item.
Definition htlist.H:1446
T & get_curr_ne() const noexcept
Return the current link guaranteeing no exception. Be careful.
Definition htlist.H:1436
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T remove_first_ne() noexcept
Definition htlist.H:1322
T & get_first_ne() const noexcept
Return the first item of the list without exception.
Definition htlist.H:1353
Fixed length stack.
size_t size() const noexcept
Return the number of elements stored in the stack.
bool is_empty() const noexcept
Return true if stack is empty.
T pop() noexcept
Pop by moving the top of stack.
T & push(const T &data) noexcept(std::is_nothrow_copy_assignable_v< T >)
Push a copy of data
void next_ne() noexcept
Move the iterator one position forward guaranteeing no exception.
Definition htlist.H:965
Slinknc * get_curr() const
Definition htlist.H:952
bool has_curr() const noexcept
Definition htlist.H:930
Single linked list of nodes.
Definition htlist.H:403
Slinknc * get_first() const noexcept
Definition htlist.H:443
constexpr bool is_empty() const noexcept
Definition htlist.H:419
void concat_list(HTList &l) noexcept
Definition htlist.H:542
void append(Slinknc *link) noexcept
Definition htlist.H:495
size_t split_list(HTList &l, HTList &r) noexcept
It divides 'this' into two equal lists without modifying.
Definition htlist.H:731
Slinknc * remove_first_ne() noexcept
Definition htlist.H:639
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1065
void insert(Slinknc *link) noexcept
Definition htlist.H:473
Slinknc * get_last() const noexcept
Return the last item of the list (nullptr if the list is empty)
Definition htlist.H:446
size_t split_list_ne(HTList &l, HTList &r) noexcept
Definition htlist.H:772
bool is_unitarian_or_empty() const noexcept
Return true if list contains one element or is empty.
Definition htlist.H:431
Comparator wrapper that inverts the comparison order.
Negate_Compare(Compare __cmp=Compare()) noexcept(std::is_nothrow_copy_assignable_v< Compare >)
Construct from a comparator.
bool operator()(const T &e1, const T &e2) const noexcept(noexcept(std::declval< const Compare & >()(e2, e1)))
Compare in reverse order: returns cmp(e2, e1).
Link of a single linked list non-circular and without header node.
Definition htlist.H:95
void insert(Slinknc *p) noexcept
insert(p) inserts the node pointed by p after this.
Definition htlist.H:143
Value concept accepted by counting sort overloads.
Integral value accepted by linear-time integer sorting algorithms.
Value concept accepted by radix sort overloads.
#define N
Definition fib.C:294
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_min_function > > min(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4122
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4129
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Singly linked list implementations with head-tail access.
const long double offset[]
Offset values indexed by symbol string length (bounded by MAX_OFFSET_INDEX)
void insertion_sort_range(T *a, const size_t lo, const size_t hi, const Compare &cmp) noexcept(std::is_nothrow_move_constructible_v< T > and std::is_nothrow_move_assignable_v< T > and noexcept(std::declval< const Compare & >()(std::declval< const T & >(), std::declval< const T & >())))
Insertion sort a sub-array [lo, hi).
void apply_sort_with_temp(Container &a, SortFn sort_fn, const Compare &cmp)
Helper to sort any container by copying to a temporary array.
void bucket_sort_impl(T *a, const size_t n, const size_t num_buckets, const BucketKey &bucket_key, const Compare &cmp)
Core bucket sort implementation on a raw pointer range.
void radix_sort_impl(C< IntT > &a)
Internal radix-sort implementation for array-like containers.
constexpr radix_unsigned_t< IntT > radix_key(const IntT value) noexcept
Map an integer value to an unsigned radix key.
IntT counting_value_from_bucket(const size_t bucket, const IntT min_key) noexcept
Retrieve the original value from a bucket index in counting sort.
std::make_unsigned_t< std::remove_cv_t< IntT > > counting_unsigned_t
Unsigned counterpart used to measure counting-sort key ranges.
std::make_unsigned_t< std::remove_cv_t< IntT > > radix_unsigned_t
Unsigned key type used by radix sort passes.
size_t counting_bucket(const IntT value, const IntT min_key) noexcept
Map a value to its bucket index in counting sort.
size_t counting_bucket_count(const IntT min_key, const IntT max_key)
Compute the number of buckets required for a counting sort range.
void counting_sort_impl(C< IntT > &a)
Internal implementation of counting sort for containers.
void merge_force_collapse(T *a, size_t *run_base, size_t *run_len, size_t &stack_size, T *tmp, const Compare &cmp)
Force-merge all remaining runs at the end.
void merge_at(T *a, size_t *run_base, size_t *run_len, size_t &stack_size, const size_t at, T *tmp, const Compare &cmp)
Merge runs[at] with runs[at+1].
void binary_insertion_sort(T *a, const size_t lo, const size_t hi, const size_t start, const Compare &cmp) noexcept(std::is_nothrow_move_constructible_v< T > and std::is_nothrow_move_assignable_v< T > and noexcept(std::declval< const Compare & >()(std::declval< const T & >(), std::declval< const T & >())))
Binary insertion sort on [lo, hi) starting from start.
void timsort_impl(T *a, const size_t n, const Compare &cmp)
Core Timsort implementation.
size_t count_run_and_make_ascending(T *a, const size_t lo, const size_t hi, const Compare &cmp)
Find the length of the natural run starting at lo.
size_t gallop_left(const T &key, const T *a, const size_t len, const size_t hint, const Compare &cmp) noexcept(noexcept(std::declval< const Compare & >()(std::declval< const T & >(), std::declval< const T & >())))
Gallop left: find the insertion point for a key in a sorted range.
void merge_hi(T *a, const size_t base1, const size_t len1, const size_t base2, const size_t len2, T *tmp, const Compare &cmp)
Merge two adjacent sorted runs where len1 >= len2.
void merge_collapse(T *a, size_t *run_base, size_t *run_len, size_t &stack_size, T *tmp, const Compare &cmp)
Maintain the timsort run stack invariants.
void merge_lo(T *a, const size_t base1, const size_t len1, const size_t base2, const size_t len2, T *tmp, const Compare &cmp)
Merge two adjacent sorted runs a[base1..base1+len1) and a[base2..base2+len2) using temporary buffer.
size_t gallop_right(const T &key, const T *a, const size_t len, const size_t hint, const Compare &cmp) noexcept(noexcept(std::declval< const Compare & >()(std::declval< const T & >(), std::declval< const T & >())))
Gallop right: find the insertion point for a key in a sorted range.
constexpr size_t compute_minrun(size_t n) noexcept
Compute the minimum run length for timsort.
void reverse_range(T *a, size_t lo, size_t hi) noexcept(std::is_nothrow_swappable_v< T >)
Reverse elements in [lo, hi).
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Array< const T * > build_index_ptr(const C< T > &a, const Compare &cmp=Compare())
Build an index array of pointers for indirect sorting (const version).
DynList< size_t > binary_search_dup(const C< T > &a, const T &x, const Compare &cmp=Compare())
Binary search for all occurrences of a value.
and std::convertible_to< std::invoke_result_t< const KeyFn &, size_t >, int > void counting_sort_indices(Array< size_t > &sa, Array< size_t > &tmp, const size_t n, const int min_key, const int max_key, const KeyFn &key_of)
Stable counting sort on an index array by integer keys.
long search_min(T *a, const long l, const long r, const Compare &cmp=Compare())
Returns the smallest element of the array a between l and r.
bool is_sorted(const Container< T > &cont, const Compare &cmp=Compare())
Check if a container is sorted in ascending order.
static std::pair< long, long > partition_three_way(T *a, long l, long r, const Compare &cmp)
long select_pivot(T *a, long l, long r, const Compare &cmp=Compare()) noexcept(noexcept(cmp(a[0], a[0])) &&std::is_nothrow_swappable_v< T >)
Select a pivot for partitioning using median-of-three.
Array< const T * > stable_build_index_ptr(const C< T > &a, const Compare &cmp=Compare())
Build a stable index array of pointers for indirect sorting (const version).
static void insertion_sort_range(Container &a, long l, long r, const Compare &cmp)
long select_pivot_op_impl(const Container &a, const long l, const long r, const Compare &cmp)
Implementation of pivot selection using operator().
std::pair< bool, size_t > search_inversion(const Container< T > &cont, const Compare &cmp=Compare())
Find the first inversion in a container.
void heapsort_subrange(DynArray< T > &a, const long l, const long r, const Compare &cmp)
Heapsort implementation for a subrange of a DynArray.
void heapsort(T *array, const size_t n, const Compare &cmp=Compare())
Sort an array using the heapsort algorithm.
size_t introsort_depth_limit(size_t n) noexcept
Forward declaration.
bool eq(const C1 &c1, const C2 &c2, Eq e=Eq())
Check equality of two containers using a predicate.
long select_pivot_op(const DynArray< T > &a, const long l, const long r, const Compare &cmp=Compare())
Selects a pivot element as the median between the ends and the center.
void faster_heapsort(T *array, const size_t n, const Compare &cmp=Compare())
Optimized version of heapsort.
Dnode< T > * dlink_random_search(Dlink &list, const T &x, const Compare &cmp=Compare())
Random search for an element in a dlink list.
Array< size_t > stable_build_index(const C &a, const Compare &cmp=Compare())
Build a stable index array for indirect sorting.
void sift_down(T *ptr, const size_t l, const size_t r, Compare &cmp)
Restore the heap property by moving the element at position l downwards.
void sift_down_subrange(DynArray< T > &a, long start, const long n, const long offset, const Compare &cmp)
Sift down operation for heapsort on a DynArray subrange.
void quicksort_no_tail(T *a, long l, long r, const Compare &cmp=Compare())
Quicksort implementation with tail-recursion optimization.
long random_search(T *a, const T &x, const long l, const long r, const Compare &cmp=Compare())
Random search for an element in an array.
size_t Quicksort_Threshold
Threshold for using quicksort vs simpler algorithms.
void insertion_sort(T *a, const long l, const long r, const Compare &cmp=Compare()) noexcept(noexcept(cmp(std::declval< T & >(), std::declval< T & >())) and std::is_nothrow_move_constructible_v< T > and std::is_nothrow_move_assignable_v< T >)
Sort an array using insertion sort.
const int Not_Found
Return value for search functions when element is not found.
void counting_sort(DynArray< T > &a)
Stable counting sort for integral DynArray values.
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.
std::pair< bool, size_t > test_sorted(const Container< T > &cont, const Compare &cmp=Compare())
Test if a container is sorted, returning the inversion position.
void introsort_loop(T *a, long l, long r, size_t depth_limit, const Compare &cmp)
Internal recursive function for introsort.
long search_max(T *a, const long l, const long r, const Compare &cmp=Compare())
Returns the maximum element of the array a between l and r.
void timsort(T *a, const size_t n, const Compare &cmp=Compare())
Timsort — adaptive, stable, natural merge sort.
void mergeinsertsort(Tlist< T > &list, const Compare &cmp=Compare(), const size_t lsz=Aleph::Insertion_Threshold)
Sort a list by mergesort combined with the insert method.
void insert_sorted(Dlink &list, Dlink *p, const Compare &cmp)
Inserts a node orderly into a doubly linked list.
Dlink * dlink_random_select(Dlink &list, const size_t i, const Compare &cmp=Compare())
Random selection of the ith element from a list based on Dlink.
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
T * bsearch(C< T > &a, const T &x, const Compare &cmp=Compare())
Search for a value in a sorted container returning a pointer.
Array< size_t > build_index(const C &a, const Compare &cmp=Compare())
Build an index array for indirect sorting.
void list_insertion_sort(ListType &list, const Compare &cmp)
Generic insertion sort for linked lists.
const T & random_select(DynArray< T > &a, const long i, const Compare &cmp=Compare())
Select the i-th smallest element in a DynArray.
void bucket_sort(T *a, const size_t n, const size_t num_buckets, const BucketKey &bucket_key, const Compare &cmp=Compare())
Bucket sort with user-supplied bucket mapping.
static long back_index(const long i) noexcept
Convert a 1-based heap index to a 0-based array index.
void bubble_sort(DynArray< T > &a, const Compare &cmp=Compare())
Sort a dynamic array using bubble sort.
bool diff(const C1 &c1, const C2 &c2, Eq e=Eq())
Check if two containers differ.
void quicksort(T *a, const long l, const long r, const Compare &cmp=Compare())
Sort an array using iterative quicksort with optimizations.
void radix_sort(DynArray< T > &a)
LSD radix sort for integral DynArray values.
void mergesort(T *a, const long l, const long r, Array< T > &buf, Compare cmp)
Sort an array using merge sort with a reusable buffer.
void shellsort(DynArray< T > &a, const Compare &cmp=Compare())
Sort a dynamic array using Shell sort.
bool are_equals(const T &op1, const T &op2, Compare &&cmp=Compare())
Determines if operands are equal using a comparison operator.
Definition ahFunction.H:983
static std::pair< long, long > partition_three_way_op(Container &a, long l, long r, const Compare &cmp)
Itor3 merge(Itor1 source1Beg, Itor1 source1End, Itor2 source2Beg, Itor2 source2End, Itor3 destBeg)
Merge two sorted ranges.
Definition ahAlgo.H:1410
void quicksort_rec_min(T *a, const long l, const long r, const Compare &cmp=Compare())
Sorts an array according to the quicksort method with minimum space consumption.
long sequential_search(T *a, const T &x, const long l, const long r, Equal eq=Equal())
Linear search for an element in an array.
Link * search_extreme(const Link &list, const Compare &cmp)
Find the extreme (minimum or maximum) element in a linked list.
bool binary_search(Itor beg, Itor end, const T &value)
Binary search for a value.
Definition ahAlgo.H:1284
static const T & __random_select(T *a, const long i, long l, long r, const Compare &cmp)
void quicksort_rec(T *a, const long l, const long r, const Compare &cmp=Compare())
Recursively sort an array using quicksort.
DynList< long > binindex_dup(const C< T > &a, const T &x, const Compare &cmp=Compare())
Returns the indices of all occurrences of a value in a sorted container.
bool is_inversely_sorted(const Container< T > &cont, const Compare &cmp=Compare())
Check if a container is sorted in descending order.
DynList< const T * > bsearch_dup(const C< T > &a, const T &x, const Compare &cmp=Compare())
Search for all occurrences of a value returning pointers (const version).
long partition_op_impl(Container &a, const long l, const long r, const Compare &cmp)
Implementation of partitioning using operator().
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 selection_sort(T *a, const size_t n, const Compare &cmp=Compare()) noexcept(noexcept(cmp(a[0], a[0])) &&std::is_nothrow_swappable_v< T >)
Sort an array using the selection sort algorithm.
void introsort(T *a, const long l, const long r, const Compare &cmp=Compare())
Sort an array using introsort (introspective sort).
long binindex(const C< T > &a, const T &x, const Compare &cmp=Compare())
Returns the index where a value appears (or should be inserted) in a sorted container.
void quicksort_insertion(T *a, const long l, const long r, const Compare &cmp=Compare())
Sorts an array by the improved quicksort method.
size_t Insertion_Threshold
Threshold for switching to insertion sort in hybrid algorithms.
long binary_search_rec(T *a, const T &x, const long l, const long r, const Compare &cmp=Compare())
Recursive binary search on an ordered array.
long partition_op(DynArray< T > &a, const long l, const long r, const Compare &cmp=Compare())
Partition a DynArray using operator().
T & sift_up(T *ptr, const size_t l, const size_t r, Compare &cmp)
Restore the heap property by moving the element at position r upwards.
void merge_lists(Tlist &l1, Tlist &l2, Tlist &result, const Compare &cmp=Compare())
Merge two sorted lists into a single sorted list.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
void push2(Stack &stack, const A &a, const B &b)
Push two values onto a stack.
void quicksort_op(C< T > &a, const Compare &cmp=Compare(), const size_t threshold=Quicksort_Threshold)
Optimized quicksort for containers using operator().
STL namespace.
Comparator specialization for Dnode objects.
Compare_Dnode(const Compare &cmp=Compare()) noexcept(std::is_nothrow_copy_constructible_v< Compare >)
Factory for bucket_sort with extra parameters.
Factory for lambdas that forward to a sort function.
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
DynList< int > l1
DynList< int > l2
static int * k
gsl_rng * r
Fixed-capacity binary heap and heapsort algorithms.
Stack implementations backed by dynamic or fixed arrays.
Dynamic array container with automatic resizing.
Lazy and scalable dynamic array implementation.
Dynamic doubly linked list implementation.
DynList< int > l