Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
51 changes: 51 additions & 0 deletions libcxx/include/__algorithm/ranges_fold.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct in_value_result {
template <class _Ip, class _Tp>
using fold_left_with_iter_result = in_value_result<_Ip, _Tp>;

template <class _Ip, class _Tp>
using fold_left_first_with_iter_result = in_value_result<_Ip, _Tp>;

template <class _Fp, class _Tp, class _Ip, class _Rp, class _Up = decay_t<_Rp>>
concept __indirectly_binary_left_foldable_impl =
convertible_to<_Rp, _Up> && //
Expand Down Expand Up @@ -118,6 +121,54 @@ struct __fold_left {
};

inline constexpr auto fold_left = __fold_left();

struct __fold_left_first_with_iter {
template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
using _Up = decltype(fold_left(std::move(__first), __last, iter_value_t<_Ip>(*__first), __f));

// case of empty range
if (__first == __last) {
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>()};
}

optional<_Up> __result(std::in_place, *__first);
for (++__first; __first != __last; ++__first) {
*__result = std::invoke(__f, std::move(*__result), *__first);
}

return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>(std::move(__result))};
}

template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
auto __result = operator()(ranges::begin(__r), ranges::end(__r), std::ref(__f));

using _Up = decltype(fold_left(ranges::begin(__r), ranges::end(__r), range_value_t<_Rp>(*ranges::begin(__r)), __f));
return fold_left_first_with_iter_result<borrowed_iterator_t<_Rp>, optional<_Up>>{std::move(__result.in), optional(std::move(__result.value))};
}
};

inline constexpr auto fold_left_first_with_iter = __fold_left_first_with_iter();

struct __fold_left_first {
template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
return fold_left_first_with_iter(std::move(__first), std::move(__last), std::ref(__f)).value;
}

template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
return fold_left_first_with_iter(ranges::begin(__r), ranges::end(__r), std::ref(__f)).value;
}
};

inline constexpr auto fold_left_first = __fold_left_first();

} // namespace ranges

#endif // _LIBCPP_STD_VER >= 23
Expand Down
21 changes: 21 additions & 0 deletions libcxx/include/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,15 @@ namespace ranges {
template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
constexpr auto fold_left(R&& r, T init, F f); // since C++23

template<input_iterator I, sentinel_for<I> S,
indirectly-binary-left-foldable<T, I> F>
requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
constexpr see below fold_left_first(I first, S last, F f); // since C++23

template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
requires constructible_from<range_value_t<R>, range_reference_t<R>>
constexpr see below fold_left_first(R&& r, F f); // since C++23

template<class I, class T>
using fold_left_with_iter_result = in_value_result<I, T>; // since C++23

Expand All @@ -948,6 +957,18 @@ namespace ranges {
template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
constexpr see below fold_left_with_iter(R&& r, T init, F f); // since C++23

template<class I, class T>
using fold_left_first_with_iter_result = in_value_result<I, T>; // since C++23

template<input_iterator I, sentinel_for<I> S,
indirectly-binary-left-foldable<T, I> F>
requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
constexpr see below fold_left_first_with_iter(I first, S last, F f); // since C++23

template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
requires constructible_from<range_value_t<R>, range_reference_t<R>>
constexpr see below fold_left_first_with_iter(R&& r, F f); // since C++23

template<forward_iterator I1, sentinel_for<I1> S1, forward_iterator I2, sentinel_for<I2> S2,
class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity>
requires indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
Expand Down
7 changes: 3 additions & 4 deletions libcxx/modules/std/algorithm.inc
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,14 @@ export namespace std {

// [alg.fold], fold
using std::ranges::fold_left;
using std::ranges::fold_left_first;
using std::ranges::fold_left_first_with_iter;
using std::ranges::fold_left_first_with_iter_result;
using std::ranges::fold_left_with_iter;
using std::ranges::fold_left_with_iter_result;
# if 0
using std::ranges::fold_left_first;
using std::ranges::fold_right;
using std::ranges::fold_right_last;
using std::ranges::fold_left_with_iter;
using std::ranges::fold_left_first_with_iter;
using std::ranges::fold_left_first_with_iter;
# endif
#endif // _LIBCPP_STD_VER >= 23
} // namespace ranges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,5 +392,9 @@ void test() {
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left_with_iter(iter, iter, 0, std::plus());
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left_first_with_iter(range, std::plus());
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left_first_with_iter(iter, iter, std::plus());
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <string_view>
#include <string>
#include <vector>
#include <optional>

#include "test_macros.h"
#include "test_range.h"
Expand All @@ -50,7 +51,9 @@
#endif

using std::ranges::fold_left;
using std::ranges::fold_left_first;
using std::ranges::fold_left_with_iter;
using std::ranges::fold_left_first_with_iter;

template <class Result, class Range, class T>
concept is_in_value_result =
Expand Down Expand Up @@ -105,6 +108,47 @@ constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected
}
}

