From 4df410493726d7a41cfcf7e5953fa2be25a6ec99 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 24 Aug 2026 23:33:43 +0200 Subject: [PATCH 01/11] CI: add an empty Forgejo workflow --- .forgejo/workflows/do-nothing.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .forgejo/workflows/do-nothing.yml diff --git a/.forgejo/workflows/do-nothing.yml b/.forgejo/workflows/do-nothing.yml new file mode 100644 index 00000000..f8726764 --- /dev/null +++ b/.forgejo/workflows/do-nothing.yml @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Morwenn +# SPDX-License-Identifier: MIT + +# Codeberg seems to run the repository's GitHub actions when +# there is no Forgejo workflow: the goa of this file is to +# skip that callback +name: Do Nothing + +on: + workflow_dispatch: From 2f049519ffff3ae1a23f5bf715fccb9b6bd8a4d0 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Mon, 24 Aug 2026 23:46:13 +0200 Subject: [PATCH 02/11] CI: upgrade actions/checkout to v7 --- .github/workflows/build-macos.yml | 2 +- .github/workflows/build-mingw.yml | 4 ++-- .github/workflows/build-msvc.yml | 4 ++-- .github/workflows/build-ubuntu.yml | 4 ++-- .github/workflows/code-coverage.yml | 4 ++-- .github/workflows/mirror-to-codeberg.yml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index afc529ac..53ddf4d9 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -40,7 +40,7 @@ jobs: sanitize: address,undefined steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Configure CMake working-directory: ${{runner.workspace}} diff --git a/.github/workflows/build-mingw.yml b/.github/workflows/build-mingw.yml index 8fbf99ac..34f9f2ad 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 85b0bdbb..2201fac1 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 diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 892a51b7..f27b419c 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 - name: Install GCC if: ${{matrix.cxx == 'g++-9'}} diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 76b3f7c5..ea918dff 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 @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout project - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install LCOV run: sudo apt-get install -y lcov 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 From dd34a2d6345ea8f23ef9d2d55c28c79afe01a664 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 25 Jan 2026 23:14:38 +0100 Subject: [PATCH 03/11] Add test section for total_greater --- tests/CMakeLists.txt | 2 +- tests/comparators/total_less.cpp | 35 ----------------- tests/comparators/total_order.cpp | 62 +++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 36 deletions(-) delete mode 100644 tests/comparators/total_less.cpp create mode 100644 tests/comparators/total_order.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 827f1c6e..f1dd138e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -195,7 +195,7 @@ add_executable(main-tests comparators/flip_not.cpp comparators/natural_less.cpp comparators/projection_compare.cpp - comparators/total_less.cpp + comparators/total_order.cpp comparators/transparent_comparators.cpp # Distributions tests 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..21f3d31c --- /dev/null +++ b/tests/comparators/total_order.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016-2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include + +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]) ); + } +} From b5b8b4969525396aedd08528f868268b8044f5e4 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 25 Jan 2026 23:27:45 +0100 Subject: [PATCH 04/11] Use std::strong_order for total_less and total_greater when possible --- docs/Changelog.md | 7 +++++++ docs/Comparators.md | 8 ++++++-- include/cpp-sort/comparators/total_greater.h | 17 ++++++++++++++--- include/cpp-sort/comparators/total_less.h | 18 +++++++++++++++--- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/docs/Changelog.md b/docs/Changelog.md index 8619db38..da7d19c0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -68,6 +68,11 @@ When compiled with C++17, **cpp-sort** might gain a few additional features depe 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. @@ -110,5 +115,7 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe [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-string-view]: https://en.cppreference.com/w/cpp/string/basic_string_view + [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 31673f6e..e8d84681 100644 --- a/docs/Comparators.md +++ b/docs/Comparators.md @@ -11,7 +11,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 @@ -23,7 +23,9 @@ 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]. @@ -31,6 +33,8 @@ Total order comparators are considered as [generating branchless code][branchles *Changed in version 1.13.1:* support for `[un]signed __int128`. +*Changed in version 1.17.3:* `total_less` and `total_greater` now correctly implement IEEE 754 `totalOrder` in C++20 mode, when the feature-test macro `__cpp_lib_three_way_comparison` is defined. + ### Weak order comparators ```cpp diff --git a/include/cpp-sort/comparators/total_greater.h b/include/cpp-sort/comparators/total_greater.h index ffe62a7f..b2f70929 100644 --- a/include/cpp-sort/comparators/total_greater.h +++ b/include/cpp-sort/comparators/total_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2022 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_TOTAL_GREATER_H_ @@ -8,14 +8,19 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#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 @@ -37,6 +42,11 @@ namespace cppsort auto total_greater(T lhs, T rhs) -> detail::enable_if_t::value, 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); @@ -47,6 +57,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 76e9bafb..7ee33b59 100644 --- a/include/cpp-sort/comparators/total_less.h +++ b/include/cpp-sort/comparators/total_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2022 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_TOTAL_LESS_H_ @@ -8,14 +8,20 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include #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 @@ -37,6 +43,11 @@ namespace cppsort auto total_less(T lhs, T rhs) -> detail::enable_if_t::value, 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); @@ -47,6 +58,7 @@ namespace cppsort int lhs_weight = total_weight(lhs); int rhs_weight = total_weight(rhs); return lhs_weight < rhs_weight; +#endif } //////////////////////////////////////////////////////////// From 43faa8e28f078fff7759621b67acdf5c3436d566 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Wed, 11 Feb 2026 23:01:07 +0100 Subject: [PATCH 05/11] Fix weak_less and weak_greater with floating-point numbers They simply never worked for floating-point numbers because the overload was ambiguous. Adding simple tests revealed the bug. --- include/cpp-sort/comparators/weak_greater.h | 7 ++- include/cpp-sort/comparators/weak_less.h | 7 ++- tests/CMakeLists.txt | 1 + tests/comparators/weak_order.cpp | 57 +++++++++++++++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 tests/comparators/weak_order.cpp diff --git a/include/cpp-sort/comparators/weak_greater.h b/include/cpp-sort/comparators/weak_greater.h index 00ed6930..6940a9f1 100644 --- a/include/cpp-sort/comparators/weak_greater.h +++ b/include/cpp-sort/comparators/weak_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_WEAK_GREATER_H_ @@ -43,7 +43,10 @@ namespace cppsort template 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::value, + 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 f4ecda3f..1dcf1cf7 100644 --- a/include/cpp-sort/comparators/weak_less.h +++ b/include/cpp-sort/comparators/weak_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_WEAK_LESS_H_ @@ -43,7 +43,10 @@ namespace cppsort template 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::value, + decltype(cppsort::total_less(lhs, rhs)) + > { return cppsort::total_less(lhs, rhs); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1dd138e..51e12363 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -197,6 +197,7 @@ add_executable(main-tests comparators/projection_compare.cpp comparators/total_order.cpp comparators/transparent_comparators.cpp + comparators/weak_order.cpp # Distributions tests distributions/all_equal.cpp diff --git a/tests/comparators/weak_order.cpp b/tests/comparators/weak_order.cpp new file mode 100644 index 00000000..34dc5043 --- /dev/null +++ b/tests/comparators/weak_order.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include + +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]) ); + } +} From 5a53671872a50d18924c11a1ad018ea4241fe363 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 1 Mar 2026 19:05:58 +0100 Subject: [PATCH 06/11] Mark [weak/partial]_[less/greater] fallbacks constexpr --- .../cpp-sort/comparators/partial_greater.h | 4 +- include/cpp-sort/comparators/partial_less.h | 4 +- include/cpp-sort/comparators/weak_greater.h | 2 +- include/cpp-sort/comparators/weak_less.h | 2 +- tests/CMakeLists.txt | 1 + tests/comparators/partial_order.cpp | 23 +++++ tests/comparators/total_order.cpp | 10 +++ tests/comparators/weak_order.cpp | 13 +++ tests/testing-tools/comparators.h | 83 +++++++++++++++++++ 9 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 tests/comparators/partial_order.cpp create mode 100644 tests/testing-tools/comparators.h diff --git a/include/cpp-sort/comparators/partial_greater.h b/include/cpp-sort/comparators/partial_greater.h index fe5c2bf9..16366c44 100644 --- a/include/cpp-sort/comparators/partial_greater.h +++ b/include/cpp-sort/comparators/partial_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_PARTIAL_GREATER_H_ @@ -33,7 +33,7 @@ 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)) { diff --git a/include/cpp-sort/comparators/partial_less.h b/include/cpp-sort/comparators/partial_less.h index 7bebbaf9..5cfc41c4 100644 --- a/include/cpp-sort/comparators/partial_less.h +++ b/include/cpp-sort/comparators/partial_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021 Morwenn + * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_PARTIAL_LESS_H_ @@ -33,7 +33,7 @@ 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)) { diff --git a/include/cpp-sort/comparators/weak_greater.h b/include/cpp-sort/comparators/weak_greater.h index 6940a9f1..dd9347c4 100644 --- a/include/cpp-sort/comparators/weak_greater.h +++ b/include/cpp-sort/comparators/weak_greater.h @@ -41,7 +41,7 @@ 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))) -> detail::enable_if_t< not std::is_floating_point::value, diff --git a/include/cpp-sort/comparators/weak_less.h b/include/cpp-sort/comparators/weak_less.h index 1dcf1cf7..dc3c8d65 100644 --- a/include/cpp-sort/comparators/weak_less.h +++ b/include/cpp-sort/comparators/weak_less.h @@ -41,7 +41,7 @@ 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))) -> detail::enable_if_t< not std::is_floating_point::value, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 51e12363..631f3d39 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -194,6 +194,7 @@ 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_order.cpp comparators/transparent_comparators.cpp diff --git a/tests/comparators/partial_order.cpp b/tests/comparators/partial_order.cpp new file mode 100644 index 00000000..544786b4 --- /dev/null +++ b/tests/comparators/partial_order.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include + +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_order.cpp b/tests/comparators/total_order.cpp index 21f3d31c..f1aa923b 100644 --- a/tests/comparators/total_order.cpp +++ b/tests/comparators/total_order.cpp @@ -8,6 +8,7 @@ #include #include #include +#include TEST_CASE( "IEEE 754 totalOrder implementation", "[comparison]" ) { @@ -60,3 +61,12 @@ TEST_CASE( "IEEE 754 totalOrder implementation", "[comparison]" ) 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 index 34dc5043..7e529c54 100644 --- a/tests/comparators/weak_order.cpp +++ b/tests/comparators/weak_order.cpp @@ -8,6 +8,7 @@ #include #include #include +#include TEST_CASE( "Weak ordering of floating-point numbers", "[comparison]" ) { @@ -55,3 +56,15 @@ TEST_CASE( "Weak ordering of floating-point numbers", "[comparison]" ) 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/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_ From 9951f1b37dc007a93d369d049a4a75ac812a95fe Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 1 Mar 2026 20:47:16 +0100 Subject: [PATCH 07/11] Fix partial_less and partial_greater with floating-point numbers They simply never worked for floating-point numbers because the overload was ambiguous. Adding simple tests revealed the bug. --- .../cpp-sort/comparators/partial_greater.h | 5 +- include/cpp-sort/comparators/partial_less.h | 5 +- tests/comparators/partial_order.cpp | 46 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/cpp-sort/comparators/partial_greater.h b/include/cpp-sort/comparators/partial_greater.h index 16366c44..972d8e0e 100644 --- a/include/cpp-sort/comparators/partial_greater.h +++ b/include/cpp-sort/comparators/partial_greater.h @@ -35,7 +35,10 @@ namespace cppsort template 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::value, + 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 5cfc41c4..77c1c405 100644 --- a/include/cpp-sort/comparators/partial_less.h +++ b/include/cpp-sort/comparators/partial_less.h @@ -35,7 +35,10 @@ namespace cppsort template 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::value, + decltype(cppsort::weak_less(lhs, rhs)) + > { return cppsort::weak_less(lhs, rhs); } diff --git a/tests/comparators/partial_order.cpp b/tests/comparators/partial_order.cpp index 544786b4..b70c9259 100644 --- a/tests/comparators/partial_order.cpp +++ b/tests/comparators/partial_order.cpp @@ -2,11 +2,57 @@ * Copyright (c) 2026 Morwenn * SPDX-License-Identifier: MIT */ +#include +#include +#include +#include +#include #include #include #include +#include #include +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( std::is_sorted(std::begin(array), std::end(array)) ); + } + + SECTION( "weak_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( std::is_sorted(std::begin(array), std::end(array), std::greater<>{}) ); + } +} + TEST_CASE( "Partial order customization point", "[comparison]" ) { helpers::totally_comparable ta, tb; From 8eab02525bc86500a9655f8bb5c42d134c18834f Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sat, 1 Aug 2026 22:54:58 +0200 Subject: [PATCH 08/11] Test partial/weak/greater comparators over integers --- tests/comparators/partial_order.cpp | 19 ++++++++++++++++++- tests/comparators/total_order.cpp | 19 +++++++++++++++++++ tests/comparators/weak_order.cpp | 19 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/comparators/partial_order.cpp b/tests/comparators/partial_order.cpp index b70c9259..76f94d59 100644 --- a/tests/comparators/partial_order.cpp +++ b/tests/comparators/partial_order.cpp @@ -13,6 +13,23 @@ #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( std::is_sorted(std::begin(array), std::end(array)) ); + } + + SECTION( "partial_greater" ) + { + cppsort::heap_sort(array, cppsort::partial_greater); + CHECK( std::is_sorted(std::begin(array), std::end(array), std::greater<>{}) ); + } +} + TEST_CASE( "Partial ordering of floating-point numbers", "[comparison]" ) { constexpr double inf = std::numeric_limits::infinity(); @@ -36,7 +53,7 @@ TEST_CASE( "Partial ordering of floating-point numbers", "[comparison]" ) CHECK( std::is_sorted(std::begin(array), std::end(array)) ); } - SECTION( "weak_greater" ) + SECTION( "partial_greater" ) { cppsort::heap_sort(array, cppsort::partial_greater); diff --git a/tests/comparators/total_order.cpp b/tests/comparators/total_order.cpp index f1aa923b..99af3bdf 100644 --- a/tests/comparators/total_order.cpp +++ b/tests/comparators/total_order.cpp @@ -2,7 +2,9 @@ * Copyright (c) 2016-2026 Morwenn * SPDX-License-Identifier: MIT */ +#include #include +#include #include #include #include @@ -10,6 +12,23 @@ #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( std::is_sorted(std::begin(array), std::end(array)) ); + } + + SECTION( "total_greater" ) + { + cppsort::heap_sort(array, cppsort::total_greater); + CHECK( std::is_sorted(std::begin(array), std::end(array), std::greater<>{}) ); + } +} + TEST_CASE( "IEEE 754 totalOrder implementation", "[comparison]" ) { constexpr double nan = std::numeric_limits::quiet_NaN(); diff --git a/tests/comparators/weak_order.cpp b/tests/comparators/weak_order.cpp index 7e529c54..766a93d0 100644 --- a/tests/comparators/weak_order.cpp +++ b/tests/comparators/weak_order.cpp @@ -2,7 +2,9 @@ * Copyright (c) 2026 Morwenn * SPDX-License-Identifier: MIT */ +#include #include +#include #include #include #include @@ -10,6 +12,23 @@ #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( std::is_sorted(std::begin(array), std::end(array)) ); + } + + SECTION( "weak_greater" ) + { + cppsort::heap_sort(array, cppsort::weak_greater); + CHECK( std::is_sorted(std::begin(array), std::end(array), std::greater<>{}) ); + } +} + TEST_CASE( "Weak ordering of floating-point numbers", "[comparison]" ) { constexpr double inf = std::numeric_limits::infinity(); From b50cd4c3291ca95380de80d3641482b1c83cd8c1 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 23 Aug 2026 14:10:21 +0200 Subject: [PATCH 09/11] CI: get rid of CMake warning in MSVC builds --- .github/workflows/build-msvc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-msvc.yml b/.github/workflows/build-msvc.yml index 2201fac1..d586d035 100644 --- a/.github/workflows/build-msvc.yml +++ b/.github/workflows/build-msvc.yml @@ -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_EXAMPLES=ON From c631df516fe8ec7c70b37c0d467b6c1f6c3ab666 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Fri, 23 Jan 2026 20:19:09 +0100 Subject: [PATCH 10/11] Smaller sorting network for 26 inputs --- docs/Fixed-size-sorters.md | 2 +- .../cpp-sort/detail/sorting_network/sort26.h | 228 +++++++++--------- tools/generate_sorting_network.py | 9 +- 3 files changed, 120 insertions(+), 119 deletions(-) diff --git a/docs/Fixed-size-sorters.md b/docs/Fixed-size-sorters.md index dd9ce78d..5c313fd1 100644 --- a/docs/Fixed-size-sorters.md +++ b/docs/Fixed-size-sorters.md @@ -153,7 +153,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/include/cpp-sort/detail/sorting_network/sort26.h b/include/cpp-sort/detail/sorting_network/sort26.h index 3b49b6d9..0d0481bf 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-2022 Morwenn + * Copyright (c) 2015-2026 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT26_H_ @@ -26,132 +26,131 @@ namespace 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); @@ -168,22 +167,23 @@ namespace detail template CPPSORT_ATTRIBUTE_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/tools/generate_sorting_network.py b/tools/generate_sorting_network.py index 88f8ba88..f133387e 100644 --- a/tools/generate_sorting_network.py +++ b/tools/generate_sorting_network.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- +# Copyright (c) 2022-2026 Morwenn +# SPDX-License-Identifier: MIT + import argparse import ast import textwrap @@ -69,9 +72,7 @@ def verify_network(pairs: list[tuple]): def generate_cxx(network: list[list[tuple]]): template = textwrap.dedent(""" - namespace cppsort - {{ - namespace detail + namespace cppsort::detail {{ template<> struct sorting_network_sorter_impl<{nb_inputs}> @@ -101,7 +102,7 @@ def generate_cxx(network: list[list[tuple]]): }}}}; }} }}; - }}}} + }} """) pairs = sum(network, []) From 0523fd6b91db0981cfb1789e28a9ea6be86c0209 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Tue, 25 Aug 2026 00:51:55 +0200 Subject: [PATCH 11/11] Preparing release 1.17.3 --- CMakeLists.txt | 2 +- README.md | 4 ++-- conanfile.py | 2 +- docs/Home.md | 2 +- docs/Tooling.md | 4 ++-- include/cpp-sort/version.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7f3a45b..08813f71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.8.0) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -project(cpp-sort VERSION 1.17.2 LANGUAGES CXX) +project(cpp-sort VERSION 1.17.3 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(cpp-sort-utils) diff --git a/README.md b/README.md index 4385e4d6..e9c5efc0 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-1.17.2-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.17.2) -[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.17.2-blue.svg)](https://conan.io/center/recipes/cpp-sort?version=1.17.2) +[![Latest Release](https://img.shields.io/badge/release-1.17.3-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.17.3) +[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.17.3-blue.svg)](https://conan.io/center/recipes/cpp-sort?version=1.17.3) [![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/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 40320a6e..314055b8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -16,7 +16,7 @@ class CppSortConan(ConanFile): name = "cpp-sort" - version = "1.17.2" + version = "1.17.3" description = "Sorting algorithms & related tools" license = "MIT" url = "https://github.com/Morwenn/cpp-sort" diff --git a/docs/Home.md b/docs/Home.md index 6258a6a3..094349af 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 1.17.2** documentation! +Welcome to the **cpp-sort 1.17.3** 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/Tooling.md b/docs/Tooling.md index bc7c46e3..c8d336ad 100644 --- a/docs/Tooling.md +++ b/docs/Tooling.md @@ -63,10 +63,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 1.17.2): +And then install any version to your local cache as follows (here with version 1.17.3): ```sh -conan install --requires=cpp-sort/1.17.2 +conan install --requires=cpp-sort/1.17.3 ``` 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/include/cpp-sort/version.h b/include/cpp-sort/version.h index b69d9ce3..c002dc2b 100644 --- a/include/cpp-sort/version.h +++ b/include/cpp-sort/version.h @@ -9,6 +9,6 @@ #define CPPSORT_VERSION_MAJOR 1 #define CPPSORT_VERSION_MINOR 17 -#define CPPSORT_VERSION_PATCH 2 +#define CPPSORT_VERSION_PATCH 3 #endif // CPPSORT_VERSION_H_