Skip to content

Commit

Permalink
Implement P2440R1 ranges::iota, ranges::shift_left, ranges::shift_rig…
Browse files Browse the repository at this point in the history
  • Loading branch information
MitalAshok committed Feb 18, 2022
1 parent 7b2c54b commit bb9e78c
Show file tree
Hide file tree
Showing 12 changed files with 527 additions and 2 deletions.
162 changes: 162 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,22 @@ namespace ranges {
}
};

template <class _Out, class _Ty>
struct out_value_result {
/* [[no_unique_address]] */ _Out out;
/* [[no_unique_address]] */ _Ty value;

template <_Convertible_from<const _Out&> _OOut, _Convertible_from<const _Ty&> _Ty2>
constexpr operator out_value_result<_OOut, _Ty2>() const& {
return {out, value};
}

template <_Convertible_from<_Out&&> _OOut, _Convertible_from<_Ty&&> _Ty2>
constexpr operator out_value_result<_OOut, _Ty2>() && {
return {_STD move(out), _STD move(value)};
}
};

template <forward_iterator _Wrapped, sentinel_for<_Wrapped> _Se>
_NODISCARD constexpr auto _Get_final_iterator_unwrapped(const _Unwrapped_t<_Wrapped>& _UFirst, _Se&& _Last) {
// find the iterator in [_UFirst, _Get_unwrapped(_Last)) which equals _Get_unwrapped(_Last) [possibly O(N)]
Expand Down Expand Up @@ -5118,6 +5134,152 @@ _FwdIt shift_right(_ExPo&&, _FwdIt _First, _FwdIt _Last, _Iter_diff_t<_FwdIt> _P
// not parallelized as benchmarks show it isn't worth it
return _STD shift_right(_First, _Last, _Pos_to_shift);
}

namespace ranges {
template <class _It, class _Se>
constexpr _It _Shift_left_unchecked(_It _First, const _Se _Last, _Iter_diff_t<_It> _Pos_to_shift) {
if (_Pos_to_shift <= 0) {
_RANGES advance(_First, _Last);
return _First;
}

auto _Start_at = _First;

if constexpr (sized_sentinel_for<_Se, _It>) {
if (_Pos_to_shift >= _Last - _First) {
return _First;
}
_RANGES advance(_Start_at, _Pos_to_shift);
} else {
for (; 0 < _Pos_to_shift; --_Pos_to_shift) {
if (_Start_at == _Last) {
return _First;
}
++_Start_at;
}
}

return _RANGES _Move_unchecked(_Start_at, _Last, _First).out;
}

template <permutable _It, sentinel_for<_It> _Se>
constexpr subrange<_It> shift_left(_It _First, _Se _Last, _Iter_diff_t<_It> _Pos_to_shift) {
_Adl_verify_range(_First, _Last);
auto _Result = _First;
_Seek_wrapped(_Result, _Shift_left_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Pos_to_shift));
return {_First, _Result};
}

template <forward_range _Rng>
requires permutable<iterator_t<_Rng>>
constexpr borrowed_subrange_t<_Rng> shift_left(_Rng&& _Range, range_difference_t<_Rng> _Pos_to_shift) {
auto _First = _RANGES begin(_Range);
auto _Result = _First;
_Seek_wrapped(_Result, _Shift_left_unchecked(_STD move(_Get_unwrapped(_Result)), _Uend(_Range), _Pos_to_shift));
return {_First, _Result};
}

template <class _It, typename _Se>
constexpr subrange<_It> _Shift_right(_It _First, const _Se _Last, _Iter_diff_t<_It> _Pos_to_shift) {
if (_Pos_to_shift <= 0) {
return { _First, _RANGES next(_First, _Last) };
}

const auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);

if constexpr (bidirectional_iterator<_Se>) {
auto _UEnd_at = _ULast;
if constexpr (sized_sentinel_for<_Se, _It>) {
auto _Size = _ULast - _UFirst;
if (_Pos_to_shift >= _Size) {
_RANGES advance(_First, _Size);
return {_First, _First};
}
_RANGES advance(_UEnd_at, -_Pos_to_shift);
} else {
for (; 0 < _Pos_to_shift; --_Pos_to_shift) {
if (_UEnd_at == _UFirst) {
_First = _RANGES next(_First, _Last);
return {_First, _First};
}
--_UEnd_at;
}
}

_Seek_wrapped(_First, _RANGES _Move_backward_common(_UFirst, _UEnd_at, _ULast));
return { _First, _RANGES next(_First, _Last) };
} else if constexpr (sized_sentinel_for<_Se, _It>) {
auto _Size = _ULast - _UFirst;
if (_Pos_to_shift >= _Size) {
_RANGES advance(_UFirst, _Size);
_Seek_wrapped(_First, _UFirst);
return {_First, _First};
}

auto _UEnd_at = _RANGES next(_UFirst, _Size - _Pos_to_shift);
_Seek_wrapped(_First, _RANGES _Move_backward_common(_UFirst, _UEnd_at, _ULast));
return { _First, _RANGES next(_First, _Size) };
} else {
auto _UResult = _UFirst;

for (; 0 < _Pos_to_shift; --_Pos_to_shift) {
if (_UResult == _ULast) {
_Seek_wrapped(_First, _UResult);
return {_First, _First};
}
++_UResult;
}
_Seek_wrapped(_First, _UResult);

auto _Trail = _UFirst;
auto _Lead = _UResult;

for (; _Trail != _UResult; ++_Trail, (void) ++_Lead) {
if (_Lead == _ULast) {
_RANGES _Move_unchecked(_UFirst, _Trail, _UResult);

auto _WLead = _First;
_Seek_wrapped(_WLead, _Lead);

return {_First, _WLead};
}
}

// Here, _Trail = _UFirst + original _Pos_to_shift
// Here, _Lead = _UFirst + 2 * original _Pos_to_shift

for (;;) {
// This loop swaps the range [_UFirst, _UResult) with [_Trail, _Lead),
// advancing _Trail and _Lead by _Pos_to_shift
for (auto _Mid = _UFirst; _Mid != _UResult; ++_Mid, (void) ++_Trail, ++_Lead) {
if (_Lead == _ULast) {
_Trail = _RANGES _Move_unchecked(_Mid, _UResult, _Trail).out;
_RANGES _Move_unchecked(_UFirst, _Mid, _Trail);

auto _WLead = _First;
_Seek_wrapped(_WLead, _Lead);

return {_First, _WLead};
}
_RANGES iter_swap(_Mid, _Trail);
}
}
}
}

