139034388SChristopher Di Bella //===----------------------------------------------------------------------===//
239034388SChristopher Di Bella //
339034388SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
439034388SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information.
539034388SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
639034388SChristopher Di Bella //
739034388SChristopher Di Bella //===----------------------------------------------------------------------===//
839034388SChristopher Di Bella
939034388SChristopher Di Bella // <algorithm>
1039034388SChristopher Di Bella
1139034388SChristopher Di Bella // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
1239034388SChristopher Di Bella
13c9535d7bSStephan T. Lavavej // MSVC warning C4244: 'argument': conversion from 'double' to 'const int', possible loss of data
14c9535d7bSStephan T. Lavavej // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
15c9535d7bSStephan T. Lavavej
1639034388SChristopher Di Bella // template<input_iterator I, sentinel_for<I> S, class T,
1739034388SChristopher Di Bella // indirectly-binary-left-foldable<T, I> F>
1839034388SChristopher Di Bella // constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f);
1939034388SChristopher Di Bella //
2039034388SChristopher Di Bella // template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
2139034388SChristopher Di Bella // constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);
2239034388SChristopher Di Bella
2339034388SChristopher Di Bella // template<input_iterator I, sentinel_for<I> S, class T,
2439034388SChristopher Di Bella // indirectly-binary-left-foldable<T, I> F>
2539034388SChristopher Di Bella // constexpr see below ranges::fold_left(I first, S last, T init, F f);
2639034388SChristopher Di Bella //
2739034388SChristopher Di Bella // template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
2839034388SChristopher Di Bella // constexpr see below ranges::fold_left(R&& r, T init, F f);
2939034388SChristopher Di Bella
3039034388SChristopher Di Bella #include <algorithm>
3139034388SChristopher Di Bella #include <cassert>
3239034388SChristopher Di Bella #include <concepts>
3339034388SChristopher Di Bella #include <deque>
3439034388SChristopher Di Bella #include <forward_list>
3539034388SChristopher Di Bella #include <functional>
3639034388SChristopher Di Bella #include <iterator>
3739034388SChristopher Di Bella #include <list>
3839034388SChristopher Di Bella #include <ranges>
3939034388SChristopher Di Bella #include <string_view>
4039034388SChristopher Di Bella #include <string>
4139034388SChristopher Di Bella #include <vector>
4239034388SChristopher Di Bella
4339034388SChristopher Di Bella #include "test_macros.h"
4439034388SChristopher Di Bella #include "test_range.h"
4539034388SChristopher Di Bella #include "invocable_with_telemetry.h"
4639034388SChristopher Di Bella #include "maths.h"
4739034388SChristopher Di Bella
4839034388SChristopher Di Bella #if !defined(TEST_HAS_NO_LOCALIZATION)
4939034388SChristopher Di Bella # include <sstream>
5039034388SChristopher Di Bella #endif
5139034388SChristopher Di Bella
5239034388SChristopher Di Bella using std::ranges::fold_left;
5339034388SChristopher Di Bella using std::ranges::fold_left_with_iter;
5439034388SChristopher Di Bella
5539034388SChristopher Di Bella template <class Result, class Range, class T>
5639034388SChristopher Di Bella concept is_in_value_result =
5739034388SChristopher Di Bella std::same_as<Result, std::ranges::fold_left_with_iter_result<std::ranges::iterator_t<Range>, T>>;
5839034388SChristopher Di Bella
5939034388SChristopher Di Bella template <class Result, class T>
6039034388SChristopher Di Bella concept is_dangling_with = std::same_as<Result, std::ranges::fold_left_with_iter_result<std::ranges::dangling, T>>;
6139034388SChristopher Di Bella
62*f0ea888eSChristopher Di Bella struct Integer {
6339034388SChristopher Di Bella int value;
6439034388SChristopher Di Bella
IntegerInteger65*f0ea888eSChristopher Di Bella constexpr Integer(int const x) : value(x) {}
6639034388SChristopher Di Bella
plusInteger67*f0ea888eSChristopher Di Bella constexpr Integer plus(int const x) const { return Integer{value + x}; }
6839034388SChristopher Di Bella
69*f0ea888eSChristopher Di Bella friend constexpr bool operator==(Integer const& x, Integer const& y) = default;
7039034388SChristopher Di Bella };
7139034388SChristopher Di Bella
7239034388SChristopher Di Bella template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
7339034388SChristopher Di Bella requires std::copyable<R>
check_iterator(R & r,T const & init,F f,Expected const & expected)7439034388SChristopher Di Bella constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected) {
7539034388SChristopher Di Bella {
7639034388SChristopher Di Bella is_in_value_result<R, Expected> decltype(auto) result = fold_left_with_iter(r.begin(), r.end(), init, f);
7739034388SChristopher Di Bella assert(result.in == r.end());
7839034388SChristopher Di Bella assert(result.value == expected);
7939034388SChristopher Di Bella }
80*f0ea888eSChristopher Di Bella
8139034388SChristopher Di Bella {
8239034388SChristopher Di Bella auto telemetry = invocable_telemetry();
8339034388SChristopher Di Bella auto f2 = invocable_with_telemetry(f, telemetry);
8439034388SChristopher Di Bella is_in_value_result<R, Expected> decltype(auto) result = fold_left_with_iter(r.begin(), r.end(), init, f2);
8539034388SChristopher Di Bella assert(result.in == r.end());
8639034388SChristopher Di Bella assert(result.value == expected);
8739034388SChristopher Di Bella assert(telemetry.invocations == std::ranges::distance(r));
8839034388SChristopher Di Bella assert(telemetry.moves == 0);
8939034388SChristopher Di Bella assert(telemetry.copies == 1);
9039034388SChristopher Di Bella }
9139034388SChristopher Di Bella
9239034388SChristopher Di Bella {
9339034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(r.begin(), r.end(), init, f);
9439034388SChristopher Di Bella assert(result == expected);
9539034388SChristopher Di Bella }
96*f0ea888eSChristopher Di Bella
9739034388SChristopher Di Bella {
9839034388SChristopher Di Bella auto telemetry = invocable_telemetry();
9939034388SChristopher Di Bella auto f2 = invocable_with_telemetry(f, telemetry);
10039034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(r.begin(), r.end(), init, f2);
10139034388SChristopher Di Bella assert(result == expected);
10239034388SChristopher Di Bella assert(telemetry.invocations == std::ranges::distance(r));
10339034388SChristopher Di Bella assert(telemetry.moves == 0);
10439034388SChristopher Di Bella assert(telemetry.copies == 1);
10539034388SChristopher Di Bella }
10639034388SChristopher Di Bella }
10739034388SChristopher Di Bella
10839034388SChristopher Di Bella template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
10939034388SChristopher Di Bella requires std::copyable<R>
check_lvalue_range(R & r,T const & init,F f,Expected const & expected)11039034388SChristopher Di Bella constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
11139034388SChristopher Di Bella {
11239034388SChristopher Di Bella is_in_value_result<R, Expected> decltype(auto) result = fold_left_with_iter(r, init, f);
11339034388SChristopher Di Bella assert(result.in == r.end());
11439034388SChristopher Di Bella assert(result.value == expected);
11539034388SChristopher Di Bella }
116*f0ea888eSChristopher Di Bella
11739034388SChristopher Di Bella {
11839034388SChristopher Di Bella auto telemetry = invocable_telemetry();
11939034388SChristopher Di Bella auto f2 = invocable_with_telemetry(f, telemetry);
12039034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(r, init, f2);
12139034388SChristopher Di Bella assert(result == expected);
12239034388SChristopher Di Bella assert(telemetry.invocations == std::ranges::distance(r));
12339034388SChristopher Di Bella assert(telemetry.moves == 0);
12439034388SChristopher Di Bella assert(telemetry.copies == 1);
12539034388SChristopher Di Bella }
12639034388SChristopher Di Bella
12739034388SChristopher Di Bella {
12839034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(r, init, f);
12939034388SChristopher Di Bella assert(result == expected);
13039034388SChristopher Di Bella }
131*f0ea888eSChristopher Di Bella
13239034388SChristopher Di Bella {
13339034388SChristopher Di Bella auto telemetry = invocable_telemetry();
13439034388SChristopher Di Bella auto f2 = invocable_with_telemetry(f, telemetry);
13539034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(r, init, f2);
13639034388SChristopher Di Bella assert(result == expected);
13739034388SChristopher Di Bella assert(telemetry.invocations == std::ranges::distance(r));
13839034388SChristopher Di Bella assert(telemetry.moves == 0);
13939034388SChristopher Di Bella assert(telemetry.copies == 1);
14039034388SChristopher Di Bella }
14139034388SChristopher Di Bella }
14239034388SChristopher Di Bella
14339034388SChristopher Di Bella template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
14439034388SChristopher Di Bella requires std::copyable<R>
check_rvalue_range(R & r,T const & init,F f,Expected const & expected)14539034388SChristopher Di Bella constexpr void check_rvalue_range(R& r, T const& init, F f, Expected const& expected) {
14639034388SChristopher Di Bella {
14739034388SChristopher Di Bella auto r2 = r;
14839034388SChristopher Di Bella is_dangling_with<Expected> decltype(auto) result = fold_left_with_iter(std::move(r2), init, f);
14939034388SChristopher Di Bella assert(result.value == expected);
15039034388SChristopher Di Bella }
151*f0ea888eSChristopher Di Bella
15239034388SChristopher Di Bella {
15339034388SChristopher Di Bella auto telemetry = invocable_telemetry();
15439034388SChristopher Di Bella auto f2 = invocable_with_telemetry(f, telemetry);
15539034388SChristopher Di Bella auto r2 = r;
15639034388SChristopher Di Bella is_dangling_with<Expected> decltype(auto) result = fold_left_with_iter(std::move(r2), init, f2);
15739034388SChristopher Di Bella assert(result.value == expected);
15839034388SChristopher Di Bella assert(telemetry.invocations == std::ranges::distance(r));
15939034388SChristopher Di Bella assert(telemetry.moves == 0);
16039034388SChristopher Di Bella assert(telemetry.copies == 1);
16139034388SChristopher Di Bella }
16239034388SChristopher Di Bella
16339034388SChristopher Di Bella {
16439034388SChristopher Di Bella auto r2 = r;
16539034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(std::move(r2), init, f);
16639034388SChristopher Di Bella assert(result == expected);
16739034388SChristopher Di Bella }
168*f0ea888eSChristopher Di Bella
16939034388SChristopher Di Bella {
17039034388SChristopher Di Bella auto telemetry = invocable_telemetry();
17139034388SChristopher Di Bella auto f2 = invocable_with_telemetry(f, telemetry);
17239034388SChristopher Di Bella auto r2 = r;
17339034388SChristopher Di Bella std::same_as<Expected> decltype(auto) result = fold_left(std::move(r2), init, f2);
17439034388SChristopher Di Bella assert(result == expected);
17539034388SChristopher Di Bella assert(telemetry.invocations == std::ranges::distance(r));
17639034388SChristopher Di Bella assert(telemetry.moves == 0);
17739034388SChristopher Di Bella assert(telemetry.copies == 1);
17839034388SChristopher Di Bella }
17939034388SChristopher Di Bella }
18039034388SChristopher Di Bella
18139034388SChristopher Di Bella template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
18239034388SChristopher Di Bella requires std::copyable<R>
check(R r,T const & init,F f,Expected const & expected)18339034388SChristopher Di Bella constexpr void check(R r, T const& init, F f, Expected const& expected) {
18439034388SChristopher Di Bella check_iterator(r, init, f, expected);
18539034388SChristopher Di Bella check_lvalue_range(r, init, f, expected);
18639034388SChristopher Di Bella check_rvalue_range(r, init, f, expected);
18739034388SChristopher Di Bella }
18839034388SChristopher Di Bella
empty_range_test_case()18939034388SChristopher Di Bella constexpr void empty_range_test_case() {
19039034388SChristopher Di Bella auto const data = std::vector<int>{};
19139034388SChristopher Di Bella check(data, 100, std::plus(), 100);
19239034388SChristopher Di Bella check(data, -100, std::multiplies(), -100);
19339034388SChristopher Di Bella
19439034388SChristopher Di Bella check(data | std::views::take_while([](auto) { return false; }), 1.23, std::plus(), 1.23);
195*f0ea888eSChristopher Di Bella check(data, Integer(52), &Integer::plus, Integer(52));
19639034388SChristopher Di Bella }
19739034388SChristopher Di Bella
common_range_test_case()19839034388SChristopher Di Bella constexpr void common_range_test_case() {
19939034388SChristopher Di Bella auto const data = std::vector<int>{1, 2, 3, 4};
20039034388SChristopher Di Bella check(data, 0, std::plus(), triangular_sum(data));
20139034388SChristopher Di Bella check(data, 1, std::multiplies(), factorial(data.back()));
20239034388SChristopher Di Bella
20339034388SChristopher Di Bella auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
20439034388SChristopher Di Bella auto const result = x * y * n;
20539034388SChristopher Di Bella n = y;
20639034388SChristopher Di Bella return static_cast<std::size_t>(result);
20739034388SChristopher Di Bella };
20839034388SChristopher Di Bella check(data, 1, multiply_with_prev, factorial(data.size()) * factorial(data.size() - 1));
20939034388SChristopher Di Bella
21039034388SChristopher Di Bella auto fib = [n = 1](auto x, auto) mutable {
21139034388SChristopher Di Bella auto old_x = x;
21239034388SChristopher Di Bella x += n;
21339034388SChristopher Di Bella n = old_x;
21439034388SChristopher Di Bella return x;
21539034388SChristopher Di Bella };
21639034388SChristopher Di Bella check(data, 0, fib, fibonacci(data.back()));
21739034388SChristopher Di Bella
218*f0ea888eSChristopher Di Bella check(data, Integer(0), &Integer::plus, Integer(triangular_sum(data)));
21939034388SChristopher Di Bella }
22039034388SChristopher Di Bella
non_common_range_test_case()22139034388SChristopher Di Bella constexpr void non_common_range_test_case() {
22239034388SChristopher Di Bella auto parse = [](std::string_view const s) {
22339034388SChristopher Di Bella return s == "zero" ? 0.0
22439034388SChristopher Di Bella : s == "one" ? 1.0
22539034388SChristopher Di Bella : s == "two" ? 2.0
22639034388SChristopher Di Bella : s == "three" ? 3.0
22739034388SChristopher Di Bella : s == "four" ? 4.0
22839034388SChristopher Di Bella : s == "five" ? 5.0
22939034388SChristopher Di Bella : s == "six" ? 6.0
23039034388SChristopher Di Bella : s == "seven" ? 7.0
23139034388SChristopher Di Bella : s == "eight" ? 8.0
23239034388SChristopher Di Bella : s == "nine" ? 9.0
23339034388SChristopher Di Bella : (assert(false), 10.0); // the number here is arbitrary
23439034388SChristopher Di Bella };
23539034388SChristopher Di Bella
23639034388SChristopher Di Bella {
23739034388SChristopher Di Bella auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
23839034388SChristopher Di Bella auto range = data | std::views::transform(parse);
23939034388SChristopher Di Bella check(range, 0, std::plus(), triangular_sum(range));
24039034388SChristopher Di Bella }
24139034388SChristopher Di Bella
24239034388SChristopher Di Bella {
24339034388SChristopher Di Bella auto data = std::string("five three two six one four");
24439034388SChristopher Di Bella auto to_string_view = [](auto&& r) {
24539034388SChristopher Di Bella auto const n = std::ranges::distance(r);
24639034388SChristopher Di Bella return std::string_view(&*r.begin(), n);
24739034388SChristopher Di Bella };
24839034388SChristopher Di Bella auto range =
24939034388SChristopher Di Bella std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
25039034388SChristopher Di Bella check(range, 0, std::plus(), triangular_sum(range));
25139034388SChristopher Di Bella }
25239034388SChristopher Di Bella }
25339034388SChristopher Di Bella
test_case()25439034388SChristopher Di Bella constexpr bool test_case() {
25539034388SChristopher Di Bella empty_range_test_case();
25639034388SChristopher Di Bella common_range_test_case();
25739034388SChristopher Di Bella non_common_range_test_case();
25839034388SChristopher Di Bella return true;
25939034388SChristopher Di Bella }
26039034388SChristopher Di Bella
26139034388SChristopher Di Bella // Most containers aren't constexpr
runtime_only_test_case()26239034388SChristopher Di Bella void runtime_only_test_case() {
26339034388SChristopher Di Bella #if !defined(TEST_HAS_NO_LOCALIZATION)
26439034388SChristopher Di Bella { // istream_view is a genuine input range and needs specific handling.
26539034388SChristopher Di Bella constexpr auto raw_data = "Shells Orange Syrup Baratie Cocoyashi Loguetown";
26639034388SChristopher Di Bella constexpr auto expected = "WindmillShellsOrangeSyrupBaratieCocoyashiLoguetown";
26739034388SChristopher Di Bella auto const init = std::string("Windmill");
26839034388SChristopher Di Bella
26939034388SChristopher Di Bella {
27039034388SChristopher Di Bella auto input = std::istringstream(raw_data);
27139034388SChristopher Di Bella auto data = std::views::istream<std::string>(input);
27239034388SChristopher Di Bella is_in_value_result<std::ranges::basic_istream_view<std::string, char>, std::string> decltype(auto) result =
27339034388SChristopher Di Bella fold_left_with_iter(data.begin(), data.end(), init, std::plus());
27439034388SChristopher Di Bella
27539034388SChristopher Di Bella assert(result.in == data.end());
27639034388SChristopher Di Bella assert(result.value == expected);
27739034388SChristopher Di Bella }
278*f0ea888eSChristopher Di Bella
27939034388SChristopher Di Bella {
28039034388SChristopher Di Bella auto input = std::istringstream(raw_data);
28139034388SChristopher Di Bella auto data = std::views::istream<std::string>(input);
28239034388SChristopher Di Bella is_in_value_result<std::ranges::basic_istream_view<std::string, char>, std::string> decltype(auto) result =
28339034388SChristopher Di Bella fold_left_with_iter(data, init, std::plus());
28439034388SChristopher Di Bella assert(result.in == data.end());
28539034388SChristopher Di Bella assert(result.value == expected);
28639034388SChristopher Di Bella }
287*f0ea888eSChristopher Di Bella
28839034388SChristopher Di Bella {
28939034388SChristopher Di Bella auto input = std::istringstream(raw_data);
29039034388SChristopher Di Bella auto data = std::views::istream<std::string>(input);
29139034388SChristopher Di Bella assert(fold_left(data.begin(), data.end(), init, std::plus()) == expected);
29239034388SChristopher Di Bella }
293*f0ea888eSChristopher Di Bella
29439034388SChristopher Di Bella {
29539034388SChristopher Di Bella auto input = std::istringstream(raw_data);
29639034388SChristopher Di Bella auto data = std::views::istream<std::string>(input);
29739034388SChristopher Di Bella assert(fold_left(data, init, std::plus()) == expected);
29839034388SChristopher Di Bella }
29939034388SChristopher Di Bella }
30039034388SChristopher Di Bella #endif
30139034388SChristopher Di Bella {
30239034388SChristopher Di Bella auto const data = std::forward_list<int>{1, 3, 5, 7, 9};
30339034388SChristopher Di Bella auto const n = std::ranges::distance(data);
30439034388SChristopher Di Bella auto const expected = static_cast<float>(n * n); // sum of n consecutive odd numbers = n^2
30539034388SChristopher Di Bella check(data, 0.0f, std::plus(), expected);
30639034388SChristopher Di Bella }
30739034388SChristopher Di Bella
30839034388SChristopher Di Bella {
30939034388SChristopher Di Bella auto const data = std::list<int>{2, 4, 6, 8, 10, 12};
31039034388SChristopher Di Bella auto const expected = triangular_sum(data);
31139034388SChristopher Di Bella check(data, 0, std::plus<long>(), static_cast<long>(expected));
31239034388SChristopher Di Bella }
31339034388SChristopher Di Bella
31439034388SChristopher Di Bella {
31539034388SChristopher Di Bella auto const data = std::deque<double>{-1.1, -2.2, -3.3, -4.4, -5.5, -6.6};
31639034388SChristopher Di Bella auto plus = [](int const x, double const y) { return x + y; };
31739034388SChristopher Di Bella auto const expected = -21.6; // int( 0.0) + -1.1 = 0 + -1.1 = -1.1
31839034388SChristopher Di Bella // int(- 1.1) + -2.2 = - 1 + -2.2 = -3.2
31939034388SChristopher Di Bella // int(- 3.2) + -3.3 = - 3 + -3.3 = -6.3
32039034388SChristopher Di Bella // int(- 6.3) + -4.4 = - 6 + -4.4 = -10.4
32139034388SChristopher Di Bella // int(-10.4) + -5.5 = -10 + -5.5 = -15.5
32239034388SChristopher Di Bella // int(-15.5) + -6.6 = -15 + -6.6 = -21.6.
32339034388SChristopher Di Bella check(data, 0.0, plus, expected);
32439034388SChristopher Di Bella }
32539034388SChristopher Di Bella }
32639034388SChristopher Di Bella
main(int,char **)32739034388SChristopher Di Bella int main(int, char**) {
32839034388SChristopher Di Bella test_case();
32939034388SChristopher Di Bella static_assert(test_case());
33039034388SChristopher Di Bella runtime_only_test_case();
33139034388SChristopher Di Bella return 0;
33239034388SChristopher Di Bella }
333