diff --git a/examples/quickstart/CMakeLists.txt b/examples/quickstart/CMakeLists.txt index f6532a710ef..b9d560677d6 100644 --- a/examples/quickstart/CMakeLists.txt +++ b/examples/quickstart/CMakeLists.txt @@ -42,7 +42,9 @@ if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT HPX_WITH_STATIC_LINKING) set(example_programs ${example_programs} init_globally) endif() -if(NOT (MSVC AND HPX_WITH_CXX_MODULES)) +if(NOT ((MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") AND HPX_WITH_CXX_MODULES + ) +) list(APPEND example_programs sender_diamond) endif() diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp index 203f92dfdd6..bf4b3bdd155 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp @@ -614,13 +614,14 @@ namespace hpx::parallel { }); }; - auto f4 = [first, dest, flags](std::vector&& items, + auto f4 = [first, count, dest, flags]( + std::vector&& items, std::vector>&& data) mutable -> util::in_out_result { HPX_UNUSED(flags); auto dist = items.back(); - std::advance(first, dist); + std::advance(first, count); std::advance(dest, dist); // make sure iterators embedded in function object that is diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/remove_copy.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/remove_copy.hpp index 3a9cd5364b7..ae968be5f6f 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/remove_copy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/remove_copy.hpp @@ -382,13 +382,10 @@ namespace hpx::parallel { parallel(ExPolicy&& policy, FwdIter1 first, Sent last, FwdIter2 dest, F&& f, Proj&& proj) { - using value_type = - typename std::iterator_traits::value_type; - return copy_if().call( HPX_FORWARD(ExPolicy, policy), first, last, dest, - [f = HPX_FORWARD(F, f)](value_type const& a) -> bool { - return !HPX_INVOKE(f, a); + [f = HPX_FORWARD(F, f)](auto&& a) -> bool { + return !HPX_INVOKE(f, HPX_FORWARD(decltype(a), a)); }, HPX_FORWARD(Proj, proj)); } @@ -444,10 +441,12 @@ namespace hpx { > ) // clang-format on + // clang-format off friend typename parallel::util::detail::algorithm_result::type tag_fallback_invoke(hpx::remove_copy_if_t, ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, Pred pred) + // clang-format on { static_assert(std::forward_iterator, "Required at least forward iterator."); @@ -503,10 +502,12 @@ namespace hpx { hpx::traits::is_iterator_v ) // clang-format on + // clang-format off friend typename parallel::util::detail::algorithm_result::type tag_fallback_invoke(hpx::remove_copy_t, ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, T const& value) + // clang-format on { static_assert(std::forward_iterator, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp index 62f43d47039..b0172d793b9 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp @@ -339,11 +339,11 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = - typename std::iterator_traits::value_type; + using projected_type = std::decay_t::reference>>; FwdIter result = first; - element_type result_projected = HPX_INVOKE(proj, *result); + projected_type result_projected = HPX_INVOKE(proj, *result); while (++first != last) { if (!HPX_INVOKE( @@ -500,12 +500,12 @@ namespace hpx::parallel { HPX_MOVE(first), HPX_MOVE(dest)}; } - using element_type = - typename std::iterator_traits::value_type; + using projected_type = std::decay_t::reference>>; FwdIter base = first; *dest++ = *first; - element_type base_projected = HPX_INVOKE(proj, *base); + projected_type base_projected = HPX_INVOKE(proj, *base); while (++first != last) { @@ -533,8 +533,10 @@ namespace hpx::parallel { using element_type = typename std::iterator_traits::value_type; + using projected_type = std::decay_t::reference>>; element_type base_val = *first; - element_type base_projected = HPX_INVOKE(proj, base_val); + projected_type base_projected = HPX_INVOKE(proj, base_val); *dest++ = base_val; diff --git a/libs/core/algorithms/include/hpx/parallel/container_algorithms/remove_copy.hpp b/libs/core/algorithms/include/hpx/parallel/container_algorithms/remove_copy.hpp index 218dece502f..da5b286651f 100644 --- a/libs/core/algorithms/include/hpx/parallel/container_algorithms/remove_copy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/container_algorithms/remove_copy.hpp @@ -614,8 +614,9 @@ namespace hpx::ranges { hpx::parallel::traits::is_projected_v && std::sentinel_for && hpx::traits::is_iterator_v && - hpx::is_invocable_v::value_type + hpx::parallel::traits::is_indirect_callable_v< + hpx::execution::sequenced_policy, Pred, + hpx::parallel::traits::projected > ) // clang-format on @@ -638,12 +639,11 @@ namespace hpx::ranges { typename Proj = hpx::identity> // clang-format off requires( - std::ranges::range&& - hpx::parallel::traits::is_projected_range_v && - hpx::is_invocable_v - >::value_type + std::ranges::range && + hpx::parallel::traits::is_projected_range_v && + hpx::parallel::traits::is_indirect_callable_v< + hpx::execution::sequenced_policy, Pred, + hpx::parallel::traits::projected_range > ) // clang-format on @@ -664,13 +664,14 @@ namespace hpx::ranges { typename Pred, typename Proj = hpx::identity> // clang-format off requires( - hpx::is_execution_policy_v&& + hpx::is_execution_policy_v && hpx::traits::is_iterator_v && std::sentinel_for && hpx::traits::is_iterator_v && hpx::parallel::traits::is_projected_v && - hpx::is_invocable_v::value_type + hpx::parallel::traits::is_indirect_callable_v< + ExPolicy, Pred, + hpx::parallel::traits::projected > ) // clang-format on @@ -698,10 +699,9 @@ namespace hpx::ranges { hpx::is_execution_policy_v && std::ranges::range && hpx::parallel::traits::is_projected_range_v && - hpx::is_invocable_v - >::value_type + hpx::parallel::traits::is_indirect_callable_v< + ExPolicy, Pred, + hpx::parallel::traits::projected_range > ) // clang-format on @@ -745,11 +745,9 @@ namespace hpx::ranges { static_assert( std::input_iterator, "Required at least input iterator."); - using type = typename std::iterator_traits::value_type; - return hpx::ranges::remove_copy_if( first, last, dest, - [value](type const& a) -> bool { return value == a; }, + [value](T const& a) -> bool { return value == a; }, HPX_MOVE(proj)); } @@ -769,12 +767,9 @@ namespace hpx::ranges { static_assert(std::input_iterator>, "Required at input forward iterator."); - using type = typename std::iterator_traits< - std::ranges::iterator_t>::value_type; - return hpx::ranges::remove_copy_if( HPX_FORWARD(Rng, rng), dest, - [value](type const& a) -> bool { return value == a; }, + [value](T const& a) -> bool { return value == a; }, HPX_MOVE(proj)); } @@ -799,11 +794,9 @@ namespace hpx::ranges { static_assert(std::forward_iterator, "Required at least forward iterator."); - using type = typename std::iterator_traits::value_type; - return hpx::ranges::remove_copy_if( HPX_FORWARD(ExPolicy, policy), first, last, dest, - [value](type const& a) -> bool { return value == a; }, + [value](T const& a) -> bool { return value == a; }, HPX_MOVE(proj)); } @@ -826,12 +819,9 @@ namespace hpx::ranges { static_assert(std::forward_iterator>, "Required at least forward iterator."); - using type = typename std::iterator_traits< - std::ranges::iterator_t>::value_type; - return hpx::ranges::remove_copy_if( HPX_FORWARD(ExPolicy, policy), HPX_FORWARD(Rng, rng), dest, - [value](type const& a) -> bool { return value == a; }, + [value](T const& a) -> bool { return value == a; }, HPX_MOVE(proj)); } } remove_copy{}; diff --git a/libs/core/algorithms/tests/unit/container_algorithms/partition_copy_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/partition_copy_range.cpp index b3119f36f5c..9754fb85349 100644 --- a/libs/core/algorithms/tests/unit/container_algorithms/partition_copy_range.cpp +++ b/libs/core/algorithms/tests/unit/container_algorithms/partition_copy_range.cpp @@ -280,10 +280,150 @@ void test_partition_copy() test_partition_copy_sent(par_unseq); } +//////////////////////////////////////////////////////////////////////////// +// Projection tests: project on the 'val' field of user_defined_type and +// apply a simple integer threshold predicate, distinct from the name-aware +// operator<(int) used by the existing tests. +void test_partition_copy_projection() +{ + using DataType = user_defined_type; + using hpx::get; + + std::size_t const size = 10007; + int rand_base = std::rand(); + + // pred operates on the projected int value + auto proj = [](DataType const& t) -> int { return t.val; }; + auto pred = [rand_base](int v) -> bool { return v < rand_base; }; + // oracle: apply proj then pred inline + auto std_pred = [rand_base, &proj](DataType const& e) -> bool { + return proj(e) < rand_base; + }; + + // No-policy (sequential) - range form + { + std::vector c(size), d_true(size), d_false(size), t_sol(size), + f_sol(size); + std::generate( + std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + auto res = hpx::ranges::partition_copy( + c, std::begin(d_true), std::begin(d_false), pred, proj); + auto sol = std::partition_copy(std::begin(c), std::end(c), + std::begin(t_sol), std::begin(f_sol), std_pred); + + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal( + std::begin(d_true), res.out1, std::begin(t_sol), sol.first)); + HPX_TEST(test::equal( + std::begin(d_false), res.out2, std::begin(f_sol), sol.second)); + } + + // seq policy + { + std::vector c(size), d_true(size), d_false(size), t_sol(size), + f_sol(size); + std::generate( + std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + auto res = hpx::ranges::partition_copy(hpx::execution::seq, c, + std::begin(d_true), std::begin(d_false), pred, proj); + auto sol = std::partition_copy(std::begin(c), std::end(c), + std::begin(t_sol), std::begin(f_sol), std_pred); + + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal( + std::begin(d_true), res.out1, std::begin(t_sol), sol.first)); + HPX_TEST(test::equal( + std::begin(d_false), res.out2, std::begin(f_sol), sol.second)); + } + + // par policy + { + std::vector c(size), d_true(size), d_false(size), t_sol(size), + f_sol(size); + std::generate( + std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + auto res = hpx::ranges::partition_copy(hpx::execution::par, c, + std::begin(d_true), std::begin(d_false), pred, proj); + auto sol = std::partition_copy(std::begin(c), std::end(c), + std::begin(t_sol), std::begin(f_sol), std_pred); + + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal( + std::begin(d_true), res.out1, std::begin(t_sol), sol.first)); + HPX_TEST(test::equal( + std::begin(d_false), res.out2, std::begin(f_sol), sol.second)); + } + + // par_unseq policy + { + std::vector c(size), d_true(size), d_false(size), t_sol(size), + f_sol(size); + std::generate( + std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + auto res = hpx::ranges::partition_copy(hpx::execution::par_unseq, c, + std::begin(d_true), std::begin(d_false), pred, proj); + auto sol = std::partition_copy(std::begin(c), std::end(c), + std::begin(t_sol), std::begin(f_sol), std_pred); + + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal( + std::begin(d_true), res.out1, std::begin(t_sol), sol.first)); + HPX_TEST(test::equal( + std::begin(d_false), res.out2, std::begin(f_sol), sol.second)); + } + + // seq(task) - async + { + std::vector c(size), d_true(size), d_false(size), t_sol(size), + f_sol(size); + std::generate( + std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + auto f = hpx::ranges::partition_copy( + hpx::execution::seq(hpx::execution::task), c, std::begin(d_true), + std::begin(d_false), pred, proj); + auto sol = std::partition_copy(std::begin(c), std::end(c), + std::begin(t_sol), std::begin(f_sol), std_pred); + auto res = f.get(); + + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal( + std::begin(d_true), res.out1, std::begin(t_sol), sol.first)); + HPX_TEST(test::equal( + std::begin(d_false), res.out2, std::begin(f_sol), sol.second)); + } + + // par(task) - async + { + std::vector c(size), d_true(size), d_false(size), t_sol(size), + f_sol(size); + std::generate( + std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + auto f = hpx::ranges::partition_copy( + hpx::execution::par(hpx::execution::task), c, std::begin(d_true), + std::begin(d_false), pred, proj); + auto sol = std::partition_copy(std::begin(c), std::end(c), + std::begin(t_sol), std::begin(f_sol), std_pred); + auto res = f.get(); + + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal( + std::begin(d_true), res.out1, std::begin(t_sol), sol.first)); + HPX_TEST(test::equal( + std::begin(d_false), res.out2, std::begin(f_sol), sol.second)); + } +} + void test_partition_copy() { test_partition_copy(); test_partition_copy(); + test_partition_copy_projection(); } int hpx_main(hpx::program_options::variables_map& vm) diff --git a/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_if_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_if_range.cpp index bba044a4207..5215beb9525 100644 --- a/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_if_range.cpp +++ b/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_if_range.cpp @@ -20,6 +20,21 @@ #include "test_utils.hpp" +//////////////////////////////////////////////////////////////////////////// +// projected_element: two-field struct for testing projection support. +// 'key' is the value inspected by the projection; 'tag' distinguishes +// elements that share the same key, allowing post-hoc identity checks. +struct projected_element +{ + int key; + int tag; + + bool operator==(projected_element const& o) const noexcept + { + return key == o.key && tag == o.tag; + } +}; + //////////////////////////////////////////////////////////////////////////// void test_remove_copy_if_sent() { @@ -252,6 +267,91 @@ void remove_copy_if_test() test_remove_copy_if(); } +//////////////////////////////////////////////////////////////////////////////// +// Projection tests for hpx::ranges::remove_copy_if +// The projection extracts 'key'; predicate removes elements with odd key. +void test_remove_copy_if_projection() +{ + // Build input: 20 elements, keys cycling 0..4, tags unique + std::size_t const size = 20; + std::vector c(size); + for (std::size_t i = 0; i != size; ++i) + c[i] = {static_cast(i % 5), static_cast(i)}; + + auto proj = [](projected_element const& e) -> int { return e.key; }; + // Remove elements where projected key is odd + auto pred = [](int k) -> bool { return k % 2 != 0; }; + + // Expected: only elements with even key are kept + std::vector expected; + for (auto const& e : c) + if (e.key % 2 == 0) + expected.push_back(e); + + // No-policy (sequential) - range form + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy_if(c, std::begin(dest), pred, proj); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // seq policy + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy_if( + hpx::execution::seq, c, std::begin(dest), pred, proj); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // par policy + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy_if( + hpx::execution::par, c, std::begin(dest), pred, proj); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // par_unseq policy + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy_if( + hpx::execution::par_unseq, c, std::begin(dest), pred, proj); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // seq(task) - async + { + std::vector dest(size); + auto f = hpx::ranges::remove_copy_if( + hpx::execution::seq(hpx::execution::task), c, std::begin(dest), + pred, proj); + auto res = f.get(); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // par(task) - async + { + std::vector dest(size); + auto f = hpx::ranges::remove_copy_if( + hpx::execution::par(hpx::execution::task), c, std::begin(dest), + pred, proj); + auto res = f.get(); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } +} + /////////////////////////////////////////////////////////////////////////////// template void test_remove_copy_if_exception(ExPolicy policy, IteratorTag) @@ -445,6 +545,7 @@ int hpx_main(hpx::program_options::variables_map& vm) remove_copy_if_test(); remove_copy_if_exception_test(); remove_copy_if_bad_alloc_test(); + test_remove_copy_if_projection(); return hpx::local::finalize(); } diff --git a/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_range.cpp index b90f6be3502..73b4937ad4f 100644 --- a/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_range.cpp +++ b/libs/core/algorithms/tests/unit/container_algorithms/remove_copy_range.cpp @@ -21,6 +21,21 @@ #include "test_utils.hpp" +//////////////////////////////////////////////////////////////////////////////// +// projected_element: two-field struct for testing projection support. +// 'key' is the value inspected by the projection; 'tag' distinguishes +// elements that share the same key, allowing post-hoc identity checks. +struct projected_element +{ + int key; + int tag; + + bool operator==(projected_element const& o) const noexcept + { + return key == o.key && tag == o.tag; + } +}; + //////////////////////////////////////////////////////////////////////////////// void test_remove_copy_sent() { @@ -233,6 +248,89 @@ void remove_copy_test() test_remove_copy(); } +//////////////////////////////////////////////////////////////////////////////// +// Projection tests for hpx::ranges::remove_copy +// The projection extracts 'key'; removal is done when INVOKE(proj,*it)==value. +void test_remove_copy_projection() +{ + // Build input: 20 elements, keys cycling 0..4, tags unique + std::size_t const size = 20; + std::vector c(size); + for (std::size_t i = 0; i != size; ++i) + c[i] = {static_cast(i % 5), static_cast(i)}; + + // Remove all elements where key == 2 via projection + int const remove_key = 2; + auto proj = [](projected_element const& e) -> int { return e.key; }; + + // Expected: elements with key != 2 (4 out of every 5 are kept) + std::vector expected; + for (auto const& e : c) + if (e.key != remove_key) + expected.push_back(e); + + // No-policy (sequential) - range form + { + std::vector dest(size); + auto res = + hpx::ranges::remove_copy(c, std::begin(dest), remove_key, proj); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // seq policy + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy( + hpx::execution::seq, c, std::begin(dest), remove_key, proj); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // par policy + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy( + hpx::execution::par, c, std::begin(dest), remove_key, proj); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // par_unseq policy + { + std::vector dest(size); + auto res = hpx::ranges::remove_copy( + hpx::execution::par_unseq, c, std::begin(dest), remove_key, proj); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // seq(task) - async + { + std::vector dest(size); + auto f = + hpx::ranges::remove_copy(hpx::execution::seq(hpx::execution::task), + c, std::begin(dest), remove_key, proj); + auto res = f.get(); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } + + // par(task) - async + { + std::vector dest(size); + auto f = + hpx::ranges::remove_copy(hpx::execution::par(hpx::execution::task), + c, std::begin(dest), remove_key, proj); + auto res = f.get(); + HPX_TEST(test::equal(std::begin(dest), res.out, std::begin(expected), + std::end(expected))); + } +} + /////////////////////////////////////////////////////////////////////////////// template void test_remove_copy_exception(ExPolicy policy, IteratorTag) @@ -441,6 +539,7 @@ int hpx_main(hpx::program_options::variables_map& vm) remove_copy_test(); remove_copy_exception_test(); remove_copy_bad_alloc_test(); + test_remove_copy_projection(); return hpx::local::finalize(); } diff --git a/libs/core/algorithms/tests/unit/container_algorithms/unique_copy_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/unique_copy_range.cpp index 052cf47d3fa..218d8627f49 100644 --- a/libs/core/algorithms/tests/unit/container_algorithms/unique_copy_range.cpp +++ b/libs/core/algorithms/tests/unit/container_algorithms/unique_copy_range.cpp @@ -207,10 +207,107 @@ void test_unique_copy() hpx::execution::par(hpx::execution::task), DataType()); } +//////////////////////////////////////////////////////////////////////////// +// Projection tests: project on the 'val' field of user_defined_type so +// that two elements with the same val but different name are considered +// equal by the predicate, exercising the Proj code path. +void test_unique_copy_projection() +{ + using hpx::get; + using DataType = user_defined_type; + + // With range [0,6), many consecutive val-duplicates will exist + std::size_t const size = 10007; + std::vector c(size), dest_res(size), dest_sol(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + + auto proj = [](DataType const& t) -> int { return t.val; }; + auto pred = [](int a, int b) -> bool { return a == b; }; + auto std_pred = [](DataType const& a, DataType const& b) -> bool { + return a.val == b.val; + }; + + // Sequential (no policy) + { + std::vector dr(size), ds(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + auto res = hpx::ranges::unique_copy(c, std::begin(dr), pred, proj); + auto sol = std::unique_copy( + std::begin(c), std::end(c), std::begin(ds), std_pred); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dr), res.out, std::begin(ds), sol)); + } + + // seq policy + { + std::vector dr(size), ds(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + auto res = hpx::ranges::unique_copy( + hpx::execution::seq, c, std::begin(dr), pred, proj); + auto sol = std::unique_copy( + std::begin(c), std::end(c), std::begin(ds), std_pred); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dr), res.out, std::begin(ds), sol)); + } + + // par policy + { + std::vector dr(size), ds(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + auto res = hpx::ranges::unique_copy( + hpx::execution::par, c, std::begin(dr), pred, proj); + auto sol = std::unique_copy( + std::begin(c), std::end(c), std::begin(ds), std_pred); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dr), res.out, std::begin(ds), sol)); + } + + // par_unseq policy + { + std::vector dr(size), ds(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + auto res = hpx::ranges::unique_copy( + hpx::execution::par_unseq, c, std::begin(dr), pred, proj); + auto sol = std::unique_copy( + std::begin(c), std::end(c), std::begin(ds), std_pred); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dr), res.out, std::begin(ds), sol)); + } + + // seq(task) - async + { + std::vector dr(size), ds(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + auto f = + hpx::ranges::unique_copy(hpx::execution::seq(hpx::execution::task), + c, std::begin(dr), pred, proj); + auto sol = std::unique_copy( + std::begin(c), std::end(c), std::begin(ds), std_pred); + auto res = f.get(); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dr), res.out, std::begin(ds), sol)); + } + + // par(task) - async + { + std::vector dr(size), ds(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + auto f = + hpx::ranges::unique_copy(hpx::execution::par(hpx::execution::task), + c, std::begin(dr), pred, proj); + auto sol = std::unique_copy( + std::begin(c), std::end(c), std::begin(ds), std_pred); + auto res = f.get(); + HPX_TEST(res.in == std::end(c)); + HPX_TEST(test::equal(std::begin(dr), res.out, std::begin(ds), sol)); + } +} + void test_unique_copy() { test_unique_copy(); test_unique_copy(); + test_unique_copy_projection(); } int hpx_main(hpx::program_options::variables_map& vm)