template <permutable _It, sentinel_for<_It> _Se>
constexpr subrange<_It> shift_right(_It _First, _Se _Last, _Iter_diff_t<_It> _Pos_to_shift) {
_Adl_verify_range(_First, _Last);
return _Shift_right(_First, _Last, _Pos_to_shift);
}

template <forward_range _Rng>
requires permutable<iterator_t<_Rng>>
constexpr borrowed_subrange_t<_Rng> shift_right(_Rng&& _Range, range_difference_t<_Rng> _Pos_to_shift) {
return _Shift_right(_RANGES begin(_Range), _RANGES end(_Range), _Pos_to_shift);
}
}
#endif // _HAS_CXX20

template <class _FwdIt, class _Pr>
Expand Down
8 changes: 6 additions & 2 deletions stl/inc/forward_list
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,12 @@ public:
return _Unchecked_const_iterator(_Mypair._Myval2._Myhead, nullptr);
}

_Default_sentinel _Unchecked_end() const noexcept {
return {};
_Unchecked_iterator _Unchecked_end() noexcept {
return _Unchecked_iterator(nullptr, nullptr);
}

_Unchecked_const_iterator _Unchecked_end() const noexcept {
return _Unchecked_const_iterator(nullptr, nullptr);
}

_Unchecked_const_iterator _Unchecked_end_iter() const noexcept {
Expand Down
35 changes: 35 additions & 0 deletions stl/inc/numeric
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#include <limits>
#endif // _HAS_CXX17

#ifdef __cpp_lib_concepts
#include <algorithm>
#include <iterator>
#endif // __cpp_lib_concepts

#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
Expand Down Expand Up @@ -523,6 +528,36 @@ _CONSTEXPR20 void iota(_FwdIt _First, _FwdIt _Last, _Ty _Val) {
}
}

#ifdef __cpp_lib_concepts
namespace ranges {
template <class _Out, class _Ty>
using iota_result = out_value_result<_Out, _Ty>;

template <class _It, class _Se, class _Ty>
constexpr _It _Iota_unchecked(_It _First, const _Se& _Last, _Ty& _Val) {
for (; _First != _Last; ++_First, (void) ++_Val) {
*_First = const_cast<const _Ty&>(_Val);
}
return _STD move(_First);
}

template <input_or_output_iterator _It, sentinel_for<_It> _Se, weakly_incrementable _Ty>
requires indirectly_writable<_It, const _Ty&>
constexpr iota_result<_It, _Ty> iota(_It _First, _Se _Last, _Ty _Val) {
_Adl_verify_range(_First, _Last);
_Seek_wrapped(_First, _Iota_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_Last), _Val));
return { _STD move(_First), _STD move(_Val) };
}

