From 6121bfe5b872b0ae3a0a1046578616d60598130a Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 19 Oct 2020 14:46:54 +0200 Subject: [PATCH 01/20] Implement ranges::stable_partition --- stl/inc/algorithm | 242 ++++++++++++++++-- stl/inc/memory | 64 +---- stl/inc/xmemory | 58 +++++ tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 67 +++++ 6 files changed, 357 insertions(+), 79 deletions(-) create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_partition/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 9e0d2458f7d..82f86273fe6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1795,6 +1795,19 @@ namespace ranges { using move_result = in_out_result<_In, _Out>; // VARIABLE ranges::move + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_movable<_It, _Out> + constexpr move_result<_It, _Out> _Move_unchecked(_It _First, const _Se _Last, _Out _Result) { + // clang-format on + + for (; _First != _Last; ++_First, (void) ++_Result) { + *_Result = _RANGES iter_move(_First); + } + + return {_STD move(_First), _STD move(_Result)}; + } + class _Move_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -1803,39 +1816,27 @@ namespace ranges { template _Se, weakly_incrementable _Out> requires indirectly_movable<_It, _Out> constexpr move_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { + // clang-format on _Adl_verify_range(_First, _Last); - auto _UResult = _Move_unchecked( + auto _UResult = _RANGES _Move_unchecked( _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Result)); _Seek_wrapped(_First, _STD move(_UResult.in)); return {_STD move(_First), _STD move(_UResult.out)}; } + // clang-format off template requires indirectly_movable, _Out> constexpr move_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _Move_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result)); + // clang-format on + auto _First = _RANGES begin(_Range); + auto _UResult = + _RANGES _Move_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result)); _Seek_wrapped(_First, _STD move(_UResult.in)); return {_STD move(_First), _STD move(_UResult.out)}; } - // clang-format on - - private: - template - _NODISCARD static constexpr move_result<_It, _Out> _Move_unchecked(_It _First, const _Se _Last, _Out _Result) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); - _STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_It, _Out>); - - for (; _First != _Last; ++_First, (void) ++_Result) { - *_Result = _RANGES iter_move(_First); - } - - return {_STD move(_First), _STD move(_Result)}; - } }; inline constexpr _Move_fn move{_Not_quite_object::_Construct_tag{}}; @@ -6067,6 +6068,209 @@ _BidIt stable_partition(_ExPo&&, _BidIt _First, _BidIt _Last, _Pr _Pred) noexcep } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::stable_partition + template + _It _Buffered_rotate_common(const _It _First, const _It _Mid, const _It _Last, const iter_difference_t<_It> _Count1, + const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity) { + // rotate [_First, _Last) using temp buffer + // precondition: _Count1 == distance(_First, _Mid) + // precondition: _Count2 == distance(_Mid, _Last) + if (_Count1 == 0) { + return _Last; + } + + if (_Count2 == 0) { + return _First; + } + + if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then copy parts + _Uninitialized_backout*> _Backout{ + _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Temp_ptr, _Temp_ptr + _Count1).out}; + const _It _New_mid = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)).out; + _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _New_mid); + return _New_mid; + } + + if (_Count2 <= _Capacity) { // buffer right range, then copy parts + _Uninitialized_backout*> _Backout{ + _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Temp_ptr, _Temp_ptr + _Count2).out}; + _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); + return _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _STD move(_First)).out; + } + + // buffer too small, rotate in place + return _RANGES rotate(_STD move(_First), _STD move(_Mid), _STD move(_Last)).begin(); + } + + class _Stable_partition_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + requires permutable<_It> + subrange<_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)); + + auto _UResult = + _Stable_partition_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template , _Pj>> _Pr> + requires permutable> + borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + // clang-format on + auto _ULast = _Get_final_iterator_unwrapped(_Range); + auto _UResult = + _Stable_partition_common(_Ubegin(_Range), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + + private: + template + _NODISCARD static subrange<_It> _Stable_partition_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + // partition preserving order of equivalents + auto _Saved_last = _Last; + for (;;) { // skip in-place elements at front + if (_First == _Last) { // the input range range is true (already partitioned) + return {_STD move(_First), _STD move(_Saved_last)}; + } + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + ++_First; + } + + do { // skip in-place elements at end + --_Last; + if (_First == _Last) { + return {_STD move(_First), _STD move(_Saved_last)}; + } + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last))); + + const iter_difference_t<_It> _Temp_count = _RANGES distance(_First, _Last); + _Optimistic_temporary_buffer> _Temp_buf{_Temp_count}; + + // _Temp_count + 1 since we work on closed ranges + const iter_difference_t<_It> _Total_count = _Temp_count + static_cast>(1); + auto _Result = _Stable_partition_common_buffered(_STD move(_First), _STD move(_Last), _STD move(_Pred), + _STD move(_Proj), _Total_count, _Temp_buf._Data, _Temp_buf._Capacity); + return {_STD move(_Result.first), _STD move(_Saved_last)}; + } + + template + _NODISCARD static pair<_It, iter_difference_t<_It>> _Stable_partition_common_buffered(_It _First, _It _Last, + _Pr _Pred, _Pj _Proj, const iter_difference_t<_It> _Count, iter_value_t<_It>* const _Temp_ptr, + const ptrdiff_t _Capacity) { + // implement stable_partition of [_First, _Last] (note: closed range) + // precondition: !_STD invoke(_Pred, _STD invoke(_Proj, *_First)) + // precondition: _STD invoke(_Pred, _STD invoke(_Proj, *_Last)) + // precondition: _RANGES distance(_First, _Last) + 1 == _Count + // note: _Count >= 2 and _First != _Last + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + using _Diff = iter_difference_t<_It>; + if (_Count - static_cast<_Diff>(1) <= _Capacity) { // - 1 since we never need to store *_Last + _Uninitialized_backout*> _Backout{_Temp_ptr}; + _It _Next = _First; + _Backout._Emplace_back(_RANGES iter_move(_First)); + while (++_First != _Last) { + // test each element, copying to _Temp_ptr if it's in the false range, or + // assigning backwards if it's in the true range + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + *_Next = _RANGES iter_move(_First); + ++_Next; + } else { + _Backout._Emplace_back(_RANGES iter_move(_First)); + } + } + + // move the last true element, *_Last, to the end of the true range + *_Next = _RANGES iter_move(_Last); + ++_Next; + // copy back the false range + _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _Next); + const _Diff _True_distance = + static_cast<_Diff>(_Count - static_cast<_Diff>(_Backout._Last - _Backout._First)); + return {_STD move(_Next), _True_distance}; + } + + const _Diff _Mid_offset = _Count >> 1; // note: >= 1 because _Count >= 2 + const _It _Mid = _RANGES next(_First, _Mid_offset); + // form [_First, _Left) true range, [_Left, _Mid) false range + _It _Left = _Mid; + _Diff _Left_true_count = _Mid_offset; + for (;;) { // skip over the trailing false range before _Mid + --_Left; + if (_First == _Left) { // the entire left range is false + --_Left_true_count; // to exclude *_First + break; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Left))) { + // excluded the false range before _Mid, invariants reestablished, recurse + const auto _Low = _Stable_partition_common_buffered( + _First, _Left, _Pred, _Proj, _Left_true_count, _Temp_ptr, _Capacity); + _Left = _STD move(_Low.first); + _Left_true_count = _Low.second; + break; + } + + --_Left_true_count; + } + + // form [_Mid, _Right) true range, [_Right, next(_Last)) false range + _It _Right = _Mid; + _Diff _Right_true_count = 0; + for (;;) { // skip over the leading true range after and including _Mid + if (_Right == _Last) { // the entire right range is true + ++_Right; // to include _Last + ++_Right_true_count; + break; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Right))) { + // excluded the true range after and including _Mid, invariants reestablished, recurse + const _Diff _Right_count = _Count - _Mid_offset; + const _Diff _Remaining = _Right_count - _Right_true_count; + const auto _High = _Stable_partition_common_buffered( + _Right, _Last, _Pred, _Proj, _Remaining, _Temp_ptr, _Capacity); + _Right = _STD move(_High.first); + _Right_true_count += _High.second; + break; + } + + ++_Right; + ++_Right_true_count; + } + + // swap the [_Left, _Mid) false range with the [_Mid, _Right) true range + const auto _Partition_point = + _RANGES _Buffered_rotate_common(_STD move(_Left), _STD move(_Mid), _STD move(_Right), + static_cast<_Diff>(_Mid_offset - _Left_true_count), _Right_true_count, _Temp_ptr, _Capacity); + return {_Partition_point, static_cast<_Diff>(_Left_true_count + _Right_true_count)}; + } + }; + + inline constexpr _Stable_partition_fn stable_partition{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE push_heap template _CONSTEXPR20 void _Push_heap_by_index( diff --git a/stl/inc/memory b/stl/inc/memory index d168f49e698..7b0dd9d9514 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -28,35 +28,6 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN #ifdef __cpp_lib_concepts namespace ranges { - // clang-format off - // CONCEPT _No_throw_input_iterator - template - concept _No_throw_input_iterator = input_iterator<_It> - && is_lvalue_reference_v> - && same_as>, iter_value_t<_It>>; - - // CONCEPT _No_throw_sentinel_for - template - concept _No_throw_sentinel_for = sentinel_for<_Se, _It>; - - // CONCEPT _No_throw_forward_iterator - template - concept _No_throw_forward_iterator = _No_throw_input_iterator<_It> - && forward_iterator<_It> - && _No_throw_sentinel_for<_It, _It>; - - // CONCEPT _No_throw_input_range - template - concept _No_throw_input_range = range<_Rng> - && _No_throw_input_iterator> - && _No_throw_sentinel_for, iterator_t<_Rng>>; - - // CONCEPT _No_throw_forward_range - template - concept _No_throw_forward_range = _No_throw_input_range<_Rng> - && _No_throw_forward_iterator>; - // clang-format on - // ALIAS TEMPLATE uninitialized_copy_result template using uninitialized_copy_result = in_out_result<_In, _Out>; @@ -249,10 +220,6 @@ _NoThrowFwdIt uninitialized_move(const _InIt _First, const _InIt _Last, _NoThrow #ifdef __cpp_lib_concepts namespace ranges { - // ALIAS TEMPLATE uninitialized_move_result - template - using uninitialized_move_result = in_out_result<_In, _Out>; - // VARIABLE ranges::uninitialized_move class _Uninitialized_move_fn : private _Not_quite_object { public: @@ -266,9 +233,9 @@ namespace ranges { // clang-format on _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - auto _UResult = - _Uninitialized_move_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), - _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2))); + auto _UResult = _RANGES _Uninitialized_move_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2))); _Seek_wrapped(_First1, _STD move(_UResult.in)); _Seek_wrapped(_First2, _STD move(_UResult.out)); @@ -282,35 +249,12 @@ namespace ranges { _Rng1&& _Range1, _Rng2&& _Range2) const { // clang-format on auto _First1 = _RANGES begin(_Range1); - auto _UResult = _Uninitialized_move_unchecked( + auto _UResult = _RANGES _Uninitialized_move_unchecked( _Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2)); _Seek_wrapped(_First1, _STD move(_UResult.in)); return {_STD move(_First1), _Rewrap_iterator(_Range2, _STD move(_UResult.out))}; } - - private: - template - _NODISCARD static uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( - _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_Out>); - _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); - _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); - - if constexpr (is_same_v<_Se, _It> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { - return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); - } else { - _Uninitialized_backout _Backout{_STD move(_OFirst)}; - - for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { - _Backout._Emplace_back(_RANGES iter_move(_IFirst)); - } - - return {_STD move(_IFirst), _Backout._Release()}; - } - } }; inline constexpr _Uninitialized_move_fn uninitialized_move{_Not_quite_object::_Construct_tag{}}; diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 7ad649cf512..3b4c172df46 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1518,6 +1518,64 @@ _NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _No } #endif // _HAS_IF_CONSTEXPR +#ifdef __cpp_lib_concepts +namespace ranges { + // clang-format off + // CONCEPT _No_throw_input_iterator + template + concept _No_throw_input_iterator = input_iterator<_It> + && is_lvalue_reference_v> + && same_as>, iter_value_t<_It>>; + + // CONCEPT _No_throw_sentinel_for + template + concept _No_throw_sentinel_for = sentinel_for<_Se, _It>; + + // CONCEPT _No_throw_forward_iterator + template + concept _No_throw_forward_iterator = _No_throw_input_iterator<_It> + && forward_iterator<_It> + && _No_throw_sentinel_for<_It, _It>; + + // CONCEPT _No_throw_input_range + template + concept _No_throw_input_range = range<_Rng> + && _No_throw_input_iterator> + && _No_throw_sentinel_for, iterator_t<_Rng>>; + + // CONCEPT _No_throw_forward_range + template + concept _No_throw_forward_range = _No_throw_input_range<_Rng> + && _No_throw_forward_iterator>; + // clang-format on + + // ALIAS TEMPLATE uninitialized_move_result + template + using uninitialized_move_result = in_out_result<_In, _Out>; + + // FUNCTION TEMPLATE _Uninitialized_move_unchecked + // clang-format off + template _Se, _No_throw_forward_iterator _Out, + _No_throw_sentinel_for<_Out> _OSe> + requires constructible_from, iter_rvalue_reference_t<_It>> + _NODISCARD uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( + _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { + // clang-format on + if constexpr (is_same_v<_Se, _It> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { + return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); + } else { + _Uninitialized_backout _Backout{_STD move(_OFirst)}; + + for (; _IFirst != _ILast && _Backout._Last != _OLast; ++_IFirst) { + _Backout._Emplace_back(_RANGES iter_move(_IFirst)); + } + + return {_STD move(_IFirst), _Backout._Release()}; + } + } +} // namespace ranges +#endif // __cpp_lib_concepts + // STRUCT TEMPLATE _Uninitialized_backout_al template class _Uninitialized_backout_al { // struct to undo partially constructed ranges in _Uninitialized_xxx_al algorithms diff --git a/tests/std/test.lst b/tests/std/test.lst index 2ac454e53c6..547c75ec2b6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -315,6 +315,7 @@ 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_stable_partition 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_stable_partition/env.lst b/tests/std/tests/P0896R4_ranges_alg_stable_partition/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_partition/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_stable_partition/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp new file mode 100644 index 00000000000..3686b3d27ad --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; + +constexpr auto is_even = [](int i) { return i % 2 == 0; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, is_even)), ranges::dangling>); +STATIC_ASSERT(same_as{}, is_even)), ranges::subrange>); + +struct instantiator { + template + static void call() { + using ranges::is_partitioned, ranges::is_sorted, ranges::iterator_t, ranges::stable_partition, ranges::subrange; + + { // Validate range overload + P input[] = {P{0, 1}, P{1, 2}, P{0, 3}, P{1, 4}, P{0, 5}, P{1, 6}, P{0, 7}, P{1, 8}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as>> auto result = stable_partition(range, is_even, get_first); + assert(result.begin() == mid); + assert(result.end() == range.end()); + assert(is_partitioned(range, is_even, get_first)); + assert(is_sorted(range)); + + // Validate empty range + const Range empty_range{}; + const same_as>> auto empty_result = + stable_partition(empty_range, is_even, get_first); + assert(empty_result.begin() == empty_range.end()); + assert(empty_result.end() == empty_range.end()); + } + + { // Validate iterator overloads of stable_partition, is_partitioned, partition_point + P input[] = {P{0, 1}, P{1, 2}, P{0, 3}, P{1, 4}, P{0, 5}, P{1, 6}, P{0, 7}, P{1, 8}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as>> auto result = + stable_partition(range.begin(), range.end(), is_even, get_first); + assert(result.begin() == mid); + assert(result.end() == range.end()); + assert(is_partitioned(range, is_even, get_first)); + assert(is_sorted(range)); + + // Validate empty ranges + const Range empty_range{}; + const same_as>> auto empty_result = + stable_partition(empty_range.begin(), empty_range.end(), is_even, get_first); + assert(empty_result.begin() == empty_range.end()); + assert(empty_result.end() == empty_range.end()); + } + } +}; + +int main() { + test_bidi(); +} From d6a06d19d4b1fd0ec05adedd0e9b93a1a08755a1 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 19 Oct 2020 15:39:25 +0200 Subject: [PATCH 02/20] Implement ranges::inplace_merge --- stl/inc/algorithm | 325 ++++++++++++++++++ tests/std/test.lst | 1 + .../P0896R4_ranges_alg_inplace_merge/env.lst | 4 + .../P0896R4_ranges_alg_inplace_merge/test.cpp | 60 ++++ 4 files changed, 390 insertions(+) create mode 100644 tests/std/tests/P0896R4_ranges_alg_inplace_merge/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 82f86273fe6..6df880d8a44 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -7609,6 +7609,331 @@ void inplace_merge(_ExPo&&, _BidIt _First, _BidIt _Mid, _BidIt _Last) noexcept / } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::inplace_merge + template + void _Rotate_one_right(_It _First, _It _Mid, _It _Last) { + // exchanges the range [_First, _Mid) with [_Mid, _Last) + // pre: distance(_Mid, _Last) is 1 + const auto _Temp = _RANGES iter_move(_Mid); + _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); + *_First = _STD move(_Temp); + } + + template + void _Rotate_one_left(_It _First, _It _Mid, _It _Last) { + // exchanges the range [_First, _Mid) with [_Mid, _Last) + // pre: distance(_First, _Mid) is 1 + const auto _Temp = _RANGES iter_move(_Mid); + auto _Result = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)); + *_Result.out = _STD move(_Temp); + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Inplace_merge_buffer_left(_It _First, _It _Mid, _It _Last, iter_value_t<_It>* _Left_first, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // clang-format on + // move the range [_First, _Mid) to _Left_first, and merge it with [_Mid, _Last) to _First + // usual invariants apply + using _Ty = iter_value_t<_It>; + + _Ty* _Left_last = _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Left_first, _Left_first + _Capacity).out; + _Uninitialized_backout<_Ty*> _Backout(_Left_first, _Left_last); + + // we already know, that _Backout._Last - 1 is the highest element so do not compare against it again. + --_Left_last; + + // we already know, that _Mid points to the lowest element and that there is more than 1 element left. + *_First = _RANGES iter_move(_Mid); + ++_First; + ++_Mid; + + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_Left_first))) { + *_First = _RANGES iter_move(_Mid); // the lowest element is now in position + ++_First; + ++_Mid; + if (_Mid == _Last) { + // move the remaining left partition + _RANGES _Move_unchecked(_Left_first, _Backout._Last, _First); + return; + } + } else { + *_First = _RANGES iter_move(_Left_first); + ++_First; + ++_Left_first; + if (_Left_first == _Left_last) { + // move the remaining right partition and highest element, since *_Left_first is highest + const auto _Final = _RANGES _Move_unchecked(_Mid, _Last, _First); + *_Final.out = _RANGES iter_move(_Left_first); + return; + } + } + } + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Inplace_merge_buffer_right(_It _First, _It _Mid, _It _Last, iter_value_t<_It>* _Right_first, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // clang-format on + // move the range [_Mid, _Last) to _Right_first, and merge it with [_First, _Mid) to _Last + // usual invariants apply + using _Ty = iter_value_t<_It>; + + _Ty* _Right_last = + _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Right_first, _Right_first + _Capacity).out; + _Uninitialized_backout<_Ty*> _Backout(_Right_first, _Right_last); + + // we already know, that _Mid points to the next highest element and that there is more than 1 element left. + *--_Last = _RANGES iter_move(--_Mid); + + // we already know, that _Backout._Last - 1 is the highest element so do not compare against it again. + --_Mid; + --_Right_last; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Right_last), _STD invoke(_Proj, *_Mid))) { + *--_Last = _RANGES iter_move(_Mid); // the lowest element is now in position + if (_First == _Mid) { + *--_Last = _RANGES iter_move(_Right_last); // to make [_Right_first, _Right_last) a half-open range + _RANGES _Move_backward_common(_Right_first, _Right_last, _STD move(_Last)); + return; + } + --_Mid; + } else { + *--_Last = _RANGES iter_move(_Right_last); + --_Right_last; + if (_Right_first == _Right_last) { // we can't compare with *_Right_first, but we know it is lowest + *--_Last = _RANGES iter_move(_Mid); // restore half-open range [_First, _Mid) + _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); + *_First = _RANGES iter_move(_Right_first); + return; + } + } + } + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_divide_and_conquer2(_It _First, _It _Mid, _It _Last, + const iter_difference_t<_It> _Count1, const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj, _It _Firstn, _It _Lastn, const iter_difference_t<_It> _Count1n, + const iter_difference_t<_It> _Count2n) { + // clang-format on + // common block of _Buffered_inplace_merge_divide_and_conquer, below + _It _Midn = _RANGES _Buffered_rotate_common(_Firstn, _Mid, _Lastn, + static_cast>(_Count1 - _Count1n), _Count2n, _Temp_ptr, + _Capacity); // rearrange middle + _RANGES _Buffered_inplace_merge_common( + _First, _Firstn, _Midn, _Count1n, _Count2n, _Temp_ptr, _Capacity, _Pred, _Proj); // merge each new part + _RANGES _Buffered_inplace_merge_common(_Midn, _Lastn, _Last, + static_cast>(_Count1 - _Count1n), + static_cast>(_Count2 - _Count2n), _Temp_ptr, _Capacity, _Pred, _Proj); + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_divide_and_conquer(_It _First, _It _Mid, _It _Last, + const iter_difference_t<_It> _Count1, const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, + const ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj) { + // clang-format on + // merge sorted [_First, _Mid) with sorted [_Mid, _Last) + // usual invariants apply + if (_Count1 <= _Count2) { + const iter_difference_t<_It> _Count1n = _Count1 >> 1; // shift for codegen + const _It _Firstn = _RANGES next(_First, _Count1n); + const _It _Lastn = + _RANGES _Lower_bound_unchecked(_Mid, _Count1, _STD invoke(_Proj, *_Firstn), _Pred, _Proj); + const auto _Count2n = _RANGES distance(_Mid, _Lastn); + _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj), _STD move(_Firstn), + _STD move(_Lastn), _Count1n, _Count2n); + } else { + const iter_difference_t<_It> _Count2n = _Count2 >> 1; // shift for codegen + const _It _Lastn = _RANGES next(_Mid, _Count2n); + const _It _Firstn = + _RANGES _Upper_bound_unchecked(_First, _Count2, _STD invoke(_Proj, *_Lastn), _Pred, _Proj); + const auto _Count1n = _RANGES distance(_First, _Firstn); + _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj), _STD move(_Firstn), + _STD move(_Lastn), _Count1n, _Count2n); + } + } + + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_common(_It _First, _It _Mid, _It _Last, iter_difference_t<_It> _Count1, + iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity, _Pr _Pred, + _Pj _Proj) { + // clang-format on + // merge sorted [_First, _Mid) with sorted [_Mid, _Last) + // usual invariants *do not* apply; only sortedness applies + // establish the usual invariants + if (_First == _Mid || _Mid == _Last) { + return; + } + + // Find first element in [_First, _Mid) that is not less than *_Mid + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + --_Count1; + if (++_First == _Mid) { + return; + } + } + + // Fast early return if there is only one element to be moved + if (--_Last == _Mid) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + --_Count2; + + // Find last element in [_Mid, _Last) that is less than *--_Mid + const auto _Highest = _Prev_iter(_Mid); + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))) { + if (_Mid == --_Last) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + --_Count2; + } + ++_Last; + ++_Count2; + + if (_Count1 == 1) { + _RANGES _Rotate_one_left(_STD move(_First), _STD move(_Mid), _STD move(_Last)); + return; + } + + if (_Count1 <= _Count2 && _Count1 <= _Capacity) { + _RANGES _Inplace_merge_buffer_left(_STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, + _Capacity, _STD move(_Pred), _STD move(_Proj)); + } else if (_Count2 <= _Capacity) { + _RANGES _Inplace_merge_buffer_right(_STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, + _Capacity, _STD move(_Pred), _STD move(_Proj)); + } else { + _RANGES _Buffered_inplace_merge_divide_and_conquer(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj)); + } + } + + class _Inplace_merge_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> + _It operator()(_It _First, _It _Mid, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + + _Inplace_merge_common(_STD move(_UFirst), _Get_unwrapped(_STD move(_Mid)), _STD move(_ULast), + _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + borrowed_iterator_t<_Rng> operator()( + _Rng&& _Range, iterator_t<_Rng> _Mid, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _First = _RANGES begin(_Range); + auto _Last = _RANGES end(_Range); + + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + + _Inplace_merge_common(_STD move(_UFirst), _Get_unwrapped(_STD move(_Mid)), _STD move(_ULast), + _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _First; + } + + private: + template + static void _Inplace_merge_common(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + if (_First == _Mid || _Mid == _Last) { + return; + } +#if _ITERATOR_DEBUG_LEVEL == 2 + _STL_VERIFY(_RANGES is_sorted(_First, _Mid, _Pred, _Proj), + "ranges::inplace_merge requires the range [first, middle) to be sorted"); + _STL_VERIFY(_RANGES is_sorted(_Mid, _Last, _Pred, _Proj), + "ranges::inplace_merge requires the range [middle, last)] to be sorted"); +#endif //_ITERATOR_DEBUG_LEVEL == 2 + + // Find first element in [_First, _Mid) that is not less than *_Mid + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + if (++_First == _Mid) { + return; + } + } + + // Fast early return if there is only one element to be moved + if (_Mid == --_Last) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + + // Find last element in [_Mid, _Last) that is less than *--_Mid + const auto _Highest = _Prev_iter(_Mid); + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))) { + if (_Mid == --_Last) { + // rotate only element remaining in right partition to the beginning, without allocating + _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); + return; + } + } + ++_Last; + + const iter_difference_t<_It> _Count1 = _RANGES distance(_First, _Mid); + if (_Count1 == 1) { // rotate only element remaining in left partition to the end, without allocating + _RANGES _Rotate_one_left(_STD move(_First), _STD move(_Mid), _STD move(_Last)); + return; + } + + const iter_difference_t<_It> _Count2 = _RANGES distance(_Mid, _Last); + _Optimistic_temporary_buffer> _Temp_buf{(_STD min)(_Count1, _Count2)}; + if (_Count1 <= _Count2 && _Count1 <= _Temp_buf._Capacity) { + _RANGES _Inplace_merge_buffer_left(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Temp_buf._Data, _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + } else if (_Count2 <= _Temp_buf._Capacity) { + _RANGES _Inplace_merge_buffer_right(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Temp_buf._Data, _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + } else { + _RANGES _Buffered_inplace_merge_divide_and_conquer(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Count1, _Count2, _Temp_buf._Data, _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + } + } + }; + + inline constexpr _Inplace_merge_fn inplace_merge{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE sort template _CONSTEXPR20 _BidIt _Insertion_sort_unchecked(const _BidIt _First, const _BidIt _Last, _Pr _Pred) { diff --git a/tests/std/test.lst b/tests/std/test.lst index 547c75ec2b6..fb9c68e4008 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -278,6 +278,7 @@ tests\P0896R4_ranges_alg_generate tests\P0896R4_ranges_alg_generate_n tests\P0896R4_ranges_alg_heap tests\P0896R4_ranges_alg_includes +tests\P0896R4_ranges_alg_inplace_merge tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_is_sorted tests\P0896R4_ranges_alg_lexicographical_compare diff --git a/tests/std/tests/P0896R4_ranges_alg_inplace_merge/env.lst b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/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_inplace_merge/test.cpp b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp new file mode 100644 index 00000000000..ac8b9c2d650 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using P = pair; +// Validate dangling story +STATIC_ASSERT(same_as{}, nullptr_to)), ranges::dangling>); +STATIC_ASSERT(same_as{}, nullptr_to)), int*>); + +struct instantiator { + static constexpr P expected[] = {P{0, 1}, P{0, 5}, P{4, 2}, P{4, 6}, P{6, 7}, P{7, 3}, P{8, 4}, P{9, 8}, P{10, 9}}; + + template + static void call() { + using ranges::equal, ranges::is_sorted, ranges::iterator_t, ranges::inplace_merge; + + { // Validate range overload + P input[] = {P{0, 1}, P{4, 2}, P{7, 3}, P{8, 4}, P{0, 5}, P{4, 6}, P{6, 7}, P{9, 8}, P{10, 9}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as> auto result = inplace_merge(range, mid, ranges::less{}, get_first); + assert(result == range.end()); + assert(equal(input, expected)); + + // Validate empty range + const Range empty_range{}; + const same_as> auto empty_result = + inplace_merge(empty_range, empty_range.begin(), ranges::less{}, get_first); + assert(empty_result == empty_range.begin()); + } + + { // Validate iterator overloads of inplace_merge, is_partitioned, partition_point + P input[] = {P{0, 1}, P{4, 2}, P{7, 3}, P{8, 4}, P{0, 5}, P{4, 6}, P{6, 7}, P{9, 8}, P{10, 9}}; + Range range{input}; + const auto mid = ranges::next(range.begin(), 4); + const same_as> auto result = + inplace_merge(range.begin(), mid, range.end(), ranges::less{}, get_first); + assert(result == range.end()); + assert(equal(input, expected)); + + // Validate empty ranges + const Range empty_range{}; + const same_as> auto empty_result = + inplace_merge(empty_range.begin(), empty_range.begin(), empty_range.end(), ranges::less{}, get_first); + assert(empty_result == empty_range.end()); + } + } +}; + +int main() { + test_bidi(); +} From d343edd1c9b55c7b91c88f1fbb62b6cb75b08d81 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 20 Oct 2020 11:39:15 +0200 Subject: [PATCH 03/20] Move some helper functions around --- stl/inc/algorithm | 34 +++++++++++----------------------- stl/inc/xutility | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 6df880d8a44..001a0c48c67 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1850,7 +1850,7 @@ namespace ranges { // concept-constrained for strict enforcement as it is used by several algorithms template requires indirectly_movable<_It1, _It2> - _NODISCARD constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) { + constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) { if constexpr (_Ptr_move_cat<_It1, _It2>::_Trivially_copyable) { if (!_STD is_constant_evaluated()) { return _Copy_backward_memmove(_First, _Last, _Result); @@ -7717,6 +7717,14 @@ namespace ranges { } } + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + void _Buffered_inplace_merge_common(_It _First, _It _Mid, _It _Last, iter_difference_t<_It> _Count1, + iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity, _Pr _Pred, + _Pj _Proj); + // clang-format on + // clang-format off template requires sortable<_It, _Pr, _Pj> @@ -7878,9 +7886,9 @@ namespace ranges { return; } #if _ITERATOR_DEBUG_LEVEL == 2 - _STL_VERIFY(_RANGES is_sorted(_First, _Mid, _Pred, _Proj), + _STL_VERIFY(_RANGES _Is_sorted_until_unchecked(_First, _Mid, _Pred, _Proj) == _Mid, "ranges::inplace_merge requires the range [first, middle) to be sorted"); - _STL_VERIFY(_RANGES is_sorted(_Mid, _Last, _Pred, _Proj), + _STL_VERIFY(_RANGES _Is_sorted_until_unchecked(_Mid, _Last, _Pred, _Proj) == _Last, "ranges::inplace_merge requires the range [middle, last)] to be sorted"); #endif //_ITERATOR_DEBUG_LEVEL == 2 @@ -10546,26 +10554,6 @@ _NODISCARD bool is_sorted(_ExPo&& _Exec, _FwdIt _First, _FwdIt _Last) noexcept / #ifdef __cpp_lib_concepts namespace ranges { - // FUNCTION TEMPLATE _Is_sorted_until_unchecked - template - _NODISCARD constexpr _It _Is_sorted_until_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It, _Pj>>); - - if (_First == _Last) { - return _First; - } - - for (auto _Prev = _First; ++_First != _Last; ++_Prev) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { - break; - } - } - - return _First; - } - // VARIABLE ranges::is_sorted class _Is_sorted_fn : private _Not_quite_object { public: diff --git a/stl/inc/xutility b/stl/inc/xutility index fa244555d08..f8748217d9a 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5899,6 +5899,30 @@ _NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, _FwdIt _Last, const _T return _STD lower_bound(_First, _Last, _Val, less<>{}); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // FUNCTION TEMPLATE _Is_sorted_until_unchecked + // clang-format off + template _Se, class _Pr, class _Pj> + requires indirect_strict_weak_order<_Pr, projected<_It, _Pj>> + _NODISCARD constexpr _It _Is_sorted_until_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on + if (_First == _Last) { + return _First; + } + + for (auto _Prev = _First; ++_First != _Last; ++_Prev) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { + break; + } + } + + return _First; + } +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE _Swap_ranges_unchecked template _CONSTEXPR20 _FwdIt2 _Swap_ranges_unchecked(_FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { From a5201903cf2d31f86a8f6bc2d941d09b3d4f73a8 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 21 Oct 2020 15:44:11 +0200 Subject: [PATCH 04/20] Implement ranges::stable_sort --- stl/inc/algorithm | 322 +++++++++++++++++- tests/std/test.lst | 1 + .../P0896R4_ranges_alg_stable_sort/env.lst | 4 + .../P0896R4_ranges_alg_stable_sort/test.cpp | 55 +++ 4 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_sort/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 001a0c48c67..29bcb139c12 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -8129,12 +8129,12 @@ namespace ranges { // clang-format off template requires sortable<_It, _Pr, _Pj> - constexpr void _Insertion_sort_common(const _It _First, const _It _Last, _Pr _Pred, _Pj _Proj) { + constexpr _It _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 - return; + return _Last; } for (auto _Mid = _First; ++_Mid != _Last;) { // order next element @@ -8154,6 +8154,7 @@ namespace ranges { *_Hole = _STD move(_Val); // insert element in hole } + return _Last; } // clang-format off @@ -8541,6 +8542,323 @@ void stable_sort(_ExPo&& _Exec, _BidIt _First, _BidIt _Last) noexcept /* termina } #endif // _HAS_CXX17 + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::stable_sort + class _Stable_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> + _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; + _Stable_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> + 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; + _Stable_sort_common(_STD move(_UFirst), _ULast, _Count, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_iterator(_Range, _STD move(_ULast)); + } + + private: + template + static constexpr iter_difference_t<_It> _Isort_max = static_cast>(_ISORT_MAX); + + template + static void _Stable_sort_common( + _It _First, _It _Last, const iter_difference_t<_It> _Count, _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>); + + if (_Count <= _Isort_max<_It>) { + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + return; + } + + _Optimistic_temporary_buffer<_Iter_value_t<_It>> _Temp_buf{_Count - _Count / 2}; + _Stable_sort_common_buffered(_STD move(_First), _STD move(_Last), _Count, _Temp_buf._Data, + _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + } + + template + static void _Stable_sort_common_buffered(_It _First, _It _Last, const iter_difference_t<_It> _Count, + iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity, _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>); + + if (_Count <= _Isort_max<_It>) { + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + } else { // sort halves and merge + const iter_difference_t<_It> _Half_count = _Count >> 1; + const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count; + const _It _Mid = _First + _Half_count_ceil; + if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer + _Buffered_merge_sort_common(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Pred, _Proj); + _Buffered_merge_sort_common(_Mid, _Last, _Half_count, _Temp_ptr, _Pred, _Proj); + } else { // temp buffer not big enough, divide and conquer + _Stable_sort_common_buffered(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Capacity, _Pred, _Proj); + _Stable_sort_common_buffered(_Mid, _Last, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj); + } + // merge halves + _RANGES _Buffered_inplace_merge_common(_STD move(_First), _STD move(_Mid), _STD move(_Last), + _Half_count_ceil, _Half_count, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj)); + } + } + + template + static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const iter_difference_t<_It> _Count, + iter_value_t<_It>* const _Temp_ptr, _Pr _Pred, _Pj _Proj) { + // sort using temp buffer for merges + // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred, _Proj); + // merge adjacent pairs of chunks to and from temp buffer + if (_Count <= _Isort_max<_It>) { + return; + } + + // do the first merge, constructing elements in the temporary buffer + _Uninitialized_chunked_merge_common(_First, _Last, _Temp_ptr, _Count, _Pred, _Proj); + _Uninitialized_backout*> _Backout{_Temp_ptr, _Temp_ptr + _Count}; + iter_difference_t<_It> _Chunk_size = _Isort_max<_It>; + for (;;) { + // unconditionally merge elements back into the source buffer + _Chunk_size <<= 1; + _Chunked_merge_from_buffer(_Temp_ptr, _Temp_ptr + _Count, _First, _Chunk_size, _Count, _Pred, _Proj); + _Chunk_size <<= 1; + if (_Count <= _Chunk_size) { // if the input would be a single chunk, it's already sorted and we're done + return; + } + + // more merges necessary; merge to temporary buffer + _Chunked_merge_to_buffer(_First, _Last, _Temp_ptr, _Chunk_size, _Count, _Pred, _Proj); + } + } + + template + static void _Insertion_sort_isort_max_chunks( + _It _First, const _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // insertion sort every chunk of distance _Isort_max<_It> in [_First, _Last) + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + for (; _Isort_max<_It> < _Count; _Count -= _Isort_max<_It>) { // sort chunks + _First = _RANGES _Insertion_sort_common(_First, _First + _Isort_max<_It>, _Pred, _Proj); + } + + // sort partial last chunk + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + } + + template + static void _Uninitialized_chunked_merge_common(_It _First, const _It _Last, iter_value_t<_It>* const _Dest, + iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // move to uninitialized merging adjacent chunks of distance _Chunk + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + _Uninitialized_backout*> _Backout{_Dest}; + const auto _Backout_end = _Dest + _Count; + iter_difference_t<_It> _Chunk_size = _Isort_max<_It>; + while (_Chunk_size < _Count) { + _Count -= _Chunk_size; + const auto _Chunk2 = (_STD min)(_Chunk_size, _Count); + _Count -= _Chunk2; + + const auto _Mid1 = _First + _Chunk_size; + const auto _Last1 = _Mid1 + _Chunk2; + const auto _Last2 = _Backout._Last + _Chunk_size + _Chunk2; + _Backout._Last = _Uninitialized_merge_move(_First, _Mid1, _Last1, _Backout._Last, _Last2, _Pred, _Proj); + _First = _Last1; + } + + // copy partial last chunk + _RANGES _Uninitialized_move_unchecked(_STD move(_First), _STD move(_Last), _Backout._Last, _Backout_end); + _Backout._Release(); + } + + template + _NODISCARD static iter_value_t<_It>* _Uninitialized_merge_move(_It _First, _It _Mid, _It _Last, + iter_value_t<_It>* const _Dest, iter_value_t<_It>* const _Dest_last, _Pr _Pred, _Pj _Proj) { + // move merging ranges to uninitialized storage + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); + + _Uninitialized_backout*> _Backout{_Dest}; + _It _Next = _Mid; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + _Backout._Emplace_back(_RANGES iter_move(_Next)); + ++_Next; + + if (_Next == _Last) { + _Backout._Last = _RANGES _Uninitialized_move_unchecked( + _STD move(_First), _STD move(_Mid), _Backout._Last, _Dest_last) + .out; + return _Backout._Release(); + } + } else { + _Backout._Emplace_back(_RANGES iter_move(_First)); + ++_First; + + if (_First == _Mid) { + _Backout._Last = _RANGES _Uninitialized_move_unchecked( + _STD move(_Next), _STD move(_Last), _Backout._Last, _Dest_last) + .out; + return _Backout._Release(); + } + } + } + } + + template + static void _Chunked_merge_to_buffer(_It _First, const _It _Last, iter_value_t<_It>* _Dest, + const iter_difference_t<_It> _Chunk_size, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // move merging adjacent chunks of distance _Chunk_size to temporary buffer + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + _STL_INTERNAL_CHECK(_Chunk_size > 0); + + while (_Chunk_size < _Count) { + _Count -= _Chunk_size; + const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); + _Count -= _Right_chunk_size; + + const auto _Mid1 = _First + _Chunk_size; + const auto _Last1 = _Mid1 + _Right_chunk_size; + _Dest = _Merge_move_to_buffer(_First, _Mid1, _Last1, _Dest, _Pred, _Proj); + _First = _Last1; + } + + // copy partial last chunk + _RANGES _Move_unchecked(_STD move(_First), _STD move(_Last), _STD move(_Dest)); + } + + template + _NODISCARD static iter_value_t<_It>* _Merge_move_to_buffer( + _It _First, _It _Mid, _It _Last, iter_value_t<_It>* const _Dest, _Pr _Pred, _Pj _Proj) { + // move merging ranges to temporary storage + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); + + _Uninitialized_backout*> _Backout{_Dest}; + _It _Next = _Mid; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + _RANGES iter_swap(_Backout._Last, _Next); + ++_Backout._Last; + ++_Next; + + if (_Next == _Last) { + _Backout._Last = + _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _Backout._Last).out; + return _Backout._Release(); + } + } else { + _RANGES iter_swap(_Backout._Last, _First); + ++_Backout._Last; + ++_First; + + if (_First == _Mid) { + _Backout._Last = + _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _Backout._Last).out; + return _Backout._Release(); + } + } + } + } + + + template + static void _Chunked_merge_from_buffer(iter_value_t<_It>* _First, iter_value_t<_It>* _Last, _It _Dest, + const iter_difference_t<_It> _Chunk_size, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + // move merging adjacent chunks of distance _Chunk_size from temporary buffer + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + _STL_INTERNAL_CHECK(_Chunk_size > 0); + + while (_Chunk_size < _Count) { + _Count -= _Chunk_size; + const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); + _Count -= _Right_chunk_size; + + const auto _Mid1 = _First + _Chunk_size; + const auto _Last1 = _Mid1 + _Right_chunk_size; + _Dest = _Merge_move_from_buffer(_First, _Mid1, _Last1, _Dest, _Pred, _Proj); + _First = _Last1; + } + + // copy partial last chunk + _RANGES _Move_unchecked(_STD move(_First), _STD move(_Last), _STD move(_Dest)); + } + + template + _NODISCARD static _It _Merge_move_from_buffer(iter_value_t<_It>* _First, iter_value_t<_It>* _Mid, + iter_value_t<_It>* _Last, _It _Dest, _Pr _Pred, _Pj _Proj) { + // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to [_Dest_first, _Dest_last) + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); + + iter_value_t<_It>* _Next = _Mid; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + _RANGES iter_swap(_Dest, _Next); + ++_Dest; + ++_Next; + + if (_Next == _Last) { + return _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Dest)).out; + } + } else { + _RANGES iter_swap(_Dest, _First); + ++_Dest; + ++_First; + + if (_First == _Mid) { + return _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _STD move(_Dest)).out; + } + } + } + } + }; + + inline constexpr _Stable_sort_fn stable_sort{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE partial_sort template _CONSTEXPR20 void partial_sort(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { diff --git a/tests/std/test.lst b/tests/std/test.lst index fb9c68e4008..b48001868a7 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -317,6 +317,7 @@ tests\P0896R4_ranges_alg_set_union tests\P0896R4_ranges_alg_shuffle tests\P0896R4_ranges_alg_sort tests\P0896R4_ranges_alg_stable_partition +tests\P0896R4_ranges_alg_stable_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_stable_sort/env.lst b/tests/std/tests/P0896R4_ranges_alg_stable_sort/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_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_stable_sort/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp new file mode 100644 index 00000000000..4f5f0832ff8 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp @@ -0,0 +1,55 @@ +// 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{1, 0}, P{-1260655766, 1}, P{-1298559576, 2}, P{1, 3}, P{-2095681771, 4}, + P{-441494788, 5}, P{-47163201, 6}, P{1, 7}, P{1429106719, 8}, P{1, 9}}; + + template + static void call() { + using ranges::stable_sort, ranges::is_sorted, ranges::iterator_t, ranges::less; + + { // Validate range overload + auto buff = input; + const R range{buff}; + const same_as> auto result = stable_sort(range, less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range)); // No projection to check for stability + } + + { // Validate iterator overload + auto buff = input; + const R range{buff}; + const same_as> auto result = stable_sort(range.begin(), range.end(), less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range.begin(), range.end())); // No projection to check for stability + } + + { // Validate empty range + const R range{}; + const same_as> auto result = stable_sort(range, less{}, get_first); + assert(result == range.end()); + assert(is_sorted(range, less{})); + } + } +}; + +int main() { + test_random(); +} From 946d77ea287639379e495eec5930d43dbce7bbaf Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 23 Oct 2020 19:34:53 +0200 Subject: [PATCH 05/20] Do not require nodiscard on _Uninitialized_move_unchecked --- stl/inc/xmemory | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 3b4c172df46..f67147ae902 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1558,7 +1558,7 @@ namespace ranges { template _Se, _No_throw_forward_iterator _Out, _No_throw_sentinel_for<_Out> _OSe> requires constructible_from, iter_rvalue_reference_t<_It>> - _NODISCARD uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( + uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { // clang-format on if constexpr (is_same_v<_Se, _It> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { From 84aeed5182c789b887e54ec8c96ee9e87b44ac5c Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 4 Nov 2020 21:40:33 +0100 Subject: [PATCH 06/20] Apply Caseys comments Co-authored-by: Casey Carter --- stl/inc/algorithm | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 29bcb139c12..d97425bd2ea 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6165,7 +6165,7 @@ namespace ranges { _Optimistic_temporary_buffer> _Temp_buf{_Temp_count}; // _Temp_count + 1 since we work on closed ranges - const iter_difference_t<_It> _Total_count = _Temp_count + static_cast>(1); + const auto _Total_count = static_cast>(_Temp_count + 1); auto _Result = _Stable_partition_common_buffered(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj), _Total_count, _Temp_buf._Data, _Temp_buf._Capacity); return {_STD move(_Result.first), _STD move(_Saved_last)}; @@ -6185,12 +6185,12 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); using _Diff = iter_difference_t<_It>; - if (_Count - static_cast<_Diff>(1) <= _Capacity) { // - 1 since we never need to store *_Last + if (_Count - 1 <= _Capacity) { // - 1 since we never need to store *_Last _Uninitialized_backout*> _Backout{_Temp_ptr}; _It _Next = _First; _Backout._Emplace_back(_RANGES iter_move(_First)); while (++_First != _Last) { - // test each element, copying to _Temp_ptr if it's in the false range, or + // test each element, moving into the temporary buffer if it's in the false range, or // assigning backwards if it's in the true range if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { *_Next = _RANGES iter_move(_First); @@ -6205,12 +6205,11 @@ namespace ranges { ++_Next; // copy back the false range _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _Next); - const _Diff _True_distance = - static_cast<_Diff>(_Count - static_cast<_Diff>(_Backout._Last - _Backout._First)); + const auto _True_distance = static_cast<_Diff>(_Count - (_Backout._Last - _Backout._First)); return {_STD move(_Next), _True_distance}; } - const _Diff _Mid_offset = _Count >> 1; // note: >= 1 because _Count >= 2 + const _Diff _Mid_offset = _Count >> 1; // _Mid_offset >= 1 because _Count >= 2 const _It _Mid = _RANGES next(_First, _Mid_offset); // form [_First, _Left) true range, [_Left, _Mid) false range _It _Left = _Mid; @@ -6246,8 +6245,8 @@ namespace ranges { if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Right))) { // excluded the true range after and including _Mid, invariants reestablished, recurse - const _Diff _Right_count = _Count - _Mid_offset; - const _Diff _Remaining = _Right_count - _Right_true_count; + const auto _Right_count = static_cast<_Diff>(_Count - _Mid_offset); + const auto _Remaining = static_cast<_Diff>(_Right_count - _Right_true_count); const auto _High = _Stable_partition_common_buffered( _Right, _Last, _Pred, _Proj, _Remaining, _Temp_ptr, _Capacity); _Right = _STD move(_High.first); @@ -7615,8 +7614,8 @@ namespace ranges { template void _Rotate_one_right(_It _First, _It _Mid, _It _Last) { // exchanges the range [_First, _Mid) with [_Mid, _Last) - // pre: distance(_Mid, _Last) is 1 - const auto _Temp = _RANGES iter_move(_Mid); + _STL_INTERNAL_CHECK(_RANGES next(_Mid) == _Last); + auto _Temp = _RANGES iter_move(_Mid); _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); *_First = _STD move(_Temp); } @@ -7624,8 +7623,8 @@ namespace ranges { template void _Rotate_one_left(_It _First, _It _Mid, _It _Last) { // exchanges the range [_First, _Mid) with [_Mid, _Last) - // pre: distance(_First, _Mid) is 1 - const auto _Temp = _RANGES iter_move(_Mid); + _STL_INTERNAL_CHECK(_RANGES next(_First) == _Mid); + auto _Temp = _RANGES iter_move(_Mid); auto _Result = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)); *_Result.out = _STD move(_Temp); } From 1518b32ee8d0ba5cf26a9030af218568d02390a6 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 4 Nov 2020 20:11:20 -0800 Subject: [PATCH 07/20] Don't move the results of _Pass_fn --- stl/inc/algorithm | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index d97425bd2ea..cf19bb9e704 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6166,8 +6166,8 @@ namespace ranges { // _Temp_count + 1 since we work on closed ranges const auto _Total_count = static_cast>(_Temp_count + 1); - auto _Result = _Stable_partition_common_buffered(_STD move(_First), _STD move(_Last), _STD move(_Pred), - _STD move(_Proj), _Total_count, _Temp_buf._Data, _Temp_buf._Capacity); + auto _Result = _Stable_partition_common_buffered( + _STD move(_First), _STD move(_Last), _Pred, _Proj, _Total_count, _Temp_buf._Data, _Temp_buf._Capacity); return {_STD move(_Result.first), _STD move(_Saved_last)}; } @@ -6247,7 +6247,7 @@ namespace ranges { // excluded the true range after and including _Mid, invariants reestablished, recurse const auto _Right_count = static_cast<_Diff>(_Count - _Mid_offset); const auto _Remaining = static_cast<_Diff>(_Right_count - _Right_true_count); - const auto _High = _Stable_partition_common_buffered( + const auto _High = _Stable_partition_common_buffered( _Right, _Last, _Pred, _Proj, _Remaining, _Temp_ptr, _Capacity); _Right = _STD move(_High.first); _Right_true_count += _High.second; @@ -7624,9 +7624,9 @@ namespace ranges { void _Rotate_one_left(_It _First, _It _Mid, _It _Last) { // exchanges the range [_First, _Mid) with [_Mid, _Last) _STL_INTERNAL_CHECK(_RANGES next(_First) == _Mid); - auto _Temp = _RANGES iter_move(_Mid); - auto _Result = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)); - *_Result.out = _STD move(_Temp); + auto _Temp = _RANGES iter_move(_Mid); + auto _Result = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)); + *_Result.out = _STD move(_Temp); } // clang-format off @@ -7759,8 +7759,8 @@ namespace ranges { _RANGES _Lower_bound_unchecked(_Mid, _Count1, _STD invoke(_Proj, *_Firstn), _Pred, _Proj); const auto _Count2n = _RANGES distance(_Mid, _Lastn); _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Count1, _Count2, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj), _STD move(_Firstn), - _STD move(_Lastn), _Count1n, _Count2n); + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj, _STD move(_Firstn), _STD move(_Lastn), _Count1n, + _Count2n); } else { const iter_difference_t<_It> _Count2n = _Count2 >> 1; // shift for codegen const _It _Lastn = _RANGES next(_Mid, _Count2n); @@ -7768,8 +7768,8 @@ namespace ranges { _RANGES _Upper_bound_unchecked(_First, _Count2, _STD invoke(_Proj, *_Lastn), _Pred, _Proj); const auto _Count1n = _RANGES distance(_First, _Firstn); _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Count1, _Count2, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj), _STD move(_Firstn), - _STD move(_Lastn), _Count1n, _Count2n); + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj, _STD move(_Firstn), _STD move(_Lastn), _Count1n, + _Count2n); } } @@ -7822,14 +7822,14 @@ namespace ranges { } if (_Count1 <= _Count2 && _Count1 <= _Capacity) { - _RANGES _Inplace_merge_buffer_left(_STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, - _Capacity, _STD move(_Pred), _STD move(_Proj)); + _RANGES _Inplace_merge_buffer_left( + _STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, _Capacity, _Pred, _Proj); } else if (_Count2 <= _Capacity) { - _RANGES _Inplace_merge_buffer_right(_STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, - _Capacity, _STD move(_Pred), _STD move(_Proj)); + _RANGES _Inplace_merge_buffer_right( + _STD move(_First), _STD move(_Mid), _STD move(_Last), _Temp_ptr, _Capacity, _Pred, _Proj); } else { _RANGES _Buffered_inplace_merge_divide_and_conquer(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Count1, _Count2, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj)); + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj); } } @@ -7926,13 +7926,13 @@ namespace ranges { _Optimistic_temporary_buffer> _Temp_buf{(_STD min)(_Count1, _Count2)}; if (_Count1 <= _Count2 && _Count1 <= _Temp_buf._Capacity) { _RANGES _Inplace_merge_buffer_left(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Temp_buf._Data, _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); } else if (_Count2 <= _Temp_buf._Capacity) { _RANGES _Inplace_merge_buffer_right(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Temp_buf._Data, _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); } else { _RANGES _Buffered_inplace_merge_divide_and_conquer(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Count1, _Count2, _Temp_buf._Data, _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + _Count1, _Count2, _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); } } }; @@ -8589,13 +8589,13 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); if (_Count <= _Isort_max<_It>) { - _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); return; } _Optimistic_temporary_buffer<_Iter_value_t<_It>> _Temp_buf{_Count - _Count / 2}; - _Stable_sort_common_buffered(_STD move(_First), _STD move(_Last), _Count, _Temp_buf._Data, - _Temp_buf._Capacity, _STD move(_Pred), _STD move(_Proj)); + _Stable_sort_common_buffered( + _STD move(_First), _STD move(_Last), _Count, _Temp_buf._Data, _Temp_buf._Capacity, _Pred, _Proj); } template @@ -8606,7 +8606,7 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); if (_Count <= _Isort_max<_It>) { - _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); } else { // sort halves and merge const iter_difference_t<_It> _Half_count = _Count >> 1; const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count; @@ -8620,7 +8620,7 @@ namespace ranges { } // merge halves _RANGES _Buffered_inplace_merge_common(_STD move(_First), _STD move(_Mid), _STD move(_Last), - _Half_count_ceil, _Half_count, _Temp_ptr, _Capacity, _STD move(_Pred), _STD move(_Proj)); + _Half_count_ceil, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj); } } @@ -8670,7 +8670,7 @@ namespace ranges { } // sort partial last chunk - _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); } template @@ -9255,7 +9255,7 @@ namespace ranges { } // sort any remainder - _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); } }; From 4742ffe18cc71e29601aeea1bec6a5de673a9b52 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 4 Nov 2020 22:08:50 +0100 Subject: [PATCH 08/20] More review comments --- stl/inc/algorithm | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index cf19bb9e704..7610095bcb1 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6075,8 +6075,9 @@ namespace ranges { _It _Buffered_rotate_common(const _It _First, const _It _Mid, const _It _Last, const iter_difference_t<_It> _Count1, const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity) { // rotate [_First, _Last) using temp buffer - // precondition: _Count1 == distance(_First, _Mid) - // precondition: _Count2 == distance(_Mid, _Last) + _STL_INTERNAL_CHECK(_Count1 == _RANGES distance(_First == _Mid)); + _STL_INTERNAL_CHECK(_Count2 == _RANGES distance(_Mid == _Last)); + if (_Count1 == 0) { return _Last; } @@ -6085,7 +6086,7 @@ namespace ranges { return _First; } - if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then copy parts + if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then move parts _Uninitialized_backout*> _Backout{ _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Temp_ptr, _Temp_ptr + _Count1).out}; const _It _New_mid = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)).out; @@ -6093,7 +6094,7 @@ namespace ranges { return _New_mid; } - if (_Count2 <= _Capacity) { // buffer right range, then copy parts + if (_Count2 <= _Capacity) { // buffer right range, then move parts _Uninitialized_backout*> _Backout{ _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Temp_ptr, _Temp_ptr + _Count2).out}; _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); @@ -6143,10 +6144,9 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); // partition preserving order of equivalents - auto _Saved_last = _Last; for (;;) { // skip in-place elements at front if (_First == _Last) { // the input range range is true (already partitioned) - return {_STD move(_First), _STD move(_Saved_last)}; + return {_STD move(_First), _STD move(_Last)}; } if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { break; @@ -6154,6 +6154,7 @@ namespace ranges { ++_First; } + auto _Saved_last = _Last; do { // skip in-place elements at end --_Last; if (_First == _Last) { @@ -6176,13 +6177,13 @@ namespace ranges { _Pr _Pred, _Pj _Proj, const iter_difference_t<_It> _Count, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity) { // implement stable_partition of [_First, _Last] (note: closed range) - // precondition: !_STD invoke(_Pred, _STD invoke(_Proj, *_First)) - // precondition: _STD invoke(_Pred, _STD invoke(_Proj, *_Last)) - // precondition: _RANGES distance(_First, _Last) + 1 == _Count // note: _Count >= 2 and _First != _Last - _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_CHECK(!_STD invoke(_Pred, _STD invoke(_Proj, *_First))); + _STL_INTERNAL_CHECK(_STD invoke(_Pred, _STD invoke(_Proj, *_Last))); + _STL_INTERNAL_CHECK(_Count == _RANGES distance(_First, _Last) + 1); using _Diff = iter_difference_t<_It>; if (_Count - 1 <= _Capacity) { // - 1 since we never need to store *_Last @@ -6224,7 +6225,7 @@ namespace ranges { if (_STD invoke(_Pred, _STD invoke(_Proj, *_Left))) { // excluded the false range before _Mid, invariants reestablished, recurse const auto _Low = _Stable_partition_common_buffered( - _First, _Left, _Pred, _Proj, _Left_true_count, _Temp_ptr, _Capacity); + _First, _STD move(_Left), _Pred, _Proj, _Left_true_count, _Temp_ptr, _Capacity); _Left = _STD move(_Low.first); _Left_true_count = _Low.second; break; @@ -6248,7 +6249,7 @@ namespace ranges { const auto _Right_count = static_cast<_Diff>(_Count - _Mid_offset); const auto _Remaining = static_cast<_Diff>(_Right_count - _Right_true_count); const auto _High = _Stable_partition_common_buffered( - _Right, _Last, _Pred, _Proj, _Remaining, _Temp_ptr, _Capacity); + _STD move(_Right), _Last, _Pred, _Proj, _Remaining, _Temp_ptr, _Capacity); _Right = _STD move(_High.first); _Right_true_count += _High.second; break; @@ -6259,10 +6260,10 @@ namespace ranges { } // swap the [_Left, _Mid) false range with the [_Mid, _Right) true range - const auto _Partition_point = + auto _Partition_point = _RANGES _Buffered_rotate_common(_STD move(_Left), _STD move(_Mid), _STD move(_Right), static_cast<_Diff>(_Mid_offset - _Left_true_count), _Right_true_count, _Temp_ptr, _Capacity); - return {_Partition_point, static_cast<_Diff>(_Left_true_count + _Right_true_count)}; + return {_STD move(_Partition_point), static_cast<_Diff>(_Left_true_count + _Right_true_count)}; } }; @@ -7698,7 +7699,7 @@ namespace ranges { if (_STD invoke(_Pred, _STD invoke(_Proj, *_Right_last), _STD invoke(_Proj, *_Mid))) { *--_Last = _RANGES iter_move(_Mid); // the lowest element is now in position if (_First == _Mid) { - *--_Last = _RANGES iter_move(_Right_last); // to make [_Right_first, _Right_last) a half-open range + ++_Right_last; // to make [_Right_first, _Right_last) a half-open range _RANGES _Move_backward_common(_Right_first, _Right_last, _STD move(_Last)); return; } @@ -7707,7 +7708,7 @@ namespace ranges { *--_Last = _RANGES iter_move(_Right_last); --_Right_last; if (_Right_first == _Right_last) { // we can't compare with *_Right_first, but we know it is lowest - *--_Last = _RANGES iter_move(_Mid); // restore half-open range [_First, _Mid) + ++_Mid; // restore half-open range [_First, _Mid) _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); *_First = _RANGES iter_move(_Right_first); return; From 60daf31c2d38643882191d248be0ea0c105bfda1 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 5 Nov 2020 20:43:51 +0100 Subject: [PATCH 09/20] More review suggestions Co-authored-by: Casey Carter --- stl/inc/algorithm | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 7610095bcb1..2d8564b3c37 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -7755,8 +7755,8 @@ namespace ranges { // usual invariants apply if (_Count1 <= _Count2) { const iter_difference_t<_It> _Count1n = _Count1 >> 1; // shift for codegen - const _It _Firstn = _RANGES next(_First, _Count1n); - const _It _Lastn = + _It _Firstn = _RANGES next(_First, _Count1n); + _It _Lastn = _RANGES _Lower_bound_unchecked(_Mid, _Count1, _STD invoke(_Proj, *_Firstn), _Pred, _Proj); const auto _Count2n = _RANGES distance(_Mid, _Lastn); _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), @@ -7764,8 +7764,8 @@ namespace ranges { _Count2n); } else { const iter_difference_t<_It> _Count2n = _Count2 >> 1; // shift for codegen - const _It _Lastn = _RANGES next(_Mid, _Count2n); - const _It _Firstn = + _It _Lastn = _RANGES next(_Mid, _Count2n); + _It _Firstn = _RANGES _Upper_bound_unchecked(_First, _Count2, _STD invoke(_Proj, *_Lastn), _Pred, _Proj); const auto _Count1n = _RANGES distance(_First, _Firstn); _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), @@ -7805,7 +7805,7 @@ namespace ranges { --_Count2; // Find last element in [_Mid, _Last) that is less than *--_Mid - const auto _Highest = _Prev_iter(_Mid); + const auto _Highest = _RANGES prev(_Mid); while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))) { if (_Mid == --_Last) { // rotate only element remaining in right partition to the beginning, without allocating @@ -7907,7 +7907,7 @@ namespace ranges { } // Find last element in [_Mid, _Last) that is less than *--_Mid - const auto _Highest = _Prev_iter(_Mid); + const auto _Highest = _RANGES prev(_Mid); while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))) { if (_Mid == --_Last) { // rotate only element remaining in right partition to the beginning, without allocating @@ -8580,7 +8580,7 @@ namespace ranges { private: template - static constexpr iter_difference_t<_It> _Isort_max = static_cast>(_ISORT_MAX); + static constexpr auto _Isort_max = static_cast>(_ISORT_MAX); template static void _Stable_sort_common( @@ -8588,6 +8588,7 @@ namespace ranges { // 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>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); if (_Count <= _Isort_max<_It>) { _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); @@ -8605,6 +8606,8 @@ namespace ranges { // 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>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + // Pre: _Temp_ptr points to empty storage for _Capacity objects if (_Count <= _Isort_max<_It>) { _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); @@ -8798,7 +8801,6 @@ namespace ranges { } } - template static void _Chunked_merge_from_buffer(iter_value_t<_It>* _First, iter_value_t<_It>* _Last, _It _Dest, const iter_difference_t<_It> _Chunk_size, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { From 5c74fdb451607ada85e0b2810f4bb624911f9914 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 5 Nov 2020 20:44:51 +0100 Subject: [PATCH 10/20] Move _Is_sorted_until_unchecked --- stl/inc/algorithm | 19 +++++++++++++++++++ stl/inc/xutility | 24 ------------------------ 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 2d8564b3c37..cdcede1a115 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -7611,6 +7611,25 @@ void inplace_merge(_ExPo&&, _BidIt _First, _BidIt _Mid, _BidIt _Last) noexcept / #ifdef __cpp_lib_concepts namespace ranges { + // FUNCTION TEMPLATE _Is_sorted_until_unchecked + // clang-format off + template _Se, class _Pr, class _Pj> + requires indirect_strict_weak_order<_Pr, projected<_It, _Pj>> + _NODISCARD constexpr _It _Is_sorted_until_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on + if (_First == _Last) { + return _First; + } + + for (auto _Prev = _First; ++_First != _Last; ++_Prev) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { + break; + } + } + + return _First; + } + // VARIABLE ranges::inplace_merge template void _Rotate_one_right(_It _First, _It _Mid, _It _Last) { diff --git a/stl/inc/xutility b/stl/inc/xutility index f8748217d9a..fa244555d08 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5899,30 +5899,6 @@ _NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, _FwdIt _Last, const _T return _STD lower_bound(_First, _Last, _Val, less<>{}); } - -#ifdef __cpp_lib_concepts -namespace ranges { - // FUNCTION TEMPLATE _Is_sorted_until_unchecked - // clang-format off - template _Se, class _Pr, class _Pj> - requires indirect_strict_weak_order<_Pr, projected<_It, _Pj>> - _NODISCARD constexpr _It _Is_sorted_until_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { - // clang-format on - if (_First == _Last) { - return _First; - } - - for (auto _Prev = _First; ++_First != _Last; ++_Prev) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { - break; - } - } - - return _First; - } -} // namespace ranges -#endif // __cpp_lib_concepts - // FUNCTION TEMPLATE _Swap_ranges_unchecked template _CONSTEXPR20 _FwdIt2 _Swap_ranges_unchecked(_FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { From c43c4a2c774937260133928de8ef0835e28a8a76 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 5 Nov 2020 21:00:15 +0100 Subject: [PATCH 11/20] Even more review comments --- stl/inc/algorithm | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index cdcede1a115..f6ba9da4ce4 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6217,21 +6217,20 @@ namespace ranges { _Diff _Left_true_count = _Mid_offset; for (;;) { // skip over the trailing false range before _Mid --_Left; + --_Left_true_count; if (_First == _Left) { // the entire left range is false - --_Left_true_count; // to exclude *_First break; } if (_STD invoke(_Pred, _STD invoke(_Proj, *_Left))) { // excluded the false range before _Mid, invariants reestablished, recurse + ++_Left_true_count; // to include *_First const auto _Low = _Stable_partition_common_buffered( _First, _STD move(_Left), _Pred, _Proj, _Left_true_count, _Temp_ptr, _Capacity); _Left = _STD move(_Low.first); _Left_true_count = _Low.second; break; } - - --_Left_true_count; } // form [_Mid, _Right) true range, [_Right, next(_Last)) false range @@ -7775,8 +7774,7 @@ namespace ranges { if (_Count1 <= _Count2) { const iter_difference_t<_It> _Count1n = _Count1 >> 1; // shift for codegen _It _Firstn = _RANGES next(_First, _Count1n); - _It _Lastn = - _RANGES _Lower_bound_unchecked(_Mid, _Count1, _STD invoke(_Proj, *_Firstn), _Pred, _Proj); + _It _Lastn = _RANGES _Lower_bound_unchecked(_Mid, _Count1, _STD invoke(_Proj, *_Firstn), _Pred, _Proj); const auto _Count2n = _RANGES distance(_Mid, _Lastn); _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj, _STD move(_Firstn), _STD move(_Lastn), _Count1n, @@ -7784,8 +7782,7 @@ namespace ranges { } else { const iter_difference_t<_It> _Count2n = _Count2 >> 1; // shift for codegen _It _Lastn = _RANGES next(_Mid, _Count2n); - _It _Firstn = - _RANGES _Upper_bound_unchecked(_First, _Count2, _STD invoke(_Proj, *_Lastn), _Pred, _Proj); + _It _Firstn = _RANGES _Upper_bound_unchecked(_First, _Count2, _STD invoke(_Proj, *_Lastn), _Pred, _Proj); const auto _Count1n = _RANGES distance(_First, _Firstn); _RANGES _Buffered_inplace_merge_divide_and_conquer2(_STD move(_First), _STD move(_Mid), _STD move(_Last), _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _Proj, _STD move(_Firstn), _STD move(_Lastn), _Count1n, @@ -8511,7 +8508,7 @@ void _Stable_sort_unchecked(const _BidIt _First, const _BidIt _Last, const _Iter if (_Count <= _ISORT_MAX) { _Insertion_sort_unchecked(_First, _Last, _Pred); // small } else { // sort halves and merge - const auto _Half_count = static_cast<_Diff>(_Count / 2); + const auto _Half_count = _Count >> 1; const auto _Half_count_ceil = static_cast<_Diff>(_Count - _Half_count); const _BidIt _Mid = _STD next(_First, _Half_count_ceil); if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer @@ -8705,18 +8702,18 @@ namespace ranges { _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); _Uninitialized_backout*> _Backout{_Dest}; - const auto _Backout_end = _Dest + _Count; - iter_difference_t<_It> _Chunk_size = _Isort_max<_It>; - while (_Chunk_size < _Count) { - _Count -= _Chunk_size; - const auto _Chunk2 = (_STD min)(_Chunk_size, _Count); + const auto _Backout_end = _Dest + _Count; + while (_Isort_max<_It> < _Count) { + _Count -= _Isort_max<_It>; + const auto _Chunk2 = (_STD min)(_Isort_max<_It>, _Count); _Count -= _Chunk2; - const auto _Mid1 = _First + _Chunk_size; - const auto _Last1 = _Mid1 + _Chunk2; - const auto _Last2 = _Backout._Last + _Chunk_size + _Chunk2; - _Backout._Last = _Uninitialized_merge_move(_First, _Mid1, _Last1, _Backout._Last, _Last2, _Pred, _Proj); - _First = _Last1; + auto _Mid1 = _First + _Isort_max<_It>; + auto _Last1 = _Mid1 + _Chunk2; + auto _Last2 = _Backout._Last + _Isort_max<_It> + _Chunk2; + _Backout._Last = _Uninitialized_merge_move( + _STD move(_First), _STD move(_Mid1), _Last1, _Backout._Last, _Last2, _Pred, _Proj); + _First = _STD move(_Last1); } // copy partial last chunk @@ -8732,6 +8729,7 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); + _STL_INTERNAL_CHECK(_Last - _First <= _Dest_last - _Dest); _Uninitialized_backout*> _Backout{_Dest}; _It _Next = _Mid; @@ -8774,10 +8772,10 @@ namespace ranges { const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); _Count -= _Right_chunk_size; - const auto _Mid1 = _First + _Chunk_size; - const auto _Last1 = _Mid1 + _Right_chunk_size; - _Dest = _Merge_move_to_buffer(_First, _Mid1, _Last1, _Dest, _Pred, _Proj); - _First = _Last1; + auto _Mid1 = _First + _Chunk_size; + auto _Last1 = _Mid1 + _Right_chunk_size; + _Dest = _Merge_move_to_buffer(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); + _First = _STD move(_Last1); } // copy partial last chunk @@ -8792,6 +8790,7 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); + _STL_INTERNAL_CHECK(_Last - _First <= _Dest_last - _Dest); _Uninitialized_backout*> _Backout{_Dest}; _It _Next = _Mid; @@ -8834,10 +8833,10 @@ namespace ranges { const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); _Count -= _Right_chunk_size; - const auto _Mid1 = _First + _Chunk_size; - const auto _Last1 = _Mid1 + _Right_chunk_size; - _Dest = _Merge_move_from_buffer(_First, _Mid1, _Last1, _Dest, _Pred, _Proj); - _First = _Last1; + auto _Mid1 = _First + _Chunk_size; + auto _Last1 = _Mid1 + _Right_chunk_size; + _Dest = _Merge_move_from_buffer(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); + _First = _STD move(_Last1); } // copy partial last chunk @@ -8847,11 +8846,12 @@ namespace ranges { template _NODISCARD static _It _Merge_move_from_buffer(iter_value_t<_It>* _First, iter_value_t<_It>* _Mid, iter_value_t<_It>* _Last, _It _Dest, _Pr _Pred, _Pj _Proj) { - // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to [_Dest_first, _Dest_last) + // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to _Dest _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); + _STL_INTERNAL_CHECK(_Last - _First <= _Dest_last - _Dest); iter_value_t<_It>* _Next = _Mid; for (;;) { From 68b654491d3eb51474332ead9138414a0b155f3e Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 6 Nov 2020 20:29:32 +0100 Subject: [PATCH 12/20] Fix bad verify --- stl/inc/algorithm | 2 -- 1 file changed, 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f6ba9da4ce4..8f6d9d1e7b9 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -8790,7 +8790,6 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); - _STL_INTERNAL_CHECK(_Last - _First <= _Dest_last - _Dest); _Uninitialized_backout*> _Backout{_Dest}; _It _Next = _Mid; @@ -8851,7 +8850,6 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); - _STL_INTERNAL_CHECK(_Last - _First <= _Dest_last - _Dest); iter_value_t<_It>* _Next = _Mid; for (;;) { From e5eb7e8c4344a15420035dd59b6d296b77a0b36f Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 8 Nov 2020 20:50:46 +0100 Subject: [PATCH 13/20] Derp --- stl/inc/algorithm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8f6d9d1e7b9..f4e83b05887 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6075,8 +6075,8 @@ namespace ranges { _It _Buffered_rotate_common(const _It _First, const _It _Mid, const _It _Last, const iter_difference_t<_It> _Count1, const iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity) { // rotate [_First, _Last) using temp buffer - _STL_INTERNAL_CHECK(_Count1 == _RANGES distance(_First == _Mid)); - _STL_INTERNAL_CHECK(_Count2 == _RANGES distance(_Mid == _Last)); + _STL_INTERNAL_CHECK(_Count1 == _RANGES distance(_First, _Mid)); + _STL_INTERNAL_CHECK(_Count2 == _RANGES distance(_Mid, _Last)); if (_Count1 == 0) { return _Last; From b12b5856013483b9382e2da002b782c8614167a8 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 8 Nov 2020 20:54:13 +0100 Subject: [PATCH 14/20] Use _Rotate_unchecked rather than rotate --- stl/inc/algorithm | 160 ++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 82 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f4e83b05887..9d7776ca0be 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -5037,74 +5037,71 @@ namespace ranges { #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::rotate - class _Rotate_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; + template + _NODISCARD constexpr subrange<_It> _Reverse_until_mid_unchecked(_It _First, const _It _Mid, _It _Last) { + // reverse until either _First or _Last hits _Mid + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); - template _Se> - constexpr subrange<_It> operator()(_It _First, _It _Mid, _Se _Last) const { - _Adl_verify_range(_First, _Mid); - _Adl_verify_range(_Mid, _Last); - auto _UResult = _Rotate_unchecked( - _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), _Get_unwrapped(_STD move(_Last))); + do { + _RANGES iter_swap(_First, --_Last); + } while (++_First != _Mid && _Last != _Mid); - return _Rewrap_subrange>(_First, _STD move(_UResult)); - } + return {_STD move(_First), _STD move(_Last)}; + } - // clang-format off - template - requires permutable> - constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, iterator_t<_Rng> _Mid) const { - // clang-format on - _Adl_verify_range(_RANGES begin(_Range), _Mid); - _Adl_verify_range(_Mid, _RANGES end(_Range)); - auto _UResult = _Rotate_unchecked(_Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range)); + template _Se> + _NODISCARD constexpr subrange<_It> _Rotate_unchecked(_It _First, _It _Mid, _Se _Last) { + // Exchange the ranges [_First, _Mid) and [_Mid, _Last) + // that is, rotates [_First, _Last) left by distance(_First, _Mid) positions - return _Rewrap_subrange>(_Mid, _STD move(_UResult)); + if (_First == _Mid) { + auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); + return {_Final, _Final}; } - private: - template - _NODISCARD static constexpr subrange<_It> _Rotate_unchecked(_It _First, _It _Mid, _Se _Last) { - // Exchange the ranges [_First, _Mid) and [_Mid, _Last) - // that is, rotates [_First, _Last) left by distance(_First, _Mid) positions - _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + if (_Mid == _Last) { + return {_STD move(_First), _STD move(_Mid)}; + } - if (_First == _Mid) { - auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); - return {_Final, _Final}; - } + if constexpr (bidirectional_iterator<_It>) { + _Reverse_common(_First, _Mid); + auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); + _Reverse_common(_Mid, _Final); - if (_Mid == _Last) { - return {_STD move(_First), _STD move(_Mid)}; - } + if constexpr (random_access_iterator<_It>) { + _Reverse_common(_First, _Final); + _First += _Final - _Mid; - if constexpr (bidirectional_iterator<_It>) { - _Reverse_common(_First, _Mid); - auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); - _Reverse_common(_Mid, _Final); - - if constexpr (random_access_iterator<_It>) { - _Reverse_common(_First, _Final); - _First += _Final - _Mid; + return {_STD move(_First), _STD move(_Final)}; + } else { + const auto _Result = _RANGES _Reverse_until_mid_unchecked(_STD move(_First), _Mid, _Final); + auto _Mid_first = _Result.begin(); + auto _Mid_last = _Result.end(); + _Reverse_common(_Mid_first, _Mid_last); - return {_STD move(_First), _STD move(_Final)}; + if (_Mid_first == _Mid) { + return {_STD move(_Mid_last), _STD move(_Final)}; } else { - const auto _Result = _Reverse_until_mid_unchecked(_STD move(_First), _Mid, _Final); - auto _Mid_first = _Result.begin(); - auto _Mid_last = _Result.end(); - _Reverse_common(_Mid_first, _Mid_last); - - if (_Mid_first == _Mid) { - return {_STD move(_Mid_last), _STD move(_Final)}; - } else { - return {_STD move(_Mid_first), _STD move(_Final)}; - } + return {_STD move(_Mid_first), _STD move(_Final)}; } - } else { - auto _Next = _Mid; - do { // rotate the first cycle + } + } else { + auto _Next = _Mid; + do { // rotate the first cycle + _RANGES iter_swap(_First, _Next); + ++_First; + ++_Next; + if (_First == _Mid) { + _Mid = _Next; + } + } while (_Next != _Last); + + auto _Begin = _First; + + while (_Mid != _Last) { // rotate subsequent cycles + _Next = _Mid; + do { _RANGES iter_swap(_First, _Next); ++_First; ++_Next; @@ -5112,36 +5109,35 @@ namespace ranges { _Mid = _Next; } } while (_Next != _Last); - - auto _Begin = _First; - - while (_Mid != _Last) { // rotate subsequent cycles - _Next = _Mid; - do { - _RANGES iter_swap(_First, _Next); - ++_First; - ++_Next; - if (_First == _Mid) { - _Mid = _Next; - } - } while (_Next != _Last); - } - return {_STD move(_Begin), _STD move(_Mid)}; } + return {_STD move(_Begin), _STD move(_Mid)}; } + } - template - _NODISCARD static constexpr subrange<_It> _Reverse_until_mid_unchecked(_It _First, const _It _Mid, _It _Last) { - // reverse until either _First or _Last hits _Mid - _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); - _STL_INTERNAL_CHECK(_First != _Mid); - _STL_INTERNAL_CHECK(_Mid != _Last); + class _Rotate_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; - do { - _RANGES iter_swap(_First, --_Last); - } while (++_First != _Mid && _Last != _Mid); + template _Se> + constexpr subrange<_It> operator()(_It _First, _It _Mid, _Se _Last) const { + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + auto _UResult = _RANGES _Rotate_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), _Get_unwrapped(_STD move(_Last))); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } - return {_STD move(_First), _STD move(_Last)}; + // clang-format off + template + requires permutable> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, iterator_t<_Rng> _Mid) const { + // clang-format on + _Adl_verify_range(_RANGES begin(_Range), _Mid); + _Adl_verify_range(_Mid, _RANGES end(_Range)); + auto _UResult = _RANGES _Rotate_unchecked(_Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range)); + + return _Rewrap_subrange>(_Mid, _STD move(_UResult)); } }; @@ -6102,7 +6098,7 @@ namespace ranges { } // buffer too small, rotate in place - return _RANGES rotate(_STD move(_First), _STD move(_Mid), _STD move(_Last)).begin(); + return _RANGES _Rotate_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Last)).begin(); } class _Stable_partition_fn : private _Not_quite_object { From dacbea2f1848b8186c0c48bd4414f5e1312b6300 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 19 Nov 2020 22:23:37 +0100 Subject: [PATCH 15/20] Fix breakage --- stl/inc/algorithm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 74cb2874eb6..df36c9f3ac6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -8448,7 +8448,7 @@ void _Stable_sort_unchecked(const _BidIt _First, const _BidIt _Last, const _Iter if (_Count <= _ISORT_MAX) { _Insertion_sort_unchecked(_First, _Last, _Pred); // small } else { // sort halves and merge - const auto _Half_count = _Count >> 1; + const auto _Half_count = static_cast<_Diff>(_Count >> 1); const auto _Half_count_ceil = static_cast<_Diff>(_Count - _Half_count); const _BidIt _Mid = _STD next(_First, _Half_count_ceil); if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer From 464e3936c9ed54815b5be0e2ec4f1e0afaf05cc9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 22 Jan 2021 11:35:04 -0800 Subject: [PATCH 16/20] Some ranges::stable_sort cleanup: * `_Merge_move_to_buffer` shouldn't use `_Uninitialized_backout`; doing so could result in this function destroying a range of objects in the middle of the temporary buffer on exception from `iter_swap`. * `_Merge_move_to_buffer` shouldn't be using `iter_swap` anyway since we don't care about the pre-existing values in the destination range; it should indirectly_moving a la `*out = ranges::iter_move(in)`. * With the above changes, `_Merge_move_to_buffer` and `_Merge_move_from_buffer` can be refactored into a single function `_Merge_move_common`. * .`_Chunked_merge_to_buffer` and `_Chunked_merge_from_buffer` are then identical, and can be refactored into a single function `_Chunked_merge_common`. --- stl/inc/algorithm | 118 ++++++++++++---------------------------------- 1 file changed, 30 insertions(+), 88 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e617c21e5cf..c10f918b339 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -8163,7 +8163,7 @@ namespace ranges { // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + _STL_INTERNAL_CHECK(_Last - _First == _Count); _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred, _Proj); // merge adjacent pairs of chunks to and from temp buffer @@ -8178,20 +8178,20 @@ namespace ranges { for (;;) { // unconditionally merge elements back into the source buffer _Chunk_size <<= 1; - _Chunked_merge_from_buffer(_Temp_ptr, _Temp_ptr + _Count, _First, _Chunk_size, _Count, _Pred, _Proj); + _Chunked_merge_common(_Temp_ptr, _Temp_ptr + _Count, _First, _Chunk_size, _Count, _Pred, _Proj); _Chunk_size <<= 1; if (_Count <= _Chunk_size) { // if the input would be a single chunk, it's already sorted and we're done return; } // more merges necessary; merge to temporary buffer - _Chunked_merge_to_buffer(_First, _Last, _Temp_ptr, _Chunk_size, _Count, _Pred, _Proj); + _Chunked_merge_common(_First, _Last, _Temp_ptr, _Chunk_size, _Count, _Pred, _Proj); } } template static void _Insertion_sort_isort_max_chunks( - _It _First, const _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + _It _First, _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { // insertion sort every chunk of distance _Isort_max<_It> in [_First, _Last) _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); @@ -8211,6 +8211,7 @@ namespace ranges { // move to uninitialized merging adjacent chunks of distance _Chunk _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); _Uninitialized_backout*> _Backout{_Dest}; @@ -8228,7 +8229,7 @@ namespace ranges { _First = _STD move(_Last1); } - // copy partial last chunk + // move partial last chunk _RANGES _Uninitialized_move_unchecked(_STD move(_First), _STD move(_Last), _Backout._Last, _Backout_end); _Backout._Release(); } @@ -8237,11 +8238,11 @@ namespace ranges { _NODISCARD static iter_value_t<_It>* _Uninitialized_merge_move(_It _First, _It _Mid, _It _Last, iter_value_t<_It>* const _Dest, iter_value_t<_It>* const _Dest_last, _Pr _Pred, _Pj _Proj) { // move merging ranges to uninitialized storage - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); - _STL_INTERNAL_CHECK(_Last - _First <= _Dest_last - _Dest); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) <= _RANGES distance(_Dest, _Dest_last)); _Uninitialized_backout*> _Backout{_Dest}; _It _Next = _Mid; @@ -8270,73 +8271,45 @@ namespace ranges { } } - template - static void _Chunked_merge_to_buffer(_It _First, const _It _Last, iter_value_t<_It>* _Dest, - const iter_difference_t<_It> _Chunk_size, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { - // move merging adjacent chunks of distance _Chunk_size to temporary buffer - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); - _STL_INTERNAL_CHECK(_Chunk_size > 0); - - while (_Chunk_size < _Count) { - _Count -= _Chunk_size; - const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); - _Count -= _Right_chunk_size; - - auto _Mid1 = _First + _Chunk_size; - auto _Last1 = _Mid1 + _Right_chunk_size; - _Dest = _Merge_move_to_buffer(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); - _First = _STD move(_Last1); - } - - // copy partial last chunk - _RANGES _Move_unchecked(_STD move(_First), _STD move(_Last), _STD move(_Dest)); - } - - template - _NODISCARD static iter_value_t<_It>* _Merge_move_to_buffer( - _It _First, _It _Mid, _It _Last, iter_value_t<_It>* const _Dest, _Pr _Pred, _Pj _Proj) { - // move merging ranges to temporary storage - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + template + _NODISCARD static _OutIt _Merge_move_common( + _InIt _First, _InIt _Mid, _InIt _Last, _OutIt _Dest, _Pr _Pred, _Pj _Proj) { + // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to _Dest + _STL_INTERNAL_STATIC_ASSERT(sortable<_InIt, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_InIt, _OutIt>); _STL_INTERNAL_CHECK(_First != _Mid); _STL_INTERNAL_CHECK(_Mid != _Last); - _Uninitialized_backout*> _Backout{_Dest}; - _It _Next = _Mid; + _InIt _Next = _Mid; for (;;) { if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { - _RANGES iter_swap(_Backout._Last, _Next); - ++_Backout._Last; + *_Dest = _RANGES iter_move(_Next); + ++_Dest; ++_Next; if (_Next == _Last) { - _Backout._Last = - _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _Backout._Last).out; - return _Backout._Release(); + return _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Dest)).out; } } else { - _RANGES iter_swap(_Backout._Last, _First); - ++_Backout._Last; + *_Dest = _RANGES iter_move(_First); + ++_Dest; ++_First; if (_First == _Mid) { - _Backout._Last = - _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _Backout._Last).out; - return _Backout._Release(); + return _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _STD move(_Dest)).out; } } } } - template - static void _Chunked_merge_from_buffer(iter_value_t<_It>* _First, iter_value_t<_It>* _Last, _It _Dest, - const iter_difference_t<_It> _Chunk_size, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { - // move merging adjacent chunks of distance _Chunk_size from temporary buffer - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + template + static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, + const iter_difference_t<_It1> _Chunk_size, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj _Proj) { + // move merging adjacent chunks of distance _Chunk_size + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It1, _Pr, _Pj>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_It1, _It2>); + _STL_INTERNAL_CHECK(_Last - _First == _Count); _STL_INTERNAL_CHECK(_Chunk_size > 0); while (_Chunk_size < _Count) { @@ -8346,44 +8319,13 @@ namespace ranges { auto _Mid1 = _First + _Chunk_size; auto _Last1 = _Mid1 + _Right_chunk_size; - _Dest = _Merge_move_from_buffer(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); + _Dest = _Merge_move_common(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); _First = _STD move(_Last1); } // copy partial last chunk _RANGES _Move_unchecked(_STD move(_First), _STD move(_Last), _STD move(_Dest)); } - - template - _NODISCARD static _It _Merge_move_from_buffer(iter_value_t<_It>* _First, iter_value_t<_It>* _Mid, - iter_value_t<_It>* _Last, _It _Dest, _Pr _Pred, _Pj _Proj) { - // move merging adjacent ranges [_First, _Mid) and [_Mid, _Last) to _Dest - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - _STL_INTERNAL_CHECK(_First != _Mid); - _STL_INTERNAL_CHECK(_Mid != _Last); - - iter_value_t<_It>* _Next = _Mid; - for (;;) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { - _RANGES iter_swap(_Dest, _Next); - ++_Dest; - ++_Next; - - if (_Next == _Last) { - return _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Dest)).out; - } - } else { - _RANGES iter_swap(_Dest, _First); - ++_Dest; - ++_First; - - if (_First == _Mid) { - return _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _STD move(_Dest)).out; - } - } - } - } }; inline constexpr _Stable_sort_fn stable_sort{_Not_quite_object::_Construct_tag{}}; From 263c9c152bede9792ea44d353fec8e0ba99df847 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 25 Jan 2021 12:48:10 -0800 Subject: [PATCH 17/20] Apply suggestions from code review Cleanup comment copypasta --- tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp | 2 +- tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp index ac8b9c2d650..6b82ef98742 100644 --- a/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp @@ -37,7 +37,7 @@ struct instantiator { assert(empty_result == empty_range.begin()); } - { // Validate iterator overloads of inplace_merge, is_partitioned, partition_point + { // Validate iterator overload P input[] = {P{0, 1}, P{4, 2}, P{7, 3}, P{8, 4}, P{0, 5}, P{4, 6}, P{6, 7}, P{9, 8}, P{10, 9}}; Range range{input}; const auto mid = ranges::next(range.begin(), 4); diff --git a/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp index 3686b3d27ad..aa50555f167 100644 --- a/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp @@ -41,7 +41,7 @@ struct instantiator { assert(empty_result.end() == empty_range.end()); } - { // Validate iterator overloads of stable_partition, is_partitioned, partition_point + { // Validate iterator overload P input[] = {P{0, 1}, P{1, 2}, P{0, 3}, P{1, 4}, P{0, 5}, P{1, 6}, P{0, 7}, P{1, 8}}; Range range{input}; const auto mid = ranges::next(range.begin(), 4); From 6e0a59537dbfe7bb31219bbf03418a73b0b7d5c8 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 25 Jan 2021 20:15:28 -0800 Subject: [PATCH 18/20] Simple code review feedback. --- stl/inc/algorithm | 26 +++++++++---------- .../P0896R4_ranges_alg_stable_sort/test.cpp | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c10f918b339..e9c14ce8f97 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -5534,7 +5534,7 @@ template _BidIt _Stable_partition_unchecked(_BidIt _First, _BidIt _Last, _Pr _Pred) { // partition preserving order of equivalents for (;;) { - if (_First == _Last) { // the input range range is true (already partitioned) + if (_First == _Last) { // the input range is true (already partitioned) return _First; } @@ -5657,9 +5657,10 @@ namespace ranges { // partition preserving order of equivalents for (;;) { // skip in-place elements at front - if (_First == _Last) { // the input range range is true (already partitioned) + if (_First == _Last) { // the input range is true (already partitioned) return {_STD move(_First), _STD move(_Last)}; } + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { break; } @@ -7171,12 +7172,12 @@ namespace ranges { using _Ty = iter_value_t<_It>; _Ty* _Left_last = _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Left_first, _Left_first + _Capacity).out; - _Uninitialized_backout<_Ty*> _Backout(_Left_first, _Left_last); + _Uninitialized_backout<_Ty*> _Backout{_Left_first, _Left_last}; - // we already know, that _Backout._Last - 1 is the highest element so do not compare against it again. + // We already know that _Backout._Last - 1 is the highest element, so do not compare against it again. --_Left_last; - // we already know, that _Mid points to the lowest element and that there is more than 1 element left. + // We already know that _Mid points to the lowest element and that there is more than 1 element left. *_First = _RANGES iter_move(_Mid); ++_First; ++_Mid; @@ -7217,12 +7218,12 @@ namespace ranges { _Ty* _Right_last = _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Right_first, _Right_first + _Capacity).out; - _Uninitialized_backout<_Ty*> _Backout(_Right_first, _Right_last); + _Uninitialized_backout<_Ty*> _Backout{_Right_first, _Right_last}; - // we already know, that _Mid points to the next highest element and that there is more than 1 element left. + // We already know that _Mid points to the next highest element and that there is more than 1 element left. *--_Last = _RANGES iter_move(--_Mid); - // we already know, that _Backout._Last - 1 is the highest element so do not compare against it again. + // We already know that _Backout._Last - 1 is the highest element, so do not compare against it again. --_Mid; --_Right_last; for (;;) { @@ -7251,8 +7252,7 @@ namespace ranges { template requires sortable<_It, _Pr, _Pj> void _Buffered_inplace_merge_common(_It _First, _It _Mid, _It _Last, iter_difference_t<_It> _Count1, - iter_difference_t<_It> _Count2, iter_value_t<_It>* const _Temp_ptr, const ptrdiff_t _Capacity, _Pr _Pred, - _Pj _Proj); + iter_difference_t<_It> _Count2, iter_value_t<_It>* _Temp_ptr, ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj); // clang-format on // clang-format off @@ -7417,7 +7417,7 @@ namespace ranges { _STL_VERIFY(_RANGES _Is_sorted_until_unchecked(_First, _Mid, _Pred, _Proj) == _Mid, "ranges::inplace_merge requires the range [first, middle) to be sorted"); _STL_VERIFY(_RANGES _Is_sorted_until_unchecked(_Mid, _Last, _Pred, _Proj) == _Last, - "ranges::inplace_merge requires the range [middle, last)] to be sorted"); + "ranges::inplace_merge requires the range [middle, last) to be sorted"); #endif //_ITERATOR_DEBUG_LEVEL == 2 // Find first element in [_First, _Mid) that is not less than *_Mid @@ -8020,7 +8020,7 @@ void _Stable_sort_unchecked(const _BidIt _First, const _BidIt _Last, const _Iter if (_Count <= _ISORT_MAX) { _Insertion_sort_unchecked(_First, _Last, _Pred); // small } else { // sort halves and merge - const auto _Half_count = static_cast<_Diff>(_Count >> 1); + const auto _Half_count = static_cast<_Diff>(_Count >> 1); // shift for codegen const auto _Half_count_ceil = static_cast<_Diff>(_Count - _Half_count); const _BidIt _Mid = _STD next(_First, _Half_count_ceil); if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer @@ -8140,7 +8140,7 @@ namespace ranges { if (_Count <= _Isort_max<_It>) { _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); } else { // sort halves and merge - const iter_difference_t<_It> _Half_count = _Count >> 1; + const iter_difference_t<_It> _Half_count = _Count >> 1; // shift for codegen const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count; const _It _Mid = _First + _Half_count_ceil; if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer diff --git a/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp index 4f5f0832ff8..f20095aa55b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_stable_sort/test.cpp @@ -30,7 +30,7 @@ struct instantiator { const R range{buff}; const same_as> auto result = stable_sort(range, less{}, get_first); assert(result == range.end()); - assert(is_sorted(range)); // No projection to check for stability + assert(is_sorted(range)); // Check for stability by not using a projection } { // Validate iterator overload @@ -38,7 +38,7 @@ struct instantiator { const R range{buff}; const same_as> auto result = stable_sort(range.begin(), range.end(), less{}, get_first); assert(result == range.end()); - assert(is_sorted(range.begin(), range.end())); // No projection to check for stability + assert(is_sorted(range.begin(), range.end())); // Check for stability by not using a projection } { // Validate empty range From 8bc2d187c063bce51e3feac8382253fa00d5331b Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 26 Jan 2021 15:09:17 -0800 Subject: [PATCH 19/20] Review comments again: * Promote `_Isort_max` to namespace `std`; use it where appropriate in `` and ``. * Don't say "not less than" when we mean "greater than". * Refactor duplicate code into a `do`-`while` loop in `ranges::_Buffered_inplace_merge_common`. * Constant-propagate the value of `_Chunk` (`_Isort_max<_BidIt>`) through the body of `_Uninitialized_chunked_merge_unchecked`, remove the parameter, and bump the name to `_Uninitialized_chunked_merge_unchecked2` for ABI. --- stl/inc/algorithm | 51 ++++++++++++++++++++--------------------------- stl/inc/execution | 23 ++++++++++----------- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e9c14ce8f97..be003e41cf8 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -41,6 +41,9 @@ _STD_BEGIN // COMMON SORT PARAMETERS _INLINE_VAR constexpr int _ISORT_MAX = 32; // maximum size for insertion sort +template +_INLINE_VAR constexpr auto _Isort_max = _Iter_diff_t<_It>{_ISORT_MAX}; + // STRUCT TEMPLATE _Optimistic_temporary_buffer template constexpr ptrdiff_t _Temporary_buffer_size(const _Diff _Value) noexcept { @@ -7316,7 +7319,7 @@ namespace ranges { return; } - // Find first element in [_First, _Mid) that is not less than *_Mid + // Find first element in [_First, _Mid) that is greater than *_Mid while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { --_Count1; if (++_First == _Mid) { @@ -7324,24 +7327,17 @@ namespace ranges { } } - // Fast early return if there is only one element to be moved - if (--_Last == _Mid) { - // rotate only element remaining in right partition to the beginning, without allocating - _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); - return; - } - --_Count2; - // Find last element in [_Mid, _Last) that is less than *--_Mid const auto _Highest = _RANGES prev(_Mid); - while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))) { + do { + // Fast early return if there is only one element to be moved if (_Mid == --_Last) { // rotate only element remaining in right partition to the beginning, without allocating _RANGES _Rotate_one_right(_STD move(_First), _STD move(_Mid), _STD move(++_Last)); return; } --_Count2; - } + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Highest))); ++_Last; ++_Count2; @@ -7420,7 +7416,7 @@ namespace ranges { "ranges::inplace_merge requires the range [middle, last) to be sorted"); #endif //_ITERATOR_DEBUG_LEVEL == 2 - // Find first element in [_First, _Mid) that is not less than *_Mid + // Find first element in [_First, _Mid) that is greater than *_Mid while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { if (++_First == _Mid) { return; @@ -7931,16 +7927,16 @@ _OutIt _Merge_move(_InIt _First, const _InIt _Mid, const _InIt _Last, _OutIt _De } template -void _Uninitialized_chunked_merge_unchecked(_BidIt _First, const _BidIt _Last, _Ty* _Dest, - const _Iter_diff_t<_BidIt> _Chunk, _Iter_diff_t<_BidIt> _Count, _Pr _Pred) { - // move to uninitialized merging adjacent chunks of distance _Chunk +void _Uninitialized_chunked_merge_unchecked2( + _BidIt _First, const _BidIt _Last, _Ty* _Dest, _Iter_diff_t<_BidIt> _Count, _Pr _Pred) { + // move to uninitialized merging adjacent chunks of distance _Isort_max<_BidIt> // pre: _Count == distance(_First, _Last) // pre: _Chunk > 0 _Uninitialized_backout<_Ty*> _Backout{_Dest}; - while (_Chunk < _Count) { - _Count -= _Chunk; - const _BidIt _Mid1 = _STD next(_First, _Chunk); - const auto _Chunk2 = (_STD min)(_Chunk, _Count); + while (_Count > _Isort_max<_BidIt>) { + _Count -= _Isort_max<_BidIt>; + const _BidIt _Mid1 = _STD next(_First, _Isort_max<_BidIt>); + const auto _Chunk2 = (_STD min)(_Isort_max<_BidIt>, _Count); _Count -= _Chunk2; const _BidIt _Mid2 = _STD next(_Mid1, _Chunk2); _Backout._Last = _Uninitialized_merge_move(_First, _Mid1, _Mid2, _Backout._Last, _Pred); @@ -7972,10 +7968,10 @@ void _Chunked_merge_unchecked(_BidIt _First, const _BidIt _Last, _OutIt _Dest, c template void _Insertion_sort_isort_max_chunks(_BidIt _First, const _BidIt _Last, _Iter_diff_t<_BidIt> _Count, _Pr _Pred) { - // insertion sort every chunk of distance _ISORT_MAX in [_First, _Last) + // insertion sort every chunk of distance _Isort_max<_BidIt> in [_First, _Last) // pre: _Count == distance(_First, _Last) - for (; _ISORT_MAX < _Count; _Count -= _ISORT_MAX) { // sort chunks - _First = _Insertion_sort_unchecked(_First, _STD next(_First, _ISORT_MAX), _Pred); + for (; _Isort_max<_BidIt> < _Count; _Count -= _Isort_max<_BidIt>) { // sort chunks + _First = _Insertion_sort_unchecked(_First, _STD next(_First, _Isort_max<_BidIt>), _Pred); } _Insertion_sort_unchecked(_First, _Last, _Pred); // sort partial last chunk @@ -7989,14 +7985,14 @@ void _Buffered_merge_sort_unchecked(const _BidIt _First, const _BidIt _Last, con // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred); // merge adjacent pairs of chunks to and from temp buffer - auto _Chunk = static_cast<_Iter_diff_t<_BidIt>>(_ISORT_MAX); - if (_Count <= _Chunk) { + if (_Count <= _Isort_max<_BidIt>) { return; } // do the first merge, constructing elements in the temporary buffer - _Uninitialized_chunked_merge_unchecked(_First, _Last, _Temp_ptr, _Chunk, _Count, _Pred); + _Uninitialized_chunked_merge_unchecked2(_First, _Last, _Temp_ptr, _Count, _Pred); _Uninitialized_backout<_Iter_value_t<_BidIt>*> _Backout{_Temp_ptr, _Temp_ptr + _Count}; + auto _Chunk = _Isort_max<_BidIt>; for (;;) { // unconditionally merge elements back into the source buffer _Chunk <<= 1; @@ -8107,9 +8103,6 @@ namespace ranges { } private: - template - static constexpr auto _Isort_max = static_cast>(_ISORT_MAX); - template static void _Stable_sort_common( _It _First, _It _Last, const iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { @@ -8208,7 +8201,7 @@ namespace ranges { template static void _Uninitialized_chunked_merge_common(_It _First, const _It _Last, iter_value_t<_It>* const _Dest, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { - // move to uninitialized merging adjacent chunks of distance _Chunk + // move to uninitialized merging adjacent chunks of distance _Isort_max<_It> _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_rvalue_reference_t<_It>>); diff --git a/stl/inc/execution b/stl/inc/execution index b1d73575657..7953f238a2a 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -2665,24 +2665,23 @@ bool _Process_sort_work_item(const _RanIt _Basis, _Pr _Pred, _Sort_work_item<_Ra // the return value is false // _Wi's range is completely sorted // _Right_fork_wi is unmodified - using _Diff = _Iter_diff_t<_RanIt>; - constexpr auto _Diffsort_max = static_cast<_Diff>(_ISORT_MAX); - const auto _Size = _Wi._Size; - const auto _First = _Basis + _Wi._Offset; - const auto _Last = _First + _Size; - const auto _Ideal = _Wi._Ideal; - if (_Size <= _Diffsort_max) { + const auto _Size = _Wi._Size; + const auto _First = _Basis + _Wi._Offset; + const auto _Last = _First + _Size; + const auto _Ideal = _Wi._Ideal; + if (_Size <= _Isort_max<_RanIt>) { _Insertion_sort_unchecked(_First, _Last, _Pred); _Work_complete += _Size; return false; } if (0 < _Ideal) { // divide and conquer by partitioning (quicksort) - const auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred); - const auto _New_ideal = static_cast<_Diff>(_Ideal / 2 + _Ideal / 4); // allow 1.5 log2(N) divisions - _Wi._Size = _Mid.first - _First; - _Wi._Ideal = _New_ideal; - _Right_fork_wi = {_Mid.second - _Basis, _Last - _Mid.second, _New_ideal}; + const auto _Mid = _Partition_by_median_guess_unchecked(_First, _Last, _Pred); + const auto _New_ideal = + static_cast<_Iter_diff_t<_RanIt>>(_Ideal / 2 + _Ideal / 4); // allow 1.5 log2(N) divisions + _Wi._Size = _Mid.first - _First; + _Wi._Ideal = _New_ideal; + _Right_fork_wi = {_Mid.second - _Basis, _Last - _Mid.second, _New_ideal}; _Work_complete += _Mid.second - _Mid.first; return true; } From 9e178a2b6c97719ad863e4d675e46e5a7e507c7a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 26 Jan 2021 15:11:28 -0800 Subject: [PATCH 20/20] Apply suggestions from code review Say "Validate empty range" consistently instead of sometimes "ranges". --- tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp | 2 +- tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp index 6b82ef98742..9cf822b5dcb 100644 --- a/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_inplace_merge/test.cpp @@ -46,7 +46,7 @@ struct instantiator { assert(result == range.end()); assert(equal(input, expected)); - // Validate empty ranges + // Validate empty range const Range empty_range{}; const same_as> auto empty_result = inplace_merge(empty_range.begin(), empty_range.begin(), empty_range.end(), ranges::less{}, get_first); diff --git a/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp index aa50555f167..cc9cd58ee15 100644 --- a/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_stable_partition/test.cpp @@ -52,7 +52,7 @@ struct instantiator { assert(is_partitioned(range, is_even, get_first)); assert(is_sorted(range)); - // Validate empty ranges + // Validate empty range const Range empty_range{}; const same_as>> auto empty_result = stable_partition(empty_range.begin(), empty_range.end(), is_even, get_first);