diff --git a/libs/core/algorithms/CMakeLists.txt b/libs/core/algorithms/CMakeLists.txt index 8f37b9277a31..b14aceb6c63e 100644 --- a/libs/core/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/CMakeLists.txt @@ -50,6 +50,7 @@ set(algorithms_headers hpx/parallel/algorithms/exclusive_scan.hpp hpx/parallel/algorithms/fill.hpp hpx/parallel/algorithms/find.hpp + hpx/parallel/algorithms/fold.hpp hpx/parallel/algorithms/for_each.hpp hpx/parallel/algorithms/for_loop.hpp hpx/parallel/algorithms/for_loop_induction.hpp diff --git a/libs/core/algorithms/include/hpx/parallel/algorithm.hpp b/libs/core/algorithms/include/hpx/parallel/algorithm.hpp index 127c3f8993b8..c249f1c3316e 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithm.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithm.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/fold.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/fold.hpp new file mode 100644 index 000000000000..f27412d31b09 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/fold.hpp @@ -0,0 +1,316 @@ +// Copyright (c) 2020-2022 STE||AR Group +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace hpx { + inline constexpr struct fold_left_t final + : hpx::detail::tag_parallel_algorithm + { + private: + template + friend T tag_fallback_invoke(fold_left_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T init, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return hpx::reduce(HPX_FORWARD(ExPolicy, policy), first, last, init, + HPX_FORWARD(F, f)); + } + + template + friend T tag_fallback_invoke( + fold_left_t, FwdIter first, FwdIter last, T init, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return hpx::reduce( + hpx::execution::seq, first, last, init, HPX_FORWARD(F, f)); + } + } fold_left{}; +} // namespace hpx + +namespace hpx::parallel::detail { + template + auto fold_left_first_helper( + ExPolicy&& policy, FwdIter first, FwdIter last, F&& f) + { + using T = ::hpx::traits::iter_value_t; + using U = decltype(hpx::fold_left(HPX_MOVE(first), last, T(*first), f)); + + if (first == last) + return hpx::optional(); + + T init = *first++; + + return hpx::optional(hpx::fold_left(HPX_FORWARD(ExPolicy, policy), + first, last, HPX_MOVE(init), HPX_MOVE(f))); + } +} // namespace hpx::parallel::detail + +namespace hpx { + inline constexpr struct fold_left_first_t final + : hpx::detail::tag_parallel_algorithm + { + private: + template + friend auto tag_fallback_invoke(fold_left_first_t, ExPolicy&& policy, + FwdIter first, FwdIter last, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return ::hpx::parallel::detail::fold_left_first_helper( + HPX_FORWARD(ExPolicy, policy), first, last, HPX_MOVE(f)); + } + + template + friend auto tag_fallback_invoke( + fold_left_first_t, FwdIter first, FwdIter last, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return ::hpx::parallel::detail::fold_left_first_helper( + hpx::execution::seq, first, last, HPX_MOVE(f)); + } + } fold_left_first{}; +} // namespace hpx + +namespace hpx::parallel::detail { + + template + struct fold_right : public algorithm, T_> + { + constexpr fold_right() noexcept + : algorithm("fold_right") + { + } + + template + HPX_HOST_DEVICE static constexpr auto sequential( + ExPolicy&& policy, FwdIter first, Sent last, T&& init, F&& f) + { + // last++ moves backward when its reverse iterator + return hpx::fold_left(HPX_FORWARD(ExPolicy, policy), + std::make_reverse_iterator(last), + std::make_reverse_iterator(first), HPX_FORWARD(T, init), + HPX_FORWARD(F, f)); + } + + template + static constexpr auto parallel( + ExPolicy&& policy, FwdIter first, Sent last, T&& init, F&& f) + { + if (first == last) + { + return init; + } + + auto ChunkReduce = [f = HPX_FORWARD(F, f), + policy = HPX_FORWARD(ExPolicy, policy)]( + FwdIter it, std::size_t chunkSize) { + FwdIter endIter = it; + std::advance(endIter, --chunkSize); + + T init = *endIter; + + return sequential(policy, it, endIter, init, f); + }; + + auto RecursiveReduce = [f, policy, init](auto&& results) mutable { + auto begin = hpx::util::begin(results); + auto end = hpx::util::end(results); + return sequential(policy, begin, end, init, f); + }; + + return util::partitioner::call( + HPX_FORWARD(ExPolicy, policy), first, + std::distance(first, last), HPX_MOVE(ChunkReduce), + hpx::unwrapping(HPX_MOVE(RecursiveReduce))); + } + }; +} // namespace hpx::parallel::detail + +namespace hpx { + inline constexpr struct fold_right_t final + : hpx::detail::tag_parallel_algorithm + { + private: + template + friend T tag_fallback_invoke(fold_right_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T init, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return hpx::parallel::detail::fold_right().call( + HPX_FORWARD(ExPolicy, policy), first, last, HPX_MOVE(init), + HPX_MOVE(f)); + } + + template + friend T tag_fallback_invoke( + fold_right_t, FwdIter first, FwdIter last, T init, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return hpx::parallel::detail::fold_right().call( + ::hpx::execution::seq, first, last, HPX_MOVE(init), + HPX_MOVE(f)); + } + } fold_right{}; +} // namespace hpx + +namespace hpx::parallel::detail { + template + auto fold_right_first_helper( + ExPolicy&& policy, FwdIter first, FwdIter last, F&& f) + { + using T = ::hpx::traits::iter_value_t; + using U = + decltype(hpx::fold_right(HPX_MOVE(first), last, T(*first), f)); + + if (first == last) + return hpx::optional(); + + T init = *--last; + + return hpx::optional(hpx::fold_right(HPX_FORWARD(ExPolicy, policy), + first, last, HPX_MOVE(init), HPX_MOVE(f))); + } +} // namespace hpx::parallel::detail + +namespace hpx { + inline constexpr struct fold_right_first_t final + : hpx::detail::tag_parallel_algorithm + { + private: + template + friend auto tag_fallback_invoke(fold_right_first_t, ExPolicy&& policy, + FwdIter first, FwdIter last, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return hpx::parallel::detail::fold_right_first_helper( + HPX_FORWARD(ExPolicy, policy), first, last, HPX_MOVE(f)); + } + + template + friend auto tag_fallback_invoke( + fold_right_first_t, FwdIter first, FwdIter last, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return hpx::parallel::detail::fold_right_first_helper( + hpx::execution::seq, first, last, HPX_MOVE(f)); + } + } fold_right_first{}; +} // namespace hpx + +namespace hpx { + inline constexpr struct fold_left_with_iter_t final + : hpx::detail::tag_parallel_algorithm + { + private: + template + using fold_left_with_iter_ty = hpx::ranges::in_value_result; + + template + friend auto tag_fallback_invoke(fold_left_with_iter_t, + ExPolicy&& policy, FwdIter first, FwdIter last, T init, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + T fold_left_value = hpx::fold_left(HPX_FORWARD(ExPolicy, policy), + first, last, init, HPX_FORWARD(F, f)); + + return fold_left_with_iter_ty{ + last, HPX_MOVE(fold_left_value)}; + } + + template + friend auto tag_fallback_invoke( + fold_left_with_iter_t, FwdIter first, FwdIter last, T init, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + T fold_left_value = hpx::fold_left( + hpx::execution::seq, first, last, init, HPX_FORWARD(F, f)); + + return fold_left_with_iter_ty{ + last, HPX_MOVE(fold_left_value)}; + } + } fold_left_with_iter{}; +} // namespace hpx + +namespace hpx::parallel::detail { + + template + auto fold_left_first_with_iter_helper( + ExPolicy&& policy, FwdIter first, Sent last, F&& f) + { + using T = ::hpx::traits::iter_value_t; + using fold_left_first_with_iter_ty = + hpx::ranges::in_value_result>; + + return fold_left_first_with_iter_ty{last, + hpx::fold_left_first( + HPX_FORWARD(ExPolicy, policy), first, last, HPX_MOVE(f))}; + } +} // namespace hpx::parallel::detail + +namespace hpx { + inline constexpr struct fold_left_first_with_iter_t final + : hpx::detail::tag_parallel_algorithm + { + private: + template + friend auto tag_fallback_invoke(fold_left_first_with_iter_t, + ExPolicy&& policy, FwdIter first, Sent last, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return ::hpx::parallel::detail::fold_left_first_with_iter_helper( + HPX_FORWARD(ExPolicy, policy), first, last, f); + } + + template + friend auto tag_fallback_invoke( + fold_left_first_with_iter_t, FwdIter first, Sent last, F f) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Requires at least forward iterator."); + + return ::hpx::parallel::detail::fold_left_first_with_iter_helper( + hpx::execution::seq, first, last, f); + } + } fold_left_first_with_iter{}; +} // namespace hpx diff --git a/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp b/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp index c4a8c0e1374a..ae2577e69394 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp @@ -101,6 +101,25 @@ namespace hpx::parallel::util { } }; + template + struct in_value_result + { + HPX_NO_UNIQUE_ADDRESS Iter in; + HPX_NO_UNIQUE_ADDRESS T value; + + template + constexpr operator in_value_result() const& + { + return {in, value}; + } + + template + constexpr operator in_value_result() && + { + return {HPX_MOVE(in), HPX_MOVE(value)}; + } + }; + /////////////////////////////////////////////////////////////////////// template std::pair get_pair(util::in_out_result&& p) @@ -496,6 +515,7 @@ namespace hpx::ranges { using hpx::parallel::util::in_in_result; using hpx::parallel::util::in_out_out_result; using hpx::parallel::util::in_out_result; + using hpx::parallel::util::in_value_result; using hpx::parallel::util::min_max_result; } // namespace hpx::ranges // namespace hpx::ranges diff --git a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index cbdca61a4bdd..c011bc2bf854 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -50,6 +50,7 @@ set(tests findfirstof_binary findif findifnot + fold_ foreach foreach_executors foreach_prefetching diff --git a/libs/core/algorithms/tests/unit/algorithms/fold_.cpp b/libs/core/algorithms/tests/unit/algorithms/fold_.cpp new file mode 100644 index 000000000000..443e9b3825e2 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/fold_.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2014-2020 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include + +#include "fold_tests.hpp" + +template +void fold_left_test_dispatch_2(IteratorTag) +{ + fold_left_test1(IteratorTag()); + fold_left_test1(hpx::execution::seq, IteratorTag()); + fold_left_test1(hpx::execution::par, IteratorTag()); +} + +void fold_left_test_dispatch_1() +{ + fold_left_test_dispatch_2(std::random_access_iterator_tag()); + fold_left_test_dispatch_2(std::forward_iterator_tag()); +} + +void fold_left_test_dispatch() +{ + fold_left_test_dispatch_1(); +} + +template +void fold_right_test_dispatch_2(IteratorTag) +{ + fold_right_test1(IteratorTag()); + fold_right_test1(hpx::execution::seq, IteratorTag()); + fold_right_test1(hpx::execution::par, IteratorTag()); +} + +void fold_right_test_dispatch_1() +{ + fold_right_test_dispatch_2(std::random_access_iterator_tag()); +} + +void fold_right_test_dispatch() +{ + fold_right_test_dispatch_1(); +} + +template +void fold_left_first_test_dispatch_2(IteratorTag) +{ + fold_left_first_test1(IteratorTag()); + fold_left_first_test1(hpx::execution::seq, IteratorTag()); + fold_left_first_test1(hpx::execution::par, IteratorTag()); +} + +void fold_left_first_test_dispatch_1() +{ + fold_left_first_test_dispatch_2(std::random_access_iterator_tag()); + fold_left_first_test_dispatch_2(std::forward_iterator_tag()); +} + +void fold_left_first_test_dispatch() +{ + fold_left_first_test_dispatch_1(); +} + +template +void fold_right_first_test_dispatch_2(IteratorTag) +{ + fold_right_first_test1(IteratorTag()); + fold_right_first_test1(hpx::execution::seq, IteratorTag()); + fold_right_first_test1(hpx::execution::par, IteratorTag()); +} + +void fold_right_first_test_dispatch_1() +{ + fold_right_first_test_dispatch_2(std::random_access_iterator_tag()); +} + +void fold_right_first_test_dispatch() +{ + fold_right_first_test_dispatch_1(); +} + +template +void fold_left_with_iter_test_dispatch_2(IteratorTag) +{ + fold_left_with_iter_test1(IteratorTag()); + fold_left_with_iter_test1(hpx::execution::seq, IteratorTag()); + fold_left_with_iter_test1(hpx::execution::par, IteratorTag()); +} + +void fold_left_with_iter_test_dispatch_1() +{ + fold_left_with_iter_test_dispatch_2(std::random_access_iterator_tag()); + fold_left_with_iter_test_dispatch_2(std::forward_iterator_tag()); +} + +void fold_left_with_iter_test_dispatch() +{ + fold_left_with_iter_test_dispatch_1(); +} + +template +void fold_left_first_with_iter_test_dispatch_2(IteratorTag) +{ + fold_left_first_with_iter_test1(IteratorTag()); + fold_left_first_with_iter_test1(hpx::execution::seq, IteratorTag()); + fold_left_first_with_iter_test1(hpx::execution::par, IteratorTag()); +} + +void fold_left_first_with_iter_test_dispatch_1() +{ + fold_left_first_with_iter_test_dispatch_2( + std::random_access_iterator_tag()); + fold_left_first_with_iter_test_dispatch_2(std::forward_iterator_tag()); +} + +void fold_left_first_with_iter_test_dispatch() +{ + fold_left_first_with_iter_test_dispatch_1(); +} + +/////////////////////////////////////////////////////////////////////////////// +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + gen.seed(seed); + + fold_left_test_dispatch(); + fold_right_test_dispatch(); + + fold_left_first_test_dispatch(); + fold_right_first_test_dispatch(); + + fold_left_with_iter_test_dispatch(); + fold_left_first_with_iter_test_dispatch(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/fold_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/fold_tests.hpp new file mode 100644 index 000000000000..30768211f5dc --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/fold_tests.hpp @@ -0,0 +1,264 @@ +// Copyright (c) 2014-2020 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +int seed = std::random_device{}(); +std::mt19937 gen(seed); + +// fold_left(begin, end, init, op) +template +void fold_left_test1(IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + int val(1); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + int r1 = + hpx::fold_left(iterator(std::begin(c)), iterator(std::end(c)), val, op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1, r2); +} + +// fold_left(policy, begin, end, init, op) +template +void fold_left_test1(ExPolicy policy, IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + int val(1); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + int r1 = hpx::fold_left( + policy, iterator(std::begin(c)), iterator(std::end(c)), val, op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1, r2); +} + +// fold_right(begin, end, init, op) +template +void fold_right_test1(IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + int val(1); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + int r1 = hpx::fold_right( + iterator(std::begin(c)), iterator(std::end(c)), val, op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1, r2); +} + +// fold_right(policy, begin, end, init, op) +template +void fold_right_test1(ExPolicy policy, IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + int val(1); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + int r1 = hpx::fold_right( + policy, iterator(std::begin(c)), iterator(std::end(c)), val, op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1, r2); +} + +// fold_left(begin, end, init, op) +template +void fold_left_first_test1(IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::optional r1 = hpx::fold_left_first( + iterator(std::begin(c)), iterator(std::end(c)), op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value(), r2); +} + +// fold_left_first(policy, begin, end, op) +template +void fold_left_first_test1(ExPolicy policy, IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::optional r1 = hpx::fold_left_first( + policy, iterator(std::begin(c)), iterator(std::end(c)), op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value(), r2); +} + +// fold_right_first(begin, end, op) +template +void fold_right_first_test1(IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::optional r1 = hpx::fold_right_first( + iterator(std::begin(c)), iterator(std::end(c)), op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value(), r2); +} + +// fold_right_first(policy, begin, end, op) +template +void fold_right_first_test1(ExPolicy policy, IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::optional r1 = hpx::fold_right_first( + policy, iterator(std::begin(c)), iterator(std::end(c)), op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value(), r2); +} + +// fold_left(begin, end, init, op) +template +void fold_left_with_iter_test1(IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + int val(1); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::ranges::in_value_result r1 = hpx::fold_left_with_iter( + iterator(std::begin(c)), iterator(std::end(c)), val, op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value, r2); + HPX_TEST(r1.in == iterator(std::end(c))); +} + +// fold_left_first_with_iter(policy, begin, end, init, op) +template +void fold_left_with_iter_test1(ExPolicy policy, IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + int val(1); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::ranges::in_value_result> r1 = + hpx::fold_left_with_iter( + policy, iterator(std::begin(c)), iterator(std::end(c)), val, op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value.value(), r2); + HPX_TEST(r1.in == iterator(std::end(c))); +} + +// fold_left(begin, end, init, op) +template +void fold_left_first_with_iter_test1(IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::ranges::in_value_result> r1 = + hpx::fold_left_first_with_iter( + iterator(std::begin(c)), iterator(std::end(c)), op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value.value(), r2); + HPX_TEST(r1.in == iterator(std::end(c))); +} + +// fold_left_first_with_iter(policy, begin, end, init, op) +template +void fold_left_first_with_iter_test1(ExPolicy policy, IteratorTag) +{ + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c = {1, 2, 3, 4, 5}; + + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + hpx::ranges::in_value_result> r1 = + hpx::fold_left_first_with_iter( + policy, iterator(std::begin(c)), iterator(std::end(c)), op); + + // verify values + int r2 = 120; + HPX_TEST_EQ(r1.value.value(), r2); + HPX_TEST(r1.in == iterator(std::end(c))); +}