template <weakly_incrementable _Ty, output_range<const _Ty&> _Rng>
constexpr iota_result<borrowed_iterator_t<_Rng>, _Ty> iota(_Rng&& _Range, _Ty _Val) {
auto _First = _RANGES begin(_Range);
_Seek_wrapped(_First, _Iota_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val));
return { _STD move(_First), _STD move(_Val) };
}
}
#endif // __cpp_lib_concepts

#if _HAS_CXX17
template <class _Integral>
_NODISCARD constexpr auto _Abs_u(const _Integral _Val) noexcept {
Expand Down
5 changes: 5 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1332,7 +1332,11 @@

#define __cpp_lib_remove_cvref 201711L
#define __cpp_lib_semaphore 201907L
#ifdef __cpp_lib_concepts
#define __cpp_lib_shift 202202L // P2440R1 ranges::shift_left, ranges::shift_right
#else
#define __cpp_lib_shift 201806L
#endif // __cpp_lib_concepts
#define __cpp_lib_smart_ptr_for_overwrite 202002L

#ifdef __cpp_consteval
Expand Down Expand Up @@ -1371,6 +1375,7 @@
#ifdef __cpp_lib_concepts
#define __cpp_lib_out_ptr 202106L
#define __cpp_lib_ranges_starts_ends_with 202106L
#define __cpp_lib_ranges_iota 202202L
#endif // __cpp_lib_concepts

#define __cpp_lib_spanstream 202106L
Expand Down
3 changes: 3 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ tests\P2162R2_std_visit_for_derived_classes_from_variant
tests\P2231R1_complete_constexpr_optional_variant
tests\P2401R0_conditional_noexcept_for_exchange
tests\P2415R2_owning_view
tests\P2440R1_ranges_alg_shift_left
tests\P2440R1_ranges_alg_shift_right
tests\P2440R1_ranges_numeric_iota
tests\VSO_0000000_allocator_propagation
tests\VSO_0000000_any_calling_conventions
tests\VSO_0000000_c_math_functions
Expand Down
88 changes: 88 additions & 0 deletions tests/std/tests/P0769R2_shift_left_shift_right/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
#include <iterator>
#include <list>
#include <vector>
#include <version>

#if __cpp_lib_shift >= 202202L
// Test ranges::shift_left, ranges::shift_right from P2440R1
#define TEST_RANGES
#endif

using namespace std;

Expand Down Expand Up @@ -106,12 +112,44 @@ void test_case_shift_left(const ptrdiff_t tmpSize) {
for (ptrdiff_t pos_to_shift = 0; pos_to_shift < tmpSize; ++pos_to_shift) {
fill_iota(tmp, tmpSize);
test_iota(tmp.begin(), shift_left(tmp.begin(), tmp.end(), pos_to_shift), pos_to_shift + 1, tmpSize);

#ifdef TEST_RANGES
{
fill_iota(tmp, tmpSize);
auto [ first, last ] = ranges::shift_left(tmp.begin(), tmp.end(), pos_to_shift);
assert(first == tmp.begin());
test_iota(first, last, pos_to_shift + 1, tmpSize);
}

{
fill_iota(tmp, tmpSize);
auto [ first, last ] = ranges::shift_left(tmp, pos_to_shift);
assert(first == tmp.begin());
test_iota(first, last, pos_to_shift + 1, tmpSize);
}
#endif
}

fill_iota(tmp, tmpSize);
for (int i = 0; i < 3; ++i) {
test_iota(shift_left(tmp.begin(), tmp.end(), tmpSize + i), tmp.end(), 1, tmpSize);
}

#ifdef TEST_RANGES
fill_iota(tmp, tmpSize);
for (int i = 0; i < 3; ++i) {
auto [ first, last ] = ranges::shift_left(tmp.begin(), tmp.end(), tmpSize + i);
assert(first == tmp.begin());
test_iota(last, tmp.end(), 1, tmpSize);
}

fill_iota(tmp, tmpSize);
for (int i = 0; i < 3; ++i) {
auto [ first, last ] = ranges::shift_left(tmp, tmpSize + i);
assert(first == tmp.begin());
test_iota(last, tmp.end(), 1, tmpSize);
}
#endif
}

template <typename Container>
Expand All @@ -121,15 +159,65 @@ void test_case_shift_right(const ptrdiff_t tmpSize) {

test_iota(shift_right(tmp.begin(), tmp.end(), -1), tmp.end(), 1, tmpSize);

#ifdef TEST_RANGES
{
fill_iota(tmp, tmpSize);

auto [ first, last ] = ranges::shift_right(tmp.begin(), tmp.end(), -1);
assert(last == tmp.end());
test_iota(first, last, 1, tmpSize);
}

{
fill_iota(tmp, tmpSize);

auto [ first, last ] = ranges::shift_right(tmp, -1);
assert(last == tmp.end());
test_iota(first, last, 1, tmpSize);
}
#endif

for (ptrdiff_t pos_to_shift = 0; pos_to_shift < tmpSize; ++pos_to_shift) {
fill_iota(tmp, tmpSize);
test_iota(shift_right(tmp.begin(), tmp.end(), pos_to_shift), tmp.end(), 1, tmpSize - pos_to_shift);

#ifdef TEST_RANGES
{
fill_iota(tmp, tmpSize);
auto [ first, last ] = ranges::shift_right(tmp.begin(), tmp.end(), pos_to_shift);
assert(last == tmp.end());
test_iota(first, last, 1, tmpSize - pos_to_shift);
}

{
fill_iota(tmp, tmpSize);
auto [ first, last ] = ranges::shift_right(tmp, pos_to_shift);
assert(last == tmp.end());
test_iota(first, last, 1, tmpSize - pos_to_shift);
}
#endif
}

fill_iota(tmp, tmpSize);
for (int i = 0; i < 3; ++i) {
test_iota(tmp.begin(), shift_right(tmp.begin(), tmp.end(), tmpSize + i), 1, tmpSize);
}

#ifdef TEST_RANGES
fill_iota(tmp, tmpSize);
for (int i = 0; i < 3; ++i) {
auto [ first, last ] = ranges::shift_right(tmp.begin(), tmp.end(), tmpSize + i);
assert(last == tmp.end());
test_iota(tmp.begin(), first, 1, tmpSize);
}

fill_iota(tmp, tmpSize);
for (int i = 0; i < 3; ++i) {
auto [ first, last ] = ranges::shift_right(tmp, tmpSize + i);
assert(last == tmp.end());
test_iota(tmp.begin(), first, 1, tmpSize);
}
#endif
}

int main() {
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2440R1_ranges_alg_shift_left/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_20_matrix.lst
Loading

0 comments on commit bb9e78c

Please sign in to comment.