Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ranges::rotate and ranges::rotate_copy #1073

Merged
merged 7 commits into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 174 additions & 2 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -4845,15 +4845,15 @@ 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;
}

template <bidirectional_range _Rng>
requires permutable<iterator_t<_Rng>>
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
Expand Down Expand Up @@ -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
miscco marked this conversation as resolved.
Show resolved Hide resolved
class _Rotate_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <permutable _It, sentinel_for<_It> _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<subrange<_It>>(_First, _STD move(_UResult));
}

// clang-format off
template <forward_range _Rng>
requires permutable<iterator_t<_Rng>>
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<borrowed_subrange_t<_Rng>>(_Mid, _STD move(_UResult));
}

private:
template <class _It, class _Se>
_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
miscco marked this conversation as resolved.
Show resolved Hide resolved
_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 <class _It>
_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 <class _FwdIt, class _OutIt>
_CONSTEXPR20 _OutIt rotate_copy(_FwdIt _First, _FwdIt _Mid, _FwdIt _Last, _OutIt _Dest) {
Expand All @@ -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 <class _In, class _Out>
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 <forward_iterator _It, sentinel_for<_It> _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 <forward_range _Rng, weakly_incrementable _Out>
requires indirectly_copyable<iterator_t<_Rng>, _Out>
constexpr rotate_copy_result<borrowed_iterator_t<_Rng>, _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 <class _It, class _Se, class _Out>
_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 <class _PopIt, class _SampleIt, class _Diff, class _RngFn>
_SampleIt _Sample_reservoir_unchecked(
Expand Down
2 changes: 2 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_rotate/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
52 changes: 52 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_rotate/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>
using namespace std;
using P = pair<int, int>;

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::rotate(borrowed<false>{}, nullptr_to<int>)), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::rotate(borrowed<true>{}, nullptr_to<int>)), ranges::subrange<int*>>);

struct instantiator {
static constexpr P expected[5] = {{3, 47}, {4, 99}, {0, 99}, {1, 47}, {2, 99}};

template <ranges::forward_range ReadWrite>
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<decltype(result), subrange<iterator_t<ReadWrite>>>);
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<decltype(result), subrange<iterator_t<ReadWrite>>>);
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<instantiator, P>(), true));
#endif // TRANSITION, VSO-938163
test_fwd<instantiator, P>();
}
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_rotate_copy/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_matrix.lst
56 changes: 56 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_rotate_copy/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <concepts>
#include <ranges>

#include <range_algorithm_support.hpp>
using namespace std;

// Validate that rotate_copy_result aliases in_out_result
STATIC_ASSERT(same_as<ranges::rotate_copy_result<int, double>, ranges::in_out_result<int, double>>);

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::rotate_copy(borrowed<false>{}, nullptr_to<int>, nullptr_to<int>)),
ranges::rotate_copy_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(ranges::rotate_copy(borrowed<true>{}, nullptr_to<int>, nullptr_to<int>)),
ranges::rotate_copy_result<int*, int*>>);

struct instantiator {
static constexpr int input[5] = {1, 2, 3, 4, 5};
static constexpr int expected[5] = {4, 5, 1, 2, 3};

template <ranges::forward_range Read, indirectly_writable<ranges::range_reference_t<Read>> 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<Read> mid = next(wrapped_input.begin(), 3);

auto result = rotate_copy(wrapped_input.begin(), mid, wrapped_input.end(), Write{output});
STATIC_ASSERT(same_as<decltype(result), rotate_copy_result<iterator_t<Read>, 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<Read> mid = next(wrapped_input.begin(), 3);

auto result = rotate_copy(wrapped_input, mid, Write{output});
STATIC_ASSERT(same_as<decltype(result), rotate_copy_result<iterator_t<Read>, Write>>);
assert(result.in == wrapped_input.end());
assert(result.out.peek() == end(output));
assert(equal(expected, output));
}
}
};

int main() {
STATIC_ASSERT((test_fwd_write<instantiator, const int, int>(), true));
test_fwd_write<instantiator, const int, int>();
}