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

Folding algorithms #3099

Merged
merged 43 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
35bde4c
Add `in_value_result`
JMazurkiewicz Sep 10, 2022
b58023e
Add concepts from the paper
JMazurkiewicz Sep 10, 2022
4582ca2
Add feature test macro
JMazurkiewicz Sep 12, 2022
077d532
Initial implementation of `ranges::fold*`
JMazurkiewicz Sep 12, 2022
c11d6a2
Add missing test coverage for `ranges::in_value_result`
JMazurkiewicz Sep 12, 2022
3fa441b
Change return type of `_Fold_left_with_iter_unchecked`
JMazurkiewicz Sep 12, 2022
21a44eb
Test coverage cleanup
JMazurkiewicz Sep 12, 2022
fdca2fe
Check `ranges::dangling` in call to `ranges::fold_left[_first]_with_i…
JMazurkiewicz Sep 12, 2022
ef7dbc6
Test more folds with empty ranges
JMazurkiewicz Sep 12, 2022
d8fe8db
Rename variables in tests: `resN` -> `sumN`
JMazurkiewicz Sep 12, 2022
8c56ffc
Rename variables in tests: `(left/right)_folded_sum` -> `(left/right)…
JMazurkiewicz Sep 12, 2022
728f92d
Test multiplication in `fold_right(_first)` tests
JMazurkiewicz Sep 12, 2022
20ee9df
Test subtraction in `fold_left_first(_with_iter)` tests
JMazurkiewicz Sep 12, 2022
2fd84c4
A little bit better test of `ranges::in_value_result`
JMazurkiewicz Sep 12, 2022
f168505
Add test coverage to `test.lst`
JMazurkiewicz Sep 12, 2022
7ee8a1b
Move test comment in `yvals_core.h`
JMazurkiewicz Sep 12, 2022
9e27501
Add `empty_rng` variable in tests
JMazurkiewicz Sep 12, 2022
df4a3cb
Tests: `vector<double>` -> `const vector<double>` (+ last tweaks befo…
JMazurkiewicz Sep 12, 2022
1684bdf
Make `_Fold_right_last_unchecked` invoke `_Fold_right_unchecked` dire…
JMazurkiewicz Sep 13, 2022
932f78a
`__cpp_lib_fold` -> `__cpp_lib_ranges_fold`
JMazurkiewicz Sep 14, 2022
623df07
Use `static_cast` and `_Ubegin`
JMazurkiewicz Sep 14, 2022
727ca6d
Create temporary `_Init_ref` variable
JMazurkiewicz Sep 14, 2022
7ea2809
Include `<optional>` only in C++23 mode
JMazurkiewicz Sep 15, 2022
fc7741f
Fix comment in `yvals_core.h`
JMazurkiewicz Sep 15, 2022
216fede
Make `fold_left` and `fold_left_with_iter(R)` call `fold_left_with_it…
JMazurkiewicz Sep 15, 2022
9abf727
Make `fold_left_first` and `fold_left_first_with_iter(R)` call `fold_…
JMazurkiewicz Sep 15, 2022
bd996f3
Make `_Fold_left_(first_)with_iter` functions private members
JMazurkiewicz Sep 15, 2022
2a767da
Example of what I mean
strega-nil Sep 15, 2022
b660eec
really should have run build/tests
strega-nil Sep 15, 2022
f161da3
Merge remote-tracking branch 'upstream/main' into JMazurkiewicz/fold
strega-nil Sep 15, 2022
e60d090
Merge branch 'main' into fold
JMazurkiewicz Sep 19, 2022
d5ef5a6
_EXPORT_STD!
JMazurkiewicz Sep 19, 2022
14b6c75
Rename `in_value_result`'s template arguments (for consistency)
JMazurkiewicz Sep 20, 2022
bb1c947
Merge branch 'main' into fold
JMazurkiewicz Oct 15, 2022
ca43eac
Update formatting
JMazurkiewicz Oct 15, 2022
e0e9de8
Update formatting (again)
JMazurkiewicz Oct 15, 2022
5dab66e
Remove `clang-format [off/on]` comments
JMazurkiewicz Oct 15, 2022
5fcedeb
Include <utility> for move().
StephanTLavavej Oct 20, 2022
10a9b3b
Fix typos: fold_left => fold_right.
StephanTLavavej Oct 20, 2022
d7b7bd2
Add _RANGES qualification.
StephanTLavavej Oct 20, 2022
ca4cc6f
Use static_cast<iter_value_t<_It>> to avoid functional-style casts.
StephanTLavavej Oct 20, 2022
d505bcd
Use braces to construct optional.
StephanTLavavej Oct 20, 2022
5c57818
Merge branch 'main' into fold
StephanTLavavej Oct 24, 2022
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
296 changes: 296 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define _ALGORITHM_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#include <optional>
#include <xmemory>

#pragma pack(push, _CRT_PACKING)
Expand Down Expand Up @@ -254,6 +255,26 @@ namespace ranges {
return {_STD move(in), found};
}
};

