diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp index 27d7c543a52..60fd4fa36a2 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp @@ -276,27 +276,34 @@ namespace hpx::parallel { util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; - - constexpr bool scheduler_policy = + constexpr bool has_scheduler_policy = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!scheduler_policy) + FwdIter1 prev = first; + difference_type count; + + if (first == last) { - if (first == last) + if constexpr (!has_scheduler_policy) { return result::get(HPX_MOVE(dest)); } + else + { + count = static_cast(0); + } } + else + { + count = detail::distance(first, last) - 1; - difference_type count = detail::distance(first, last) - 1; - - FwdIter1 prev = first; - hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type> - tmp = *first++; - *dest++ = HPX_MOVE(tmp); + hpx::traits::proxy_value_t< + typename std::iterator_traits::value_type> + tmp = *first++; + *dest++ = HPX_MOVE(tmp); + } - if constexpr (!scheduler_policy) + if constexpr (!has_scheduler_policy) { if (count == 0) { diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp index 519df47ca03..510f3124db9 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp @@ -182,10 +182,10 @@ namespace hpx::parallel { using result = util::detail::algorithm_result; - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!is_scheduler_policy) + if constexpr (!has_scheduler_executor) { if (first == last) { @@ -236,6 +236,13 @@ namespace hpx::parallel { return HPX_MOVE(first); }; + if constexpr (has_scheduler_executor) + { + // underflow prevention for the upcoming call + if (count == 0) + ++count; + } + using partitioner_type = util::partitioner; return partitioner_type::call_with_index( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp index d8a16c1b472..c9a5ae0bd0b 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp @@ -377,10 +377,10 @@ namespace hpx::parallel { Sent last, F&& op, Proj&& proj) { using result = util::detail::algorithm_result; - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!is_scheduler_policy) + if constexpr (!has_scheduler_executor) { if (first == last) { @@ -389,12 +389,15 @@ namespace hpx::parallel { } using policy_type = std::decay_t; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [op = HPX_FORWARD(F, op), tok, proj = HPX_FORWARD(Proj, proj)]( FwdIter part_begin, - std::size_t part_count) mutable -> bool { + std::size_t part_count) mutable + -> intermediate_result_t { detail::sequential_find_if(part_begin, part_count, tok, HPX_FORWARD(F, op), HPX_FORWARD(Proj, proj)); @@ -411,9 +414,11 @@ namespace hpx::parallel { hpx::util::end(results); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), first, - detail::distance(first, last), HPX_MOVE(f1), HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), + first, detail::distance(first, last), HPX_MOVE(f1), + HPX_MOVE(f2)); } }; /// \endcond @@ -447,10 +452,10 @@ namespace hpx::parallel { Sent last, F&& op, Proj&& proj) { using result = util::detail::algorithm_result; - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!is_scheduler_policy) + if constexpr (!has_scheduler_executor) { if (first == last) { @@ -459,12 +464,15 @@ namespace hpx::parallel { } using policy_type = std::decay_t; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [op = HPX_FORWARD(F, op), tok, proj = HPX_FORWARD(Proj, proj)]( FwdIter part_begin, - std::size_t part_count) mutable -> bool { + std::size_t part_count) mutable + -> intermediate_result_t { detail::sequential_find_if(part_begin, part_count, tok, HPX_FORWARD(F, op), HPX_FORWARD(Proj, proj)); @@ -481,9 +489,11 @@ namespace hpx::parallel { hpx::util::end(results); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), first, - detail::distance(first, last), HPX_MOVE(f1), HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), + first, detail::distance(first, last), HPX_MOVE(f1), + HPX_MOVE(f2)); } }; /// \endcond @@ -516,10 +526,10 @@ namespace hpx::parallel { Sent last, F&& op, Proj&& proj) { using result = util::detail::algorithm_result; - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!is_scheduler_policy) + if constexpr (!has_scheduler_executor) { if (first == last) { @@ -528,12 +538,15 @@ namespace hpx::parallel { } using policy_type = std::decay_t; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [op = HPX_FORWARD(F, op), tok, proj = HPX_FORWARD(Proj, proj)]( FwdIter part_begin, - std::size_t part_count) mutable -> bool { + std::size_t part_count) mutable + -> intermediate_result_t { detail::sequential_find_if_not(part_begin, part_count, tok, HPX_FORWARD(F, op), HPX_FORWARD(Proj, proj)); @@ -550,9 +563,11 @@ namespace hpx::parallel { hpx::util::end(results); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), first, - detail::distance(first, last), HPX_MOVE(f1), HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), + first, detail::distance(first, last), HPX_MOVE(f1), + HPX_MOVE(f2)); } }; /// \endcond diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp index f859d99d857..c1eca482f7b 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp @@ -721,12 +721,12 @@ namespace hpx { hpx::traits::is_output_iterator_v), "Requires at least forward iterator or sequential execution."); - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; if (hpx::parallel::detail::is_negative(count)) { - if constexpr (is_scheduler_policy) + if constexpr (has_scheduler_executor) { count = static_cast(0); } diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp index a85904d72a5..823d67b62c9 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp @@ -325,10 +325,10 @@ namespace hpx::parallel { static decltype(auto) parallel(ExPolicy&& policy, IterB first, IterE last, T const& value, Proj&& proj) { - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!is_scheduler_policy) + if constexpr (!has_scheduler_executor) { if (first == last) { @@ -390,10 +390,10 @@ namespace hpx::parallel { static decltype(auto) parallel(ExPolicy&& policy, IterB first, IterE last, Pred&& op, Proj&& proj) { - constexpr bool is_scheduler_policy = + constexpr bool has_scheduler_executor = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!is_scheduler_policy) + if constexpr (!has_scheduler_executor) { if (first == last) { diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp index e4af9c099c9..1e4049fef36 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp @@ -193,8 +193,10 @@ namespace hpx::parallel { decltype(auto) parallel_sequential_destroy_n( ExPolicy&& policy, Iter first, std::size_t count) { - if constexpr (!hpx::execution_policy_has_scheduler_executor_v< - ExPolicy>) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { if (count == 0) { @@ -344,11 +346,12 @@ namespace hpx { { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; if (hpx::parallel::detail::is_negative(count)) { - if constexpr (hpx::execution_policy_has_scheduler_executor_v< - ExPolicy>) + if constexpr (has_scheduler_executor) { count = static_cast(0); } diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp index c1e45eb24ad..8234f5c3ca3 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp @@ -147,17 +147,18 @@ namespace hpx::parallel { Sent1 last1, Iter2 first2, Sent2 last2, Pred&& pred, Proj1&& proj1, Proj2&& proj2) { - auto const drop = detail::distance(first1, last1) - - detail::distance(first2, last2); + auto distance1 = detail::distance(first1, last1); + auto distance2 = detail::distance(first2, last2); - if (drop < 0) + if (distance1 < distance2) return false; + std::advance(first1, distance1 - distance2); + return hpx::parallel::detail::equal_binary().call( - hpx::execution::seq, std::next(HPX_MOVE(first1), drop), - HPX_MOVE(last1), HPX_MOVE(first2), HPX_MOVE(last2), - HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj1, proj1), - HPX_FORWARD(Proj2, proj2)); + hpx::execution::seq, HPX_MOVE(first1), HPX_MOVE(last1), + HPX_MOVE(first2), HPX_MOVE(last2), HPX_FORWARD(Pred, pred), + HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); } template ; + + auto distance1 = detail::distance(first1, last1); + auto distance2 = detail::distance(first2, last2); + auto diff = distance1 - distance2; - if (drop < 0) + if (distance1 < distance2) { - if constexpr (hpx:: - execution_policy_has_scheduler_executor_v< - ExPolicy>) + if constexpr (has_scheduler_executor) { - return hpx::execution::experimental::transfer_just( - policy.executor().sched(), false); + diff = 0; } else { @@ -186,11 +188,13 @@ namespace hpx::parallel { } } + std::advance(first1, diff); + return hpx::parallel::detail::equal_binary().call( - HPX_FORWARD(ExPolicy, policy), - std::next(HPX_MOVE(first1), drop), HPX_MOVE(last1), - HPX_MOVE(first2), HPX_MOVE(last2), HPX_FORWARD(Pred, pred), - HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); + HPX_FORWARD(ExPolicy, policy), HPX_MOVE(first1), + HPX_MOVE(last1), HPX_MOVE(first2), HPX_MOVE(last2), + HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); } }; /// \endcond diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp index f10ce010716..f1039c72961 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp @@ -508,21 +508,24 @@ namespace hpx::parallel { typename std::iterator_traits::difference_type; using difference_type2 = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + execution_policy_has_scheduler_executor_v; - if (first1 == last1) + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - first2 == last2); + if (first1 == last1) + { + return util::detail::algorithm_result::get(first2 == last2); + } + + if (first2 == last2) + { + return util::detail::algorithm_result::get(false); + } } - if (first2 == last2) - { - return util::detail::algorithm_result::get( - false); - } - - difference_type1 count1 = detail::distance(first1, last1); - // The specification of std::equal(_binary) states that if // FwdIter1 and FwdIter2 meet the requirements of // RandomAccessIterator and last1 - first1 != last2 - first2 @@ -530,11 +533,18 @@ namespace hpx::parallel { // // We perform this check for any iterator type better than input // iterators. This could turn into a QoI issue. + + difference_type1 count1 = detail::distance(first1, last1); difference_type2 count2 = detail::distance(first2, last2); - if (count1 != count2) + bool different_lengths = (count1 != count2); + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - false); + if (different_lengths) + { + return util::detail::algorithm_result::get(false); + } } auto policy = parallel::util::adapt_placement_mode( @@ -543,27 +553,41 @@ namespace hpx::parallel { using policy_type = decltype(policy); using zip_iterator = hpx::util::zip_iterator; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [tok, f = HPX_FORWARD(F, f), proj1 = HPX_FORWARD(Proj1, proj1), proj2 = HPX_FORWARD(Proj2, proj2)]( - zip_iterator it, - std::size_t part_count) mutable -> bool { + zip_iterator it, std::size_t part_count) mutable + -> intermediate_result_t { sequential_equal_binary(it, part_count, tok, HPX_FORWARD(F, f), HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); return !tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), zip_iterator(first1, first2), count1, HPX_MOVE(f1), - [](auto&& results) -> bool { + [different_lengths](auto&& results) -> bool { + if constexpr (has_scheduler_executor) + { + if (different_lengths) + { + return false; + } + } + else + { + (void) different_lengths; + } + return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { return val.get(); }); + hpx::util::end(results), hpx::functional::unwrap{}); }); } }; @@ -592,14 +616,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter1 first1, FwdIter1 last1, - FwdIter2 first2, F&& f) + static decltype(auto) parallel(ExPolicy&& orgpolicy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, F&& f) { - if (first1 == last1) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); + if (first1 == last1) + { + return util::detail::algorithm_result::get(true); + } } using difference_type = @@ -613,22 +642,25 @@ namespace hpx::parallel { using policy_type = std::decay_t; using zip_iterator = hpx::util::zip_iterator; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; - auto f1 = [f, tok](zip_iterator it, - std::size_t part_count) mutable -> bool { + auto f1 = [f, tok]( + zip_iterator it, std::size_t part_count) mutable + -> intermediate_result_t { sequential_equal( it, part_count, tok, HPX_FORWARD(F, f)); return !tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), zip_iterator(first1, first2), count, HPX_MOVE(f1), [](auto&& results) { return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { return val.get(); }); + hpx::util::end(results), hpx::functional::unwrap{}); }); } }; diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp index 97c5302b295..ea7887487d8 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp @@ -221,13 +221,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, T const& val) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(first)); + if (first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(first)); + } } return for_each_n().call(HPX_FORWARD(ExPolicy, policy), @@ -260,9 +266,8 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, std::size_t count, - T const& val) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + std::size_t count, T const& val) { return for_each_n().call( HPX_FORWARD(ExPolicy, policy), first, count, @@ -289,10 +294,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result< - ExPolicy>::type - tag_fallback_invoke(fill_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T const& value) + friend decltype(auto) tag_fallback_invoke(fill_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T const& value) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -338,19 +341,27 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(fill_n_t, ExPolicy&& policy, FwdIter first, - Size count, T const& value) + friend decltype(auto) tag_fallback_invoke(fill_n_t, ExPolicy&& policy, + FwdIter first, Size count, T const& value) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + // if count is representing a negative value, we do nothing if (hpx::parallel::detail::is_negative(count)) { - return hpx::parallel::util::detail::algorithm_result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + return hpx::parallel::util::detail::algorithm_result< + ExPolicy, FwdIter>::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::fill_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp index effbc144dfc..cf4aab031e4 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp @@ -11,6 +11,7 @@ /// \headerfile hpx/algorithm.hpp #pragma once +#include #if defined(DOXYGEN) namespace hpx { @@ -831,17 +832,19 @@ namespace hpx::parallel { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - difference_type count = detail::distance(first, last); - if constexpr (!hpx::execution_policy_has_scheduler_executor_v< - ExPolicy>) + if constexpr (!has_scheduler_executor) { - if (count <= 0) + if (first == last) { return result::get(HPX_MOVE(last)); } } + difference_type count = detail::distance(first, last); + decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), hpx::threads::thread_placement_hint::breadth_first); @@ -914,17 +917,24 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter first, Sent last, F&& f, - Proj&& proj = Proj()) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter first, + Sent last, F&& f, Proj&& proj = Proj()) { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) + { + if (first == last) + { + return result::get(HPX_MOVE(last)); + } + } difference_type count = detail::distance(first, last); - if (count <= 0) - return result::get(HPX_MOVE(last)); decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -943,10 +953,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> Iter { + auto&&... data) mutable -> Iter { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } auto find_res = static_cast(tok.get_data()); @@ -994,17 +1008,24 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter first, Sent last, F&& f, - Proj&& proj = Proj()) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter first, + Sent last, F&& f, Proj&& proj = Proj()) { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_policy = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_policy) + { + if (first == last) + { + return result::get(HPX_MOVE(last)); + } + } difference_type count = detail::distance(first, last); - if (count <= 0) - return result::get(HPX_MOVE(last)); decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -1023,10 +1044,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> Iter { + auto&&... data) mutable -> Iter { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } auto find_res = static_cast(tok.get_data()); @@ -1078,26 +1103,41 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter1 first1, Sent1 last1, Iter2 first2, - Sent2 last2, Pred&& op, Proj1&& proj1, Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter1 first1, + Sent1 last1, Iter2 first2, Sent2 last2, Pred&& op, + Proj1&& proj1, Proj2&& proj2) { using result_type = util::detail::algorithm_result; - using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - difference_type diff = detail::distance(first2, last2); - if (diff <= 0) + if constexpr (!has_scheduler_executor) { - return result_type::get(HPX_MOVE(last1)); + if (first2 == last2) + { + return result_type::get(HPX_MOVE(last1)); + } } difference_type count = detail::distance(first1, last1); - if (diff > count) + difference_type diff = detail::distance(first2, last2); + + if constexpr (!has_scheduler_executor) { - return result_type::get(HPX_MOVE(last1)); + if (diff > count) + { + return result_type::get(HPX_MOVE(last1)); + } + } + + difference_type partitioner_count = count - diff + 1; + if constexpr (has_scheduler_executor) + { + if (diff == 0 || diff > count) + partitioner_count = static_cast(0); } decltype(auto) policy = parallel::util::adapt_placement_mode( @@ -1121,10 +1161,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first1, last1]( - auto&& data) mutable -> Iter1 { + auto&&... data) mutable -> Iter1 { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } difference_type find_end_res = tok.get_data(); @@ -1143,7 +1187,7 @@ namespace hpx::parallel { util::partitioner; return partitioner_type::call_with_index( HPX_FORWARD(decltype(policy), policy), first1, - count - diff + 1, 1, HPX_MOVE(f1), HPX_MOVE(f2)); + partitioner_count, 1, HPX_MOVE(f1), HPX_MOVE(f2)); } }; } // namespace detail @@ -1173,25 +1217,36 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter first, FwdIter last, - FwdIter2 s_first, FwdIter2 s_last, Pred&& op, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, FwdIter first, + FwdIter last, FwdIter2 s_first, FwdIter2 s_last, Pred&& op, + Proj1&& proj1, Proj2&& proj2) { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; - using s_difference_type = - typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - s_difference_type diff = std::distance(s_first, s_last); - if (diff <= 0) - return result::get(HPX_MOVE(last)); + if constexpr (!has_scheduler_executor) + { + if (first == last) + return result::get(HPX_MOVE(last)); + } difference_type count = std::distance(first, last); - if (diff > count) - return result::get(HPX_MOVE(last)); + + if (s_first == s_last) + { + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + return result::get(HPX_MOVE(last)); + } + } decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -1212,10 +1267,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> FwdIter { + auto&&... data) mutable -> FwdIter { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } difference_type find_first_of_res = tok.get_data(); @@ -1302,9 +1361,7 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( find_if_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -1351,9 +1408,7 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( find_if_not_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -1403,10 +1458,9 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_end_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred op) + friend decltype(auto) tag_fallback_invoke(find_end_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, + Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -1426,10 +1480,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_end_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) + friend decltype(auto) tag_fallback_invoke(find_end_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -1508,10 +1560,9 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_first_of_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 s_first, FwdIter2 s_last, Pred op) + friend decltype(auto) tag_fallback_invoke(find_first_of_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 s_first, + FwdIter2 s_last, Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -1531,10 +1582,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_first_of_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 s_first, FwdIter2 s_last) + friend decltype(auto) tag_fallback_invoke(find_first_of_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 s_first, + FwdIter2 s_last) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp index 83688e3bd46..8f90c42cbb6 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp @@ -625,19 +625,29 @@ namespace hpx { std::is_integral_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::for_each_n_t, ExPolicy&& policy, FwdIter first, - Size count, F f) + friend decltype(auto) tag_fallback_invoke(hpx::for_each_n_t, + ExPolicy&& policy, FwdIter first, Size count, F f) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + // if count is representing a negative value, we do nothing if (parallel::detail::is_negative(count)) { - using result = - parallel::util::detail::algorithm_result; - return result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + using result = + parallel::util::detail::algorithm_result; + return result::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::for_each_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp index eb3cfdf5075..fdc3dc4ecac 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp @@ -245,7 +245,7 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, Iter first, Sent last, F&& f) { auto f1 = [policy, f = HPX_FORWARD(F, f)]( @@ -287,7 +287,7 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, std::size_t count, F&& f) { auto f1 = [policy, f = HPX_FORWARD(F, f)](FwdIter part_begin, @@ -322,9 +322,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( generate_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -365,18 +363,26 @@ namespace hpx { std::is_integral_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( generate_n_t, ExPolicy&& policy, FwdIter first, Size count, F f) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); + constexpr bool has_scheduler_executor = + execution_policy_has_scheduler_executor_v; + if (hpx::parallel::detail::is_negative(count)) { - return hpx::parallel::util::detail::algorithm_result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + return hpx::parallel::util::detail::algorithm_result< + ExPolicy, FwdIter>::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::generate_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp index dc62cfe105e..79e561a35f4 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp @@ -136,12 +136,12 @@ namespace hpx::parallel { namespace detail { /// \cond NOINTERNAL - inline bool sequential_is_partitioned( - std::vector>&& res) + template + inline bool sequential_is_partitioned(std::vector&& res) { auto first = res.begin(); auto const last = res.end(); - while (first != last && first->get()) + while (first != last && hpx::unwrap(*first)) { ++first; } @@ -150,7 +150,7 @@ namespace hpx::parallel { ++first; while (first != last) { - if (first->get()) + if (hpx::unwrap(*first)) return false; ++first; } @@ -185,21 +185,28 @@ namespace hpx::parallel { using difference_type = typename std::iterator_traits::difference_type; using result = util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = std::distance(first, last); - if (count <= 1) - return result::get(true); + + if (!has_scheduler_executor) + { + if (count <= 1) + return result::get(true); + } util::invoke_projected pred_projected( HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)); - util::cancellation_token<> tok; + using intermediate_result_t = + std::conditional_t; // Note: replacing the invoke() with HPX_INVOKE() // below makes gcc generate errors auto f1 = [tok, pred_projected = HPX_MOVE(pred_projected)]( - Iter part_begin, - std::size_t part_count) mutable -> bool { + Iter part_begin, std::size_t part_count) mutable + -> intermediate_result_t { bool fst_bool = HPX_INVOKE(pred_projected, *part_begin); if (part_count == 1) return fst_bool; @@ -226,9 +233,9 @@ namespace hpx::parallel { return sequential_is_partitioned(HPX_MOVE(results)); }; - return util::partitioner::call( - HPX_FORWARD(ExPolicy, policy), first, count, HPX_MOVE(f1), - HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(ExPolicy, policy), + first, count, HPX_MOVE(f1), HPX_MOVE(f2)); } }; /// \endcond @@ -263,10 +270,8 @@ namespace hpx { hpx::traits::is_forward_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::is_partitioned_t, ExPolicy&& policy, - FwdIter first, FwdIter last, Pred pred) + friend decltype(auto) tag_fallback_invoke(hpx::is_partitioned_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred) { return hpx::parallel::detail::is_partitioned() .call(HPX_FORWARD(ExPolicy, policy), first, last, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp index a7613d7a897..5c6ecb31d62 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp @@ -280,22 +280,29 @@ namespace hpx::parallel { typename std::iterator_traits::difference_type; using result = typename util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = std::distance(first, last); - if (count <= 1) - return result::get(true); + + if (!has_scheduler_executor) + { + if (count <= 1) + return result::get(true); + } util::invoke_projected pred_projected{ HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)}; - hpx::parallel::util::cancellation_token<> tok; + using intermediate_result_t = + std::conditional_t; // Note: replacing the invoke() with HPX_INVOKE() // below makes gcc generate errors auto f1 = [tok, last, pred_projected = HPX_MOVE(pred_projected)]( - FwdIter part_begin, - std::size_t part_size) mutable -> bool { + FwdIter part_begin, std::size_t part_size) mutable + -> intermediate_result_t { FwdIter trail = part_begin++; util::loop_n>(part_begin, part_size - 1, @@ -322,15 +329,12 @@ namespace hpx::parallel { auto f2 = [](auto&& results) { return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) -> bool { - return val.get(); - }); + hpx::util::end(results), hpx::functional::unwrap{}); }; - return util::partitioner::call( - HPX_FORWARD(ExPolicy, policy), first, count, HPX_MOVE(f1), - HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(ExPolicy, policy), + first, count, HPX_MOVE(f1), HPX_MOVE(f2)); } }; /// \endcond @@ -358,9 +362,8 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter first, Sent last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& orgpolicy, FwdIter first, + Sent last, Pred&& pred, Proj&& proj) { using reference = typename std::iterator_traits::reference; @@ -368,10 +371,15 @@ namespace hpx::parallel { typename std::iterator_traits::difference_type; using result = typename util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = std::distance(first, last); - if (count <= 1) - return result::get(HPX_MOVE(last)); + if constexpr (!has_scheduler_executor) + { + if (count <= 1) + return result::get(HPX_MOVE(last)); + } util::invoke_projected pred_projected{ HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)}; @@ -417,10 +425,14 @@ namespace hpx::parallel { } }; - auto f2 = [first, tok](auto&& data) mutable -> FwdIter { + auto f2 = [first, tok](auto&&... data) mutable -> FwdIter { // make sure iterators embedded in function object that is // attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } difference_type loc = tok.get_data(); std::advance(first, loc); @@ -474,10 +486,8 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::is_sorted_t, ExPolicy&& policy, FwdIter first, - FwdIter last, Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::is_sorted_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred = Pred()) { return hpx::parallel::detail::is_sorted().call( HPX_FORWARD(ExPolicy, policy), first, last, HPX_MOVE(pred), @@ -519,10 +529,8 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::is_sorted_until_t, ExPolicy&& policy, - FwdIter first, FwdIter last, Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::is_sorted_until_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred = Pred()) { return hpx::parallel::detail::is_sorted_until() .call(HPX_FORWARD(ExPolicy, policy), first, last, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp index 11650877977..5ef38f97ac6 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp @@ -224,30 +224,34 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter1 first1, Sent1 last1, - FwdIter2 first2, Sent2 last2, Pred&& pred, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, + FwdIter1 first1, Sent1 last1, FwdIter2 first2, Sent2 last2, + Pred&& pred, Proj1&& proj1, Proj2&& proj2) { using zip_iterator = hpx::util::zip_iterator; using reference = typename zip_iterator::reference; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; std::size_t const count1 = detail::distance(first1, last1); std::size_t const count2 = detail::distance(first2, last2); // An empty range is lexicographically less than any non-empty // range - if (count1 == 0 && count2 != 0) + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); - } + if (count1 == 0 && count2 != 0) + { + return util::detail::algorithm_result::get(true); + } - if (count2 == 0 && count1 != 0) - { - return util::detail::algorithm_result::get( - false); + if (count2 == 0 && count1 != 0) + { + return util::detail::algorithm_result::get(false); + } } decltype(auto) policy = parallel::util::adapt_placement_mode( @@ -279,10 +283,14 @@ namespace hpx::parallel { }; auto f2 = [tok, first1, first2, last1, last2, pred, proj1, - proj2](auto&& data) mutable -> bool { + proj2](auto&&... data) mutable -> bool { // make sure iterators embedded in function object that is // attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } std::size_t mismatched = tok.get_data(); @@ -357,10 +365,9 @@ namespace hpx { > )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::lexicographical_compare_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, - Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke( + hpx::lexicographical_compare_t, ExPolicy&& policy, FwdIter1 first1, + FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred pred = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 1e76ca7cc5a..b3c8ffec82f 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -493,13 +493,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, F&& f, Proj&& proj) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(first)); + if (first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(first)); + } } auto f1 = [f, proj, policy]( @@ -508,9 +514,22 @@ namespace hpx::parallel { policy, it, part_count, f, proj); }; - auto f2 = [policy, f = HPX_FORWARD(F, f), + auto f2 = [policy, first, f = HPX_FORWARD(F, f), proj = HPX_FORWARD(Proj, proj)]( auto&& positions) -> FwdIter { + if constexpr (has_scheduler_executor) + { + if (positions.size() == 0) + { + return first; + } + } + else + { + // for silencing the unused capture warning + (void) first; + } + return min_element::sequential_minmax_element_ind( policy, positions.begin(), positions.size(), f, proj); }; @@ -628,13 +647,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, F&& f, Proj&& proj) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(first)); + if (first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(first)); + } } auto f1 = [f, proj, policy]( @@ -643,9 +668,22 @@ namespace hpx::parallel { policy, it, part_count, f, proj); }; - auto f2 = [policy, f = HPX_FORWARD(F, f), + auto f2 = [policy, first, f = HPX_FORWARD(F, f), proj = HPX_FORWARD(Proj, proj)]( auto&& positions) -> FwdIter { + if constexpr (has_scheduler_executor) + { + if (positions.size() == 0) + { + return first; + } + } + else + { + // for silencing the unused capture warning + (void) first; + } + return max_element::sequential_minmax_element_ind( policy, positions.begin(), positions.size(), f, proj); }; @@ -789,18 +827,22 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, F&& f, Proj&& proj) { using result_type = minmax_element_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; result_type result = {first, first}; - if (first == last || ++first == last) + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(result)); + if (first == last || ++first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(result)); + } } auto f1 = [f, proj, policy](FwdIter it, std::size_t part_count) @@ -809,9 +851,22 @@ namespace hpx::parallel { policy, it, part_count, f, proj); }; - auto f2 = [policy, f = HPX_FORWARD(F, f), + auto f2 = [policy, first, f = HPX_FORWARD(F, f), proj = HPX_FORWARD(Proj, proj)]( auto&& positions) -> result_type { + if constexpr (has_scheduler_executor) + { + if (positions.size() == 0) + { + return result_type{first, first}; + } + } + else + { + // for silencing the unused capture warning + (void) first; + } + return minmax_element::sequential_minmax_element_ind( policy, positions.begin(), positions.size(), f, proj); }; @@ -862,10 +917,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::min_element_t, ExPolicy&& policy, - FwdIter first, FwdIter last, F f = F()) + friend decltype(auto) tag_fallback_invoke(hpx::min_element_t, + ExPolicy&& policy, FwdIter first, FwdIter last, F f = F()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -906,10 +959,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::max_element_t, ExPolicy&& policy, - FwdIter first, FwdIter last, F f = F()) + friend decltype(auto) tag_fallback_invoke(hpx::max_element_t, + ExPolicy&& policy, FwdIter first, FwdIter last, F f = F()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -950,10 +1001,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(hpx::minmax_element_t, ExPolicy&& policy, - FwdIter first, FwdIter last, F f = F()) + friend decltype(auto) tag_fallback_invoke(hpx::minmax_element_t, + ExPolicy&& policy, FwdIter first, FwdIter last, F f = F()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp index d4c7b7ddb66..e42a79de455 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp @@ -402,14 +402,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIterB first, FwdIterE last, T_&& init, - Reduce&& r) + static decltype(auto) parallel(ExPolicy&& policy, FwdIterB first, + FwdIterE last, T_&& init, Reduce&& r) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - HPX_FORWARD(T_, init)); + if (first == last) + { + return util::detail::algorithm_result::get( + HPX_FORWARD(T_, init)); + } } auto f1 = [r](FwdIterB part_begin, std::size_t part_size) -> T { @@ -450,9 +455,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T init, F f) + friend auto tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T init, F f) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -470,9 +474,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T init) + friend auto tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T init) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -489,9 +492,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t::value_type> - tag_fallback_invoke( + friend auto tag_fallback_invoke( hpx::reduce_t, ExPolicy&& policy, FwdIter first, FwdIter last) { static_assert(hpx::traits::is_forward_iterator_v, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp index b07d8c8595c..15517937af1 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp @@ -553,9 +553,9 @@ namespace hpx::parallel { template - static constexpr util::detail::algorithm_result_t - parallel(ExPolicy&& policy, FwdIter first, Sent last, F&& f, - T const& new_value, Proj&& proj) + static constexpr decltype(auto) parallel(ExPolicy&& policy, + FwdIter first, Sent last, F&& f, T const& new_value, + Proj&& proj) { return sequential_replace_if( HPX_FORWARD(ExPolicy, policy), first, last, @@ -632,10 +632,9 @@ namespace hpx::parallel { template - static constexpr util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, FwdIter1 first, Sent sent, - FwdIter2 dest, F&& f, T const& new_value, Proj&& proj) + static constexpr decltype(auto) parallel(ExPolicy&& policy, + FwdIter1 first, Sent sent, FwdIter2 dest, F&& f, + T const& new_value, Proj&& proj) { return sequential_replace_copy_if( HPX_FORWARD(ExPolicy, policy), first, sent, dest, @@ -688,10 +687,9 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_if_t, ExPolicy&& policy, FwdIter first, - FwdIter last, Pred pred, T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_if_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred, + T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -738,10 +736,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T const& old_value, T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_t, + ExPolicy&& policy, FwdIter first, FwdIter last, T const& old_value, + T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -800,11 +797,9 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_copy_if_t, ExPolicy&& policy, - FwdIter1 first, FwdIter1 last, FwdIter2 dest, Pred pred, - T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_copy_if_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, + Pred pred, T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -860,11 +855,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_copy_t, ExPolicy&& policy, - FwdIter1 first, FwdIter1 last, FwdIter2 dest, T const& old_value, - T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_copy_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, + T const& old_value, T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp index 3e8d8d18137..e3611148248 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp @@ -299,10 +299,8 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, BidirIter first, Sent last, - FwdIter dest_first) + static decltype(auto) parallel(ExPolicy&& policy, BidirIter first, + Sent last, FwdIter dest_first) { auto last2{hpx::ranges::next(first, last)}; typedef std::reverse_iterator iterator; @@ -402,9 +400,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::reverse_copy_t, ExPolicy&& policy, - BidirIter first, BidirIter last, FwdIter dest) + friend decltype(auto) tag_fallback_invoke(hpx::reverse_copy_t, + ExPolicy&& policy, BidirIter first, BidirIter last, FwdIter dest) { static_assert(hpx::traits::is_bidirectional_iterator_v, "Requires at least bidirectional iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp b/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp index c7aeb025865..f4138cab594 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp @@ -23,6 +23,7 @@ #include #endif +#include #include #include @@ -203,7 +204,8 @@ namespace hpx::parallel::util::detail { hpx::execution_policy_has_scheduler_executor_v>> { // The return type of the initiating function. - using type = T; + using type = + decltype(hpx::execution::experimental::just(std::declval())); template static constexpr auto get(T_&& t) @@ -226,7 +228,7 @@ namespace hpx::parallel::util::detail { hpx::execution_policy_has_scheduler_executor_v>> { // The return type of the initiating function. - using type = void; + using type = decltype(hpx::execution::experimental::just()); template static constexpr auto get(T_&& t) diff --git a/libs/core/algorithms/tests/regressions/CMakeLists.txt b/libs/core/algorithms/tests/regressions/CMakeLists.txt index 9c132a490c2..4b8d61cd1fb 100644 --- a/libs/core/algorithms/tests/regressions/CMakeLists.txt +++ b/libs/core/algorithms/tests/regressions/CMakeLists.txt @@ -7,6 +7,7 @@ set(tests count_3646 fill_executor_5016 + findfirstof_more_searched_for for_each_annotated_function for_loop_2281 for_loop_5735 diff --git a/libs/core/algorithms/tests/regressions/findfirstof_more_searched_for.cpp b/libs/core/algorithms/tests/regressions/findfirstof_more_searched_for.cpp new file mode 100644 index 00000000000..da4384bbfad --- /dev/null +++ b/libs/core/algorithms/tests/regressions/findfirstof_more_searched_for.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// 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) + +// It should be no issue for find_first_of if the range being searched +// is smaller than the range of the searched elements. + +#include +#include +#include +#include + +#include +#include +#include + +void find_first_of_failing_test() +{ + std::vector a{1, 2, 3, 4, 5}; + std::vector b{10, 11, 12, 2, 13, 14, 15}; + + auto result = hpx::find_first_of( + hpx::execution::par, a.begin(), a.end(), b.begin(), b.end()); + auto expected = ++a.begin(); + + HPX_TEST(result == expected); +} + +int hpx_main() +{ + find_first_of_failing_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + 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/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index afa760c4d92..676f9c6ec76 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -44,30 +44,31 @@ set(tests destroyn destroyn_sender ends_with - # ends_with_sender + ends_with_sender equal equal_binary - # equal_sender + equal_sender + equal_binary_sender exclusive_scan exclusive_scan2 exclusive_scan_exception exclusive_scan_bad_alloc exclusive_scan_validate fill - # fill_sender + fill_sender filln - # filln_sender + filln_sender find find_sender findend - # findend_sender + findend_sender findfirstof findfirstof_binary - # findfirstof_sender + findfirstof_sender findif - # findif_sender + findif_sender findifnot - # findifnot_sender + findifnot_sender foreach foreach_executors foreach_prefetching @@ -76,7 +77,7 @@ set(tests foreachn foreachn_exception foreachn_bad_alloc - # foreachn_sender + foreachn_sender for_loop for_loop_exception for_loop_induction @@ -87,13 +88,13 @@ set(tests # for_loop_n_strided_sender for_loop_reduction for_loop_reduction_async - for_loop_sender + # for_loop_sender for_loop_strided # for_loop_strided_sender generate - # generate_sender + generate_sender generaten - # generaten_sender + generaten_sender is_heap # is_heap_sender is_heap_until @@ -105,23 +106,23 @@ set(tests inplace_merge # inplace_merge_sender is_partitioned - # is_partitioned_sender + is_partitioned_sender is_sorted - # is_sorted_sender + is_sorted_sender is_sorted_until - # is_sorted_until_sender + is_sorted_until_sender lexicographical_compare - # lexicographical_compare_sender + lexicographical_compare_sender make_heap # make_heap_sender max_element - # max_element_sender + max_element_sender merge # merge_sender min_element - # min_element_sender + min_element_sender minmax_element - # minmax_element_sender + minmax_element_sender mismatch mismatch_binary # mismatch_sender @@ -140,7 +141,7 @@ set(tests # partition_sender partition_copy reduce_ - # reduce_sender + reduce_sender reduce_by_key remove # remove_sender @@ -152,21 +153,20 @@ set(tests remove_copy remove_copy_if replace - # replace_sender + replace_sender replace_if - # replace_if_sender + replace_if_sender replace_copy - # replace_copy_sender + replace_copy_sender replace_copy_if - # replace_copy_if_sender + replace_copy_if_sender reverse reverse_copy reverse_sender - # reverse_copy_sender + reverse_copy_sender rotate rotate_copy - rotate_sender - # rotate_copy_sender + # rotate_sender rotate_copy_sender search # search_sender searchn diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp index d1b2f477c91..e7e4e1fbef2 100644 --- a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp @@ -13,7 +13,6 @@ #include #include - #include #include #include @@ -86,21 +85,38 @@ void test_adjacent_difference_sender(Policy l, ExPolicy&& policy) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#ifdef HPX_HAVE_STDEXEC + auto result = tt::sync_wait(ex::just(std::begin(c), std::end(c), std::begin(d)) | hpx::adjacent_difference(policy.on(exec))); -#else - auto result = ex::just(std::begin(c), std::end(c), std::begin(d)) | - hpx::adjacent_difference(policy.on(exec)) | tt::sync_wait(); -#endif std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), [](auto lhs, auto rhs) { return lhs == rhs; })); - HPX_TEST(std::end(d) == hpx::get<0>(*result)); + + // 1st edge case: first == last + result = + tt::sync_wait(ex::just(std::begin(c), std::begin(c), std::begin(d)) | + hpx::adjacent_difference(policy.on(exec))); + + std::adjacent_difference(std::begin(c), std::begin(c), std::begin(d_ans)); + + HPX_TEST(std::begin(d) == hpx::get<0>(*result)); + HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), + [](auto lhs, auto rhs) { return lhs == rhs; })); + + // 2nd edge case: first + 1 == last + result = + tt::sync_wait(ex::just(std::begin(c), ++std::begin(c), std::begin(d)) | + hpx::adjacent_difference(policy.on(exec))); + + std::adjacent_difference(std::begin(c), ++std::begin(c), std::begin(d_ans)); + + HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), + [](auto lhs, auto rhs) { return lhs == rhs; })); + HPX_TEST(++std::begin(d) == hpx::get<0>(*result)); } template diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp index 1653cb77a2c..5ea47b8d538 100644 --- a/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp @@ -82,10 +82,16 @@ void test_adjacent_find_sender( hpx::adjacent_find(ex_policy.on(exec))); iterator index = hpx::get<0>(*snd_result); - base_iterator test_index = std::begin(c) + random_pos; HPX_TEST(index == iterator(test_index)); + + // edge case: first == last + snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::adjacent_find(ex_policy.on(exec))); + + HPX_TEST(iterator(std::begin(c)) == hpx::get<0>(*snd_result)); } template diff --git a/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp index bab9ebd2d35..0192a61b49a 100644 --- a/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp @@ -91,6 +91,11 @@ void test_copy_n_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + tt::sync_wait(ex::just(iterator(std::begin(c)), -1, std::begin(d)) | + hpx::copy_n(ex_policy.on(exec))); + HPX_TEST(std::all_of(std::begin(d), std::end(d), + [](std::size_t i) { return i == std::size_t{}; })); + tt::sync_wait(ex::just(iterator(std::begin(c)), c.size(), std::begin(d)) | hpx::copy_n(ex_policy.on(exec))); diff --git a/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp index c85dcbd7190..5d0f08c35aa 100644 --- a/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp @@ -71,6 +71,10 @@ void test_destroy_n_sender( auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + tt::sync_wait( + ex::just(iterator(p), -1) | hpx::destroy_n(ex_policy.on(exec))); + HPX_TEST_EQ(destruct_count.load(), static_cast(0)); + tt::sync_wait( ex::just(iterator(p), data_size) | hpx::destroy_n(ex_policy.on(exec))); diff --git a/libs/core/algorithms/tests/unit/algorithms/ends_with_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/ends_with_sender.cpp new file mode 100644 index 00000000000..cfe75c77d73 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/ends_with_sender.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2018 Christopher Ogle +// Copyright (c) 2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// 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 +#include +#include +#include +#include + +#include "test_utils.hpp" + +unsigned int seed = std::random_device{}(); +std::mt19937 gen(seed); + +//////////////////////////////////////////////////////////////////////////// +template +void test_ends_with_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + + using scheduler_t = ex::thread_pool_policy_scheduler; + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::uniform_int_distribution dis1(3, 10007); + auto end1 = dis1(gen); + std::uniform_int_distribution dis2(2, end1 - 1); + auto end2 = dis2(gen); + + auto some_ints = std::vector(end1); + std::iota(some_ints.begin(), some_ints.end(), 1); + auto some_more_ints = std::vector(end1 - end2 + 1); + std::iota(some_more_ints.begin(), some_more_ints.end(), end2); + auto some_wrong_ints = std::vector(end2); + std::iota(some_wrong_ints.begin(), some_wrong_ints.end(), 1); + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_ints)), + iterator(std::end(some_ints)), + iterator(std::begin(some_more_ints)), + iterator(std::end(some_more_ints))) | + hpx::ends_with(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + HPX_TEST(result); + } + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_ints)), + iterator(std::end(some_ints)), + iterator(std::begin(some_wrong_ints)), + iterator(std::end(some_wrong_ints))) | + hpx::ends_with(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + HPX_TEST(!result); + } + + { + // edge case: second ranger large than the first + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_more_ints)), + iterator(std::end(some_more_ints)), + iterator(std::begin(some_ints)), + iterator(std::end(some_ints))) | + hpx::ends_with(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + HPX_TEST(!result); + } +} + +template +void ends_with_sender_test() +{ + using namespace hpx::execution; + test_ends_with_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_ends_with_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_ends_with_sender(hpx::launch::async, par(task), IteratorTag()); + test_ends_with_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +void ends_with_test() +{ + ends_with_sender_test(); + ends_with_sender_test(); +} + +//////////////////////////////////////////////////////////////////////////// +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); + + ends_with_test(); + 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/equal_binary.cpp b/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp index cf2f64421f9..f1598752986 100644 --- a/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp @@ -102,6 +102,26 @@ void equal_binary_bad_alloc_test() test_equal_binary_bad_alloc(); } +//////////////////////////////////////////////////////////////////////////////// + +template +void test_equal_binary_edge_cases() +{ + using namespace hpx::execution; + + test_equal_binary_edge_cases(seq, IteratorTag()); + test_equal_binary_edge_cases(par, IteratorTag()); + + test_equal_binary_edge_cases(seq(task), IteratorTag()); + test_equal_binary_edge_cases(par(task), IteratorTag()); +} + +void equal_binary_edge_cases_test() +{ + test_equal_binary_edge_cases(); + test_equal_binary_edge_cases(); +} + /////////////////////////////////////////////////////////////////////////////// int hpx_main(hpx::program_options::variables_map& vm) { @@ -115,6 +135,7 @@ int hpx_main(hpx::program_options::variables_map& vm) equal_binary_test2(); equal_binary_exception_test(); equal_binary_bad_alloc_test(); + equal_binary_edge_cases_test(); return hpx::local::finalize(); } diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_binary_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/equal_binary_sender.cpp new file mode 100644 index 00000000000..e9f5b648b75 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/equal_binary_sender.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// 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 "equal_binary_tests.hpp" + +template +void equal_binary_sender_test() +{ + using namespace hpx::execution; + test_equal_binary_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_equal_binary_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_equal_binary_sender(hpx::launch::async, par(task), IteratorTag()); + test_equal_binary_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +template +void equal_binary_edge_cases_sender_test() +{ + using namespace hpx::execution; + + test_equal_binary_edge_cases_sender( + hpx::launch::sync, seq(task), IteratorTag()); + test_equal_binary_edge_cases_sender( + hpx::launch::sync, unseq(task), IteratorTag()); + + test_equal_binary_edge_cases_sender( + hpx::launch::async, par(task), IteratorTag()); + test_equal_binary_edge_cases_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +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; + std::srand(seed); + + equal_binary_sender_test(); + equal_binary_sender_test(); + + equal_binary_edge_cases_sender_test(); + equal_binary_edge_cases_sender_test(); + + 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/equal_binary_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp index c9a63df771f..298b1a6e24a 100644 --- a/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp @@ -459,3 +459,173 @@ void test_equal_binary_bad_alloc_async(ExPolicy&& p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +template +void test_equal_binary_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1(10007); + std::vector c2(c1.size()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + auto policy = ex_policy.on(exec); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2)) | + hpx::equal(policy)); + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + std::uniform_int_distribution<> dis(0, c1.size() - 1); + c1[dis(gen)] += 1; //-V104 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2)) | + hpx::equal(policy)); + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +template +void test_equal_binary_edge_cases(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c1{0, 1, 2, 3, 4}; + std::vector c2{5, 6, 7, 8, 9}; + + { + // both ranges empty + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c2), std::begin(c2)); + HPX_TEST(hpx::unwrap(result)); + } + + { + // only first range empty + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c1), std::end(c1)); + HPX_TEST(!hpx::unwrap(result)); + } + + { + // only second range empty + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::end(c1)), std::begin(c1), std::begin(c1)); + HPX_TEST(!hpx::unwrap(result)); + } + + { + // ranges of different length + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::begin(c1) + 1), std::begin(c1), std::begin(c1) + 2); + HPX_TEST(!hpx::unwrap(result)); + } +} + +template +void test_equal_binary_edge_cases_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1{0, 1, 2, 3, 4}; + std::vector c2{5, 6, 7, 8, 9}; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + auto policy = ex_policy.on(exec); + + { + // both ranges empty + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c2), std::begin(c2)) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } + + { + // only first range empty + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c1), std::end(c1)) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } + + { + // only second range emtpy + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c1), std::begin(c1)) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } + + { + // ranges of different length + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1) + 1), + std::begin(c1), std::begin(c1) + 2) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } +} diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp index 7ecddc1f6c2..34681e10478 100644 --- a/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp @@ -122,9 +122,9 @@ void test_equal1_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) std::iota(std::begin(c1), std::end(c1), first_value); std::iota(std::begin(c2), std::end(c2), first_value); - { - auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + { auto snd_result = tt::sync_wait( ex::just(std::begin(c1), std::end(c1), std::begin(c2)) | hpx::equal(ex_policy.on(exec))); @@ -142,8 +142,6 @@ void test_equal1_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) std::uniform_int_distribution<> dis(0, c1.size() - 1); ++c1[dis(gen)]; //-V104 - auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = tt::sync_wait(ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), std::begin(c2)) | @@ -157,6 +155,19 @@ void test_equal1_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) // verify values HPX_TEST_EQ(result, expected); } + + { + // edge case: empty range + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c2)) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } } template @@ -300,9 +311,9 @@ void test_equal2_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) std::iota(std::begin(c1), std::end(c1), first_value); std::iota(std::begin(c2), std::end(c2), first_value); - { - auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + { auto snd_result = tt::sync_wait( ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), std::begin(c2), std::equal_to<>()) | @@ -321,8 +332,6 @@ void test_equal2_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) std::uniform_int_distribution<> dis(0, c1.size() - 1); ++c1[dis(gen)]; //-V104 - auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = tt::sync_wait( ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), std::begin(c2), std::equal_to<>()) | @@ -336,6 +345,19 @@ void test_equal2_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) // verify values HPX_TEST_EQ(result, expected); } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c2), std::equal_to<>()) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } } template diff --git a/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp index 71bf95732a5..4984f5593f6 100644 --- a/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp @@ -114,6 +114,20 @@ void test_find_end1_sender( iterator(std::end(c)), std::begin(h), std::end(h)); HPX_TEST(index == test_index); + + // 1st edge case: first2 == end2 + snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::begin(h)) | + hpx::find_end(ex_policy.on(exec))); + HPX_TEST(iterator(std::end(c)) == hpx::get<0>(*snd_result)); + + // 2nd edge case: distance(first2, end2) > distance(first1, end1) + snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(h), std::end(h)) | + hpx::find_end(ex_policy.on(exec))); + HPX_TEST(iterator(std::begin(c)) == hpx::get<0>(*snd_result)); } template diff --git a/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp index a395113e6cc..3e323d9d4c4 100644 --- a/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp @@ -44,14 +44,40 @@ void test_is_partitioned_sender( auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = - tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), - [](std::size_t n) { return n % 2 == 0; }) | + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](std::size_t n) { return n % 2 == 0; }) | hpx::is_partitioned(ex_policy.on(exec))); - bool parted = hpx::get<0>(*snd_result); + bool parted = hpx::get<0>(*snd_result); - HPX_TEST(parted); + HPX_TEST(parted); + } + + { + // 1st edge case: first == last + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + [](std::size_t) { return true; }) | + hpx::is_partitioned(ex_policy.on(exec))); + + auto parted = hpx::get<0>(*snd_result); + + HPX_TEST(parted); + } + + { + // 2nd edge case: first + 1 == last + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c)), + [](std::size_t) { return true; }) | + hpx::is_partitioned(ex_policy.on(exec))); + + auto parted = hpx::get<0>(*snd_result); + + HPX_TEST(parted); + } } template diff --git a/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp index e90a83c3dd1..63c40cfc18d 100644 --- a/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp @@ -441,11 +441,35 @@ void test_is_sorted_sender( auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = - tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | hpx::is_sorted(ex_policy.on(exec))); - bool is_ordered = hpx::get<0>(*snd_result); + bool is_ordered = hpx::get<0>(*snd_result); - HPX_TEST(is_ordered); + HPX_TEST(is_ordered); + } + + { + // 1st edge case: first == last + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::is_sorted(ex_policy.on(exec))); + + bool is_ordered = hpx::get<0>(*snd_result); + + HPX_TEST(is_ordered); + } + + { + // 2nd edge case: first + 1 == last + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c))) | + hpx::is_sorted(ex_policy.on(exec))); + + bool is_ordered = hpx::get<0>(*snd_result); + + HPX_TEST(is_ordered); + } } diff --git a/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp index 6f3dc1623c9..58ae91a05c1 100644 --- a/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp @@ -42,15 +42,37 @@ void test_is_sorted_until_sender( auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = - tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | hpx::is_sorted_until(ex_policy.on(exec))); - iterator until = hpx::get<0>(*snd_result); + iterator until = hpx::get<0>(*snd_result); - base_iterator test_index = std::end(c); + HPX_TEST(until == iterator(std::end(c))); + } - HPX_TEST(until == iterator(test_index)); + { + // 1st edge case: first == last + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::is_sorted_until(ex_policy.on(exec))); + + iterator until = hpx::get<0>(*snd_result); + + HPX_TEST(until == iterator(std::begin(c))); + } + + { + // 2nd edge case: first + 1 == last + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c))) | + hpx::is_sorted_until(ex_policy.on(exec))); + + iterator until = hpx::get<0>(*snd_result); + + HPX_TEST(until == iterator(++std::begin(c))); + } } template diff --git a/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp index 15e8584caf0..97bbcc3377d 100644 --- a/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp @@ -45,14 +45,52 @@ void test_lexicographical_compare_sender( auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = - tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), - std::begin(d), std::end(d)) | + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d), std::end(d)) | hpx::lexicographical_compare(ex_policy.on(exec))); - bool res = hpx::get<0>(*snd_result); + bool res = hpx::get<0>(*snd_result); - HPX_TEST(!res); + HPX_TEST(!res); + } + + { + // edge case: first1 == end1 && first2 != end2 + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(d), std::end(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + + bool res = hpx::get<0>(*snd_result); + + HPX_TEST(res); + } + + { + // edge case: first1 != end1 && first2 == end2 + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d), std::begin(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + + bool res = hpx::get<0>(*snd_result); + + HPX_TEST(!res); + } + + { + // edge case: first1 == end1 && first2 == end2 + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(d), std::begin(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + + bool res = hpx::get<0>(*snd_result); + + HPX_TEST(!res); + } } template diff --git a/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp index 13f2be4814e..124143f9947 100644 --- a/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp @@ -62,6 +62,13 @@ void test_max_element_sender( ref = std::max_element(std::begin(c), std::end(c)); HPX_TEST(ref != ref_end); HPX_TEST_EQ(*ref, *r); + + // edge case: empty range + snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::max_element(ex_policy.on(exec))); + r = hpx::get<0>(*snd_result); + HPX_TEST(r == iterator(std::begin(c))); } template diff --git a/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp index 6809ee51f2a..8966e6a0a0d 100644 --- a/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp @@ -19,38 +19,6 @@ /////////////////////////////////////////////////////////////////////////////// -template -void test_min_element(ExPolicy policy, IteratorTag) -{ - static_assert(hpx::is_execution_policy::value, - "hpx::is_execution_policy::value"); - - typedef std::vector::iterator base_iterator; - typedef test::test_iterator iterator; - - std::vector c = test::random_iota(10007); - - iterator end(std::end(c)); - base_iterator ref_end(std::end(c)); - - iterator r = hpx::min_element(policy, iterator(std::begin(c)), - iterator(end), std::less()); - HPX_TEST(r != end); - - base_iterator ref = - std::min_element(std::begin(c), std::end(c), std::less()); - HPX_TEST(ref != ref_end); - HPX_TEST_EQ(*ref, *r); - - r = hpx::min_element( - policy, iterator(std::begin(c)), iterator(std::end(c))); - HPX_TEST(r != end); - - ref = std::min_element(std::begin(c), std::end(c)); - HPX_TEST(ref != ref_end); - HPX_TEST_EQ(*ref, *r); -} - template void test_min_element_sender( LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) @@ -95,6 +63,13 @@ void test_min_element_sender( ref = std::min_element(std::begin(c), std::end(c)); HPX_TEST(ref != ref_end); HPX_TEST_EQ(*ref, *r); + + // edge case: empty range + snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::min_element(ex_policy.on(exec))); + r = hpx::get<0>(*snd_result); + HPX_TEST(r == iterator(std::begin(c))); } template diff --git a/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp index 6ad75707bfd..ddb949b4ba8 100644 --- a/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp @@ -68,6 +68,15 @@ void test_minmax_element_sender( HPX_TEST_EQ(*ref.first, *r.min); HPX_TEST_EQ(*ref.second, *r.max); + + // edge case: empty range + snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::minmax_element(ex_policy.on(exec))); + + r = hpx::get<0>(*snd_result); + HPX_TEST( + r.min == iterator(std::begin(c)) && r.max == iterator(std::begin(c))); } template diff --git a/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp index 2929c8516fb..ff7afad0de8 100644 --- a/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp @@ -363,13 +363,24 @@ void test_reduce_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); - auto snd_result = tt::sync_wait( - ex::just(iterator(std::begin(c)), iterator(std::end(c)), val, op) | - hpx::reduce(ex_policy.on(exec))); + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), val, op) | + hpx::reduce(ex_policy.on(exec))); - int r1 = hpx::get<0>(*snd_result); + int r1 = hpx::get<0>(*snd_result); - // verify values - int r2 = std::accumulate(std::begin(c), std::end(c), val, op); - HPX_TEST_EQ(r1, r2); + // verify values + int r2 = std::accumulate(std::begin(c), std::end(c), val, op); + HPX_TEST_EQ(r1, r2); + } + + { + auto snd_result = tt::sync_wait(ex::just(iterator(std::begin(c)), + iterator(std::begin(c)), val, op) | + hpx::reduce(ex_policy.on(exec))); + + int res = hpx::get<0>(*snd_result); + HPX_TEST_EQ(res, val); + } } diff --git a/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp index 70a9613f1f7..a37fc0384ed 100644 --- a/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp @@ -76,8 +76,8 @@ int hpx_main(hpx::program_options::variables_map& vm) std::cout << "using seed: " << seed << std::endl; std::srand(seed); - reverse_copy_sender_test(); reverse_copy_sender_test(); + reverse_copy_sender_test(); return hpx::local::finalize(); }