template <std::ranges::input_range R, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
{
is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f);
assert(result.in == r.end());
assert(result.value == expected);
}

{
auto telemetry = invocable_telemetry();
auto f2 = invocable_with_telemetry(f, telemetry);
is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f2);
assert(result.in == r.end());
assert(result.value == expected);
if (result.value.has_value()) {
assert(telemetry.invocations == std::ranges::distance(r) - 1);
assert(telemetry.moves == 0);
assert(telemetry.copies == 1);
}
}

{
std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f);
assert(result == expected);
}

{
auto telemetry = invocable_telemetry();
auto f2 = invocable_with_telemetry(f, telemetry);
std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f2);
assert(result == expected);
if (result.has_value()) {
assert(telemetry.invocations == std::ranges::distance(r) - 1);
assert(telemetry.moves == 0);
assert(telemetry.copies == 1);
}
}
}


template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
Expand Down Expand Up @@ -186,26 +230,37 @@ constexpr void check(R r, T const& init, F f, Expected const& expected) {
check_rvalue_range(r, init, f, expected);
}

template <std::ranges::input_range R, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check(R r, F f, std::optional<Expected> const& expected) {
check_iterator(r, f, expected);
}

constexpr void empty_range_test_case() {
auto const data = std::vector<int>{};
check(data, 100, std::plus(), 100);
check(data, -100, std::multiplies(), -100);
check(data, std::plus(), std::optional<int>());

check(data | std::views::take_while([](auto) { return false; }), 1.23, std::plus(), 1.23);
check(data, Integer(52), &Integer::plus, Integer(52));
check(data | std::views::take_while([](auto) { return false; }), std::plus(), std::optional<int>());
}

constexpr void common_range_test_case() {
auto const data = std::vector<int>{1, 2, 3, 4};
check(data, 0, std::plus(), triangular_sum(data));
check(data, 1, std::multiplies(), factorial(data.back()));
check(data, std::plus(), std::optional(triangular_sum(data)));
check(data, std::multiplies(), std::optional(factorial(data.back())));

auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
auto const result = x * y * n;
n = y;
return static_cast<std::size_t>(result);
};
check(data, 1, multiply_with_prev, factorial(data.size()) * factorial(data.size() - 1));
check(data, multiply_with_prev, std::optional(factorial(data.size()) * factorial(data.size() - 1)));

auto fib = [n = 1](auto x, auto) mutable {
auto old_x = x;
Expand Down Expand Up @@ -237,6 +292,7 @@ constexpr void non_common_range_test_case() {
auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
auto range = data | std::views::transform(parse);
check(range, 0, std::plus(), triangular_sum(range));
check(range, std::plus(), std::optional(triangular_sum(range)));
}

{
Expand All @@ -248,6 +304,7 @@ constexpr void non_common_range_test_case() {
auto range =
std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
check(range, 0, std::plus(), triangular_sum(range));
check(range, std::plus(), std::optional(triangular_sum(range)));
}
}

Expand Down
Loading