#if _HAS_CXX23
template <class _It, class _Ty>
struct in_value_result {
/* [[no_unique_address]] */ _It in;
/* [[no_unique_address]] */ _Ty value;

template <class _It2, class _Ty2>
requires convertible_to<const _It&, _It2> && convertible_to<const _Ty&, _Ty2>
constexpr operator in_value_result<_It2, _Ty2>() const& {
return {in, value};
}

template <class _It2, class _Ty2>
requires convertible_to<_It, _It2> && convertible_to<_Ty, _Ty2>
constexpr operator in_value_result<_It2, _Ty2>() && {
return {_STD move(in), _STD move(value)};
}
};
#endif // _HAS_CXX23
} // namespace ranges
#endif // __cpp_lib_concepts

Expand Down Expand Up @@ -2577,6 +2598,281 @@ namespace ranges {
};

inline constexpr _Ends_with_fn ends_with{_Not_quite_object::_Construct_tag{}};

template <class _Fn>
class _Flipped {
private:
_Fn _Func;

public:
// clang-format off
template <class _Ty, class _Uty>
requires invocable<_Fn&, _Uty, _Ty>
invoke_result_t<_Fn&, _Uty, _Ty> operator()(_Ty&&, _Uty&&);
// clang-format on
};

template <class _Fn, class _Ty, class _It, class _Uty>
concept _Indirectly_binary_left_foldable_impl =
movable<_Ty> && movable<_Uty> && convertible_to<_Ty, _Uty> && invocable<_Fn&, _Uty,
iter_reference_t<_It>> && assignable_from<_Uty&, invoke_result_t<_Fn&, _Uty, iter_reference_t<_It>>>;

template <class _Fn, class _Ty, class _It>
concept _Indirectly_binary_left_foldable = copy_constructible<_Fn> && indirectly_readable<_It> && //
invocable<_Fn&, _Ty, iter_reference_t<_It>> && //
convertible_to<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>,
decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>> && //
_Indirectly_binary_left_foldable_impl<_Fn, _Ty, _It,
decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>>;

template <class _Fn, class _Ty, class _It>
concept _Indirectly_binary_right_foldable = _Indirectly_binary_left_foldable<_Flipped<_Fn>, _Ty, _It>;

template <class _It, class _Ty>
using fold_left_with_iter_result = in_value_result<_It, _Ty>;

template <class _It, class _Ty>
using fold_left_first_with_iter_result = in_value_result<_It, _Ty>;

template <class _It, class _Se, class _Ty, class _Fn>
_NODISCARD constexpr auto _Fold_left_with_iter_unchecked(_It _First, _Se _Last, _Ty _Init, _Fn _Func) {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_left_foldable<_Fn, _Ty, _It>);

using _Uty = decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>;
using _Return_type = fold_left_with_iter_result<_It, _Uty>;
if (_First == _Last) {
return _Return_type{_STD move(_First), static_cast<_Uty>(_STD move(_Init))};
} else {
_Uty _Accum = _STD invoke(_Func, _STD move(_Init), *_First);
for (++_First; _First != _Last; ++_First) {
_Accum = _STD invoke(_Func, _STD move(_Accum), *_First);
}
return _Return_type{_STD move(_First), _STD move(_Accum)};
}
}

class _Fold_left_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, _Indirectly_binary_left_foldable<_Ty, _It> _Fn>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
return _Fold_left_with_iter_unchecked(_Unwrap_iter<_Se>(_STD move(_First)),
_Unwrap_sent<_It>(_STD move(_Last)), _STD move(_Init), _Pass_fn(_Func))
.value;
}

template <input_range _Rng, class _Ty, _Indirectly_binary_left_foldable<_Ty, iterator_t<_Rng>> _Fn>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const {
return _Fold_left_with_iter_unchecked(_Ubegin(_Range), _Uend(_Range), _STD move(_Init), _Pass_fn(_Func))
.value;
}
};

inline constexpr _Fold_left_fn fold_left{_Not_quite_object::_Construct_tag{}};

class _Fold_left_with_iter_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, _Indirectly_binary_left_foldable<_Ty, _It> _Fn>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _RANGES _Fold_left_with_iter_unchecked(_Unwrap_iter<_Se>(_STD move(_First)),
_Unwrap_sent<_It>(_STD move(_Last)), _STD move(_Init), _Pass_fn(_Func));
_Seek_wrapped(_First, _STD move(_UResult.in));

