diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8eb7c131ae2..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 { @@ -1697,6 +1700,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; @@ -1705,39 +1721,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{}}; @@ -1751,7 +1755,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); @@ -4617,74 +4621,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 (_First == _Mid) { - auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); - return {_Final, _Final}; - } + if (_Mid == _Last) { + return {_STD move(_First), _STD move(_Mid)}; + } - if (_Mid == _Last) { - return {_STD move(_First), _STD move(_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 (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; - 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; @@ -4692,36 +4693,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)); + } + + // 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 {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_Mid, _STD move(_UResult)); } }; @@ -5537,7 +5537,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; } @@ -5583,6 +5583,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 + _STL_INTERNAL_CHECK(_Count1 == _RANGES distance(_First, _Mid)); + _STL_INTERNAL_CHECK(_Count2 == _RANGES distance(_Mid, _Last)); + + if (_Count1 == 0) { + return _Last; + } + + if (_Count2 == 0) { + return _First; + } + + 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; + _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _New_mid); + return _New_mid; + } + + 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)); + return _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _STD move(_First)).out; + } + + // buffer too small, rotate in place + return _RANGES _Rotate_unchecked(_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 + for (;;) { // skip in-place elements at front + 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; + } + ++_First; + } + + auto _Saved_last = _Last; + 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 auto _Total_count = static_cast>(_Temp_count + 1); + 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)}; + } + + 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) + // note: _Count >= 2 and _First != _Last + _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 + _Uninitialized_backout*> _Backout{_Temp_ptr}; + _It _Next = _First; + _Backout._Emplace_back(_RANGES iter_move(_First)); + while (++_First != _Last) { + // 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); + ++_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 auto _True_distance = static_cast<_Diff>(_Count - (_Backout._Last - _Backout._First)); + return {_STD move(_Next), _True_distance}; + } + + 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; + _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 + 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; + } + } + + // 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 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( + _STD move(_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 + 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 {_STD move(_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( @@ -6921,50 +7124,392 @@ void inplace_merge(_ExPo&&, _BidIt _First, _BidIt _Mid, _BidIt _Last) noexcept / } #endif // _HAS_CXX17 -// FUNCTION TEMPLATE sort -template -_CONSTEXPR20 _BidIt _Insertion_sort_unchecked(const _BidIt _First, const _BidIt _Last, _Pr _Pred) { - // insertion sort [_First, _Last) - if (_First != _Last) { - for (_BidIt _Mid = _First; ++_Mid != _Last;) { // order next element - _BidIt _Hole = _Mid; - _Iter_value_t<_BidIt> _Val = _STD move(*_Mid); - - if (_DEBUG_LT_PRED(_Pred, _Val, *_First)) { // found new earliest element, move to front - _Move_backward_unchecked(_First, _Mid, ++_Hole); - *_First = _STD move(_Val); - } else { // look for insertion point after first - for (_BidIt _Prev = _Hole; _DEBUG_LT_PRED(_Pred, _Val, *--_Prev); _Hole = _Prev) { - *_Hole = _STD move(*_Prev); // move hole down - } +#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; + } - *_Hole = _STD move(_Val); // insert element in hole + for (auto _Prev = _First; ++_First != _Last; ++_Prev) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Prev))) { + break; } } + + return _First; } - return _Last; -} + // VARIABLE ranges::inplace_merge + template + void _Rotate_one_right(_It _First, _It _Mid, _It _Last) { + // exchanges the range [_First, _Mid) with [_Mid, _Last) + _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); + } -template -_CONSTEXPR20 void _Med3_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { - // sort median of three elements to middle - if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { - _STD iter_swap(_Mid, _First); + template + 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); } - if (_DEBUG_LT_PRED(_Pred, *_Last, *_Mid)) { // swap middle and last, then test first again - _STD iter_swap(_Last, _Mid); + // 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>; - if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { - _STD iter_swap(_Mid, _First); - } - } -} + _Ty* _Left_last = _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Left_first, _Left_first + _Capacity).out; + _Uninitialized_backout<_Ty*> _Backout{_Left_first, _Left_last}; -template -_CONSTEXPR20 void _Guess_median_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { - // sort median element to middle + // 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) { + ++_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 + ++_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_common(_It _First, _It _Mid, _It _Last, iter_difference_t<_It> _Count1, + iter_difference_t<_It> _Count2, iter_value_t<_It>* _Temp_ptr, ptrdiff_t _Capacity, _Pr _Pred, _Pj _Proj); + // clang-format on + + // 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 + _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), + _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 + _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), + _Count1, _Count2, _Temp_ptr, _Capacity, _Pred, _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 greater than *_Mid + while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + --_Count1; + if (++_First == _Mid) { + return; + } + } + + // Find last element in [_Mid, _Last) that is less than *--_Mid + const auto _Highest = _RANGES prev(_Mid); + 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; + + 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, _Pred, _Proj); + } else if (_Count2 <= _Capacity) { + _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, _Pred, _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_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"); +#endif //_ITERATOR_DEBUG_LEVEL == 2 + + // 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; + } + } + + // 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 = _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 + _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, _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, _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, _Pred, _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) { + // insertion sort [_First, _Last) + if (_First != _Last) { + for (_BidIt _Mid = _First; ++_Mid != _Last;) { // order next element + _BidIt _Hole = _Mid; + _Iter_value_t<_BidIt> _Val = _STD move(*_Mid); + + if (_DEBUG_LT_PRED(_Pred, _Val, *_First)) { // found new earliest element, move to front + _Move_backward_unchecked(_First, _Mid, ++_Hole); + *_First = _STD move(_Val); + } else { // look for insertion point after first + for (_BidIt _Prev = _Hole; _DEBUG_LT_PRED(_Pred, _Val, *--_Prev); _Hole = _Prev) { + *_Hole = _STD move(*_Prev); // move hole down + } + + *_Hole = _STD move(_Val); // insert element in hole + } + } + } + + return _Last; +} + +template +_CONSTEXPR20 void _Med3_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { + // sort median of three elements to middle + if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { + _STD iter_swap(_Mid, _First); + } + + if (_DEBUG_LT_PRED(_Pred, *_Last, *_Mid)) { // swap middle and last, then test first again + _STD iter_swap(_Last, _Mid); + + if (_DEBUG_LT_PRED(_Pred, *_Mid, *_First)) { + _STD iter_swap(_Mid, _First); + } + } +} + +template +_CONSTEXPR20 void _Guess_median_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred) { + // sort median element to middle using _Diff = _Iter_diff_t<_RanIt>; const _Diff _Count = _Last - _First; if (40 < _Count) { // Tukey's ninther @@ -7108,12 +7653,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 @@ -7133,6 +7678,7 @@ namespace ranges { *_Hole = _STD move(_Val); // insert element in hole } + return _Last; } // clang-format off @@ -7381,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); @@ -7422,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 @@ -7439,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; @@ -7470,7 +8016,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 = 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 @@ -7520,6 +8066,265 @@ 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 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>); + _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); + 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, _Pred, _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>); + _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); + } else { // sort halves and merge + 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 + _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, _Pred, _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(_Last - _First == _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_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_common(_First, _Last, _Temp_ptr, _Chunk_size, _Count, _Pred, _Proj); + } + } + + template + static void _Insertion_sort_isort_max_chunks( + _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>); + _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), _Pred, _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 _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>>); + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + + _Uninitialized_backout*> _Backout{_Dest}; + 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; + + 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); + } + + // move 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(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(_RANGES distance(_First, _Last) <= _RANGES distance(_Dest, _Dest_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 + _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); + + _InIt _Next = _Mid; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_First))) { + *_Dest = _RANGES iter_move(_Next); + ++_Dest; + ++_Next; + + if (_Next == _Last) { + return _RANGES _Move_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_Dest)).out; + } + } else { + *_Dest = _RANGES iter_move(_First); + ++_Dest; + ++_First; + + if (_First == _Mid) { + return _RANGES _Move_unchecked(_STD move(_Next), _STD move(_Last), _STD move(_Dest)).out; + } + } + } + } + + 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) { + _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_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)); + } + }; + + 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) { @@ -7917,7 +8722,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); } }; @@ -9533,26 +10338,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/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; } diff --git a/stl/inc/memory b/stl/inc/memory index beac884f890..279effbcc6b 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>; @@ -214,10 +185,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: @@ -231,9 +198,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)); @@ -247,35 +214,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 38209d5c9cd..65f4ad2b209 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1439,6 +1439,64 @@ _NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _No } } +#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>> + 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 _NODISCARD _Uninitialized_backout_al { diff --git a/tests/std/test.lst b/tests/std/test.lst index 014d2c798d4..96be4a7ca1f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -287,6 +287,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 @@ -324,6 +325,8 @@ 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_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_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..9cf822b5dcb --- /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 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.begin(), mid, range.end(), 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.begin(), empty_range.begin(), empty_range.end(), ranges::less{}, get_first); + assert(empty_result == empty_range.end()); + } + } +}; + +int main() { + test_bidi(); +} 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..cc9cd58ee15 --- /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 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.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 range + 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(); +} 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..f20095aa55b --- /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)); // Check for stability by not using a projection + } + + { // 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())); // Check for stability by not using a projection + } + + { // 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(); +}