Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
utility.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstdint>
5#include <cstdio>
6#include <tuple>
7#include <type_traits>
8#include <utility>
9
10#if GAIA_PLATFORM_WINDOWS
11 #include <malloc.h>
12#else
13 #include <alloca.h>
14#endif
15
16#include "gaia/core/iterator.h"
17
18namespace gaia {
20 constexpr uint32_t BadIndex = uint32_t(-1);
21
22#if GAIA_COMPILER_MSVC || GAIA_PLATFORM_WINDOWS
23 #define GAIA_STRCPY(var, max_len, text) \
24 strncpy_s((var), (text), (max_len)); \
25 (var)[(max_len) - 1] = 0;
26 #define GAIA_STRFMT(var, max_len, fmt, ...) sprintf_s((var), (max_len), fmt, __VA_ARGS__)
27 #define GAIA_STRLEN(var, max_len) strnlen_s((var), (max_len))
28#else
29 #define GAIA_STRCPY(var, max_len, text) \
30 { \
31 strncpy((var), (text), (max_len)); \
32 (var)[(max_len) - 1] = 0; \
33 }
34 #define GAIA_STRFMT(var, max_len, fmt, ...) snprintf((var), (max_len), fmt, __VA_ARGS__)
35 #define GAIA_STRLEN(var, max_len) strnlen((var), (max_len))
36#endif
37
38 namespace core {
43 template <typename C>
44 struct fixed_capacity: std::integral_constant<uint32_t, 0> {};
45
47 namespace detail {
50 template <class T>
51 struct rem_rp {
53 using type = std::remove_reference_t<std::remove_pointer_t<T>>;
54 };
55
58 template <typename T>
59 struct is_mut:
60 std::bool_constant<
61 !std::is_const_v<typename rem_rp<T>::type> &&
62 (std::is_pointer<T>::value || std::is_reference<T>::value)> {};
63
67 template <typename Type, typename SizeProbe = size_t>
68 struct is_complete: std::false_type {};
69
72 template <typename T>
73 struct is_complete<T, decltype(sizeof(T))>: std::true_type {};
74
79 template <typename C>
80 constexpr auto size(C& c) noexcept -> decltype(c.size()) {
81 return c.size();
82 }
87 template <typename C>
88 constexpr auto size(const C& c) noexcept -> decltype(c.size()) {
89 return c.size();
90 }
95 template <typename T, auto N>
96 constexpr auto size(const T (&)[N]) noexcept {
97 return N;
98 }
99
104 template <typename C>
105 constexpr auto data(C& c) noexcept -> decltype(c.data()) {
106 return c.data();
107 }
112 template <typename C>
113 constexpr auto data(const C& c) noexcept -> decltype(c.data()) {
114 return c.data();
115 }
121 template <typename T, auto N>
122 constexpr T* data(T (&array)[N]) noexcept {
123 return array;
124 }
125 } // namespace detail
127
129 struct zero_t {
130 explicit constexpr zero_t() = default;
131 };
132
134 inline constexpr zero_t zero{};
135
138 template <typename T>
139 using rem_rp_t = typename detail::rem_rp<T>::type;
140
143 template <typename T>
144 inline constexpr bool is_mut_v = detail::is_mut<T>::value;
145
148 template <typename T>
149 using raw_t = typename std::decay_t<std::remove_pointer_t<T>>;
150
153 template <typename T>
154 inline constexpr bool is_raw_v = std::is_same_v<T, raw_t<T>> && !std::is_array_v<T>;
155
158 template <typename T>
159 inline constexpr bool is_complete_v = detail::is_complete<T>::value;
160
165 template <typename T>
166 constexpr T* addressof(T& obj) noexcept {
167 return &obj;
168 }
169
172 template <typename T>
173 const T* addressof(const T&&) = delete;
174
179 template <typename T>
180 constexpr bool check_alignment(const T* ptr) noexcept {
181 return (reinterpret_cast<uintptr_t>(ptr)) % alignof(T) == 0;
182 }
183
186 template <typename T>
187 struct lock_scope {
190
193 lock_scope(T& ctx): m_ctx(ctx) {
194 ctx.lock();
195 }
198 m_ctx.unlock();
199 }
200
202 lock_scope(const lock_scope&) = delete;
204 lock_scope& operator=(const lock_scope&) = delete;
209 };
210
211 //----------------------------------------------------------------------
212 // Container identification
213 //----------------------------------------------------------------------
214
218 template <typename T, typename Probe = void>
219 struct has_data_size: std::false_type {};
220
223 template <typename T>
225 T, std::void_t< //
226 decltype(detail::data(std::declval<T>())), //
227 decltype(detail::size(std::declval<T>())) //
228 >>: std::true_type {};
229
233 template <typename T, typename Probe = void>
234 struct has_size_begin_end: std::false_type {};
235
238 template <typename T>
240 T, std::void_t< //
241 decltype(std::declval<T>().begin()), //
242 decltype(std::declval<T>().end()), //
243 decltype(detail::size(std::declval<T>())) //
244 >>: std::true_type {};
245
246 //----------------------------------------------------------------------
247 // Bit-byte conversion
248 //----------------------------------------------------------------------
249
254 template <typename T>
255 constexpr T as_bits(T value) {
256 static_assert(std::is_integral_v<T>);
257 return value * (T)8;
258 }
259
264 template <typename T>
265 constexpr T as_bytes(T value) {
266 static_assert(std::is_integral_v<T>);
267 return value / (T)8;
268 }
269
270 //----------------------------------------------------------------------
271 // Memory size helpers
272 //----------------------------------------------------------------------
273
278 template <typename T>
279 constexpr uint32_t count_bits(T number) {
280 uint32_t bits_needed = 0;
281 while (number > 0U) {
282 number >>= 1U;
283 ++bits_needed;
284 }
285 return bits_needed;
286 }
287
292 template <typename T>
293 constexpr bool is_pow2(T number) {
294 static_assert(std::is_integral<T>::value, "is_pow2 must be used with integer types");
295
296 return (number & (number - 1)) == 0;
297 }
298
303 template <typename T>
304 constexpr T closest_pow2(T number) {
305 static_assert(std::is_integral<T>::value, "closest_pow2 must be used with integer types");
306
307 if (is_pow2(number))
308 return number;
309
310 // Collapse all bits below the highest set bit
311 number |= number >> 1;
312 number |= number >> 2;
313 number |= number >> 4;
314 if constexpr (sizeof(T) > 1)
315 number |= number >> 8;
316 if constexpr (sizeof(T) > 2)
317 number |= number >> 16;
318 if constexpr (sizeof(T) > 4)
319 number |= number >> 32;
320
321 // The result is now one less than the next power of two, so shift back
322 return number - (number >> 1);
323 }
324
325 //----------------------------------------------------------------------
326 // Element construction / destruction
327 //----------------------------------------------------------------------
328
332 template <typename T>
333 void call_ctor_raw(T* pData) {
334 GAIA_ASSERT(pData != nullptr);
335 (void)::new (const_cast<void*>(static_cast<const volatile void*>(core::addressof(*pData)))) T;
336 }
337
342 template <typename T>
343 void call_ctor_raw_n(T* pData, size_t cnt) {
344 GAIA_ASSERT(pData != nullptr);
345 for (size_t i = 0; i < cnt; ++i) {
346 auto* ptr = pData + i;
347 (void)::new (const_cast<void*>(static_cast<const volatile void*>(core::addressof(*ptr)))) T;
348 }
349 }
350
354 template <typename T>
355 void call_ctor_val(T* pData) {
356 GAIA_ASSERT(pData != nullptr);
357 (void)::new (const_cast<void*>(static_cast<const volatile void*>(core::addressof(*pData)))) T();
358 }
359
364 template <typename T>
365 void call_ctor_val_n(T* pData, size_t cnt) {
366 GAIA_ASSERT(pData != nullptr);
367 for (size_t i = 0; i < cnt; ++i) {
368 auto* ptr = pData + i;
369 (void)::new (const_cast<void*>(static_cast<const volatile void*>(core::addressof(*ptr)))) T();
370 }
371 }
372
377 template <typename T>
378 void call_ctor([[maybe_unused]] T* pData) {
379 GAIA_ASSERT(pData != nullptr);
380 if constexpr (!std::is_trivially_constructible_v<T>) {
381 (void)::new (pData) T();
382 }
383 }
384
389 template <typename T>
390 void call_ctor_n([[maybe_unused]] T* pData, [[maybe_unused]] size_t cnt) {
391 GAIA_ASSERT(pData != nullptr);
392 if constexpr (!std::is_trivially_constructible_v<T>) {
393 for (size_t i = 0; i < cnt; ++i)
394 (void)::new (pData + i) T();
395 }
396 }
397
403 template <typename T, typename... Args>
404 void call_ctor(T* pData, Args&&... args) {
405 GAIA_ASSERT(pData != nullptr);
406 if constexpr (std::is_constructible_v<T, Args...>)
407 (void)::new (pData) T(GAIA_FWD(args)...);
408 else
409 (void)::new (pData) T{GAIA_FWD(args)...};
410 }
411
416 template <typename T>
417 void call_dtor([[maybe_unused]] T* pData) {
418 GAIA_ASSERT(pData != nullptr);
419 if constexpr (!std::is_trivially_destructible_v<T>) {
420 pData->~T();
421 }
422 }
423
428 template <typename T>
429 void call_dtor_n([[maybe_unused]] T* pData, [[maybe_unused]] size_t cnt) {
430 GAIA_ASSERT(pData != nullptr);
431 if constexpr (!std::is_trivially_destructible_v<T>) {
432 for (size_t i = 0; i < cnt; ++i)
433 pData[i].~T();
434 }
435 }
436
437 //----------------------------------------------------------------------
438 // Element swapping
439 //----------------------------------------------------------------------
440
445 template <typename T>
446 constexpr void swap(T& left, T& right) {
447 T tmp = GAIA_MOV(left);
448 left = GAIA_MOV(right);
449 right = GAIA_MOV(tmp);
450 }
451
459 template <typename T, typename TCmpFunc>
460 constexpr void swap_if(T* c, size_t lhs, size_t rhs, TCmpFunc cmpFunc) noexcept {
461 if (!cmpFunc(c[lhs], c[rhs]))
462 core::swap(c[lhs], c[rhs]);
463 }
464
471 template <typename T, typename TCmpFunc>
472 constexpr void swap_if(T& lhs, T& rhs, TCmpFunc cmpFunc) noexcept {
473 if (!cmpFunc(lhs, rhs))
474 core::swap(lhs, rhs);
475 }
476
483 template <typename T, typename TCmpFunc>
484 constexpr void swap_if_not(T& lhs, T& rhs, TCmpFunc cmpFunc) noexcept {
485 if (cmpFunc(lhs, rhs))
486 core::swap(lhs, rhs);
487 }
488
498 template <typename T, typename TCmpFunc, typename TSwapFunc>
499 constexpr void try_swap_if(T* c, uint32_t lhs, uint32_t rhs, TCmpFunc cmpFunc, TSwapFunc swapFunc) noexcept {
500 if (!cmpFunc(c[lhs], c[rhs]))
501 swapFunc(lhs, rhs);
502 }
503
513 template <typename C, typename TCmpFunc, typename TSwapFunc>
514 constexpr void try_swap_if(
515 C& c, typename C::size_type lhs, typename C::size_type rhs, TCmpFunc cmpFunc, TSwapFunc swapFunc) noexcept {
516 if (!cmpFunc(c[lhs], c[rhs]))
517 swapFunc(lhs, rhs);
518 }
519
529 template <typename C, typename TCmpFunc, typename TSwapFunc>
530 constexpr void try_swap_if_not(
531 C& c, typename C::size_type lhs, typename C::size_type rhs, TCmpFunc cmpFunc, TSwapFunc swapFunc) noexcept {
532 if (cmpFunc(c[lhs], c[rhs]))
533 swapFunc(lhs, rhs);
534 }
535
536 //----------------------------------------------------------------------
537 // Value filling
538 //----------------------------------------------------------------------
539
546 template <class ForwardIt, class T>
547 constexpr void fill(ForwardIt first, ForwardIt last, const T& value) {
548 for (; first != last; ++first) {
549 *first = value;
550 }
551 }
552
553 //----------------------------------------------------------------------
554 // Value range checking
555 //----------------------------------------------------------------------
556
562 template <class T>
563 constexpr const T& get_min(const T& a, const T& b) {
564 return (b < a) ? b : a;
565 }
566
572 template <class T>
573 constexpr const T& get_max(const T& a, const T& b) {
574 return (b > a) ? b : a;
575 }
576
577 //----------------------------------------------------------------------
578 // Checking if a template arg is unique among the rest
579 //----------------------------------------------------------------------
580
583 template <typename... Types>
584 inline constexpr auto is_unique = std::true_type{};
585
589 template <typename T, typename... Rest>
590 inline constexpr auto is_unique<T, Rest...> =
591 std::bool_constant<(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>>{};
592
594 namespace detail {
597 template <typename T>
598 struct type_identity {
600 using type = T;
601 };
602 } // namespace detail
604
608 template <typename T, typename... Ts>
609 struct unique: detail::type_identity<T> {}; // TODO: In C++20 we could use std::type_identity
610
615 template <typename... Ts, typename U, typename... Us>
616 struct unique<std::tuple<Ts...>, U, Us...>:
617 std::conditional_t<
618 (std::is_same_v<U, Ts> || ...), unique<std::tuple<Ts...>, Us...>, unique<std::tuple<Ts..., U>, Us...>> {};
619
622 template <typename... Ts>
623 using unique_tuple = typename unique<std::tuple<>, Ts...>::type;
624
625 //----------------------------------------------------------------------
626 // Calculating total size of all types of tuple
627 //----------------------------------------------------------------------
628
633 template <typename... Args>
634 constexpr unsigned get_args_size(std::tuple<Args...> const& tuple) {
635 (void)tuple;
636 return (sizeof(Args) + ...);
637 }
638
639 //----------------------------------------------------------------------
640 // Function arguments type checks
641 //----------------------------------------------------------------------
642
645 template <typename... Type>
646 struct func_type_list {};
647
653 template <typename Class, typename Ret, typename... Args>
654 func_type_list<Args...> func_args(Ret (Class::*)(Args...) const);
655
656 //----------------------------------------------------------------------
657 // Member function checks
658 //----------------------------------------------------------------------
659
660#if __cpp_concepts
661 #define GAIA_DEFINE_HAS_MEMBER_FUNC(function_name) \
662 template <typename T, typename... Args> \
663 concept has_mfunc_check_##function_name = requires(T&& t, Args&&... args) { t.function_name(GAIA_FWD(args)...); }; \
664 \
665 template <typename T, typename... Args> \
666 struct has_func_##function_name { \
667 static constexpr bool value = has_mfunc_check_##function_name<T, Args...>; \
668 }
669#else
671 namespace detail {
677 template <class Default, class AlwaysVoid, template <class...> class Op, class... Args>
678 struct member_func_checker {
680 using value_t = std::false_type;
682 using type = Default;
683 };
684
689 template <class Default, template <class...> class Op, class... Args>
690 struct member_func_checker<Default, std::void_t<Op<Args...>>, Op, Args...> {
692 using value_t = std::true_type;
694 using type = Op<Args...>;
695 };
696
698 struct member_func_none {
699 ~member_func_none() = delete;
700 member_func_none(member_func_none const&) = delete;
701 member_func_none(member_func_none&&) = delete;
702 void operator=(member_func_none const&) = delete;
703 void operator=(member_func_none&&) = delete;
704 };
705 } // namespace detail
707
711 template <template <class...> class Op, typename... Args>
712 using has_mfunc = typename detail::member_func_checker<detail::member_func_none, void, Op, Args...>::value_t;
713
714 #define GAIA_DEFINE_HAS_MEMBER_FUNC(function_name) \
715 template <typename T, typename... Args> \
716 using has_mfunc_check_##function_name = decltype(std::declval<T>().function_name(std::declval<Args>()...)); \
717 \
718 template <typename T, typename... Args> \
719 struct has_func_##function_name { \
720 static constexpr bool value = gaia::core::has_mfunc<has_mfunc_check_##function_name, T, Args...>::value; \
721 }
722#endif
723
725 GAIA_DEFINE_HAS_MEMBER_FUNC(find);
726 GAIA_DEFINE_HAS_MEMBER_FUNC(find_if);
727 GAIA_DEFINE_HAS_MEMBER_FUNC(find_if_not);
729
730 //----------------------------------------------------------------------
731 // Special function checks
732 //----------------------------------------------------------------------
733
735 namespace detail {
739 template <typename T>
740 constexpr auto has_mfunc_equals_check(int)
741 -> decltype(std::declval<T>().operator==(std::declval<T>()), std::true_type{});
746 template <typename T, typename... Args>
747 constexpr std::false_type has_mfunc_equals_check(...);
748
752 template <typename T>
753 constexpr auto has_ffunc_equals_check(int)
754 -> decltype(operator==(std::declval<T>(), std::declval<T>()), std::true_type{});
759 template <typename T, typename... Args>
760 constexpr std::false_type has_ffunc_equals_check(...);
761 } // namespace detail
763
766 template <typename T>
769 static constexpr bool value = decltype(detail::has_mfunc_equals_check<T>(0))::value;
770 };
771
774 template <typename T>
777 static constexpr bool value = decltype(detail::has_ffunc_equals_check<T>(0))::value;
778 };
779
780 //----------------------------------------------------------------------
781 // Type helpers
782 //----------------------------------------------------------------------
783
786 template <typename... Type>
787 struct type_list {
791 static constexpr auto size = sizeof...(Type);
792 };
793
797 template <typename TypesA, typename TypesB>
799
803 template <typename... TypesA, typename... TypesB>
804 struct type_list_concat<type_list<TypesA...>, type_list<TypesB...>> {
806 using type = type_list<TypesA..., TypesB...>;
807 };
808
809 //----------------------------------------------------------------------
810 // Looping
811 //----------------------------------------------------------------------
812
814 namespace detail {
821 template <auto FirstIdx, auto Iters, typename Func, auto... Is>
822 constexpr void each_impl(Func func, std::integer_sequence<decltype(Iters), Is...> /*no_name*/) {
823 if constexpr ((std::is_invocable_v<Func&&, std::integral_constant<decltype(Is), Is>> && ...))
824 (func(std::integral_constant<decltype(Is), FirstIdx + Is>{}), ...);
825 else
826 (((void)Is, func()), ...);
827 }
828
835 template <auto FirstIdx, typename Tuple, typename Func, auto... Is>
836 void each_tuple_impl(Func func, std::integer_sequence<decltype(FirstIdx), Is...> /*no_name*/) {
837 if constexpr (
838 (std::is_invocable_v<
839 Func&&, decltype(std::tuple_element_t<FirstIdx + Is, Tuple>{}),
840 std::integral_constant<decltype(FirstIdx), Is>> &&
841 ...))
842 // func(Args&& arg, uint32_t idx)
843 (func(
844 std::tuple_element_t<FirstIdx + Is, Tuple>{},
845 std::integral_constant<decltype(FirstIdx), FirstIdx + Is>{}),
846 ...);
847 else
848 // func(Args&& arg)
849 (func(std::tuple_element_t<FirstIdx + Is, Tuple>{}), ...);
850 }
851
859 template <auto FirstIdx, typename Tuple, typename Func, auto... Is>
860 void each_tuple_impl(Tuple&& tuple, Func func, std::integer_sequence<decltype(FirstIdx), Is...> /*no_name*/) {
861 if constexpr (
862 (std::is_invocable_v<
863 Func&&, decltype(std::get<FirstIdx + Is>(tuple)), std::integral_constant<decltype(FirstIdx), Is>> &&
864 ...))
865 // func(Args&& arg, uint32_t idx)
866 (func(std::get<FirstIdx + Is>(tuple), std::integral_constant<decltype(FirstIdx), FirstIdx + Is>{}), ...);
867 else
868 // func(Args&& arg)
869 (func(std::get<FirstIdx + Is>(tuple)), ...);
870 }
871 } // namespace detail
873
890 template <auto Iters, typename Func>
891 constexpr void each(Func func) {
892 using TIters = decltype(Iters);
893 constexpr TIters First = 0;
894 detail::each_impl<First, Iters, Func>(func, std::make_integer_sequence<TIters, Iters>());
895 }
896
915 template <auto FirstIdx, auto LastIdx, typename Func>
916 constexpr void each_ext(Func func) {
917 static_assert(LastIdx >= FirstIdx);
918 const auto Iters = LastIdx - FirstIdx;
919 detail::each_impl<FirstIdx, Iters, Func>(func, std::make_integer_sequence<decltype(Iters), Iters>());
920 }
921
941 template <auto FirstIdx, auto LastIdx, auto Inc, typename Func>
942 constexpr void each_ext(Func func) {
943 if constexpr (FirstIdx < LastIdx) {
944 if constexpr (std::is_invocable_v<Func&&, std::integral_constant<decltype(FirstIdx), FirstIdx>>)
945 func(std::integral_constant<decltype(FirstIdx), FirstIdx>());
946 else
947 func();
948
949 each_ext<FirstIdx + Inc, LastIdx, Inc>(func);
950 }
951 }
952
967 template <typename Func, typename... Args>
968 constexpr void each_pack(Func func, Args&&... args) {
969 (func(GAIA_FWD(args)), ...);
970 }
971
989 template <typename Tuple, typename Func>
990 constexpr void each_tuple(Tuple&& tuple, Func func) {
991 using TTSize = uint32_t;
992 constexpr auto TSize = (TTSize)std::tuple_size<std::remove_reference_t<Tuple>>::value;
993 detail::each_tuple_impl<(TTSize)0>(GAIA_FWD(tuple), func, std::make_integer_sequence<TTSize, TSize>{});
994 }
995
1014 template <typename Tuple, typename Func>
1015 constexpr void each_tuple(Func func) {
1016 using TTSize = uint32_t;
1017 constexpr auto TSize = (TTSize)std::tuple_size<std::remove_reference_t<Tuple>>::value;
1018 detail::each_tuple_impl<(TTSize)0, Tuple>(func, std::make_integer_sequence<TTSize, TSize>{});
1019 }
1020
1040 template <auto FirstIdx, auto LastIdx, typename Tuple, typename Func>
1041 constexpr void each_tuple_ext(Tuple&& tuple, Func func) {
1042 constexpr auto TSize = std::tuple_size<std::remove_reference_t<Tuple>>::value;
1043 static_assert(LastIdx >= FirstIdx);
1044 static_assert(LastIdx <= TSize);
1045 constexpr auto Iters = LastIdx - FirstIdx;
1046 detail::each_tuple_impl<FirstIdx>(GAIA_FWD(tuple), func, std::make_integer_sequence<decltype(FirstIdx), Iters>{});
1047 }
1048
1069 template <auto FirstIdx, auto LastIdx, typename Tuple, typename Func>
1070 constexpr void each_tuple_ext(Func func) {
1071 constexpr auto TSize = std::tuple_size<std::remove_reference_t<Tuple>>::value;
1072 static_assert(LastIdx >= FirstIdx);
1073 static_assert(LastIdx <= TSize);
1074 constexpr auto Iters = LastIdx - FirstIdx;
1075 detail::each_tuple_impl<FirstIdx, Tuple>(func, std::make_integer_sequence<decltype(FirstIdx), Iters>{});
1076 }
1077
1085 template <typename InputIt, typename Func>
1086 constexpr Func each(InputIt first, InputIt last, Func func) {
1087 for (; first != last; ++first)
1088 func(*first);
1089 return func;
1090 }
1091
1098 template <typename C, typename Func>
1099 constexpr auto each(const C& arr, Func func) {
1100 return each(arr.begin(), arr.end(), func);
1101 }
1102
1103 //----------------------------------------------------------------------
1104 // Lookups
1105 //----------------------------------------------------------------------
1106
1114 template <typename InputIt, typename T>
1115 constexpr InputIt find(InputIt first, InputIt last, const T& value) {
1116 if constexpr (std::is_pointer_v<InputIt>) {
1117 auto size = distance(first, last);
1118 auto it = first;
1119 for (decltype(size) i = 0; i < size; ++i, ++it) {
1120 if (*it == value)
1121 return it;
1122 }
1123 } else if constexpr (is_random_iter_v<InputIt>) {
1124 auto size = distance(first, last);
1125 auto it = first;
1126 for (decltype(size) i = 0; i < size; ++i, ++it) {
1127 if (*it == value)
1128 return it;
1129 }
1130 } else {
1131 for (; first != last; ++first) {
1132 if (*first == value)
1133 return first;
1134 }
1135 }
1136 return last;
1137 }
1138
1145 template <typename C, typename V>
1146 constexpr auto find(const C& arr, const V& item) {
1147 if constexpr (has_func_find<C>::value)
1148 return arr.find(item);
1149 else
1150 return core::find(arr.begin(), arr.end(), item);
1151 }
1152
1160 template <typename InputIt, typename Func>
1161 constexpr InputIt find_if(InputIt first, InputIt last, Func func) {
1162 if constexpr (std::is_pointer_v<InputIt>) {
1163 auto size = distance(first, last);
1164 auto it = first;
1165 for (decltype(size) i = 0; i < size; ++i, ++it) {
1166 if (func(*it))
1167 return it;
1168 }
1169 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
1170 auto size = distance(first, last);
1171 auto it = first;
1172 for (decltype(size) i = 0; i < size; ++i, ++it) {
1173 if (func(*it))
1174 return it;
1175 }
1176 } else {
1177 for (; first != last; ++first) {
1178 if (func(*first))
1179 return first;
1180 }
1181 }
1182 return last;
1183 }
1184
1191 template <typename UnaryPredicate, typename C>
1192 constexpr auto find_if(const C& arr, UnaryPredicate predicate) {
1193 if constexpr (has_func_find_if<C, UnaryPredicate>::value)
1194 return arr.find_id(predicate);
1195 else
1196 return core::find_if(arr.begin(), arr.end(), predicate);
1197 }
1198
1206 template <typename InputIt, typename Func>
1207 constexpr InputIt find_if_not(InputIt first, InputIt last, Func func) {
1208 if constexpr (std::is_pointer_v<InputIt>) {
1209 auto size = distance(first, last);
1210 auto it = first;
1211 for (decltype(size) i = 0; i < size; ++i, ++it) {
1212 if (!func(*it))
1213 return it;
1214 }
1215 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
1216 auto size = distance(first, last);
1217 auto it = first;
1218 for (decltype(size) i = 0; i < size; ++i, ++it) {
1219 if (!func(*it))
1220 return it;
1221 }
1222 } else {
1223 for (; first != last; ++first) {
1224 if (!func(*first))
1225 return first;
1226 }
1227 }
1228 return last;
1229 }
1230
1237 template <typename UnaryPredicate, typename C>
1238 constexpr auto find_if_not(const C& arr, UnaryPredicate predicate) {
1239 if constexpr (has_func_find_if_not<C, UnaryPredicate>::value)
1240 return arr.find_if_not(predicate);
1241 else
1242 return core::find_if_not(arr.begin(), arr.end(), predicate);
1243 }
1244
1245 //----------------------------------------------------------------------
1246
1253 template <typename C, typename V>
1254 constexpr bool has(const C& arr, const V& item) {
1255 const auto it = find(arr, item);
1256 return it != arr.end();
1257 }
1258
1265 template <typename UnaryPredicate, typename C>
1266 constexpr bool has_if(const C& arr, UnaryPredicate predicate) {
1267 const auto it = find_if(arr, predicate);
1268 return it != arr.end();
1269 }
1270
1271 //----------------------------------------------------------------------
1272
1278 template <typename C>
1279 constexpr auto get_index(const C& arr, typename C::const_reference item) {
1280 const auto it = find(arr, item);
1281 if (it == arr.end())
1282 return BadIndex;
1283
1284 return (decltype(BadIndex))core::distance(arr.begin(), it);
1285 }
1286
1293 template <typename C>
1294 constexpr auto get_index_unsafe(const C& arr, typename C::const_reference item) {
1295 return (decltype(BadIndex))core::distance(arr.begin(), find(arr, item));
1296 }
1297
1304 template <typename UnaryPredicate, typename C>
1305 constexpr auto get_index_if(const C& arr, UnaryPredicate predicate) {
1306 const auto it = find_if(arr, predicate);
1307 if (it == arr.end())
1308 return BadIndex;
1309
1310 return (decltype(BadIndex))core::distance(arr.begin(), it);
1311 }
1312
1320 template <typename UnaryPredicate, typename C>
1321 constexpr auto get_index_if_unsafe(const C& arr, UnaryPredicate predicate) {
1322 return (decltype(BadIndex))core::distance(arr.begin(), find_if(arr, predicate));
1323 }
1324
1325 //----------------------------------------------------------------------
1326 // Erasure
1327 //----------------------------------------------------------------------
1328
1337 template <typename C>
1338 void swap_erase_unsafe(C& arr, typename C::size_type idx) {
1339 GAIA_ASSERT(idx < arr.size());
1340
1341 if (idx + 1 != arr.size())
1342 arr[idx] = arr[arr.size() - 1];
1343
1344 arr.pop_back();
1345 }
1346
1354 template <typename C>
1355 void swap_erase(C& arr, typename C::size_type idx) {
1356 if (idx >= arr.size())
1357 return;
1358
1359 if (idx + 1 != arr.size())
1360 arr[idx] = arr[arr.size() - 1];
1361
1362 arr.pop_back();
1363 }
1364
1365 //----------------------------------------------------------------------
1366 // Comparison
1367 //----------------------------------------------------------------------
1368
1371 template <typename T>
1372 struct equal_to {
1377 constexpr bool operator()(const T& lhs, const T& rhs) const {
1378 return lhs == rhs;
1379 }
1380 };
1381
1384 template <typename T>
1385 struct is_smaller {
1390 constexpr bool operator()(const T& lhs, const T& rhs) const {
1391 return lhs < rhs;
1392 }
1393 };
1394
1397 template <typename T>
1403 constexpr bool operator()(const T& lhs, const T& rhs) const {
1404 return lhs <= rhs;
1405 }
1406 };
1407
1410 template <typename T>
1411 struct is_greater {
1416 constexpr bool operator()(const T& lhs, const T& rhs) const {
1417 return lhs > rhs;
1418 }
1419 };
1420
1421 //----------------------------------------------------------------------
1422 // Sorting
1423 //----------------------------------------------------------------------
1424
1426 namespace detail {
1436 template <typename Container, typename TCmpFunc>
1437 int sort_median_of_three(Container& arr, int low, int mid, int high, TCmpFunc cmpFunc) {
1438 if (cmpFunc(arr[(uint32_t)low], arr[(uint32_t)mid])) {
1439 if (cmpFunc(arr[(uint32_t)mid], arr[(uint32_t)high]))
1440 return mid;
1441 return cmpFunc(arr[(uint32_t)low], arr[(uint32_t)high]) ? high : low;
1442 }
1443
1444 if (cmpFunc(arr[(uint32_t)low], arr[(uint32_t)high]))
1445 return low;
1446 return cmpFunc(arr[(uint32_t)mid], arr[(uint32_t)high]) ? high : mid;
1447 }
1448
1457 template <typename Container, typename TCmpFunc>
1458 int sort_choose_pivot(Container& arr, int low, int high, TCmpFunc cmpFunc) {
1459 const int size = high - low + 1;
1460 const int mid = low + (size >> 1);
1461 if (size > 1024) {
1462 const int step = size >> 3;
1463 const int lowMed = sort_median_of_three(arr, low, low + step, low + step + step, cmpFunc);
1464 const int midMed = sort_median_of_three(arr, mid - step, mid, mid + step, cmpFunc);
1465 const int highMed = sort_median_of_three(arr, high - step - step, high - step, high, cmpFunc);
1466 return sort_median_of_three(arr, lowMed, midMed, highMed, cmpFunc);
1467 }
1468
1469 return sort_median_of_three(arr, low, mid, high, cmpFunc);
1470 }
1471
1481 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1482 void insertion_sort_range(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1483 for (int i = low + 1; i <= high; ++i) {
1484 for (int j = i; j > low && cmpFunc(arr[(uint32_t)j], arr[(uint32_t)(j - 1)]); --j)
1485 swapFunc((uint32_t)(j - 1), (uint32_t)j);
1486 }
1487 }
1488
1498 template <typename Container, typename TCmpFunc>
1499 void insertion_sort_range(Container& arr, int low, int high, TCmpFunc cmpFunc) {
1500 for (int i = low + 1; i <= high; ++i) {
1501 if (cmpFunc(arr[(uint32_t)i], arr[(uint32_t)(i - 1)])) {
1502 auto tmp = GAIA_MOV(arr[(uint32_t)i]);
1503 int j = i;
1504 do {
1505 arr[(uint32_t)j] = GAIA_MOV(arr[(uint32_t)(j - 1)]);
1506 --j;
1507 } while (j > low && cmpFunc(tmp, arr[(uint32_t)(j - 1)]));
1508 arr[(uint32_t)j] = GAIA_MOV(tmp);
1509 }
1510 }
1511 }
1512
1523 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1524 void sort_order_three(Container& arr, int lhs, int mid, int rhs, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1525 if (cmpFunc(arr[(uint32_t)mid], arr[(uint32_t)lhs]))
1526 swapFunc((uint32_t)lhs, (uint32_t)mid);
1527 if (cmpFunc(arr[(uint32_t)rhs], arr[(uint32_t)mid])) {
1528 swapFunc((uint32_t)mid, (uint32_t)rhs);
1529 if (cmpFunc(arr[(uint32_t)mid], arr[(uint32_t)lhs]))
1530 swapFunc((uint32_t)lhs, (uint32_t)mid);
1531 }
1532 }
1533
1544 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1545 void sort_prepare_pivot_first(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1546 constexpr int NintherThreshold = 128;
1547 const int size = high - low + 1;
1548 const int mid = low + (size >> 1);
1549
1550 if (size > NintherThreshold) {
1551 sort_order_three(arr, low, mid, high, cmpFunc, swapFunc);
1552 sort_order_three(arr, low + 1, mid - 1, high - 1, cmpFunc, swapFunc);
1553 sort_order_three(arr, low + 2, mid + 1, high - 2, cmpFunc, swapFunc);
1554 sort_order_three(arr, mid - 1, mid, mid + 1, cmpFunc, swapFunc);
1555 swapFunc((uint32_t)low, (uint32_t)mid);
1556 } else {
1557 sort_order_three(arr, mid, low, high, cmpFunc, swapFunc);
1558 }
1559 }
1560
1571 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1572 int quick_sort_partition_pivot_first(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1573 const int begin = low;
1574 int first = low;
1575 int last = high + 1;
1576 const int end = last;
1577 auto pivot = GAIA_MOV(arr[(uint32_t)first]);
1578
1579 do {
1580 ++first;
1581 } while (first != end && cmpFunc(arr[(uint32_t)first], pivot));
1582
1583 if (begin == first - 1) {
1584 while (first < last && !cmpFunc(arr[(uint32_t)(--last)], pivot)) {
1585 }
1586 } else {
1587 do {
1588 --last;
1589 } while (!cmpFunc(arr[(uint32_t)last], pivot));
1590 }
1591
1592 while (first < last) {
1593 swapFunc((uint32_t)first, (uint32_t)last);
1594 do {
1595 ++first;
1596 } while (cmpFunc(arr[(uint32_t)first], pivot));
1597 do {
1598 --last;
1599 } while (!cmpFunc(arr[(uint32_t)last], pivot));
1600 }
1601
1602 const int pivotPos = first - 1;
1603 if (begin != pivotPos)
1604 arr[(uint32_t)begin] = GAIA_MOV(arr[(uint32_t)pivotPos]);
1605 arr[(uint32_t)pivotPos] = GAIA_MOV(pivot);
1606 return pivotPos;
1607 }
1608
1618 template <typename T, typename TCmpFunc, typename TSwapFunc>
1619 bool try_sorted_or_reversed(T* beg, uint32_t n, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1620 if (n <= 1)
1621 return true;
1622
1623 bool sorted = true;
1624 for (uint32_t i = 1; i < n; ++i) {
1625 if (cmpFunc(beg[i], beg[i - 1])) {
1626 sorted = false;
1627 break;
1628 }
1629 }
1630 if (sorted)
1631 return true;
1632
1633 bool reversed = true;
1634 for (uint32_t i = 1; i < n; ++i) {
1635 if (cmpFunc(beg[i - 1], beg[i])) {
1636 reversed = false;
1637 break;
1638 }
1639 }
1640 if (!reversed)
1641 return false;
1642
1643 for (uint32_t i = 0, j = n - 1; i < j; ++i, --j)
1644 swapFunc(i, j);
1645 return true;
1646 }
1647
1658 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1659 int quick_sort_partition_hoare(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1660 const int pivotIdx = sort_choose_pivot(arr, low, high, cmpFunc);
1661 const auto pivot = arr[(uint32_t)pivotIdx];
1662
1663 int i = low - 1;
1664 int j = high + 1;
1665
1666 while (true) {
1667 do {
1668 ++i;
1669 } while (cmpFunc(arr[(uint32_t)i], pivot));
1670
1671 do {
1672 --j;
1673 } while (cmpFunc(pivot, arr[(uint32_t)j]));
1674
1675 if (i >= j)
1676 return j;
1677
1678 swapFunc((uint32_t)i, (uint32_t)j);
1679 }
1680 }
1681
1692 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1693 void heap_sift_down(Container& arr, int low, int root, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1694 while (true) {
1695 const int left = low + ((root - low) << 1) + 1;
1696 if (left > high)
1697 return;
1698
1699 int child = left;
1700 const int right = left + 1;
1701 if (right <= high && cmpFunc(arr[(uint32_t)child], arr[(uint32_t)right]))
1702 child = right;
1703
1704 if (!cmpFunc(arr[(uint32_t)root], arr[(uint32_t)child]))
1705 return;
1706
1707 swapFunc((uint32_t)root, (uint32_t)child);
1708 root = child;
1709 }
1710 }
1711
1721 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1722 void heap_sort_range(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
1723 for (int start = low + ((high - low - 1) >> 1); start >= low; --start)
1724 heap_sift_down(arr, low, start, high, cmpFunc, swapFunc);
1725
1726 for (int end = high; end > low; --end) {
1727 swapFunc((uint32_t)low, (uint32_t)end);
1728 heap_sift_down(arr, low, low, end - 1, cmpFunc, swapFunc);
1729 }
1730 }
1731
1735 inline uint32_t sort_depth_limit(uint32_t n) {
1736 uint32_t depth = 0;
1737 while (n > 1) {
1738 ++depth;
1739 n >>= 1;
1740 }
1741 return depth * 2;
1742 }
1743
1754 template <typename Container, typename TCmpFunc, typename TSwapFunc>
1755 void
1756 quick_sort_impl(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc, uint32_t depthLimit) {
1757 constexpr int InsertionSortThreshold = 24;
1758
1759 while (high - low > InsertionSortThreshold) {
1760 if (depthLimit == 0) {
1761 heap_sort_range(arr, low, high, cmpFunc, swapFunc);
1762 return;
1763 }
1764 --depthLimit;
1765
1766 const int split = quick_sort_partition_hoare(arr, low, high, cmpFunc, swapFunc);
1767 const int leftSize = split - low + 1;
1768 const int rightSize = high - split;
1769
1770 if (leftSize < rightSize) {
1771 quick_sort_impl(arr, low, split, cmpFunc, swapFunc, depthLimit);
1772 low = split + 1;
1773 } else {
1774 quick_sort_impl(arr, split + 1, high, cmpFunc, swapFunc, depthLimit);
1775 high = split;
1776 }
1777 }
1778
1779 if (low < high)
1780 insertion_sort_range(arr, low, high, cmpFunc, swapFunc);
1781 }
1782
1793 template <typename Container, typename TCmpFunc>
1794 void quick_sort_impl(Container& arr, int low, int high, TCmpFunc cmpFunc, uint32_t depthLimit) {
1795 constexpr int InsertionSortThreshold = 24;
1796 auto swapFunc = [&arr](uint32_t a, uint32_t b) {
1797 core::swap(arr[a], arr[b]);
1798 };
1799
1800 while (high - low > InsertionSortThreshold) {
1801 if (depthLimit == 0) {
1802 heap_sort_range(arr, low, high, cmpFunc, swapFunc);
1803 return;
1804 }
1805 --depthLimit;
1806
1807 sort_prepare_pivot_first(arr, low, high, cmpFunc, swapFunc);
1808 const int pivot = quick_sort_partition_pivot_first(arr, low, high, cmpFunc, swapFunc);
1809 const int leftSize = pivot - low;
1810 const int rightSize = high - pivot;
1811
1812 if (leftSize < rightSize) {
1813 quick_sort_impl(arr, low, pivot - 1, cmpFunc, depthLimit);
1814 low = pivot + 1;
1815 } else {
1816 quick_sort_impl(arr, pivot + 1, high, cmpFunc, depthLimit);
1817 high = pivot - 1;
1818 }
1819 }
1820
1821 if (low < high)
1822 insertion_sort_range(arr, low, high, cmpFunc);
1823 }
1824
1826 inline constexpr uint32_t SortNetworkMaxN = 17;
1828 inline constexpr uint32_t SortSmallMaxN = 32;
1829
1834 template <uint32_t N>
1835 struct sort_network;
1836 template <>
1837 struct sort_network<2> {
1839 static constexpr uint8_t pairs[][2] = {{0, 1}};
1840 };
1841 template <>
1842 struct sort_network<3> {
1844 static constexpr uint8_t pairs[][2] = {{1, 2}, {0, 2}, {0, 1}};
1845 };
1846 template <>
1847 struct sort_network<4> {
1849 static constexpr uint8_t pairs[][2] = {{0, 1}, {2, 3}, {0, 2}, {1, 3}, {1, 2}};
1850 };
1851 template <>
1852 struct sort_network<5> {
1854 static constexpr uint8_t pairs[][2] = {{0, 1}, {3, 4}, {2, 4}, {2, 3}, {1, 4}, {0, 3}, {0, 2}, {1, 3}, {1, 2}};
1855 };
1856 template <>
1857 struct sort_network<6> {
1859 static constexpr uint8_t pairs[][2] = {{1, 2}, {4, 5}, {0, 2}, {3, 5}, {0, 1}, {3, 4},
1860 {2, 5}, {0, 3}, {1, 4}, {2, 4}, {1, 3}, {2, 3}};
1861 };
1862 template <>
1863 struct sort_network<7> {
1865 static constexpr uint8_t pairs[][2] = {{1, 2}, {3, 4}, {5, 6}, {0, 2}, {3, 5}, {4, 6}, {0, 1}, {4, 5},
1866 {2, 6}, {0, 4}, {1, 5}, {0, 3}, {2, 5}, {1, 3}, {2, 4}, {2, 3}};
1867 };
1868 template <>
1869 struct sort_network<8> {
1871 static constexpr uint8_t pairs[][2] = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {0, 2}, {1, 3}, {4, 6},
1872 {5, 7}, {1, 2}, {5, 6}, {0, 4}, {3, 7}, {1, 5}, {2, 6},
1873 {1, 4}, {3, 6}, {2, 4}, {3, 5}, {3, 4}};
1874 };
1875 template <>
1876 struct sort_network<9> {
1878 static constexpr uint8_t pairs[][2] = {{0, 1}, {3, 4}, {6, 7}, {1, 2}, {4, 5}, {7, 8}, {0, 1}, {3, 4}, {6, 7},
1879 {0, 3}, {3, 6}, {0, 3}, {1, 4}, {4, 7}, {1, 4}, {2, 5}, {5, 8}, {2, 5},
1880 {1, 3}, {5, 7}, {2, 6}, {4, 6}, {2, 4}, {2, 3}, {5, 6}};
1881 };
1882 template <>
1883 struct sort_network<10> {
1885 static constexpr uint8_t pairs[][2] = {{0, 8}, {1, 9}, {2, 7}, {3, 5}, {4, 6}, {0, 2}, {1, 4}, {5, 8},
1886 {7, 9}, {0, 3}, {2, 4}, {5, 7}, {6, 9}, {0, 1}, {3, 6}, {8, 9},
1887 {1, 5}, {2, 3}, {4, 8}, {6, 7}, {1, 2}, {3, 5}, {4, 6}, {7, 8},
1888 {2, 3}, {4, 5}, {6, 7}, {3, 4}, {5, 6}};
1889 };
1890 template <>
1891 struct sort_network<11> {
1893 static constexpr uint8_t pairs[][2] = {
1894 {0, 9}, {1, 6}, {2, 4}, {3, 7}, {5, 8}, {0, 1}, {3, 5}, {4, 10}, {6, 9}, {7, 8}, {1, 3}, {2, 5},
1895 {4, 7}, {8, 10}, {0, 4}, {1, 2}, {3, 7}, {5, 9}, {6, 8}, {0, 1}, {2, 6}, {4, 5}, {7, 8}, {9, 10},
1896 {2, 4}, {3, 6}, {5, 7}, {8, 9}, {1, 2}, {3, 4}, {5, 6}, {7, 8}, {2, 3}, {4, 5}, {6, 7}};
1897 };
1898 template <>
1899 struct sort_network<12> {
1901 static constexpr uint8_t pairs[][2] = {{0, 8}, {1, 7}, {2, 6}, {3, 11}, {4, 10}, {5, 9}, {0, 1}, {2, 5},
1902 {3, 4}, {6, 9}, {7, 8}, {10, 11}, {0, 2}, {1, 6}, {5, 10}, {9, 11},
1903 {0, 3}, {1, 2}, {4, 6}, {5, 7}, {8, 11}, {9, 10}, {1, 4}, {3, 5},
1904 {6, 8}, {7, 10}, {1, 3}, {2, 5}, {6, 9}, {8, 10}, {2, 3}, {4, 5},
1905 {6, 7}, {8, 9}, {4, 6}, {5, 7}, {3, 4}, {5, 6}, {7, 8}};
1906 };
1907 template <>
1908 struct sort_network<13> {
1910 static constexpr uint8_t pairs[][2] = {
1911 {0, 12}, {1, 10}, {2, 9}, {3, 7}, {5, 11}, {6, 8}, {1, 6}, {2, 3}, {4, 11}, {7, 9}, {8, 10}, {0, 4},
1912 {1, 2}, {3, 6}, {7, 8}, {9, 10}, {11, 12}, {4, 6}, {5, 9}, {8, 11}, {10, 12}, {0, 5}, {3, 8}, {4, 7},
1913 {6, 11}, {9, 10}, {0, 1}, {2, 5}, {6, 9}, {7, 8}, {10, 11}, {1, 3}, {2, 4}, {5, 6}, {9, 10}, {1, 2},
1914 {3, 4}, {5, 7}, {6, 8}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {3, 4}, {5, 6}};
1915 };
1916 template <>
1917 struct sort_network<14> {
1919 static constexpr uint8_t pairs[][2] = {
1920 {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {0, 2}, {1, 3}, {4, 8}, {5, 9},
1921 {10, 12}, {11, 13}, {0, 4}, {1, 2}, {3, 7}, {5, 8}, {6, 10}, {9, 13}, {11, 12}, {0, 6}, {1, 5},
1922 {3, 9}, {4, 10}, {7, 13}, {8, 12}, {2, 10}, {3, 11}, {4, 6}, {7, 9}, {1, 3}, {2, 8}, {5, 11},
1923 {6, 7}, {10, 12}, {1, 4}, {2, 6}, {3, 5}, {7, 11}, {8, 10}, {9, 12}, {2, 4}, {3, 6}, {5, 8},
1924 {7, 10}, {9, 11}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {6, 7}};
1925 };
1926 template <>
1927 struct sort_network<15> {
1929 static constexpr uint8_t pairs[][2] = {
1930 {1, 2}, {3, 10}, {4, 14}, {5, 8}, {6, 13}, {7, 12}, {9, 11}, {0, 14}, {1, 5}, {2, 8},
1931 {3, 7}, {6, 9}, {10, 12}, {11, 13}, {0, 7}, {1, 6}, {2, 9}, {4, 10}, {5, 11}, {8, 13},
1932 {12, 14}, {0, 6}, {2, 4}, {3, 5}, {7, 11}, {8, 10}, {9, 12}, {13, 14}, {0, 3}, {1, 2},
1933 {4, 7}, {5, 9}, {6, 8}, {10, 11}, {12, 13}, {0, 1}, {2, 3}, {4, 6}, {7, 9}, {10, 12},
1934 {11, 13}, {1, 2}, {3, 5}, {8, 10}, {11, 12}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {2, 3},
1935 {4, 5}, {6, 7}, {8, 9}, {10, 11}, {5, 6}, {7, 8}};
1936 };
1937 template <>
1938 struct sort_network<16> {
1940 static constexpr uint8_t pairs[][2] = {
1941 {0, 13}, {1, 12}, {2, 15}, {3, 14}, {4, 8}, {5, 6}, {7, 11}, {9, 10}, {0, 5}, {1, 7},
1942 {2, 9}, {3, 4}, {6, 13}, {8, 14}, {10, 15}, {11, 12}, {0, 1}, {2, 3}, {4, 5}, {6, 8},
1943 {7, 9}, {10, 11}, {12, 13}, {14, 15}, {0, 2}, {1, 3}, {4, 10}, {5, 11}, {6, 7}, {8, 9},
1944 {12, 14}, {13, 15}, {1, 2}, {3, 12}, {4, 6}, {5, 7}, {8, 10}, {9, 11}, {13, 14}, {1, 4},
1945 {2, 6}, {5, 8}, {7, 10}, {9, 13}, {11, 14}, {2, 4}, {3, 6}, {9, 12}, {11, 13}, {3, 5},
1946 {6, 8}, {7, 9}, {10, 12}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {6, 7}, {8, 9}};
1947 };
1948 template <>
1949 struct sort_network<17> {
1951 static constexpr uint8_t pairs[][2] = {
1952 {0, 11}, {1, 15}, {2, 10}, {3, 5}, {4, 6}, {8, 12}, {9, 16}, {13, 14}, {0, 6},
1953 {1, 13}, {2, 8}, {4, 14}, {5, 15}, {7, 11}, {0, 8}, {3, 7}, {4, 9}, {6, 16},
1954 {10, 11}, {12, 14}, {0, 2}, {1, 4}, {5, 6}, {7, 13}, {8, 9}, {10, 12}, {11, 14},
1955 {15, 16}, {0, 3}, {2, 5}, {6, 11}, {7, 10}, {9, 13}, {12, 15}, {14, 16}, {0, 1},
1956 {3, 4}, {5, 10}, {6, 9}, {7, 8}, {11, 15}, {13, 14}, {1, 2}, {3, 7}, {4, 8},
1957 {6, 12}, {11, 13}, {14, 15}, {1, 3}, {2, 7}, {4, 5}, {9, 11}, {10, 12}, {13, 14},
1958 {2, 3}, {4, 6}, {5, 7}, {8, 10}, {3, 4}, {6, 8}, {7, 9}, {10, 12}, {5, 6},
1959 {7, 8}, {9, 10}, {11, 12}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}};
1960 };
1961
1967 template <uint32_t N, typename TCompareExchange, size_t... I>
1968 void sort_network_run(TCompareExchange& cex, std::index_sequence<I...>) {
1969 (cex(sort_network<N>::pairs[I][0], sort_network<N>::pairs[I][1]), ...);
1970 }
1971
1979 template <uint32_t N, uint32_t Last, typename TCompareExchange>
1980 void sort_network_dispatch(uint32_t n, TCompareExchange& cex) {
1981 if (n == N) {
1982 constexpr size_t Pairs = sizeof(sort_network<N>::pairs) / sizeof(sort_network<N>::pairs[0]);
1983 sort_network_run<N>(cex, std::make_index_sequence<Pairs>{});
1984 return;
1985 }
1986 if constexpr (N < Last)
1987 sort_network_dispatch<N + 1, Last>(n, cex);
1988 }
1989
1994 template <typename T>
1995 inline constexpr bool sort_uses_network = std::is_trivially_copyable_v<T> && sizeof(T) <= 8;
1996
2002 template <typename T, typename TCmpFunc>
2003 struct sort_cex_select {
2005 T* beg;
2007 TCmpFunc& cmpFunc;
2008
2012 void operator()(uint32_t lhs, uint32_t rhs) const {
2013 const T a = beg[lhs];
2014 const T b = beg[rhs];
2015 const bool swap = cmpFunc(b, a);
2016 beg[lhs] = swap ? b : a;
2017 beg[rhs] = swap ? a : b;
2018 }
2019 };
2020
2027 template <typename T, typename TCmpFunc, typename TSwapFunc>
2028 struct sort_cex_swap {
2030 T* beg;
2032 TCmpFunc& cmpFunc;
2034 TSwapFunc& swapFunc;
2035
2039 void operator()(uint32_t lhs, uint32_t rhs) const {
2040 if (cmpFunc(beg[rhs], beg[lhs]))
2041 swapFunc(lhs, rhs);
2042 }
2043 };
2044
2055 template <uint32_t MaxN, typename T, typename TCmpFunc>
2056 bool sort_small(T* beg, uint32_t n, TCmpFunc& cmpFunc) {
2057 if constexpr (MaxN > SortSmallMaxN) {
2058 if (n > SortSmallMaxN)
2059 return false;
2060 }
2061
2062 if constexpr (sort_uses_network<T>) {
2063 constexpr uint32_t Last = MaxN < SortNetworkMaxN ? MaxN : SortNetworkMaxN;
2064 if constexpr (Last >= 2) {
2065 if (n <= Last) {
2066 sort_cex_select<T, TCmpFunc> cex{beg, cmpFunc};
2067 sort_network_dispatch<2, Last>(n, cex);
2068 return true;
2069 }
2070 }
2071 }
2072
2073 T* arr = beg;
2074 insertion_sort_range(arr, 0, (int)n - 1, cmpFunc);
2075 return true;
2076 }
2077
2089 template <uint32_t MaxN, typename T, typename TCmpFunc, typename TSwapFunc>
2090 bool sort_small(T* beg, uint32_t n, TCmpFunc& cmpFunc, TSwapFunc& swapFunc) {
2091 if constexpr (MaxN > SortSmallMaxN) {
2092 if (n > SortSmallMaxN)
2093 return false;
2094 }
2095
2096 constexpr uint32_t Last = MaxN < SortNetworkMaxN ? MaxN : SortNetworkMaxN;
2097 if constexpr (Last >= 2) {
2098 if (n <= Last) {
2099 sort_cex_swap<T, TCmpFunc, TSwapFunc> cex{beg, cmpFunc, swapFunc};
2100 sort_network_dispatch<2, Last>(n, cex);
2101 return true;
2102 }
2103 }
2104
2105 insertion_sort_range(beg, 0, (int)n - 1, cmpFunc, swapFunc);
2106 return true;
2107 }
2108 } // namespace detail
2110
2112 template <typename Container, typename TCmpFunc, typename TSwapFunc>
2113 void quick_sort(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc);
2114
2122 template <typename Container, typename TCmpFunc>
2123 void quick_sort(Container& arr, int low, int high, TCmpFunc cmpFunc) {
2124 if (low >= high)
2125 return;
2126 detail::quick_sort_impl(arr, low, high, cmpFunc, detail::sort_depth_limit((uint32_t)(high - low + 1)));
2127 }
2128
2138 template <typename Container, typename TCmpFunc, typename TSwapFunc>
2139 void quick_sort(Container& arr, int low, int high, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
2140 if (low >= high)
2141 return;
2142 detail::quick_sort_impl(arr, low, high, cmpFunc, swapFunc, detail::sort_depth_limit((uint32_t)(high - low + 1)));
2143 }
2144
2145 namespace detail {
2153 template <uint32_t MaxN, typename T, typename TCmpFunc>
2154 void sort_bounded(T* beg, T* end, TCmpFunc cmpFunc) {
2155 const auto n = (uint32_t)(end - beg);
2156 if (try_sorted_or_reversed(beg, n, cmpFunc, [beg](uint32_t lhs, uint32_t rhs) {
2157 core::swap(beg[lhs], beg[rhs]);
2158 }))
2159 return;
2160 if (sort_small<MaxN>(beg, n, cmpFunc))
2161 return;
2162
2163 if constexpr (MaxN > SortSmallMaxN)
2164 core::quick_sort(beg, 0, (int)n - 1, cmpFunc);
2165 }
2166
2176 template <uint32_t MaxN, typename T, typename TCmpFunc, typename TSwapFunc>
2177 void sort_bounded(T* beg, T* end, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
2178 const auto n = (uint32_t)(end - beg);
2179 if (try_sorted_or_reversed(beg, n, cmpFunc, swapFunc))
2180 return;
2181 if (sort_small<MaxN>(beg, n, cmpFunc, swapFunc))
2182 return;
2183
2184 if constexpr (MaxN > SortSmallMaxN)
2185 core::quick_sort(beg, 0, (int)n - 1, cmpFunc, swapFunc);
2186 }
2187 } // namespace detail
2188
2200 template <uint32_t MaxN = (uint32_t)-1, typename T, typename TCmpFunc>
2201 void sort(T* beg, T* end, TCmpFunc cmpFunc) {
2202 GAIA_ASSERT((uintptr_t)(end - beg) <= MaxN);
2203 detail::sort_bounded<MaxN>(beg, end, cmpFunc);
2204 }
2205
2211 template <typename C, typename TCmpFunc>
2212 void sort(C& c, TCmpFunc cmpFunc) {
2213 constexpr uint32_t Cap = fixed_capacity<std::remove_cv_t<C>>::value;
2214 detail::sort_bounded<Cap != 0 ? Cap : (uint32_t)-1>(c.begin(), c.end(), cmpFunc);
2215 }
2216
2231 template <uint32_t MaxN = (uint32_t)-1, typename T, typename TCmpFunc, typename TSwapFunc>
2232 void sort(T* beg, T* end, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
2233 GAIA_ASSERT((uintptr_t)(end - beg) <= MaxN);
2234 detail::sort_bounded<MaxN>(beg, end, cmpFunc, swapFunc);
2235 }
2236
2244 template <typename C, typename TCmpFunc, typename TSwapFunc>
2245 void sort(C& c, TCmpFunc cmpFunc, TSwapFunc swapFunc) {
2246 constexpr uint32_t Cap = fixed_capacity<std::remove_cv_t<C>>::value;
2247 detail::sort_bounded<Cap != 0 ? Cap : (uint32_t)-1>(c.begin(), c.end(), cmpFunc, swapFunc);
2248 }
2249 } // namespace core
2250} // namespace gaia
Equality comparison functor using operator==.
Definition utility.h:1372
constexpr bool operator()(const T &lhs, const T &rhs) const
Compares two values for equality.
Definition utility.h:1377
Compile-time capacity of a container that can never grow. Zero for containers that can....
Definition utility.h:44
Lightweight carrier for a function argument type pack.
Definition utility.h:646
Detects containers exposing both data() and size().
Definition utility.h:219
Detects whether T supports a free operator==.
Definition utility.h:775
static constexpr bool value
True when the free equality operator probe succeeds.
Definition utility.h:777
Detects whether T defines operator== as a member function.
Definition utility.h:767
static constexpr bool value
True when the member equality operator probe succeeds.
Definition utility.h:769
Detects containers exposing begin(), end(), and size().
Definition utility.h:234
Strict weak ordering functor using operator>.
Definition utility.h:1411
constexpr bool operator()(const T &lhs, const T &rhs) const
Compares whether the left value is greater than the right value.
Definition utility.h:1416
Comparison functor using operator<=.
Definition utility.h:1398
constexpr bool operator()(const T &lhs, const T &rhs) const
Compares whether the left value is smaller than or equal to the right value.
Definition utility.h:1403
Strict weak ordering functor using operator<.
Definition utility.h:1385
constexpr bool operator()(const T &lhs, const T &rhs) const
Compares whether the left value is smaller than the right value.
Definition utility.h:1390
RAII helper that calls lock() on construction and unlock() on destruction.
Definition utility.h:187
T & m_ctx
Guarded lockable object.
Definition utility.h:189
lock_scope & operator=(const lock_scope &)=delete
Copy assignment is disabled because the lock ownership is scoped.
lock_scope(lock_scope &&)=delete
Move construction is disabled because the lock ownership is scoped.
lock_scope(T &ctx)
Acquires the lock represented by ctx.
Definition utility.h:193
lock_scope(const lock_scope &)=delete
Copy construction is disabled because the lock ownership is scoped.
~lock_scope()
Releases the guarded lock.
Definition utility.h:197
lock_scope & operator=(lock_scope &&)=delete
Move assignment is disabled because the lock ownership is scoped.
Concatenates two type_list instances.
Definition utility.h:798
Compile-time list of types.
Definition utility.h:787
static constexpr auto size
Number of stored types.
Definition utility.h:791
Builds a tuple type from the first occurrence of each type in a parameter pack.
Definition utility.h:609
Tag type used to request zero-initialization in APIs that accept marker objects.
Definition utility.h:129