diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f684affcc07..00388d7a4c6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1925,7 +1925,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It1>(_UFirst, _STD move(_Last)); _Seek_wrapped(_First, _ULast); - _Result = _RANGES _Move_backward_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result)); + _Result = _Move_backward_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result)); return {_STD move(_First), _STD move(_Result)}; } @@ -1933,7 +1933,7 @@ namespace ranges { requires indirectly_movable, _It> constexpr move_backward_result, _It> operator()(_Rng&& _Range, _It _Result) const { auto _ULast = _Get_final_iterator_unwrapped(_Range); - _Result = _RANGES _Move_backward_common(_Ubegin(_Range), _ULast, _STD move(_Result)); + _Result = _Move_backward_common(_Ubegin(_Range), _ULast, _STD move(_Result)); return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)}; } // clang-format on @@ -6402,7 +6402,7 @@ namespace ranges { // FUNCTION TEMPLATE make_heap template _CONSTEXPR20 void _Make_heap_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) { - // make nontrivial [_First, _Last) into a heap + // make [_First, _Last) into a heap using _Diff = _Iter_diff_t<_RanIt>; _Diff _Bottom = _Last - _First; for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen @@ -6431,8 +6431,8 @@ namespace ranges { template requires sortable<_It, _Pr, _Pj> constexpr void _Make_heap_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { - // make nontrivial [_First, _Last) into a heap with respect to _Pred and _Proj // clang-format on + // make [_First, _Last) into a heap with respect to _Pred and _Proj using _Diff = iter_difference_t<_It>; const _Diff _Bottom = _Last - _First; for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen @@ -6452,6 +6452,7 @@ namespace ranges { template _Se, class _Pr = ranges::less, class _Pj = identity> requires sortable<_It, _Pr, _Pj> constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); @@ -6460,9 +6461,11 @@ namespace ranges { return _First; } + // clang-format off template requires sortable, _Pr, _Pj> constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on if constexpr (common_range<_Rng>) { _Make_heap_common(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _RANGES end(_Range); @@ -6472,7 +6475,6 @@ namespace ranges { return _Rewrap_iterator(_Range, _STD move(_ULast)); } } - // clang-format on }; inline constexpr _Make_heap_fn make_heap{_Not_quite_object::_Construct_tag{}}; @@ -6665,8 +6667,8 @@ namespace ranges { template requires sortable<_It, _Pr, _Pj> constexpr void _Sort_heap_common(const _It _First, _It _Last, _Pr _Pred, _Pj _Proj) { - // order heap by repeatedly popping // clang-format on + // order heap by repeatedly popping for (; _Last - _First >= 2; --_Last) { _RANGES _Pop_heap_unchecked(_First, _Last, _Pred, _Proj); } @@ -6680,6 +6682,7 @@ namespace ranges { template _Se, class _Pr = ranges::less, class _Pj = identity> requires sortable<_It, _Pr, _Pj> constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); @@ -6688,9 +6691,11 @@ namespace ranges { return _First; } + // clang-format off template requires sortable, _Pr, _Pj> constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on if constexpr (common_range<_Rng>) { _Sort_heap_common(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _RANGES end(_Range); @@ -6700,7 +6705,6 @@ namespace ranges { return _Rewrap_iterator(_Range, _STD move(_ULast)); } } - // clang-format on }; inline constexpr _Sort_heap_fn sort_heap{_Not_quite_object::_Construct_tag{}}; @@ -7628,6 +7632,7 @@ void sort(_ExPo&& _Exec, const _RanIt _First, const _RanIt _Last) noexcept /* te // order [_First, _Last) _STD sort(_STD forward<_ExPo>(_Exec), _First, _Last, less{}); } +#endif // _HAS_CXX17 #ifdef __cpp_lib_concepts namespace ranges { @@ -7635,6 +7640,7 @@ namespace ranges { template requires sortable<_It, _Pr, _Pj> constexpr void _Insertion_sort_common(const _It _First, const _It _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on // insertion sort [_First, _Last) if (_First == _Last) { // empty range is sorted @@ -7643,7 +7649,7 @@ namespace ranges { for (auto _Mid = _First; ++_Mid != _Last;) { // order next element iter_value_t<_It> _Val = _RANGES iter_move(_Mid); - auto _Hole = _Mid; + auto _Hole = _Mid; for (auto _Prev = _Hole;;) { --_Prev; @@ -7660,9 +7666,11 @@ namespace ranges { } } + // clang-format off template requires sortable<_It, _Pr, _Pj> - constexpr void _Med3_unchecked(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + constexpr void _Med3_common(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on // sort median of three elements to middle if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { _RANGES iter_swap(_Mid, _First); @@ -7680,33 +7688,37 @@ namespace ranges { } } + // clang-format off template requires sortable<_It, _Pr, _Pj> - constexpr void _Guess_median_unchecked(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + constexpr void _Guess_median_common(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on // sort median element to middle using _Diff = iter_difference_t<_It>; const _Diff _Count = _Last - _First; if (_Count > 40) { // Tukey's ninther const _Diff _Step = (_Count + 1) >> 3; // +1 can't overflow because range was made inclusive in caller const _Diff _Two_step = _Step << 1; // note: intentionally discards low-order bit - _Med3_unchecked(_First, _First + _Step, _First + _Two_step, _Pred, _Proj); - _Med3_unchecked(_Mid - _Step, _Mid, _Mid + _Step, _Pred, _Proj); - _Med3_unchecked(_Last - _Two_step, _Last - _Step, _Last, _Pred, _Proj); - _Med3_unchecked(_First + _Step, _Mid, _Last - _Step, _Pred, _Proj); + _Med3_common(_First, _First + _Step, _First + _Two_step, _Pred, _Proj); + _Med3_common(_Mid - _Step, _Mid, _Mid + _Step, _Pred, _Proj); + _Med3_common(_Last - _Two_step, _Last - _Step, _Last, _Pred, _Proj); + _Med3_common(_First + _Step, _Mid, _Last - _Step, _Pred, _Proj); } else { - _Med3_unchecked(_First, _Mid, _Last, _Pred, _Proj); + _Med3_common(_First, _Mid, _Last, _Pred, _Proj); } } + // clang-format off template requires sortable<_It, _Pr, _Pj> - _NODISCARD constexpr subrange<_It> _Partition_by_median_guess_unchecked( + _NODISCARD constexpr subrange<_It> _Partition_by_median_guess_common( _It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on // Choose a pivot, partition [_First, _Last) into elements less than pivot, elements equal to pivot, and // elements greater than pivot; return the equal partition as a subrange. _It _Mid = _First + ((_Last - _First) >> 1); // shift for codegen - _RANGES _Guess_median_unchecked(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj); + _Guess_median_common(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj); _It _Pfirst = _Mid; _It _Plast = _RANGES next(_Pfirst); @@ -7716,8 +7728,7 @@ namespace ranges { --_Pfirst; } - while (_Plast < _Last - && !_STD invoke(_Pred, _STD invoke(_Proj, *_Plast), _STD invoke(_Proj, *_Pfirst)) + while (_Plast < _Last && !_STD invoke(_Pred, _STD invoke(_Proj, *_Plast), _STD invoke(_Proj, *_Pfirst)) && !_STD invoke(_Pred, _STD invoke(_Proj, *_Pfirst), _STD invoke(_Proj, *_Plast))) { ++_Plast; } @@ -7775,10 +7786,76 @@ namespace ranges { } } } - // clang-format on + + // VARIABLE ranges::sort + class _Sort_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + const auto _Count = _ULast - _UFirst; + _Sort_common(_STD move(_UFirst), _STD move(_ULast), _Count, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _UFirst = _Ubegin(_Range); + auto _ULast = _Get_final_iterator_unwrapped(_Range); + const auto _Count = _ULast - _UFirst; + _Sort_common(_STD move(_UFirst), _ULast, _Count, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + + private: + template + static constexpr void _Sort_common(_It _First, _It _Last, iter_difference_t<_It> _Ideal, _Pr _Pred, _Pj _Proj) { + // sort [_First, _Last) with respect to _Pred and _Proj + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + for (;;) { + if (_Last - _First <= _ISORT_MAX) { // small + _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + return; + } + + if (_Ideal <= 0) { // heap sort if too many divisions + _Make_heap_common(_First, _Last, _Pred, _Proj); + _Sort_heap_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + return; + } + + // divide and conquer by quicksort + auto [_Mid_first, _Mid_last] = _Partition_by_median_guess_common(_First, _Last, _Pred, _Proj); + + _Ideal = (_Ideal >> 1) + (_Ideal >> 2); // allow 1.5 log2(N) divisions + + if (_Mid_first - _First < _Last - _Mid_last) { // loop on second half + _Sort_common(_First, _STD move(_Mid_first), _Ideal, _Pred, _Proj); + _First = _STD move(_Mid_last); + } else { // loop on first half + _Sort_common(_STD move(_Mid_last), _Last, _Ideal, _Pred, _Proj); + _Last = _STD move(_Mid_first); + } + } + } + }; + + inline constexpr _Sort_fn sort{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts -#endif // _HAS_CXX17 // FUNCTION TEMPLATE stable_sort template @@ -8184,7 +8261,7 @@ namespace ranges { } while (_ISORT_MAX < _Last - _First) { // divide and conquer, ordering partition containing Nth - subrange<_It> _Mid = _RANGES _Partition_by_median_guess_unchecked(_First, _Last, _Pred, _Proj); + subrange<_It> _Mid = _Partition_by_median_guess_common(_First, _Last, _Pred, _Proj); if (_Mid.end() <= _Nth) { _First = _Mid.end(); @@ -8196,7 +8273,7 @@ namespace ranges { } // sort any remainder - _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); } }; diff --git a/tests/std/test.lst b/tests/std/test.lst index fb1f14add67..7b2631d2e27 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -298,6 +298,7 @@ tests\P0896R4_ranges_alg_set_intersection tests\P0896R4_ranges_alg_set_symmetric_difference tests\P0896R4_ranges_alg_set_union tests\P0896R4_ranges_alg_shuffle +tests\P0896R4_ranges_alg_sort tests\P0896R4_ranges_alg_swap_ranges tests\P0896R4_ranges_alg_transform_binary tests\P0896R4_ranges_alg_transform_unary diff --git a/tests/std/tests/P0896R4_ranges_alg_sort/env.lst b/tests/std/tests/P0896R4_ranges_alg_sort/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_sort/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_sort/test.cpp b/tests/std/tests/P0896R4_ranges_alg_sort/test.cpp new file mode 100644 index 00000000000..88c838a4eba --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_sort/test.cpp @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{})), ranges::dangling>); +STATIC_ASSERT(same_as{})), int*>); + +struct instantiator { + static constexpr array input = {P{-1200257975, 0}, P{-1260655766, 1}, P{-1298559576, 2}, P{-1459960308, 3}, + P{-2095681771, 4}, P{-441494788, 5}, P{-47163201, 6}, P{-912489821, 7}, P{1429106719, 8}, P{1668617627, 9}}; + + template + static constexpr void call() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if constexpr (!ranges::contiguous_range) +#endif // TRANSITION, VSO-938163 + { + using ranges::sort, ranges::is_sorted, ranges::iterator_t, ranges::less; + + { // Validate range overload + auto buff = input; + const R range{buff}; + const same_as> auto result = sort(range, less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range, less{}, get_first)); + } + + { // Validate iterator overload + auto buff = input; + const R range{buff}; + const same_as> auto result = sort(range.begin(), range.end(), less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range.begin(), range.end(), less{}, get_first)); + } + + { // Validate empty range + const R range{}; + const same_as> auto result = sort(range, less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range, less{}, get_first)); + } + } + } +}; + +int main() { + STATIC_ASSERT((test_random(), true)); + test_random(); +}