├── docs ├── .redirects.gollum ├── images │ ├── stable_t.png │ ├── stable_adapter.png │ ├── sorting-network-23.png │ ├── sorting-network-24.png │ ├── sorting-network-29.png │ ├── pairwise-order-shadow.png │ ├── merge-exchange-network-8.png │ ├── odd-even-merge-network-8.png │ └── partial-ordering-measures-of-disorder.png ├── _Sidebar.md └── Chainable-projections.md ├── cmake ├── cpp-sort-config.cmake.in ├── cpp-sort-utils.cmake └── llvm-cov-wrapper ├── .gitignore ├── .clang-tidy ├── include └── cpp-sort │ ├── version.h │ ├── metrics.h │ ├── fixed_sorters.h │ ├── detail │ ├── front_inserter │ │ ├── inserter0.h │ │ ├── inserter1.h │ │ ├── inserter2.h │ │ ├── inserter3.h │ │ ├── inserter4.h │ │ ├── inserter5.h │ │ ├── inserter6.h │ │ ├── inserter7.h │ │ ├── inserter8.h │ │ └── inserter9.h │ ├── iter_sort3.h │ ├── selection_sort.h │ ├── memcpy_cast.h │ ├── low_comparisons │ │ ├── sort2.h │ │ ├── sort4.h │ │ ├── sort8.h │ │ ├── sort10.h │ │ ├── sort11.h │ │ ├── sort7.h │ │ └── sort3.h │ ├── rotate_right.h │ ├── rotate_left.h │ ├── is_p_sorted.h │ ├── low_moves │ │ ├── sort2.h │ │ ├── sort4.h │ │ └── sort3.h │ ├── bubble_sort.h │ ├── is_sorted_until.h │ ├── stable_adapter_hybrid_adapter.h │ ├── sorting_network │ │ ├── sort4.h │ │ ├── sort2.h │ │ ├── sort3.h │ │ ├── sort5.h │ │ └── sort6.h │ ├── upper_bound.h │ ├── min_element.h │ ├── checkers.h │ ├── front_insert.h │ ├── iterator_traits.h │ ├── empty_sorter.h │ ├── binary_tree.h │ ├── sized_range.h │ ├── comparison_counter.h │ ├── spreadsort │ │ ├── detail │ │ │ └── constants.h │ │ └── float_sort.h │ └── equal_range.h │ ├── probes.h │ ├── adapters.h │ ├── sorters │ └── spread_sorter.h │ ├── sorters.h │ ├── utility │ ├── quicksort_adversary.h │ ├── as_function.h │ ├── size.h │ └── apply_permutation.h │ ├── refined.h │ ├── probes │ └── sus.h │ └── metrics │ └── running_time.h ├── test_package ├── CMakeLists.txt ├── cpp-sort-integrity.cpp └── conanfile.py ├── tests ├── testing-tools │ ├── memory_exhaustion.h │ ├── catch_rng_seed.h │ ├── random.cpp │ ├── span.h │ ├── algorithm.h │ └── internal_compare.h ├── comparators │ ├── natural_less.cpp │ └── total_less.cpp ├── sorters │ ├── poplar_sorter.cpp │ ├── spin_sorter.cpp │ ├── merge_insertion_sorter_projection.cpp │ ├── every_sorter_internal_compare.cpp │ ├── every_sorter_non_const_compare.cpp │ └── every_sorter_move_only.cpp ├── utility │ ├── as_comparison.cpp │ ├── metric_tools.cpp │ ├── buffer.cpp │ └── sorting_networks.cpp ├── probes │ ├── sus.cpp │ ├── block.cpp │ ├── rem.cpp │ ├── inv.cpp │ ├── runs.cpp │ ├── amp.cpp │ ├── max.cpp │ ├── enc.cpp │ ├── ham.cpp │ └── mono.cpp ├── adapters │ ├── every_adapter_tricky_difference_type.cpp │ ├── hybrid_adapter_many_sorters.cpp │ └── container_aware_adapter.cpp ├── metrics │ └── running_time.cpp └── distributions │ ├── descending.cpp │ ├── pipe_organ.cpp │ ├── push_front.cpp │ ├── alternating.cpp │ ├── push_middle.cpp │ ├── all_equal.cpp │ ├── shuffled_16_values.cpp │ └── ascending.cpp ├── examples ├── CMakeLists.txt ├── readme_example.cpp └── list_selection_sorter.cpp ├── .github ├── .codecov.yml └── workflows │ ├── mirror-to-codeberg.yml │ ├── build-mingw.yml │ ├── deploy-to-wiki.yml │ ├── build-msvc.yml │ ├── build-macos.yml │ └── code-coverage.yml ├── benchmarks ├── benchmarking-tools │ ├── statistics.h │ ├── filesystem.h │ ├── rdtsc.h │ └── cpu_cycles.h ├── small-array │ └── plot.py └── disorder │ ├── plot-distribution.py │ └── check-distribution.cpp ├── tools ├── stable_adapter.dot ├── release_template.md ├── release-checklist.md ├── stable_t.dot ├── sorting-network.tex ├── sorting-network-23.tex └── sorting-network-24.tex ├── LICENSE.txt └── conanfile.py /docs/.redirects.gollum: -------------------------------------------------------------------------------- 1 | --- 2 | Measures-of-presortedness.md: Measures-of-disorder.md 3 | -------------------------------------------------------------------------------- /docs/images/stable_t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/stable_t.png -------------------------------------------------------------------------------- /docs/images/stable_adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/stable_adapter.png -------------------------------------------------------------------------------- /docs/images/sorting-network-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/sorting-network-23.png -------------------------------------------------------------------------------- /docs/images/sorting-network-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/sorting-network-24.png -------------------------------------------------------------------------------- /docs/images/sorting-network-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/sorting-network-29.png -------------------------------------------------------------------------------- /docs/images/pairwise-order-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/pairwise-order-shadow.png -------------------------------------------------------------------------------- /docs/images/merge-exchange-network-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/merge-exchange-network-8.png -------------------------------------------------------------------------------- /docs/images/odd-even-merge-network-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/odd-even-merge-network-8.png -------------------------------------------------------------------------------- /docs/images/partial-ordering-measures-of-disorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morwenn/cpp-sort/HEAD/docs/images/partial-ordering-measures-of-disorder.png -------------------------------------------------------------------------------- /cmake/cpp-sort-config.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2020 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | @PACKAGE_INIT@ 5 | 6 | if (NOT TARGET cpp-sort::cpp-sort) 7 | include(${CMAKE_CURRENT_LIST_DIR}/cpp-sort-targets.cmake) 8 | endif() 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-2022 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | # Usual build directory 5 | build* 6 | 7 | # Benchmark results directories 8 | results 9 | 10 | # Files generated by project scripts 11 | *.csv 12 | tools/*.pdf 13 | tools/*.txt 14 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | # Disabled: 5 | # -modernize-avoid-c-arrays: C arrays are cool enough 6 | Checks: > 7 | -*, 8 | bugprone-*, 9 | clang-analyzer-*, 10 | modernize-*, 11 | performance-*, 12 | portability-*, 13 | -modernize-avoid-c-arrays 14 | -------------------------------------------------------------------------------- /include/cpp-sort/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_VERSION_H_ 6 | #define CPPSORT_VERSION_H_ 7 | 8 | // Semantic versioning macros 9 | 10 | #define CPPSORT_VERSION_MAJOR 2 11 | #define CPPSORT_VERSION_MINOR 0 12 | #define CPPSORT_VERSION_PATCH 0 13 | 14 | #endif // CPPSORT_VERSION_H_ 15 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | cmake_minimum_required(VERSION 3.5.0) 5 | 6 | project(test_package LANGUAGES CXX) 7 | 8 | find_package(cpp-sort REQUIRED CONFIG) 9 | 10 | add_executable(${PROJECT_NAME} cpp-sort-integrity.cpp) 11 | target_link_libraries(${PROJECT_NAME} PRIVATE cpp-sort::cpp-sort) 12 | -------------------------------------------------------------------------------- /include/cpp-sort/metrics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_METRICS_H_ 6 | #define CPPSORT_METRICS_H_ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #endif // CPPSORT_METRICS_H_ 16 | -------------------------------------------------------------------------------- /tests/testing-tools/memory_exhaustion.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2021 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_TESTSUITE_MEMORY_EXHAUSTION_H_ 6 | #define CPPSORT_TESTSUITE_MEMORY_EXHAUSTION_H_ 7 | 8 | // Class to make memory exhaustion fail in the current scope 9 | struct scoped_memory_exhaustion 10 | { 11 | scoped_memory_exhaustion() noexcept; 12 | ~scoped_memory_exhaustion(); 13 | }; 14 | 15 | #endif // CPPSORT_TESTSUITE_MEMORY_EXHAUSTION_H_ 16 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2022 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | include(cpp-sort-utils) 5 | 6 | # Quick & dirty script to compile the examples 7 | foreach(filename bubble_sorter.cpp list_selection_sorter.cpp randomizing_adapter.cpp readme_example.cpp) 8 | get_filename_component(name ${filename} NAME_WE) 9 | add_executable(${name} ${filename}) 10 | target_link_libraries(${name} PRIVATE cpp-sort::cpp-sort) 11 | cppsort_add_warnings(${name}) 12 | endforeach() 13 | -------------------------------------------------------------------------------- /tests/testing-tools/catch_rng_seed.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_TESTSUITE_CATCH_RNG_SEED_H_ 6 | #define CPPSORT_TESTSUITE_CATCH_RNG_SEED_H_ 7 | 8 | namespace Catch 9 | { 10 | // This functions is only available in an internal header that 11 | // drags a lot of dependencies, it's cheaper to just declare 12 | // it ourselves in this wrapper 13 | unsigned int rngSeed(); 14 | } 15 | 16 | #endif // CPPSORT_TESTSUITE_CATCH_RNG_SEED_H_ 17 | -------------------------------------------------------------------------------- /test_package/cpp-sort-integrity.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | std::array arr = { 5, 8, 3, 2, 9 }; 14 | cppsort::smooth_sort(arr); 15 | assert(std::is_sorted(arr.begin(), arr.end())); 16 | 17 | // prints 2 3 5 8 9 18 | for (int val: arr) { 19 | std::cout << val << ' '; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/.codecov.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2022 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | coverage: 5 | ignore: 6 | - "tests" 7 | # This unrolled version of a merge-insertion sort derivative was tested 8 | # exhaustively for every permutation of an integer sequence of 9 elements, 9 | # thus we don't include it 10 | - "include/cpp-sort/detail/low_comparisons/sort9.h" 11 | # Sorting networks have been tested independently with the 0-1 principle 12 | # when needed, so we do not need to test them exhaustively in the test 13 | # suite 14 | - "include/cpp-sort/detail/sorting_network/*" 15 | -------------------------------------------------------------------------------- /tests/testing-tools/random.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include "catch_rng_seed.h" 7 | #include "random.h" 8 | 9 | namespace hasard 10 | { 11 | auto engine() 12 | -> xoshiro256ss& 13 | { 14 | thread_local xoshiro256ss res(Catch::rngSeed()); 15 | return res; 16 | } 17 | 18 | auto bit_gen() 19 | -> rand_bit_generator& 20 | { 21 | thread_local rand_bit_generator res{ 22 | xoshiro256ss(Catch::rngSeed()) 23 | }; 24 | return res; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /include/cpp-sort/fixed_sorters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2021 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_FIXED_SORTERS_H_ 6 | #define CPPSORT_FIXED_SORTERS_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #endif // CPPSORT_FIXED_SORTERS_H_ 18 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter0.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER0_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER0_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct front_inserter_n<0u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare, 16 | typename Projection 17 | > 18 | auto operator()(RandomAccessIterator, Compare, Projection) const 19 | -> void 20 | {} 21 | }; 22 | } 23 | 24 | #endif // CPPSORT_DETAIL_FRONT_INSERTER0_H_ 25 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER1_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER1_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct front_inserter_n<1u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare, 16 | typename Projection 17 | > 18 | auto operator()(RandomAccessIterator, Compare, Projection) const 19 | -> void 20 | {} 21 | }; 22 | } 23 | 24 | #endif // CPPSORT_DETAIL_FRONT_INSERTER1_H_ 25 | -------------------------------------------------------------------------------- /.github/workflows/mirror-to-codeberg.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | name: Mirror Commits to Codeberg 5 | 6 | on: [push, workflow_dispatch] 7 | 8 | jobs: 9 | mirror-to-codeberg: 10 | name: Mirror to Codeberg 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v5 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Mirror 21 | uses: yesolutions/mirror-action@v0.7.0 22 | with: 23 | REMOTE: 'https://codeberg.org/Morwenn/cpp-sort.git' 24 | GIT_USERNAME: Morwenn 25 | GIT_PASSWORD: ${{ secrets.GIT_PASSWORD }} 26 | -------------------------------------------------------------------------------- /benchmarks/benchmarking-tools/statistics.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | 7 | // Simple statistics functions 8 | 9 | template 10 | auto average(const Range& values) 11 | -> double 12 | { 13 | double avg = 0.0; 14 | for (auto value : values) { 15 | avg += value / double(values.size()); 16 | } 17 | return avg; 18 | } 19 | 20 | template 21 | auto standard_deviation(const Range& values, double avg) 22 | -> double 23 | { 24 | double stddev = 0.0; 25 | for (auto value : values) { 26 | stddev += (value - avg) * (value - avg) / double(values.size()); 27 | } 28 | return std::sqrt(stddev); 29 | } 30 | -------------------------------------------------------------------------------- /cmake/cpp-sort-utils.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2023 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | # Add a selection of warnings to a target 5 | macro(cppsort_add_warnings target) 6 | if (MSVC) 7 | target_compile_options(${target} PRIVATE /W2) 8 | else() 9 | target_compile_options(${target} PRIVATE 10 | -Wall -Wextra -Wcast-align -Wmissing-declarations -Wmissing-include-dirs 11 | -Wnon-virtual-dtor -Wodr -Wpedantic -Wredundant-decls -Wundef -Wunreachable-code 12 | $<$:-Wlogical-op -Wuseless-cast -Wzero-as-null-pointer-constant> 13 | # The warning when initializing an std::array is just too much of a bother 14 | $<$:-Wno-missing-braces> 15 | ) 16 | endif() 17 | endmacro() 18 | -------------------------------------------------------------------------------- /benchmarks/benchmarking-tools/filesystem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | // Simple filesystem functions 10 | 11 | auto safe_file_name(std::string filename) 12 | -> std::string 13 | { 14 | char invalid_characters[] = {'/', '\\', ':', '*', '?', '"', '<', '>', '|'}; 15 | 16 | // Replace characters to make a filename usable 17 | for (char& character : filename) { 18 | auto found_it = std::find(std::begin(invalid_characters), 19 | std::end(invalid_characters), 20 | character); 21 | if (found_it != std::end(invalid_characters)) { 22 | character = '_'; 23 | } 24 | } 25 | 26 | return filename; 27 | } 28 | -------------------------------------------------------------------------------- /tools/stable_adapter.dot: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Morwenn 2 | // SPDX-License-Identifier: MIT 3 | 4 | digraph G { 5 | 6 | // Nodes 7 | node [fontname="consolas"]; 8 | sorter[label="Sorter"] 9 | stable_adapter[label="stable_adapter"] 10 | make_stable[label="make_stable"] 11 | use_specialization[label="Use specialization"] 12 | node [shape="diamond"] 13 | is_stable_v[label="is_stable"] 14 | specialized[label="Specialized for Sorter?"] 15 | 16 | // Flow 17 | stable_adapter -> specialized 18 | specialized -> use_specialization[label="true",fontname="consolas",fontsize="10"] 19 | specialized -> is_stable_v[label="false",fontname="consolas",fontsize="10"] 20 | is_stable_v -> sorter[label="true",fontname="consolas",fontsize="10"] 21 | is_stable_v -> make_stable[label="false",fontname="consolas",fontsize="10"] 22 | } 23 | -------------------------------------------------------------------------------- /include/cpp-sort/probes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_PROBES_H_ 6 | #define CPPSORT_PROBES_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #endif // CPPSORT_PROBES_H_ 27 | -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (c) 2018-2023 Morwenn 5 | # SPDX-License-Identifier: MIT 6 | 7 | import os.path 8 | 9 | from conan import ConanFile 10 | from conan.tools.cmake import CMake, cmake_layout 11 | from conan.tools.build import can_run 12 | 13 | 14 | class CppsortTestConan(ConanFile): 15 | settings = "os", "compiler", "build_type", "arch" 16 | generators = "CMakeDeps", "CMakeToolchain" 17 | 18 | def requirements(self): 19 | self.requires(self.tested_reference_str) 20 | 21 | def build(self): 22 | cmake = CMake(self) 23 | cmake.configure() 24 | cmake.build() 25 | 26 | def layout(self): 27 | cmake_layout(self) 28 | 29 | def test(self): 30 | if can_run(self): 31 | cmd = os.path.join(self.cpp.build.bindir, "test_package") 32 | self.run(cmd, env="conanrun") 33 | -------------------------------------------------------------------------------- /include/cpp-sort/adapters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_ADAPTERS_H_ 6 | #define CPPSORT_ADAPTERS_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #endif // CPPSORT_ADAPTERS_H_ 24 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/iter_sort3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_ITER_SORT3_H_ 6 | #define CPPSORT_DETAIL_ITER_SORT3_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "swap_if.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template 17 | auto iter_sort3(Iterator a, Iterator b, Iterator c, 18 | Compare compare, Projection projection) 19 | -> Iterator 20 | { 21 | iter_swap_if(b, c, compare, projection); 22 | iter_swap_if(a, c, compare, projection); 23 | iter_swap_if(a, b, std::move(compare), std::move(projection)); 24 | return b; // Return median of 3 25 | } 26 | } 27 | 28 | #endif // CPPSORT_DETAIL_ITER_SORT3_H_ 29 | -------------------------------------------------------------------------------- /tests/comparators/natural_less.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE( "string natural sort with natural_less", "[comparison]" ) 12 | { 13 | std::array array = { 14 | "Yay", 15 | "Yay 32 lol", 16 | "Yuy 32 lol", 17 | "Yay 045", 18 | "Yay 01245 huhuhu", 19 | "Yay 45", 20 | "Yay 1234" 21 | }; 22 | cppsort::heap_sort(array, cppsort::natural_less); 23 | 24 | std::array expected = { 25 | "Yay", 26 | "Yay 32 lol", 27 | "Yay 45", 28 | "Yay 045", 29 | "Yay 1234", 30 | "Yay 01245 huhuhu", 31 | "Yuy 32 lol" 32 | }; 33 | CHECK( array == expected ); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/selection_sort.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SELECTION_SORT_H_ 6 | #define CPPSORT_DETAIL_SELECTION_SORT_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "min_element.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template 17 | auto selection_sort(ForwardIterator first, ForwardIterator last, 18 | Compare compare, Projection projection) 19 | -> void 20 | { 21 | for (ForwardIterator it = first ; it != last ; ++it) { 22 | using utility::iter_swap; 23 | iter_swap(it, unchecked_min_element(it, last, compare, projection)); 24 | } 25 | } 26 | } 27 | 28 | #endif // CPPSORT_DETAIL_SELECTION_SORT_H_ 29 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/memcpy_cast.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_MEMCPY_CAST_H_ 6 | #define CPPSORT_DETAIL_MEMCPY_CAST_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cppsort::detail 16 | { 17 | template 18 | auto memcpy_cast(const From& value) 19 | -> To 20 | { 21 | static_assert(std::is_trivially_copyable_v); 22 | static_assert(std::is_trivially_copyable_v); 23 | static_assert(sizeof(From) == sizeof(To)); 24 | 25 | To result; 26 | std::memcpy(std::addressof(result), 27 | std::addressof(value), 28 | sizeof(From)); 29 | return result; 30 | } 31 | } 32 | 33 | #endif // CPPSORT_DETAIL_MEMCPY_CAST_H_ 34 | -------------------------------------------------------------------------------- /benchmarks/small-array/plot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2015-2022 Morwenn 4 | # SPDX-License-Identifier: MIT 5 | 6 | import sys 7 | from matplotlib import pyplot 8 | 9 | 10 | if __name__ == '__main__': 11 | # Name and results of timing functions 12 | names = [] 13 | values = [] 14 | 15 | # Fetch the results 16 | with open(sys.argv[1]) as fd: 17 | for line in fd: 18 | name, *results = line.rstrip().rstrip(',').split(',') 19 | # Get sorter name 20 | names.append(name) 21 | # Plot the results 22 | results = list(map(int, results)) 23 | xaxis = list(range(1, len(results) + 1)) 24 | val, = pyplot.plot(xaxis, results) 25 | values.append(val) 26 | 27 | # Add a legend 28 | pyplot.legend(values, names) 29 | pyplot.title("Sorting std::array") 30 | pyplot.xlabel("Number of elements to sort") 31 | pyplot.ylabel("Cycles per element (lower is better)") 32 | pyplot.show() 33 | -------------------------------------------------------------------------------- /tests/sorters/poplar_sorter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE( "poplar_sorter tests", "[poplar_sorter]" ) 13 | { 14 | // Distribution used to generate the data to sort 15 | auto distribution = dist::shuffled{}; 16 | 17 | SECTION( "tricky test case 131073" ) 18 | { 19 | // With signed 32-bit difference_type for an iterator, 20 | // sorting a collection of 131073 elements triggered a 21 | // problem with the underlying bit tricks that eventually 22 | // made the algorithm fail 23 | 24 | auto size = 131073; // This part matters the most 25 | std::vector vec; vec.reserve(size); 26 | distribution(std::back_inserter(vec), size, -1568); 27 | cppsort::poplar_sort(vec); 28 | CHECK( std::is_sorted(vec.begin(), vec.end()) ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT2_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT2_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct low_comparisons_sorter_impl<2u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | iter_swap_if(first, first + 1u, 26 | std::move(compare), std::move(projection)); 27 | } 28 | }; 29 | } 30 | 31 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT2_H_ 32 | -------------------------------------------------------------------------------- /tools/release_template.md: -------------------------------------------------------------------------------- 1 | TODO: description of the new version 2 | 3 | ### Deprecations 4 | 5 | TODO 6 | 7 | Deprecated components will be removed in cpp-sort 3.0.0. 8 | 9 | Deprecation warnings can be disabled by defining the preprocessor macro [`CPPSORT_DISABLE_DEPRECATION_WARNINGS`][deprecation-warnings]. 10 | 11 | ### New features 12 | 13 | TODO 1 14 | 15 | TODO 2 16 | 17 | ### Bug fixes 18 | 19 | * TODO: bug fix 1 20 | * TODO: bug fix 2 21 | 22 | ### Improvements 23 | 24 | Algorithmic & speed improvements: 25 | * TODO 26 | 27 | Other improvements: 28 | * TODO 29 | 30 | ### Tooling 31 | 32 | Documentation: 33 | * TODO 34 | 35 | Benchmarks: 36 | * TODO 37 | 38 | Test suite: 39 | * TODO 40 | 41 | Miscellaneous: 42 | * TODO 43 | 44 | ### Known bugs 45 | 46 | I didn't manage to fix every bug I could find since the previous release, so you might want to check the [list of known bugs][known-bugs]. 47 | 48 | 49 | [deprecation-warnings]: https://github.com/Morwenn/cpp-sort/wiki#deprecation-warnings 50 | [known-bugs]: https://github.com/Morwenn/cpp-sort/issues?q=is%3Aissue+is%3Aopen+label%3Abug 51 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/rotate_right.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_ROTATE_RIGHT_H_ 6 | #define CPPSORT_DETAIL_ROTATE_RIGHT_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include "iterator_traits.h" 15 | 16 | namespace cppsort::detail 17 | { 18 | template 19 | auto rotate_right(RandomAccessIterator first) 20 | -> void 21 | { 22 | static_assert(N > 1); 23 | 24 | using utility::iter_move; 25 | using difference_type = difference_type_t; 26 | 27 | auto tmp = iter_move(first + N - 1); 28 | for (difference_type i = N - 1; i > 0; --i) { 29 | first[i] = iter_move(first + (i - 1)); 30 | } 31 | first[0] = std::move(tmp); 32 | } 33 | } 34 | 35 | #endif // CPPSORT_DETAIL_ROTATE_RIGHT_H_ 36 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/rotate_left.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_ROTATE_LEFT_H_ 6 | #define CPPSORT_DETAIL_ROTATE_LEFT_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include "iterator_traits.h" 15 | 16 | namespace cppsort::detail 17 | { 18 | template 19 | auto rotate_left(RandomAccessIterator first) 20 | -> void 21 | { 22 | static_assert(N > 1); 23 | 24 | using utility::iter_move; 25 | using difference_type = difference_type_t; 26 | 27 | auto tmp = iter_move(first); 28 | for (difference_type i = 0; i < static_cast(N - 1); ++i) { 29 | first[i] = iter_move(first + (i + 1)); 30 | } 31 | first[N-1] = std::move(tmp); 32 | } 33 | } 34 | 35 | #endif // CPPSORT_DETAIL_ROTATE_LEFT_H_ 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Morwenn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/readme_example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | struct wrapper { int value; }; 16 | 17 | std::forward_list li = { {5}, {8}, {3}, {2}, {9} }; 18 | std::vector vec = { {5}, {8}, {3}, {2}, {9} }; 19 | 20 | // When used, this sorter will use a pattern-defeating quicksort 21 | // to sort random-access collections, and a mergesort otherwise 22 | cppsort::hybrid_adapter< 23 | cppsort::pdq_sorter, 24 | cppsort::merge_sorter 25 | > sorter; 26 | 27 | // Sort li and vec in reverse order using their value member 28 | sorter(li, std::greater{}, &wrapper::value); 29 | sorter(vec, std::greater{}, &wrapper::value); 30 | 31 | assert(std::equal( 32 | li.begin(), li.end(), 33 | vec.begin(), vec.end(), 34 | [](const auto& lhs, const auto& rhs) { return lhs.value == rhs.value; } 35 | )); 36 | } 37 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort4.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT4_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT4_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct low_comparisons_sorter_impl<4u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | low_comparisons_sorter<3u>{}(first+1u, first+4u, compare, projection); 26 | front_insert<4u>(std::move(first), std::move(compare), std::move(projection)); 27 | } 28 | }; 29 | } 30 | 31 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT4_H_ 32 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT8_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT8_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct low_comparisons_sorter_impl<8u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | low_comparisons_sorter<7u>{}(first+1u, first+8u, compare, projection); 26 | front_insert<8u>(std::move(first), std::move(compare), std::move(projection)); 27 | } 28 | }; 29 | } 30 | 31 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT8_H_ 32 | -------------------------------------------------------------------------------- /include/cpp-sort/sorters/spread_sorter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_SORTERS_SPREAD_SORTER_H_ 6 | #define CPPSORT_SORTERS_SPREAD_SORTER_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace cppsort 17 | { 18 | //////////////////////////////////////////////////////////// 19 | // Sorter 20 | 21 | struct spread_sorter: 22 | hybrid_adapter< 23 | integer_spread_sorter, 24 | float_spread_sorter, 25 | string_spread_sorter 26 | > 27 | {}; 28 | 29 | //////////////////////////////////////////////////////////// 30 | // Sort function 31 | 32 | inline constexpr spread_sorter spread_sort{}; 33 | } 34 | 35 | #endif // CPPSORT_SORTERS_SPREAD_SORTER_H_ 36 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort10.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT10_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT10_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct low_comparisons_sorter_impl<10u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | low_comparisons_sorter<9u>{}(first+1u, first+10u, compare, projection); 26 | front_insert<10u>(std::move(first), std::move(compare), std::move(projection)); 27 | } 28 | }; 29 | } 30 | 31 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT10_H_ 32 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort11.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT11_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT11_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct low_comparisons_sorter_impl<11u> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | low_comparisons_sorter<10u>{}(first+1u, first+11u, compare, projection); 26 | front_insert<11u>(std::move(first), std::move(compare), std::move(projection)); 27 | } 28 | }; 29 | } 30 | 31 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT11_H_ 32 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort7.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT7_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT7_H_ 7 | 8 | 9 | namespace cppsort::detail 10 | { 11 | template<> 12 | struct low_comparisons_sorter_impl<7u> 13 | { 14 | template< 15 | typename RandomAccessIterator, 16 | typename Compare = std::less<>, 17 | typename Projection = utility::identity, 18 | typename = detail::enable_if_t> 21 | > 22 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 23 | Compare compare={}, Projection projection={}) const 24 | -> void 25 | { 26 | low_comparisons_sorter<6u>{}(first+1u, first+7u, compare, projection); 27 | front_insert<7u>(std::move(first), std::move(compare), std::move(projection)); 28 | } 29 | }; 30 | } 31 | 32 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT7_H_ 33 | -------------------------------------------------------------------------------- /tests/utility/as_comparison.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE( "is_transparent over as_projection", "[utility][comparison][is_transparent]" ) 12 | { 13 | using cmp1 = cppsort::utility::detail::as_comparison_fn>; 14 | using cmp2 = cppsort::utility::detail::as_comparison_fn>; 15 | 16 | STATIC_CHECK_FALSE( cppsort::detail::has_is_transparent_v ); 17 | STATIC_CHECK( cppsort::detail::has_is_transparent_v ); 18 | 19 | std::map mapping1 = { 20 | {1, 1}, 21 | {2, 2}, 22 | {3, 3}, 23 | }; 24 | CHECK_THROWS( mapping1.find(is_transparent_helper_compared(2)) ); 25 | 26 | std::map mapping2 = { 27 | {1, 1}, 28 | {2, 2}, 29 | {3, 3}, 30 | }; 31 | CHECK_NOTHROW( mapping2.find(is_transparent_helper_compared(2)) ); 32 | } 33 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER2_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER2_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<2u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | 30 | if (comp(proj(first[1u]), proj(first[0u]))) { 31 | rotate_left<2u>(first); 32 | } 33 | } 34 | }; 35 | } 36 | 37 | #endif // CPPSORT_DETAIL_FRONT_INSERTER0_H_ 38 | -------------------------------------------------------------------------------- /docs/_Sidebar.md: -------------------------------------------------------------------------------- 1 | * [Home](Home.md) 2 | * [Quickstart](Quickstart.md) 3 | * Sorting library 4 | * [Library nomenclature](Library-nomenclature.md) 5 | * [Sorters](Sorters.md) 6 | * [Fixed-size sorters](Fixed-size-sorters.md) 7 | * [Sorter adapters](Sorter-adapters.md) 8 | * [Metrics](Metrics.md) 9 | * [Sorter facade](Sorter-facade.md) 10 | * [Sorter traits](Sorter-traits.md) 11 | * [Measures of disorder](Measures-of-disorder.md) 12 | * [Comparators and projections](Comparators-and-projections.md) 13 | * [Comparators](Comparators.md) 14 | * [Comparator adapters](Comparator-adapters.md) 15 | * [Chainable projections](Chainable-projections.md) 16 | * [Refined functions](Refined-functions.md) 17 | * [Miscellaneous utilities](Miscellaneous-utilities.md) 18 | * Tutorials 19 | * [Writing a sorter](Writing-a-sorter.md) 20 | * [Writing a `bubble_sorter`](Writing-a-bubble_sorter.md) 21 | * [Writing a `randomizing_adapter`](Writing-a-randomizing_adapter.md) 22 | * [Writing a container-aware algorithm](Writing-a-container-aware-algorithm.md) 23 | * [Tooling](Tooling.md) 24 | * [Benchmarks](Benchmarks.md) 25 | * [Changelog](Changelog.md) 26 | * [Original research](Original-research.md) 27 | -------------------------------------------------------------------------------- /tests/probes/sus.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE( "measure of disorder: sus", "[probe][sus]" ) 13 | { 14 | using cppsort::probe::sus; 15 | 16 | SECTION( "simple test" ) 17 | { 18 | const std::forward_list li = { 6, 9, 79, 41, 44, 49, 11, 16, 69, 15 }; 19 | CHECK( sus(li) == 3 ); 20 | CHECK( sus(li.begin(), li.end()) == 3 ); 21 | 22 | const std::vector> tricky(li.begin(), li.end()); 23 | CHECK( sus(tricky, &internal_compare::compare_to) == 3 ); 24 | } 25 | 26 | SECTION( "upper bound" ) 27 | { 28 | // The upper bound should correspond to the size of 29 | // the input sequence minus one 30 | 31 | const std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 32 | auto max_n = sus.max_for_size(cppsort::utility::size(li)); 33 | CHECK( max_n == 10 ); 34 | CHECK( sus(li) == max_n ); 35 | CHECK( sus(li.begin(), li.end()) == max_n ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/comparators/total_less.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | TEST_CASE( "IEEE 754 totalOrder implementation", "[comparison]" ) 12 | { 13 | static constexpr double nan = std::numeric_limits::quiet_NaN(); 14 | static constexpr double inf = std::numeric_limits::infinity(); 15 | 16 | double array[] = { +1.0, +inf, -1.0, -nan, +0.0, -inf, +nan, -0.0 }; 17 | cppsort::heap_sort(array, cppsort::total_less); 18 | 19 | // Check for IEEE 754 totalOrder, 20 | // ignore quiet vs. signaling NaNs 21 | CHECK( std::isnan(array[0]) ); 22 | CHECK( std::signbit(array[0]) ); 23 | CHECK( std::isinf(array[1]) ); 24 | CHECK( std::signbit(array[1]) ); 25 | CHECK( array[2] == -1.0 ); 26 | CHECK( array[3] == 0.0 ); 27 | CHECK( std::signbit(array[3]) ); 28 | CHECK( array[4] == 0.0 ); 29 | CHECK( not std::signbit(array[4]) ); 30 | CHECK( array[5] == +1.0 ); 31 | CHECK( std::isinf(array[6]) ); 32 | CHECK( not std::signbit(array[6]) ); 33 | CHECK( std::isnan(array[7]) ); 34 | CHECK( not std::signbit(array[7]) ); 35 | } 36 | -------------------------------------------------------------------------------- /cmake/llvm-cov-wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file is part of CMake-codecov. 4 | # 5 | # Copyright (c) 6 | # 2015-2020 RWTH Aachen University, Federal Republic of Germany 7 | # 8 | # See the LICENSE file in the package base directory for details 9 | # 10 | # Written by Alexander Haase, alexander.haase@rwth-aachen.de 11 | # 12 | 13 | if [ -z "$LLVM_COV_BIN" ] 14 | then 15 | echo "LLVM_COV_BIN not set!" >& 2 16 | exit 1 17 | fi 18 | 19 | 20 | # Get LLVM version to find out. 21 | LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \ 22 | | sed "s/^\([A-Za-z ]*\)\([0-9]*\).\([0-9]*\).*$/\2.\3/g") 23 | 24 | if [ "$1" = "-v" ] 25 | then 26 | echo "llvm-cov-wrapper $LLVM_VERSION" 27 | exit 0 28 | fi 29 | 30 | 31 | if [ -n "$LLVM_VERSION" ] 32 | then 33 | MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1) 34 | MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2) 35 | 36 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ] 37 | then 38 | if [ -f "$1" ] 39 | then 40 | filename=$(basename "$1") 41 | extension="${filename##*.}" 42 | 43 | case "$extension" in 44 | "gcno") exec $LLVM_COV_BIN --gcno="$1" ;; 45 | "gcda") exec $LLVM_COV_BIN --gcda="$1" ;; 46 | esac 47 | fi 48 | fi 49 | 50 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ] 51 | then 52 | exec $LLVM_COV_BIN $@ 53 | fi 54 | fi 55 | 56 | exec $LLVM_COV_BIN gcov $@ 57 | -------------------------------------------------------------------------------- /tools/release-checklist.md: -------------------------------------------------------------------------------- 1 | List of actions to perform when releasing a new cpp-sort version. 2 | 3 | ### During the development 4 | 5 | - [ ] Update the documentation. 6 | - [ ] Update the releases notes. 7 | - [ ] Update `NOTICE.txt` and `README.md` when stealing code. 8 | - [ ] Keep track of the things that will change in 3.0.0. 9 | 10 | ### Before the release 11 | 12 | - [ ] Check that there aren't warnings left in the latest CI logs. 13 | - [ ] Check that all issues linked to the milestone are closed. 14 | - [ ] Check `NOTICE.txt` and `README.md` conformance for stolen code. 15 | - [ ] Make sure that tests pass and examples build. 16 | - [ ] Regenerate the benchmarks as needed. 17 | - [ ] Bump the version number with tools/update-version.py. 18 | - [ ] Verify that the Conan recipe works. 19 | - [ ] Try to open `docs` with the latest version of Gollum. 20 | - [ ] Find a name for the new version. 21 | - [ ] Open a merge request, let the CI do its job. 22 | - [ ] Merge `-develop` into `-stable`. 23 | - [ ] Publish the release, don't forget to target `-stable`. 24 | 25 | ### After the release 26 | 27 | - [ ] Add the Zenodo badge to the release notes. 28 | - [ ] Close the new version's milestone. 29 | - [ ] Check that the documentation was correctly uploaded. 30 | - [ ] Add the new version to Conan Center Index. 31 | - [ ] Brag about it where relevant. 32 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/is_p_sorted.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_IS_P_SORTED_H_ 6 | #define CPPSORT_DETAIL_IS_P_SORTED_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | 14 | namespace cppsort::detail 15 | { 16 | template 17 | auto is_p_sorted(ForwardIterator first, ForwardIterator last, ForwardIterator pth, 18 | Compare compare, Projection projection) 19 | -> bool 20 | { 21 | auto&& comp = utility::as_function(compare); 22 | auto&& proj = utility::as_function(projection); 23 | 24 | // pth is the iterator such as pth - first == p 25 | 26 | auto max_it = first; 27 | for (auto it1 = std::next(pth); it1 != last; ++it1) { 28 | if (comp(proj(*max_it), proj(*first))) { 29 | max_it = first; 30 | } 31 | if (comp(proj(*it1), proj(*max_it))) { 32 | return false; 33 | } 34 | ++first; 35 | } 36 | return true; 37 | } 38 | } 39 | 40 | #endif // CPPSORT_DETAIL_IS_P_SORTED_H_ 41 | -------------------------------------------------------------------------------- /tests/adapters/every_adapter_tricky_difference_type.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEMPLATE_TEST_CASE( "test adapters with an int8_t difference_type", "[adapters]", 15 | cppsort::indirect_adapter, 16 | cppsort::out_of_place_adapter, 17 | cppsort::schwartz_adapter, 18 | cppsort::stable_adapter, 19 | cppsort::verge_adapter 20 | ) 21 | { 22 | // Test that adapters work as expected when the iterator to sort has 23 | // a small difference_type, despite potential promotions to int 24 | 25 | test_vector collection(127); 26 | auto distribution = dist::shuffled{}; 27 | distribution(std::back_inserter(collection), 127); 28 | 29 | TestType sorter; 30 | sorter(collection); 31 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 32 | } 33 | -------------------------------------------------------------------------------- /tests/probes/block.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEST_CASE( "measure of disorder: block", "[probe][block]" ) 15 | { 16 | using cppsort::probe::block; 17 | 18 | SECTION( "simple test" ) 19 | { 20 | const std::forward_list li = { 74, 59, 62, 23, 86, 69, 18, 52, 77, 68 }; 21 | CHECK( block(li) == 8 ); 22 | CHECK( block(li.begin(), li.end()) == 8 ); 23 | 24 | std::vector> tricky(li.begin(), li.end()); 25 | CHECK( block(tricky, &internal_compare::compare_to) == 8 ); 26 | } 27 | 28 | SECTION( "upper bound" ) 29 | { 30 | // The upper bound should correspond to the size of 31 | // the input sequence minus one 32 | 33 | const std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 34 | auto max_n = block.max_for_size(cppsort::utility::size(li)); 35 | CHECK( max_n == 10 ); 36 | CHECK( block(li) == max_n ); 37 | CHECK( block(li.begin(), li.end()) == max_n ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER3_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER3_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<3u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[1u]), proj0)) { 32 | if (comp(proj(first[2u]), proj0)) { 33 | rotate_left<3u>(first); 34 | } else { 35 | rotate_left<2u>(first); 36 | } 37 | } 38 | } 39 | }; 40 | } 41 | 42 | #endif // CPPSORT_DETAIL_FRONT_INSERTER3_H_ 43 | -------------------------------------------------------------------------------- /tests/metrics/running_time.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEST_CASE( "basic metrics::running_time tests", "[metrics]" ) 15 | { 16 | using namespace std::chrono_literals; 17 | 18 | std::list collection; 19 | auto distribution = dist::shuffled{}; 20 | distribution(std::back_inserter(collection), 102); 21 | 22 | SECTION( "with default duration type" ) 23 | { 24 | cppsort::metrics::running_time< 25 | cppsort::splay_sorter 26 | > sorter; 27 | 28 | auto res = sorter(collection); 29 | CHECK( res > 0s ); 30 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 31 | } 32 | 33 | SECTION( "with explicit duration type" ) 34 | { 35 | cppsort::metrics::running_time< 36 | cppsort::splay_sorter, 37 | std::chrono::microseconds 38 | > sorter; 39 | 40 | auto res = sorter(collection); 41 | CHECK( res > 0s ); 42 | CHECK( res < 1s ); 43 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tests/testing-tools/span.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_TESTSUITE_SPAN_H_ 6 | #define CPPSORT_TESTSUITE_SPAN_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | 13 | //////////////////////////////////////////////////////////// 14 | // Very basic span type 15 | // 16 | // While the original goal of sorters is to sort some kinds 17 | // of collections that are typically lvalues, there are more 18 | // and more span-like types (GSL, Ranges v3...) and these 19 | // might be passed to sorters as temporary values. 20 | // 21 | // Therefore sorters need to handle temporary span-like 22 | // classes like the one below too. 23 | // 24 | 25 | template 26 | class span 27 | { 28 | public: 29 | 30 | template 31 | explicit span(Range& range): 32 | _begin(std::begin(range)), 33 | _end(std::end(range)) 34 | {} 35 | 36 | auto begin() const { return _begin; } 37 | auto end() const { return _end; } 38 | 39 | private: 40 | 41 | Iterator _begin, _end; 42 | }; 43 | 44 | template 45 | explicit span(Range& range) 46 | -> span; 47 | 48 | #endif // CPPSORT_TESTSUITE_SPAN_H_ 49 | -------------------------------------------------------------------------------- /tests/utility/metric_tools.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE("simple utility::metric tests", "[utility][metrics]") 13 | { 14 | using namespace cppsort::utility; 15 | using namespace std::chrono_literals; 16 | 17 | struct time_tag {}; 18 | 19 | constexpr metric m1(5s); 20 | constexpr metric m2(5000ms); 21 | constexpr metric m3(4000ms); 22 | STATIC_CHECK( m1 == m2 ); 23 | STATIC_CHECK( m3 != m2 ); 24 | STATIC_CHECK( m3 < m1 ); 25 | } 26 | 27 | TEST_CASE("simple utility::metrics tests", "[utility][metrics]") 28 | { 29 | using std::get; 30 | using namespace cppsort::utility; 31 | 32 | struct foo_tag {}; 33 | struct bar_tag {}; 34 | using metric1 = metric; 35 | using metric2 = metric; 36 | 37 | metrics mm(metric1(42), metric2(140.0)); 38 | auto& m1 = get(mm); 39 | CHECK( m1 == 42 ); 40 | auto m2 = get(mm); 41 | CHECK( m2 == 140.0 ); 42 | 43 | get(mm) = 35; 44 | CHECK( m1 == 35 ); 45 | } 46 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_moves/sort2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_MOVES_SORT2_H_ 6 | #define CPPSORT_DETAIL_LOW_MOVES_SORT2_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "../swap_if.h" 17 | #include "../type_traits.h" 18 | 19 | namespace cppsort::detail 20 | { 21 | template<> 22 | struct low_moves_sorter_impl<2u> 23 | { 24 | template< 25 | typename RandomAccessIterator, 26 | typename Compare = std::less<>, 27 | typename Projection = utility::identity, 28 | typename = detail::enable_if_t> 31 | > 32 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 33 | Compare compare={}, Projection projection={}) const 34 | -> void 35 | { 36 | iter_swap_if(first, first + 1u, 37 | std::move(compare), std::move(projection)); 38 | } 39 | }; 40 | } 41 | 42 | #endif // CPPSORT_DETAIL_LOW_MOVES_SORT2_H_ 43 | -------------------------------------------------------------------------------- /tests/sorters/spin_sorter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE( "spin_sorter tests", "[spin_sorter]" ) 13 | { 14 | // spin_sorter has different paths that can only be reached depending 15 | // on the size of the input that generic tests don't seem to catch, 16 | // hence those additional tests. The different paths depend on the 17 | // following criteria: 18 | // - Whether log2(size(input)) is even or odd 19 | // - TODO 20 | 21 | std::vector collection; 22 | auto distribution = dist::shuffled{}; 23 | 24 | SECTION( "even number of levels" ) 25 | { 26 | const int size = 2800; 27 | collection.reserve(size); 28 | distribution(std::back_inserter(collection), size, 0); 29 | cppsort::spin_sort(collection); 30 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 31 | } 32 | 33 | SECTION( "odd number of levels" ) 34 | { 35 | const int size = 2300; 36 | collection.reserve(size); 37 | distribution(std::back_inserter(collection), size, 0); 38 | cppsort::spin_sort(collection); 39 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tools/stable_t.dot: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Morwenn 2 | // SPDX-License-Identifier: MIT 3 | 4 | digraph G { 5 | 6 | // Nodes 7 | node [fontname="consolas"]; 8 | sorter[label="Sorter"] 9 | sorter_type[label="Sorter::type"] 10 | stable_t[label="stable_t"] 11 | stable_adapter[label="stable_adapter"] 12 | stable_adapter_type[label="stable_adapter::type"] 13 | node [shape="diamond"] 14 | is_always_stable[label="is_always_stable_v"] 15 | stable_adapter_type_exists[label="stable_adapter::type exists?"] 16 | sorter_type_exists[label="Sorter::type exists?"] 17 | is_specialization[label="is_specialization_of"] 18 | 19 | // Flow 20 | stable_t -> is_always_stable 21 | is_always_stable -> is_specialization[label="true",fontname="consolas",fontsize="10"] 22 | is_specialization -> sorter[label="false",fontname="consolas",fontsize="10"] 23 | is_specialization -> sorter_type_exists[label="true",fontname="consolas",fontsize="10"] 24 | is_always_stable -> stable_adapter_type_exists[label="false",fontname="consolas",fontsize="10"] 25 | stable_adapter_type_exists -> stable_adapter_type[label="true",fontname="consolas",fontsize="10"] 26 | stable_adapter_type_exists -> stable_adapter[label="false",fontname="consolas",fontsize="10"] 27 | sorter_type_exists -> sorter_type[label="true",fontname="consolas",fontsize="10"] 28 | sorter_type_exists -> sorter[label="false",fontname="consolas",fontsize="10"] 29 | } 30 | -------------------------------------------------------------------------------- /tests/utility/buffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | TEST_CASE( "miscellaneous tests for buffer providers", 10 | "[utility][buffer]" ) 11 | { 12 | using namespace cppsort; 13 | 14 | SECTION( "fixed_buffer" ) 15 | { 16 | utility::fixed_buffer<128>::buffer buffer; 17 | 18 | CHECK( buffer.size() == 128 ); 19 | CHECK( buffer.begin() == buffer.cbegin() ); 20 | CHECK( buffer.end() == buffer.cend() ); 21 | CHECK( buffer.end() == buffer.begin() + buffer.size() ); 22 | } 23 | 24 | SECTION( "fixed_buffer of size 0" ) 25 | { 26 | utility::fixed_buffer<0>::buffer buffer; 27 | 28 | CHECK( buffer.size() == 0 ); 29 | CHECK( buffer.begin() == buffer.cbegin() ); 30 | CHECK( buffer.end() == buffer.cend() ); 31 | CHECK( buffer.end() == buffer.begin() + buffer.size() ); 32 | CHECK( buffer.begin() == buffer.end() ); 33 | } 34 | 35 | SECTION( "dynamic_buffer" ) 36 | { 37 | utility::dynamic_buffer::buffer buffer(25); 38 | 39 | CHECK( buffer.size() == 5 ); 40 | CHECK( buffer.begin() == buffer.cbegin() ); 41 | CHECK( buffer.end() == buffer.cend() ); 42 | CHECK( buffer.end() == buffer.begin() + buffer.size() ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/probes/rem.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE( "measure of disorder: rem", "[probe][rem]" ) 13 | { 14 | using cppsort::probe::rem; 15 | 16 | SECTION( "simple test" ) 17 | { 18 | // Forward iterators 19 | const std::forward_list li = { 6, 9, 79, 41, 44, 49, 11, 16, 69, 15 }; 20 | CHECK( rem(li) == 4 ); 21 | CHECK( rem(li.begin(), li.end()) == 4 ); 22 | 23 | // Random-access iterators 24 | const std::vector vec(li.begin(), li.end()); 25 | CHECK( rem(vec) == 4 ); 26 | CHECK( rem(vec.begin(), vec.end()) == 4 ); 27 | 28 | const std::vector> tricky(li.begin(), li.end()); 29 | CHECK( rem(tricky, &internal_compare::compare_to) == 4 ); 30 | } 31 | 32 | SECTION( "upper bound" ) 33 | { 34 | // The upper bound should correspond to the size of 35 | // the input sequence minus one 36 | 37 | const std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 38 | auto max_n = rem.max_for_size(cppsort::utility::size(li)); 39 | CHECK( max_n == 10 ); 40 | CHECK( rem(li) == max_n ); 41 | CHECK( rem(li.begin(), li.end()) == max_n ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /include/cpp-sort/sorters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_SORTERS_H_ 6 | #define CPPSORT_SORTERS_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #endif // CPPSORT_SORTERS_H_ 37 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter4.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER4_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER4_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<4u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[2u]), proj0)) { 32 | if (comp(proj(first[3u]), proj0)) { 33 | rotate_left<4u>(first); 34 | } else { 35 | rotate_left<3u>(first); 36 | } 37 | } else { 38 | if (comp(proj(first[1u]), proj0)) { 39 | rotate_left<2u>(first); 40 | } 41 | } 42 | } 43 | }; 44 | } 45 | 46 | #endif // CPPSORT_DETAIL_FRONT_INSERTER4_H_ 47 | -------------------------------------------------------------------------------- /tools/sorting-network.tex: -------------------------------------------------------------------------------- 1 | % sortingnetwork.tex 2 | % The sortingnetwork macro defined in this file uses Tikz package to draw a sorting network. 3 | % 4 | % written by: kaayy 5 | % Oct. 10, 2010 6 | % modified by: Morwenn 7 | % Oct. 30, 2015 8 | % .draw network from top to bottom 9 | % .make rows indices 0-based 10 | % .make nodes smaller 11 | % 12 | % Usage: 13 | % \begin{sortingnetwork}[number of inputs][number of layers][scale] 14 | % \nodeconnection{{1, 2},{3, 4}, ...} 15 | % \nodelabel{1, 2, 3, 4, ...} 16 | % ... 17 | % \end{sortingnetwork} 18 | % 19 | 20 | \makeatletter 21 | 22 | \newcounter{sncolumncounter} 23 | \newcounter{snrowcounter} 24 | 25 | \def \nodelabel#1{% 26 | \setcounter{snrowcounter}{1} 27 | \foreach \i in {#1}{% 28 | \draw (\value{sncolumncounter},\value{snrowcounter}) node[anchor=north]{\i}; 29 | \addtocounter{snrowcounter}{1} 30 | } 31 | \addtocounter{sncolumncounter}{1} 32 | } 33 | 34 | \def \nodeconnection#1{% 35 | \foreach \i in {#1}{% 36 | \GetTokens{nodesrc}{nodedest}{\i} 37 | \draw (\value{sncolumncounter},\nodesrc) node[circle]{}--(\value{sncolumncounter},\nodedest) node[circle]{}; 38 | } 39 | \addtocounter{sncolumncounter}{1} 40 | } 41 | 42 | \newenvironment{sortingnetwork}[3] 43 | { 44 | \setcounter{sncolumncounter}{0} 45 | \def \sn@fullsize{15} 46 | \begin{tikzpicture}[y=-1cm,scale=#3*\sn@fullsize/#2,every node/.style={fill=black,inner sep=0pt,minimum size=0.2cm}] 47 | \foreach \i in {1, ..., #1} 48 | { 49 | \draw (0,\i-1)--(#2-1,\i-1); 50 | } 51 | } 52 | { 53 | \end{tikzpicture} 54 | } 55 | \makeatother -------------------------------------------------------------------------------- /.github/workflows/build-mingw.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021-2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | name: MinGW-w64 Builds 5 | 6 | on: 7 | push: 8 | paths: 9 | - '.github/workflows/build-mingw.yml' 10 | - 'CMakeLists.txt' 11 | - 'cmake/**' 12 | - 'examples/**' 13 | - 'include/**' 14 | - 'tests/**' 15 | pull_request: 16 | paths: 17 | - '.github/workflows/build-mingw.yml' 18 | - 'CMakeLists.txt' 19 | - 'cmake/**' 20 | - 'examples/**' 21 | - 'include/**' 22 | - 'tests/**' 23 | 24 | jobs: 25 | build: 26 | runs-on: windows-2022 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | build_type: [Debug, Release] 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - name: Configure CMake 37 | shell: pwsh 38 | working-directory: ${{runner.workspace}} 39 | run: | 40 | cmake -H${{github.event.repository.name}} -Bbuild ` 41 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ` 42 | -G"MinGW Makefiles" ` 43 | -DCPPSORT_BUILD_TESTING=ON ` 44 | -DCPPSORT_BUILD_EXAMPLES=ON 45 | 46 | - name: Build the test suite 47 | working-directory: ${{runner.workspace}}/build 48 | run: cmake --build . --config ${{matrix.build_type}} -j 2 49 | 50 | - name: Run the test suite 51 | env: 52 | CTEST_OUTPUT_ON_FAILURE: 1 53 | working-directory: ${{runner.workspace}}/build 54 | run: ctest -C ${{matrix.build_type}} --no-tests=error 55 | -------------------------------------------------------------------------------- /tests/probes/inv.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | TEST_CASE( "measure of disorder: inv", "[probe][inv]" ) 14 | { 15 | using cppsort::probe::inv; 16 | 17 | SECTION( "simple test" ) 18 | { 19 | const std::forward_list li = { 48, 43, 96, 44, 42, 34, 42, 57, 68, 69 }; 20 | CHECK( inv(li) == 19 ); 21 | CHECK( inv(li.begin(), li.end()) == 19 ); 22 | 23 | std::vector> tricky(li.begin(), li.end()); 24 | CHECK( inv(tricky, &internal_compare::compare_to) == 19 ); 25 | } 26 | 27 | SECTION( "upper bound" ) 28 | { 29 | // The upper bound should correspond to: 30 | // size * (size - 1) / 2 31 | 32 | const std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 33 | auto max_n = inv.max_for_size(cppsort::utility::size(li)); 34 | CHECK( max_n == 55 ); 35 | CHECK( inv(li) == max_n ); 36 | CHECK( inv(li.begin(), li.end()) == max_n ); 37 | } 38 | 39 | SECTION( "Sorting and Measures of Disorder, Theorem 3.18" ) 40 | { 41 | int size = 1000; 42 | 43 | std::vector vec(size, 0); 44 | vec[0] = size - 1; 45 | std::iota(vec.begin() + 1, vec.end(), 0); 46 | CHECK( inv(vec) <= size ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/deploy-to-wiki.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020-2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | name: Wiki Deployment 5 | 6 | on: 7 | push: 8 | branches: 9 | - 2.x.y-stable 10 | paths: 11 | - 'docs/**' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | sync-wiki-files: 16 | name: Sync Wiki Files 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout /docs 22 | uses: actions/checkout@v4 23 | with: 24 | repository: ${{github.repository}} 25 | path: main 26 | 27 | - name: Checkout wiki 28 | uses: actions/checkout@v4 29 | with: 30 | repository: ${{github.repository}}.wiki 31 | path: wiki 32 | 33 | - name: Sync wiki files 34 | run: | 35 | for docname in main/docs/*.md; do 36 | old=$(basename "$docname"); 37 | new=${old%.*}; 38 | find main/docs -name "*.md" -exec sed -i "s/$old/$new/g" {} \; 39 | done 40 | rsync -avzr --delete --exclude='.git/' "main/docs/" "wiki/" 41 | 42 | - name: Commit changes 43 | working-directory: wiki 44 | run: | 45 | git config --local user.email "action@github.com" 46 | git config --local user.name "GitHub Action" 47 | git add . 48 | git commit -m "Synchronize wiki with docs/" 49 | 50 | - name: Push changes to wiki 51 | uses: ad-m/github-push-action@master 52 | with: 53 | directory: wiki 54 | repository: ${{github.repository}}.wiki 55 | branch: master 56 | -------------------------------------------------------------------------------- /tests/probes/runs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE( "measure of disorder: runs", "[probe][runs]" ) 13 | { 14 | using cppsort::probe::runs; 15 | 16 | SECTION( "simple tests" ) 17 | { 18 | const std::forward_list li = { 40, 49, 58, 99, 60, 70, 12, 87, 9, 8, 82, 91, 99, 67, 82, 92 }; 19 | CHECK( runs(li) == 5 ); 20 | CHECK( runs(li.begin(), li.end()) == 5 ); 21 | 22 | // From Right invariant metrics and measures of 23 | // presortedness by Estivill-Castro, Mannila and Wood 24 | const std::forward_list li2 = { 4, 2, 6, 5, 3, 1, 9, 7, 10, 8 }; 25 | CHECK( runs(li2) == 6 ); 26 | CHECK( runs(li2.begin(), li2.end()) == 6 ); 27 | 28 | const std::vector> tricky(li.begin(), li.end()); 29 | CHECK( runs(tricky, &internal_compare::compare_to) == 5 ); 30 | } 31 | 32 | SECTION( "upper bound" ) 33 | { 34 | // The upper bound should correspond to the size of 35 | // the input sequence minus one 36 | 37 | const std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 38 | auto max_n = runs.max_for_size(cppsort::utility::size(li)); 39 | CHECK( max_n == 10 ); 40 | CHECK( runs(li) == max_n ); 41 | CHECK( runs(li.begin(), li.end()) == max_n ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/bubble_sort.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_BUBBLE_SORT_H_ 6 | #define CPPSORT_DETAIL_BUBBLE_SORT_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "iterator_traits.h" 13 | #include "swap_if.h" 14 | 15 | namespace cppsort::detail 16 | { 17 | // 18 | // This sorting algorithm isn't exposed to users of the 19 | // library, it's only intended to be used as a fallback 20 | // by other stable algorithms to sort small collections 21 | // 22 | // These recursive algorithms tend to compute the size 23 | // of the collection, so bubble_sort can use it to have 24 | // a decreasing bound for forward iterators 25 | // 26 | 27 | template 28 | auto bubble_sort(ForwardIterator first, difference_type_t size, 29 | Compare compare, Projection projection) 30 | -> void 31 | { 32 | if (size < 2) return; 33 | 34 | while (--size) { 35 | ForwardIterator current = first; 36 | ForwardIterator next = std::next(current); 37 | for (difference_type_t i = 0 ; i < size ; ++i) { 38 | iter_swap_if(current, next, compare, projection); 39 | ++next; 40 | ++current; 41 | } 42 | } 43 | } 44 | } 45 | 46 | #endif // CPPSORT_DETAIL_BUBBLE_SORT_H_ 47 | -------------------------------------------------------------------------------- /tests/probes/amp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEST_CASE( "measure of disorder: amp", "[probe][amp]" ) 15 | { 16 | using cppsort::probe::amp; 17 | 18 | SECTION( "simple test" ) 19 | { 20 | std::forward_list li = { 4, 6, 5, 2, 9, 1, 3, 8, 0, 7 }; 21 | CHECK( amp(li) == 7 ); 22 | CHECK( amp(li.begin(), li.end()) == 7 ); 23 | 24 | std::vector> tricky(li.begin(), li.end()); 25 | CHECK( amp(tricky, &internal_compare::compare_to) == 7 ); 26 | } 27 | 28 | SECTION( "upper bound" ) 29 | { 30 | // The upper bound should correspond to a sequence that 31 | // oscillates at every step 32 | 33 | std::forward_list li = { 0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9 }; 34 | auto max_n = amp.max_for_size(cppsort::utility::size(li)); 35 | CHECK( max_n == 9 ); 36 | CHECK( amp(li) == max_n ); 37 | CHECK( amp(li.begin(), li.end()) == max_n ); 38 | } 39 | 40 | // https://morwenn.github.io/presortedness/2025/10/18/TSB005-symmetry-of-amp.html 41 | rc::prop("Amp(Reversed(X)) = Amp(X)", [](std::vector sequence) { 42 | auto amp_x = amp(sequence); 43 | std::reverse(sequence.begin(), sequence.end()); 44 | return amp(sequence) == amp_x; 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /tests/probes/max.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEST_CASE( "measure of disorder: max", "[probe][max]" ) 15 | { 16 | using cppsort::probe::max; 17 | 18 | SECTION( "simple test" ) 19 | { 20 | const std::forward_list li = { 12, 28, 17, 59, 13, 10, 39, 21, 31, 30 }; 21 | CHECK( (max)(li) == 6 ); 22 | CHECK( (max)(li.begin(), li.end()) == 6 ); 23 | 24 | const std::vector> tricky(li.begin(), li.end()); 25 | CHECK( (max)(tricky, &internal_compare::compare_to) == 6 ); 26 | } 27 | 28 | SECTION( "upper bound" ) 29 | { 30 | // The upper bound should correspond to the size of 31 | // the input sequence minus one 32 | 33 | const std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 34 | auto max_n = (max).max_for_size(cppsort::utility::size(li)); 35 | CHECK( max_n == 10 ); 36 | CHECK( (max)(li) == max_n ); 37 | CHECK( (max)(li.begin(), li.end()) == max_n ); 38 | } 39 | 40 | SECTION( "regressions" ) 41 | { 42 | std::vector collection; 43 | collection.reserve(100); 44 | auto distribution = dist::ascending_duplicates{}; 45 | distribution(std::back_inserter(collection), 100); 46 | 47 | CHECK( (max)(collection) == 0 ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/is_sorted_until.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_IS_SORTED_UNTIL_H_ 6 | #define CPPSORT_DETAIL_IS_SORTED_UNTIL_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | 13 | namespace cppsort::detail 14 | { 15 | template 16 | constexpr auto is_sorted_until(ForwardIterator first, ForwardIterator last, 17 | Compare compare, Projection projection) 18 | -> ForwardIterator 19 | { 20 | if (first != last) { 21 | auto&& comp = utility::as_function(compare); 22 | auto&& proj = utility::as_function(projection); 23 | 24 | ForwardIterator next = first; 25 | while (++next != last) { 26 | if (comp(proj(*next), proj(*first))) { 27 | return next; 28 | } 29 | first = next; 30 | } 31 | } 32 | return last; 33 | } 34 | 35 | template 36 | constexpr auto is_sorted(ForwardIterator first, ForwardIterator last, 37 | Compare compare, Projection projection) 38 | -> bool 39 | { 40 | return detail::is_sorted_until( 41 | first, last, 42 | std::move(compare), std::move(projection) 43 | ) == last; 44 | } 45 | } 46 | 47 | #endif // CPPSORT_DETAIL_IS_SORTED_UNTIL_H_ 48 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER5_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER5_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<5u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[2u]), proj0)) { 32 | if (comp(proj(first[3u]), proj0)) { 33 | if (comp(proj(first[4u]), proj0)) { 34 | rotate_left<5u>(first); 35 | } else { 36 | rotate_left<4u>(first); 37 | } 38 | } else { 39 | rotate_left<3u>(first); 40 | } 41 | } else { 42 | if (comp(proj(first[1u]), proj0)) { 43 | rotate_left<2u>(first); 44 | } 45 | } 46 | } 47 | }; 48 | } 49 | 50 | #endif // CPPSORT_DETAIL_FRONT_INSERTER5_H_ 51 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/stable_adapter_hybrid_adapter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2023 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_STABLE_ADAPTER_HYBRID_ADAPTER_H_ 6 | #define CPPSORT_DETAIL_STABLE_ADAPTER_HYBRID_ADAPTER_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cppsort 16 | { 17 | template 18 | struct stable_adapter>: 19 | hybrid_adapter...> 20 | { 21 | private: 22 | 23 | template 24 | constexpr explicit stable_adapter(std::index_sequence, hybrid_adapter&& sorters): 25 | hybrid_adapter...>( 26 | (stable_t(std::move(sorters).template get()))... 27 | ) 28 | {} 29 | 30 | public: 31 | 32 | //////////////////////////////////////////////////////////// 33 | // Construction 34 | 35 | stable_adapter() = default; 36 | 37 | constexpr explicit stable_adapter(hybrid_adapter sorters): 38 | stable_adapter(std::make_index_sequence{}, std::move(sorters)) 39 | {} 40 | 41 | //////////////////////////////////////////////////////////// 42 | // Sorter traits 43 | 44 | using is_always_stable = std::true_type; 45 | }; 46 | } 47 | 48 | #endif // CPPSORT_DETAIL_STABLE_ADAPTER_HYBRID_ADAPTER_H_ 49 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_moves/sort4.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_MOVES_SORT4_H_ 6 | #define CPPSORT_DETAIL_LOW_MOVES_SORT4_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../min_element.h" 18 | #include "../type_traits.h" 19 | 20 | namespace cppsort::detail 21 | { 22 | template<> 23 | struct low_moves_sorter_impl<4u> 24 | { 25 | template< 26 | typename RandomAccessIterator, 27 | typename Compare = std::less<>, 28 | typename Projection = utility::identity, 29 | typename = detail::enable_if_t> 32 | > 33 | auto operator()(RandomAccessIterator first, RandomAccessIterator last, 34 | Compare compare={}, Projection projection={}) const 35 | -> void 36 | { 37 | using utility::iter_swap; 38 | 39 | RandomAccessIterator min = min_element(first, last, compare, projection); 40 | if (min != first) 41 | { 42 | iter_swap(min, first); 43 | } 44 | low_moves_sorter<3u>{}(first+1u, last, std::move(compare), std::move(projection)); 45 | } 46 | }; 47 | } 48 | 49 | #endif // CPPSORT_DETAIL_LOW_MOVES_SORT4_H_ 50 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/sorting_network/sort4.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT4_H_ 6 | #define CPPSORT_DETAIL_SORTING_NETWORK_SORT4_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct sorting_network_sorter_impl<4> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | iter_swap_if(first, first + 2, compare, projection); 26 | iter_swap_if(first + 1, first + 3, compare, projection); 27 | iter_swap_if(first, first + 1, compare, projection); 28 | iter_swap_if(first + 2, first + 3, compare, projection); 29 | iter_swap_if(first + 1, first + 2, compare, projection); 30 | } 31 | 32 | template 33 | [[nodiscard]] 34 | static constexpr auto index_pairs() noexcept 35 | -> std::array, 5> 36 | { 37 | return {{ 38 | {0, 2}, {1, 3}, 39 | {0, 1}, {2, 3}, 40 | {1, 2}, 41 | }}; 42 | } 43 | }; 44 | } 45 | 46 | #endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT4_H_ 47 | -------------------------------------------------------------------------------- /include/cpp-sort/utility/quicksort_adversary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_QUICKSORT_ADVERSARY_H_ 6 | #define CPPSORT_QUICKSORT_ADVERSARY_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | 14 | namespace cppsort::utility 15 | { 16 | // Implementation of a quicksort adversary as described by M. D. McIlroy 17 | // in *A Killer Adversary for Quicksort* 18 | 19 | template 20 | auto quicksort_adversary(Sorter&& sorter, Integer size) 21 | { 22 | Integer solid = 0; 23 | auto gas = size - 1; 24 | std::vector elements(size, gas); 25 | 26 | std::vector values(size, 0); 27 | std::iota(values.begin(), values.end(), 0); 28 | 29 | int pivot_candidate = size; // Too big to match any 30 | return sorter(values, [&, gas](Integer lhs_idx, Integer rhs_idx) { 31 | int& lhs = elements[lhs_idx]; 32 | int& rhs = elements[rhs_idx]; 33 | if (lhs == gas && rhs == gas) { 34 | if (lhs_idx == pivot_candidate) { 35 | lhs = solid++; 36 | } else { 37 | rhs = solid++; 38 | } 39 | } 40 | if (lhs == gas) { 41 | pivot_candidate = lhs_idx; 42 | } else if (rhs == gas) { 43 | pivot_candidate = rhs_idx; 44 | } 45 | return lhs < rhs; 46 | }); 47 | } 48 | } 49 | 50 | #endif // CPPSORT_QUICKSORT_ADVERSARY_H_ -------------------------------------------------------------------------------- /include/cpp-sort/detail/sorting_network/sort2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT2_H_ 6 | #define CPPSORT_DETAIL_SORTING_NETWORK_SORT2_H_ 7 | 8 | namespace cppsort 9 | { 10 | namespace detail 11 | { 12 | template<> 13 | struct sorting_network_sorter_impl<2> 14 | { 15 | template< 16 | typename RandomAccessIterator, 17 | typename Compare = std::less<>, 18 | typename Projection = utility::identity, 19 | typename = detail::enable_if_t> 22 | > 23 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 24 | Compare compare={}, Projection projection={}) const 25 | -> void 26 | { 27 | iter_swap_if(first, first + 1, compare, projection); 28 | } 29 | 30 | template 31 | [[nodiscard]] 32 | static constexpr auto index_pairs() noexcept 33 | -> std::array, 1> 34 | { 35 | return {{ 36 | {0, 1}, 37 | }}; 38 | } 39 | }; 40 | } 41 | 42 | template<> 43 | struct sorter_traits> 44 | { 45 | using iterator_category = std::random_access_iterator_tag; 46 | using is_always_stable = std::true_type; 47 | }; 48 | } 49 | 50 | #endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT2_H_ 51 | -------------------------------------------------------------------------------- /tests/probes/enc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | TEST_CASE( "measure of disorder: enc", "[probe][enc]" ) 13 | { 14 | using cppsort::probe::enc; 15 | 16 | SECTION( "simple test" ) 17 | { 18 | const std::forward_list li = { 4, 6, 5, 2, 9, 1, 3, 8, 0, 7 }; 19 | CHECK( enc(li) == 3 ); 20 | CHECK( enc(li.begin(), li.end()) == 3 ); 21 | 22 | std::vector> tricky(li.begin(), li.end()); 23 | CHECK( enc(tricky, &internal_compare::compare_to) == 3 ); 24 | } 25 | 26 | SECTION( "upper bound" ) 27 | { 28 | // The upper bound should correspond to half the size 29 | // of the input sequence minus one 30 | 31 | { 32 | // Even number of elements 33 | const std::forward_list li = { 11, 10, 0, 9, 1, 8, 2, 7, 3, 6, 4, 5 }; 34 | auto max_n = enc.max_for_size(cppsort::utility::size(li)); 35 | CHECK( max_n == 6 ); 36 | CHECK( enc(li) == max_n ); 37 | CHECK( enc(li.begin(), li.end()) == max_n ); 38 | } 39 | 40 | { 41 | // Odd number of elements 42 | const std::forward_list li = { 11, 10, 0, 9, 1, 8, 2, 7, 3, 6, 4 }; 43 | auto max_n = enc.max_for_size(cppsort::utility::size(li)); 44 | CHECK( max_n == 5 ); 45 | CHECK( enc(li) == max_n ); 46 | CHECK( enc(li.begin(), li.end()) == max_n ); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/upper_bound.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_UPPER_BOUND_H_ 6 | #define CPPSORT_DETAIL_UPPER_BOUND_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "iterator_traits.h" 16 | #include "lower_bound.h" 17 | 18 | namespace cppsort::detail 19 | { 20 | template 22 | auto upper_bound_n(ForwardIterator first, difference_type_t size, 23 | T&& value, Compare compare, Projection projection) 24 | -> ForwardIterator 25 | { 26 | return lower_bound_n( 27 | first, size, 28 | std::forward(value), 29 | cppsort::not_fn(cppsort::flip(std::move(compare))), 30 | std::move(projection) 31 | ); 32 | } 33 | 34 | template 36 | auto upper_bound(ForwardIterator first, ForwardIterator last, T&& value, 37 | Compare compare, Projection projection) 38 | -> ForwardIterator 39 | { 40 | return lower_bound_n( 41 | first, std::distance(first, last), 42 | std::forward(value), 43 | cppsort::not_fn(cppsort::flip(std::move(compare))), 44 | std::move(projection) 45 | ); 46 | } 47 | } 48 | 49 | #endif // CPPSORT_DETAIL_UPPER_BOUND_H_ 50 | -------------------------------------------------------------------------------- /benchmarks/disorder/plot-distribution.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2023-2025 Morwenn 4 | # SPDX-License-Identifier: MIT 5 | 6 | import argparse 7 | import pathlib 8 | import sys 9 | 10 | from matplotlib import pyplot 11 | 12 | 13 | if __name__ == '__main__': 14 | parser = argparse.ArgumentParser(description="Plot a distribution with a varying parameter.") 15 | parser.add_argument('path', help="path to the file to plot") 16 | args = parser.parse_args() 17 | 18 | path = pathlib.Path(args.path) 19 | if not path.exists(): 20 | print(f"File {path} does not exist") 21 | sys.exit(1) 22 | 23 | with path.open() as fd: 24 | # Read metadata from the first line 25 | mod_name, _, max_disorder, _ = fd.readline().strip().split(',') 26 | 27 | # Read the rest of the file 28 | percentages = [] 29 | disorders = [] 30 | for line in fd: 31 | pct, disorder = line.strip().split(',') 32 | percentages.append(int(pct)) 33 | disorders.append(int(disorder)) 34 | 35 | fix, axes = pyplot.subplots() 36 | axes2 = axes.twinx() 37 | 38 | # Plot the results 39 | axes.plot(percentages, disorders, linestyle='dashed') 40 | axes.ticklabel_format(style='plain') 41 | # Add a tick for the maximum disorder possible 42 | axes2.axhline(int(max_disorder), linestyle=':', color='gray') 43 | axes2.set_yticks([int(max_disorder)], [f"$max({mod_name.capitalize()}(X))$"]) 44 | 45 | pyplot.title(f"${mod_name.capitalize()}(X)$ generated by dist::{mod_name}") 46 | axes.set_ylabel(mod_name.capitalize()) 47 | 48 | pyplot.tight_layout() 49 | pyplot.xlim(left=0, right=100) 50 | axes.set_ylim(bottom=0) 51 | axes2.set_ylim(bottom=0) 52 | pyplot.show() 53 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/min_element.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_MIN_ELEMENT_H_ 6 | #define CPPSORT_DETAIL_MIN_ELEMENT_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include "config.h" 14 | 15 | namespace cppsort::detail 16 | { 17 | template 18 | auto unchecked_min_element(ForwardIterator first, ForwardIterator last, 19 | Compare compare, Projection projection) 20 | -> ForwardIterator 21 | { 22 | // Same algorithm as min_element, but assumes that the 23 | // the input range is never empty 24 | CPPSORT_ASSUME(first != last); 25 | 26 | auto&& comp = utility::as_function(compare); 27 | auto&& proj = utility::as_function(projection); 28 | 29 | auto min = first; 30 | while (++first != last) { 31 | if (comp(proj(*first), proj(*min))) { 32 | min = first; 33 | } 34 | } 35 | return min; 36 | } 37 | 38 | template 39 | auto min_element(ForwardIterator first, ForwardIterator last, 40 | Compare compare, Projection projection) 41 | -> ForwardIterator 42 | { 43 | if (first == last) return last; 44 | return unchecked_min_element(std::move(first), std::move(last), 45 | std::move(compare), std::move(projection)); 46 | } 47 | } 48 | 49 | #endif // CPPSORT_DETAIL_MIN_ELEMENT_H_ 50 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter6.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER6_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER6_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<6u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[3u]), proj0)) { 32 | if (comp(proj(first[4u]), proj0)) { 33 | if (comp(proj(first[5u]), proj0)) { 34 | rotate_left<6u>(first); 35 | } else { 36 | rotate_left<5u>(first); 37 | } 38 | } else { 39 | rotate_left<4u>(first); 40 | } 41 | } else { 42 | if (comp(proj(first[2u]), proj0)) { 43 | rotate_left<3u>(first); 44 | } else { 45 | if (comp(proj(first[1u]), proj0)) { 46 | rotate_left<2u>(first); 47 | } 48 | } 49 | } 50 | } 51 | }; 52 | } 53 | 54 | #endif // CPPSORT_DETAIL_FRONT_INSERTER6_H_ 55 | -------------------------------------------------------------------------------- /tests/testing-tools/algorithm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2021 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_TESTSUITE_ALGORITHM_H_ 6 | #define CPPSORT_TESTSUITE_ALGORITHM_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | 15 | namespace helpers 16 | { 17 | template< 18 | typename Iterator, 19 | typename Compare = std::less<>, 20 | typename Projection = cppsort::utility::identity 21 | > 22 | constexpr auto is_sorted(Iterator first, Iterator last, 23 | Compare compare={}, Projection projection={}) 24 | -> bool 25 | { 26 | auto&& comp = cppsort::utility::as_function(compare); 27 | auto&& proj = cppsort::utility::as_function(projection); 28 | 29 | if (first == last) { 30 | return true; 31 | } 32 | 33 | auto next = first; 34 | while (++next != last) { 35 | if (comp(proj(*next), proj(*first))) { 36 | return false; 37 | } 38 | ++first; 39 | } 40 | return true; 41 | } 42 | 43 | template< 44 | typename ForwardIterator, 45 | typename T, 46 | typename Projection = cppsort::utility::identity 47 | > 48 | auto iota(ForwardIterator first, ForwardIterator last, 49 | T value, Projection projection={}) 50 | -> void 51 | { 52 | auto&& proj = cppsort::utility::as_function(projection); 53 | 54 | while (first != last) 55 | { 56 | proj(*first++) = value; 57 | ++value; 58 | } 59 | } 60 | } 61 | 62 | #endif // CPPSORT_TESTSUITE_ALGORITHM_H_ 63 | -------------------------------------------------------------------------------- /benchmarks/benchmarking-tools/rdtsc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2020 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | 6 | /* 7 | pdqsort.h - Pattern-defeating quicksort. 8 | 9 | Copyright (c) 2015 Orson Peters 10 | 11 | This software is provided 'as-is', without any express or implied warranty. In no event will the 12 | authors be held liable for any damages arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, including commercial 15 | applications, and to alter it and redistribute it freely, subject to the following restrictions: 16 | 17 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the 18 | original software. If you use this software in a product, an acknowledgment in the product 19 | documentation would be appreciated but is not required. 20 | 21 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as 22 | being the original software. 23 | 24 | 3. This notice may not be removed or altered from any source distribution. 25 | */ 26 | 27 | #ifdef _WIN32 28 | #include 29 | inline unsigned long long rdtsc() { 30 | return __rdtsc(); 31 | } 32 | #else 33 | #ifdef __i586__ 34 | static __inline__ unsigned long long rdtsc() { 35 | unsigned long long int x; 36 | __asm__ volatile(".byte 0x0f, 0x31" : "=A" (x)); 37 | return x; 38 | } 39 | #elif defined(__x86_64__) 40 | static __inline__ unsigned long long rdtsc(){ 41 | unsigned hi, lo; 42 | __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); 43 | return ((unsigned long long) lo) | (((unsigned long long) hi) << 32); 44 | } 45 | #else 46 | #error no rdtsc implementation 47 | #endif 48 | #endif 49 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/checkers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_CHECKERS_H_ 6 | #define CPPSORT_DETAIL_CHECKERS_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include "raw_checkers.h" 14 | 15 | namespace cppsort::detail 16 | { 17 | //////////////////////////////////////////////////////////// 18 | // High-level checkers (check with sorter_traits) 19 | 20 | template 21 | struct check_iterator_category_impl {}; 22 | 23 | template 24 | struct check_iterator_category_impl 25 | { 26 | using iterator_category = std::common_type_t< 27 | typename sorter_traits::iterator_category... 28 | >; 29 | }; 30 | 31 | template 32 | struct check_iterator_category: 33 | check_iterator_category_impl< 34 | (has_iterator_category>::value && ...), 35 | Sorters... 36 | > 37 | {}; 38 | 39 | template 40 | struct check_is_always_stable_impl {}; 41 | 42 | template 43 | struct check_is_always_stable_impl 44 | { 45 | using is_always_stable = std::bool_constant< 46 | (typename sorter_traits::is_always_stable{}() && ...) 47 | >; 48 | }; 49 | 50 | template 51 | struct check_is_always_stable: 52 | check_is_always_stable_impl< 53 | (has_is_always_stable>::value && ...), 54 | Sorters... 55 | > 56 | {}; 57 | } 58 | 59 | #endif // CPPSORT_DETAIL_CHECKERS_H_ 60 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_insert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERT_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERT_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | 14 | namespace cppsort::detail 15 | { 16 | // 17 | // Family of fixed-size functions which will try to insert 18 | // *first in the rest of the collection, assuming that the 19 | // following elements are sorted with compare 20 | // 21 | // When there are enough elements, binary search is used to 22 | // locate the position where to insert *first 23 | // 24 | 25 | template 26 | struct front_inserter_n; 27 | 28 | template< 29 | std::size_t N, 30 | typename RandomAccessIterator, 31 | typename Compare, 32 | typename Projection 33 | > 34 | auto front_insert(RandomAccessIterator first, Compare compare, Projection projection) 35 | -> decltype(auto) 36 | { 37 | return front_inserter_n{}(std::move(first), std::move(compare), std::move(projection)); 38 | } 39 | } 40 | 41 | // Specializations of low_moves_sorter_n for some values of N 42 | #include "front_inserter/inserter0.h" 43 | #include "front_inserter/inserter1.h" 44 | #include "front_inserter/inserter2.h" 45 | #include "front_inserter/inserter3.h" 46 | #include "front_inserter/inserter4.h" 47 | #include "front_inserter/inserter5.h" 48 | #include "front_inserter/inserter6.h" 49 | #include "front_inserter/inserter7.h" 50 | #include "front_inserter/inserter8.h" 51 | #include "front_inserter/inserter9.h" 52 | #include "front_inserter/inserter10.h" 53 | #include "front_inserter/inserter11.h" 54 | 55 | #endif // CPPSORT_DETAIL_FRONT_INSERT_H_ 56 | -------------------------------------------------------------------------------- /tests/sorters/merge_insertion_sorter_projection.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEST_CASE( "merge_insertion_sorter tests with projections", 15 | "[merge_insertion_sorter][projection]" ) 16 | { 17 | // Wrapper to hide the integer 18 | using wrapper = generic_wrapper; 19 | 20 | // Collection to sort 21 | std::vector vec; 22 | auto distribution = dist::shuffled{}; 23 | distribution(std::back_inserter(vec), 80); 24 | 25 | SECTION( "sort with random-access range" ) 26 | { 27 | cppsort::merge_insertion_sort(vec, &wrapper::value); 28 | CHECK( helpers::is_sorted(vec.begin(), vec.end(), std::less{}, &wrapper::value) ); 29 | } 30 | 31 | SECTION( "sort with random-access range and compare" ) 32 | { 33 | cppsort::merge_insertion_sort(vec, std::greater{}, &wrapper::value); 34 | CHECK( helpers::is_sorted(vec.begin(), vec.end(), std::greater{}, &wrapper::value) ); 35 | } 36 | 37 | SECTION( "sort with random-access iterators" ) 38 | { 39 | cppsort::merge_insertion_sort(vec.begin(), vec.end(), &wrapper::value); 40 | CHECK( helpers::is_sorted(vec.begin(), vec.end(), std::less{}, &wrapper::value) ); 41 | } 42 | 43 | SECTION( "sort with random-access iterators and compare" ) 44 | { 45 | cppsort::merge_insertion_sort(vec.begin(), vec.end(), 46 | std::greater{}, &wrapper::value); 47 | CHECK( helpers::is_sorted(vec.begin(), vec.end(), std::greater{}, &wrapper::value) ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /benchmarks/benchmarking-tools/cpu_cycles.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "rdtsc.h" 11 | 12 | //////////////////////////////////////////////////////////// 13 | // Tag 14 | 15 | struct cpu_cycles_tag {}; 16 | 17 | //////////////////////////////////////////////////////////// 18 | // Metric 19 | 20 | template 21 | struct cpu_cycles: 22 | cppsort::utility::adapter_storage, 23 | cppsort::detail::check_iterator_category, 24 | cppsort::detail::check_is_always_stable, 25 | cppsort::detail::sorter_facade_fptr< 26 | cpu_cycles, 27 | std::is_empty_v 28 | > 29 | { 30 | using tag_t = cpu_cycles_tag; 31 | using metric_t = cppsort::utility::metric; 32 | 33 | cpu_cycles() = default; 34 | 35 | constexpr explicit cpu_cycles(Sorter sorter): 36 | cppsort::utility::adapter_storage(std::move(sorter)) 37 | {} 38 | 39 | template 40 | auto operator()(Args&&... args) const 41 | -> decltype( 42 | this->get()(std::forward(args)...), 43 | metric_t(std::declval()) 44 | ) 45 | { 46 | auto start = ::rdtsc(); 47 | this->get()(std::forward(args)...); 48 | auto stop = ::rdtsc(); 49 | return metric_t(stop - start); 50 | } 51 | }; 52 | 53 | //////////////////////////////////////////////////////////// 54 | // is_stable specialization 55 | 56 | namespace cppsort 57 | { 58 | template 59 | struct is_stable(Args...)>: 60 | is_stable 61 | {}; 62 | } 63 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/sorting_network/sort3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT3_H_ 6 | #define CPPSORT_DETAIL_SORTING_NETWORK_SORT3_H_ 7 | 8 | namespace cppsort 9 | { 10 | namespace detail 11 | { 12 | template<> 13 | struct sorting_network_sorter_impl<3> 14 | { 15 | template< 16 | typename RandomAccessIterator, 17 | typename Compare = std::less<>, 18 | typename Projection = utility::identity, 19 | typename = detail::enable_if_t> 22 | > 23 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 24 | Compare compare={}, Projection projection={}) const 25 | -> void 26 | { 27 | iter_swap_if(first, first + 1, compare, projection); 28 | iter_swap_if(first + 1, first + 2, compare, projection); 29 | iter_swap_if(first, first + 1, compare, projection); 30 | } 31 | 32 | template 33 | [[nodiscard]] 34 | static constexpr auto index_pairs() noexcept 35 | -> std::array, 3> 36 | { 37 | return {{ 38 | {0, 1}, 39 | {1, 2}, 40 | {0, 1}, 41 | }}; 42 | } 43 | }; 44 | } 45 | 46 | template<> 47 | struct sorter_traits> 48 | { 49 | using iterator_category = std::random_access_iterator_tag; 50 | using is_always_stable = std::true_type; 51 | }; 52 | } 53 | 54 | #endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT3_H_ 55 | -------------------------------------------------------------------------------- /.github/workflows/build-msvc.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021-2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | name: MSVC Builds 5 | 6 | on: 7 | push: 8 | paths: 9 | - '.github/workflows/build-msvc.yml' 10 | - 'CMakeLists.txt' 11 | - 'cmake/**' 12 | - 'examples/**' 13 | - 'include/**' 14 | - 'tests/**' 15 | pull_request: 16 | paths: 17 | - '.github/workflows/build-msvc.yml' 18 | - 'CMakeLists.txt' 19 | - 'cmake/**' 20 | - 'examples/**' 21 | - 'include/**' 22 | - 'tests/**' 23 | 24 | jobs: 25 | build: 26 | runs-on: windows-2022 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | config: 32 | - build_type: Release 33 | - build_type: Debug 34 | exclude: "stable_t" 35 | - build_type: Debug 36 | build_tools: '-T ClangCL' 37 | 38 | steps: 39 | - uses: actions/checkout@v4 40 | 41 | - name: Configure CMake 42 | shell: pwsh 43 | working-directory: ${{runner.workspace}} 44 | run: | 45 | cmake -H${{github.event.repository.name}} -Bbuild ` 46 | -DCMAKE_CONFIGURATION_TYPES=${{matrix.config.build_type}} ` 47 | -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} ` 48 | -G"Visual Studio 17 2022" -A x64 ${{matrix.config.build_tools}} ` 49 | -DCPPSORT_BUILD_TESTING=ON ` 50 | -DCPPSORT_BUILD_EXAMPLES=ON 51 | 52 | - name: Build the test suite 53 | working-directory: ${{runner.workspace}}/build 54 | run: cmake --build . --config ${{matrix.config.build_type}} 55 | 56 | - name: Run the test suite 57 | env: 58 | CTEST_OUTPUT_ON_FAILURE: 1 59 | working-directory: ${{runner.workspace}}/build 60 | run: | 61 | ctest -C ${{matrix.config.build_type}} ` 62 | -E "${{matrix.config.exclude}}" ` 63 | --no-tests=error 64 | -------------------------------------------------------------------------------- /.github/workflows/build-macos.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021-2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | name: MacOS Builds 5 | 6 | on: 7 | push: 8 | paths: 9 | - '.github/workflows/build-macos.yml' 10 | - 'CMakeLists.txt' 11 | - 'cmake/**' 12 | - 'examples/**' 13 | - 'include/**' 14 | - 'tests/**' 15 | pull_request: 16 | paths: 17 | - '.github/workflows/build-macos.yml' 18 | - 'CMakeLists.txt' 19 | - 'cmake/**' 20 | - 'examples/**' 21 | - 'include/**' 22 | - 'tests/**' 23 | 24 | jobs: 25 | build: 26 | runs-on: macos-14 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | config: 32 | # Release build 33 | - cxx: g++-13 34 | build_type: Release 35 | - cxx: clang++ 36 | build_type: Release 37 | # Debug builds 38 | - cxx: clang++ 39 | build_type: Debug 40 | sanitize: address,undefined 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | - uses: rui314/setup-mold@v1 45 | 46 | - name: Configure CMake 47 | working-directory: ${{runner.workspace}} 48 | run: | 49 | export CXX=${{matrix.config.cxx}} 50 | cmake -H${{github.event.repository.name}} -Bbuild \ 51 | -DCMAKE_BUILD_TYPE=${{matrix.config.build_type}} \ 52 | -DCPPSORT_BUILD_TESTING=ON \ 53 | -DCPPSORT_SANITIZE=${{matrix.config.sanitize}} \ 54 | -DCPPSORT_BUILD_EXAMPLES=ON 55 | 56 | - name: Build the test suite 57 | shell: bash 58 | working-directory: ${{runner.workspace}}/build 59 | run: cmake --build . --config ${{matrix.config.build_type}} -j 2 60 | 61 | - name: Run the test suite 62 | env: 63 | CTEST_OUTPUT_ON_FAILURE: 1 64 | working-directory: ${{runner.workspace}}/build 65 | run: ctest -C ${{matrix.config.build_type}} --no-tests=error 66 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/iterator_traits.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_ITERATOR_TRAITS_H_ 6 | #define CPPSORT_DETAIL_ITERATOR_TRAITS_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include "type_traits.h" 15 | 16 | namespace cppsort::detail 17 | { 18 | // 19 | // A few type aliases to make it easier to write 20 | // code that uses std::iterator_traits; there are 21 | // equivalent aliases in the Ranges TS 22 | // 23 | 24 | template 25 | using difference_type_t = typename std::iterator_traits::difference_type; 26 | 27 | template 28 | using value_type_t = typename std::iterator_traits::value_type; 29 | 30 | template 31 | using pointer_t = typename std::iterator_traits::pointer; 32 | 33 | template 34 | using reference_t = typename std::iterator_traits::reference; 35 | 36 | template 37 | using iterator_category_t = typename std::iterator_traits::iterator_category; 38 | 39 | // Addition used by proxy iterators from P0022 40 | template 41 | using rvalue_reference_t = utility::rvalue_reference_t; 42 | 43 | // Additional common type to use instead of value_t 44 | template 45 | using rvalue_type_t = remove_cvref_t>; 46 | 47 | // Handy addition from time to time 48 | template 49 | using projected_t = remove_cvref_t< 50 | std::invoke_result_t())> 51 | >; 52 | } 53 | 54 | #endif // CPPSORT_DETAIL_ITERATOR_TRAITS_H_ 55 | -------------------------------------------------------------------------------- /include/cpp-sort/utility/as_function.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | 6 | // Range v3 library 7 | // 8 | // Copyright Eric Niebler 2013-2014 9 | // 10 | // Use, modification and distribution is subject to the 11 | // Boost Software License, Version 1.0. (See accompanying 12 | // file LICENSE_1_0.txt or copy at 13 | // http://www.boost.org/LICENSE_1_0.txt) 14 | // 15 | // Project home: https://github.com/ericniebler/range-v3 16 | // 17 | #ifndef CPPSORT_UTILITY_AS_FUNCTION_H_ 18 | #define CPPSORT_UTILITY_AS_FUNCTION_H_ 19 | 20 | //////////////////////////////////////////////////////////// 21 | // Headers 22 | //////////////////////////////////////////////////////////// 23 | #include 24 | #include 25 | #include 26 | #include "../detail/type_traits.h" 27 | 28 | namespace cppsort::utility 29 | { 30 | namespace detail 31 | { 32 | struct as_function_fn 33 | { 34 | template 35 | constexpr auto operator()(T&& t) const 36 | noexcept(noexcept(std::mem_fn(t))) 37 | -> cppsort::detail::enable_if_t< 38 | std::is_member_pointer_v>, 39 | decltype(std::mem_fn(t)) 40 | > 41 | { 42 | return std::mem_fn(t); 43 | } 44 | 45 | template 46 | constexpr auto operator()(T&& t) const 47 | noexcept(std::is_nothrow_constructible_v) 48 | -> cppsort::detail::enable_if_t< 49 | not std::is_member_pointer_v>, 50 | T 51 | > 52 | { 53 | return std::forward(t); 54 | } 55 | }; 56 | } 57 | 58 | inline constexpr detail::as_function_fn as_function{}; 59 | } 60 | 61 | #endif // CPPSORT_UTILITY_AS_FUNCTION_H_ 62 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter7.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER7_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER7_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<7u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[3u]), proj0)) { 32 | if (comp(proj(first[5u]), proj0)) { 33 | if (comp(proj(first[6u]), proj0)) { 34 | rotate_left<7u>(first); 35 | } else { 36 | rotate_left<6u>(first); 37 | } 38 | } else { 39 | if (comp(proj(first[4u]), proj0)) { 40 | rotate_left<5u>(first); 41 | } else { 42 | rotate_left<4u>(first); 43 | } 44 | } 45 | } else { 46 | if (comp(proj(first[2u]), proj0)) { 47 | rotate_left<3u>(first); 48 | } else { 49 | if (comp(proj(first[1u]), proj0)) { 50 | rotate_left<2u>(first); 51 | } 52 | } 53 | } 54 | } 55 | }; 56 | } 57 | 58 | #endif // CPPSORT_DETAIL_FRONT_INSERTER7_H_ 59 | -------------------------------------------------------------------------------- /tests/distributions/descending.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with descending distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<7>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::descending{}); 47 | } 48 | -------------------------------------------------------------------------------- /tests/distributions/pipe_organ.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with pipe_organ distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<9>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::pipe_organ{}); 47 | } 48 | -------------------------------------------------------------------------------- /tests/distributions/push_front.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with push_front distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<2>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::push_front{}); 47 | } 48 | -------------------------------------------------------------------------------- /tests/distributions/alternating.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with alternating distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<3>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::alternating{}); 47 | } 48 | -------------------------------------------------------------------------------- /tests/distributions/push_middle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with push_middle distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<3>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::push_middle{}); 47 | } 48 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/sorting_network/sort5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT5_H_ 6 | #define CPPSORT_DETAIL_SORTING_NETWORK_SORT5_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct sorting_network_sorter_impl<5> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | iter_swap_if(first, first + 3, compare, projection); 26 | iter_swap_if(first + 1, first + 4, compare, projection); 27 | iter_swap_if(first, first + 2, compare, projection); 28 | iter_swap_if(first + 1, first + 3, compare, projection); 29 | iter_swap_if(first, first + 1, compare, projection); 30 | iter_swap_if(first + 2, first + 4, compare, projection); 31 | iter_swap_if(first + 1, first + 2, compare, projection); 32 | iter_swap_if(first + 3, first + 4, compare, projection); 33 | iter_swap_if(first + 2, first + 3, compare, projection); 34 | } 35 | 36 | template 37 | [[nodiscard]] 38 | static constexpr auto index_pairs() noexcept 39 | -> std::array, 9> 40 | { 41 | return {{ 42 | {0, 3}, {1, 4}, 43 | {0, 2}, {1, 3}, 44 | {0, 1}, {2, 4}, 45 | {1, 2}, {3, 4}, 46 | {2, 3}, 47 | }}; 48 | } 49 | }; 50 | } 51 | 52 | #endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT5_H_ 53 | -------------------------------------------------------------------------------- /tests/distributions/all_equal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test random-access sorters with all_equal distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<2>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::all_equal{}); 47 | } 48 | -------------------------------------------------------------------------------- /include/cpp-sort/refined.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2021 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_REFINED_H_ 6 | #define CPPSORT_REFINED_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include "detail/type_traits.h" 14 | 15 | namespace cppsort 16 | { 17 | namespace detail 18 | { 19 | //////////////////////////////////////////////////////////// 20 | // Whether a comparator has a member refine method 21 | 22 | template 23 | using has_refine_method_t 24 | = decltype(std::declval().template refine()); 25 | 26 | template 27 | constexpr bool has_refine_method 28 | = is_detected_v; 29 | } 30 | 31 | //////////////////////////////////////////////////////////// 32 | // Return a refined comparison function if possible else 33 | // the original comparison function 34 | 35 | template< 36 | typename T, 37 | typename Function, 38 | typename = detail::enable_if_t> 39 | > 40 | auto refined(Function func) 41 | noexcept(noexcept(func.template refine())) 42 | -> decltype(func.template refine()) 43 | { 44 | return func.template refine(); 45 | } 46 | 47 | template< 48 | typename T, 49 | typename Function, 50 | typename = detail::enable_if_t> 51 | > 52 | constexpr auto refined(Function&& func) noexcept 53 | -> Function&& 54 | { 55 | return std::forward(func); 56 | } 57 | 58 | template 59 | using refined_t = detail::remove_cvref_t< 60 | decltype(refined>(std::declval())) 61 | >; 62 | } 63 | 64 | #endif // CPPSORT_REFINED_H_ 65 | -------------------------------------------------------------------------------- /tests/distributions/shuffled_16_values.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with shuffled_16_values distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<4>, 18 | old_drop_merge_sorter, 19 | cppsort::grail_sorter<>, 20 | cppsort::grail_sorter< 21 | cppsort::utility::dynamic_buffer 22 | >, 23 | cppsort::heap_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::ska_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::spread_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter<>, 41 | cppsort::wiki_sorter< 42 | cppsort::utility::dynamic_buffer 43 | > ) 44 | { 45 | std::vector collection; 46 | helpers::test_distribution(collection, 10'000, dist::shuffled_16_values{}); 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/code-coverage.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020-2025 Morwenn 2 | # SPDX-License-Identifier: MIT 3 | 4 | name: Coverage Upload to Codecov 5 | 6 | on: 7 | push: 8 | branches: 9 | - 2.x.y-develop 10 | - 2.x.y-stable 11 | paths: 12 | - '.github/workflows/code-coverage.yml' 13 | - '.github/.codecov.yml' 14 | - 'CMakeLists.txt' 15 | - 'cmake/**' 16 | - 'include/**' 17 | - 'tests/**' 18 | 19 | jobs: 20 | upload-coverage: 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Checkout project 25 | uses: actions/checkout@v4 26 | - uses: rui314/setup-mold@v1 27 | 28 | - name: Install LCOV 29 | run: sudo apt-get install -y lcov 30 | 31 | - name: Configure CMake 32 | shell: bash 33 | working-directory: ${{runner.workspace}} 34 | run: > 35 | cmake -S ${{github.event.repository.name}} -B build 36 | -DCMAKE_BUILD_TYPE=Debug 37 | -DCPPSORT_BUILD_TESTING=ON 38 | -DCPPSORT_ENABLE_COVERAGE=ON 39 | -DGENINFO_EXTRA_FLAGS="--ignore-errors mismatch,mismatch" 40 | -DLCOV_EXTRA_FLAGS="--ignore-errors unused" 41 | -G"Unix Makefiles" 42 | 43 | - name: Build with coverage 44 | shell: bash 45 | working-directory: ${{runner.workspace}}/build 46 | run: cmake --build . --config Debug -j 2 47 | 48 | - name: Run the test suite 49 | shell: bash 50 | env: 51 | CTEST_OUTPUT_ON_FAILURE: 1 52 | working-directory: ${{runner.workspace}}/build 53 | run: ctest -C Debug --no-tests=error 54 | 55 | - name: Capture coverage info 56 | shell: bash 57 | working-directory: ${{runner.workspace}}/build 58 | run: cmake --build . --target lcov-capture 59 | 60 | - name: Upload coverage info 61 | uses: codecov/codecov-action@v3 62 | with: 63 | token: ${{ secrets.CODECOV_TOKEN }} 64 | directory: ${{runner.workspace}}/build/lcov/data 65 | files: '*.info' 66 | fail_ci_if_error: true 67 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/empty_sorter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_EMPTY_SORTER_H_ 6 | #define CPPSORT_DETAIL_EMPTY_SORTER_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "type_traits.h" 19 | 20 | namespace cppsort::detail 21 | { 22 | //////////////////////////////////////////////////////////// 23 | // Basic empty fixed-size "sorter", generally the one used 24 | // by fixed-size sorters of size 0 or 1, which don't need 25 | // to reorder anything 26 | 27 | struct empty_sorter_impl 28 | { 29 | template< 30 | typename ForwardIterator, 31 | typename Compare = std::less<>, 32 | typename Projection = utility::identity, 33 | typename = detail::enable_if_t> 36 | > 37 | constexpr auto operator()(ForwardIterator, ForwardIterator, 38 | Compare={}, Projection={}) const noexcept 39 | -> void 40 | {} 41 | }; 42 | 43 | //////////////////////////////////////////////////////////// 44 | // Dedicated empty sorter for sorting networks, providing 45 | // additional sorting network-specific functions 46 | 47 | struct empty_network_sorter_impl: 48 | empty_sorter_impl 49 | { 50 | template 51 | [[nodiscard]] 52 | static constexpr auto index_pairs() noexcept 53 | -> std::array, 0> 54 | { 55 | return {}; 56 | } 57 | }; 58 | } 59 | 60 | #endif // CPPSORT_DETAIL_EMPTY_SORTER_H_ 61 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/binary_tree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_BINARY_TREE_H_ 6 | #define CPPSORT_DETAIL_BINARY_TREE_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | 14 | namespace cppsort::detail 15 | { 16 | struct binary_tree_node_base 17 | { 18 | constexpr binary_tree_node_base(binary_tree_node_base* parent, 19 | binary_tree_node_base* left_child, 20 | binary_tree_node_base* right_child) noexcept: 21 | parent(parent), 22 | left_child(left_child), 23 | right_child(right_child) 24 | {} 25 | 26 | // Make tree nodes immovable 27 | binary_tree_node_base(const binary_tree_node_base&) = delete; 28 | binary_tree_node_base(binary_tree_node_base&&) = delete; 29 | binary_tree_node_base& operator=(const binary_tree_node_base&) = delete; 30 | binary_tree_node_base& operator=(binary_tree_node_base&&) = delete; 31 | 32 | // Parent node 33 | binary_tree_node_base* parent; 34 | // Children nodes 35 | binary_tree_node_base* left_child; 36 | binary_tree_node_base* right_child; 37 | }; 38 | 39 | template 40 | struct binary_tree_node: 41 | binary_tree_node_base 42 | { 43 | constexpr binary_tree_node(T&& value, binary_tree_node_base* parent, 44 | binary_tree_node_base* left_child = nullptr, 45 | binary_tree_node_base* right_child = nullptr) 46 | noexcept(std::is_nothrow_move_constructible_v): 47 | binary_tree_node_base(parent, left_child, right_child), 48 | value(std::move(value)) 49 | {} 50 | 51 | // Stored value 52 | T value; 53 | }; 54 | } 55 | 56 | #endif // CPPSORT_DETAIL_BINARY_TREE_H_ 57 | -------------------------------------------------------------------------------- /include/cpp-sort/utility/size.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_UTILITY_SIZE_H_ 6 | #define CPPSORT_UTILITY_SIZE_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "../detail/type_traits.h" 16 | 17 | namespace cppsort::utility 18 | { 19 | namespace detail 20 | { 21 | template 22 | using has_size_method_t = decltype(std::declval().size()); 23 | } 24 | 25 | template< 26 | typename Range, 27 | typename = cppsort::detail::enable_if_t< 28 | cppsort::detail::is_detected_v 29 | > 30 | > 31 | constexpr auto size(const Range& range) 32 | -> decltype(range.size()) 33 | { 34 | return range.size(); 35 | } 36 | 37 | template< 38 | typename Range, 39 | typename = cppsort::detail::enable_if_t< 40 | not cppsort::detail::is_detected_v 41 | > 42 | > 43 | constexpr auto size(const Range& range) 44 | -> decltype(std::distance(std::begin(range), std::end(range))) 45 | { 46 | return std::distance(std::begin(range), std::end(range)); 47 | } 48 | 49 | template< 50 | typename Range, 51 | typename = cppsort::detail::enable_if_t< 52 | not cppsort::detail::is_detected_v 53 | > 54 | > 55 | constexpr auto size(Range& range) 56 | -> decltype(std::distance(std::begin(range), std::end(range))) 57 | { 58 | return std::distance(std::begin(range), std::end(range)); 59 | } 60 | 61 | template 62 | constexpr auto size(const T (&)[N]) noexcept 63 | -> std::size_t 64 | { 65 | return N; 66 | } 67 | } 68 | 69 | #endif // CPPSORT_UTILITY_SIZE_H_ 70 | -------------------------------------------------------------------------------- /tests/sorters/every_sorter_internal_compare.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEMPLATE_TEST_CASE( "test every sorter with a pointer to member function comparison", 15 | "[sorters][as_function]", 16 | cppsort::adaptive_shivers_sorter, 17 | cppsort::cartesian_tree_sorter, 18 | cppsort::d_ary_heap_sorter<4>, 19 | old_drop_merge_sorter, 20 | cppsort::grail_sorter<>, 21 | cppsort::heap_sorter, 22 | cppsort::insertion_sorter, 23 | cppsort::mel_sorter, 24 | cppsort::merge_insertion_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::selection_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::std_sorter, 37 | cppsort::tim_sorter, 38 | old_verge_sorter, 39 | cppsort::wiki_sorter<> ) 40 | { 41 | std::vector> collection; 42 | collection.reserve(35); 43 | auto distribution = dist::shuffled{}; 44 | distribution(std::back_inserter(collection), 35, -47); 45 | 46 | using sorter = TestType; 47 | sorter{}(collection, &internal_compare::compare_to); 48 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 49 | } 50 | -------------------------------------------------------------------------------- /docs/Chainable-projections.md: -------------------------------------------------------------------------------- 1 | Sometimes one needs to apply several transformations to the elements of a collection before comparing them. To support this use case, some projection functions in **cpp-sort** can be composed with `operator|`. 2 | 3 | ```cpp 4 | struct my_negate: 5 | cppsort::utility::projection_base 6 | { 7 | int operator()(int value) const 8 | { 9 | return -value; 10 | } 11 | }; 12 | ``` 13 | 14 | Making a function object `F` inherit from `utility::projection_base` allows it to benefit from the `operator|` overload used to compose projections; the projection inheriting from that class can appear on any side of the operator, and the other argument can be any suitable [*Callable*][callable]. Here is an example of what is possible with the custom projection defined above: 15 | 16 | ```cpp 17 | // Create a vector of wrapper 18 | struct wrapper { int value; }; 19 | std::vector vec = { /* ... */ }; 20 | 21 | my_negate projection; 22 | // Applies &wrapper::value to the elements of vec, then my_negate 23 | cppsort::poplar_sort(vec, &wrapper::value | projection); 24 | ``` 25 | 26 | The object returned by the utility function [`utility::as_projection`][as_projection] also inherits from `utility::projection_base`, making `as_projection` the proper function to turn any suitable projection into a projection composable with `operator|`. If one of the arguments to `operator|` is either [`utility::identity`][utility-identity] or [`std::identity`][std-identity], then the other argument is returned directly. 27 | 28 | If both of the projections composed with `operator|` are [*transparent*][transparent-func], then the returned object is also a *transparent* projection. 29 | 30 | 31 | [as_projection]: Miscellaneous-utilities.md#as_comparison-and-as_projection 32 | [callable]: https://en.cppreference.com/w/cpp/named_req/Callable 33 | [std-identity]: https://en.cppreference.com/w/cpp/utility/functional/identity 34 | [transparent-func]: Comparators-and-projections.md#Transparent-function-objects 35 | [utility-identity]: Miscellaneous-utilities.md#miscellaneous-function-objects 36 | -------------------------------------------------------------------------------- /tests/probes/ham.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | TEST_CASE( "measure of disorder: ham", "[probe][ham]" ) 17 | { 18 | using cppsort::probe::ham; 19 | 20 | SECTION( "simple test" ) 21 | { 22 | const std::forward_list li = { 34, 43, 96, 42, 44, 48, 57, 42, 68, 69 }; 23 | CHECK( ham(li) == 6 ); 24 | CHECK( ham(li.begin(), li.end()) == 6 ); 25 | 26 | std::vector> tricky(li.begin(), li.end()); 27 | CHECK( ham(tricky, &internal_compare::compare_to) == 6 ); 28 | } 29 | 30 | SECTION( "upper bound" ) 31 | { 32 | // The upper bound should correspond to the size of 33 | // the input sequence 34 | 35 | const std::forward_list li = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 36 | auto max_n = ham.max_for_size(cppsort::utility::size(li)); 37 | CHECK( max_n == 11 ); 38 | CHECK( ham(li) == max_n ); 39 | CHECK( ham(li.begin(), li.end()) == max_n ); 40 | } 41 | 42 | SECTION( "regressions" ) 43 | { 44 | std::vector collection; 45 | collection.reserve(100); 46 | auto distribution = dist::ascending_duplicates{}; 47 | distribution(std::back_inserter(collection), 100); 48 | 49 | CHECK( ham(collection) == 0 ); 50 | } 51 | 52 | SECTION( "Example of Ham(subsequence(X)) > Ham(X)" ) 53 | { 54 | const std::forward_list seq = { 3, 1, 2, 0 }; 55 | const std::forward_list subseq = { 3, 1, 2 }; 56 | CHECK( ham(seq) == 2 ); 57 | CHECK( ham(subseq) == 3 ); 58 | } 59 | 60 | rc::prop("Ham(X) ≠ 1", [](const std::vector& sequence) { 61 | return cppsort::probe::ham(sequence) != 1; 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/sized_range.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SIZED_RANGE_H_ 6 | #define CPPSORT_DETAIL_SIZED_RANGE_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include "config.h" 14 | #include "iterator_traits.h" 15 | 16 | namespace cppsort::detail 17 | { 18 | // Very simple class to bundle together a pair of begin/end 19 | // iterators and a corresponding size - shallow constness 20 | 21 | template 22 | class sized_range 23 | { 24 | public: 25 | 26 | constexpr sized_range(ForwardIterator begin, ForwardIterator end, 27 | difference_type_t size): 28 | begin_(begin), 29 | end_(end), 30 | size_(size) 31 | { 32 | using category = iterator_category_t; 33 | if constexpr (std::is_base_of_v) { 34 | CPPSORT_ASSERT(std::distance(begin, end) == size); 35 | } else { 36 | CPPSORT_AUDIT(std::distance(begin, end) == size); 37 | } 38 | } 39 | 40 | constexpr auto begin() const 41 | -> ForwardIterator 42 | { 43 | return begin_; 44 | } 45 | 46 | constexpr auto end() const 47 | -> ForwardIterator 48 | { 49 | return end_; 50 | } 51 | 52 | constexpr auto size() const noexcept 53 | -> difference_type_t 54 | { 55 | return size_; 56 | } 57 | 58 | private: 59 | 60 | ForwardIterator begin_; 61 | ForwardIterator end_; 62 | difference_type_t size_; 63 | }; 64 | } 65 | 66 | #endif // CPPSORT_DETAIL_SIZED_RANGE_H_ 67 | -------------------------------------------------------------------------------- /benchmarks/disorder/check-distribution.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "../benchmarking-tools/distributions.h" 13 | 14 | //////////////////////////////////////////////////////////// 15 | // Configuration variables 16 | 17 | // Distribution 18 | using dist_t = dist::runs; 19 | 20 | // Measure of disorder 21 | auto measure = cppsort::probe::runs; 22 | 23 | // Size of the collections to check 24 | constexpr std::size_t size = 1'000'000; 25 | 26 | //////////////////////////////////////////////////////////// 27 | // Generate data for distribution 28 | // 29 | // The raison d'être of this script is to be able to visualize 30 | // the aspects of the distributions used to test whether some 31 | // sorter adapt to given measures of disorder: for example 32 | // dist::inv, when given a percentage pct, should be able to 33 | // create a random collection X such as: 34 | // prove::inv(X) = pct * probe::inv.max_for_size(|X|) 35 | // 36 | // Being able to visualize these results helps to check the 37 | // soundness of the distributions. 38 | 39 | int main() 40 | { 41 | // Seed the distribution PRNG manually to ensure reproducibility 42 | std::uint_fast32_t seed = std::time(nullptr); 43 | distributions_prng.seed(seed); 44 | 45 | // Print metadata about the check 46 | std::cout << dist_t::name << ',' 47 | << size << ',' 48 | << measure.max_for_size(size) << ',' 49 | << seed << std::endl; 50 | 51 | for (int idx = 0; idx <= 100; ++idx) { 52 | // Generate data distribution 53 | double factor = 0.01 * idx; 54 | auto distribution = dist_t(factor); 55 | 56 | // Compute disorder 57 | std::vector collection; 58 | collection.reserve(size); 59 | distribution(std::back_inserter(collection), size); 60 | auto disorder = measure(collection); 61 | 62 | // Display results 63 | std::cout << idx << ',' << disorder << std::endl; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /include/cpp-sort/probes/sus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_PROBES_SUS_H_ 6 | #define CPPSORT_PROBES_SUS_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../detail/longest_increasing_subsequence.h" 18 | #include "../detail/type_traits.h" 19 | 20 | namespace cppsort::probe 21 | { 22 | namespace detail 23 | { 24 | struct sus_impl 25 | { 26 | template< 27 | typename ForwardIterator, 28 | typename Compare = std::less<>, 29 | typename Projection = utility::identity, 30 | typename = cppsort::detail::enable_if_t< 31 | is_projection_iterator_v 32 | > 33 | > 34 | auto operator()(ForwardIterator first, ForwardIterator last, 35 | Compare compare={}, Projection projection={}) const 36 | -> decltype(auto) 37 | { 38 | // We don't need the size information, so we can avoid 39 | // computing it altogether 40 | auto res = cppsort::detail::longest_increasing_subsequence( 41 | first, last, 42 | 0, // Dummy value, not useful here 43 | cppsort::flip(compare), std::move(projection) 44 | ); 45 | return res.first > 0 ? res.first - 1 : 0; 46 | } 47 | 48 | template 49 | static constexpr auto max_for_size(Integer n) 50 | -> Integer 51 | { 52 | return n == 0 ? 0 : n - 1; 53 | } 54 | }; 55 | } 56 | 57 | inline constexpr sorter_facade sus{}; 58 | } 59 | 60 | #endif // CPPSORT_PROBES_SUS_H_ 61 | -------------------------------------------------------------------------------- /tests/testing-tools/internal_compare.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2024 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_TESTSUITE_INTERNAL_COMPARE_H_ 6 | #define CPPSORT_TESTSUITE_INTERNAL_COMPARE_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | 13 | //////////////////////////////////////////////////////////// 14 | // Class with internal comparison function 15 | // 16 | // Some classes happen to provide a Java-style compareTo 17 | // function to compare the current instance with another 18 | // instance passed as a parameter to this function. The 19 | // class internal_compare is such a class and is used to 20 | // check that the library's algorithms can handle such 21 | // functions passed as a pointer to member function. 22 | // 23 | 24 | template 25 | class internal_compare 26 | { 27 | private: 28 | 29 | // Let it be default-constructed 30 | T value; 31 | 32 | public: 33 | 34 | internal_compare() = default; 35 | 36 | internal_compare(const T& value): 37 | value(value) 38 | {} 39 | 40 | auto operator=(const T& value) 41 | -> internal_compare& 42 | { 43 | this->value = value; 44 | return *this; 45 | } 46 | 47 | auto compare_to(const internal_compare& other) const 48 | -> bool 49 | { 50 | return value < other.value; 51 | } 52 | 53 | friend auto operator<(const internal_compare& lhs, const internal_compare& rhs) 54 | -> bool 55 | { 56 | return lhs.value < rhs.value; 57 | } 58 | 59 | friend auto operator>(const internal_compare& lhs, const internal_compare& rhs) 60 | -> bool 61 | { 62 | return lhs.value > rhs.value; 63 | } 64 | 65 | friend auto operator-(const internal_compare& value) 66 | -> internal_compare 67 | { 68 | return internal_compare(-value.value); 69 | } 70 | }; 71 | 72 | #endif // CPPSORT_TESTSUITE_INTERNAL_COMPARE_H_ 73 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_comparisons/sort3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT3_H_ 6 | #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT3_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include "../rotate_left.h" 12 | #include "../rotate_right.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct low_comparisons_sorter_impl<3u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare = std::less<>, 22 | typename Projection = utility::identity, 23 | typename = detail::enable_if_t> 26 | > 27 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 28 | Compare compare={}, Projection projection={}) const 29 | -> void 30 | { 31 | using utility::iter_swap; 32 | auto&& comp = utility::as_function(compare); 33 | auto&& proj = utility::as_function(projection); 34 | 35 | if (comp(proj(first[1u]), proj(first[0u]))) { 36 | if (comp(proj(first[2u]), proj(first[0u]))) { 37 | if (comp(proj(first[2u]), proj(first[1u]))) { 38 | iter_swap(first, first + 2u); 39 | } else { 40 | rotate_left<3u>(first); 41 | } 42 | } else { 43 | iter_swap(first, first + 1u); 44 | } 45 | } else { 46 | if (comp(proj(first[2u]), proj(first[1u]))) { 47 | if (comp(proj(first[2u]), proj(first[0u]))) { 48 | rotate_right<3u>(first); 49 | } else { 50 | iter_swap(first + 1u, first + 2u); 51 | } 52 | } 53 | } 54 | } 55 | }; 56 | } 57 | 58 | #endif // CPPSORT_DETAIL_LOW_COMPARISONS_SORT3_H_ 59 | -------------------------------------------------------------------------------- /tests/probes/mono.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | TEST_CASE( "measure of disorder: mono", "[probe][mono]" ) 16 | { 17 | using cppsort::probe::mono; 18 | 19 | SECTION( "simple test" ) 20 | { 21 | const std::forward_list li = { 48, 43, 96, 44, 42, 34, 42, 57, 68, 69 }; 22 | CHECK( mono(li) == 2 ); 23 | CHECK( mono(li.begin(), li.end()) == 2 ); 24 | 25 | std::vector> tricky(li.begin(), li.end()); 26 | CHECK( mono(tricky, &internal_compare::compare_to) == 2 ); 27 | } 28 | 29 | SECTION( "lower bound" ) 30 | { 31 | const std::forward_list li1 = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; 32 | CHECK( mono(li1) == 0 ); 33 | } 34 | 35 | SECTION( "upper bound" ) 36 | { 37 | // The upper bound should correspond to: 38 | // size / 2 39 | 40 | const std::forward_list li = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 }; 41 | auto max_n = mono.max_for_size(cppsort::utility::size(li)); 42 | CHECK( max_n == 5 ); 43 | CHECK( mono(li) == max_n ); 44 | CHECK( mono(li.begin(), li.end()) == max_n ); 45 | } 46 | 47 | SECTION( "equal neighbours in the sequence" ) 48 | { 49 | const std::forward_list li = { 0, 0, 0, 1, 2, 3, 4, 6, 5, 3 }; 50 | CHECK( mono(li) == 1 ); 51 | 52 | const std::forward_list li1 = { 6, 5, 4, 3, 2, 2, 2, 2 }; 53 | CHECK( mono(li1) == 0 ); 54 | 55 | const std::forward_list li2 = { 1, 1, 2, 8, 3, 3, 2, 1, 1, 5, 6 }; 56 | CHECK( mono(li2) == 2 ); 57 | } 58 | 59 | rc::prop("Mono(Reversed(X)) = Mono(X)", [](std::vector sequence) { 60 | auto mono_x = mono(sequence); 61 | std::reverse(sequence.begin(), sequence.end()); 62 | return mono(sequence) == mono_x; 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER8_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER8_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<8u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[4u]), proj0)) { 32 | if (comp(proj(first[6u]), proj0)) { 33 | if (comp(proj(first[7u]), proj0)) { 34 | rotate_left<8u>(first); 35 | } else { 36 | rotate_left<7u>(first); 37 | } 38 | } else { 39 | if (comp(proj(first[5u]), proj0)) { 40 | rotate_left<6u>(first); 41 | } else { 42 | rotate_left<5u>(first); 43 | } 44 | } 45 | } else { 46 | if (comp(proj(first[2u]), proj0)) { 47 | if (comp(proj(first[3u]), proj0)) { 48 | rotate_left<4u>(first); 49 | } else { 50 | rotate_left<3u>(first); 51 | } 52 | } else { 53 | if (comp(proj(first[1u]), proj0)) { 54 | rotate_left<2u>(first); 55 | } 56 | } 57 | } 58 | } 59 | }; 60 | } 61 | 62 | #endif // CPPSORT_DETAIL_FRONT_INSERTER8_H_ 63 | -------------------------------------------------------------------------------- /examples/list_selection_sorter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2017 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace example 13 | { 14 | template> 15 | struct list: 16 | std::list 17 | { 18 | // Yes, we cheat and simply inherit from std::list 19 | template 20 | list(Args&&... args): 21 | std::list(std::forward(args)...) 22 | {} 23 | }; 24 | 25 | template> 26 | auto sort(cppsort::selection_sorter, list& collection, 27 | Compare compare={}) 28 | -> void 29 | { 30 | // Dedicated selection sort algorithm for example::list 31 | 32 | auto&& comp = cppsort::utility::as_function(compare); 33 | 34 | auto it = std::begin(collection); 35 | auto last = std::end(collection); 36 | while (it != last) { 37 | auto min_it = std::min_element(it, last, comp); 38 | if (min_it == it) { 39 | ++it; 40 | } else { 41 | collection.splice(it, collection, min_it); 42 | } 43 | } 44 | } 45 | } 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | int main() 56 | { 57 | // Initialize list with random integers 58 | std::vector vec(40, 0); 59 | std::iota(std::begin(vec), std::end(vec), 0); 60 | std::mt19937_64 engine(std::time(nullptr)); 61 | std::shuffle(std::begin(vec), std::end(vec), engine); 62 | example::list li(std::begin(vec), std::end(vec)); 63 | 64 | // Sort with container_aware_adapter 65 | cppsort::container_aware_adapter< 66 | cppsort::selection_sorter 67 | > sorter; 68 | sorter(li); 69 | 70 | assert(std::is_sorted(std::begin(li), std::end(li))); 71 | } 72 | -------------------------------------------------------------------------------- /tests/adapters/hybrid_adapter_many_sorters.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace 13 | { 14 | enum struct sorter_type 15 | { 16 | bidirectional, 17 | random_access 18 | }; 19 | 20 | template 21 | struct random_access_sorter_impl 22 | { 23 | template 24 | auto operator()(Iterator, Iterator) const 25 | -> sorter_type 26 | { 27 | return sorter_type::random_access; 28 | } 29 | 30 | using iterator_category = std::random_access_iterator_tag; 31 | }; 32 | 33 | struct bidirectional_sorter_impl 34 | { 35 | template 36 | auto operator()(Iterator, Iterator) const 37 | -> sorter_type 38 | { 39 | return sorter_type::bidirectional; 40 | } 41 | 42 | using iterator_category = std::bidirectional_iterator_tag; 43 | }; 44 | 45 | template 46 | struct random_access_sorter: 47 | cppsort::sorter_facade> 48 | {}; 49 | 50 | struct bidirectional_sorter: 51 | cppsort::sorter_facade 52 | {}; 53 | } 54 | 55 | TEST_CASE( "hybrid_adapter with many sorters", 56 | "[hybrid_adapter]" ) 57 | { 58 | // Check that hybrid_adapter scales even when there are 59 | // a great many sorters involved, especially check that 60 | // the indices scale well 61 | 62 | cppsort::hybrid_adapter< 63 | bidirectional_sorter, 64 | random_access_sorter<0>, 65 | random_access_sorter<1>, 66 | random_access_sorter<2>, 67 | random_access_sorter<3>, 68 | random_access_sorter<4>, 69 | random_access_sorter<5> 70 | > sorter; 71 | 72 | std::vector vec; 73 | auto res1 = sorter(vec); 74 | CHECK( res1 == sorter_type::random_access ); 75 | 76 | std::list li; 77 | auto res2 = sorter(li); 78 | CHECK( res2 == sorter_type::bidirectional ); 79 | } 80 | -------------------------------------------------------------------------------- /tests/utility/sorting_networks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2022 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEST_CASE( "sorting with index pairs", "[utility][sorting_networks]" ) 15 | { 16 | // Index pairs for a "brick" sorting network to sort 8 inputs 17 | constexpr std::array, 28> pairs = {{ 18 | {0, 1}, {2, 3}, {4, 5}, {6, 7}, 19 | {1, 2}, {3, 4}, {5, 6}, 20 | {0, 1}, {2, 3}, {4, 5}, {6, 7}, 21 | {1, 2}, {3, 4}, {5, 6}, 22 | {0, 1}, {2, 3}, {4, 5}, {6, 7}, 23 | {1, 2}, {3, 4}, {5, 6}, 24 | {0, 1}, {2, 3}, {4, 5}, {6, 7}, 25 | {1, 2}, {3, 4}, {5, 6}, 26 | }}; 27 | 28 | std::vector vec; 29 | auto distribution = dist::shuffled{}; 30 | distribution(std::back_inserter(vec), 8); 31 | 32 | SECTION( "swap_index_pairs" ) 33 | { 34 | cppsort::utility::swap_index_pairs(vec.begin(), pairs); 35 | CHECK( std::is_sorted(vec.begin(), vec.end()) ); 36 | } 37 | 38 | SECTION( "swap_index_pairs_force_unroll" ) 39 | { 40 | cppsort::utility::swap_index_pairs_force_unroll(vec.begin(), pairs); 41 | CHECK( std::is_sorted(vec.begin(), vec.end()) ); 42 | } 43 | } 44 | 45 | TEST_CASE( "sorting with index pairs from sorting_network_sorter", 46 | "[utility][sorting_networks]" ) 47 | { 48 | constexpr auto pairs = cppsort::sorting_network_sorter<8>::index_pairs(); 49 | 50 | std::vector vec; 51 | auto distribution = dist::shuffled{}; 52 | distribution(std::back_inserter(vec), 8); 53 | 54 | SECTION( "swap_index_pairs" ) 55 | { 56 | cppsort::utility::swap_index_pairs(vec.begin(), pairs); 57 | CHECK( std::is_sorted(vec.begin(), vec.end()) ); 58 | } 59 | 60 | SECTION( "swap_index_pairs_force_unroll" ) 61 | { 62 | cppsort::utility::swap_index_pairs_force_unroll(vec.begin(), pairs); 63 | CHECK( std::is_sorted(vec.begin(), vec.end()) ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/comparison_counter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_COMPARISON_COUNTER_H_ 6 | #define CPPSORT_DETAIL_COMPARISON_COUNTER_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cppsort 16 | { 17 | namespace detail 18 | { 19 | template 20 | class comparison_counter 21 | { 22 | public: 23 | 24 | comparison_counter(Compare compare, CountType& count): 25 | compare(std::move(compare)), 26 | count(count) 27 | {} 28 | 29 | template 30 | auto operator()(T&& lhs, U&& rhs) 31 | -> decltype(auto) 32 | { 33 | ++count; 34 | auto&& comp = utility::as_function(compare); 35 | return comp(std::forward(lhs), std::forward(rhs)); 36 | } 37 | 38 | // Accessible member data 39 | Compare compare; 40 | 41 | private: 42 | 43 | // Comparison functions are generally passed by value, 44 | // therefore we need to know where is the original counter 45 | // in order to increment it 46 | CountType& count; 47 | }; 48 | } 49 | 50 | namespace utility 51 | { 52 | template 53 | struct is_probably_branchless_comparison< 54 | cppsort::detail::comparison_counter, 55 | T 56 | >: 57 | // Lie about being branchless if needed: what matters is to get 58 | // an accurate count of the number of comparisons performed by 59 | // algorithms even when not under analysis 60 | is_probably_branchless_comparison 61 | {}; 62 | } 63 | } 64 | 65 | #endif // CPPSORT_DETAIL_COMPARISON_COUNTER_H_ 66 | -------------------------------------------------------------------------------- /include/cpp-sort/utility/apply_permutation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_UTILITY_APPLY_PERMUTATION_H_ 6 | #define CPPSORT_UTILITY_APPLY_PERMUTATION_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include "../detail/config.h" 15 | #include "../detail/iterator_traits.h" 16 | 17 | namespace cppsort::utility 18 | { 19 | template 20 | auto apply_permutation(RandomAccessIterator1 first, [[maybe_unused]] RandomAccessIterator1 last, 21 | RandomAccessIterator2 indices_first, RandomAccessIterator2 indices_last) 22 | -> void 23 | { 24 | using difference_type = cppsort::detail::difference_type_t; 25 | using utility::iter_move; 26 | CPPSORT_ASSERT( (last - first) == (indices_last - indices_first) ); 27 | 28 | auto size = indices_last - indices_first; 29 | for (difference_type idx = 0; idx < size; ++idx) { 30 | if (idx != indices_first[idx]) { 31 | auto current_idx = idx; 32 | auto tmp = iter_move(first + current_idx); 33 | do { 34 | auto next_idx = indices_first[current_idx]; 35 | first[current_idx] = iter_move(first + next_idx); 36 | indices_first[current_idx] = current_idx; 37 | current_idx = next_idx; 38 | } while (idx != indices_first[current_idx]); 39 | indices_first[current_idx] = current_idx; 40 | first[current_idx] = std::move(tmp); 41 | } 42 | } 43 | } 44 | 45 | template 46 | auto apply_permutation(RandomAccessRange1&& range, RandomAccessRange2&& indices) 47 | -> void 48 | { 49 | apply_permutation(std::begin(range), std::end(range), 50 | std::begin(indices), std::end(indices)); 51 | } 52 | } 53 | 54 | #endif // CPPSORT_UTILITY_APPLY_PERMUTATION_H_ 55 | -------------------------------------------------------------------------------- /tests/sorters/every_sorter_non_const_compare.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | TEMPLATE_TEST_CASE( "test extended compatibility with LWG 3031", "[sorters]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | cppsort::d_ary_heap_sorter<7>, 18 | old_default_sorter, 19 | old_drop_merge_sorter, 20 | cppsort::grail_sorter<>, 21 | cppsort::heap_sorter, 22 | cppsort::insertion_sorter, 23 | cppsort::mel_sorter, 24 | cppsort::merge_insertion_sorter, 25 | cppsort::merge_sorter, 26 | cppsort::pdq_sorter, 27 | cppsort::poplar_sorter, 28 | cppsort::quick_merge_sorter, 29 | cppsort::quick_sorter, 30 | cppsort::selection_sorter, 31 | cppsort::slab_sorter, 32 | cppsort::smooth_sorter, 33 | cppsort::spin_sorter, 34 | cppsort::splay_sorter, 35 | old_split_sorter, 36 | cppsort::std_sorter, 37 | cppsort::tim_sorter, 38 | old_verge_sorter, 39 | cppsort::wiki_sorter> ) 40 | { 41 | // LWG3031 allows algorithms taking a predicate to work correctly when 42 | // said predicate takes non-const references on either side as long as 43 | // it doesn't modify its parameters 44 | 45 | std::vector collection; 46 | collection.reserve(50); 47 | auto distribution = dist::shuffled{}; 48 | distribution(std::back_inserter(collection), 50, -25); 49 | 50 | TestType sorter; 51 | sorter(collection, [](int& lhs, int& rhs) { return lhs < rhs; }); 52 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 53 | } 54 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/sorting_network/sort6.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_SORTING_NETWORK_SORT6_H_ 6 | #define CPPSORT_DETAIL_SORTING_NETWORK_SORT6_H_ 7 | 8 | namespace cppsort::detail 9 | { 10 | template<> 11 | struct sorting_network_sorter_impl<6> 12 | { 13 | template< 14 | typename RandomAccessIterator, 15 | typename Compare = std::less<>, 16 | typename Projection = utility::identity, 17 | typename = detail::enable_if_t> 20 | > 21 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 22 | Compare compare={}, Projection projection={}) const 23 | -> void 24 | { 25 | iter_swap_if(first, first + 5, compare, projection); 26 | iter_swap_if(first + 1, first + 3, compare, projection); 27 | iter_swap_if(first + 2, first + 4, compare, projection); 28 | iter_swap_if(first + 1, first + 2, compare, projection); 29 | iter_swap_if(first + 3, first + 4, compare, projection); 30 | iter_swap_if(first, first + 3, compare, projection); 31 | iter_swap_if(first + 2, first + 5, compare, projection); 32 | iter_swap_if(first, first + 1, compare, projection); 33 | iter_swap_if(first + 2, first + 3, compare, projection); 34 | iter_swap_if(first + 4, first + 5, compare, projection); 35 | iter_swap_if(first + 1, first + 2, compare, projection); 36 | iter_swap_if(first + 3, first + 4, compare, projection); 37 | } 38 | 39 | template 40 | [[nodiscard]] 41 | static constexpr auto index_pairs() noexcept 42 | -> std::array, 12> 43 | { 44 | return {{ 45 | {0, 5}, {1, 3}, {2, 4}, 46 | {1, 2}, {3, 4}, 47 | {0, 3}, {2, 5}, 48 | {0, 1}, {2, 3}, {4, 5}, 49 | {1, 2}, {3, 4}, 50 | }}; 51 | } 52 | }; 53 | } 54 | 55 | #endif // CPPSORT_DETAIL_SORTING_NETWORK_SORT6_H_ 56 | -------------------------------------------------------------------------------- /tools/sorting-network-23.tex: -------------------------------------------------------------------------------- 1 | % Copyright (c) 2015-2021 Morwenn 2 | % SPDX-License-Identifier: MIT 3 | 4 | \documentclass{standalone} 5 | \usepackage{tikz} 6 | \usepackage[trim]{tokenizer} 7 | 8 | \input{sorting-network} 9 | 10 | \begin{document} 11 | 12 | \begin{sortingnetwork}{23}{48}{1} 13 | \nodeconnection{{0, 1},{2, 3},{4, 5},{6, 7},{8, 9},{10, 11},{12, 13},{14, 15},{16, 17},{18, 19},{20, 21}} 14 | \nodeconnection{{1, 3},{5, 7},{9, 11},{13, 15},{17, 19}} 15 | \nodeconnection{{0, 2},{4, 6},{8, 10},{12, 14},{16, 18},{20, 22}} 16 | \nodeconnection{{1, 2},{5, 6},{9, 10},{13, 14},{17, 18},{21, 22}} 17 | \nodeconnection{{1, 5},{6, 10},{13, 17},{18, 22}} 18 | \nodeconnection{{2, 6},{14, 18}} 19 | \nodeconnection{{5, 9},{17, 21}} 20 | \nodeconnection{{1, 5},{6, 10},{13, 17},{18, 22}} 21 | \nodeconnection{{0, 4},{7, 11},{12, 16}} 22 | \nodeconnection{{3, 7},{15, 19}} 23 | \nodeconnection{{4, 8},{16, 20}} 24 | \nodeconnection{{0, 4},{7, 11},{12, 16}} 25 | \nodeconnection{{1, 4},{7, 10},{13, 16},{19, 22}} 26 | \nodeconnection{{3, 8},{15, 20}} 27 | \nodeconnection{{2, 3},{8, 9},{14, 15},{20, 21}} 28 | \nodeconnection{{2, 4},{7, 9},{14, 16},{19, 21}} 29 | \nodeconnection{{3, 5},{6, 8},{15, 17},{18, 20}} 30 | \nodeconnection{{3, 4},{5, 6},{7, 8},{15, 16},{17, 18},{19, 20}} 31 | 32 | \nodeconnection{{0, 12}} 33 | \nodeconnection{{1, 13}} 34 | \nodeconnection{{2, 14}} 35 | \nodeconnection{{3, 15}} 36 | \nodeconnection{{4, 16}} 37 | \nodeconnection{{5, 17}} 38 | \nodeconnection{{6, 18}} 39 | \nodeconnection{{7, 19}} 40 | \nodeconnection{{8, 20}} 41 | \nodeconnection{{9, 21}} 42 | \nodeconnection{{10, 22}} 43 | 44 | \nodeconnection{{2, 12}} 45 | \nodeconnection{{3, 13}} 46 | \nodeconnection{{10, 20}} 47 | \nodeconnection{{11, 21}} 48 | 49 | \nodeconnection{{4, 12}} 50 | \nodeconnection{{5, 13}} 51 | \nodeconnection{{6, 14}} 52 | \nodeconnection{{7, 15}} 53 | \nodeconnection{{8, 16}} 54 | \nodeconnection{{9, 17}} 55 | \nodeconnection{{10, 18}} 56 | \nodeconnection{{11, 19}} 57 | \nodeconnection{{8, 12}} 58 | \nodeconnection{{9, 13}} 59 | \nodeconnection{{10, 14}} 60 | \nodeconnection{{11, 15}} 61 | \nodeconnection{{6, 8},{10, 12},{14, 16}} 62 | \nodeconnection{{7, 9},{11, 13},{15, 17}} 63 | 64 | \nodeconnection{{1, 2},{3, 4},{5, 6},{7, 8},{9, 10},{11, 12},{13, 14},{15, 16},{17, 18},{19, 20},{21, 22}} 65 | \end{sortingnetwork} 66 | 67 | \end{document} 68 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2018-2025 Morwenn 4 | # SPDX-License-Identifier: MIT 5 | 6 | import os.path 7 | 8 | from conan import ConanFile 9 | from conan.tools.build import check_min_cppstd 10 | from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout 11 | from conan.tools.files import copy 12 | from conan.tools.microsoft import is_msvc 13 | 14 | required_conan_version = ">=1.50.0" 15 | 16 | 17 | class CppSortConan(ConanFile): 18 | name = "cpp-sort" 19 | version = "2.0.0" 20 | description = "Sorting algorithms & related tools" 21 | license = "MIT" 22 | url = "https://github.com/Morwenn/cpp-sort" 23 | homepage = url 24 | topics = "cpp-sort", "sorting", "algorithms" 25 | author = "Morwenn " 26 | 27 | # Minimal export (excludes tests, coverage, etc.) 28 | exports = ["LICENSE.txt", "NOTICE.txt"] 29 | exports_sources = [ 30 | "include/*", 31 | "CMakeLists.txt", 32 | "cmake/cpp-sort-config.cmake.in" 33 | ] 34 | settings = "os", "compiler", "build_type", "arch" 35 | package_type = "header-library" 36 | no_copy_source = True 37 | 38 | def validate(self): 39 | if self.settings.get_safe("compiler.cppstd"): 40 | check_min_cppstd(self, 17) 41 | 42 | def layout(self): 43 | cmake_layout(self) 44 | 45 | def generate(self): 46 | tc = CMakeToolchain(self) 47 | tc.variables["BUILD_TESTING"] = "OFF" 48 | tc.generate() 49 | 50 | def package(self): 51 | # Install with CMake 52 | cmake = CMake(self) 53 | cmake.configure() 54 | cmake.install() 55 | 56 | # Copy license files 57 | for file in ["LICENSE.txt", "NOTICE.txt"]: 58 | copy(self, file, self.recipe_folder, os.path.join(self.package_folder, "licenses"), keep_path=False) 59 | 60 | def package_info(self): 61 | self.cpp_info.set_property("cmake_file_name", "cpp-sort") 62 | self.cpp_info.set_property("cmake_target_name", "cpp-sort::cpp-sort") 63 | self.cpp_info.bindirs = [] 64 | self.cpp_info.libdirs = [] 65 | 66 | if is_msvc(self): 67 | self.cpp_info.cxxflags = ["/permissive-", "/Zc:preprocessor"] 68 | 69 | def package_id(self): 70 | self.info.clear() # Header-only 71 | -------------------------------------------------------------------------------- /tests/sorters/every_sorter_move_only.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | TEMPLATE_TEST_CASE( "test every sorter with move-only types", "[sorters]", 16 | cppsort::adaptive_shivers_sorter, 17 | cppsort::cartesian_tree_sorter, 18 | cppsort::d_ary_heap_sorter<5>, 19 | old_default_sorter, 20 | old_drop_merge_sorter, 21 | cppsort::grail_sorter<>, 22 | cppsort::heap_sorter, 23 | cppsort::insertion_sorter, 24 | cppsort::mel_sorter, 25 | cppsort::merge_insertion_sorter, 26 | cppsort::merge_sorter, 27 | cppsort::pdq_sorter, 28 | cppsort::poplar_sorter, 29 | cppsort::quick_merge_sorter, 30 | cppsort::quick_sorter, 31 | cppsort::selection_sorter, 32 | cppsort::slab_sorter, 33 | cppsort::smooth_sorter, 34 | cppsort::spin_sorter, 35 | cppsort::splay_sorter, 36 | old_split_sorter, 37 | cppsort::std_sorter, 38 | cppsort::tim_sorter, 39 | old_verge_sorter, 40 | cppsort::wiki_sorter> ) 41 | { 42 | // General test to make sure that every sorter compiles fine with 43 | // move-only types, additionally checking that no read-after-move 44 | // operation is performed and that no self-move is performed 45 | 46 | std::vector> collection; 47 | collection.reserve(491); 48 | auto distribution = dist::shuffled{}; 49 | distribution.call(std::back_inserter(collection), 491, -125); 50 | 51 | TestType sorter; 52 | sorter(collection); 53 | CHECK( std::is_sorted(collection.begin(), collection.end()) ); 54 | } 55 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/spreadsort/detail/constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | 6 | //constant definitions for the Boost Sort library 7 | 8 | // Copyright Steven J. Ross 2001 - 2014 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // (See accompanying file LICENSE_1_0.txt or copy at 11 | // http://www.boost.org/LICENSE_1_0.txt) 12 | 13 | // See http://www.boost.org/libs/sort for library home page. 14 | 15 | #ifndef CPPSORT_DETAIL_SPREADSORT_DETAIL_CONSTANTS_H_ 16 | #define CPPSORT_DETAIL_SPREADSORT_DETAIL_CONSTANTS_H_ 17 | 18 | namespace cppsort::detail::spreadsort::detail 19 | { 20 | //Tuning constants 21 | enum { 22 | //This should be tuned to your processor cache; 23 | //if you go too large you get cache misses on bins 24 | //The smaller this number, the less worst-case memory usage. 25 | //If too small, too many recursions slow down spreadsort 26 | max_splits = 11, 27 | //It's better to have a few cache misses and finish sorting 28 | //than to run another iteration 29 | max_finishing_splits = max_splits + 1, 30 | //Sets the minimum number of items per bin. 31 | int_log_mean_bin_size = 2, 32 | //Used to force a comparison-based sorting for small bins, if it's faster. 33 | //Minimum value 1 34 | int_log_min_split_count = 9, 35 | //This is the minimum split count to use spreadsort when it will finish in one 36 | //iteration. Make this larger the faster std::sort is relative to integer_sort. 37 | int_log_finishing_count = 31, 38 | //Sets the minimum number of items per bin for floating point. 39 | float_log_mean_bin_size = 2, 40 | //Used to force a comparison-based sorting for small bins, if it's faster. 41 | //Minimum value 1 42 | float_log_min_split_count = 8, 43 | //This is the minimum split count to use spreadsort when it will finish in one 44 | //iteration. Make this larger the faster std::sort is relative to float_sort. 45 | float_log_finishing_count = 4, 46 | //There is a minimum size below which it is not worth using spreadsort 47 | min_sort_size = 1000 48 | }; 49 | } 50 | 51 | #endif // CPPSORT_DETAIL_SPREADSORT_DETAIL_CONSTANTS_H_ 52 | -------------------------------------------------------------------------------- /tools/sorting-network-24.tex: -------------------------------------------------------------------------------- 1 | % Copyright (c) 2015-2021 Morwenn 2 | % SPDX-License-Identifier: MIT 3 | 4 | \documentclass{standalone} 5 | \usepackage{tikz} 6 | \usepackage[trim]{tokenizer} 7 | 8 | \input{sorting-network} 9 | 10 | \begin{document} 11 | 12 | \begin{sortingnetwork}{24}{49}{1} 13 | \nodeconnection{{0, 1},{2, 3},{4, 5},{6, 7},{8, 9},{10, 11},{12, 13},{14, 15},{16, 17},{18, 19},{20, 21},{22, 23}} 14 | \nodeconnection{{1, 3},{5, 7},{9, 11},{13, 15},{17, 19},{21, 23}} 15 | \nodeconnection{{0, 2},{4, 6},{8, 10},{12, 14},{16, 18},{20, 22}} 16 | \nodeconnection{{1, 2},{5, 6},{9, 10},{13, 14},{17, 18},{21, 22}} 17 | \nodeconnection{{1, 5},{6, 10},{13, 17},{18, 22}} 18 | \nodeconnection{{2, 6},{14, 18}} 19 | \nodeconnection{{5, 9},{17, 21}} 20 | \nodeconnection{{1, 5},{6, 10},{13, 17},{18, 22}} 21 | \nodeconnection{{0, 4},{7, 11},{12, 16},{19, 23}} 22 | \nodeconnection{{3, 7},{15, 19}} 23 | \nodeconnection{{4, 8},{16, 20}} 24 | \nodeconnection{{0, 4},{7, 11},{12, 16},{19, 23}} 25 | \nodeconnection{{1, 4},{7, 10},{13, 16},{19, 22}} 26 | \nodeconnection{{3, 8},{15, 20}} 27 | \nodeconnection{{2, 3},{8, 9},{14, 15},{20, 21}} 28 | \nodeconnection{{2, 4},{7, 9},{14, 16},{19, 21}} 29 | \nodeconnection{{3, 5},{6, 8},{15, 17},{18, 20}} 30 | \nodeconnection{{3, 4},{5, 6},{7, 8},{15, 16},{17, 18},{19, 20}} 31 | 32 | \nodeconnection{{0, 12}} 33 | \nodeconnection{{1, 13}} 34 | \nodeconnection{{2, 14}} 35 | \nodeconnection{{3, 15}} 36 | \nodeconnection{{4, 16}} 37 | \nodeconnection{{5, 17}} 38 | \nodeconnection{{6, 18}} 39 | \nodeconnection{{7, 19}} 40 | \nodeconnection{{8, 20}} 41 | \nodeconnection{{9, 21}} 42 | \nodeconnection{{10, 22}} 43 | \nodeconnection{{11, 23}} 44 | 45 | \nodeconnection{{2, 12}} 46 | \nodeconnection{{3, 13}} 47 | \nodeconnection{{10, 20}} 48 | \nodeconnection{{11, 21}} 49 | 50 | \nodeconnection{{4, 12}} 51 | \nodeconnection{{5, 13}} 52 | \nodeconnection{{6, 14}} 53 | \nodeconnection{{7, 15}} 54 | \nodeconnection{{8, 16}} 55 | \nodeconnection{{9, 17}} 56 | \nodeconnection{{10, 18}} 57 | \nodeconnection{{11, 19}} 58 | \nodeconnection{{8, 12}} 59 | \nodeconnection{{9, 13}} 60 | \nodeconnection{{10, 14}} 61 | \nodeconnection{{11, 15}} 62 | \nodeconnection{{6, 8},{10, 12},{14, 16}} 63 | \nodeconnection{{7, 9},{11, 13},{15, 17}} 64 | 65 | \nodeconnection{{1, 2},{3, 4},{5, 6},{7, 8},{9, 10},{11, 12},{13, 14},{15, 16},{17, 18},{19, 20},{21, 22}} 66 | \end{sortingnetwork} 67 | 68 | \end{document} 69 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/equal_range.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | 6 | //===----------------------------------------------------------------------===// 7 | // 8 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 9 | // See https://llvm.org/LICENSE.txt for license information. 10 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef CPPSORT_DETAIL_EQUAL_RANGE_H_ 15 | #define CPPSORT_DETAIL_EQUAL_RANGE_H_ 16 | 17 | //////////////////////////////////////////////////////////// 18 | // Headers 19 | //////////////////////////////////////////////////////////// 20 | #include 21 | #include 22 | #include 23 | #include "bitops.h" 24 | #include "iterator_traits.h" 25 | #include "lower_bound.h" 26 | #include "upper_bound.h" 27 | 28 | namespace cppsort::detail 29 | { 30 | template 32 | auto equal_range(ForwardIterator first, ForwardIterator last, const T& value, 33 | Compare compare, Projection projection) 34 | -> std::pair 35 | { 36 | auto&& comp = utility::as_function(compare); 37 | auto&& proj = utility::as_function(projection); 38 | 39 | using difference_type = difference_type_t; 40 | difference_type len = std::distance(first, last); 41 | while (len != 0) { 42 | difference_type l2 = half(len); 43 | ForwardIterator m = first; 44 | std::advance(m, l2); 45 | if (comp(proj(*m), value)) { 46 | first = ++m; 47 | len -= l2 + 1; 48 | } else if (comp(value, proj(*m))) { 49 | last = m; 50 | len = l2; 51 | } else { 52 | ForwardIterator mp1 = m; 53 | return std::pair( 54 | lower_bound(first, m, value, compare, projection), 55 | upper_bound(++mp1, last, value, compare, projection) 56 | ); 57 | } 58 | } 59 | return std::pair(first, first); 60 | } 61 | } 62 | 63 | #endif // CPPSORT_DETAIL_EQUAL_RANGE_H_ 64 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/front_inserter/inserter9.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_FRONT_INSERTER9_H_ 6 | #define CPPSORT_DETAIL_FRONT_INSERTER9_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include "../rotate_left.h" 13 | 14 | namespace cppsort::detail 15 | { 16 | template<> 17 | struct front_inserter_n<9u> 18 | { 19 | template< 20 | typename RandomAccessIterator, 21 | typename Compare, 22 | typename Projection 23 | > 24 | auto operator()(RandomAccessIterator first, Compare compare, Projection projection) const 25 | -> void 26 | { 27 | auto&& comp = utility::as_function(compare); 28 | auto&& proj = utility::as_function(projection); 29 | auto&& proj0 = proj(first[0u]); 30 | 31 | if (comp(proj(first[4u]), proj0)) { 32 | if (comp(proj(first[6u]), proj0)) { 33 | if (comp(proj(first[7u]), proj0)) { 34 | if (comp(proj(first[8u]), proj0)) { 35 | rotate_left<9u>(first); 36 | } else { 37 | rotate_left<8u>(first); 38 | } 39 | } else { 40 | rotate_left<7u>(first); 41 | } 42 | } else { 43 | if (comp(proj(first[5u]), proj0)) { 44 | rotate_left<6u>(first); 45 | } else { 46 | rotate_left<5u>(first); 47 | } 48 | } 49 | } else { 50 | if (comp(proj(first[2u]), proj0)) { 51 | if (comp(proj(first[3u]), proj0)) { 52 | rotate_left<4u>(first); 53 | } else { 54 | rotate_left<3u>(first); 55 | } 56 | } else { 57 | if (comp(proj(first[1u]), proj0)) { 58 | rotate_left<2u>(first); 59 | } 60 | } 61 | } 62 | } 63 | }; 64 | } 65 | 66 | #endif // CPPSORT_DETAIL_FRONT_INSERTER9_H_ 67 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/spreadsort/float_sort.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | 6 | //Templated Spreadsort-based implementation of float_sort and float_mem_cast 7 | 8 | // Copyright Steven J. Ross 2001 - 2014. 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // (See accompanying file LICENSE_1_0.txt or copy at 11 | // http://www.boost.org/LICENSE_1_0.txt) 12 | 13 | // See http://www.boost.org/libs/sort/ for library home page. 14 | 15 | /* 16 | Some improvements suggested by: 17 | Phil Endecott and Frank Gennari 18 | */ 19 | #ifndef CPPSORT_DETAIL_SPREADSORT_FLOAT_SORT_H_ 20 | #define CPPSORT_DETAIL_SPREADSORT_FLOAT_SORT_H_ 21 | 22 | //////////////////////////////////////////////////////////// 23 | // Headers 24 | //////////////////////////////////////////////////////////// 25 | #include 26 | #include 27 | #include "detail/constants.h" 28 | #include "detail/float_sort.h" 29 | #include "../pdqsort.h" 30 | 31 | namespace cppsort::detail::spreadsort 32 | { 33 | /*! 34 | \brief @c float_sort with casting to the appropriate size. 35 | 36 | \param[in] first Iterator pointer to first element. 37 | \param[in] last Iterator pointing to one beyond the end of data. 38 | 39 | Some performance plots of runtime vs. n and log(range) are provided:\n 40 | windows_float_sort 41 | \n 42 | osx_float_sort 43 | 44 | 45 | 46 | \par A simple example of sorting some floating-point is: 47 | \code 48 | vector vec; 49 | vec.push_back(1.0); 50 | vec.push_back(2.3); 51 | vec.push_back(1.3); 52 | spreadsort(vec.begin(), vec.end()); 53 | \endcode 54 | \par The sorted vector contains ascending values "1.0 1.3 2.3". 55 | 56 | */ 57 | template 58 | auto float_sort(RandomAccessIter first, RandomAccessIter last, Projection projection) 59 | -> void 60 | { 61 | if (last - first < detail::min_sort_size) 62 | pdqsort(std::move(first), std::move(last), 63 | std::less{}, std::move(projection)); 64 | else 65 | detail::float_sort(std::move(first), std::move(last), 66 | std::move(projection)); 67 | } 68 | } 69 | 70 | #endif // CPPSORT_DETAIL_SPREADSORT_FLOAT_SORT_H_ 71 | -------------------------------------------------------------------------------- /include/cpp-sort/metrics/running_time.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_METRICS_RUNNING_TIME_H_ 6 | #define CPPSORT_METRICS_RUNNING_TIME_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../detail/checkers.h" 18 | 19 | namespace cppsort::metrics 20 | { 21 | //////////////////////////////////////////////////////////// 22 | // Tag 23 | 24 | struct running_time_tag {}; 25 | 26 | //////////////////////////////////////////////////////////// 27 | // Metric 28 | 29 | template 30 | struct running_time: 31 | utility::adapter_storage, 32 | cppsort::detail::check_iterator_category, 33 | cppsort::detail::check_is_always_stable 34 | { 35 | using tag_t = running_time_tag; 36 | using metric_t = utility::metric; 37 | 38 | running_time() = default; 39 | 40 | constexpr explicit running_time(Sorter sorter): 41 | utility::adapter_storage(std::move(sorter)) 42 | {} 43 | 44 | template 45 | auto operator()(Args&&... args) const 46 | -> decltype( 47 | this->get()(std::forward(args)...), 48 | metric_t(std::declval()) 49 | ) 50 | { 51 | auto start = std::chrono::steady_clock::now(); 52 | this->get()(std::forward(args)...); 53 | auto stop = std::chrono::steady_clock::now(); 54 | return metric_t(std::chrono::duration_cast(stop - start)); 55 | } 56 | }; 57 | } 58 | 59 | namespace cppsort 60 | { 61 | //////////////////////////////////////////////////////////// 62 | // is_stable specialization 63 | 64 | template 65 | struct is_stable(Args...)>: 66 | is_stable 67 | {}; 68 | } 69 | 70 | #endif // CPPSORT_METRICS_RUNNING_TIME_H_ 71 | -------------------------------------------------------------------------------- /include/cpp-sort/detail/low_moves/sort3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #ifndef CPPSORT_DETAIL_LOW_MOVES_SORT3_H_ 6 | #define CPPSORT_DETAIL_LOW_MOVES_SORT3_H_ 7 | 8 | //////////////////////////////////////////////////////////// 9 | // Headers 10 | //////////////////////////////////////////////////////////// 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "../rotate_left.h" 18 | #include "../rotate_right.h" 19 | #include "../type_traits.h" 20 | 21 | namespace cppsort::detail 22 | { 23 | template<> 24 | struct low_moves_sorter_impl<3u> 25 | { 26 | template< 27 | typename RandomAccessIterator, 28 | typename Compare = std::less<>, 29 | typename Projection = utility::identity, 30 | typename = detail::enable_if_t> 33 | > 34 | auto operator()(RandomAccessIterator first, RandomAccessIterator, 35 | Compare compare={}, Projection projection={}) const 36 | -> void 37 | { 38 | using utility::iter_swap; 39 | auto&& comp = utility::as_function(compare); 40 | auto&& proj = utility::as_function(projection); 41 | 42 | if (comp(proj(first[1u]), proj(first[0u]))) { 43 | if (comp(proj(first[2u]), proj(first[0u]))) { 44 | if (comp(proj(first[2u]), proj(first[1u]))) { 45 | iter_swap(first, first + 2u); 46 | } else { 47 | rotate_left<3u>(first); 48 | } 49 | } else { 50 | iter_swap(first, first + 1u); 51 | } 52 | } else { 53 | if (comp(proj(first[2u]), proj(first[1u]))) { 54 | if (comp(proj(first[2u]), proj(first[0u]))) { 55 | rotate_right<3u>(first); 56 | } else { 57 | iter_swap(first + 1u, first + 2u); 58 | } 59 | } 60 | } 61 | } 62 | }; 63 | } 64 | 65 | #endif // CPPSORT_DETAIL_LOW_MOVES_SORT3_H_ 66 | -------------------------------------------------------------------------------- /tests/distributions/ascending.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "test_distribution.h" 13 | 14 | TEMPLATE_TEST_CASE( "test sorter with ascending distribution", "[distributions]", 15 | cppsort::adaptive_shivers_sorter, 16 | cppsort::cartesian_tree_sorter, 17 | // While counting_sort shouldn't be affected by patterns, its 18 | // underlying minmax_element_and_is_sorted function had a bug 19 | // that could specifically appear with an ascending distribution, 20 | // so here is the dedicated test (see issue #103) 21 | cppsort::counting_sorter, 22 | cppsort::d_ary_heap_sorter<5>, 23 | old_drop_merge_sorter, 24 | cppsort::grail_sorter<>, 25 | cppsort::grail_sorter< 26 | cppsort::utility::dynamic_buffer 27 | >, 28 | cppsort::heap_sorter, 29 | cppsort::mel_sorter, 30 | cppsort::merge_sorter, 31 | cppsort::pdq_sorter, 32 | cppsort::poplar_sorter, 33 | cppsort::quick_merge_sorter, 34 | cppsort::quick_sorter, 35 | cppsort::ska_sorter, 36 | cppsort::slab_sorter, 37 | cppsort::smooth_sorter, 38 | cppsort::spin_sorter, 39 | cppsort::splay_sorter, 40 | old_split_sorter, 41 | cppsort::spread_sorter, 42 | cppsort::std_sorter, 43 | cppsort::tim_sorter, 44 | old_verge_sorter, 45 | cppsort::wiki_sorter<>, 46 | cppsort::wiki_sorter< 47 | cppsort::utility::dynamic_buffer 48 | > ) 49 | { 50 | std::vector collection; 51 | helpers::test_distribution(collection, 10'000, dist::ascending{}); 52 | } 53 | -------------------------------------------------------------------------------- /tests/adapters/container_aware_adapter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2025 Morwenn 3 | * SPDX-License-Identifier: MIT 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace foobar 14 | { 15 | template 16 | struct cool_list: 17 | std::list 18 | { 19 | using std::list::list; 20 | }; 21 | 22 | template< 23 | typename T, 24 | typename Compare, 25 | typename = std::enable_if_t< 26 | not cppsort::is_projection_v 27 | > 28 | > 29 | auto sort(cppsort::merge_sorter, cool_list&, Compare) 30 | -> bool 31 | { 32 | return true; 33 | } 34 | } 35 | 36 | TEST_CASE( "basic tests with container_aware_adapter", 37 | "[container_aware_adapter]" ) 38 | { 39 | using sorter_t = cppsort::container_aware_adapter< 40 | cppsort::merge_sorter 41 | >; 42 | sorter_t sorter; 43 | 44 | // Cool list to "sort" 45 | foobar::cool_list collection; 46 | 47 | SECTION( "with comparison" ) 48 | { 49 | CHECK( sorter(collection, std::greater{}) ); 50 | STATIC_CHECK( not cppsort::is_stable_v&, std::greater<>)> ); 51 | } 52 | 53 | SECTION( "with projection" ) 54 | { 55 | CHECK( sorter(collection, std::negate{}) ); 56 | STATIC_CHECK( not cppsort::is_stable_v&, std::negate<>)> ); 57 | } 58 | 59 | SECTION( "with automagic comparison-projection" ) 60 | { 61 | CHECK( sorter(collection, std::greater{}, std::negate{}) ); 62 | STATIC_CHECK( not cppsort::is_stable_v&, 63 | std::greater<>, std::negate<>)> ); 64 | } 65 | 66 | SECTION( "more about stability" ) 67 | { 68 | STATIC_CHECK( cppsort::is_stable_v&)> ); 69 | STATIC_CHECK( cppsort::is_stable_v::iterator, std::list::iterator)> ); 70 | STATIC_CHECK( cppsort::is_stable_v::iterator, 71 | foobar::cool_list::iterator)> ); 72 | } 73 | } 74 | --------------------------------------------------------------------------------