diff --git a/.forgejo/workflows/deploy-to-wiki.yml b/.forgejo/workflows/deploy-to-wiki.yml index eda21ce6..c8f195dd 100644 --- a/.forgejo/workflows/deploy-to-wiki.yml +++ b/.forgejo/workflows/deploy-to-wiki.yml @@ -19,13 +19,13 @@ jobs: steps: - name: Checkout /docs - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: ${{forgejo.repository}} path: main - name: Checkout wiki - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: repository: ${{forgejo.repository}}.wiki path: wiki diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index d53467d8..c748ed5f 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 Morwenn +# Copyright (c) 2021-2026 Morwenn # SPDX-License-Identifier: MIT name: MacOS Builds @@ -40,7 +40,7 @@ jobs: sanitize: address,undefined steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: rui314/setup-mold@v1 - name: Configure CMake diff --git a/.github/workflows/build-mingw.yml b/.github/workflows/build-mingw.yml index 45b13574..b31aeed5 100644 --- a/.github/workflows/build-mingw.yml +++ b/.github/workflows/build-mingw.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 Morwenn +# Copyright (c) 2021-2026 Morwenn # SPDX-License-Identifier: MIT name: MinGW-w64 Builds @@ -31,7 +31,7 @@ jobs: build_type: [Debug, Release] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Configure CMake shell: pwsh diff --git a/.github/workflows/build-msvc.yml b/.github/workflows/build-msvc.yml index 66186857..a429281e 100644 --- a/.github/workflows/build-msvc.yml +++ b/.github/workflows/build-msvc.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 Morwenn +# Copyright (c) 2021-2026 Morwenn # SPDX-License-Identifier: MIT name: MSVC Builds @@ -36,7 +36,7 @@ jobs: build_tools: '-T ClangCL' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Configure CMake shell: pwsh @@ -44,7 +44,6 @@ jobs: run: | cmake -H${{github.event.repository.name}} -Bbuild ` -DCMAKE_CONFIGURATION_TYPES=${{matrix.config.build_type}} ` - -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ` -G"Visual Studio 17 2022" -A x64 ${{matrix.config.build_tools}} ` -DCPPSORT_BUILD_TESTING=ON ` -DCPPSORT_BUILD_EXAMPLES=ON diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 1c96c444..78fa7b29 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2025 Morwenn +# Copyright (c) 2021-2026 Morwenn # SPDX-License-Identifier: MIT name: Ubuntu Builds @@ -41,7 +41,7 @@ jobs: sanitize: address,undefined steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: rui314/setup-mold@v1 - name: Install GCC diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 673b6941..a2fcc7de 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2025 Morwenn +# Copyright (c) 2020-2026 Morwenn # SPDX-License-Identifier: MIT name: Coverage Upload to Codecov @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@v4 + uses: actions/checkout@v7 - uses: rui314/setup-mold@v1 - name: Install LCOV diff --git a/.github/workflows/deploy-to-wiki.yml b/.github/workflows/deploy-to-wiki.yml index f2b1b964..faae526f 100644 --- a/.github/workflows/deploy-to-wiki.yml +++ b/.github/workflows/deploy-to-wiki.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2025 Morwenn +# Copyright (c) 2020-2026 Morwenn # SPDX-License-Identifier: MIT name: Wiki Deployment @@ -19,13 +19,13 @@ jobs: steps: - name: Checkout /docs - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: repository: ${{github.repository}} path: main - name: Checkout wiki - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: repository: ${{github.repository}}.wiki path: wiki diff --git a/.github/workflows/mirror-to-codeberg.yml b/.github/workflows/mirror-to-codeberg.yml index d13a05b6..416b1c49 100644 --- a/.github/workflows/mirror-to-codeberg.yml +++ b/.github/workflows/mirror-to-codeberg.yml @@ -1,4 +1,4 @@ -# Copyright (c) 2025 Morwenn +# Copyright (c) 2025-2026 Morwenn # SPDX-License-Identifier: MIT name: Mirror Commits to Codeberg @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v7 with: fetch-depth: 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index a760d814..4b51af75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.11.0) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -project(cpp-sort VERSION 2.1.0 LANGUAGES CXX) +project(cpp-sort VERSION 2.2.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(cpp-sort-utils) diff --git a/README.md b/README.md index 55617ae9..8ed95ace 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![cpp-sort logo](docs/images/cpp-sort-logo.svg) -[![Latest Release](https://img.shields.io/badge/release-2.1.0-blue.svg)](https://codeberg.org/Morwenn/cpp-sort/releases/tag/v2.1.0) -[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F2.1.0-blue.svg)](https://conan.io/center/recipes/cpp-sort?version=2.1.0) +[![Latest Release](https://img.shields.io/badge/release-2.2.0-blue.svg)](https://codeberg.org/Morwenn/cpp-sort/releases/tag/v2.2.0) +[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F2.2.0-blue.svg)](https://conan.io/center/recipes/cpp-sort?version=2.2.0) [![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/2.x.y-develop/graph/badge.svg)](https://codecov.io/gh/Morwenn/cpp-sort) [![Pitchfork Layout](https://img.shields.io/badge/standard-PFL-orange.svg)](https://github.com/vector-of-bool/pitchfork) diff --git a/conanfile.py b/conanfile.py index 37f81c03..e9a2bbe3 100644 --- a/conanfile.py +++ b/conanfile.py @@ -16,7 +16,7 @@ class CppSortConan(ConanFile): name = "cpp-sort" - version = "2.1.0" + version = "2.2.0" description = "Sorting algorithms & related tools" license = "MIT" url = "https://codeberg.org/Morwenn/cpp-sort" diff --git a/docs/Benchmarks.md b/docs/Benchmarks.md index 1ca87fa7..a8ea19e1 100644 --- a/docs/Benchmarks.md +++ b/docs/Benchmarks.md @@ -1,7 +1,7 @@ *Note: this page only benchmarks sorting algorithms under specific conditions. It can be used as a quick guide but if you really need a fast algorithm for a specific use case, you better run your own benchmarks.* *Last meaningful updates:* -* *2.1.0 for measures of disorder* +* *2.2.0 for measures of disorder* * *1.16.0 for slow O(n log n) sorts* * *1.14.0 for small array sorts* * *1.13.1 for unstable random-access sorts, forward sorts, and the expensive move/cheap comparison benchmark* @@ -14,7 +14,7 @@ It is worth noting that most benchmarks on this page use collections of `double` All of the graphs on this page have been generated with slightly modified versions of the scripts found in the project's benchmarks folder. There are just too many things to check; if you ever want a specific benchmark, don't hesitate to ask for it. *The benchmarks were run on:* -* *For version 2.0.0 and newer: EndeavourOS with g++ 15.2, with the flags -O3 -march=native -std=c++26.* +* *For version 2.0.0 and newer: EndeavourOS with g++ 16.2, with the flags -O3 -march=native -std=c++26.* * *For older versions: Windows 10 with 64-bit MinGW-w64 g++ 12.0, with the flags -O3 -march=native -std=c++20.* # Random-access collections @@ -176,10 +176,10 @@ We can see several trends in these benchmarks, rather consistant across `int` an This benchmark for [measures of disorder][Measures-of-disorder] is small and only intends to show the cost that these tools might incur. It is not meant to be exhaustive in any way. -![Benchmark speed of measures of disorder for increasing size for std::vector<int>](https://i.imgur.com/uGoVIcE.png) +![Benchmark speed of measures of disorder for increasing size for std::vector<int>](https://i.imgur.com/xOJIhm1.png) It makes rather easy to see the different groups of complexities: -* *Amp(X)*, *Runs(X)* and *Mono(X)* are obvious O(n) algorithms. +* *Amp(X)*, *Reve(X)*, *Runs(X)* and *Mono(X)* are obvious O(n) algorithms. * *Dis(X)* is a more involved O(n) algorithm. * All of the other measures of disorder run in O(n log n) time. diff --git a/docs/Changelog.md b/docs/Changelog.md index f52a0847..53b342bd 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,11 @@ While **cpp-sort** theoretically requires a fully C++17-compliant compiler, a fe When compiled with C++20, **cpp-sort** might gain a few additional features depending on the level of C++20 support provided by the compiler. The availability of those features depends on the presence of corresponding [feature-testing macros][feature-test-macros] when possible, even though some checks are more granular. Don't hesitate to open an issue if your compiler and standard library supports one of those features but it doesn't seem to work in **cpp-sort**. +**Correctness improvements:** +* When possible, the total order predicates [`total_less` and `total_greater`][total-greater-less] use [`std::strong_order`][std-strong-order] to compare floating-point numbers, leading to a supposedly correct implementation of the IEEE *totalOrder* operator. In C++17 and earlier, those comparators don't actually implement a proper total order: they are unable to differentiate between quiet and signaling NaNs, and connot read NaN payloads. + + The feature-test macro `__cpp_lib_three_way_comparison` can be used to check whether `std::strong_order` is available. + **New features:** * When available, [`std::identity`][std-identity] benefits from dedicated support wherever [`utility::identity`][cpp-sort-function-objects] is supported, with equivalent semantics. @@ -44,12 +49,13 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe [feature-test-macros]: https://wg21.link/SD6 [pdq-sorter]: Sorters.md#pdq_sorter [ska-sorter]: Sorters.md#ska_sorter - [sorter-facade]: Sorter-facade.md [std-greater-void]: https://en.cppreference.com/w/cpp/utility/functional/greater_void [std-identity]: https://en.cppreference.com/w/cpp/utility/functional/identity [std-less-void]: https://en.cppreference.com/w/cpp/utility/functional/less_void [std-mem-fn]: https://en.cppreference.com/w/cpp/utility/functional/mem_fn [std-ranges-greater]: https://en.cppreference.com/w/cpp/utility/functional/ranges/greater [std-ranges-less]: https://en.cppreference.com/w/cpp/utility/functional/ranges/less + [std-strong-order]: https://en.cppreference.com/w/cpp/utility/compare/strong_order [std-swap]: https://en.cppreference.com/w/cpp/algorithm/swap + [total-greater-less]: Comparators.md#total-order-comparators [utility-iter-move]: Miscellaneous-utilities.md#iter_move-and-iter_swap diff --git a/docs/Comparators.md b/docs/Comparators.md index 0fb46650..9fb590bf 100644 --- a/docs/Comparators.md +++ b/docs/Comparators.md @@ -9,7 +9,7 @@ Every non-refined comparator described below is also a [transparent comparator][ #include ``` -The comparators `total_less` and `total_order` are [customization points][custom-point] implementing a [total order][total-order], inspired by the similar functions described in [P0100][P0100]. The provided functions handle built-in integer out of the box (using the built-in relational operators) and implement IEEE 754's `totalOrder` for floating point numbers (from lesser to greater): +The comparators `total_less` and `total_order` are [customization points][custom-point] implementing a [total order][total-order], inspired by the similar functions described in [P0100][P0100]. The provided functions handle built-in integer types out of the box (using the built-in relational operators) and attempt to implement IEEE 754's `totalOrder` for floating point numbers (from lesser to greater): * positive quiet NaNs * positive signaling NaNs * positive infinity @@ -21,10 +21,14 @@ The comparators `total_less` and `total_order` are [customization points][custom * negative signaling NaNs * negative quiet NaNs -That said, the comparators are currently unable to discriminate between quiet and signaling NaNs, so they are considered to be *equivalent*. When it doesn't handle a type natively and ADL doesn't find any suitable `total_less` function in a class namespace, `cppsort::total_less` does *not* fall back to `operator<`; see [P0100][P0100] for the rationale (it applies to the whole `total_*` family of customization points). +That said, the comparators are unable to discriminate between quiet and signaling NaNs, and have no way to inspect the bit patterns of NaNs to compare them. All NaNs that share a same sign are therefore considered to be *equivalent* in C++17. In C++20 however, the comparators rely on [`std::strong_order`][std-strong-order] when available, which correctly implements `totalOrder` for IEEE 754 floating point numbers. + +When it doesn't handle a type natively and ADL doesn't find any suitable `total_less` function in a class namespace, `cppsort::total_less` does *not* fall back to `operator<`; see [P0100][P0100] for the rationale (it applies to the whole `total_*` family of customization points). Total order comparators are considered as [generating branchless code][branchless-traits] when comparing instances of a type that satisfies [`std::is_integral`][std-is-integral]. +*Changed in version 2.1.0:* `total_less` and `total_greater` now correctly implement IEEE 754 `totalOrder` in C++20 mode, when the featur-test macro `__cpp_lib_three_way_comparison` is defined. + ### Weak order comparators ```cpp @@ -99,7 +103,6 @@ The two-parameter version of the customization point calls the three-parameter o [binary-predicate]: https://en.cppreference.com/w/cpp/named_req/BinaryPredicate [branchless-traits]: Miscellaneous-utilities.md#branchless-traits - [callable]: https://en.cppreference.com/w/cpp/named_req/Callable [case-sensitivity]: https://en.wikipedia.org/wiki/Case_sensitivity [cppcon2015-compare]: https://github.com/CppCon/CppCon2015/tree/master/Presentations/Comparison%20is%20not%20simple%2C%20but%20it%20can%20be%20simpler [custom-point]: https://ericniebler.com/2014/10/21/customization-point-design-in-c11-and-beyond/ diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index e4570f8b..a2f3e424 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -149,7 +149,7 @@ Size | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: **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** -**CEs** | 71 | 77 | 85 | 91 | 99 | 106 | 114 | 120 | 130 | 139 | 147 | 155 | 164 | 172 | 180 | 185 +**CEs** | 71 | 77 | 85 | 91 | 99 | 106 | 114 | 120 | 130 | 138 | 147 | 155 | 164 | 172 | 180 | 185 **Size** | **33** | **34** | **35** | **36** | **37** | **38** | **39** | **40** | **41** | **42** | **43** | **44** | **45** | **46** | **47** | **48** **CEs** | 199 | 209 | 220 | 227 | 240 | 250 | 259 | 265 | 282 | 291 | 303 | 309 | 324 | 332 | 340 | 346 **Size** | **49** | **50** | **51** | **52** | **53** | **54** | **55** | **56** | **57** | **58** | **59** | **60** | **61** | **62** | **63** | **64** diff --git a/docs/Home.md b/docs/Home.md index 68437350..b8694446 100644 --- a/docs/Home.md +++ b/docs/Home.md @@ -1,6 +1,6 @@ ![cpp-sort logo](images/cpp-sort-logo.svg) -Welcome to the **cpp-sort 2.1.0** documentation! +Welcome to the **cpp-sort 2.2.0** documentation! 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, as well as a few tutorials about writing 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/Measures-of-disorder.md b/docs/Measures-of-disorder.md index 0398a0da..58bbb8ad 100644 --- a/docs/Measures-of-disorder.md +++ b/docs/Measures-of-disorder.md @@ -164,10 +164,12 @@ Where $N_{\mathit{eq}}(X)$ is the number of pairs of neighbors that compare equi | ----------- | ----------- | ------------- | --------- | | n | 1 | Forward | No | -`max_for_size`: $\lvert X \rvert - 2$ when the sign of $comp$ changes for every pair of neighbors. +`max_for_size`: $\lvert X \rvert - 2$ for [zigzag permutations][zigzag-permutation]. **Note:** *Amp* does not respect Mannila's criterion 4: $\mathit{Amp}(\langle 1, 2, 3 \rangle) = 0$ and $\mathit{Amp}(\langle 6, 5, 4 \rangle) = 0$, but $\mathit{Amp}(\langle 1, 2, 3, 6, 5, 4 \rangle) = 4$. +*New in version 2.1.0* + ### *Block* ```cpp @@ -236,7 +238,7 @@ $$ | ----------- | ----------- | ------------- | --------- | | n log n | n | Forward | No | -`max_for_size`: $\frac{\lvert X \rvert}{2}$ when all values extracted from $X$ are within the bounds of already extracted encroaching lists (for example the sequence $\langle 10, 0, 9, 1, 8, 2, 7, 3, 6, 4, 5 \rangle$ triggers the worst case). +`max_for_size`: $\lfloor \frac{\lvert X \rvert}{2} \rfloor$ when all values extracted from $X$ are within the bounds of already extracted encroaching lists (for example the sequence $\langle 10, 0, 9, 1, 8, 2, 7, 3, 6, 4, 5 \rangle$ triggers the worst case). ### *Exc* @@ -290,7 +292,7 @@ Computes the number of inversions in $X$, where an inversion corresponds to a pa | ----------- | ----------- | ------------- | --------- | | n log n | n | Forward | Yes | -`max_for_size`: $\frac{\lvert X \rvert(\lvert X \rvert - 1)}{2}$ when $X$ is sorted in reverse order. +`max_for_size`: $\lfloor \frac{\lvert X \rvert(\lvert X \rvert - 1)}{2} \rfloor$ when $X$ is sorted in reverse order. ### *Max* @@ -318,11 +320,13 @@ The measure of disorder is slightly different from its original description in [ * It subtracts 1 from the number of runs, thus returning 0 when $X$ is sorted. * It explicitly handles non-increasing and non-decreasing runs, not only the strictly increasing or decreasing ones. +![Illustration of Mono showing how a sequence of integers can be split into monotonic runs](images/measure-of-disorder-mono.png) + | Complexity | Memory | Iterators | Monotonic | | ----------- | ----------- | ------------- | --------- | | n | 1 | Forward | No | -`max_for_size`: $\frac{\lvert X \rvert + 1}{2} - 1$ when $X$ is a sequence of elements that are alternatively greater then lesser than their previous neighbour. +`max_for_size`: $\lfloor \frac{\lvert X \rvert + 1}{2} \rfloor - 1$ for [zigzag permutations][zigzag-permutation] (but not only those). **Note:** `probe::mono` does not respect Mannila's criterion 4: $\mathit{Mono}(\langle 1, 2, 3, 4, 5 \rangle) = 0$ and $\mathit{Mono}(\langle 10, 9, 8, 7, 6 \rangle) = 0$, but $\mathit{Mono}(\langle 1, 2, 3, 4, 5, 10, 9, 8, 7, 6 \rangle) = 1$. @@ -366,6 +370,26 @@ Computes the minimum number of elements that must be removed from $X$ to obtain `max_for_size`: $\lvert X \rvert - 1$ when $X$ is sorted in reverse order. +### *Reve* + +```cpp +#include +``` + +The number of reversals in the growth direction of a sequence. + +![Illustration showing a line diagram of a sequence of integer numbers, highlighting the inflection points in the growth direction](images/measure-of-disorder-reve.png) + +| Complexity | Memory | Iterators | Monotonic | +| ----------- | ----------- | ------------- | --------- | +| n | 1 | Forward | No | + +`max_for_size`: $\lvert X \rvert - 2$ for [zigzag permutations][zigzag-permutation]. + +**Note:** `probe::reve` does not respect Mannila's criterion 4: $\mathit{Reve}(\langle 1, 2, 3, 4, 5 \rangle) = 0$ and $\mathit{Reve}(\langle 10, 9, 8, 7, 6 \rangle) = 0$, but $\mathit{Reve}(\langle 1, 2, 3, 4, 5, 10, 9, 8, 7, 6 \rangle) = 1$. + +*New in version 2.2.0* + ### *Runs* ```cpp @@ -392,9 +416,11 @@ Spearman's footrule distance: sum of distances between the position of individua | ----------- | ----------- | ------------- | --------- | | n log n | n | Forward | Yes | -`max_for_size`: $\frac{\lvert X \rvert²}{2}$ when $X$ is sorted in reverse order. +`max_for_size`: $\lfloor \frac{\lvert X \rvert^2}{2} \rfloor$ when $X$ is sorted in reverse order. + +**Note:** *Spear* does not respect Mannila's criterion 5: $\mathit{Spear}(\langle 4, 1, 2, 3 \rangle) \not \le \lvert \langle 4 \rangle \rvert + \mathit{Spear}(\langle 1, 2, 3 \rangle)$. -**Note:** *Spear* does not respect Mannila's criterion 5: $\mathit{Spear}(\langle 4, 1, 2, 3 \rangle) \not \le \lvert \langle 1, 2, 3 \rangle \rvert + \mathit{Spear}(\langle 1, 2, 3 \rangle)$. +**Note²:** $\lfloor \frac{\mathit{Spear}(X)}{2} \rfloor$ respects Mannila's criterion 5, and is a proper measure of presortedness. A future version of **cpp-sort** might replace the current implementation with one that halves its result. ### *SUS* @@ -473,3 +499,4 @@ Nevertheless we do know a few of the measure's properties: [probe-sms]: Measures-of-disorder.md#sms [probe-sus]: Measures-of-disorder.md#sus [sort-race]: https://arxiv.org/ftp/arxiv/papers/1609/1609.04471.pdf + [zigzag-permutation]: https://en.wikipedia.org/wiki/Alternating_permutation diff --git a/docs/Miscellaneous-utilities.md b/docs/Miscellaneous-utilities.md index 0b82ba7a..dd697bba 100644 --- a/docs/Miscellaneous-utilities.md +++ b/docs/Miscellaneous-utilities.md @@ -549,7 +549,6 @@ assert(not cppsort::utility::check_strict_weak_ordering(vec, std::less{});) [std-greater]: https://en.cppreference.com/w/cpp/utility/functional/greater [std-greater-void]: https://en.cppreference.com/w/cpp/utility/functional/greater_void [std-identity]: https://en.cppreference.com/w/cpp/utility/functional/identity - [std-integer-sequence]: https://en.cppreference.com/w/cpp/utility/integer_sequence [std-integral-constant]: https://en.cppreference.com/w/cpp/types/integral_constant [std-invoke]: https://en.cppreference.com/w/cpp/utility/functional/invoke [std-is-arithmetic]: https://en.cppreference.com/w/cpp/types/is_arithmetic diff --git a/docs/Original-research.md b/docs/Original-research.md index 8416583d..285e7461 100644 --- a/docs/Original-research.md +++ b/docs/Original-research.md @@ -236,10 +236,9 @@ The following relations have yet to be analyzed: - $\mathit{Osc} \preceq \mathit{Mono}$ - $\mathit{Loc} \preceq \mathit{Mono}$ -Another interesting property of $\mathit{Mono}$ is that it returns the same amount of disorder for a sequence $X$ and for a sequence corresponding to $X$ with the order of all elements reversed, a property that is only shared with $\mathit{Osc}$ in the library. +Another interesting property of $\mathit{Mono}$ is that it returns the same amount of disorder for a sequence $X$ and for a sequence corresponding to $X$ with the order of all elements reversed, a property that is shared with $\mathit{Amp}$, $\mathit{Osc}$ and $\mathit{Reve}$ in the library. - [better-sorting-networks]: https://etd.ohiolink.edu/!etd.send_file?accession=kent1239814529 [cycle-sort]: https://en.wikipedia.org/wiki/Cycle_sort [divide-sort-merge-strategy]: https://apps.dtic.mil/sti/tr/pdf/AD0737270.pdf [exact-sort]: https://www.geocities.ws/p356spt/ @@ -248,7 +247,7 @@ Another interesting property of $\mathit{Mono}$ is that it returns the same amou [mountain_sort]: https://github.com/Morwenn/mountain-sort [poplar-heap]: https://github.com/Morwenn/poplar-heap [post-order-heap]: https://people.csail.mit.edu/nickh/Publications/PostOrderHeap/FUN04-PostOrderHeap.pdf - [probe-mono]: Measures-of-disorder.md + [probe-mono]: Measures-of-disorder.md#mono [quick-merge-sort]: https://github.com/Morwenn/quick_merge_sort [quick-merge-sort-arxiv]: https://arxiv.org/pdf/1804.10062.pdf [sort-race]: https://arxiv.org/ftp/arxiv/papers/1609/1609.04471.pdf diff --git a/docs/Quickstart.md b/docs/Quickstart.md index d4420377..8cce1ef2 100644 --- a/docs/Quickstart.md +++ b/docs/Quickstart.md @@ -201,7 +201,7 @@ The specific restrictions are all documented in the adapters descriptions. ## Two-step sorting Sometimes the information is not represented as simple collection of class instances, but as [parallel arrays][parallel-arrays] (also known as structure of arrays). To sort those, **cpp-sort** provides components for two-step sorting of random-access collections: -1. Extract the sorted indices of a collection with [`utility::sorted_indices`][utility-sorter-indices] (similar to [`numpy.argsort`][numpy-argsort]). +1. Extract the sorted indices of a collection with [`utility::sorted_indices`][utility-sorted-indices] (similar to [`numpy.argsort`][numpy-argsort]). 2. Use the sorted indices to permute the arrays with [`utility::apply_permutation`][utility-apply-permutation]. ```cpp @@ -259,7 +259,6 @@ The previous sections describe some of the main tools provided by **cpp-sort** b [ska-sorter]: Sorters.md#ska_sorter [sorter-adapters]: Sorter-adapters.md [sorter-facade]: Sorter-facade.md - [sorter-traits]: Sorter-traits.md [sorters]: Sorters.md [std-searchers]: https://en.cppreference.com/w/cpp/utility/functional#Searchers [std-sort]: https://en.cppreference.com/w/cpp/algorithm/sort diff --git a/docs/Sorter-adapters.md b/docs/Sorter-adapters.md index 2ab17ab7..5d163d8e 100644 --- a/docs/Sorter-adapters.md +++ b/docs/Sorter-adapters.md @@ -322,7 +322,6 @@ When wrapped into [`stable_adapter`][stable-adapter], it has a slightly differen [This page][vergesort-fallbacks] contains benchmarks of vergesort on top of several sorting algorithms, showing that it can be valuable tool to add on top of most sorting algorithms. - [branchless-traits]: Miscellaneous-utilities.md#branchless-traits [ctad]: https://en.cppreference.com/w/cpp/language/class_template_argument_deduction [cycle-sort]: https://en.wikipedia.org/wiki/Cycle_sort [drop-merge-sort]: https://github.com/emilk/drop-merge-sort @@ -335,7 +334,6 @@ When wrapped into [`stable_adapter`][stable-adapter], it has a slightly differen [iterator-category]: Sorter-traits.md#iterator_category [iterator-tags]: https://en.cppreference.com/w/cpp/iterator/iterator_tags [low-moves-sorter]: Fixed-size-sorters.md#low_moves_sorter - [metrics-comparisons]: Metrics.md#comparisons [mountain-sort]: https://github.com/Morwenn/mountain-sort [probe-mono]: Measures-of-disorder.md#mono [probe-rem]: Measures-of-disorder.md#rem diff --git a/docs/Sorter-facade.md b/docs/Sorter-facade.md index 77b1e7f4..535f1d3f 100644 --- a/docs/Sorter-facade.md +++ b/docs/Sorter-facade.md @@ -182,8 +182,6 @@ While it does not appear in this documentation, `sorter_facade` actually relies [issue-185]: https://github.com/Morwenn/cpp-sort/issues/185 [selection-sort]: https://en.wikipedia.org/wiki/Selection_sort - [std-begin]: https://en.cppreference.com/w/cpp/iterator/begin - [std-end]: https://en.cppreference.com/w/cpp/iterator/end [std-identity]: https://en.cppreference.com/w/cpp/utility/functional/identity [std-less-void]: https://en.cppreference.com/w/cpp/utility/functional/less_void [std-ranges-less]: https://en.cppreference.com/w/cpp/utility/functional/ranges/less diff --git a/docs/Sorters.md b/docs/Sorters.md index ca82711c..207954f4 100644 --- a/docs/Sorters.md +++ b/docs/Sorters.md @@ -285,7 +285,7 @@ Implements a [smoothsort][smoothsort]. | ----------- | ----------- | ----------- | ----------- | ----------- | ------------- | | n | n log n | n log n | 1 | No | Random-access | -While the complexity guarantees of this algorithm are optimal, this smoothsort isn't actually *fast* in practice. Except for some specific patterns (where `tim_sorter` or `pdq_sorter` are still faster anyway), it is always almost twice as slow as `heap_sorter`. Huge collections and/or huge objects may make a difference, but I have yet to see a case where this is a useful sorting algorithm. +While the complexity guarantees of this algorithm are optimal, this smoothsort isn't actually *fast* in practice. Except for some specific patterns (where [`tim_sorter`][tim-sorter] or [`pdq_sorter`][pdq-sorter] are still faster anyway), it is always almost twice as slow as `heap_sorter`. Huge collections and/or huge objects may make a difference, but I have yet to see a case where this is a useful sorting algorithm. ### `spin_sorter` @@ -452,7 +452,6 @@ struct spread_sorter: [adaptive-quickselect]: https://arxiv.org/abs/1606.00484 [adaptive-shivers-sort]: https://arxiv.org/abs/1809.08411 - [bitmap-allocator]: https://gcc.gnu.org/onlinedocs/libstdc++/manual/bitmap_allocator.html [block-sort]: https://en.wikipedia.org/wiki/Block_sort [bottom-up-heapsort]: https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort [branchless-traits]: Miscellaneous-utilities.md#branchless-traits @@ -487,8 +486,8 @@ struct spread_sorter: [std-sort]: https://en.cppreference.com/w/cpp/algorithm/sort [std-stable-sort]: https://en.cppreference.com/w/cpp/algorithm/stable_sort [std-vector-bool]: https://en.cppreference.com/w/cpp/container/vector_bool + [tim-sorter]: Sorters.md#tim_sorter [timsort]: https://en.wikipedia.org/wiki/Timsort [wiki-sort]: https://github.com/BonzaiThePenguin/WikiSort - [wiki-sorter]: Sorters.md#wiki_sorter [writing-a-sorter]: Writing-a-sorter.md [writing-a-bubble-sorter]: Writing-a-bubble_sorter.md diff --git a/docs/Tooling.md b/docs/Tooling.md index a56e4ed6..1a2bd8af 100644 --- a/docs/Tooling.md +++ b/docs/Tooling.md @@ -45,10 +45,10 @@ Note: when `CPPSORT_ENABLE_AUDITS` is `ON`, assertions in the library are enable conan search cpp-sort --remote=conancenter ``` -And then install any version to your local cache as follows (here with version 2.1.0): +And then install any version to your local cache as follows (here with version 2.2.0): ```sh -conan install --requires=cpp-sort/2.1.0 +conan install --requires=cpp-sort/2.2.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 create your own package with the `conanfile.py` available in this repository. diff --git a/docs/Writing-a-bubble_sorter.md b/docs/Writing-a-bubble_sorter.md index 2ba74f75..d9b96aa2 100644 --- a/docs/Writing-a-bubble_sorter.md +++ b/docs/Writing-a-bubble_sorter.md @@ -421,7 +421,6 @@ That's it: we have covered pretty much every interesting aspect of writing a sim [as-function]: Miscellaneous-utilities.md#as_function [bubble-sort]: https://en.wikipedia.org/wiki/Bubble_sort - [bubble-sorter]: https://en.wikipedia.org/wiki/Bubble_sort [heap-sorter]: Sorters.md#heap_sorter [hybrid-adapter]: Sorter-adapters.md#hybrid_adapter [is-projection]: Sorter-traits.md#is_projection-and-is_projection_iterator diff --git a/docs/Writing-a-randomizing_adapter.md b/docs/Writing-a-randomizing_adapter.md index a29c274a..6d730492 100644 --- a/docs/Writing-a-randomizing_adapter.md +++ b/docs/Writing-a-randomizing_adapter.md @@ -143,12 +143,9 @@ The full implementation can be found in the `examples` folder. [golden-tests]: https://en.wikipedia.org/wiki/Characterization_test [hyrums-law]: https://www.hyrumslaw.com/ [iterator-category]: https://en.cppreference.com/w/cpp/iterator - [proxy-iterators]: https://wg21.link/P0022 [quick-sorter]: Sorters.md#quick_sorter [sfinae]: https://en.cppreference.com/w/cpp/language/sfinae [sorter-adapters]: Sorter-adapters.md [sorter-facade]: Sorter-facade.md - [sorter-traits]: Sorter-traits.md#sorter_traits [std-sort]: https://en.cppreference.com/w/cpp/algorithm/sort.html - [writing-a-sorter]: Writing-a-sorter.md [writing-a-bubble-sorter]: Writing-a-bubble_sorter.md diff --git a/docs/Writing-a-sorter.md b/docs/Writing-a-sorter.md index 64c3a154..523f8fca 100644 --- a/docs/Writing-a-sorter.md +++ b/docs/Writing-a-sorter.md @@ -593,7 +593,6 @@ In the example above, the resulting sorter will use our `low_projections_sorter` [std-ranges-less]: https://en.cppreference.com/w/cpp/utility/functional/ranges/less [std-sort]: https://en.cppreference.com/w/cpp/algorithm/sort [stlab]: https://stlab.adobe.com/ - [utility-as-function]: Miscellaneous-utilities.md#as_function [utility-function-objects]: Miscellaneous-utilities.md#miscellaneous-function-objects [utility-identity]: Miscellaneous-utilities.md#miscellaneous-function-objects [wiki-sorter]: Sorters.md#wiki_sorter diff --git a/docs/images/measure-of-disorder-mono.png b/docs/images/measure-of-disorder-mono.png new file mode 100644 index 00000000..bda61f16 Binary files /dev/null and b/docs/images/measure-of-disorder-mono.png differ diff --git a/docs/images/measure-of-disorder-reve.png b/docs/images/measure-of-disorder-reve.png new file mode 100644 index 00000000..f096029e Binary files /dev/null and b/docs/images/measure-of-disorder-reve.png differ diff --git a/docs/images/partial-ordering-measures-of-disorder.png b/docs/images/partial-ordering-measures-of-disorder.png index cebc34f6..c52aff84 100644 Binary files a/docs/images/partial-ordering-measures-of-disorder.png and b/docs/images/partial-ordering-measures-of-disorder.png differ diff --git a/include/cpp-sort/comparators/partial_greater.h b/include/cpp-sort/comparators/partial_greater.h index 8fea4f5f..a491400d 100644 --- a/include/cpp-sort/comparators/partial_greater.h +++ b/include/cpp-sort/comparators/partial_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_PARTIAL_GREATER_H_ @@ -32,9 +32,12 @@ namespace cppsort // Generic overload: a weak order is also a partial order template - auto partial_greater(const T& lhs, const T& rhs) + constexpr auto partial_greater(const T& lhs, const T& rhs) noexcept(noexcept(cppsort::weak_greater(lhs, rhs))) - -> decltype(cppsort::weak_greater(lhs, rhs)) + -> detail::enable_if_t< + not std::is_floating_point_v, + decltype(cppsort::weak_greater(lhs, rhs)) + > { return cppsort::weak_greater(lhs, rhs); } diff --git a/include/cpp-sort/comparators/partial_less.h b/include/cpp-sort/comparators/partial_less.h index a076e15c..35cd7d8c 100644 --- a/include/cpp-sort/comparators/partial_less.h +++ b/include/cpp-sort/comparators/partial_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_PARTIAL_LESS_H_ @@ -32,9 +32,12 @@ namespace cppsort // Generic overload: a weak order is also a partial order template - auto partial_less(const T& lhs, const T& rhs) + constexpr auto partial_less(const T& lhs, const T& rhs) noexcept(noexcept(cppsort::weak_less(lhs, rhs))) - -> decltype(cppsort::weak_less(lhs, rhs)) + -> detail::enable_if_t< + not std::is_floating_point_v, + decltype(cppsort::weak_less(lhs, rhs)) + > { return cppsort::weak_less(lhs, rhs); } diff --git a/include/cpp-sort/comparators/total_greater.h b/include/cpp-sort/comparators/total_greater.h index 83a0e86c..bf0a91f9 100644 --- a/include/cpp-sort/comparators/total_greater.h +++ b/include/cpp-sort/comparators/total_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_TOTAL_GREATER_H_ @@ -8,13 +8,18 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include #include #include #include -#include "../detail/floating_point_weight.h" #include "../detail/type_traits.h" +#if defined(__cpp_lib_three_way_comparison) +# include +#else +# include +# include "../detail/floating_point_weight.h" +#endif + namespace cppsort { namespace detail @@ -36,6 +41,11 @@ namespace cppsort auto total_greater(T lhs, T rhs) -> detail::enable_if_t, bool> { +#if defined(__cpp_lib_three_way_comparison) + // Full implementation of totalOrder + return std::is_gt(std::strong_order(lhs, rhs)); +#else + // Approximation, unable to differentiate NaNs if (std::isfinite(lhs) && std::isfinite(rhs)) { if (lhs == 0 && rhs == 0) { return std::signbit(rhs) && not std::signbit(lhs); @@ -46,6 +56,7 @@ namespace cppsort int lhs_weight = total_weight(lhs); int rhs_weight = total_weight(rhs); return lhs_weight > rhs_weight; +#endif } //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/comparators/total_less.h b/include/cpp-sort/comparators/total_less.h index 7bbfdd0c..9d7d52b1 100644 --- a/include/cpp-sort/comparators/total_less.h +++ b/include/cpp-sort/comparators/total_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_TOTAL_LESS_H_ @@ -11,10 +11,17 @@ #include #include #include +#include #include -#include "../detail/floating_point_weight.h" #include "../detail/type_traits.h" +#if defined(__cpp_lib_three_way_comparison) +# include +#else +# include +# include "../detail/floating_point_weight.h" +#endif + namespace cppsort { namespace detail @@ -36,6 +43,11 @@ namespace cppsort auto total_less(T lhs, T rhs) -> detail::enable_if_t, bool> { +#if defined(__cpp_lib_three_way_comparison) + // Full implementation of totalOrder + return std::is_lt(std::strong_order(lhs, rhs)); +#else + // Approximation, unable to differentiate NaNs if (std::isfinite(lhs) && std::isfinite(rhs)) { if (lhs == 0 && rhs == 0) { return std::signbit(lhs) && not std::signbit(rhs); @@ -46,6 +58,7 @@ namespace cppsort int lhs_weight = total_weight(lhs); int rhs_weight = total_weight(rhs); return lhs_weight < rhs_weight; +#endif } //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/comparators/weak_greater.h b/include/cpp-sort/comparators/weak_greater.h index 7c464ff4..528680e9 100644 --- a/include/cpp-sort/comparators/weak_greater.h +++ b/include/cpp-sort/comparators/weak_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_WEAK_GREATER_H_ @@ -40,9 +40,12 @@ namespace cppsort // Generic overload: a total order is also a weak order template - auto weak_greater(const T& lhs, const T& rhs) + constexpr auto weak_greater(const T& lhs, const T& rhs) noexcept(noexcept(cppsort::total_greater(lhs, rhs))) - -> decltype(cppsort::total_greater(lhs, rhs)) + -> detail::enable_if_t< + not std::is_floating_point_v, + decltype(cppsort::total_greater(lhs, rhs)) + > { return cppsort::total_greater(lhs, rhs); } diff --git a/include/cpp-sort/comparators/weak_less.h b/include/cpp-sort/comparators/weak_less.h index 4a372bb8..66fa7474 100644 --- a/include/cpp-sort/comparators/weak_less.h +++ b/include/cpp-sort/comparators/weak_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_WEAK_LESS_H_ @@ -40,9 +40,12 @@ namespace cppsort // Generic overload: a total order is also a weak order template - auto weak_less(const T& lhs, const T& rhs) + constexpr auto weak_less(const T& lhs, const T& rhs) noexcept(noexcept(cppsort::total_less(lhs, rhs))) - -> decltype(cppsort::total_less(lhs, rhs)) + -> detail::enable_if_t< + not std::is_floating_point_v, + decltype(cppsort::total_less(lhs, rhs)) + > { return cppsort::total_less(lhs, rhs); } diff --git a/include/cpp-sort/detail/sorting_network/sort26.h b/include/cpp-sort/detail/sorting_network/sort26.h index d19f8e80..2dd5845d 100644 --- a/include/cpp-sort/detail/sorting_network/sort26.h +++ b/include/cpp-sort/detail/sorting_network/sort26.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2025 Morwenn + * Copyright (c) 2015-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT26_H_ @@ -24,132 +24,131 @@ namespace cppsort::detail { iter_swap_if(first, first + 1, compare, projection); iter_swap_if(first + 2, first + 3, compare, projection); - iter_swap_if(first + 4, first + 21, compare, projection); - iter_swap_if(first + 5, first + 6, compare, projection); - iter_swap_if(first + 7, first + 8, compare, projection); - iter_swap_if(first + 9, first + 10, compare, projection); - iter_swap_if(first + 11, first + 12, compare, projection); - iter_swap_if(first + 13, first + 14, compare, projection); - iter_swap_if(first + 15, first + 16, compare, projection); - iter_swap_if(first + 17, first + 18, compare, projection); - iter_swap_if(first + 19, first + 20, compare, projection); + iter_swap_if(first + 4, first + 5, compare, projection); + iter_swap_if(first + 6, first + 7, compare, projection); + iter_swap_if(first + 8, first + 9, compare, projection); + iter_swap_if(first + 10, first + 11, compare, projection); + iter_swap_if(first + 12, first + 13, compare, projection); + iter_swap_if(first + 14, first + 15, compare, projection); + iter_swap_if(first + 16, first + 17, compare, projection); + iter_swap_if(first + 18, first + 19, compare, projection); + iter_swap_if(first + 20, first + 21, compare, projection); iter_swap_if(first + 22, first + 23, compare, projection); iter_swap_if(first + 24, first + 25, compare, projection); - iter_swap_if(first, first + 24, compare, projection); - iter_swap_if(first + 1, first + 25, compare, projection); - iter_swap_if(first + 3, first + 21, compare, projection); - iter_swap_if(first + 4, first + 22, compare, projection); + iter_swap_if(first, first + 2, compare, projection); + iter_swap_if(first + 1, first + 3, compare, projection); + iter_swap_if(first + 4, first + 6, compare, projection); iter_swap_if(first + 5, first + 7, compare, projection); - iter_swap_if(first + 6, first + 8, compare, projection); + iter_swap_if(first + 8, first + 10, compare, projection); iter_swap_if(first + 9, first + 11, compare, projection); - iter_swap_if(first + 10, first + 12, compare, projection); - iter_swap_if(first + 13, first + 15, compare, projection); iter_swap_if(first + 14, first + 16, compare, projection); - iter_swap_if(first + 17, first + 19, compare, projection); + iter_swap_if(first + 15, first + 17, compare, projection); iter_swap_if(first + 18, first + 20, compare, projection); - iter_swap_if(first, first + 4, compare, projection); - iter_swap_if(first + 2, first + 22, compare, projection); - iter_swap_if(first + 3, first + 23, compare, projection); - iter_swap_if(first + 5, first + 9, compare, projection); - iter_swap_if(first + 6, first + 10, compare, projection); - iter_swap_if(first + 7, first + 11, compare, projection); - iter_swap_if(first + 8, first + 12, compare, projection); - iter_swap_if(first + 13, first + 17, compare, projection); - iter_swap_if(first + 14, first + 18, compare, projection); - iter_swap_if(first + 15, first + 19, compare, projection); - iter_swap_if(first + 16, first + 20, compare, projection); - iter_swap_if(first + 21, first + 25, compare, projection); - iter_swap_if(first, first + 2, compare, projection); - iter_swap_if(first + 1, first + 3, compare, projection); - iter_swap_if(first + 4, first + 21, compare, projection); - iter_swap_if(first + 5, first + 13, compare, projection); - iter_swap_if(first + 6, first + 14, compare, projection); - iter_swap_if(first + 7, first + 15, compare, projection); - iter_swap_if(first + 8, first + 16, compare, projection); - iter_swap_if(first + 9, first + 17, compare, projection); - iter_swap_if(first + 10, first + 18, compare, projection); - iter_swap_if(first + 11, first + 19, compare, projection); - iter_swap_if(first + 12, first + 20, compare, projection); + iter_swap_if(first + 19, first + 21, compare, projection); iter_swap_if(first + 22, first + 24, compare, projection); iter_swap_if(first + 23, first + 25, compare, projection); - iter_swap_if(first, first + 5, compare, projection); - iter_swap_if(first + 1, first + 7, compare, projection); - iter_swap_if(first + 2, first + 4, compare, projection); - iter_swap_if(first + 3, first + 10, compare, projection); - iter_swap_if(first + 15, first + 22, compare, projection); - iter_swap_if(first + 18, first + 24, compare, projection); - iter_swap_if(first + 20, first + 25, compare, projection); - iter_swap_if(first + 21, first + 23, compare, projection); - iter_swap_if(first + 2, first + 9, compare, projection); + iter_swap_if(first, first + 4, compare, projection); + iter_swap_if(first + 1, first + 6, compare, projection); + iter_swap_if(first + 2, first + 5, compare, projection); iter_swap_if(first + 3, first + 7, compare, projection); - iter_swap_if(first + 4, first + 14, compare, projection); - iter_swap_if(first + 5, first + 13, compare, projection); - iter_swap_if(first + 6, first + 15, compare, projection); - iter_swap_if(first + 10, first + 19, compare, projection); - iter_swap_if(first + 11, first + 21, compare, projection); - iter_swap_if(first + 12, first + 20, compare, projection); - iter_swap_if(first + 16, first + 23, compare, projection); + iter_swap_if(first + 8, first + 14, compare, projection); + iter_swap_if(first + 9, first + 16, compare, projection); + iter_swap_if(first + 10, first + 15, compare, projection); + iter_swap_if(first + 11, first + 17, compare, projection); iter_swap_if(first + 18, first + 22, compare, projection); - iter_swap_if(first + 1, first + 5, compare, projection); - iter_swap_if(first + 2, first + 6, compare, projection); - iter_swap_if(first + 3, first + 15, compare, projection); - iter_swap_if(first + 7, first + 17, compare, projection); - iter_swap_if(first + 8, first + 18, compare, projection); - iter_swap_if(first + 10, first + 22, compare, projection); - iter_swap_if(first + 11, first + 13, compare, projection); - iter_swap_if(first + 12, first + 14, compare, projection); - iter_swap_if(first + 19, first + 23, compare, projection); - iter_swap_if(first + 20, first + 24, compare, projection); - iter_swap_if(first + 1, first + 2, compare, projection); - iter_swap_if(first + 4, first + 7, compare, projection); - iter_swap_if(first + 5, first + 11, compare, projection); + iter_swap_if(first + 19, first + 24, compare, projection); + iter_swap_if(first + 20, first + 23, compare, projection); + iter_swap_if(first + 21, first + 25, compare, projection); + iter_swap_if(first, first + 18, compare, projection); + iter_swap_if(first + 1, first + 19, compare, projection); + iter_swap_if(first + 2, first + 20, compare, projection); + iter_swap_if(first + 3, first + 21, compare, projection); + iter_swap_if(first + 4, first + 22, compare, projection); + iter_swap_if(first + 5, first + 23, compare, projection); + iter_swap_if(first + 6, first + 24, compare, projection); + iter_swap_if(first + 7, first + 25, compare, projection); + iter_swap_if(first + 9, first + 12, compare, projection); + iter_swap_if(first + 13, first + 16, compare, projection); + iter_swap_if(first + 3, first + 11, compare, projection); iter_swap_if(first + 8, first + 9, compare, projection); - iter_swap_if(first + 10, first + 12, compare, projection); - iter_swap_if(first + 13, first + 15, compare, projection); - iter_swap_if(first + 14, first + 20, compare, projection); - iter_swap_if(first + 16, first + 17, compare, projection); - iter_swap_if(first + 18, first + 21, compare, projection); - iter_swap_if(first + 23, first + 24, compare, projection); - iter_swap_if(first + 3, first + 8, compare, projection); - iter_swap_if(first + 4, first + 6, compare, projection); - iter_swap_if(first + 7, first + 16, compare, projection); - iter_swap_if(first + 9, first + 18, compare, projection); iter_swap_if(first + 10, first + 13, compare, projection); iter_swap_if(first + 12, first + 15, compare, projection); - iter_swap_if(first + 17, first + 22, compare, projection); - iter_swap_if(first + 19, first + 21, compare, projection); - iter_swap_if(first + 4, first + 5, compare, projection); - iter_swap_if(first + 6, first + 11, compare, projection); + iter_swap_if(first + 14, first + 22, compare, projection); + iter_swap_if(first + 16, first + 17, compare, projection); + iter_swap_if(first, first + 8, compare, projection); + iter_swap_if(first + 1, first + 9, compare, projection); + iter_swap_if(first + 2, first + 14, compare, projection); + iter_swap_if(first + 6, first + 12, compare, projection); + iter_swap_if(first + 7, first + 15, compare, projection); + iter_swap_if(first + 10, first + 18, compare, projection); + iter_swap_if(first + 11, first + 23, compare, projection); + iter_swap_if(first + 13, first + 19, compare, projection); + iter_swap_if(first + 16, first + 24, compare, projection); + iter_swap_if(first + 17, first + 25, compare, projection); + iter_swap_if(first + 1, first + 2, compare, projection); + iter_swap_if(first + 3, first + 18, compare, projection); + iter_swap_if(first + 4, first + 8, compare, projection); + iter_swap_if(first + 7, first + 22, compare, projection); + iter_swap_if(first + 17, first + 21, compare, projection); + iter_swap_if(first + 23, first + 24, compare, projection); + iter_swap_if(first + 3, first + 14, compare, projection); + iter_swap_if(first + 4, first + 10, compare, projection); + iter_swap_if(first + 5, first + 18, compare, projection); + iter_swap_if(first + 7, first + 20, compare, projection); + iter_swap_if(first + 8, first + 13, compare, projection); + iter_swap_if(first + 11, first + 22, compare, projection); + iter_swap_if(first + 12, first + 17, compare, projection); + iter_swap_if(first + 15, first + 21, compare, projection); + iter_swap_if(first + 1, first + 4, compare, projection); + iter_swap_if(first + 5, first + 6, compare, projection); iter_swap_if(first + 7, first + 9, compare, projection); - iter_swap_if(first + 14, first + 19, compare, projection); + iter_swap_if(first + 8, first + 10, compare, projection); + iter_swap_if(first + 15, first + 17, compare, projection); iter_swap_if(first + 16, first + 18, compare, projection); - iter_swap_if(first + 20, first + 21, compare, projection); + iter_swap_if(first + 19, first + 20, compare, projection); + iter_swap_if(first + 21, first + 24, compare, projection); + iter_swap_if(first + 2, first + 5, compare, projection); + iter_swap_if(first + 3, first + 10, compare, projection); + iter_swap_if(first + 6, first + 14, compare, projection); + iter_swap_if(first + 9, first + 13, compare, projection); + iter_swap_if(first + 11, first + 19, compare, projection); + iter_swap_if(first + 12, first + 16, compare, projection); + iter_swap_if(first + 15, first + 22, compare, projection); + iter_swap_if(first + 20, first + 23, compare, projection); + iter_swap_if(first + 2, first + 8, compare, projection); + iter_swap_if(first + 5, first + 7, compare, projection); + iter_swap_if(first + 6, first + 9, compare, projection); + iter_swap_if(first + 11, first + 12, compare, projection); + iter_swap_if(first + 13, first + 14, compare, projection); + iter_swap_if(first + 16, first + 19, compare, projection); + iter_swap_if(first + 17, first + 23, compare, projection); + iter_swap_if(first + 18, first + 20, compare, projection); iter_swap_if(first + 2, first + 4, compare, projection); - iter_swap_if(first + 3, first + 6, compare, projection); + iter_swap_if(first + 3, first + 5, compare, projection); + iter_swap_if(first + 6, first + 11, compare, projection); iter_swap_if(first + 7, first + 10, compare, projection); - iter_swap_if(first + 8, first + 11, compare, projection); - iter_swap_if(first + 9, first + 12, compare, projection); - iter_swap_if(first + 13, first + 16, compare, projection); - iter_swap_if(first + 14, first + 17, compare, projection); + iter_swap_if(first + 9, first + 16, compare, projection); + iter_swap_if(first + 12, first + 13, compare, projection); + iter_swap_if(first + 14, first + 19, compare, projection); iter_swap_if(first + 15, first + 18, compare, projection); - iter_swap_if(first + 19, first + 22, compare, projection); + iter_swap_if(first + 20, first + 22, compare, projection); iter_swap_if(first + 21, first + 23, compare, projection); iter_swap_if(first + 3, first + 4, compare, projection); iter_swap_if(first + 5, first + 8, compare, projection); iter_swap_if(first + 6, first + 7, compare, projection); - iter_swap_if(first + 9, first + 10, compare, projection); - iter_swap_if(first + 11, first + 13, compare, projection); - iter_swap_if(first + 12, first + 14, compare, projection); - iter_swap_if(first + 15, first + 16, compare, projection); + iter_swap_if(first + 9, first + 11, compare, projection); + iter_swap_if(first + 10, first + 12, compare, projection); + iter_swap_if(first + 13, first + 15, compare, projection); + iter_swap_if(first + 14, first + 16, compare, projection); iter_swap_if(first + 17, first + 20, compare, projection); iter_swap_if(first + 18, first + 19, compare, projection); iter_swap_if(first + 21, first + 22, compare, projection); iter_swap_if(first + 5, first + 6, compare, projection); iter_swap_if(first + 7, first + 8, compare, projection); - iter_swap_if(first + 9, first + 11, compare, projection); - iter_swap_if(first + 10, first + 13, compare, projection); - iter_swap_if(first + 12, first + 15, compare, projection); - iter_swap_if(first + 14, first + 16, compare, projection); + iter_swap_if(first + 9, first + 10, compare, projection); + iter_swap_if(first + 11, first + 12, compare, projection); + iter_swap_if(first + 13, first + 14, compare, projection); + iter_swap_if(first + 15, first + 16, compare, projection); iter_swap_if(first + 17, first + 18, compare, projection); iter_swap_if(first + 19, first + 20, compare, projection); iter_swap_if(first + 4, first + 5, compare, projection); @@ -166,22 +165,23 @@ namespace cppsort::detail template [[nodiscard]] static constexpr auto index_pairs() noexcept - -> std::array, 139> + -> std::array, 138> { return {{ - {0, 1}, {2, 3}, {4, 21}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {22, 23}, {24, 25}, - {0, 24}, {1, 25}, {3, 21}, {4, 22}, {5, 7}, {6, 8}, {9, 11}, {10, 12}, {13, 15}, {14, 16}, {17, 19}, {18, 20}, - {0, 4}, {2, 22}, {3, 23}, {5, 9}, {6, 10}, {7, 11}, {8, 12}, {13, 17}, {14, 18}, {15, 19}, {16, 20}, {21, 25}, - {0, 2}, {1, 3}, {4, 21}, {5, 13}, {6, 14}, {7, 15}, {8, 16}, {9, 17}, {10, 18}, {11, 19}, {12, 20}, {22, 24}, {23, 25}, - {0, 5}, {1, 7}, {2, 4}, {3, 10}, {15, 22}, {18, 24}, {20, 25}, {21, 23}, - {2, 9}, {3, 7}, {4, 14}, {5, 13}, {6, 15}, {10, 19}, {11, 21}, {12, 20}, {16, 23}, {18, 22}, - {1, 5}, {2, 6}, {3, 15}, {7, 17}, {8, 18}, {10, 22}, {11, 13}, {12, 14}, {19, 23}, {20, 24}, - {1, 2}, {4, 7}, {5, 11}, {8, 9}, {10, 12}, {13, 15}, {14, 20}, {16, 17}, {18, 21}, {23, 24}, - {3, 8}, {4, 6}, {7, 16}, {9, 18}, {10, 13}, {12, 15}, {17, 22}, {19, 21}, - {4, 5}, {6, 11}, {7, 9}, {14, 19}, {16, 18}, {20, 21}, - {2, 4}, {3, 6}, {7, 10}, {8, 11}, {9, 12}, {13, 16}, {14, 17}, {15, 18}, {19, 22}, {21, 23}, - {3, 4}, {5, 8}, {6, 7}, {9, 10}, {11, 13}, {12, 14}, {15, 16}, {17, 20}, {18, 19}, {21, 22}, - {5, 6}, {7, 8}, {9, 11}, {10, 13}, {12, 15}, {14, 16}, {17, 18}, {19, 20}, + {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}, + {0, 2}, {1, 3}, {4, 6}, {5, 7}, {8, 10}, {9, 11}, {14, 16}, {15, 17}, {18, 20}, {19, 21}, {22, 24}, {23, 25}, + {0, 4}, {1, 6}, {2, 5}, {3, 7}, {8, 14}, {9, 16}, {10, 15}, {11, 17}, {18, 22}, {19, 24}, {20, 23}, {21, 25}, + {0, 18}, {1, 19}, {2, 20}, {3, 21}, {4, 22}, {5, 23}, {6, 24}, {7, 25}, {9, 12}, {13, 16}, + {3, 11}, {8, 9}, {10, 13}, {12, 15}, {14, 22}, {16, 17}, + {0, 8}, {1, 9}, {2, 14}, {6, 12}, {7, 15}, {10, 18}, {11, 23}, {13, 19}, {16, 24}, {17, 25}, + {1, 2}, {3, 18}, {4, 8}, {7, 22}, {17, 21}, {23, 24}, + {3, 14}, {4, 10}, {5, 18}, {7, 20}, {8, 13}, {11, 22}, {12, 17}, {15, 21}, + {1, 4}, {5, 6}, {7, 9}, {8, 10}, {15, 17}, {16, 18}, {19, 20}, {21, 24}, + {2, 5}, {3, 10}, {6, 14}, {9, 13}, {11, 19}, {12, 16}, {15, 22}, {20, 23}, + {2, 8}, {5, 7}, {6, 9}, {11, 12}, {13, 14}, {16, 19}, {17, 23}, {18, 20}, + {2, 4}, {3, 5}, {6, 11}, {7, 10}, {9, 16}, {12, 13}, {14, 19}, {15, 18}, {20, 22}, {21, 23}, + {3, 4}, {5, 8}, {6, 7}, {9, 11}, {10, 12}, {13, 15}, {14, 16}, {17, 20}, {18, 19}, {21, 22}, + {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 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/sort33.h b/include/cpp-sort/detail/sorting_network/sort33.h index 63e71c33..2576c801 100644 --- a/include/cpp-sort/detail/sorting_network/sort33.h +++ b/include/cpp-sort/detail/sorting_network/sort33.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023-2025 Morwenn +* Copyright (c) 2023-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT33_H_ @@ -102,113 +102,114 @@ namespace cppsort::detail iter_swap_if(first + 13, first + 29, compare, projection); iter_swap_if(first + 14, first + 30, compare, projection); iter_swap_if(first + 15, first + 31, compare, projection); - iter_swap_if(first + 1, first + 4, compare, projection); - iter_swap_if(first + 2, first + 16, compare, projection); - iter_swap_if(first + 3, first + 20, compare, projection); - iter_swap_if(first + 5, first + 18, compare, projection); - iter_swap_if(first + 6, first + 17, compare, projection); - iter_swap_if(first + 7, first + 22, compare, projection); - iter_swap_if(first + 9, first + 24, compare, projection); - iter_swap_if(first + 10, first + 12, compare, projection); - iter_swap_if(first + 11, first + 28, compare, projection); - iter_swap_if(first + 13, first + 26, compare, projection); - iter_swap_if(first + 14, first + 25, compare, projection); - iter_swap_if(first + 15, first + 29, compare, projection); - iter_swap_if(first + 19, first + 21, compare, projection); - iter_swap_if(first + 23, first + 32, compare, projection); - iter_swap_if(first + 27, first + 30, compare, projection); iter_swap_if(first + 1, first + 2, compare, projection); - iter_swap_if(first + 3, first + 9, compare, projection); + iter_swap_if(first + 3, first + 12, compare, projection); + iter_swap_if(first + 4, first + 8, compare, projection); + iter_swap_if(first + 5, first + 10, compare, projection); + iter_swap_if(first + 6, first + 9, compare, projection); + iter_swap_if(first + 7, first + 11, compare, projection); + iter_swap_if(first + 13, first + 14, compare, projection); + iter_swap_if(first + 15, first + 32, compare, projection); + iter_swap_if(first + 17, first + 18, compare, projection); + iter_swap_if(first + 19, first + 28, compare, projection); + iter_swap_if(first + 20, first + 24, compare, projection); + iter_swap_if(first + 21, first + 26, compare, projection); + iter_swap_if(first + 22, first + 25, compare, projection); + iter_swap_if(first + 23, first + 27, compare, projection); + iter_swap_if(first + 29, first + 30, compare, projection); + iter_swap_if(first + 3, first + 17, compare, projection); iter_swap_if(first + 4, first + 16, compare, projection); - iter_swap_if(first + 6, first + 10, compare, projection); - iter_swap_if(first + 7, first + 19, compare, projection); - iter_swap_if(first + 11, first + 17, compare, projection); - iter_swap_if(first + 12, first + 24, compare, projection); - iter_swap_if(first + 13, first + 23, compare, projection); - iter_swap_if(first + 15, first + 27, compare, projection); - iter_swap_if(first + 22, first + 26, compare, projection); + iter_swap_if(first + 5, first + 20, compare, projection); + iter_swap_if(first + 7, first + 13, compare, projection); + iter_swap_if(first + 10, first + 22, compare, projection); + iter_swap_if(first + 11, first + 29, compare, projection); + iter_swap_if(first + 12, first + 21, compare, projection); + iter_swap_if(first + 14, first + 23, compare, projection); + iter_swap_if(first + 15, first + 19, compare, projection); + iter_swap_if(first + 18, first + 24, compare, projection); iter_swap_if(first + 25, first + 28, compare, projection); - iter_swap_if(first + 29, first + 30, compare, projection); - iter_swap_if(first + 3, first + 6, compare, projection); - iter_swap_if(first + 7, first + 12, compare, projection); - iter_swap_if(first + 9, first + 18, compare, projection); - iter_swap_if(first + 10, first + 20, compare, projection); + iter_swap_if(first + 27, first + 30, compare, projection); + iter_swap_if(first + 3, first + 5, compare, projection); + iter_swap_if(first + 7, first + 18, compare, projection); + iter_swap_if(first + 8, first + 16, compare, projection); + iter_swap_if(first + 9, first + 20, compare, projection); + iter_swap_if(first + 10, first + 17, compare, projection); + iter_swap_if(first + 12, first + 15, compare, projection); iter_swap_if(first + 13, first + 14, compare, projection); - iter_swap_if(first + 15, first + 28, compare, projection); - iter_swap_if(first + 17, first + 22, compare, projection); - iter_swap_if(first + 21, first + 25, compare, projection); + iter_swap_if(first + 19, first + 26, compare, projection); + iter_swap_if(first + 21, first + 22, compare, projection); + iter_swap_if(first + 23, first + 29, compare, projection); + iter_swap_if(first + 28, first + 32, compare, projection); + iter_swap_if(first + 1, first + 8, compare, projection); + iter_swap_if(first + 2, first + 16, compare, projection); + iter_swap_if(first + 5, first + 10, compare, projection); + iter_swap_if(first + 6, first + 12, compare, projection); + iter_swap_if(first + 11, first + 19, compare, projection); + iter_swap_if(first + 13, first + 24, compare, projection); + iter_swap_if(first + 14, first + 25, compare, projection); + iter_swap_if(first + 15, first + 20, compare, projection); + iter_swap_if(first + 17, first + 21, compare, projection); + iter_swap_if(first + 26, first + 28, compare, projection); iter_swap_if(first + 27, first + 32, compare, projection); - iter_swap_if(first + 5, first + 13, compare, projection); - iter_swap_if(first + 6, first + 16, compare, projection); - iter_swap_if(first + 9, first + 10, compare, projection); - iter_swap_if(first + 12, first + 20, compare, projection); - iter_swap_if(first + 14, first + 19, compare, projection); - iter_swap_if(first + 15, first + 23, compare, projection); - iter_swap_if(first + 25, first + 26, compare, projection); + iter_swap_if(first, first + 6, compare, projection); + iter_swap_if(first + 2, first + 8, compare, projection); + iter_swap_if(first + 7, first + 10, compare, projection); + iter_swap_if(first + 9, first + 16, compare, projection); + iter_swap_if(first + 11, first + 18, compare, projection); + iter_swap_if(first + 12, first + 15, compare, projection); + iter_swap_if(first + 20, first + 22, compare, projection); + iter_swap_if(first + 24, first + 26, compare, projection); + iter_swap_if(first + 27, first + 28, compare, projection); iter_swap_if(first + 30, first + 32, compare, projection); iter_swap_if(first + 5, first + 8, compare, projection); - iter_swap_if(first + 11, first + 13, compare, projection); - iter_swap_if(first + 14, first + 18, compare, projection); - iter_swap_if(first + 15, first + 21, compare, projection); + iter_swap_if(first + 6, first + 9, compare, projection); + iter_swap_if(first + 11, first + 17, compare, projection); + iter_swap_if(first + 12, first + 16, compare, projection); + iter_swap_if(first + 13, first + 15, compare, projection); + iter_swap_if(first + 14, first + 20, compare, projection); + iter_swap_if(first + 18, first + 21, compare, projection); iter_swap_if(first + 19, first + 22, compare, projection); iter_swap_if(first + 23, first + 27, compare, projection); + iter_swap_if(first + 28, first + 29, compare, projection); iter_swap_if(first + 31, first + 32, compare, projection); - iter_swap_if(first + 4, first + 5, compare, projection); - iter_swap_if(first + 7, first + 11, compare, projection); - iter_swap_if(first + 8, first + 16, compare, projection); - iter_swap_if(first + 12, first + 14, compare, projection); - iter_swap_if(first + 13, first + 24, compare, projection); - iter_swap_if(first + 15, first + 17, compare, projection); - iter_swap_if(first + 18, first + 20, compare, projection); - iter_swap_if(first + 19, first + 21, compare, projection); - iter_swap_if(first + 22, first + 28, compare, projection); - iter_swap_if(first + 23, first + 25, compare, projection); - iter_swap_if(first + 27, first + 29, compare, projection); - iter_swap_if(first + 1, first + 4, compare, projection); - iter_swap_if(first + 3, first + 5, compare, projection); - iter_swap_if(first + 6, first + 8, compare, projection); - iter_swap_if(first + 7, first + 9, compare, projection); - iter_swap_if(first + 10, first + 11, compare, projection); - iter_swap_if(first + 13, first + 15, compare, projection); - iter_swap_if(first + 17, first + 24, compare, projection); - iter_swap_if(first + 22, first + 23, compare, projection); - iter_swap_if(first + 26, first + 27, compare, projection); - iter_swap_if(first + 29, first + 30, compare, projection); - iter_swap_if(first, first + 1, compare, projection); - iter_swap_if(first + 2, first + 6, compare, projection); + iter_swap_if(first + 3, first + 6, compare, projection); + iter_swap_if(first + 7, first + 12, compare, projection); iter_swap_if(first + 8, first + 9, compare, projection); - iter_swap_if(first + 11, first + 16, compare, projection); - iter_swap_if(first + 12, first + 13, compare, projection); + iter_swap_if(first + 10, first + 16, compare, projection); + iter_swap_if(first + 11, first + 13, compare, projection); iter_swap_if(first + 14, first + 17, compare, projection); iter_swap_if(first + 15, first + 18, compare, projection); - iter_swap_if(first + 19, first + 22, compare, projection); - iter_swap_if(first + 20, first + 24, compare, projection); - iter_swap_if(first + 25, first + 26, compare, projection); - iter_swap_if(first + 2, first + 4, compare, projection); - iter_swap_if(first + 5, first + 8, compare, projection); + iter_swap_if(first + 19, first + 24, compare, projection); + iter_swap_if(first + 20, first + 21, compare, projection); + iter_swap_if(first + 22, first + 25, compare, projection); + iter_swap_if(first + 23, first + 26, compare, projection); + iter_swap_if(first + 27, first + 28, compare, projection); + iter_swap_if(first + 29, first + 30, compare, projection); + iter_swap_if(first + 3, first + 4, compare, projection); iter_swap_if(first + 6, first + 7, compare, projection); iter_swap_if(first + 9, first + 11, compare, projection); iter_swap_if(first + 10, first + 12, compare, projection); - iter_swap_if(first + 13, first + 16, compare, projection); - iter_swap_if(first + 14, first + 15, compare, projection); + iter_swap_if(first + 13, first + 14, compare, projection); + iter_swap_if(first + 15, first + 16, compare, projection); iter_swap_if(first + 17, first + 20, compare, projection); iter_swap_if(first + 18, first + 19, compare, projection); - iter_swap_if(first + 21, first + 24, compare, projection); - iter_swap_if(first + 23, first + 25, compare, projection); - iter_swap_if(first + 26, first + 28, compare, projection); - iter_swap_if(first + 3, first + 4, compare, projection); + iter_swap_if(first + 21, first + 22, compare, projection); + iter_swap_if(first + 23, first + 24, compare, projection); + iter_swap_if(first + 25, first + 26, compare, projection); + iter_swap_if(first + 1, first + 3, compare, projection); + iter_swap_if(first + 2, first + 4, compare, projection); iter_swap_if(first + 5, first + 6, compare, projection); iter_swap_if(first + 7, first + 8, compare, projection); iter_swap_if(first + 9, first + 10, compare, projection); iter_swap_if(first + 11, first + 12, compare, projection); - iter_swap_if(first + 13, first + 14, compare, projection); - iter_swap_if(first + 15, first + 16, compare, projection); + iter_swap_if(first + 13, first + 15, compare, projection); + iter_swap_if(first + 14, first + 16, compare, projection); iter_swap_if(first + 17, first + 18, compare, projection); iter_swap_if(first + 19, first + 20, compare, projection); - iter_swap_if(first + 21, first + 22, compare, projection); - iter_swap_if(first + 23, first + 24, compare, projection); - iter_swap_if(first + 25, first + 26, compare, projection); - iter_swap_if(first + 27, first + 28, compare, projection); + iter_swap_if(first + 21, first + 23, compare, projection); + iter_swap_if(first + 22, first + 24, compare, projection); + iter_swap_if(first + 26, first + 27, compare, projection); + iter_swap_if(first + 2, first + 3, compare, projection); iter_swap_if(first + 4, first + 5, compare, projection); iter_swap_if(first + 6, first + 7, compare, projection); iter_swap_if(first + 8, first + 9, compare, projection); @@ -220,7 +221,6 @@ namespace cppsort::detail iter_swap_if(first + 20, first + 21, compare, projection); iter_swap_if(first + 22, first + 23, compare, projection); iter_swap_if(first + 24, first + 25, compare, projection); - iter_swap_if(first + 28, first + 29, compare, projection); } template @@ -234,17 +234,16 @@ namespace cppsort::detail {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, 17}, {2, 18}, {3, 19}, {4, 20}, {5, 21}, {6, 22}, {7, 23}, {8, 24}, {9, 25}, {10, 26}, {11, 27}, {12, 28}, {13, 29}, {14, 30}, {15, 31}, - {1, 4}, {2, 16}, {3, 20}, {5, 18}, {6, 17}, {7, 22}, {9, 24}, {10, 12}, {11, 28}, {13, 26}, {14, 25}, {15, 29}, {19, 21}, {23, 32}, {27, 30}, - {1, 2}, {3, 9}, {4, 16}, {6, 10}, {7, 19}, {11, 17}, {12, 24}, {13, 23}, {15, 27}, {22, 26}, {25, 28}, {29, 30}, - {3, 6}, {7, 12}, {9, 18}, {10, 20}, {13, 14}, {15, 28}, {17, 22}, {21, 25}, {27, 32}, - {5, 13}, {6, 16}, {9, 10}, {12, 20}, {14, 19}, {15, 23}, {25, 26}, {30, 32}, - {5, 8}, {11, 13}, {14, 18}, {15, 21}, {19, 22}, {23, 27}, {31, 32}, - {4, 5}, {7, 11}, {8, 16}, {12, 14}, {13, 24}, {15, 17}, {18, 20}, {19, 21}, {22, 28}, {23, 25}, {27, 29}, - {1, 4}, {3, 5}, {6, 8}, {7, 9}, {10, 11}, {13, 15}, {17, 24}, {22, 23}, {26, 27}, {29, 30}, - {0, 1}, {2, 6}, {8, 9}, {11, 16}, {12, 13}, {14, 17}, {15, 18}, {19, 22}, {20, 24}, {25, 26}, - {2, 4}, {5, 8}, {6, 7}, {9, 11}, {10, 12}, {13, 16}, {14, 15}, {17, 20}, {18, 19}, {21, 24}, {23, 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}, - {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, {28, 29}, + {1, 2}, {3, 12}, {4, 8}, {5, 10}, {6, 9}, {7, 11}, {13, 14}, {15, 32}, {17, 18}, {19, 28}, {20, 24}, {21, 26}, {22, 25}, {23, 27}, {29, 30}, + {3, 17}, {4, 16}, {5, 20}, {7, 13}, {10, 22}, {11, 29}, {12, 21}, {14, 23}, {15, 19}, {18, 24}, {25, 28}, {27, 30}, + {3, 5}, {7, 18}, {8, 16}, {9, 20}, {10, 17}, {12, 15}, {13, 14}, {19, 26}, {21, 22}, {23, 29}, {28, 32}, + {1, 8}, {2, 16}, {5, 10}, {6, 12}, {11, 19}, {13, 24}, {14, 25}, {15, 20}, {17, 21}, {26, 28}, {27, 32}, + {0, 6}, {2, 8}, {7, 10}, {9, 16}, {11, 18}, {12, 15}, {20, 22}, {24, 26}, {27, 28}, {30, 32}, + {5, 8}, {6, 9}, {11, 17}, {12, 16}, {13, 15}, {14, 20}, {18, 21}, {19, 22}, {23, 27}, {28, 29}, {31, 32}, + {3, 6}, {7, 12}, {8, 9}, {10, 16}, {11, 13}, {14, 17}, {15, 18}, {19, 24}, {20, 21}, {22, 25}, {23, 26}, {27, 28}, {29, 30}, + {3, 4}, {6, 7}, {9, 11}, {10, 12}, {13, 14}, {15, 16}, {17, 20}, {18, 19}, {21, 22}, {23, 24}, {25, 26}, + {1, 3}, {2, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 15}, {14, 16}, {17, 18}, {19, 20}, {21, 23}, {22, 24}, {26, 27}, + {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, }}; } }; diff --git a/include/cpp-sort/detail/sorting_network/sort41.h b/include/cpp-sort/detail/sorting_network/sort41.h index 6205fc37..be427fd2 100644 --- a/include/cpp-sort/detail/sorting_network/sort41.h +++ b/include/cpp-sort/detail/sorting_network/sort41.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023-2025 Morwenn +* Copyright (c) 2023-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT41_H_ @@ -38,10 +38,10 @@ namespace cppsort::detail iter_swap_if(first + 26, first + 27, compare, projection); iter_swap_if(first + 28, first + 29, compare, projection); iter_swap_if(first + 30, first + 31, compare, projection); - iter_swap_if(first + 32, first + 35, compare, projection); - iter_swap_if(first + 33, first + 39, compare, projection); - iter_swap_if(first + 34, first + 37, compare, projection); - iter_swap_if(first + 36, first + 40, compare, projection); + iter_swap_if(first + 32, first + 33, compare, projection); + iter_swap_if(first + 34, first + 35, compare, projection); + iter_swap_if(first + 36, first + 37, compare, projection); + iter_swap_if(first + 38, first + 39, compare, projection); iter_swap_if(first, first + 2, compare, projection); iter_swap_if(first + 1, first + 3, compare, projection); iter_swap_if(first + 4, first + 6, compare, projection); @@ -58,10 +58,10 @@ namespace cppsort::detail iter_swap_if(first + 25, first + 27, compare, projection); iter_swap_if(first + 28, first + 30, compare, projection); iter_swap_if(first + 29, first + 31, compare, projection); - iter_swap_if(first + 32, first + 39, compare, projection); - iter_swap_if(first + 34, first + 36, compare, projection); - iter_swap_if(first + 35, first + 40, compare, projection); - iter_swap_if(first + 37, first + 38, compare, projection); + iter_swap_if(first + 32, first + 34, compare, projection); + iter_swap_if(first + 33, first + 35, compare, projection); + iter_swap_if(first + 36, first + 38, compare, projection); + iter_swap_if(first + 37, first + 39, compare, projection); iter_swap_if(first, first + 4, compare, projection); iter_swap_if(first + 1, first + 5, compare, projection); iter_swap_if(first + 2, first + 6, compare, projection); @@ -78,10 +78,10 @@ namespace cppsort::detail iter_swap_if(first + 25, first + 29, compare, projection); iter_swap_if(first + 26, first + 30, compare, projection); iter_swap_if(first + 27, first + 31, compare, projection); - iter_swap_if(first + 32, first + 34, compare, projection); - iter_swap_if(first + 33, first + 35, compare, projection); - iter_swap_if(first + 36, first + 37, compare, projection); - iter_swap_if(first + 39, first + 40, compare, projection); + iter_swap_if(first + 32, first + 36, compare, projection); + iter_swap_if(first + 33, first + 37, compare, projection); + iter_swap_if(first + 34, first + 38, compare, projection); + iter_swap_if(first + 35, first + 39, compare, projection); iter_swap_if(first, first + 8, compare, projection); iter_swap_if(first + 1, first + 9, compare, projection); iter_swap_if(first + 2, first + 10, compare, projection); @@ -98,9 +98,9 @@ namespace cppsort::detail iter_swap_if(first + 21, first + 29, compare, projection); iter_swap_if(first + 22, first + 30, compare, projection); iter_swap_if(first + 23, first + 31, compare, projection); - iter_swap_if(first + 33, first + 36, compare, projection); - iter_swap_if(first + 35, first + 38, compare, projection); - iter_swap_if(first + 37, first + 39, compare, projection); + iter_swap_if(first + 33, first + 34, compare, projection); + iter_swap_if(first + 36, first + 40, compare, projection); + iter_swap_if(first + 37, first + 38, compare, projection); iter_swap_if(first, first + 16, compare, projection); iter_swap_if(first + 1, first + 17, compare, projection); iter_swap_if(first + 2, first + 18, compare, projection); @@ -117,11 +117,8 @@ namespace cppsort::detail iter_swap_if(first + 13, first + 29, compare, projection); iter_swap_if(first + 14, first + 30, compare, projection); iter_swap_if(first + 15, first + 31, compare, projection); - iter_swap_if(first + 32, first + 33, compare, projection); - iter_swap_if(first + 34, first + 36, compare, projection); - iter_swap_if(first + 35, first + 37, compare, projection); - iter_swap_if(first + 38, first + 40, compare, projection); - iter_swap_if(first, first + 32, compare, projection); + iter_swap_if(first + 33, first + 36, compare, projection); + iter_swap_if(first + 34, first + 40, compare, projection); iter_swap_if(first + 1, first + 2, compare, projection); iter_swap_if(first + 3, first + 12, compare, projection); iter_swap_if(first + 4, first + 8, compare, projection); @@ -136,148 +133,163 @@ namespace cppsort::detail iter_swap_if(first + 22, first + 25, compare, projection); iter_swap_if(first + 23, first + 27, compare, projection); iter_swap_if(first + 29, first + 30, compare, projection); - iter_swap_if(first + 31, first + 40, compare, projection); - iter_swap_if(first + 34, first + 35, compare, projection); - iter_swap_if(first + 36, first + 37, compare, projection); - iter_swap_if(first + 38, first + 39, compare, projection); - iter_swap_if(first + 5, first + 6, compare, projection); + iter_swap_if(first + 32, first + 33, compare, projection); + iter_swap_if(first + 34, first + 36, compare, projection); + iter_swap_if(first + 35, first + 40, compare, projection); + iter_swap_if(first + 1, first + 4, compare, projection); + iter_swap_if(first + 2, first + 8, compare, projection); + iter_swap_if(first + 3, first + 20, compare, projection); + iter_swap_if(first + 5, first + 17, compare, projection); iter_swap_if(first + 7, first + 13, compare, projection); iter_swap_if(first + 9, first + 10, compare, projection); - iter_swap_if(first + 11, first + 39, compare, projection); - iter_swap_if(first + 15, first + 31, compare, projection); + iter_swap_if(first + 11, first + 26, compare, projection); + iter_swap_if(first + 14, first + 28, compare, projection); iter_swap_if(first + 16, first + 32, compare, projection); iter_swap_if(first + 18, first + 24, compare, projection); + iter_swap_if(first + 19, first + 39, compare, projection); iter_swap_if(first + 21, first + 22, compare, projection); - iter_swap_if(first + 23, first + 29, compare, projection); - iter_swap_if(first + 25, first + 26, compare, projection); - iter_swap_if(first + 33, first + 34, compare, projection); - iter_swap_if(first + 35, first + 36, compare, projection); - iter_swap_if(first + 37, first + 38, compare, projection); - iter_swap_if(first + 1, first + 36, compare, projection); - iter_swap_if(first + 2, first + 33, compare, projection); + iter_swap_if(first + 35, first + 37, compare, projection); + iter_swap_if(first + 38, first + 40, compare, projection); + iter_swap_if(first, first + 16, compare, projection); + iter_swap_if(first + 2, first + 34, compare, projection); + iter_swap_if(first + 3, first + 36, compare, projection); + iter_swap_if(first + 4, first + 35, compare, projection); iter_swap_if(first + 6, first + 17, compare, projection); - iter_swap_if(first + 10, first + 21, compare, projection); - iter_swap_if(first + 11, first + 22, compare, projection); - iter_swap_if(first + 12, first + 37, compare, projection); - iter_swap_if(first + 14, first + 38, compare, projection); - iter_swap_if(first + 15, first + 19, compare, projection); - iter_swap_if(first + 18, first + 34, compare, projection); - iter_swap_if(first + 23, first + 25, compare, projection); - iter_swap_if(first + 28, first + 35, compare, projection); - iter_swap_if(first + 30, first + 39, compare, projection); - iter_swap_if(first + 1, first + 16, compare, projection); - iter_swap_if(first + 2, first + 4, compare, projection); - iter_swap_if(first + 3, first + 28, compare, projection); - iter_swap_if(first + 6, first + 18, compare, projection); - iter_swap_if(first + 8, first + 33, compare, projection); - iter_swap_if(first + 14, first + 34, compare, projection); - iter_swap_if(first + 15, first + 36, compare, projection); - iter_swap_if(first + 19, first + 29, compare, projection); - iter_swap_if(first + 26, first + 37, compare, projection); - iter_swap_if(first + 27, first + 38, compare, projection); - iter_swap_if(first + 30, first + 35, compare, projection); - iter_swap_if(first + 1, first + 2, compare, projection); - iter_swap_if(first + 3, first + 20, compare, projection); - iter_swap_if(first + 7, first + 14, compare, projection); - iter_swap_if(first + 8, first + 16, compare, projection); - iter_swap_if(first + 9, first + 28, compare, projection); - iter_swap_if(first + 11, first + 34, compare, projection); - iter_swap_if(first + 12, first + 33, compare, projection); - iter_swap_if(first + 15, first + 32, compare, projection); - iter_swap_if(first + 17, first + 26, compare, projection); - iter_swap_if(first + 19, first + 27, compare, projection); - iter_swap_if(first + 22, first + 30, compare, projection); - iter_swap_if(first + 25, first + 36, compare, projection); - iter_swap_if(first + 31, first + 38, compare, projection); - iter_swap_if(first + 3, first + 4, compare, projection); - iter_swap_if(first + 5, first + 15, compare, projection); - iter_swap_if(first + 7, first + 18, compare, projection); + iter_swap_if(first + 11, first + 25, compare, projection); + iter_swap_if(first + 12, first + 32, compare, projection); + iter_swap_if(first + 15, first + 39, compare, projection); + iter_swap_if(first + 19, first + 21, compare, projection); + iter_swap_if(first + 24, first + 40, compare, projection); + iter_swap_if(first + 26, first + 38, compare, projection); + iter_swap_if(first + 2, first + 12, compare, projection); + iter_swap_if(first + 4, first + 16, compare, projection); + iter_swap_if(first + 5, first + 34, compare, projection); + iter_swap_if(first + 6, first + 33, compare, projection); + iter_swap_if(first + 8, first + 24, compare, projection); iter_swap_if(first + 10, first + 32, compare, projection); - iter_swap_if(first + 11, first + 20, compare, projection); + iter_swap_if(first + 15, first + 23, compare, projection); + iter_swap_if(first + 17, first + 35, compare, projection); + iter_swap_if(first + 19, first + 36, compare, projection); + iter_swap_if(first + 22, first + 26, compare, projection); + iter_swap_if(first + 25, first + 37, compare, projection); + iter_swap_if(first + 27, first + 39, compare, projection); + iter_swap_if(first + 28, first + 40, compare, projection); + iter_swap_if(first + 1, first + 6, compare, projection); + iter_swap_if(first + 3, first + 5, compare, projection); + iter_swap_if(first + 7, first + 19, compare, projection); + iter_swap_if(first + 8, first + 12, compare, projection); + iter_swap_if(first + 10, first + 22, compare, projection); + iter_swap_if(first + 11, first + 36, compare, projection); + iter_swap_if(first + 18, first + 33, compare, projection); + iter_swap_if(first + 20, first + 34, compare, projection); + iter_swap_if(first + 21, first + 25, compare, projection); + iter_swap_if(first + 28, first + 37, compare, projection); + iter_swap_if(first + 30, first + 40, compare, projection); + iter_swap_if(first + 32, first + 35, compare, projection); + iter_swap_if(first + 1, first + 4, compare, projection); + iter_swap_if(first + 3, first + 6, compare, projection); + iter_swap_if(first + 8, first + 16, compare, projection); + iter_swap_if(first + 9, first + 21, compare, projection); + iter_swap_if(first + 10, first + 18, compare, projection); + iter_swap_if(first + 11, first + 19, compare, projection); iter_swap_if(first + 12, first + 17, compare, projection); - iter_swap_if(first + 13, first + 25, compare, projection); - iter_swap_if(first + 21, first + 33, compare, projection); - iter_swap_if(first + 24, first + 34, compare, projection); - iter_swap_if(first + 27, first + 36, compare, projection); - iter_swap_if(first + 31, first + 35, compare, projection); - iter_swap_if(first + 38, first + 39, compare, projection); - iter_swap_if(first + 4, first + 16, compare, projection); - iter_swap_if(first + 5, first + 6, compare, projection); - iter_swap_if(first + 9, first + 11, compare, projection); - iter_swap_if(first + 10, first + 15, compare, projection); - iter_swap_if(first + 14, first + 24, compare, projection); - iter_swap_if(first + 20, first + 28, compare, projection); - iter_swap_if(first + 23, first + 32, compare, projection); + iter_swap_if(first + 13, first + 34, compare, projection); + iter_swap_if(first + 14, first + 33, compare, projection); + iter_swap_if(first + 24, first + 32, compare, projection); + iter_swap_if(first + 27, first + 37, compare, projection); + iter_swap_if(first + 28, first + 35, compare, projection); + iter_swap_if(first + 29, first + 36, compare, projection); + iter_swap_if(first + 30, first + 38, compare, projection); + iter_swap_if(first + 31, first + 40, compare, projection); + iter_swap_if(first + 3, first + 8, compare, projection); + iter_swap_if(first + 6, first + 10, compare, projection); + iter_swap_if(first + 9, first + 20, compare, projection); + iter_swap_if(first + 13, first + 21, compare, projection); + iter_swap_if(first + 14, first + 22, compare, projection); + iter_swap_if(first + 15, first + 29, compare, projection); + iter_swap_if(first + 17, first + 24, compare, projection); + iter_swap_if(first + 23, first + 36, compare, projection); iter_swap_if(first + 25, first + 34, compare, projection); + iter_swap_if(first + 26, first + 33, compare, projection); + iter_swap_if(first + 27, first + 30, compare, projection); + iter_swap_if(first + 28, first + 32, compare, projection); + iter_swap_if(first + 31, first + 37, compare, projection); + iter_swap_if(first + 39, first + 40, compare, projection); + iter_swap_if(first + 2, first + 8, compare, projection); + iter_swap_if(first + 5, first + 9, compare, projection); + iter_swap_if(first + 10, first + 16, compare, projection); + iter_swap_if(first + 13, first + 20, compare, projection); + iter_swap_if(first + 14, first + 18, compare, projection); + iter_swap_if(first + 15, first + 19, compare, projection); + iter_swap_if(first + 21, first + 25, compare, projection); + iter_swap_if(first + 22, first + 26, compare, projection); + iter_swap_if(first + 23, first + 30, compare, projection); iter_swap_if(first + 29, first + 35, compare, projection); - iter_swap_if(first + 5, first + 8, compare, projection); - iter_swap_if(first + 7, first + 10, compare, projection); + iter_swap_if(first + 33, first + 34, compare, projection); + iter_swap_if(first + 36, first + 38, compare, projection); + iter_swap_if(first + 37, first + 39, compare, projection); + iter_swap_if(first + 2, first + 4, compare, projection); + iter_swap_if(first + 5, first + 10, compare, projection); + iter_swap_if(first + 7, first + 13, compare, projection); iter_swap_if(first + 9, first + 12, compare, projection); - iter_swap_if(first + 11, first + 17, compare, projection); - iter_swap_if(first + 13, first + 23, compare, projection); - iter_swap_if(first + 15, first + 21, compare, projection); - iter_swap_if(first + 18, first + 20, compare, projection); - iter_swap_if(first + 19, first + 32, compare, projection); - iter_swap_if(first + 22, first + 28, compare, projection); - iter_swap_if(first + 24, first + 25, compare, projection); - iter_swap_if(first + 27, first + 34, compare, projection); - iter_swap_if(first + 29, first + 37, compare, projection); - iter_swap_if(first + 35, first + 38, compare, projection); - iter_swap_if(first + 2, first + 5, compare, projection); - iter_swap_if(first + 3, first + 8, compare, projection); - iter_swap_if(first + 7, first + 9, compare, projection); - iter_swap_if(first + 12, first + 16, compare, projection); - iter_swap_if(first + 13, first + 14, compare, projection); - iter_swap_if(first + 15, first + 18, compare, projection); - iter_swap_if(first + 19, first + 26, compare, projection); - iter_swap_if(first + 20, first + 21, compare, projection); - iter_swap_if(first + 22, first + 24, compare, projection); - iter_swap_if(first + 23, first + 33, compare, projection); - iter_swap_if(first + 25, first + 28, compare, projection); - iter_swap_if(first + 29, first + 30, compare, projection); - iter_swap_if(first + 31, first + 32, compare, projection); - iter_swap_if(first + 35, first + 37, compare, projection); - iter_swap_if(first + 3, first + 5, compare, projection); - iter_swap_if(first + 4, first + 9, compare, projection); - iter_swap_if(first + 6, first + 12, compare, projection); - iter_swap_if(first + 10, first + 16, compare, projection); - iter_swap_if(first + 11, first + 13, compare, projection); + iter_swap_if(first + 11, first + 18, compare, projection); iter_swap_if(first + 14, first + 17, compare, projection); - iter_swap_if(first + 19, first + 23, compare, projection); - iter_swap_if(first + 26, first + 33, compare, projection); + iter_swap_if(first + 15, first + 28, compare, projection); + iter_swap_if(first + 19, first + 25, compare, projection); + iter_swap_if(first + 20, first + 24, compare, projection); + iter_swap_if(first + 21, first + 22, compare, projection); + iter_swap_if(first + 23, first + 33, compare, projection); + iter_swap_if(first + 26, first + 32, compare, projection); iter_swap_if(first + 27, first + 29, compare, projection); iter_swap_if(first + 30, first + 34, compare, projection); - iter_swap_if(first + 32, first + 36, compare, projection); - iter_swap_if(first + 4, first + 7, compare, projection); + iter_swap_if(first + 31, first + 36, compare, projection); + iter_swap_if(first + 37, first + 38, compare, projection); + iter_swap_if(first + 3, first + 4, compare, projection); + iter_swap_if(first + 6, first + 9, compare, projection); + iter_swap_if(first + 7, first + 14, compare, projection); + iter_swap_if(first + 11, first + 20, compare, projection); + iter_swap_if(first + 12, first + 16, compare, projection); + iter_swap_if(first + 13, first + 17, compare, projection); + iter_swap_if(first + 15, first + 21, compare, projection); + iter_swap_if(first + 18, first + 24, compare, projection); + iter_swap_if(first + 19, first + 26, compare, projection); + iter_swap_if(first + 22, first + 28, compare, projection); + iter_swap_if(first + 23, first + 27, compare, projection); + iter_swap_if(first + 25, first + 32, compare, projection); + iter_swap_if(first + 29, first + 33, compare, projection); + iter_swap_if(first + 30, first + 35, compare, projection); + iter_swap_if(first + 36, first + 37, compare, projection); iter_swap_if(first + 6, first + 8, compare, projection); + iter_swap_if(first + 7, first + 9, compare, projection); iter_swap_if(first + 10, first + 12, compare, projection); - iter_swap_if(first + 11, first + 15, compare, projection); - iter_swap_if(first + 13, first + 20, compare, projection); - iter_swap_if(first + 14, first + 18, compare, projection); - iter_swap_if(first + 17, first + 21, compare, projection); - iter_swap_if(first + 19, first + 22, compare, projection); - iter_swap_if(first + 23, first + 25, compare, projection); - iter_swap_if(first + 24, first + 26, compare, projection); - iter_swap_if(first + 27, first + 31, compare, projection); - iter_swap_if(first + 28, first + 33, compare, projection); + iter_swap_if(first + 11, first + 16, compare, projection); + iter_swap_if(first + 13, first + 14, compare, projection); + iter_swap_if(first + 15, first + 20, compare, projection); + iter_swap_if(first + 17, first + 18, compare, projection); + iter_swap_if(first + 19, first + 24, compare, projection); + iter_swap_if(first + 21, first + 22, compare, projection); + iter_swap_if(first + 23, first + 26, compare, projection); + iter_swap_if(first + 25, first + 28, compare, projection); + iter_swap_if(first + 27, first + 30, compare, projection); iter_swap_if(first + 29, first + 32, compare, projection); - iter_swap_if(first + 35, first + 36, compare, projection); - iter_swap_if(first + 4, first + 6, compare, projection); - iter_swap_if(first + 7, first + 8, compare, projection); - iter_swap_if(first + 9, first + 11, compare, projection); - iter_swap_if(first + 13, first + 15, compare, projection); + iter_swap_if(first + 31, first + 35, compare, projection); + iter_swap_if(first + 5, first + 8, compare, projection); + iter_swap_if(first + 7, first + 10, compare, projection); + iter_swap_if(first + 9, first + 12, compare, projection); + iter_swap_if(first + 11, first + 13, compare, projection); iter_swap_if(first + 14, first + 16, compare, projection); - iter_swap_if(first + 17, first + 19, compare, projection); + iter_swap_if(first + 15, first + 17, compare, projection); iter_swap_if(first + 18, first + 20, compare, projection); - iter_swap_if(first + 21, first + 23, compare, projection); + iter_swap_if(first + 19, first + 21, compare, projection); iter_swap_if(first + 22, first + 24, compare, projection); - iter_swap_if(first + 25, first + 26, compare, projection); - iter_swap_if(first + 27, first + 28, compare, projection); - iter_swap_if(first + 29, first + 33, compare, projection); - iter_swap_if(first + 30, first + 31, compare, projection); - iter_swap_if(first + 32, first + 34, compare, projection); - iter_swap_if(first + 4, first + 5, compare, projection); - iter_swap_if(first + 6, first + 7, compare, projection); + iter_swap_if(first + 23, first + 25, compare, projection); + iter_swap_if(first + 26, first + 28, compare, projection); + iter_swap_if(first + 27, first + 29, compare, projection); + iter_swap_if(first + 30, first + 32, compare, projection); + iter_swap_if(first + 31, first + 33, compare, projection); + iter_swap_if(first + 34, first + 35, compare, projection); + iter_swap_if(first + 5, first + 6, compare, projection); + iter_swap_if(first + 7, first + 8, compare, projection); iter_swap_if(first + 9, first + 10, compare, projection); iter_swap_if(first + 11, first + 12, compare, projection); iter_swap_if(first + 13, first + 14, compare, projection); @@ -286,24 +298,12 @@ namespace cppsort::detail iter_swap_if(first + 19, first + 20, compare, projection); iter_swap_if(first + 21, first + 22, compare, projection); iter_swap_if(first + 23, first + 24, compare, projection); - iter_swap_if(first + 25, first + 27, compare, projection); - iter_swap_if(first + 26, first + 28, compare, projection); + iter_swap_if(first + 25, first + 26, compare, projection); + iter_swap_if(first + 27, first + 28, compare, projection); iter_swap_if(first + 29, first + 30, compare, projection); - iter_swap_if(first + 31, first + 33, compare, projection); - iter_swap_if(first + 34, first + 35, compare, projection); - iter_swap_if(first + 8, first + 9, compare, projection); - iter_swap_if(first + 10, first + 11, compare, projection); - iter_swap_if(first + 12, first + 13, compare, projection); - iter_swap_if(first + 14, first + 15, compare, projection); - iter_swap_if(first + 16, first + 17, compare, projection); - iter_swap_if(first + 18, first + 19, compare, projection); - iter_swap_if(first + 20, first + 21, compare, projection); - iter_swap_if(first + 22, first + 23, compare, projection); - iter_swap_if(first + 24, first + 25, compare, projection); - iter_swap_if(first + 26, first + 27, compare, projection); - iter_swap_if(first + 28, first + 29, compare, projection); - iter_swap_if(first + 30, first + 31, compare, projection); - iter_swap_if(first + 32, first + 33, compare, projection); + iter_swap_if(first + 31, first + 32, compare, projection); + iter_swap_if(first + 33, first + 34, compare, projection); + iter_swap_if(first + 35, first + 36, compare, projection); } template @@ -312,25 +312,24 @@ namespace cppsort::detail -> std::array, 282> { 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}, {32, 35}, {33, 39}, {34, 37}, {36, 40}, - {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}, {32, 39}, {34, 36}, {35, 40}, {37, 38}, - {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}, {32, 34}, {33, 35}, {36, 37}, {39, 40}, - {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}, {33, 36}, {35, 38}, {37, 39}, - {0, 16}, {1, 17}, {2, 18}, {3, 19}, {4, 20}, {5, 21}, {6, 22}, {7, 23}, {8, 24}, {9, 25}, {10, 26}, {11, 27}, {12, 28}, {13, 29}, {14, 30}, {15, 31}, {32, 33}, {34, 36}, {35, 37}, {38, 40}, - {0, 32}, {1, 2}, {3, 12}, {4, 8}, {5, 10}, {6, 9}, {7, 11}, {13, 14}, {17, 18}, {19, 28}, {20, 24}, {21, 26}, {22, 25}, {23, 27}, {29, 30}, {31, 40}, {34, 35}, {36, 37}, {38, 39}, - {5, 6}, {7, 13}, {9, 10}, {11, 39}, {15, 31}, {16, 32}, {18, 24}, {21, 22}, {23, 29}, {25, 26}, {33, 34}, {35, 36}, {37, 38}, - {1, 36}, {2, 33}, {6, 17}, {10, 21}, {11, 22}, {12, 37}, {14, 38}, {15, 19}, {18, 34}, {23, 25}, {28, 35}, {30, 39}, - {1, 16}, {2, 4}, {3, 28}, {6, 18}, {8, 33}, {14, 34}, {15, 36}, {19, 29}, {26, 37}, {27, 38}, {30, 35}, - {1, 2}, {3, 20}, {7, 14}, {8, 16}, {9, 28}, {11, 34}, {12, 33}, {15, 32}, {17, 26}, {19, 27}, {22, 30}, {25, 36}, {31, 38}, - {3, 4}, {5, 15}, {7, 18}, {10, 32}, {11, 20}, {12, 17}, {13, 25}, {21, 33}, {24, 34}, {27, 36}, {31, 35}, {38, 39}, - {4, 16}, {5, 6}, {9, 11}, {10, 15}, {14, 24}, {20, 28}, {23, 32}, {25, 34}, {29, 35}, - {5, 8}, {7, 10}, {9, 12}, {11, 17}, {13, 23}, {15, 21}, {18, 20}, {19, 32}, {22, 28}, {24, 25}, {27, 34}, {29, 37}, {35, 38}, - {2, 5}, {3, 8}, {7, 9}, {12, 16}, {13, 14}, {15, 18}, {19, 26}, {20, 21}, {22, 24}, {23, 33}, {25, 28}, {29, 30}, {31, 32}, {35, 37}, - {3, 5}, {4, 9}, {6, 12}, {10, 16}, {11, 13}, {14, 17}, {19, 23}, {26, 33}, {27, 29}, {30, 34}, {32, 36}, - {4, 7}, {6, 8}, {10, 12}, {11, 15}, {13, 20}, {14, 18}, {17, 21}, {19, 22}, {23, 25}, {24, 26}, {27, 31}, {28, 33}, {29, 32}, {35, 36}, - {4, 6}, {7, 8}, {9, 11}, {13, 15}, {14, 16}, {17, 19}, {18, 20}, {21, 23}, {22, 24}, {25, 26}, {27, 28}, {29, 33}, {30, 31}, {32, 34}, - {4, 5}, {6, 7}, {9, 10}, {11, 12}, {13, 14}, {15, 16}, {17, 18}, {19, 20}, {21, 22}, {23, 24}, {25, 27}, {26, 28}, {29, 30}, {31, 33}, {34, 35}, - {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, {20, 21}, {22, 23}, {24, 25}, {26, 27}, {28, 29}, {30, 31}, {32, 33}, + {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}, {32, 33}, {34, 35}, {36, 37}, {38, 39}, + {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}, {32, 34}, {33, 35}, {36, 38}, {37, 39}, + {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}, {32, 36}, {33, 37}, {34, 38}, {35, 39}, + {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}, {33, 34}, {36, 40}, {37, 38}, + {0, 16}, {1, 17}, {2, 18}, {3, 19}, {4, 20}, {5, 21}, {6, 22}, {7, 23}, {8, 24}, {9, 25}, {10, 26}, {11, 27}, {12, 28}, {13, 29}, {14, 30}, {15, 31}, {33, 36}, {34, 40}, + {1, 2}, {3, 12}, {4, 8}, {5, 10}, {6, 9}, {7, 11}, {13, 14}, {17, 18}, {19, 28}, {20, 24}, {21, 26}, {22, 25}, {23, 27}, {29, 30}, {32, 33}, {34, 36}, {35, 40}, + {1, 4}, {2, 8}, {3, 20}, {5, 17}, {7, 13}, {9, 10}, {11, 26}, {14, 28}, {16, 32}, {18, 24}, {19, 39}, {21, 22}, {35, 37}, {38, 40}, + {0, 16}, {2, 34}, {3, 36}, {4, 35}, {6, 17}, {11, 25}, {12, 32}, {15, 39}, {19, 21}, {24, 40}, {26, 38}, + {2, 12}, {4, 16}, {5, 34}, {6, 33}, {8, 24}, {10, 32}, {15, 23}, {17, 35}, {19, 36}, {22, 26}, {25, 37}, {27, 39}, {28, 40}, + {1, 6}, {3, 5}, {7, 19}, {8, 12}, {10, 22}, {11, 36}, {18, 33}, {20, 34}, {21, 25}, {28, 37}, {30, 40}, {32, 35}, + {1, 4}, {3, 6}, {8, 16}, {9, 21}, {10, 18}, {11, 19}, {12, 17}, {13, 34}, {14, 33}, {24, 32}, {27, 37}, {28, 35}, {29, 36}, {30, 38}, {31, 40}, + {3, 8}, {6, 10}, {9, 20}, {13, 21}, {14, 22}, {15, 29}, {17, 24}, {23, 36}, {25, 34}, {26, 33}, {27, 30}, {28, 32}, {31, 37}, {39, 40}, + {2, 8}, {5, 9}, {10, 16}, {13, 20}, {14, 18}, {15, 19}, {21, 25}, {22, 26}, {23, 30}, {29, 35}, {33, 34}, {36, 38}, {37, 39}, + {2, 4}, {5, 10}, {7, 13}, {9, 12}, {11, 18}, {14, 17}, {15, 28}, {19, 25}, {20, 24}, {21, 22}, {23, 33}, {26, 32}, {27, 29}, {30, 34}, {31, 36}, {37, 38}, + {3, 4}, {6, 9}, {7, 14}, {11, 20}, {12, 16}, {13, 17}, {15, 21}, {18, 24}, {19, 26}, {22, 28}, {23, 27}, {25, 32}, {29, 33}, {30, 35}, {36, 37}, + {6, 8}, {7, 9}, {10, 12}, {11, 16}, {13, 14}, {15, 20}, {17, 18}, {19, 24}, {21, 22}, {23, 26}, {25, 28}, {27, 30}, {29, 32}, {31, 35}, + {5, 8}, {7, 10}, {9, 12}, {11, 13}, {14, 16}, {15, 17}, {18, 20}, {19, 21}, {22, 24}, {23, 25}, {26, 28}, {27, 29}, {30, 32}, {31, 33}, {34, 35}, + {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, 32}, {33, 34}, {35, 36}, }}; } }; diff --git a/include/cpp-sort/probes.h b/include/cpp-sort/probes.h index 3b0699ea..44cf63a7 100644 --- a/include/cpp-sort/probes.h +++ b/include/cpp-sort/probes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_PROBES_H_ @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/include/cpp-sort/probes/reve.h b/include/cpp-sort/probes/reve.h new file mode 100644 index 00000000..b1ce900b --- /dev/null +++ b/include/cpp-sort/probes/reve.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_PROBES_REVE_H_ +#define CPPSORT_PROBES_REVE_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" + +namespace cppsort::probe +{ + namespace detail + { + struct reve_impl + { + template< + typename ForwardIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = cppsort::detail::enable_if_t< + is_projection_iterator_v + > + > + auto operator()(ForwardIterator first, ForwardIterator last, + Compare compare={}, Projection projection={}) const + -> cppsort::detail::difference_type_t + { + using difference_type = cppsort::detail::difference_type_t; + auto&& comp = utility::as_function(compare); + auto&& proj = utility::as_function(projection); + + if (first == last || std::next(first) == last) { + return 0; + } + + // Current "direction" of the run: + // -1: we are in a descending run + // 0: initial value, we don't know the direction of the first run yet + // 1: we are in an ascending run + int direction = 0; + + auto current = first; + auto next = std::next(first); + + difference_type count = 0; // Number of "reversals" in the sequence + for (;next != last; ++current, ++next) { + if (comp(proj(*current), proj(*next))) { + if (direction == -1) { + ++count; + } + direction = 1; + } else if (comp(proj(*next), proj(*current))) { + if (direction == 1) { + ++count; + } + direction = -1; + } + // When neighbors compare equivalent, the direction does not change + } + return count; + } + + template + static constexpr auto max_for_size(Integer n) + -> Integer + { + return n < 2 ? 0 : n - 2; + } + }; + } + + inline constexpr sorter_facade reve{}; +} + +#endif // CPPSORT_PROBES_REVE_H_ diff --git a/include/cpp-sort/version.h b/include/cpp-sort/version.h index 0ea4eb92..6536414e 100644 --- a/include/cpp-sort/version.h +++ b/include/cpp-sort/version.h @@ -8,7 +8,7 @@ // Semantic versioning macros #define CPPSORT_VERSION_MAJOR 2 -#define CPPSORT_VERSION_MINOR 1 +#define CPPSORT_VERSION_MINOR 2 #define CPPSORT_VERSION_PATCH 0 #endif // CPPSORT_VERSION_H_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f2f5fc25..5b4cfc2d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,7 +25,7 @@ else() FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2 - GIT_TAG 88abf9bf325c798c33f54f6b9220ef885b267f4f # v3.12.0 + GIT_TAG 8b08d4d79514f45f7e4ce2a607ac9c94e920d1bb # v3.15.3 ) FetchContent_GetProperties(Catch2) if (NOT Catch2_POPULATED) @@ -186,9 +186,11 @@ add_executable(main-tests comparators/case_insensitive_less.cpp comparators/flip_not.cpp comparators/natural_less.cpp + comparators/partial_order.cpp comparators/projection_compare.cpp - comparators/total_less.cpp + comparators/total_order.cpp comparators/transparent_comparators.cpp + comparators/weak_order.cpp # Distributions tests distributions/all_equal.cpp @@ -218,11 +220,13 @@ add_executable(main-tests probes/mono.cpp probes/osc.cpp probes/rem.cpp + probes/reve.cpp probes/runs.cpp probes/spear.cpp probes/sus.cpp probes/relations.cpp probes/every_probe_common.cpp + probes/every_probe_common_extra.cpp probes/every_probe_move_compare_projection.cpp # Sorters tests diff --git a/tests/comparators/partial_order.cpp b/tests/comparators/partial_order.cpp new file mode 100644 index 00000000..e3aad3f2 --- /dev/null +++ b/tests/comparators/partial_order.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE( "Partial ordering of integers", "[comparison]" ) +{ + long array[] = { -52, 1, 4, 32, 9, -9, -8, -7, 123456, -7, 12, -987 }; + + SECTION( "partial_less" ) + { + cppsort::heap_sort(array, cppsort::partial_less); + CHECK( cppsort::utility::is_sorted(array) ); + } + + SECTION( "partial_greater" ) + { + cppsort::heap_sort(array, cppsort::partial_greater); + CHECK( cppsort::utility::is_sorted(array, std::greater{}) ); + } +} + +TEST_CASE( "Partial ordering of floating-point numbers", "[comparison]" ) +{ + constexpr double inf = std::numeric_limits::infinity(); + + // We can't do anything with NaNs in the partial order + double array[] = { +1.0, +inf, -1.0, -2.3, +0.0, -inf, +5.6, -0.0, +inf, -inf }; + + SECTION( "partial_less" ) + { + cppsort::heap_sort(array, cppsort::partial_less); + + CHECK( std::isinf(array[0]) ); + CHECK( std::signbit(array[0]) ); + CHECK( std::isinf(array[1]) ); + CHECK( std::signbit(array[1]) ); + CHECK( std::isinf(array[8]) ); + CHECK( not std::signbit(array[8]) ); + CHECK( std::isinf(array[9]) ); + CHECK( not std::signbit(array[9]) ); + + CHECK( cppsort::utility::is_sorted(array) ); + } + + SECTION( "partial_greater" ) + { + cppsort::heap_sort(array, cppsort::partial_greater); + + CHECK( std::isinf(array[0]) ); + CHECK( not std::signbit(array[0]) ); + CHECK( std::isinf(array[1]) ); + CHECK( not std::signbit(array[1]) ); + CHECK( std::isinf(array[8]) ); + CHECK( std::signbit(array[8]) ); + CHECK( std::isinf(array[9]) ); + CHECK( std::signbit(array[9]) ); + + CHECK( cppsort::utility::is_sorted(array, std::greater{}) ); + } +} + +TEST_CASE( "Partial order customization point", "[comparison]" ) +{ + helpers::totally_comparable ta, tb; + helpers::weakly_comparable wa, wb; + helpers::partially_comparable pa, pb; + + // Ensure that overload resolution is correct + STATIC_CHECK( cppsort::partial_less(ta, tb) == helpers::compare_result::total_less ); + STATIC_CHECK( cppsort::partial_greater(ta, tb) == helpers::compare_result::total_greater ); + STATIC_CHECK( cppsort::partial_less(wa, wb) == helpers::compare_result::weak_less ); + STATIC_CHECK( cppsort::partial_greater(wa, wb) == helpers::compare_result::weak_greater ); + STATIC_CHECK( cppsort::partial_less(pa, pb) == helpers::compare_result::partial_less ); + STATIC_CHECK( cppsort::partial_greater(pa, pb) == helpers::compare_result::partial_greater ); +} diff --git a/tests/comparators/total_less.cpp b/tests/comparators/total_less.cpp deleted file mode 100644 index a661dd0a..00000000 --- a/tests/comparators/total_less.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016-2022 Morwenn - * SPDX-License-Identifier: MIT - */ -#include -#include -#include -#include -#include - -TEST_CASE( "IEEE 754 totalOrder implementation", "[comparison]" ) -{ - static constexpr double nan = std::numeric_limits::quiet_NaN(); - static constexpr double inf = std::numeric_limits::infinity(); - - double array[] = { +1.0, +inf, -1.0, -nan, +0.0, -inf, +nan, -0.0 }; - cppsort::heap_sort(array, cppsort::total_less); - - // Check for IEEE 754 totalOrder, - // ignore quiet vs. signaling NaNs - CHECK( std::isnan(array[0]) ); - CHECK( std::signbit(array[0]) ); - CHECK( std::isinf(array[1]) ); - CHECK( std::signbit(array[1]) ); - CHECK( array[2] == -1.0 ); - CHECK( array[3] == 0.0 ); - CHECK( std::signbit(array[3]) ); - CHECK( array[4] == 0.0 ); - CHECK( not std::signbit(array[4]) ); - CHECK( array[5] == +1.0 ); - CHECK( std::isinf(array[6]) ); - CHECK( not std::signbit(array[6]) ); - CHECK( std::isnan(array[7]) ); - CHECK( not std::signbit(array[7]) ); -} diff --git a/tests/comparators/total_order.cpp b/tests/comparators/total_order.cpp new file mode 100644 index 00000000..2be5c8c3 --- /dev/null +++ b/tests/comparators/total_order.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016-2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE( "Total ordering of integers", "[comparison]" ) +{ + long array[] = { -52, 1, 4, 32, 9, -9, -8, -7, 123456, -7, 12, -987 }; + + SECTION( "total_less" ) + { + cppsort::heap_sort(array, cppsort::total_less); + CHECK( cppsort::utility::is_sorted(array) ); + } + + SECTION( "total_greater" ) + { + cppsort::heap_sort(array, cppsort::total_greater); + CHECK( cppsort::utility::is_sorted(array, std::greater{}) ); + } +} + +TEST_CASE( "IEEE 754 totalOrder implementation", "[comparison]" ) +{ + constexpr double nan = std::numeric_limits::quiet_NaN(); + constexpr double inf = std::numeric_limits::infinity(); + + double array[] = { +1.0, +inf, -1.0, -nan, +0.0, -inf, +nan, -0.0 }; + + SECTION( "total_less" ) + { + cppsort::heap_sort(array, cppsort::total_less); + + // Check for IEEE 754 totalOrder, + // ignore quiet vs. signaling NaNs + CHECK( std::isnan(array[0]) ); + CHECK( std::signbit(array[0]) ); + CHECK( std::isinf(array[1]) ); + CHECK( std::signbit(array[1]) ); + CHECK( array[2] == -1.0 ); + CHECK( array[3] == 0.0 ); + CHECK( std::signbit(array[3]) ); + CHECK( array[4] == 0.0 ); + CHECK( not std::signbit(array[4]) ); + CHECK( array[5] == +1.0 ); + CHECK( std::isinf(array[6]) ); + CHECK( not std::signbit(array[6]) ); + CHECK( std::isnan(array[7]) ); + CHECK( not std::signbit(array[7]) ); + } + + SECTION( "total_greater" ) + { + cppsort::heap_sort(array, cppsort::total_greater); + + // Check for IEEE 754 totalOrder, + // ignore quiet vs. signaling NaNs + CHECK( not std::signbit(array[0]) ); + CHECK( std::isnan(array[0]) ); + CHECK( std::isinf(array[1]) ); + CHECK( not std::signbit(array[1]) ); + CHECK( array[2] == +1.0 ); + CHECK( array[3] == 0.0 ); + CHECK( not std::signbit(array[3]) ); + CHECK( array[4] == 0.0 ); + CHECK( std::signbit(array[4]) ); + CHECK( array[5] == -1.0 ); + CHECK( std::isinf(array[6]) ); + CHECK( std::signbit(array[6]) ); + CHECK( std::isnan(array[7]) ); + CHECK( std::signbit(array[7]) ); + } +} + +TEST_CASE( "Total order customization point", "[comparison]" ) +{ + helpers::totally_comparable ta, tb; + + // Ensure that overload resolution is correct + STATIC_CHECK( cppsort::total_less(ta, tb) == helpers::compare_result::total_less ); + STATIC_CHECK( cppsort::total_greater(ta, tb) == helpers::compare_result::total_greater ); +} diff --git a/tests/comparators/weak_order.cpp b/tests/comparators/weak_order.cpp new file mode 100644 index 00000000..83278b5e --- /dev/null +++ b/tests/comparators/weak_order.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE( "Weak ordering of integers", "[comparison]" ) +{ + long array[] = { -52, 1, 4, 32, 9, -9, -8, -7, 123456, -7, 12, -987 }; + + SECTION( "weak_less" ) + { + cppsort::heap_sort(array, cppsort::weak_less); + CHECK( cppsort::utility::is_sorted(array) ); + } + + SECTION( "weak_greater" ) + { + cppsort::heap_sort(array, cppsort::weak_greater); + CHECK( cppsort::utility::is_sorted(array, std::greater{}) ); + } +} + +TEST_CASE( "Weak ordering of floating-point numbers", "[comparison]" ) +{ + constexpr double inf = std::numeric_limits::infinity(); + + double array[] = { +1.0, +inf, -1.0, -std::nan("2"), +0.0, -inf, +std::nan("1"), -0.0 }; + + SECTION( "weak_less" ) + { + cppsort::heap_sort(array, cppsort::weak_less); + + // Check that equivalent values compare equivalent, + // regardless of their representation + CHECK( std::isnan(array[0]) ); + CHECK( std::signbit(array[0]) ); + CHECK( std::isinf(array[1]) ); + CHECK( std::signbit(array[1]) ); + CHECK( array[2] == -1.0 ); + CHECK( array[3] == 0.0 ); + CHECK( array[4] == 0.0 ); + CHECK( array[5] == +1.0 ); + CHECK( std::isinf(array[6]) ); + CHECK( not std::signbit(array[6]) ); + CHECK( std::isnan(array[7]) ); + CHECK( not std::signbit(array[7]) ); + } + + SECTION( "weak_greater" ) + { + cppsort::heap_sort(array, cppsort::weak_greater); + + // Check that equivalent values compare equivalent, + // regardless of their representation + CHECK( not std::signbit(array[0]) ); + CHECK( std::isnan(array[0]) ); + CHECK( std::isinf(array[1]) ); + CHECK( not std::signbit(array[1]) ); + CHECK( array[2] == +1.0 ); + CHECK( array[3] == 0.0 ); + CHECK( array[4] == 0.0 ); + CHECK( array[5] == -1.0 ); + CHECK( std::isinf(array[6]) ); + CHECK( std::signbit(array[6]) ); + CHECK( std::isnan(array[7]) ); + CHECK( std::signbit(array[7]) ); + } +} + +TEST_CASE( "Weak order customization point", "[comparison]" ) +{ + helpers::totally_comparable ta, tb; + helpers::weakly_comparable wa, wb; + + // Ensure that overload resolution is correct + STATIC_CHECK( cppsort::weak_less(ta, tb) == helpers::compare_result::total_less ); + STATIC_CHECK( cppsort::weak_greater(ta, tb) == helpers::compare_result::total_greater ); + STATIC_CHECK( cppsort::weak_less(wa, wb) == helpers::compare_result::weak_less ); + STATIC_CHECK( cppsort::weak_greater(wa, wb) == helpers::compare_result::weak_greater ); +} diff --git a/tests/probes/amp.cpp b/tests/probes/amp.cpp index 4cebd6ca..1898776e 100644 --- a/tests/probes/amp.cpp +++ b/tests/probes/amp.cpp @@ -1,12 +1,10 @@ /* - * Copyright (c) 2025 Morwenn + * Copyright (c) 2025-2026 Morwenn * SPDX-License-Identifier: MIT */ #include #include #include -#include -#include #include #include #include @@ -36,11 +34,4 @@ TEST_CASE( "measure of disorder: amp", "[probe][amp]" ) CHECK( amp(li) == max_n ); CHECK( amp(li.begin(), li.end()) == max_n ); } - - // https://morwenn.github.io/presortedness/2025/10/18/TSB005-symmetry-of-amp.html - rc::prop("Amp(Reversed(X)) = Amp(X)", [](std::vector sequence) { - auto amp_x = amp(sequence); - std::reverse(sequence.begin(), sequence.end()); - return amp(sequence) == amp_x; - }); } diff --git a/tests/probes/block.cpp b/tests/probes/block.cpp index 05748f04..305d351f 100644 --- a/tests/probes/block.cpp +++ b/tests/probes/block.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2025 Morwenn + * Copyright (c) 2021-2026 Morwenn * SPDX-License-Identifier: MIT */ #include diff --git a/tests/probes/enc.cpp b/tests/probes/enc.cpp index 35625792..f0e5e9d6 100644 --- a/tests/probes/enc.cpp +++ b/tests/probes/enc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #include diff --git a/tests/probes/every_probe_common.cpp b/tests/probes/every_probe_common.cpp index 6c90ff5b..5e7cfc09 100644 --- a/tests/probes/every_probe_common.cpp +++ b/tests/probes/every_probe_common.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2025 Morwenn + * Copyright (c) 2021-2026 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -14,7 +14,10 @@ #include "testing-tools/random.h" // -// Test common properties of measures of presortedness +// Test common properties of measures of presortedness: +// - Basic properties such a checking max_for_size consistency +// - Mannilla 5 axioms for measures of presortedness +// - Estivill-Castro additional properties such as monotonicity // TEMPLATE_TEST_CASE( "test every probe with all_equal distribution", "[probe]", @@ -29,11 +32,12 @@ TEMPLATE_TEST_CASE( "test every probe with all_equal distribution", "[probe]", decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) { - // First property formalized by Mannila + // First axiom formalized by Mannila // Ensure that all measures of disorder return 0 when // given a collection where all elements are equal @@ -55,6 +59,7 @@ TEMPLATE_TEST_CASE( "test every probe with a sorted collection", "[probe]", decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) @@ -82,6 +87,7 @@ TEMPLATE_TEST_CASE( "test every probe with a 0 or 1 element", "[probe]", decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) @@ -121,6 +127,7 @@ TEMPLATE_TEST_CASE( "test order isomorphism for every probe", "[probe]", decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) @@ -177,6 +184,7 @@ TEMPLATE_TEST_CASE( "test M(subsequence(X)) <= M(X) for most probes M", "[probe] decltype(cppsort::probe::max), decltype(cppsort::probe::mono), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) @@ -193,8 +201,9 @@ TEMPLATE_TEST_CASE( "test M(subsequence(X)) <= M(X) for most probes M", "[probe] } TEMPLATE_TEST_CASE( "test M(subsequence(X)) <= M(X) for most probes M (unique elements)", "[probe]", - decltype(cppsort::probe::osc), - decltype(cppsort::probe::block) ) + decltype(cppsort::probe::block), + decltype(cppsort::probe::reve), + decltype(cppsort::probe::osc) ) { // Same as above, but for probes that aren't well-behaved // with regard to equivalent elements @@ -284,7 +293,7 @@ TEMPLATE_TEST_CASE( "test M(2, 1, 4, 3, 6, 5, ...) <= |X| * M(2, 1) / 2 for most { // From *Sorting and Measures of Disorder* by Estivill-Castro: // property derived from Mannila's criteria 2 & 4 - // The following probes don't satisfy it: Amp, Block, Mono, Osc + // The following probes don't satisfy it: Amp, Block, Mono, Osc, Reve int size = 1000; std::vector sequence(size, 0); @@ -309,6 +318,7 @@ TEMPLATE_TEST_CASE( "test M(aX) <= |X| + M(X) for most probes M", "[probe]", decltype(cppsort::probe::max), decltype(cppsort::probe::mono), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::sus) ) { @@ -341,7 +351,7 @@ TEMPLATE_TEST_CASE( "test prefix monotonicity", "[probe]", decltype(cppsort::probe::sus) ) { // Property formalized by Estivill-Castro in *Sorting and Measures of Disorder* - // The following probes don't satisfy it: Amp, Block, Mono, Osc + // The following probes don't satisfy it: Amp, Block, Mono, Osc, Reve // Note: the original paper claims that Osc also satisfies this property, // but it fails for X=⟨3, 0⟩ Y=⟨⟩ Z=⟨4, 2⟩ @@ -441,6 +451,7 @@ TEMPLATE_TEST_CASE( "test that probes never produce more disorder than their the decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) diff --git a/tests/probes/every_probe_common_extra.cpp b/tests/probes/every_probe_common_extra.cpp new file mode 100644 index 00000000..29fb2455 --- /dev/null +++ b/tests/probes/every_probe_common_extra.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include "testing-tools/random.h" + +// +// Test extra commonenough properties of measures of disorder: +// - Whether neighbors that compare equivalent affect the sequence +// - Horizontal symmetry +// + +TEMPLATE_TEST_CASE( "test measures that ignore equal neighbors", "[probe]", + decltype(cppsort::probe::amp), + decltype(cppsort::probe::enc), + decltype(cppsort::probe::mono), + decltype(cppsort::probe::reve), + decltype(cppsort::probe::runs), + decltype(cppsort::probe::sus) ) +{ + // The literature about measures of disorder generally only considers + // sequences of distinct elements. This test checks that some of them + // return the same result with neighboring elements that compare + // equivalent, and when reducing these groups to one element. + + rc::prop("M(Unique(X)) = M(X)", [](std::vector sequence) { + std::decay_t measure; + + auto measure_x = measure(sequence); + sequence.erase( + std::unique(sequence.begin(), sequence.end()), + sequence.end() + ); + return measure(sequence) == measure_x; + }); +} + +TEMPLATE_TEST_CASE( "test measures that find the same disorder in a reversed sequence", "[probe]", + decltype(cppsort::probe::amp), + decltype(cppsort::probe::mono), + decltype(cppsort::probe::osc), + decltype(cppsort::probe::reve) ) +{ + // Several measures of presortedness find as much disorder in a sequence, + // than in the same sequence flipped horizontally (reversed) + // Those tend to violate Mannila's 4th axiom: + // https://morwenn.github.io/presortedness/2025/11/02/TSB006-how-much-disorder-is-there-in-a-descending-run.html + + rc::prop("M(Reversed(X)) = M(X)", [](std::vector sequence) { + std::decay_t measure; + + auto measure_x = measure(sequence); + std::reverse(sequence.begin(), sequence.end()); + return measure(sequence) == measure_x; + }); +} diff --git a/tests/probes/every_probe_heap_memory_exhaustion.cpp b/tests/probes/every_probe_heap_memory_exhaustion.cpp index 436cff71..eb801bca 100644 --- a/tests/probes/every_probe_heap_memory_exhaustion.cpp +++ b/tests/probes/every_probe_heap_memory_exhaustion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Morwenn + * Copyright (c) 2020-2026 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -23,6 +23,7 @@ TEMPLATE_TEST_CASE( "heap exhaustion for random-access probes", "[probe][heap_ex decltype(cppsort::probe::amp), decltype(cppsort::probe::dis), decltype(cppsort::probe::mono), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs) ) { std::vector collection; collection.reserve(491); @@ -42,6 +43,7 @@ TEMPLATE_TEST_CASE( "heap exhaustion for bidirectional probes", "[probe][heap_ex decltype(cppsort::probe::amp), decltype(cppsort::probe::dis), decltype(cppsort::probe::mono), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs) ) { std::list collection; @@ -61,6 +63,7 @@ TEMPLATE_TEST_CASE( "heap exhaustion for forward probes", "[probe][heap_exhausti decltype(cppsort::probe::amp), decltype(cppsort::probe::dis), decltype(cppsort::probe::mono), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs) ) { std::forward_list collection; diff --git a/tests/probes/every_probe_move_compare_projection.cpp b/tests/probes/every_probe_move_compare_projection.cpp index bea72d37..91f89b6d 100644 --- a/tests/probes/every_probe_move_compare_projection.cpp +++ b/tests/probes/every_probe_move_compare_projection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2025 Morwenn + * Copyright (c) 2020-2026 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -10,6 +10,7 @@ #include TEMPLATE_TEST_CASE( "every probe with comparison function altered by move", "[probe]", + decltype(cppsort::probe::amp), decltype(cppsort::probe::block), decltype(cppsort::probe::dis), decltype(cppsort::probe::enc), @@ -20,6 +21,7 @@ TEMPLATE_TEST_CASE( "every probe with comparison function altered by move", "[pr decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) @@ -34,6 +36,7 @@ TEMPLATE_TEST_CASE( "every probe with comparison function altered by move", "[pr } TEMPLATE_TEST_CASE( "every probe with projection function altered by move", "[probe]", + decltype(cppsort::probe::amp), decltype(cppsort::probe::block), decltype(cppsort::probe::dis), decltype(cppsort::probe::enc), @@ -44,6 +47,7 @@ TEMPLATE_TEST_CASE( "every probe with projection function altered by move", "[pr decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), decltype(cppsort::probe::rem), + decltype(cppsort::probe::reve), decltype(cppsort::probe::runs), decltype(cppsort::probe::spear), decltype(cppsort::probe::sus) ) diff --git a/tests/probes/mono.cpp b/tests/probes/mono.cpp index e0825a4f..7f3c1bd4 100644 --- a/tests/probes/mono.cpp +++ b/tests/probes/mono.cpp @@ -1,13 +1,10 @@ /* - * Copyright (c) 2018-2025 Morwenn + * Copyright (c) 2018-2026 Morwenn * SPDX-License-Identifier: MIT */ -#include #include #include #include -#include -#include #include #include #include @@ -55,10 +52,4 @@ TEST_CASE( "measure of disorder: mono", "[probe][mono]" ) const std::forward_list li2 = { 1, 1, 2, 8, 3, 3, 2, 1, 1, 5, 6 }; CHECK( mono(li2) == 2 ); } - - rc::prop("Mono(Reversed(X)) = Mono(X)", [](std::vector sequence) { - auto mono_x = mono(sequence); - std::reverse(sequence.begin(), sequence.end()); - return mono(sequence) == mono_x; - }); } diff --git a/tests/probes/osc.cpp b/tests/probes/osc.cpp index f757650a..4166008b 100644 --- a/tests/probes/osc.cpp +++ b/tests/probes/osc.cpp @@ -1,14 +1,11 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ -#include #include #include #include #include -#include -#include #include #include #include @@ -68,12 +65,6 @@ TEST_CASE( "measure of disorder: osc", "[probe][osc]" ) CHECK( osc(vec, comp, &wrapper::value) == 17 ); } - rc::prop("Osc(Reversed(X)) = Osc(X)", [](std::vector sequence) { - auto osc_x = cppsort::probe::osc(sequence); - std::reverse(sequence.begin(), sequence.end()); - return cppsort::probe::osc(sequence) == osc_x; - }); - SECTION( "Sorting and Measures of Disorder, Theorem 3.18" ) { int size = 1000; diff --git a/tests/probes/relations.cpp b/tests/probes/relations.cpp index f4a14288..0fea763b 100644 --- a/tests/probes/relations.cpp +++ b/tests/probes/relations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -174,25 +174,44 @@ TEST_CASE( "relations between measures of disorder", "[probe]" ) return spear(sequence) <= 2 * inv(sequence); }); - // Original research about Mono + // Parity of permutations + // https://en.wikipedia.org/wiki/Parity_of_a_permutation#Equivalence_of_the_two_definitions - rc::prop("Mono(X) ≤ Runs(X)", [](const std::vector& sequence) { - return mono(sequence) <= runs(sequence); + rc::prop("Inv(X) % 2 = Exc(X) % 2", []() { + auto sequence = *rc::gen::unique>(rc::gen::arbitrary()); + return inv(sequence) % 2 == exc(sequence) % 2; }); - rc::prop("Enc(X) ≤ Mono(X) + 1", [](const std::vector& sequence) { - return enc(sequence) <= mono(sequence) + 1; - }); + // Original research about Amp, Mono and Reve: + // https://morwenn.github.io/presortedness/2025/11/09/TSB007-relationship-between-amp-and-runs.html + // https://morwenn.github.io/presortedness/2026/07/26/TSB012-relationship-between-amp-and-mono.html - // Original research about Amp + rc::prop("Mono(X) ≤ Runs(X)", [](const std::vector& sequence) { + return mono(sequence) <= runs(sequence); + }); - // https://morwenn.github.io/presortedness/2025/11/09/TSB007-relationship-between-amp-and-runs.html rc::prop("Amp(X) ≤ 2 Runs(X)", [](const std::vector& sequence) { return amp(sequence) <= 2 * runs(sequence); }); - // Conjecture rc::prop("Mono(X) ≤ Amp(X)", [](const std::vector& sequence) { return mono(sequence) <= amp(sequence); }); + + rc::prop("Mono(X) ≤ Reve(X)", [](const std::vector& sequence) { + return mono(sequence) <= reve(sequence); + }); + + rc::prop("Reve(X) ≤ 2 Mono(X)", [](const std::vector& sequence) { + return reve(sequence) <= 2 * mono(sequence); + }); + + rc::prop("Reve(X) ≤ Amp(X)", [](const std::vector& sequence) { + return reve(sequence) <= amp(sequence); + }); + + // See "Original research" page in the documentation + rc::prop("Enc(X) ≤ Mono(X) + 1", [](const std::vector& sequence) { + return enc(sequence) <= mono(sequence) + 1; + }); } diff --git a/tests/probes/reve.cpp b/tests/probes/reve.cpp new file mode 100644 index 00000000..339b7be8 --- /dev/null +++ b/tests/probes/reve.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include + +TEST_CASE( "measure of disorder: reve", "[probe][reve]" ) +{ + using cppsort::probe::reve; + + SECTION( "simple test" ) + { + const std::forward_list li = { 48, 43, 96, 44, 42, 34, 42, 57, 68, 69 }; + CHECK( reve(li) == 3 ); + CHECK( reve(li.begin(), li.end()) == 3 ); + + std::vector> tricky(li.begin(), li.end()); + CHECK( reve(tricky, &internal_compare::compare_to) == 3 ); + } + + SECTION( "lower bound" ) + { + const std::forward_list li1 = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + CHECK( reve(li1) == 0 ); + } + + SECTION( "upper bound" ) + { + // The upper bound should correspond to: size - 2 + + const std::forward_list li = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 }; + auto max_n = reve.max_for_size(cppsort::utility::size(li)); + CHECK( max_n == 9 ); + CHECK( reve(li) == max_n ); + CHECK( reve(li.begin(), li.end()) == max_n ); + } + + SECTION( "equal neighbours in the sequence" ) + { + const std::forward_list li = { 0, 0, 0, 1, 2, 3, 4, 6, 5, 3 }; + CHECK( reve(li) == 1 ); + + const std::forward_list li1 = { 6, 5, 4, 3, 2, 2, 2, 2 }; + CHECK( reve(li1) == 0 ); + + const std::forward_list li2 = { 1, 1, 2, 8, 3, 3, 2, 1, 1, 5, 6 }; + CHECK( reve(li2) == 2 ); + } +} diff --git a/tests/probes/runs.cpp b/tests/probes/runs.cpp index 129e8311..5a87b852 100644 --- a/tests/probes/runs.cpp +++ b/tests/probes/runs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2025 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #include diff --git a/tests/probes/sus.cpp b/tests/probes/sus.cpp index d98e60fd..6c61d074 100644 --- a/tests/probes/sus.cpp +++ b/tests/probes/sus.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2025 Morwenn + * Copyright (c) 2021-2026 Morwenn * SPDX-License-Identifier: MIT */ #include diff --git a/tests/testing-tools/comparators.h b/tests/testing-tools/comparators.h new file mode 100644 index 00000000..84e89f04 --- /dev/null +++ b/tests/testing-tools/comparators.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_TESTSUITE_COMPARATORS_H_ +#define CPPSORT_TESTSUITE_COMPARATORS_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +namespace helpers +{ + // Identify the function that was called + enum struct compare_result + { + total_less, + total_greater, + weak_less, + weak_greater, + partial_less, + partial_greater, + }; + + // Collection of empty classes and empty comparison overloads used + // to identify what overload was selected during resolution + + struct totally_comparable + { + friend constexpr auto total_less(const totally_comparable&, + const totally_comparable&) + -> compare_result + { + return compare_result::total_less; + } + }; + + constexpr auto total_greater(const totally_comparable&, + const totally_comparable&) + -> compare_result + { + return compare_result::total_greater; + } + + struct weakly_comparable + { + friend constexpr auto weak_less(const weakly_comparable&, + const weakly_comparable&) + -> compare_result + { + return compare_result::weak_less; + } + }; + + constexpr auto weak_greater(const weakly_comparable&, + const weakly_comparable&) + -> compare_result + { + return compare_result::weak_greater; + } + + struct partially_comparable + { + friend constexpr auto partial_less(const partially_comparable&, + const partially_comparable&) + -> compare_result + { + return compare_result::partial_less; + } + }; + + constexpr auto partial_greater(const partially_comparable&, + const partially_comparable&) + -> compare_result + { + return compare_result::partial_greater; + } +} + +#endif // CPPSORT_TESTSUITE_COMPARATORS_H_ diff --git a/tools/generate_sorting_network.py b/tools/generate_sorting_network.py index 7e909c2b..64c6cceb 100644 --- a/tools/generate_sorting_network.py +++ b/tools/generate_sorting_network.py @@ -1,11 +1,15 @@ # -*- coding: utf-8 -*- +# Copyright (c) 2022-2026 Morwenn +# SPDX-License-Identifier: MIT + import argparse import ast import textwrap from pathlib import Path import z3 +from natsort import natsorted def find_sorter_hunter_file(path: Path, size: int): @@ -14,10 +18,7 @@ def find_sorter_hunter_file(path: Path, size: int): one corresponding for sorting a network of the given size, pereferring the ones that minimize the number of compare-exchanges. """ - return min( - path.glob(f"Sort_{size}_*.json"), - key=lambda x: int(x.name.split('_')[2]) - ) + return natsorted(path.glob(f"Sort_{size}_*.json"))[0] def parse_sorter_hunter_network(path: Path) -> list[list[tuple]]: @@ -68,10 +69,8 @@ def verify_network(pairs: list[tuple]): def generate_cxx(network: list[list[tuple]]): - template = textwrap.dedent(""" - namespace cppsort - {{ - namespace detail + template = textwrap.dedent("""\ + namespace cppsort::detail {{ template<> struct sorting_network_sorter_impl<{nb_inputs}> @@ -101,7 +100,7 @@ def generate_cxx(network: list[list[tuple]]): }}}}; }} }}; - }}}} + }} """) pairs = sum(network, []) @@ -136,6 +135,8 @@ def main(): parser = argparse.ArgumentParser(description="Turn a SorterHunter network into a cpp-sort one") parser.add_argument('-s', '--size', type=int, help="Number of inputs the network should sort") + parser.add_argument('-o', '--output-file', type=str, + help="File where to store the generate C++ code") parser.add_argument('directory', help="Directory containing the SorterHunter networks") args = parser.parse_args() @@ -144,15 +145,19 @@ def main(): network = parse_sorter_hunter_network(path) pairs = sum(network, []) - print(f"Number of pairs: {len(pairs)}") + print(f"Number of pairs: {len(pairs)}\n") valid, failing_input = verify_network(pairs) if not valid: print("Network failed to sort: {failing_input}") exit(1) - print(generate_cxx(network)) - + generated_code = generate_cxx(network) + if args.output_file: + with open(args.output_file, "w", encoding="utf-8") as fd: + fd.write(generated_code) + else: + print(generated_code) if __name__ == '__main__': main() diff --git a/tools/partial-ordering-measures-of-disorder.tex b/tools/partial-ordering-measures-of-disorder.tex index 7d36686c..4d4942da 100644 --- a/tools/partial-ordering-measures-of-disorder.tex +++ b/tools/partial-ordering-measures-of-disorder.tex @@ -1,4 +1,4 @@ -% Copyright (c) 2021-2025 Morwenn +% Copyright (c) 2021-2026 Morwenn % SPDX-License-Identifier: MIT \documentclass{standalone} @@ -65,13 +65,13 @@ \path[-] (runs) edge node {} (m01); \path[-] (m01) edge node {} (m0); - % Sort Race - % by H. Zhang, B. Meng and Y. Liang - \node[state] (mono) [right=of sus] {$\bm{\mathit{Mono}}$}; + % Mono: Sort Race, by H. Zhang, B. Meng and Y. Liang + % Reve: original research + \node[state] (mono) [right=of sus] {$\bm{\mathit{Mono}}\equiv \bm{\mathit{Reve}}$}; % See the Original Research page of the docs \path[-] (enc) edge node {} (mono); - - % Original research: Amp + + % Amp: original research \node[state] (amp) [below=of mono] {$\bm{\mathit{Amp}}$}; \path[-] (amp) edge node {} (runs); % Conjecture diff --git a/tools/release-checklist.md b/tools/release-checklist.md index 4cca2e63..7072cd7b 100644 --- a/tools/release-checklist.md +++ b/tools/release-checklist.md @@ -25,6 +25,7 @@ List of actions to perform when releasing a new cpp-sort version. ### After the release - [ ] Add the Zenodo badge to the release notes. +- [ ] Copy the release to mirrors as needed. - [ ] Close the new version's milestone. - [ ] Check that the documentation was correctly uploaded. - [ ] Add the new version to Conan Center Index.