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
31
95#ifndef TPL_SORT_UTILS_H
96#define TPL_SORT_UTILS_H
97
98#include <ahUtils.H>
99#include <ahFunctional.H>
100#include <ah-concepts.H>
101#include <ah-errors.H>
102#include <tpl_arrayStack.H>
103#include <tpl_array.H>
104#include <tpl_dynArray.H>
105#include <tpl_dynDlist.H>
106#include <tpl_arrayHeap.H>
107#include <htlist.H>
108#include <limits>
109#include <type_traits>
110#include <vector>
111#include <array>
112#include <utility>
113#include <cmath>
114#include <algorithm>
115
116namespace Aleph
117{
128 extern size_t Insertion_Threshold;
129
136 extern size_t Quicksort_Threshold;
137
144 extern const int Not_Found;
145
166 template <template <typename> class Container, typename T,
167 class Compare = Aleph::less<T>>
168 [[nodiscard]] bool is_sorted(const Container<T> & cont, const Compare & cmp = Compare())
169 {
170 if (cont.is_empty())
171 return true;
172
173 typename Container<T>::Iterator it(cont);
174 const T & first = it.get_curr();
175 const T *prev = &first;
176 it.next_ne();
177 for (; it.has_curr(); it.next_ne())
178 {
179 const T & curr = it.get_curr();
180 if (cmp(curr, *prev))
181 return false;
182 prev = &curr;
183 }
184 return true;
185 }
186
209 template <template <typename> class Container, typename T,
210 class Compare = Aleph::less<T>>
211 [[nodiscard]] std::pair<bool, size_t> search_inversion(const Container<T> & cont,
212 const Compare & cmp = Compare())
213 {
214 if (cont.is_empty())
215 return std::make_pair(true, 0);
216
217 typename Container<T>::Iterator it(cont);
218 const T & first = it.get_curr();
219 const T *prev = &first;
220 size_t i = 1;
221 it.next_ne();
222 for (; it.has_curr(); it.next_ne(), ++i)
223 {
224 const T & curr = it.get_curr();
225 if (cmp(curr, *prev))
226 return std::make_pair(false, i);
227 prev = &curr;
228 }
229
230 return std::make_pair(true, i);
231 }
232
252 template <template <typename> class Container, typename T,
253 class Compare = Aleph::less<T>>
254 [[nodiscard]] bool is_inversely_sorted(const Container<T> & cont, const Compare & cmp = Compare())
255 {
256 if (cont.is_empty())
257 return true;
258
259 typename Container<T>::Iterator it(cont);
260 const T & first = it.get_curr();
261 const T *prev = &first;
262 it.next_ne();
263 for (; it.has_curr(); it.next_ne())
264 {
265 const T & curr = it.get_curr();
266 if (cmp(*prev, curr))
267 return false;
268 prev = &curr;
269 }
270 return true;
271 }
272
295 template <template <typename> class Container, typename T,
296 class Compare = Aleph::less<T>>
297 [[nodiscard]] std::pair<bool, size_t>
298 test_sorted(const Container<T> & cont, const Compare & cmp = Compare())
299 {
300 if (cont.is_empty())
301 return {true, 0};
302
303 typename Container<T>::Iterator it(cont);
304 const T & first = it.get_curr();
305 const T *prev = &first;
306 size_t i = 1;
307 it.next_ne();
308 for (; it.has_curr(); it.next_ne(), ++i)
309 {
310 const T & curr = it.get_curr();
311 if (cmp(curr, *prev))
312 return {false, i};
313 prev = &curr;
314 }
315 return {true, i};
316 }
317
318
352 template <typename T, class Compare = Aleph::less<T>>
353 inline
354 void selection_sort(T *a, const size_t n, const Compare & cmp = Compare())
356 {
357 if (n < 2)
358 return;
359
360 for (size_t i = 0, min, j; i < n - 1; ++i)
361 {
362 for (min = i, j = i + 1; j < n; ++j)
363 if (cmp(a[j], a[min]))
364 min = j;
365
366 if (cmp(a[min], a[i]))
367 std::swap(a[min], a[i]);
368 }
369 }
370
393 template <class Link, class Compare>
394 inline
395 Link * search_extreme(const Link & list, const Compare & cmp)
396 {
397 typename Link::Iterator it(const_cast<Link &>(list));
398 Link *extreme = it.get_curr();
399
400 for (it.next(); it.has_curr(); it.next_ne())
401 {
402 Link *curr = it.get_curr();
403 if (cmp(curr, extreme))
404 extreme = curr;
405 }
406
407 return extreme;
408 }
409
410 template <typename T, class Compare = Aleph::less<T>>
411 inline
412 const T * search_extreme(const DynList<T> & list, const Compare & cmp = Compare())
413 {
414 if (list.is_empty())
415 return nullptr;
416
417 typename DynList<T>::Iterator it(list);
418 const T *extreme = &it.get_curr();
419 it.next_ne();
420 for (; it.has_curr(); it.next_ne())
421 {
422 const T & curr = it.get_curr();
423 if (cmp(curr, *extreme))
424 extreme = &curr;
425 }
426
427 return extreme;
428 }
429
430 template <class Compare>
431 inline
432 Dlink * search_extreme(const Dlink & list, const Compare & cmp = Compare())
433 {
435 }
436
437 template <class Compare>
438 inline
439 Slinknc * search_extreme(const Slinknc & list, const Compare & cmp = Compare())
440 {
442 }
443
453 template <class Compare>
454 inline
455 void selection_sort(Dlink & list, Compare cmp)
456 {
457 Dlink aux;
458 while (not list.is_empty())
459 {
461 extreme->del(); // remove extreme from list
462 aux.append(extreme); // insert it ordered into aux
463 }
464
465 list.swap(&aux);
466 }
467
477 template <typename Tlink,
478 template <class> class Tnode,
479 typename T, class Compare>
481 {
482 Compare cmp;
483
484 public:
487 : cmp(cmp_fct)
488 { /* empty */
489 }
490
492 bool operator ()(Tlink *l1, Tlink *l2) const
493 noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(), std::declval<const T &>())))
494 {
495 auto *n1 = static_cast<Tnode<T> *>(l1);
496 auto *n2 = static_cast<Tnode<T> *>(l2);
497
498 assert(n1 == l1 and n2 == l2);
499
500 return cmp(n1->get_data(), n2->get_data());
501 }
502
504 bool operator ()(Tlink *l, const T & x) const
505 noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(), std::declval<const T &>())))
506 {
507 auto *n = static_cast<Tnode<T> *>(l);
508
509 assert(n == l);
510
511 return cmp(n->get_data(), x);
512 }
513 };
514
518 template <typename T, class Compare>
519 struct Compare_Dnode : public Compare_Tnode<Dlink, Dnode, T, Compare>
520 {
521 Compare_Dnode(const Compare & cmp = Compare())
523 : Compare_Tnode<Dlink, Dnode, T, Compare>(cmp)
524 { /* empty */
525 }
526 };
527
531 template <typename T, class Compare>
533
534
542 template <typename T, class Compare = Aleph::less<T>>
543 inline void selection_sort(Dnode<T> & list, Compare cmp)
544 {
547 }
548
570 template <typename T, class Equal = Aleph::equal_to<T>>
571 [[nodiscard]] inline
572 long sequential_search(T *a, const T & x, const long l, const long r,
573 Equal eq = Equal())
574 {
575 for (long i = l; i <= r; ++i)
576 if (eq(a[i], x)) return i;
577
578 return Not_Found;
579 }
580
598 template <typename T, class Equal = Aleph::equal_to<T>>
599 inline
600 long sequential_search(const DynArray<T> & a, const T & x,
601 const long l, const long r, Equal eq = Equal())
602 {
603 for (long i = l; i <= r; ++i)
604 if (a.exist(i))
605 if (eq(a(i), x))
606 return i;
607
608 return Not_Found;
609 }
610
623 template <class Link, typename T, class Equal>
624 Link * sequential_search(const Link & list, const T & x, Equal & eq)
625 {
626 for (typename Link::Iterator it(const_cast<Link &>(list));
627 it.has_curr(); it.next_ne())
628 if (Link *curr = it.get_curr(); eq(curr, x))
629 return curr;
630
631 return nullptr;
632 }
633
640 template <typename T, class Equal = Aleph::equal_to<T>>
641 Dlink * sequential_search(const Dlink & list, const T & x,
642 Equal eq = Equal())
643 {
646 }
647
654 template <typename T, class Equal = Aleph::equal_to<T>>
655 Slinknc * sequential_search(const Slinknc & list, const T & x, Equal eq = Equal())
656 {
659 }
660
667 template <typename T, class Equal = Aleph::equal_to<T>>
668 inline
669 Dnode<T> * sequential_search(Dnode<T> & list, const T & x, Equal & eq)
670 {
673
674 return ret == nullptr ? nullptr : static_cast<Dnode<T> *>(ret);
675 }
676
678 template <typename T, class Equal = Aleph::equal_to<T>>
679 inline
680 Dnode<T> * sequential_search(Dnode<T> & list, const T & x, Equal && eq = Equal())
681 {
682 return sequential_search<T, Equal>(list, x, eq);
683 }
684
691 template <typename T, class Equal = Aleph::equal_to<T>>
692 inline
693 const Dnode<T> * sequential_search(const Dnode<T> & list, const T & x, Equal & eq)
694 {
696 const auto & base = static_cast<const Dlink &>(list);
698
699 return ret == nullptr ? nullptr : static_cast<const Dnode<T> *>(ret);
700 }
701
703 template <typename T, class Equal = Aleph::equal_to<T>>
704 inline
705 const Dnode<T> * sequential_search(const Dnode<T> & list, const T & x, Equal && eq = Equal())
706 {
707 return sequential_search<T, Equal>(list, x, eq);
708 }
709
716 template <typename T, class Equal = Aleph::equal_to<T>>
717 inline
718 T * sequential_search(DynDlist<T> & list, const T & x, Equal eq = Equal())
719 {
720 Dnode<T> *ret = sequential_search<T, Equal>(static_cast<Dnode<T> &>(list), x, eq);
721 return ret != nullptr ? &ret->get_data() : nullptr;
722 }
723
730 template <typename T, class Equal = Aleph::equal_to<T>>
731 inline
732 const T * sequential_search(const DynDlist<T> & list, const T & x, Equal eq = Equal())
733 {
734 const Dnode<T> *ret = sequential_search<T, Equal>(static_cast<const Dnode<T> &>(list), x, eq);
735 return ret != nullptr ? &ret->get_data() : nullptr;
736 }
737
744 template <typename T, class Equal = Aleph::equal_to<T>>
745 inline
746 T * sequential_search(DynList<T> & list, const T & x, Equal eq = Equal())
747 {
748 for (typename DynList<T>::Iterator it(list); it.has_curr(); it.next_ne())
749 {
750 T & curr = it.get_curr_ne();
751 if (eq(curr, x))
752 return &curr;
753 }
754 return nullptr;
755 }
756
763 template <typename T, class Equal = Aleph::equal_to<T>>
764 inline
765 const T * sequential_search(const DynList<T> & list, const T & x, Equal eq = Equal())
766 {
767 for (typename DynList<T>::Iterator it(list); it.has_curr(); it.next_ne())
768 {
769 const T & curr = it.get_curr_ne();
770 if (eq(curr, x))
771 return &curr;
772 }
773 return nullptr;
774 }
775
776
791 template <typename T, class Compare = Aleph::less<T>>
792 inline
793 long search_extreme(T *a, const long l, const long r, const Compare & cmp = Compare())
794 {
795 long extreme_index = l;
796 for (long i = l + 1; i <= r; ++i)
797 if (cmp(a[i], a[extreme_index])) // is there a new minimum?
798 extreme_index = i; // yes
799
800 return extreme_index;
801 }
802
807 template <typename T, class Compare = Aleph::less<T>>
808 inline
809 long search_min(T *a, const long l, const long r, const Compare & cmp = Compare())
810 {
811 return search_extreme<T, Compare>(a, l, r, cmp);
812 }
813
818 template <typename T, class Compare = Aleph::greater<T>>
819 inline
820 long search_max(T *a, const long l, const long r, const Compare & cmp = Compare())
821 {
822 return search_extreme<T, Compare>(a, l, r, cmp);
823 }
824
828 template <typename T, class Compare = Aleph::less<T>>
829 inline
830 Dnode<T> * search_extreme(const Dnode<T> & list, const Compare & cmp = Compare())
831 {
833 Dlink *ret =
835 (const_cast<Dlink &>(static_cast<const Dlink &>(list)), cmp_dnode);
836
837 return static_cast<Dnode<T> *>(ret);
838 }
839
850 template <typename T, class Compare = Aleph::less<T>>
851 inline
852 T * search_extreme(DynDlist<T> & list, const Compare & cmp = Compare())
853 {
854 const auto & base = static_cast<const Dnode<T> &>(list);
855 Dnode<T> *ret = search_extreme<T, Compare>(const_cast<Dnode<T> &>(base), cmp);
856
857 return ret != nullptr ? &(ret->get_data()) : nullptr;
858 }
859
861 template <typename T, class Compare = Aleph::less<T>>
862 inline
863 const T * search_extreme(const DynDlist<T> & list, const Compare & cmp = Compare())
864 {
865 const auto & base = static_cast<const Dnode<T> &>(list);
866 Dnode<T> *ret = search_extreme<T, Compare>(const_cast<Dnode<T> &>(base), cmp);
867
868 return ret != nullptr ? &(ret->get_data()) : nullptr;
869 }
870
876 template <typename T, class Compare = Aleph::less<T>>
877 inline
878 T * search_extreme(DynList<T> & list, const Compare & cmp = Compare())
879 {
880 if (list.is_empty())
881 return nullptr;
882
883 typename DynList<T>::Iterator it(list);
884 T *extreme = &it.get_curr_ne();
885 it.next_ne();
886 for (; it.has_curr(); it.next_ne())
887 {
888 T & curr = it.get_curr_ne();
889 if (cmp(curr, *extreme))
890 extreme = &curr;
891 }
892
893 return extreme;
894 }
895
901 template <typename T, class Compare = Aleph::less<T>>
902 inline
903 T * search_min(DynDlist<T> & list, const Compare & cmp = Compare())
904 {
905 return search_extreme<T, Compare>(list, cmp);
906 }
907
909 template <typename T, class Compare = Aleph::less<T>>
910 inline
911 const T * search_min(const DynDlist<T> & list, const Compare & cmp = Compare())
912 {
913 return search_extreme<T, Compare>(list, cmp);
914 }
915
921 template <typename T, class Compare = Aleph::greater<T>>
922 inline
923 T * search_max(DynDlist<T> & list, const Compare & cmp = Compare())
924 {
925 return search_extreme<T, Compare>(list, cmp);
926 }
927
929 template <typename T, class Compare = Aleph::greater<T>>
930 inline
931 const T * search_max(const DynDlist<T> & list, const Compare & cmp = Compare())
932 {
933 return search_extreme<T, Compare>(list, cmp);
934 }
935
971 template <typename T, class Compare = Aleph::less<T>>
972 inline
973 void insertion_sort(T *a, const long l, const long r,
974 const Compare & cmp = Compare())
978 {
979 if (l >= r)
980 return;
981 for (long i = l + 1, j; i <= r; ++i)
982 {
983 T tmp = std::move(a[i]); // store a[i], since it will be overwritten
984 for (j = i; j > l and cmp(tmp, a[j - 1]); --j)
985 a[j] = std::move(a[j - 1]); // shift to the right
986
987 a[j] = std::move(tmp); // insert tmp into the gap
988 }
989 }
990
1004 template <class Compare>
1005 inline
1006 void insert_sorted(Dlink & list, Dlink *p, const Compare & cmp)
1007 {
1008 Dlink::Iterator it(list);
1009 while (it.has_curr() and cmp(it.get_curr(), p))
1010 it.next_ne();
1011
1012 if (it.has_curr())
1013 it.get_curr()->append(p); // insert before current
1014 else
1015 list.append(p);
1016 }
1017
1018 template <class Compare>
1019 inline
1020 void insert_sorted(HTList & list, Slinknc *p, const Compare & cmp)
1021 {
1022 // Handle empty list case
1023 if (list.is_empty())
1024 {
1025 list.insert(p);
1026 return;
1027 }
1028
1029 if (Slinknc *first = list.get_first(); cmp(p, first) or not cmp(first, p)) // p <= first?
1030 {
1031 list.insert(p);
1032 return;
1033 }
1034
1035 if (Slinknc *last = list.get_last(); cmp(last, p) or not cmp(p, last)) // p >= last?
1036 {
1037 list.append(p);
1038 return;
1039 }
1040
1041 Slinknc *prev = list.get_first();
1042 HTList::Iterator it(list);
1043 for (it.next(); it.has_curr(); it.next_ne())
1044 {
1045 Slinknc *curr = it.get_curr();
1046 if (cmp(p, curr)) // p < curr
1047 {
1048 prev->insert(p);
1049 return;
1050 }
1051 prev = curr;
1052 }
1054 << "insert_sorted(): reached end of list traversal without inserting";
1055 }
1056
1064 template <class ListType, class Compare>
1065 inline
1066 void list_insertion_sort(ListType & list, const Compare & cmp)
1067 {
1068 if (list.is_empty())
1069 return;
1070
1071 ListType aux;
1072 aux.append(list.remove_first());
1073 while (not list.is_empty())
1074 insert_sorted<Compare>(aux, list.remove_first(), cmp);
1075
1076 list.swap(aux);
1077 }
1078
1086 template <typename T, class Compare = Aleph::less<T>>
1087 inline
1088 void insertion_sort(DynList<T> & l, const Compare & cmp = Compare())
1089 {
1091 Cmp c(cmp);
1093 }
1094
1106 template <typename T, class Compare = Aleph::less<T>>
1107 inline
1108 DynList<T> insertion_sort(DynList<T> && l, const Compare & cmp = Compare())
1109 {
1111 Cmp c(cmp);
1113 return std::move(l);
1114 }
1115
1123 template <typename T, class Compare = Aleph::less<T>>
1124 inline void insertion_sort(Dnode<T> & list, const Compare & cmp = Compare())
1125 {
1127 Cmp c(cmp);
1129 }
1130
1159 template <typename T, class Compare = Aleph::less<T>>
1160 inline
1161 void merge(T *a, const long l, const long m, const long r,
1162 std::vector<T> & buf, const Compare & cmp = Compare())
1163 {
1164 const long s = r - l + 1;
1165
1166 // Resize buffer once instead of multiple push_back calls
1167 // This avoids repeated capacity checks and potential reallocations
1168 if (buf.size() < static_cast<size_t>(s))
1169 buf.resize(static_cast<size_t>(s));
1170
1171 // Copy left partition [l..m] to buffer start
1172 long buf_idx = 0;
1173 for (long i = l; i <= m; ++i)
1174 buf[buf_idx++] = std::move(a[i]);
1175
1176 // Copy right partition [m+1..r] in reverse to buffer end
1177 buf_idx = s - 1;
1178 for (long j = m + 1; j <= r; ++j)
1179 buf[buf_idx--] = std::move(a[j]);
1180
1181 // Merge from both ends toward the middle
1182 long i = 0;
1183 long j = s - 1;
1184
1185 for (long k = l; k <= r; ++k)
1186 if (not cmp(buf[j], buf[i]))
1187 a[k] = std::move(buf[i++]);
1188 else
1189 a[k] = std::move(buf[j--]);
1190 }
1191
1206 template <typename T, class Compare = Aleph::less<T>>
1207 inline
1208 void merge(T *a, const long l, const long m, const long r,
1209 const Compare & cmp = Compare())
1210 {
1211 std::vector<T> buf;
1212 buf.reserve(r - l + 1);
1213 merge(a, l, m, r, buf, cmp);
1214 }
1215
1223 template <typename T, class Compare>
1224 void mergesort(T *a, const long l, const long r, std::vector<T> & buf, Compare cmp)
1225 {
1226 if (l >= r)
1227 return;
1228
1229 const long m = l + (r - l) / 2;
1230
1231 mergesort<T, Compare>(a, l, m, buf, cmp);
1232 mergesort<T, Compare>(a, m + 1, r, buf, cmp);
1233
1234 if (not cmp(a[m + 1], a[m]))
1235 return;
1236
1237 merge<T, Compare>(a, l, m, r, buf, cmp);
1238 }
1239
1274 template <typename T, class Compare = Aleph::less<T>>
1275 inline
1276 void mergesort(T *a, const long l, const long r, const Compare & cmp = Compare())
1277 {
1278 if (l >= r)
1279 return;
1280
1281 std::vector<T> buf;
1282 buf.reserve(r - l + 1);
1283 mergesort(a, l, r, buf, cmp);
1284 }
1285
1311 template <typename Tlist, class Compare>
1312 inline
1313 void merge_lists(Tlist & l1, Tlist & l2, Tlist & result,
1314 const Compare & cmp = Compare())
1315 {
1316 assert(result.is_empty());
1317
1318 while (not l1.is_empty() and not l2.is_empty())
1319 if (cmp(l1.get_first_ne(), l2.get_first_ne()))
1320 result.append(l1.remove_first_ne());
1321 else
1322 result.append(l2.remove_first_ne());
1323
1324 if (l1.is_empty())
1325 result.concat_list(l2);
1326 else
1327 result.concat_list(l1);
1328
1330 }
1331
1341 template <typename T, class Compare = Aleph::less<T>>
1342 inline
1344 const Compare & cmp = Compare())
1345 {
1347 }
1348
1379 template <typename Tlist, class Compare>
1380 inline
1381 void mergesort(Tlist & list, const Compare & cmp = Compare())
1382 {
1383 if (list.is_unitarian_or_empty())
1384 return;
1385
1386 Tlist l, r;
1387 list.split_list_ne(l, r); // split into two lists
1388
1391
1392 merge_lists<Tlist, Compare>(l, r, list, cmp); // merge them
1393 }
1394
1413 template <template <typename> class Tlist, typename T,
1414 class Compare = Aleph::less<T>>
1415 inline
1416 void mergeinsertsort(Tlist<T> & list, const Compare & cmp = Compare(),
1417 const size_t lsz = Aleph::Insertion_Threshold)
1418 {
1419 if (list.is_unitarian_or_empty())
1420 return;
1421
1422 if (list.size() <= lsz)
1423 {
1425 return;
1426 }
1427
1428 Tlist<T> l, r;
1429 list.split_list(l, r); // split into two lists
1430
1433
1434 merge_lists<Tlist<T>, Compare>(l, r, list, cmp); // merge them
1435 }
1436
1445 template <template <typename> class Tlist, typename T,
1446 class Compare = Aleph::less<T>>
1447 inline
1448 void mergesort(Tlist<T> & list, const Compare & cmp = Compare())
1449 {
1450 if (list.is_unitarian_or_empty())
1451 return;
1452
1453 Tlist<T> l, r;
1454 list.split_list(l, r); // split into two lists
1455
1458
1459 merge_lists<Tlist<T>, Compare>(l, r, list, cmp); // merge them
1460 }
1461
1469 template <typename T, class Compare = Aleph::less<T>>
1470 inline
1471 void mergesort(Dnode<T> & list, const Compare & cmp = Compare())
1472 {
1474 }
1475
1483 template <typename T, class Compare = Aleph::less<T>>
1484 inline
1485 void mergesort(DynDlist<T> & list, const Compare & cmp = Compare())
1486 {
1488 }
1489
1497 template <typename T, class Compare = Aleph::less<T>>
1498 inline
1499 void mergesort(DynList<T> & list, const Compare & cmp = Compare())
1500 {
1501 mergesort<DynList<T>, Compare>(list, cmp);
1502 }
1503
1504
1515 template <typename T, class Compare = Aleph::less<T>>
1516 inline
1517 DynList<T> mergesort(DynList<T> && list, const Compare & cmp = Compare())
1518 {
1519 mergesort<DynList<T>, Compare>(list, cmp);
1520 return std::move(list);
1521 }
1522
1530 template <typename T, class Compare = Aleph::less<T>>
1531 inline
1532 long select_pivot(T *a, long l, long r, const Compare & cmp = Compare())
1533 noexcept(noexcept(cmp(a[0], a[0])) && std::is_nothrow_swappable_v<T>);
1534
1535
1553 template <typename T, class Compare = Aleph::less<T>>
1554 inline
1555 long partition(T *a, const long l, const long r, const Compare & cmp = Compare())
1556 noexcept(noexcept(cmp(a[0], a[0])) && std::is_nothrow_swappable_v<T>)
1557 {
1558 if (l >= r)
1559 return l;
1560
1561 const long p = select_pivot<T, Compare>(a, l, r, cmp);
1562 std::swap(a[p], a[r]);
1563
1564 // Note: pivot stays at a[r] throughout the loop - no copy needed
1565 // This saves a potentially expensive copy for large T types
1566 long i = l - 1; // index first element to the left > pivot
1567 long j = r; // index first element to the right < pivot
1568 while (true)
1569 {
1570 // advance while a[i] < pivot (pivot is at a[r])
1571 while (cmp(a[++i], a[r])) {}
1572
1573 while (cmp(a[r], a[--j])) // advance while pivot < a[j]
1574 if (j == l) // Has the left edge been reached?
1575 break; // yes ==> the iteration must be finished
1576
1577 if (i >= j)
1578 break;
1579
1580 // At this point there is an inversion a[i] > pivot > a[j]
1581 std::swap(a[i], a[j]); // Eliminate inversion
1582 }
1583
1584 std::swap(a[i], a[r]); // Place pivot in its final position
1585
1586 return i;
1587 }
1588
1607 template <typename T, class Compare = Aleph::less<T>>
1608 inline
1609 void quicksort_rec(T *a, const long l, const long r, const Compare & cmp = Compare())
1610 {
1611 if (l >= r)
1612 return;
1613
1614 const long pivot = partition<T, Compare>(a, l, r, cmp);
1615
1618 }
1619
1633 template <typename T, class Compare = Aleph::less<T>>
1634 inline
1635 void quicksort_no_tail(T *a, long l, long r, const Compare & cmp = Compare())
1636 {
1637 while (l < r)
1638 {
1639 const long pivot = partition<T, Compare>(a, l, r, cmp);
1640 const long left_size = pivot - l;
1641 const long right_size = r - pivot;
1642
1643 // Recurse on the smaller partition to ensure O(log n) stack depth.
1644 if (left_size < right_size)
1645 {
1647 l = pivot + 1; // tail recurse on the right by looping
1648 }
1649 else
1650 {
1652 r = pivot - 1; // tail recurse on the left by looping
1653 }
1654 }
1655 }
1656
1673 template <typename T, class Compare = Aleph::less<T>>
1674 inline
1675 void quicksort_rec_min(T *a, const long l, const long r,
1676 const Compare & cmp = Compare())
1677 {
1678 if (r <= l)
1679 return;
1680
1681 if (const long pivot = partition<T, Compare>(a, l, r, cmp); pivot - l < r - pivot)
1682 // which partition is smaller?
1683 { // left partition is smaller
1686 }
1687 else
1688 { // right partition is smaller
1691 }
1692 }
1693
1694 template <typename T, class Compare>
1695 inline
1696 long select_pivot(T *a, const long l, const long r, const Compare & cmp)
1697 noexcept(noexcept(cmp(a[0], a[0])) && std::is_nothrow_swappable_v<T>)
1698 {
1699 const long m = l + (r - l) / 2;
1700
1701 if (cmp(a[r], a[l]))
1702 std::swap(a[l], a[r]);
1703 if (cmp(a[m], a[l]))
1704 std::swap(a[m], a[l]);
1705 if (cmp(a[r], a[m]))
1706 std::swap(a[r], a[m]);
1707
1708 return m;
1709 }
1710
1712 inline size_t introsort_depth_limit(size_t n) noexcept;
1713
1751 template <typename T, class Compare = Aleph::less<T>>
1752 inline
1753 void quicksort(T *a, const long l, const long r, const Compare & cmp = Compare())
1754 {
1755 if (r - l < Quicksort_Threshold)
1756 {
1757 insertion_sort(a, l, r, cmp);
1758 return;
1759 }
1760
1761 const auto n = static_cast<size_t>(r - l + 1);
1762
1763 typedef std::pair<long, long> Partition;
1765 stack.push(Partition(l, r));
1766
1767 while (stack.size() > 0)
1768 {
1769 auto [fst, snd] = stack.pop();
1770 const long diff = snd - fst;
1771
1772 if (diff <= 0)
1773 continue;
1774
1776 {
1777 insertion_sort(a, fst, snd, cmp);
1778 continue;
1779 }
1780
1781 if (const long pivot = partition<T, Compare>(a, fst, snd, cmp); pivot - fst < snd - pivot)
1782 // which one is smaller?
1783 { // the left partition is smaller
1784 stack.push(Partition(pivot + 1, snd));
1785 stack.push(Partition(fst, pivot - 1));
1786 }
1787 else
1788 { // the right partition is smaller
1789 stack.push(Partition(fst, pivot - 1));
1790 stack.push(Partition(pivot + 1, snd));
1791 }
1792 }
1793 }
1794
1803 inline size_t introsort_depth_limit(size_t n) noexcept
1804 {
1805 size_t depth = 0;
1806 while (n > 1)
1807 {
1808 n >>= 1;
1809 ++depth;
1810 }
1811 return depth * 2;
1812 }
1813
1818 template <typename T, class Compare>
1819 void introsort_loop(T *a, long l, long r, size_t depth_limit,
1820 const Compare & cmp)
1821 {
1822 while (r - l >= static_cast<long>(Quicksort_Threshold))
1823 {
1824 if (depth_limit == 0) [[unlikely]]
1825 {
1826 // Depth limit reached: switch to heapsort for guaranteed O(n log n)
1827 faster_heapsort(a + l, static_cast<size_t>(r - l + 1), cmp);
1828 return;
1829 }
1830
1831 --depth_limit;
1832
1833 // Median-of-three pivot selection
1834 // Recurse on smaller partition, iterate on larger (tail call elimination)
1835 if (const long pivot = partition<T, Compare>(a, l, r, cmp); pivot - l < r - pivot)
1836 {
1838 l = pivot + 1;
1839 }
1840 else
1841 {
1843 r = pivot - 1;
1844 }
1845 }
1846 }
1847
1895 template <typename T, class Compare = Aleph::less<T>>
1896 inline
1897 void introsort(T *a, const long l, const long r, const Compare & cmp = Compare())
1898 {
1899 if (r <= l)
1900 return;
1901
1902 const auto n = static_cast<size_t>(r - l + 1);
1903
1904 if (n < Quicksort_Threshold)
1905 {
1906 insertion_sort(a, l, r, cmp);
1907 return;
1908 }
1909
1910 // Main introsort loop with depth limit
1912
1913 // Final insertion sort pass for small unsorted subarrays
1914 insertion_sort(a, l, r, cmp);
1915 }
1916
1928 template <typename T, class Compare = Aleph::less<T>>
1929 inline
1930 void introsort(T *a, const size_t n, const Compare & cmp = Compare())
1931 {
1932 if (n <= 1)
1933 return;
1934 introsort(a, 0L, static_cast<long>(n - 1), cmp);
1935 }
1936
1964 template <typename T, class Compare = Aleph::less<T>>
1965 inline
1966 void introsort(T *begin, T *end, const Compare & cmp = Compare())
1967 {
1968 if (begin >= end)
1969 return;
1970 const auto n = static_cast<size_t>(end - begin);
1971 introsort(begin, n, cmp);
1972 }
1973
1981 template <typename T, class Compare = Aleph::less<T>>
1982 inline
1983 void introsort(Array<T> & arr, const Compare & cmp = Compare())
1984 {
1985 const size_t n = arr.size();
1986 if (n <= 1)
1987 return;
1988 // Array uses contiguous memory via MemArray, so we can use pointer arithmetic
1989 introsort(&arr(0), n, cmp);
1990 }
1991
2005 template <class Compare>
2006 void quicksort(Dlink & list, const Compare & cmp = Compare())
2007 {
2008 if (list.is_unitarian_or_empty())
2009 return;
2010
2011 Dlink *pivot = list.remove_next();
2012 Dlink smaller, bigger; // lists of smaller and larger than pivot
2013
2014 while (not list.is_empty())
2015 if (Dlink *p = list.remove_next(); cmp(p, pivot))
2016 smaller.append(p);
2017 else
2018 bigger.append(p);
2019
2021 quicksort<Compare>(smaller, cmp);
2022
2023 list.concat_list(&smaller); // restore sorted lists into list
2024 list.append(pivot);
2025 list.concat_list(&bigger);
2026 }
2027
2049 template <typename T, class Compare = Aleph::less<T>>
2050 void quicksort(Dnode<T> & list, const Compare & cmp = Compare())
2051 {
2053 }
2054
2059 template <typename T, class Compare>
2060 void quicksort(HTList & list, const Compare & cmp = Compare())
2061 {
2062 if (list.is_unitarian_or_empty())
2063 return;
2064
2065 auto *pivot = static_cast<Snodenc<T> *>(list.remove_first_ne());
2066 HTList smaller, bigger; // lists of smaller and larger than pivot
2067
2068 while (not list.is_empty())
2069 if (auto *p = static_cast<Snodenc<T> *>(list.remove_first_ne()); cmp(p->get_data(), pivot->get_data()))
2070 smaller.append(p);
2071 else
2072 bigger.append(p);
2073
2075 quicksort<T, Compare>(smaller, cmp);
2076
2077 list.concat_list(smaller); // restore sorted lists into list
2078 list.append(pivot);
2079 list.concat_list(bigger);
2080 }
2081
2086 template <typename T, class Compare = Aleph::less<T>>
2087 void quicksort(DynList<T> & list, const Compare & cmp = Compare())
2088 {
2089 quicksort<T, Compare>(static_cast<HTList &>(list), cmp);
2090 }
2091
2130 template <typename T, class Compare = Aleph::less<T>>
2131 inline
2132 void quicksort_insertion(T *a, const long l, const long r,
2133 const Compare & cmp = Compare())
2134 {
2135 if (r <= l)
2136 return;
2137
2138 const long pivot = partition<T, Compare>(a, l, r, cmp);
2139
2140 const long l_size = pivot - l; // left partition size
2141 const long r_size = r - pivot; // right partition size
2142 bool left_done = false; // true if left partition is sorted
2143 bool right_done = false; // true if der partition is sorted
2144
2146 { // sort left partition by insertion
2148 left_done = true;
2149 }
2150
2152 { // sort der partition by insertion
2154 right_done = true;
2155 }
2156
2158 return; //both partitions sorted by insertion
2159
2160 if (left_done) // left partition sorted by insertion?
2161 { // Yeah; It only remains to recursively sort the right partition
2163 return;
2164 }
2165
2166 if (right_done) // der partition sorted by insertion?
2167 { // Yeah; It only remains to recursively sort the left partition
2169 return;
2170 }
2171
2172 // here, both partitions were not insertion sorted
2173 if (l_size < r_size) // sort smallest partition first
2174 { //smaller left partition
2177 }
2178 else
2179 { // smaller right partition
2182 }
2183 }
2184
2218 template <typename T, class Compare = Aleph::less<T>>
2219 inline
2220 long random_search(T *a, const T & x, const long l, const long r,
2221 const Compare & cmp = Compare())
2222 {
2223 if (l > r)
2224 return Not_Found;
2225
2226 const long pivot = partition<T, Compare>(a, l, r, cmp);
2227
2228 if (cmp(x, a[pivot]))
2229 return random_search<T, Compare>(a, x, l, pivot - 1, cmp);
2230 if (cmp(a[pivot], x))
2231 return random_search<T, Compare>(a, x, pivot + 1, r, cmp);
2232
2233 return pivot; // element found at index x
2234 }
2235
2244 template <typename T, class Compare = Aleph::less<T>>
2245 inline
2246 long random_search(DynArray<T> & a, const T & x, const long l, const long r,
2247 const Compare & cmp = Compare())
2248 {
2249 if (l > r)
2250 return Not_Found;
2251
2252 const long pivot = partition(a, l, r, cmp);
2253
2254 if (cmp(x, a(pivot)))
2255 return random_search<T, Compare>(a, x, l, pivot - 1, cmp);
2256 if (cmp(a(pivot), x))
2257 return random_search<T, Compare>(a, x, pivot + 1, r, cmp);
2258
2259 return pivot; // element found at index x
2260 }
2261
2288 template <typename T, class Compare>
2289 inline
2291 const Compare & cmp = Compare())
2292 {
2293 if (list.is_empty())
2294 return nullptr;
2295
2296 Dnode<T> item(x);
2297 Dnode<T> *item_ptr = &item; // pointer to the cell containing x
2298
2299 Dlink smaller; // list of those smaller than pivot
2300 Dlink bigger; // list of those greater than pivot
2301
2302 auto *pivot = static_cast<Dnode<T> *>(list.remove_next());
2303
2304 while (not list.is_empty())
2305 if (Dlink *p = list.remove_next(); cmp(p, pivot))
2306 smaller.append(p);
2307 else
2308 bigger.append(p);
2309
2310 Dnode<T> *ret_val = nullptr;
2311 if (cmp(item_ptr, pivot))
2313 else if (cmp(pivot, item_ptr))
2315 else
2316 ret_val = pivot;
2317
2318 assert(list.is_empty());
2319
2320 list.swap(&smaller);
2321 list.append(pivot);
2322 list.concat_list(&bigger);
2323
2324 return ret_val;
2325 }
2326
2356 template <typename T, class Compare = Aleph::less<T>>
2357 Dnode<T> * random_search(Dlink & list, const T & x, const Compare & cmp = Compare())
2358 {
2360 }
2361
2391 template <typename T, class Compare = Aleph::less<T>>
2392 inline
2393 T * random_search(DynDlist<T> & list, const T & x, const Compare & cmp = Compare())
2394 {
2396
2397 return p == nullptr ? nullptr : &(p->get_data());
2398 }
2399
2400 template <typename T, class Compare>
2401 static inline
2402 std::pair<long, long>
2403 partition_three_way(T *a, long l, long r, const Compare & cmp)
2404 {
2405 const long pivot_idx = select_pivot<T, Compare>(a, l, r, cmp);
2406 std::swap(a[pivot_idx], a[r]);
2407
2408 long lt = l;
2409 long gt = r - 1;
2410 long current = l;
2411
2412 while (current <= gt)
2413 if (cmp(a[current], a[r]))
2414 {
2415 std::swap(a[lt], a[current]);
2416 lt++;
2417 current++;
2418 }
2419 else if (cmp(a[r], a[current]))
2420 {
2421 std::swap(a[current], a[gt]);
2422 gt--;
2423 }
2424 else
2425 current++;
2426
2427 std::swap(a[current], a[r]);
2428 return {lt, current};
2429 }
2430
2431 template <class Container, typename T, class Compare>
2432 static inline
2433 std::pair<long, long>
2434 partition_three_way_op(Container & a, long l, long r, const Compare & cmp)
2435 {
2436 const long pivot_idx = select_pivot_op<T, Compare>(a, l, r, cmp);
2437 std::swap(a(pivot_idx), a(r));
2438
2439 long lt = l;
2440 long gt = r - 1;
2441 long current = l;
2442
2443 while (current <= gt)
2444 if (cmp(a(current), a(r)))
2445 {
2446 std::swap(a(lt), a(current));
2447 lt++;
2448 current++;
2449 }
2450 else if (cmp(a(r), a(current)))
2451 {
2452 std::swap(a(current), a(gt));
2453 gt--;
2454 }
2455 else
2456 current++;
2457
2458 std::swap(a(current), a(r));
2459 return {lt, current};
2460 }
2461
2462 template <typename T, class Compare>
2463 static inline
2464 const T &__random_select(T *a, const long i, long l, long r,
2465 const Compare & cmp)
2466 {
2467 while (l < r)
2468 {
2469 if (r - l < 10)
2470 {
2472 return a[i];
2473 }
2474
2475 const auto [lt, current] = partition_three_way<T, Compare>(a, l, r, cmp);
2476
2477 if (i >= lt and i <= current)
2478 return a[i];
2479
2480 if (i < lt)
2481 r = lt - 1;
2482 else
2483 l = current + 1;
2484 }
2485
2486 return a[l];
2487 }
2488
2497 template <typename T, class Compare, typename Container>
2498 inline
2499 long select_pivot_op_impl(const Container & a, const long l, const long r,
2500 const Compare & cmp)
2501 {
2502 assert(l <= r);
2503
2504 if (r - l <= 5)
2505 return r;
2506
2507 const long m = l + (r - l) / 2; // central element
2508
2509 // Access array entries only once
2510 const T & la = a(l);
2511 const T & ra = a(r);
2512 const T & ma = a(m);
2513
2515
2516 if (med_ptr == &la)
2517 return l;
2518
2519 if (med_ptr == &ma)
2520 return m;
2521
2522 assert(med_ptr == &ra);
2523
2524 return r;
2525 }
2526
2532 template <typename T, class Compare = Aleph::less<T>>
2533 inline
2534 long select_pivot_op(const DynArray<T> & a, const long l, const long r,
2535 const Compare & cmp = Compare())
2536 {
2538 }
2539
2545 template <typename T, class Compare = Aleph::less<T>>
2546 inline
2547 long select_pivot_op(const Array<T> & a, const long l, const long r,
2548 const Compare & cmp = Compare())
2549 {
2551 }
2552
2561 template <typename T, class Compare, typename Container>
2562 inline
2563 long partition_op_impl(Container & a, const long l, const long r,
2564 const Compare & cmp)
2565 {
2566 if (l == r)
2567 return l;
2568
2569 long i = l - 1;
2570 long j = r;
2571
2572 // Move the selected pivot to position r
2573 if (const long pivot_idx = select_pivot_op<T, Compare>(a, l, r, cmp); pivot_idx != r)
2574 std::swap(a(pivot_idx), a(r));
2575
2576 // Now pivot value is at a(r) - use direct access instead of reference
2577 while (true)
2578 {
2579 while (cmp(a(++i), a(r))) {}
2580
2581 while (cmp(a(r), a(--j)))
2582 if (j == l)
2583 break;
2584
2585 if (i >= j)
2586 break;
2587
2588 std::swap(a(i), a(j));
2589 }
2590
2591 std::swap(a(i), a(r));
2592
2593 return i;
2594 }
2595
2603 template <typename T, class Compare = Aleph::less<T>>
2604 inline
2605 long partition_op(DynArray<T> & a, const long l, const long r,
2606 const Compare & cmp = Compare())
2607 {
2608 return partition_op_impl<T, Compare>(a, l, r, cmp);
2609 }
2610
2618 template <typename T, class Compare = Aleph::less<T>>
2619 inline
2620 long partition_op(Array<T> & a, const long l, const long r,
2621 const Compare & cmp = Compare())
2622 {
2623 return partition_op_impl<T, Compare>(a, l, r, cmp);
2624 }
2625
2626 template <typename Container, class Compare>
2627 static inline
2628 void insertion_sort_range(Container & a, long l, long r, const Compare & cmp)
2629 {
2630 using T = typename Container::Item_Type;
2631 for (long p = l + 1; p <= r; ++p)
2632 {
2633 T key = std::move(a(p));
2634 long j = p - 1;
2635 while (j >= l and cmp(key, a(j)))
2636 {
2637 a(j + 1) = std::move(a(j));
2638 j--;
2639 }
2640 a(j + 1) = std::move(key);
2641 }
2642 }
2643
2644 template <typename T, class Compare>
2645 static inline
2646 const T &__random_select(DynArray<T> & a, const long i,
2647 long l, long r,
2648 const Compare & cmp = Compare())
2649 {
2650 assert(i >= l and i <= r);
2651
2652 while (l < r)
2653 {
2654 if (r - l < 10)
2655 {
2657 return a(i);
2658 }
2659
2660 const auto [lt, current] = partition_three_way_op<DynArray<T>, T, Compare>(a, l, r, cmp);
2661
2662 if (i >= lt and i <= current)
2663 return a(i);
2664
2665 if (i < lt)
2666 r = lt - 1;
2667 else
2668 l = current + 1;
2669 }
2670
2671 return a(l);
2672 }
2673
2674 template <typename T, class Compare>
2675 static inline
2676 const T &__random_select(Array<T> & a, const long i,
2677 long l, long r,
2678 const Compare & cmp = Compare())
2679 {
2680 assert(i >= l and i <= r);
2681
2682 while (l < r)
2683 {
2684 if (r - l < 10)
2685 {
2687 return a(i);
2688 }
2689
2690 const auto [lt, current] = partition_three_way_op<Array<T>, T, Compare>(a, l, r, cmp);
2691
2692 if (i >= lt and i <= current)
2693 return a(i);
2694
2695 if (i < lt)
2696 r = lt - 1;
2697 else
2698 l = current + 1;
2699 }
2700
2701 return a(l);
2702 }
2703
2711 template <typename T, class Compare = Aleph::less<T>>
2712 const T &random_select(DynArray<T> & a, const long i, const Compare & cmp = Compare())
2713 {
2714 const auto n_sz = a.size();
2715 ah_out_of_range_error_if(n_sz == 0 or i < 0) << "index out of range";
2716
2717 const long n = static_cast<long>(n_sz) - 1;
2718 ah_out_of_range_error_if(i > n) << "index out of range";
2719
2720 return __random_select<T, Compare>(a, i, 0, n, cmp);
2721 }
2722
2730 template <typename T, class Compare = Aleph::less<T>>
2731 const T &random_select(Array<T> & a, const long i, const Compare & cmp = Compare())
2732 {
2733 const auto n_sz = a.size();
2734 ah_out_of_range_error_if(n_sz == 0 or i < 0) << "index out of range";
2735
2736 const long n = static_cast<long>(n_sz) - 1;
2737 ah_out_of_range_error_if(i > n) << "index out of range";
2738
2739 return __random_select<T, Compare>(a, i, 0, n, cmp);
2740 }
2741
2782 template <typename T, class Compare = Aleph::less<T>>
2783 const T &random_select(T *a, const long i, const long n,
2784 const Compare & cmp = Compare())
2785 {
2786 ah_invalid_argument_if(a == nullptr and n > 0) << "null pointer";
2787
2788 ah_out_of_range_error_if(n <= 0 or i < 0 or i >= n) << "index out of range";
2789
2790 return __random_select<T, Compare>(a, i, 0, n - 1, cmp);
2791 }
2792
2800 template <typename T, class Compare = Aleph::less<T>>
2801 const T &random_select(T *a, const long i, const long n,
2802 Compare && cmp = Compare())
2803 {
2804 return random_select<T, Compare>(a, i, n, cmp);
2805 }
2806
2829 template <class Compare>
2830 Dlink * dlink_random_select(Dlink & list, const size_t i,
2831 const Compare & cmp = Compare())
2832 {
2833 if (list.is_empty())
2834 return nullptr;
2835
2836 Dlink smaller; // list of minors than pivot
2837 Dlink bigger; // list of majors than pivot
2838
2839 size_t smaller_count = 0, // number of elements in smaller
2840 bigger_count = 0; // number of elements in bigger
2841
2842 Dlink *pivot = list.remove_next();
2843
2844 while (not list.is_empty())
2845 if (Dlink *p = list.remove_next(); cmp(p, pivot)) // p < pivot?
2846 {
2847 smaller.append(p);
2848 ++smaller_count;
2849 }
2850 else
2851 {
2852 bigger.append(p);
2853 ++bigger_count;
2854 }
2855
2856 if (i >= smaller_count + bigger_count + 1)
2857 {
2858 list.concat_list(&smaller);
2859 list.append(pivot);
2860 list.concat_list(&bigger);
2861 ah_out_of_range_error() << "index of selection greater than list's size";
2862 }
2863
2864 Dlink *ret_val = nullptr;
2865 if (i == smaller_count)
2866 ret_val = pivot;
2867 else if (i < smaller_count)
2869 else
2871
2872 list.concat_list(&smaller);
2873 list.append(pivot);
2874 list.concat_list(&bigger);
2875
2876 return ret_val;
2877 }
2878
2904 template <typename T, class Compare = Aleph::less<T>>
2905 Dnode<T> * random_select(Dlink & list, const size_t i, const Compare & cmp = Compare())
2906 {
2907 return static_cast<Dnode<T> *>(dlink_random_select<Compare_Dnode<T, Compare>>(list, i, cmp));
2908 }
2909
2935 template <typename T, class Compare = Aleph::less<T>>
2936 T * random_select(DynDlist<T> & list, const size_t i, const Compare & cmp = Compare())
2937 {
2939
2940 auto *p = static_cast<Dnode<T> *>(link);
2941
2942 return p != nullptr ? &(p->get_data()) : nullptr;
2943 }
2944
2964 template <typename T, class Compare = Aleph::less<T>>
2965 inline
2966 void selection_sort(DynArray<T> & a, const Compare & cmp = Compare())
2967 {
2968 const long n = static_cast<long>(a.size());
2969 if (n < 2)
2970 return;
2971
2972 for (long i = 0; i < n - 1; ++i)
2973 {
2974 long min = i;
2975
2976 for (long j = i + 1; j < n; ++j)
2977 if (cmp(a(j), a(min)))
2978 min = j;
2979
2980 if (cmp(a(min), a(i)))
2981 std::swap(a(min), a(i));
2982 }
2983 }
2984
3015 template <typename T, class Compare = Aleph::less<T>>
3016 inline
3017 void bubble_sort(DynArray<T> & a, const Compare & cmp = Compare())
3018 {
3019 const long n = static_cast<long>(a.size());
3020 if (n < 2)
3021 return;
3022
3023 for (long i = 0; i < n - 1; ++i)
3024 for (long j = n - 1; j > i; --j)
3025 if (cmp(a(j), a(j - 1)))
3026 std::swap(a(j - 1), a(j));
3027 }
3028
3055 template <template <typename> class C, typename T,
3056 class Compare = Aleph::less<T>>
3057 inline
3058 void insertion_sort(C<T> & a, const long l, const long r, const Compare & cmp = Compare())
3059 {
3060 for (long i = l + 1; i <= r; i++)
3061 {
3062 T tmp = std::move(a(i));
3063 long j = i;
3064 for (/* nothing */; j > l and cmp(tmp, a(j - 1)); --j)
3065 a(j) = std::move(a(j - 1));
3066
3067 a(j) = std::move(tmp);
3068 }
3069 }
3070
3075 template <template <typename> class C, typename T,
3076 class Compare = Aleph::less<T>>
3077 inline
3078 void insertion_sort(C<T> & a, const Compare & cmp = Compare())
3079 {
3080 const auto n = a.size();
3081 if (n <= 1)
3082 return;
3083 insertion_sort(a, 0, static_cast<long>(n) - 1, cmp);
3084 }
3085
3120 template <typename T, class Compare = Aleph::less<T>>
3121 inline
3122 void shellsort(DynArray<T> & a, const Compare & cmp = Compare())
3123 {
3124 const long n = a.size();
3125 if (n <= 1)
3126 return;
3127
3128 // Ciura's gap sequence - empirically optimal for most data distributions
3129 // Extended with ~2.25x multiplier for larger arrays
3130 static constexpr long ciura_gaps[] =
3131 {
3132 1, 4, 10, 23, 57, 132, 301, 701, 1750, 4024, 9233,
3133 21223, 48805, 112217, 258100, 593630, 1365513, 3140680
3134 };
3135 static constexpr int num_gaps = sizeof(ciura_gaps) / sizeof(ciura_gaps[0]);
3136
3137 // Find the first gap smaller than the array size
3138 int gap_idx = num_gaps - 1;
3139 while (gap_idx >= 0 && ciura_gaps[gap_idx] >= n)
3140 --gap_idx;
3141
3142 // Sort with decreasing gaps
3143 for (; gap_idx >= 0; --gap_idx)
3144 {
3145 const long h = ciura_gaps[gap_idx];
3146 for (long i = h; i < n; i++)
3147 {
3148 T tmp = std::move(a(i));
3149 long j = i;
3150
3151 while (j >= h and cmp(tmp, a(j - h)))
3152 {
3153 a(j) = std::move(a(j - h));
3154 j -= h;
3155 }
3156
3157 a(j) = std::move(tmp);
3158 }
3159 }
3160 }
3161
3162
3164 inline static long back_index(const long i) noexcept { return i - 1; }
3165
3172 template <typename T, class Compare>
3173 inline
3174 void sift_up(DynArray<T> & table, const long n, const Compare & cmp)
3175 {
3176 long p;
3177 for (long i = n; i > 1; i = p)
3178 {
3179 p = i >> 1; // c = i/2
3180
3181 if (cmp(table(back_index(p)), table(back_index(i))))
3182 return;
3183
3184 std::swap(table(back_index(p)), table(back_index(i)));
3185 }
3186 }
3187
3195 template <typename T, class Compare>
3196 inline
3197 void sift_down(DynArray<T> & table, const long start, const long n, const Compare & cmp)
3198 {
3199 long i = start;
3200
3201 while (true)
3202 {
3203 long c = i << 1; // c = 2*i (left child)
3204
3205 if (c > n)
3206 return;
3207
3208 // Select the smaller/larger child depending on comparator
3209 if (c + 1 <= n)
3210 if (cmp(table(back_index(c + 1)), table(back_index(c))))
3211 c++;
3212
3213 if (cmp(table(back_index(i)), table(back_index(c))))
3214 return;
3215
3216 std::swap(table(back_index(c)), table(back_index(i)));
3217 i = c;
3218 }
3219 }
3220
3222 template <typename T, class Compare>
3223 inline
3224 void sift_down(DynArray<T> & table, const long n, const Compare & cmp)
3225 {
3226 sift_down<T, Compare>(table, 1, n, cmp);
3227 }
3228
3241 template <typename T, class Compare>
3243 {
3244 Compare cmp;
3245
3246 public:
3248 Negate_Compare(Compare __cmp = Compare())
3250 : cmp(__cmp)
3251 { /* Empty */
3252 }
3253
3255 bool operator ()(const T & e1, const T & e2) const
3256 noexcept(noexcept(std::declval<const Compare &>()(e2, e1)))
3257 {
3258 return cmp(e2, e1);
3259 }
3260 };
3261
3302 template <typename T, class Compare = Aleph::less<T>>
3303 inline
3304 void heapsort(DynArray<T> & a, const Compare & cmp = Compare())
3305 {
3306 const long n = a.size();
3307 if (n <= 1)
3308 return;
3309
3311
3312 // Floyd's heap construction: O(n) instead of O(n log n)
3313 // Start from the last non-leaf node and sift down each node
3314 for (long i = n / 2; i >= 1; --i)
3316
3317 // Extract elements from the heap one by one
3318 for (long i = n; i >= 2; --i)
3319 {
3320 std::swap(a(0), a(i - 1));
3322 }
3323 }
3324
3333 template <template <typename> class C, typename T,
3334 class Compare = Aleph::less<T>>
3335 inline
3336 long partition(C<T> & a, const long l, const long r, const Compare & cmp = Compare())
3337 {
3338 if (l == r)
3339 return l;
3340
3341 long i = l - 1;
3342 long j = r;
3343 const T & pivot = a(r);
3344
3345 while (true)
3346 {
3347 while (cmp(a(++i), pivot)) { /* Nothing else */ }
3348
3349 while (cmp(pivot, a(--j)))
3350 if (j == l)
3351 break;
3352
3353 if (i >= j)
3354 break;
3355
3356 std::swap(a(i), a(j));
3357 }
3358
3359 std::swap(a(i), a(r));
3360
3361 return i;
3362 }
3363
3370 template <typename T, class Compare = Aleph::less<T>>
3371 inline
3372 void quicksort_rec(DynArray<T> & a, const long l, const long r,
3373 const Compare & cmp = Compare())
3374 {
3375 if (r <= l)
3376 return;
3377
3378 if (const long i = partition(a, l, r, cmp); i - l < r - i)
3379 {
3380 quicksort_rec<T, Compare>(a, l, i - 1, cmp);
3381 quicksort_rec<T, Compare>(a, i + 1, r, cmp);
3382 }
3383 else
3384 {
3385 quicksort_rec<T, Compare>(a, i + 1, r, cmp);
3386 quicksort_rec<T, Compare>(a, l, i - 1, cmp);
3387 }
3388 }
3389
3401 template <class Stack, class A, class B>
3402 inline
3403 void push2(Stack & stack, const A & a, const B & b)
3404 {
3405 stack.push(b);
3406 stack.push(a);
3407 }
3408
3437 template <typename T, class Compare = Aleph::less<T>>
3438 inline
3439 void quicksort(DynArray<T> & a, const Compare & cmp = Compare())
3440 {
3441 long l = 0, r = a.size() - 1;
3442
3443 const size_t n = a.size();
3444 FixedStack<long> stack((introsort_depth_limit(n) + 2) * 2);
3445
3446 push2(stack, l, r);
3447
3448 while (not stack.is_empty())
3449 {
3450 l = stack.pop();
3451 r = stack.pop();
3452
3453 if (r <= l)
3454 continue;
3455
3456 if (const long i = partition(a, l, r, cmp); i - l > r - i)
3457 {
3458 push2(stack, l, i - 1);
3459 push2(stack, i + 1, r);
3460 }
3461 else
3462 {
3463 push2(stack, i + 1, r);
3464 push2(stack, l, i - 1);
3465 }
3466 }
3467 }
3468
3478 template <typename T, class Compare>
3479 inline
3480 void sift_down_subrange(DynArray<T> & a, long start, const long n,
3481 const long offset, const Compare & cmp)
3482 {
3483 while (true)
3484 {
3485 long c = start << 1; // c = 2*start (left child)
3486 if (c > n)
3487 return;
3488
3489 // Select the child with higher priority
3490 if (c + 1 <= n)
3491 if (cmp(a(offset + c), a(offset + c - 1)))
3492 c++;
3493
3494 if (cmp(a(offset + start - 1), a(offset + c - 1)))
3495 return;
3496
3497 std::swap(a(offset + start - 1), a(offset + c - 1));
3498 start = c;
3499 }
3500 }
3501
3518 template <typename T, class Compare>
3519 inline
3520 void heapsort_subrange(DynArray<T> & a, const long l, const long r,
3521 const Compare & cmp)
3522 {
3523 const long n = r - l + 1;
3524 if (n <= 1)
3525 return;
3526
3528
3529 // Floyd's heap construction: O(n)
3530 for (long i = n / 2; i >= 1; --i)
3532
3533 // Extract elements
3534 for (long i = n; i >= 2; --i)
3535 {
3536 std::swap(a(l), a(l + i - 1));
3538 }
3539 }
3540
3545 template <typename T, class Compare>
3546 void introsort_loop(DynArray<T> & a, long l, long r, size_t depth_limit,
3547 const Compare & cmp)
3548 {
3549 while (r - l >= static_cast<long>(Quicksort_Threshold))
3550 {
3551 if (depth_limit == 0) [[unlikely]]
3552 {
3553 // Depth limit reached: switch to heapsort for guaranteed O(n log n)
3555 return;
3556 }
3557
3558 --depth_limit;
3559
3560 // Partition and continue
3561
3562 // Recurse on smaller partition, iterate on larger (tail call elimination)
3563 if (const long pivot = partition(a, l, r, cmp); pivot - l < r - pivot)
3564 {
3566 l = pivot + 1;
3567 }
3568 else
3569 {
3571 r = pivot - 1;
3572 }
3573 }
3574 }
3575
3606 template <typename T, class Compare = Aleph::less<T>>
3607 inline
3608 void introsort(DynArray<T> & a, const Compare & cmp = Compare())
3609 {
3610 const long n = a.size();
3611 if (n <= 1)
3612 return;
3613
3614 if (n < static_cast<long>(Quicksort_Threshold))
3615 {
3616 insertion_sort(a, 0, n - 1, cmp);
3617 return;
3618 }
3619
3620 // Main introsort loop with depth limit
3622
3623 // Final insertion sort pass for small unsorted subarrays
3624 insertion_sort(a, 0, n - 1, cmp);
3625 }
3626
3641 template <typename T, class Compare = Aleph::less<T>>
3642 inline
3643 long search_extreme(const DynArray<T> & a, const long l, const long r,
3644 const Compare & cmp = Compare())
3645 {
3646 long extreme_index = l;
3647
3648 for (long i = l + 1; i <= r; i++)
3649 if (cmp(a(i), a(extreme_index)))
3650 extreme_index = i;
3651
3652 return extreme_index;
3653 }
3654
3659 template <typename T, class Compare = Aleph::greater<T>>
3660 inline
3661 long search_max(const DynArray<T> & a, const long l, const long r,
3662 const Compare & cmp = Compare())
3663 {
3664 return search_extreme<T, Compare>(a, l, r, cmp);
3665 }
3666
3712 template <template <typename> class C, typename T,
3713 class Compare = Aleph::less<T>>
3714 inline
3715 long binary_search(const C<T> & a, const T & x, long l, long r,
3716 const Compare & cmp = Compare())
3717 {
3718 if (l > r)
3719 return l;
3720
3721 while (l <= r)
3722 if (long m = l + (r - l) / 2; cmp(x, a(m)))
3723 r = m - 1;
3724 else if (cmp(a(m), x))
3725 l = m + 1;
3726 else
3727 return m; // key found
3728
3729 return l;
3730 }
3731
3749 template <template <typename> class C, typename T,
3750 class Compare = Aleph::less<T>>
3751 inline
3752 long binary_search(const C<T *> & a, const T & x, long l, long r,
3753 const Compare & cmp = Compare())
3754 {
3755 if (l > r)
3756 return l;
3757
3758 while (l <= r)
3759 if (long m = l + (r - l) / 2; cmp(x, *a(m)))
3760 r = m - 1;
3761 else if (cmp(*a(m), x))
3762 l = m + 1;
3763 else
3764 return m; // found key
3765
3766 return l;
3767 }
3768
3780 template <template <typename> class C, typename T,
3781 class Compare = Aleph::less<T>>
3782 inline
3783 long binary_search(const C<T *> & a, const T & x, Compare && cmp = Compare())
3784 {
3785 const auto n = a.size();
3786 if (n == 0)
3787 return 0;
3788
3789 return binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3790 }
3791
3818 template <template <typename> class C, typename T,
3819 class Compare = Aleph::less<T>>
3820 inline
3821 long binary_search(const C<T> & a, const T & x, const Compare & cmp = Compare())
3822 {
3823 const auto n = a.size();
3824 if (n == 0)
3825 return 0;
3826
3827 return binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3828 }
3829
3843 template <template <typename> class C, typename T,
3844 class Compare = Aleph::less<T>>
3845 inline
3847 const Compare & cmp = Compare())
3848 {
3850 const auto n = a.size();
3851 if (n == 0)
3852 return ret;
3853
3854 long idx = binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
3855 if (idx < 0)
3856 return ret;
3857
3858 if (not are_equals(a(idx), x, cmp))
3859 return ret;
3860
3861 ret.append(idx);
3862 for (long i = idx - 1; i >= 0; --i)
3863 {
3864 if (not are_equals(a(i), x, cmp))
3865 break;
3866 ret.insert(i);
3867 }
3868
3869 for (long i = idx + 1; i < static_cast<long>(n); ++i)
3870 {
3871 if (not are_equals(a(i), x, cmp))
3872 break;
3873 ret.append(i);
3874 }
3875
3876 return ret;
3877 }
3878
3892 template <template <typename> class C, typename T,
3893 class Compare = Aleph::less<T>>
3894 inline
3895 DynArray<const T *> build_index_ptr(const C<T> & a, const Compare & cmp = Compare())
3896 {
3897 const size_t & n = a.size();
3899 ret.reserve(a.size());
3900 for (size_t i = 0; i < n; ++i)
3901 ret(i) = &a(i);
3902
3903 quicksort_op(ret, [&cmp](const T *ptr1, const T *ptr2)
3904 {
3905 return cmp(*ptr1, *ptr2);
3906 });
3907
3908 return ret;
3909 }
3910
3925 template <template <typename> class C, typename T,
3926 class Compare = Aleph::less<T>>
3927 inline
3928 DynList<const T *> bsearch_dup(const C<T> & a, const T & x, const Compare & cmp = Compare())
3929 {
3931 long idx = binary_search(a, x, cmp);
3932 if (idx < 0)
3933 return ret;
3934
3935 const T *found_ptr = &a(idx);
3936 if (not are_equals(*found_ptr, x, cmp))
3937 return ret;
3938
3939 for (long i = idx - 1; i >= 0; --i)
3940 {
3941 const T *ptr = &a(i);
3942 if (not are_equals(*ptr, x, cmp))
3943 break;
3944 ret.insert(ptr);
3945 }
3946
3947 ret.append(found_ptr);
3948
3949 for (long i = idx + 1, n = a.size(); i < n; ++i)
3950 {
3951 const T *ptr = &a(i);
3952 if (not are_equals(*ptr, x, cmp))
3953 break;
3954 ret.append(ptr);
3955 }
3956
3957 return ret;
3958 }
3959
3971 template <template <typename> class C, typename T,
3972 class Compare = Aleph::less<T>>
3973 inline
3974 T * bsearch(C<T> & a, const T & x, const Compare & cmp = Compare())
3975 {
3976 long i = binary_search(a, x, cmp);
3977 if (i < 0)
3978 return nullptr;
3979 T *ptr = &a(i);
3980 return are_equals(*ptr, x, cmp) ? ptr : nullptr;
3981 }
3982
3984 template <template <typename> class C, typename T,
3985 class Compare = Aleph::less<T>>
3986 inline
3987 const T * bsearch(const C<T> & a, const T & x, const Compare & cmp = Compare())
3988 {
3989 long i = binary_search(a, x, cmp);
3990 if (i < 0)
3991 return nullptr;
3992 const T *ptr = &a(i);
3993 return are_equals(*ptr, x, cmp) ? ptr : nullptr;
3994 }
3995
4007 template <template <typename> class C, typename T,
4008 class Compare = Aleph::less<T>>
4009 inline
4010 T * bsearch(const C<T *> & a, const T & x, const Compare & cmp = Compare())
4011 {
4012 long i = binary_search(a, x, cmp);
4013 if (i < 0)
4014 return nullptr;
4015 T *ptr = a(i);
4016 return are_equals(*ptr, x, cmp) ? ptr : nullptr;
4017 }
4018
4030 template <template <typename> class C, typename T,
4031 class Compare = Aleph::less<T>>
4032 inline
4033 DynList<T *> bsearch_dup(C<T> & a, const T & x, const Compare & cmp = Compare())
4034 {
4036 long idx = binary_search(a, x, cmp);
4037 if (idx < 0)
4038 return ret;
4039
4040 T *found_ptr = &a(idx);
4041 if (not are_equals(*found_ptr, x, cmp))
4042 return ret;
4043
4044 for (long i = idx - 1; i >= 0; --i)
4045 {
4046 T *ptr = &a(i);
4047 if (not are_equals(*ptr, x, cmp))
4048 break;
4049 ret.insert(ptr);
4050 }
4051
4052 ret.append(found_ptr);
4053
4054 for (long i = idx + 1, n = a.size(); i < n; ++i)
4055 {
4056 T *ptr = &a(i);
4057 if (not are_equals(*ptr, x, cmp))
4058 break;
4059 ret.append(ptr);
4060 }
4061
4062 return ret;
4063 }
4064
4076 template <template <typename> class C, typename T,
4077 class Compare = Aleph::less<T>>
4078 inline
4079 DynList<T *> bsearch_dup(const C<T *> & a, const T & x, const Compare & cmp = Compare())
4080 {
4082 const auto n = a.size();
4083 if (n == 0)
4084 return ret;
4085
4086 long idx = binary_search(a, x, 0, static_cast<long>(n) - 1, cmp);
4087 if (idx < 0)
4088 return ret;
4089
4090 T *found_ptr = a(idx);
4091 if (not are_equals(*found_ptr, x, cmp))
4092 return ret;
4093
4094 for (long i = idx - 1; i >= 0; --i)
4095 {
4096 T *ptr = a(i);
4097 if (not are_equals(*ptr, x, cmp))
4098 break;
4099 ret.insert(ptr);
4100 }
4101
4102 ret.append(found_ptr);
4103
4104 for (long i = idx + 1, n = a.size(); i < n; ++i)
4105 {
4106 T *ptr = a(i);
4107 if (not are_equals(*ptr, x, cmp))
4108 break;
4109 ret.append(ptr);
4110 }
4111
4112 return ret;
4113 }
4114
4148 template <template <typename> class C, typename T,
4149 class Compare = Aleph::less<T>>
4150 inline
4151 long binindex(const C<T> & a, const T & x, const Compare & cmp = Compare())
4152 {
4153 return binary_search(a, x, cmp);
4154 }
4155
4185 template <template <typename> class C, typename T,
4186 class Compare = Aleph::less<T>>
4187 inline
4188 DynList<long> binindex_dup(const C<T> & a, const T & x, const Compare & cmp = Compare())
4189 {
4191 long idx = binary_search(a, x, cmp);
4192 if (idx < 0)
4193 return ret;
4194
4195 if (not are_equals(a(idx), x, cmp))
4196 return ret;
4197
4198 const long mid = idx;
4199
4200 for (long i = idx - 1; i >= 0; --i)
4201 {
4202 if (not are_equals(a(i), x, cmp))
4203 break;
4204 ret.insert(i);
4205 }
4206
4207 ret.append(mid);
4208
4209 for (long i = idx + 1, n = a.size(); i < n; ++i)
4210 {
4211 if (not are_equals(a(i), x, cmp))
4212 break;
4213 ret.append(i);
4214 }
4215
4216 return ret;
4217 }
4218
4230 template <template <typename> class C, typename T,
4231 class Compare = Aleph::less<T>>
4232 inline
4233 DynList<long> binindex_dup(const C<T *> & a, const T & x, const Compare & cmp = Compare())
4234 {
4236 long idx = binary_search(a, x, cmp);
4237 if (idx < 0)
4238 return ret;
4239
4240 T *ptr = a(idx);
4241 if (not are_equals(*ptr, x, cmp))
4242 return ret;
4243
4244 for (long i = idx - 1; i >= 0; --i)
4245 {
4246 ptr = a(i);
4247 if (not are_equals(*ptr, x, cmp))
4248 break;
4249 ret.insert(i);
4250 }
4251
4252 ret.append(idx);
4253
4254 for (long i = idx + 1, n = a.size(); i < n; ++i)
4255 {
4256 T *ptr = a(i);
4257 if (not are_equals(*ptr, x, cmp))
4258 break;
4259 ret.append(i);
4260 }
4261
4262 return ret;
4263 }
4264
4300 template <template <typename> class C, typename T,
4301 class Compare = Aleph::less<T>>
4302 inline
4303 DynArray<size_t> build_index(const C<T> & a, const Compare & cmp = Compare())
4304 {
4305 const size_t & n = a.size();
4307 ret.reserve(a.size());
4308 for (size_t i = 0; i < n; ++i)
4309 ret(i) = i;
4310
4312 [&a, &cmp](size_t i, size_t j) { return cmp(a(i), a(j)); });
4313
4314 return ret;
4315 }
4316
4342 template <template <typename> class C, typename T,
4343 class Compare = Aleph::less<T>>
4344 inline
4345 DynArray<T *> build_index_ptr(C<T> & a, const Compare & cmp = Compare())
4346 {
4347 const size_t & n = a.size();
4349 ret.reserve(a.size());
4350 for (size_t i = 0; i < n; ++i)
4351 ret(i) = &a(i);
4352
4353 quicksort_op(ret, [&cmp](const T *ptr1, const T *ptr2)
4354 {
4355 return cmp(*ptr1, *ptr2);
4356 });
4357
4358 return ret;
4359 }
4360
4388 template <template <typename> class C, typename T,
4389 class Compare = Aleph::less<T>>
4390 inline
4391 void quicksort_op(C<T> & a, const Compare & cmp = Compare(),
4392 const size_t threshold = Quicksort_Threshold)
4393 {
4394 const size_t n = a.size();
4395 if (n <= 1)
4396 return;
4397
4398 size_t l = 0, r = n - 1;
4399
4400 FixedStack<size_t> stack((introsort_depth_limit(n) + 2) * 2);
4401
4402 push2(stack, l, r);
4403
4404 while (not stack.is_empty())
4405 {
4406 l = stack.pop();
4407 r = stack.pop();
4408
4409 const size_t partition_size = r - l + 1;
4410 if (partition_size <= 1)
4411 continue;
4412
4413 if (partition_size <= threshold)
4414 {
4415 insertion_sort(a, l, r, cmp);
4416 continue;
4417 }
4418
4419 const auto i =
4420 static_cast<size_t>(partition_op<T, Compare>(a,
4421 static_cast<long>(l),
4422 static_cast<long>(r),
4423 cmp));
4424
4425 const size_t left_size = i > l ? i - l : 0;
4426 const size_t right_size = r > i ? r - i : 0;
4427
4428 if (left_size > right_size)
4429 {
4430 if (left_size > 0)
4431 push2(stack, l, i - 1);
4432 if (right_size > 0)
4433 push2(stack, i + 1, r);
4434 }
4435 else
4436 {
4437 if (right_size > 0)
4438 push2(stack, i + 1, r);
4439 if (left_size > 0)
4440 push2(stack, l, i - 1);
4441 }
4442 }
4443 }
4444
4453 template <typename T, class Compare = Aleph::less<T>>
4454 inline
4455 long binary_search_rec(T *a, const T & x, const long l, const long r,
4456 const Compare & cmp = Compare())
4457 {
4458 if (l > r)
4459 return l;
4460
4461 const long m = l + (r - l) / 2;
4462
4463 if (cmp(x, a[m]))
4464 return binary_search_rec<T, Compare>(a, x, l, m - 1, cmp);
4465 if (cmp(a[m], x))
4466 return binary_search_rec<T, Compare>(a, x, m + 1, r, cmp);
4467
4468 return m;
4469 }
4470
4479 template <typename T, class Compare = Aleph::less<T>>
4480 inline
4481 long binary_search(T *a, const T & x, long l, long r,
4482 const Compare & cmp = Compare())
4483 {
4484 while (l <= r)
4485 {
4486 long m = l + (r - l) / 2;
4487 if (cmp(x, a[m]))
4488 r = m - 1;
4489 else if (cmp(a[m], x))
4490 l = m + 1;
4491 else
4492 return m;
4493 }
4494 return l;
4495 }
4496
4497
4524 template <typename KeyFn>
4525 requires std::invocable<const KeyFn &, size_t> and
4526 std::convertible_to<std::invoke_result_t<const KeyFn &, size_t>, int>
4529 const size_t n,
4530 const int min_key,
4531 const int max_key,
4532 const KeyFn & key_of)
4533 {
4534 ah_domain_error_if(max_key < min_key)
4535 << "counting_sort_indices(): max_key < min_key";
4536
4537 if (n == 0)
4538 return;
4539
4540 ah_out_of_range_error_if(sa.size() < n or tmp.size() < n)
4541 << "counting_sort_indices(): sa/tmp size is smaller than n";
4542
4543 const auto range_minus_one =
4544 static_cast<unsigned long long>(static_cast<long long>(max_key) -
4545 static_cast<long long>(min_key));
4546 constexpr auto max_count =
4547 static_cast<unsigned long long>(std::numeric_limits<size_t>::max() - size_t(1));
4549 << "counting_sort_indices(): key range is too large";
4550
4551 const auto k = static_cast<size_t>(range_minus_one) + 1;
4553 for (size_t i = 0; i < k; ++i)
4554 count[i] = 0;
4555
4556 for (size_t i = 0; i < n; ++i)
4557 {
4558 const int key = key_of(sa[i]);
4560 << "counting_sort_indices(): key out of expected range";
4561 const auto bucket =
4562 static_cast<size_t>(static_cast<unsigned long long>(
4563 static_cast<long long>(key) - static_cast<long long>(min_key)));
4564 ++count[bucket];
4565 }
4566
4567 for (size_t j = 1; j < k; ++j)
4568 count[j] += count[j - 1];
4569
4570 for (size_t i = n; i > 0; --i)
4571 {
4572 const int key = key_of(sa[i - 1]);
4573 const auto idx =
4574 static_cast<size_t>(static_cast<unsigned long long>(
4575 static_cast<long long>(key) - static_cast<long long>(min_key)));
4576 tmp[--count[idx]] = sa[i - 1];
4577 }
4578
4579 for (size_t i = 0; i < n; ++i)
4580 sa[i] = tmp[i];
4581 }
4582
4583
4584 namespace sort_utils_detail
4585 {
4590 template <typename T>
4592
4593
4598 template <typename T>
4600
4601
4602 template <typename IntT>
4603 using counting_unsigned_t = std::make_unsigned_t<std::remove_cv_t<IntT>>;
4604
4605
4606 template <typename IntT>
4607 using radix_unsigned_t = std::make_unsigned_t<std::remove_cv_t<IntT>>;
4608
4609
4619 template <typename IntT>
4620 [[nodiscard]] size_t
4621 counting_bucket_count(const IntT min_key, const IntT max_key)
4622 {
4623 ah_domain_error_if(max_key < min_key) << "counting_sort(): max_key < min_key";
4624
4626 const U range_minus_one =
4627 static_cast<U>(max_key) - static_cast<U>(min_key);
4628
4629 if constexpr (sizeof(U) >= sizeof(size_t))
4630 {
4631 constexpr size_t max_count =
4632 std::numeric_limits<size_t>::max() - static_cast<size_t>(1);
4634 << "counting_sort(): key range is too large";
4635 }
4636
4637 return static_cast<size_t>(range_minus_one) + 1;
4638 }
4639
4640
4648 template <typename IntT>
4649 [[nodiscard]] inline size_t
4650 counting_bucket(const IntT value, const IntT min_key) noexcept
4651 {
4652 return static_cast<size_t>(
4653 static_cast<counting_unsigned_t<IntT>>(value) -
4654 static_cast<counting_unsigned_t<IntT>>(min_key));
4655 }
4656
4657
4665 template <typename IntT>
4666 [[nodiscard]] inline IntT
4667 counting_value_from_bucket(const size_t bucket, const IntT min_key) noexcept
4668 {
4670 return static_cast<IntT>(static_cast<U>(min_key) + static_cast<U>(bucket));
4671 }
4672
4673
4680 template <template <typename> class C, typename IntT>
4681 requires counting_integer<IntT>
4683 {
4684 const size_t n = a.size();
4685 if (n < 2)
4686 return;
4687
4688 IntT min_key = a(0), max_key = a(0);
4689 for (size_t i = 1; i < n; ++i)
4690 {
4691 if (a(i) < min_key)
4692 min_key = a(i);
4693 if (a(i) > max_key)
4694 max_key = a(i);
4695 }
4696
4697 const size_t k = counting_bucket_count(min_key, max_key);
4699 for (size_t i = 0; i < k; ++i)
4700 count[i] = 0;
4701
4702 for (size_t i = 0; i < n; ++i)
4703 ++count[counting_bucket(a(i), min_key)];
4704
4705 for (size_t i = 1; i < k; ++i)
4706 count[i] += count[i - 1];
4707
4709 for (size_t i = n; i > 0; --i)
4710 {
4711 const auto bucket = counting_bucket(a(i - 1), min_key);
4712 tmp[--count[bucket]] = a(i - 1);
4713 }
4714
4715 for (size_t i = 0; i < n; ++i)
4716 a(i) = tmp[i];
4717 }
4718
4719
4720 template <typename IntT>
4721 requires counting_integer<IntT>
4722 void counting_sort_impl(IntT *a, const size_t n)
4723 {
4724 if (n < 2)
4725 return;
4726
4727 IntT min_key = a[0], max_key = a[0];
4728 for (size_t i = 1; i < n; ++i)
4729 {
4730 if (a[i] < min_key)
4731 min_key = a[i];
4732 if (a[i] > max_key)
4733 max_key = a[i];
4734 }
4735
4736 const size_t k = counting_bucket_count(min_key, max_key);
4738 for (size_t i = 0; i < k; ++i)
4739 count[i] = 0;
4740
4741 for (size_t i = 0; i < n; ++i)
4742 ++count[counting_bucket(a[i], min_key)];
4743
4744 for (size_t i = 1; i < k; ++i)
4745 count[i] += count[i - 1];
4746
4748 for (size_t i = n; i > 0; --i)
4749 {
4750 const auto bucket = counting_bucket(a[i - 1], min_key);
4751 tmp[--count[bucket]] = a[i - 1];
4752 }
4753
4754 for (size_t i = 0; i < n; ++i)
4755 a[i] = tmp[i];
4756 }
4757
4758
4759 template <typename IntT>
4760 requires counting_integer<IntT>
4762 {
4763 const size_t n = list.size();
4764 if (n < 2)
4765 return;
4766
4767 typename DynList<IntT>::Iterator it(list);
4768 IntT min_key = it.get_curr_ne();
4769 IntT max_key = min_key;
4770 for (it.next_ne(); it.has_curr(); it.next_ne())
4771 {
4772 const IntT value = it.get_curr_ne();
4773 if (value < min_key)
4774 min_key = value;
4775 if (value > max_key)
4776 max_key = value;
4777 }
4778
4779 const size_t k = counting_bucket_count(min_key, max_key);
4781 for (size_t i = 0; i < k; ++i)
4782 count[i] = 0;
4783
4784 for (typename DynList<IntT>::Iterator scan(list); scan.has_curr(); scan.next_ne())
4785 ++count[counting_bucket(scan.get_curr_ne(), min_key)];
4786
4787 typename DynList<IntT>::Iterator out(list);
4788 for (size_t bucket = 0; bucket < k; ++bucket)
4789 {
4790 const size_t times = count[bucket];
4791 if (times == 0)
4792 continue;
4793
4794 const IntT value = counting_value_from_bucket(bucket, min_key);
4795 for (size_t c = 0; c < times; ++c)
4796 {
4797 out.get_curr_ne() = value;
4798 out.next_ne();
4799 }
4800 }
4801 }
4802
4803
4804 template <typename IntT>
4805 requires counting_integer<IntT>
4807 {
4808 const size_t n = list.size();
4809 if (n < 2)
4810 return;
4811
4812 typename DynDlist<IntT>::Iterator it(list);
4813 IntT min_key = it.get_curr_ne();
4814 IntT max_key = min_key;
4815 for (it.next_ne(); it.has_curr(); it.next_ne())
4816 {
4817 const IntT value = it.get_curr_ne();
4818 if (value < min_key)
4819 min_key = value;
4820 if (value > max_key)
4821 max_key = value;
4822 }
4823
4824 const size_t k = counting_bucket_count(min_key, max_key);
4826 for (size_t i = 0; i < k; ++i)
4827 count[i] = 0;
4828
4829 for (typename DynDlist<IntT>::Iterator scan(list); scan.has_curr(); scan.next_ne())
4830 ++count[counting_bucket(scan.get_curr_ne(), min_key)];
4831
4832 typename DynDlist<IntT>::Iterator out(list);
4833 for (size_t bucket = 0; bucket < k; ++bucket)
4834 {
4835 const size_t times = count[bucket];
4836 if (times == 0)
4837 continue;
4838
4839 const IntT value = counting_value_from_bucket(bucket, min_key);
4840 for (size_t c = 0; c < times; ++c)
4841 {
4842 out.get_curr_ne() = value;
4843 out.next_ne();
4844 }
4845 }
4846 }
4847
4848
4849 template <typename IntT>
4850 [[nodiscard]] constexpr radix_unsigned_t<IntT>
4851 radix_key(const IntT value) noexcept
4852 {
4853 using U = radix_unsigned_t<IntT>;
4854 if constexpr (std::is_signed_v<std::remove_cv_t<IntT>>)
4855 return static_cast<U>(value) ^
4856 (U{1} << (std::numeric_limits<U>::digits - 1));
4857 else
4858 return static_cast<U>(value);
4859 }
4860
4861
4862 template <template <typename> class C, typename IntT>
4863 requires radix_integer<IntT>
4865 {
4866 using U = radix_unsigned_t<IntT>;
4867
4868 const size_t n = a.size();
4869 if (n < 2)
4870 return;
4871
4874
4875 for (size_t pass = 0; pass < sizeof(U); ++pass)
4876 {
4877 for (size_t i = 0; i < 256; ++i)
4878 count[i] = 0;
4879
4880 const size_t shift = pass * 8;
4881 for (size_t i = 0; i < n; ++i)
4882 {
4883 const U key = radix_key<IntT>(a(i));
4884 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4885 ++count[bucket];
4886 }
4887
4888 for (size_t i = 1; i < 256; ++i)
4889 count[i] += count[i - 1];
4890
4891 for (size_t i = n; i > 0; --i)
4892 {
4893 const U key = radix_key<IntT>(a(i - 1));
4894 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4895 tmp[--count[bucket]] = a(i - 1);
4896 }
4897
4898 for (size_t i = 0; i < n; ++i)
4899 a(i) = tmp[i];
4900 }
4901 }
4902
4903
4912 template <typename IntT>
4913 requires radix_integer<IntT>
4914 void radix_sort_impl(IntT *a, const size_t n)
4915 {
4916 using U = radix_unsigned_t<IntT>;
4917
4918 if (n < 2)
4919 return;
4920
4923
4924 for (size_t pass = 0; pass < sizeof(U); ++pass)
4925 {
4926 for (size_t i = 0; i < 256; ++i)
4927 count[i] = 0;
4928
4929 const size_t shift = pass * 8;
4930 for (size_t i = 0; i < n; ++i)
4931 {
4932 const U key = radix_key<IntT>(a[i]);
4933 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4934 ++count[bucket];
4935 }
4936
4937 for (size_t i = 1; i < 256; ++i)
4938 count[i] += count[i - 1];
4939
4940 for (size_t i = n; i > 0; --i)
4941 {
4942 const U key = radix_key<IntT>(a[i - 1]);
4943 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4944 tmp[--count[bucket]] = a[i - 1];
4945 }
4946
4947 for (size_t i = 0; i < n; ++i)
4948 a[i] = tmp[i];
4949 }
4950 }
4951
4952
4961 template <typename IntT>
4962 requires radix_integer<IntT>
4964 {
4965 using U = radix_unsigned_t<IntT>;
4966
4967 if (const size_t n = list.size(); n < 2)
4968 return;
4969
4970 Array<HTList> buckets;
4971 buckets.reserve(256);
4972 for (size_t i = 0; i < 256; ++i)
4973 buckets.append(HTList());
4974
4975 for (size_t pass = 0; pass < sizeof(U); ++pass)
4976 {
4977 const size_t shift = pass * 8;
4978
4979 while (not list.is_empty())
4980 {
4981 Slinknc *link = list.HTList::remove_first_ne();
4982 auto *node = static_cast<Snodenc<IntT> *>(link);
4983 const U key = radix_key<IntT>(node->get_data());
4984 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
4985 buckets[bucket].append(link);
4986 }
4987
4988 for (auto & bucket: buckets)
4989 list.HTList::append(bucket);
4990 }
4991 }
4992
4993
5001 template <typename IntT>
5002 requires radix_integer<IntT>
5004 {
5005 using U = radix_unsigned_t<IntT>;
5006
5007 if (const size_t n = list.size(); n < 2)
5008 return;
5009
5010 Array<Dnode<IntT>> buckets;
5011 buckets.reserve(256);
5012 for (size_t i = 0; i < 256; ++i)
5013 buckets.append(Dnode<IntT>());
5014
5015 for (size_t pass = 0; pass < sizeof(U); ++pass)
5016 {
5017 const size_t shift = pass * 8;
5018
5019 while (not list.template Dnode<IntT>::is_empty())
5020 {
5021 Dnode<IntT> *node = list.template Dnode<IntT>::remove_first_ne();
5022 const U key = radix_key<IntT>(node->get_data());
5023 const auto bucket = static_cast<size_t>((key >> shift) & U{0xFF});
5024 buckets[bucket].append(node);
5025 }
5026
5027 for (auto & bucket: buckets)
5028 list.template Dnode<IntT>::append_list(&bucket);
5029 }
5030 }
5031 } // namespace sort_utils_detail
5032
5033
5038 template <typename T>
5040
5041
5046 template <typename T>
5048
5049
5065 template <typename T>
5066 requires CountingSortable<T>
5071
5072
5088 template <typename T>
5089 requires CountingSortable<T>
5094
5095
5115 template <typename T>
5116 requires CountingSortable<T>
5121
5122
5142 template <typename T>
5143 requires CountingSortable<T>
5148
5149
5164 template <typename T>
5165 requires CountingSortable<T>
5166 void counting_sort(T *a, const size_t n)
5167 {
5168 ah_invalid_argument_if(a == nullptr and n > 0)
5169 << "counting_sort(): null pointer with non-zero length";
5171 }
5172
5173
5183 template <typename T, size_t N>
5184 requires CountingSortable<T>
5185 void counting_sort(T (&a)[N])
5186 {
5187 counting_sort(a, N);
5188 }
5189
5190
5205 template <typename T>
5206 requires RadixSortable<T>
5211
5212
5227 template <typename T>
5228 requires RadixSortable<T>
5233
5234
5245 template <typename T>
5246 requires RadixSortable<T>
5248 {
5250 }
5251
5252
5263 template <typename T>
5264 requires RadixSortable<T>
5266 {
5268 }
5269
5270
5283 template <typename T>
5284 requires RadixSortable<T>
5285 void radix_sort(T *a, const size_t n)
5286 {
5287 ah_invalid_argument_if(a == nullptr and n > 0)
5288 << "radix_sort(): null pointer with non-zero length";
5290 }
5291
5292
5302 template <typename T, size_t N>
5303 requires RadixSortable<T>
5304 void radix_sort(T (&a)[N])
5305 {
5306 radix_sort(a, N);
5307 }
5308
5309
5310 // ================================================================
5311 // Bucket Sort
5312 // ================================================================
5313
5314 namespace bucket_sort_detail
5315 {
5319 template <typename T, class Compare>
5320 void insertion_sort_range(T *a, const size_t lo, const size_t hi,
5321 const Compare & cmp)
5322 noexcept(std::is_nothrow_move_constructible_v<T> and
5323 std::is_nothrow_move_assignable_v<T> and
5324 noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
5325 std::declval<const T &>())))
5326 {
5327 for (size_t i = lo + 1; i < hi; ++i)
5328 {
5329 T tmp = std::move(a[i]);
5330 size_t j = i;
5331 while (j > lo and cmp(tmp, a[j - 1]))
5332 {
5333 a[j] = std::move(a[j - 1]);
5334 --j;
5335 }
5336 a[j] = std::move(tmp);
5337 }
5338 }
5339
5340
5351 template <typename T, class Compare, class BucketKey>
5352 void bucket_sort_impl(T *a, const size_t n, const size_t num_buckets,
5353 const BucketKey & bucket_key, const Compare & cmp)
5354 {
5355 if (n < 2)
5356 return;
5357
5358 ah_domain_error_if(num_buckets == 0)
5359 << "bucket_sort: num_buckets must be > 0";
5360
5361 // Count elements per bucket
5363 for (size_t i = 0; i < num_buckets; ++i)
5364 counts[i] = 0;
5365
5366 for (size_t i = 0; i < n; ++i)
5367 {
5368 const size_t b = bucket_key(a[i]);
5369 ah_domain_error_if(b >= num_buckets)
5370 << "bucket_sort: bucket_key returned " << b
5371 << " which is >= num_buckets (" << num_buckets << ")";
5372 ++counts[b];
5373 }
5374
5375 // Compute bucket start offsets (prefix sum)
5377 offsets[0] = 0;
5378 for (size_t i = 1; i < num_buckets; ++i)
5379 offsets[i] = offsets[i - 1] + counts[i - 1];
5380
5381 // Distribute elements into a temporary array (stable)
5383 // Reset counts for placement
5384 for (size_t i = 0; i < num_buckets; ++i)
5385 counts[i] = 0;
5386
5387 for (size_t i = 0; i < n; ++i)
5388 {
5389 const size_t b = bucket_key(a[i]);
5390 tmp[offsets[b] + counts[b]] = std::move(a[i]);
5391 ++counts[b];
5392 }
5393
5394 // Sort each bucket with insertion sort
5395 for (size_t b = 0; b < num_buckets; ++b)
5396 if (counts[b] > 1)
5398 offsets[b] + counts[b], cmp);
5399
5400 // Copy back
5401 for (size_t i = 0; i < n; ++i)
5402 a[i] = std::move(tmp[i]);
5403 }
5404
5413 template <typename T, class Container, class SortFn, class Compare>
5415 {
5416 const size_t n = a.size();
5417 if (n < 2)
5418 return;
5419
5421 size_t i = 0;
5422 for (auto it = a.get_it(); it.has_curr(); it.next_ne())
5423 tmp[i++] = std::move(it.get_curr_ne());
5424
5425 sort_fn(&tmp[0], n, cmp);
5426
5427 i = 0;
5428 for (auto it = a.get_it(); it.has_curr(); it.next_ne())
5429 it.get_curr_ne() = std::move(tmp[i++]);
5430 }
5431
5433 template <auto SortFn>
5435 {
5436 template <class Compare>
5437 auto operator()(const Compare &) const
5438 {
5439 return [](auto *p, size_t n, const Compare & c) { SortFn(p, n, c); };
5440 }
5441 };
5442
5444 template <class BucketKey>
5446 {
5449
5450 template <class Compare>
5451 auto operator()(const Compare &) const
5452 {
5453 return [this](auto *p, size_t n, const Compare & c)
5454 {
5456 };
5457 }
5458 };
5459 } // namespace bucket_sort_detail
5460
5461
5495 template <typename T, class Compare = Aleph::less<T>, class BucketKey>
5496 void bucket_sort(T *a, const size_t n, const size_t num_buckets,
5497 const BucketKey & bucket_key,
5498 const Compare & cmp = Compare())
5499 {
5500 ah_invalid_argument_if(a == nullptr and n > 0)
5501 << "bucket_sort(): null pointer with non-zero length";
5502 bucket_sort_detail::bucket_sort_impl(a, n, num_buckets, bucket_key, cmp);
5503 }
5504
5505
5531 template <typename T, class Compare = Aleph::less<T>>
5532 requires std::floating_point<T>
5533 void bucket_sort(T *a, const size_t n, const Compare & cmp = Compare())
5534 {
5535 ah_invalid_argument_if(a == nullptr and n > 0)
5536 << "bucket_sort(): null pointer with non-zero length";
5537
5538 if (n < 2)
5539 return;
5540
5541 T min_val = a[0], max_val = a[0];
5542 for (size_t i = 0; i < n; ++i)
5543 {
5544 if (not std::isfinite(a[i])) [[unlikely]]
5545 {
5546 timsort(a, n, cmp);
5547 return;
5548 }
5549 if (a[i] < min_val) min_val = a[i];
5550 if (a[i] > max_val) max_val = a[i];
5551 }
5552
5553 const T range = max_val - min_val;
5554 if (range == T{0})
5555 return; // all equal
5556
5557 // Numeric interpolation is only correct for the default arithmetic
5558 // comparators (Aleph::less / Aleph::greater). For any other comparator
5559 // the mapping from value to bucket index is undefined, so fall back to
5560 // the stable timsort which respects the caller's comparator directly.
5561 if constexpr (std::is_same_v<Compare, Aleph::less<T>> or
5562 std::is_same_v<Compare, Aleph::greater<T>>)
5563 {
5564 const bool descending = cmp(max_val, min_val) and not cmp(min_val, max_val);
5565 const size_t num_buckets = n;
5566 auto key = [min_val, range, num_buckets, descending](const T & val) -> size_t
5567 {
5568 auto b = static_cast<size_t>((val - min_val) / range *
5569 static_cast<T>(num_buckets - 1));
5570 if (b >= num_buckets)
5571 b = num_buckets - 1;
5572
5573 if (descending)
5574 b = (num_buckets - 1) - b;
5575
5576 return b;
5577 };
5578
5579 bucket_sort_detail::bucket_sort_impl(a, n, num_buckets, key, cmp);
5580 }
5581 else
5582 {
5583 // Custom comparator: bucket interpolation is not order-preserving;
5584 // delegate to timsort which respects any strict-weak-ordering comparator.
5585 timsort(a, n, cmp);
5586 }
5587 }
5588
5589
5602 template <typename T, class Compare = Aleph::less<T>>
5603 requires std::floating_point<T>
5604 void bucket_sort(DynArray<T> & a, const Compare & cmp = Compare())
5605 {
5606 bucket_sort_detail::apply_sort_with_temp<T>(a,
5607 bucket_sort_detail::sort_invoker_factory<static_cast<void(*)(T*, size_t, const Compare&)>(bucket_sort)>{}(cmp),
5608 cmp);
5609 }
5610
5611
5621 template <typename T, class Compare = Aleph::less<T>>
5622 requires std::floating_point<T>
5623 void bucket_sort(Array<T> & a, const Compare & cmp = Compare())
5624 {
5625 if (a.size() < 2)
5626 return;
5627 bucket_sort(&a[0], a.size(), cmp);
5628 }
5629
5630
5641 template <typename T, size_t N, class Compare = Aleph::less<T>>
5642 requires std::floating_point<T>
5643 void bucket_sort(T (&a)[N], const Compare & cmp = Compare())
5644 {
5645 bucket_sort(static_cast<T *>(a), N, cmp);
5646 }
5647
5648
5661 template <typename T, class Compare = Aleph::less<T>>
5662 requires std::floating_point<T>
5663 void bucket_sort(DynList<T> & list, const Compare & cmp = Compare())
5664 {
5665 bucket_sort_detail::apply_sort_with_temp<T>(list,
5666 bucket_sort_detail::sort_invoker_factory<static_cast<void(*)(T*, size_t, const Compare&)>(bucket_sort)>{}(cmp),
5667 cmp);
5668 }
5669
5670
5683 template <typename T, class Compare = Aleph::less<T>, class BucketKey>
5684 void bucket_sort(DynArray<T> & a, const size_t num_buckets,
5685 const BucketKey & bucket_key,
5686 const Compare & cmp = Compare())
5687 {
5688 bucket_sort_detail::apply_sort_with_temp<T>(a,
5690 cmp);
5691 }
5692
5693
5706 template <typename T, class Compare = Aleph::less<T>, class BucketKey>
5707 void bucket_sort(Array<T> & a, const size_t num_buckets,
5708 const BucketKey & bucket_key,
5709 const Compare & cmp = Compare())
5710 {
5711 if (a.size() < 2)
5712 return;
5713 bucket_sort(&a[0], a.size(), num_buckets, bucket_key, cmp);
5714 }
5715
5716
5717 // ================================================================
5718 // Timsort
5719 // ================================================================
5720
5721 namespace timsort_detail
5722 {
5729 [[nodiscard]] constexpr size_t compute_minrun(size_t n) noexcept
5730 {
5731 size_t r = 0;
5732 while (n >= 64)
5733 {
5734 r |= n & 1;
5735 n >>= 1;
5736 }
5737 return n + r;
5738 }
5739
5740
5744 template <typename T>
5745 void reverse_range(T *a, size_t lo, size_t hi)
5746 noexcept(std::is_nothrow_swappable_v<T>)
5747 {
5748 while (lo + 1 < hi)
5749 {
5750 std::swap(a[lo], a[hi - 1]);
5751 ++lo;
5752 --hi;
5753 }
5754 }
5755
5756
5765 template <typename T, class Compare>
5766 size_t count_run_and_make_ascending(T *a, const size_t lo,
5767 const size_t hi,
5768 const Compare & cmp)
5769 {
5770 if (hi - lo <= 1)
5771 return hi - lo;
5772
5773 size_t run_hi = lo + 1;
5774 if (cmp(a[run_hi], a[lo])) // descending
5775 {
5776 while (run_hi < hi and cmp(a[run_hi], a[run_hi - 1]))
5777 ++run_hi;
5778 reverse_range(a, lo, run_hi);
5779 }
5780 else // ascending
5781 while (run_hi < hi and not cmp(a[run_hi], a[run_hi - 1]))
5782 ++run_hi;
5783 return run_hi - lo;
5784 }
5785
5786
5795 template <typename T, class Compare>
5796 void binary_insertion_sort(T *a, const size_t lo, const size_t hi,
5797 const size_t start, const Compare & cmp)
5798 noexcept(std::is_nothrow_move_constructible_v<T> and
5799 std::is_nothrow_move_assignable_v<T> and
5800 noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
5801 std::declval<const T &>())))
5802
5803 {
5804 size_t i = (start <= lo) ? lo + 1 : start;
5805 for (; i < hi; ++i)
5806 {
5807 T pivot = std::move(a[i]);
5808
5809 // Binary search for insertion point in [lo, i)
5810 size_t left = lo;
5811 size_t right = i;
5812 while (left < right)
5813 {
5814 const size_t mid = left + (right - left) / 2;
5815 if (cmp(pivot, a[mid]))
5816 right = mid;
5817 else
5818 left = mid + 1;
5819 }
5820
5821 // Shift elements [left, i) right by one
5822 for (size_t p = i; p > left; --p)
5823 a[p] = std::move(a[p - 1]);
5824 a[left] = std::move(pivot);
5825 }
5826 }
5827
5828
5846 template <typename T, class Compare>
5847 size_t gallop_left(const T & key, const T *a, const size_t len,
5848 const size_t hint, const Compare & cmp)
5849 noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
5850 std::declval<const T &>())))
5851 {
5852 size_t last_ofs = 0;
5853 size_t ofs = 1;
5854
5855 if (cmp(a[hint], key)) // key > a[hint]: search right
5856 {
5857 const size_t max_ofs = len - hint;
5858 while (ofs < max_ofs and cmp(a[hint + ofs], key))
5859 {
5860 last_ofs = ofs;
5861 ofs = (ofs << 1) + 1;
5862 if (ofs > max_ofs)
5863 ofs = max_ofs;
5864 }
5865 last_ofs += hint;
5866 ofs += hint;
5867 }
5868 else // key <= a[hint]: search left
5869 {
5870 const size_t max_ofs = hint + 1;
5871 while (ofs < max_ofs and not cmp(a[hint - ofs], key))
5872 {
5873 last_ofs = ofs;
5874 ofs = (ofs << 1) + 1;
5875 if (ofs > max_ofs)
5876 ofs = max_ofs;
5877 }
5878 const size_t tmp = last_ofs;
5879 // Note: last_ofs may intentionally underflow if hint < ofs.
5880 // This is compensated by the subsequent ++last_ofs to effectively
5881 // start the binary search at index 0. This is safe for unsigned types.
5882 last_ofs = hint - ofs;
5883 ofs = hint - tmp;
5884 }
5885
5886 // Binary search in (last_ofs, ofs]
5887 ++last_ofs;
5888 while (last_ofs < ofs)
5889 {
5890 const size_t mid = last_ofs + (ofs - last_ofs) / 2;
5891 if (cmp(a[mid], key))
5892 last_ofs = mid + 1;
5893 else
5894 ofs = mid;
5895 }
5896 return ofs;
5897 }
5898
5899
5916 template <typename T, class Compare>
5917 size_t gallop_right(const T & key, const T *a, const size_t len,
5918 const size_t hint, const Compare & cmp)
5919 noexcept(noexcept(std::declval<const Compare &>()(std::declval<const T &>(),
5920 std::declval<const T &>())))
5921 {
5922 size_t last_ofs = 0;
5923 size_t ofs = 1;
5924
5925 if (cmp(key, a[hint])) // key < a[hint]: search left
5926 {
5927 const size_t max_ofs = hint + 1;
5928 while (ofs < max_ofs and cmp(key, a[hint - ofs]))
5929 {
5930 last_ofs = ofs;
5931 ofs = (ofs << 1) + 1;
5932 if (ofs > max_ofs)
5933 ofs = max_ofs;
5934 }
5935 const size_t tmp = last_ofs;
5936 // Note: last_ofs may intentionally underflow if hint < ofs.
5937 // This is compensated by the subsequent ++last_ofs to effectively
5938 // start the binary search at index 0. This is safe for unsigned types.
5939 last_ofs = hint - ofs;
5940 ofs = hint - tmp;
5941 }
5942 else // key >= a[hint]: search right
5943 {
5944 const size_t max_ofs = len - hint;
5945 while (ofs < max_ofs and not cmp(key, a[hint + ofs]))
5946 {
5947 last_ofs = ofs;
5948 ofs = (ofs << 1) + 1;
5949 if (ofs > max_ofs)
5950 ofs = max_ofs;
5951 }
5952 last_ofs += hint;
5953 ofs += hint;
5954 }
5955
5956 ++last_ofs;
5957 while (last_ofs < ofs)
5958 {
5959 const size_t mid = last_ofs + (ofs - last_ofs) / 2;
5960 if (cmp(key, a[mid]))
5961 ofs = mid;
5962 else
5963 last_ofs = mid + 1;
5964 }
5965 return ofs;
5966 }
5967
5968
5975 template <typename T, class Compare>
5976 void merge_lo(T *a, const size_t base1, const size_t len1,
5977 const size_t base2, const size_t len2,
5978 T *tmp, const Compare & cmp)
5979 {
5980 // Copy run1 into tmp
5981 for (size_t i = 0; i < len1; ++i)
5982 tmp[i] = std::move(a[base1 + i]);
5983
5984 size_t c1 = 0; // cursor into tmp (run1 copy)
5985 size_t c2 = base2; // cursor into a (run2)
5986 size_t dest = base1; // write cursor into a
5987 const size_t end1 = len1;
5988 const size_t end2 = base2 + len2;
5989
5990 while (c1 < end1 and c2 < end2)
5991 if (cmp(a[c2], tmp[c1]))
5992 a[dest++] = std::move(a[c2++]);
5993 else
5994 a[dest++] = std::move(tmp[c1++]);
5995
5996 // Copy remaining from tmp (run1)
5997 while (c1 < end1)
5998 a[dest++] = std::move(tmp[c1++]);
5999 // Remaining run2 elements are already in place
6000 }
6001
6002
6007 template <typename T, class Compare>
6008 void merge_hi(T *a, const size_t base1, const size_t len1,
6009 const size_t base2, const size_t len2,
6010 T *tmp, const Compare & cmp)
6011 {
6012 // Copy run2 into tmp
6013 for (size_t i = 0; i < len2; ++i)
6014 tmp[i] = std::move(a[base2 + i]);
6015
6016 // Merge from the right (highest to lowest)
6017 size_t dest = base2 + len2; // one past end
6018 size_t c1 = base1 + len1; // one past end of run1 in a
6019 size_t c2 = len2; // one past end of run2 in tmp
6020
6021 while (c1 > base1 and c2 > 0)
6022 if (cmp(tmp[c2 - 1], a[c1 - 1]))
6023 a[--dest] = std::move(a[--c1]);
6024 else
6025 a[--dest] = std::move(tmp[--c2]);
6026
6027 // Copy remaining from tmp (run2)
6028 while (c2 > 0)
6029 a[--dest] = std::move(tmp[--c2]);
6030 // Remaining run1 elements are already in place
6031 }
6032
6033
6039 template <typename T, class Compare>
6040 void merge_at(T *a, size_t *run_base, size_t *run_len,
6041 size_t & stack_size, const size_t at,
6042 T *tmp, const Compare & cmp)
6043 {
6044 size_t base1 = run_base[at];
6045 size_t len1 = run_len[at];
6046 size_t base2 = run_base[at + 1];
6047 size_t len2 = run_len[at + 1];
6048
6049 // Record merged run
6050 run_len[at] = len1 + len2;
6051 if (at == stack_size - 3)
6052 {
6053 run_base[at + 1] = run_base[at + 2];
6054 run_len[at + 1] = run_len[at + 2];
6055 }
6056 --stack_size;
6057
6058 // Where does the first element of run2 go in run1?
6059 size_t k = gallop_right(a[base2], a + base1, len1, 0, cmp);
6060 base1 += k;
6061 len1 -= k;
6062 if (len1 == 0)
6063 return;
6064
6065 // Where does the last element of run1 go in run2?
6066 len2 = gallop_left(a[base1 + len1 - 1], a + base2, len2,
6067 len2 - 1, cmp);
6068 if (len2 == 0)
6069 return;
6070
6071 if (len1 <= len2)
6072 merge_lo(a, base1, len1, base2, len2, tmp, cmp);
6073 else
6074 merge_hi(a, base1, len1, base2, len2, tmp, cmp);
6075 }
6076
6077
6085 template <typename T, class Compare>
6086 void merge_collapse(T *a, size_t *run_base, size_t *run_len,
6087 size_t & stack_size, T *tmp,
6088 const Compare & cmp)
6089 {
6090 while (stack_size > 1)
6091 {
6092 long n = static_cast<long>(stack_size) - 2;
6093 if ((n > 0 and run_len[n - 1] <= run_len[n] + run_len[n + 1]) or
6094 (n > 1 and run_len[n - 2] <= run_len[n - 1] + run_len[n]))
6095 {
6096 if (run_len[n - 1] < run_len[n + 1])
6097 --n;
6099 }
6100 else if (run_len[n] <= run_len[n + 1])
6102 else
6103 break;
6104 }
6105 }
6106
6107
6109 template <typename T, class Compare>
6110 void merge_force_collapse(T *a, size_t *run_base, size_t *run_len,
6111 size_t & stack_size, T *tmp,
6112 const Compare & cmp)
6113 {
6114 while (stack_size > 1)
6115 {
6116 long n = static_cast<long>(stack_size) - 2;
6117 if (n > 0 and run_len[n - 1] < run_len[n + 1])
6118 --n;
6120 }
6121 }
6122
6123
6138 template <typename T, class Compare>
6139 void timsort_impl(T *a, const size_t n, const Compare & cmp)
6140 {
6141 if (n < 2)
6142 return;
6143
6144 // For very small arrays, just use binary insertion sort
6145 if (n < 64)
6146 {
6147 const size_t run_len = count_run_and_make_ascending(a, 0, n, cmp);
6149 return;
6150 }
6151
6152 const size_t minrun = compute_minrun(n);
6153
6154 // Run stack — log2(n) + 1 entries is sufficient
6155 // (timsort invariant guarantees Fibonacci-like growth)
6156 constexpr size_t MAX_STACK = 85; // enough for 2^64 elements
6157 size_t run_base_arr[MAX_STACK];
6158 size_t run_len_arr[MAX_STACK];
6159 size_t stack_size = 0;
6160
6161 // Temporary merge buffer — allocated once, sized to n/2
6162 // (merge_lo/merge_hi copy the smaller run)
6163 Array<T> tmp_buf = Array<T>::create(n / 2 + 1);
6164 T *tmp = &tmp_buf[0];
6165
6166 size_t lo = 0;
6167 size_t remaining = n;
6168
6169 while (remaining > 0)
6170 {
6171 // Find the next natural run
6172 size_t run_len = count_run_and_make_ascending(a, lo, lo + remaining,
6173 cmp);
6174
6175 // If the run is too short, extend it with insertion sort
6176 if (run_len < minrun)
6177 {
6178 const size_t force = remaining < minrun ? remaining : minrun;
6179 binary_insertion_sort(a, lo, lo + force, lo + run_len, cmp);
6180 run_len = force;
6181 }
6182
6183 // Push this run onto the stack
6185 << "timsort: run stack overflow (stack_size=" << stack_size
6186 << " >= MAX_STACK=" << MAX_STACK << "); this should never happen";
6189 ++stack_size;
6190
6191 // Maintain the stack invariants
6193
6194 lo += run_len;
6195 remaining -= run_len;
6196 }
6197
6198 // Force-merge all remaining runs
6200 }
6201 } // namespace timsort_detail
6202
6203
6231 template <typename T, class Compare = Aleph::less<T>>
6232 void timsort(T *a, const size_t n, const Compare & cmp = Compare())
6233 {
6234 if (n == 0)
6235 return;
6236
6237 ah_invalid_argument_if(a == nullptr)
6238 << "timsort(): null pointer with non-zero length";
6239
6240 if (n < 2)
6241 return;
6242
6244 }
6245
6246
6259 template <typename T, class Compare = Aleph::less<T>>
6260 void timsort(T *a, const long l, const long r,
6261 const Compare & cmp = Compare())
6262 {
6263 ah_range_error_if(l < 0 or r < 0)
6264 << "timsort(): negative bounds [" << l << ", " << r << "] are not allowed";
6265
6266 if (l >= r)
6267 return;
6268
6269 ah_invalid_argument_if(a == nullptr)
6270 << "timsort(): null pointer contract violation for range ["
6271 << l << ", " << r << "]; cannot call timsort_detail::timsort_impl()";
6272
6273 timsort_detail::timsort_impl(a + l, static_cast<size_t>(r - l + 1), cmp);
6274 }
6275
6276
6286 template <typename T, class Compare = Aleph::less<T>>
6287 void timsort(DynArray<T> & a, const Compare & cmp = Compare())
6288 {
6289 bucket_sort_detail::apply_sort_with_temp<T>(a,
6290 bucket_sort_detail::sort_invoker_factory<static_cast<void(*)(T*, size_t, const Compare&)>(timsort)>{}(cmp),
6291 cmp);
6292 }
6293
6294
6304 template <typename T, class Compare = Aleph::less<T>>
6305 void timsort(Array<T> & a, const Compare & cmp = Compare())
6306 {
6307 if (a.size() < 2)
6308 return;
6309 timsort(&a[0], a.size(), cmp);
6310 }
6311
6312
6323 template <typename T, size_t N, class Compare = Aleph::less<T>>
6324 requires std::is_invocable_r_v<bool, Compare, const T &, const T &>
6325 void timsort(T (&a)[N], const Compare & cmp = Compare())
6326 {
6327 timsort(static_cast<T *>(a), N, cmp);
6328 }
6329
6330
6343 template <typename T, class Compare = Aleph::less<T>>
6344 void timsort(DynList<T> & list, const Compare & cmp = Compare())
6345 {
6346 bucket_sort_detail::apply_sort_with_temp<T>(list,
6347 bucket_sort_detail::sort_invoker_factory<static_cast<void(*)(T*, size_t, const Compare&)>(timsort)>{}(cmp),
6348 cmp);
6349 }
6350
6351
6364 template <typename T, class Compare = Aleph::less<T>>
6365 void timsort(DynDlist<T> & list, const Compare & cmp = Compare())
6366 {
6367 bucket_sort_detail::apply_sort_with_temp<T>(list,
6368 bucket_sort_detail::sort_invoker_factory<static_cast<void(*)(T*, size_t, const Compare&)>(timsort)>{}(cmp),
6369 cmp);
6370 }
6371}
6372
6373#endif // TPL_SORT_UTILS_H
C++20 concepts for constraining comparison functors.
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:250
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:579
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_logic_error()
Throws std::logic_error unconditionally.
Definition ah-errors.H:341
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
#define ah_range_error_if(C)
Throws std::range_error if condition holds.
Definition ah-errors.H:207
#define ah_out_of_range_error()
Throws std::out_of_range unconditionally.
Definition ah-errors.H:611
Functional programming utilities for Aleph-w containers.
General utility functions and helpers.
long double h
Definition btreepic.C:154
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
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.
void reserve(const size_t l, const size_t r)
Allocate a range of entries.
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
Dynamic singly linked list with functional programming support.
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.
Value concept for counting sort.
Value concept for radix sort.
#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:4111
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4118
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.
constexpr radix_unsigned_t< IntT > radix_key(const IntT value) noexcept
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
void counting_sort_impl(C< IntT > &a)
Internal implementation of counting sort for containers.
std::make_unsigned_t< std::remove_cv_t< IntT > > radix_unsigned_t
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 radix_sort_impl(C< IntT > &a)
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
and
Check uniqueness with explicit hash + equality functors.
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.
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.
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.
Dnode< T > * dlink_random_search(Dlink &list, const T &x, const Compare &cmp=Compare())
Random search for an element in a dlink list.
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.
void heapsort(T *array, const size_t n, const Compare &cmp=Compare())
Sort an array using the heapsort algorithm.
void faster_heapsort(T *array, const size_t n, const Compare &cmp=Compare())
Optimized version of heapsort.
void mergesort(T *a, const long l, const long r, std::vector< T > &buf, Compare cmp)
Sort an array using merge sort with a reusable buffer.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
T * bsearch(C< T > &a, const T &x, const Compare &cmp=Compare())
Search for a value in a sorted container returning a pointer.
DynArray< size_t > build_index(const C< T > &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 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)
DynArray< const T * > build_index_ptr(const C< T > &a, const Compare &cmp=Compare())
Build an index array of pointers for indirect sorting (const version).
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