using _Uty = decay_t<invoke_result_t<_Fn&, _Ty, iter_reference_t<_It>>>;
return fold_left_with_iter_result<_It, _Uty>{_STD move(_First), _STD move(_UResult.value)};
}

template <input_range _Rng, class _Ty, _Indirectly_binary_left_foldable<_Ty, iterator_t<_Rng>> _Fn>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const {
auto _First = _RANGES begin(_Range);
auto _UResult = _RANGES _Fold_left_with_iter_unchecked(
_Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _STD move(_Init), _Pass_fn(_Func));
_Seek_wrapped(_First, _STD move(_UResult.in));

using _Uty = decay_t<invoke_result_t<_Fn&, _Ty, range_reference_t<_Rng>>>;
return fold_left_with_iter_result<borrowed_iterator_t<_Rng>, _Uty>{
_STD move(_First), _STD move(_UResult.value)};
JMazurkiewicz marked this conversation as resolved.
Show resolved Hide resolved
}
};

inline constexpr _Fold_left_with_iter_fn fold_left_with_iter{_Not_quite_object::_Construct_tag{}};

template <class _It, class _Se, class _Fn>
_NODISCARD constexpr auto _Fold_left_first_with_iter_unchecked(_It _First, _Se _Last, _Fn _Func) {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_left_foldable<_Fn, iter_value_t<_It>, _It>);
_STL_INTERNAL_STATIC_ASSERT(constructible_from<iter_value_t<_It>, iter_reference_t<_It>>);

using _Uty = decltype(_RANGES fold_left(_STD move(_First), _Last, iter_value_t<_It>(*_First), _Func));
using _Return_type = fold_left_first_with_iter_result<_It, optional<_Uty>>;
if (_First == _Last) {
return _Return_type{_STD move(_First), optional<_Uty>()};
} else {
optional<_Uty> _Init(in_place, *_First);
JMazurkiewicz marked this conversation as resolved.
Show resolved Hide resolved
_Uty& _Init_ref = *_Init;
for (++_First; _First != _Last; ++_First) {
_Init_ref = _STD invoke(_Func, _STD move(_Init_ref), *_First);
}
return _Return_type{_STD move(_First), _STD move(_Init)};
}
}

class _Fold_left_first_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se,
_Indirectly_binary_left_foldable<iter_value_t<_It>, _It> _Fn>
requires constructible_from<iter_value_t<_It>, iter_reference_t<_It>>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const {
// clang-format on
_Adl_verify_range(_First, _Last);
return _Fold_left_first_with_iter_unchecked(
_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Func))
.value;
}

// clang-format off
template <input_range _Rng, _Indirectly_binary_left_foldable<range_value_t<_Rng>, iterator_t<_Rng>> _Fn>
requires constructible_from<range_value_t<_Rng>, range_reference_t<_Rng>>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const {
// clang-format on
return _Fold_left_first_with_iter_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Func)).value;
}
};

inline constexpr _Fold_left_first_fn fold_left_first{_Not_quite_object::_Construct_tag{}};

class _Fold_left_first_with_iter_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se,
_Indirectly_binary_left_foldable<iter_value_t<_It>, _It> _Fn>
requires constructible_from<iter_value_t<_It>, iter_reference_t<_It>>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const {
// clang-format on
_Adl_verify_range(_First, _Last);
auto _UResult = _Fold_left_first_with_iter_unchecked(
_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Func));
_Seek_wrapped(_First, _STD move(_UResult.in));

using _Uty = decltype(_RANGES fold_left(_STD move(_First), _Last, iter_value_t<_It>(*_First), _Func));
return fold_left_first_with_iter_result<_It, optional<_Uty>>{_STD move(_First), _STD move(_UResult.value)};
}

// clang-format off
template <input_range _Rng, _Indirectly_binary_left_foldable<range_value_t<_Rng>, iterator_t<_Rng>> _Fn>
requires constructible_from<range_value_t<_Rng>, range_reference_t<_Rng>>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const {
// clang-format on
auto _First = _RANGES begin(_Range);
auto _UResult = _Fold_left_first_with_iter_unchecked(
_Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Func));
_Seek_wrapped(_First, _STD move(_UResult.in));

using _Uty = decltype(_RANGES fold_left(
_STD move(_First), _RANGES end(_Range), range_value_t<_Rng>(*_First), _Func));
return fold_left_first_with_iter_result<borrowed_iterator_t<_Rng>, optional<_Uty>>{
_STD move(_First), _STD move(_UResult.value)};
}
};

inline constexpr _Fold_left_first_with_iter_fn fold_left_first_with_iter{_Not_quite_object::_Construct_tag{}};

