From f39af4491123620981f67630ea99a7eb65499a61 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 1 Apr 2021 23:43:19 +0200 Subject: [PATCH 01/56] Mark invert_t as probably branchless when its predicate is --- include/cpp-sort/detail/functional.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/cpp-sort/detail/functional.h b/include/cpp-sort/detail/functional.h index 6ea2cfaa..bc5bae08 100644 --- a/include/cpp-sort/detail/functional.h +++ b/include/cpp-sort/detail/functional.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace cppsort { @@ -143,4 +144,14 @@ namespace detail } }} +namespace cppsort +{ +namespace utility +{ + template + struct is_probably_branchless_comparison, T>: + is_probably_branchless_comparison + {}; +}} + #endif // CPPSORT_DETAIL_FUNCTIONAL_H_ From 0a403e5b4eb09e1234381d88fa80e65f7935fafc Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 2 Apr 2021 10:09:25 +0200 Subject: [PATCH 02/56] Bump Clang version on GitHub Actions to 9 The default version on the Ubuntu 16.04 image was change to clang 9.0.1, change to that for now, investigate installing older versions later. --- .github/workflows/build-ubuntu.yml | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 305d196a..d9058c02 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -30,7 +30,7 @@ jobs: matrix: cxx: - g++-5 - - clang++-6.0 + - clang++-9 config: # Release build - build_type: Release diff --git a/README.md b/README.md index 45565d0a..74ee89c6 100644 --- a/README.md +++ b/README.md @@ -117,8 +117,8 @@ wiki page](https://github.com/Morwenn/cpp-sort/wiki/Benchmarks). or more recent versions of these compilers. So far, the library should work with the following compilers: * g++5.5 or more recent. It is known not to work with some older g++5 versions. -* clang++6 or more recent. It should work with clang++ versions all the way back to 3.8, but the CI pipeline doesn't have test for those anymore. -* Visual Studio 2019 version 16.8.3 or more recent, only with `/permissive-`. A few features are unavailable. +* clang++9 or more recent. It should work with clang++ versions all the way back to 3.8, but the CI pipeline doesn't have test for those anymore. +* Visual Studio 2019 version 16.8.3 or more recent, only with `/permissive-`. A few features are still unavailable. * The versions of MinGW-w64 and AppleClang equivalent to the compilers mentioned above. * Clang is notably tested with both libstdc++ and libc++. From bc296f21d04e3977542ae255c2975bffe93c6cd5 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 2 Apr 2021 12:25:36 +0200 Subject: [PATCH 03/56] Optimize probe::enc with lower_monobound_n when possible Monobound binary search is a variation on standard binary search proposed by @scandum: instead of computing the exact size of the next partition where to look for an element, it always recurses in a partition of size n-n/2. This change means that a few redudant operations are performed when the size isn't a power of 2, but it also means that the generated code can be much smaller. Here we use it for probe::enc when the comparison and projection functions are likely branchless: benchmarks showed that tis measure of presortedness becomes up to 40% when measuring the presortedness of a std::vector. The gain is sufficient to switch to monobound binary search for the cases where it is an obvious improvement. probe::enc still uses a standard binary search the rest of the time. --- include/cpp-sort/detail/lower_bound.h | 32 ++++++++++++++++++-- include/cpp-sort/probes/enc.h | 43 ++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/include/cpp-sort/detail/lower_bound.h b/include/cpp-sort/detail/lower_bound.h index 8b8f3557..4630d4d6 100644 --- a/include/cpp-sort/detail/lower_bound.h +++ b/include/cpp-sort/detail/lower_bound.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOWER_BOUND_H_ @@ -28,8 +28,7 @@ namespace detail auto&& proj = utility::as_function(projection); while (size > 0) { - ForwardIterator it = first; - std::advance(it, half(size)); + auto it = std::next(first, half(size));; if (comp(proj(*it), value)) { first = ++it; size -= half(size) + 1; @@ -50,6 +49,33 @@ namespace detail std::forward(value), std::move(compare), std::move(projection)); } + + template + auto lower_monobound_n(ForwardIterator first, difference_type_t size, + T&& value, Compare compare, Projection projection) + -> ForwardIterator + { + auto&& comp = utility::as_function(compare); + auto&& proj = utility::as_function(projection); + + if (size == 0) { + return first; + } + + while (size > 1) { + auto it = std::next(first, half(size)); + if (comp(proj(*it), value)) { + first = it; + } + size -= half(size); + } + + if (comp(proj(*first), value)) { + ++first; + } + return first; + } }} #endif // CPPSORT_DETAIL_LOWER_BOUND_H_ diff --git a/include/cpp-sort/probes/enc.h b/include/cpp-sort/probes/enc.h index ee6e442a..e0fc97ab 100644 --- a/include/cpp-sort/probes/enc.h +++ b/include/cpp-sort/probes/enc.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include "../detail/functional.h" @@ -28,6 +29,35 @@ namespace probe { namespace detail { + template + auto enc_lower_bound(std::vector>& lists, + T& value, Compare compare, Projection projection, + ForwardIterator std::pair::* accessor) + -> typename std::vector>::iterator + { + using value_type = cppsort::detail::value_type_t; + using projected_type = cppsort::detail::projected_t; + constexpr bool can_optimize = + utility::is_probably_branchless_comparison_v && + utility::is_probably_branchless_projection_v; + + auto proj = [&projection, &accessor](const auto& list) -> decltype(auto) { + return projection(*(list.*accessor)); + }; + + if (can_optimize) { + return cppsort::detail::lower_monobound_n( + lists.begin(), lists.size() - 1, value, + std::move(compare), proj + ); + } else { + return cppsort::detail::lower_bound_n( + lists.begin(), lists.size() - 1, value, + std::move(compare), proj + ); + } + } + struct enc_impl { template< @@ -63,17 +93,16 @@ namespace probe auto& last_list = lists.back(); if (not comp(value, proj(*last_list.second))) { // Element belongs to the tails (bigger elements) - auto insertion_point = cppsort::detail::lower_bound( - lists.begin(), std::prev(lists.end()), value, - cppsort::detail::invert(compare), - [&proj](auto& list) -> decltype(auto) { return proj(*list.second); } + auto insertion_point = enc_lower_bound( + lists, value, cppsort::detail::invert(compare), proj, + &std::pair::second ); insertion_point->second = first; } else if (not comp(proj(*last_list.first), value)) { // Element belongs to the heads (smaller elements) - auto insertion_point = cppsort::detail::lower_bound( - lists.begin(), std::prev(lists.end()), value, compare, - [&proj](auto& list) -> decltype(auto) { return proj(*list.first); } + auto insertion_point = enc_lower_bound( + lists, value, compare, proj, + &std::pair::first ); insertion_point->first = first; } else { From 4aebb4d2bdc6088c5089baed90ee1ec2403f7c58 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 2 Apr 2021 14:56:54 +0200 Subject: [PATCH 04/56] Remove [[nodiscard]] in fixed_size_list::is_empty The attribute isn't C++14, warns in Clang because of that, is not in the public interface, and unlike empty() it's obvious that the function is not modifying the collection, so it's not worth adding more macros for that single function. --- include/cpp-sort/detail/fixed_size_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpp-sort/detail/fixed_size_list.h b/include/cpp-sort/detail/fixed_size_list.h index c0735bc0..2278f75f 100644 --- a/include/cpp-sort/detail/fixed_size_list.h +++ b/include/cpp-sort/detail/fixed_size_list.h @@ -493,7 +493,7 @@ namespace detail //////////////////////////////////////////////////////////// // Capacity - [[nodiscard]] auto is_empty() const noexcept + auto is_empty() const noexcept -> bool { return &sentinel_node_ == sentinel_node_.next; From e6c7caed0f5ffe007b1b1d2a7c3056a00d3e1e24 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 4 Apr 2021 17:39:04 +0200 Subject: [PATCH 05/56] Improve container_aware_adapter(insertion_sort) over std::list The algorithm was using upper_bound in the inner loop, which was recomputing the size of the range where to insert the new element at each iteration. This changes the algorithm which now manages the size itself and feeds it to upper_bound_n instead, saving O(n) operations per iteration. --- .../detail/container_aware/insertion_sort.h | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/cpp-sort/detail/container_aware/insertion_sort.h b/include/cpp-sort/detail/container_aware/insertion_sort.h index a8a24b9d..03c03997 100644 --- a/include/cpp-sort/detail/container_aware/insertion_sort.h +++ b/include/cpp-sort/detail/container_aware/insertion_sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_CONTAINER_AWARE_INSERTION_SORT_H_ @@ -38,21 +38,21 @@ namespace cppsort auto last = collection.end(); if (it == last) return; + // Size of the list where a value is insrted + typename std::list::difference_type size = 1; + ++it; - while (it != last) - { - auto insertion_point = upper_bound(collection.begin(), it, proj(*it), - compare, projection); - if (insertion_point == it) - { + while (it != last) { + auto insertion_point = upper_bound_n(collection.begin(), size, proj(*it), + compare, projection); + if (insertion_point == it) { ++it; - } - else - { + } else { auto next = std::next(it); collection.splice(insertion_point, collection, it); it = next; } + ++size; } } From b2b39fc7cc2b93cfcf3cef2bbb72bcbd7464cefe Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 5 Apr 2021 13:32:37 +0200 Subject: [PATCH 06/56] Fix bug in container_aware_adapter(insertion_sort) for std::forward_list This bug basically made the algorithm unusable, and slow when it actually happened to work. This commit fixes the bug and uses binary search instead of linear search to perform no more than O(n log n) comparisons. --- .../detail/container_aware/insertion_sort.h | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/include/cpp-sort/detail/container_aware/insertion_sort.h b/include/cpp-sort/detail/container_aware/insertion_sort.h index 03c03997..c3fe6634 100644 --- a/include/cpp-sort/detail/container_aware/insertion_sort.h +++ b/include/cpp-sort/detail/container_aware/insertion_sort.h @@ -19,6 +19,7 @@ #include #include #include +#include "../bitops.h" #include "../std_list_traits.h" #include "../type_traits.h" #include "../upper_bound.h" @@ -38,7 +39,7 @@ namespace cppsort auto last = collection.end(); if (it == last) return; - // Size of the list where a value is insrted + // Size of the list where a value is inserted typename std::list::difference_type size = 1; ++it; @@ -66,30 +67,34 @@ namespace cppsort auto it = collection.before_begin(); auto last = collection.end(); - if (std::next(it) == last) return; + if (++it == last) return; - ++it; - while (std::next(it) != last) - { - // Linear search in list + // Size of the list where a value is inserted + typename std::forward_list::difference_type size = 1; + + while (std::next(it) != last) { + // Binary search to find where to insert the value, we can't + // use upper_bound because of the specificities of forward_list auto&& value = proj(*std::next(it)); auto insertion_point = collection.before_begin(); - while (std::next(insertion_point) != last) - { - if (not comp(proj(*std::next(insertion_point)), value)) break; - ++insertion_point; + for (auto search_size = size ; search_size > 0 ;) { + auto search_it = std::next(insertion_point, half(search_size)); + if (not comp(value, proj(*std::next(search_it)))) { + insertion_point = ++search_it; + search_size -= half(search_size) + 1; + } else { + search_size = half(search_size); + } } - if (insertion_point == it) - { + if (insertion_point == it) { ++it; - } - else - { - auto next = std::next(it); + } else { + // We splice the elements after it to some position before it, + // so this effectively advances the iterator towards last collection.splice_after(insertion_point, collection, it); - it = next; } + ++size; } } } From 16d746c9fd7d8dabc6fad736821bae12597d7169 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 6 Apr 2021 13:36:20 +0200 Subject: [PATCH 07/56] fixed_size_list: extract iteration to list_node_base Many operations on list nodes only act on the iteration and the relinking, and the specific type of the list node isn't required for those. This creates a new non-template list_node_base class with the pointers prev & next, and changes many operations to accept a pointer to a list_node_base instead of a node_list. The major gain is that it allows to use a list_node_base for sentinel nodes instead of a full list_node, saving sizeof(T) space for every list - space that was never used anyway. This means that the memory cost of melsort is reduced by up to sqrt(n)*sizeof(T) while that of slbasort is reduced by up to 2log(n)*sizeof(T). --- include/cpp-sort/detail/fixed_size_list.h | 95 +++++++++++++++-------- include/cpp-sort/detail/melsort.h | 2 +- include/cpp-sort/detail/slabsort.h | 23 +++--- 3 files changed, 71 insertions(+), 49 deletions(-) diff --git a/include/cpp-sort/detail/fixed_size_list.h b/include/cpp-sort/detail/fixed_size_list.h index 2278f75f..c3df6300 100644 --- a/include/cpp-sort/detail/fixed_size_list.h +++ b/include/cpp-sort/detail/fixed_size_list.h @@ -20,6 +20,37 @@ namespace cppsort { namespace detail { + //////////////////////////////////////////////////////////// + // List node base + // + // A list node base only provides the prev and next fields + // required for iteration - it makes it possible to iterate + // without requiring to know what a node actually holds, and + // to only downcast when useful. The main use case is to use + // a simple list_node_base as a sentinel node to save space. + + struct list_node_base + { + explicit list_node_base(list_node_base* next) noexcept: + next(next) + {} + + list_node_base(list_node_base* prev, list_node_base* next) noexcept: + prev(prev), + next(next) + {} + + // Make list nodes immovable + list_node_base(const list_node_base&) = delete; + list_node_base(list_node_base&&) = delete; + list_node_base& operator=(const list_node_base&) = delete; + list_node_base& operator=(list_node_base&&) = delete; + + // Pointers to the previous and next nodes + list_node_base* prev; + list_node_base* next; + }; + //////////////////////////////////////////////////////////// // List node // @@ -31,34 +62,26 @@ namespace detail // constructed or destructed. template - struct list_node + struct list_node: + list_node_base { using value_type = T; - constexpr explicit list_node(list_node* next) noexcept: - next(next) - {} + using list_node_base::list_node_base; - constexpr list_node(list_node* prev, list_node* next) noexcept: - prev(prev), - next(next) + explicit list_node(list_node_base* next) noexcept: + list_node_base(next) {} - // Make list nodes immovable - list_node(const list_node&) = delete; - list_node(list_node&&) = delete; - list_node& operator=(const list_node&) = delete; - list_node& operator=(list_node&&) = delete; + list_node(list_node_base* prev, list_node_base* next) noexcept: + list_node_base(prev, next) + {} // The list takes care of managing the lifetime of value ~list_node() {} // Inhibit construction of the node with a value union { T value; }; - - // Pointers to the previous and next nodes - list_node* prev; - list_node* next; }; //////////////////////////////////////////////////////////// @@ -158,12 +181,12 @@ namespace detail { // Retrieve next free node CPPSORT_ASSERT(first_free_ != nullptr); - node_type* new_node = first_free_; + auto new_node = first_free_; first_free_ = first_free_->next; - return new_node; + return static_cast(new_node); } - auto retrieve_nodes(node_type* first, node_type* last) + auto retrieve_nodes(list_node_base* first, list_node_base* last) -> void { // Get back a range of nodes linked together, this function @@ -215,7 +238,7 @@ namespace detail std::unique_ptr buffer_; // First free node of the pool - node_type* first_free_; + list_node_base* first_free_; // Number of nodes the pool holds std::ptrdiff_t capacity_; @@ -247,6 +270,10 @@ namespace detail fixed_size_list_iterator() = default; + constexpr explicit fixed_size_list_iterator(list_node_base* ptr) noexcept: + ptr_(ptr) + {} + constexpr explicit fixed_size_list_iterator(NodeType* ptr) noexcept: ptr_(ptr) {} @@ -257,7 +284,7 @@ namespace detail constexpr auto base() const -> node_type* { - return ptr_; + return static_cast(ptr_); } //////////////////////////////////////////////////////////// @@ -266,7 +293,7 @@ namespace detail auto operator*() const -> reference { - return ptr_->value; + return static_cast(ptr_)->value; } auto operator->() const @@ -327,7 +354,7 @@ namespace detail private: - node_type* ptr_ = nullptr; + list_node_base* ptr_ = nullptr; }; //////////////////////////////////////////////////////////// @@ -440,12 +467,12 @@ namespace detail ~fixed_size_list() { - node_type* ptr = sentinel_node_.next; + auto ptr = sentinel_node_.next; if (ptr != &sentinel_node_) { // Destroy the node's values do { auto next_ptr = ptr->next; - node_destructor_(ptr); + node_destructor_(static_cast(ptr)); ptr = next_ptr; } while (ptr != &sentinel_node_); @@ -460,13 +487,13 @@ namespace detail auto front() -> reference { - return sentinel_node_.next->value; + return static_cast(sentinel_node_.next)->value; } auto back() -> reference { - return sentinel_node_.prev->value; + return static_cast(sentinel_node_.prev)->value; } auto node_pool() @@ -552,14 +579,14 @@ namespace detail insert_node_(sentinel_node_.next, std::forward(setter)); } - auto extract(node_type* node) + auto extract(list_node_base* node) -> node_type* { CPPSORT_ASSERT(node != &sentinel_node_); CPPSORT_ASSERT(not is_empty()); node->prev->next = node->next; node->next->prev = node->prev; - return node; + return static_cast(node); } auto extract(iterator pos) @@ -742,7 +769,7 @@ namespace detail //////////////////////////////////////////////////////////// // Helper functions - auto insert_node_(node_type* pos, const value_type& value) + auto insert_node_(list_node_base* pos, const value_type& value) -> node_type* { node_type* new_node = node_pool_->next_free_node(); @@ -751,7 +778,7 @@ namespace detail return new_node; } - auto insert_node_(node_type* pos, value_type&& value) + auto insert_node_(list_node_base* pos, value_type&& value) -> node_type* { node_type* new_node = node_pool_->next_free_node(); @@ -761,7 +788,7 @@ namespace detail } template - auto insert_node_(node_type* pos, Callable setter) + auto insert_node_(list_node_base* pos, Callable setter) -> node_type* { node_type* new_node = node_pool_->next_free_node(); @@ -770,7 +797,7 @@ namespace detail return new_node; } - auto link_node_before_(node_type* node, node_type* pos) + auto link_node_before_(list_node_base* node, list_node_base* pos) -> void { // Relink pointers to a new node @@ -807,7 +834,7 @@ namespace detail // Sentinel node: its prev field points to the last element // of the list, its next field to the first one - node_type sentinel_node_; + list_node_base sentinel_node_; // Function pointer to a node's value destructor node_value_destructor_t node_destructor_; diff --git a/include/cpp-sort/detail/melsort.h b/include/cpp-sort/detail/melsort.h index 0c2967f6..4ce883da 100644 --- a/include/cpp-sort/detail/melsort.h +++ b/include/cpp-sort/detail/melsort.h @@ -49,7 +49,7 @@ namespace detail fixed_size_list edges(lists.front().node_pool()); if (extract_edges) { - auto insert_node = edges.end().base(); + list_node_base* insert_node = edges.end().base(); // Add minimums of encroaching lists for (auto& list : lists) { insert_node->next = list.extract_front(); diff --git a/include/cpp-sort/detail/slabsort.h b/include/cpp-sort/detail/slabsort.h index d467fbc6..baa5de89 100644 --- a/include/cpp-sort/detail/slabsort.h +++ b/include/cpp-sort/detail/slabsort.h @@ -17,6 +17,7 @@ #include "fixed_size_list.h" #include "functional.h" #include "iterator_traits.h" +#include "lower_bound.h" #include "melsort.h" #include "memory.h" #include "stable_partition.h" @@ -32,23 +33,20 @@ namespace detail // to move the values inside the list at some point. template - struct slabsort_list_node + struct slabsort_list_node: + list_node_base { using value_type = rvalue_type_t; - constexpr explicit slabsort_list_node(slabsort_list_node* next) noexcept: - next(next) - {} + using list_node_base::list_node_base; - constexpr slabsort_list_node(slabsort_list_node* prev, slabsort_list_node* next) noexcept: - prev(prev), - next(next) + explicit slabsort_list_node(list_node_base* next) noexcept: + list_node_base(next) {} - slabsort_list_node(const slabsort_list_node&) = delete; - slabsort_list_node(slabsort_list_node&&) = delete; - slabsort_list_node& operator=(const slabsort_list_node&) = delete; - slabsort_list_node& operator=(slabsort_list_node&&) = delete; + slabsort_list_node(list_node_base* prev, list_node_base* next) noexcept: + list_node_base(prev, next) + {} ~slabsort_list_node() {} @@ -56,9 +54,6 @@ namespace detail value_type value; Iterator it; }; - - slabsort_list_node* prev; - slabsort_list_node* next; }; template From 11f72af48b1ec7f4b59a1bb3fb00fa7285da25bb Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 6 Apr 2021 23:38:22 +0200 Subject: [PATCH 08/56] Remove unused includes --- include/cpp-sort/detail/inplace_merge.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/cpp-sort/detail/inplace_merge.h b/include/cpp-sort/detail/inplace_merge.h index e9721227..99ce5a0b 100644 --- a/include/cpp-sort/detail/inplace_merge.h +++ b/include/cpp-sort/detail/inplace_merge.h @@ -13,9 +13,7 @@ #include #include #include -#include "config.h" #include "iterator_traits.h" -#include "lower_bound.h" #include "memory.h" #include "recmerge_bidirectional.h" #include "recmerge_forward.h" From 0f0e9d422bdb9dca5e582ef730a17a0330ff4ca0 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 7 Apr 2021 12:29:35 +0200 Subject: [PATCH 09/56] Make benchmark results easier to read Add "lower is better" to the legend of the relevant axis in each plot drawing script. --- benchmarks/errorbar-plot/plot.py | 4 ++-- benchmarks/inversions/plot.py | 4 ++-- benchmarks/patterns/bars.py | 2 +- benchmarks/small-array/plot.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmarks/errorbar-plot/plot.py b/benchmarks/errorbar-plot/plot.py index 90012710..8156ae48 100644 --- a/benchmarks/errorbar-plot/plot.py +++ b/benchmarks/errorbar-plot/plot.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2020 Morwenn +# Copyright (c) 2020-2021 Morwenn # SPDX-License-Identifier: MIT import argparse @@ -58,7 +58,7 @@ def main(): ax.grid(True) ax.set_xlabel('Size') - ax.set_ylabel('Time [s]') + ax.set_ylabel('Time [s] (lower is better)') ax.set_xscale('log', base=2) ax.set_yscale('log') diff --git a/benchmarks/inversions/plot.py b/benchmarks/inversions/plot.py index d4d8ce7e..6c0d985e 100644 --- a/benchmarks/inversions/plot.py +++ b/benchmarks/inversions/plot.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2020 Morwenn +# Copyright (c) 2020-2021 Morwenn # SPDX-License-Identifier: MIT import argparse @@ -59,5 +59,5 @@ def fetch_results(fresults): pyplot.legend(loc='best') pyplot.title('Sorting std::vector with $10^6$ elements') pyplot.xlabel('Percentage of inversions') - pyplot.ylabel('Cycles') + pyplot.ylabel('Cycles (lower is better)') pyplot.show() diff --git a/benchmarks/patterns/bars.py b/benchmarks/patterns/bars.py index 0ac3945f..11403988 100644 --- a/benchmarks/patterns/bars.py +++ b/benchmarks/patterns/bars.py @@ -119,7 +119,7 @@ def main(): groupnames = ['\n'.join(wrap(l, 11)) for l in groupnames] pyplot.yticks([barwidth * groupsize/2 + groupwidth*n for n in range(len(groupnames))], groupnames, horizontalalignment='center') - pyplot.xlabel("Cycles per element") + pyplot.xlabel("Cycles per element (lower is better)") # Turn off ticks for y-axis. pyplot.tick_params(axis="y", diff --git a/benchmarks/small-array/plot.py b/benchmarks/small-array/plot.py index 397864b1..3308ea8c 100644 --- a/benchmarks/small-array/plot.py +++ b/benchmarks/small-array/plot.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2015-2020 Morwenn +# Copyright (c) 2015-2021 Morwenn # SPDX-License-Identifier: MIT import sys @@ -36,5 +36,5 @@ def fetch_results(fresults): plt.legend(values, names, loc='upper left') plt.title('Sorting std::array') plt.xlabel('Number of elements to sort') - plt.ylabel('Cycles') + plt.ylabel('Cycles (lower is better)') plt.show() From 6b5862c3e50816ba7f8a752cb3d17c2853fc862b Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 7 Apr 2021 12:33:09 +0200 Subject: [PATCH 10/56] Benchmarks: add a projection to turn an interger into a long string This allows to test every existing distribution in the benchmark suite (save maybe the ones that generate negative numbers) with std::string in a way that makes the comparison expensive: the constructed string is always 50-character long and ends with the characters corresponding to the digits of the input number. --- benchmarks/benchmarking-tools/distributions.h | 11 ++++++++ tools/test_failing_sorter.cpp | 27 ++----------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/benchmarks/benchmarking-tools/distributions.h b/benchmarks/benchmarking-tools/distributions.h index ffdaf2ad..b35e8f2e 100644 --- a/benchmarks/benchmarking-tools/distributions.h +++ b/benchmarks/benchmarking-tools/distributions.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -398,4 +399,14 @@ namespace dist static constexpr const char* output = "vergesort_killer.txt"; }; + + struct as_long_string + { + auto operator()(long long int value) + -> std::string + { + auto str = std::to_string(value); + return std::string(50 - str.size(), '0') + std::move(str); + } + }; } diff --git a/tools/test_failing_sorter.cpp b/tools/test_failing_sorter.cpp index c1adf6d3..6b95237b 100644 --- a/tools/test_failing_sorter.cpp +++ b/tools/test_failing_sorter.cpp @@ -22,37 +22,14 @@ #include "../testsuite/testing-tools/algorithm.h" #include "../testsuite/testing-tools/wrapper.h" -struct shuffled_string: - dist::base_distribution -{ - template - auto operator()(OutputIterator out, long long int size, T start=T(0)) const - -> void - { - // Pseudo-random number generator - thread_local std::mt19937 engine(15321); - - std::vector vec; - vec.reserve(size); - - T end = start + size; - for (auto i = start ; i < end ; ++i) { - auto s = std::to_string(i); - vec.push_back(std::string(50 - s.size(), '0') + std::move(s)); - } - std::shuffle(std::begin(vec), std::end(vec), engine); - std::move(std::begin(vec), std::end(vec), out); - } -}; - template void test(const char* name) { const int size = 5000; std::vector collection; - auto distribution = shuffled_string{}; - distribution(std::back_inserter(collection), size); + auto distribution = dist::shuffled{}; + distribution(std::back_inserter(collection), size, dist::as_long_string{}); auto copy = collection; cppsort::quick_sort(std::begin(copy), std::end(copy)); From c74eeaac956af125b6a7fab4b1df273986b95c14 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 7 Apr 2021 20:53:57 +0200 Subject: [PATCH 11/56] Fix potential small issues in stable_adapter stable_compare was calling std::forwarf twice on its parameters, and a few other functions were calling std::move instead of std::forward on forwardinf references. It probably hadn't caused any issue so far, but those issues are still best fixed. --- include/cpp-sort/adapters/stable_adapter.h | 43 ++++++++-------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/include/cpp-sort/adapters/stable_adapter.h b/include/cpp-sort/adapters/stable_adapter.h index d689ad55..93992dc3 100644 --- a/include/cpp-sort/adapters/stable_adapter.h +++ b/include/cpp-sort/adapters/stable_adapter.h @@ -34,10 +34,7 @@ namespace cppsort //////////////////////////////////////////////////////////// // Stable comparison function - template< - typename Compare, - typename Projection = utility::identity - > + template class stable_compare { private: @@ -52,34 +49,20 @@ namespace cppsort public: - stable_compare(Compare compare, Projection projection={}): + stable_compare(Compare compare, Projection projection): data(utility::as_function(compare), utility::as_function(projection)) {} - auto compare() const - -> compare_t - { - return std::get<0>(data); - } - - auto projection() const - -> projection_t - { - return std::get<1>(data); - } - template auto operator()(T&& lhs, U&& rhs) -> bool { - if (std::get<0>(data)(std::get<1>(data)(std::forward(lhs).get()), - std::get<1>(data)(std::forward(rhs).get()))) - { + if (std::get<0>(data)(std::get<1>(data)(lhs.get()), + std::get<1>(data)(rhs.get()))) { return true; } - if (std::get<0>(data)(std::get<1>(data)(std::forward(rhs).get()), - std::get<1>(data)(std::forward(lhs).get()))) - { + if (std::get<0>(data)(std::get<1>(data)(rhs.get()), + std::get<1>(data)(lhs.get()))) { return false; } return lhs.data < rhs.data; @@ -134,7 +117,10 @@ namespace cppsort return std::forward(sorter)( make_associate_iterator(iterators.get()), make_associate_iterator(iterators.get() + size), - make_stable_compare(std::move(compare), std::move(projection)) + make_stable_compare( + std::forward(compare), + std::forward(projection) + ) ); } @@ -150,9 +136,12 @@ namespace cppsort { // Hack to get the stable bidirectional version of vergesort // to work correctly without duplicating tons of code - return make_stable_and_sort(first.base(), size, - std::move(compare), std::move(projection), - std::move(sorter)); + return make_stable_and_sort( + first.base(), size, + std::forward(compare), + std::forward(projection), + std::forward(sorter) + ); } //////////////////////////////////////////////////////////// From 4a0bcc0c05b8c6648a51a12e81f84ec732ba5499 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 8 Apr 2021 16:59:18 +0200 Subject: [PATCH 12/56] Revamp original research section about sorting networks --- docs/Original-research.md | 43 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/docs/Original-research.md b/docs/Original-research.md index ac5e58a0..296c4800 100644 --- a/docs/Original-research.md +++ b/docs/Original-research.md @@ -41,58 +41,71 @@ A double liner gnome sort (same as double linear insertion sort, but putting the ### Half-cleaner network tricks -A half-cleaner network is a comparison network of depth 1 in which input line i is compared with line `i + n/2` for `i = 1, 2, ..., n/2`. For the sake of simplicity, we only consider networks whose size is even. This kind of network is mostly mentioned when describing a particular step in bitonic sorting networks. I played a bit with half-cleaners to try to create new merging networks. The results are of course not as good as the existing sorting networks, but interesting nonetheless. +A half-cleaner network is a comparison network of depth 1 in which input every line `i` is compared with line `i + n/2` for `i = 1, 2, ..., n/2`. For the sake of simplicity, we only consider networks whose size is even. This kind of network is mostly mentioned when describing a particular step in bitonic sorting networks. I played a bit with half-cleaners to try to create new merging networks. The results are of course not as good as the existing sorting networks, but interesting nonetheless. -When given two sorted inputs of size `n/2`, a half-cleaner seems to have the following property: the first element of the output will be the smallest of the network and the last element of the output will be the greatest of the network. Moreover, both halves of the output are sorted. That means that it is possible to create what I would call a *shrinking merge* network that half-cleans the input of size `n`, then half-cleans the output of size `n-2`, then half-cleans the output of size `n-4`, etc... While easy to implement, it is a merging network slower than the most well-known ones and is of depth `n/2`, which is rather bad... +When given two sorted inputs of size `n/2`, a half-cleaner seems to have the following property: the first element of the output will be the smallest of the network and the last element of the output will be the greatest of the network. Moreover, both halves of the output are sorted. That means that it is possible to create what I would call a *shrinking merge* network that half-cleans the input of size `n`, then half-cleans the output of size `n-2`, then half-cleans the output of size `n-4`, etc... While easy to implement, it gives a merging network with `(n/4)*(n/4+1)` compare-exchanges and a depth of `n/2`, which is rather bad... Anyway, the nice property of this half-cleaning is that it can be performed on networks of any size (as long as the size is even). It means that it is for example possible to half-clean an input of size 12 (elements 1 and 12 will be in their final position, elements [2-6] and [7-11] are sorted), then to sort the elements 2 and 11, and to perform an odd-even merge on the elements [3-10]. Once again, it does not produce better results than an odd-even merge, but it's easy to implement for any size of arrays and is sometimes as good as an odd-even merge. ### Sorting networks for 23 and 24 inputs -While trying to reimplement size-optimal sorting networks as described by [*Finding Better Sorting Networks*][better-sorting-networks], I ended up implementing a sorting network for 24 inputs whose size was equivalent to that of the one described in the paper (123 compare-exchange units). However, it seems that this sorting network does not use an odd-even merge network but another merge network, obtained by the method described in the previous section. From this network, it was also trivial to generate the corresponding network for 23 inputs, whose size also corresponds to the best-known one for that many inputs (118). The depth of both networks is 18, which is probably one more than the depth of the sorting networks using the odd-even merge (if I'm not mistaken, the depth of the odd-even merge is one less). Here are the two networks and the corresponding 0-based sequences of indices: +The following 24 inputs network uses the technique described in the previous section: +* Sort the first two halves with the best 12-sorter known so far +* Use half-cleaner to initiate a partial merge of the two arrays +* Compare-exchange elements 2-12, 3-13, 10-20 and 11-21 +* Perform an odd-even merge on elements 4-19 +* Compare-exchange elements 1-2, 3-4, 19-20 and 21-22 -![Sorting network 23](https://github.com/Morwenn/cpp-sort/wiki/images/sorting-network-23.png) +The resulting network has 123 compare-exchanges and a depth of 18. The 12-12 merging network has 45 compare-exchanges, which is equivalent to a 12-12 odd-even merge. - [[0, 1],[2, 3],[4, 5],[6, 7],[8, 9],[10, 11],[12, 13],[14, 15],[16, 17],[18, 19],[20, 21]] - [[1, 3],[5, 7],[9, 11],[0, 2],[4, 6],[8, 10],[13, 15],[17, 19],[12, 14],[16, 18],[20, 22]] +![Sorting network 24](https://github.com/Morwenn/cpp-sort/wiki/images/sorting-network-24.png) + + [[0, 1],[2, 3],[4, 5],[6, 7],[8, 9],[10, 11],[12, 13],[14, 15],[16, 17],[18, 19],[20, 21],[22, 23]] + [[1, 3],[5, 7],[9, 11],[0, 2],[4, 6],[8, 10],[13, 15],[17, 19],[21, 23],[12, 14],[16, 18],[20, 22]] [[1, 2],[5, 6],[9, 10],[13, 14],[17, 18],[21, 22]] [[1, 5],[6, 10],[13, 17],[18, 22]] [[5, 9],[2, 6],[17, 21],[14, 18]] - [[1, 5],[6, 10],[0, 4],[7, 11],[13, 17],[18, 22],[12, 16]] + [[1, 5],[6, 10],[0, 4],[7, 11],[13, 17],[18, 22],[12, 16],[19, 23]] [[3, 7],[4, 8],[15, 19],[16, 20]] - [[0, 4],[7, 11],[12, 16]] + [[0, 4],[7, 11],[12, 16],[19, 23]] [[1, 4],[7, 10],[3, 8],[13, 16],[19, 22],[15, 20]] [[2, 3],[8, 9],[14, 15],[20, 21]] [[2, 4],[7, 9],[3, 5],[6, 8],[14, 16],[19, 21],[15, 17],[18, 20]] [[3, 4],[5, 6],[7, 8],[15, 16],[17, 18],[19, 20]] - [[0, 12],[1, 13],[2, 14],[3, 15],[4, 16],[5, 17],[6, 18],[7, 19],[8, 20],[9, 21],[10, 22]] + [[0, 12],[1, 13],[2, 14],[3, 15],[4, 16],[5, 17],[6, 18],[7, 19],[8, 20],[9, 21],[10, 22],[11, 23]] [[2, 12],[3, 13],[10, 20],[11, 21]] [[4, 12],[5, 13],[6, 14],[7, 15],[8, 16],[9, 17],[10, 18],[11, 19]] [[8, 12],[9, 13],[10, 14],[11, 15]] [[6, 8],[10, 12],[14, 16],[7, 9],[11, 13],[15, 17]] [[1, 2],[3, 4],[5, 6],[7, 8],[9, 10],[11, 12],[13, 14],[15, 16],[17, 18],[19, 20],[21, 22]] -![Sorting network 24](https://github.com/Morwenn/cpp-sort/wiki/images/sorting-network-24.png) +Removing the first or the last line of the network yields the following 23-sorter with 118 compare-exchanges and a depth of 18. - [[0, 1],[2, 3],[4, 5],[6, 7],[8, 9],[10, 11],[12, 13],[14, 15],[16, 17],[18, 19],[20, 21],[22, 23]] - [[1, 3],[5, 7],[9, 11],[0, 2],[4, 6],[8, 10],[13, 15],[17, 19],[21, 23],[12, 14],[16, 18],[20, 22]] +![Sorting network 23](https://github.com/Morwenn/cpp-sort/wiki/images/sorting-network-23.png) + + [[0, 1],[2, 3],[4, 5],[6, 7],[8, 9],[10, 11],[12, 13],[14, 15],[16, 17],[18, 19],[20, 21]] + [[1, 3],[5, 7],[9, 11],[0, 2],[4, 6],[8, 10],[13, 15],[17, 19],[12, 14],[16, 18],[20, 22]] [[1, 2],[5, 6],[9, 10],[13, 14],[17, 18],[21, 22]] [[1, 5],[6, 10],[13, 17],[18, 22]] [[5, 9],[2, 6],[17, 21],[14, 18]] - [[1, 5],[6, 10],[0, 4],[7, 11],[13, 17],[18, 22],[12, 16],[19, 23]] + [[1, 5],[6, 10],[0, 4],[7, 11],[13, 17],[18, 22],[12, 16]] [[3, 7],[4, 8],[15, 19],[16, 20]] - [[0, 4],[7, 11],[12, 16],[19, 23]] + [[0, 4],[7, 11],[12, 16]] [[1, 4],[7, 10],[3, 8],[13, 16],[19, 22],[15, 20]] [[2, 3],[8, 9],[14, 15],[20, 21]] [[2, 4],[7, 9],[3, 5],[6, 8],[14, 16],[19, 21],[15, 17],[18, 20]] [[3, 4],[5, 6],[7, 8],[15, 16],[17, 18],[19, 20]] - [[0, 12],[1, 13],[2, 14],[3, 15],[4, 16],[5, 17],[6, 18],[7, 19],[8, 20],[9, 21],[10, 22],[11, 23]] + [[0, 12],[1, 13],[2, 14],[3, 15],[4, 16],[5, 17],[6, 18],[7, 19],[8, 20],[9, 21],[10, 22]] [[2, 12],[3, 13],[10, 20],[11, 21]] [[4, 12],[5, 13],[6, 14],[7, 15],[8, 16],[9, 17],[10, 18],[11, 19]] [[8, 12],[9, 13],[10, 14],[11, 15]] [[6, 8],[10, 12],[14, 16],[7, 9],[11, 13],[15, 17]] [[1, 2],[3, 4],[5, 6],[7, 8],[9, 10],[11, 12],[13, 14],[15, 16],[17, 18],[19, 20],[21, 22]] +When I originally released them, those networks were the smallest 23-sorters and 24-sorters (though other networks witht the same number of comparators existed). Nowadays there are way better networks for 23 and 24 inputs. + +I tried to apply the same technique to create a 40-sorter, but the resulting 20-20 merging network has 93 compare-exchanges while the 20-20 odd-even merge network has 89. This merging technique probably becomes gradually worse compared to odd-even merge. + ### Sorting network for 29 inputs The following sorting network for 29 inputs has 165 compare-exchange-units, which is one less that the most size-optimal 29-input sorting networks that I could find in the literature. Here is how I generated it: first it sorts the first 16 inputs and the last 13 inputs independently. Then it merges the two sorted subarrays using a size 32 Batcher odd-even merge network (the version that does not need the inputs to be interleaved), where all compare-exchange units working on indexes greater than 28 have been dropped. Dropping comparators in such a way is ok: consider that the values at the indexes [29, 32) are greater than every other value in the array to sort, and it will become intuitive that dropping them generates a correct merging network of a smaller size. From dd5c370da1a0a90fea23cb106effa5ba1dd0023f Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 10 Apr 2021 15:48:04 +0200 Subject: [PATCH 13/56] Simplify spinsort implementation a bit --- include/cpp-sort/detail/boost_common/range.h | 14 ++-- include/cpp-sort/detail/spinsort.h | 79 ++++++++------------ 2 files changed, 39 insertions(+), 54 deletions(-) diff --git a/include/cpp-sort/detail/boost_common/range.h b/include/cpp-sort/detail/boost_common/range.h index 3c1c043a..7db63d6f 100644 --- a/include/cpp-sort/detail/boost_common/range.h +++ b/include/cpp-sort/detail/boost_common/range.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -40,10 +40,10 @@ namespace boost_common /// @brief this represent a range between two iterators /// @remarks //---------------------------------------------------------------------------- - template + template struct range { - Iter_t first, last; + RandomAccessIterator first, last; //------------------------------------------------------------------------ // function : range @@ -57,7 +57,7 @@ namespace boost_common /// @param frs : iterator to the first element /// @param lst : iterator to the last element //----------------------------------------------------------------------- - range(const Iter_t& frs, const Iter_t& lst): + range(const RandomAccessIterator& frs, const RandomAccessIterator& lst): first(frs), last(lst) {} @@ -101,7 +101,7 @@ namespace boost_common /// @return size //----------------------------------------------------------------------- auto size() const - -> std::size_t + -> difference_type_t { return last - first; } @@ -112,7 +112,7 @@ namespace boost_common /// @return iterator //----------------------------------------------------------------------- auto front() const - -> Iter_t + -> RandomAccessIterator { return first; } @@ -123,7 +123,7 @@ namespace boost_common /// @return iterator //------------------------------------------------------------------------- auto back() const - -> Iter_t + -> RandomAccessIterator { return std::prev(last); } diff --git a/include/cpp-sort/detail/spinsort.h b/include/cpp-sort/detail/spinsort.h index 59bd8c4c..ee4f99a1 100644 --- a/include/cpp-sort/detail/spinsort.h +++ b/include/cpp-sort/detail/spinsort.h @@ -38,6 +38,7 @@ #include "iterator_traits.h" #include "memory.h" #include "move.h" +#include "swap_ranges.h" #include "type_traits.h" #include "upper_bound.h" @@ -73,7 +74,7 @@ namespace detail { using utility::iter_move; - CPPSORT_ASSERT(std::size_t(last - mid) <= rng_aux.size()); + CPPSORT_ASSERT(last - mid <= rng_aux.size()); if (mid == last || first == mid) { return; } @@ -86,15 +87,10 @@ namespace detail // the data are inserted in rng_aux //----------------------------------------------------------------------- std::vector viter; - auto beta = rng_aux.first; auto data = rng_aux.first; + detail::move(mid, last, data); - for (auto alpha = mid ; alpha != last ; ++alpha) { - *beta = iter_move(alpha); - ++beta; - } - - std::size_t ndata = last - mid; + auto ndata = last - mid; RandomAccessIterator1 linf = first, lsup = mid; for (std::uint32_t i = 0 ; i < ndata ; ++i) { @@ -102,9 +98,9 @@ namespace detail viter.push_back(it1); linf = it1; } + viter.push_back(mid); // moving the elements - viter.push_back(mid); for (std::uint32_t i = viter.size() - 1; i != 0; --i) { RandomAccessIterator1 src = viter[i], limit = viter[i - 1]; RandomAccessIterator1 dest = src + i; @@ -123,7 +119,7 @@ namespace detail // @param range_input : range with the elements to sort // @param range_buffer : range with the elements sorted // @param comp : object for to compare two elements - // @param level : when is 1, sort with the insertionsort algorithm + // @param level : when is 1, sort with the insertion sort algorithm // if not make a recursive call splitting the ranges // // @comments : if the number of levels is odd, the data are in the first @@ -136,16 +132,15 @@ namespace detail Compare compare, Projection projection) -> bool { - // the maximun number of elements not ordered, for to be inserted in the - // sorted part - //const ptrdiff_t min_insert_partial_sort = 32 ; - const std::size_t ndata = rng_data.size(); + auto ndata = rng_data.size(); if (ndata < 32) { insertion_sort(rng_data.first, rng_data.last, compare, projection); return true; } - const std::size_t min_insert_partial_sort = ((ndata >> 3) < 33) ? 32 : (ndata >> 3); - if (ndata < 2) return true; // TODO: redundant? + + // Maximum number of elements not ordered, for to be inserted in the + // sorted part + auto min_insert_partial_sort = (ndata / 8 < 33) ? 32 : ndata / 8; // check if sorted auto it = detail::is_sorted_until(rng_data.first, rng_data.last, compare, projection); @@ -154,7 +149,7 @@ namespace detail } // insert the elements between it1 and last - if (std::size_t(rng_data.last - it) < min_insert_partial_sort) { + if (rng_data.last - it < min_insert_partial_sort) { sort_range_sort(range(it, rng_data.last), rng_aux, compare, projection); insert_partial_sort(rng_data.first, it, rng_data.last, compare, projection, rng_aux); return true; @@ -166,19 +161,14 @@ namespace detail } it = detail::is_sorted_until(rng_data.first, rng_data.last, detail::not_fn(compare), projection); - if (std::size_t(rng_data.last - it) >= min_insert_partial_sort) { + if (rng_data.last - it >= min_insert_partial_sort) { return false; } // reverse the elements between first and it1 - std::size_t nreverse = it - rng_data.first; - auto beta = std::prev(it); - auto mid = rng_data.first + (nreverse >> 1); - for (auto alpha = rng_data.first ; alpha != mid ; ++alpha) { - using utility::iter_swap; - iter_swap(alpha, beta); - --beta; - } + auto nreverse = it - rng_data.first; + auto mid = rng_data.first + nreverse / 2; + detail::swap_ranges_overlap(rng_data.first, mid, std::make_reverse_iterator(it)); // insert the elements between it1 and last if (it != rng_data.last) { @@ -226,7 +216,7 @@ namespace detail } //------------------- normal process ----------------------------------- - std::size_t nelem1 = (range1.size() + 1) >> 1; + auto nelem1 = (range1.size() + 1) / 2; range_it1 range_input1(range1.first, range1.first + nelem1), range_input2(range1.first + nelem1, range1.last); @@ -300,12 +290,6 @@ namespace detail // by the insertion sort algorithm static constexpr std::uint32_t Sort_min = 36; - // Pointer to the auxiliary memory - std::unique_ptr ptr; - - // Number of elements in the auxiliary memory - std::size_t nptr; - public: //------------------------------------------------------------------------- @@ -319,37 +303,38 @@ namespace detail // @param projection : projection object //------------------------------------------------------------------------ spinsort(RandomAccessIterator first, RandomAccessIterator last, - Compare compare, Projection projection): - ptr(nullptr), - nptr(0) + Compare compare, Projection projection) { using utility::iter_move; range range_input(first, last); CPPSORT_ASSERT(range_input.valid()); - std::size_t nelem = range_input.size(); - if (nelem <= (Sort_min << 1)) { + auto size = range_input.size(); + if (size <= Sort_min * 2) { insertion_sort(range_input.first, range_input.last, std::move(compare), std::move(projection)); return; } - nptr = (nelem + 1) >> 1; - std::size_t nelem_1 = nptr; - std::size_t nelem_2 = nelem - nelem_1; - ptr.reset(static_cast( - ::operator new(nptr * sizeof(rvalue_type)) - )); - range_buf range_aux(ptr.get(), (ptr.get() + nptr)); + auto nelem_1 = (size + 1) / 2; + auto nelem_2 = size - nelem_1; + + // Buffer used by the merge operations + auto buffer_size = nelem_1; + std::unique_ptr buffer( + static_cast(::operator new(buffer_size * sizeof(rvalue_type))), + operator_deleter(buffer_size * sizeof(rvalue_type)) + ); + range_buf range_aux(buffer.get(), (buffer.get() + buffer_size)); destruct_n d(0); - std::unique_ptr&> h2(ptr.get(), d); + std::unique_ptr&> h2(buffer.get(), d); //--------------------------------------------------------------------- // Process //--------------------------------------------------------------------- - std::uint32_t nlevel = detail::log2(((nelem + Sort_min - 1) / Sort_min) - 1); + std::uint32_t nlevel = detail::log2(((size + Sort_min - 1) / Sort_min) - 1); CPPSORT_ASSERT(nlevel != 0); if ((nlevel & 1) == 1) { From 359e120ab4e4378eecdd31bf3c79d47e12a5ce2b Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 14 Apr 2021 10:31:43 +0200 Subject: [PATCH 14/56] grail_sort: remove unused include --- include/cpp-sort/detail/grail_sort.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/cpp-sort/detail/grail_sort.h b/include/cpp-sort/detail/grail_sort.h index 411a01e2..36219736 100644 --- a/include/cpp-sort/detail/grail_sort.h +++ b/include/cpp-sort/detail/grail_sort.h @@ -27,7 +27,6 @@ #include "insertion_sort.h" #include "iterator_traits.h" #include "lower_bound.h" -#include "merge_move.h" #include "move.h" #include "rotate.h" #include "swap_ranges.h" From aa657144c4cfef14ab2c309e8c4249dd21443f48 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 15 Apr 2021 16:47:13 +0200 Subject: [PATCH 15/56] Allow void return type for sorter_facade to fptr conversion --- docs/Sorter-facade.md | 4 ++ include/cpp-sort/sorter_facade.h | 32 +++++++++-- testsuite/CMakeLists.txt | 1 + testsuite/sorter_facade_fptr.cpp | 99 ++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 testsuite/sorter_facade_fptr.cpp diff --git a/docs/Sorter-facade.md b/docs/Sorter-facade.md index c6f9334a..eb715766 100644 --- a/docs/Sorter-facade.md +++ b/docs/Sorter-facade.md @@ -43,6 +43,8 @@ template constexpr operator Ret(*)(Iterator, Iterator, Args...)() const; ``` +The return type `Ret` can either match that of the sorter, or be `void`, in which case the return value is discarded. + Note that the function pointer conversion syntax above is made up, but it allows to clearly highlight what it does while hiding the `typedef`s needed for the syntax to be valid. In these signatures, `Ret` is the [`std::result_of_t`][std-result-of] of the sorter called with the parameters. The actual implementation is more verbose and redundant, but it allows to transform a sorter into a function pointer corresponding to any valid overload of `operator()`. ***WARNING:** conversion to function pointers does not work with MSVC (issue #185).* @@ -51,6 +53,8 @@ Note that the function pointer conversion syntax above is made up, but it allows *Changed in version 1.10.0:* the conversion operators are always `constexpr` (it used to be a C++17 feature). +*Changed in version 1.11.0:* the return type of the function pointer type can be `void` regardless of the type(s) returned by the sorter. + ### `operator()` for pairs of iterators `sorter_facade` provides the following overloads of `operator()` to handle pairs of iterators: diff --git a/include/cpp-sort/sorter_facade.h b/include/cpp-sort/sorter_facade.h index 991e172a..ce65b9cb 100644 --- a/include/cpp-sort/sorter_facade.h +++ b/include/cpp-sort/sorter_facade.h @@ -25,15 +25,37 @@ namespace cppsort template struct invoker { - // This function is used to create function pointers from - // stateless sorters - - template + // These functions are used to create function pointers + // from stateless sorters: the first overload allows to + // create function pointers with a result similar to that + // of the sorter, the second one allows to cast away the + // result to void + + template< + typename Ret, + typename... Args + > static constexpr auto invoke(Args... args) - -> Ret + -> std::enable_if_t< + not std::is_void::value, + Ret + > { return Sorter{}(std::forward(args)...); } + + template< + typename Ret, + typename... Args + > + static constexpr auto invoke(Args... args) + -> std::enable_if_t< + std::is_void::value, + void + > + { + Sorter{}(std::forward(args)...); + } }; // Helper class to allow to convert a sorter_facade into a variety diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 2c80e535..96e6cc7e 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -123,6 +123,7 @@ add_executable(main-tests sorter_facade.cpp sorter_facade_constexpr.cpp sorter_facade_defaults.cpp + $<$>:sorter_facade_fptr.cpp> sorter_facade_iterable.cpp stable_sort_array.cpp diff --git a/testsuite/sorter_facade_fptr.cpp b/testsuite/sorter_facade_fptr.cpp new file mode 100644 index 00000000..bf750a78 --- /dev/null +++ b/testsuite/sorter_facade_fptr.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + struct dummy_t {}; + + struct sorter_impl + { + template< + typename Iterator, + typename Compare = std::less<>, + typename Projection = cppsort::utility::identity, + typename = std::enable_if_t> + > + auto operator()(Iterator, Iterator, Compare={}, Projection={}) const + -> dummy_t + { + return {}; + } + }; + + struct sorter: + cppsort::sorter_facade + {}; +} + +TEST_CASE( "check conversions from sorter_facade to fptr", + "[sorter_facade][function_pointer]" ) +{ + // Collection to "sort" + std::vector vec; + sorter sort_it; + + // The tests in this section are just compilation tests + + SECTION( "conversion to function pointer" ) + { + constexpr dummy_t(*sort1)(std::vector&) = sort_it; + constexpr dummy_t(*sort2)(std::vector&, std::greater<>) = sort_it; + constexpr dummy_t(*sort3)(std::vector&, std::negate<>) = sort_it; + constexpr dummy_t(*sort4)(std::vector&, std::greater<>, std::negate<>) = sort_it; + constexpr dummy_t(*sort5)(std::vector::iterator, std::vector::iterator) = sort_it; + constexpr dummy_t(*sort6)(std::vector::iterator, std::vector::iterator, + std::greater<>) = sort_it; + constexpr dummy_t(*sort7)(std::vector::iterator, std::vector::iterator, + std::negate<>) = sort_it; + constexpr dummy_t(*sort8)(std::vector::iterator, std::vector::iterator, + std::greater<>, std::negate<>) = sort_it; + + (void)sort1; + (void)sort2; + (void)sort3; + (void)sort4; + (void)sort5; + (void)sort6; + (void)sort7; + (void)sort8; + + CHECK( true ); + } + + SECTION( "conversion to function pointer with void return type" ) + { + constexpr void(*sort1)(std::vector&) = sort_it; + constexpr void(*sort2)(std::vector&, std::greater<>) = sort_it; + constexpr void(*sort3)(std::vector&, std::negate<>) = sort_it; + constexpr void(*sort4)(std::vector&, std::greater<>, std::negate<>) = sort_it; + constexpr void(*sort5)(std::vector::iterator, std::vector::iterator) = sort_it; + constexpr void(*sort6)(std::vector::iterator, std::vector::iterator, + std::greater<>) = sort_it; + constexpr void(*sort7)(std::vector::iterator, std::vector::iterator, + std::negate<>) = sort_it; + constexpr void(*sort8)(std::vector::iterator, std::vector::iterator, + std::greater<>, std::negate<>) = sort_it; + + (void)sort1; + (void)sort2; + (void)sort3; + (void)sort4; + (void)sort5; + (void)sort6; + (void)sort7; + (void)sort8; + + CHECK( true ); + } +} From 68236fe8b6011ad2892e3c44016fa8f6c10ae413 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 17 Apr 2021 14:03:46 +0200 Subject: [PATCH 16/56] Bump downloaded Catch2 version to v2.13.6 --- testsuite/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 96e6cc7e..6fc74f3b 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -31,7 +31,7 @@ else() message(STATUS "Catch2 not found") download_project(PROJ Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2 - GIT_TAG v2.13.4 + GIT_TAG v2.13.6 UPDATE_DISCONNECTED 1 ) add_subdirectory(${Catch2_SOURCE_DIR} ${Catch2_BINARY_DIR}) From 30d7ce95081894207f5d83fe6c74ece5f55827ea Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 20 Apr 2021 11:48:33 +0200 Subject: [PATCH 17/56] Micro-optimize fixed_size_list::merge() --- include/cpp-sort/detail/fixed_size_list.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/cpp-sort/detail/fixed_size_list.h b/include/cpp-sort/detail/fixed_size_list.h index c3df6300..16cacbd9 100644 --- a/include/cpp-sort/detail/fixed_size_list.h +++ b/include/cpp-sort/detail/fixed_size_list.h @@ -631,7 +631,7 @@ namespace detail auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); - if (is_empty()) { + if (first == last) { splice(last, other); return; } @@ -642,7 +642,7 @@ namespace detail auto other_it = other.begin(); auto other_end = other.end(); - while (other_it != other_end) { + do { if (comp(proj(*other_it), proj(*first))) { // The following loop finds a series of nodes to splice // into the current list, which is faster than splicing @@ -664,7 +664,7 @@ namespace detail other.sentinel_node_.prev = &other.sentinel_node_; return; } - } + } while (other_it != other_end); // Reset the other list's sentinel node, // fast_splice_ does no do it From cf52466baf51cf3109f9aaa4b11220866214e0d7 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 20 Apr 2021 11:50:15 +0200 Subject: [PATCH 18/56] doc: fix memory complexity of conainer_aware_adapter(mel_sort) --- docs/Sorters.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Sorters.md b/docs/Sorters.md index a9c3bfdf..a2233c34 100644 --- a/docs/Sorters.md +++ b/docs/Sorters.md @@ -179,8 +179,8 @@ This sorter also has the following dedicated algorithms when used together with | Container | Best | Average | Worst | Memory | Stable | | ------------------- | ----------- | ----------- | ----------- | ----------- | ----------- | -| `std::list` | n | n log n | n log n | sqrt n | No | -| `std::forward_list` | n | n log n | n log n | sqrt n | No | +| `std::list` | n | n log n | n log n | n | No | +| `std::forward_list` | n | n log n | n log n | n | No | None of the container-aware algorithms invalidates iterators. From edfd5768a9561eb4d74ed8093285eb9286c8ec55 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 24 Apr 2021 23:19:09 +0200 Subject: [PATCH 19/56] Introduce immovable_vector class Really similar to std::sort, but with the following properties: it's got a fixed capacity determined at construction time, and it is not movable. Unlike std::vector it allows to store immovable types. We use that new class everywhere it makes sense, simplifying a bunch of std::unique_ptr/destroy_n thingies. This commit also changes cartesian_tree_sort quite a bit and fixes a potential bug in its destructor: when an exception was thrown during the construction of the Cartesian tree, destroy_at was called on every allocated element instead of every constructed element. --- include/cpp-sort/adapters/indirect_adapter.h | 24 +-- .../cpp-sort/adapters/out_of_place_adapter.h | 38 ++-- include/cpp-sort/adapters/schwartz_adapter.h | 23 +-- include/cpp-sort/adapters/stable_adapter.h | 22 +-- include/cpp-sort/detail/cartesian_tree_sort.h | 46 ++--- include/cpp-sort/detail/immovable_vector.h | 165 ++++++++++++++++++ include/cpp-sort/detail/indiesort.h | 33 ++-- .../cpp-sort/detail/merge_insertion_sort.h | 17 +- include/cpp-sort/detail/slabsort.h | 29 ++- 9 files changed, 235 insertions(+), 162 deletions(-) create mode 100644 include/cpp-sort/detail/immovable_vector.h diff --git a/include/cpp-sort/adapters/indirect_adapter.h b/include/cpp-sort/adapters/indirect_adapter.h index e6ac5b80..dd026af2 100644 --- a/include/cpp-sort/adapters/indirect_adapter.h +++ b/include/cpp-sort/adapters/indirect_adapter.h @@ -10,7 +10,6 @@ //////////////////////////////////////////////////////////// #include #include -#include #include #include #include @@ -22,9 +21,9 @@ #include #include "../detail/checkers.h" #include "../detail/functional.h" +#include "../detail/immovable_vector.h" #include "../detail/indiesort.h" #include "../detail/iterator_traits.h" -#include "../detail/memory.h" #include "../detail/scope_exit.h" #include "../detail/type_traits.h" @@ -59,22 +58,15 @@ namespace cppsort //////////////////////////////////////////////////////////// // Indirectly sort the iterators - std::unique_ptr iterators( - static_cast(::operator new(size * sizeof(RandomAccessIterator))), - operator_deleter(size * sizeof(RandomAccessIterator)) - ); - destruct_n d(0); - std::unique_ptr&> h2(iterators.get(), d); - - auto ptr = iterators.get(); - for (auto it = first; it != last; ++it, (void) ++ptr, ++d) { - ::new(ptr) RandomAccessIterator(it); + immovable_vector iterators(size); + for (auto it = first; it != last; ++it) { + iterators.emplace_back(it); } #ifndef __cpp_lib_uncaught_exceptions // Sort the iterators on pointed values std::forward(sorter)( - iterators.get(), iterators.get() + size, + iterators.begin(), iterators.end(), std::move(compare), indirect(projection) ); #else @@ -93,7 +85,7 @@ namespace cppsort // Find the element to put in current's place auto current = start; auto next_pos = current - first; - auto next = iterators.get()[next_pos]; + auto next = iterators[next_pos]; sorted[next_pos] = true; // Process the current cycle @@ -103,7 +95,7 @@ namespace cppsort *current = iter_move(next); current = next; auto next_pos = next - first; - next = iterators.get()[next_pos]; + next = iterators[next_pos]; sorted[next_pos] = true; } *current = std::move(tmp); @@ -123,7 +115,7 @@ namespace cppsort } return std::forward(sorter)( - iterators.get(), iterators.get() + size, + iterators.begin(), iterators.end(), std::move(compare), indirect(projection) ); #endif diff --git a/include/cpp-sort/adapters/out_of_place_adapter.h b/include/cpp-sort/adapters/out_of_place_adapter.h index 77c7dd5e..3236c11c 100644 --- a/include/cpp-sort/adapters/out_of_place_adapter.h +++ b/include/cpp-sort/adapters/out_of_place_adapter.h @@ -10,8 +10,7 @@ //////////////////////////////////////////////////////////// #include #include -#include -#include +#include #include #include #include @@ -19,8 +18,8 @@ #include #include #include "../detail/checkers.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" -#include "../detail/memory.h" #include "../detail/scope_exit.h" #include "../detail/type_traits.h" @@ -32,43 +31,31 @@ namespace cppsort namespace detail { template - auto sort_out_of_place(ForwardIterator first, Size size, const Sorter& sorter, Args&&... args) + auto sort_out_of_place(ForwardIterator first, ForwardIterator last, + Size size, const Sorter& sorter, Args&&... args) -> decltype(auto) { using utility::iter_move; using rvalue_type = rvalue_type_t; // Copy the collection into contiguous memory buffer - std::unique_ptr buffer( - static_cast(::operator new(size * sizeof(rvalue_type))), - operator_deleter(size * sizeof(rvalue_type)) - ); - destruct_n d(0); - std::unique_ptr&> h2(buffer.get(), d); - - auto it = first; - auto ptr = buffer.get(); - for (Size i = 0 ; i < size ; ++i) { - ::new(ptr) rvalue_type(iter_move(it)); - ++it; - ++ptr; - ++d; - } + immovable_vector buffer(size); + buffer.insert_back(first, last); #ifdef __cpp_lib_uncaught_exceptions // Work around the sorters that return void auto exit_function = make_scope_success([&] { // Copy the sorted elements back in the original collection - std::move(buffer.get(), buffer.get() + size, first); + std::move(buffer.begin(), buffer.end(), first); }); // Sort the elements in the memory buffer - return sorter(buffer.get(), buffer.get() + size, std::forward(args)...); + return sorter(buffer.begin(), buffer.end(), std::forward(args)...); #else // Sort the elements in the memory buffer - sorter(buffer.get(), buffer.get() + size, std::forward(args)...); + sorter(buffer.begin(), buffer.end(), std::forward(args)...); // Copy the sorted elements back in the original collection - std::move(buffer.get(), buffer.get() + size, first); + std::move(buffer.begin(), buffer.end(), first); #endif } } @@ -99,7 +86,7 @@ namespace cppsort -> decltype(auto) { auto size = std::distance(first, last); - return detail::sort_out_of_place(first, size, this->get(), std::forward(args)...); + return detail::sort_out_of_place(first, last, size, this->get(), std::forward(args)...); } template @@ -108,7 +95,8 @@ namespace cppsort { // Might be an optimization for forward/bidirectional iterables auto size = utility::size(iterable); - return detail::sort_out_of_place(std::begin(iterable), size, this->get(), std::forward(args)...); + return detail::sort_out_of_place(std::begin(iterable), std::end(iterable), size, + this->get(), std::forward(args)...); } //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/adapters/schwartz_adapter.h b/include/cpp-sort/adapters/schwartz_adapter.h index 9f4eff59..f157c1d1 100644 --- a/include/cpp-sort/adapters/schwartz_adapter.h +++ b/include/cpp-sort/adapters/schwartz_adapter.h @@ -10,7 +10,6 @@ //////////////////////////////////////////////////////////// #include #include -#include #include #include #include @@ -24,8 +23,8 @@ #include "../detail/associate_iterator.h" #include "../detail/checkers.h" #include "../detail/config.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" -#include "../detail/memory.h" #include "../detail/type_traits.h" namespace cppsort @@ -77,27 +76,17 @@ namespace cppsort using value_t = association; using difference_type = difference_type_t; - // Collection of projected elements - std::unique_ptr projected( - static_cast(::operator new(size * sizeof(value_t))), - operator_deleter(size * sizeof(value_t)) - ); - destruct_n d(0); - std::unique_ptr&> h2(projected.get(), d); - // Associate iterator to projected element - auto ptr = projected.get(); - for (difference_type count = 0 ; count != size ; ++count) { - ::new(ptr) value_t(first, proj(*first)); - ++d; + immovable_vector projected(size); + for (difference_type count = 0; count != size; ++count) { + projected.emplace_back(first, proj(*first)); ++first; - ++ptr; } // Indirectly sort the original sequence return std::forward(sorter)( - make_associate_iterator(projected.get()), - make_associate_iterator(projected.get() + size), + make_associate_iterator(projected.begin()), + make_associate_iterator(projected.end()), std::move(compare), data_getter{} ); diff --git a/include/cpp-sort/adapters/stable_adapter.h b/include/cpp-sort/adapters/stable_adapter.h index 93992dc3..122c2dcd 100644 --- a/include/cpp-sort/adapters/stable_adapter.h +++ b/include/cpp-sort/adapters/stable_adapter.h @@ -10,7 +10,6 @@ //////////////////////////////////////////////////////////// #include #include -#include #include #include #include @@ -22,8 +21,8 @@ #include #include "../detail/associate_iterator.h" #include "../detail/checkers.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" -#include "../detail/memory.h" #include "../detail/sized_iterator.h" #include "../detail/type_traits.h" @@ -95,28 +94,19 @@ namespace cppsort //////////////////////////////////////////////////////////// // Bind index to iterator - std::unique_ptr iterators( - static_cast(::operator new(size * sizeof(value_t))), - operator_deleter(size * sizeof(value_t)) - ); - destruct_n d(0); - std::unique_ptr&> h2(iterators.get(), d); - // Associate iterators to their position - auto ptr = iterators.get(); - for (difference_type count = 0 ; count != size ; ++count) { - ::new(ptr) value_t(first, count); - ++d; + immovable_vector iterators(size); + for (difference_type count = 0; count != size; ++count) { + iterators.emplace_back(first, count); ++first; - ++ptr; } //////////////////////////////////////////////////////////// // Sort but takes the index into account to ensure stability return std::forward(sorter)( - make_associate_iterator(iterators.get()), - make_associate_iterator(iterators.get() + size), + make_associate_iterator(iterators.begin()), + make_associate_iterator(iterators.end()), make_stable_compare( std::forward(compare), std::forward(projection) diff --git a/include/cpp-sort/detail/cartesian_tree_sort.h b/include/cpp-sort/detail/cartesian_tree_sort.h index 44515052..3a3ecba6 100644 --- a/include/cpp-sort/detail/cartesian_tree_sort.h +++ b/include/cpp-sort/detail/cartesian_tree_sort.h @@ -9,7 +9,6 @@ // Headers //////////////////////////////////////////////////////////// #include -#include #include #include #include @@ -17,8 +16,8 @@ #include #include "../detail/heapsort.h" #include "../detail/functional.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" -#include "../detail/memory.h" #include "../detail/type_traits.h" namespace cppsort @@ -67,50 +66,31 @@ namespace detail template explicit cartesian_tree(Iterator first, Iterator last, Compare compare, Projection projection): - // Allocate enough space to store N nodes - size_(last - first), - buffer_(static_cast(::operator new(size_ * sizeof(node_type))), - operator_deleter(size_ * sizeof(node_type))), - root_(buffer_.get()) // Original root is first element + buffer_(last - first), + root_(buffer_.begin()) // Original root is first element { using utility::iter_move; auto&& proj = utility::as_function(projection); - // Pointer to the next memory where to allocate a node - node_type* current_node = buffer_.get(); // Pointer to the last node that was inserted - node_type* prev_node = current_node; + node_type* prev_node = buffer_.begin(); // Create the first node - ::new(current_node) node_type(iter_move(first), nullptr, nullptr); + buffer_.emplace_back(iter_move(first), nullptr, nullptr); // Advance to the next element ++first; - ++current_node; - for (; first != last ; ++first, ++current_node) { + for (; first != last ; ++first) { auto&& proj_value = proj(*first); node_type* new_parent = _find_insertion_parent(prev_node, proj_value, compare, projection); if (new_parent == nullptr) { - ::new(current_node) node_type(iter_move(first), nullptr, root_); - root_ = current_node; + buffer_.emplace_back(iter_move(first), nullptr, root_); + root_ = &buffer_.back(); } else { - ::new(current_node) node_type(iter_move(first), new_parent, new_parent->right_child); - new_parent->right_child = current_node; + buffer_.emplace_back(iter_move(first), new_parent, new_parent->right_child); + new_parent->right_child = &buffer_.back(); } - prev_node = current_node; - } - } - - //////////////////////////////////////////////////////////// - // Destructor - - ~cartesian_tree() - { - // Destroy the nodes - auto ptr = buffer_.get(); - for (difference_type idx = 0 ; idx < size_ ; ++idx) { - detail::destroy_at(ptr); - ++ptr; + prev_node = &buffer_.back(); } } @@ -148,10 +128,8 @@ namespace detail //////////////////////////////////////////////////////////// // Data members - // Number of nodes in the tree - difference_type size_; // Backing storage - std::unique_ptr buffer_; + immovable_vector buffer_; // Root of the Cartesian tree node_type* root_; }; diff --git a/include/cpp-sort/detail/immovable_vector.h b/include/cpp-sort/detail/immovable_vector.h new file mode 100644 index 00000000..b823b943 --- /dev/null +++ b/include/cpp-sort/detail/immovable_vector.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_DETAIL_IMMOVABLE_VECTOR_H_ +#define CPPSORT_DETAIL_IMMOVABLE_VECTOR_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include "config.h" + +namespace cppsort +{ +namespace detail +{ + //////////////////////////////////////////////////////////// + // Immovable vector + // + // Lightweight class similar to std::vector, but with a few + // key differences: it allocates a buffer sufficient to store + // a given number of elements during construction, and it can + // not be moved. While its original goal was to provide an + // std::vector-like class for immovable types, it is also + // through the library as a light contigusou collection when + // the number of elements to allocate is already known at + // construction time. + + template + class immovable_vector + { + public: + + // Make the collection immovable + immovable_vector(const immovable_vector&) = delete; + immovable_vector(immovable_vector&&) = delete; + immovable_vector& operator=(const immovable_vector&) = delete; + immovable_vector& operator=(immovable_vector&&) = delete; + + //////////////////////////////////////////////////////////// + // Construction + + immovable_vector(std::ptrdiff_t n): + capacity_(n), + memory_( + static_cast(::operator new(n * sizeof(T))) + ), + end_(memory_) + {} + + //////////////////////////////////////////////////////////// + // Destruction + + ~immovable_vector() + { + // Destroy the constructed elements + for (auto ptr = memory_; ptr != end_; ++ptr) { + ptr->~T(); + } + + // Free the allocated memory +#ifdef __cpp_sized_deallocation + ::operator delete(memory_, capacity_ * sizeof(T)); +#else + ::operator delete(memory_); +#endif + } + + //////////////////////////////////////////////////////////// + // Element access + + auto operator[](std::ptrdiff_t pos) + -> T& + { + CPPSORT_ASSERT(pos <= end_ - memory_); + return memory_[pos]; + } + + auto front() + -> T& + { + CPPSORT_ASSERT(memory_ != end_); + return *memory_; + } + + auto back() + -> T& + { + CPPSORT_ASSERT(end_ - memory_ > 0); + return *(end_ - 1); + } + + //////////////////////////////////////////////////////////// + // Iterators + + auto begin() + -> T* + { + return memory_; + } + + auto end() + -> T* + { + return end_; + } + + //////////////////////////////////////////////////////////// + // Modifiers + + auto clear() noexcept + -> void + { + // Destroy the constructed elements + for (auto ptr = memory_; ptr != end_; ++ptr) { + ptr->~T(); + } + + // Ensure the new size is zero + end_ = memory_; + } + + template + auto emplace_back(Args&&... args) + -> void + { + CPPSORT_ASSERT(end_ - memory_ < capacity_); + ::new(end_) T(std::forward(args)...); + ++end_; + } + + template + auto insert_back(Iterator first, Iterator last) + -> void + { + auto writer = end_; + try { + for (; first != last; ++first) { + using utility::iter_move; + ::new(writer) T(iter_move(first)); + ++writer; + } + } catch (...) { + // Cleanup + for (auto ptr = end_; ptr != writer; ++ptr) { + ptr->~T(); + } + throw; + } + end_ = writer; + } + + private: + + std::ptrdiff_t capacity_; + T* memory_; + T* end_; + }; +}} + +#endif // CPPSORT_DETAIL_IMMOVABLE_VECTOR_H_ diff --git a/include/cpp-sort/detail/indiesort.h b/include/cpp-sort/detail/indiesort.h index f64c628e..e2d9aa5a 100644 --- a/include/cpp-sort/detail/indiesort.h +++ b/include/cpp-sort/detail/indiesort.h @@ -29,12 +29,11 @@ // Headers //////////////////////////////////////////////////////////// #include -#include #include #include #include +#include "../detail/immovable_vector.h" #include "iterator_traits.h" -#include "memory.h" #include "scope_exit.h" namespace cppsort @@ -66,29 +65,19 @@ namespace detail auto&& proj = utility::as_function(projection); using item_index_tuple = pointer_index_tuple; - std::unique_ptr storage( - static_cast(::operator new(size * sizeof(item_index_tuple))), - operator_deleter(size * sizeof(item_index_tuple)) - ); - destruct_n d(0); - std::unique_ptr&> h2(storage.get(), d); - - auto sort_array = storage.get(); - item_index_tuple* tuple_pointer = storage.get(); + immovable_vector storage(size); - // Construct pointers to all elements in the sequence: + // Construct pointers to all elements in the sequence difference_type index = 0; for (auto current_element = first; current_element != last; ++current_element) { - ::new(tuple_pointer) item_index_tuple(current_element, index); - ++tuple_pointer; + storage.emplace_back(current_element, index); ++index; - ++d; } #ifndef __cpp_lib_uncaught_exceptions // Sort the iterators on pointed values std::forward(sorter)( - sort_array, sort_array + size, std::move(compare), + storage.begin(), storage.end(), std::move(compare), [&proj](auto& elem) -> decltype(auto) { return proj(*(elem.original_location)); } @@ -100,7 +89,7 @@ namespace detail // Sort the actual elements via the tuple array: index = 0; - for (auto current_tuple = sort_array; current_tuple != tuple_pointer; ++current_tuple, ++index) { + for (auto current_tuple = storage.begin(); current_tuple != storage.end(); ++current_tuple, ++index) { if (current_tuple->original_index != index) { auto end_value = iter_move(current_tuple->original_location); @@ -108,13 +97,13 @@ namespace detail difference_type source_index = current_tuple->original_index; do { - *(sort_array[destination_index].original_location) = iter_move(sort_array[source_index].original_location); + *(storage[destination_index].original_location) = iter_move(storage[source_index].original_location); destination_index = source_index; - source_index = sort_array[destination_index].original_index; - sort_array[destination_index].original_index = destination_index; + source_index = storage[destination_index].original_index; + storage[destination_index].original_index = destination_index; } while (source_index != index); - *(sort_array[destination_index].original_location) = std::move(end_value); + *(storage[destination_index].original_location) = std::move(end_value); } } @@ -127,7 +116,7 @@ namespace detail // Sort the iterators on pointed values return std::forward(sorter)( - sort_array, sort_array + size, std::move(compare), + storage.begin(), storage.end(), std::move(compare), [&proj](auto& elem) -> decltype(auto) { return proj(*(elem.original_location)); } diff --git a/include/cpp-sort/detail/merge_insertion_sort.h b/include/cpp-sort/detail/merge_insertion_sort.h index 5a9fffc6..7aa321e4 100644 --- a/include/cpp-sort/detail/merge_insertion_sort.h +++ b/include/cpp-sort/detail/merge_insertion_sort.h @@ -10,7 +10,6 @@ //////////////////////////////////////////////////////////// #include #include -#include #include #include #include @@ -19,8 +18,8 @@ #include "config.h" #include "fixed_size_list.h" #include "functional.h" +#include "immovable_vector.h" #include "iterator_traits.h" -#include "memory.h" #include "move.h" #include "scope_exit.h" #include "swap_if.h" @@ -404,21 +403,13 @@ namespace detail // Number of sub-iterators auto full_size = size * first.size(); - using rvalue_type = rvalue_type_t; - std::unique_ptr cache( - static_cast(::operator new(full_size * sizeof(rvalue_type))), - operator_deleter(full_size * sizeof(rvalue_type)) - ); - destruct_n d(0); - std::unique_ptr&> h2(cache.get(), d); - - rvalue_type* buff_it = cache.get(); + immovable_vector> cache(full_size); for (auto&& it: chain) { auto begin = it.base(); auto end = begin + it.size(); - buff_it = uninitialized_move(begin, end, buff_it, d); + cache.insert_back(begin, end); } - detail::move(cache.get(), cache.get() + full_size, first.base()); + detail::move(cache.begin(), cache.end(), first.base()); // Everything else worked, it's now safe to reset the node pool node_pool_reset.activate(); diff --git a/include/cpp-sort/detail/slabsort.h b/include/cpp-sort/detail/slabsort.h index baa5de89..849de861 100644 --- a/include/cpp-sort/detail/slabsort.h +++ b/include/cpp-sort/detail/slabsort.h @@ -9,17 +9,16 @@ // Headers //////////////////////////////////////////////////////////// #include -#include #include #include #include #include "bitops.h" #include "fixed_size_list.h" #include "functional.h" +#include "immovable_vector.h" #include "iterator_traits.h" #include "lower_bound.h" #include "melsort.h" -#include "memory.h" #include "stable_partition.h" #include "nth_element.h" @@ -58,7 +57,7 @@ namespace detail template auto slabsort_get_median(RandomAccessIterator first, RandomAccessIterator last, - RandomAccessIterator* iterators_buffer, + immovable_vector& iterators_buffer, Compare compare, Projection projection) -> RandomAccessIterator { @@ -68,30 +67,25 @@ namespace detail //////////////////////////////////////////////////////////// // Bind index to iterator - destruct_n d(0); - std::unique_ptr&> h2(iterators_buffer, d); - // Associate iterators to their position - auto ptr = iterators_buffer; - for (difference_type count = 0 ; count != size ; ++count) { - ::new(ptr) RandomAccessIterator(first); - ++d; + iterators_buffer.clear(); + for (difference_type count = 0; count != size; ++count) { + iterators_buffer.emplace_back(first); ++first; - ++ptr; } //////////////////////////////////////////////////////////// // Run nth_element stably and indirectly return *nth_element( - iterators_buffer, iterators_buffer + size, size / 2, size, + iterators_buffer.begin(), iterators_buffer.end(), size / 2, size, std::move(compare), indirect(std::move(projection)) ); } template auto slabsort_partition(RandomAccessIterator first, RandomAccessIterator last, - RandomAccessIterator* iterators_buffer, + immovable_vector& iterators_buffer, Compare compare, Projection projection) -> void { @@ -220,7 +214,7 @@ namespace detail difference_type_t size, difference_type_t original_p, difference_type_t current_p, - RandomAccessIterator* iterators_buffer, + immovable_vector& iterators_buffer, fixed_size_list_node_pool>& node_pool, Compare compare, Projection projection) -> void @@ -277,15 +271,12 @@ namespace detail } // Allocate a buffer that will be used for median finding - std::unique_ptr iterators_buffer( - static_cast(::operator new(size * sizeof(RandomAccessIterator))), - operator_deleter(size * sizeof(RandomAccessIterator)) - ); + immovable_vector iterators_buffer(size); difference_type_t original_p = 2; return slabsort_impl( first, last, size, original_p, original_p, - iterators_buffer.get(), node_pool, + iterators_buffer, node_pool, std::move(compare), std::move(projection) ); } From 6c9f003497ceec1c5100d3563f49fc05e7c7b320 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 25 Apr 2021 18:43:30 +0200 Subject: [PATCH 20/56] Add and use destroy/destroy_n --- include/cpp-sort/detail/immovable_vector.h | 15 ++++------- include/cpp-sort/detail/memory.h | 29 +++++++++++++++++----- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/include/cpp-sort/detail/immovable_vector.h b/include/cpp-sort/detail/immovable_vector.h index b823b943..a81d0a59 100644 --- a/include/cpp-sort/detail/immovable_vector.h +++ b/include/cpp-sort/detail/immovable_vector.h @@ -13,6 +13,7 @@ #include #include #include "config.h" +#include "memory.h" namespace cppsort { @@ -26,7 +27,7 @@ namespace detail // a given number of elements during construction, and it can // not be moved. While its original goal was to provide an // std::vector-like class for immovable types, it is also - // through the library as a light contigusou collection when + // through the library as a light contiguous collection when // the number of elements to allocate is already known at // construction time. @@ -58,9 +59,7 @@ namespace detail ~immovable_vector() { // Destroy the constructed elements - for (auto ptr = memory_; ptr != end_; ++ptr) { - ptr->~T(); - } + detail::destroy(memory_, end_); // Free the allocated memory #ifdef __cpp_sized_deallocation @@ -116,9 +115,7 @@ namespace detail -> void { // Destroy the constructed elements - for (auto ptr = memory_; ptr != end_; ++ptr) { - ptr->~T(); - } + detail::destroy(memory_, end_); // Ensure the new size is zero end_ = memory_; @@ -146,9 +143,7 @@ namespace detail } } catch (...) { // Cleanup - for (auto ptr = end_; ptr != writer; ++ptr) { - ptr->~T(); - } + detail::destroy(end_, writer); throw; } end_ = writer; diff --git a/include/cpp-sort/detail/memory.h b/include/cpp-sort/detail/memory.h index a1dbed26..cce8ee81 100644 --- a/include/cpp-sort/detail/memory.h +++ b/include/cpp-sort/detail/memory.h @@ -19,6 +19,7 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include #include "type_traits.h" @@ -28,7 +29,7 @@ namespace cppsort namespace detail { //////////////////////////////////////////////////////////// - // C++17 std::destroy_at + // C++17 std::destroy and friends template auto destroy_at(T* ptr) @@ -39,6 +40,25 @@ namespace detail ptr->~T(); } + template + auto destroy(ForwardIterator first, ForwardIterator last) + -> void + { + for (; first != last; ++first) { + detail::destroy_at(std::addressof(*first)); + } + } + + template + auto destroy_n(ForwardIterator first, Size n) + -> void + { + for (; n > 0; --n) { + destroy_at(std::addressof(*first)); + ++first; + } + } + //////////////////////////////////////////////////////////// // Deleter for ::operator new(std::size_t) @@ -101,10 +121,7 @@ namespace detail constexpr auto operator()(T* pointer) noexcept -> void { - for (std::size_t i = 0 ; i < size ; ++i) { - detail::destroy_at(pointer); - ++pointer; - } + detail::destroy_n(pointer, size); } // Number of allocated objects to destroy @@ -130,7 +147,7 @@ namespace detail * This functions tries to allocate enough storage for at least * \a count objects. Failing that it will try to allocate storage * for fewer objects and will give up if it can't allocate more - * than \a min_size objects. + * than \a min_count objects. */ template auto get_temporary_buffer(std::ptrdiff_t count, std::ptrdiff_t min_count) noexcept From c3d940f8aedf540cfdbc54bd45dd60ca10b7f769 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 27 Apr 2021 16:07:38 +0200 Subject: [PATCH 21/56] Move is_in_pack to type_traits.h --- .../cpp-sort/adapters/small_array_adapter.h | 3 +- include/cpp-sort/detail/is_in_pack.h | 44 ------------------- .../cpp-sort/detail/schwartz_small_array.h | 4 +- include/cpp-sort/detail/type_traits.h | 30 ++++++++++++- 4 files changed, 32 insertions(+), 49 deletions(-) delete mode 100644 include/cpp-sort/detail/is_in_pack.h diff --git a/include/cpp-sort/adapters/small_array_adapter.h b/include/cpp-sort/adapters/small_array_adapter.h index 87b2c13e..7d0e628a 100644 --- a/include/cpp-sort/adapters/small_array_adapter.h +++ b/include/cpp-sort/adapters/small_array_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_ADAPTERS_SMALL_ARRAY_ADAPTER_H_ @@ -15,7 +15,6 @@ #include #include #include "../detail/any_all.h" -#include "../detail/is_in_pack.h" #include "../detail/type_traits.h" namespace cppsort diff --git a/include/cpp-sort/detail/is_in_pack.h b/include/cpp-sort/detail/is_in_pack.h deleted file mode 100644 index e9092121..00000000 --- a/include/cpp-sort/detail/is_in_pack.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2015-2018 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_IS_IN_PACK_H_ -#define CPPSORT_DETAIL_IS_IN_PACK_H_ - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include "type_traits.h" - -namespace cppsort -{ -namespace detail -{ - template - struct is_in_pack_impl; - - template< - std::size_t Value, - std::size_t Head, - std::size_t... Tail - > - struct is_in_pack_impl: - conditional_t< - Value == Head, - std::true_type, - is_in_pack_impl - > - {}; - - template - struct is_in_pack_impl: - std::false_type - {}; - - template - constexpr bool is_in_pack = is_in_pack_impl::value; -}} - -#endif // CPPSORT_DETAIL_IS_IN_PACK_H_ diff --git a/include/cpp-sort/detail/schwartz_small_array.h b/include/cpp-sort/detail/schwartz_small_array.h index 3e73978a..2004f5f1 100644 --- a/include/cpp-sort/detail/schwartz_small_array.h +++ b/include/cpp-sort/detail/schwartz_small_array.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_ADAPTERS_SCHWARTZ_SMALL_ARRAY_H_ @@ -15,7 +15,7 @@ #include #include #include "../detail/any_all.h" -#include "../detail/is_in_pack.h" +#include "../detail/type_traits.h" namespace cppsort { diff --git a/include/cpp-sort/detail/type_traits.h b/include/cpp-sort/detail/type_traits.h index c9ceadab..ada16a4a 100644 --- a/include/cpp-sort/detail/type_traits.h +++ b/include/cpp-sort/detail/type_traits.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_TYPE_TRAITS_H_ @@ -291,6 +291,34 @@ namespace detail template using is_unsigned = std::is_unsigned; #endif + + //////////////////////////////////////////////////////////// + // is_in_pack: check whether a given std::size_t value + // appears in a std::size_t... parameter pack + + template + struct is_in_pack_impl; + + template< + std::size_t Value, + std::size_t Head, + std::size_t... Tail + > + struct is_in_pack_impl: + conditional_t< + Value == Head, + std::true_type, + is_in_pack_impl + > + {}; + + template + struct is_in_pack_impl: + std::false_type + {}; + + template + constexpr bool is_in_pack = is_in_pack_impl::value; }} #endif // CPPSORT_DETAIL_TYPE_TRAITS_H_ From e8634efcda4c52fd40e6c395bab475aaf4d583a8 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 27 Apr 2021 17:01:02 +0200 Subject: [PATCH 22/56] Reduce template instantiations in is_in_pack --- include/cpp-sort/detail/type_traits.h | 35 ++++++++++++--------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/include/cpp-sort/detail/type_traits.h b/include/cpp-sort/detail/type_traits.h index ada16a4a..45ef41f6 100644 --- a/include/cpp-sort/detail/type_traits.h +++ b/include/cpp-sort/detail/type_traits.h @@ -296,29 +296,24 @@ namespace detail // is_in_pack: check whether a given std::size_t value // appears in a std::size_t... parameter pack - template - struct is_in_pack_impl; + template + constexpr auto is_in_pack_impl(std::size_t value) noexcept + -> bool + { + std::size_t arr[] = { Values... }; + for (std::size_t val: arr) { + if (val == value) { + return true; + } + } + return false; + } - template< - std::size_t Value, - std::size_t Head, - std::size_t... Tail - > - struct is_in_pack_impl: - conditional_t< - Value == Head, - std::true_type, - is_in_pack_impl - > - {}; + template + constexpr bool is_in_pack = is_in_pack_impl(Value); template - struct is_in_pack_impl: - std::false_type - {}; - - template - constexpr bool is_in_pack = is_in_pack_impl::value; + constexpr bool is_in_pack = false; }} #endif // CPPSORT_DETAIL_TYPE_TRAITS_H_ From 25431c687294990e1fdc773c9bb2f7b846714ceb Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 5 May 2021 19:51:50 +0200 Subject: [PATCH 23/56] Tiny grailsort tweaks --- include/cpp-sort/detail/grail_sort.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/cpp-sort/detail/grail_sort.h b/include/cpp-sort/detail/grail_sort.h index 36219736..3c00a30d 100644 --- a/include/cpp-sort/detail/grail_sort.h +++ b/include/cpp-sort/detail/grail_sort.h @@ -502,9 +502,7 @@ namespace grail if (rest > h) { merge_left_with_extra_buffer(p0, p0+h, last, p0-h, compare, projection); } else { - for (; p0 < last ; ++p0) { - p0[-h] = iter_move(p0); - } + detail::move(p0, last, p0 - h); } first -= h; last -= h; @@ -685,7 +683,7 @@ namespace grail return; } - difference_type lblock = 1; + difference_type lblock = 4; while (lblock * lblock < size) { lblock *= 2; } From f177a774a5ed9b2c6f73ae5d3b9893bf092f3ac9 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 6 May 2021 12:39:04 +0200 Subject: [PATCH 24/56] Make iter_move and iter_swap hidden friends --- include/cpp-sort/detail/associate_iterator.h | 54 ++++++++++--------- .../cpp-sort/detail/merge_insertion_sort.h | 16 +++--- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/include/cpp-sort/detail/associate_iterator.h b/include/cpp-sort/detail/associate_iterator.h index 1460a2c0..5e5356c6 100644 --- a/include/cpp-sort/detail/associate_iterator.h +++ b/include/cpp-sort/detail/associate_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_ASSOCIATE_ITERATOR_H_ @@ -12,7 +12,6 @@ #include #include #include "iterator_traits.h" -#include "type_traits.h" namespace cppsort { @@ -41,6 +40,10 @@ namespace detail template struct association { + // Public types + using iterator_type = Iterator; + using data_type = Data; + // Public members Iterator it; Data data; @@ -68,7 +71,7 @@ namespace detail return *this; } - auto operator=(associated_value, Data>&& other) + auto operator=(associated_value, Data>&& other) -> association& { *it = std::move(other.value); @@ -155,7 +158,7 @@ namespace detail //////////////////////////////////////////////////////////// // Public types - using iterator_category = iterator_category_t; + using iterator_category = std::random_access_iterator_tag; using iterator_type = Iterator; using value_type = value_type_t; using difference_type = difference_type_t; @@ -326,32 +329,33 @@ namespace detail return lhs.base() - rhs.base(); } + //////////////////////////////////////////////////////////// + // iter_move/iter_swap + + friend auto iter_swap(associate_iterator lhs, associate_iterator rhs) + -> void + { + using utility::iter_swap; + iter_swap(lhs.base(), rhs.base()); + } + + friend auto iter_move(associate_iterator it) + -> associated_value< + value_type_t::iterator_type>, + typename value_type_t::data_type + > + { + return { + std::move(*(it->it)), + std::move(it->data) + }; + } + private: Iterator _it; }; - template - auto iter_swap(associate_iterator lhs, associate_iterator rhs) - -> void - { - using utility::iter_swap; - iter_swap(lhs.base(), rhs.base()); - } - - template - auto iter_move(associate_iterator it) - -> associated_value< - remove_cvref_tit))>, - remove_cvref_tdata)> - > - { - return { - std::move(*(it->it)), - std::move(it->data) - }; - } - //////////////////////////////////////////////////////////// // Construction function diff --git a/include/cpp-sort/detail/merge_insertion_sort.h b/include/cpp-sort/detail/merge_insertion_sort.h index 7aa321e4..36ae3a98 100644 --- a/include/cpp-sort/detail/merge_insertion_sort.h +++ b/include/cpp-sort/detail/merge_insertion_sort.h @@ -221,19 +221,21 @@ namespace detail return (lhs.base() - rhs.base()) / lhs.size(); } + //////////////////////////////////////////////////////////// + // iter_swap + + friend auto iter_swap(group_iterator lhs, group_iterator rhs) + -> void + { + detail::swap_ranges_inner(lhs.base(), lhs.base() + lhs.size(), rhs.base()); + } + private: Iterator _it; difference_type _size; }; - template - auto iter_swap(group_iterator lhs, group_iterator rhs) - -> void - { - detail::swap_ranges_inner(lhs.base(), lhs.base() + lhs.size(), rhs.base()); - } - //////////////////////////////////////////////////////////// // Construction function From b35ee98406fe7f59a6862f2ec74a04927afd4f6e Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 7 May 2021 20:06:54 +0200 Subject: [PATCH 25/56] Fix ADL issue in destroy_n --- include/cpp-sort/detail/memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpp-sort/detail/memory.h b/include/cpp-sort/detail/memory.h index cce8ee81..08ad8b32 100644 --- a/include/cpp-sort/detail/memory.h +++ b/include/cpp-sort/detail/memory.h @@ -54,7 +54,7 @@ namespace detail -> void { for (; n > 0; --n) { - destroy_at(std::addressof(*first)); + detail::destroy_at(std::addressof(*first)); ++first; } } From a2ccbbf2e351c0a83af835a05c3e762dadb57344 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 15 May 2021 20:35:24 +0200 Subject: [PATCH 26/56] Add [[nodiscard]] to relevant sized_iterator operations Changed inspired by P2377 --- include/cpp-sort/detail/attributes.h | 14 +++++++++++++- include/cpp-sort/detail/sized_iterator.h | 11 ++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/cpp-sort/detail/attributes.h b/include/cpp-sort/detail/attributes.h index b7382747..fc83c2be 100644 --- a/include/cpp-sort/detail/attributes.h +++ b/include/cpp-sort/detail/attributes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 Morwenn + * Copyright (c) 2018-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_ATTRIBUTES_H_ @@ -35,4 +35,16 @@ # define CPPSORT_ATTRIBUTE_FALLTHROUGH (void)0 #endif +// CPPSORT_ATTRIBUTE_NODISCARD + +#if __has_cpp_attribute(nodiscard) && !(defined(__clang__) && __cplusplus < 201703L) +# define CPPSORT_ATTRIBUTE_NODISCARD [[nodiscard]] +#elif defined(__clang__) +# define CPPSORT_ATTRIBUTE_NODISCARD [[clang::warn_unused_result]] +#elif defined(__GNUC__) +# define CPPSORT_ATTRIBUTE_NODISCARD [[gnu::warn_unused_result]] +#else +# define CPPSORT_ATTRIBUTE_NODISCARD +#endif + #endif // CPPSORT_DETAIL_ATTRIBUTES_H_ diff --git a/include/cpp-sort/detail/sized_iterator.h b/include/cpp-sort/detail/sized_iterator.h index d65b1fad..900a21af 100644 --- a/include/cpp-sort/detail/sized_iterator.h +++ b/include/cpp-sort/detail/sized_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Morwenn + * Copyright (c) 2020-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SIZED_ITERATOR_H_ @@ -8,6 +8,9 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include +#include "attributes.h" +#include "iterator_traits.h" namespace cppsort { @@ -52,12 +55,14 @@ namespace detail //////////////////////////////////////////////////////////// // Members access + CPPSORT_ATTRIBUTE_NODISCARD auto base() const -> iterator_type { return _it; } + CPPSORT_ATTRIBUTE_NODISCARD auto size() const -> difference_type { @@ -67,12 +72,14 @@ namespace detail //////////////////////////////////////////////////////////// // Element access + CPPSORT_ATTRIBUTE_NODISCARD auto operator*() const -> reference { return *_it; } + CPPSORT_ATTRIBUTE_NODISCARD auto operator->() const -> pointer { @@ -88,6 +95,7 @@ namespace detail // Alternative to std::distance meant to be picked up by ADL in // specific places, uses the size of the *second* iterator template + CPPSORT_ATTRIBUTE_NODISCARD constexpr auto distance(sized_iterator, sized_iterator last) -> difference_type_t { @@ -95,6 +103,7 @@ namespace detail } template + CPPSORT_ATTRIBUTE_NODISCARD auto make_sized_iterator(Iterator it, difference_type_t size) -> sized_iterator { From fe80545a3798b6753fe74f3b5f37976e4ba6b951 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 15 May 2021 20:58:46 +0200 Subject: [PATCH 27/56] GitHub Actions: fix Valgrind builds --- .github/workflows/build-ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index d9058c02..e4b0cfef 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -47,7 +47,7 @@ jobs: - name: Install Valgrind if: ${{matrix.config.valgrind == 'ON'}} - run: sudo apt install -y valgrind + run: sudo apt-get update && sudo apt-get install -y valgrind - name: Configure CMake working-directory: ${{runner.workspace}} From b90d1e987ea639c93ea1a88a092182410fc71616 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 15 May 2021 21:42:55 +0200 Subject: [PATCH 28/56] Work around a false positive warning with Clang --- include/cpp-sort/detail/container_aware/mel_sort.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/cpp-sort/detail/container_aware/mel_sort.h b/include/cpp-sort/detail/container_aware/mel_sort.h index 724b4850..71b02cb9 100644 --- a/include/cpp-sort/detail/container_aware/mel_sort.h +++ b/include/cpp-sort/detail/container_aware/mel_sort.h @@ -114,15 +114,13 @@ namespace cppsort // to implement melsort for forward iterators struct flist { - using iterator = typename std::forward_list::iterator; - flist(): list(), last(list.begin()) {} std::forward_list list; - iterator last; + typename std::forward_list::iterator last; }; // Encroaching lists From e7d06a148db53ffe7a2746e21b0dbc4903b66ee8 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 16 May 2021 12:08:13 +0200 Subject: [PATCH 29/56] CI: manually install Clang 6 on Ubuntu --- .github/workflows/build-ubuntu.yml | 6 +++++- README.md | 6 ++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index e4b0cfef..2f8223d2 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -30,7 +30,7 @@ jobs: matrix: cxx: - g++-5 - - clang++-9 + - clang++-6.0 config: # Release build - build_type: Release @@ -45,6 +45,10 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Install Clang + if: ${{matrix.cxx == 'clang++-6.0'}} + run: sudo apt-get install -y clang-6.0 lld-6.0 + - name: Install Valgrind if: ${{matrix.config.valgrind == 'ON'}} run: sudo apt-get update && sudo apt-get install -y valgrind diff --git a/README.md b/README.md index 74ee89c6..d1c6709e 100644 --- a/README.md +++ b/README.md @@ -113,11 +113,9 @@ wiki page](https://github.com/Morwenn/cpp-sort/wiki/Benchmarks). ![Windows builds status](https://github.com/Morwenn/cpp-sort/workflows/Windows%20Builds/badge.svg?branch=develop) ![MacOS builds status](https://github.com/Morwenn/cpp-sort/workflows/MacOS%20Builds/badge.svg?branch=develop) -**cpp-sort** currently requires C++14 support, and only works with g++5 and clang++3.8 -or more recent versions of these compilers. So far, the library should work with the -following compilers: +**cpp-sort** requires C++14 support, and should work with the following compilers: * g++5.5 or more recent. It is known not to work with some older g++5 versions. -* clang++9 or more recent. It should work with clang++ versions all the way back to 3.8, but the CI pipeline doesn't have test for those anymore. +* clang++6.0 or more recent. It should work with clang++ versions all the way back to 3.8, but the CI pipeline doesn't have test for those anymore. * Visual Studio 2019 version 16.8.3 or more recent, only with `/permissive-`. A few features are still unavailable. * The versions of MinGW-w64 and AppleClang equivalent to the compilers mentioned above. * Clang is notably tested with both libstdc++ and libc++. From 5fd543236e627470c87e769f042d57e4843931b9 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 16 May 2021 12:53:23 +0200 Subject: [PATCH 30/56] [[nodiscard]] for associate_iterator and friends --- include/cpp-sort/detail/associate_iterator.h | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/cpp-sort/detail/associate_iterator.h b/include/cpp-sort/detail/associate_iterator.h index 5e5356c6..23878564 100644 --- a/include/cpp-sort/detail/associate_iterator.h +++ b/include/cpp-sort/detail/associate_iterator.h @@ -11,6 +11,7 @@ #include #include #include +#include "attributes.h" #include "iterator_traits.h" namespace cppsort @@ -79,12 +80,14 @@ namespace detail return *this; } + CPPSORT_ATTRIBUTE_NODISCARD auto get() -> decltype(*it) { return *it; } + CPPSORT_ATTRIBUTE_NODISCARD auto get() const -> decltype(*it) { @@ -131,12 +134,14 @@ namespace detail return *this; } + CPPSORT_ATTRIBUTE_NODISCARD auto get() -> Value& { return value; } + CPPSORT_ATTRIBUTE_NODISCARD auto get() const -> const Value& { @@ -177,6 +182,7 @@ namespace detail //////////////////////////////////////////////////////////// // Members access + CPPSORT_ATTRIBUTE_NODISCARD auto base() const -> iterator_type { @@ -186,12 +192,14 @@ namespace detail //////////////////////////////////////////////////////////// // Element access + CPPSORT_ATTRIBUTE_NODISCARD auto operator*() const -> decltype(*base()) { return *base(); } + CPPSORT_ATTRIBUTE_NODISCARD auto operator->() const -> pointer { @@ -248,12 +256,14 @@ namespace detail //////////////////////////////////////////////////////////// // Elements access operators + CPPSORT_ATTRIBUTE_NODISCARD auto operator[](difference_type pos) -> decltype(base()[pos]) { return base()[pos]; } + CPPSORT_ATTRIBUTE_NODISCARD auto operator[](difference_type pos) const -> decltype(base()[pos]) { @@ -263,12 +273,14 @@ namespace detail //////////////////////////////////////////////////////////// // Comparison operators + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator==(const associate_iterator& lhs, const associate_iterator& rhs) -> bool { return lhs.base() == rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator!=(const associate_iterator& lhs, const associate_iterator& rhs) -> bool { @@ -278,24 +290,28 @@ namespace detail //////////////////////////////////////////////////////////// // Relational operators + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator<(const associate_iterator& lhs, const associate_iterator& rhs) -> bool { return lhs.base() < rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator<=(const associate_iterator& lhs, const associate_iterator& rhs) -> bool { return lhs.base() <= rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator>(const associate_iterator& lhs, const associate_iterator& rhs) -> bool { return lhs.base() > rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator>=(const associate_iterator& lhs, const associate_iterator& rhs) -> bool { @@ -305,24 +321,28 @@ namespace detail //////////////////////////////////////////////////////////// // Arithmetic operators + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator+(associate_iterator it, difference_type size) -> associate_iterator { return it += size; } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator+(difference_type size, associate_iterator it) -> associate_iterator { return it += size; } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator-(associate_iterator it, difference_type size) -> associate_iterator { return it -= size; } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator-(const associate_iterator& lhs, const associate_iterator& rhs) -> difference_type { @@ -339,6 +359,7 @@ namespace detail iter_swap(lhs.base(), rhs.base()); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto iter_move(associate_iterator it) -> associated_value< value_type_t::iterator_type>, @@ -360,6 +381,7 @@ namespace detail // Construction function template + CPPSORT_ATTRIBUTE_NODISCARD auto make_associate_iterator(Iterator it) -> associate_iterator { From 96889efd043cb174c2f27a34883aa8c5548bac96 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 16 May 2021 23:56:32 +0200 Subject: [PATCH 31/56] Add note to check CI warnings before release --- tools/release-checklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/release-checklist.md b/tools/release-checklist.md index 94e70cd1..59446125 100644 --- a/tools/release-checklist.md +++ b/tools/release-checklist.md @@ -10,6 +10,7 @@ development phase: ### Before the release +- [ ] Check that there aren't warnings left in the latest CI logs. - [ ] Check that all issues linked to the milestone are closed. - [ ] Check `NOTICE.txt` and `README.md` conformance for stolen code. - [ ] Make sure that tests pass and examples build. From 2e47b7c5e3b9aa9875a3e0e93d5d4b7324d7a1c9 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 18 May 2021 10:23:01 +0200 Subject: [PATCH 32/56] [[nodiscard]] for fixed_size_list --- include/cpp-sort/detail/fixed_size_list.h | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/include/cpp-sort/detail/fixed_size_list.h b/include/cpp-sort/detail/fixed_size_list.h index 16cacbd9..2b1011af 100644 --- a/include/cpp-sort/detail/fixed_size_list.h +++ b/include/cpp-sort/detail/fixed_size_list.h @@ -13,6 +13,7 @@ #include #include #include +#include "attributes.h" #include "config.h" #include "memory.h" @@ -176,6 +177,7 @@ namespace detail //////////////////////////////////////////////////////////// // Node providing/retrieval + CPPSORT_ATTRIBUTE_NODISCARD auto next_free_node() -> node_type* { @@ -281,6 +283,7 @@ namespace detail //////////////////////////////////////////////////////////// // Members access + CPPSORT_ATTRIBUTE_NODISCARD constexpr auto base() const -> node_type* { @@ -290,12 +293,14 @@ namespace detail //////////////////////////////////////////////////////////// // Element access + CPPSORT_ATTRIBUTE_NODISCARD auto operator*() const -> reference { return static_cast(ptr_)->value; } + CPPSORT_ATTRIBUTE_NODISCARD auto operator->() const -> pointer { @@ -338,15 +343,15 @@ namespace detail //////////////////////////////////////////////////////////// // Comparison operators - constexpr - friend auto operator==(const fixed_size_list_iterator& lhs, const fixed_size_list_iterator& rhs) + CPPSORT_ATTRIBUTE_NODISCARD + friend constexpr auto operator==(const fixed_size_list_iterator& lhs, const fixed_size_list_iterator& rhs) -> bool { return lhs.base() == rhs.base(); } - constexpr - friend auto operator!=(const fixed_size_list_iterator& lhs, const fixed_size_list_iterator& rhs) + CPPSORT_ATTRIBUTE_NODISCARD + friend constexpr auto operator!=(const fixed_size_list_iterator& lhs, const fixed_size_list_iterator& rhs) -> bool { return lhs.base() != rhs.base(); @@ -484,18 +489,21 @@ namespace detail //////////////////////////////////////////////////////////// // Element access + CPPSORT_ATTRIBUTE_NODISCARD auto front() -> reference { return static_cast(sentinel_node_.next)->value; } + CPPSORT_ATTRIBUTE_NODISCARD auto back() -> reference { return static_cast(sentinel_node_.prev)->value; } + CPPSORT_ATTRIBUTE_NODISCARD auto node_pool() -> fixed_size_list_node_pool& { @@ -505,12 +513,14 @@ namespace detail //////////////////////////////////////////////////////////// // Iterators + CPPSORT_ATTRIBUTE_NODISCARD auto begin() -> iterator { return iterator(sentinel_node_.next); } + CPPSORT_ATTRIBUTE_NODISCARD auto end() -> iterator { @@ -520,6 +530,7 @@ namespace detail //////////////////////////////////////////////////////////// // Capacity + CPPSORT_ATTRIBUTE_NODISCARD auto is_empty() const noexcept -> bool { @@ -579,6 +590,7 @@ namespace detail insert_node_(sentinel_node_.next, std::forward(setter)); } + CPPSORT_ATTRIBUTE_NODISCARD auto extract(list_node_base* node) -> node_type* { @@ -589,18 +601,21 @@ namespace detail return static_cast(node); } + CPPSORT_ATTRIBUTE_NODISCARD auto extract(iterator pos) -> node_type* { return extract(pos.base()); } + CPPSORT_ATTRIBUTE_NODISCARD auto extract_back() -> node_type* { return extract(sentinel_node_.prev); } + CPPSORT_ATTRIBUTE_NODISCARD auto extract_front() -> node_type* { From f8c70434d65ba70bb125262b707dca4b9ae174d8 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 18 May 2021 11:17:39 +0200 Subject: [PATCH 33/56] [[nodiscard]] for merge_insertion_sort --- .../cpp-sort/detail/merge_insertion_sort.h | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/include/cpp-sort/detail/merge_insertion_sort.h b/include/cpp-sort/detail/merge_insertion_sort.h index 36ae3a98..e2a28c52 100644 --- a/include/cpp-sort/detail/merge_insertion_sort.h +++ b/include/cpp-sort/detail/merge_insertion_sort.h @@ -15,6 +15,7 @@ #include #include #include +#include "attributes.h" #include "config.h" #include "fixed_size_list.h" #include "functional.h" @@ -62,12 +63,14 @@ namespace detail //////////////////////////////////////////////////////////// // Members access + CPPSORT_ATTRIBUTE_NODISCARD auto base() const -> iterator_type { return _it; } + CPPSORT_ATTRIBUTE_NODISCARD auto size() const -> difference_type { @@ -77,13 +80,14 @@ namespace detail //////////////////////////////////////////////////////////// // Element access - + CPPSORT_ATTRIBUTE_NODISCARD auto operator*() const -> reference { return _it[_size - 1]; } + CPPSORT_ATTRIBUTE_NODISCARD auto operator->() const -> pointer { @@ -140,12 +144,14 @@ namespace detail //////////////////////////////////////////////////////////// // Elements access operators + CPPSORT_ATTRIBUTE_NODISCARD auto operator[](difference_type pos) -> decltype(base()[pos * size() + size() - 1]) { return base()[pos * size() + size() - 1]; } + CPPSORT_ATTRIBUTE_NODISCARD auto operator[](difference_type pos) const -> decltype(base()[pos * size() + size() - 1]) { @@ -155,12 +161,14 @@ namespace detail //////////////////////////////////////////////////////////// // Comparison operators + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator==(const group_iterator& lhs, const group_iterator& rhs) -> bool { return lhs.base() == rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator!=(const group_iterator& lhs, const group_iterator& rhs) -> bool { @@ -170,24 +178,28 @@ namespace detail //////////////////////////////////////////////////////////// // Relational operators + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator<(const group_iterator& lhs, const group_iterator& rhs) -> bool { return lhs.base() < rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator<=(const group_iterator& lhs, const group_iterator& rhs) -> bool { return lhs.base() <= rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator>(const group_iterator& lhs, const group_iterator& rhs) -> bool { return lhs.base() > rhs.base(); } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator>=(const group_iterator& lhs, const group_iterator& rhs) -> bool { @@ -197,24 +209,28 @@ namespace detail //////////////////////////////////////////////////////////// // Arithmetic operators + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator+(group_iterator it, difference_type size) -> group_iterator { return it += size; } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator+(difference_type size, group_iterator it) -> group_iterator { return it += size; } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator-(group_iterator it, difference_type size) -> group_iterator { return it -= size; } + CPPSORT_ATTRIBUTE_NODISCARD friend auto operator-(const group_iterator& lhs, const group_iterator& rhs) -> difference_type { @@ -240,6 +256,7 @@ namespace detail // Construction function template + CPPSORT_ATTRIBUTE_NODISCARD auto make_group_iterator(Iterator it, difference_type_t> size) -> group_iterator { @@ -247,6 +264,7 @@ namespace detail } template + CPPSORT_ATTRIBUTE_NODISCARD auto make_group_iterator(group_iterator it, difference_type_t> size) -> group_iterator { From ccaf3ac0c79921b7fca686891acb3e05560d7940 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 18 May 2021 17:11:15 +0200 Subject: [PATCH 34/56] Introduce wiki_sort, deprecate block_sort WikiSort is just *a* block sort and we already have grail_sort in the library which is another block sort which works differently. Therefore reserving the name block_sort to wiki feels a bit misleading, especially with the growing body of block sorts that appear nowadays. --- README.md | 4 +- docs/Library-nomenclature.md | 2 +- docs/Sorters.md | 45 ++++++---- docs/Writing-a-sorter.md | 14 ++-- .../detail/{block_sort.h => wiki_sort.h} | 10 +-- include/cpp-sort/fwd.h | 2 + include/cpp-sort/sorters.h | 1 + include/cpp-sort/sorters/block_sorter.h | 10 ++- include/cpp-sort/sorters/wiki_sorter.h | 82 +++++++++++++++++++ .../indirect_adapter_every_sorter.cpp | 4 +- .../schwartz_adapter_every_sorter.cpp | 4 +- ...schwartz_adapter_every_sorter_reversed.cpp | 4 +- .../adapters/stable_adapter_every_sorter.cpp | 4 +- .../adapters/verge_adapter_every_sorter.cpp | 4 +- testsuite/distributions/all_equal.cpp | 10 +-- testsuite/distributions/alternating.cpp | 10 +-- testsuite/distributions/ascending.cpp | 10 +-- .../distributions/ascending_sawtooth.cpp | 10 +-- testsuite/distributions/descending.cpp | 10 +-- .../distributions/descending_sawtooth.cpp | 10 +-- .../distributions/median_of_3_killer.cpp | 10 +-- testsuite/distributions/pipe_organ.cpp | 10 +-- testsuite/distributions/push_front.cpp | 10 +-- testsuite/distributions/push_middle.cpp | 10 +-- testsuite/distributions/shuffled.cpp | 10 +-- .../distributions/shuffled_16_values.cpp | 10 +-- testsuite/every_instantiated_sorter.cpp | 12 +-- testsuite/every_sorter.cpp | 10 +-- testsuite/every_sorter_internal_compare.cpp | 4 +- testsuite/every_sorter_long_string.cpp | 10 +-- .../every_sorter_move_compare_projection.cpp | 20 ++--- testsuite/every_sorter_move_only.cpp | 4 +- testsuite/every_sorter_no_post_iterator.cpp | 4 +- testsuite/every_sorter_non_const_compare.cpp | 4 +- testsuite/every_sorter_rvalue_projection.cpp | 4 +- testsuite/every_sorter_span.cpp | 10 +-- testsuite/every_sorter_throwing_moves.cpp | 10 +-- testsuite/heap_memory_exhaustion.cpp | 6 +- tools/count-compare-copies.cpp | 6 +- 39 files changed, 259 insertions(+), 155 deletions(-) rename include/cpp-sort/detail/{block_sort.h => wiki_sort.h} (99%) create mode 100644 include/cpp-sort/sorters/wiki_sorter.h diff --git a/README.md b/README.md index d1c6709e..e48e1c0f 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,11 @@ when there isn't enough memory available to perform an out-of-place merge. directly adapted from [Keith Schwarz's implementation](http://www.keithschwarz.com/interesting/code/?dir=smoothsort) of the algorithm. -* The algorithm used by `block_sorter` has been adapted from BonzaiThePenguin's +* The algorithm used by `wiki_sorter` has been adapted from BonzaiThePenguin's [WikiSort](https://github.com/BonzaiThePenguin/WikiSort). * The algorithm used by `grail_sorter` has been adapted from Mrrl's -[GrailSort](https://github.com/Mrrl/GrailSort), hence the name. +[GrailSort](https://github.com/Mrrl/GrailSort). * The algorithm used by `indirect_adapter` with forward or bidirectional iterators is a slightly modified version of Matthew Bentley's [indiesort](https://github.com/mattreecebentley/plf_indiesort). diff --git a/docs/Library-nomenclature.md b/docs/Library-nomenclature.md index f81f53ee..1e498af9 100644 --- a/docs/Library-nomenclature.md +++ b/docs/Library-nomenclature.md @@ -2,7 +2,7 @@ * *Buffered sorter*: some sorting algorithms optionally use a buffer where they store elements to improve the performance of the sort. Some of them, such as block sort, will manage to sort the collection regardless of the actual size of the buffer, which will only have on influence on the performance of the sort. A buffered sorter is a sorter that takes a *buffer provider* template parameter that tells how the temporary buffer should be allocated, and uses this provider to create the buffer. A *buffer provider* is a class that has a nested `buffer` class which implements a set of basic operations (construction with a size, `begin`, `end` and `size`). Implementing a buffer provider is a bit tricky, but using them should be easy enough: - using sorter = cppsort::block_sorter< + using sorter = cppsort::grail_sorter< cppsort::utility::fixed_buffer<512> >; diff --git a/docs/Sorters.md b/docs/Sorters.md index a2233c34..0bd73dcd 100644 --- a/docs/Sorters.md +++ b/docs/Sorters.md @@ -20,22 +20,7 @@ The following sorters are available and will work with any type for which `std:: #include ``` -Implements a [block sort](https://en.wikipedia.org/wiki/Block_sort). - -| Best | Average | Worst | Memory | Stable | Iterators | -| ----------- | ----------- | ----------- | ----------- | ----------- | ------------- | -| n | n log n | n log n | 1 | Yes | Random-access | - -`block_sorter` is a *buffered sorter* whose default specialization allocates a fixed buffer of 512 elements to achieve O(1) auxiliary memory. This memory complexity may change depending of the *buffer provider* passed to it. - -```cpp -template< - typename BufferProvider = utility::fixed_buffer<512> -> -struct block_sorter; -``` - -Whether this sorter works with types that are not default-constructible depends on the memory allocation strategy of the buffer provider. The default specialization does not work with such types. +***WARNING:** `block_sorter` is deprecated in version 1.11.0 and removed in version 2.0.0, use [`wiki_sorter`][wiki-sorter] instead.* ### `cartesian_tree_sorter` @@ -455,6 +440,31 @@ When wrapped into [`stable_adapter`][stable-adapter], it has a slightly differen *New in version 1.9.0:* explicit specialization for `stable_adapter`. +### `wiki_sorter<>` + +```cpp +#include +``` + +Implements [WikiSort][wiki-sort], a kind of [block sort][block-sort]. + +| Best | Average | Worst | Memory | Stable | Iterators | +| ----------- | ----------- | ----------- | ----------- | ----------- | ------------- | +| n | n log n | n log n | 1 | Yes | Random-access | + +`wiki_sorter` is a *buffered sorter* whose default specialization allocates a fixed buffer of 512 elements to achieve O(1) auxiliary memory. This memory complexity may change depending of the *buffer provider* passed to it. + +```cpp +template< + typename BufferProvider = utility::fixed_buffer<512> +> +struct wiki_sorter; +``` + +Whether this sorter works with types that are not default-constructible depends on the memory allocation strategy of the *buffer provider*. The default specialization does not work with such types. + +*New in version 1.11.0* + ## Type-specific sorters The following sorters are available but will only work for some specific types instead of using a user-provided comparison function. Some of them also accept projections as long as the result of the projection can be handled by the sorter. @@ -536,8 +546,11 @@ struct spread_sorter: [adaptive-quickselect]: https://arxiv.org/abs/1606.00484 + [block-sort]: https://en.wikipedia.org/wiki/Block_sort [cartesian-tree-sort]: https://en.wikipedia.org/wiki/Cartesian_tree#Application_in_sorting [container-aware-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#container_aware_adapter [introselect]: https://en.wikipedia.org/wiki/Introselect [quick-mergesort]: https://arxiv.org/abs/1307.3033 [selection-algorithm]: https://en.wikipedia.org/wiki/Selection_algorithm + [wiki-sort]: https://github.com/BonzaiThePenguin/WikiSort + [wiki-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#wiki_sorter diff --git a/docs/Writing-a-sorter.md b/docs/Writing-a-sorter.md index 53d0aa91..d788ee06 100644 --- a/docs/Writing-a-sorter.md +++ b/docs/Writing-a-sorter.md @@ -420,21 +420,21 @@ There are no specific rules to differentiate the way *stateful* and *stateless s ## Buffered sorters -Some sorting algorithms can take advantage of buffers of any size to improve their performance, generally to perform merge operations. However, depending on many parameters not known to a sorter's implementer, the best method used to get a buffer of reasonable size might not always be the same. To avoid this problem (and to avoid the duplication of similar algorithms), **cpp-sort** provides some tools to specify the way a buffer is allocated. Let's take a simple example: [SqrtSort](https://github.com/Mrrl/SqrtSort) is apparently a [GrailSort](https://github.com/Mrrl/GrailSort) whose exchange buffer has a size roughly equal to the square root of the size of the collection to sort. While these algorithms are presented as separate algorithms, one can simply pass a *buffer provider* template parameter to a `grail_sorter` and use it to define define `sqrt_sorter`: +Some sorting algorithms can take advantage of buffers of any size to improve their performance, generally to perform merge operations. However, depending on many parameters not known to a sorter's implementer, the best method used to get a buffer of reasonable size might not always be the same. To avoid this problem (and to avoid the duplication of similar algorithms), **cpp-sort** provides some tools to specify the way a buffer is allocated. Let's take a simple example: [SqrtSort](https://github.com/Mrrl/SqrtSort) is apparently a [GrailSort](https://github.com/Mrrl/GrailSort) whose exchange buffer has a size roughly equal to the square root of the size of the collection to sort. While these algorithms are presented as separate algorithms, one can simply pass a *buffer provider* template parameter to a [`grail_sorter`][grail-sorter] and use it to define define `sqrt_sorter`: ```cpp using sqrt_sorter = grail_sorter>; ``` -In this example, `dynamic_buffer` is one of the [*buffer providers*](https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#buffer-providers) that come with the library. A *buffer provider* is a class that describes how the buffer should be allocated; it has a nested class template `buffer` that actually contains the allocated memory. While `sqrt_sorter` is a regular sorter, `grail_sorter` is a *buffered sorter*. +In this example, `dynamic_buffer` is one of the [*buffer providers*](https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#buffer-providers) that come with the library. A *buffer provider* is a class that describes how the buffer should be allocated; it has a nested class template `buffer` that actually contains the allocated memory. While `sqrt_sorter` is a regular sorter, [`grail_sorter`][grail-sorter] is a *buffered sorter*. -The library's `dynamic_buffer` takes what is called a *size policy*: this is a class with an overloaded `operator()` taking an instance of `std::size_t` and returning an instance of a type convertible to `std::size_t`. The library provides [some function objects](https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#miscellaneous-function-objects) that can be used as size policies. It also provides a `fixed_buffer` which takes an `std::size_t` template parameter corresponding to the number of elements to allocate on the stack for the buffer. For example, [`block_sorter`](https://github.com/Morwenn/cpp-sort/wiki/Sorters#block_sorter) uses a fixed-size buffer of 512 elements unless told otherwise: +The library's `dynamic_buffer` takes what is called a *size policy*: this is a class with an overloaded `operator()` taking an instance of `std::size_t` and returning an instance of a type convertible to `std::size_t`. The library provides [some function objects](https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#miscellaneous-function-objects) that can be used as size policies. It also provides a `fixed_buffer` which takes an `std::size_t` template parameter corresponding to the number of elements to allocate on the stack for the buffer. For example, [`wiki_sorter`][wiki-sorter] uses a fixed-size buffer of 512 elements unless told otherwise: ```cpp template< typename BufferProvider = utility::fixed_buffer<512> > -struct block_sorter; +struct wiki_sorter; ``` **Rule 7.1:** a *buffer provider* is a class that has a nested class template named `buffer`. The nested class template shall be explicitly constructible from an instance of `std::size_t` and shall at least implement the methods `begin`, `end`, `data` and `size`, with semantics similar to those of the random-access containers in the standard library. @@ -551,4 +551,8 @@ In the example above, the resulting sorter will use our `low_projections_sorter` **Rule 8.2:** any valid specialization of a *fixed-size sorter* for a given size is a *sorter*, and shall therefore obey all the rules defined for sorters provided the collection to sort has the correct size. It can also be a *comparison sorter* and/or a *projection sorter*, in which case it shall obey the specific rules for these kinds of sorters too. -**Rule 8.3:** a *fixed-size sorter* shall specialize the class `cppsort::fixed_sorter_traits` if it needs to provide information about its domain, its iterator category or its stability. See the exact meaning of these types and how to define them in the dedicated part of the documentation. \ No newline at end of file +**Rule 8.3:** a *fixed-size sorter* shall specialize the class `cppsort::fixed_sorter_traits` if it needs to provide information about its domain, its iterator category or its stability. See the exact meaning of these types and how to define them in the dedicated part of the documentation. + + + [grail-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#grail_sorter + [wiki-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#wiki_sorter diff --git a/include/cpp-sort/detail/block_sort.h b/include/cpp-sort/detail/wiki_sort.h similarity index 99% rename from include/cpp-sort/detail/block_sort.h rename to include/cpp-sort/detail/wiki_sort.h index fe4a81b6..fc3e5cfb 100644 --- a/include/cpp-sort/detail/block_sort.h +++ b/include/cpp-sort/detail/wiki_sort.h @@ -7,8 +7,8 @@ * WikiSort: a public domain implementation of "Block Sort" * https://github.com/BonzaiThePenguin/WikiSort */ -#ifndef CPPSORT_DETAIL_BLOCK_SORT_H_ -#define CPPSORT_DETAIL_BLOCK_SORT_H_ +#ifndef CPPSORT_DETAIL_WIKI_SORT_H_ +#define CPPSORT_DETAIL_WIKI_SORT_H_ //////////////////////////////////////////////////////////// // Headers @@ -923,8 +923,8 @@ namespace detail template - auto block_sort(RandomAccessIterator first, RandomAccessIterator last, - Compare compare, Projection projection) + auto wiki_sort(RandomAccessIterator first, RandomAccessIterator last, + Compare compare, Projection projection) -> void { Wiki::sort(std::move(first), std::move(last), @@ -932,4 +932,4 @@ namespace detail } }} -#endif // CPPSORT_DETAIL_BLOCK_SORT_H_ +#endif // CPPSORT_DETAIL_WIKI_SORT_H_ diff --git a/include/cpp-sort/fwd.h b/include/cpp-sort/fwd.h index 953a808a..6fef281b 100644 --- a/include/cpp-sort/fwd.h +++ b/include/cpp-sort/fwd.h @@ -54,6 +54,8 @@ namespace cppsort struct string_spread_sorter; struct tim_sorter; struct verge_sorter; + template + struct wiki_sorter; //////////////////////////////////////////////////////////// // Fixed-size sorters diff --git a/include/cpp-sort/sorters.h b/include/cpp-sort/sorters.h index 3c9b7b4f..3aed709b 100644 --- a/include/cpp-sort/sorters.h +++ b/include/cpp-sort/sorters.h @@ -33,5 +33,6 @@ #include #include #include +#include #endif // CPPSORT_SORTERS_H_ diff --git a/include/cpp-sort/sorters/block_sorter.h b/include/cpp-sort/sorters/block_sorter.h index cc8201de..93ebad60 100644 --- a/include/cpp-sort/sorters/block_sorter.h +++ b/include/cpp-sort/sorters/block_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_BLOCK_SORTER_H_ @@ -17,8 +17,9 @@ #include #include #include -#include "../detail/block_sort.h" +#include "../detail/attributes.h" #include "../detail/iterator_traits.h" +#include "../detail/wiki_sort.h" namespace cppsort { @@ -50,8 +51,8 @@ namespace cppsort "block_sorter requires at least random-access iterators" ); - block_sort(std::move(first), std::move(last), - std::move(compare), std::move(projection)); + wiki_sort(std::move(first), std::move(last), + std::move(compare), std::move(projection)); } //////////////////////////////////////////////////////////// @@ -74,6 +75,7 @@ namespace cppsort namespace { + CPPSORT_DEPRECATED("block_sort() is deprecated and will be removed in version 2.0.0, use wiki_sort() instead") constexpr auto&& block_sort = utility::static_const>::value; } diff --git a/include/cpp-sort/sorters/wiki_sorter.h b/include/cpp-sort/sorters/wiki_sorter.h new file mode 100644 index 00000000..45270d87 --- /dev/null +++ b/include/cpp-sort/sorters/wiki_sorter.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015-2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_SORTERS_WIKI_SORTER_H_ +#define CPPSORT_SORTERS_WIKI_SORTER_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../detail/iterator_traits.h" +#include "../detail/wiki_sort.h" + +namespace cppsort +{ + //////////////////////////////////////////////////////////// + // Sorter + + namespace detail + { + template + struct wiki_sorter_impl + { + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + auto operator()(RandomAccessIterator first, RandomAccessIterator last, + Compare compare={}, Projection projection={}) const + -> void + { + static_assert( + std::is_base_of< + std::random_access_iterator_tag, + iterator_category_t + >::value, + "wiki_sorter requires at least random-access iterators" + ); + + wiki_sort(std::move(first), std::move(last), + std::move(compare), std::move(projection)); + } + + //////////////////////////////////////////////////////////// + // Sorter traits + + using iterator_category = std::random_access_iterator_tag; + using is_always_stable = std::true_type; + }; + } + + template< + typename BufferProvider = utility::fixed_buffer<512> + > + struct wiki_sorter: + sorter_facade> + {}; + + //////////////////////////////////////////////////////////// + // Sort function + + namespace + { + constexpr auto&& wiki_sort + = utility::static_const>::value; + } +} + +#endif // CPPSORT_SORTERS_WIKI_SORTER_H_ diff --git a/testsuite/adapters/indirect_adapter_every_sorter.cpp b/testsuite/adapters/indirect_adapter_every_sorter.cpp index 1f2013aa..7fa30f5b 100644 --- a/testsuite/adapters/indirect_adapter_every_sorter.cpp +++ b/testsuite/adapters/indirect_adapter_every_sorter.cpp @@ -12,7 +12,6 @@ #include TEMPLATE_TEST_CASE( "every random-access sorter with indirect adapter", "[indirect_adapter]", - cppsort::block_sorter<>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -36,7 +35,8 @@ TEMPLATE_TEST_CASE( "every random-access sorter with indirect adapter", "[indire cppsort::std_sorter, cppsort::stable_adapter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<> ) { std::vector collection; collection.reserve(412); auto distribution = dist::shuffled{}; diff --git a/testsuite/adapters/schwartz_adapter_every_sorter.cpp b/testsuite/adapters/schwartz_adapter_every_sorter.cpp index a2713d54..b231e49b 100644 --- a/testsuite/adapters/schwartz_adapter_every_sorter.cpp +++ b/testsuite/adapters/schwartz_adapter_every_sorter.cpp @@ -24,7 +24,6 @@ template using wrapper = generic_wrapper; TEMPLATE_TEST_CASE( "every random-access sorter with Schwartzian transform adapter", "[schwartz_adapter]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -44,7 +43,8 @@ TEMPLATE_TEST_CASE( "every random-access sorter with Schwartzian transform adapt cppsort::spin_sorter, cppsort::split_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter> ) { std::vector> collection; auto distribution = dist::shuffled{}; diff --git a/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp b/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp index bda60ffa..b5a089d0 100644 --- a/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp +++ b/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp @@ -24,7 +24,6 @@ using wrapper = generic_wrapper; TEMPLATE_TEST_CASE( "every sorter with Schwartzian transform adapter and reverse iterators", "[schwartz_adapter][reverse_iterator]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -44,7 +43,8 @@ TEMPLATE_TEST_CASE( "every sorter with Schwartzian transform adapter and reverse cppsort::spin_sorter, cppsort::split_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter> ) { std::vector> collection; auto distribution = dist::shuffled{}; diff --git a/testsuite/adapters/stable_adapter_every_sorter.cpp b/testsuite/adapters/stable_adapter_every_sorter.cpp index 6e06e382..a525f95d 100644 --- a/testsuite/adapters/stable_adapter_every_sorter.cpp +++ b/testsuite/adapters/stable_adapter_every_sorter.cpp @@ -27,7 +27,6 @@ using wrapper = generic_stable_wrapper; // are also in ascending order TEMPLATE_TEST_CASE( "every random-access sorter with stable_adapter", "[stable_adapter]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -48,7 +47,8 @@ TEMPLATE_TEST_CASE( "every random-access sorter with stable_adapter", "[stable_a cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter> ) { cppsort::stable_t sorter; std::vector collection(412); diff --git a/testsuite/adapters/verge_adapter_every_sorter.cpp b/testsuite/adapters/verge_adapter_every_sorter.cpp index 88047c29..978f0e74 100644 --- a/testsuite/adapters/verge_adapter_every_sorter.cpp +++ b/testsuite/adapters/verge_adapter_every_sorter.cpp @@ -12,7 +12,6 @@ #include TEMPLATE_TEST_CASE( "every sorter with verge_adapter", "[verge_adapter]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -34,7 +33,8 @@ TEMPLATE_TEST_CASE( "every sorter with verge_adapter", "[verge_adapter]", cppsort::split_sorter, cppsort::spread_sorter, cppsort::std_sorter, - cppsort::tim_sorter ) + cppsort::tim_sorter, + cppsort::wiki_sorter> ) { std::vector collection; collection.reserve(412); auto distribution = dist::shuffled{}; diff --git a/testsuite/distributions/all_equal.cpp b/testsuite/distributions/all_equal.cpp index db9f54c0..5ab6cfdc 100644 --- a/testsuite/distributions/all_equal.cpp +++ b/testsuite/distributions/all_equal.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test random-access sorters with all_equal distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test random-access sorters with all_equal distribution", "[ cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/alternating.cpp b/testsuite/distributions/alternating.cpp index 2a24f9d8..eedd67a1 100644 --- a/testsuite/distributions/alternating.cpp +++ b/testsuite/distributions/alternating.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with alternating distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with alternating distribution", "[distributions cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/ascending.cpp b/testsuite/distributions/ascending.cpp index 14a30e58..6bc39996 100644 --- a/testsuite/distributions/ascending.cpp +++ b/testsuite/distributions/ascending.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with ascending distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, // While counting_sort shouldn't be affected by patterns, its // underlying minmax_element_and_is_sorted function had a bug // that could specifically appear with an ascending distribution, @@ -42,7 +38,11 @@ TEMPLATE_TEST_CASE( "test sorter with ascending distribution", "[distributions]" cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/ascending_sawtooth.cpp b/testsuite/distributions/ascending_sawtooth.cpp index 7e10b3be..e4519836 100644 --- a/testsuite/distributions/ascending_sawtooth.cpp +++ b/testsuite/distributions/ascending_sawtooth.cpp @@ -13,10 +13,6 @@ #include TEMPLATE_TEST_CASE( "test random-access sorters with ascending_sawtooth distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -38,7 +34,11 @@ TEMPLATE_TEST_CASE( "test random-access sorters with ascending_sawtooth distribu cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/descending.cpp b/testsuite/distributions/descending.cpp index 982085e0..44f41d39 100644 --- a/testsuite/distributions/descending.cpp +++ b/testsuite/distributions/descending.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with descending distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with descending distribution", "[distributions] cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/descending_sawtooth.cpp b/testsuite/distributions/descending_sawtooth.cpp index a5ef7705..642362cd 100644 --- a/testsuite/distributions/descending_sawtooth.cpp +++ b/testsuite/distributions/descending_sawtooth.cpp @@ -13,10 +13,6 @@ #include TEMPLATE_TEST_CASE( "test random-access sorters with descending_sawtooth distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -38,7 +34,11 @@ TEMPLATE_TEST_CASE( "test random-access sorters with descending_sawtooth distrib cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/median_of_3_killer.cpp b/testsuite/distributions/median_of_3_killer.cpp index 57d21909..8653a1bf 100644 --- a/testsuite/distributions/median_of_3_killer.cpp +++ b/testsuite/distributions/median_of_3_killer.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test random-access sorters with median_of_3_killer distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test random-access sorters with median_of_3_killer distribu cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(1000); diff --git a/testsuite/distributions/pipe_organ.cpp b/testsuite/distributions/pipe_organ.cpp index 0327e010..e21d47c6 100644 --- a/testsuite/distributions/pipe_organ.cpp +++ b/testsuite/distributions/pipe_organ.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with pipe_organ distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with pipe_organ distribution", "[distributions] cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/push_front.cpp b/testsuite/distributions/push_front.cpp index 8c6db10a..c815361c 100644 --- a/testsuite/distributions/push_front.cpp +++ b/testsuite/distributions/push_front.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with push_front distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with push_front distribution", "[distributions] cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/push_middle.cpp b/testsuite/distributions/push_middle.cpp index e7b8da17..71c9b407 100644 --- a/testsuite/distributions/push_middle.cpp +++ b/testsuite/distributions/push_middle.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with push_middle distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with push_middle distribution", "[distributions cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/shuffled.cpp b/testsuite/distributions/shuffled.cpp index 876d2539..3509e41c 100644 --- a/testsuite/distributions/shuffled.cpp +++ b/testsuite/distributions/shuffled.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with shuffled distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with shuffled distribution", "[distributions]", cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/distributions/shuffled_16_values.cpp b/testsuite/distributions/shuffled_16_values.cpp index 1be5d50f..ac8a5097 100644 --- a/testsuite/distributions/shuffled_16_values.cpp +++ b/testsuite/distributions/shuffled_16_values.cpp @@ -12,10 +12,6 @@ #include TEMPLATE_TEST_CASE( "test sorter with shuffled_16_values distribution", "[distributions]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -37,7 +33,11 @@ TEMPLATE_TEST_CASE( "test sorter with shuffled_16_values distribution", "[distri cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(10'000); diff --git a/testsuite/every_instantiated_sorter.cpp b/testsuite/every_instantiated_sorter.cpp index 0b1f4750..117b6a9f 100644 --- a/testsuite/every_instantiated_sorter.cpp +++ b/testsuite/every_instantiated_sorter.cpp @@ -28,12 +28,6 @@ TEST_CASE( "test every instantiated sorter", "[sorters]" ) std::list li(std::begin(collection), std::end(collection)); std::forward_list fli(std::begin(collection), std::end(collection)); - SECTION( "block_sort" ) - { - cppsort::block_sort(collection); - CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); - } - SECTION( "cartesian_tree_sort" ) { cppsort::cartesian_tree_sort(collection); @@ -177,4 +171,10 @@ TEST_CASE( "test every instantiated sorter", "[sorters]" ) cppsort::verge_sort(collection); CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); } + + SECTION( "wiki_sort" ) + { + cppsort::wiki_sort(collection); + CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); + } } diff --git a/testsuite/every_sorter.cpp b/testsuite/every_sorter.cpp index 099dde05..34e161e7 100644 --- a/testsuite/every_sorter.cpp +++ b/testsuite/every_sorter.cpp @@ -15,10 +15,6 @@ #include TEMPLATE_TEST_CASE( "test every random-access sorter", "[sorters]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::counting_sorter, cppsort::drop_merge_sorter, @@ -43,7 +39,11 @@ TEMPLATE_TEST_CASE( "test every random-access sorter", "[sorters]", cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { // General test to make sure that every sorter compiles fine // and is able to sort a vector of numbers. spread_sorter is diff --git a/testsuite/every_sorter_internal_compare.cpp b/testsuite/every_sorter_internal_compare.cpp index 1aeb52c5..404f1626 100644 --- a/testsuite/every_sorter_internal_compare.cpp +++ b/testsuite/every_sorter_internal_compare.cpp @@ -12,7 +12,6 @@ TEMPLATE_TEST_CASE( "test every sorter with a pointer to member function comparison", "[sorters][as_function]", - cppsort::block_sorter<>, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -32,7 +31,8 @@ TEMPLATE_TEST_CASE( "test every sorter with a pointer to member function compari cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<> ) { std::vector> collection; collection.reserve(35); diff --git a/testsuite/every_sorter_long_string.cpp b/testsuite/every_sorter_long_string.cpp index efc0b57e..0e234948 100644 --- a/testsuite/every_sorter_long_string.cpp +++ b/testsuite/every_sorter_long_string.cpp @@ -41,10 +41,6 @@ namespace } TEMPLATE_TEST_CASE( "test every sorter with long std::string", "[sorters]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -69,7 +65,11 @@ TEMPLATE_TEST_CASE( "test every sorter with long std::string", "[sorters]", cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { // This test primarily exists to detect self-move issues in the library: // long std::string instances actually self-destruct on self-move and can diff --git a/testsuite/every_sorter_move_compare_projection.cpp b/testsuite/every_sorter_move_compare_projection.cpp index 95aad895..c1f48eda 100644 --- a/testsuite/every_sorter_move_compare_projection.cpp +++ b/testsuite/every_sorter_move_compare_projection.cpp @@ -13,10 +13,6 @@ #include TEMPLATE_TEST_CASE( "every sorter with comparison function altered by move", "[sorters]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -39,7 +35,11 @@ TEMPLATE_TEST_CASE( "every sorter with comparison function altered by move", "[s cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(491); @@ -52,10 +52,6 @@ TEMPLATE_TEST_CASE( "every sorter with comparison function altered by move", "[s } TEMPLATE_TEST_CASE( "every sorter with projection function altered by move", "[sorters][projection]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -79,7 +75,11 @@ TEMPLATE_TEST_CASE( "every sorter with projection function altered by move", "[s cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { std::vector collection; collection.reserve(491); diff --git a/testsuite/every_sorter_move_only.cpp b/testsuite/every_sorter_move_only.cpp index ff26c48d..fdc9c809 100644 --- a/testsuite/every_sorter_move_only.cpp +++ b/testsuite/every_sorter_move_only.cpp @@ -13,7 +13,6 @@ #include TEMPLATE_TEST_CASE( "test every sorter with move-only types", "[sorters]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -34,7 +33,8 @@ TEMPLATE_TEST_CASE( "test every sorter with move-only types", "[sorters]", cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter> ) { // General test to make sure that every sorter compiles fine with // move-only types, additionally checking that no read-after-move diff --git a/testsuite/every_sorter_no_post_iterator.cpp b/testsuite/every_sorter_no_post_iterator.cpp index f9231477..958d725c 100644 --- a/testsuite/every_sorter_no_post_iterator.cpp +++ b/testsuite/every_sorter_no_post_iterator.cpp @@ -14,7 +14,6 @@ #include TEMPLATE_TEST_CASE( "test most sorters with no_post_iterator", "[sorters]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::counting_sorter, cppsort::default_sorter, @@ -37,7 +36,8 @@ TEMPLATE_TEST_CASE( "test most sorters with no_post_iterator", "[sorters]", cppsort::split_sorter, cppsort::spread_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter> ) { std::vector collection; auto distribution = dist::shuffled{}; diff --git a/testsuite/every_sorter_non_const_compare.cpp b/testsuite/every_sorter_non_const_compare.cpp index 5719cd3d..27dc1f6d 100644 --- a/testsuite/every_sorter_non_const_compare.cpp +++ b/testsuite/every_sorter_non_const_compare.cpp @@ -11,7 +11,6 @@ #include TEMPLATE_TEST_CASE( "test extended compatibility with LWG 3031", "[sorters]", - cppsort::block_sorter>, cppsort::cartesian_tree_sorter, cppsort::default_sorter, cppsort::drop_merge_sorter, @@ -32,7 +31,8 @@ TEMPLATE_TEST_CASE( "test extended compatibility with LWG 3031", "[sorters]", cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter> ) { // LWG3031 allows algorithms taking a predicate to work correctly when // said predicate takes non-const references on either side as long as diff --git a/testsuite/every_sorter_rvalue_projection.cpp b/testsuite/every_sorter_rvalue_projection.cpp index 4dd79539..b349e833 100644 --- a/testsuite/every_sorter_rvalue_projection.cpp +++ b/testsuite/every_sorter_rvalue_projection.cpp @@ -14,7 +14,6 @@ #include TEMPLATE_TEST_CASE( "random-access sorters with a projection returning an rvalue", "[sorters][projection]", - cppsort::block_sorter<>, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -35,7 +34,8 @@ TEMPLATE_TEST_CASE( "random-access sorters with a projection returning an rvalue cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<> ) { // This test is meant to check that sorters can correctly handle // projections that return an rvalue, we use std::negate as diff --git a/testsuite/every_sorter_span.cpp b/testsuite/every_sorter_span.cpp index 69bfc9eb..9447bd11 100644 --- a/testsuite/every_sorter_span.cpp +++ b/testsuite/every_sorter_span.cpp @@ -13,10 +13,6 @@ #include TEMPLATE_TEST_CASE( "test every sorter with temporary span", "[sorters][span]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::counting_sorter, cppsort::default_sorter, @@ -43,7 +39,11 @@ TEMPLATE_TEST_CASE( "test every sorter with temporary span", "[sorters][span]", cppsort::spread_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { // General test to make sure that every sorter compiles fine // and is able to sort a temporary span referencing a vector diff --git a/testsuite/every_sorter_throwing_moves.cpp b/testsuite/every_sorter_throwing_moves.cpp index 9c6ef6fb..20ffda83 100644 --- a/testsuite/every_sorter_throwing_moves.cpp +++ b/testsuite/every_sorter_throwing_moves.cpp @@ -77,10 +77,6 @@ namespace } TEMPLATE_TEST_CASE( "random-access sorters against throwing move operations", "[sorters][throwing_moves]", - cppsort::block_sorter<>, - cppsort::block_sorter< - cppsort::utility::dynamic_buffer - >, cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::grail_sorter<>, @@ -103,7 +99,11 @@ TEMPLATE_TEST_CASE( "random-access sorters against throwing move operations", "[ cppsort::split_sorter, cppsort::std_sorter, cppsort::tim_sorter, - cppsort::verge_sorter ) + cppsort::verge_sorter, + cppsort::wiki_sorter<>, + cppsort::wiki_sorter< + cppsort::utility::dynamic_buffer + > ) { auto distribution = dist::shuffled{}; // Initialize counters diff --git a/testsuite/heap_memory_exhaustion.cpp b/testsuite/heap_memory_exhaustion.cpp index 8a63cf65..a2f4e29f 100644 --- a/testsuite/heap_memory_exhaustion.cpp +++ b/testsuite/heap_memory_exhaustion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -20,7 +20,6 @@ // TEMPLATE_TEST_CASE( "test heap exhaustion for random-access sorters", "[sorters][heap_exhaustion]", - cppsort::block_sorter<>, cppsort::grail_sorter<>, cppsort::heap_sorter, cppsort::insertion_sorter, @@ -32,7 +31,8 @@ TEMPLATE_TEST_CASE( "test heap exhaustion for random-access sorters", "[sorters] cppsort::ska_sorter, cppsort::smooth_sorter, cppsort::split_sorter, - cppsort::std_sorter ) + cppsort::std_sorter, + cppsort::wiki_sorter<> ) { std::vector collection; collection.reserve(491); auto distribution = dist::shuffled{}; diff --git a/tools/count-compare-copies.cpp b/tools/count-compare-copies.cpp index 8229cf31..2027597b 100644 --- a/tools/count-compare-copies.cpp +++ b/tools/count-compare-copies.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -65,7 +65,6 @@ int main() using namespace std::literals; std::pair> sorts[] = { - { "block_sort", cppsort::block_sort }, { "grail_sort", cppsort::grail_sort }, { "heap_sort", cppsort::heap_sort }, { "insertion_sort", cppsort::insertion_sort }, @@ -78,7 +77,8 @@ int main() { "smooth_sort", cppsort::smooth_sort }, { "std_sort", cppsort::std_sort }, { "tim_sort", cppsort::tim_sort }, - { "verge_sort", cppsort::verge_sort } + { "verge_sort", cppsort::verge_sort }, + { "wiki_sort", cppsort::wiki_sort }, }; for (auto& sort_item: sorts) { From 2eb4f9a8d7411612ee685e4d4ebf6b6a8e899972 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 22 May 2021 19:58:26 +0200 Subject: [PATCH 35/56] Improve noexcept specifications in adapter_storage --- include/cpp-sort/utility/adapter_storage.h | 28 ++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/include/cpp-sort/utility/adapter_storage.h b/include/cpp-sort/utility/adapter_storage.h index 2ecf94c1..483f0e9d 100644 --- a/include/cpp-sort/utility/adapter_storage.h +++ b/include/cpp-sort/utility/adapter_storage.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 Morwenn + * Copyright (c) 2018-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_UTILITY_ADAPTER_STORAGE_H_ @@ -36,17 +36,23 @@ namespace utility struct adapter_storage { adapter_storage() = default; - explicit constexpr adapter_storage(Sorter) noexcept {} + explicit constexpr adapter_storage(const Sorter&) noexcept {} template constexpr auto operator()(Args&&... args) const +#ifdef __cpp_lib_is_invocable + noexcept(std::is_nothrow_default_constructible_v && + std::is_nothrow_invocable_v) +#else noexcept(noexcept(Sorter{}(std::forward(args)...))) +#endif -> decltype(Sorter{}(std::forward(args)...)) { return Sorter{}(std::forward(args)...); } constexpr auto get() const + noexcept(std::is_nothrow_default_constructible::value) -> Sorter { return Sorter{}; @@ -66,13 +72,17 @@ namespace utility {} explicit constexpr adapter_storage(Sorter&& sorter) - noexcept(std::is_nothrow_copy_constructible::value): + noexcept(std::is_nothrow_move_constructible::value): sorter(std::move(sorter)) {} template constexpr auto operator()(Args&&... args) const +#ifdef __cpp_lib_is_invocable + noexcept(std::is_nothrow_invocable_v) +#else noexcept(noexcept(sorter(std::forward(args)...))) +#endif -> decltype(sorter(std::forward(args)...)) { return sorter(std::forward(args)...); @@ -80,31 +90,35 @@ namespace utility template constexpr auto operator()(Args&&... args) +#ifdef __cpp_lib_is_invocable + noexcept(std::is_nothrow_invocable_v) +#else noexcept(noexcept(sorter(std::forward(args)...))) +#endif -> decltype(sorter(std::forward(args)...)) { return sorter(std::forward(args)...); } - constexpr auto get() & + constexpr auto get() & noexcept -> Sorter& { return static_cast(sorter); } - constexpr auto get() const& + constexpr auto get() const& noexcept -> const Sorter& { return static_cast(sorter); } - constexpr auto get() && + constexpr auto get() && noexcept -> Sorter&& { return static_cast(sorter); } - constexpr auto get() const&& + constexpr auto get() const&& noexcept -> const Sorter&& { return static_cast(sorter); From b5b5cabe2b70314dc3e5658357687a0423cbb41d Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 24 May 2021 12:33:05 +0200 Subject: [PATCH 36/56] Simplify every_sorter_move_only --- testsuite/every_sorter_move_only.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/testsuite/every_sorter_move_only.cpp b/testsuite/every_sorter_move_only.cpp index fdc9c809..e7e81434 100644 --- a/testsuite/every_sorter_move_only.cpp +++ b/testsuite/every_sorter_move_only.cpp @@ -4,7 +4,6 @@ */ #include #include -#include #include #include #include @@ -40,16 +39,12 @@ TEMPLATE_TEST_CASE( "test every sorter with move-only types", "[sorters]", // move-only types, additionally checking that no read-after-move // operation is performed and that no self-move is performed - std::vector numbers; - numbers.reserve(491); + std::vector> collection; + collection.reserve(491); auto distribution = dist::shuffled{}; - distribution(std::back_inserter(numbers), 491, -125); - - std::mt19937 engine(Catch::rngSeed()); - std::shuffle(std::begin(numbers), std::end(numbers), engine); - std::vector> collection(std::begin(numbers), std::end(numbers)); + distribution(std::back_inserter(collection), 491, -125); TestType sorter; sorter(collection); - CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); } From 8bebb905510c28de862e6c30f51454b2fa197302 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 24 May 2021 18:39:23 +0200 Subject: [PATCH 37/56] Diagnose more issues with move_only --- testsuite/testing-tools/move_only.h | 43 +++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/testsuite/testing-tools/move_only.h b/testsuite/testing-tools/move_only.h index 1cd6d61b..005b02cb 100644 --- a/testsuite/testing-tools/move_only.h +++ b/testsuite/testing-tools/move_only.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_TESTSUITE_MOVE_ONLY_H_ @@ -9,6 +9,7 @@ // Headers //////////////////////////////////////////////////////////// #include +#include #include //////////////////////////////////////////////////////////// @@ -33,6 +34,10 @@ inline namespace i_need_a_namespace_for_proper_adl template struct move_only { + // Keep track of the constructed instances() of move_only + static std::unordered_set instances; + + // Not default-constructible move_only() = delete; @@ -44,7 +49,9 @@ inline namespace i_need_a_namespace_for_proper_adl move_only(const T& value): can_read(true), value(value) - {} + { + instances.insert(this); + } // Move operators @@ -52,14 +59,33 @@ inline namespace i_need_a_namespace_for_proper_adl can_read(true), value(std::move(other.value)) { + if (instances.find(&other) == instances.end()) { + throw std::logic_error("read from a ghost move_only instance"); + } if (not std::exchange(other.can_read, false)) { throw std::logic_error("illegal read from a moved-from value"); } + instances.insert(this); + } + + ~move_only() + { + instances.erase(this); } auto operator=(move_only&& other) -> move_only& { + // Ensure that the current instance and the other instance were + // correctly constructed and not merely willed into existence + // from uninitialized memory + if (instances.find(this) == instances.end()) { + throw std::logic_error("write to a ghost move_only instance"); + } + if (instances.find(&other) == instances.end()) { + throw std::logic_error("read from a ghost move_only instance"); + } + // Self-move should be ok if the object is already in a moved-from // state because it incurs no data loss, but should otherwise be // frowned upon @@ -87,6 +113,9 @@ inline namespace i_need_a_namespace_for_proper_adl T value; }; + template + std::unordered_set*> move_only::instances; + template auto operator<(const move_only& lhs, const move_only& rhs) -> bool @@ -104,6 +133,16 @@ inline namespace i_need_a_namespace_for_proper_adl // does the right thing, and not the responsibility of algorithm // authors to prevent them from happening + // Ensure that the current instance and the other instance were + // correctly constructed and not merely willed into existence + // from uninitialized memory + if (move_only::instances.find(&lhs) == move_only::instances.end()) { + throw std::logic_error("write to a ghost move_only instance"); + } + if (move_only::instances.find(&rhs) == move_only::instances.end()) { + throw std::logic_error("write a ghost move_only instance"); + } + // Both operands need to be readable if (not (lhs.can_read || rhs.can_read)) { throw std::logic_error("illegal read from a moved-from value"); From 475816a84aba1caaaec36f63df20bc89ce6fb9ed Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 27 May 2021 15:30:36 +0200 Subject: [PATCH 38/56] Patterns benchmark: add optional errorbars --- benchmarks/patterns/bars.py | 10 +++++++--- benchmarks/patterns/bench.cpp | 19 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/benchmarks/patterns/bars.py b/benchmarks/patterns/bars.py index 11403988..4afee7cd 100644 --- a/benchmarks/patterns/bars.py +++ b/benchmarks/patterns/bars.py @@ -36,6 +36,9 @@ def main(): parser.add_argument('--alternative-palette', dest='use_alt_palette', action='store_true', default=False, help="Use another color palette") + parser.add_argument('--errorbars', dest='display_errorbars', + action='store_true', default=False, + help="Display errorbars") args = parser.parse_args() distribution_names = { @@ -73,8 +76,8 @@ def main(): size = int(size) distribution = distribution_names[distribution] results = [int(result) for result in results] - if not size in data: data[size] = {} - if not distribution in data[size]: data[size][distribution] = {} + data.setdefault(size, {}) + data[size].setdefault(distribution, {}) data[size][distribution][algo] = results # Choose the colour palette and markers to use @@ -112,8 +115,9 @@ def main(): for i, algo in enumerate(algos): heights = [numpy.median(data[size][distribution][algo]) for distribution in distributions] errors = [numpy.std(data[size][distribution][algo]) for distribution in distributions] + kwargs = {"xerr": errors} if args.display_errorbars else {} pyplot.barh([barwidth * i + groupwidth * n for n in range(len(distributions))], - heights, 0.6, color=next(colors), label=algo) + heights, 0.6, color=next(colors), label=algo, **kwargs) # Set axes limits and labels. groupnames = ['\n'.join(wrap(l, 11)) for l in groupnames] diff --git a/benchmarks/patterns/bench.cpp b/benchmarks/patterns/bench.cpp index 9d3f4e21..30f4c848 100644 --- a/benchmarks/patterns/bench.cpp +++ b/benchmarks/patterns/bench.cpp @@ -81,7 +81,7 @@ int main() { "verge_sort", cppsort::verge_sort }, }; - std::size_t sizes[] = { 10'000'000 }; + std::size_t sizes[] = { 1'000'000 }; // Poor seed, yet enough for our benchmarks std::uint_fast32_t seed = std::time(nullptr); @@ -98,7 +98,7 @@ int main() auto total_start = clock_type::now(); auto total_end = clock_type::now(); - while (std::chrono::duration_cast(total_end - total_start) < 5s) { + while (total_end - total_start < 5s) { collection_t collection; distribution.second(std::back_inserter(collection), size); std::uint64_t start = rdtsc(); @@ -109,12 +109,15 @@ int main() total_end = clock_type::now(); } - std::sort(std::begin(cycles), std::end(cycles)); - - std::cerr << size << ", " << distribution.first << ", " << sort.first - << ", " << cycles[cycles.size() / 2] << '\n'; - std::cout << size << ", " << distribution.first << ", " << sort.first - << ", " << cycles[cycles.size() / 2] << '\n'; + for (std::ostream* stream: {&std::cout, &std::cerr}) { + (*stream) << size << ", " << distribution.first << ", " << sort.first << ", "; + auto it = cycles.begin(); + (*stream) << *it; + while (++it != cycles.end()) { + (*stream) << ", " << *it; + } + (*stream) << std::endl; + } } } } From 830f23dd5d0eb148d71e7e6ada985fc22e427ac0 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 30 May 2021 23:14:54 +0200 Subject: [PATCH 39/56] errorbar-plot: simplify condition --- benchmarks/errorbar-plot/bench.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/benchmarks/errorbar-plot/bench.cpp b/benchmarks/errorbar-plot/bench.cpp index 0604a614..8df9618d 100644 --- a/benchmarks/errorbar-plot/bench.cpp +++ b/benchmarks/errorbar-plot/bench.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Morwenn + * Copyright (c) 2020-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -92,8 +92,7 @@ int main(int argc, char** argv) auto total_start = clock_type::now(); auto total_end = clock_type::now(); - while (std::chrono::duration_cast(total_end - total_start) < max_run_time && - times.size() < max_runs_per_size) { + while (total_end - total_start < max_run_time && times.size() < max_runs_per_size) { collection_t collection; distribution(std::back_inserter(collection), size); auto start = clock_type::now(); From fef74e7be2388bb8a32900b8f19eb5829bd145fe Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 6 Jun 2021 17:15:57 +0200 Subject: [PATCH 40/56] Make cartesian_tree_sort work with forward iterators --- docs/Sorters.md | 4 +- include/cpp-sort/detail/cartesian_tree_sort.h | 20 +++++---- .../cpp-sort/sorters/cartesian_tree_sorter.h | 43 +++++++++++++++---- .../indirect_adapter_every_sorter.cpp | 2 + .../schwartz_adapter_every_sorter.cpp | 2 + .../adapters/stable_adapter_every_sorter.cpp | 2 + .../distributions/ascending_sawtooth.cpp | 1 + .../distributions/descending_sawtooth.cpp | 1 + .../distributions/median_of_3_killer.cpp | 1 + testsuite/every_sorter.cpp | 2 + testsuite/every_sorter_rvalue_projection.cpp | 2 + testsuite/every_sorter_throwing_moves.cpp | 2 + 12 files changed, 65 insertions(+), 17 deletions(-) diff --git a/docs/Sorters.md b/docs/Sorters.md index 0bd73dcd..c844965f 100644 --- a/docs/Sorters.md +++ b/docs/Sorters.md @@ -32,10 +32,12 @@ Implements a [Cartesian tree sort][cartesian-tree-sort], a rather slow but highl | Best | Average | Worst | Memory | Stable | Iterators | | ----------- | ----------- | ----------- | ----------- | ----------- | ------------- | -| n | n log n | n log n | n | No | Random-access | +| n | n log n | n log n | n | No | Forward | *New in version 1.10.0* +*Changed in version 1.11.0:* `cartesian_tree_sorter` now works with forward iterators. It used to only work with random-access iterators. + ### `default_sorter` ```cpp diff --git a/include/cpp-sort/detail/cartesian_tree_sort.h b/include/cpp-sort/detail/cartesian_tree_sort.h index 3a3ecba6..a87ca939 100644 --- a/include/cpp-sort/detail/cartesian_tree_sort.h +++ b/include/cpp-sort/detail/cartesian_tree_sort.h @@ -64,9 +64,11 @@ namespace detail //////////////////////////////////////////////////////////// // Constructor - template - explicit cartesian_tree(Iterator first, Iterator last, Compare compare, Projection projection): - buffer_(last - first), + template + explicit cartesian_tree(ForwardIterator first, ForwardIterator last, + difference_type_t size, + Compare compare, Projection projection): + buffer_(size), root_(buffer_.begin()) // Original root is first element { using utility::iter_move; @@ -134,18 +136,20 @@ namespace detail node_type* root_; }; - template - auto cartesian_tree_sort(Iterator first, Iterator last, Compare compare, Projection projection) + template + auto cartesian_tree_sort(ForwardIterator first, ForwardIterator last, + difference_type_t size, + Compare compare, Projection projection) -> void { - using tree_type = cartesian_tree>; + using tree_type = cartesian_tree>; using node_type = typename tree_type::node_type; - if ((last - first) < 2) { + if (size < 2) { return; } - tree_type tree(first, last, compare, projection); + tree_type tree(first, last, size, compare, projection); std::vector pq; // Priority queue pq.push_back(tree.root()); diff --git a/include/cpp-sort/sorters/cartesian_tree_sorter.h b/include/cpp-sort/sorters/cartesian_tree_sorter.h index 0199086d..df275ff6 100644 --- a/include/cpp-sort/sorters/cartesian_tree_sorter.h +++ b/include/cpp-sort/sorters/cartesian_tree_sorter.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "../detail/cartesian_tree_sort.h" #include "../detail/iterator_traits.h" @@ -29,33 +30,59 @@ namespace cppsort struct cartesian_tree_sorter_impl { template< - typename RandomAccessIterator, + typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, typename = std::enable_if_t< - is_projection_iterator_v + is_projection_v > > - auto operator()(RandomAccessIterator first, RandomAccessIterator last, + auto operator()(ForwardIterable&& iterable, Compare compare={}, Projection projection={}) const -> void { static_assert( std::is_base_of< - std::random_access_iterator_tag, - iterator_category_t + std::forward_iterator_tag, + iterator_category_t >::value, - "cartesian_tree_sorter requires at least random-access iterators" + "cartesian_tree_sorter requires at least forward iterators" ); - cartesian_tree_sort(std::move(first), std::move(last), + cartesian_tree_sort(std::begin(iterable), std::end(iterable), + utility::size(iterable), + std::move(compare), std::move(projection)); + } + + template< + typename ForwardIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t< + is_projection_iterator_v + > + > + auto operator()(ForwardIterator first, ForwardIterator last, + Compare compare={}, Projection projection={}) const + -> void + { + static_assert( + std::is_base_of< + std::forward_iterator_tag, + iterator_category_t + >::value, + "cartesian_tree_sorter requires at least forward iterators" + ); + + auto size = std::distance(first, last); + cartesian_tree_sort(std::move(first), std::move(last), size, std::move(compare), std::move(projection)); } //////////////////////////////////////////////////////////// // Sorter traits - using iterator_category = std::random_access_iterator_tag; + using iterator_category = std::forward_iterator_tag; using is_always_stable = std::false_type; }; } diff --git a/testsuite/adapters/indirect_adapter_every_sorter.cpp b/testsuite/adapters/indirect_adapter_every_sorter.cpp index 7fa30f5b..feb6ab4f 100644 --- a/testsuite/adapters/indirect_adapter_every_sorter.cpp +++ b/testsuite/adapters/indirect_adapter_every_sorter.cpp @@ -48,6 +48,7 @@ TEMPLATE_TEST_CASE( "every random-access sorter with indirect adapter", "[indire } TEMPLATE_TEST_CASE( "every bidirectional sorter with indirect_adapter", "[indirect_adapter]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::insertion_sorter, cppsort::mel_sorter, @@ -68,6 +69,7 @@ TEMPLATE_TEST_CASE( "every bidirectional sorter with indirect_adapter", "[indire } TEMPLATE_TEST_CASE( "every forward sorter with with indirect_adapter", "[indirect_adapter]", + cppsort::cartesian_tree_sorter, cppsort::mel_sorter, cppsort::merge_sorter, cppsort::pdq_sorter, // Check extended support diff --git a/testsuite/adapters/schwartz_adapter_every_sorter.cpp b/testsuite/adapters/schwartz_adapter_every_sorter.cpp index b231e49b..22d4ae9f 100644 --- a/testsuite/adapters/schwartz_adapter_every_sorter.cpp +++ b/testsuite/adapters/schwartz_adapter_every_sorter.cpp @@ -57,6 +57,7 @@ TEMPLATE_TEST_CASE( "every random-access sorter with Schwartzian transform adapt } TEMPLATE_TEST_CASE( "every bidirectional sorter with Schwartzian transform adapter", "[schwartz_adapter]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::insertion_sorter, cppsort::mel_sorter, @@ -77,6 +78,7 @@ TEMPLATE_TEST_CASE( "every bidirectional sorter with Schwartzian transform adapt } TEMPLATE_TEST_CASE( "every forward sorter with Schwartzian transform adapter", "[schwartz_adapter]", + cppsort::cartesian_tree_sorter, cppsort::mel_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, diff --git a/testsuite/adapters/stable_adapter_every_sorter.cpp b/testsuite/adapters/stable_adapter_every_sorter.cpp index a525f95d..b23f2519 100644 --- a/testsuite/adapters/stable_adapter_every_sorter.cpp +++ b/testsuite/adapters/stable_adapter_every_sorter.cpp @@ -72,6 +72,7 @@ TEMPLATE_TEST_CASE( "every random-access sorter with stable_adapter", "[stable_a } TEMPLATE_TEST_CASE( "every bidirectional sorter with stable_adapter", "[stable_adapter]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::insertion_sorter, cppsort::mel_sorter, @@ -103,6 +104,7 @@ TEMPLATE_TEST_CASE( "every bidirectional sorter with stable_adapter", "[stable_a } TEMPLATE_TEST_CASE( "every forward sorter with with stable_adapter", "[stable_adapter]", + cppsort::cartesian_tree_sorter, cppsort::mel_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, diff --git a/testsuite/distributions/ascending_sawtooth.cpp b/testsuite/distributions/ascending_sawtooth.cpp index e4519836..5388814b 100644 --- a/testsuite/distributions/ascending_sawtooth.cpp +++ b/testsuite/distributions/ascending_sawtooth.cpp @@ -51,6 +51,7 @@ TEMPLATE_TEST_CASE( "test random-access sorters with ascending_sawtooth distribu } TEMPLATE_TEST_CASE( "test bidirectional sorters with ascending_sawtooth distribution", "[distributions]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, diff --git a/testsuite/distributions/descending_sawtooth.cpp b/testsuite/distributions/descending_sawtooth.cpp index 642362cd..a636f0dc 100644 --- a/testsuite/distributions/descending_sawtooth.cpp +++ b/testsuite/distributions/descending_sawtooth.cpp @@ -51,6 +51,7 @@ TEMPLATE_TEST_CASE( "test random-access sorters with descending_sawtooth distrib } TEMPLATE_TEST_CASE( "test bidirectional sorters with descending_sawtooth distribution", "[distributions]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, diff --git a/testsuite/distributions/median_of_3_killer.cpp b/testsuite/distributions/median_of_3_killer.cpp index 8653a1bf..01e7e8d6 100644 --- a/testsuite/distributions/median_of_3_killer.cpp +++ b/testsuite/distributions/median_of_3_killer.cpp @@ -50,6 +50,7 @@ TEMPLATE_TEST_CASE( "test random-access sorters with median_of_3_killer distribu } TEMPLATE_TEST_CASE( "test bidirectional sorters with median_of_3_killer distribution", "[distributions]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, diff --git a/testsuite/every_sorter.cpp b/testsuite/every_sorter.cpp index 34e161e7..088ce56e 100644 --- a/testsuite/every_sorter.cpp +++ b/testsuite/every_sorter.cpp @@ -74,6 +74,7 @@ TEMPLATE_TEST_CASE( "test every random-access sorter", "[sorters]", } TEMPLATE_TEST_CASE( "test every bidirectional sorter", "[sorters]", + cppsort::cartesian_tree_sorter, cppsort::counting_sorter, cppsort::drop_merge_sorter, cppsort::insertion_sorter, @@ -97,6 +98,7 @@ TEMPLATE_TEST_CASE( "test every bidirectional sorter", "[sorters]", } TEMPLATE_TEST_CASE( "test every forward sorter", "[sorters]", + cppsort::cartesian_tree_sorter, cppsort::counting_sorter, cppsort::mel_sorter, cppsort::merge_sorter, diff --git a/testsuite/every_sorter_rvalue_projection.cpp b/testsuite/every_sorter_rvalue_projection.cpp index b349e833..91194582 100644 --- a/testsuite/every_sorter_rvalue_projection.cpp +++ b/testsuite/every_sorter_rvalue_projection.cpp @@ -65,6 +65,7 @@ TEMPLATE_TEST_CASE( "random-access sorters with a projection returning an rvalue } TEMPLATE_TEST_CASE( "bidirectional sorters with a projection returning an rvalue", "[sorters][projection]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::insertion_sorter, cppsort::mel_sorter, @@ -87,6 +88,7 @@ TEMPLATE_TEST_CASE( "bidirectional sorters with a projection returning an rvalue } TEMPLATE_TEST_CASE( "forward sorters with a projection returning an rvalue", "[sorters][projection]", + cppsort::cartesian_tree_sorter, cppsort::mel_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, diff --git a/testsuite/every_sorter_throwing_moves.cpp b/testsuite/every_sorter_throwing_moves.cpp index 20ffda83..c33fec37 100644 --- a/testsuite/every_sorter_throwing_moves.cpp +++ b/testsuite/every_sorter_throwing_moves.cpp @@ -145,6 +145,7 @@ TEMPLATE_TEST_CASE( "random-access sorters against throwing move operations", "[ } TEMPLATE_TEST_CASE( "bidirectional sorters against throwing move operations", "[sorters][throwing_moves]", + cppsort::cartesian_tree_sorter, cppsort::drop_merge_sorter, cppsort::insertion_sorter, cppsort::mel_sorter, @@ -178,6 +179,7 @@ TEMPLATE_TEST_CASE( "bidirectional sorters against throwing move operations", "[ } TEMPLATE_TEST_CASE( "forward sorters against throwing move operations", "[sorters][throwing_moves]", + cppsort::cartesian_tree_sorter, cppsort::mel_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, From f1f01cacc401af76744a02049781b7328ea91f2e Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 6 Jun 2021 18:28:56 +0200 Subject: [PATCH 41/56] More immovable_vector for collections of known fixed size --- include/cpp-sort/probes/exc.h | 8 ++++---- include/cpp-sort/probes/ham.h | 9 ++++----- include/cpp-sort/probes/max.h | 9 ++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/include/cpp-sort/probes/exc.h b/include/cpp-sort/probes/exc.h index 81f55de9..0dec911b 100644 --- a/include/cpp-sort/probes/exc.h +++ b/include/cpp-sort/probes/exc.h @@ -20,6 +20,7 @@ #include #include #include "../detail/functional.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" @@ -47,10 +48,9 @@ namespace probe // Indirectly sort the iterators // Copy the iterators in a vector - std::vector iterators; - iterators.reserve(size); - for (auto it = first ; it != last ; ++it) { - iterators.push_back(it); + cppsort::detail::immovable_vector iterators(size); + for (auto it = first; it != last; ++it) { + iterators.emplace_back(it); } // Sort the iterators on pointed values diff --git a/include/cpp-sort/probes/ham.h b/include/cpp-sort/probes/ham.h index cf7bb923..cbf17ad2 100644 --- a/include/cpp-sort/probes/ham.h +++ b/include/cpp-sort/probes/ham.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -20,6 +19,7 @@ #include #include #include "../detail/functional.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" @@ -47,10 +47,9 @@ namespace probe // Indirectly sort the iterators // Copy the iterators in a vector - std::vector iterators; - iterators.reserve(size); - for (ForwardIterator it = first ; it != last ; ++it) { - iterators.push_back(it); + cppsort::detail::immovable_vector iterators(size); + for (auto it = first; it != last; ++it) { + iterators.emplace_back(it); } // Sort the iterators on pointed values diff --git a/include/cpp-sort/probes/max.h b/include/cpp-sort/probes/max.h index 2f7583e3..ab54d7db 100644 --- a/include/cpp-sort/probes/max.h +++ b/include/cpp-sort/probes/max.h @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,7 @@ #include #include "../detail/equal_range.h" #include "../detail/functional.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" @@ -48,10 +48,9 @@ namespace probe // Indirectly sort the iterators // Copy the iterators in a vector - std::vector iterators; - iterators.reserve(size); - for (ForwardIterator it = first ; it != last ; ++it) { - iterators.push_back(it); + cppsort::detail::immovable_vector iterators(size); + for (auto it = first; it != last; ++it) { + iterators.emplace_back(it); } // Sort the iterators on pointed values From 05960cdd7b939501475796cc0dba647f8d7aaa51 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 7 Jun 2021 16:24:22 +0200 Subject: [PATCH 42/56] More immovable_vector --- include/cpp-sort/detail/merge_insertion_sort.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/cpp-sort/detail/merge_insertion_sort.h b/include/cpp-sort/detail/merge_insertion_sort.h index e2a28c52..14aa1a36 100644 --- a/include/cpp-sort/detail/merge_insertion_sort.h +++ b/include/cpp-sort/detail/merge_insertion_sort.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include "attributes.h" @@ -351,19 +350,18 @@ namespace detail chain.push_back(std::next(first)); // Upper bounds for the insertion of pend elements - std::vector pend; - pend.reserve((size + 1) / 2 - 1); + immovable_vector pend((size + 1) / 2 - 1); for (auto it = first + 2 ; it != end ; it += 2) { auto tmp = chain.insert(chain.end(), std::next(it)); - pend.push_back(tmp); + pend.emplace_back(tmp); } // Add the last element to pend if it exists; when it // exists, it always has to be inserted in the full chain, // so giving it chain.end() as end insertion point is ok if (has_stray) { - pend.push_back(chain.end()); + pend.emplace_back(chain.end()); } //////////////////////////////////////////////////////////// From 1cd52a223d22629d62824f10784557a4be883daf Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 27 Jun 2021 16:03:44 +0200 Subject: [PATCH 43/56] Tools for comparator networks --- docs/Miscellaneous-utilities.md | 52 ++++++++ include/cpp-sort/utility/sorting_networks.h | 128 ++++++++++++++++++++ testsuite/CMakeLists.txt | 1 + testsuite/utility/sorting_networks.cpp | 42 +++++++ 4 files changed, 223 insertions(+) create mode 100644 include/cpp-sort/utility/sorting_networks.h create mode 100644 testsuite/utility/sorting_networks.cpp diff --git a/docs/Miscellaneous-utilities.md b/docs/Miscellaneous-utilities.md index c73bb0c6..3d6cd6e9 100644 --- a/docs/Miscellaneous-utilities.md +++ b/docs/Miscellaneous-utilities.md @@ -247,6 +247,57 @@ using make_index_range = make_integer_range; `size` is a function that can be used to get the size of an iterable. It is equivalent to the C++17 function [`std::size`][std-size] but has an additional tweak so that, if the iterable is not a fixed-size C array and doesn't have a `size` method, it calls `std::distance(std::begin(iter), std::end(iter))` on the iterable. Therefore, this function can also be used for `std::forward_list` as well as some implementations of ranges. +### Sorting network tools + +```cpp +#include +``` + +Some of the library's *fixed-size sorters* implement [sorting networks][sorting-network]. It is a subdomain of sorting that has seen extensive research and there is no way all of the interesting bits could be provided as mere sorters; therefore the following tools are provided specifically to experiment with sorting networks, and with comparator networks more generally. + +All comparator networks execute a fixed sequence of compare-exchanges, operations that compare two elements and exchange them if they are out-of-order. The following `index_pair` class template represents a pair of indices meant to contain the indices used to perform a compare-exchange operation: + +```cpp +template +struct index_pair +{ + IndexType first, second; +}; +``` + +This pretty rough template acts as the base vocabulary type for comparator networks. *Fixed-size sorters* that happen to be sorting networks should provide an `index_pairs()` static methods returning a sequence of `index_pair` sufficient to sort a collection of a given size. + +The following functions accept a sequence of `index_pair` and use it to sort a given random-access collection (represented by an iterator to its first element): + +```cpp +template< + typename RandomAccessIterator, + typename IndexType, + std::size_t N, + typename Compare = std::less<>, + typename Projection = utility::identity +> +auto sort_index_pairs(RandomAccessIterator first, const std::array, N>& index_pairs, + Compare compare={}, Projection projection={}) + -> void; + +template< + typename RandomAccessIterator, + typename IndexType, + std::size_t N, + typename Compare = std::less<>, + typename Projection = utility::identity +> +auto sort_index_pairs_force_unroll(RandomAccessIterator first, + const std::array, N>& index_pairs, + Compare compare={}, Projection projection={}) + -> void; +``` + +`sort_index_pairs` loops over the index pairs in the simplest fashion and calls the compare-exchange operations in the simplest possible way. `sort_index_pairs_force_unroll` is a best effort function trying to achieve the same job by unrolling the loop over indices the best it can - a perfect unrolling is thus attempted, but never guaranteed, which might or might result in faster runtime and/or increased binary size. + +*New in version 1.11.0* + ### `static_const` ```cpp @@ -275,6 +326,7 @@ You can read more about this instantiation pattern in [this article][eric-nieble [p0022]: https://wg21.link/P0022 [pdq-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#pdq_sorter [range-v3]: https://github.com/ericniebler/range-v3 + [sorting-network]: https://en.wikipedia.org/wiki/Sorting_network [std-array]: https://en.cppreference.com/w/cpp/container/array [std-bad-alloc]: https://en.cppreference.com/w/cpp/memory/new/bad_alloc [std-greater]: https://en.cppreference.com/w/cpp/utility/functional/greater diff --git a/include/cpp-sort/utility/sorting_networks.h b/include/cpp-sort/utility/sorting_networks.h new file mode 100644 index 00000000..b5a0caa6 --- /dev/null +++ b/include/cpp-sort/utility/sorting_networks.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_UTILITY_SORTING_NETWORKS_H_ +#define CPPSORT_UTILITY_SORTING_NETWORKS_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include "../detail/swap_if.h" + +namespace cppsort +{ +namespace utility +{ + //////////////////////////////////////////////////////////// + // Extremely simple but sufficient replacement for std::pair + // to represent a pair of indices to compare and exchange in + // sorting networks + + template + struct index_pair + { + IndexType first, second; + }; + + //////////////////////////////////////////////////////////// + // sort_index_pairs + // + // Let the compiler decide whether to unroll or not + // depending on the number of elements to handle + + template< + typename RandomAccessIterator, + typename IndexType, + std::size_t N, + typename Compare = std::less<>, + typename Projection = utility::identity + > + auto sort_index_pairs(RandomAccessIterator first, const std::array, N>& index_pairs, + Compare compare={}, Projection projection={}) + -> void + { + for (const index_pair& pair: index_pairs) { + cppsort::detail::iter_swap_if(first + pair.first, first + pair.second, + compare, projection); + } + } + + //////////////////////////////////////////////////////////// + // sort_index_pairs_force_unroll + // + // This is a best effort function to try to force the + // compiler to generate unrolled code, but the result is + // hardly ever perfect + + namespace detail + { + template + struct index_pair_sorter_force_unroll + { + template< + typename RandomAccessIterator, + typename IndexType, + std::size_t N, + typename Compare, + typename Projection + > + constexpr static auto do_it(RandomAccessIterator first, + const std::array, N>& index_pairs, + Compare compare, Projection projection) + -> void + { + static_assert(CurrentPairIndex < N, "this should never happen, what did you do? o_o"); + cppsort::detail::iter_swap_if(first + index_pairs[CurrentPairIndex].first, + first + index_pairs[CurrentPairIndex].second, + compare, projection); + index_pair_sorter_force_unroll::do_it(first, index_pairs, + std::move(compare), std::move(projection)); + } + + template< + typename RandomAccessIterator, + typename IndexType, + typename Compare, + typename Projection + > + constexpr static auto do_it(RandomAccessIterator first, + const std::array, CurrentPairIndex + 1>& index_pairs, + Compare compare, Projection projection) + -> void + { + cppsort::detail::iter_swap_if(first + index_pairs[CurrentPairIndex].first, + first + index_pairs[CurrentPairIndex].second, + std::move(compare), std::move(projection)); + } + }; + } + + template< + typename RandomAccessIterator, + typename IndexType, + std::size_t N, + typename Compare = std::less<>, + typename Projection = utility::identity + > + auto sort_index_pairs_force_unroll(RandomAccessIterator first, + const std::array, N>& index_pairs, + Compare compare={}, Projection projection={}) + -> void + { + detail::index_pair_sorter_force_unroll<0>::do_it(first, index_pairs, std::move(compare), std::move(projection)); + } + + template + auto sort_index_pairs_force_unroll(RandomAccessIterator, const std::array, 0>&, + Compare, Projection) + -> void + {} +}} + +#endif // CPPSORT_UTILITY_SORTING_NETWORKS_H_ diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 6fc74f3b..88a4954b 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -217,6 +217,7 @@ add_executable(main-tests utility/buffer.cpp utility/chainable_projections.cpp utility/iter_swap.cpp + utility/sorting_networks.cpp ) configure_tests(main-tests) diff --git a/testsuite/utility/sorting_networks.cpp b/testsuite/utility/sorting_networks.cpp new file mode 100644 index 00000000..eda1d8d0 --- /dev/null +++ b/testsuite/utility/sorting_networks.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE( "sorting with index pairs", "[utility][sorting_networks]" ) +{ + // Index pairs for a "brick" sorting network to sort 8 inputs + constexpr std::array, 28> pairs = {{ + {0, 1}, {2, 3}, {4, 5}, {6, 7}, + {1, 2}, {3, 4}, {5, 6}, + {0, 1}, {2, 3}, {4, 5}, {6, 7}, + {1, 2}, {3, 4}, {5, 6}, + {0, 1}, {2, 3}, {4, 5}, {6, 7}, + {1, 2}, {3, 4}, {5, 6}, + {0, 1}, {2, 3}, {4, 5}, {6, 7}, + {1, 2}, {3, 4}, {5, 6}, + }}; + + std::vector vec; + auto distribution = dist::shuffled{}; + distribution(std::back_inserter(vec), 8); + + SECTION( "sort_index_pairs" ) + { + sort_index_pairs(vec.begin(), pairs); + CHECK( std::is_sorted(vec.begin(), vec.end()) ); + } + + SECTION( "sort_index_pairs_force_unroll" ) + { + sort_index_pairs_force_unroll(vec.begin(), pairs); + CHECK( std::is_sorted(vec.begin(), vec.end()) ); + } +} From 15d00b8d276d0e240f63a744e661e9ecfeab75d2 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 29 Jun 2021 11:41:56 +0200 Subject: [PATCH 44/56] Simplify includes for sorting_network_sorter specializations --- include/cpp-sort/detail/sorting_network/sort0.h | 10 +--------- include/cpp-sort/detail/sorting_network/sort1.h | 10 +--------- include/cpp-sort/detail/sorting_network/sort10.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort11.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort12.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort13.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort14.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort15.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort16.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort17.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort18.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort19.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort2.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort20.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort21.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort22.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort23.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort24.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort25.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort26.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort27.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort28.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort29.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort3.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort30.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort31.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort32.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort4.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort5.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort6.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort7.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort8.h | 11 +---------- include/cpp-sort/detail/sorting_network/sort9.h | 11 +---------- include/cpp-sort/fixed/sorting_network_sorter.h | 7 ++++++- 34 files changed, 39 insertions(+), 329 deletions(-) diff --git a/include/cpp-sort/detail/sorting_network/sort0.h b/include/cpp-sort/detail/sorting_network/sort0.h index 27e478a2..22c0ad3e 100644 --- a/include/cpp-sort/detail/sorting_network/sort0.h +++ b/include/cpp-sort/detail/sorting_network/sort0.h @@ -1,18 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT0_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT0_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort1.h b/include/cpp-sort/detail/sorting_network/sort1.h index a07534db..f48be689 100644 --- a/include/cpp-sort/detail/sorting_network/sort1.h +++ b/include/cpp-sort/detail/sorting_network/sort1.h @@ -1,18 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT1_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT1_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort10.h b/include/cpp-sort/detail/sorting_network/sort10.h index eb69e1f5..dc3fbefa 100644 --- a/include/cpp-sort/detail/sorting_network/sort10.h +++ b/include/cpp-sort/detail/sorting_network/sort10.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT10_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT10_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort11.h b/include/cpp-sort/detail/sorting_network/sort11.h index 22176296..90a5ae80 100644 --- a/include/cpp-sort/detail/sorting_network/sort11.h +++ b/include/cpp-sort/detail/sorting_network/sort11.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT11_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT11_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort12.h b/include/cpp-sort/detail/sorting_network/sort12.h index d7e83816..2486f81e 100644 --- a/include/cpp-sort/detail/sorting_network/sort12.h +++ b/include/cpp-sort/detail/sorting_network/sort12.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT12_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT12_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort13.h b/include/cpp-sort/detail/sorting_network/sort13.h index da0c89a7..a02364f8 100644 --- a/include/cpp-sort/detail/sorting_network/sort13.h +++ b/include/cpp-sort/detail/sorting_network/sort13.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT13_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT13_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort14.h b/include/cpp-sort/detail/sorting_network/sort14.h index be21919d..2ca62842 100644 --- a/include/cpp-sort/detail/sorting_network/sort14.h +++ b/include/cpp-sort/detail/sorting_network/sort14.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT14_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT14_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort15.h b/include/cpp-sort/detail/sorting_network/sort15.h index e69e1510..08cdebdb 100644 --- a/include/cpp-sort/detail/sorting_network/sort15.h +++ b/include/cpp-sort/detail/sorting_network/sort15.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT15_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT15_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort16.h b/include/cpp-sort/detail/sorting_network/sort16.h index 1a767be7..4b4dda19 100644 --- a/include/cpp-sort/detail/sorting_network/sort16.h +++ b/include/cpp-sort/detail/sorting_network/sort16.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT16_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT16_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort17.h b/include/cpp-sort/detail/sorting_network/sort17.h index bd866c27..2a852022 100644 --- a/include/cpp-sort/detail/sorting_network/sort17.h +++ b/include/cpp-sort/detail/sorting_network/sort17.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT17_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT17_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort18.h b/include/cpp-sort/detail/sorting_network/sort18.h index 95b91231..942b4a69 100644 --- a/include/cpp-sort/detail/sorting_network/sort18.h +++ b/include/cpp-sort/detail/sorting_network/sort18.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT18_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT18_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort19.h b/include/cpp-sort/detail/sorting_network/sort19.h index 1ac2b16c..15b0f564 100644 --- a/include/cpp-sort/detail/sorting_network/sort19.h +++ b/include/cpp-sort/detail/sorting_network/sort19.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT19_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT19_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort2.h b/include/cpp-sort/detail/sorting_network/sort2.h index a9bd4ee5..cbcfcab4 100644 --- a/include/cpp-sort/detail/sorting_network/sort2.h +++ b/include/cpp-sort/detail/sorting_network/sort2.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT2_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT2_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort20.h b/include/cpp-sort/detail/sorting_network/sort20.h index fc782df5..9a5b28f4 100644 --- a/include/cpp-sort/detail/sorting_network/sort20.h +++ b/include/cpp-sort/detail/sorting_network/sort20.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT20_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT20_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort21.h b/include/cpp-sort/detail/sorting_network/sort21.h index c5b8fc25..b7d6ef0a 100644 --- a/include/cpp-sort/detail/sorting_network/sort21.h +++ b/include/cpp-sort/detail/sorting_network/sort21.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT21_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT21_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort22.h b/include/cpp-sort/detail/sorting_network/sort22.h index ae642911..f014ae70 100644 --- a/include/cpp-sort/detail/sorting_network/sort22.h +++ b/include/cpp-sort/detail/sorting_network/sort22.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT22_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT22_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort23.h b/include/cpp-sort/detail/sorting_network/sort23.h index 76336e8a..bae6be1a 100644 --- a/include/cpp-sort/detail/sorting_network/sort23.h +++ b/include/cpp-sort/detail/sorting_network/sort23.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT23_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT23_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort24.h b/include/cpp-sort/detail/sorting_network/sort24.h index 94869975..692d1724 100644 --- a/include/cpp-sort/detail/sorting_network/sort24.h +++ b/include/cpp-sort/detail/sorting_network/sort24.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT24_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT24_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort25.h b/include/cpp-sort/detail/sorting_network/sort25.h index 256da453..b6b3ae28 100644 --- a/include/cpp-sort/detail/sorting_network/sort25.h +++ b/include/cpp-sort/detail/sorting_network/sort25.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT25_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT25_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort26.h b/include/cpp-sort/detail/sorting_network/sort26.h index 44aa419d..f3f44664 100644 --- a/include/cpp-sort/detail/sorting_network/sort26.h +++ b/include/cpp-sort/detail/sorting_network/sort26.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT26_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT26_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort27.h b/include/cpp-sort/detail/sorting_network/sort27.h index cef9094d..7c969399 100644 --- a/include/cpp-sort/detail/sorting_network/sort27.h +++ b/include/cpp-sort/detail/sorting_network/sort27.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT27_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT27_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort28.h b/include/cpp-sort/detail/sorting_network/sort28.h index bdf1de5a..535a5238 100644 --- a/include/cpp-sort/detail/sorting_network/sort28.h +++ b/include/cpp-sort/detail/sorting_network/sort28.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT28_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT28_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort29.h b/include/cpp-sort/detail/sorting_network/sort29.h index 0d2b9f1a..abe374b6 100644 --- a/include/cpp-sort/detail/sorting_network/sort29.h +++ b/include/cpp-sort/detail/sorting_network/sort29.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT29_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT29_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort3.h b/include/cpp-sort/detail/sorting_network/sort3.h index 8381d343..2c8673d0 100644 --- a/include/cpp-sort/detail/sorting_network/sort3.h +++ b/include/cpp-sort/detail/sorting_network/sort3.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT3_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT3_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort30.h b/include/cpp-sort/detail/sorting_network/sort30.h index a71426d7..48555eee 100644 --- a/include/cpp-sort/detail/sorting_network/sort30.h +++ b/include/cpp-sort/detail/sorting_network/sort30.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT30_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT30_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort31.h b/include/cpp-sort/detail/sorting_network/sort31.h index a501f242..e04ebd41 100644 --- a/include/cpp-sort/detail/sorting_network/sort31.h +++ b/include/cpp-sort/detail/sorting_network/sort31.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT31_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT31_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort32.h b/include/cpp-sort/detail/sorting_network/sort32.h index ea25d07a..bc7c986e 100644 --- a/include/cpp-sort/detail/sorting_network/sort32.h +++ b/include/cpp-sort/detail/sorting_network/sort32.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT32_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT32_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort4.h b/include/cpp-sort/detail/sorting_network/sort4.h index 96505f9d..ea274513 100644 --- a/include/cpp-sort/detail/sorting_network/sort4.h +++ b/include/cpp-sort/detail/sorting_network/sort4.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT4_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT4_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort5.h b/include/cpp-sort/detail/sorting_network/sort5.h index ab14f0bf..94ad4776 100644 --- a/include/cpp-sort/detail/sorting_network/sort5.h +++ b/include/cpp-sort/detail/sorting_network/sort5.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT5_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT5_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort6.h b/include/cpp-sort/detail/sorting_network/sort6.h index 6e89fa8b..c41e2fa0 100644 --- a/include/cpp-sort/detail/sorting_network/sort6.h +++ b/include/cpp-sort/detail/sorting_network/sort6.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT6_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT6_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort7.h b/include/cpp-sort/detail/sorting_network/sort7.h index ccb7540d..ca1a1a21 100644 --- a/include/cpp-sort/detail/sorting_network/sort7.h +++ b/include/cpp-sort/detail/sorting_network/sort7.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT7_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT7_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort8.h b/include/cpp-sort/detail/sorting_network/sort8.h index 597131d9..b5c491a5 100644 --- a/include/cpp-sort/detail/sorting_network/sort8.h +++ b/include/cpp-sort/detail/sorting_network/sort8.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT8_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT8_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/detail/sorting_network/sort9.h b/include/cpp-sort/detail/sorting_network/sort9.h index bdb8389e..6035dcc9 100644 --- a/include/cpp-sort/detail/sorting_network/sort9.h +++ b/include/cpp-sort/detail/sorting_network/sort9.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT9_H_ #define CPPSORT_DETAIL_SORTING_NETWORK_SORT9_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail diff --git a/include/cpp-sort/fixed/sorting_network_sorter.h b/include/cpp-sort/fixed/sorting_network_sorter.h index b6958676..ea070e5a 100644 --- a/include/cpp-sort/fixed/sorting_network_sorter.h +++ b/include/cpp-sort/fixed/sorting_network_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_FIXED_SORTING_NETWORK_SORTER_H_ @@ -61,6 +61,11 @@ namespace cppsort }; } +// Common includes for specializations +#include +#include +#include "../detail/swap_if.h" + // Specializations of sorting_network_sorter for some values of N #include "../detail/sorting_network/sort0.h" #include "../detail/sorting_network/sort1.h" From 6d70316b9017b7489b05ebce9e9d1924b0cf9d9a Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 29 Jun 2021 21:48:19 +0200 Subject: [PATCH 45/56] Add sorting_network_sorter::index_pairs() --- docs/Fixed-size-sorters.md | 19 +++++++++++++-- .../cpp-sort/detail/sorting_network/sort0.h | 7 ++++++ .../cpp-sort/detail/sorting_network/sort1.h | 7 ++++++ .../cpp-sort/detail/sorting_network/sort10.h | 16 +++++++++++++ .../cpp-sort/detail/sorting_network/sort11.h | 16 +++++++++++++ .../cpp-sort/detail/sorting_network/sort12.h | 17 ++++++++++++++ .../cpp-sort/detail/sorting_network/sort13.h | 18 +++++++++++++++ .../cpp-sort/detail/sorting_network/sort14.h | 18 +++++++++++++++ .../cpp-sort/detail/sorting_network/sort15.h | 18 +++++++++++++++ .../cpp-sort/detail/sorting_network/sort16.h | 18 +++++++++++++++ .../cpp-sort/detail/sorting_network/sort17.h | 20 ++++++++++++++++ .../cpp-sort/detail/sorting_network/sort18.h | 21 +++++++++++++++++ .../cpp-sort/detail/sorting_network/sort19.h | 20 ++++++++++++++++ .../cpp-sort/detail/sorting_network/sort2.h | 9 ++++++++ .../cpp-sort/detail/sorting_network/sort20.h | 20 ++++++++++++++++ .../cpp-sort/detail/sorting_network/sort21.h | 20 ++++++++++++++++ .../cpp-sort/detail/sorting_network/sort22.h | 21 +++++++++++++++++ .../cpp-sort/detail/sorting_network/sort23.h | 21 +++++++++++++++++ .../cpp-sort/detail/sorting_network/sort24.h | 21 +++++++++++++++++ .../cpp-sort/detail/sorting_network/sort25.h | 23 +++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort26.h | 23 +++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort27.h | 23 +++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort28.h | 23 +++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort29.h | 23 +++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort3.h | 11 +++++++++ .../cpp-sort/detail/sorting_network/sort30.h | 23 +++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort31.h | 22 ++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort32.h | 22 ++++++++++++++++++ .../cpp-sort/detail/sorting_network/sort4.h | 11 +++++++++ .../cpp-sort/detail/sorting_network/sort5.h | 13 +++++++++++ .../cpp-sort/detail/sorting_network/sort6.h | 13 +++++++++++ .../cpp-sort/detail/sorting_network/sort7.h | 14 +++++++++++ .../cpp-sort/detail/sorting_network/sort8.h | 14 +++++++++++ .../cpp-sort/detail/sorting_network/sort9.h | 15 ++++++++++++ .../cpp-sort/fixed/sorting_network_sorter.h | 4 +++- testsuite/utility/sorting_networks.cpp | 23 +++++++++++++++++++ 36 files changed, 624 insertions(+), 3 deletions(-) diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index 82eea144..a06bcf5d 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -88,7 +88,7 @@ Note that this fixed-sized sorter is *not* move-optimal: it tries to perform a f #include ``` -This sorter provides [sorting network](https://en.wikipedia.org/wiki/Sorting_network) algorithms to sort collections of size 0 thru 32. While using a generic algorithm for the task such as a Batcher's odd-even mergesort may be too slow to be usable, the resulting unrolled sorting networks may be fast enough and even tend to be faster than everything else when it comes to sorting small arrays of integers without requiring additional memory. +This sorter provides size-optimal [sorting networks][sorting-network] for 0 thru 32 inputs. While using a generic algorithm for the task such as a Batcher's odd-even mergesort may be too slow to be usable, the resulting unrolled sorting networks may be fast enough and even tend to be faster than everything else when it comes to sorting small arrays of integers without requiring additional memory. ```cpp template @@ -107,8 +107,23 @@ One of the main advantages of sorting networks is the fixed number of CEUs requi *Note:* don't be fooled by the name; none of the algorithms in this fixed-size sorter explicitly perform any operation in parallel. Everything is sequential. The algorithms are but long sequences of compare-exchange units. +All specializations of `sorting_network_sorter` provide a `index_pairs() static` function template which returns an [`std::array`][std-array] of [`utility::index_pair`][utility-sorting-networks]. Those pairs represent the indices used in the CEUs of the network and can be passed manipulated and passed to dedicated [sorting network tools][utility-sorting-networks] from the library's utility module. The function is templated of the index/difference type, which must be constructible from `int`. + +```cpp +template +static constexpr auto index_pairs() + -> std::array, /* Number of CEUs in the network */>; +``` + *Changed in version 1.2.0:* sorting 21 inputs requires 100 CEUs instead of 101. *Changed in version 1.3.0:* sorting 23, 24, 25 and 26 inputs respectively require 115, 120, 132 and 139 CEUs instead of 116, 121, 133 and 140. -*Changed in version 1.8.0:* sorting 18 inputs requires 77 CEUs instead of 78. \ No newline at end of file +*Changed in version 1.8.0:* sorting 18 inputs requires 77 CEUs instead of 78. + +*Changed in version 1.10.0:* added `sorting_network_sorter::index_pairs` + + + [sorting-network]: https://en.wikipedia.org/wiki/Sorting_network + [std-array]: + [utility-sorting-networks]: https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#Sorting-network-tools diff --git a/include/cpp-sort/detail/sorting_network/sort0.h b/include/cpp-sort/detail/sorting_network/sort0.h index 22c0ad3e..1b103368 100644 --- a/include/cpp-sort/detail/sorting_network/sort0.h +++ b/include/cpp-sort/detail/sorting_network/sort0.h @@ -24,6 +24,13 @@ namespace detail Compare={}, Projection={}) const -> void {} + + template + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort1.h b/include/cpp-sort/detail/sorting_network/sort1.h index f48be689..bb79c981 100644 --- a/include/cpp-sort/detail/sorting_network/sort1.h +++ b/include/cpp-sort/detail/sorting_network/sort1.h @@ -24,6 +24,13 @@ namespace detail Compare={}, Projection={}) const -> void {} + + template + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort10.h b/include/cpp-sort/detail/sorting_network/sort10.h index dc3fbefa..e5ed6a93 100644 --- a/include/cpp-sort/detail/sorting_network/sort10.h +++ b/include/cpp-sort/detail/sorting_network/sort10.h @@ -54,6 +54,22 @@ namespace detail iter_swap_if(first + 5u, first + 6u, compare, projection); iter_swap_if(first + 4u, first + 5u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 29> + { + return {{ + {0, 8}, {1, 9}, {2, 7}, {3, 5}, {4, 6}, + {0, 2}, {1, 4}, {5, 8}, {7, 9}, + {0, 3}, {2, 4}, {5, 7}, {6, 9}, + {0, 1}, {3, 6}, {8, 9}, + {1, 5}, {2, 3}, {4, 8}, {6, 7}, + {1, 2}, {3, 5}, {4, 6}, {7, 8}, + {2, 3}, {4, 5}, {6, 7}, + {3, 4}, {5, 6}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort11.h b/include/cpp-sort/detail/sorting_network/sort11.h index 90a5ae80..f5014df9 100644 --- a/include/cpp-sort/detail/sorting_network/sort11.h +++ b/include/cpp-sort/detail/sorting_network/sort11.h @@ -60,6 +60,22 @@ namespace detail iter_swap_if(first + 5u, first + 6u, compare, projection); iter_swap_if(first + 7u, first + 8u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 35> + { + return {{ + {0, 9}, {1, 6}, {2, 4}, {3, 7}, {5, 8}, + {0, 1}, {3, 5}, {4, 10}, {6, 9}, {7, 8}, + {1, 3}, {2, 5}, {4, 7}, {8, 10}, + {0, 4}, {1, 2}, {3, 7}, {5, 9}, {6, 8}, + {0, 1}, {2, 6}, {4, 5}, {7, 8}, {9, 10}, + {2, 4}, {3, 6}, {5, 7}, {8, 9}, + {1, 2}, {3, 4}, {5, 6}, {7, 8}, + {2, 3}, {4, 5}, {6, 7}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort12.h b/include/cpp-sort/detail/sorting_network/sort12.h index 2486f81e..2c3d0eb3 100644 --- a/include/cpp-sort/detail/sorting_network/sort12.h +++ b/include/cpp-sort/detail/sorting_network/sort12.h @@ -64,6 +64,23 @@ namespace detail iter_swap_if(first + 5u, first + 6u, compare, projection); iter_swap_if(first + 7u, first + 8u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 39> + { + return {{ + {0, 8}, {1, 7}, {2, 6}, {3, 11}, {4, 10}, {5, 9}, + {0, 1}, {2, 5}, {3, 4}, {6, 9}, {7, 8}, {10, 11}, + {0, 2}, {1, 6}, {5, 10}, {9, 11}, + {0, 3}, {1, 2}, {4, 6}, {5, 7}, {8, 11}, {9, 10}, + {1, 4}, {3, 5}, {6, 8}, {7, 10}, + {1, 3}, {2, 5}, {6, 9}, {8, 10}, + {2, 3}, {4, 5}, {6, 7}, {8, 9}, + {4, 6}, {5, 7}, + {3, 4}, {5, 6}, {7, 8}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort13.h b/include/cpp-sort/detail/sorting_network/sort13.h index a02364f8..320aa83d 100644 --- a/include/cpp-sort/detail/sorting_network/sort13.h +++ b/include/cpp-sort/detail/sorting_network/sort13.h @@ -70,6 +70,24 @@ namespace detail iter_swap_if(first + 3u, first + 4u, compare, projection); iter_swap_if(first + 5u, first + 6u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 45> + { + return {{ + {0, 12}, {1, 10}, {2, 9}, {3, 7}, {5, 11}, {6, 8}, + {1, 6}, {2, 3}, {4, 11}, {7, 9}, {8, 10}, + {0, 4}, {1, 2}, {3, 6}, {7, 8}, {9, 10}, {11, 12}, + {4, 6}, {5, 9}, {8, 11}, {10, 12}, + {0, 5}, {3, 8}, {4, 7}, {6, 11}, {9, 10}, + {0, 1}, {2, 5}, {6, 9}, {7, 8}, {10, 11}, + {1, 3}, {2, 4}, {5, 6}, {9, 10}, + {1, 2}, {3, 4}, {5, 7}, {6, 8}, + {2, 3}, {4, 5}, {6, 7}, {8, 9}, + {3, 4}, {5, 6}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort14.h b/include/cpp-sort/detail/sorting_network/sort14.h index 2ca62842..5dccda23 100644 --- a/include/cpp-sort/detail/sorting_network/sort14.h +++ b/include/cpp-sort/detail/sorting_network/sort14.h @@ -76,6 +76,24 @@ namespace detail iter_swap_if(first + 6u, first + 7u, compare, projection); iter_swap_if(first + 8u, first + 9u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 51> + { + return {{ + {0, 6}, {1, 11}, {2, 12}, {3, 10}, {4, 5}, {7, 13}, {8, 9}, + {1, 2}, {3, 7}, {4, 8}, {5, 9}, {6, 10}, {11, 12}, + {0, 4}, {1, 3}, {5, 6}, {7, 8}, {9, 13}, {10, 12}, + {0, 1}, {2, 9}, {3, 7}, {4, 11}, {6, 10}, {12, 13}, + {2, 5}, {4, 7}, {6, 9}, {8, 11}, + {1, 2}, {3, 4}, {6, 7}, {9, 10}, {11, 12}, + {1, 3}, {2, 4}, {5, 6}, {7, 8}, {9, 11}, {10, 12}, + {2, 3}, {4, 7}, {6, 9}, {10, 11}, + {4, 5}, {6, 7}, {8, 9}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort15.h b/include/cpp-sort/detail/sorting_network/sort15.h index 08cdebdb..e8c84379 100644 --- a/include/cpp-sort/detail/sorting_network/sort15.h +++ b/include/cpp-sort/detail/sorting_network/sort15.h @@ -81,6 +81,24 @@ namespace detail iter_swap_if(first + 6u, first + 7u, compare, projection); iter_swap_if(first + 8u, first + 9u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 56> + { + return {{ + {1, 2}, {3, 10}, {4, 14}, {5, 8}, {6, 13}, {7, 12}, {9, 11}, + {0, 14}, {1, 5}, {2, 8}, {3, 7}, {6, 9}, {10, 12}, {11, 13}, + {0, 7}, {1, 6}, {2, 9}, {4, 10}, {5, 11}, {8, 13}, {12, 14}, + {0, 6}, {2, 4}, {3, 5}, {7, 11}, {8, 10}, {9, 12}, {13, 14}, + {0, 3}, {1, 2}, {4, 7}, {5, 9}, {6, 8}, {10, 11}, {12, 13}, + {0, 1}, {2, 3}, {4, 6}, {7, 9}, {10, 12}, {11, 13}, + {1, 2}, {3, 5}, {8, 10}, {11, 12}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, + {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, + {5, 6}, {7, 8}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort16.h b/include/cpp-sort/detail/sorting_network/sort16.h index 4b4dda19..7b44df5e 100644 --- a/include/cpp-sort/detail/sorting_network/sort16.h +++ b/include/cpp-sort/detail/sorting_network/sort16.h @@ -85,6 +85,24 @@ namespace detail iter_swap_if(first + 6u, first + 7u, compare, projection); iter_swap_if(first + 8u, first + 9u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 60> + { + return {{ + {0, 13}, {1, 12}, {2, 15}, {3, 14}, {4, 8}, {5, 6}, {7, 11}, {9, 10}, + {0, 5}, {1, 7}, {2, 9}, {3, 4}, {6, 13}, {8, 14}, {10, 15}, {11, 12}, + {0, 1}, {2, 3}, {4, 5}, {6, 8}, {7, 9}, {10, 11}, {12, 13}, {14, 15}, + {0, 2}, {1, 3}, {4, 10}, {5, 11}, {6, 7}, {8, 9}, {12, 14}, {13, 15}, + {1, 2}, {3, 12}, {4, 6}, {5, 7}, {8, 10}, {9, 11}, {13, 14}, + {1, 4}, {2, 6}, {5, 8}, {7, 10}, {9, 13}, {11, 14}, + {2, 4}, {3, 6}, {9, 12}, {11, 13}, + {3, 5}, {6, 8}, {7, 9}, {10, 12}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, + {6, 7}, {8, 9}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort17.h b/include/cpp-sort/detail/sorting_network/sort17.h index 2a852022..77bb45b6 100644 --- a/include/cpp-sort/detail/sorting_network/sort17.h +++ b/include/cpp-sort/detail/sorting_network/sort17.h @@ -96,6 +96,26 @@ namespace detail iter_swap_if(first + 8u, first + 9u, compare, projection); iter_swap_if(first + 7u, first + 8u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 71> + { + return {{ + {0, 11}, {1, 15}, {2, 10}, {3, 5}, {4, 6}, {8, 12}, {9, 16}, {13, 14}, + {0, 6}, {1, 13}, {2, 8}, {4, 14}, {5, 15}, {7, 11}, + {0, 8}, {3, 7}, {4, 9}, {6, 16}, {10, 11}, {12, 14}, + {0, 2}, {1, 4}, {5, 6}, {7, 13}, {8, 9}, {10, 12}, {11, 14}, {15, 16}, + {0, 3}, {2, 5}, {6, 11}, {7, 10}, {9, 13}, {12, 15}, {14, 16}, + {0, 1}, {3, 4}, {5, 10}, {6, 9}, {7, 8}, {11, 15}, {13, 14}, + {1, 2}, {3, 7}, {4, 8}, {6, 12}, {11, 13}, {14, 15}, + {1, 3}, {2, 7}, {4, 5}, {9, 11}, {10, 12}, {13, 14}, + {2, 3}, {4, 6}, {5, 7}, {8, 10}, + {3, 4}, {6, 8}, {7, 9}, {10, 12}, + {5, 6}, {7, 8}, {9, 10}, {11, 12}, + {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort18.h b/include/cpp-sort/detail/sorting_network/sort18.h index 942b4a69..f5a62023 100644 --- a/include/cpp-sort/detail/sorting_network/sort18.h +++ b/include/cpp-sort/detail/sorting_network/sort18.h @@ -102,6 +102,27 @@ namespace detail iter_swap_if(first + 9u, first + 10u, compare, projection); iter_swap_if(first + 11u, first + 12u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 77> + { + return {{ + {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, + {1, 5}, {2, 6}, {3, 7}, {4, 10}, {8, 16}, {9, 17}, {12, 14}, {13, 15}, + {0, 8}, {1, 10}, {2, 12}, {3, 14}, {6, 13}, {7, 15}, {9, 16}, {11, 17}, + {0, 4}, {1, 9}, {5, 17}, {8, 11}, {10, 16}, + {0, 2}, {1, 6}, {4, 10}, {5, 9}, {14, 16}, {15, 17}, + {1, 2}, {3, 10}, {4, 12}, {5, 7}, {6, 14}, {9, 13}, {15, 16}, + {3, 8}, {5, 12}, {7, 11}, {9, 10}, + {3, 4}, {6, 8}, {7, 14}, {9, 12}, {11, 13}, + {1, 3}, {2, 4}, {7, 9}, {8, 12}, {11, 15}, {13, 16}, + {2, 3}, {4, 5}, {6, 7}, {10, 11}, {12, 14}, {13, 15}, + {4, 6}, {5, 8}, {9, 10}, {11, 14}, + {3, 4}, {5, 7}, {8, 9}, {10, 12}, {13, 14}, + {5, 6}, {7, 8}, {9, 10}, {11, 12}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort19.h b/include/cpp-sort/detail/sorting_network/sort19.h index 15b0f564..c98812d2 100644 --- a/include/cpp-sort/detail/sorting_network/sort19.h +++ b/include/cpp-sort/detail/sorting_network/sort19.h @@ -110,6 +110,26 @@ namespace detail iter_swap_if(first + 12u, first + 13u, compare, projection); iter_swap_if(first + 14u, first + 15u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 85> + { + return {{ + {0, 12}, {1, 4}, {2, 8}, {3, 5}, {6, 17}, {7, 11}, {9, 14}, {10, 13}, {15, 16}, + {0, 2}, {1, 7}, {3, 6}, {4, 11}, {5, 17}, {8, 12}, {10, 15}, {13, 16}, {14, 18}, + {3, 10}, {4, 14}, {5, 15}, {6, 13}, {7, 9}, {11, 17}, {16, 18}, + {0, 7}, {1, 10}, {4, 6}, {9, 15}, {11, 16}, {12, 17}, {13, 14}, + {0, 3}, {2, 6}, {5, 7}, {8, 11}, {12, 16}, + {1, 8}, {2, 9}, {3, 4}, {6, 15}, {7, 13}, {10, 11}, {12, 18}, + {1, 3}, {2, 5}, {6, 9}, {7, 12}, {8, 10}, {11, 14}, {17, 18}, + {0, 1}, {2, 3}, {4, 8}, {6, 10}, {9, 12}, {14, 15}, {16, 17}, + {1, 2}, {5, 8}, {6, 7}, {9, 11}, {10, 13}, {14, 16}, {15, 17}, + {3, 6}, {4, 5}, {7, 9}, {8, 10}, {11, 12}, {13, 14}, {15, 16}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 13}, {12, 14}, + {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort2.h b/include/cpp-sort/detail/sorting_network/sort2.h index cbcfcab4..acbf77e4 100644 --- a/include/cpp-sort/detail/sorting_network/sort2.h +++ b/include/cpp-sort/detail/sorting_network/sort2.h @@ -26,6 +26,15 @@ namespace detail { iter_swap_if(first, first + 1u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 1> + { + return {{ + {0, 1}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort20.h b/include/cpp-sort/detail/sorting_network/sort20.h index 9a5b28f4..ad03df6d 100644 --- a/include/cpp-sort/detail/sorting_network/sort20.h +++ b/include/cpp-sort/detail/sorting_network/sort20.h @@ -116,6 +116,26 @@ namespace detail iter_swap_if(first + 13u, first + 14u, compare, projection); iter_swap_if(first + 15u, first + 16u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 91> + { + return {{ + {0, 3}, {1, 7}, {2, 5}, {4, 8}, {6, 9}, {10, 13}, {11, 15}, {12, 18}, {14, 17}, {16, 19}, + {0, 14}, {1, 11}, {2, 16}, {3, 17}, {4, 12}, {5, 19}, {6, 10}, {7, 15}, {8, 18}, {9, 13}, + {0, 4}, {1, 2}, {3, 8}, {5, 7}, {11, 16}, {12, 14}, {15, 19}, {17, 18}, + {1, 6}, {2, 12}, {3, 5}, {4, 11}, {7, 17}, {8, 15}, {13, 18}, {14, 16}, + {0, 1}, {2, 6}, {7, 10}, {9, 12}, {13, 17}, {18, 19}, + {1, 6}, {5, 9}, {7, 11}, {8, 12}, {10, 14}, {13, 18}, + {3, 5}, {4, 7}, {8, 10}, {9, 11}, {12, 15}, {14, 16}, + {1, 3}, {2, 4}, {5, 7}, {6, 10}, {9, 13}, {12, 14}, {15, 17}, {16, 18}, + {1, 2}, {3, 4}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {15, 16}, {17, 18}, + {2, 3}, {4, 6}, {5, 8}, {7, 9}, {10, 12}, {11, 14}, {13, 15}, {16, 17}, + {4, 5}, {6, 8}, {7, 10}, {9, 12}, {11, 13}, {14, 15}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort21.h b/include/cpp-sort/detail/sorting_network/sort21.h index b7d6ef0a..33f53c37 100644 --- a/include/cpp-sort/detail/sorting_network/sort21.h +++ b/include/cpp-sort/detail/sorting_network/sort21.h @@ -125,6 +125,26 @@ namespace detail iter_swap_if(first + 14u, first + 15u, compare, projection); iter_swap_if(first + 16u, first + 17u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 100> + { + return {{ + {0, 7}, {1, 10}, {3, 5}, {4, 8}, {6, 13}, {9, 19}, {11, 14}, {12, 17}, {15, 16}, {18, 20}, + {0, 11}, {1, 15}, {2, 12}, {3, 4}, {5, 8}, {6, 9}, {7, 14}, {10, 16}, {13, 19}, {17, 20}, + {0, 6}, {1, 3}, {2, 18}, {4, 15}, {5, 10}, {8, 16}, {11, 17}, {12, 13}, {14, 20}, + {2, 6}, {5, 12}, {7, 18}, {8, 14}, {9, 11}, {10, 17}, {13, 19}, {16, 20}, + {1, 2}, {4, 7}, {5, 9}, {6, 17}, {10, 13}, {11, 12}, {14, 19}, {15, 18}, + {0, 2}, {3, 6}, {4, 5}, {7, 10}, {8, 11}, {9, 15}, {12, 16}, {13, 18}, {14, 17}, {19, 20}, + {0, 1}, {2, 3}, {5, 9}, {6, 12}, {7, 8}, {11, 14}, {13, 15}, {16, 19}, {17, 18}, + {1, 2}, {3, 9}, {6, 13}, {10, 11}, {12, 15}, {16, 17}, {18, 19}, + {1, 4}, {2, 5}, {3, 7}, {6, 10}, {8, 9}, {11, 12}, {13, 14}, {17, 18}, + {2, 4}, {5, 6}, {7, 8}, {9, 11}, {10, 13}, {12, 15}, {14, 16}, + {3, 4}, {5, 7}, {6, 8}, {9, 10}, {11, 13}, {12, 14}, {15, 16}, + {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort22.h b/include/cpp-sort/detail/sorting_network/sort22.h index f014ae70..8e46fd8b 100644 --- a/include/cpp-sort/detail/sorting_network/sort22.h +++ b/include/cpp-sort/detail/sorting_network/sort22.h @@ -132,6 +132,27 @@ namespace detail iter_swap_if(first + 12u, first + 13u, compare, projection); iter_swap_if(first + 14u, first + 15u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 107> + { + return {{ + {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, + {0, 12}, {1, 13}, {2, 6}, {3, 7}, {4, 10}, {8, 20}, {9, 21}, {11, 17}, {14, 18}, {15, 19}, + {0, 2}, {1, 6}, {3, 12}, {4, 16}, {5, 17}, {7, 13}, {8, 14}, {9, 18}, {15, 20}, {19, 21}, + {0, 8}, {1, 15}, {2, 14}, {3, 9}, {5, 11}, {6, 20}, {7, 19}, {10, 16}, {12, 18}, {13, 21}, + {0, 4}, {1, 10}, {3, 8}, {5, 9}, {7, 14}, {11, 20}, {12, 16}, {13, 18}, {17, 21}, + {1, 3}, {2, 5}, {4, 8}, {6, 9}, {7, 10}, {11, 14}, {12, 15}, {13, 17}, {16, 19}, {18, 20}, + {2, 4}, {3, 12}, {5, 8}, {6, 11}, {9, 18}, {10, 15}, {13, 16}, {17, 19}, + {1, 2}, {3, 4}, {5, 7}, {6, 12}, {8, 11}, {9, 15}, {10, 13}, {14, 16}, {17, 18}, {19, 20}, + {2, 3}, {4, 5}, {7, 12}, {8, 10}, {9, 14}, {11, 13}, {16, 17}, {18, 19}, + {4, 6}, {5, 8}, {9, 11}, {10, 12}, {13, 16}, {15, 17}, + {3, 4}, {6, 7}, {9, 10}, {11, 12}, {14, 15}, {17, 18}, + {5, 6}, {7, 8}, {10, 11}, {13, 14}, {15, 16}, + {6, 7}, {8, 9}, {12, 13}, {14, 15}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort23.h b/include/cpp-sort/detail/sorting_network/sort23.h index bae6be1a..8f69d845 100644 --- a/include/cpp-sort/detail/sorting_network/sort23.h +++ b/include/cpp-sort/detail/sorting_network/sort23.h @@ -140,6 +140,27 @@ namespace detail iter_swap_if(first + 15u, first + 16u, compare, projection); iter_swap_if(first + 17u, first + 18u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 115> + { + return {{ + {0, 20}, {1, 12}, {2, 16}, {4, 6}, {5, 10}, {7, 21}, {8, 14}, {9, 15}, {11, 22}, {13, 18}, {17, 19}, + {0, 3}, {1, 11}, {2, 7}, {4, 17}, {5, 13}, {6, 19}, {8, 9}, {10, 18}, {12, 22}, {14, 15}, {16, 21}, + {0, 1}, {2, 4}, {3, 12}, {5, 8}, {6, 9}, {7, 10}, {11, 20}, {13, 16}, {14, 17}, {15, 18}, {19, 21}, + {2, 5}, {4, 8}, {6, 11}, {7, 14}, {9, 16}, {12, 17}, {15, 19}, {18, 21}, + {1, 8}, {3, 14}, {4, 7}, {9, 20}, {10, 12}, {11, 13}, {15, 22}, {16, 19}, + {0, 7}, {1, 5}, {3, 4}, {6, 11}, {8, 15}, {9, 14}, {10, 13}, {12, 17}, {18, 22}, {19, 20}, + {0, 2}, {1, 6}, {4, 7}, {5, 9}, {8, 10}, {13, 15}, {14, 18}, {16, 19}, {17, 22}, {20, 21}, + {2, 3}, {4, 5}, {6, 8}, {7, 9}, {10, 11}, {12, 13}, {14, 16}, {15, 17}, {18, 19}, {21, 22}, + {1, 2}, {3, 6}, {4, 10}, {7, 8}, {9, 11}, {12, 14}, {13, 19}, {15, 16}, {17, 20}, + {2, 3}, {5, 10}, {6, 7}, {8, 9}, {13, 18}, {14, 15}, {16, 17}, {20, 21}, + {3, 4}, {5, 7}, {10, 12}, {11, 13}, {16, 18}, {19, 20}, + {4, 6}, {8, 10}, {9, 12}, {11, 14}, {13, 15}, {17, 19}, + {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort24.h b/include/cpp-sort/detail/sorting_network/sort24.h index 692d1724..71d14d5e 100644 --- a/include/cpp-sort/detail/sorting_network/sort24.h +++ b/include/cpp-sort/detail/sorting_network/sort24.h @@ -145,6 +145,27 @@ namespace detail iter_swap_if(first + 15u, first + 16u, compare, projection); iter_swap_if(first + 17u, first + 18u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 120> + { + return {{ + {0, 20}, {1, 12}, {2, 16}, {3, 23}, {4, 6}, {5, 10}, {7, 21}, {8, 14}, {9, 15}, {11, 22}, {13, 18}, {17, 19}, + {0, 3}, {1, 11}, {2, 7}, {4, 17}, {5, 13}, {6, 19}, {8, 9}, {10, 18}, {12, 22}, {14, 15}, {16, 21}, {20, 23}, + {0, 1}, {2, 4}, {3, 12}, {5, 8}, {6, 9}, {7, 10}, {11, 20}, {13, 16}, {14, 17}, {15, 18}, {19, 21}, {22, 23}, + {2, 5}, {4, 8}, {6, 11}, {7, 14}, {9, 16}, {12, 17}, {15, 19}, {18, 21}, + {1, 8}, {3, 14}, {4, 7}, {9, 20}, {10, 12}, {11, 13}, {15, 22}, {16, 19}, + {0, 7}, {1, 5}, {3, 4}, {6, 11}, {8, 15}, {9, 14}, {10, 13}, {12, 17}, {16, 23}, {18, 22}, {19, 20}, + {0, 2}, {1, 6}, {4, 7}, {5, 9}, {8, 10}, {13, 15}, {14, 18}, {16, 19}, {17, 22}, {21, 23}, + {2, 3}, {4, 5}, {6, 8}, {7, 9}, {10, 11}, {12, 13}, {14, 16}, {15, 17}, {18, 19}, {20, 21}, + {1, 2}, {3, 6}, {4, 10}, {7, 8}, {9, 11}, {12, 14}, {13, 19}, {15, 16}, {17, 20}, {21, 22}, + {2, 3}, {5, 10}, {6, 7}, {8, 9}, {13, 18}, {14, 15}, {16, 17}, {20, 21}, + {3, 4}, {5, 7}, {10, 12}, {11, 13}, {16, 18}, {19, 20}, + {4, 6}, {8, 10}, {9, 12}, {11, 14}, {13, 15}, {17, 19}, + {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort25.h b/include/cpp-sort/detail/sorting_network/sort25.h index b6b3ae28..f3ee3085 100644 --- a/include/cpp-sort/detail/sorting_network/sort25.h +++ b/include/cpp-sort/detail/sorting_network/sort25.h @@ -157,6 +157,29 @@ namespace detail iter_swap_if(first + 16u, first + 17u, compare, projection); iter_swap_if(first + 18u, first + 19u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 132> + { + return {{ + {0, 2}, {1, 8}, {3, 18}, {4, 17}, {5, 20}, {6, 19}, {7, 9}, {10, 11}, {12, 13}, {14, 16}, {15, 22}, {21, 23}, + {0, 3}, {1, 15}, {2, 18}, {4, 12}, {5, 21}, {6, 10}, {7, 14}, {8, 22}, {9, 16}, {11, 19}, {13, 17}, {20, 23}, + {0, 4}, {1, 7}, {2, 13}, {3, 12}, {5, 6}, {8, 14}, {9, 15}, {10, 21}, {11, 20}, {16, 22}, {17, 18}, {19, 23}, + {0, 5}, {2, 11}, {3, 6}, {4, 10}, {7, 16}, {8, 9}, {12, 21}, {13, 19}, {14, 15}, {17, 20}, {18, 23}, + {2, 7}, {6, 9}, {8, 11}, {14, 24}, {18, 21}, + {3, 8}, {7, 10}, {11, 12}, {13, 14}, {15, 21}, {18, 20}, {22, 24}, + {4, 13}, {10, 16}, {11, 15}, {18, 24}, {19, 22}, + {1, 4}, {8, 11}, {9, 19}, {13, 17}, {14, 18}, {16, 20}, {23, 24}, + {0, 1}, {4, 5}, {6, 13}, {9, 14}, {10, 17}, {12, 16}, {18, 19}, {20, 21}, {22, 23}, + {2, 6}, {3, 4}, {5, 13}, {7, 9}, {12, 18}, {15, 17}, {16, 19}, {20, 22}, {21, 23}, + {1, 2}, {5, 8}, {6, 7}, {9, 10}, {11, 13}, {14, 15}, {17, 20}, {21, 22}, + {1, 3}, {2, 4}, {5, 6}, {7, 11}, {8, 9}, {10, 13}, {12, 14}, {15, 16}, {17, 18}, {19, 20}, + {2, 3}, {4, 8}, {6, 7}, {9, 12}, {10, 11}, {13, 14}, {15, 17}, {16, 18}, {20, 21}, + {3, 5}, {4, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 15}, {14, 17}, {16, 19}, + {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort26.h b/include/cpp-sort/detail/sorting_network/sort26.h index f3f44664..47db075b 100644 --- a/include/cpp-sort/detail/sorting_network/sort26.h +++ b/include/cpp-sort/detail/sorting_network/sort26.h @@ -164,6 +164,29 @@ namespace detail iter_swap_if(first + 18u, first + 19u, compare, projection); iter_swap_if(first + 20u, first + 21u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 139> + { + return {{ + {0, 25}, {1, 3}, {2, 9}, {4, 19}, {5, 18}, {6, 21}, {7, 20}, {8, 10}, {11, 12}, {13, 14}, {15, 17}, {16, 23}, {22, 24}, + {1, 4}, {2, 16}, {3, 19}, {5, 13}, {6, 22}, {7, 11}, {8, 15}, {9, 23}, {10, 17}, {12, 20}, {14, 18}, {21, 24}, + {1, 5}, {2, 8}, {3, 14}, {4, 13}, {6, 7}, {9, 15}, {10, 16}, {11, 22}, {12, 21}, {17, 23}, {18, 19}, {20, 24}, + {0, 10}, {1, 6}, {3, 7}, {4, 11}, {5, 12}, {13, 20}, {14, 21}, {15, 25}, {18, 22}, {19, 24}, + {0, 4}, {8, 10}, {12, 13}, {15, 17}, {21, 25}, + {0, 2}, {4, 8}, {10, 12}, {13, 15}, {17, 21}, {23, 25}, + {0, 1}, {2, 3}, {4, 5}, {8, 14}, {9, 13}, {11, 17}, {12, 16}, {20, 21}, {22, 23}, {24, 25}, + {1, 4}, {3, 10}, {6, 9}, {7, 13}, {8, 11}, {12, 18}, {14, 17}, {15, 22}, {16, 19}, {21, 24}, + {2, 6}, {3, 8}, {5, 7}, {9, 12}, {13, 16}, {17, 22}, {18, 20}, {19, 23}, + {1, 2}, {4, 6}, {5, 9}, {7, 10}, {11, 12}, {13, 14}, {15, 18}, {16, 20}, {19, 21}, {23, 24}, + {2, 4}, {3, 5}, {7, 13}, {8, 9}, {10, 14}, {11, 15}, {12, 18}, {16, 17}, {20, 22}, {21, 23}, + {3, 4}, {6, 9}, {7, 11}, {10, 12}, {13, 15}, {14, 18}, {16, 19}, {21, 22}, + {5, 7}, {6, 8}, {9, 13}, {10, 11}, {12, 16}, {14, 15}, {17, 19}, {18, 20}, + {5, 6}, {7, 8}, {9, 10}, {11, 13}, {12, 14}, {15, 16}, {17, 18}, {19, 20}, + {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort27.h b/include/cpp-sort/detail/sorting_network/sort27.h index 7c969399..ce0896b3 100644 --- a/include/cpp-sort/detail/sorting_network/sort27.h +++ b/include/cpp-sort/detail/sorting_network/sort27.h @@ -175,6 +175,29 @@ namespace detail iter_swap_if(first + 23u, first + 24u, compare, projection); iter_swap_if(first + 25u, first + 26u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 150> + { + return {{ + {0, 9}, {1, 6}, {2, 4}, {3, 7}, {5, 8}, {11, 24}, {12, 23}, {13, 26}, {14, 25}, {15, 19}, {16, 17}, {18, 22}, {20, 21}, + {0, 1}, {3, 5}, {4, 10}, {6, 9}, {7, 8}, {11, 16}, {12, 18}, {13, 20}, {14, 15}, {17, 24}, {19, 25}, {21, 26}, {22, 23}, + {1, 3}, {2, 5}, {4, 7}, {8, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 19}, {18, 20}, {21, 22}, {23, 24}, {25, 26}, + {0, 4}, {1, 2}, {3, 7}, {5, 9}, {6, 8}, {11, 13}, {12, 14}, {15, 21}, {16, 22}, {17, 18}, {19, 20}, {23, 25}, {24, 26}, + {0, 1}, {2, 6}, {4, 5}, {7, 8}, {9, 10}, {12, 13}, {14, 23}, {15, 17}, {16, 18}, {19, 21}, {20, 22}, {24, 25}, + {0, 11}, {2, 4}, {3, 6}, {5, 7}, {8, 9}, {12, 15}, {13, 17}, {16, 19}, {18, 21}, {20, 24}, {22, 25}, + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {13, 15}, {14, 17}, {20, 23}, {22, 24}, + {1, 12}, {2, 3}, {4, 5}, {6, 7}, {14, 16}, {17, 19}, {18, 20}, {21, 23}, + {2, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, + {3, 14}, {4, 15}, {5, 16}, {10, 21}, {17, 18}, {19, 20}, + {6, 17}, {7, 18}, {8, 19}, {9, 20}, {10, 13}, {14, 22}, {15, 23}, {16, 24}, + {6, 10}, {7, 14}, {8, 11}, {9, 12}, {17, 25}, {18, 26}, {19, 23}, {20, 24}, + {4, 8}, {5, 9}, {11, 15}, {12, 16}, {13, 17}, {18, 22}, {21, 25}, {24, 26}, + {2, 4}, {3, 5}, {6, 8}, {7, 9}, {10, 11}, {12, 14}, {13, 15}, {16, 18}, {17, 19}, {20, 22}, {21, 23}, {25, 26}, + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort28.h b/include/cpp-sort/detail/sorting_network/sort28.h index 535a5238..a62d4faa 100644 --- a/include/cpp-sort/detail/sorting_network/sort28.h +++ b/include/cpp-sort/detail/sorting_network/sort28.h @@ -180,6 +180,29 @@ namespace detail iter_swap_if(first + 21u, first + 22u, compare, projection); iter_swap_if(first + 23u, first + 24u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 155> + { + return {{ + {0, 9}, {1, 20}, {2, 21}, {3, 22}, {4, 19}, {5, 24}, {6, 25}, {7, 26}, {8, 23}, {10, 15}, {11, 13}, {12, 17}, {14, 16}, {18, 27}, + {0, 18}, {1, 7}, {2, 6}, {3, 5}, {4, 8}, {9, 27}, {10, 12}, {11, 14}, {13, 16}, {15, 17}, {19, 23}, {20, 26}, {21, 25}, {22, 24}, + {1, 2}, {3, 4}, {5, 19}, {6, 20}, {7, 21}, {8, 22}, {9, 18}, {10, 11}, {12, 14}, {13, 15}, {16, 17}, {23, 24}, {25, 26}, + {0, 3}, {1, 10}, {5, 8}, {6, 7}, {11, 13}, {14, 16}, {17, 26}, {19, 22}, {20, 21}, {24, 27}, + {0, 1}, {2, 7}, {3, 10}, {4, 8}, {12, 13}, {14, 15}, {17, 24}, {19, 23}, {20, 25}, {26, 27}, + {1, 3}, {2, 6}, {4, 5}, {7, 19}, {8, 20}, {11, 12}, {13, 14}, {15, 16}, {21, 25}, {22, 23}, {24, 26}, + {2, 4}, {5, 12}, {7, 8}, {9, 11}, {10, 14}, {13, 17}, {15, 22}, {16, 18}, {19, 20}, {23, 25}, + {2, 9}, {4, 11}, {5, 6}, {7, 13}, {8, 10}, {14, 20}, {16, 23}, {17, 19}, {18, 25}, {21, 22}, + {1, 2}, {3, 16}, {4, 9}, {6, 12}, {10, 14}, {11, 24}, {13, 17}, {15, 21}, {18, 23}, {25, 26}, + {2, 8}, {3, 5}, {4, 7}, {6, 16}, {9, 15}, {11, 21}, {12, 18}, {19, 25}, {20, 23}, {22, 24}, + {2, 3}, {5, 8}, {7, 9}, {11, 15}, {12, 16}, {18, 20}, {19, 22}, {24, 25}, + {6, 8}, {10, 12}, {11, 13}, {14, 16}, {15, 17}, {19, 21}, + {5, 6}, {8, 10}, {9, 11}, {12, 13}, {14, 15}, {16, 18}, {17, 19}, {21, 22}, + {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 14}, {13, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort29.h b/include/cpp-sort/detail/sorting_network/sort29.h index abe374b6..3efa401d 100644 --- a/include/cpp-sort/detail/sorting_network/sort29.h +++ b/include/cpp-sort/detail/sorting_network/sort29.h @@ -88,6 +88,29 @@ namespace detail iter_swap_if(first + 25u, first + 26u, compare, projection); iter_swap_if(first + 27u, first + 28u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 165> + { + return {{ + {0, 12}, {1, 10}, {2, 9}, {3, 7}, {5, 11}, {6, 8}, {13, 26}, {14, 25}, {15, 28}, {16, 27}, {17, 21}, {18, 19}, {20, 24}, {22, 23}, + {1, 6}, {2, 3}, {4, 11}, {7, 9}, {8, 10}, {13, 18}, {14, 20}, {15, 22}, {16, 17}, {19, 26}, {21, 27}, {23, 28}, {24, 25}, + {0, 4}, {1, 2}, {3, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 21}, {20, 22}, {23, 24}, {25, 26}, {27, 28}, + {4, 6}, {5, 9}, {8, 11}, {10, 12}, {13, 15}, {14, 16}, {17, 23}, {18, 24}, {19, 20}, {21, 22}, {25, 27}, {26, 28}, + {0, 5}, {3, 8}, {4, 7}, {6, 11}, {9, 10}, {14, 15}, {16, 25}, {17, 19}, {18, 20}, {21, 23}, {22, 24}, {26, 27}, + {0, 1}, {2, 5}, {6, 9}, {7, 8}, {10, 11}, {14, 17}, {15, 19}, {18, 21}, {20, 23}, {22, 26}, {24, 27}, + {0, 13}, {1, 3}, {2, 4}, {5, 6}, {9, 10}, {15, 17}, {16, 19}, {22, 25}, {24, 26}, + {1, 2}, {3, 4}, {5, 7}, {6, 8}, {16, 18}, {19, 21}, {20, 22}, {23, 25}, + {1, 14}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, + {2, 15}, {3, 4}, {5, 6}, {10, 23}, {11, 24}, {12, 25}, {19, 20}, {21, 22}, + {3, 16}, {4, 17}, {5, 18}, {6, 19}, {7, 20}, {8, 21}, {9, 22}, {10, 15}, + {6, 10}, {8, 13}, {9, 14}, {11, 16}, {12, 17}, {18, 26}, {19, 27}, {20, 28}, + {4, 8}, {5, 9}, {7, 11}, {12, 13}, {14, 18}, {15, 19}, {16, 20}, {17, 21}, {22, 26}, {23, 27}, {24, 28}, + {2, 4}, {3, 5}, {6, 8}, {7, 9}, {10, 12}, {11, 14}, {13, 15}, {16, 18}, {17, 19}, {20, 22}, {21, 23}, {24, 26}, {25, 27}, + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, {25, 26}, {27, 28}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort3.h b/include/cpp-sort/detail/sorting_network/sort3.h index 2c8673d0..26225c6b 100644 --- a/include/cpp-sort/detail/sorting_network/sort3.h +++ b/include/cpp-sort/detail/sorting_network/sort3.h @@ -28,6 +28,17 @@ namespace detail iter_swap_if(first, first + 2u, compare, projection); iter_swap_if(first, first + 1u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 3> + { + return {{ + {0, 2}, + {0, 1}, + {1, 2}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort30.h b/include/cpp-sort/detail/sorting_network/sort30.h index 48555eee..fccb778a 100644 --- a/include/cpp-sort/detail/sorting_network/sort30.h +++ b/include/cpp-sort/detail/sorting_network/sort30.h @@ -197,6 +197,29 @@ namespace detail iter_swap_if(first + 25u, first + 26u, compare, projection); iter_swap_if(first + 27u, first + 28u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 172> + { + return {{ + {1, 2}, {3, 10}, {4, 14}, {5, 8}, {6, 13}, {7, 12}, {9, 11}, {16, 17}, {18, 25}, {19, 29}, {20, 23}, {21, 28}, {22, 27}, {24, 26}, + {0, 14}, {1, 5}, {2, 8}, {3, 7}, {6, 9}, {10, 12}, {11, 13}, {15, 29}, {16, 20}, {17, 23}, {18, 22}, {21, 24}, {25, 27}, {26, 28}, + {0, 7}, {1, 6}, {2, 9}, {4, 10}, {5, 11}, {8, 13}, {12, 14}, {15, 22}, {16, 21}, {17, 24}, {19, 25}, {20, 26}, {23, 28}, {27, 29}, + {0, 6}, {2, 4}, {3, 5}, {7, 11}, {8, 10}, {9, 12}, {13, 14}, {15, 21}, {17, 19}, {18, 20}, {22, 26}, {23, 25}, {24, 27}, {28, 29}, + {0, 3}, {1, 2}, {4, 7}, {5, 9}, {6, 8}, {10, 11}, {12, 13}, {14, 29}, {15, 18}, {16, 17}, {19, 22}, {20, 24}, {21, 23}, {25, 26}, {27, 28}, + {0, 1}, {2, 3}, {4, 6}, {7, 9}, {10, 12}, {11, 13}, {15, 16}, {17, 18}, {19, 21}, {22, 24}, {25, 27}, {26, 28}, + {0, 15}, {1, 2}, {3, 5}, {8, 10}, {11, 12}, {13, 28}, {16, 17}, {18, 20}, {23, 25}, {26, 27}, + {1, 16}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {12, 27}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, + {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, {25, 26}, + {2, 17}, {3, 18}, {4, 19}, {5, 6}, {7, 8}, {9, 24}, {10, 25}, {11, 26}, {20, 21}, {22, 23}, + {5, 20}, {6, 21}, {7, 22}, {8, 23}, {9, 16}, {10, 17}, {11, 18}, {12, 19}, + {5, 9}, {6, 10}, {7, 11}, {8, 15}, {13, 20}, {14, 21}, {18, 22}, {19, 23}, + {3, 5}, {4, 8}, {7, 9}, {12, 15}, {13, 16}, {14, 17}, {20, 24}, {21, 25}, + {2, 4}, {6, 8}, {10, 12}, {11, 13}, {14, 15}, {16, 18}, {17, 19}, {20, 22}, {21, 23}, {24, 26}, {25, 27}, + {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, {25, 26}, {27, 28}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort31.h b/include/cpp-sort/detail/sorting_network/sort31.h index e04ebd41..1f09bd60 100644 --- a/include/cpp-sort/detail/sorting_network/sort31.h +++ b/include/cpp-sort/detail/sorting_network/sort31.h @@ -92,6 +92,28 @@ namespace detail iter_swap_if(first + 27u, first + 28u, compare, projection); iter_swap_if(first + 29u, first + 30u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 180> + { + return {{ + {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, {26, 27}, {28, 29}, + {0, 2}, {1, 3}, {4, 6}, {5, 7}, {8, 10}, {9, 11}, {12, 14}, {13, 15}, {16, 18}, {17, 19}, {20, 22}, {21, 23}, {24, 26}, {25, 27}, {28, 30}, + {0, 4}, {1, 5}, {2, 6}, {3, 7}, {8, 12}, {9, 13}, {10, 14}, {11, 15}, {16, 20}, {17, 21}, {18, 22}, {19, 23}, {24, 28}, {25, 29}, {26, 30}, + {0, 8}, {1, 9}, {2, 10}, {3, 11}, {4, 12}, {5, 13}, {6, 14}, {7, 15}, {16, 24}, {17, 25}, {18, 26}, {19, 27}, {20, 28}, {21, 29}, {22, 30}, + {0, 16}, {1, 8}, {2, 4}, {3, 12}, {5, 10}, {6, 9}, {7, 14}, {11, 13}, {17, 24}, {18, 20}, {19, 28}, {21, 26}, {22, 25}, {23, 30}, {27, 29}, + {1, 2}, {3, 5}, {4, 8}, {6, 22}, {7, 11}, {9, 25}, {10, 12}, {13, 14}, {17, 18}, {19, 21}, {20, 24}, {23, 27}, {26, 28}, {29, 30}, + {1, 17}, {2, 18}, {3, 19}, {4, 20}, {5, 10}, {7, 23}, {8, 24}, {11, 27}, {12, 28}, {13, 29}, {14, 30}, {21, 26}, + {3, 17}, {4, 16}, {5, 21}, {6, 18}, {7, 9}, {8, 20}, {10, 26}, {11, 23}, {13, 25}, {14, 28}, {15, 27}, {22, 24}, + {1, 4}, {3, 8}, {5, 16}, {7, 17}, {9, 21}, {10, 22}, {11, 19}, {12, 20}, {14, 24}, {15, 26}, {23, 28}, {27, 30}, + {2, 5}, {7, 8}, {9, 18}, {11, 17}, {12, 16}, {13, 22}, {14, 20}, {15, 19}, {23, 24}, {26, 29}, + {2, 4}, {6, 12}, {9, 16}, {10, 11}, {13, 17}, {14, 18}, {15, 22}, {19, 25}, {20, 21}, {27, 29}, + {5, 6}, {8, 12}, {9, 10}, {11, 13}, {14, 16}, {15, 17}, {18, 20}, {19, 23}, {21, 22}, {25, 26}, + {3, 5}, {6, 7}, {8, 9}, {10, 12}, {11, 14}, {13, 16}, {15, 18}, {17, 20}, {19, 21}, {22, 23}, {24, 25}, {26, 28}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, {25, 26}, {27, 28}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort32.h b/include/cpp-sort/detail/sorting_network/sort32.h index bc7c986e..1ba756cf 100644 --- a/include/cpp-sort/detail/sorting_network/sort32.h +++ b/include/cpp-sort/detail/sorting_network/sort32.h @@ -93,6 +93,28 @@ namespace detail iter_swap_if(first + 27u, first + 28u, compare, projection); iter_swap_if(first + 29u, first + 30u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 185> + { + return {{ + {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, {26, 27}, {28, 29}, {30, 31}, + {0, 2}, {1, 3}, {4, 6}, {5, 7}, {8, 10}, {9, 11}, {12, 14}, {13, 15}, {16, 18}, {17, 19}, {20, 22}, {21, 23}, {24, 26}, {25, 27}, {28, 30}, {29, 31}, + {0, 4}, {1, 5}, {2, 6}, {3, 7}, {8, 12}, {9, 13}, {10, 14}, {11, 15}, {16, 20}, {17, 21}, {18, 22}, {19, 23}, {24, 28}, {25, 29}, {26, 30}, {27, 31}, + {0, 8}, {1, 9}, {2, 10}, {3, 11}, {4, 12}, {5, 13}, {6, 14}, {7, 15}, {16, 24}, {17, 25}, {18, 26}, {19, 27}, {20, 28}, {21, 29}, {22, 30}, {23, 31}, + {0, 16}, {1, 8}, {2, 4}, {3, 12}, {5, 10}, {6, 9}, {7, 14}, {11, 13}, {15, 31}, {17, 24}, {18, 20}, {19, 28}, {21, 26}, {22, 25}, {23, 30}, {27, 29}, + {1, 2}, {3, 5}, {4, 8}, {6, 22}, {7, 11}, {9, 25}, {10, 12}, {13, 14}, {17, 18}, {19, 21}, {20, 24}, {23, 27}, {26, 28}, {29, 30}, + {1, 17}, {2, 18}, {3, 19}, {4, 20}, {5, 10}, {7, 23}, {8, 24}, {11, 27}, {12, 28}, {13, 29}, {14, 30}, {21, 26}, + {3, 17}, {4, 16}, {5, 21}, {6, 18}, {7, 9}, {8, 20}, {10, 26}, {11, 23}, {13, 25}, {14, 28}, {15, 27}, {22, 24}, + {1, 4}, {3, 8}, {5, 16}, {7, 17}, {9, 21}, {10, 22}, {11, 19}, {12, 20}, {14, 24}, {15, 26}, {23, 28}, {27, 30}, + {2, 5}, {7, 8}, {9, 18}, {11, 17}, {12, 16}, {13, 22}, {14, 20}, {15, 19}, {23, 24}, {26, 29}, + {2, 4}, {6, 12}, {9, 16}, {10, 11}, {13, 17}, {14, 18}, {15, 22}, {19, 25}, {20, 21}, {27, 29}, + {5, 6}, {8, 12}, {9, 10}, {11, 13}, {14, 16}, {15, 17}, {18, 20}, {19, 23}, {21, 22}, {25, 26}, + {3, 5}, {6, 7}, {8, 9}, {10, 12}, {11, 14}, {13, 16}, {15, 18}, {17, 20}, {19, 21}, {22, 23}, {24, 25}, {26, 28}, + {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, {25, 26}, {27, 28}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort4.h b/include/cpp-sort/detail/sorting_network/sort4.h index ea274513..152fee95 100644 --- a/include/cpp-sort/detail/sorting_network/sort4.h +++ b/include/cpp-sort/detail/sorting_network/sort4.h @@ -30,6 +30,17 @@ namespace detail iter_swap_if(first + 1u, first + 3u, compare, projection); iter_swap_if(first + 1u, first + 2u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 5> + { + return {{ + {0, 2}, {1, 3}, + {0, 1}, {2, 3}, + {1, 2}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort5.h b/include/cpp-sort/detail/sorting_network/sort5.h index 94ad4776..023c824a 100644 --- a/include/cpp-sort/detail/sorting_network/sort5.h +++ b/include/cpp-sort/detail/sorting_network/sort5.h @@ -34,6 +34,19 @@ namespace detail iter_swap_if(first + 1u, first + 3u, compare, projection); iter_swap_if(first + 1u, first + 2u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 9> + { + return {{ + {0, 3}, {1,4}, + {0, 2}, {1,3}, + {0, 1}, {2,4}, + {1, 2}, {3,4}, + {2, 3}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort6.h b/include/cpp-sort/detail/sorting_network/sort6.h index c41e2fa0..383fbc6d 100644 --- a/include/cpp-sort/detail/sorting_network/sort6.h +++ b/include/cpp-sort/detail/sorting_network/sort6.h @@ -37,6 +37,19 @@ namespace detail iter_swap_if(first + 1u, first + 3u, compare, projection); iter_swap_if(first + 2u, first + 3u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 12> + { + return {{ + {0, 5}, {1, 3}, {2, 4}, + {1, 2}, {3, 4}, + {0, 3}, {2, 5}, + {0, 1}, {2, 3}, {4, 5}, + {1, 2}, {3, 4}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort7.h b/include/cpp-sort/detail/sorting_network/sort7.h index ca1a1a21..8ee0367e 100644 --- a/include/cpp-sort/detail/sorting_network/sort7.h +++ b/include/cpp-sort/detail/sorting_network/sort7.h @@ -41,6 +41,20 @@ namespace detail iter_swap_if(first + 2u, first + 4u, compare, projection); iter_swap_if(first + 2u, first + 3u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 16> + { + return {{ + {0, 6}, {2, 3}, {4, 5}, + {0, 2}, {1, 4}, {3, 6}, + {0, 1}, {2, 5}, {3, 4}, + {1, 2}, {4, 6}, + {2, 3}, {4, 5}, + {1, 2}, {3, 4}, {5, 6}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort8.h b/include/cpp-sort/detail/sorting_network/sort8.h index b5c491a5..16f6316c 100644 --- a/include/cpp-sort/detail/sorting_network/sort8.h +++ b/include/cpp-sort/detail/sorting_network/sort8.h @@ -44,6 +44,20 @@ namespace detail iter_swap_if(first + 3u, first + 5u, compare, projection); iter_swap_if(first + 3u, first + 4u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 19> + { + return {{ + {0, 2}, {1, 3}, {4, 6}, {5, 7}, + {0, 4}, {1, 5}, {2, 6}, {3, 7}, + {0, 1}, {2, 3}, {4, 5}, {6, 7}, + {2, 4}, {3, 5}, + {1, 4}, {3, 6}, + {1, 2}, {3, 4}, {5, 6}, + }}; + } }; }} diff --git a/include/cpp-sort/detail/sorting_network/sort9.h b/include/cpp-sort/detail/sorting_network/sort9.h index 6035dcc9..34ec1a2b 100644 --- a/include/cpp-sort/detail/sorting_network/sort9.h +++ b/include/cpp-sort/detail/sorting_network/sort9.h @@ -50,6 +50,21 @@ namespace detail iter_swap_if(first + 2u, first + 3u, compare, projection); iter_swap_if(first + 5u, first + 6u, compare, projection); } + + template + static constexpr auto index_pairs() + -> std::array, 25> + { + return {{ + {0, 3}, {1, 7}, {2, 5}, {4, 8}, + {0, 7}, {2, 4}, {3, 8}, {5, 6}, + {0, 2}, {1, 3}, {4, 5}, {7, 8}, + {1, 4}, {3, 6}, {5, 7}, + {0, 1}, {2, 4}, {3, 5}, {6, 8}, + {2, 3}, {4, 5}, {6, 7}, + {1, 2}, {3, 4}, {5, 6}, + }}; + } }; }} diff --git a/include/cpp-sort/fixed/sorting_network_sorter.h b/include/cpp-sort/fixed/sorting_network_sorter.h index ea070e5a..5f26c570 100644 --- a/include/cpp-sort/fixed/sorting_network_sorter.h +++ b/include/cpp-sort/fixed/sorting_network_sorter.h @@ -45,7 +45,7 @@ namespace cppsort { using iterator_category = std::random_access_iterator_tag; - // Some of the algorithms are stable, some other are not, + // Some of the algorithms are stable, others are not, // the stability *could* be documented depending on which // fixed-size algorithms are used, but it would be lots of // work... @@ -62,8 +62,10 @@ namespace cppsort } // Common includes for specializations +#include #include #include +#include #include "../detail/swap_if.h" // Specializations of sorting_network_sorter for some values of N diff --git a/testsuite/utility/sorting_networks.cpp b/testsuite/utility/sorting_networks.cpp index eda1d8d0..b03f0008 100644 --- a/testsuite/utility/sorting_networks.cpp +++ b/testsuite/utility/sorting_networks.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -40,3 +41,25 @@ TEST_CASE( "sorting with index pairs", "[utility][sorting_networks]" ) CHECK( std::is_sorted(vec.begin(), vec.end()) ); } } + +TEST_CASE( "sorting with index pairs from sorting_network_sorter", + "[utility][sorting_networks]" ) +{ + constexpr auto pairs = cppsort::sorting_network_sorter<8>::index_pairs(); + + std::vector vec; + auto distribution = dist::shuffled{}; + distribution(std::back_inserter(vec), 8); + + SECTION( "sort_index_pairs" ) + { + sort_index_pairs(vec.begin(), pairs); + CHECK( std::is_sorted(vec.begin(), vec.end()) ); + } + + SECTION( "sort_index_pairs_force_unroll" ) + { + sort_index_pairs_force_unroll(vec.begin(), pairs); + CHECK( std::is_sorted(vec.begin(), vec.end()) ); + } +} From ccd2fd1b0a331301a2e10dc55b2ed5daeaa3faf2 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 3 Jul 2021 17:59:13 +0200 Subject: [PATCH 46/56] Compare-exchange units -> compare-exchange operations Also abbreviate to CEs instead of CEUs to match the abbreviation used by the SorterHunter project, which is currently the main source of information used for the sorting networks. Some drive-by documentation fixes. --- docs/Fixed-size-sorters.md | 22 +++++++++++----------- docs/Original-research.md | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index a06bcf5d..196a2a28 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -95,35 +95,35 @@ template struct sorting_network_sorter; ``` -The following table gives the number of *compare-exchange units* (CEUs) used to sort a fixed collection of a given size. These numbers should correspond to the best-known size-optimal sorting networks at the time of writing (as opposed to depth-optimal sorting networks). If you ever find a sorting network using fewer CEUs for one of these sizes, don't hesitate to let me know, but you might as well write a research paper about it. +The following table gives the number of *compare-exchange* operations (CEs) used to sort a fixed collection of a given size. These numbers should correspond to the best-known size-optimal sorting networks at the time of writing (as opposed to depth-optimal sorting networks). If you ever find a sorting network using fewer CEs for one of these sizes, don't hesitate to open an issue (but you might as well write a research paper about it). Size | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: -**CEUs** | 0 | 1 | 3 | 5 | 9 | 12 | 16 | 19 | 25 | 29 | 35 | 39 | 45 | 51 | 56 | 60 +**CEs** | 0 | 1 | 3 | 5 | 9 | 12 | 16 | 19 | 25 | 29 | 35 | 39 | 45 | 51 | 56 | 60 **Size** | **17** | **18** | **19** | **20** | **21** | **22** | **23** | **24** | **25** | **26** | **27** | **28** | **29** | **30** | **31** | **32** -**CEUs** | 71 | 77 | 85 | 91 | 100 | 107 | 115 | 120 | 132 | 139 | 150 | 155 | 165 | 172 | 180 | 185 +**CEs** | 71 | 77 | 85 | 91 | 100 | 107 | 115 | 120 | 132 | 139 | 150 | 155 | 165 | 172 | 180 | 185 -One of the main advantages of sorting networks is the fixed number of CEUs required to sort a collection: this means that sorting networks are far more resistant to time and cache attacks since the number of performed comparisons does not depend on the contents of the collection. However, additional care (not provided by the library) is required to ensure that the algorithms always perform the same amount of memory loads and stores. For example, one could create a `constant_time_iterator` with a dedicated `iter_swap` tuned to perform a constant-time compare-exchange operation. +One of the main advantages of sorting networks is the fixed number of CEs required to sort a collection: this means that sorting networks are far more resistant to time and cache attacks since the number of performed comparisons does not depend on the contents of the collection. However, additional care (not provided by the library) is required to ensure that the algorithms always perform the same amount of memory loads and stores. For example, one could create a `constant_time_iterator` with a dedicated `iter_swap` tuned to perform a constant-time compare-exchange operation. -*Note:* don't be fooled by the name; none of the algorithms in this fixed-size sorter explicitly perform any operation in parallel. Everything is sequential. The algorithms are but long sequences of compare-exchange units. +*Note:* don't be fooled by the name; none of the algorithms in this fixed-size sorter explicitly perform any operation in parallel. Everything is sequential. The algorithms are but long sequences of compare-exchange operations. -All specializations of `sorting_network_sorter` provide a `index_pairs() static` function template which returns an [`std::array`][std-array] of [`utility::index_pair`][utility-sorting-networks]. Those pairs represent the indices used in the CEUs of the network and can be passed manipulated and passed to dedicated [sorting network tools][utility-sorting-networks] from the library's utility module. The function is templated of the index/difference type, which must be constructible from `int`. +All specializations of `sorting_network_sorter` provide a `index_pairs() static` function template which returns an [`std::array`][std-array] of [`utility::index_pair`][utility-sorting-networks]. Those pairs represent the indices used by the CE operations of the network and can be passed manipulated and passed to dedicated [sorting network tools][utility-sorting-networks] from the library's utility module. The function is templated of the index/difference type, which must be constructible from `int`. ```cpp template static constexpr auto index_pairs() - -> std::array, /* Number of CEUs in the network */>; + -> std::array, /* Number of CEs in the network */>; ``` -*Changed in version 1.2.0:* sorting 21 inputs requires 100 CEUs instead of 101. +*Changed in version 1.2.0:* sorting 21 inputs requires 100 CEs instead of 101. -*Changed in version 1.3.0:* sorting 23, 24, 25 and 26 inputs respectively require 115, 120, 132 and 139 CEUs instead of 116, 121, 133 and 140. +*Changed in version 1.3.0:* sorting 23, 24, 25 and 26 inputs respectively require 115, 120, 132 and 139 CEs instead of 116, 121, 133 and 140. -*Changed in version 1.8.0:* sorting 18 inputs requires 77 CEUs instead of 78. +*Changed in version 1.8.0:* sorting 18 inputs requires 77 CEs instead of 78. *Changed in version 1.10.0:* added `sorting_network_sorter::index_pairs` [sorting-network]: https://en.wikipedia.org/wiki/Sorting_network - [std-array]: + [std-array]: https://en.cppreference.com/w/cpp/container/array [utility-sorting-networks]: https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#Sorting-network-tools diff --git a/docs/Original-research.md b/docs/Original-research.md index 296c4800..73aa233f 100644 --- a/docs/Original-research.md +++ b/docs/Original-research.md @@ -108,13 +108,13 @@ I tried to apply the same technique to create a 40-sorter, but the resulting 20- ### Sorting network for 29 inputs -The following sorting network for 29 inputs has 165 compare-exchange-units, which is one less that the most size-optimal 29-input sorting networks that I could find in the literature. Here is how I generated it: first it sorts the first 16 inputs and the last 13 inputs independently. Then it merges the two sorted subarrays using a size 32 Batcher odd-even merge network (the version that does not need the inputs to be interleaved), where all compare-exchange units working on indexes greater than 28 have been dropped. Dropping comparators in such a way is ok: consider that the values at the indexes [29, 32) are greater than every other value in the array to sort, and it will become intuitive that dropping them generates a correct merging network of a smaller size. +The following sorting network for 29 inputs has 165 *compare-exchange* operations (CEs), which is one less that the most size-optimal 29-input sorting networks that I could find in the literature. Here is how I generated it: first it sorts the first 16 inputs and the last 13 inputs independently. Then it merges the two sorted subarrays using a size 32 Batcher odd-even merge network (the version that does not need the inputs to be interleaved), where all compare-exchange operations working on indexes greater than 28 have been dropped. Dropping comparators in such a way is ok: consider that the values at the indexes [29, 32) are greater than every other value in the array to sort, and it will become intuitive that dropping them generates a correct merging network of a smaller size. -That said, even though I have been unable to find a 29-input sorting network with as few compare-exchange units as 165 in the literature, I can't claim that I found the technique used to generate it: the unclassified 1971 paper [*A Generalization of the Divide-Sort-Merge Strategy for Sorting Networks*][divide-sort-merge-strategy] by David C. Van Voorhis already describes the as follows: +That said, even though I have been unable to find a 29-input sorting network with as few compare-exchange operations as 165 in the literature, I can't claim that I found the technique used to generate it: the 1971 paper [*A Generalization of the Divide-Sort-Merge Strategy for Sorting Networks*][divide-sort-merge-strategy] by David C. Van Voorhis already describes the as follows: > The improved 26-,27-,28-, and 34-sorters all use two initial sort units, one of them the particularly efficient 16-sorter designed by M. W. Green, followed by Batcher's [2,2] merge network. -The paper does not mention a better result than 166 CEUs for the 29-input sorting networks, but that's only because our solution relies on a 13-input sorting networks that uses 45 CEUs, while the best known such network in 1971 used 46 CEUs. I couldn't find any resource using the technique to improve the 29-input sorting network since then, even though some of them mention a 156-CEU 28-input sorting network that has apparently only been described in the aforementioned unclassified paper. +The paper does not mention a better result than 166 CEs for the 29-input sorting networks, but that's only because our solution relies on a 13-input sorting networks that uses 45 CEs, while the best known such network in 1971 used 46 CEs. I couldn't find any resource using the technique to improve the 29-input sorting network since then, even though some of them mention a 156-CE 28-input sorting network that has apparently only been described in the aforementioned paper. ![Sorting network 29](https://github.com/Morwenn/cpp-sort/wiki/images/sorting-network-29.png) From b49c5ec8ef38cf917b8bc37dcbd7a7e3dfe27b04 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 12 Jul 2021 12:10:46 +0200 Subject: [PATCH 47/56] Add merge_exchange_network_sorter --- docs/Fixed-size-sorters.md | 24 +++ docs/images/merge-exchange-network-8.png | Bin 0 -> 5350 bytes include/cpp-sort/detail/bitops.h | 16 +- include/cpp-sort/detail/make_array.h | 41 ++++ .../fixed/merge_exchange_network_sorter.h | 188 ++++++++++++++++++ include/cpp-sort/fixed_sorters.h | 3 +- include/cpp-sort/fwd.h | 2 + .../schwartz_adapter_fixed_sorters.cpp | 55 ++++- 8 files changed, 326 insertions(+), 3 deletions(-) create mode 100644 docs/images/merge-exchange-network-8.png create mode 100644 include/cpp-sort/detail/make_array.h create mode 100644 include/cpp-sort/fixed/merge_exchange_network_sorter.h diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index 196a2a28..c0ef34d7 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -82,6 +82,28 @@ struct low_moves_sorter; Note that this fixed-sized sorter is *not* move-optimal: it tries to perform a few moves without wasting too much memory and with a somewhat reasonable number of comparisons for small collections. If you really need a sorting algorithm that performs the lowest possible number of move operations, you can use the library's [`indirect_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#indirect_adapter) instead, but it comes at the cost of a higher memory footprint. You probably want to use if only when the objects are *really* expensive to copy. +### `merge_exchange_network_sorter` + +```cpp +#include +``` + +This fixed-size sorter implements *merge-exchange sort* a variation of Batcher's [odd-even mergesort][odd-even-mergesort] described by Knuth in *[The Art of Computer Programming][taocp] vol.3 - Sorting and Searching*. Unlike the algorithm described in the Wikipedia article, this produces two interleaved [sorting networks][sorting-network] and merges them. + +![Merge-exchange sorting network for 8 inputs](https://raw.githubusercontent.com/Morwenn/cpp-sort/master/docs/images/merge-exchange-network-8.png) + +```cpp +template +struct merge_exchange_network_sorter; +``` + +All specializations of `merge_exchange_network_sorter` provide a `index_pairs() static` function template which returns an [`std::array`][std-array] of [`utility::index_pair`][utility-sorting-networks]. Those pairs represent the indices used by the CE operations of the network and can be passed manipulated and passed to dedicated [sorting network tools][utility-sorting-networks] from the library's utility module. The function is templated of the index/difference type, which must be constructible from `int`. + +```cpp +template +[[nodiscard]] static constexpr auto index_pairs() + -> std::array, /* Number of CEs in the network */>; + ### `sorting_network_sorter` ```cpp @@ -124,6 +146,8 @@ static constexpr auto index_pairs() *Changed in version 1.10.0:* added `sorting_network_sorter::index_pairs` + [odd-even-mergesort]: https://en.wikipedia.org/wiki/Batcher_odd%E2%80%93even_mergesort [sorting-network]: https://en.wikipedia.org/wiki/Sorting_network [std-array]: https://en.cppreference.com/w/cpp/container/array + [taocp]: https://en.wikipedia.org/wiki/The_Art_of_Computer_Programming [utility-sorting-networks]: https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#Sorting-network-tools diff --git a/docs/images/merge-exchange-network-8.png b/docs/images/merge-exchange-network-8.png new file mode 100644 index 0000000000000000000000000000000000000000..e2afb705073c3b6076e9861f680b1400c6a5dde1 GIT binary patch literal 5350 zcmd5=c{o(<-=7V|jPWFum@H){V(eQYYqqhAC~1()2pRiKo>W4%5JO?ICSiG*DSv37CF$!*71*6(VUEu!aFP*ah#W%IUva6?QGBOg1qxmJU*jDRsNPa z;_?v-%$R*}l@VV%>j>ZFKVbV(`Q9?t-%Pkit?4dw1W)H|=MA*16)*ixQIE3Dqi-_T zsq1+IN@{6j6^aSP0f>RAylF)F%JV`3IEG;@oELJ{lO!7j0d>j)fZKEb`$sCtWsCBj zB_B7^p5Dc#{Ni7vl?TS0M=htSq)XO*mOeJu+m~&s+ppg7$OGDQQgBy^ZL<(alQe-5 zqy=}yh++3@d}ebj!oE~+CP@htF0HuY?h7Cj0u5uYb!=YUM4MuqN3TRmu^wOGwB1)T zo?y+v%Q?KMbWbf&Xc6b+ZXCFO=9rt^NKv3@`>$*JG_YQhmTpmia{TrLgPLz|*#{4} z6fE^ar7B|BDEpI@q=Fp^+6OIONG#kRaM)9`@J-`+)9zwi=>TPIP9Q(ri?@n*R4bJ4 zmeT&l16!n3(WP9oxyWfa&7;zJq+K4TEoX{fyg$!dz&8w!k-#h*Y$J8sq|)}cDyN^g zg%>nW{i_BkVK(Cumr7&H8=B!r8Tltal5dc1J1l|5dVjJ z{|rOvCPz?2;M;i1!hkFo6)ud5L}-W+sFG>qG|Duzh0Dro7SpQ!pzz2_oFbz=z!~Yr ztVj&$v;}OsKjU5aOLK-{@FqiS7L;kS{=5F@s2pB8Wcfh+n-sg}l1Dep;>YwJsoLt& z6KCm@%%6t2!fy4j3zM(6U#={tR!33L$-OjtLKGkdykHu{HA6>VdHc%}PZFco=1O?x zVFblJY+Pj+U^9JzmdpeZt{sH)*n~#ib;r)pXh58mkwUTRBH~$tyi?QH)+Z8Lcu2HN zgIcq5;<@>ieVEImNg!ehT&H=i(G|uM`a~7X=9!yC$QD;i&fl@Mvia95q$-ZrFLiX3 zH=YasqupZ00a(8tha0iK_Apox#0#IV#F-5&glYXn<(F-*4a)eLm5@ivyX=x?w1W$rL<9Y_4qN*po_yA!Q0&-+KO6lY{`x0w5;M&t}UXJKwZKHdc zP&rC|+=!wn7pYF2YfI(b`ge$uBEICnCUiaMw$FPmrqMcG7cSo=SSG1wvQ9@q*E+LH zJ_WOunPPq)LV%;$`UB;zF+PuDV>~(81-w%&r9D!)0!}g$n!vY=>L~IiO#PdVWfVQ< zQm4P6R_iuyZLU^TkOZv?TNQ2n&hecfn8PU#O*y|vwkxTj>?N-5?pm*I#UzUb;{^07vcpbZvzs7&j&k|SzZcWEnK}zQR%KO zmM`>2Nw;=2PAZH{mIBSFm}4-#eLBp<#0YPV8)=j@)9#3f%e^>tF1QHnLhDxMcl9S_ z?t)3957Ji(f%G1irOVo^D=b`B=3>x#7jan)o&>czQ9(Tz{1uYeaRtRb&ca}swL``~ zI@uOpL0oJ;- zqSpU3o4#AN9Hb4Es!h1Y8F@2UJZC^2*N1x@~CK3su(&AbNr@f zl@?@Hu8}wek37_P_3~o^RmcMw8RvJhh8JRIiqCNvupUown&)MBYRNd2FOcyO)F)O& zso!fHKbH)@YS668PA})?H2vr_zt)Vz6+D_X2x=Rk$;Hbaaj7fq+N`<%y}{8jC1zTP zM*o}y$++<%Fw^W!aeOzecz_@Q>a~$5t3USw3c#AM$njV4v)o zxI%MDbBhf<^BaO_9w%Rs$EjzuF2GWu-KN!El)|}v(c&N}8)i5bL+{rmD2VCG^`ELl zf?wnmmz=?{5|8?Fzn~_fGv!-ahN~W2n9Dy!-KzBOA`#irXQbVBh~N&jX6dky4Bg(c z(%A=MG`Wg$GpUh?g+!bZdW(~YCVGycN$>e-DlZ5ry&ocduFO@yp9x3%`Uh~XB3|+6 zWNBKId!SpeYMZGUCpIM%avVEbflzV848)&w~V6GUk1`{mIBoP zo)QWh_zKpL9%}=LWchgBn9`_wewcWS$XR7KFJEy1Aq*2nET?K(%VlAc>LY=L?2}lq{+5h*uIHhz)dQ} z+>L{-3D(FObXY};Q2vSWx`u}rKCF~|O@VlM*y!vU>{TA9H|}HsY>Y_nYH;E1#Pio; zo)Stm1E|mxZw);M`PNW7=5!ee=BOHBqeqy2D_CYU)J5bY#m~|mX0#{JOp{X+vA~Cf zCRc}QWM#ev;j@pX=CaV7@rdoK+82IXYh|90tTSEqs=sLExeuCR)3c0T%!X@)CUXu* z?-o>Y)UwelWF946FZaU4E%WxJJWUN zCu<9$AKgO{Rly(7a4+U3_k$y)Be0ja-17Jl$Qvl36GLmWpxh%z1#!uo;%XEaJq50` z2G_Gyj#gtg9mAKVolUPyazpK0B^3I<{|Gz*>cG`mPy4-=wEHrJ+w@`|F z7}ob)$F|9f5=G{}&gDx)5l?q+x##+SLt!gj&`^BH^VgPuH{(m`tn%U^0E1m4gr@Mi zj#oVacb^}y02!)mTv-M%@yTnbvtMWnKlN@?<+iC*f&;GIn{k#_zR(f2?WeA`qs)>6 zB!HR8QO``5xH-FaSk2SWH(TMxAZPaIktH zyw438nfA@zcgVgP3B7=fq|_b_L>fd_yjK=}RF*Mwr!FDN#bd~E2sCuq)h|tkgEkJ` zY!UAMq|_7F?)<~v{r{je{1wPz_-zOeId|RImhT=J4ZRKh^$@fOJmdv97c>Xo%kYa} zJptq>5BCJK2>u2l1yk&ydZ*=}<+Pii_Kt%7jg*wznmA=^YT1a3umcdK`Qbc9^}v6mwmMFb#v$>H1d- zESHtD(dP_lx#6Fw`L51!b53>2I?1!Y)ew;ypj2hSpB8BloaT|L{zn(J-)FBZfaP~i zpqqWY>*d7@bLfo_HhKqLWy!U+R(~s9L|z1uw-lIhqbQ~8M;_C_90}2e#q?lkiMbSS z8m5VR9yIn9;_izjOwlU$&aH6P>M^{pYGUxcC+8k~ZY>SMtB4y??qZIwF9J;xbJNh?Phi1? zL5U%qTWhA%L)0s9bsPHT_C8;U)4QW7w6#T(AQ0?=GIr&7Ozm*dKxOD<8I zQdV`DNGkWacS6O0vT#coNd4Z7bqb!c22<=6Dpo6kNI}(8c+I&M5(j8#k_b_kh#7Kg zFMTeXAqGHc5iR%O#Wu|W``&;rgIVRywyCv>MZZHG2zhI**D|X8(S2ZCJ@s!1ss9t` z%N3L4hd_N(>|jqXEo!5^DOUPV0~V1L0-TlD&svqq}_|c^d*}X_%MhtMK^+-u!2yBBY!BzgDaPD;NHuR$gE-p?4w; zFmT~d%tr0R_xtxjOs-fS9DU=X_ZSWROj*G943@=vZyCq0t0CyBV3Np~X;T7YVPMQQ z7zVL{D_rLk1oed*{XU>` zpWcoU%o+w~)XcHRRy=#G^-(3QwkZ0-swgUnV+vg3ID6w5ek{40%tJA`&Sh&dpQ_jG zXboLReI{)mVD0*>HZCeJ_*T|HCgqmIT+B)c-;=s^RjfzRF2zq?ETIG>;v0Y$NWar$ zOi-NcjPhbz;Qn@rIxxzY-R76+ooJ{5Grd~;SU_*KbmK_sGoMxHNlD!ydx^dLYF!+K zZaloD2ewtPALU!`dU5+oED!)V@C7uvT@jRNNDLyw<`wo~w=JMZ`3M{)E39|eJ^+FE z8BnH@PPU07+ox6jZ9S9!hGWSe=b>qohkHmwddey(hVq5HJHgnzIKebHu`ygst^@e0 z4v;)RD^h)AOWSv980rD}`CJlLd;^-1rZ*tQI^-EJY?K;4>to!ql~_3$1jeqz(9htW z&QkLSQ}jFq+xvojdu^5yt$Hu%qk2{y`MiVpl)&i7%c|d#U3ZGEd4~ujnmH%Gl;i^B zn0MSJTbN4emo=zRBD#KGdK{ZL1w-CoM?18h>FQ>q0aSA$P7YqH*eVn*bk 0 + template + constexpr auto ceil_log2(Integer n) + -> Integer + { + Integer log = 0; + --n; + while (n > 0) { + ++log; + n >>= 1; + } + return log; + } + // Halves a positive number, using unsigned division if possible template diff --git a/include/cpp-sort/detail/make_array.h b/include/cpp-sort/detail/make_array.h new file mode 100644 index 00000000..f1732629 --- /dev/null +++ b/include/cpp-sort/detail/make_array.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_DETAIL_MAKE_ARRAY_H_ +#define CPPSORT_DETAIL_MAKE_ARRAY_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +namespace cppsort +{ +namespace detail +{ + //////////////////////////////////////////////////////////// + // The constexpr capabilities of std::array are seriously + // underpowered in C++14, so we instead fill C arrays in + // some algorithms and use the following function to turn + // them into std::array + + template + constexpr auto make_array_impl(T(&arr)[N], std::index_sequence) + -> std::array + { + return std::array{{ arr[Indices]... }}; + } + + template + constexpr auto make_array(T(&arr)[N]) + -> std::array + { + using indices = std::make_index_sequence; + return make_array_impl(arr, indices{}); + } +}} + +#endif // CPPSORT_DETAIL_MAKE_ARRAY_H_ diff --git a/include/cpp-sort/fixed/merge_exchange_network_sorter.h b/include/cpp-sort/fixed/merge_exchange_network_sorter.h new file mode 100644 index 00000000..22cec501 --- /dev/null +++ b/include/cpp-sort/fixed/merge_exchange_network_sorter.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_FIXED_MERGE_EXCHANGE_NETWORK_SORTER_H_ +#define CPPSORT_FIXED_MERGE_EXCHANGE_NETWORK_SORTER_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../detail/attributes.h" +#include "../detail/bitops.h" +#include "../detail/iterator_traits.h" +#include "../detail/make_array.h" + +namespace cppsort +{ + //////////////////////////////////////////////////////////// + // Adapter + + template + struct merge_exchange_network_sorter; + + namespace detail + { + template + constexpr auto merge_exchange_pairs_number(DifferenceType n) noexcept + -> DifferenceType + { + DifferenceType nb_pairs = 0; + DifferenceType t = detail::ceil_log2(n); + + for (DifferenceType p = 1 << (t - 1); p > 0; p /= 2) { + DifferenceType q = 1 << (t - 1); + DifferenceType r = 0; + DifferenceType d = p; + + while (d > 0) { + for (DifferenceType i = 0; i < n - d; ++i) { + if ((i & p) == r) { + ++nb_pairs; + } + } + d = q - p; + q /= 2; + r = p; + } + } + + return nb_pairs; + } + + template + struct merge_exchange_network_sorter_impl + { + template + CPPSORT_ATTRIBUTE_NODISCARD + static constexpr auto index_pairs() + -> auto + { + constexpr DifferenceType n = N; + constexpr DifferenceType nb_pairs = merge_exchange_pairs_number(n); + + utility::index_pair pairs[nb_pairs] = {}; + std::size_t current_pair_idx = 0; + + DifferenceType t = detail::ceil_log2(n); + for (DifferenceType p = 1 << (t - 1); p > 0; p /= 2) { + DifferenceType q = 1 << (t - 1); + DifferenceType r = 0; + DifferenceType d = p; + + while (d > 0) { + for (DifferenceType i = 0; i < n - d; ++i) { + if ((i & p) == r) { + pairs[current_pair_idx] = { i, i + d }; + ++current_pair_idx; + } + } + d = q - p; + q /= 2; + r = p; + } + } + + return cppsort::detail::make_array(pairs); + } + + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + constexpr auto operator()(RandomAccessIterator first, RandomAccessIterator, + Compare compare={}, Projection projection={}) const + -> void + { + using difference_type = difference_type_t; + auto pairs = index_pairs(); + utility::sort_index_pairs(first, pairs, std::move(compare), std::move(projection)); + } + }; + + template<> + struct merge_exchange_network_sorter_impl<0u> + { + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + auto operator()(RandomAccessIterator, RandomAccessIterator, + Compare={}, Projection={}) const + -> void + {} + + template + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } + }; + + template<> + struct merge_exchange_network_sorter_impl<1u> + { + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + auto operator()(RandomAccessIterator, RandomAccessIterator, + Compare={}, Projection={}) const + -> void + {} + + template + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } + }; + } + + template + struct merge_exchange_network_sorter: + sorter_facade> + {}; + + //////////////////////////////////////////////////////////// + // Sorter traits + + template + struct sorter_traits> + { + using iterator_category = std::random_access_iterator_tag; + using is_always_stable = std::false_type; + }; + + template<> + struct fixed_sorter_traits + { + using iterator_category = std::random_access_iterator_tag; + using is_always_stable = std::false_type; + }; +} + +#endif // CPPSORT_FIXED_MERGE_EXCHANGE_NETWORK_SORTER_H_ diff --git a/include/cpp-sort/fixed_sorters.h b/include/cpp-sort/fixed_sorters.h index 5f29d960..660687e8 100644 --- a/include/cpp-sort/fixed_sorters.h +++ b/include/cpp-sort/fixed_sorters.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_FIXED_SORTERS_H_ @@ -10,6 +10,7 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #endif // CPPSORT_FIXED_SORTERS_H_ diff --git a/include/cpp-sort/fwd.h b/include/cpp-sort/fwd.h index 6fef281b..a3581c4f 100644 --- a/include/cpp-sort/fwd.h +++ b/include/cpp-sort/fwd.h @@ -65,6 +65,8 @@ namespace cppsort template struct low_moves_sorter; template + struct merge_exchange_network_sorter; + template struct sorting_network_sorter; //////////////////////////////////////////////////////////// diff --git a/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp b/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp index acef6084..4b408299 100644 --- a/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp +++ b/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -28,6 +28,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", cppsort::low_moves_sorter > >{}; + auto&& merge_exchange_sort = cppsort::schwartz_adapter< + cppsort::small_array_adapter< + cppsort::merge_exchange_network_sorter + > + >{}; auto&& sorting_network_sort = cppsort::schwartz_adapter< cppsort::small_array_adapter< cppsort::sorting_network_sorter @@ -42,6 +47,7 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", low_comparisons_sort(collection, &wrapper::value); low_moves_sort(collection, &wrapper::value); + merge_exchange_sort(collection, &wrapper::value); sorting_network_sort(collection, &wrapper::value); } @@ -51,6 +57,7 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", low_comparisons_sort(collection, &wrapper::value); low_moves_sort(collection, &wrapper::value); + merge_exchange_sort(collection, &wrapper::value); sorting_network_sort(collection, &wrapper::value); } @@ -69,6 +76,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), @@ -90,6 +102,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), @@ -111,6 +128,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), @@ -132,6 +154,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), @@ -153,11 +180,37 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); } + + SECTION( "size 31" ) + { + std::array collection; + helpers::iota(collection.begin(), collection.end(), -10.0, &wrapper::value); + + std::shuffle(collection.begin(), collection.end(), engine); + low_moves_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + + std::shuffle(collection.begin(), collection.end(), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + + std::shuffle(collection.begin(), collection.end(), engine); + sorting_network_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + } } TEST_CASE( "stability of Schwartzian transform adapter with fixed-size sorters", From c54d6a0736b63f0d8041bfb817847155ea451388 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 12 Jul 2021 13:34:00 +0200 Subject: [PATCH 48/56] LaTeX sorting networks: change document class to standalone --- tools/sorting-network-23.tex | 4 ++-- tools/sorting-network-24.tex | 4 ++-- tools/sorting-network-29.tex | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/sorting-network-23.tex b/tools/sorting-network-23.tex index 4fd63340..6040e2f0 100644 --- a/tools/sorting-network-23.tex +++ b/tools/sorting-network-23.tex @@ -1,7 +1,7 @@ -% Copyright (c) 2015-2020 Morwenn +% Copyright (c) 2015-2021 Morwenn % SPDX-License-Identifier: MIT -\documentclass{minimal} +\documentclass{standalone} \usepackage{tikz} \usepackage[trim]{tokenizer} diff --git a/tools/sorting-network-24.tex b/tools/sorting-network-24.tex index b515efc5..81780eed 100644 --- a/tools/sorting-network-24.tex +++ b/tools/sorting-network-24.tex @@ -1,7 +1,7 @@ -% Copyright (c) 2015-2020 Morwenn +% Copyright (c) 2015-2021 Morwenn % SPDX-License-Identifier: MIT -\documentclass{minimal} +\documentclass{standalone} \usepackage{tikz} \usepackage[trim]{tokenizer} diff --git a/tools/sorting-network-29.tex b/tools/sorting-network-29.tex index 65dafcd2..10652dad 100644 --- a/tools/sorting-network-29.tex +++ b/tools/sorting-network-29.tex @@ -1,7 +1,7 @@ -% Copyright (c) 2016-2020 Morwenn +% Copyright (c) 2016-2021 Morwenn % SPDX-License-Identifier: MIT -\documentclass{minimal} +\documentclass{standalone} \usepackage{tikz} \usepackage[trim]{tokenizer} From d574ae61fc4f81af6d225d651dd6264c77624fa0 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 14 Jul 2021 18:56:32 +0200 Subject: [PATCH 49/56] Tweak CMake/Conan integration --- conanfile.py | 4 ++++ docs/Tooling.md | 6 +++--- test_package/CMakeLists.txt | 8 +++++--- test_package/conanfile.py | 11 ++++++----- tools/release-checklist.md | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/conanfile.py b/conanfile.py index 481a8a47..dd04ddd1 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,6 +5,8 @@ from conans import CMake, ConanFile +required_conan_version = ">=1.33.0" + class CppSortConan(ConanFile): name = "cpp-sort" @@ -43,6 +45,8 @@ def package(self): self.copy(file, dst="licenses") def package_info(self): + self.cpp_info.names["cmake_find_package"] = "cpp-sort" + self.cpp_info.names["cmake_find_package_multi"] = "cpp-sort" if self.settings.compiler == "Visual Studio": self.cpp_info.cxxflags = ["/permissive-"] diff --git a/docs/Tooling.md b/docs/Tooling.md index 7381f7ba..c7e6cc26 100644 --- a/docs/Tooling.md +++ b/docs/Tooling.md @@ -9,11 +9,11 @@ The library's repository does contain files specific to other tools, but most of **cpp-sort** can be installed via CMake, in which case it exports a `cpp-sort::cpp-sort` target and all the files required for a basic integration. Once it has been installed on the system, the following lines should be enough to use it as a dependency: ```cmake -find_package(cpp-sort REQUIRED) +find_package(cpp-sort REQUIRED CONFIG) target_link_libraries(my-target PRIVATE cpp-sort::cpp-sort) ``` -Alternatively if you don't want to install **cpp-sort** directly, it can still be used directly as a subdirectory: +If you don't want to install **cpp-sort** directly, it can still be used directly as a subdirectory: ```cmake add_subdirectory(third_party/cpp-sort) @@ -51,7 +51,7 @@ The same options exist without the `CPPSORT_` prefix exist, but are deprecated. conan search cpp-sort --remote=conan-center ``` -And then install any version to your local cache as follows (here with version 1.9.0): +And then install any version to your local cache as follows (here with version 1.10.0): ```sh conan install cpp-sort/1.10.0 diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index 6f4b58cd..161b6eb7 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2018-2020 Morwenn +# Copyright (c) 2018-2021 Morwenn # SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 2.8.11) @@ -6,7 +6,9 @@ cmake_minimum_required(VERSION 2.8.11) project(test_package LANGUAGES CXX) include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) -conan_basic_setup() +conan_basic_setup(TARGETS) + +find_package(cpp-sort REQUIRED CONFIG) add_executable(${PROJECT_NAME} cpp-sort-integrity.cpp) -target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS}) +target_link_libraries(${PROJECT_NAME} PRIVATE cpp-sort::cpp-sort) diff --git a/test_package/conanfile.py b/test_package/conanfile.py index dac20544..932df7bb 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -1,17 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2018-2020 Morwenn +# Copyright (c) 2018-2021 Morwenn # SPDX-License-Identifier: MIT import os.path -from conans import ConanFile, CMake +from conans import CMake, ConanFile, tools class CppsortTestConan(ConanFile): settings = "os", "compiler", "build_type", "arch" - generators = "cmake" + generators = "cmake", "cmake_find_package_multi" def build(self): cmake = CMake(self) @@ -19,5 +19,6 @@ def build(self): cmake.build() def test(self): - bin_path = os.path.join("bin", "test_package") - self.run(bin_path, run_environment=True) + if not tools.cross_building(self.settings): + bin_path = os.path.join("bin", "test_package") + self.run(bin_path, run_environment=True) diff --git a/tools/release-checklist.md b/tools/release-checklist.md index 59446125..f249ddae 100644 --- a/tools/release-checklist.md +++ b/tools/release-checklist.md @@ -21,7 +21,7 @@ development phase: - [ ] README.md - [ ] version.h - [ ] Home.md in the documentation - - [ ] Tooling.md/Conan in the documentation + - [ ] Tooling.md/Conan in the documentation (2 mentions) - [ ] Make sure that the Conan recipe works. - [ ] Find a name for the new version. - [ ] Open a merge request, let the CI do its job. From 8f9e016e1b422da2fc45cb1b009cca812e7f9259 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 17 Jul 2021 23:57:29 +0200 Subject: [PATCH 50/56] Add odd_even_merge_network_sorter --- docs/Fixed-size-sorters.md | 24 ++- include/cpp-sort/detail/bitops.h | 12 ++ .../fixed/odd_even_merge_network_sorter.h | 179 ++++++++++++++++++ include/cpp-sort/fixed_sorters.h | 1 + include/cpp-sort/fwd.h | 2 + .../schwartz_adapter_fixed_sorters.cpp | 43 +++++ 6 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 include/cpp-sort/fixed/odd_even_merge_network_sorter.h diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index c0ef34d7..c88e549e 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -88,7 +88,7 @@ Note that this fixed-sized sorter is *not* move-optimal: it tries to perform a f #include ``` -This fixed-size sorter implements *merge-exchange sort* a variation of Batcher's [odd-even mergesort][odd-even-mergesort] described by Knuth in *[The Art of Computer Programming][taocp] vol.3 - Sorting and Searching*. Unlike the algorithm described in the Wikipedia article, this produces two interleaved [sorting networks][sorting-network] and merges them. +This fixed-size sorter implements *merge-exchange sort* a variation of Batcher's [*odd-even mergesort*][odd-even-mergesort] described by Knuth in *[The Art of Computer Programming][taocp] vol.3 - Sorting and Searching*. Unlike the algorithm described in the Wikipedia article, this produces two interleaved [sorting networks][sorting-network] and merges them. ![Merge-exchange sorting network for 8 inputs](https://raw.githubusercontent.com/Morwenn/cpp-sort/master/docs/images/merge-exchange-network-8.png) @@ -99,6 +99,28 @@ struct merge_exchange_network_sorter; All specializations of `merge_exchange_network_sorter` provide a `index_pairs() static` function template which returns an [`std::array`][std-array] of [`utility::index_pair`][utility-sorting-networks]. Those pairs represent the indices used by the CE operations of the network and can be passed manipulated and passed to dedicated [sorting network tools][utility-sorting-networks] from the library's utility module. The function is templated of the index/difference type, which must be constructible from `int`. +```cpp +template +[[nodiscard]] static constexpr auto index_pairs() + -> std::array, /* Number of CEs in the network */>; + +### `merge_exchange_network_sorter` + +```cpp +#include +``` + +This fixed-size sorter implements Batcher's [*odd-even mergesort*][odd-even-mergesort], which can be implemented as a family of sorting networks recursively sorting both halves of the input and merging them. + +![Odd-even mergesort network for 8 inputs](https://raw.githubusercontent.com/Morwenn/cpp-sort/master/docs/images/odd-even-merge-network-8.png) + +```cpp +template +struct odd_even_merge_network_sorter; +``` + +All specializations of `odd_even_merge_network_sorter` provide a `index_pairs() static` function template which returns an [`std::array`][std-array] of [`utility::index_pair`][utility-sorting-networks]. Those pairs represent the indices used by the CE operations of the network and can be passed manipulated and passed to dedicated [sorting network tools][utility-sorting-networks] from the library's utility module. The function is templated of the index/difference type, which must be constructible from `int`. + ```cpp template [[nodiscard]] static constexpr auto index_pairs() diff --git a/include/cpp-sort/detail/bitops.h b/include/cpp-sort/detail/bitops.h index 25e22790..56eebf97 100644 --- a/include/cpp-sort/detail/bitops.h +++ b/include/cpp-sort/detail/bitops.h @@ -90,6 +90,18 @@ namespace detail { return value / 2; } + + // Returns whether an integer has a single bit set, generally + // used to check whether an integer is a power of 2, + // assumes n >= 0 + + template + constexpr auto has_single_bit(Integer n) noexcept + -> bool + { + auto x = static_cast>(n); + return x != 0 && (x & (x - 1)) == 0; + } }} #endif // CPPSORT_DETAIL_BITOPS_H_ diff --git a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h new file mode 100644 index 00000000..2549f681 --- /dev/null +++ b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_FIXED_ODD_EVEN_MERGE_NETWORK_SORTER_H_ +#define CPPSORT_FIXED_ODD_EVEN_MERGE_NETWORK_SORTER_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../detail/attributes.h" +#include "../detail/bitops.h" +#include "../detail/iterator_traits.h" +#include "../detail/make_array.h" + +namespace cppsort +{ + //////////////////////////////////////////////////////////// + // Adapter + + template + struct odd_even_merge_network_sorter; + + namespace detail + { + template + constexpr auto odd_even_merge_pairs_number(DifferenceType n) noexcept + -> DifferenceType + { + DifferenceType nb_pairs = 0; + + for (DifferenceType p = 1; p < n; p *= 2) { + for (auto k = p; k > 0; k /= 2) { + for (auto j = k % p; j < n - k; j += 2 * k) { + for (DifferenceType i = 0; i < k; ++i) { + if ((i + j) / (p * 2) == (i + j + k) / (p * 2)) { + ++nb_pairs; + } + } + } + } + } + + return nb_pairs; + } + + template + struct odd_even_merge_network_sorter_impl + { + template + CPPSORT_ATTRIBUTE_NODISCARD + static constexpr auto index_pairs() + -> auto + { + constexpr DifferenceType n = N; + constexpr DifferenceType nb_pairs = odd_even_merge_pairs_number(n); + + utility::index_pair pairs[nb_pairs] = {}; + std::size_t current_pair_idx = 0; + + for (DifferenceType p = 1; p < n; p *= 2) { + for (auto k = p; k > 0; k /= 2) { + for (auto j = k % p; j < n - k; j += 2 * k) { + for (DifferenceType i = 0; i < k; ++i) { + if ((i + j) / (p * 2) == (i + j + k) / (p * 2)) { + pairs[current_pair_idx] = { i + j, i + j + k }; + ++current_pair_idx; + } + } + } + } + } + + return cppsort::detail::make_array(pairs); + } + + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + constexpr auto operator()(RandomAccessIterator first, RandomAccessIterator, + Compare compare={}, Projection projection={}) const + -> void + { + using difference_type = difference_type_t; + auto pairs = index_pairs(); + utility::sort_index_pairs(first, pairs, std::move(compare), std::move(projection)); + } + }; + + template<> + struct odd_even_merge_network_sorter_impl<0u> + { + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + auto operator()(RandomAccessIterator, RandomAccessIterator, + Compare={}, Projection={}) const + -> void + {} + + template + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } + }; + + template<> + struct odd_even_merge_network_sorter_impl<1u> + { + template< + typename RandomAccessIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + auto operator()(RandomAccessIterator, RandomAccessIterator, + Compare={}, Projection={}) const + -> void + {} + + template + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } + }; + } + + template + struct odd_even_merge_network_sorter: + sorter_facade> + { + static_assert(N == 0 || detail::has_single_bit(N), + "odd_even_merge_network_sorter only works when N is a power of 2"); + }; + + //////////////////////////////////////////////////////////// + // Sorter traits + + template + struct sorter_traits> + { + using iterator_category = std::random_access_iterator_tag; + using is_always_stable = std::false_type; + }; + + template<> + struct fixed_sorter_traits + { + using iterator_category = std::random_access_iterator_tag; + using is_always_stable = std::false_type; + }; +} + +#endif // CPPSORT_FIXED_ODD_EVEN_MERGE_NETWORK_SORTER_H_ diff --git a/include/cpp-sort/fixed_sorters.h b/include/cpp-sort/fixed_sorters.h index 660687e8..f5a22850 100644 --- a/include/cpp-sort/fixed_sorters.h +++ b/include/cpp-sort/fixed_sorters.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #endif // CPPSORT_FIXED_SORTERS_H_ diff --git a/include/cpp-sort/fwd.h b/include/cpp-sort/fwd.h index a3581c4f..d720f73b 100644 --- a/include/cpp-sort/fwd.h +++ b/include/cpp-sort/fwd.h @@ -67,6 +67,8 @@ namespace cppsort template struct merge_exchange_network_sorter; template + struct odd_even_merge_network_sorter; + template struct sorting_network_sorter; //////////////////////////////////////////////////////////// diff --git a/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp b/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp index 4b408299..b4c5e126 100644 --- a/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp +++ b/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp @@ -33,6 +33,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", cppsort::merge_exchange_network_sorter > >{}; + auto&& odd_even_merge_sort = cppsort::schwartz_adapter< + cppsort::small_array_adapter< + cppsort::odd_even_merge_network_sorter + > + >{}; auto&& sorting_network_sort = cppsort::schwartz_adapter< cppsort::small_array_adapter< cppsort::sorting_network_sorter @@ -48,6 +53,7 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", low_comparisons_sort(collection, &wrapper::value); low_moves_sort(collection, &wrapper::value); merge_exchange_sort(collection, &wrapper::value); + odd_even_merge_sort(collection, &wrapper::value); sorting_network_sort(collection, &wrapper::value); } @@ -58,6 +64,7 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", low_comparisons_sort(collection, &wrapper::value); low_moves_sort(collection, &wrapper::value); merge_exchange_sort(collection, &wrapper::value); + odd_even_merge_sort(collection, &wrapper::value); sorting_network_sort(collection, &wrapper::value); } @@ -81,6 +88,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + odd_even_merge_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), @@ -133,6 +145,11 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); + odd_even_merge_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), + std::less<>{}, &wrapper::value) ); + std::shuffle(std::begin(collection), std::end(collection), engine); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), @@ -211,6 +228,32 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); } + + SECTION( "size 32" ) + { + std::array collection; + helpers::iota(collection.begin(), collection.end(), -10.0, &wrapper::value); + + std::shuffle(collection.begin(), collection.end(), engine); + low_moves_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + + std::shuffle(collection.begin(), collection.end(), engine); + merge_exchange_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + + std::shuffle(collection.begin(), collection.end(), engine); + odd_even_merge_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + + std::shuffle(collection.begin(), collection.end(), engine); + sorting_network_sort(collection, &wrapper::value); + CHECK( helpers::is_sorted(collection.begin(), collection.end(), + std::less<>{}, &wrapper::value) ); + } } TEST_CASE( "stability of Schwartzian transform adapter with fixed-size sorters", From 313c2f8150dec3ee6dc00cbb1a52a87067a54cd9 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 18 Jul 2021 00:49:44 +0200 Subject: [PATCH 51/56] tweak fixed-size sorters doc --- docs/Fixed-size-sorters.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index c88e549e..464aaf72 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -1,8 +1,8 @@ Fixed-size sorters, sometimes called *fixed sorters* for simplicity are a special kind of sorters designed to sort a fixed number of values. Their `operator()` also takes either an iterable or a pair of iterators as well as an optional comparison and projection functions. Most of the time the end iterator is unused, but future versions of the library may start to use it to optionally perform bound-checking. -Fixed-sized sorters are not actual sorters *per se* but class templates that take an `std::size_t` template parameter. Every valid specialization of a fixed-size sorter for a given size yields a "valid" sorter. Several fixed-size sorters have specializations for some sizes only and will trigger a compile-time error when one tries to instantiate a specialization which is not part of the fixed-size sorter's domain (the domain corresponds to the set of valid specializations). Information about fixed-size sorters can be obtained via [`fixed_sorter_traits`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-traits#fixed_sorter_traits). One can also make sure that a given fixed-size sorter is automatically used to sort small fixed-size arrays thanks to [`small_array_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#small_array_adapter). +Fixed-size sorters are not actual sorters *per se* but class templates that take an `std::size_t` template parameter. Every valid specialization of a fixed-size sorter for a given size yields a "valid" sorter. Several fixed-size sorters have specializations for some sizes only and will trigger a compile-time error when one tries to instantiate a specialization which is not part of the fixed-size sorter's domain (the domain corresponds to the set of valid specializations). Information about fixed-size sorters can be obtained via [`fixed_sorter_traits`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-traits#fixed_sorter_traits). One can also make sure that a given fixed-size sorter is automatically used to sort small fixed-size arrays thanks to [`small_array_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#small_array_adapter). -It is possible to include all the fixed-sized sorters at once with the following directive: +It is possible to include all the fixed-size sorters at once with the following directive: ```cpp #include @@ -80,7 +80,7 @@ template struct low_moves_sorter; ``` -Note that this fixed-sized sorter is *not* move-optimal: it tries to perform a few moves without wasting too much memory and with a somewhat reasonable number of comparisons for small collections. If you really need a sorting algorithm that performs the lowest possible number of move operations, you can use the library's [`indirect_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#indirect_adapter) instead, but it comes at the cost of a higher memory footprint. You probably want to use if only when the objects are *really* expensive to copy. +Note that this fixed-size sorter is *not* move-optimal: it tries to perform a few moves without wasting too much memory and with a somewhat reasonable number of comparisons for small collections. If you really need a sorting algorithm that performs the lowest possible number of move operations, you can use the library's [`indirect_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#indirect_adapter) instead, but it comes at the cost of a higher memory footprint. You probably want to use if only when the objects are *really* expensive to copy. ### `merge_exchange_network_sorter` @@ -90,7 +90,7 @@ Note that this fixed-sized sorter is *not* move-optimal: it tries to perform a f This fixed-size sorter implements *merge-exchange sort* a variation of Batcher's [*odd-even mergesort*][odd-even-mergesort] described by Knuth in *[The Art of Computer Programming][taocp] vol.3 - Sorting and Searching*. Unlike the algorithm described in the Wikipedia article, this produces two interleaved [sorting networks][sorting-network] and merges them. -![Merge-exchange sorting network for 8 inputs](https://raw.githubusercontent.com/Morwenn/cpp-sort/master/docs/images/merge-exchange-network-8.png) +![Merge-exchange sorting network for 8 inputs](https://github.com/Morwenn/cpp-sort/wiki/images/merge-exchange-network-8.png) ```cpp template @@ -112,7 +112,7 @@ template This fixed-size sorter implements Batcher's [*odd-even mergesort*][odd-even-mergesort], which can be implemented as a family of sorting networks recursively sorting both halves of the input and merging them. -![Odd-even mergesort network for 8 inputs](https://raw.githubusercontent.com/Morwenn/cpp-sort/master/docs/images/odd-even-merge-network-8.png) +![Odd-even mergesort network for 8 inputs](https://github.com/Morwenn/cpp-sort/wiki/images/odd-even-merge-network-8.png) ```cpp template From 451806079c0dc05320f5593766fcc920b15460b4 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 18 Jul 2021 13:19:53 +0200 Subject: [PATCH 52/56] Add missing image to documentation --- docs/images/odd-even-merge-network-8.png | Bin 0 -> 5215 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/odd-even-merge-network-8.png diff --git a/docs/images/odd-even-merge-network-8.png b/docs/images/odd-even-merge-network-8.png new file mode 100644 index 0000000000000000000000000000000000000000..ffcd5fc3e3a7d3b1665c0bb5578a8dd0a1807b4a GIT binary patch literal 5215 zcmdUz`8!no|HseFSh5Vtl5H$mO30SXBuR8LVq_~zmmX03v_y9k2{d5jg;m*pIa^bBfGe z7`|2M;S>AjpQU$L;kw}S0IvZ4NS5+h*lXM3-_MG{C!bXhq@R>1bJjdbgVm@wlsMWG zZmW3m6-w%;EhrtjEq$XT>23SP^ge4k->ZH5em$e4Or2OwH5v|GcO~Y-K_2ETzPAvb)YRLA#>D6o!l5447EHMvGl5eq^Ls9ug;(Sghhv)L__n!Y9WJun|*jx9~em*VU?f+=dlIxvMB zrnrt~!AQcpbxF`NI`>@5MhBR!HXMX#9sH)1#%hp>ESOK8(H7R{2^fZ&zEKXl3-{Zy zPYj;sN9SjmrMHujwM+NdUP%+-vguyWGaW8l$52+aDy_p|1CCu=)ZF|&155jn4VCn9 zq_u-%71q{`t>-ErpKEA;tqAs9rhCg7|I2{IY+=L$Pll{=%W6o}7&?vbl@E9i3|>^S z4M1+oqctPMeEu{j0pzsf&kOpOc4oA}$ov7b_{=~TyXd(f=S3yPRl^zSSgi;#Y`;Sd z;bxlZP1@vPtY8Q0f$DG9HnC}c{kg4*{o+DSqX%o5Vja+#d8!)nopxywDgSSe5spq8 zAqPbV#Rp5PNN0_C(5OMRi2Lfka3Sg^`0MN{;Us0KAJhPAv#YG)zgpqRZ8%EWO zN)NH3ppE%Ib`GD4soGs@F^h){gM9287)ZF^+U zr|Q(Q-D*L?<$=zUex>CFkvNm{N888c{I7t@5HBNnksr*_XrFKWMwz5*&K^A}HCW=! z4#E4RR@rz<1;@lM1u9F1OVZ$42+uAQooWR1p0DNn?sPlNZ6z8%m$pEFdsXb@&(n$Y zT2+Ihi6$LbYE4>sq6pAx=2q~$APX*JT^si(Y|c~C7*>%V`Knss$EkkpHgPJEdb1(^ zcqJuF_-+Jh86Wi23h_mWaf$gjfbe0%zM0mXPlMfhGTZsMLS;1LF0D7iDRgMAQ(}W- z>TTP1xj)Nf+>C_ds1Og8v}r$!#EqI0HED%o=cCuF1YOQ^9<4+yxYNy_IW6+>@229s-h)(wDs}%rZC%ZX-?8X} zE;+^);(7UGP6bS)JExdK;}7r+!x{3nR z+Fg%Op5@nR3!m&q5yaCam7?#s%DX{L)R1~-jgRYrT|7smm|)dhe1Z;nSZ(_)Dk0_f zZI^ko>nOdqa(ymz0c7O7ejs~^(n38LjkU}bna7V>1={rfGK}Hxio(4 z{LYJsearHd-<f8!6Z%mdoEz(rq(s8DAKBoPtxYik3^Zrw4 z!fdDk#qtPq<|sb;&E=g%^`y{7nZY`mnH?~YDz{otdo}#MA$Kh$e#DpEQNOsxLe$^i z!i?_A%`vnO&Geeq+FL$8&ul|`&Mku(X2Xf1M^+uTpDl_(8ndbWc41ec9jZH(1526v zo8qC5L?^G_-(8|)P2KBtGEG;c*L^JKDF;Ltxjy<@ZA>?d=ykSQPnA>6k}iaUQnWd) z3&Y|+#AcA6Q#_w|o!Kf9naw3@w2`lE-W=11=p)K}T|Qu8gSo}m|02OxyOGU7HdT{i z%72e^s#^H&4dGoq~SCllth`Zlm z9RD9=`twGP9pBX5;S;DImQd+fNA?H)^R+0rJbz4EZGHwz4qh9yJX!)eYTqri50wju zGpr=)#0wW+fx>#6{WH3G*8wI2<$WMr4VAYN!teLQzMR)-L$8}kXFZ?Y4XO@k*6Hs& z)k-IRZggpS{B>a>UZUdV@u;}-B%Bw|Izz^vQa?Ww}RE-*Zn3X)$xISGtgCm)2O1_%3&U$J8i9 z-Wsszc5c}Q#>{d$KR1(8{CAV73sxQ1wWs~@SBm(&&bY1QeJp{slfgS+N<+M`bZ5m% zsVCX6Bh51`n`Zo0b=7>+%*`Pt&A;`#{+JhH;{!DL>Y`UU*1{t%#-lBer~M zro)Z)504)%ZttD#r0S(l8L_VI?iaCXjf{UWW&Ct=gMOAfATQeW{c?KwL*8ZuwC)~I z$?2j74I;$+;JuUv_tRtd!3mn^LgS~&Y`-t;t0A0L%S?KS4o zOgR@z)Y>tz3DpOF+%ne|Mq4zdJ@#acMo@~LNmLoHD{FVB)Fp&{^{+(1=e=I;C__vI zLUvgLT`^s8Qbb>%LU}a*RF1Kg_%Qr1U^FSx0}gFxRL@r!7b+}ca9_BK>V(}pKP_U36=Fg;*LmoCMp9YYh3nhXM2L%$qV^IRj zKzkWsQn>K(ka%RyRxIaKa4_?N?cRpishr=8N@{b=>&z)`;AX;OI}&cGuqK0fTN&D+ z^TjIs(3<&jRkB;na$`ZBO|2YmHE_z_Kf3NBPoBQV%;GthTI&Cr@d`UsdpHQ{>Bih)M2y zd!DjFK~d{q#*>=4N(^&kN-8t>gTJxY_VPhNzlbfK=oL#c^e7Ma_F>9ez; zfjqc9_CWEL6_PzoA97Q*lky-W@!_wZ8w%Hvh`I}Lx#d7OH^!;vl>R=#PGnR3m~kMu zAHwuxoN?wF(`j=O&a!ySW9Qh`pL#|B3@v@I*<1>}f@ z-n1`%{LeVQ;@@D2wZa#CS28)&qq5JoHW_{4F3!RqBeRt=K z#wrK9?;*N7bOk?9zo3bIPkp{??&3VIVq&UD6raKT425)esvR)SrKdX$^v%)35%*!i zQkB)-><;S28v4A!xatl$#SlL7iTOkb>tnPY&iWe8H&5jXcM-9jxrJ#_CKkLI!^Sdy zQr&D^2W+l**P+Md*sV7Ps2>rtsl8tI-IJTIsYNC4w@PrrF}jsJ^*Os}7}uPErUzrz z(SkpOxYh8u_$}=j!C8KKyv>3!C5(567pm@ZRe#M&_S5Wff6Ni)*3Hal=WfI<@(0qd zp(?{N2JuvpA=(qdV(n$DK(PIy7~>7g(G;CsZQ;d;n>hCG?uGx$-^T@(U>AYs6g6-l z>f7;Z<7G*R7a~KsT<6UZ)q0OAxWg?apc^Cr9l#$U9$M>>hw>;$&>Bw~L)&)qDlkxI zU32+QS)IIC0rRahhS-!pNO^LfQPo1eK--hk)4ou3Zhp@9L>61WeJ3u8Tf#Gxp|&H7 z9HVLB{$D2#_Fhfum>Ip>bRlo->ysreckl(nmR;s#nx?Gq9MW)tm1&=Ii{p^cpaR+? z;yzzEeLHcng|66c+j8%z!Y#F6mv`%(6ap8jUxq8cuyJ0?b%=;5_8x|FTkS#(@GZk`#jICjESP>cn8H(Q=ka*TrSxMKgrim3huCMvOk5(;|;$~_%KRkzY+d{4i0nIYnpyfxp~)jM{35pvXra7`RwNs>9z>=fg3ed~t#@`-t^m>$l0Ggy#Q>BAD&J z*&;tEDZPl#{2tC<%Uj!tvXB+iLHNRlr$q{OjpXW4+@Zw(Om#SNe68U3+;Z4hy5S#v z;|UZy_#><7J~!xvt2h^)eO4_S*Q0gYhQOq@T+VbELtLWt8`$V2JZ<9qv2EJ)A=CX0fFiiX8uRb){Q^n^brdRjJ z{Btg+^qWlV*d_KUfv$HCv9W=&%U2*09<_W9pICN>@AXwEi%y6ge?<^?Xkx;4v%+w( hqCZ|Ylbj4WqLhSH!a4ZYr`(AlfIWw^C_jt8{U7iz`EvjO literal 0 HcmV?d00001 From a8ec718558e02eaaf16de40948cb72dfb4caaa4f Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 18 Jul 2021 15:57:34 +0200 Subject: [PATCH 53/56] Simplify fixed-size sorters with empty_sorter --- include/cpp-sort/detail/empty_sorter.h | 62 +++++++++++++++++++ .../cpp-sort/detail/low_comparisons/sort0.h | 38 ------------ .../cpp-sort/detail/low_comparisons/sort1.h | 38 ------------ include/cpp-sort/detail/low_moves/sort0.h | 38 ------------ include/cpp-sort/detail/low_moves/sort1.h | 38 ------------ .../cpp-sort/detail/sorting_network/sort0.h | 37 ----------- .../cpp-sort/detail/sorting_network/sort1.h | 37 ----------- .../cpp-sort/fixed/low_comparisons_sorter.h | 15 ++++- include/cpp-sort/fixed/low_moves_sorter.h | 15 ++++- .../fixed/merge_exchange_network_sorter.h | 51 +++------------ .../fixed/odd_even_merge_network_sorter.h | 51 +++------------ .../cpp-sort/fixed/sorting_network_sorter.h | 13 +++- 12 files changed, 111 insertions(+), 322 deletions(-) create mode 100644 include/cpp-sort/detail/empty_sorter.h delete mode 100644 include/cpp-sort/detail/low_comparisons/sort0.h delete mode 100644 include/cpp-sort/detail/low_comparisons/sort1.h delete mode 100644 include/cpp-sort/detail/low_moves/sort0.h delete mode 100644 include/cpp-sort/detail/low_moves/sort1.h delete mode 100644 include/cpp-sort/detail/sorting_network/sort0.h delete mode 100644 include/cpp-sort/detail/sorting_network/sort1.h diff --git a/include/cpp-sort/detail/empty_sorter.h b/include/cpp-sort/detail/empty_sorter.h new file mode 100644 index 00000000..e9523979 --- /dev/null +++ b/include/cpp-sort/detail/empty_sorter.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_DETAIL_EMPTY_SORTER_H_ +#define CPPSORT_DETAIL_EMPTY_SORTER_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include "attributes.h" + +namespace cppsort +{ +namespace detail +{ + //////////////////////////////////////////////////////////// + // Basic empty fixed-size "sorter", generally the one used + // by fixed-size sorters of size 0 or 1, which don't need + // to reorder anything + + struct empty_sorter_impl + { + template< + typename ForwardIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = std::enable_if_t> + > + constexpr auto operator()(ForwardIterator, ForwardIterator, + Compare={}, Projection={}) const noexcept + -> void + {} + }; + + //////////////////////////////////////////////////////////// + // Dedicated empty sorter for sorting networks, providing + // additional sorting network-specific functions + + struct empty_network_sorter_impl: + empty_sorter_impl + { + template + CPPSORT_ATTRIBUTE_NODISCARD + static constexpr auto index_pairs() + -> std::array, 0> + { + return {}; + } + }; +}} + +#endif // CPPSORT_DETAIL_EMPTY_SORTER_H_ diff --git a/include/cpp-sort/detail/low_comparisons/sort0.h b/include/cpp-sort/detail/low_comparisons/sort0.h deleted file mode 100644 index 03c97d24..00000000 --- a/include/cpp-sort/detail/low_comparisons/sort0.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2015-2016 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT0_H_ -#define CPPSORT_DETAIL_LOW_COMPARISONS_SORT0_H_ - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - -namespace cppsort -{ -namespace detail -{ - template<> - struct low_comparisons_sorter_impl<0u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - }; -}} - -#endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT0_H_ diff --git a/include/cpp-sort/detail/low_comparisons/sort1.h b/include/cpp-sort/detail/low_comparisons/sort1.h deleted file mode 100644 index 586190f6..00000000 --- a/include/cpp-sort/detail/low_comparisons/sort1.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2015-2016 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT1_H_ -#define CPPSORT_DETAIL_LOW_COMPARISONS_SORT1_H_ - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - -namespace cppsort -{ -namespace detail -{ - template<> - struct low_comparisons_sorter_impl<1u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - }; -}} - -#endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT1_H_ diff --git a/include/cpp-sort/detail/low_moves/sort0.h b/include/cpp-sort/detail/low_moves/sort0.h deleted file mode 100644 index 0acf6b1e..00000000 --- a/include/cpp-sort/detail/low_moves/sort0.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2015-2016 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_LOW_MOVES_SORT0_H_ -#define CPPSORT_DETAIL_LOW_MOVES_SORT0_H_ - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - -namespace cppsort -{ -namespace detail -{ - template<> - struct low_moves_sorter_impl<0u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - }; -}} - -#endif // CPPSORT_DETAIL_LOW_MOVES_SORT0_H_ diff --git a/include/cpp-sort/detail/low_moves/sort1.h b/include/cpp-sort/detail/low_moves/sort1.h deleted file mode 100644 index 84bd066a..00000000 --- a/include/cpp-sort/detail/low_moves/sort1.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2015-2016 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_LOW_MOVES_SORT1_H_ -#define CPPSORT_DETAIL_LOW_MOVES_SORT1_H_ - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include - -namespace cppsort -{ -namespace detail -{ - template<> - struct low_moves_sorter_impl<1u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - }; -}} - -#endif // CPPSORT_DETAIL_LOW_MOVES_SORT1_H_ diff --git a/include/cpp-sort/detail/sorting_network/sort0.h b/include/cpp-sort/detail/sorting_network/sort0.h deleted file mode 100644 index 1b103368..00000000 --- a/include/cpp-sort/detail/sorting_network/sort0.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2015-2021 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT0_H_ -#define CPPSORT_DETAIL_SORTING_NETWORK_SORT0_H_ - -namespace cppsort -{ -namespace detail -{ - template<> - struct sorting_network_sorter_impl<0u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - - template - static constexpr auto index_pairs() - -> std::array, 0> - { - return {}; - } - }; -}} - -#endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT0_H_ diff --git a/include/cpp-sort/detail/sorting_network/sort1.h b/include/cpp-sort/detail/sorting_network/sort1.h deleted file mode 100644 index bb79c981..00000000 --- a/include/cpp-sort/detail/sorting_network/sort1.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2015-2021 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT1_H_ -#define CPPSORT_DETAIL_SORTING_NETWORK_SORT1_H_ - -namespace cppsort -{ -namespace detail -{ - template<> - struct sorting_network_sorter_impl<1u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - - template - static constexpr auto index_pairs() - -> std::array, 0> - { - return {}; - } - }; -}} - -#endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT1_H_ diff --git a/include/cpp-sort/fixed/low_comparisons_sorter.h b/include/cpp-sort/fixed/low_comparisons_sorter.h index 80aca1d7..fac933a0 100644 --- a/include/cpp-sort/fixed/low_comparisons_sorter.h +++ b/include/cpp-sort/fixed/low_comparisons_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_FIXED_LOW_COMPARISONS_SORTER_H_ @@ -14,6 +14,7 @@ #include #include #include +#include "../detail/empty_sorter.h" namespace cppsort { @@ -30,6 +31,16 @@ namespace cppsort "low_comparisons_sorter has no specialization for this size of N" ); }; + + template<> + struct low_comparisons_sorter_impl<0u>: + cppsort::detail::empty_sorter_impl + {}; + + template<> + struct low_comparisons_sorter_impl<1u>: + cppsort::detail::empty_sorter_impl + {}; } template @@ -62,8 +73,6 @@ namespace cppsort } // Specializations of low_comparisons_sorter for some values of N -#include "../detail/low_comparisons/sort0.h" -#include "../detail/low_comparisons/sort1.h" #include "../detail/low_comparisons/sort2.h" #include "../detail/low_comparisons/sort3.h" #include "../detail/low_comparisons/sort4.h" diff --git a/include/cpp-sort/fixed/low_moves_sorter.h b/include/cpp-sort/fixed/low_moves_sorter.h index f4d75266..4ab33554 100644 --- a/include/cpp-sort/fixed/low_moves_sorter.h +++ b/include/cpp-sort/fixed/low_moves_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_FIXED_LOW_MOVES_SORTER_H_ @@ -18,6 +18,7 @@ #include #include #include +#include "../detail/empty_sorter.h" #include "../detail/minmax_element.h" namespace cppsort @@ -77,6 +78,16 @@ namespace cppsort std::move(compare), std::move(projection)); } }; + + template<> + struct low_moves_sorter_impl<0u>: + cppsort::detail::empty_sorter_impl + {}; + + template<> + struct low_moves_sorter_impl<1u>: + cppsort::detail::empty_sorter_impl + {}; } template @@ -108,8 +119,6 @@ namespace cppsort } // Specializations of low_moves_sorter for some values of N -#include "../detail/low_moves/sort0.h" -#include "../detail/low_moves/sort1.h" #include "../detail/low_moves/sort2.h" #include "../detail/low_moves/sort3.h" #include "../detail/low_moves/sort4.h" diff --git a/include/cpp-sort/fixed/merge_exchange_network_sorter.h b/include/cpp-sort/fixed/merge_exchange_network_sorter.h index 22cec501..f39cf0f9 100644 --- a/include/cpp-sort/fixed/merge_exchange_network_sorter.h +++ b/include/cpp-sort/fixed/merge_exchange_network_sorter.h @@ -19,6 +19,7 @@ #include #include "../detail/attributes.h" #include "../detail/bitops.h" +#include "../detail/empty_sorter.h" #include "../detail/iterator_traits.h" #include "../detail/make_array.h" @@ -114,52 +115,14 @@ namespace cppsort }; template<> - struct merge_exchange_network_sorter_impl<0u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - - template - static constexpr auto index_pairs() - -> std::array, 0> - { - return {}; - } - }; + struct merge_exchange_network_sorter_impl<0u>: + cppsort::detail::empty_network_sorter_impl + {}; template<> - struct merge_exchange_network_sorter_impl<1u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - - template - static constexpr auto index_pairs() - -> std::array, 0> - { - return {}; - } - }; + struct merge_exchange_network_sorter_impl<1u>: + cppsort::detail::empty_network_sorter_impl + {}; } template diff --git a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h index 2549f681..27814549 100644 --- a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h +++ b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h @@ -19,6 +19,7 @@ #include #include "../detail/attributes.h" #include "../detail/bitops.h" +#include "../detail/empty_sorter.h" #include "../detail/iterator_traits.h" #include "../detail/make_array.h" @@ -102,52 +103,14 @@ namespace cppsort }; template<> - struct odd_even_merge_network_sorter_impl<0u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - - template - static constexpr auto index_pairs() - -> std::array, 0> - { - return {}; - } - }; + struct odd_even_merge_network_sorter_impl<0u>: + cppsort::detail::empty_network_sorter_impl + {}; template<> - struct odd_even_merge_network_sorter_impl<1u> - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t> - > - auto operator()(RandomAccessIterator, RandomAccessIterator, - Compare={}, Projection={}) const - -> void - {} - - template - static constexpr auto index_pairs() - -> std::array, 0> - { - return {}; - } - }; + struct odd_even_merge_network_sorter_impl<1u>: + cppsort::detail::empty_network_sorter_impl + {}; } template diff --git a/include/cpp-sort/fixed/sorting_network_sorter.h b/include/cpp-sort/fixed/sorting_network_sorter.h index 5f26c570..f0d26675 100644 --- a/include/cpp-sort/fixed/sorting_network_sorter.h +++ b/include/cpp-sort/fixed/sorting_network_sorter.h @@ -14,6 +14,7 @@ #include #include #include +#include "../detail/empty_sorter.h" namespace cppsort { @@ -30,6 +31,16 @@ namespace cppsort "sorting_network_sorter has no specialization for this size of N" ); }; + + template<> + struct sorting_network_sorter_impl<0u>: + cppsort::detail::empty_network_sorter_impl + {}; + + template<> + struct sorting_network_sorter_impl<1u>: + cppsort::detail::empty_network_sorter_impl + {}; } template @@ -69,8 +80,6 @@ namespace cppsort #include "../detail/swap_if.h" // Specializations of sorting_network_sorter for some values of N -#include "../detail/sorting_network/sort0.h" -#include "../detail/sorting_network/sort1.h" #include "../detail/sorting_network/sort2.h" #include "../detail/sorting_network/sort3.h" #include "../detail/sorting_network/sort4.h" From 3b52b86bf06610ce930e5ec3714b65b06c8e90a8 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 19 Jul 2021 18:19:26 +0200 Subject: [PATCH 54/56] More explicit constructors in the test suite --- testsuite/adapters/every_adapter_stateful_sorter.cpp | 2 +- testsuite/testing-tools/span.h | 6 +++--- testsuite/utility/adapter_storage.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/testsuite/adapters/every_adapter_stateful_sorter.cpp b/testsuite/adapters/every_adapter_stateful_sorter.cpp index 8ff2dd8d..21e59523 100644 --- a/testsuite/adapters/every_adapter_stateful_sorter.cpp +++ b/testsuite/adapters/every_adapter_stateful_sorter.cpp @@ -25,7 +25,7 @@ namespace stateful_sorter_impl() = default; - constexpr stateful_sorter_impl(int a): + explicit constexpr stateful_sorter_impl(int a): dummy(a) {} diff --git a/testsuite/testing-tools/span.h b/testsuite/testing-tools/span.h index e87b5771..e7dc6df6 100644 --- a/testsuite/testing-tools/span.h +++ b/testsuite/testing-tools/span.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_TESTSUITE_SPAN_H_ @@ -28,7 +28,7 @@ class span public: template - span(Iterable& iterable): + explicit span(Iterable& iterable): _begin(std::begin(iterable)), _end(std::end(iterable)) {} @@ -45,7 +45,7 @@ template auto make_span(Iterable& iterable) -> span { - return { iterable }; + return span(iterable); } #endif // CPPSORT_TESTSUITE_SPAN_H_ diff --git a/testsuite/utility/adapter_storage.cpp b/testsuite/utility/adapter_storage.cpp index 145fbd29..feca1b7d 100644 --- a/testsuite/utility/adapter_storage.cpp +++ b/testsuite/utility/adapter_storage.cpp @@ -25,7 +25,7 @@ namespace { dummy_adapter_impl() = default; - constexpr dummy_adapter_impl(Sorter&& sorter): + explicit constexpr dummy_adapter_impl(Sorter&& sorter): cppsort::utility::adapter_storage(std::move(sorter)) {} @@ -50,7 +50,7 @@ namespace { dummy_adapter() = default; - constexpr dummy_adapter(Sorter sorter): + explicit constexpr dummy_adapter(Sorter sorter): cppsort::sorter_facade>(std::move(sorter)) {} }; From 4c0ca3b4529e2278cde3302562fd390845c616c5 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Thu, 22 Jul 2021 22:13:12 +0200 Subject: [PATCH 55/56] Rename sort_index_pairs* to swap_index_pairs* --- docs/Miscellaneous-utilities.md | 8 ++++---- .../fixed/merge_exchange_network_sorter.h | 2 +- .../fixed/odd_even_merge_network_sorter.h | 2 +- include/cpp-sort/utility/sorting_networks.h | 16 ++++++++-------- testsuite/utility/sorting_networks.cpp | 16 ++++++++-------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/Miscellaneous-utilities.md b/docs/Miscellaneous-utilities.md index 3d6cd6e9..f975f2c9 100644 --- a/docs/Miscellaneous-utilities.md +++ b/docs/Miscellaneous-utilities.md @@ -267,7 +267,7 @@ struct index_pair This pretty rough template acts as the base vocabulary type for comparator networks. *Fixed-size sorters* that happen to be sorting networks should provide an `index_pairs()` static methods returning a sequence of `index_pair` sufficient to sort a collection of a given size. -The following functions accept a sequence of `index_pair` and use it to sort a given random-access collection (represented by an iterator to its first element): +The following functions accept a sequence of `index_pair` and swap the elements of a given random-access collection (represented by an iterator to its first element) according to the index pairs: ```cpp template< @@ -277,7 +277,7 @@ template< typename Compare = std::less<>, typename Projection = utility::identity > -auto sort_index_pairs(RandomAccessIterator first, const std::array, N>& index_pairs, +auto swap_index_pairs(RandomAccessIterator first, const std::array, N>& index_pairs, Compare compare={}, Projection projection={}) -> void; @@ -288,13 +288,13 @@ template< typename Compare = std::less<>, typename Projection = utility::identity > -auto sort_index_pairs_force_unroll(RandomAccessIterator first, +auto swap_index_pairs_force_unroll(RandomAccessIterator first, const std::array, N>& index_pairs, Compare compare={}, Projection projection={}) -> void; ``` -`sort_index_pairs` loops over the index pairs in the simplest fashion and calls the compare-exchange operations in the simplest possible way. `sort_index_pairs_force_unroll` is a best effort function trying to achieve the same job by unrolling the loop over indices the best it can - a perfect unrolling is thus attempted, but never guaranteed, which might or might result in faster runtime and/or increased binary size. +`swap_index_pairs` loops over the index pairs in the simplest fashion and calls the compare-exchange operations in the simplest possible way. `swap_index_pairs_force_unroll` is a best effort function trying to achieve the same job by unrolling the loop over indices the best it can - a perfect unrolling is thus attempted, but never guaranteed, which might or might result in faster runtime and/or increased binary size. *New in version 1.11.0* diff --git a/include/cpp-sort/fixed/merge_exchange_network_sorter.h b/include/cpp-sort/fixed/merge_exchange_network_sorter.h index f39cf0f9..3cbe9333 100644 --- a/include/cpp-sort/fixed/merge_exchange_network_sorter.h +++ b/include/cpp-sort/fixed/merge_exchange_network_sorter.h @@ -110,7 +110,7 @@ namespace cppsort { using difference_type = difference_type_t; auto pairs = index_pairs(); - utility::sort_index_pairs(first, pairs, std::move(compare), std::move(projection)); + utility::swap_index_pairs(first, pairs, std::move(compare), std::move(projection)); } }; diff --git a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h index 27814549..5415aa51 100644 --- a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h +++ b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h @@ -98,7 +98,7 @@ namespace cppsort { using difference_type = difference_type_t; auto pairs = index_pairs(); - utility::sort_index_pairs(first, pairs, std::move(compare), std::move(projection)); + utility::swap_index_pairs(first, pairs, std::move(compare), std::move(projection)); } }; diff --git a/include/cpp-sort/utility/sorting_networks.h b/include/cpp-sort/utility/sorting_networks.h index b5a0caa6..cc79f369 100644 --- a/include/cpp-sort/utility/sorting_networks.h +++ b/include/cpp-sort/utility/sorting_networks.h @@ -31,7 +31,7 @@ namespace utility }; //////////////////////////////////////////////////////////// - // sort_index_pairs + // swap_index_pairs // // Let the compiler decide whether to unroll or not // depending on the number of elements to handle @@ -43,7 +43,7 @@ namespace utility typename Compare = std::less<>, typename Projection = utility::identity > - auto sort_index_pairs(RandomAccessIterator first, const std::array, N>& index_pairs, + auto swap_index_pairs(RandomAccessIterator first, const std::array, N>& index_pairs, Compare compare={}, Projection projection={}) -> void { @@ -54,7 +54,7 @@ namespace utility } //////////////////////////////////////////////////////////// - // sort_index_pairs_force_unroll + // swap_index_pairs_force_unroll // // This is a best effort function to try to force the // compiler to generate unrolled code, but the result is @@ -63,7 +63,7 @@ namespace utility namespace detail { template - struct index_pair_sorter_force_unroll + struct index_pair_swapper_force_unroll { template< typename RandomAccessIterator, @@ -81,7 +81,7 @@ namespace utility cppsort::detail::iter_swap_if(first + index_pairs[CurrentPairIndex].first, first + index_pairs[CurrentPairIndex].second, compare, projection); - index_pair_sorter_force_unroll::do_it(first, index_pairs, + index_pair_swapper_force_unroll::do_it(first, index_pairs, std::move(compare), std::move(projection)); } @@ -110,16 +110,16 @@ namespace utility typename Compare = std::less<>, typename Projection = utility::identity > - auto sort_index_pairs_force_unroll(RandomAccessIterator first, + auto swap_index_pairs_force_unroll(RandomAccessIterator first, const std::array, N>& index_pairs, Compare compare={}, Projection projection={}) -> void { - detail::index_pair_sorter_force_unroll<0>::do_it(first, index_pairs, std::move(compare), std::move(projection)); + detail::index_pair_swapper_force_unroll<0>::do_it(first, index_pairs, std::move(compare), std::move(projection)); } template - auto sort_index_pairs_force_unroll(RandomAccessIterator, const std::array, 0>&, + auto swap_index_pairs_force_unroll(RandomAccessIterator, const std::array, 0>&, Compare, Projection) -> void {} diff --git a/testsuite/utility/sorting_networks.cpp b/testsuite/utility/sorting_networks.cpp index b03f0008..3dae5317 100644 --- a/testsuite/utility/sorting_networks.cpp +++ b/testsuite/utility/sorting_networks.cpp @@ -29,15 +29,15 @@ TEST_CASE( "sorting with index pairs", "[utility][sorting_networks]" ) auto distribution = dist::shuffled{}; distribution(std::back_inserter(vec), 8); - SECTION( "sort_index_pairs" ) + SECTION( "swap_index_pairs" ) { - sort_index_pairs(vec.begin(), pairs); + swap_index_pairs(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } - SECTION( "sort_index_pairs_force_unroll" ) + SECTION( "swap_index_pairs_force_unroll" ) { - sort_index_pairs_force_unroll(vec.begin(), pairs); + swap_index_pairs_force_unroll(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } } @@ -51,15 +51,15 @@ TEST_CASE( "sorting with index pairs from sorting_network_sorter", auto distribution = dist::shuffled{}; distribution(std::back_inserter(vec), 8); - SECTION( "sort_index_pairs" ) + SECTION( "swap_index_pairs" ) { - sort_index_pairs(vec.begin(), pairs); + swap_index_pairs(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } - SECTION( "sort_index_pairs_force_unroll" ) + SECTION( "swap_index_pairs_force_unroll" ) { - sort_index_pairs_force_unroll(vec.begin(), pairs); + swap_index_pairs_force_unroll(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } } From ca9bfcb370ff30f780c22086c649d44137de28f3 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 24 Jul 2021 17:17:12 +0200 Subject: [PATCH 56/56] Preparing release 1.11.0 --- CMakeLists.txt | 2 +- README.md | 4 ++-- conanfile.py | 2 +- docs/Home.md | 2 +- docs/Tooling.md | 4 ++-- include/cpp-sort/version.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b28f653b..8eadcf5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.8.0) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -project(cpp-sort VERSION 1.10.0 LANGUAGES CXX) +project(cpp-sort VERSION 1.11.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(GNUInstallDirs) diff --git a/README.md b/README.md index e48e1c0f..43f0d3a1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Latest Release](https://img.shields.io/badge/release-1.10.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.10.0) -[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.10.0-blue.svg)](https://conan.io/center/cpp-sort?version=1.10.0) +[![Latest Release](https://img.shields.io/badge/release-1.11.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.11.0) +[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.11.0-blue.svg)](https://conan.io/center/cpp-sort?version=1.11.0) [![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/develop/graph/badge.svg)](https://codecov.io/gh/Morwenn/cpp-sort) > *It would be nice if only one or two of the sorting methods would dominate all of the others, diff --git a/conanfile.py b/conanfile.py index dd04ddd1..deb99e82 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ class CppSortConan(ConanFile): name = "cpp-sort" - version = "1.10.0" + version = "1.11.0" description = "Additional sorting algorithms & related tools" topics = "conan", "cpp-sort", "sorting", "algorithms" url = "https://github.com/Morwenn/cpp-sort" diff --git a/docs/Home.md b/docs/Home.md index 21a60f46..a7d33604 100644 --- a/docs/Home.md +++ b/docs/Home.md @@ -1,4 +1,4 @@ -Welcome to the **cpp-sort 1.10.0** documentation! +Welcome to the **cpp-sort 1.11.0** documentation! You probably read the introduction in the README, so I won't repeat it here. This wiki contains documentation about the library: basic documentation about the many sorting tools and how to use them, documentation about the additional utilities provided by the library and even some detailed tutorials if you ever want to write your own sorters or sorter adapters. This main page explains a few general things that didn't quite fit in other parts of the documentation. diff --git a/docs/Tooling.md b/docs/Tooling.md index c7e6cc26..0b7c6c28 100644 --- a/docs/Tooling.md +++ b/docs/Tooling.md @@ -51,10 +51,10 @@ The same options exist without the `CPPSORT_` prefix exist, but are deprecated. conan search cpp-sort --remote=conan-center ``` -And then install any version to your local cache as follows (here with version 1.10.0): +And then install any version to your local cache as follows (here with version 1.11.0): ```sh -conan install cpp-sort/1.10.0 +conan install cpp-sort/1.11.0 ``` The packages downloaded from conan-center are minimal and only contain the files required to use **cpp-sort** as a library: the headers, CMake files and licensing information. If you need anything else you have to build your own package with the `conanfile.py` available in this repository. diff --git a/include/cpp-sort/version.h b/include/cpp-sort/version.h index 7c7035fa..3e9237cc 100644 --- a/include/cpp-sort/version.h +++ b/include/cpp-sort/version.h @@ -8,7 +8,7 @@ // Semantic versioning macros #define CPPSORT_VERSION_MAJOR 1 -#define CPPSORT_VERSION_MINOR 10 +#define CPPSORT_VERSION_MINOR 11 #define CPPSORT_VERSION_PATCH 0 #endif // CPPSORT_VERSION_H_