diff --git a/codecov.yml b/.github/.codecov.yml similarity index 100% rename from codecov.yml rename to .github/.codecov.yml diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 10a117a3..85534772 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -64,4 +64,4 @@ jobs: env: CTEST_OUTPUT_ON_FAILURE: 1 working-directory: ${{runner.workspace}}/build - run: ctest -C ${{matrix.config.build_type}} + run: ctest -C ${{matrix.config.build_type}} --no-tests=error diff --git a/.github/workflows/build-mingw.yml b/.github/workflows/build-mingw.yml index d78f9505..0ebcdec7 100644 --- a/.github/workflows/build-mingw.yml +++ b/.github/workflows/build-mingw.yml @@ -51,4 +51,4 @@ jobs: env: CTEST_OUTPUT_ON_FAILURE: 1 working-directory: ${{runner.workspace}}/build - run: ctest -C ${{matrix.build_type}} + run: ctest -C ${{matrix.build_type}} --no-tests=error diff --git a/.github/workflows/build-msvc.yml b/.github/workflows/build-msvc.yml index bb3b05b5..09603a4e 100644 --- a/.github/workflows/build-msvc.yml +++ b/.github/workflows/build-msvc.yml @@ -51,4 +51,4 @@ jobs: env: CTEST_OUTPUT_ON_FAILURE: 1 working-directory: ${{runner.workspace}}/build - run: ctest -C ${{matrix.build_type}} + run: ctest -C ${{matrix.build_type}} --no-tests=error diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 2f8223d2..ce824030 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -23,7 +23,7 @@ on: jobs: build: - runs-on: ubuntu-16.04 + runs-on: ubuntu-18.04 strategy: fail-fast: false @@ -45,6 +45,10 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Install GCC + if: ${{matrix.cxx == 'g++-5'}} + run: sudo apt-get install -y g++-5 + - name: Install Clang if: ${{matrix.cxx == 'clang++-6.0'}} run: sudo apt-get install -y clang-6.0 lld-6.0 @@ -83,6 +87,9 @@ jobs: env: CTEST_OUTPUT_ON_FAILURE: 1 working-directory: ${{runner.workspace}}/build - run: | - ctest -T memcheck -C ${{matrix.config.build_type}} -j 2 - find ./Testing/Temporary -name "MemoryChecker.*.log" -size +1300c | xargs cat; + run: ctest -T memcheck -C ${{matrix.config.build_type}} --no-tests=error -j 2 + + - name: Show Valgrind logs + if: ${{failure() && matrix.config.valgrind == 'ON'}} + working-directory: ${{runner.workspace}}/build + run: find ./Testing/Temporary -name "MemoryChecker.*.log" -size +1300c | xargs cat diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 74d7571d..00930489 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -11,15 +11,12 @@ on: - 2.0.0-develop paths: - '.github/workflows/code-coverage.yml' + - '.github/.codecov.yml' - 'CMakeLists.txt' - 'cmake/**' - - 'codecov.yml' - 'include/**' - 'testsuite/**' -env: - BUILD_TYPE: Debug - jobs: upload-coverage: runs-on: ubuntu-latest @@ -28,32 +25,38 @@ jobs: - name: Checkout project uses: actions/checkout@v2 + - name: Install LCOV + run: sudo apt-get install -y lcov + - name: Configure CMake shell: bash working-directory: ${{runner.workspace}} run: > cmake -H${{github.event.repository.name}} -Bbuild - -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" - -DCPPSORT_ENABLE_COVERAGE=true + -DCMAKE_BUILD_TYPE=Debug + -DCPPSORT_ENABLE_COVERAGE=ON -G"Unix Makefiles" - name: Build with coverage shell: bash working-directory: ${{runner.workspace}}/build - run: cmake --build . --config $BUILD_TYPE -j 2 + run: cmake --build . --config Debug -j 2 - name: Run the test suite shell: bash + env: + CTEST_OUTPUT_ON_FAILURE: 1 working-directory: ${{runner.workspace}}/build - run: ctest -C Release --output-on-failure + run: ctest -C Debug --no-tests=error - - name: Create coverage info + - name: Capture coverage info shell: bash working-directory: ${{runner.workspace}}/build - run: make gcov + run: cmake --build . --target lcov-capture - name: Upload coverage info - uses: codecov/codecov-action@v1.2.1 + uses: codecov/codecov-action@v2 with: - directory: ${{runner.workspace}}/build - functionalities: gcov + directory: ${{runner.workspace}}/build/lcov/data + files: '*.info' + fail_ci_if_error: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eadcf5b..a014b15b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.8.0) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -project(cpp-sort VERSION 1.11.0 LANGUAGES CXX) +project(cpp-sort VERSION 1.12.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) include(GNUInstallDirs) diff --git a/README.md b/README.md index 43f0d3a1..834a6574 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Latest Release](https://img.shields.io/badge/release-1.11.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.11.0) -[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.11.0-blue.svg)](https://conan.io/center/cpp-sort?version=1.11.0) +[![Latest Release](https://img.shields.io/badge/release-1.12.0-blue.svg)](https://github.com/Morwenn/cpp-sort/releases/tag/1.12.0) +[![Conan Package](https://img.shields.io/badge/conan-cpp--sort%2F1.12.0-blue.svg)](https://conan.io/center/cpp-sort?version=1.12.0) [![Code Coverage](https://codecov.io/gh/Morwenn/cpp-sort/branch/develop/graph/badge.svg)](https://codecov.io/gh/Morwenn/cpp-sort) > *It would be nice if only one or two of the sorting methods would dominate all of the others, @@ -47,7 +47,6 @@ Here is a more complete example of what can be done with the library: #include #include #include -#include #include #include #include @@ -71,9 +70,9 @@ int main() sorter(vec, std::greater<>{}, &wrapper::value); assert(std::equal( - std::begin(li), std::end(li), - std::begin(vec), std::end(vec), - [](auto& lhs, auto& rhs) { return lhs.value == rhs.value; } + li.begin(), li.end(), + vec.begin(), vec.end(), + [](const auto& lhs, const auto& rhs) { return lhs.value == rhs.value; } )); } ``` @@ -110,7 +109,7 @@ wiki page](https://github.com/Morwenn/cpp-sort/wiki/Benchmarks). # Compiler support & tooling ![Ubuntu builds status](https://github.com/Morwenn/cpp-sort/workflows/Ubuntu%20Builds/badge.svg?branch=develop) -![Windows builds status](https://github.com/Morwenn/cpp-sort/workflows/Windows%20Builds/badge.svg?branch=develop) +![Windows builds status](https://github.com/Morwenn/cpp-sort/workflows/MSVC%20Builds/badge.svg?branch=develop) ![MacOS builds status](https://github.com/Morwenn/cpp-sort/workflows/MacOS%20Builds/badge.svg?branch=develop) **cpp-sort** requires C++14 support, and should work with the following compilers: @@ -219,6 +218,11 @@ discussion](https://stackoverflow.com/q/2786899/1364752) on StackOverflow and ar backed by the article [*Applying Sorting Networks to Synthesize Optimized Sorting Libraries*](https://arxiv.org/abs/1505.01962). +* The test suite reimplements random number algorithms originally found in the following places: + - [xoshiro256\*\*](https://prng.di.unimi.it/) + - [*Optimal Discrete Uniform Generation from Coin Flips, and Applications*](https://arxiv.org/abs/1304.1916) + - [*All numbers in a given range but random order*](https://stackoverflow.com/a/44821946/1364752) + * The LaTeX scripts used to draw the sorting networks are modified versions of kaayy's [`sortingnetwork.tex`](https://github.com/kaayy/kaayy-s-code-sinppets), slightly adapted to be 0-based and draw the network from top to bottom. diff --git a/benchmarks/errorbar-plot/bench.cpp b/benchmarks/errorbar-plot/bench.cpp index 8df9618d..61015249 100644 --- a/benchmarks/errorbar-plot/bench.cpp +++ b/benchmarks/errorbar-plot/bench.cpp @@ -17,9 +17,9 @@ #include #include #include -#include "distributions.h" -#include "filesystem.h" -#include "statistics.h" +#include "../benchmarking-tools/distributions.h" +#include "../benchmarking-tools/filesystem.h" +#include "../benchmarking-tools/statistics.h" using namespace std::chrono_literals; diff --git a/benchmarks/inversions/inv-bench.cpp b/benchmarks/inversions/inv-bench.cpp index a1df50d6..eb45e5f7 100644 --- a/benchmarks/inversions/inv-bench.cpp +++ b/benchmarks/inversions/inv-bench.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Morwenn + * Copyright (c) 2020-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -14,10 +14,10 @@ #include #include #include -#include "distributions.h" -#include "filesystem.h" -#include "rdtsc.h" -#include "statistics.h" +#include "../benchmarking-tools/distributions.h" +#include "../benchmarking-tools/filesystem.h" +#include "../benchmarking-tools/rdtsc.h" +#include "../benchmarking-tools/statistics.h" using namespace std::chrono_literals; diff --git a/benchmarks/patterns/bench.cpp b/benchmarks/patterns/bench.cpp index 30f4c848..7dcf02b2 100644 --- a/benchmarks/patterns/bench.cpp +++ b/benchmarks/patterns/bench.cpp @@ -36,8 +36,8 @@ #include #include #include -#include "distributions.h" -#include "rdtsc.h" +#include "../benchmarking-tools/distributions.h" +#include "../benchmarking-tools/rdtsc.h" // Type of data to sort during the benchmark using value_t = double; diff --git a/benchmarks/small-array/benchmark.cpp b/benchmarks/small-array/benchmark.cpp index 99addbb8..712ae60e 100644 --- a/benchmarks/small-array/benchmark.cpp +++ b/benchmarks/small-array/benchmark.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -16,8 +16,8 @@ #include #include #include -#include "distributions.h" -#include "rdtsc.h" +#include "../benchmarking-tools/distributions.h" +#include "../benchmarking-tools/rdtsc.h" using namespace std::chrono_literals; diff --git a/cmake/FindGcov.cmake b/cmake/FindGcov.cmake index 6ffd6eac..db03e534 100644 --- a/cmake/FindGcov.cmake +++ b/cmake/FindGcov.cmake @@ -1,7 +1,7 @@ # This file is part of CMake-codecov. # # Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany +# 2015-2020 RWTH Aachen University, Federal Republic of Germany # # See the LICENSE file in the package base directory for details # @@ -19,7 +19,7 @@ set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) foreach (LANG ${ENABLED_LANGUAGES}) - # Gcov evaluation is dependend on the used compiler. Check gcov support for + # Gcov evaluation is dependent on the used compiler. Check gcov support for # each compiler that is used. If gcov binary was already found for this # compiler, do not try to find it again. if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN) @@ -35,7 +35,7 @@ foreach (LANG ${ENABLED_LANGUAGES}) find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov HINTS ${COMPILER_PATH}) - elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang") + elseif ("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "^(Apple)?Clang$") # Some distributions like Debian ship llvm-cov with the compiler # version appended as llvm-cov-x.y. To find this binary we'll build # the suggested binary name with the compiler version. @@ -105,7 +105,8 @@ endif (NOT TARGET gcov) # Gcov on any source file of once and store the gcov file in the same # directory. function (add_gcov_target TNAME) - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) + get_target_property(TBIN_DIR ${TNAME} BINARY_DIR) + set(TDIR ${TBIN_DIR}/CMakeFiles/${TNAME}.dir) # We don't have to check, if the target has support for coverage, thus this # will be checked by add_coverage_target in Findcoverage.cmake. Instead we @@ -135,14 +136,18 @@ function (add_gcov_target TNAME) set(BUFFER "") + set(NULL_DEVICE "/dev/null") + if(WIN32) + set(NULL_DEVICE "NUL") + endif() foreach(FILE ${SOURCES}) get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH) # call gcov add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov - COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null + COMMAND ${GCOV_ENV} ${GCOV_BIN} -p ${TDIR}/${FILE}.gcno > ${NULL_DEVICE} DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno - WORKING_DIRECTORY ${FILE_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) list(APPEND BUFFER ${TDIR}/${FILE}.gcov) diff --git a/cmake/FindLcov.cmake b/cmake/FindLcov.cmake index beb925ae..244d1c20 100644 --- a/cmake/FindLcov.cmake +++ b/cmake/FindLcov.cmake @@ -1,7 +1,7 @@ # This file is part of CMake-codecov. # # Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany +# 2015-2020 RWTH Aachen University, Federal Republic of Germany # # See the LICENSE file in the package base directory for details # @@ -53,7 +53,7 @@ include(FindPackageHandleStandardArgs) find_program(LCOV_BIN lcov) find_program(GENINFO_BIN geninfo) find_program(GENHTML_BIN genhtml) -find_package_handle_standard_args(lcov +find_package_handle_standard_args(Lcov REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN ) @@ -159,7 +159,9 @@ function (lcov_capture_initial_tgt TNAME) set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) + get_target_property(TBIN_DIR ${TNAME} BINARY_DIR) + set(TDIR ${TBIN_DIR}/CMakeFiles/${TNAME}.dir) + set(GENINFO_FILES "") foreach(FILE ${SOURCES}) # generate empty coverage files @@ -249,8 +251,9 @@ function (lcov_capture_tgt TNAME) set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}") set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}") + get_target_property(TBIN_DIR ${TNAME} BINARY_DIR) + set(TDIR ${TBIN_DIR}/CMakeFiles/${TNAME}.dir) - set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir) set(GENINFO_FILES "") foreach(FILE ${SOURCES}) # Generate coverage files. If no .gcda file was generated during @@ -258,14 +261,20 @@ function (lcov_capture_tgt TNAME) set(OUTFILE "${TDIR}/${FILE}.info") list(APPEND GENINFO_FILES ${OUTFILE}) + # Create an empty .gcda file, so the target capture file can have a dependency on it. + # The capture file will only use this .gcda if it has a non-zero size (test -s). + add_custom_command(OUTPUT "${TDIR}/${FILE}.gcda" + COMMAND "${CMAKE_COMMAND}" -E touch "${TDIR}/${FILE}.gcda" + ) + add_custom_command(OUTPUT ${OUTFILE} - COMMAND test -f "${TDIR}/${FILE}.gcda" + COMMAND test -s "${TDIR}/${FILE}.gcda" && ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory ${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcda || cp ${OUTFILE}.init ${OUTFILE} - DEPENDS ${TNAME} ${TNAME}-capture-init + DEPENDS ${TNAME} ${TNAME}-capture-init "${TDIR}/${FILE}.gcda" COMMENT "Capturing coverage data for ${FILE}" ) endforeach() diff --git a/cmake/Findcodecov.cmake b/cmake/Findcodecov.cmake index fa135fa8..89f9198d 100644 --- a/cmake/Findcodecov.cmake +++ b/cmake/Findcodecov.cmake @@ -1,7 +1,7 @@ # This file is part of CMake-codecov. # # Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany +# 2015-2020 RWTH Aachen University, Federal Republic of Germany # # See the LICENSE file in the package base directory for details # @@ -68,13 +68,18 @@ endif () -# Find the reuired flags foreach language. +# Find the required flags foreach language. set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY}) get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) foreach (LANG ${ENABLED_LANGUAGES}) - # Coverage flags are not dependend on language, but the used compiler. So + if (NOT ${LANG} MATCHES "^(C|CXX|Fortran)$") + message(STATUS "Skipping coverage for unsupported language: ${LANG}") + continue() + endif () + + # Coverage flags are not dependent on language, but the used compiler. So # instead of searching flags foreach language, search flags foreach compiler # used. set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) @@ -133,7 +138,15 @@ set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) # Helper function to get the language of a source file. function (codecov_lang_of_source FILE RETURN_VAR) - get_filename_component(FILE_EXT "${FILE}" EXT) + # Usually, only the last extension of the file should be checked, to avoid + # template files (i.e. *.t.cpp) are checked with the full file extension. + # However, this feature requires CMake 3.14 or later. + set(EXT_COMP "LAST_EXT") + if(${CMAKE_VERSION} VERSION_LESS "3.14.0") + set(EXT_COMP "EXT") + endif() + + get_filename_component(FILE_EXT "${FILE}" ${EXT_COMP}) string(TOLOWER "${FILE_EXT}" FILE_EXT) string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) @@ -241,8 +254,13 @@ function(add_coverage_target TNAME) list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}") endforeach() - set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES - "${CLEAN_FILES}") + if(${CMAKE_VERSION} VERSION_LESS "3.15.0") + set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES + "${CLEAN_FILES}") + else() + set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES + "${CLEAN_FILES}") + endif() add_gcov_target(${TNAME}) diff --git a/cmake/llvm-cov-wrapper b/cmake/llvm-cov-wrapper index 2ac33102..f1cca5ef 100644 --- a/cmake/llvm-cov-wrapper +++ b/cmake/llvm-cov-wrapper @@ -3,7 +3,7 @@ # This file is part of CMake-codecov. # # Copyright (c) -# 2015-2017 RWTH Aachen University, Federal Republic of Germany +# 2015-2020 RWTH Aachen University, Federal Republic of Germany # # See the LICENSE file in the package base directory for details # @@ -19,7 +19,7 @@ fi # Get LLVM version to find out. LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \ - | sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g") + | sed "s/^\([A-Za-z ]*\)\([0-9]*\).\([0-9]*\).*$/\2.\3/g") if [ "$1" = "-v" ] then diff --git a/conanfile.py b/conanfile.py index deb99e82..0c0cc3b4 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ class CppSortConan(ConanFile): name = "cpp-sort" - version = "1.11.0" + version = "1.12.0" description = "Additional sorting algorithms & related tools" topics = "conan", "cpp-sort", "sorting", "algorithms" url = "https://github.com/Morwenn/cpp-sort" diff --git a/docs/Benchmarks.md b/docs/Benchmarks.md index a0c1316d..43be3e95 100644 --- a/docs/Benchmarks.md +++ b/docs/Benchmarks.md @@ -1,6 +1,6 @@ *Note: this page only benchmarls sorting algorithms under specific conditions. It can be used as a quick guide but if you really need a fast algorithm for a specific use case, you better run your own benchmarks.* -*Last meaningful update: 1.9.0 release.* +*Last meaningful update: 1.9.0 release, 1.12.0 for measures of presortedness.* Benchmarking is hard and I might not be doing it right. Moreover, benchmarking sorting algorithms highlights that the time needed to sort a collection of elements depends on several things: the type to sort, the size of the collection, the cost of comparing two values, the cost of moving an element, the patterns formed by the distribution of the values in the collection to sort, the type of the collection itself, etc. The aim of this page is to help you choose a sorting algorithm depending on your needs. You can find two main kinds of benchmarks: the ones that compare algorithms against shuffled collections of different sizes, and the ones that compare algorithms against different data patterns for a given collection size. @@ -169,11 +169,12 @@ The spikes in the otherwise smooth sorting networks curve when sorting arrays of This benchmark for [measures of presortedness][measures-of-presortedness] is small and only intends to show the cost that these tools might incur. It is not meant to be exhaustive in any way. -![Benchmark speed of measures of presortedness for increasing size for std::vector](https://i.imgur.com/5Hxpb37.png) +![Benchmark speed of measures of presortedness for increasing size for std::vector](https://i.imgur.com/pjc7zJF.png) -While the graph reasonably shows the relative cost of the different measures of presortedness, there are a few hidden traps: -* *Par(X)* seems to beat every other measure, but it is a highly adaptative O(n² log n) algorithm, whose theoretical worst case might be the worst of all measures of presortedness. -* *Dis(X)* looks like a O(n) algorithm in this graph, but it is actually a O(n²) algorithm with extremely efficient short-circuits in most cases. Its worst case would put it closer from *Osc(X)*. +It makes rather easy to see the different groups of complexities: +* *Run(X)* and *Mono(X)* are obvious O(n) algorithms. +* *Dis(X)* is a more involved O(n) algorithm. +* All of the other measures of presortedness run in O(n log n) time. [measures-of-presortedness]: https://github.com/Morwenn/cpp-sort/wiki/Measures-of-presortedness diff --git a/docs/Changelog.md b/docs/Changelog.md index 5e936fda..6cf986ad 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -50,7 +50,7 @@ When compiled with C++17, **cpp-sort** might gain a few additional features depe This feature is available when the feature-testing macro `__cpp_nontype_template_parameter_auto` is defined. -* [[`sorter_facade`|Sorter facade]] range overloads can now be used in `constexpr` functions. +* [`sorter_facade`][sorter-facade] range overloads can now be used in `constexpr` functions. There is no specific feature macro available to test this, it starts working when `std::begin` and `std::end` are `constexpr`. @@ -59,6 +59,11 @@ When compiled with C++17, **cpp-sort** might gain a few additional features depe The C++17 traits are used as is when the feature-test macro `__cpp_lib_is_invocable` is defined. +**Size improvements:** +* When used in different translation units, [`smooth_sorter`][smooth-sorter] might produce fewer duplicates and consume less binary size in C++17. + + This optimization is available when the feature-testing macro `__cpp_inline_variables` is available. + ## C++20 features When compiled with C++20, **cpp-sort** might gain a few additional features depending on the level of C++20 support provided by the compiler. The availability of those features depends on the presence of corresponding [feature-testing macros][feature-test-macros] when possible, even though some checks are more granular. Don't hesitate to open an issue if your compiler and standard library supports one of those features but it doesn't seem to work in **cpp-sort**. @@ -94,11 +99,12 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe [feature-test-macros]: https://wg21.link/SD6 [pdq-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#pdq_sorter [ska-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#ska_sorter + [sorter-facade]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-facade [std-greater-void]: https://en.cppreference.com/w/cpp/utility/functional/greater_void [std-identity]: https://en.cppreference.com/w/cpp/utility/functional/identity [std-less-void]: https://en.cppreference.com/w/cpp/utility/functional/less_void [std-mem-fn]: https://en.cppreference.com/w/cpp/utility/functional/mem_fn [std-ranges-greater]: https://en.cppreference.com/w/cpp/utility/functional/ranges/greater [std-ranges-less]: https://en.cppreference.com/w/cpp/utility/functional/ranges/less - [std-string-view]: https://en.cppreference.com/w/cpp/string/basic_string_view) + [std-string-view]: https://en.cppreference.com/w/cpp/string/basic_string_view [utility-iter-move]: https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#iter_move-and-iter_swap diff --git a/docs/Comparators.md b/docs/Comparators.md index 0139fb1a..27eedde5 100644 --- a/docs/Comparators.md +++ b/docs/Comparators.md @@ -132,7 +132,7 @@ The two-parameter version of the customization point calls the three-parameter o [binary-predicate]: https://en.cppreference.com/w/cpp/concept/BinaryPredicate - [branchless-traits]https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#branchless-traits + [branchless-traits]: https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#branchless-traits [callable]: https://en.cppreference.com/w/cpp/named_req/Callable [case-sensitivity]: https://en.wikipedia.org/wiki/Case_sensitivity [cppcon2015-compare]: https://github.com/CppCon/CppCon2015/tree/master/Presentations/Comparison%20is%20not%20simple%2C%20but%20it%20can%20be%20simpler%20-%20Lawrence%20Crowl%20-%20CppCon%202015 diff --git a/docs/Home.md b/docs/Home.md index a7d33604..c9239310 100644 --- a/docs/Home.md +++ b/docs/Home.md @@ -1,4 +1,4 @@ -Welcome to the **cpp-sort 1.11.0** documentation! +Welcome to the **cpp-sort 1.12.0** documentation! You probably read the introduction in the README, so I won't repeat it here. This wiki contains documentation about the library: basic documentation about the many sorting tools and how to use them, documentation about the additional utilities provided by the library and even some detailed tutorials if you ever want to write your own sorters or sorter adapters. This main page explains a few general things that didn't quite fit in other parts of the documentation. diff --git a/docs/Measures-of-presortedness.md b/docs/Measures-of-presortedness.md index dae70cd1..9f275b59 100644 --- a/docs/Measures-of-presortedness.md +++ b/docs/Measures-of-presortedness.md @@ -4,7 +4,7 @@ Given a measure of presortedness *M*, a comparison sort is said to be *M*-optima ## Formal definition -Measures of presortedness were formally defined by Manilla in *Measures of presortedness and optimal sorting algorithms*. Here is the formal definition as reformulated by La rocca & Cantone in [*NeatSort - A practical adaptive algorithm*][neatsort]: +Measures of presortedness were formally defined by H. Mannila in *Measures of presortedness and optimal sorting algorithms*. Here is the formal definition as reformulated by M. La Rocca & D. Cantone in [*NeatSort - A practical adaptive algorithm*][neatsort]: > Given two sequences *X* and *Y* of distinct elements, a measure of disorder *M* is a function that satisfies the following properties: > @@ -18,7 +18,7 @@ A few measures of presortedness described in the research papers actually return ### Partial ordering of measures of presortedness -La rocca & Cantone also define a partial order on measure of presortedness as follows: +La Rocca & Cantone also define a partial order on measures of presortedness as follows: Let *M1* and *M2* be two measures of presortedness. - *M1* is algorithmically finer than *M2* if and only if any *M1*-optimal algorithm is also *M2*-optimal. @@ -31,7 +31,7 @@ The graph below shows the partial ordering of several measures of presortedness: ![Partial ordering of measures of presortedness](https://github.com/Morwenn/cpp-sort/wiki/images/mops-partial-ordering.png) -This graph is more complete version of the one in *A framework for adaptive sorting* by O. Petersson and A. Moffat. The *Max* ≡ *Dis* equivalence comes from [*NeatSort - A practical adaptive algorithm*][neatsort] by La rocca & Cantone. The relations of *Mono* are empirically derived [original research][original-research]. +This graph is a modified version of the one in *A framework for adaptive sorting* by O. Petersson and A. Moffat. The relations of *Mono* are empirically derived [original research][original-research] and incomplete (unknown relations with *Max*, *Osc* and *SUS*). The measures of presortedness in bold in the graph are available in **cpp-sort**, the others are not. @@ -85,6 +85,26 @@ Measures of presortedness are pretty formalized, so the names of the functions i In the following descriptions we use *X* to represent the input sequence, and |*X*| to represent the size of that sequence. +### *Block* + +```cpp +#include +``` + +Computes the number of elements in a sequence that aren't followed by the same element in the sorted sequence. + +Our implementation is slightly different from the original description in *Sublinear merging and natural mergesort* by S. Carlsson, C. Levcopoulos and O. Petersson: +* It doesn't add 1 to the general result, thus returning 0 when *X* is sorted - therefore respecting the Mannila definition of a MOP. +* It explicitly handles elements that compare equivalent, while the original formal definition makes it difficult. + +| Complexity | Memory | Iterators | +| ----------- | ----------- | ------------- | +| n log n | n | Forward | + +`max_for_size`: |*X*| - 1 when *X* is sorted in reverse order. + +*New in version 1.12.0* + ### *Dis* ```cpp @@ -95,21 +115,24 @@ Computes the maximum distance determined by an inversion. | Complexity | Memory | Iterators | | ----------- | ----------- | ------------- | -| n² | 1 | Forward | +| n | n | Bidirectional | +| n log n | 1 | Forward | -`max_for_size`: |*X*| - 1 when *X* is sorted in reverse order. +When enough memory is available, `probe::dis` runs in O(n), otherwise it falls back to an O(n log n) algorithm that does not require extra memory. If forward iterators are passed, the O(n log n) algorithm is always used. -*Warning: this algorithm might be noticeably slower when the passed iterable is not random-access.* +`max_for_size`: |*X*| - 1 when the last element of *X* is smaller than the first one. *Changed in version 1.8.0:* `probe::dis` is now O(n²) instead of accidentally being O(n³) when passed forward or bidirectional iterators. +*Changed in version 1.12.0:* `probe::dis` is now O(n log n) instead of O(n²). When sorting bidirectional iterators, if enough heap memory is available, it runs in O(n) time and O(n) space. + ### *Enc* ```cpp #include ``` -Computes the number of encroaching lists that can be extracted from *X* minus one (see Skiena's *Encroaching lists as a measure of presortedness*). +Computes the number of encroaching lists that can be extracted from *X* minus one (see *Encroaching lists as a measure of presortedness* by S. Skiena). | Complexity | Memory | Iterators | | ----------- | ----------- | ------------- | @@ -183,11 +206,11 @@ Computes the maximum distance an element in *X* must travel to find its sorted p #include ``` -Computes the number of non-increasing and non-decreasing runs in *X* minus one. +Computes the number of non-increasing and non-decreasing consecutive runs of adjacent elements that need to be removed from *X* to make it sorted The measure of presortedness is slightly different from its original description in [*Sort Race*][sort-race] by H. Zhang, B. Meng and Y. Liang: -* It subtracts 1 from the number of runs, thus returning 0 when *X* is sorted -* It explicitly handles non-increasing and non-decreasing runs, not only the strictly increasing or decreasing ones +* It subtracts 1 from the number of runs, thus returning 0 when *X* is sorted. +* It explicitly handles non-increasing and non-decreasing runs, not only the strictly increasing or decreasing ones. | Complexity | Memory | Iterators | | ----------- | ----------- | ------------- | @@ -203,33 +226,28 @@ The measure of presortedness is slightly different from its original description #include ``` -Computes the *Oscillation* measure described by Levcopoulos and Petersson in *Adaptive Heapsort*. +Computes the *Oscillation* measure described by C. Levcopoulos and O. Petersson in *Adaptive Heapsort*. | Complexity | Memory | Iterators | | ----------- | ----------- | ------------- | +| n log n | n | Forward | | n² | 1 | Forward | +When there isn't enough extra memory available, `probe::osc` falls back to an in-place O(n²) algorithm. + `max_for_size`: (|*X*| * (|*X*| - 2) - 1) / 2 when the values in *X* are strongly oscillating. +***WARNING:** the O(n²) fallback of `probe::osc` is deprecated since version 1.12.0 and removed in version 2.0.0.* + +*Changed in version 1.12.0:* `probe::osc` is now O(n log n) instead of O(n²) but now also requires O(n) memory. The O(n²) is kept for backward compatibility but will be removed in the future. + ### *Par* ```cpp #include ``` -Computes the *Par* measure described by Estivill-Castro and Wood in *A New Measure of Presortedness* as follows: - -> *Par(X)* = min { *p* | *X* is *p*-sorted } - -The following definition is also given to determine whether a sequence is *p*-sorted: - -> *X* is *p*-sorted iff for all *i*, *j* ∈ {1, 2, ..., |*X*|}, *i* - *j* > *p* implies *Xj* ≤ *Xi*. - -| Complexity | Memory | Iterators | -| ----------- | ----------- | ------------- | -| n² log n | 1 | Random-access | - -`max_for_size`: |*X*| - 1 when the last element of *X* is smaller than the first one. +***WARNING:** `probe::par` is deprecated since version 1.12.0 and removed in version 2.0.0, use [`probe::dis`][probe-dis] instead.* ### *Rem* @@ -267,7 +285,7 @@ Computes the number of non-decreasing runs in *X* minus one. Computes the minimum number of non-decreasing subsequences (of possibly not adjacent elements) into which *X* can be partitioned. It happens to correspond to the size of the [longest decreasing subsequence][longest-increasing-subsequence] of *X*. -*SUS* stands for *Shuffled Up-Sequences* and was introduced in *Sorting Shuffled Monotone Sequences* by Levcopoulos and Petersson. +*SUS* stands for *Shuffled Up-Sequences* and was introduced in *Sorting Shuffled Monotone Sequences* by C. Levcopoulos and O. Petersson. | Complexity | Memory | Iterators | | ----------- | ----------- | ------------- | @@ -277,9 +295,34 @@ Computes the minimum number of non-decreasing subsequences (of possibly not adja *New in version 1.10.0* +## Other measures of presortedness + +Some additional measures of presortedness how been described in the literature but do not appear in the partial ordering graph. This section describes some of them but is not an exhaustive list. + +### *Par* + +*Par* was described by V. Estivill-Castro and D. Wood in *A New Measure of Presortedness* as follows: + +> *Par(X)* = min { *p* | *X* is *p*-sorted } + +The following definition is also given to determine whether a sequence is *p*-sorted: + +> *X* is *p*-sorted iff for all *i*, *j* ∈ {1, 2, ..., |*X*|}, *i* - *j* > *p* implies *Xj* ≤ *Xi*. + +*Right invariant metrics and measures of presortedness* by V. Estivill-Castro, H. Mannila and D. Wood mentions that: + +> In fact, *Par*(*X*) = *Dis*(*X*), for all *X*. + +In their subsequent papers, those authors consistently use *Dis* instead of *Par*, often accompanied by a link to *A New Measure of Presortedness*. + +### *Radius* + +T. Altman and Y. Igarashi mention the concept of *k*-sortedness and the measure *Radius*(*X*) in *Roughly Sorting: Sequential and Parallel Approach*. However *k*-sortedness is the same as *p*-sortedness, and *Radius* is just another name for *Par* (and thus for *Dis*). + [hamming-distance]: https://en.wikipedia.org/wiki/Hamming_distance [longest-increasing-subsequence]: https://en.wikipedia.org/wiki/Longest_increasing_subsequence [neatsort]: https://arxiv.org/pdf/1407.6183.pdf [original-research]: https://github.com/Morwenn/cpp-sort/wiki/Original-research#partial-ordering-of-mono + [probe-dis]: https://github.com/Morwenn/cpp-sort/wiki/Measures-of-presortedness#dis [sort-race]: https://arxiv.org/ftp/arxiv/papers/1609/1609.04471.pdf diff --git a/docs/Original-research.md b/docs/Original-research.md index 73aa233f..ba45c941 100644 --- a/docs/Original-research.md +++ b/docs/Original-research.md @@ -6,7 +6,7 @@ You can find some experiments and interesting pieces of code [in my Gist][morwen One of the main observations which naturally occured as long as I was putting together this library was about the best complexity tradeoffs between time and memory depending on the iterator categories of the different sorting algorithms (only taking comparison sorts into account): * Algorithms that work on random-access iterators can run in O(n log n) time with O(1) extra memory, and can even be stable with such guarantees (block sort being the best example). -* Unstable algorithms that work on bidirectional iterators can run in O(n log n) time with O(1) extra memory: QuickMergesort [can be implemented][https://github.com/Morwenn/quick_merge_sort] with a bottom-up mergesort and a raw median-of-medians algorithms (instead of the introselect mutual recursion). +* Unstable algorithms that work on bidirectional iterators can run in O(n log n) time with O(1) extra memory: QuickMergesort [can be implemented][quick-merge-sort] with a bottom-up mergesort and a raw median-of-medians algorithms (instead of the introselect mutual recursion). * Stable algorithms that work on bidirectional iterators can run in O(n log n) time with O(n) extra memory (mergesort), or in O(n log² n) time with O(1) extra memory (mergesort with in-place merge). * Stable algorithms that work on forward iterators can get down to the same time and memory complexities than the ones working on bidirectional iterators: mergesort works just as well. * Unstable algorithms that work on forward iterators can run in O(n log² n) time and O(1) space, QuickMergesort being once again the prime example of such an algorithm. @@ -205,14 +205,14 @@ The measure of presortedness *Mono* is described in [*Sort Race*][sort-race] by > Intuitively, if *Mono*(*X*) = *k*, then *X* is the concatenation of *k* monotonic lists (either sorted or reversely sorted). -It counts the number of ascending or descending runs in *X*. Technically this definition in the paper makes it return 1 when the *X* is sorted, which goes against the original definition of a measure of presortedness by Manilla, which starts with the following condition: +It computes the number of ascending or descending runs in *X*. Technically the definition in the paper makes it return 1 when the *X* is sorted, which goes against the original definition of a measure of presortedness by Mannila, which starts with the following condition: > If *X* is sorted, then *M*(*X*) = 0 Therefore we redefine *Mono*(*X*) as the number of non-increasing and non-decreasing consecutive runs of adjacent elements that need to be removed from *X* to make it sorted. - ***Mono* ⊇ *Runs***: this relation is already mentioned in *Sort Race* and rather intuitive: since *Mono* detects both non-increasing and non-decreasing runs, it is as least as good as *Runs* that only detects non-decreasing runs. - ***SMS* ⊇ *Mono***: this one seems intuitive too: *SMS* which removes runs of non-adjacent elements should be at least as good as *Mono* which only removes runs of adjacent elements. -- ***Enc* ⊇ *Mono***: when making encroaching lists, *Enc* is guaranteed to not create no more than one such new list per non-increasing or non-decreasing runs, so the result will be at most as big as that of *Mono*. However *Enc* can also find presortedness in patterns such as {5, 6, 4, 7, 3, 8, 2, 9, 1, 10} where *Mono* will find maximum disorder. Therefore *Enc* is strictly better than *Mono*. +- ***Enc* ⊇ *Mono***: when making encroaching lists, *Enc* is guaranteed to create no more than one such new list per non-increasing or non-decreasing run found in *X*, so the result will be at most as big as that of *Mono*. However *Enc* can also find presortedness in patterns such as {5, 6, 4, 7, 3, 8, 2, 9, 1, 10} where *Mono* will find maximum disorder. Therefore *Enc*(*X*) should always be at most as big as *Mono*(*X*). The following relations can be transitively deduced from the results presented in *A framework for adaptive sorting*: - ***Mono* ⊋ *Exc***: we know that *SMS* ⊇ *Mono* and *SMS* ⊋ *Exc* diff --git a/docs/Sorter-adapters.md b/docs/Sorter-adapters.md index 33aee2a8..c1e0026d 100644 --- a/docs/Sorter-adapters.md +++ b/docs/Sorter-adapters.md @@ -225,51 +225,57 @@ using sorter = cppsort::hybrid_adapter< #include ``` -This adapter takes a sorter and alters its behavior (if needed) to produce a stable sorter. It does so by associating every element of the collection to sort to its starting position and, whenever two elements compare equivalent, the algorithm compares the starting positions of the elements to ensure that their relative starting positions are preserved. Compared to a raw sorter, it requires O(n) additional space to store the starting positions. +Those *sorter adapters* are similar in that they all take any *sorter* and produce a *resulting sorter* that is guaranteed to implement a stable sort. From lower level to higher level: +* `make_stable`: artifically make a sorter stable +* `stable_adapter`: main customization point +* `stable_t`: higher-level interface -If the *adapted sorter* already implements a stable sorting algorithm when called with a specific set of parameters (if [`is_stable`][is-stable] is `std::true_type` for the parameters), then the *resulting sorter* will call the *adapted sorter* directly. +The *resulting sorter* is always stable and returns the result of the *adapted sorter* if any. -`stable_adapter` and its specializations might expose a `type` member type which aliases the *adapter sorter* or some intermediate sorter which is always stable, or the *resulting sorter* otherwise. Its goal is to provide the least nested type that is known to always be stable in order to sometimes skip some template nesting. - -The *resulting sorter* is always stable. +```cpp +template +struct make_stable; +``` -`stable_adapter` returns the result of the *adapted sorter* if any. +`make_stable` takes a sorter and artificially alters its behavior to produce a stable sorter. It does so by associating every element of the collection to sort to its starting position and then uses the *adapted sorter* to sort the collection with a special comparator: whenever two elements compare equivalent, it compares the starting positions of the elements to ensure that their relative starting positions are preserved. Storing the starting positions requires O(n) additional space. ```cpp template struct stable_adapter; ``` -One can provide a dedicated stable algorithm by explicitly specializing `stable_adapter` to bypass the automatic transformation and allow optimizations, which can be useful when a pair of stable and unstable sorting algorithms are closely related. For example, while [`std_sorter`][std-sorter] calls [`std::sort`][std-sort], the explicit specialization `stable_adapter` calls [`std::stable_sort`][std-stable-sort] instead. In **cpp-sort**, `stable_adapter` has specializations for the following components: +`stable_adapter` is the main customization point of those stable sorting facilities, and as such can be specialized to provide stable versions of your own unstable sorters or adapters. **cpp-sort** itself provides `stable_adapter` specializations for the following components: * [`default_sorter`][default-sorter] -* [`std_sorter`][std-sorter] +* [`std_sorter`][std-sorter] (calls [`std::stable_sort`][std-stable-sort] instead of [`std::sort`][std-sort]) * [`verge_sorter`][verge-sorter] * [`hybrid_adapter`][hybrid-adapter] * [`self_sort_adapter`][self-sort-adapter] +* `stable_adapter` itself (automatic unnesting) * [`verge_adapter`][verge-adapter] -If such a user specialization is provided, it shall alias `is_always_stable` to `std::true_type` and provide a `type` member type which follows the rules mentioned earlier. +Specializations of `stable_adapter` must provide an `is_always_stable` member type aliasing [`std::true_type`][std-true-type]. Additionally, they might expose a `type` member type aliasing either the *adapted sorter* or some intermediate sorter which is guaranteed to always be stable (it can also alias the `stable_adapter` specialization itself, but it is generally useless). Its goal is to provide the least nested type that is known to always be stable in order to sometimes skip some template nesting. When present, this `::type` must be constructible from an instance of the *adapted sorter* (note however that `stable_adapter::type` does not have to be constructible from an instance of `stable_adapter`!). -While `stable_adapter` is the "high-level" adapter to use whenever one wants a stable sorting algorithm, the header also provides `make_stable`, which directly exposes the raw mechanism used to transform an unstable sorter into a stable one without applying any of the short-circuits described above: +The main `stable_adapter` template uses [`is_stable`][is-stable] when called to check whether the *adapted sorter* produces a stable sorter when called with a given set of parameters. If the call is already stable then th *adapted sorter* is used directly otherwise `make_stable` is used to artificially turn it into a stable sort. -```cpp -template -struct make_stable; -``` - -Contrary to `stable_adapter`, `make_stable` isn't meant to be specialized by end users. In C++17, `make_stable` like most of the other adapters can take advantage of deduction guides. - -The header also provides the `stable_t` type alias, which either alias the passed sorter if it is always stable, or `stable_adapter` otherwise. +![Visual explanation of what stable_adapter does](https://github.com/Morwenn/cpp-sort/wiki/images/stable_adapter.png) ```cpp template using stable_t = /* implementation-defined */; ``` -*New in version 1.9.0:* `stable_t` +`stable_t` is the recommended way to obtain a stable sorter from any sorter. Its goal is to alias the "most nested" type that can be used as stable version of the *adapted sorter*. As such it aliases (let `Sorter` be the *adapted sorter*): +* `Sorter::type` if `Sorter` is a `stable_adapter` specialization and such a member type exists. +* `Sorter` otherwise, if it is guaranteed [to always be stable][is-always-stable]. +* `stable_adapter::type` otherwise, if such a member type exists. +* `stable_adapter` otherwise. + +This little dance sometimes allows to reduce the nesting of function calls and to get better error messages in some places (it notably unwraps nested top-level `stable_adapter`). As such `stable_t` is generally a better alternative to `stable_adapter` from a consumer point of view. + +![Visual explanation of what stable_t aliases](https://github.com/Morwenn/cpp-sort/wiki/images/stable_t.png) -It is roughly equivalent to `stable_adapter::type`, except that it doesn't instantiate `stable_adapter` when it doesn't need to. It can be used to reduce the template nesting and improve error messages in some places, and as such is often a better alternative to a raw `stable_adapter`. +*New in version 1.9.0:* `stable_t` and `stable_adapter::type` ### `verge_adapter` @@ -297,17 +303,19 @@ When wrapped into [`stable_adapter`][stable-adapter], it has a slightly differen [fixed-size-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Fixed-size-sorters [fixed-sorter-traits]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-traits#fixed_sorter_traits [hybrid-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#hybrid_adapter + [is-always-stable]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-traits#is_always_stable [is-stable]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-traits#is_stable [issue-104]: https://github.com/Morwenn/cpp-sort/issues/104 [low-moves-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Fixed-size-sorters#low_moves_sorter [mountain-sort]: https://github.com/Morwenn/mountain-sort [schwartzian-transform]: https://en.wikipedia.org/wiki/Schwartzian_transform - [stable-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#stable_adapter + [stable-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#stable_adapter-make_stable-and-stable_t [self-sort-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#self_sort_adapter [std-index-sequence]: https://en.cppreference.com/w/cpp/utility/integer_sequence [std-sort]: https://en.cppreference.com/w/cpp/algorithm/sort [std-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#std_sorter [std-stable-sort]: https://en.cppreference.com/w/cpp/algorithm/stable_sort + [std-true-type]: https://en.cppreference.com/w/cpp/types/integral_constant [verge-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#verge_adapter [verge-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#verge_sorter [vergesort-fallbacks]: https://github.com/Morwenn/vergesort/blob/master/fallbacks.md diff --git a/docs/Sorter-traits.md b/docs/Sorter-traits.md index c2f69e0c..b18c6aa7 100644 --- a/docs/Sorter-traits.md +++ b/docs/Sorter-traits.md @@ -106,7 +106,7 @@ constexpr bool is_comparison_projection_sorter_iterator_v ### `sorter_traits` -The class template `sorter_traits` contains information about *sorters* and *[[sorter adapters|Sorter adapters]]* such as the kind of iterators accepted by a sorter and whether it implements or not a stable sorting algorithm. +The class template `sorter_traits` contains information about *[[sorters|Sorters]]* and *[[sorter adapters|Sorter adapters]]* such as the kind of iterators accepted by a sorter and whether it is guaranteed to always sort stably. ```cpp template @@ -114,11 +114,12 @@ struct sorter_traits; ``` This class template peeks into `Sorter` to extract the following types: - * `using iterator_category = typename Sorter::iterator_category;` * `using is_always_stable = typename Sorter::is_always_stable;` -This class is a bit different than the trait classes in the standard library: if one of the types above doesn't exist in the passed sorter, it won't exist in `sorter_traits` either. That means that the traits are not tightly coupled: if a sorter doesn't define `is_always_stable` but defines `iterator_category`, it can still be used in [`hybrid_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#hybrid_adapter); instantiating the corresponding `sorter_traits` won't cause a compile-time error because of the missing `is_always_stable`. +Its behaviour is however a bit different from that of the trait classes in the standard library: if one of the types above doesn't exist in the passed sorter, it won't exist in the corresponding `sorter_traits` specialization either. That means that the traits are not tightly coupled: for example if a sorter doesn't define `is_always_stable` but defines `iterator_category`, it can still be used in [`hybrid_adapter`][hybrid-adapter]; instantiating the corresponding `sorter_traits` won't cause a compile-time error because of the missing `is_always_stable`. + +`sorter_traits` can be specialized for user-defined *sorters* or *sorter adapters*, and those specialization are sometimes the only place where the information exists. As such, `sorter_traits` is the preferred way to query information about a sorter - it's all the more important as **cpp-sort** itself sometimes fully relies on a `sorter_traits` specialization to provide some information. The in-class traits should be seen as convenient short-hands, but `sorter_traits` as the main source of truth - it is considered an error if both exist and differ. ### `iterator_category` @@ -127,9 +128,9 @@ template using iterator_category = typename sorter_traits::iterator_category; ``` -Some tools need to know which category of iterators a sorting algorithm can work with. Therefore, a well-defined sorter shall provide one of the standard library [iterator tags](https://en.cppreference.com/w/cpp/iterator/iterator_tags) in order to document that. +Some tools need to know which category of iterators a sorting algorithm can work with. A sorter that intends to work with those tools must document its iterator category by aliasing one of the standard library [iterator tags][iterator-tags]. -When a sorter is adapted so that it may be used with several categories of iterators, the resulting sorter's iterator category will correspond to the most permissive among the original sorters. For example, if an [`hybrid_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#hybrid_adapter) merges sorting algorithms with `std::forward_iterator_tag` and `std::random_access_iterator_tag`, the resulting sorter's category will be `std::forward_iterator_tag` since it is guaranteed to work with any iterable type which has *at least* forward iterators. +The iterator category of the *resulting sorter* of a [[*sorter adapter*|Sorter adapters]] doesn't always match that of the *adapted sorter*: for example [`heap_sorter`][heap-sorter] only accepts random-access iterators, but it accepts forward iterators when wrapped into [`out_of_place_adapter`][out-of-place-adapter]. ### `is_always_stable` @@ -142,9 +143,9 @@ constexpr bool is_always_stable_v = is_always_stable::value; ``` -This type trait is always either [`std::true_type`](https://en.cppreference.com/w/cpp/types/integral_constant) or `std::false_type` and tells whether a sorting algorithm is always [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability) or not. This information may be useful in some contexts, most notably to make sure that [`stable_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#stable_adapter) can use a stable sorter directly instead of artificially making it stable. +This type trait is always either [`std::true_type` or `std::false_type`][integral-constant] and tells whether a sorter is always [stable][stability] or not. This information may be useful in some contexts, and is most notably by [`stable_t`][stable-adapter] to avoid unnecessarily nesting templates when possible. -When a sorter adapter is used, the *resulting sorter* is stable if and only if its stability can be guaranteed and unstable otherwise, even when the *adapted sorter* may be stable (for example, [`self_sort_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#self_sort_adapter)'s `is_always_stable` is aliased to `std::false_type` since it is impossible to guarantee the stability of every `sort` method). +When a sorter adapter is used, the *resulting sorter* is considered always stable if and only if its stability can be guaranteed, and considered unstable otherwise, even when the *adapted sorter* may be stable (for example, [`self_sort_adapter`][self-sort-adapter]`::is_always_stable` is aliased to `std::false_type` since it is impossible to guarantee the stability of every collection's `sort` method). ### `is_stable` @@ -161,16 +162,16 @@ template constexpr bool is_stable_v = is_stable::value; ``` -This trait is a more flexible version of `is_always_stable`: it tells whether a given sorter will use a stable sorting algorithm when called with a specific set of parameters. I can be used as follows: +This trait is a more flexible version of [`is_always_stable`][is-always-stable]: it tells whether a given sorter uses a stable sorting algorithm when called with a specific set of parameters. It can be used as follows: ```cpp -using sorter = self_sort_adapter; +using sorter = self_sort_adapter; static_assert(is_stable&)>, ""); ``` -[`self_sort_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#self_sort_adapter) is a sorter adapter that checks whether a container can sort itself and, if so, uses the container's sorting method instead of the adapted sorter. As a matter of fact, `std::list::sort` implements a stable sorting algorithm (and there is a tweak in **cpp-sort** to take that information into account), so `is_stable&)>` will inherit from `std::true_type`. However, `is_stable&)>` or `is_stable::iterator, std::list::iterator)>` will inherit from `std::false_type` instead. +[`self_sort_adapter`][self-sort-adapter] is a [[*sorter adapter*|Sorter adapters]] that checks whether a container can sort itself and, if so, uses the container's sorting method instead of the *adapted sorter*. As a matter of fact, [`std::list::sort`][std-list-sort] implements a stable sorting algorithm and **cpp-sort** specializes `is_stable` to take that information into account, so `is_stable_v&)>` is `true` despite `is_always_stable_v` being `false`. However, `is_stable_v&)>` and `is_stable_v::iterator, std::list::iterator)>` remain `false`. -The default version of `is_stable` will use `sorter_traits::is_always_stable` to infer the stability of a sorter, but most sorter adapters have dedicated specializations. These specializations allow [`stable_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#stable_adapter) to sometimes avoid `make_stable` and to instead use the *adapted sorter* directly when it knows that calling it with specific parameters already yields a stable sort. +The default version of `is_stable` uses `sorter_traits::is_always_stable` to infer the stability of a sorter, but most sorter adapters have dedicated specializations. These specializations notably allow [`stable_adapter`][stable-adapter] to sometimes avoid using `make_stable` and to instead use the *adapted sorter* directly when it knows that calling it with specific parameters already yields a stable sort. ### `rebind_iterator_category` @@ -179,7 +180,7 @@ template struct rebind_iterator_category; ``` -This class allows to get a sorter similar to the one passed but with a stricter iterator category. For example, it can generate a sorter whose iterator category is `std::bidirectional_iterator_tag` from another sorter whose iterator category is `std::forward_iterator_tag`. It is mostly useful to make several sorters with the same iterator category work for different iterator categories in an [`hybrid_adapter`](https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#hybrid_adapter). Let's say we have two sorters, `foo_sorter` and `bar_sorter`; both have the iterator category `std::forward_iterator_tag`, but `foo_sorter` is the best to sort forward iterators while `bar_sorter` benefits from some optimizations for bidirectional and random-access iterators. It is possible to use the best of both with the following sorter: +This class allows to get a sorter similar to the one passed but with a stricter [iterator category][iterator-tags]. For example, it can generate a sorter whose iterator category is `std::bidirectional_iterator_tag` from another sorter whose iterator category is `std::forward_iterator_tag`. It is mostly useful to make several sorters with the same iterator category work for different iterator categories in [`hybrid_adapter`][hybrid-adapter]. Let's say we have two sorters, `foo_sorter` and `bar_sorter`; both have the iterator category `std::forward_iterator_tag`, but `foo_sorter` is the best to sort forward iterators while `bar_sorter` benefits from some optimizations for bidirectional and random-access iterators. It is possible to use the best of both with the following sorter: ```cpp using sorter = cppsort::hybrid_adapter< @@ -191,7 +192,7 @@ using sorter = cppsort::hybrid_adapter< >; ``` -The sorter above will pick `foo_sorter` for forward iterators, but will pick `bar_sorter` for bidirectional and random-access iterators. A compile-time error will occur if one tries to rebind to an iterator category that is not derived from the sorter's original iterator category. +The sorter above will pick `foo_sorter` for forward iterators, but `bar_sorter` for bidirectional and random-access iterators. A compile-time error occurs when one tries to rebind a sorter to a less strict iterator category than its own. ### `fixed_sorter_traits` @@ -208,6 +209,19 @@ struct fixed_sorter_traits; This class template can be specialized for any fixed-size sorter and exposes the following properties: -* `domain`: a specialization of [`std::index_sequence`](https://en.cppreference.com/w/cpp/utility/integer_sequence) containing all the sizes for which a specialization of the fixed-size sorter exists. If this trait isn't specified, it is assumed that the fixed-size sorter can be specialized for any value of `N`. +* `domain`: a specialization of [`std::index_sequence`][std-integer-sequence] containing all the sizes for which a specialization of the fixed-size sorter exists. If this trait isn't specified, it is assumed that the fixed-size sorter can be specialized for any value of `N`. * `iterator_category`: the category of iterators every specialization of the fixed-size sorter is guaranteed to work with. Individual specializations may work with less strict iterator categories. -* `is_always_stable`: an alias for [`std::true_type`](https://en.cppreference.com/w/cpp/types/integral_constant) if every specialization of the fixed-size sorter is guaranteed to always be stable, and `std::false_type` otherwise. \ No newline at end of file +* `is_always_stable`: an alias for [`std::true_type`][std-integral-constant] if every specialization of the fixed-size sorter is guaranteed to always be stable, and `std::false_type` otherwise. + + + [heap-sorter]: https://github.com/Morwenn/cpp-sort/wiki/Sorters#heap_sorter + [hybrid-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#hybrid_adapter + [is-always-stable]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-traits#is_always_stable + [iterator-tags]: https://en.cppreference.com/w/cpp/iterator/iterator_tags + [out-of-place-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#out_of_place_adapter + [self-sort-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#self_sort_adapter + [stability]: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability + [stable-adapter]: https://github.com/Morwenn/cpp-sort/wiki/Sorter-adapters#stable_adapter-make_stable-and-stable_t + [std-integer-sequence]: https://en.cppreference.com/w/cpp/utility/integer_sequence + [std-integral-constant]: https://en.cppreference.com/w/cpp/types/integral_constant + [std-list-sort]: https://en.cppreference.com/w/cpp/container/list/sort diff --git a/docs/Tooling.md b/docs/Tooling.md index 0b7c6c28..05c24c33 100644 --- a/docs/Tooling.md +++ b/docs/Tooling.md @@ -51,10 +51,10 @@ The same options exist without the `CPPSORT_` prefix exist, but are deprecated. conan search cpp-sort --remote=conan-center ``` -And then install any version to your local cache as follows (here with version 1.11.0): +And then install any version to your local cache as follows (here with version 1.12.0): ```sh -conan install cpp-sort/1.11.0 +conan install cpp-sort/1.12.0 ``` The packages downloaded from conan-center are minimal and only contain the files required to use **cpp-sort** as a library: the headers, CMake files and licensing information. If you need anything else you have to build your own package with the `conanfile.py` available in this repository. diff --git a/docs/images/mops-partial-ordering.png b/docs/images/mops-partial-ordering.png index 258cfd96..e845259e 100644 Binary files a/docs/images/mops-partial-ordering.png and b/docs/images/mops-partial-ordering.png differ diff --git a/docs/images/stable_adapter.png b/docs/images/stable_adapter.png new file mode 100644 index 00000000..eb710aaf Binary files /dev/null and b/docs/images/stable_adapter.png differ diff --git a/docs/images/stable_t.png b/docs/images/stable_t.png new file mode 100644 index 00000000..57b626c6 Binary files /dev/null and b/docs/images/stable_t.png differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8ebf2a3f..1e2bed46 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,10 +1,10 @@ -# Copyright (c) 2019-2020 Morwenn +# Copyright (c) 2019-2021 Morwenn # SPDX-License-Identifier: MIT include(cpp-sort-utils) # Quick & dirty script to compile the examples -foreach(filename bubble_sorter.cpp list_selection_sorter.cpp) +foreach(filename bubble_sorter.cpp list_selection_sorter.cpp readme_example.cpp) get_filename_component(name ${filename} NAME_WE) add_executable(${name} ${filename}) target_link_libraries(${name} PRIVATE cpp-sort::cpp-sort) diff --git a/examples/readme_example.cpp b/examples/readme_example.cpp new file mode 100644 index 00000000..9806ee46 --- /dev/null +++ b/examples/readme_example.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + struct wrapper { int value; }; + + std::forward_list li = { {5}, {8}, {3}, {2}, {9} }; + std::vector vec = { {5}, {8}, {3}, {2}, {9} }; + + // When used, this sorter will use a pattern-defeating quicksort + // to sort random-access collections, and a mergesort otherwise + cppsort::hybrid_adapter< + cppsort::pdq_sorter, + cppsort::merge_sorter + > sorter; + + // Sort li and vec in reverse order using their value member + sorter(li, std::greater<>{}, &wrapper::value); + sorter(vec, std::greater<>{}, &wrapper::value); + + assert(std::equal( + li.begin(), li.end(), + vec.begin(), vec.end(), + [](const auto& lhs, const auto& rhs) { return lhs.value == rhs.value; } + )); +} diff --git a/include/cpp-sort/adapters/container_aware_adapter.h b/include/cpp-sort/adapters/container_aware_adapter.h index adac55a5..51a26116 100644 --- a/include/cpp-sort/adapters/container_aware_adapter.h +++ b/include/cpp-sort/adapters/container_aware_adapter.h @@ -103,7 +103,7 @@ namespace cppsort typename Iterable > auto operator()(Iterable& iterable) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::can_sort::value, conditional_t< Stability, @@ -120,7 +120,7 @@ namespace cppsort typename Iterable > auto operator()(Iterable& iterable) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::can_sort::value, conditional_t< Stability, @@ -138,7 +138,7 @@ namespace cppsort typename Compare > auto operator()(Iterable& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::can_comparison_sort::value, conditional_t< Stability, @@ -156,7 +156,7 @@ namespace cppsort typename Compare > auto operator()(Iterable& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< not is_projection::value && not detail::can_comparison_sort::value, conditional_t< @@ -175,7 +175,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::can_comparison_sort::value && detail::can_projection_sort::value, conditional_t< @@ -194,7 +194,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::can_projection_sort::value && detail::can_comparison_projection_sort, Projection>::value, conditional_t< @@ -215,7 +215,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::can_projection_sort::value && not detail::can_comparison_projection_sort, Projection>::value && detail::can_comparison_sort< @@ -241,7 +241,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection::value && not detail::can_projection_sort::value && not detail::can_comparison_projection_sort, Projection>::value && @@ -267,7 +267,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::can_comparison_projection_sort::value, conditional_t< Stability, @@ -288,7 +288,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::can_comparison_projection_sort::value && detail::can_comparison_sort< Sorter, @@ -314,7 +314,7 @@ namespace cppsort typename Projection > auto operator()(Iterable& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::can_comparison_projection_sort::value && not detail::can_comparison_sort< Sorter, diff --git a/include/cpp-sort/adapters/counting_adapter.h b/include/cpp-sort/adapters/counting_adapter.h index 6107f637..47c6b286 100644 --- a/include/cpp-sort/adapters/counting_adapter.h +++ b/include/cpp-sort/adapters/counting_adapter.h @@ -17,6 +17,7 @@ #include #include "../detail/checkers.h" #include "../detail/comparison_counter.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -40,7 +41,7 @@ namespace cppsort template< typename Iterable, typename Compare = std::less<>, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_projection_v > > @@ -56,7 +57,7 @@ namespace cppsort template< typename Iterator, typename Compare = std::less<>, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_projection_iterator_v > > @@ -73,7 +74,7 @@ namespace cppsort typename Iterable, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -90,7 +91,7 @@ namespace cppsort typename Iterator, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/adapters/hybrid_adapter.h b/include/cpp-sort/adapters/hybrid_adapter.h index aff28104..33fec453 100644 --- a/include/cpp-sort/adapters/hybrid_adapter.h +++ b/include/cpp-sort/adapters/hybrid_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_ADAPTERS_HYBRID_ADAPTER_H_ @@ -115,7 +115,7 @@ namespace cppsort template static auto _detail_stability(choice, Args&&... args) - -> std::enable_if_t< + -> detail::enable_if_t< is_invocable_v, is_stable >; @@ -394,7 +394,7 @@ namespace cppsort template static constexpr auto get_flat_tuple(Sorter&& value) - -> std::enable_if_t< + -> detail::enable_if_t< not detail::is_hybrid_adapter::value, std::tuple&&> > @@ -404,7 +404,7 @@ namespace cppsort template static constexpr auto get_flat_tuple(Sorter&& value) - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_hybrid_adapter::value, decltype(get_sorters_from_impl(std::move(value))) > diff --git a/include/cpp-sort/adapters/indirect_adapter.h b/include/cpp-sort/adapters/indirect_adapter.h index dd026af2..3cc03a5f 100644 --- a/include/cpp-sort/adapters/indirect_adapter.h +++ b/include/cpp-sort/adapters/indirect_adapter.h @@ -136,7 +136,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -155,7 +155,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/adapters/schwartz_adapter.h b/include/cpp-sort/adapters/schwartz_adapter.h index f157c1d1..9e146211 100644 --- a/include/cpp-sort/adapters/schwartz_adapter.h +++ b/include/cpp-sort/adapters/schwartz_adapter.h @@ -111,7 +111,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -127,7 +127,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > @@ -142,7 +142,7 @@ namespace cppsort template> auto operator()(ForwardIterable&& iterable, Compare compare={}) const - -> std::enable_if_t< + -> detail::enable_if_t< not is_projection_v, decltype(this->get()(std::forward(iterable), std::move(compare))) > @@ -154,7 +154,7 @@ namespace cppsort template> auto operator()(ForwardIterator first, ForwardIterator last, Compare compare={}) const - -> std::enable_if_t< + -> detail::enable_if_t< not is_projection_iterator_v, decltype(this->get()(std::move(first), std::move(last), std::move(compare))) > diff --git a/include/cpp-sort/adapters/self_sort_adapter.h b/include/cpp-sort/adapters/self_sort_adapter.h index 0d568867..402539a4 100644 --- a/include/cpp-sort/adapters/self_sort_adapter.h +++ b/include/cpp-sort/adapters/self_sort_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_ADAPTERS_SELF_SORT_ADAPTER_H_ @@ -81,7 +81,7 @@ namespace cppsort template auto operator()(Iterable&& iterable, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_sort_method, decltype(std::forward(iterable).sort(utility::as_function(args)...)) > @@ -91,7 +91,7 @@ namespace cppsort template auto operator()(Iterable&& iterable, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_sort_method && detail::has_stable_sort_method, decltype(std::forward(iterable).stable_sort(utility::as_function(args)...)) @@ -102,7 +102,7 @@ namespace cppsort template auto operator()(Iterable&& iterable, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_sort_method && not detail::has_stable_sort_method, decltype(this->get()(std::forward(iterable), std::forward(args)...)) diff --git a/include/cpp-sort/adapters/small_array_adapter.h b/include/cpp-sort/adapters/small_array_adapter.h index 7d0e628a..942a4ebe 100644 --- a/include/cpp-sort/adapters/small_array_adapter.h +++ b/include/cpp-sort/adapters/small_array_adapter.h @@ -71,7 +71,7 @@ namespace cppsort typename... Args > auto operator()(std::array& array, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_in_pack, decltype(FixedSizeSorter{}(array, std::forward(args)...)) > @@ -83,10 +83,10 @@ namespace cppsort typename T, std::size_t N, typename... Args, - typename = std::enable_if_t> + typename = detail::enable_if_t> > auto operator()(T (&array)[N], Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_in_pack, decltype(FixedSizeSorter{}(array, std::forward(args)...)) > diff --git a/include/cpp-sort/adapters/stable_adapter.h b/include/cpp-sort/adapters/stable_adapter.h index 122c2dcd..4472669e 100644 --- a/include/cpp-sort/adapters/stable_adapter.h +++ b/include/cpp-sort/adapters/stable_adapter.h @@ -23,6 +23,7 @@ #include "../detail/checkers.h" #include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" +#include "../detail/raw_checkers.h" #include "../detail/sized_iterator.h" #include "../detail/type_traits.h" @@ -152,7 +153,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -169,7 +170,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > @@ -219,13 +220,17 @@ namespace cppsort { stable_adapter() = default; - constexpr explicit stable_adapter(Sorter sorter): + constexpr explicit stable_adapter(const Sorter& sorter): + utility::adapter_storage(sorter) + {} + + constexpr explicit stable_adapter(Sorter&& sorter): utility::adapter_storage(std::move(sorter)) {} template< typename... Args, - typename = std::enable_if_t> + typename = detail::enable_if_t> > auto operator()(Args&&... args) const -> decltype(this->get()(std::forward(args)...)) @@ -235,7 +240,7 @@ namespace cppsort template< typename... Args, - typename = std::enable_if_t>, + typename = detail::enable_if_t>, typename = void > auto operator()(Args&&... args) const @@ -248,15 +253,20 @@ namespace cppsort // Sorter traits using is_always_stable = std::true_type; - using type = stable_adapter; + using type = detail::conditional_t< + cppsort::is_always_stable_v, + Sorter, + stable_adapter + >; }; - // Accidental nesting can happen, unwrap template - struct stable_adapter>: - stable_adapter + struct sorter_traits>: + detail::raw_check_iterator_category> { - using type = stable_adapter; + // Ensure that all user-defined specializations of stable_adapter + // are considered always by default by sorter_traits + using is_always_stable = std::true_type; }; //////////////////////////////////////////////////////////// @@ -265,32 +275,52 @@ namespace cppsort namespace detail { template - struct stable_t_impl_false + struct stable_t_impl_type_or { - // The sorter is not always stable and does not have - // a type member named 'type' - using type = stable_adapter; + using type = Sorter; }; template - struct stable_t_impl_false::type>> + struct stable_t_impl_type_or> { - // The sorter is not always stable but has a type member - // called 'type', use that one - using type = typename stable_adapter::type; + using type = typename Sorter::type; }; - template - struct stable_t_impl + template + struct stable_t_impl_true { - // The sorter is always stable, alias it directly + // The sorter is a stable_adapter specialization, use its ::type + // member if it exists, otherwise use the specialization directly + using type = typename stable_t_impl_type_or::type; + }; + + template + struct stable_t_impl_true + { + // The sorter is always stable but not a specialization of + // stable_adapter, alias it directly using type = Sorter; }; + template + struct stable_t_impl + { + // The sorter is always stable, check whether it is already a + // stable_adapter specialization + using type = typename stable_t_impl_true< + Sorter, + is_specialization_of_v + >::type; + }; + template struct stable_t_impl { - using type = typename stable_t_impl_false::type; + // The sorter is not always stable, wrap it in stable_adapter or + // in something close enough + using type = typename stable_t_impl_type_or< + stable_adapter + >::type; }; } @@ -299,6 +329,28 @@ namespace cppsort Sorter, cppsort::is_always_stable_v >::type; + + //////////////////////////////////////////////////////////// + // stable_adapter> + + // Accidental nesting can happen, in which case we try to + // unwrap as many levels as possible + template + struct stable_adapter>: + stable_adapter + { + stable_adapter() = default; + + constexpr explicit stable_adapter(const stable_adapter& sorter): + stable_adapter(sorter) + {} + + constexpr explicit stable_adapter(stable_adapter&& sorter): + stable_adapter(std::move(sorter)) + {} + + using type = typename detail::stable_t_impl_type_or>::type; + }; } #ifdef CPPSORT_ADAPTERS_HYBRID_ADAPTER_DONE_ diff --git a/include/cpp-sort/adapters/verge_adapter.h b/include/cpp-sort/adapters/verge_adapter.h index 3fe6871d..7c9cc4b1 100644 --- a/include/cpp-sort/adapters/verge_adapter.h +++ b/include/cpp-sort/adapters/verge_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 Morwenn + * Copyright (c) 2017-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_ADAPTERS_VERGE_ADAPTER_H_ @@ -18,6 +18,7 @@ #include #include #include "../detail/vergesort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -40,7 +41,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/comparators/case_insensitive_less.h b/include/cpp-sort/comparators/case_insensitive_less.h index 69f84649..ff9d6add 100644 --- a/include/cpp-sort/comparators/case_insensitive_less.h +++ b/include/cpp-sort/comparators/case_insensitive_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_CASE_INSENSITIVE_LESS_H_ @@ -102,7 +102,7 @@ namespace cppsort template< typename T, - typename = std::enable_if_t> + typename = detail::enable_if_t> > auto refine() const -> adl_barrier::refined_case_insensitive_less_locale_fn @@ -125,7 +125,7 @@ namespace cppsort template< typename T, - typename = std::enable_if_t> + typename = detail::enable_if_t> > auto refine() const -> adl_barrier::refined_case_insensitive_less_fn @@ -184,7 +184,7 @@ namespace cppsort template auto operator()(const T& lhs, const T& rhs) const - -> std::enable_if_t< + -> detail::enable_if_t< not is_invocable_r_v, decltype(case_insensitive_less(lhs, rhs, loc)) > @@ -194,7 +194,7 @@ namespace cppsort template auto operator()(const T& lhs, const T& rhs) const - -> std::enable_if_t< + -> detail::enable_if_t< is_invocable_r_v, bool > @@ -224,7 +224,7 @@ namespace cppsort template auto operator()(const T& lhs, const T& rhs) const - -> std::enable_if_t< + -> detail::enable_if_t< negation>::value, decltype(case_insensitive_less(lhs, rhs)) > @@ -234,7 +234,7 @@ namespace cppsort template auto operator()(const T& lhs, const T& rhs) const - -> std::enable_if_t< + -> detail::enable_if_t< conjunction< is_invocable_r, negation> @@ -247,7 +247,7 @@ namespace cppsort template auto operator()(const T& lhs, const T& rhs) const - -> std::enable_if_t< + -> detail::enable_if_t< conjunction< is_invocable_r, is_invocable_r diff --git a/include/cpp-sort/comparators/partial_greater.h b/include/cpp-sort/comparators/partial_greater.h index 2d50750c..fe5c2bf9 100644 --- a/include/cpp-sort/comparators/partial_greater.h +++ b/include/cpp-sort/comparators/partial_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_PARTIAL_GREATER_H_ @@ -13,6 +13,7 @@ #include #include #include +#include "../detail/type_traits.h" namespace cppsort { @@ -23,7 +24,7 @@ namespace cppsort template auto partial_greater(T lhs, T rhs) noexcept - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { return lhs > rhs; } diff --git a/include/cpp-sort/comparators/partial_less.h b/include/cpp-sort/comparators/partial_less.h index 4fece372..7bebbaf9 100644 --- a/include/cpp-sort/comparators/partial_less.h +++ b/include/cpp-sort/comparators/partial_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_PARTIAL_LESS_H_ @@ -13,6 +13,7 @@ #include #include #include +#include "../detail/type_traits.h" namespace cppsort { @@ -23,7 +24,7 @@ namespace cppsort template auto partial_less(T lhs, T rhs) noexcept - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { return lhs < rhs; } diff --git a/include/cpp-sort/comparators/total_greater.h b/include/cpp-sort/comparators/total_greater.h index 77aa1ca3..80c6bc72 100644 --- a/include/cpp-sort/comparators/total_greater.h +++ b/include/cpp-sort/comparators/total_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_TOTAL_GREATER_H_ @@ -14,6 +14,7 @@ #include #include #include "../detail/floating_point_weight.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -24,7 +25,7 @@ namespace cppsort template constexpr auto total_greater(T lhs, T rhs) noexcept - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { return lhs > rhs; } @@ -34,7 +35,7 @@ namespace cppsort template auto total_greater(T lhs, T rhs) - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { if (std::isfinite(lhs) && std::isfinite(rhs)) { if (lhs == 0 && rhs == 0) { diff --git a/include/cpp-sort/comparators/total_less.h b/include/cpp-sort/comparators/total_less.h index b4d23c82..6e09ae82 100644 --- a/include/cpp-sort/comparators/total_less.h +++ b/include/cpp-sort/comparators/total_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_TOTAL_LESS_H_ @@ -14,6 +14,7 @@ #include #include #include "../detail/floating_point_weight.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -24,7 +25,7 @@ namespace cppsort template constexpr auto total_less(T lhs, T rhs) noexcept - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { return lhs < rhs; } @@ -34,7 +35,7 @@ namespace cppsort template auto total_less(T lhs, T rhs) - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { if (std::isfinite(lhs) && std::isfinite(rhs)) { if (lhs == 0 && rhs == 0) { diff --git a/include/cpp-sort/comparators/weak_greater.h b/include/cpp-sort/comparators/weak_greater.h index a7f5dba7..00ed6930 100644 --- a/include/cpp-sort/comparators/weak_greater.h +++ b/include/cpp-sort/comparators/weak_greater.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_WEAK_GREATER_H_ @@ -15,6 +15,7 @@ #include #include #include "../detail/floating_point_weight.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -25,7 +26,7 @@ namespace cppsort template auto weak_greater(T lhs, T rhs) - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { if (std::isfinite(lhs) && std::isfinite(rhs)) { return lhs > rhs; diff --git a/include/cpp-sort/comparators/weak_less.h b/include/cpp-sort/comparators/weak_less.h index 060a7273..f4ecda3f 100644 --- a/include/cpp-sort/comparators/weak_less.h +++ b/include/cpp-sort/comparators/weak_less.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_COMPARATORS_WEAK_LESS_H_ @@ -15,6 +15,7 @@ #include #include #include "../detail/floating_point_weight.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -25,7 +26,7 @@ namespace cppsort template auto weak_less(T lhs, T rhs) - -> std::enable_if_t::value, bool> + -> detail::enable_if_t::value, bool> { if (std::isfinite(lhs) && std::isfinite(rhs)) { return lhs < rhs; diff --git a/include/cpp-sort/detail/associate_iterator.h b/include/cpp-sort/detail/associate_iterator.h index 23878564..3fde94f1 100644 --- a/include/cpp-sort/detail/associate_iterator.h +++ b/include/cpp-sort/detail/associate_iterator.h @@ -325,21 +325,24 @@ namespace detail friend auto operator+(associate_iterator it, difference_type size) -> associate_iterator { - return it += size; + it += size; + return it; } CPPSORT_ATTRIBUTE_NODISCARD friend auto operator+(difference_type size, associate_iterator it) -> associate_iterator { - return it += size; + it += size; + return it; } CPPSORT_ATTRIBUTE_NODISCARD friend auto operator-(associate_iterator it, difference_type size) -> associate_iterator { - return it -= size; + it -= size; + return it; } CPPSORT_ATTRIBUTE_NODISCARD diff --git a/include/cpp-sort/detail/bitops.h b/include/cpp-sort/detail/bitops.h index 56eebf97..26681760 100644 --- a/include/cpp-sort/detail/bitops.h +++ b/include/cpp-sort/detail/bitops.h @@ -11,6 +11,8 @@ #include #include #include +#include "../detail/config.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -28,6 +30,19 @@ namespace detail return n & ~(n >> 1); } + // Returns 2^ceil(log2(n)), assumes n > 0 + template + constexpr auto hyperceil(Unsigned n) + -> Unsigned + { + constexpr auto bound = std::numeric_limits::digits / 2; + --n; + for (std::size_t i = 1 ; i <= bound ; i <<= 1) { + n |= (n >> i); + } + return ++n; + } + // Returns floor(log2(n)), assumes n > 0 #if defined(__GNUC__) || defined(__clang__) @@ -79,14 +94,14 @@ namespace detail template constexpr auto half(Integer value) - -> std::enable_if_t::value, Integer> + -> detail::enable_if_t::value, Integer> { return static_cast(static_cast>(value) / 2); } template constexpr auto half(T value) - -> std::enable_if_t::value, T> + -> detail::enable_if_t::value, T> { return value / 2; } @@ -102,6 +117,16 @@ namespace detail auto x = static_cast>(n); return x != 0 && (x & (x - 1)) == 0; } + + // Left bit rotation + template + constexpr auto rotl(Unsigned x, int s) + -> Unsigned + { + CPPSORT_ASSERT(s > 0); + constexpr auto n = std::numeric_limits::digits; + return (x << s) | (x >> (n - s)); + } }} #endif // CPPSORT_DETAIL_BITOPS_H_ diff --git a/include/cpp-sort/detail/config.h b/include/cpp-sort/detail/config.h index 4fd795bc..25c8e5d4 100644 --- a/include/cpp-sort/detail/config.h +++ b/include/cpp-sort/detail/config.h @@ -16,6 +16,18 @@ # define __has_cpp_attribute(x) 0 #endif +//////////////////////////////////////////////////////////// +// Check for C++17 features + +// Make sure that there is a single variable before C++17, +// default to static in C++14 to avoid ODR issues + +#if defined(__cpp_inline_variables) +# define CPPSORT_INLINE_VARIABLE inline +#else +# define CPPSORT_INLINE_VARIABLE static +#endif + //////////////////////////////////////////////////////////// // Check for C++20 features diff --git a/include/cpp-sort/detail/container_aware/insertion_sort.h b/include/cpp-sort/detail/container_aware/insertion_sort.h index c3fe6634..fec76a8f 100644 --- a/include/cpp-sort/detail/container_aware/insertion_sort.h +++ b/include/cpp-sort/detail/container_aware/insertion_sort.h @@ -20,7 +20,6 @@ #include #include #include "../bitops.h" -#include "../std_list_traits.h" #include "../type_traits.h" #include "../upper_bound.h" @@ -122,7 +121,7 @@ namespace cppsort template auto operator()(std::list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -131,7 +130,7 @@ namespace cppsort template auto operator()(std::list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -142,7 +141,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > @@ -165,7 +164,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -174,7 +173,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -185,7 +184,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > @@ -201,9 +200,9 @@ namespace cppsort template< typename First, typename... Args, - typename = std::enable_if_t< - not detail::is_std_list>::value && - not detail::is_std_forward_list>::value + typename = detail::enable_if_t< + not detail::is_specialization_of_v, std::list> && + not detail::is_specialization_of_v, std::forward_list> > > auto operator()(First&& first, Args&&... args) const diff --git a/include/cpp-sort/detail/container_aware/mel_sort.h b/include/cpp-sort/detail/container_aware/mel_sort.h index 71b02cb9..f04b19d7 100644 --- a/include/cpp-sort/detail/container_aware/mel_sort.h +++ b/include/cpp-sort/detail/container_aware/mel_sort.h @@ -23,6 +23,7 @@ #include #include "../functional.h" #include "../lower_bound.h" +#include "../type_traits.h" namespace cppsort { @@ -216,7 +217,7 @@ namespace cppsort template auto operator()(std::list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -225,7 +226,7 @@ namespace cppsort template auto operator()(std::list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -236,7 +237,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > @@ -259,7 +260,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -268,7 +269,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -279,7 +280,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > diff --git a/include/cpp-sort/detail/container_aware/merge_sort.h b/include/cpp-sort/detail/container_aware/merge_sort.h index 94836f8d..5bf3c750 100644 --- a/include/cpp-sort/detail/container_aware/merge_sort.h +++ b/include/cpp-sort/detail/container_aware/merge_sort.h @@ -20,6 +20,7 @@ #include #include #include +#include "../type_traits.h" namespace cppsort { @@ -92,7 +93,7 @@ namespace cppsort template auto operator()(std::list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -101,7 +102,7 @@ namespace cppsort template auto operator()(std::list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -112,7 +113,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > @@ -136,7 +137,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -146,7 +147,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -158,7 +159,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > diff --git a/include/cpp-sort/detail/container_aware/selection_sort.h b/include/cpp-sort/detail/container_aware/selection_sort.h index 7d9b1be8..6bc6972d 100644 --- a/include/cpp-sort/detail/container_aware/selection_sort.h +++ b/include/cpp-sort/detail/container_aware/selection_sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_CONTAINER_AWARE_SELECTION_SORT_H_ @@ -20,6 +20,7 @@ #include #include #include "../min_element.h" +#include "../type_traits.h" namespace cppsort { @@ -100,7 +101,7 @@ namespace cppsort template auto operator()(std::list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -109,7 +110,7 @@ namespace cppsort template auto operator()(std::list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -120,7 +121,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > @@ -143,7 +144,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v, Compare> > { @@ -152,7 +153,7 @@ namespace cppsort template auto operator()(std::forward_list& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< is_projection_v> > { @@ -163,7 +164,7 @@ namespace cppsort typename Compare, typename Projection, typename... Args, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v, Compare> > > diff --git a/include/cpp-sort/detail/empty_sorter.h b/include/cpp-sort/detail/empty_sorter.h index e9523979..99be395a 100644 --- a/include/cpp-sort/detail/empty_sorter.h +++ b/include/cpp-sort/detail/empty_sorter.h @@ -16,6 +16,7 @@ #include #include #include "attributes.h" +#include "type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace detail typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/fixed_size_list.h b/include/cpp-sort/detail/fixed_size_list.h index 2b1011af..80ef28f8 100644 --- a/include/cpp-sort/detail/fixed_size_list.h +++ b/include/cpp-sort/detail/fixed_size_list.h @@ -147,6 +147,8 @@ namespace detail first_free_(buffer_.get()), capacity_(capacity) { + CPPSORT_ASSERT(capacity > 0); + // Node constructors are noexcept, so we don't need to handle // the cleanup of list nodes that would have been required if // exceptions could have been thrown diff --git a/include/cpp-sort/detail/introselect.h b/include/cpp-sort/detail/introselect.h index c28ee5d4..1e883d1b 100644 --- a/include/cpp-sort/detail/introselect.h +++ b/include/cpp-sort/detail/introselect.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Morwenn + * Copyright (c) 2018-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_INTROSELECT_H_ @@ -146,7 +146,7 @@ namespace detail template auto introselect(ForwardIterator first, ForwardIterator last, difference_type_t nth_pos, - difference_type_t size, int bad_allowed, + difference_type_t size, Compare compare, Projection projection) -> ForwardIterator; @@ -197,7 +197,7 @@ namespace detail size = rounded_size == size ? size / 5 : size / 5 + 1; // Mutual recursion with introselect - return introselect(first, last, size / 2, size, detail::log2(size), + return introselect(first, last, size / 2, size, std::move(compare), std::move(projection)); } @@ -260,12 +260,12 @@ namespace detail } //////////////////////////////////////////////////////////// - // Forward nth_element based on introselect + // Introselect template auto introselect(ForwardIterator first, ForwardIterator last, difference_type_t nth_pos, - difference_type_t size, int bad_allowed, + difference_type_t size, Compare compare, Projection projection) -> ForwardIterator { @@ -273,53 +273,54 @@ namespace detail auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); - - if (size <= 32) { - small_sort(first, last, size, std::move(compare), std::move(projection)); - return std::next(first, nth_pos); - } - - // Choose pivot as either median of 9 or median of medians - auto temp = pick_pivot(first, last, size, bad_allowed, compare, projection); - auto median_it = temp.first; - auto last_1 = temp.second; - - // Put the pivot at position std::prev(last) and partition - iter_swap(median_it, last_1); - auto&& pivot1 = proj(*last_1); - auto middle1 = detail::partition( - first, last_1, - [&](auto&& elem) { return comp(proj(elem), pivot1); } - ); - - // Put the pivot in its final position and partition - iter_swap(middle1, last_1); - auto&& pivot2 = proj(*middle1); - auto middle2 = detail::partition( - std::next(middle1), last, - [&](auto&& elem) { return not comp(pivot2, proj(elem)); } - ); - - // Recursive call: heuristic trick here: in real world cases, - // the middle partition is more likely to be smaller than the - // right one, so computing its size should generally be cheaper - auto size_left = std::distance(first, middle1); - auto size_middle = std::distance(middle1, middle2); - auto size_right = size - size_left - size_middle; - - // TODO: unroll tail recursion - // We're done if the nth element is in the middle partition - if (nth_pos < size_left) { - return introselect(first, middle1, nth_pos, - size_left, --bad_allowed, - std::move(compare), std::move(projection)); - } else if (nth_pos > size_left + size_middle) { - return introselect(middle2, last, nth_pos - size_left - size_middle, - size_right, --bad_allowed, - std::move(compare), std::move(projection)); + int bad_allowed = detail::log2(size); + + while (size > 32) { + // Choose pivot as either median of 9 or median of medians + auto temp = pick_pivot(first, last, size, bad_allowed, compare, projection); + auto median_it = temp.first; + auto last_1 = temp.second; + + // Put the pivot at position std::prev(last) and partition + iter_swap(median_it, last_1); + auto&& pivot1 = proj(*last_1); + auto middle1 = detail::partition( + first, last_1, + [&](auto&& elem) { return comp(proj(elem), pivot1); } + ); + + // Put the pivot in its final position and partition + iter_swap(middle1, last_1); + auto&& pivot2 = proj(*middle1); + auto middle2 = detail::partition( + std::next(middle1), last, + [&](auto&& elem) { return not comp(pivot2, proj(elem)); } + ); + + // Recursive call: heuristic trick here: in real world cases, + // the middle partition is more likely to be smaller than the + // right one, so computing its size should generally be cheaper + auto size_left = std::distance(first, middle1); + auto size_middle = std::distance(middle1, middle2); + auto size_right = size - size_left - size_middle; + + // We're done if the nth element is in the middle partition + if (nth_pos < size_left) { + last = middle1; + size = size_left; + } else if (nth_pos > size_left + size_middle) { + first = middle2; + nth_pos -= size_left + size_middle; + size = size_right; + } else { + // Return an iterator to the nth element + return std::next(middle1, nth_pos - size_left); + } + --bad_allowed; } - // Return an iterator to the nth element - return std::next(middle1, nth_pos - size_left); + // Fallback when the collection is small enough + small_sort(first, last, size, std::move(compare), std::move(projection)); + return std::next(first, nth_pos); } }} diff --git a/include/cpp-sort/detail/is_p_sorted.h b/include/cpp-sort/detail/is_p_sorted.h index a39c1c10..5a39ce4e 100644 --- a/include/cpp-sort/detail/is_p_sorted.h +++ b/include/cpp-sort/detail/is_p_sorted.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_IS_P_SORTED_H_ @@ -15,22 +15,25 @@ namespace cppsort { namespace detail { - template - auto is_p_sorted(RandomAccessIterator first, RandomAccessIterator last, - difference_type_t p, + template + auto is_p_sorted(ForwardIterator first, ForwardIterator last, ForwardIterator pth, Compare compare, Projection projection) -> bool { auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); - for (auto it1 = first + p ; it1 != last ; ++it1) { - auto&& value = proj(*it1); - for (auto it2 = first ; it2 != it1 - p ; ++it2) { - if (comp(value, proj(*it2))) { - return false; - } + // pth is the iterator such as pth - first == p + + auto max_it = first; + for (auto it1 = std::next(pth); it1 != last; ++it1) { + if (comp(proj(*max_it), proj(*first))) { + max_it = first; + } + if (comp(proj(*it1), proj(*max_it))) { + return false; } + ++first; } return true; } diff --git a/include/cpp-sort/detail/low_comparisons/sort10.h b/include/cpp-sort/detail/low_comparisons/sort10.h index d3527d16..e6b984c8 100644 --- a/include/cpp-sort/detail/low_comparisons/sort10.h +++ b/include/cpp-sort/detail/low_comparisons/sort10.h @@ -1,20 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT10_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT10_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include "../front_insert.h" - namespace cppsort { namespace detail @@ -26,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort11.h b/include/cpp-sort/detail/low_comparisons/sort11.h index 98cefa7f..313d2361 100644 --- a/include/cpp-sort/detail/low_comparisons/sort11.h +++ b/include/cpp-sort/detail/low_comparisons/sort11.h @@ -1,20 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT11_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT11_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include "../front_insert.h" - namespace cppsort { namespace detail @@ -26,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort12.h b/include/cpp-sort/detail/low_comparisons/sort12.h index a795bc01..17379454 100644 --- a/include/cpp-sort/detail/low_comparisons/sort12.h +++ b/include/cpp-sort/detail/low_comparisons/sort12.h @@ -1,22 +1,10 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT12_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT12_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail @@ -28,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort13.h b/include/cpp-sort/detail/low_comparisons/sort13.h index 6b0a57a9..5b89e533 100644 --- a/include/cpp-sort/detail/low_comparisons/sort13.h +++ b/include/cpp-sort/detail/low_comparisons/sort13.h @@ -1,22 +1,10 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT13_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT13_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail @@ -28,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort2.h b/include/cpp-sort/detail/low_comparisons/sort2.h index 85aa9018..9ff88f24 100644 --- a/include/cpp-sort/detail/low_comparisons/sort2.h +++ b/include/cpp-sort/detail/low_comparisons/sort2.h @@ -1,20 +1,10 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT2_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT2_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include "../swap_if.h" - namespace cppsort { namespace detail @@ -26,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort3.h b/include/cpp-sort/detail/low_comparisons/sort3.h index b4e6aa96..7197b62c 100644 --- a/include/cpp-sort/detail/low_comparisons/sort3.h +++ b/include/cpp-sort/detail/low_comparisons/sort3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT3_H_ @@ -8,12 +8,6 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include #include "../rotate_left.h" #include "../rotate_right.h" @@ -28,7 +22,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort4.h b/include/cpp-sort/detail/low_comparisons/sort4.h index 7c05be5d..2c0ab9cc 100644 --- a/include/cpp-sort/detail/low_comparisons/sort4.h +++ b/include/cpp-sort/detail/low_comparisons/sort4.h @@ -1,20 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT4_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT4_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include "../front_insert.h" - namespace cppsort { namespace detail @@ -26,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort5.h b/include/cpp-sort/detail/low_comparisons/sort5.h index 611d22e5..0821d9ba 100644 --- a/include/cpp-sort/detail/low_comparisons/sort5.h +++ b/include/cpp-sort/detail/low_comparisons/sort5.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT5_H_ @@ -8,15 +8,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include #include "../rotate_right.h" -#include "../swap_if.h" namespace cppsort { @@ -29,7 +21,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort6.h b/include/cpp-sort/detail/low_comparisons/sort6.h index 40d5c65b..70bf7a5d 100644 --- a/include/cpp-sort/detail/low_comparisons/sort6.h +++ b/include/cpp-sort/detail/low_comparisons/sort6.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT6_H_ @@ -8,15 +8,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include #include "../rotate_right.h" -#include "../swap_if.h" namespace cppsort { @@ -29,7 +21,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort7.h b/include/cpp-sort/detail/low_comparisons/sort7.h index ef2ef83d..30638590 100644 --- a/include/cpp-sort/detail/low_comparisons/sort7.h +++ b/include/cpp-sort/detail/low_comparisons/sort7.h @@ -1,19 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT7_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT7_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include "../front_insert.h" namespace cppsort { @@ -26,7 +17,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort8.h b/include/cpp-sort/detail/low_comparisons/sort8.h index b80d9ede..c760c3f8 100644 --- a/include/cpp-sort/detail/low_comparisons/sort8.h +++ b/include/cpp-sort/detail/low_comparisons/sort8.h @@ -1,20 +1,10 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT8_H_ #define CPPSORT_DETAIL_LOW_COMPARISONS_SORT8_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include "../front_insert.h" - namespace cppsort { namespace detail @@ -26,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_comparisons/sort9.h b/include/cpp-sort/detail/low_comparisons/sort9.h index abd48ff5..d0475d17 100644 --- a/include/cpp-sort/detail/low_comparisons/sort9.h +++ b/include/cpp-sort/detail/low_comparisons/sort9.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_COMPARISONS_SORT9_H_ @@ -8,15 +8,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include #include "../rotate_right.h" -#include "../swap_if.h" namespace cppsort { @@ -29,7 +21,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_moves/sort2.h b/include/cpp-sort/detail/low_moves/sort2.h index 0d088fb6..daf5c256 100644 --- a/include/cpp-sort/detail/low_moves/sort2.h +++ b/include/cpp-sort/detail/low_moves/sort2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_MOVES_SORT2_H_ @@ -14,6 +14,7 @@ #include #include #include "../swap_if.h" +#include "../type_traits.h" namespace cppsort { @@ -26,7 +27,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_moves/sort3.h b/include/cpp-sort/detail/low_moves/sort3.h index ad007d4c..e2ecf5c4 100644 --- a/include/cpp-sort/detail/low_moves/sort3.h +++ b/include/cpp-sort/detail/low_moves/sort3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_MOVES_SORT3_H_ @@ -16,6 +16,7 @@ #include #include "../rotate_left.h" #include "../rotate_right.h" +#include "../type_traits.h" namespace cppsort { @@ -28,7 +29,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/low_moves/sort4.h b/include/cpp-sort/detail/low_moves/sort4.h index b23234e0..e60b3153 100644 --- a/include/cpp-sort/detail/low_moves/sort4.h +++ b/include/cpp-sort/detail/low_moves/sort4.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_LOW_MOVES_SORT4_H_ @@ -15,6 +15,7 @@ #include #include #include "../min_element.h" +#include "../type_traits.h" namespace cppsort { @@ -27,7 +28,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/merge_insertion_sort.h b/include/cpp-sort/detail/merge_insertion_sort.h index 14aa1a36..b4496284 100644 --- a/include/cpp-sort/detail/merge_insertion_sort.h +++ b/include/cpp-sort/detail/merge_insertion_sort.h @@ -8,14 +8,11 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include #include -#include #include #include #include #include "attributes.h" -#include "config.h" #include "fixed_size_list.h" #include "functional.h" #include "immovable_vector.h" @@ -24,7 +21,6 @@ #include "scope_exit.h" #include "swap_if.h" #include "swap_ranges.h" -#include "type_traits.h" #include "upper_bound.h" namespace cppsort @@ -212,21 +208,24 @@ namespace detail friend auto operator+(group_iterator it, difference_type size) -> group_iterator { - return it += size; + it += size; + return it; } CPPSORT_ATTRIBUTE_NODISCARD friend auto operator+(difference_type size, group_iterator it) -> group_iterator { - return it += size; + it += size; + return it; } CPPSORT_ATTRIBUTE_NODISCARD friend auto operator-(group_iterator it, difference_type size) -> group_iterator { - return it -= size; + it -= size; + return it; } CPPSORT_ATTRIBUTE_NODISCARD @@ -280,24 +279,6 @@ namespace detail Compare compare, Projection projection) -> void { - // Cache all the differences between a Jacobsthal number and its - // predecessor that fit in 64 bits, starting with the difference - // between the Jacobsthal numbers 4 and 3 (the previous ones are - // unneeded) - constexpr std::uint_fast64_t jacobsthal_diff[] = { - 2u, 2u, 6u, 10u, 22u, 42u, 86u, 170u, 342u, 682u, 1366u, - 2730u, 5462u, 10922u, 21846u, 43690u, 87382u, 174762u, 349526u, 699050u, - 1398102u, 2796202u, 5592406u, 11184810u, 22369622u, 44739242u, 89478486u, - 178956970u, 357913942u, 715827882u, 1431655766u, 2863311530u, 5726623062u, - 11453246122u, 22906492246u, 45812984490u, 91625968982u, 183251937962u, - 366503875926u, 733007751850u, 1466015503702u, 2932031007402u, 5864062014806u, - 11728124029610u, 23456248059222u, 46912496118442u, 93824992236886u, 187649984473770u, - 375299968947542u, 750599937895082u, 1501199875790165u, 3002399751580331u, - 6004799503160661u, 12009599006321322u, 24019198012642644u, 48038396025285288u, - 96076792050570576u, 192153584101141152u, 384307168202282304u, 768614336404564608u, - 1537228672809129216u, 3074457345618258432u, 6148914691236516864u - }; - auto size = last - first; if (size < 2) return; @@ -370,17 +351,29 @@ namespace detail auto current_it = first; auto current_pend = pend.begin(); - for (int k = 0 ; ; ++k) { - // Should be safe: in this code, std::distance should always return - // a positive number, so there is no risk of comparing funny values - using size_type = std::common_type_t< - std::uint_fast64_t, - typename list_t::difference_type - >; - - // Find next index - auto dist = jacobsthal_diff[k]; - if (dist > static_cast(pend.end() - current_pend)) break; + // At each cycle, we need to find the optimal pend element where to + // start the insertion cycle again in a way that minimizes the number + // of comparisons performed. The indices of the optimal sequence of + // pend elements happens to follow a sequence known as Jacobsthal + // numbers: https://oeis.org/A001045 + // + // Due to the element-per-element nature of the algorithm we are using, + // we don't need to know the position of a pend element, but its + // relative position to the previous one, in other words we don't need + // J(n+1), but J(n+1) - J(n). Thanks to the following equivalence it + // can be computed fairly cheaply: + // J(n+1) = 2^n - J(n) + // J(n+2) - J(n+1) = 2^(n+1) - J(n+1) - J(n+1) + // = 2^(n+1) - 2J(n+1) + // = 2(2^n - J(n+1)) + // = 2(2^n - (2^n - J(n))) + // = 2(2^n - 2^n + Jn) + // = 2J(n) + // By keeping track of powers of two and J(n), J(n+1) - J(n) can thus + // be computed with two shifts and a subtraction. + for (typename list_t::difference_type pow2 = 1, jn = 0, dist = 2; + dist <= pend.end() - current_pend; + pow2 *= 2, jn = pow2 - jn, dist = 2 * jn) { auto it = current_it + dist * 2; auto pe = current_pend + dist; @@ -442,9 +435,14 @@ namespace detail Compare compare, Projection projection) -> void { + auto size = last - first; + if (size < 2) { + return; + } + // Make a node pool big enough to hold all the values using node_type = list_node>; - fixed_size_list_node_pool node_pool(last - first); + fixed_size_list_node_pool node_pool(size); merge_insertion_sort_impl( make_group_iterator(std::move(first), 1), diff --git a/include/cpp-sort/detail/move.h b/include/cpp-sort/detail/move.h index 2ebbdad9..b8ba06da 100644 --- a/include/cpp-sort/detail/move.h +++ b/include/cpp-sort/detail/move.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_MOVE_H_ @@ -56,7 +56,7 @@ namespace detail template auto move(InputIterator first, InputIterator last, OutputIterator result) - -> std::enable_if_t< + -> detail::enable_if_t< not is_invocable_v, OutputIterator > @@ -66,7 +66,7 @@ namespace detail template auto move(InputIterator first, InputIterator last, OutputIterator result) - -> std::enable_if_t< + -> detail::enable_if_t< is_invocable_v, OutputIterator > @@ -83,7 +83,7 @@ namespace detail template auto move_backward(InputIterator first, InputIterator last, OutputIterator result) - -> std::enable_if_t< + -> detail::enable_if_t< not is_invocable_v, OutputIterator > @@ -93,7 +93,7 @@ namespace detail template auto move_backward(InputIterator first, InputIterator last, OutputIterator result) - -> std::enable_if_t< + -> detail::enable_if_t< is_invocable_v, OutputIterator > diff --git a/include/cpp-sort/detail/nth_element.h b/include/cpp-sort/detail/nth_element.h index 4c8db6a8..2d2b9e98 100644 --- a/include/cpp-sort/detail/nth_element.h +++ b/include/cpp-sort/detail/nth_element.h @@ -29,7 +29,7 @@ namespace detail Compare compare, Projection projection) -> ForwardIterator { - return introselect(first, last, nth_pos, size, detail::log2(size), + return introselect(first, last, nth_pos, size, std::move(compare), std::move(projection)); } diff --git a/include/cpp-sort/detail/pdqsort.h b/include/cpp-sort/detail/pdqsort.h index d2823e3a..aa8f3cf0 100644 --- a/include/cpp-sort/detail/pdqsort.h +++ b/include/cpp-sort/detail/pdqsort.h @@ -422,8 +422,7 @@ namespace detail } - template + template auto pdqsort_loop(RandomAccessIterator begin, RandomAccessIterator end, Compare compare, Projection projection, int bad_allowed, bool leftmost=true) @@ -431,6 +430,13 @@ namespace detail { using utility::iter_swap; using difference_type = difference_type_t; + using value_type = value_type_t; + using projected_type = projected_t; + + constexpr bool is_branchless = + utility::is_probably_branchless_comparison_v && + utility::is_probably_branchless_projection_v; + auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); @@ -471,7 +477,7 @@ namespace detail } // Partition and get results. - std::pair part_result = Branchless ? + std::pair part_result = is_branchless ? partition_right_branchless(begin, end, compare, projection) : partition_right(begin, end, compare, projection); RandomAccessIterator pivot_pos = part_result.first; @@ -526,8 +532,7 @@ namespace detail // Sort the left partition first using recursion and do tail recursion elimination for // the right-hand partition. - pdqsort_loop( - begin, pivot_pos, compare, projection, bad_allowed, leftmost); + pdqsort_loop(begin, pivot_pos, compare, projection, bad_allowed, leftmost); begin = pivot_pos + 1; leftmost = false; } @@ -539,19 +544,12 @@ namespace detail Compare compare, Projection projection) -> void { - using value_type = value_type_t; - using projected_type = projected_t; - constexpr bool is_branchless = - utility::is_probably_branchless_comparison_v && - utility::is_probably_branchless_projection_v; - auto size = end - begin; if (size < 2) return; - pdqsort_detail::pdqsort_loop( - std::move(begin), std::move(end), - std::move(compare), std::move(projection), - detail::log2(size)); + pdqsort_detail::pdqsort_loop(std::move(begin), std::move(end), + std::move(compare), std::move(projection), + detail::log2(size)); } }} diff --git a/include/cpp-sort/detail/schwartz_small_array.h b/include/cpp-sort/detail/schwartz_small_array.h index 2004f5f1..4465a870 100644 --- a/include/cpp-sort/detail/schwartz_small_array.h +++ b/include/cpp-sort/detail/schwartz_small_array.h @@ -38,7 +38,7 @@ namespace cppsort typename... Args > auto operator()(std::array& array, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_in_pack, decltype(schwartz_adapter>{}(array, std::forward(args)...)) > @@ -51,10 +51,10 @@ namespace cppsort typename T, std::size_t N, typename... Args, - typename = std::enable_if_t> + typename = detail::enable_if_t> > auto operator()(T (&array)[N], Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_in_pack, decltype(schwartz_adapter>{}(array, std::forward(args)...)) > diff --git a/include/cpp-sort/detail/ska_sort.h b/include/cpp-sort/detail/ska_sort.h index 1233c5be..15bf2c5e 100644 --- a/include/cpp-sort/detail/ska_sort.h +++ b/include/cpp-sort/detail/ska_sort.h @@ -348,7 +348,7 @@ namespace detail }; template - struct FallbackSubKey()))>::value>>: + struct FallbackSubKey()))>::value>>: SubKey()))> {}; @@ -533,7 +533,7 @@ namespace detail }; template - struct FallbackSubKey()[0])>::value>>: + struct FallbackSubKey()[0])>::value>>: ListSubKey {}; @@ -921,7 +921,7 @@ namespace detail template struct FallbackInplaceSorter()[0])>::value>>: + detail::enable_if_t()[0])>::value>>: ListInplaceSorter {}; diff --git a/include/cpp-sort/detail/slabsort.h b/include/cpp-sort/detail/slabsort.h index 849de861..ac046e16 100644 --- a/include/cpp-sort/detail/slabsort.h +++ b/include/cpp-sort/detail/slabsort.h @@ -256,8 +256,12 @@ namespace detail Compare compare, Projection projection) -> void { - // Node pool used by all try_melsort invocations auto size = last - first; + if (size < 2) { + return; + } + + // Node pool used by all try_melsort invocations using node_type = slabsort_list_node; fixed_size_list_node_pool node_pool(size); diff --git a/include/cpp-sort/detail/smoothsort.h b/include/cpp-sort/detail/smoothsort.h index c047bb1b..5ca738d8 100644 --- a/include/cpp-sort/detail/smoothsort.h +++ b/include/cpp-sort/detail/smoothsort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -28,6 +28,7 @@ #include #include #include +#include "config.h" namespace cppsort { @@ -37,12 +38,12 @@ namespace detail /* A constant containing the number of Leonardo numbers that can fit into * 64 bits. */ - static constexpr std::size_t kNumLeonardoNumbers = 92; + CPPSORT_INLINE_VARIABLE constexpr std::size_t kNumLeonardoNumbers = 92; /* A list of all the Leonardo numbers below 2^64, precomputed for * efficiency. */ - static constexpr std::uint_fast64_t kLeonardoNumbers[kNumLeonardoNumbers] = { + CPPSORT_INLINE_VARIABLE constexpr std::uint_fast64_t kLeonardoNumbers[kNumLeonardoNumbers] = { 1u, 1u, 3u, 5u, 9u, 15u, 25u, 41u, 67u, 109u, 177u, 287u, 465u, 753u, 1219u, 1973u, 3193u, 5167u, 8361u, 13529u, 21891u, 35421u, 57313u, 92735u, 150049u, 242785u, 392835u, 635621u, 1028457u, 1664079u, diff --git a/include/cpp-sort/detail/sorting_network/sort10.h b/include/cpp-sort/detail/sorting_network/sort10.h index e5ed6a93..b34e712f 100644 --- a/include/cpp-sort/detail/sorting_network/sort10.h +++ b/include/cpp-sort/detail/sorting_network/sort10.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort11.h b/include/cpp-sort/detail/sorting_network/sort11.h index f5014df9..0dc582c0 100644 --- a/include/cpp-sort/detail/sorting_network/sort11.h +++ b/include/cpp-sort/detail/sorting_network/sort11.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort12.h b/include/cpp-sort/detail/sorting_network/sort12.h index 2c3d0eb3..39d05c19 100644 --- a/include/cpp-sort/detail/sorting_network/sort12.h +++ b/include/cpp-sort/detail/sorting_network/sort12.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort13.h b/include/cpp-sort/detail/sorting_network/sort13.h index 320aa83d..444959a0 100644 --- a/include/cpp-sort/detail/sorting_network/sort13.h +++ b/include/cpp-sort/detail/sorting_network/sort13.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort14.h b/include/cpp-sort/detail/sorting_network/sort14.h index 5dccda23..11ca07c0 100644 --- a/include/cpp-sort/detail/sorting_network/sort14.h +++ b/include/cpp-sort/detail/sorting_network/sort14.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort15.h b/include/cpp-sort/detail/sorting_network/sort15.h index e8c84379..539dadd7 100644 --- a/include/cpp-sort/detail/sorting_network/sort15.h +++ b/include/cpp-sort/detail/sorting_network/sort15.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort16.h b/include/cpp-sort/detail/sorting_network/sort16.h index 7b44df5e..cf1be55c 100644 --- a/include/cpp-sort/detail/sorting_network/sort16.h +++ b/include/cpp-sort/detail/sorting_network/sort16.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort17.h b/include/cpp-sort/detail/sorting_network/sort17.h index 77bb45b6..ae1e35c5 100644 --- a/include/cpp-sort/detail/sorting_network/sort17.h +++ b/include/cpp-sort/detail/sorting_network/sort17.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort18.h b/include/cpp-sort/detail/sorting_network/sort18.h index f5a62023..7520709c 100644 --- a/include/cpp-sort/detail/sorting_network/sort18.h +++ b/include/cpp-sort/detail/sorting_network/sort18.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort19.h b/include/cpp-sort/detail/sorting_network/sort19.h index c98812d2..b606b109 100644 --- a/include/cpp-sort/detail/sorting_network/sort19.h +++ b/include/cpp-sort/detail/sorting_network/sort19.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort2.h b/include/cpp-sort/detail/sorting_network/sort2.h index acbf77e4..621d01a2 100644 --- a/include/cpp-sort/detail/sorting_network/sort2.h +++ b/include/cpp-sort/detail/sorting_network/sort2.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort20.h b/include/cpp-sort/detail/sorting_network/sort20.h index ad03df6d..4d6b1082 100644 --- a/include/cpp-sort/detail/sorting_network/sort20.h +++ b/include/cpp-sort/detail/sorting_network/sort20.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort21.h b/include/cpp-sort/detail/sorting_network/sort21.h index 33f53c37..ad36e368 100644 --- a/include/cpp-sort/detail/sorting_network/sort21.h +++ b/include/cpp-sort/detail/sorting_network/sort21.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort22.h b/include/cpp-sort/detail/sorting_network/sort22.h index 8e46fd8b..95ac0388 100644 --- a/include/cpp-sort/detail/sorting_network/sort22.h +++ b/include/cpp-sort/detail/sorting_network/sort22.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort23.h b/include/cpp-sort/detail/sorting_network/sort23.h index 8f69d845..4eeec33b 100644 --- a/include/cpp-sort/detail/sorting_network/sort23.h +++ b/include/cpp-sort/detail/sorting_network/sort23.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort24.h b/include/cpp-sort/detail/sorting_network/sort24.h index 71d14d5e..24977333 100644 --- a/include/cpp-sort/detail/sorting_network/sort24.h +++ b/include/cpp-sort/detail/sorting_network/sort24.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort25.h b/include/cpp-sort/detail/sorting_network/sort25.h index f3ee3085..53fc6fe5 100644 --- a/include/cpp-sort/detail/sorting_network/sort25.h +++ b/include/cpp-sort/detail/sorting_network/sort25.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort26.h b/include/cpp-sort/detail/sorting_network/sort26.h index 47db075b..20f93ae8 100644 --- a/include/cpp-sort/detail/sorting_network/sort26.h +++ b/include/cpp-sort/detail/sorting_network/sort26.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort27.h b/include/cpp-sort/detail/sorting_network/sort27.h index ce0896b3..6896fd63 100644 --- a/include/cpp-sort/detail/sorting_network/sort27.h +++ b/include/cpp-sort/detail/sorting_network/sort27.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort28.h b/include/cpp-sort/detail/sorting_network/sort28.h index a62d4faa..1654aac5 100644 --- a/include/cpp-sort/detail/sorting_network/sort28.h +++ b/include/cpp-sort/detail/sorting_network/sort28.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort29.h b/include/cpp-sort/detail/sorting_network/sort29.h index 3efa401d..785f8768 100644 --- a/include/cpp-sort/detail/sorting_network/sort29.h +++ b/include/cpp-sort/detail/sorting_network/sort29.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort3.h b/include/cpp-sort/detail/sorting_network/sort3.h index 26225c6b..085db9a7 100644 --- a/include/cpp-sort/detail/sorting_network/sort3.h +++ b/include/cpp-sort/detail/sorting_network/sort3.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort30.h b/include/cpp-sort/detail/sorting_network/sort30.h index fccb778a..4cf2257a 100644 --- a/include/cpp-sort/detail/sorting_network/sort30.h +++ b/include/cpp-sort/detail/sorting_network/sort30.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort31.h b/include/cpp-sort/detail/sorting_network/sort31.h index 1f09bd60..6d089737 100644 --- a/include/cpp-sort/detail/sorting_network/sort31.h +++ b/include/cpp-sort/detail/sorting_network/sort31.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort32.h b/include/cpp-sort/detail/sorting_network/sort32.h index 1ba756cf..4bb59619 100644 --- a/include/cpp-sort/detail/sorting_network/sort32.h +++ b/include/cpp-sort/detail/sorting_network/sort32.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort4.h b/include/cpp-sort/detail/sorting_network/sort4.h index 152fee95..b91bc162 100644 --- a/include/cpp-sort/detail/sorting_network/sort4.h +++ b/include/cpp-sort/detail/sorting_network/sort4.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort5.h b/include/cpp-sort/detail/sorting_network/sort5.h index 023c824a..10084520 100644 --- a/include/cpp-sort/detail/sorting_network/sort5.h +++ b/include/cpp-sort/detail/sorting_network/sort5.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort6.h b/include/cpp-sort/detail/sorting_network/sort6.h index 383fbc6d..03787f02 100644 --- a/include/cpp-sort/detail/sorting_network/sort6.h +++ b/include/cpp-sort/detail/sorting_network/sort6.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort7.h b/include/cpp-sort/detail/sorting_network/sort7.h index 8ee0367e..8fbdbbb2 100644 --- a/include/cpp-sort/detail/sorting_network/sort7.h +++ b/include/cpp-sort/detail/sorting_network/sort7.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort8.h b/include/cpp-sort/detail/sorting_network/sort8.h index 16f6316c..f6c87924 100644 --- a/include/cpp-sort/detail/sorting_network/sort8.h +++ b/include/cpp-sort/detail/sorting_network/sort8.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/sorting_network/sort9.h b/include/cpp-sort/detail/sorting_network/sort9.h index 34ec1a2b..cb9440d5 100644 --- a/include/cpp-sort/detail/sorting_network/sort9.h +++ b/include/cpp-sort/detail/sorting_network/sort9.h @@ -16,7 +16,7 @@ namespace detail typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/detail/spreadsort/detail/common.h b/include/cpp-sort/detail/spreadsort/detail/common.h index cc746644..58a4ab1e 100644 --- a/include/cpp-sort/detail/spreadsort/detail/common.h +++ b/include/cpp-sort/detail/spreadsort/detail/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -27,6 +27,7 @@ Phil Endecott and Frank Gennari #include #include #include "constants.h" +#include "../../type_traits.h" namespace cppsort { @@ -38,7 +39,7 @@ namespace detail { //Well, we're not using Boost in the end template - using disable_if_t = std::enable_if_t; + using disable_if_t = cppsort::detail::enable_if_t; //This only works on unsigned data types template diff --git a/include/cpp-sort/detail/spreadsort/detail/float_sort.h b/include/cpp-sort/detail/spreadsort/detail/float_sort.h index aff0eeb8..2264910f 100644 --- a/include/cpp-sort/detail/spreadsort/detail/float_sort.h +++ b/include/cpp-sort/detail/spreadsort/detail/float_sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -37,6 +37,7 @@ Phil Endecott and Frank Gennari #include "../../iterator_traits.h" #include "../../memcpy_cast.h" #include "../../pdqsort.h" +#include "../../type_traits.h" namespace cppsort { @@ -363,7 +364,7 @@ namespace detail //Checking whether the value type is a float, and trying a 32-bit integer template auto float_sort(RandomAccessIter first, RandomAccessIter last, Projection projection) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< sizeof(std::uint32_t) == sizeof(projected_t) && std::numeric_limits>::is_iec559, void @@ -378,7 +379,7 @@ namespace detail //Checking whether the value type is a double, and using a 64-bit integer template auto float_sort(RandomAccessIter first, RandomAccessIter last, Projection projection) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< sizeof(std::uint64_t) == sizeof(projected_t) && std::numeric_limits>::is_iec559, void diff --git a/include/cpp-sort/detail/spreadsort/detail/integer_sort.h b/include/cpp-sort/detail/spreadsort/detail/integer_sort.h index 50faef2c..d304f9a4 100644 --- a/include/cpp-sort/detail/spreadsort/detail/integer_sort.h +++ b/include/cpp-sort/detail/spreadsort/detail/integer_sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -33,6 +33,7 @@ Phil Endecott and Frank Gennari #include "common.h" #include "constants.h" #include "../../pdqsort.h" +#include "../../type_traits.h" namespace cppsort { @@ -202,7 +203,7 @@ namespace spreadsort template auto integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, Projection projection) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< sizeof(Div_type) <= sizeof(std::size_t), void > @@ -218,7 +219,7 @@ namespace spreadsort template auto integer_sort(RandomAccessIter first, RandomAccessIter last, Div_type, Projection projection) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< (sizeof(Div_type) > sizeof(std::size_t)) && sizeof(Div_type) <= sizeof(std::uintmax_t), void diff --git a/include/cpp-sort/detail/spreadsort/detail/string_sort.h b/include/cpp-sort/detail/spreadsort/detail/string_sort.h index cf749515..06cd0029 100644 --- a/include/cpp-sort/detail/spreadsort/detail/string_sort.h +++ b/include/cpp-sort/detail/spreadsort/detail/string_sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -33,6 +33,7 @@ Phil Endecott and Frank Gennari #include #include "common.h" #include "constants.h" +#include "../../type_traits.h" namespace cppsort { @@ -380,7 +381,7 @@ namespace spreadsort typename Unsigned_char_type> auto string_sort(RandomAccessIter first, RandomAccessIter last, Projection projection, Unsigned_char_type) - -> std::enable_if_t + -> cppsort::detail::enable_if_t { std::size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; std::vector bin_cache; @@ -393,7 +394,7 @@ namespace spreadsort typename Unsigned_char_type> auto reverse_string_sort(RandomAccessIter first, RandomAccessIter last, Projection projection, Unsigned_char_type) - -> std::enable_if_t + -> cppsort::detail::enable_if_t { std::size_t bin_sizes[(1 << (8 * sizeof(Unsigned_char_type))) + 1]; std::vector bin_cache; diff --git a/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h b/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h index a0ca5c46..439ea898 100644 --- a/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h +++ b/include/cpp-sort/detail/stable_adapter_self_sort_adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_STABLE_ADAPTER_SELF_SORT_ADAPTER_H_ @@ -17,6 +17,7 @@ #include #include #include "checkers.h" +#include "type_traits.h" namespace cppsort { @@ -45,7 +46,7 @@ namespace cppsort template auto operator()(Iterable&& iterable, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_stable_sort_method, decltype(std::forward(iterable).stable_sort(utility::as_function(args)...)) > @@ -55,7 +56,7 @@ namespace cppsort template auto operator()(Iterable&& iterable, Args&&... args) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_stable_sort_method, decltype(this->get()(std::forward(iterable), std::forward(args)...)) > @@ -84,7 +85,7 @@ namespace cppsort template< typename T, typename Compare, - typename = std::enable_if_t&>> + typename = detail::enable_if_t&>> > auto operator()(std::forward_list& iterable, Compare compare) const -> void @@ -102,7 +103,7 @@ namespace cppsort template< typename T, typename Compare, - typename = std::enable_if_t&>> + typename = detail::enable_if_t&>> > auto operator()(std::list& iterable, Compare compare) const -> void @@ -114,7 +115,6 @@ namespace cppsort // Sorter traits using is_always_stable = std::true_type; - using type = stable_adapter>; }; } diff --git a/include/cpp-sort/detail/std_list_traits.h b/include/cpp-sort/detail/std_list_traits.h deleted file mode 100644 index 0a5f1f57..00000000 --- a/include/cpp-sort/detail/std_list_traits.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2016 Morwenn - * SPDX-License-Identifier: MIT - */ -#ifndef CPPSORT_DETAIL_STD_LIST_TRAITS_H_ -#define CPPSORT_DETAIL_STD_LIST_TRAITS_H_ - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include - -namespace cppsort -{ -namespace detail -{ - // Small helpers to help disambiguate the standard list - // types in some specific scenarios - - template - struct is_std_list: - std::false_type - {}; - - template - struct is_std_list>: - std::true_type - {}; - - template - struct is_std_forward_list: - std::false_type - {}; - - template - struct is_std_forward_list>: - std::true_type - {}; -}} - -#endif // CPPSORT_DETAIL_STD_LIST_TRAITS_H_ diff --git a/include/cpp-sort/detail/swap_if.h b/include/cpp-sort/detail/swap_if.h index 1a1cb511..1d3a97e0 100644 --- a/include/cpp-sort/detail/swap_if.h +++ b/include/cpp-sort/detail/swap_if.h @@ -48,7 +48,7 @@ namespace detail template auto swap_if(Integer& x, Integer& y, std::less<>, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { Integer dx = x; x = (std::min)(x, y); @@ -57,7 +57,7 @@ namespace detail template auto swap_if(Float& x, Float& y, std::less<>, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { Float dx = x; x = (std::min)(x, y); @@ -66,7 +66,7 @@ namespace detail template auto swap_if(Integer& x, Integer& y, std::greater<>, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { Integer dx = x; x = (std::max)(x, y); @@ -75,7 +75,7 @@ namespace detail template auto swap_if(Float& x, Float& y, std::greater<>, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { Float dx = x; x = (std::max)(x, y); @@ -85,28 +85,28 @@ namespace detail #if CPPSORT_STD_IDENTITY_AVAILABLE template auto swap_if(Integer& x, Integer& y, std::less<> comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Float& x, Float& y, std::less<> comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Integer& x, Integer& y, std::greater<> comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Float& x, Float& y, std::greater<> comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } @@ -115,56 +115,56 @@ namespace detail #ifdef __cpp_lib_ranges template auto swap_if(Integer& x, Integer& y, std::ranges::less comp, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Float& x, Float& y, std::ranges::less comp, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Integer& x, Integer& y, std::ranges::greater comp, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Float& x, Float& y, std::ranges::greater comp, utility::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Integer& x, Integer& y, std::ranges::less comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Float& x, Float& y, std::ranges::less comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Integer& x, Integer& y, std::ranges::greater comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } template auto swap_if(Float& x, Float& y, std::ranges::greater comp, std::identity) noexcept - -> std::enable_if_t::value> + -> detail::enable_if_t::value> { return swap_if(x, y, comp, utility::identity{}); } @@ -177,7 +177,7 @@ namespace detail typename Iterator, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_detected_v || is_detected_v > @@ -198,7 +198,7 @@ namespace detail typename Iterator, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_detected_v && not is_detected_v >, diff --git a/include/cpp-sort/detail/type_traits.h b/include/cpp-sort/detail/type_traits.h index 45ef41f6..97642869 100644 --- a/include/cpp-sort/detail/type_traits.h +++ b/include/cpp-sort/detail/type_traits.h @@ -16,7 +16,7 @@ namespace cppsort namespace detail { //////////////////////////////////////////////////////////// - // Alternative to std::conditional from C++11 + // Alternative to std::conditional_t from C++11 template struct conditional @@ -35,6 +35,22 @@ namespace detail template using conditional_t = typename conditional::template type; + //////////////////////////////////////////////////////////// + // Alternative to std::enable_if_t from C++11 + + template + struct enable_if_impl {}; + + template<> + struct enable_if_impl + { + template + using type = T; + }; + + template + using enable_if_t = typename enable_if_impl::template type; + //////////////////////////////////////////////////////////// // std::void_t from C++17 @@ -292,6 +308,27 @@ namespace detail using is_unsigned = std::is_unsigned; #endif + //////////////////////////////////////////////////////////// + // is_specialization_of: check that a given type is a + // specialization of a given class template, with the caveat + // that the class template can only have type template + // parameters + // + // See https://wg21.link/P2098R0 + + template class Template> + struct is_specialization_of: + std::false_type + {}; + + template class Template, typename... Args> + struct is_specialization_of, Template>: + std::true_type + {}; + + template class Template> + constexpr bool is_specialization_of_v = is_specialization_of::value; + //////////////////////////////////////////////////////////// // is_in_pack: check whether a given std::size_t value // appears in a std::size_t... parameter pack diff --git a/include/cpp-sort/detail/vergesort.h b/include/cpp-sort/detail/vergesort.h index e18b257f..ff7004bc 100644 --- a/include/cpp-sort/detail/vergesort.h +++ b/include/cpp-sort/detail/vergesort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_DETAIL_VERGESORT_H_ @@ -505,9 +505,9 @@ namespace verge template auto get_maybe_stable(std::true_type, Sorter&& sorter) - -> cppsort::stable_adapter + -> cppsort::stable_t { - return cppsort::stable_adapter(std::move(sorter)); + return cppsort::stable_t(std::move(sorter)); } template @@ -531,10 +531,10 @@ namespace verge { // Adapt the fallback sorter depending on whether a stable // or an unstable sort is wanted - verge::sort(iterator_category_t{}, - std::move(first), std::move(last), size, - std::move(compare), std::move(projection), - get_maybe_stable(std::integral_constant{}, std::move(fallback))); + verge::sort(iterator_category_t{}, + std::move(first), std::move(last), size, + std::move(compare), std::move(projection), + get_maybe_stable(std::integral_constant{}, std::move(fallback))); } constexpr auto default_sorter_for_impl(std::bidirectional_iterator_tag) diff --git a/include/cpp-sort/detail/wiki_sort.h b/include/cpp-sort/detail/wiki_sort.h index fc3e5cfb..23fa5e79 100644 --- a/include/cpp-sort/detail/wiki_sort.h +++ b/include/cpp-sort/detail/wiki_sort.h @@ -327,15 +327,15 @@ namespace detail } }; - // bottom-up merge sort combined with an in-place merge algorithm for O(1) memory use - template auto sort(RandomAccessIterator first, RandomAccessIterator last, + CacheIterator cache_begin, difference_type_t cache_size, Compare compare, Projection projection) -> void { using utility::iter_swap; - using rvalue_type = rvalue_type_t; using difference_type = difference_type_t; difference_type size = last - first; @@ -406,16 +406,7 @@ namespace detail } } - // use a small cache to speed up some of the operations - // just keep in mind that making it too small ruins the point (nothing will fit into it), - // and making it too large also ruins the point (so much for "low memory"!) - typename BufferProvider::template buffer cache(size); - difference_type cache_size = cache.size(); - - // may be pointer of something else - using cache_iterator = decltype(cache.begin()); - - // then merge sort the higher levels, which can be 8-15, 16-31, 32-63, 64-127, etc. + // merge sort the higher levels, which can be 8-15, 16-31, 32-63, 64-127, etc. while (true) { // if every A and B block will fit into the cache, use a special branch specifically for merging with the cache // (we use < rather than <= since the block size might be one more than iterator.length()) @@ -434,11 +425,11 @@ namespace detail if (comp(proj(*std::prev(B1.end)), proj(*A1.start))) { // the two ranges are in reverse order, so move them in reverse order into the cache - detail::move(A1.start, A1.end, cache.begin() + B1.length()); - detail::move(B1.start, B1.end, cache.begin()); + detail::move(A1.start, A1.end, cache_begin + B1.length()); + detail::move(B1.start, B1.end, cache_begin); } else if (comp(proj(*B1.start), proj(*std::prev(A1.end)))) { // these two ranges weren't already in order, so merge them into the cache - merge_move(A1.start, A1.end, B1.start, B1.end, cache.begin(), + merge_move(A1.start, A1.end, B1.start, B1.end, cache_begin, compare, projection, projection); } else { // if A1, B1, A2, and B2 are all in order, skip doing anything else @@ -446,30 +437,30 @@ namespace detail not comp(proj(*A2.start), proj(*std::prev(B1.end)))) continue; // move A1 and B1 into the cache in the same order - detail::move(A1.start, B1.end, cache.begin()); + detail::move(A1.start, B1.end, cache_begin); } A1 = { A1.start, B1.end }; // merge A2 and B2 into the cache if (comp(proj(*std::prev(B2.end)), proj(*A2.start))) { // the two ranges are in reverse order, so move them in reverse order into the cache - detail::move(A2.start, A2.end, cache.begin() + (A1.length() + B2.length())); - detail::move(B2.start, B2.end, cache.begin() + A1.length()); + detail::move(A2.start, A2.end, cache_begin + (A1.length() + B2.length())); + detail::move(B2.start, B2.end, cache_begin + A1.length()); } else if (comp(proj(*B2.start), proj(*std::prev(A2.end)))) { // these two ranges weren't already in order, so merge them into the cache - merge_move(A2.start, A2.end, B2.start, B2.end, cache.begin() + A1.length(), + merge_move(A2.start, A2.end, B2.start, B2.end, cache_begin + A1.length(), compare, projection, projection); } else { // move A2 and B2 into the cache in the same order - detail::move(A2.start, B2.end, cache.begin() + A1.length()); + detail::move(A2.start, B2.end, cache_begin + A1.length()); } A2 = { A2.start, B2.end }; // merge A1 and A2 from the cache into the array - Range A3 = { cache.begin(), cache.begin() + A1.length() }; - Range B3 = { - cache.begin() + A1.length(), - cache.begin() + (A1.length() + A2.length()) + Range A3 = { cache_begin, cache_begin + A1.length() }; + Range B3 = { + cache_begin + A1.length(), + cache_begin + (A1.length() + A2.length()) }; if (comp(proj(*std::prev(B3.end)), proj(*A3.start))) { @@ -502,7 +493,7 @@ namespace detail } else if (comp(proj(*B.start), proj(*std::prev(A.end)))) { // these two ranges weren't already in order, so we need to merge them buffered_inplace_merge(A.start, A.end, B.end, compare, projection, - A.length(), B.length(), cache.begin()); + A.length(), B.length(), cache_begin); } } } @@ -763,7 +754,7 @@ namespace detail // if the first unevenly sized A block fits into the cache, move it there for when we go to Merge it // otherwise, if the second buffer is available, block swap the contents into that if (cache_size > 0 && lastA.length() <= cache_size) { - detail::move(lastA.start, lastA.end, cache.begin()); + detail::move(lastA.start, lastA.end, cache_begin); } else if (buffer2.length() > 0) { detail::swap_ranges_overlap(lastA.start, lastA.end, buffer2.start); } @@ -797,7 +788,7 @@ namespace detail // internal buffer exists we'll use it, otherwise we'll use a strictly // in-place merge algorithm if (cache_size > 0 && lastA.length() <= cache_size) { - half_inplace_merge(cache.begin(), cache.begin() + lastA.length(), + half_inplace_merge(cache_begin, cache_begin + lastA.length(), lastA.end, B_split, lastA.start, (std::min)(lastA.length(), B_split - lastA.end), compare, projection); @@ -812,7 +803,7 @@ namespace detail // move the previous A block into the cache or buffer2, since // that's where we need it to be when we go to merge it anyway if (block_size <= cache_size) { - detail::move(blockA.start, blockA.start + block_size, cache.begin()); + detail::move(blockA.start, blockA.start + block_size, cache_begin); detail::move(B_split, B_split + B_remaining, blockA.start + (block_size - B_remaining)); } else { detail::swap_ranges_overlap(blockA.start, blockA.start + block_size, buffer2.start); @@ -859,7 +850,7 @@ namespace detail // merge the last A block with the remaining B values if (cache_size > 0 && lastA.length() <= cache_size) { - half_inplace_merge(cache.begin(), cache.begin() + lastA.length(), + half_inplace_merge(cache_begin, cache_begin + lastA.length(), lastA.end, B.end, lastA.start, (std::min)(lastA.length(), B.end - lastA.end), compare, projection); @@ -927,8 +918,15 @@ namespace detail Compare compare, Projection projection) -> void { - Wiki::sort(std::move(first), std::move(last), - std::move(compare), std::move(projection)); + // use a small cache to speed up some of the operations + // just keep in mind that making it too small ruins the point (nothing will fit into it), + // and making it too large also ruins the point (so much for "low memory"!) + using rvalue_type = rvalue_type_t; + typename BufferProvider::template buffer cache(last - first); + + Wiki::sort(std::move(first), std::move(last), + cache.begin(), cache.size(), + std::move(compare), std::move(projection)); } }} diff --git a/include/cpp-sort/fixed/low_comparisons_sorter.h b/include/cpp-sort/fixed/low_comparisons_sorter.h index fac933a0..be6b5726 100644 --- a/include/cpp-sort/fixed/low_comparisons_sorter.h +++ b/include/cpp-sort/fixed/low_comparisons_sorter.h @@ -72,6 +72,16 @@ namespace cppsort }; } +// Common includes for specializations +#include +#include +#include +#include +#include +#include "../detail/front_insert.h" +#include "../detail/swap_if.h" +#include "../detail/type_traits.h" + // Specializations of low_comparisons_sorter for some values of N #include "../detail/low_comparisons/sort2.h" #include "../detail/low_comparisons/sort3.h" diff --git a/include/cpp-sort/fixed/low_moves_sorter.h b/include/cpp-sort/fixed/low_moves_sorter.h index 4ab33554..0b460a2e 100644 --- a/include/cpp-sort/fixed/low_moves_sorter.h +++ b/include/cpp-sort/fixed/low_moves_sorter.h @@ -38,7 +38,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/fixed/merge_exchange_network_sorter.h b/include/cpp-sort/fixed/merge_exchange_network_sorter.h index 3cbe9333..75957bbf 100644 --- a/include/cpp-sort/fixed/merge_exchange_network_sorter.h +++ b/include/cpp-sort/fixed/merge_exchange_network_sorter.h @@ -22,6 +22,7 @@ #include "../detail/empty_sorter.h" #include "../detail/iterator_traits.h" #include "../detail/make_array.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -100,7 +101,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h index 5415aa51..e7f8a128 100644 --- a/include/cpp-sort/fixed/odd_even_merge_network_sorter.h +++ b/include/cpp-sort/fixed/odd_even_merge_network_sorter.h @@ -22,6 +22,7 @@ #include "../detail/empty_sorter.h" #include "../detail/iterator_traits.h" #include "../detail/make_array.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -88,7 +89,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/fixed/sorting_network_sorter.h b/include/cpp-sort/fixed/sorting_network_sorter.h index f0d26675..73de1c7b 100644 --- a/include/cpp-sort/fixed/sorting_network_sorter.h +++ b/include/cpp-sort/fixed/sorting_network_sorter.h @@ -78,6 +78,7 @@ namespace cppsort #include #include #include "../detail/swap_if.h" +#include "../detail/type_traits.h" // Specializations of sorting_network_sorter for some values of N #include "../detail/sorting_network/sort2.h" diff --git a/include/cpp-sort/probes.h b/include/cpp-sort/probes.h index e58dd791..c36830ea 100644 --- a/include/cpp-sort/probes.h +++ b/include/cpp-sort/probes.h @@ -8,6 +8,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// +#include #include #include #include diff --git a/include/cpp-sort/probes/block.h b/include/cpp-sort/probes/block.h new file mode 100644 index 00000000..a66c64de --- /dev/null +++ b/include/cpp-sort/probes/block.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_PROBES_BLOCK_H_ +#define CPPSORT_PROBES_BLOCK_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../detail/functional.h" +#include "../detail/immovable_vector.h" +#include "../detail/iterator_traits.h" +#include "../detail/pdqsort.h" +#include "../detail/type_traits.h" + +namespace cppsort +{ +namespace probe +{ + namespace detail + { + template + auto block_probe_algo(ForwardIterator first, ForwardIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection) + -> ::cppsort::detail::difference_type_t + { + using difference_type = ::cppsort::detail::difference_type_t; + auto&& comp = utility::as_function(compare); + auto&& proj = utility::as_function(projection); + + if (size < 2) { + return 0; + } + + //////////////////////////////////////////////////////////// + // Indirectly sort the iterators + + // Copy the iterators in a vector + cppsort::detail::immovable_vector iterators(size); + for (auto it = first; it != last; ++it) { + iterators.emplace_back(it); + } + + // Sort the iterators on pointed values + cppsort::detail::pdqsort( + iterators.begin(), iterators.end(), compare, + cppsort::detail::indirect(projection) + ); + + //////////////////////////////////////////////////////////// + // Count the number of consecutive pairs in the original + // collection that can't be found in the sorted one + + difference_type count = 0; + auto it_last_1 = std::prev(iterators.end()); + for (auto it = iterators.begin(); it != it_last_1; ++it) { + auto orig_next = std::next(*it); + if (orig_next == last) { + if (comp(proj(**it), proj(**it_last_1)) || comp(proj(**it_last_1), proj(**it))) { + ++count; + } + continue; + } + auto sorted_next = *std::next(it); + if (comp(proj(*orig_next), proj(*sorted_next)) || comp(proj(*sorted_next), proj(*orig_next))) { + ++count; + } + } + return count; + } + + struct block_impl + { + template< + typename ForwardIterable, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = cppsort::detail::enable_if_t< + is_projection_v + > + > + auto operator()(ForwardIterable&& iterable, + Compare compare={}, Projection projection={}) const + -> decltype(auto) + { + return block_probe_algo(std::begin(iterable), std::end(iterable), + utility::size(iterable), + std::move(compare), std::move(projection)); + } + + template< + typename ForwardIterator, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = cppsort::detail::enable_if_t< + is_projection_iterator_v + > + > + auto operator()(ForwardIterator first, ForwardIterator last, + Compare compare={}, Projection projection={}) const + -> decltype(auto) + { + return block_probe_algo(first, last, std::distance(first, last), + std::move(compare), std::move(projection)); + } + + template + static constexpr auto max_for_size(Integer n) + -> Integer + { + return n == 0 ? 0 : n - 1; + } + }; + } + + namespace + { + constexpr auto&& block = utility::static_const< + sorter_facade + >::value; + } +}} + +#endif // CPPSORT_PROBES_BLOCK_H_ diff --git a/include/cpp-sort/probes/dis.h b/include/cpp-sort/probes/dis.h index 69cc87c6..952fde70 100644 --- a/include/cpp-sort/probes/dis.h +++ b/include/cpp-sort/probes/dis.h @@ -11,14 +11,18 @@ #include #include #include +#include #include +#include #include #include -#include #include #include #include +#include "../detail/immovable_vector.h" +#include "../detail/is_p_sorted.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -27,36 +31,110 @@ namespace probe namespace detail { template - auto dis_probe_algo(ForwardIterator first, ForwardIterator last, - cppsort::detail::difference_type_t size, - Compare compare, Projection projection) + auto inplace_dis_probe_algo(ForwardIterator first, ForwardIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection) -> ::cppsort::detail::difference_type_t { - using difference_type = cppsort::detail::difference_type_t; + // Simple algorithm in O(n log n) time and O(1) space + + auto res = 0; + auto res_it = first; + while (size > 0) { + auto p = res; + auto pth = res_it; + p += size / 2; + std::advance(pth, size / 2); + if (cppsort::detail::is_p_sorted(first, last, pth, compare, projection)) { + size /= 2; + } else { + res = ++p; + res_it = ++pth; + size -= size / 2 + 1; + } + } + return res; + } + + template + auto allocating_dis_probe_algo(BidirectionalIterator first, BidirectionalIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection) + -> ::cppsort::detail::difference_type_t + { + using difference_type = ::cppsort::detail::difference_type_t; auto&& comp = utility::as_function(compare); auto&& proj = utility::as_function(projection); + // Space-optimized version of the algorithm described in *Roughly Sorting: + // Sequential and Parallel Approach* by T. Altman and Y. Igarashi + if (size < 2) { return 0; } - difference_type max_dist = 0; - auto it1 = first; - do { - auto&& value = proj(*it1); + // Algorithm LR: cumulative max from left to right + cppsort::detail::immovable_vector lr_cummax(size); + lr_cummax.emplace_back(first); + for (auto it = std::next(first) ; it != last ; ++it) { + if (comp(proj(*lr_cummax.back()), proj(*it))) { + lr_cummax.emplace_back(it); + } else { + lr_cummax.emplace_back(lr_cummax.back()); + } + } - difference_type dist = 1; // Distance between it1 and it2 - for (auto it2 = std::next(it1) ; it2 != last ; ++it2) { - if (comp(proj(*it2), value)) { - max_dist = (std::max)(max_dist, dist); + // Merged algorithms without extra storage: + // - RL: cumulative min from right to left + // - DM: max distance of an inversion + difference_type res = 0; + difference_type i = size; + auto rl_it = std::prev(last); // Iterator to the current RL element + auto rl_min_it = rl_it; // Iterator to the current minimum of RL + for (auto j = i ; j > 0 ; --j) { + while (j <= i && not comp(proj(*lr_cummax[j - 1]), proj(*rl_min_it)) + && (j == 1 || not comp(proj(*rl_min_it), proj(*lr_cummax[j - 2])))) { + // Compute the next value of DM + res = std::max(res, i - j); + // Compute the next value of RL + if (--i <= res) { + return res; + } + --rl_it; + if (comp(proj(*rl_it), proj(*rl_min_it))) { + rl_min_it = rl_it; } - ++dist; } + } - ++it1; - --size; - } while (max_dist < size); - return max_dist; + return res; + } + + template + auto dis_probe_algo(BidirectionalIterator first, BidirectionalIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection, + std::bidirectional_iterator_tag) + -> ::cppsort::detail::difference_type_t + { + try { + return allocating_dis_probe_algo(first, last, size, compare, projection); + } catch (std::bad_alloc&) { + return inplace_dis_probe_algo( + first, last, size, + std::move(compare), std::move(projection) + ); + } + } + + template + auto dis_probe_algo(ForwardIterator first, ForwardIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection, + std::forward_iterator_tag) + -> ::cppsort::detail::difference_type_t + { + return inplace_dis_probe_algo(first, last, size, compare, projection); } struct dis_impl @@ -65,24 +143,27 @@ namespace probe typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_v > > - auto operator()(ForwardIterable&& iterable, - Compare compare={}, Projection projection={}) const + auto operator()(ForwardIterable&& iterable, Compare compare={}, Projection projection={}) const -> decltype(auto) { + using category = cppsort::detail::iterator_category_t< + cppsort::detail::remove_cvref_t + >; return dis_probe_algo(std::begin(iterable), std::end(iterable), utility::size(iterable), - std::move(compare), std::move(projection)); + std::move(compare), std::move(projection), + category{}); } template< typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > @@ -90,8 +171,10 @@ namespace probe Compare compare={}, Projection projection={}) const -> decltype(auto) { + using category = cppsort::detail::iterator_category_t; return dis_probe_algo(first, last, std::distance(first, last), - std::move(compare), std::move(projection)); + std::move(compare), std::move(projection), + category{}); } template diff --git a/include/cpp-sort/probes/enc.h b/include/cpp-sort/probes/enc.h index e0fc97ab..76cc4ff0 100644 --- a/include/cpp-sort/probes/enc.h +++ b/include/cpp-sort/probes/enc.h @@ -22,6 +22,7 @@ #include "../detail/functional.h" #include "../detail/iterator_traits.h" #include "../detail/lower_bound.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -64,7 +65,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/exc.h b/include/cpp-sort/probes/exc.h index 0dec911b..c97c76e8 100644 --- a/include/cpp-sort/probes/exc.h +++ b/include/cpp-sort/probes/exc.h @@ -23,6 +23,7 @@ #include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -113,7 +114,7 @@ namespace probe typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_v > > @@ -130,7 +131,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/ham.h b/include/cpp-sort/probes/ham.h index cbf17ad2..03ee0086 100644 --- a/include/cpp-sort/probes/ham.h +++ b/include/cpp-sort/probes/ham.h @@ -22,6 +22,7 @@ #include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -78,7 +79,7 @@ namespace probe typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_v > > @@ -95,7 +96,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/inv.h b/include/cpp-sort/probes/inv.h index 8158dd03..cf521125 100644 --- a/include/cpp-sort/probes/inv.h +++ b/include/cpp-sort/probes/inv.h @@ -21,6 +21,7 @@ #include "../detail/count_inversions.h" #include "../detail/functional.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -61,7 +62,7 @@ namespace probe typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_v > > @@ -78,7 +79,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/max.h b/include/cpp-sort/probes/max.h index ab54d7db..94dbf57d 100644 --- a/include/cpp-sort/probes/max.h +++ b/include/cpp-sort/probes/max.h @@ -24,6 +24,7 @@ #include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -92,7 +93,7 @@ namespace probe typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_v > > @@ -109,7 +110,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/mono.h b/include/cpp-sort/probes/mono.h index 82b2daf4..ae311b70 100644 --- a/include/cpp-sort/probes/mono.h +++ b/include/cpp-sort/probes/mono.h @@ -17,6 +17,7 @@ #include #include #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -30,7 +31,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/osc.h b/include/cpp-sort/probes/osc.h index 78107d9e..b527b355 100644 --- a/include/cpp-sort/probes/osc.h +++ b/include/cpp-sort/probes/osc.h @@ -11,13 +11,23 @@ #include #include #include +#include +#include #include +#include +#include #include #include #include #include +#include #include +#include "../detail/equal_range.h" +#include "../detail/functional.h" +#include "../detail/immovable_vector.h" #include "../detail/iterator_traits.h" +#include "../detail/pdqsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -25,50 +35,177 @@ namespace probe { namespace detail { + template + auto inplace_osc_algo(ForwardIterator first, ForwardIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection) + -> ::cppsort::detail::difference_type_t + { + // Deprecated in-place O(n^2) algorithm + + using difference_type = cppsort::detail::difference_type_t; + auto&& comp = utility::as_function(compare); + auto&& proj = utility::as_function(projection); + + if (size < 2) { + return 0; + } + + difference_type count = 0; + for (auto it = first; it != last; ++it) { + auto&& value = proj(*it); + + auto current = first; + auto next = std::next(first); + + while (next != last) { + if (comp((std::min)(proj(*current), proj(*next), comp), value) && + comp(value, (std::max)(proj(*current), proj(*next), comp))) { + ++count; + } + ++current; + ++next; + } + } + return count; + } + + template + auto allocating_osc_algo(ForwardIterator first, ForwardIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection) + -> ::cppsort::detail::difference_type_t + { + using difference_type = ::cppsort::detail::difference_type_t; + auto&& comp = utility::as_function(compare); + auto&& proj = utility::as_function(projection); + + if (size < 2) { + return 0; + } + + //////////////////////////////////////////////////////////// + // Indirectly sort the iterators + + // Copy the iterators in a vector + cppsort::detail::immovable_vector iterators(size); + for (auto it = first; it != last; ++it) { + iterators.emplace_back(it); + } + + // Sort the iterators on pointed values + cppsort::detail::pdqsort( + iterators.begin(), iterators.end(), compare, + cppsort::detail::indirect(projection) + ); + + //////////////////////////////////////////////////////////// + // Compute the oscillation + + // This algorithm was proposed by Control and works as follow: + // initialize a vector of N 0, then for each pair of elements + // in the collection (ex: (a,b), (b,c), (c,d), etc.), find the + // position of the elements of the pair in the sorted collection + // (pos_min and pos_max), increment cross[pos_min + 1] and + // decrement cross[pos_max - 1]. Then compute the prefix sum of + // cross, the oscillation is the sum of that prefix sum. + + // Note: in the following algorithm, all upper_bound calls are + // performed on the full iterator sequence without taking + // advantage of the information we already have to reduce + // the search space. This is because benchmarks reported + // that reducing the search space made the algorithm up to + // twice as slow. Comments in the code contain the lines + // required to reduce the search space again. + + std::vector cross(size, 0); + + auto prev_bounds = cppsort::detail::equal_range( + iterators.begin(), iterators.end(), proj(*first), + compare, cppsort::detail::indirect(projection) + ); + + for (auto prev = first, current = std::next(prev); current != last; ++prev, (void)++current) { + difference_type min_idx, max_idx; + if (comp(proj(*prev), proj(*current))) { + auto current_bounds = cppsort::detail::equal_range( + //prev_bounds.second, std::prev(iterators.end()), proj(*current), + iterators.begin(), iterators.end(), proj(*current), + compare, cppsort::detail::indirect(projection) + ); + min_idx = prev_bounds.second - iterators.begin(); + max_idx = current_bounds.first - iterators.begin(); + prev_bounds = current_bounds; + } else if (comp(proj(*current), proj(*prev))) { + auto current_bounds = cppsort::detail::equal_range( + //iterators.begin(), prev_bounds.first, proj(*current), + iterators.begin(), iterators.end(), proj(*current), + compare, cppsort::detail::indirect(projection) + ); + min_idx = current_bounds.second - iterators.begin(); + max_idx = prev_bounds.first - iterators.begin(); + prev_bounds = current_bounds; + } else { + // *prev == *current, bounds don't change + continue; + } + + cross[min_idx] += 1; + cross[max_idx] -= 1; + } + + std::partial_sum(cross.begin(), cross.end(), cross.begin()); + return std::accumulate(cross.begin(), cross.end(), difference_type(0)); + } + + template + auto osc_algo(ForwardIterator first, ForwardIterator last, + cppsort::detail::difference_type_t size, + Compare compare, Projection projection) + -> ::cppsort::detail::difference_type_t + { + try { + return allocating_osc_algo(first, last, size, compare, projection); + } catch (std::bad_alloc&) { + return inplace_osc_algo( + first, last, size, + std::move(compare), std::move(projection) + ); + } + } + struct osc_impl { + template< + typename ForwardIterable, + typename Compare = std::less<>, + typename Projection = utility::identity, + typename = cppsort::detail::enable_if_t< + is_projection_v + > + > + auto operator()(ForwardIterable&& iterable, Compare compare={}, Projection projection={}) const + -> decltype(auto) + { + return osc_algo(std::begin(iterable), std::end(iterable), + utility::size(iterable), + std::move(compare), std::move(projection)); + } + template< typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > auto operator()(ForwardIterator first, ForwardIterator last, Compare compare={}, Projection projection={}) const - -> cppsort::detail::difference_type_t + -> decltype(auto) { - using difference_type = cppsort::detail::difference_type_t; - auto&& comp = utility::as_function(compare); - auto&& proj = utility::as_function(projection); - - if (first == last || std::next(first) == last) - { - return 0; - } - - difference_type count = 0; - for (auto it = first ; it != last ; ++it) - { - auto&& value = proj(*it); - - auto current = first; - auto next = std::next(first); - - while (next != last) - { - if (comp((std::min)(proj(*current), proj(*next)), value) && - comp(value, (std::max)(proj(*current), proj(*next)))) - { - ++count; - } - - ++current; - ++next; - } - } - return count; + return osc_algo(first, last, std::distance(first, last), + std::move(compare), std::move(projection)); } template diff --git a/include/cpp-sort/probes/par.h b/include/cpp-sort/probes/par.h index dae6d1a2..14635eab 100644 --- a/include/cpp-sort/probes/par.h +++ b/include/cpp-sort/probes/par.h @@ -8,13 +8,10 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include +#include #include -#include -#include #include -#include "../detail/is_p_sorted.h" +#include "../detail/attributes.h" namespace cppsort { @@ -22,47 +19,14 @@ namespace probe { namespace detail { - struct par_impl - { - template< - typename RandomAccessIterator, - typename Compare = std::less<>, - typename Projection = utility::identity, - typename = std::enable_if_t< - is_projection_iterator_v - > - > - auto operator()(RandomAccessIterator first, RandomAccessIterator last, - Compare compare={}, Projection projection={}) const - -> cppsort::detail::difference_type_t - { - auto size = last - first; - - auto res = 0; - while (size > 0) { - auto p = res; - p += size / 2; - if (cppsort::detail::is_p_sorted(first, last, p, compare, projection)) { - size /= 2; - } else { - res = ++p; - size -= size / 2 + 1; - } - } - return res; - } - - template - static constexpr auto max_for_size(Integer n) - -> Integer - { - return n == 0 ? 0 : n - 1; - } - }; + struct par_impl: + dis_impl + {}; } namespace { + CPPSORT_DEPRECATED("probe::par is deprecated and will be removed in version 2.0.0, use probe::dis instead") constexpr auto&& par = utility::static_const< sorter_facade >::value; diff --git a/include/cpp-sort/probes/rem.h b/include/cpp-sort/probes/rem.h index d390b25c..cdd20d6f 100644 --- a/include/cpp-sort/probes/rem.h +++ b/include/cpp-sort/probes/rem.h @@ -18,6 +18,7 @@ #include #include #include "../detail/longest_non_descending_subsequence.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -31,7 +32,7 @@ namespace probe typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_v && ( cppsort::detail::is_detected_v< cppsort::utility::detail::has_size_method_t, @@ -65,7 +66,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/runs.h b/include/cpp-sort/probes/runs.h index 444b7e58..6677f36f 100644 --- a/include/cpp-sort/probes/runs.h +++ b/include/cpp-sort/probes/runs.h @@ -17,6 +17,7 @@ #include #include #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -30,7 +31,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/probes/sus.h b/include/cpp-sort/probes/sus.h index 18d8e1de..4fbe0953 100644 --- a/include/cpp-sort/probes/sus.h +++ b/include/cpp-sort/probes/sus.h @@ -19,6 +19,7 @@ #include #include "../detail/functional.h" #include "../detail/longest_non_descending_subsequence.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace probe typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/refined.h b/include/cpp-sort/refined.h index afa1145b..bb8d5122 100644 --- a/include/cpp-sort/refined.h +++ b/include/cpp-sort/refined.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_REFINED_H_ @@ -35,7 +35,7 @@ namespace cppsort template< typename T, typename Function, - typename = std::enable_if_t> + typename = detail::enable_if_t> > auto refined(Function func) noexcept(noexcept(func.template refine())) @@ -47,7 +47,7 @@ namespace cppsort template< typename T, typename Function, - typename = std::enable_if_t> + typename = detail::enable_if_t> > constexpr auto refined(Function&& func) noexcept -> Function&& diff --git a/include/cpp-sort/sort.h b/include/cpp-sort/sort.h index 688237f1..b98692f2 100644 --- a/include/cpp-sort/sort.h +++ b/include/cpp-sort/sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORT_H_ @@ -13,6 +13,7 @@ #include #include #include "detail/config.h" +#include "detail/type_traits.h" namespace cppsort { @@ -30,7 +31,7 @@ namespace cppsort template< typename Iterable, typename Compare, - typename = std::enable_if_t> + typename = detail::enable_if_t> > CPPSORT_DEPRECATED("cppsort::sort() is deprecated and will be removed in version 2.0.0") auto sort(Iterable&& iterable, Compare compare) @@ -43,7 +44,7 @@ namespace cppsort typename Iterable, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_comparison_sorter_v && not is_projection_sorter_v && not is_sorter_iterator_v && @@ -69,7 +70,7 @@ namespace cppsort template< typename Iterator, typename Func, - typename = std::enable_if_t> + typename = detail::enable_if_t> > CPPSORT_DEPRECATED("cppsort::sort() is deprecated and will be removed in version 2.0.0") auto sort(Iterator first, Iterator last, Func func) @@ -82,7 +83,7 @@ namespace cppsort typename Iterator, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_comparison_sorter_iterator_v && not is_projection_sorter_iterator_v > @@ -101,7 +102,7 @@ namespace cppsort template< typename Sorter, typename Iterable, - typename = std::enable_if_t> + typename = detail::enable_if_t> > CPPSORT_DEPRECATED("cppsort::sort() is deprecated and will be removed in version 2.0.0") auto sort(const Sorter& sorter, Iterable&& iterable) @@ -114,7 +115,7 @@ namespace cppsort typename Sorter, typename Iterable, typename Func, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_comparison_sorter_v || is_projection_sorter_v > @@ -144,7 +145,7 @@ namespace cppsort template< typename Sorter, typename Iterator, - typename = std::enable_if_t> + typename = detail::enable_if_t> > CPPSORT_DEPRECATED("cppsort::sort() is deprecated and will be removed in version 2.0.0") auto sort(const Sorter& sorter, Iterator first, Iterator last) @@ -157,7 +158,7 @@ namespace cppsort typename Sorter, typename Iterator, typename Func, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_comparison_sorter_iterator_v || is_projection_sorter_iterator_v > diff --git a/include/cpp-sort/sorter_facade.h b/include/cpp-sort/sorter_facade.h index ce65b9cb..f18c2b25 100644 --- a/include/cpp-sort/sorter_facade.h +++ b/include/cpp-sort/sorter_facade.h @@ -36,7 +36,7 @@ namespace cppsort typename... Args > static constexpr auto invoke(Args... args) - -> std::enable_if_t< + -> detail::enable_if_t< not std::is_void::value, Ret > @@ -49,7 +49,7 @@ namespace cppsort typename... Args > static constexpr auto invoke(Args... args) - -> std::enable_if_t< + -> detail::enable_if_t< std::is_void::value, void > @@ -175,7 +175,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_sort::value, decltype(Sorter::operator()(std::forward(iterable))) > @@ -185,7 +185,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_sort::value, decltype(Sorter::operator()(std::begin(iterable), std::end(iterable))) > @@ -198,7 +198,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_sort_iterator< Sorter, Iterator, @@ -214,7 +214,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_sort< Sorter, Iterable, @@ -230,7 +230,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Compare compare) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort< Sorter, Iterable, @@ -254,7 +254,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort_iterator< Sorter, Iterator, @@ -275,7 +275,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator< Sorter, Iterator, @@ -297,7 +297,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort< Sorter, Iterable, @@ -318,7 +318,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort< Sorter, Iterable, @@ -345,7 +345,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort< Sorter, Iterable, @@ -382,7 +382,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort< Sorter, Iterable, @@ -418,7 +418,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, std::less<>) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort_iterator>::value, decltype(Sorter::operator()(std::move(first), std::move(last))) > @@ -428,7 +428,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<>) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -443,7 +443,7 @@ namespace cppsort #ifdef __cpp_lib_ranges template constexpr auto operator()(Iterator first, Iterator last, std::ranges::less) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort_iterator::value, decltype(Sorter::operator()(std::move(first), std::move(last))) > @@ -453,7 +453,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::ranges::less) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -471,7 +471,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, utility::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator::value && not detail::has_comparison_projection_sort_iterator< Sorter, @@ -487,7 +487,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, utility::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -508,7 +508,7 @@ namespace cppsort #if CPPSORT_STD_IDENTITY_AVAILABLE template constexpr auto operator()(Iterator first, Iterator last, std::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator::value && not detail::has_comparison_projection_sort_iterator< Sorter, @@ -524,7 +524,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -548,7 +548,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -567,7 +567,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_projection_sort< Sorter, Iterable, @@ -586,7 +586,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -614,7 +614,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, std::less<>, utility::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -629,7 +629,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<>, utility::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -645,7 +645,7 @@ namespace cppsort #if CPPSORT_STD_IDENTITY_AVAILABLE template constexpr auto operator()(Iterator first, Iterator last, std::less<>, std::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -660,7 +660,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<>, std::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -676,7 +676,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, std::less<> compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -693,7 +693,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, std::less<>, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -715,7 +715,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<> compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_projection_sort< Sorter, Iterable, @@ -732,7 +732,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<> compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -755,7 +755,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<>, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -783,7 +783,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::less<>, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -817,7 +817,7 @@ namespace cppsort #ifdef __cpp_lib_ranges template constexpr auto operator()(Iterator first, Iterator last, std::ranges::less, std::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -832,7 +832,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::ranges::less, std::identity) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -847,7 +847,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, std::ranges::less compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -864,7 +864,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, std::ranges::less, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -886,7 +886,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::ranges::less compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::has_comparison_projection_sort< Sorter, Iterable, @@ -903,7 +903,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::ranges::less compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -926,7 +926,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::ranges::less, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -954,7 +954,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, std::ranges::less, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort< Sorter, Iterable, @@ -991,7 +991,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator::value && not detail::has_comparison_projection_sort_iterator< Sorter, @@ -1016,7 +1016,7 @@ namespace cppsort template constexpr auto operator()(Iterator first, Iterator last, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, Iterator, @@ -1043,7 +1043,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -1073,7 +1073,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -1113,7 +1113,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), @@ -1140,7 +1140,7 @@ namespace cppsort template constexpr auto operator()(Iterable&& iterable, Compare compare, Projection projection) const - -> std::enable_if_t< + -> detail::enable_if_t< not detail::has_comparison_projection_sort_iterator< Sorter, decltype(std::begin(iterable)), diff --git a/include/cpp-sort/sorters/block_sorter.h b/include/cpp-sort/sorters/block_sorter.h index 93ebad60..e8302b54 100644 --- a/include/cpp-sort/sorters/block_sorter.h +++ b/include/cpp-sort/sorters/block_sorter.h @@ -19,6 +19,7 @@ #include #include "../detail/attributes.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" #include "../detail/wiki_sort.h" namespace cppsort @@ -35,7 +36,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/sorters/cartesian_tree_sorter.h b/include/cpp-sort/sorters/cartesian_tree_sorter.h index df275ff6..ebc20035 100644 --- a/include/cpp-sort/sorters/cartesian_tree_sorter.h +++ b/include/cpp-sort/sorters/cartesian_tree_sorter.h @@ -19,6 +19,7 @@ #include #include "../detail/cartesian_tree_sort.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -33,7 +34,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -58,7 +59,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/counting_sorter.h b/include/cpp-sort/sorters/counting_sorter.h index ec00aa09..d537e3b0 100644 --- a/include/cpp-sort/sorters/counting_sorter.h +++ b/include/cpp-sort/sorters/counting_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_COUNTING_SORTER_H_ @@ -29,7 +29,7 @@ namespace cppsort { template auto operator()(ForwardIterator first, ForwardIterator last) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_integral>::value > { @@ -46,7 +46,7 @@ namespace cppsort template auto operator()(ForwardIterator first, ForwardIterator last, std::greater<>) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_integral>::value > { @@ -64,7 +64,7 @@ namespace cppsort #ifdef __cpp_lib_ranges template auto operator()(ForwardIterator first, ForwardIterator last, std::ranges::greater) const - -> std::enable_if_t< + -> detail::enable_if_t< detail::is_integral>::value > { diff --git a/include/cpp-sort/sorters/default_sorter.h b/include/cpp-sort/sorters/default_sorter.h index 3f675928..09464af1 100644 --- a/include/cpp-sort/sorters/default_sorter.h +++ b/include/cpp-sort/sorters/default_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_DEFAULT_SORTER_H_ @@ -31,7 +31,11 @@ namespace cppsort struct stable_adapter: merge_sorter { - using type = merge_sorter; + stable_adapter() = default; + + constexpr explicit stable_adapter(const default_sorter&) noexcept: + stable_adapter() + {} }; //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/sorters/drop_merge_sorter.h b/include/cpp-sort/sorters/drop_merge_sorter.h index 991e5131..aee36daf 100644 --- a/include/cpp-sort/sorters/drop_merge_sorter.h +++ b/include/cpp-sort/sorters/drop_merge_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Morwenn + * Copyright (c) 2017-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_DROP_MERGE_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/drop_merge_sort.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename BidirectionalIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/grail_sorter.h b/include/cpp-sort/sorters/grail_sorter.h index 46f82716..4842bced 100644 --- a/include/cpp-sort/sorters/grail_sorter.h +++ b/include/cpp-sort/sorters/grail_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_GRAIL_SORTER_H_ @@ -18,6 +18,7 @@ #include #include #include "../detail/grail_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -33,7 +34,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/sorters/heap_sorter.h b/include/cpp-sort/sorters/heap_sorter.h index 76a94f14..783c8747 100644 --- a/include/cpp-sort/sorters/heap_sorter.h +++ b/include/cpp-sort/sorters/heap_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_HEAP_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/heapsort.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/insertion_sorter.h b/include/cpp-sort/sorters/insertion_sorter.h index a491ad37..c5aa074d 100644 --- a/include/cpp-sort/sorters/insertion_sorter.h +++ b/include/cpp-sort/sorters/insertion_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_INSERTION_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/insertion_sort.h" #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename BidirectionalIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/mel_sorter.h b/include/cpp-sort/sorters/mel_sorter.h index 51746c75..b52628e0 100644 --- a/include/cpp-sort/sorters/mel_sorter.h +++ b/include/cpp-sort/sorters/mel_sorter.h @@ -19,6 +19,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/melsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -33,7 +34,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -58,7 +59,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/merge_insertion_sorter.h b/include/cpp-sort/sorters/merge_insertion_sorter.h index 8d578195..fc7ddb99 100644 --- a/include/cpp-sort/sorters/merge_insertion_sorter.h +++ b/include/cpp-sort/sorters/merge_insertion_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_MERGE_INSERTION_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/merge_insertion_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/sorters/merge_sorter.h b/include/cpp-sort/sorters/merge_sorter.h index f5b7b1cc..37f30ad3 100644 --- a/include/cpp-sort/sorters/merge_sorter.h +++ b/include/cpp-sort/sorters/merge_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_MERGE_SORTER_H_ @@ -19,6 +19,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/merge_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -33,7 +34,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -58,7 +59,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/pdq_sorter.h b/include/cpp-sort/sorters/pdq_sorter.h index bfdb0237..2a404af7 100644 --- a/include/cpp-sort/sorters/pdq_sorter.h +++ b/include/cpp-sort/sorters/pdq_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_PDQ_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/pdqsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/poplar_sorter.h b/include/cpp-sort/sorters/poplar_sorter.h index 956cca2d..bf6d4f6b 100644 --- a/include/cpp-sort/sorters/poplar_sorter.h +++ b/include/cpp-sort/sorters/poplar_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_POPLAR_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/poplar_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/quick_merge_sorter.h b/include/cpp-sort/sorters/quick_merge_sorter.h index 49aed9dd..1314672b 100644 --- a/include/cpp-sort/sorters/quick_merge_sorter.h +++ b/include/cpp-sort/sorters/quick_merge_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Morwenn + * Copyright (c) 2018-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_QUICK_MERGE_SORTER_H_ @@ -19,6 +19,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/quick_merge_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -33,7 +34,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -58,7 +59,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/quick_sorter.h b/include/cpp-sort/sorters/quick_sorter.h index 37b3808e..16cebdc6 100644 --- a/include/cpp-sort/sorters/quick_sorter.h +++ b/include/cpp-sort/sorters/quick_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_QUICK_SORTER_H_ @@ -19,6 +19,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/quicksort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -33,7 +34,7 @@ namespace cppsort typename ForwardIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -58,7 +59,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/selection_sorter.h b/include/cpp-sort/sorters/selection_sorter.h index 7502cb24..7aca171f 100644 --- a/include/cpp-sort/sorters/selection_sorter.h +++ b/include/cpp-sort/sorters/selection_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SELECTION_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/selection_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename ForwardIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/ska_sorter.h b/include/cpp-sort/sorters/ska_sorter.h index 559831c3..fa39e96d 100644 --- a/include/cpp-sort/sorters/ska_sorter.h +++ b/include/cpp-sort/sorters/ska_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 Morwenn + * Copyright (c) 2017-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SKA_SORTER_H_ @@ -32,13 +32,13 @@ namespace cppsort template< typename RandomAccessIterator, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > auto operator()(RandomAccessIterator first, RandomAccessIterator last, Projection projection={}) const - -> std::enable_if_t detail::enable_if_t >> { diff --git a/include/cpp-sort/sorters/slab_sorter.h b/include/cpp-sort/sorters/slab_sorter.h index 833b289b..760b03b7 100644 --- a/include/cpp-sort/sorters/slab_sorter.h +++ b/include/cpp-sort/sorters/slab_sorter.h @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/slabsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/smooth_sorter.h b/include/cpp-sort/sorters/smooth_sorter.h index 3b02c5ae..6afb4157 100644 --- a/include/cpp-sort/sorters/smooth_sorter.h +++ b/include/cpp-sort/sorters/smooth_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SMOOTH_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/smoothsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/sorters/spin_sorter.h b/include/cpp-sort/sorters/spin_sorter.h index 0281835c..71b513e6 100644 --- a/include/cpp-sort/sorters/spin_sorter.h +++ b/include/cpp-sort/sorters/spin_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SPIN_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/spinsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/split_sorter.h b/include/cpp-sort/sorters/split_sorter.h index b0169cd6..6db5bc83 100644 --- a/include/cpp-sort/sorters/split_sorter.h +++ b/include/cpp-sort/sorters/split_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SPLIT_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/split_sort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/spread_sorter/float_spread_sorter.h b/include/cpp-sort/sorters/spread_sorter/float_spread_sorter.h index 8873fb74..8a7b341d 100644 --- a/include/cpp-sort/sorters/spread_sorter/float_spread_sorter.h +++ b/include/cpp-sort/sorters/spread_sorter/float_spread_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SPREAD_SORTER_FLOAT_SPREAD_SORTER_H_ @@ -19,6 +19,7 @@ #include #include "../../detail/iterator_traits.h" #include "../../detail/spreadsort/float_sort.h" +#include "../../detail/type_traits.h" namespace cppsort { @@ -35,7 +36,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, Projection projection={}) const - -> std::enable_if_t< + -> detail::enable_if_t< std::numeric_limits>::is_iec559 && ( sizeof(projected_t) == sizeof(std::uint32_t) || sizeof(projected_t) == sizeof(std::uint64_t) diff --git a/include/cpp-sort/sorters/spread_sorter/integer_spread_sorter.h b/include/cpp-sort/sorters/spread_sorter/integer_spread_sorter.h index 3cd1fbfc..9f5a3513 100644 --- a/include/cpp-sort/sorters/spread_sorter/integer_spread_sorter.h +++ b/include/cpp-sort/sorters/spread_sorter/integer_spread_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SPREAD_SORTER_INTEGER_SPREAD_SORTER_H_ @@ -19,6 +19,7 @@ #include #include "../../detail/iterator_traits.h" #include "../../detail/spreadsort/integer_sort.h" +#include "../../detail/type_traits.h" namespace cppsort { @@ -35,7 +36,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, Projection projection={}) const - -> std::enable_if_t< + -> detail::enable_if_t< std::is_integral>::value && ( sizeof(projected_t) <= sizeof(std::size_t) || sizeof(projected_t) <= sizeof(std::uintmax_t) diff --git a/include/cpp-sort/sorters/spread_sorter/string_spread_sorter.h b/include/cpp-sort/sorters/spread_sorter/string_spread_sorter.h index aac66f71..733df4a9 100644 --- a/include/cpp-sort/sorters/spread_sorter/string_spread_sorter.h +++ b/include/cpp-sort/sorters/spread_sorter/string_spread_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_SPREAD_SORTER_STRING_SPREAD_SORTER_H_ @@ -20,6 +20,7 @@ #include "../../detail/config.h" #include "../../detail/iterator_traits.h" #include "../../detail/spreadsort/string_sort.h" +#include "../../detail/type_traits.h" #if __cplusplus > 201402L && __has_include() # include @@ -43,7 +44,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, Projection projection={}) const - -> std::enable_if_t< + -> detail::enable_if_t< std::is_same, std::string>::value #if __cplusplus > 201402L && __has_include() || std::is_same, std::string_view>::value @@ -69,7 +70,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, Projection projection={}) const - -> std::enable_if_t<( + -> detail::enable_if_t<( std::is_same, std::wstring>::value #if __cplusplus > 201402L && __has_include() || std::is_same, std::wstring_view>::value @@ -99,7 +100,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, std::greater<> compare, Projection projection={}) const - -> std::enable_if_t< + -> detail::enable_if_t< std::is_same, std::string>::value #if __cplusplus > 201402L && __has_include() || std::is_same, std::string_view>::value @@ -126,7 +127,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, std::greater<> compare, Projection projection={}) const - -> std::enable_if_t<( + -> detail::enable_if_t<( std::is_same, std::wstring>::value #if __cplusplus > 201402L && __has_include() || std::is_same, std::wstring_view>::value @@ -155,7 +156,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, std::ranges::greater compare, Projection projection={}) const - -> std::enable_if_t< + -> detail::enable_if_t< std::is_same_v, std::string> || std::is_same_v, std::string_view> > @@ -180,7 +181,7 @@ namespace cppsort > auto operator()(RandomAccessIterator first, RandomAccessIterator last, std::ranges::greater compare, Projection projection={}) const - -> std::enable_if_t<( + -> detail::enable_if_t<( std::is_same_v, std::wstring> || std::is_same_v, std::wstring_view> ) && (sizeof(wchar_t) == 2) diff --git a/include/cpp-sort/sorters/std_sorter.h b/include/cpp-sort/sorters/std_sorter.h index 91c0cbe1..3b5797f0 100644 --- a/include/cpp-sort/sorters/std_sorter.h +++ b/include/cpp-sort/sorters/std_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_STD_SORTER_H_ @@ -19,6 +19,7 @@ #include #include #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort template< typename RandomAccessIterator, typename Compare = std::less<>, - typename = std::enable_if_t> > @@ -74,7 +75,7 @@ namespace cppsort template< typename RandomAccessIterator, typename Compare = std::less<>, - typename = std::enable_if_t> > @@ -111,8 +112,6 @@ namespace cppsort constexpr explicit stable_adapter(std_sorter) noexcept: sorter_facade() {} - - using type = stable_adapter; }; //////////////////////////////////////////////////////////// diff --git a/include/cpp-sort/sorters/tim_sorter.h b/include/cpp-sort/sorters/tim_sorter.h index 7193369d..1afbeddb 100644 --- a/include/cpp-sort/sorters/tim_sorter.h +++ b/include/cpp-sort/sorters/tim_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_TIM_SORTER_H_ @@ -18,6 +18,7 @@ #include #include "../detail/iterator_traits.h" #include "../detail/timsort.h" +#include "../detail/type_traits.h" namespace cppsort { @@ -32,7 +33,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/verge_sorter.h b/include/cpp-sort/sorters/verge_sorter.h index 7869c1ba..94bce6d3 100644 --- a/include/cpp-sort/sorters/verge_sorter.h +++ b/include/cpp-sort/sorters/verge_sorter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_SORTERS_VERGE_SORTER_H_ @@ -20,6 +20,7 @@ #include #include #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" #include "../detail/vergesort.h" namespace cppsort @@ -36,7 +37,7 @@ namespace cppsort typename BidirectionalIterable, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_v > > @@ -61,7 +62,7 @@ namespace cppsort typename BidirectionalIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_projection_iterator_v > > diff --git a/include/cpp-sort/sorters/wiki_sorter.h b/include/cpp-sort/sorters/wiki_sorter.h index 45270d87..b70f4d79 100644 --- a/include/cpp-sort/sorters/wiki_sorter.h +++ b/include/cpp-sort/sorters/wiki_sorter.h @@ -18,6 +18,7 @@ #include #include #include "../detail/iterator_traits.h" +#include "../detail/type_traits.h" #include "../detail/wiki_sort.h" namespace cppsort @@ -34,7 +35,7 @@ namespace cppsort typename RandomAccessIterator, typename Compare = std::less<>, typename Projection = utility::identity, - typename = std::enable_if_t> > diff --git a/include/cpp-sort/stable_sort.h b/include/cpp-sort/stable_sort.h index b3df6508..0265046f 100644 --- a/include/cpp-sort/stable_sort.h +++ b/include/cpp-sort/stable_sort.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_STABLE_SORT_H_ @@ -32,7 +32,7 @@ namespace cppsort template< typename Iterable, typename Compare, - typename = std::enable_if_t> + typename = detail::enable_if_t> > CPPSORT_DEPRECATED("cppsort::stable_sort() is deprecated and will be removed in version 2.0.0") auto stable_sort(Iterable&& iterable, Compare compare) @@ -45,7 +45,7 @@ namespace cppsort typename Iterable, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_comparison_sorter_v && not is_projection_sorter_v > @@ -69,7 +69,7 @@ namespace cppsort template< typename Iterator, typename Compare, - typename = std::enable_if_t> + typename = detail::enable_if_t> > CPPSORT_DEPRECATED("cppsort::stable_sort() is deprecated and will be removed in version 2.0.0") auto stable_sort(Iterator first, Iterator last, Compare compare) @@ -82,7 +82,7 @@ namespace cppsort typename Iterator, typename Compare, typename Projection, - typename = std::enable_if_t< + typename = detail::enable_if_t< not is_comparison_sorter_iterator_v && not is_projection_sorter_iterator_v > @@ -101,7 +101,7 @@ namespace cppsort template< typename Sorter, typename Iterable, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_sorter_v > > @@ -116,7 +116,7 @@ namespace cppsort typename Sorter, typename Iterable, typename Func, - typename = std::enable_if_t, is_projection_sorter >::value> @@ -146,7 +146,7 @@ namespace cppsort template< typename Sorter, typename Iterator, - typename = std::enable_if_t, Iterator >> > @@ -161,7 +161,7 @@ namespace cppsort typename Sorter, typename Iterator, typename Func, - typename = std::enable_if_t< + typename = detail::enable_if_t< is_comparison_sorter_iterator_v, Iterator, Func> || is_projection_sorter_iterator_v, Iterator, Func> > diff --git a/include/cpp-sort/utility/as_function.h b/include/cpp-sort/utility/as_function.h index 7a151d72..6b88bab1 100644 --- a/include/cpp-sort/utility/as_function.h +++ b/include/cpp-sort/utility/as_function.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ @@ -37,7 +37,7 @@ namespace utility template constexpr auto operator()(T&& t) const noexcept(noexcept(std::mem_fn(t))) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< std::is_member_pointer>::value, decltype(std::mem_fn(t)) > @@ -48,7 +48,7 @@ namespace utility template constexpr auto operator()(T && t) const noexcept(std::is_nothrow_constructible::value) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< not std::is_member_pointer>::value, T > diff --git a/include/cpp-sort/utility/functional.h b/include/cpp-sort/utility/functional.h index 1ed89129..013bb54f 100644 --- a/include/cpp-sort/utility/functional.h +++ b/include/cpp-sort/utility/functional.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_UTILITY_FUNCTIONAL_H_ @@ -61,7 +61,7 @@ namespace utility template< typename T, typename U, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< std::is_base_of>::value || std::is_base_of>::value > @@ -120,7 +120,7 @@ namespace utility template< typename Func, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< not std::is_same, as_projection_fn>::value > > @@ -186,7 +186,7 @@ namespace utility template< typename Func, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< not std::is_same, as_comparison_fn>::value > > @@ -240,7 +240,7 @@ namespace utility template constexpr auto as_projection(Function&& func) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< not detail::is_as_projection_fn>::value, detail::as_projection_fn> > @@ -250,7 +250,7 @@ namespace utility template constexpr auto as_projection(Function&& func) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< detail::is_as_projection_fn>::value, decltype(std::forward(func)) > @@ -260,7 +260,7 @@ namespace utility template constexpr auto as_comparison(Function&& func) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< not detail::is_as_comparison_fn>::value, detail::as_comparison_fn> > @@ -270,7 +270,7 @@ namespace utility template constexpr auto as_comparison(Function&& func) - -> std::enable_if_t< + -> cppsort::detail::enable_if_t< detail::is_as_comparison_fn>::value, decltype(std::forward(func)) > diff --git a/include/cpp-sort/utility/iter_move.h b/include/cpp-sort/utility/iter_move.h index 8dcb6ae3..c6c377a1 100644 --- a/include/cpp-sort/utility/iter_move.h +++ b/include/cpp-sort/utility/iter_move.h @@ -40,7 +40,7 @@ namespace utility template< typename Iterator, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< cppsort::detail::is_detected_v > > @@ -54,7 +54,7 @@ namespace utility template< typename Iterator, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< not cppsort::detail::is_detected_v >, typename = void // dummy parameter for ODR diff --git a/include/cpp-sort/utility/size.h b/include/cpp-sort/utility/size.h index 6045e745..9f40460e 100644 --- a/include/cpp-sort/utility/size.h +++ b/include/cpp-sort/utility/size.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_UTILITY_SIZE_H_ @@ -26,7 +26,7 @@ namespace utility template< typename Iterable, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< cppsort::detail::is_detected_v > > @@ -38,7 +38,7 @@ namespace utility template< typename Iterable, - typename = std::enable_if_t< + typename = cppsort::detail::enable_if_t< not cppsort::detail::is_detected_v > > diff --git a/include/cpp-sort/version.h b/include/cpp-sort/version.h index 3e9237cc..f58675ad 100644 --- a/include/cpp-sort/version.h +++ b/include/cpp-sort/version.h @@ -8,7 +8,7 @@ // Semantic versioning macros #define CPPSORT_VERSION_MAJOR 1 -#define CPPSORT_VERSION_MINOR 11 +#define CPPSORT_VERSION_MINOR 12 #define CPPSORT_VERSION_PATCH 0 #endif // CPPSORT_VERSION_H_ diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 88a4954b..da04bff0 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -14,11 +14,6 @@ option(CPPSORT_USE_VALGRIND "Whether to run the tests with Valgrind" ${USE_VALGR option(CPPSORT_ENABLE_COVERAGE "Whether to produce code coverage" ${ENABLE_COVERAGE}) set(CPPSORT_SANITIZE ${SANITIZE} CACHE STRING "Comma-separated list of options to pass to -fsanitize") -# Apparently ENABLE_COVERAGE is needed either way -if (CPPSORT_ENABLE_COVERAGE) - set(ENABLE_COVERAGE ON CACHE BOOL "Enable coverage build." FORCE) -endif() - ######################################## # Find or download Catch2 @@ -31,7 +26,7 @@ else() message(STATUS "Catch2 not found") download_project(PROJ Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2 - GIT_TAG v2.13.6 + GIT_TAG v2.13.7 UPDATE_DISCONNECTED 1 ) add_subdirectory(${Catch2_SOURCE_DIR} ${Catch2_BINARY_DIR}) @@ -39,6 +34,15 @@ else() endif() include(Catch) +######################################## +# Configure coverage + +if (CPPSORT_ENABLE_COVERAGE) + set(ENABLE_COVERAGE ON CACHE BOOL "Enable coverage build." FORCE) + find_package(codecov) + list(APPEND LCOV_REMOVE_PATTERNS "'/usr/*'") +endif() + ######################################## # Configure runtime tests @@ -91,12 +95,7 @@ macro(configure_tests target) endif() if (CPPSORT_ENABLE_COVERAGE) - find_package(codecov) add_coverage(${target}) - - # Set flags specific to coverage builds - target_compile_options(${target} PRIVATE --coverage) - set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " --coverage") endif() endmacro() @@ -104,8 +103,11 @@ endmacro() # Main tests add_executable(main-tests - # General tests + # Tooling main.cpp + testing-tools/random.cpp + + # General tests every_instantiated_sorter.cpp every_sorter.cpp every_sorter_internal_compare.cpp @@ -115,6 +117,7 @@ add_executable(main-tests every_sorter_no_post_iterator.cpp every_sorter_non_const_compare.cpp every_sorter_rvalue_projection.cpp + every_sorter_small_collections.cpp every_sorter_span.cpp every_sorter_throwing_moves.cpp is_stable.cpp @@ -176,6 +179,7 @@ add_executable(main-tests distributions/shuffled_16_values.cpp # Probes tests + probes/block.cpp probes/dis.cpp probes/enc.cpp probes/exc.cpp @@ -184,7 +188,6 @@ add_executable(main-tests probes/max.cpp probes/mono.cpp probes/osc.cpp - probes/par.cpp probes/rem.cpp probes/runs.cpp probes/sus.cpp @@ -232,29 +235,19 @@ if (NOT "${CPPSORT_SANITIZE}" MATCHES "address|memory") # which isn't something we want for the main tests main.cpp testing-tools/new_delete.cpp + testing-tools/random.cpp heap_memory_exhaustion.cpp probes/every_probe_heap_memory_exhaustion.cpp ) configure_tests(heap-memory-exhaustion-tests) endif() -######################################## -# Configure coverage - -if (CPPSORT_ENABLE_COVERAGE) - list(APPEND LCOV_REMOVE_PATTERNS "'/usr/*'") - coverage_evaluate() -endif() - ######################################## # Configure Valgrind if (CPPSORT_USE_VALGRIND) find_program(MEMORYCHECK_COMMAND valgrind REQUIRED) set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --track-origins=yes --error-exitcode=1 --show-reachable=no") - if (APPLE) - set(MEMORYCHECK_SUPPRESSIONS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/valgrind-osx.supp) - endif() endif() ######################################## diff --git a/testsuite/adapters/container_aware_adapter_forward_list.cpp b/testsuite/adapters/container_aware_adapter_forward_list.cpp index 9e5da81e..5a7be4e0 100644 --- a/testsuite/adapters/container_aware_adapter_forward_list.cpp +++ b/testsuite/adapters/container_aware_adapter_forward_list.cpp @@ -23,7 +23,7 @@ TEST_CASE( "container_aware_adapter and std::forward_list", std::vector vec; vec.reserve(187); auto distribution = dist::shuffled{}; - distribution(std::back_inserter(vec), 187, -24.0); + distribution.call(std::back_inserter(vec), 187, -24); SECTION( "insertion_sorter" ) { diff --git a/testsuite/adapters/container_aware_adapter_list.cpp b/testsuite/adapters/container_aware_adapter_list.cpp index 81e91ee3..e088b501 100644 --- a/testsuite/adapters/container_aware_adapter_list.cpp +++ b/testsuite/adapters/container_aware_adapter_list.cpp @@ -23,7 +23,7 @@ TEST_CASE( "container_aware_adapter and std::list", std::vector vec; vec.reserve(187); auto distribution = dist::shuffled{}; - distribution(std::back_inserter(vec), 187, -24.0); + distribution.call(std::back_inserter(vec), 187, -24); SECTION( "insertion_sorter" ) { diff --git a/testsuite/adapters/hybrid_adapter_is_stable.cpp b/testsuite/adapters/hybrid_adapter_is_stable.cpp index 876f9368..be978811 100644 --- a/testsuite/adapters/hybrid_adapter_is_stable.cpp +++ b/testsuite/adapters/hybrid_adapter_is_stable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -31,17 +31,17 @@ TEST_CASE( "hybrid_adapter stability checks", cppsort::pdq_sorter >; - CHECK(( not cppsort::is_stable&)>::value )); - CHECK(( not cppsort::is_stable::iterator, - std::vector::iterator)>::value )); + CHECK( not cppsort::is_stable&)>::value ); + CHECK( not cppsort::is_stable::iterator, + std::vector::iterator)>::value ); - CHECK(( cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::list::iterator)>::value )); + CHECK( cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::list::iterator)>::value ); - CHECK(( not cppsort::is_stable&)>::value )); - CHECK(( not cppsort::is_stable::iterator, - std::forward_list::iterator)>::value )); + CHECK( not cppsort::is_stable&)>::value ); + CHECK( not cppsort::is_stable::iterator, + std::forward_list::iterator)>::value ); } SECTION( "nested hybrid_adapter" ) @@ -57,17 +57,17 @@ TEST_CASE( "hybrid_adapter stability checks", cppsort::pdq_sorter >; - CHECK(( not cppsort::is_stable&)>::value )); - CHECK(( not cppsort::is_stable::iterator, - std::vector::iterator)>::value )); + CHECK( not cppsort::is_stable&)>::value ); + CHECK( not cppsort::is_stable::iterator, + std::vector::iterator)>::value ); - CHECK(( cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::list::iterator)>::value )); + CHECK( cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::list::iterator)>::value ); - CHECK(( not cppsort::is_stable&)>::value )); - CHECK(( not cppsort::is_stable::iterator, - std::forward_list::iterator)>::value )); + CHECK( not cppsort::is_stable&)>::value ); + CHECK( not cppsort::is_stable::iterator, + std::forward_list::iterator)>::value ); } SECTION( "with small_array_adapter" ) @@ -80,27 +80,27 @@ TEST_CASE( "hybrid_adapter stability checks", cppsort::merge_sorter >; - CHECK(( cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::vector::iterator)>::value )); + CHECK( cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::vector::iterator)>::value ); - CHECK(( cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::list::iterator)>::value )); + CHECK( cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::list::iterator)>::value ); - CHECK(( cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::forward_list::iterator)>::value )); + CHECK( cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::forward_list::iterator)>::value ); - CHECK(( not cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::array::iterator)>::value )); + CHECK( not cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::array::iterator)>::value ); - CHECK(( cppsort::is_stable&)>::value )); - CHECK(( cppsort::is_stable::iterator, - std::array::iterator)>::value )); + CHECK( cppsort::is_stable&)>::value ); + CHECK( cppsort::is_stable::iterator, + std::array::iterator)>::value ); - CHECK(( not cppsort::is_stable::value )); - CHECK(( cppsort::is_stable::value )); + CHECK( not cppsort::is_stable::value ); + CHECK( cppsort::is_stable::value ); } } diff --git a/testsuite/adapters/indirect_adapter_every_sorter.cpp b/testsuite/adapters/indirect_adapter_every_sorter.cpp index feb6ab4f..2dc44246 100644 --- a/testsuite/adapters/indirect_adapter_every_sorter.cpp +++ b/testsuite/adapters/indirect_adapter_every_sorter.cpp @@ -40,7 +40,7 @@ TEMPLATE_TEST_CASE( "every random-access sorter with indirect adapter", "[indire { std::vector collection; collection.reserve(412); auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 412, -125.0); + distribution.call(std::back_inserter(collection), 412, -125); cppsort::indirect_adapter sorter; sorter(collection); @@ -61,7 +61,7 @@ TEMPLATE_TEST_CASE( "every bidirectional sorter with indirect_adapter", "[indire { std::list collection; auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 412, -125.0); + distribution.call(std::back_inserter(collection), 412, -125); cppsort::indirect_adapter sorter; sorter(collection); @@ -79,7 +79,7 @@ TEMPLATE_TEST_CASE( "every forward sorter with with indirect_adapter", "[indirec { std::forward_list collection; auto distribution = dist::shuffled{}; - distribution(std::front_inserter(collection), 412, -125.0); + distribution.call(std::front_inserter(collection), 412, -125); cppsort::indirect_adapter sorter; sorter(collection); diff --git a/testsuite/adapters/mixed_adapters.cpp b/testsuite/adapters/mixed_adapters.cpp index c84e097f..8bf4aee5 100644 --- a/testsuite/adapters/mixed_adapters.cpp +++ b/testsuite/adapters/mixed_adapters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -175,51 +174,65 @@ TEST_CASE( "stability of counting_adapter over self_sort_adapter", SECTION( "is_stable" ) { - CHECK( cppsort::is_stable&)>::value ); - CHECK( not cppsort::is_stable&)>::value ); - CHECK( cppsort::is_stable&, std::greater<>)>::value ); - CHECK( not cppsort::is_stable&, std::greater<>)>::value ); - CHECK( not cppsort::is_stable&, std::negate<>)>::value ); - CHECK( not cppsort::is_stable&, std::negate<>)>::value ); - - CHECK( not cppsort::is_stable::iterator, std::list::iterator)>::value ); - CHECK( not cppsort::is_stable::iterator, std::vector::iterator)>::value ); - CHECK( not cppsort::is_stable::iterator, std::list::iterator, std::greater<>)>::value ); - CHECK( not cppsort::is_stable::iterator, std::vector::iterator, std::greater<>)>::value ); - CHECK( not cppsort::is_stable::iterator, std::list::iterator, std::negate<>)>::value ); - CHECK( not cppsort::is_stable::iterator, std::vector::iterator, std::negate<>)>::value ); - - CHECK( cppsort::is_stable&)>::value ); - CHECK( cppsort::is_stable&)>::value ); - CHECK( cppsort::is_stable&, std::greater<>)>::value ); - CHECK( cppsort::is_stable&, std::greater<>)>::value ); - CHECK( cppsort::is_stable&, std::negate<>)>::value ); - CHECK( cppsort::is_stable&, std::negate<>)>::value ); - - CHECK( cppsort::is_stable::iterator, std::list::iterator)>::value ); - CHECK( cppsort::is_stable::iterator, std::vector::iterator)>::value ); - CHECK( cppsort::is_stable::iterator, std::list::iterator, std::greater<>)>::value ); - CHECK( cppsort::is_stable::iterator, std::vector::iterator, std::greater<>)>::value ); - CHECK( cppsort::is_stable::iterator, std::list::iterator, std::negate<>)>::value ); - CHECK( cppsort::is_stable::iterator, std::vector::iterator, std::negate<>)>::value ); + using cppsort::is_stable; + + CHECK( is_stable&)>::value ); + CHECK( not is_stable&)>::value ); + CHECK( is_stable&, std::greater<>)>::value ); + CHECK( not is_stable&, std::greater<>)>::value ); + CHECK( not is_stable&, std::negate<>)>::value ); + CHECK( not is_stable&, std::negate<>)>::value ); + + CHECK( not is_stable::iterator, std::list::iterator)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator)>::value ); + CHECK( not is_stable::iterator, std::list::iterator, std::greater<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, std::greater<>)>::value ); + CHECK( not is_stable::iterator, std::list::iterator, std::negate<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, std::negate<>)>::value ); + + CHECK( is_stable&)>::value ); + CHECK( is_stable&)>::value ); + CHECK( is_stable&, std::greater<>)>::value ); + CHECK( is_stable&, std::greater<>)>::value ); + CHECK( is_stable&, std::negate<>)>::value ); + CHECK( is_stable&, std::negate<>)>::value ); + + CHECK( is_stable::iterator, std::list::iterator)>::value ); + CHECK( is_stable::iterator, std::vector::iterator)>::value ); + CHECK( is_stable::iterator, std::list::iterator, std::greater<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, std::greater<>)>::value ); + CHECK( is_stable::iterator, std::list::iterator, std::negate<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, std::negate<>)>::value ); } } -TEST_CASE( "stable_adapter over verge_adapter", - "[stable_adapter][verge_adapter]" ) +TEST_CASE( "stable_adapter over stable_adapter", "[stable_adapter]" ) { - using wrapper = generic_stable_wrapper; - std::vector collection; - auto distribution = dist::descending_plateau{}; - distribution(std::back_inserter(collection), 400); - helpers::iota(collection.begin(), collection.end(), 0, &wrapper::order); + // Wrap/nest stable_adapter several times and check that + // stable_t always returns the most nested stable sorter - cppsort::stable_adapter< - cppsort::verge_adapter< - cppsort::grail_sorter<> - > - > sorter; + SECTION( "over unstable sorter" ) + { + using sorter = cppsort::stable_adapter; + using nested1 = cppsort::stable_adapter; + using nested2 = cppsort::stable_adapter; + using nested3 = cppsort::stable_adapter; + + CHECK( std::is_same, sorter>::value ); + CHECK( std::is_same, sorter>::value ); + CHECK( std::is_same, sorter>::value ); + } - sorter(collection, &wrapper::value); - CHECK( helpers::is_sorted(collection.begin(), collection.end()) ); + SECTION( "over stable sorter" ) + { + // Wrap/nest stable_adapter several times + using sorter = cppsort::insertion_sorter; + using nested1 = cppsort::stable_adapter; + using nested2 = cppsort::stable_adapter; + using nested3 = cppsort::stable_adapter; + + CHECK( std::is_same, sorter>::value ); + CHECK( std::is_same, sorter>::value ); + CHECK( std::is_same, sorter>::value ); + } } diff --git a/testsuite/adapters/schwartz_adapter_every_sorter.cpp b/testsuite/adapters/schwartz_adapter_every_sorter.cpp index 22d4ae9f..8ec303f2 100644 --- a/testsuite/adapters/schwartz_adapter_every_sorter.cpp +++ b/testsuite/adapters/schwartz_adapter_every_sorter.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -15,6 +14,7 @@ #include #include #include +#include #include // NOTE: this test used to use wrapper, but it was later @@ -48,7 +48,7 @@ TEMPLATE_TEST_CASE( "every random-access sorter with Schwartzian transform adapt { std::vector> collection; auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 412, -125); + distribution.call(std::back_inserter(collection), 412, -125); cppsort::schwartz_adapter sorter; sorter(collection, &wrapper<>::value); @@ -69,7 +69,7 @@ TEMPLATE_TEST_CASE( "every bidirectional sorter with Schwartzian transform adapt { std::list> collection; auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 412, -125); + distribution.call(std::back_inserter(collection), 412, -125); cppsort::schwartz_adapter sorter; sorter(collection, &wrapper<>::value); @@ -87,7 +87,7 @@ TEMPLATE_TEST_CASE( "every forward sorter with Schwartzian transform adapter", " { std::forward_list> collection; auto distribution = dist::shuffled{}; - distribution(std::front_inserter(collection), 412, -125); + distribution.call(std::front_inserter(collection), 412, -125); cppsort::schwartz_adapter sorter; sorter(collection, &wrapper<>::value); @@ -100,7 +100,7 @@ TEST_CASE( "type-specific sorters with Schwartzian transform adapter", "[schwart auto distribution = dist::shuffled{}; std::vector> collection(412); - distribution(std::back_inserter(collection), 412, -125); + distribution.call(std::back_inserter(collection), 412, -125); std::vector> collection2(412); distribution(std::back_inserter(collection2), 412, -125); @@ -109,8 +109,7 @@ TEST_CASE( "type-specific sorters with Schwartzian transform adapter", "[schwart for (int i = -125 ; i < 287 ; ++i) { collection3.emplace_back(std::to_string(i)); } - std::mt19937 engine(Catch::rngSeed()); - std::shuffle(collection3.begin(), collection3.end(), engine); + std::shuffle(collection3.begin(), collection3.end(), hasard::engine()); SECTION( "ska_sorter" ) { @@ -145,7 +144,7 @@ TEST_CASE( "type-specific sorters with Schwartzian transform adapter", "[schwart CHECK( helpers::is_sorted(collection3.begin(), collection3.end(), std::less<>{}, &wrapper::value) ); - std::shuffle(collection3.begin(), collection3.end(), engine); + std::shuffle(collection3.begin(), collection3.end(), hasard::engine()); sorter(collection3, std::greater<>{}, &wrapper::value); CHECK( helpers::is_sorted(collection3.begin(), collection3.end(), std::greater<>{}, &wrapper::value) ); diff --git a/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp b/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp index b5a089d0..00ceeef4 100644 --- a/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp +++ b/testsuite/adapters/schwartz_adapter_every_sorter_reversed.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -14,6 +13,7 @@ #include #include #include +#include #include // NOTE: this test used to use wrapper, but it was later @@ -48,7 +48,7 @@ TEMPLATE_TEST_CASE( "every sorter with Schwartzian transform adapter and reverse { std::vector> collection; auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 412, -125); + distribution.call(std::back_inserter(collection), 412, -125); cppsort::schwartz_adapter sorter; sorter(collection.rbegin(), collection.rend(), &wrapper<>::value); @@ -62,7 +62,7 @@ TEST_CASE( "type-specific sorters with Schwartzian transform adapter and reverse auto distribution = dist::shuffled{}; std::vector> collection; - distribution(std::back_inserter(collection), 412, -125); + distribution.call(std::back_inserter(collection), 412, -125); std::vector> collection2; distribution(std::back_inserter(collection2), 412, -125); @@ -71,8 +71,7 @@ TEST_CASE( "type-specific sorters with Schwartzian transform adapter and reverse for (int i = -125 ; i < 287 ; ++i) { collection3.emplace_back(std::to_string(i)); } - std::mt19937 engine(Catch::rngSeed()); - std::shuffle(collection3.begin(), collection3.end(), engine); + std::shuffle(collection3.begin(), collection3.end(), hasard::engine()); SECTION( "ska_sorter" ) { @@ -107,7 +106,7 @@ TEST_CASE( "type-specific sorters with Schwartzian transform adapter and reverse CHECK( helpers::is_sorted(collection3.begin(), collection3.end(), std::greater<>{}, &wrapper::value) ); - std::shuffle(collection3.begin(), collection3.end(), engine); + std::shuffle(collection3.begin(), collection3.end(), hasard::engine()); sorter(collection3.rbegin(), collection3.rend(), std::greater<>{}, &wrapper::value); CHECK( helpers::is_sorted(collection3.begin(), collection3.end(), diff --git a/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp b/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp index b4c5e126..1b14f498 100644 --- a/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp +++ b/testsuite/adapters/schwartz_adapter_fixed_sorters.cpp @@ -5,12 +5,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include using wrapper = generic_wrapper; @@ -44,8 +44,6 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", > >{}; - std::mt19937 engine(Catch::rngSeed()); - SECTION( "size 0" ) { std::array collection; @@ -73,27 +71,27 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(std::begin(collection), std::end(collection), -10.0, &wrapper::value); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_comparisons_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); odd_even_merge_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); @@ -104,22 +102,22 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(std::begin(collection), std::end(collection), -10.0, &wrapper::value); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_comparisons_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); @@ -130,27 +128,27 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(std::begin(collection), std::end(collection), -10.0, &wrapper::value); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_comparisons_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); odd_even_merge_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); @@ -161,22 +159,22 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(std::begin(collection), std::end(collection), -10.0, &wrapper::value); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_comparisons_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); @@ -187,22 +185,22 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(std::begin(collection), std::end(collection), -10.0, &wrapper::value); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_comparisons_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); - std::shuffle(std::begin(collection), std::end(collection), engine); + std::shuffle(std::begin(collection), std::end(collection), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(std::begin(collection), std::end(collection), std::less<>{}, &wrapper::value) ); @@ -213,17 +211,17 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(collection.begin(), collection.end(), -10.0, &wrapper::value); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); @@ -234,22 +232,22 @@ TEST_CASE( "Schwartzian transform adapter with fixed-size sorters", std::array collection; helpers::iota(collection.begin(), collection.end(), -10.0, &wrapper::value); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); low_moves_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); merge_exchange_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); odd_even_merge_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); - std::shuffle(collection.begin(), collection.end(), engine); + std::shuffle(collection.begin(), collection.end(), hasard::engine()); sorting_network_sort(collection, &wrapper::value); CHECK( helpers::is_sorted(collection.begin(), collection.end(), std::less<>{}, &wrapper::value) ); diff --git a/testsuite/adapters/verge_adapter_every_sorter.cpp b/testsuite/adapters/verge_adapter_every_sorter.cpp index 978f0e74..a5776d7d 100644 --- a/testsuite/adapters/verge_adapter_every_sorter.cpp +++ b/testsuite/adapters/verge_adapter_every_sorter.cpp @@ -6,10 +6,13 @@ #include #include #include +#include #include #include #include +#include #include +#include TEMPLATE_TEST_CASE( "every sorter with verge_adapter", "[verge_adapter]", cppsort::cartesian_tree_sorter, @@ -38,9 +41,43 @@ TEMPLATE_TEST_CASE( "every sorter with verge_adapter", "[verge_adapter]", { std::vector collection; collection.reserve(412); auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 412, -125.0); + distribution.call(std::back_inserter(collection), 412, -125); cppsort::verge_adapter sorter; sorter(collection); - CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); +} + +TEMPLATE_TEST_CASE( "every sorter with stable verge_adapter", "[verge_adapter][stable_adapter]", + cppsort::cartesian_tree_sorter, + cppsort::default_sorter, + cppsort::drop_merge_sorter, + cppsort::grail_sorter<>, + cppsort::heap_sorter, + cppsort::insertion_sorter, + cppsort::mel_sorter, + cppsort::merge_insertion_sorter, + cppsort::merge_sorter, + cppsort::pdq_sorter, + cppsort::poplar_sorter, + cppsort::quick_merge_sorter, + cppsort::quick_sorter, + cppsort::selection_sorter, + cppsort::slab_sorter, + cppsort::smooth_sorter, + cppsort::spin_sorter, + cppsort::split_sorter, + cppsort::std_sorter, + cppsort::tim_sorter, + cppsort::wiki_sorter> ) +{ + using wrapper = generic_stable_wrapper; + std::vector collection; collection.reserve(400); + auto distribution = dist::descending_plateau{}; + distribution(std::back_inserter(collection), 400); + helpers::iota(collection.begin(), collection.end(), 0, &wrapper::order); + + cppsort::stable_adapter> sorter; + sorter(collection, &wrapper::value); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); } diff --git a/testsuite/every_sorter_long_string.cpp b/testsuite/every_sorter_long_string.cpp index 0e234948..9bfd2e78 100644 --- a/testsuite/every_sorter_long_string.cpp +++ b/testsuite/every_sorter_long_string.cpp @@ -4,7 +4,6 @@ */ #include #include -#include #include #include #include @@ -13,6 +12,7 @@ #include #include #include +#include namespace { @@ -23,9 +23,6 @@ namespace auto operator()(OutputIterator out, long long int size, T start=T(0)) const -> void { - // Pseudo-random number generator - thread_local std::mt19937 engine(Catch::rngSeed()); - std::vector vec; vec.reserve(size); @@ -34,7 +31,7 @@ namespace auto s = std::to_string(i); vec.push_back(std::string(100 - s.size(), '0') + std::move(s)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); std::move(std::begin(vec), std::end(vec), out); } }; diff --git a/testsuite/every_sorter_move_only.cpp b/testsuite/every_sorter_move_only.cpp index e7e81434..597d96e2 100644 --- a/testsuite/every_sorter_move_only.cpp +++ b/testsuite/every_sorter_move_only.cpp @@ -42,7 +42,7 @@ TEMPLATE_TEST_CASE( "test every sorter with move-only types", "[sorters]", std::vector> collection; collection.reserve(491); auto distribution = dist::shuffled{}; - distribution(std::back_inserter(collection), 491, -125); + distribution.call(std::back_inserter(collection), 491, -125); TestType sorter; sorter(collection); diff --git a/testsuite/every_sorter_no_post_iterator.cpp b/testsuite/every_sorter_no_post_iterator.cpp index 958d725c..41231f9a 100644 --- a/testsuite/every_sorter_no_post_iterator.cpp +++ b/testsuite/every_sorter_no_post_iterator.cpp @@ -4,7 +4,6 @@ */ #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include +#include TEMPLATE_TEST_CASE( "test most sorters with no_post_iterator", "[sorters]", cppsort::cartesian_tree_sorter, @@ -61,7 +61,7 @@ TEMPLATE_TEST_CASE( "test type-specific sorters with no_post_iterator further", auto distribution = dist::shuffled{}; std::vector collection_float; - distribution(std::back_inserter(collection_float), 310, -56); + distribution.call(std::back_inserter(collection_float), 310, -56); // Iterators with no post-increment and no post-decrement auto first_float = make_no_post_iterator(collection_float.begin()); @@ -71,7 +71,7 @@ TEMPLATE_TEST_CASE( "test type-specific sorters with no_post_iterator further", CHECK( std::is_sorted(collection_float.begin(), collection_float.end()) ); std::vector collection_double; - distribution(std::back_inserter(collection_double), 310, -56); + distribution.call(std::back_inserter(collection_double), 310, -56); // Iterators with no post-increment and no post-decrement auto first_double = make_no_post_iterator(collection_double.begin()); @@ -84,8 +84,7 @@ TEMPLATE_TEST_CASE( "test type-specific sorters with no_post_iterator further", for (long i = 56 ; i < 366 ; ++i) { collection_str.emplace_back(std::to_string(i)); } - std::mt19937 engine(Catch::rngSeed()); - std::shuffle(collection_str.begin(), collection_str.end(), engine); + std::shuffle(collection_str.begin(), collection_str.end(), hasard::engine()); // Iterators with no post-increment and no post-decrement auto first_str = make_no_post_iterator(collection_str.begin()); diff --git a/testsuite/every_sorter_small_collections.cpp b/testsuite/every_sorter_small_collections.cpp new file mode 100644 index 00000000..c9dfd989 --- /dev/null +++ b/testsuite/every_sorter_small_collections.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2016-2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include + +TEMPLATE_TEST_CASE( "test every sorter with small collections", "[sorters]", + cppsort::cartesian_tree_sorter, + cppsort::counting_sorter, + cppsort::drop_merge_sorter, + cppsort::grail_sorter<>, + cppsort::heap_sorter, + cppsort::insertion_sorter, + cppsort::mel_sorter, + cppsort::merge_insertion_sorter, + cppsort::merge_sorter, + cppsort::pdq_sorter, + cppsort::poplar_sorter, + cppsort::quick_merge_sorter, + cppsort::quick_sorter, + cppsort::selection_sorter, + cppsort::ska_sorter, + cppsort::slab_sorter, + cppsort::smooth_sorter, + cppsort::spin_sorter, + cppsort::split_sorter, + cppsort::spread_sorter, + cppsort::std_sorter, + cppsort::tim_sorter, + cppsort::verge_sorter, + cppsort::wiki_sorter<> ) +{ + // Test that all sorters are able to sort empty collections or + // collections with very few elements + TestType sorter; + + SECTION( "empty collection" ) + { + std::vector collection; + sorter(collection); + SUCCEED(); + } + + SECTION( "one element" ) + { + std::vector collection = { 5 }; + sorter(collection); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); + } + + SECTION( "2 elements" ) + { + std::vector collection = { 8, 2 }; + sorter(collection); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); + } + + SECTION( "3 elements" ) + { + std::vector collection = { 5, 6, 3 }; + sorter(collection); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); + } + + SECTION( "5 elements" ) + { + std::vector collection = { 8, 0, 1, 6, 3 }; + sorter(collection); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); + } + + SECTION( "10 element" ) + { + std::vector collection = { 5, 6, 7, 1, 2, 9, 3, 8, 0, 4 }; + sorter(collection); + CHECK( std::is_sorted(collection.begin(), collection.end()) ); + } +} diff --git a/testsuite/heap_memory_exhaustion.cpp b/testsuite/heap_memory_exhaustion.cpp index a2f4e29f..659f4681 100644 --- a/testsuite/heap_memory_exhaustion.cpp +++ b/testsuite/heap_memory_exhaustion.cpp @@ -19,7 +19,7 @@ // These tests shouldn't be part of the main test suite executable // -TEMPLATE_TEST_CASE( "test heap exhaustion for random-access sorters", "[sorters][heap_exhaustion]", +TEMPLATE_TEST_CASE( "heap exhaustion for random-access sorters", "[sorters][heap_exhaustion]", cppsort::grail_sorter<>, cppsort::heap_sorter, cppsort::insertion_sorter, @@ -46,7 +46,7 @@ TEMPLATE_TEST_CASE( "test heap exhaustion for random-access sorters", "[sorters] CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); } -TEMPLATE_TEST_CASE( "test heap exhaustion for bidirectional sorters", "[sorters][heap_exhaustion]", +TEMPLATE_TEST_CASE( "heap exhaustion for bidirectional sorters", "[sorters][heap_exhaustion]", cppsort::insertion_sorter, cppsort::merge_sorter, cppsort::quick_merge_sorter, @@ -65,7 +65,7 @@ TEMPLATE_TEST_CASE( "test heap exhaustion for bidirectional sorters", "[sorters] CHECK( std::is_sorted(std::begin(collection), std::end(collection)) ); } -TEMPLATE_TEST_CASE( "test heap exhaustion for forward sorters", "[sorters][heap_exhaustion]", +TEMPLATE_TEST_CASE( "heap exhaustion for forward sorters", "[sorters][heap_exhaustion]", cppsort::merge_sorter, cppsort::quick_merge_sorter, cppsort::quick_sorter, diff --git a/testsuite/is_stable.cpp b/testsuite/is_stable.cpp index 9fcfaa43..8ac53e45 100644 --- a/testsuite/is_stable.cpp +++ b/testsuite/is_stable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 Morwenn + * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -24,80 +24,80 @@ TEST_CASE( "test is_stable with raw sorters", { using sorter = cppsort::merge_sorter; - CHECK(( is_stable&)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator)>::value )); + CHECK( is_stable&)>::value ); + CHECK( is_stable::iterator, std::vector::iterator)>::value ); - CHECK(( is_stable&, std::less<>)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::less<>)>::value )); + CHECK( is_stable&, std::less<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::less<>)>::value ); - CHECK(( is_stable&, std::greater<>)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::greater<>)>::value )); + CHECK( is_stable&, std::greater<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::greater<>)>::value ); - CHECK(( is_stable&, identity)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - identity)>::value )); + CHECK( is_stable&, identity)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + identity)>::value ); - CHECK(( is_stable&, std::negate<>)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::negate<>)>::value )); + CHECK( is_stable&, std::negate<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::negate<>)>::value ); - CHECK(( is_stable&, std::less<>, identity)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::less<>, identity)>::value )); + CHECK( is_stable&, std::less<>, identity)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::less<>, identity)>::value ); - CHECK(( is_stable&, std::greater<>, identity)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::greater<>, identity)>::value )); + CHECK( is_stable&, std::greater<>, identity)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::greater<>, identity)>::value ); - CHECK(( is_stable&, std::less<>, std::negate<>)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::less<>, std::negate<>)>::value )); + CHECK( is_stable&, std::less<>, std::negate<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::less<>, std::negate<>)>::value ); - CHECK(( is_stable&, std::greater<>, std::negate<>)>::value )); - CHECK(( is_stable::iterator, std::vector::iterator, - std::greater<>, std::negate<>)>::value )); + CHECK( is_stable&, std::greater<>, std::negate<>)>::value ); + CHECK( is_stable::iterator, std::vector::iterator, + std::greater<>, std::negate<>)>::value ); } SECTION( "quick_sorter" ) { using sorter = cppsort::quick_sorter; - CHECK(( not is_stable&)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator)>::value )); + CHECK( not is_stable&)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator)>::value ); - CHECK(( not is_stable&, std::less<>)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::less<>)>::value )); + CHECK( not is_stable&, std::less<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::less<>)>::value ); - CHECK(( not is_stable&, std::greater<>)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::greater<>)>::value )); + CHECK( not is_stable&, std::greater<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::greater<>)>::value ); - CHECK(( not is_stable&, identity)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - identity)>::value )); + CHECK( not is_stable&, identity)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + identity)>::value ); - CHECK(( not is_stable&, std::negate<>)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::negate<>)>::value )); + CHECK( not is_stable&, std::negate<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::negate<>)>::value ); - CHECK(( not is_stable&, std::less<>, identity)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::less<>, identity)>::value )); + CHECK( not is_stable&, std::less<>, identity)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::less<>, identity)>::value ); - CHECK(( not is_stable&, std::greater<>, identity)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::greater<>, identity)>::value )); + CHECK( not is_stable&, std::greater<>, identity)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::greater<>, identity)>::value ); - CHECK(( not is_stable&, std::less<>, std::negate<>)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::less<>, std::negate<>)>::value )); + CHECK( not is_stable&, std::less<>, std::negate<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::less<>, std::negate<>)>::value ); - CHECK(( not is_stable&, std::greater<>, std::negate<>)>::value )); - CHECK(( not is_stable::iterator, std::vector::iterator, - std::greater<>, std::negate<>)>::value )); + CHECK( not is_stable&, std::greater<>, std::negate<>)>::value ); + CHECK( not is_stable::iterator, std::vector::iterator, + std::greater<>, std::negate<>)>::value ); } SECTION( "low_comparisons_sorter" ) diff --git a/testsuite/main.cpp b/testsuite/main.cpp index 934d1131..e5c805ba 100644 --- a/testsuite/main.cpp +++ b/testsuite/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #define CATCH_CONFIG_MAIN diff --git a/testsuite/probes/block.cpp b/testsuite/probes/block.cpp new file mode 100644 index 00000000..db1f0db9 --- /dev/null +++ b/testsuite/probes/block.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE( "presortedness measure: block", "[probe][block]" ) +{ + using cppsort::probe::block; + + SECTION( "simple test" ) + { + std::forward_list li = { 74, 59, 62, 23, 86, 69, 18, 52, 77, 68 }; + CHECK( block(li) == 8 ); + CHECK( block(li.begin(), li.end()) == 8 ); + + std::vector> tricky(li.begin(), li.end()); + CHECK( block(tricky, &internal_compare::compare_to) == 8 ); + } + + SECTION( "upper bound" ) + { + // The upper bound should correspond to the size of + // the input sequence minus one + + std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + auto max_n = block.max_for_size(cppsort::utility::size(li)); + CHECK( max_n == 10 ); + CHECK( block(li) == max_n ); + CHECK( block(li.begin(), li.end()) == max_n ); + } +} diff --git a/testsuite/probes/dis.cpp b/testsuite/probes/dis.cpp index cb5f26a9..26b841c3 100644 --- a/testsuite/probes/dis.cpp +++ b/testsuite/probes/dis.cpp @@ -13,14 +13,38 @@ TEST_CASE( "presortedness measure: dis", "[probe][dis]" ) { using cppsort::probe::dis; - SECTION( "simple test" ) + SECTION( "simple tests" ) { - std::forward_list li = { 47, 53, 46, 41, 59, 81, 74, 97, 100, 45 }; - CHECK( dis(li) == 9 ); - CHECK( dis(li.begin(), li.end()) == 9 ); + { + std::forward_list li = { 47, 53, 46, 41, 59, 81, 74, 97, 100, 45 }; + CHECK( dis(li) == 9 ); + CHECK( dis(li.begin(), li.end()) == 9 ); - std::vector> tricky(li.begin(), li.end()); - CHECK( dis(tricky, &internal_compare::compare_to) == 9 ); + std::vector> tricky(li.begin(), li.end()); + CHECK( dis(tricky, &internal_compare::compare_to) == 9 ); + } + { + const std::forward_list li = { 48, 43, 96, 44, 42, 34, 42, 57, 68, 69 }; + CHECK( dis(li) == 7 ); + CHECK( dis(li.begin(), li.end()) == 7 ); + + std::vector> tricky(li.begin(), li.end()); + CHECK( dis(tricky, &internal_compare::compare_to) == 7 ); + } + } + + SECTION( "roughly sorting test" ) + { + // Example from *Roughly Sorting: Sequential and Parallel Approach* + // by T. Altman and Y. Igarashi + + const std::forward_list li = { + 2, 3, 5, 1, 4, 2, 6, + 8, 7, 9, 8, 11, 6, 13, + 12, 16, 15, 17, 18, + 20, 18, 19, 21, 19 + }; + CHECK( dis(li) == 5 ); } SECTION( "upper bound" ) @@ -28,7 +52,7 @@ TEST_CASE( "presortedness measure: dis", "[probe][dis]" ) // The upper bound should correspond to the size of // the input sequence minus one - std::forward_list li = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + std::forward_list li = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; auto max_n = dis.max_for_size(cppsort::utility::size(li)); CHECK( max_n == 10 ); CHECK( dis(li) == max_n ); diff --git a/testsuite/probes/every_probe_common.cpp b/testsuite/probes/every_probe_common.cpp index 532cea8d..33d55fac 100644 --- a/testsuite/probes/every_probe_common.cpp +++ b/testsuite/probes/every_probe_common.cpp @@ -13,6 +13,7 @@ // TEMPLATE_TEST_CASE( "test every probe with all_equal distribution", "[probe]", + decltype(cppsort::probe::block), decltype(cppsort::probe::dis), decltype(cppsort::probe::enc), decltype(cppsort::probe::exc), @@ -21,7 +22,6 @@ TEMPLATE_TEST_CASE( "test every probe with all_equal distribution", "[probe]", decltype(cppsort::probe::max), decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), - decltype(cppsort::probe::par), decltype(cppsort::probe::rem), decltype(cppsort::probe::runs), decltype(cppsort::probe::sus) ) @@ -36,6 +36,7 @@ TEMPLATE_TEST_CASE( "test every probe with all_equal distribution", "[probe]", } TEMPLATE_TEST_CASE( "test every probe with a sorted collection", "[probe]", + decltype(cppsort::probe::block), decltype(cppsort::probe::dis), decltype(cppsort::probe::enc), decltype(cppsort::probe::exc), @@ -44,7 +45,6 @@ TEMPLATE_TEST_CASE( "test every probe with a sorted collection", "[probe]", decltype(cppsort::probe::max), decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), - decltype(cppsort::probe::par), decltype(cppsort::probe::rem), decltype(cppsort::probe::runs), decltype(cppsort::probe::sus) ) @@ -58,3 +58,37 @@ TEMPLATE_TEST_CASE( "test every probe with a sorted collection", "[probe]", auto presortedness = mop(collection); CHECK( presortedness == 0 ); } + +TEMPLATE_TEST_CASE( "test every probe with a 0 or 1 element", "[probe]", + decltype(cppsort::probe::block), + decltype(cppsort::probe::dis), + decltype(cppsort::probe::enc), + decltype(cppsort::probe::exc), + decltype(cppsort::probe::ham), + decltype(cppsort::probe::inv), + decltype(cppsort::probe::max), + decltype(cppsort::probe::mono), + decltype(cppsort::probe::osc), + decltype(cppsort::probe::rem), + decltype(cppsort::probe::runs), + decltype(cppsort::probe::sus) ) +{ + // Ensure that all measures of presortedness return 0 when + // given a collection with 0 or 1 element + + std::decay_t mop; + + SECTION( "empty collection" ) + { + std::vector collection; + auto presortedness = mop(collection); + CHECK( presortedness == 0 ); + } + + SECTION( "one-element collection" ) + { + std::vector collection = { 42 }; + auto presortedness = mop(collection); + CHECK( presortedness == 0 ); + } +} diff --git a/testsuite/probes/every_probe_heap_memory_exhaustion.cpp b/testsuite/probes/every_probe_heap_memory_exhaustion.cpp index a1569c4a..1e6764de 100644 --- a/testsuite/probes/every_probe_heap_memory_exhaustion.cpp +++ b/testsuite/probes/every_probe_heap_memory_exhaustion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Morwenn + * Copyright (c) 2020-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -23,7 +23,6 @@ TEMPLATE_TEST_CASE( "heap exhaustion for random-access probes", "[probe][heap_ex decltype(cppsort::probe::dis), decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), - decltype(cppsort::probe::par), decltype(cppsort::probe::runs) ) { std::vector collection; collection.reserve(491); diff --git a/testsuite/probes/every_probe_move_compare_projection.cpp b/testsuite/probes/every_probe_move_compare_projection.cpp index a1a80603..1708ee2a 100644 --- a/testsuite/probes/every_probe_move_compare_projection.cpp +++ b/testsuite/probes/every_probe_move_compare_projection.cpp @@ -10,6 +10,7 @@ #include TEMPLATE_TEST_CASE( "every probe with comparison function altered by move", "[probe]", + decltype(cppsort::probe::block), decltype(cppsort::probe::dis), decltype(cppsort::probe::enc), decltype(cppsort::probe::exc), @@ -18,7 +19,6 @@ TEMPLATE_TEST_CASE( "every probe with comparison function altered by move", "[pr decltype(cppsort::probe::max), decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), - decltype(cppsort::probe::par), decltype(cppsort::probe::rem), decltype(cppsort::probe::runs), decltype(cppsort::probe::sus) ) @@ -33,6 +33,7 @@ TEMPLATE_TEST_CASE( "every probe with comparison function altered by move", "[pr } TEMPLATE_TEST_CASE( "every probe with projection function altered by move", "[probe]", + decltype(cppsort::probe::block), decltype(cppsort::probe::dis), decltype(cppsort::probe::enc), decltype(cppsort::probe::exc), @@ -41,7 +42,6 @@ TEMPLATE_TEST_CASE( "every probe with projection function altered by move", "[pr decltype(cppsort::probe::max), decltype(cppsort::probe::mono), decltype(cppsort::probe::osc), - decltype(cppsort::probe::par), decltype(cppsort::probe::rem), decltype(cppsort::probe::runs), decltype(cppsort::probe::sus) ) diff --git a/testsuite/probes/exc.cpp b/testsuite/probes/exc.cpp index 05003d6f..93bdc8f0 100644 --- a/testsuite/probes/exc.cpp +++ b/testsuite/probes/exc.cpp @@ -2,7 +2,6 @@ * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ -#include #include #include #include @@ -46,10 +45,9 @@ TEST_CASE( "presortedness measure: exc", "[probe][exc]" ) { std::vector collection; collection.reserve(100); - auto distribution = dist::ascending_sawtooth{}; + auto distribution = dist::ascending_duplicates{}; distribution(std::back_inserter(collection), 100); - std::sort(collection.begin(), collection.end()); CHECK( exc(collection) == 0 ); } } diff --git a/testsuite/probes/ham.cpp b/testsuite/probes/ham.cpp index bb87e640..4a3ce7ca 100644 --- a/testsuite/probes/ham.cpp +++ b/testsuite/probes/ham.cpp @@ -2,7 +2,6 @@ * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ -#include #include #include #include @@ -42,10 +41,9 @@ TEST_CASE( "presortedness measure: ham", "[probe][ham]" ) { std::vector collection; collection.reserve(100); - auto distribution = dist::ascending_sawtooth{}; + auto distribution = dist::ascending_duplicates{}; distribution(std::back_inserter(collection), 100); - std::sort(collection.begin(), collection.end()); CHECK( ham(collection) == 0 ); } } diff --git a/testsuite/probes/max.cpp b/testsuite/probes/max.cpp index 24cd41b0..f866f3a0 100644 --- a/testsuite/probes/max.cpp +++ b/testsuite/probes/max.cpp @@ -2,7 +2,6 @@ * Copyright (c) 2016-2021 Morwenn * SPDX-License-Identifier: MIT */ -#include #include #include #include @@ -42,10 +41,9 @@ TEST_CASE( "presortedness measure: max", "[probe][max]" ) { std::vector collection; collection.reserve(100); - auto distribution = dist::ascending_sawtooth{}; + auto distribution = dist::ascending_duplicates{}; distribution(std::back_inserter(collection), 100); - std::sort(collection.begin(), collection.end()); CHECK( (max)(collection) == 0 ); } } diff --git a/testsuite/probes/osc.cpp b/testsuite/probes/osc.cpp index 7a173361..bceb0e25 100644 --- a/testsuite/probes/osc.cpp +++ b/testsuite/probes/osc.cpp @@ -8,6 +8,7 @@ #include #include #include +#include TEST_CASE( "presortedness measure: osc", "[probe][osc]" ) { @@ -38,4 +39,14 @@ TEST_CASE( "presortedness measure: osc", "[probe][osc]" ) CHECK( osc(li) == max_n ); CHECK( osc(li.begin(), li.end()) == max_n ); } + + SECTION( "regressions" ) + { + using wrapper = generic_wrapper>; + std::vector vec = { {{6}}, {{3}}, {{9}}, {{8}}, {{4}}, {{7}}, {{1}}, {{11}} }; + auto comp = [](generic_wrapper const& lhs, generic_wrapper const& rhs) { + return lhs.value < rhs.value; + }; + CHECK( osc(vec, comp, &wrapper::value) == 17 ); + } } diff --git a/testsuite/probes/par.cpp b/testsuite/probes/par.cpp deleted file mode 100644 index 6198df2c..00000000 --- a/testsuite/probes/par.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016-2021 Morwenn - * SPDX-License-Identifier: MIT - */ -#include -#include -#include -#include - -TEST_CASE( "presortedness measure: par", "[probe][par]" ) -{ - using cppsort::probe::par; - - SECTION( "simple test" ) - { - const std::vector vec = { 48, 43, 96, 44, 42, 34, 42, 57, 68, 69 }; - CHECK( par(vec) == 7 ); - CHECK( par(vec.begin(), vec.end()) == 7 ); - - std::vector> tricky(vec.begin(), vec.end()); - CHECK( par(tricky, &internal_compare::compare_to) == 7 ); - } - - SECTION( "upper bound" ) - { - // The upper bound should correspond to the size of - // the input sequence minus one - - const std::vector vec = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; - auto max_n = par.max_for_size(vec.end() - vec.begin()); - CHECK( max_n == 10 ); - CHECK( par(vec) == max_n ); - CHECK( par(vec.begin(), vec.end()) == max_n ); - } -} diff --git a/testsuite/probes/relations.cpp b/testsuite/probes/relations.cpp index e5254d72..993e9dbd 100644 --- a/testsuite/probes/relations.cpp +++ b/testsuite/probes/relations.cpp @@ -23,6 +23,8 @@ TEST_CASE( "relations between measures of presortedness", "[probe]" ) // tests check that these relations are respected in // the library + auto block = cppsort::probe::block(sequence); + auto dis = cppsort::probe::dis(sequence); auto enc = cppsort::probe::enc(sequence); auto exc = cppsort::probe::exc(sequence); auto ham = cppsort::probe::ham(sequence); @@ -30,7 +32,6 @@ TEST_CASE( "relations between measures of presortedness", "[probe]" ) auto max = cppsort::probe::max(sequence); auto mono = cppsort::probe::mono(sequence); auto osc = cppsort::probe::osc(sequence); - auto par = cppsort::probe::par(sequence); auto rem = cppsort::probe::rem(sequence); auto runs = cppsort::probe::runs(sequence); auto sus = cppsort::probe::sus(sequence); @@ -46,14 +47,18 @@ TEST_CASE( "relations between measures of presortedness", "[probe]" ) CHECK( exc + 1 <= ham ); CHECK( ham <= 2 * exc ); - CHECK( max <= par ); - CHECK( par <= 2 * max ); + CHECK( max <= dis ); + CHECK( dis <= 2 * max ); // A New Measure of Presortedness // by Vladimir Estivill-Castro and Derick Wood - CHECK( par <= inv ); - CHECK( rem <= size * (1 - 1 / (par + 1)) ); - CHECK( inv <= size * par / 2 ); + CHECK( dis <= inv ); + CHECK( rem <= size * (1 - 1 / (dis + 1)) ); + CHECK( inv <= size * dis / 2 ); + + // Practical Adaptive Sorting + // by Vladimir Estivill-Castro and Derick Wood + CHECK( rem <= 2 * exc ); // Encroaching lists as a measure of presortedness // by Steven S. Skiena @@ -72,7 +77,15 @@ TEST_CASE( "relations between measures of presortedness", "[probe]" ) // by Christos Levcopoulos and Ola Petersson CHECK( osc <= 4 * inv ); CHECK( osc <= 2 * size * runs + size ); - CHECK( osc <= size * par ); + CHECK( osc <= size * dis ); + + // Sublinear Merging and Natural Mergesort + // by Svante Carlsson, Christos Levcopoulos and Ola Petersson + CHECK( block <= 3 * rem ); + + // Computing and ranking measures of presortedness + // by Jingsen Chen + CHECK( enc <= dis + 1 ); // Intuitive result: a descending run can be seen as several // ascending runs diff --git a/testsuite/sorters/ska_sorter.cpp b/testsuite/sorters/ska_sorter.cpp index 8bd02722..c5b18062 100644 --- a/testsuite/sorters/ska_sorter.cpp +++ b/testsuite/sorters/ska_sorter.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -15,6 +14,7 @@ #include #include #include +#include TEST_CASE( "ska_sorter tests", "[ska_sorter]" ) { @@ -57,7 +57,7 @@ TEST_CASE( "ska_sorter tests", "[ska_sorter]" ) SECTION( "sort with float iterable" ) { std::vector vec; - distribution(std::back_inserter(vec), 100'000); + distribution.call(std::back_inserter(vec), 100'000); cppsort::ska_sort(vec); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } @@ -65,7 +65,7 @@ TEST_CASE( "ska_sorter tests", "[ska_sorter]" ) SECTION( "sort with double iterators" ) { std::vector vec; - distribution(std::back_inserter(vec), 100'000); + distribution.call(std::back_inserter(vec), 100'000); cppsort::ska_sort(vec.begin(), vec.end()); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } @@ -77,14 +77,11 @@ TEST_CASE( "ska_sorter tests", "[ska_sorter]" ) vec.push_back(std::to_string(i)); } - // Pseudo-random number engine - std::mt19937_64 engine(Catch::rngSeed()); - - std::shuffle(vec.begin(), vec.end(), engine); + std::shuffle(vec.begin(), vec.end(), hasard::engine()); cppsort::ska_sort(vec); CHECK( std::is_sorted(vec.begin(), vec.end()) ); - std::shuffle(vec.begin(), vec.end(), engine); + std::shuffle(vec.begin(), vec.end(), hasard::engine()); cppsort::ska_sort(vec.begin(), vec.end()); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } @@ -152,14 +149,14 @@ TEST_CASE( "is_ska_sortable", "[ska_sorter]" ) SECTION( "pairs and tuples" ) { // std::pair - CHECK(( is_ska_sortable> )); - CHECK(( is_ska_sortable>> )); - CHECK(( is_ska_sortable>, std::deque>> )); - CHECK_FALSE(( is_ska_sortable>, std::deque>> )); + CHECK( is_ska_sortable> ); + CHECK( is_ska_sortable>> ); + CHECK( is_ska_sortable>, std::deque>> ); + CHECK_FALSE( is_ska_sortable>, std::deque>> ); // std::tuple - CHECK(( is_ska_sortable> )); - CHECK(( is_ska_sortable>> )); - CHECK_FALSE(( is_ska_sortable, std::deque>> )); + CHECK( is_ska_sortable> ); + CHECK( is_ska_sortable>> ); + CHECK_FALSE( is_ska_sortable, std::deque>> ); } } diff --git a/testsuite/sorters/ska_sorter_projection.cpp b/testsuite/sorters/ska_sorter_projection.cpp index 00f14265..371eeb6c 100644 --- a/testsuite/sorters/ska_sorter_projection.cpp +++ b/testsuite/sorters/ska_sorter_projection.cpp @@ -5,27 +5,24 @@ #include #include #include -#include #include #include #include #include #include +#include #include TEST_CASE( "ska_sorter tests with projections", "[ska_sorter][projection]" ) { - // Pseudo-random number engine - std::mt19937_64 engine(Catch::rngSeed()); - SECTION( "sort with int iterable" ) { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, float(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::ska_sort(vec, &std::pair::first); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::second) ); @@ -35,9 +32,9 @@ TEST_CASE( "ska_sorter tests with projections", { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, float(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::ska_sort(vec, &std::pair::first); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::second) ); @@ -47,9 +44,9 @@ TEST_CASE( "ska_sorter tests with projections", { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, float(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::ska_sort(vec, &std::pair::second); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::first) ); @@ -59,9 +56,9 @@ TEST_CASE( "ska_sorter tests with projections", { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, double(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::ska_sort(vec, &std::pair::second); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::first) ); @@ -75,7 +72,7 @@ TEST_CASE( "ska_sorter tests with projections", for (int i = 0 ; i < 100'000 ; ++i) { vec.emplace_back(std::to_string(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::ska_sort(vec, &wrapper::value); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &wrapper::value) ); diff --git a/testsuite/sorters/spread_sorter.cpp b/testsuite/sorters/spread_sorter.cpp index 130ca5a7..8879f374 100644 --- a/testsuite/sorters/spread_sorter.cpp +++ b/testsuite/sorters/spread_sorter.cpp @@ -1,21 +1,18 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #include #include -#include #include #include #include #include #include +#include TEST_CASE( "spread_sorter tests", "[spread_sorter]" ) { - // Pseudo-random number engine - std::mt19937_64 engine(Catch::rngSeed()); - auto distribution = dist::shuffled{}; SECTION( "sort with int iterable" ) @@ -37,7 +34,7 @@ TEST_CASE( "spread_sorter tests", "[spread_sorter]" ) SECTION( "sort with float iterable" ) { std::vector vec; - distribution(std::back_inserter(vec), 100'000); + distribution.call(std::back_inserter(vec), 100'000); cppsort::spread_sort(vec); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } @@ -45,7 +42,7 @@ TEST_CASE( "spread_sorter tests", "[spread_sorter]" ) SECTION( "sort with double iterators" ) { std::vector vec; - distribution(std::back_inserter(vec), 100'000); + distribution.call(std::back_inserter(vec), 100'000); cppsort::spread_sort(vec.begin(), vec.end()); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } @@ -57,11 +54,11 @@ TEST_CASE( "spread_sorter tests", "[spread_sorter]" ) vec.push_back(std::to_string(i)); } - std::shuffle(vec.begin(), vec.end(), engine); + std::shuffle(vec.begin(), vec.end(), hasard::engine()); cppsort::spread_sort(vec); CHECK( std::is_sorted(vec.begin(), vec.end()) ); - std::shuffle(vec.begin(), vec.end(), engine); + std::shuffle(vec.begin(), vec.end(), hasard::engine()); cppsort::spread_sort(vec.begin(), vec.end()); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } @@ -73,11 +70,11 @@ TEST_CASE( "spread_sorter tests", "[spread_sorter]" ) vec.push_back(std::to_string(i)); } - std::shuffle(vec.begin(), vec.end(), engine); + std::shuffle(vec.begin(), vec.end(), hasard::engine()); cppsort::spread_sort(vec, std::greater<>{}); CHECK( std::is_sorted(vec.begin(), vec.end(), std::greater<>{}) ); - std::shuffle(vec.begin(), vec.end(), engine); + std::shuffle(vec.begin(), vec.end(), hasard::engine()); cppsort::spread_sort(vec.begin(), vec.end(), std::greater<>{}); CHECK( std::is_sorted(vec.begin(), vec.end(), std::greater<>{}) ); } diff --git a/testsuite/sorters/spread_sorter_defaults.cpp b/testsuite/sorters/spread_sorter_defaults.cpp index 4675a931..b317b198 100644 --- a/testsuite/sorters/spread_sorter_defaults.cpp +++ b/testsuite/sorters/spread_sorter_defaults.cpp @@ -1,16 +1,16 @@ /* - * Copyright (c) 2015-2020 Morwenn + * Copyright (c) 2015-2021 Morwenn * SPDX-License-Identifier: MIT */ #include #include #include #include -#include #include #include #include #include +#include TEST_CASE( "spread_sorter generate overloads", "[spread_sorter][sorter_facade]" ) @@ -19,19 +19,16 @@ TEST_CASE( "spread_sorter generate overloads", // to make sure that sorter_facade generates the expected // operator() overloads for non-comparison sorters - // Pseudo-random number engine - std::mt19937_64 engine(Catch::rngSeed()); - SECTION( "default operator() with std::less<>" ) { std::vector vec(100'000); std::iota(std::begin(vec), std::end(vec), 0); - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, std::less<>{}); CHECK( std::is_sorted(std::begin(vec), std::end(vec), std::less<>{}) ); - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(std::begin(vec), std::end(vec), std::less<>{}); CHECK( std::is_sorted(std::begin(vec), std::end(vec), std::less<>{}) ); } @@ -41,11 +38,11 @@ TEST_CASE( "spread_sorter generate overloads", std::vector vec(100'000); std::iota(std::begin(vec), std::end(vec), 0); - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, cppsort::utility::identity{}); CHECK( std::is_sorted(std::begin(vec), std::end(vec)) ); - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(std::begin(vec), std::end(vec), cppsort::utility::identity{}); CHECK( std::is_sorted(std::begin(vec), std::end(vec)) ); } @@ -55,11 +52,11 @@ TEST_CASE( "spread_sorter generate overloads", std::vector vec(100'000); std::iota(std::begin(vec), std::end(vec), 0); - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, std::less<>{}, cppsort::utility::identity{}); CHECK( std::is_sorted(std::begin(vec), std::end(vec)) ); - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(std::begin(vec), std::end(vec), std::less<>{}, cppsort::utility::identity{}); CHECK( std::is_sorted(std::begin(vec), std::end(vec)) ); } diff --git a/testsuite/sorters/spread_sorter_projection.cpp b/testsuite/sorters/spread_sorter_projection.cpp index 5b181cdb..cf33f9cf 100644 --- a/testsuite/sorters/spread_sorter_projection.cpp +++ b/testsuite/sorters/spread_sorter_projection.cpp @@ -5,27 +5,24 @@ #include #include #include -#include #include #include #include #include #include +#include #include TEST_CASE( "spread_sorter tests with projections", "[spread_sorter][projection]" ) { - // Pseudo-random number engine - std::mt19937_64 engine(Catch::rngSeed()); - SECTION( "sort with int iterable" ) { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, float(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, &std::pair::first); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::second) ); @@ -35,9 +32,9 @@ TEST_CASE( "spread_sorter tests with projections", { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, float(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, &std::pair::first); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::second) ); @@ -47,9 +44,9 @@ TEST_CASE( "spread_sorter tests with projections", { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, float(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, &std::pair::second); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::first) ); @@ -59,9 +56,9 @@ TEST_CASE( "spread_sorter tests with projections", { std::vector> vec; for (int i = 0 ; i < 100'000 ; ++i) { - vec.emplace_back(i, i); + vec.emplace_back(i, double(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, &std::pair::second); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &std::pair::first) ); @@ -75,7 +72,7 @@ TEST_CASE( "spread_sorter tests with projections", for (int i = 0 ; i < 100'000 ; ++i) { vec.emplace_back(std::to_string(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, &wrapper::value); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::less<>{}, &wrapper::value) ); @@ -89,7 +86,7 @@ TEST_CASE( "spread_sorter tests with projections", for (int i = 0 ; i < 100'000 ; ++i) { vec.emplace_back(std::to_string(i)); } - std::shuffle(std::begin(vec), std::end(vec), engine); + std::shuffle(std::begin(vec), std::end(vec), hasard::engine()); cppsort::spread_sort(vec, std::greater<>{}, &wrapper::value); CHECK( helpers::is_sorted(std::begin(vec), std::end(vec), std::greater<>{}, &wrapper::value) ); diff --git a/testsuite/testing-tools/distributions.h b/testsuite/testing-tools/distributions.h index 61d1d011..b1e4fffb 100644 --- a/testsuite/testing-tools/distributions.h +++ b/testsuite/testing-tools/distributions.h @@ -8,13 +8,8 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include #include +#include "random.h" namespace dist { @@ -31,60 +26,60 @@ namespace dist return Derived{}(out, size); }; } + + // Make it easier to specify explicit template parameters + template + auto call(OutputIterator out, long long int size) const + -> decltype(auto) + { + return static_cast(*this).template operator()(out, size); + } + + template + auto call(OutputIterator out, long long int size, long long int start) const + -> decltype(auto) + { + return static_cast(*this).template operator()(out, size, start); + } }; struct shuffled: distribution { - template - auto operator()(OutputIterator out, long long int size, T start=T(0)) const + template + auto operator()(OutputIterator out, long long int size, long long int start=0ll) const -> void { - // Pseudo-random number generator - thread_local std::mt19937 engine(Catch::rngSeed()); - - std::vector vec; - vec.reserve(size); - - T end = start + size; - for (auto i = start ; i < end ; ++i) { - vec.emplace_back(i); - } - std::shuffle(std::begin(vec), std::end(vec), engine); - std::move(std::begin(vec), std::end(vec), out); + hasard::fill_with_shuffle(out, size, start, hasard::bit_gen()); } }; struct shuffled_16_values: distribution { - template + static constexpr auto mod_16(long long int value) + -> long long int + { + return value % 16; + } + + template auto operator()(OutputIterator out, long long int size) const -> void { - // Pseudo-random number generator - thread_local std::mt19937 engine(Catch::rngSeed()); - - std::vector vec; - vec.reserve(size); - - for (long long int i = 0 ; i < size ; ++i) { - vec.emplace_back(i % 16); - } - std::shuffle(std::begin(vec), std::end(vec), engine); - std::move(std::begin(vec), std::end(vec), out); + hasard::fill_with_shuffle(out, size, 0, hasard::bit_gen(), &mod_16); } }; struct all_equal: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { for (long long int i = 0 ; i < size ; ++i) { - *out++ = 0; + *out++ = static_cast(0); } } }; @@ -92,12 +87,12 @@ namespace dist struct ascending: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { for (long long int i = 0 ; i < size ; ++i) { - *out++ = i; + *out++ = static_cast(i); } } }; @@ -105,12 +100,29 @@ namespace dist struct descending: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { while (size--) { - *out++ = size; + *out++ = static_cast(size); + } + } + }; + + struct ascending_duplicates: + distribution + { + // Ascending (sorted) distribution with series of 10 + // times the same integer value, used to test specific + // algorithms against inputs with duplicate values + + template + auto operator()(OutputIterator out, long long int size) const + -> void + { + for (long long int i = 0 ; i < size ; ++i) { + *out++ = static_cast(i / 10); } } }; @@ -118,15 +130,15 @@ namespace dist struct pipe_organ: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { for (long long int i = 0 ; i < size / 2 ; ++i) { - *out++ = i; + *out++ = static_cast(i); } for (long long int i = size / 2 ; i < size ; ++i) { - *out++ = size - i; + *out++ = static_cast(size - i); } } }; @@ -134,15 +146,15 @@ namespace dist struct push_front: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { if (size > 0) { for (long long int i = 0 ; i < size - 1 ; ++i) { - *out++ = i; + *out++ = static_cast(i); } - *out = 0; + *out = static_cast(0); } } }; @@ -150,17 +162,17 @@ namespace dist struct push_middle: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { if (size > 0) { for (long long int i = 0 ; i < size ; ++i) { if (i != size / 2) { - *out++ = i; + *out++ = static_cast(i); } } - *out = size / 2; + *out = static_cast(size / 2); } } }; @@ -168,13 +180,13 @@ namespace dist struct ascending_sawtooth: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { - long long int limit = size / cppsort::detail::log2(size) * 0.9; + auto limit = static_cast(size / cppsort::detail::log2(size) * 0.9); for (long long int i = 0 ; i < size ; ++i) { - *out++ = i % limit; + *out++ = static_cast(i % limit); } } }; @@ -182,13 +194,13 @@ namespace dist struct descending_sawtooth: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { - long long int limit = size / cppsort::detail::log2(size) * 0.9; + auto limit = static_cast(size / cppsort::detail::log2(size) * 0.9); while (size--) { - *out++ = size % limit; + *out++ = static_cast(size % limit); } } }; @@ -196,12 +208,12 @@ namespace dist struct alternating: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { for (long long int i = 0 ; i < size ; ++i) { - *out++ = (i % 2) ? i : -i; + *out++ = static_cast((i % 2) ? i : -i); } } }; @@ -209,21 +221,21 @@ namespace dist struct descending_plateau: distribution { - template + template auto operator()(OutputIterator out, long long int size) const -> void { long long int i = size; while (i > 2 * size / 3) { - *out++ = i; + *out++ = static_cast(i); --i; } while (i > size / 3) { - *out++ = size / 2; + *out++ = static_cast(size / 2); --i; } while (i > 0) { - *out++ = i; + *out++ = static_cast(i); --i; } } @@ -236,20 +248,20 @@ namespace dist // by M. D. McIlroy, and is supposed to trick several quicksort // implementations with common pivot selection methods go quadratic - template + template auto operator()(OutputIterator out, long long int size) const -> void { long long int j = size / 2; for (long long int i = 1 ; i < j + 1 ; ++i) { if (i % 2 != 0) { - *out++ = i; + *out++ = static_cast(i); } else { - *out++ = j + i - 1; + *out++ = static_cast(j + i - 1); } } for (long long int i = 1 ; i < j + 1 ; ++i) { - *out++ = 2 * i; + *out++ = static_cast(2 * i); } } }; diff --git a/testsuite/testing-tools/memory_exhaustion.h b/testsuite/testing-tools/memory_exhaustion.h index 55165e6f..e69ae865 100644 --- a/testsuite/testing-tools/memory_exhaustion.h +++ b/testsuite/testing-tools/memory_exhaustion.h @@ -1,29 +1,15 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ #ifndef CPPSORT_TESTSUITE_MEMORY_EXHAUSTION_H_ #define CPPSORT_TESTSUITE_MEMORY_EXHAUSTION_H_ -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// - -// This variable is defined in new_delete.h -extern thread_local bool heap_memory_exhaustion_should_fail; - // Class to make memory exhaustion fail in the current scope struct scoped_memory_exhaustion { - scoped_memory_exhaustion() noexcept - { - heap_memory_exhaustion_should_fail = true; - } - - ~scoped_memory_exhaustion() - { - heap_memory_exhaustion_should_fail = false; - } + scoped_memory_exhaustion() noexcept; + ~scoped_memory_exhaustion(); }; #endif // CPPSORT_TESTSUITE_MEMORY_EXHAUSTION_H_ diff --git a/testsuite/testing-tools/new_delete.cpp b/testsuite/testing-tools/new_delete.cpp index 5226e7a8..dcec6048 100644 --- a/testsuite/testing-tools/new_delete.cpp +++ b/testsuite/testing-tools/new_delete.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Morwenn + * Copyright (c) 2019-2021 Morwenn * SPDX-License-Identifier: MIT */ #include @@ -7,14 +7,28 @@ #include #include -// -// Replace the global new and delete for the purpose of testing -// algorithms that have fallbacks when they can't allocate -// extra memory -// +//////////////////////////////////////////////////////////// +// Variable to control whether memory exhaustion should fail -// This variable controls whether memory exhaustion should fail -thread_local bool heap_memory_exhaustion_should_fail = false; +static thread_local bool heap_memory_exhaustion_should_fail = false; + +//////////////////////////////////////////////////////////// +// scoped_memory_exhaustion (scope guard) + +scoped_memory_exhaustion::scoped_memory_exhaustion() noexcept +{ + heap_memory_exhaustion_should_fail = true; +} + +scoped_memory_exhaustion::~scoped_memory_exhaustion() +{ + heap_memory_exhaustion_should_fail = false; +} + +//////////////////////////////////////////////////////////// +// Replace the global new and delete functions for the +// purpose of testing that some algorithms still work when +// heap memory allocations fail auto operator new(std::size_t size) -> void* diff --git a/testsuite/testing-tools/random.cpp b/testsuite/testing-tools/random.cpp new file mode 100644 index 00000000..0f7f2677 --- /dev/null +++ b/testsuite/testing-tools/random.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#include +#include "random.h" + +namespace hasard +{ + auto engine() + -> xoshiro256ss& + { + thread_local xoshiro256ss res(Catch::rngSeed()); + return res; + } + + auto bit_gen() + -> rand_bit_generator& + { + thread_local rand_bit_generator res{ + xoshiro256ss(Catch::rngSeed()) + }; + return res; + } +} diff --git a/testsuite/testing-tools/random.h b/testsuite/testing-tools/random.h new file mode 100644 index 00000000..fc36d03c --- /dev/null +++ b/testsuite/testing-tools/random.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021 Morwenn + * SPDX-License-Identifier: MIT + */ +#ifndef CPPSORT_TESTSUITE_RANDOM_H_ +#define CPPSORT_TESTSUITE_RANDOM_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +namespace hasard // Blame POSIX for picking the good name +{ + //////////////////////////////////////////////////////////// + // xoshiro256** + // https://prng.di.unimi.it/xoshiro256starstar.c + + struct xoshiro256ss + { + public: + + using result_type = std::uint64_t; + + //////////////////////////////////////////////////////////// + // Construction + + constexpr xoshiro256ss(): + xoshiro256ss(0) + {} + + constexpr explicit xoshiro256ss(result_type seed): + s{splitmix64(seed), + splitmix64(seed), + splitmix64(seed), + splitmix64(seed)} + { + // From the original link: "If you have a 64-bit seed, we + // suggest to seed a splitmix64 generator and use its + // output to fill s." + } + + //////////////////////////////////////////////////////////// + // Generation + + constexpr auto operator()() + -> result_type + { + const result_type result = cppsort::detail::rotl(s[1] * 5, 7) * 9; + const result_type t = s[1] << 17; + + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + s[2] ^= t; + s[3] = cppsort::detail::rotl(s[3], 45); + + return result; + } + + //////////////////////////////////////////////////////////// + // Characteristics + + static constexpr std::size_t word_size = std::numeric_limits::digits; + + static constexpr auto min() + -> result_type + { + return 0; + } + + static constexpr auto max() + -> result_type + { + return static_cast(-1); + } + + private: + + // State of the PRNG + result_type s[4]; + + static constexpr auto splitmix64(std::uint64_t& x) + -> std::uint64_t + { + std::uint64_t z = (x += 0x9e3779b97f4a7c15ull); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ull; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebull; + return z ^ (z >> 31); + } + }; + + //////////////////////////////////////////////////////////// + // rand_bit_generator + // + // Takes a standard URBG and allows to fetch random bits one + // by one for algorithms that use such random bits + + template + struct rand_bit_generator + { + public: + using result_type = typename URBG::result_type; + + explicit rand_bit_generator(URBG&& engine): + engine(std::move(engine)), + word(0), + pos(0) + {} + + auto next_bit() + -> result_type + { + if (pos == 0) { + word = engine(); + pos = URBG::word_size; + } + --pos; + return (word & (result_type(1) << pos)) >> pos; + } + + private: + URBG engine; + result_type word; + int pos; + }; + + //////////////////////////////////////////////////////////// + // randint + // + // Returns a random integer in the range [low, high]. + // See *Optimal Discrete Uniform Generation from Coin Flips, + // and Applications* by Jérémie Lumbroso + + template + auto randint(Integer low, Integer high, URBG& generator) + -> Integer + { + using urng_int_type = typename URBG::result_type; + + if (low == high) { + return low; + } + urng_int_type n = high - low; + + urng_int_type v = 1; + urng_int_type c = 0; + while (true) { + v <<= 1; + c = (c << 1) + generator.next_bit(); + if (v >= n) { + if (c < n) { + return static_cast(low + c); + } else { + v -= n; + c -= n; + } + } + } + } + + //////////////////////////////////////////////////////////// + // fill_with_shuffle + // + // Takes an output iterator and fills the corresponding + // range with a shuffle of all the integers in the range + // [start, start + size] + + template< + typename T=long long int, + typename OutputIterator, + typename URBG, + typename Projection = cppsort::utility::identity + > + auto fill_with_shuffle(OutputIterator out, long long int size, long long int start, + URBG& engine, Projection projection={}) + -> void + { + assert(size >= 4); + auto&& proj = cppsort::utility::as_function(projection); + + // Generate a shuffle of all the integers in the range [start, start + size) + // with a linear congruential generator + // https://stackoverflow.com/a/44821946/1364752 + + long long int m = cppsort::detail::hyperceil(size); + auto a = randint(1ll, (m >> 2) - 1, engine) * 4 + 1; + auto c = randint(3ll, m, engine) | 1; + + auto x = 1ll; + for (auto i = 0ll; i < size; ++i) { + do { + x = (x * a + c) % m; + } while (x >= size); + *out = static_cast(proj(x + start)); + ++out; + } + } + + //////////////////////////////////////////////////////////// + // Thread-local "Meyers singletons" + + // Pseudo-random number generator engine + auto engine() + -> xoshiro256ss&; + + // Utility allowing to fetch random bits from a URBG one by one + auto bit_gen() + -> rand_bit_generator&; +} + +#endif // CPPSORT_TESTSUITE_RANDOM_H_ diff --git a/testsuite/utility/branchless_traits.cpp b/testsuite/utility/branchless_traits.cpp index 00c72075..00010ed3 100644 --- a/testsuite/utility/branchless_traits.cpp +++ b/testsuite/utility/branchless_traits.cpp @@ -18,32 +18,32 @@ TEST_CASE( "test that some specific comparisons are branchless", SECTION( "standard library function objects" ) { - CHECK(( is_probably_branchless_comparison_v, int> )); - CHECK(( is_probably_branchless_comparison_v, int> )); - CHECK(( is_probably_branchless_comparison_v, long double> )); + CHECK( is_probably_branchless_comparison_v, int> ); + CHECK( is_probably_branchless_comparison_v, int> ); + CHECK( is_probably_branchless_comparison_v, long double> ); #ifdef __cpp_lib_ranges - CHECK(( is_probably_branchless_comparison_v )); - CHECK(( is_probably_branchless_comparison_v )); + CHECK( is_probably_branchless_comparison_v ); + CHECK( is_probably_branchless_comparison_v ); #endif - CHECK(( is_probably_branchless_comparison_v, int> )); - CHECK(( is_probably_branchless_comparison_v, int> )); - CHECK(( is_probably_branchless_comparison_v, long double> )); + CHECK( is_probably_branchless_comparison_v, int> ); + CHECK( is_probably_branchless_comparison_v, int> ); + CHECK( is_probably_branchless_comparison_v, long double> ); #ifdef __cpp_lib_ranges - CHECK(( is_probably_branchless_comparison_v )); - CHECK(( is_probably_branchless_comparison_v )); + CHECK( is_probably_branchless_comparison_v ); + CHECK( is_probably_branchless_comparison_v ); #endif - CHECK_FALSE(( is_probably_branchless_comparison_v, std::string> )); - CHECK_FALSE(( is_probably_branchless_comparison_v, std::string> )); + CHECK_FALSE( is_probably_branchless_comparison_v, std::string> ); + CHECK_FALSE( is_probably_branchless_comparison_v, std::string> ); #ifdef __cpp_lib_ranges - CHECK_FALSE(( is_probably_branchless_comparison_v )); + CHECK_FALSE( is_probably_branchless_comparison_v ); #endif - CHECK_FALSE(( is_probably_branchless_comparison_v, std::string> )); - CHECK_FALSE(( is_probably_branchless_comparison_v, std::string> )); + CHECK_FALSE( is_probably_branchless_comparison_v, std::string> ); + CHECK_FALSE( is_probably_branchless_comparison_v, std::string> ); #ifdef __cpp_lib_ranges - CHECK_FALSE(( is_probably_branchless_comparison_v )); + CHECK_FALSE( is_probably_branchless_comparison_v ); #endif } @@ -53,26 +53,26 @@ TEST_CASE( "test that some specific comparisons are branchless", using weak_t = decltype(cppsort::weak_less); using total_t = decltype(cppsort::total_less); - CHECK(( is_probably_branchless_comparison_v )); - CHECK(( is_probably_branchless_comparison_v )); - CHECK(( is_probably_branchless_comparison_v )); + CHECK( is_probably_branchless_comparison_v ); + CHECK( is_probably_branchless_comparison_v ); + CHECK( is_probably_branchless_comparison_v ); - CHECK(( is_probably_branchless_comparison_v )); - CHECK_FALSE(( is_probably_branchless_comparison_v )); - CHECK_FALSE(( is_probably_branchless_comparison_v )); + CHECK( is_probably_branchless_comparison_v ); + CHECK_FALSE( is_probably_branchless_comparison_v ); + CHECK_FALSE( is_probably_branchless_comparison_v ); - CHECK_FALSE(( is_probably_branchless_comparison_v )); - CHECK_FALSE(( is_probably_branchless_comparison_v )); - CHECK_FALSE(( is_probably_branchless_comparison_v )); + CHECK_FALSE( is_probably_branchless_comparison_v ); + CHECK_FALSE( is_probably_branchless_comparison_v ); + CHECK_FALSE( is_probably_branchless_comparison_v ); } SECTION( "cv-qualified and reference-qualified types" ) { - CHECK(( is_probably_branchless_comparison_v, const int> )); - CHECK(( is_probably_branchless_comparison_v&, int> )); - CHECK(( is_probably_branchless_comparison_v, int&&> )); + CHECK( is_probably_branchless_comparison_v, const int> ); + CHECK( is_probably_branchless_comparison_v&, int> ); + CHECK( is_probably_branchless_comparison_v, int&&> ); #ifdef __cpp_lib_ranges - CHECK(( is_probably_branchless_comparison_v )); + CHECK( is_probably_branchless_comparison_v ); #endif } } @@ -88,16 +88,16 @@ TEST_CASE( "test that some specific projections are branchless", int bar() { return 0; } }; - CHECK(( is_probably_branchless_projection_v )); + CHECK( is_probably_branchless_projection_v ); #if CPPSORT_STD_IDENTITY_AVAILABLE - CHECK(( is_probably_branchless_projection_v )); + CHECK( is_probably_branchless_projection_v ); #endif - CHECK(( is_probably_branchless_projection_v )); - CHECK_FALSE(( is_probably_branchless_projection_v )); + CHECK( is_probably_branchless_projection_v ); + CHECK_FALSE( is_probably_branchless_projection_v ); #if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) - CHECK(( is_probably_branchless_projection_v )); + CHECK( is_probably_branchless_projection_v ); #endif - CHECK_FALSE(( is_probably_branchless_projection_v )); + CHECK_FALSE( is_probably_branchless_projection_v ); } diff --git a/testsuite/utility/sorting_networks.cpp b/testsuite/utility/sorting_networks.cpp index 3dae5317..200f3c0a 100644 --- a/testsuite/utility/sorting_networks.cpp +++ b/testsuite/utility/sorting_networks.cpp @@ -31,13 +31,13 @@ TEST_CASE( "sorting with index pairs", "[utility][sorting_networks]" ) SECTION( "swap_index_pairs" ) { - swap_index_pairs(vec.begin(), pairs); + cppsort::utility::swap_index_pairs(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } SECTION( "swap_index_pairs_force_unroll" ) { - swap_index_pairs_force_unroll(vec.begin(), pairs); + cppsort::utility::swap_index_pairs_force_unroll(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } } @@ -53,13 +53,13 @@ TEST_CASE( "sorting with index pairs from sorting_network_sorter", SECTION( "swap_index_pairs" ) { - swap_index_pairs(vec.begin(), pairs); + cppsort::utility::swap_index_pairs(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } SECTION( "swap_index_pairs_force_unroll" ) { - swap_index_pairs_force_unroll(vec.begin(), pairs); + cppsort::utility::swap_index_pairs_force_unroll(vec.begin(), pairs); CHECK( std::is_sorted(vec.begin(), vec.end()) ); } } diff --git a/tools/mops-partial-ordering.tex b/tools/mops-partial-ordering.tex index 6aea5af3..9a61bbef 100644 --- a/tools/mops-partial-ordering.tex +++ b/tools/mops-partial-ordering.tex @@ -1,7 +1,7 @@ % Copyright (c) 2021 Morwenn % SPDX-License-Identifier: MIT -\documentclass{minimal} +\documentclass{standalone} \usepackage{bm, pgf, tikz} \usetikzlibrary{arrows, automata, backgrounds, positioning} @@ -30,14 +30,14 @@ \node[state] (loc) [below of=reg] {$Loc$}; \node[state] (hist) [left=2.2cm of loc] {$Hist$}; \node[state] (sms) [right=2.2cm of loc] {$SMS$}; - \node[state] (block) [below of=hist] {$Block$}; + \node[state] (block) [below of=hist] {$\bm{Block}$}; \node[state] (osc) [below of=loc] {$\bm{Osc}$}; \node[state] (enc) [below of=sms] {$\bm{Enc}$}; \node[state] (rem) [below of=block] {$\bm{Rem}$}; \node[state] (inv) [below of=osc] {$\bm{Inv}~$$\equiv$$~DS$}; \node[state] (sus) [below of=enc] {$\bm{SUS}$}; \node[state] (exc) [below of=rem] {$\bm{Exc}~$$\equiv$$~\bm{Ham}$}; - \node[state] (max) [below of=inv] {$\bm{Max}~$$\equiv$$~\bm{Dis}~$$\equiv$$~\bm{Par}$}; + \node[state] (max) [below of=inv] {$\bm{Max}~$$\equiv$$~\bm{Dis}~$}; \node[state] (runs) [below of=sus] {$\bm{Runs}$}; \node[state] (m01) [below of=max] {$m_{01}$}; \node[state] (m0) [below of=m01] {$m_{0}$}; diff --git a/tools/stable_adapter.dot b/tools/stable_adapter.dot new file mode 100644 index 00000000..93c0f1b1 --- /dev/null +++ b/tools/stable_adapter.dot @@ -0,0 +1,22 @@ +// Copyright (c) 2021 Morwenn +// SPDX-License-Identifier: MIT + +digraph G { + + // Nodes + node [fontname="consolas"]; + sorter[label="Sorter"] + stable_adapter[label="stable_adapter"] + make_stable[label="make_stable"] + use_specialization[label="Use specialization"] + node [shape="diamond"] + is_stable_v[label="is_stable"] + specialized[label="Specialized for Sorter?"] + + // Flow + stable_adapter -> specialized + specialized -> use_specialization[label="true",fontname="consolas",fontsize="10"] + specialized -> is_stable_v[label="false",fontname="consolas",fontsize="10"] + is_stable_v -> sorter[label="true",fontname="consolas",fontsize="10"] + is_stable_v -> make_stable[label="false",fontname="consolas",fontsize="10"] +} diff --git a/tools/stable_t.dot b/tools/stable_t.dot new file mode 100644 index 00000000..b99c83df --- /dev/null +++ b/tools/stable_t.dot @@ -0,0 +1,29 @@ +// Copyright (c) 2021 Morwenn +// SPDX-License-Identifier: MIT + +digraph G { + + // Nodes + node [fontname="consolas"]; + sorter[label="Sorter"] + sorter_type[label="Sorter::type"] + stable_t[label="stable_t"] + stable_adapter[label="stable_adapter"] + stable_adapter_type[label="stable_adapter::type"] + node [shape="diamond"] + is_always_stable[label="is_always_stable_v"] + stable_adapter_type_exists[label="stable_adapter::type exists?"] + sorter_type_exists[label="Sorter::type exists?"] + is_specialization[label="is_specialization_of"] + + // Flow + stable_t -> is_always_stable + is_always_stable -> is_specialization[label="true",fontname="consolas",fontsize="10"] + is_specialization -> sorter[label="false",fontname="consolas",fontsize="10"] + is_specialization -> sorter_type_exists[label="true",fontname="consolas",fontsize="10"] + is_always_stable -> stable_adapter_type_exists[label="false",fontname="consolas",fontsize="10"] + stable_adapter_type_exists -> stable_adapter_type[label="true",fontname="consolas",fontsize="10"] + stable_adapter_type_exists -> stable_adapter[label="false",fontname="consolas",fontsize="10"] + sorter_type_exists -> sorter_type[label="true",fontname="consolas",fontsize="10"] + sorter_type_exists -> sorter[label="false",fontname="consolas",fontsize="10"] +} diff --git a/tools/test_failing_sorter.cpp b/tools/test_failing_sorter.cpp index 6b95237b..24b16010 100644 --- a/tools/test_failing_sorter.cpp +++ b/tools/test_failing_sorter.cpp @@ -60,6 +60,7 @@ void test(const char* name) // Measures of presortedness std::cout << '\n' + << "block: " << cppsort::probe::block(copy2) << std::endl << "dis: " << cppsort::probe::dis(copy2) << std::endl << "enc: " << cppsort::probe::enc(copy2) << std::endl << "exc: " << cppsort::probe::exc(copy2) << std::endl @@ -68,7 +69,6 @@ void test(const char* name) << "max: " << cppsort::probe::max(copy2) << std::endl << "mono: " << cppsort::probe::mono(copy2) << std::endl << "osc: " << cppsort::probe::osc(copy2) << std::endl - << "par: " << cppsort::probe::par(copy2) << std::endl << "rem: " << cppsort::probe::rem(copy2) << std::endl << "runs: " << cppsort::probe::runs(copy2) << std::endl << "sus: " << cppsort::probe::sus(copy2) << std::endl