xref: /llvm-project/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp (revision f0ea888e01aabcb131a8931b9e1fe1c5212a6cba)
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