template <class _It, class _Se, class _Ty, class _Fn>
_NODISCARD constexpr auto _Fold_right_unchecked(_It _First, _Se _Last, _Ty _Init, _Fn _Func) {
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_right_foldable<_Fn, _Ty, _It>);

using _Uty = decay_t<invoke_result_t<_Fn&, iter_reference_t<_It>, _Ty>>;
if (_First == _Last) {
return static_cast<_Uty>(_STD move(_Init));
} else {
_It _Tail = _RANGES next(_First, _Last);
_Uty _Accum = _STD invoke(_Func, *--_Tail, _STD move(_Init));
while (_First != _Tail) {
_Accum = _STD invoke(_Func, *--_Tail, _STD move(_Accum));
}
return _Accum;
}
}

class _Fold_right_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <bidirectional_iterator _It, sentinel_for<_It> _Se, class _Ty,
_Indirectly_binary_right_foldable<_Ty, _It> _Fn>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const {
_Adl_verify_range(_First, _Last);
return _Fold_right_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)),
_STD move(_Init), _Pass_fn(_Func));
}

template <bidirectional_range _Rng, class _Ty, _Indirectly_binary_right_foldable<_Ty, iterator_t<_Rng>> _Fn>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const {
return _Fold_right_unchecked(_Ubegin(_Range), _Uend(_Range), _STD move(_Init), _Pass_fn(_Func));
}
};

inline constexpr _Fold_right_fn fold_right{_Not_quite_object::_Construct_tag{}};

class _Fold_right_last_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <bidirectional_iterator _It, sentinel_for<_It> _Se,
_Indirectly_binary_right_foldable<iter_value_t<_It>, _It> _Fn>
requires constructible_from<iter_value_t<_It>, iter_reference_t<_It>>
_NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const {
// clang-format on
_Adl_verify_range(_First, _Last);
return _Fold_right_last_unchecked(
_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Func));
}

// clang-format off
template <bidirectional_range _Rng,
_Indirectly_binary_right_foldable<range_value_t<_Rng>, iterator_t<_Rng>> _Fn>
requires constructible_from<range_value_t<_Rng>, range_reference_t<_Rng>>
_NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const {
// clang-format on
return _Fold_right_last_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Func));
}

private:
template <class _It, class _Se, class _Fn>
_NODISCARD constexpr auto _Fold_right_last_unchecked(_It _First, _Se _Last, _Fn _Func) const {
_STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_right_foldable<_Fn, iter_value_t<_It>, _It>);
_STL_INTERNAL_STATIC_ASSERT(constructible_from<iter_value_t<_It>, iter_reference_t<_It>>);

using _Uty = decltype(_RANGES fold_right(_First, _Last, iter_value_t<_It>(*_First), _Func));
if (_First == _Last) {
return optional<_Uty>{};
} else {
_It _Tail = _RANGES prev(_RANGES next(_First, _STD move(_Last)));
return optional<_Uty>(in_place, _RANGES _Fold_right_unchecked(_STD move(_First), _Tail,
iter_value_t<_It>(*_Tail), _STD move(_Func)));
}
}
};

inline constexpr _Fold_right_last_fn fold_right_last{_Not_quite_object::_Construct_tag{}};
#endif // _HAS_CXX23
} // namespace ranges
#endif // __cpp_lib_concepts
Expand Down
2 changes: 2 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@
// P2302R4 ranges::contains, ranges::contains_subrange
// P2321R2 zip
// (changes to pair, tuple, and vector<bool>::reference only)
// P2322R6 ranges::fold
JMazurkiewicz marked this conversation as resolved.
Show resolved Hide resolved
// P2387R3 Pipe Support For User-Defined Range Adaptors
// P2417R2 More constexpr bitset
// P2438R2 string::substr() &&
Expand Down Expand Up @@ -1637,6 +1638,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_ranges_chunk 202202L
#define __cpp_lib_ranges_chunk_by 202202L
#define __cpp_lib_ranges_contains 202207L
#define __cpp_lib_ranges_fold 202207L
#define __cpp_lib_ranges_iota 202202L
#define __cpp_lib_ranges_join_with 202202L
#define __cpp_lib_ranges_slide 202202L
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ tests\P2273R3_constexpr_unique_ptr
tests\P2302R4_ranges_alg_contains
tests\P2302R4_ranges_alg_contains_subrange
tests\P2321R2_proxy_reference
tests\P2322R6_ranges_alg_fold
tests\P2387R3_bind_back
tests\P2387R3_pipe_support_for_user_defined_range_adaptors
tests\P2401R0_conditional_noexcept_for_exchange
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2322R6_ranges_alg_fold/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_latest_matrix.lst
Loading