diff --git a/include/cpp-sort/detail/ska_sort.h b/include/cpp-sort/detail/ska_sort.h index 22a6c619..636156da 100644 --- a/include/cpp-sort/detail/ska_sort.h +++ b/include/cpp-sort/detail/ska_sort.h @@ -17,46 +17,13 @@ #include #include #include +#include #include -#include namespace cppsort { namespace detail { - template - auto counting_sort_impl(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, Projection projection) - -> void - { - auto&& proj = utility::as_function(projection); - using utility::iter_move; - - count_type counts[256] = {}; - for (auto it = begin ; it != end ; ++it) { - ++counts[proj(*it)]; - } - count_type total = 0; - for (count_type& count: counts) { - count_type old_count = count; - count = total; - total += old_count; - } - for (; begin != end ; ++begin) { - std::uint8_t key = proj(*begin); - out_begin[counts[key]++] = iter_move(begin); - } - } - - template - auto counting_sort_impl(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, Projection projection) - -> void - { - counting_sort_impl(std::move(begin), std::move(end), - out_begin, std::move(projection)); - } - inline auto to_unsigned_or_bool(bool b) -> bool { @@ -178,655 +145,6 @@ namespace detail return reinterpret_cast(ptr); } - template - struct SizedRadixSorter; - - template<> - struct SizedRadixSorter<1> - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - counting_sort_impl(begin, end, buffer_begin, [&](auto&& o) { - return to_unsigned_or_bool(proj(o)); - }); - return true; - } - - static constexpr std::size_t pass_count = 2; - }; - - template<> - struct SizedRadixSorter<2> - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - std::ptrdiff_t num_elements = std::distance(begin, end); - if (num_elements <= (1ll << 32)) { - return sort_inline(begin, end, buffer_begin, buffer_begin + num_elements, projection); - } else { - return sort_inline(begin, end, buffer_begin, buffer_begin + num_elements, projection); - } - } - - template - static auto sort_inline(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, OutIt out_end, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - using utility::iter_move; - - count_type counts0[256] = {}; - count_type counts1[256] = {}; - - for (auto it = begin ; it != end ; ++it) { - std::uint16_t key = to_unsigned_or_bool(proj(*it)); - ++counts0[key & 0xff]; - ++counts1[(key >> 8) & 0xff]; - } - count_type total0 = 0; - count_type total1 = 0; - for (int i = 0 ; i < 256 ; ++i) { - count_type old_count0 = counts0[i]; - count_type old_count1 = counts1[i]; - counts0[i] = total0; - counts1[i] = total1; - total0 += old_count0; - total1 += old_count1; - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)); - out_begin[counts0[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 8; - begin[counts1[key]++] = iter_move(it); - } - return false; - } - - static constexpr std::size_t pass_count = 3; - }; - - template<> - struct SizedRadixSorter<4> - { - - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - std::ptrdiff_t num_elements = std::distance(begin, end); - if (num_elements <= (1ll << 32)) { - return sort_inline(begin, end, buffer_begin, buffer_begin + num_elements, projection); - } else { - return sort_inline(begin, end, buffer_begin, buffer_begin + num_elements, projection); - } - } - - template - static auto sort_inline(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, OutIt out_end, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - using utility::iter_move; - - count_type counts0[256] = {}; - count_type counts1[256] = {}; - count_type counts2[256] = {}; - count_type counts3[256] = {}; - - for (auto it = begin ; it != end ; ++it) { - std::uint32_t key = to_unsigned_or_bool(proj(*it)); - ++counts0[key & 0xff]; - ++counts1[(key >> 8) & 0xff]; - ++counts2[(key >> 16) & 0xff]; - ++counts3[(key >> 24) & 0xff]; - } - count_type total0 = 0; - count_type total1 = 0; - count_type total2 = 0; - count_type total3 = 0; - for (int i = 0 ; i < 256 ; ++i) { - count_type old_count0 = counts0[i]; - count_type old_count1 = counts1[i]; - count_type old_count2 = counts2[i]; - count_type old_count3 = counts3[i]; - counts0[i] = total0; - counts1[i] = total1; - counts2[i] = total2; - counts3[i] = total3; - total0 += old_count0; - total1 += old_count1; - total2 += old_count2; - total3 += old_count3; - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)); - out_begin[counts0[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 8; - begin[counts1[key]++] = iter_move(it); - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 16; - out_begin[counts2[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 24; - begin[counts3[key]++] = iter_move(it); - } - return false; - } - - static constexpr std::size_t pass_count = 5; - }; - - template<> - struct SizedRadixSorter<8> - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - std::ptrdiff_t num_elements = std::distance(begin, end); - if (num_elements <= (1ll << 32)) { - return sort_inline(begin, end, buffer_begin, buffer_begin + num_elements, projection); - } else { - return sort_inline(begin, end, buffer_begin, buffer_begin + num_elements, projection); - } - } - - template - static auto sort_inline(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, OutIt out_end, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - using utility::iter_move; - - count_type counts0[256] = {}; - count_type counts1[256] = {}; - count_type counts2[256] = {}; - count_type counts3[256] = {}; - count_type counts4[256] = {}; - count_type counts5[256] = {}; - count_type counts6[256] = {}; - count_type counts7[256] = {}; - - for (auto it = begin ; it != end ; ++it) { - std::uint64_t key = to_unsigned_or_bool(proj(*it)); - ++counts0[key & 0xff]; - ++counts1[(key >> 8) & 0xff]; - ++counts2[(key >> 16) & 0xff]; - ++counts3[(key >> 24) & 0xff]; - ++counts4[(key >> 32) & 0xff]; - ++counts5[(key >> 40) & 0xff]; - ++counts6[(key >> 48) & 0xff]; - ++counts7[(key >> 56) & 0xff]; - } - count_type total0 = 0; - count_type total1 = 0; - count_type total2 = 0; - count_type total3 = 0; - count_type total4 = 0; - count_type total5 = 0; - count_type total6 = 0; - count_type total7 = 0; - for (int i = 0 ; i < 256 ; ++i) { - count_type old_count0 = counts0[i]; - count_type old_count1 = counts1[i]; - count_type old_count2 = counts2[i]; - count_type old_count3 = counts3[i]; - count_type old_count4 = counts4[i]; - count_type old_count5 = counts5[i]; - count_type old_count6 = counts6[i]; - count_type old_count7 = counts7[i]; - counts0[i] = total0; - counts1[i] = total1; - counts2[i] = total2; - counts3[i] = total3; - counts4[i] = total4; - counts5[i] = total5; - counts6[i] = total6; - counts7[i] = total7; - total0 += old_count0; - total1 += old_count1; - total2 += old_count2; - total3 += old_count3; - total4 += old_count4; - total5 += old_count5; - total6 += old_count6; - total7 += old_count7; - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)); - out_begin[counts0[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 8; - begin[counts1[key]++] = iter_move(it); - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 16; - out_begin[counts2[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 24; - begin[counts3[key]++] = iter_move(it); - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 32; - out_begin[counts4[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 40; - begin[counts5[key]++] = iter_move(it); - } - for (auto it = begin ; it != end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 48; - out_begin[counts6[key]++] = iter_move(it); - } - for (OutIt it = out_begin ; it != out_end ; ++it) { - std::uint8_t key = to_unsigned_or_bool(proj(*it)) >> 56; - begin[counts7[key]++] = iter_move(it); - } - return false; - } - - static constexpr std::size_t pass_count = 9; - }; - - template - struct RadixSorter; - - template<> - struct RadixSorter - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - using utility::iter_move; - - std::size_t false_count = 0; - for (auto it = begin ; it != end ; ++it) { - if (not prj(*it)) { - ++false_count; - } - } - std::size_t true_position = false_count; - false_count = 0; - for (; begin != end ; ++begin) { - if (proj(*begin)) { - buffer_begin[true_position++] = iter_move(begin); - } else { - buffer_begin[false_count++] = iter_move(begin); - } - } - return true; - } - - static constexpr std::size_t pass_count = 2; - }; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template<> - struct RadixSorter: - SizedRadixSorter - {}; - - template - struct RadixSorter> - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - - bool first_result = RadixSorter::sort(begin, end, buffer_begin, [&](auto&& o) { - return proj(o).second; - }); - auto extract_first = [&](auto&& o) { - return proj(o).first; - }; - - if (first_result) { - return not RadixSorter::sort(buffer_begin, buffer_begin + (end - begin), begin, extract_first); - } else { - return RadixSorter::sort(begin, end, buffer_begin, extract_first); - } - } - - static constexpr size_t pass_count = RadixSorter::pass_count + RadixSorter::pass_count; - }; - - template - struct RadixSorter&> - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - - bool first_result = RadixSorter::sort(begin, end, buffer_begin, [&](auto&& o) -> const V& { - return proj(o).second; - }); - auto extract_first = [&](auto&& o) -> const K& { - return proj(o).first; - }; - - if (first_result) { - return not RadixSorter::sort(buffer_begin, buffer_begin + (end - begin), begin, extract_first); - } else { - return RadixSorter::sort(begin, end, buffer_begin, extract_first); - } - } - - static constexpr std::size_t pass_count = RadixSorter::pass_count + RadixSorter::pass_count; - }; - - template - struct TupleRadixSorter - { - using NextSorter = TupleRadixSorter; - using ThisSorter = RadixSorter::type>; - - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, OutIt out_end, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - - bool which = NextSorter::sort(begin, end, out_begin, out_end, projection); - auto extract_i = [&](auto&& o) { - return std::get(proj(o)); - }; - if (which) { - return not ThisSorter::sort(out_begin, out_end, begin, extract_i); - } else { - return ThisSorter::sort(begin, end, out_begin, extract_i); - } - } - - static constexpr std::size_t pass_count = ThisSorter::pass_count + NextSorter::pass_count; - }; - - template - struct TupleRadixSorter - { - using NextSorter = TupleRadixSorter; - using ThisSorter = RadixSorter::type>; - - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt out_begin, OutIt out_end, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - - bool which = NextSorter::sort(begin, end, out_begin, out_end, projection); - auto extract_i = [&](auto&& o) -> decltype(auto) { - return std::get(proj(o)); - }; - if (which) { - return not ThisSorter::sort(out_begin, out_end, begin, extract_i); - } else { - return ThisSorter::sort(begin, end, out_begin, extract_i); - } - } - - static constexpr size_t pass_count = ThisSorter::pass_count + NextSorter::pass_count; - }; - - template - struct TupleRadixSorter - { - template - static auto sort(RandomAccessIterator, RandomAccessIterator, - OutIt, OutIt, Projection) - -> bool - { - return false; - } - - static constexpr std::size_t pass_count = 0; - }; - - template - struct TupleRadixSorter - { - template - static auto sort(RandomAccessIterator, RandomAccessIterator, - OutIt, OutIt, Projection) - -> bool - { - return false; - } - - static constexpr std::size_t pass_count = 0; - }; - - template - struct RadixSorter> - { - using SorterImpl = TupleRadixSorter<0, sizeof...(Args), std::tuple>; - - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - return SorterImpl::sort(std::move(begin), std::move(end), - buffer_begin, buffer_begin + (end - begin), - std::move(projection)); - } - - static constexpr std::size_t pass_count = SorterImpl::pass_count; - }; - - template - struct RadixSorter&> - { - using SorterImpl = TupleRadixSorter<0, sizeof...(Args), const std::tuple&>; - - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - return SorterImpl::sort(std::move(begin), std::move(end), - buffer_begin, buffer_begin + (end - begin), - std::move(projection)); - } - - static constexpr std::size_t pass_count = SorterImpl::pass_count; - }; - - template - struct RadixSorter> - { - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - - auto buffer_end = buffer_begin + (end - begin); - bool which = false; - for (std::size_t i = S ; i > 0 ; --i) { - auto extract_i = [&, i=i-1](auto&& o) { - return proj(o)[i]; - }; - if (which) { - which = not RadixSorter::sort(buffer_begin, buffer_end, begin, extract_i); - } else { - which = RadixSorter::sort(begin, end, buffer_begin, extract_i); - } - } - return which; - } - - static constexpr size_t pass_count = RadixSorter::pass_count * S; - }; - - template - struct RadixSorter: - RadixSorter - {}; - - template - struct RadixSorter: - RadixSorter - {}; - - template - struct RadixSorter: - RadixSorter - {}; - - template - struct RadixSorter: - RadixSorter - {}; - - template - struct RadixSorter: - RadixSorter - {}; - - // these structs serve two purposes - // 1. they serve as illustration for how to implement the to_radix_sort_key function - // 2. they help produce better error messages. with these overloads you get the - // error message "no matching function for call to to_radix_sort(your_type)" - // without these examples, you'd get the error message "to_radix_sort_key was - // not declared in this scope" which is a much less useful error message - struct ExampleStructA { int i; }; - struct ExampleStructB { float f; }; - inline int to_radix_sort_key(ExampleStructA a) { return a.i; } - inline float to_radix_sort_key(ExampleStructB b) { return b.f; } - - template - struct FallbackRadixSorter: - RadixSorter()))> - { - using base = RadixSorter()))>; - - template - static auto sort(RandomAccessIterator begin, RandomAccessIterator end, - OutIt buffer_begin, Projection projection) - -> bool - { - auto&& proj = utility::as_function(projection); - return base::sort(begin, end, buffer_begin, [&](auto&& a) -> decltype(auto) { - return to_radix_sort_key(proj(a)); - }); - } - }; - - template - struct FallbackRadixSorter()))>::value>>: - RadixSorter()))> - {}; - - template - struct RadixSorter: - FallbackRadixSorter - {}; - template auto unroll_loop_four_times(RandomAccessIterator begin, std::size_t iteration_count, Function&& to_call_func) @@ -1200,7 +518,7 @@ namespace detail -> void { auto&& proj = utility::as_function(projection); - std::sort(std::move(begin), std::move(end), [&](auto&& l, auto&& r) { + cppsort::pdq_sort(std::move(begin), std::move(end), [&](auto&& l, auto&& r) { return proj(l) < proj(r); }); } @@ -1498,7 +816,6 @@ namespace detail } } - template static auto sort(RandomAccessIterator begin, RandomAccessIterator end, std::ptrdiff_t, Projection projection,