diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 6b93938c314..614594ed68e 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4845,7 +4845,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); _Seek_wrapped(_First, _ULast); - _RANGES _Reverse_common(_STD move(_UFirst), _STD move(_ULast)); + _Reverse_common(_STD move(_UFirst), _STD move(_ULast)); return _First; } @@ -4853,7 +4853,7 @@ namespace ranges { requires permutable> constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const { auto _ULast = _Get_final_iterator_unwrapped(_Range); - _RANGES _Reverse_common(_Ubegin(_Range), _ULast); + _Reverse_common(_Ubegin(_Range), _ULast); return _Rewrap_iterator(_Range, _STD move(_ULast)); } // clang-format on @@ -4920,6 +4920,119 @@ _FwdIt reverse_copy(_ExPo&&, _BidIt _First, _BidIt _Last, _FwdIt _Dest) noexcept } #endif // _HAS_CXX17 +#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 _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))); + + 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 = _Rotate_unchecked(_Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range)); + + return _Rewrap_subrange>(_Mid, _STD move(_UResult)); + } + + 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 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 { + auto [_Mid_first, _Mid_last] = _Reverse_until_mid_unchecked(_STD move(_First), _Mid, _Final); + _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)}; + } + } + } 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; + if (_First == _Mid) { + _Mid = _Next; + } + } while (_Next != _Last); + } + 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); + + do { + _RANGES iter_swap(_First, --_Last); + } while (++_First != _Mid && _Last != _Mid); + + return {_STD move(_First), _STD move(_Last)}; + } + }; + + inline constexpr _Rotate_fn rotate{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE rotate_copy template _CONSTEXPR20 _OutIt rotate_copy(_FwdIt _First, _FwdIt _Mid, _FwdIt _Last, _OutIt _Dest) { @@ -4944,6 +5057,65 @@ _FwdIt2 rotate_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Mid, _FwdIt1 _Last, _FwdIt return _STD rotate_copy(_First, _Mid, _Last, _Dest); } +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE rotate_copy_result + template + using rotate_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::rotate_copy + class _Rotate_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + constexpr rotate_copy_result<_It, _Out> operator()(_It _First, _It _Mid, _Se _Last, _Out _Result) const { + // clang-format on + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + auto _UResult = _Rotate_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), + _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_copyable, _Out> + constexpr rotate_copy_result, _Out> operator()( + _Rng&& _Range, iterator_t<_Rng> _Mid, _Out _Result) const { + // clang-format on + _Adl_verify_range(_RANGES begin(_Range), _Mid); + _Adl_verify_range(_Mid, _RANGES end(_Range)); + auto _UResult = _Rotate_copy_unchecked( + _Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range), _STD move(_Result)); + + return {_Rewrap_iterator(_Range, _STD move(_UResult.in)), _STD move(_UResult.out)}; + } + + private: + template + _NODISCARD static constexpr rotate_copy_result<_It, _Out> _Rotate_copy_unchecked( + _It _First, _It _Mid, _Se _Last, _Out _Result) { + // Copy the content of [_Mid, _Last) and [_First, _Mid) to _Result + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + + auto _UResult1 = _RANGES _Copy_unchecked(_Mid, _STD move(_Last), _STD move(_Result)); + auto _UResult2 = _RANGES _Copy_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_UResult1.out)); + return {_STD move(_UResult1.in), _STD move(_UResult2.out)}; + } + }; + + inline constexpr _Rotate_copy_fn rotate_copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE sample template _SampleIt _Sample_reservoir_unchecked( diff --git a/tests/std/test.lst b/tests/std/test.lst index 12dbf4d64a4..36cc83389f7 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -279,6 +279,8 @@ tests\P0896R4_ranges_alg_replace_copy tests\P0896R4_ranges_alg_replace_copy_if tests\P0896R4_ranges_alg_replace_if tests\P0896R4_ranges_alg_reverse +tests\P0896R4_ranges_alg_rotate +tests\P0896R4_ranges_alg_rotate_copy tests\P0896R4_ranges_alg_sample tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_alg_search_n diff --git a/tests/std/tests/P0896R4_ranges_alg_rotate/env.lst b/tests/std/tests/P0896R4_ranges_alg_rotate/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_rotate/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_rotate/test.cpp b/tests/std/tests/P0896R4_ranges_alg_rotate/test.cpp new file mode 100644 index 00000000000..511a83c0b98 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_rotate/test.cpp @@ -0,0 +1,52 @@ +// 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)), ranges::subrange>); + +struct instantiator { + static constexpr P expected[5] = {{3, 47}, {4, 99}, {0, 99}, {1, 47}, {2, 99}}; + + template + static constexpr void call() { + using ranges::rotate, ranges::subrange, ranges::equal, ranges::iterator_t; + { // Validate iterator overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + ReadWrite wrapped_input{input}; + + auto result = rotate(wrapped_input.begin(), next(wrapped_input.begin(), 3), wrapped_input.end()); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 2)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, input)); + } + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + ReadWrite wrapped_input{input}; + + auto result = rotate(wrapped_input, next(wrapped_input.begin(), 3)); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 2)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, input)); + } + } +}; + +int main() { +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163 + STATIC_ASSERT((test_fwd(), true)); +#endif // TRANSITION, VSO-938163 + test_fwd(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_rotate_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_rotate_copy/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_rotate_copy/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_rotate_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_rotate_copy/test.cpp new file mode 100644 index 00000000000..a28336063ae --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_rotate_copy/test.cpp @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include +using namespace std; + +// Validate that rotate_copy_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, nullptr_to, nullptr_to)), + ranges::rotate_copy_result>); +STATIC_ASSERT(same_as{}, nullptr_to, nullptr_to)), + ranges::rotate_copy_result>); + +struct instantiator { + static constexpr int input[5] = {1, 2, 3, 4, 5}; + static constexpr int expected[5] = {4, 5, 1, 2, 3}; + + template > Write> + static constexpr void call() { + using ranges::rotate_copy, ranges::rotate_copy_result, ranges::equal, ranges::iterator_t; + { // Validate iterator overload + int output[5] = {-1, -1, -1, -1, -1}; + Read wrapped_input{input}; + iterator_t mid = next(wrapped_input.begin(), 3); + + auto result = rotate_copy(wrapped_input.begin(), mid, wrapped_input.end(), Write{output}); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(expected, output)); + } + { // Validate range overload + int output[5] = {-1, -1, -1, -1, -1}; + Read wrapped_input{input}; + iterator_t mid = next(wrapped_input.begin(), 3); + + auto result = rotate_copy(wrapped_input, mid, Write{output}); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == end(output)); + assert(equal(expected, output)); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd_write(), true)); + test_fwd_write(); +}