diff --git a/docs/sphinx/api/public_api.rst b/docs/sphinx/api/public_api.rst index ef533d3fc31e..f141bc9ae14c 100644 --- a/docs/sphinx/api/public_api.rst +++ b/docs/sphinx/api/public_api.rst @@ -52,7 +52,7 @@ Functions - :cpp:func:`hpx::generate` - :cpp:func:`hpx::generate_n` - :cpp:func:`hpx::includes` -- :cpp:func:`hpx::parallel::v1::inplace_merge` +- :cpp:func:`hpx::inplace_merge` - :cpp:func:`hpx::is_heap` - :cpp:func:`hpx::is_heap_until` - :cpp:func:`hpx::parallel::v1::is_partitioned` @@ -61,7 +61,7 @@ Functions - :cpp:func:`hpx::parallel::v1::lexicographical_compare` - :cpp:func:`hpx::make_heap` - :cpp:func:`hpx::parallel::v1::max_element` -- :cpp:func:`hpx::parallel::v1::merge` +- :cpp:func:`hpx::merge` - :cpp:func:`hpx::parallel::v1::min_element` - :cpp:func:`hpx::parallel::v1::minmax_element` - :cpp:func:`hpx::parallel::v1::mismatch` @@ -118,9 +118,11 @@ Functions - :cpp:func:`hpx::ranges::generate` - :cpp:func:`hpx::ranges::generate_n` - :cpp:func:`hpx::ranges::includes` +- :cpp:func:`hpx::ranges::inplace_merge` - :cpp:func:`hpx::ranges::is_heap` - :cpp:func:`hpx::ranges::is_heap_until` - :cpp:func:`hpx::ranges::make_heap` +- :cpp:func:`hpx::ranges::merge` - :cpp:func:`hpx::ranges::move` - :cpp:func:`hpx::ranges::none_of` - :cpp:func:`hpx::ranges::set_difference` diff --git a/docs/sphinx/manual/writing_single_node_hpx_applications.rst b/docs/sphinx/manual/writing_single_node_hpx_applications.rst index b1687faac78c..ab846e1eb056 100644 --- a/docs/sphinx/manual/writing_single_node_hpx_applications.rst +++ b/docs/sphinx/manual/writing_single_node_hpx_applications.rst @@ -537,11 +537,11 @@ Parallel algorithms * Description * In header * Algorithm page at cppreference.com - * * :cpp:func:`hpx::parallel::v1::merge` + * * :cpp:func:`hpx::merge` * Merges two sorted ranges. * ```` * :cppreference-algorithm:`merge` - * * :cpp:func:`hpx::parallel::v1::inplace_merge` + * * :cpp:func:`hpx::inplace_merge` * Merges two ordered ranges in-place. * ```` * :cppreference-algorithm:`inplace_merge` diff --git a/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/merge.hpp b/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/merge.hpp index 969e2a94e1e9..2a897f9e30b9 100644 --- a/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/merge.hpp +++ b/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/merge.hpp @@ -8,24 +8,120 @@ #pragma once -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#if defined(DOXYGEN) +namespace hpx { namespace ranges { + // clang-format off -#include -#include + /// Merges two sorted ranges [first1, last1) and [first2, last2) + /// into one sorted range beginning at \a dest. The order of + /// equivalent elements in the each of original two ranges is preserved. + /// For equivalent elements in the original two ranges, the elements from + /// the first range precede the elements from the second range. + /// The destination range cannot overlap with either of the input ranges. + /// + /// \note Complexity: Performs + /// O(std::distance(first1, last1) + std::distance(first2, last2)) + /// applications of the comparison \a comp and the each projection. + /// + /// \tparam ExPolicy The type of the execution policy to use (deduced). + /// It describes the manner in which the execution + /// of the algorithm may be parallelized and the manner + /// in which it executes the assignments. + /// \tparam Iter1 The type of the source iterators used (deduced) + /// representing the first sequence. + /// This iterator type must meet the requirements of an + /// random access iterator. + /// \tparam Sent1 The type of the end source iterators used (deduced). + /// This iterator type must meet the requirements of an + /// sentinel for Iter1. + /// \tparam Iter2 The type of the source iterators used (deduced) + /// representing the second sequence. + /// This iterator type must meet the requirements of an + /// random access iterator. + /// \tparam Sent2 The type of the end source iterators used (deduced) + /// representing the second sequence. + /// This iterator type must meet the requirements of an + /// sentinel for Iter2. + /// \tparam Iter3 The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of an + /// random access iterator. + /// \tparam Comp The type of the function/function object to use + /// (deduced). Unlike its sequential form, the parallel + /// overload of \a merge requires \a Comp to meet the + /// requirements of \a CopyConstructible. This defaults + /// to std::less<> + /// \tparam Proj1 The type of an optional projection function to be + /// used for elements of the first range. This defaults + /// to \a util::projection_identity + /// \tparam Proj2 The type of an optional projection function to be + /// used for elements of the second range. This defaults + /// to \a util::projection_identity + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first1 Refers to the beginning of the sequence of elements + /// of the first range the algorithm will be applied to. + /// \param last1 Refers to the end of the sequence of elements of + /// the first range the algorithm will be applied to. + /// \param first2 Refers to the beginning of the sequence of elements + /// of the second range the algorithm will be applied to. + /// \param last2 Refers to the end of the sequence of elements of + /// the second range the algorithm will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// \param comp \a comp is a callable object which returns true if + /// the first argument is less than the second, + /// and false otherwise. The signature of this + /// comparison should be equivalent to: + /// \code + /// bool comp(const Type1 &a, const Type2 &b); + /// \endcode \n + /// The signature does not need to have const&, but + /// the function must not modify the objects passed to + /// it. The types \a Type1 and \a Type2 must be such that + /// objects of types \a RandIter1 and \a RandIter2 can be + /// dereferenced and then implicitly converted to + /// both \a Type1 and \a Type2 + /// \param proj1 Specifies the function (or function object) which + /// will be invoked for each of the elements of the + /// first range as a projection operation before the + /// actual comparison \a comp is invoked. + /// \param proj2 Specifies the function (or function object) which + /// will be invoked for each of the elements of the + /// second range as a projection operation before the + /// actual comparison \a comp is invoked. + /// + /// The assignments in the parallel \a merge algorithm invoked with + /// an execution policy object of type \a sequenced_policy + /// execute in sequential order in the calling thread. + /// + /// The assignments in the parallel \a merge algorithm invoked with + /// an execution policy object of type \a parallel_policy or + /// \a parallel_task_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a merge algorithm returns a + /// \a hpx::future> + /// if the execution policy is of type + /// \a sequenced_task_policy or + /// \a parallel_task_policy and returns + /// \a merge_result otherwise. + /// The \a merge algorithm returns the tuple of + /// the source iterator \a last1, + /// the source iterator \a last2, + /// the destination iterator to the end of the \a dest range. + /// + template + typename util::detail::algorithm_result>::type + merge(ExPolicy&& policy, Iter1 first1, Sent1 last1, Iter2 first2, + Sent2 last2, Iter3 dest, Comp&& comp = Comp(), Proj1&& proj1 = Proj1(), + Proj2&& proj2 = Proj2()); -namespace hpx { namespace parallel { inline namespace v1 { - // TODO: Support forward and bidirectional iterator. (#2826) - // For now, only support random access iterator. /// Merges two sorted ranges [first1, last1) and [first2, last2) /// into one sorted range beginning at \a dest. The order of /// equivalent elements in the each of original two ranges is preserved. @@ -103,45 +199,108 @@ namespace hpx { namespace parallel { inline namespace v1 { /// within each thread. /// /// \returns The \a merge algorithm returns a - /// \a hpx::future > + /// \a hpx::future> /// if the execution policy is of type /// \a sequenced_task_policy or /// \a parallel_task_policy and returns - /// \a tagged_tuple - /// otherwise. + /// \a merge_result otherwise. /// The \a merge algorithm returns the tuple of /// the source iterator \a last1, /// the source iterator \a last2, /// the destination iterator to the end of the \a dest range. /// template ::value&& - hpx::traits::is_range::value&& hpx::traits::is_range< - Rng2>::value&& hpx::traits::is_iterator::value&& - traits::is_projected_range::value&& - traits::is_projected_range::value&& - traits::is_indirect_callable, - traits::projected_range>::value)> + typename Proj2 = util::projection_identity> typename util::detail::algorithm_result::type), - tag::in2(typename hpx::traits::range_iterator::type), - tag::out(RandIter3)>>::type + hpx::ranges::merge_result< + typename hpx::traits::range_iterator::type, + typename hpx::traits::range_iterator::type, + RandIter3 + > + >::type merge(ExPolicy&& policy, Rng1&& rng1, Rng2&& rng2, RandIter3 dest, - Comp&& comp = Comp(), Proj1&& proj1 = Proj1(), Proj2&& proj2 = Proj2()) - { - return merge(std::forward(policy), hpx::util::begin(rng1), - hpx::util::end(rng1), hpx::util::begin(rng2), hpx::util::end(rng2), - dest, std::forward(comp), std::forward(proj1), - std::forward(proj2)); - } + Comp&& comp = Comp(), Proj1&& proj1 = Proj1(), Proj2&& proj2 = Proj2()); + + /// Merges two consecutive sorted ranges [first, middle) and + /// [middle, last) into one sorted range [first, last). The order of + /// equivalent elements in the each of original two ranges is preserved. + /// For equivalent elements in the original two ranges, the elements from + /// the first range precede the elements from the second range. + /// + /// \note Complexity: Performs O(std::distance(first, last)) + /// applications of the comparison \a comp and the each projection. + /// + /// \tparam ExPolicy The type of the execution policy to use (deduced). + /// It describes the manner in which the execution + /// of the algorithm may be parallelized and the manner + /// in which it executes the assignments. + /// \tparam Iter The type of the source iterators used (deduced). + /// This iterator type must meet the requirements of an + /// random access iterator. + /// \tparam Sent The type of the end source iterators used (deduced). + /// This iterator type must meet the requirements of an + /// sentinel for Iter1. + /// \tparam Comp The type of the function/function object to use + /// (deduced). Unlike its sequential form, the parallel + /// overload of \a inplace_merge requires \a Comp + /// to meet the requirements of \a CopyConstructible. + /// This defaults to std::less<> + /// \tparam Proj The type of an optional projection function. This + /// defaults to \a util::projection_identity + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first Refers to the beginning of the first sorted range + /// the algorithm will be applied to. + /// \param middle Refers to the end of the first sorted range and + /// the beginning of the second sorted range + /// the algorithm will be applied to. + /// \param last Refers to the end of the second sorted range + /// the algorithm will be applied to. + /// \param comp \a comp is a callable object which returns true if + /// the first argument is less than the second, + /// and false otherwise. The signature of this + /// comparison should be equivalent to: + /// \code + /// bool comp(const Type1 &a, const Type2 &b); + /// \endcode \n + /// The signature does not need to have const&, but + /// the function must not modify the objects passed to + /// it. The types \a Type1 and \a Type2 must be + /// such that objects of types \a RandIter can be + /// dereferenced and then implicitly converted to both + /// \a Type1 and \a Type2 + /// \param proj Specifies the function (or function object) which + /// will be invoked for each of the elements as a + /// projection operation before the actual predicate + /// \a is invoked. + /// + /// The assignments in the parallel \a inplace_merge algorithm invoked + /// with an execution policy object of type \a sequenced_policy + /// execute in sequential order in the calling thread. + /// + /// The assignments in the parallel \a inplace_merge algorithm invoked + /// with an execution policy object of type \a parallel_policy or + /// \a parallel_task_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a inplace_merge algorithm returns a + /// \a hpx::future if the execution policy is of type + /// \a sequenced_task_policy or \a parallel_task_policy + /// and returns \a Iter otherwise. + /// The \a inplace_merge algorithm returns + /// the source iterator \a last + /// + template + typename util::detail::algorithm_result::type + inplace_merge(ExPolicy&& policy, Iter first, Iter middle, Sent last, + Comp&& comp = Comp(), Proj&& proj = Proj()); - // TODO: Support bidirectional iterator. (#2826) - // For now, only support random access iterator. /// Merges two consecutive sorted ranges [first, middle) and /// [middle, last) into one sorted range [first, last). The order of /// equivalent elements in the each of original two ranges is preserved. @@ -212,20 +371,469 @@ namespace hpx { namespace parallel { inline namespace v1 { /// the source iterator \a last /// template ::value&& - hpx::traits::is_range::value&& hpx::traits::is_iterator< - RandIter>::value&& traits::is_projected_range::value&& traits::is_projected::value&& - traits::is_indirect_callable, - traits::projected_range>::value)> + typename Comp = hpx::ranges::less, + typename Proj = util::projection_identity> typename util::detail::algorithm_result::type inplace_merge(ExPolicy&& policy, Rng&& rng, RandIter middle, - Comp&& comp = Comp(), Proj&& proj = Proj()) + Comp&& comp = Comp(), Proj&& proj = Proj()); + + // clang-format on +}} // namespace hpx::ranges + +#else // DOXYGEN + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 { + + /////////////////////////////////////////////////////////////////////////// + // TODO: Support forward and bidirectional iterator. (#2826) + // For now, only support random access iterator. + + // clang-format off + template ::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_indirect_callable, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + HPX_DEPRECATED_V(1, 6, + "hpx::parallel::merge is deprecated, use hpx::ranges::merge instead") + typename util::detail::algorithm_result::type, + typename hpx::traits::range_iterator::type, + RandIter3>>::type merge(ExPolicy&& policy, Rng1&& rng1, + Rng2&& rng2, RandIter3 dest, Comp&& comp = Comp(), + Proj1&& proj1 = Proj1(), Proj2&& proj2 = Proj2()) + { + using iterator_type1 = typename hpx::traits::range_iterator::type; + using iterator_type2 = typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + static_assert( + (hpx::traits::is_random_access_iterator::value), + "Requires at least random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy; + using result_type = + hpx::parallel::util::in_in_out_result; + + return hpx::parallel::v1::detail::merge().call( + std::forward(policy), is_seq(), hpx::util::begin(rng1), + hpx::util::end(rng1), hpx::util::begin(rng2), hpx::util::end(rng2), + dest, std::forward(comp), std::forward(proj1), + std::forward(proj2)); + } + + /////////////////////////////////////////////////////////////////////////// + // TODO: Support bidirectional iterator. (#2826) + // For now, only support random access iterator. + + // clang-format off + template ::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_projected::value && + hpx::parallel::traits::is_indirect_callable, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + HPX_DEPRECATED_V(1, 6, + "hpx::parallel::inplace_merge is deprecated, use " + "hpx::ranges::inplace_merge instead") + typename util::detail::algorithm_result::type + inplace_merge(ExPolicy&& policy, Rng&& rng, RandIter middle, + Comp&& comp = Comp(), Proj&& proj = Proj()) { - return inplace_merge(std::forward(policy), - hpx::util::begin(rng), middle, hpx::util::end(rng), - std::forward(comp), std::forward(proj)); + using iterator_type = typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy; + + return hpx::parallel::v1::detail::inplace_merge().call( + std::forward(policy), is_seq(), hpx::util::begin(rng), + middle, hpx::util::end(rng), std::forward(comp), + std::forward(proj)); } }}} // namespace hpx::parallel::v1 + +namespace hpx { namespace ranges { + + template + using merge_result = parallel::util::in_in_out_result; + + /////////////////////////////////////////////////////////////////////////// + // CPO for hpx::ranges::merge + HPX_INLINE_CONSTEXPR_VARIABLE struct merge_t final + : hpx::functional::tag + { + private: + // clang-format off + template ::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_indirect_callable, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result::type, + typename hpx::traits::range_iterator::type, Iter3>>::type + tag_invoke(merge_t, ExPolicy&& policy, Rng1&& rng1, Rng2&& rng2, + Iter3 dest, Comp&& comp = Comp(), Proj1&& proj1 = Proj1(), + Proj2&& proj2 = Proj2()) + { + using iterator_type1 = + typename hpx::traits::range_iterator::type; + using iterator_type2 = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + using result_type = hpx::ranges::merge_result; + + return hpx::parallel::v1::detail::merge().call( + std::forward(policy), is_seq(), + hpx::util::begin(rng1), hpx::util::end(rng1), + hpx::util::begin(rng2), hpx::util::end(rng2), dest, + std::forward(comp), std::forward(proj1), + std::forward(proj2)); + } + + // clang-format off + template ::value && + hpx::traits::is_sentinel_for::value && + hpx::parallel::traits::is_projected::value && + hpx::traits::is_sentinel_for::value && + hpx::parallel::traits::is_projected::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_indirect_callable, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result>::type + tag_invoke(merge_t, ExPolicy&& policy, Iter1 first1, Sent1 last1, + Iter2 first2, Sent2 last2, Iter3 dest, Comp&& comp = Comp(), + Proj1&& proj1 = Proj1(), Proj2&& proj2 = Proj2()) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + using result_type = hpx::ranges::merge_result; + + return hpx::parallel::v1::detail::merge().call( + std::forward(policy), is_seq(), first1, last1, first2, + last2, dest, std::forward(comp), + std::forward(proj1), std::forward(proj2)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_indirect_callable< + hpx::execution::sequenced_policy, Comp, + hpx::parallel::traits::projected_range, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend hpx::ranges::merge_result< + typename hpx::traits::range_iterator::type, + typename hpx::traits::range_iterator::type, Iter3> + tag_invoke(merge_t, Rng1&& rng1, Rng2&& rng2, Iter3 dest, + Comp&& comp = Comp(), Proj1&& proj1 = Proj1(), + Proj2&& proj2 = Proj2()) + { + using iterator_type1 = + typename hpx::traits::range_iterator::type; + using iterator_type2 = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + + using result_type = hpx::ranges::merge_result; + + return hpx::parallel::v1::detail::merge().call( + hpx::execution::seq, std::true_type(), hpx::util::begin(rng1), + hpx::util::end(rng1), hpx::util::begin(rng2), + hpx::util::end(rng2), dest, std::forward(comp), + std::forward(proj1), std::forward(proj2)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_projected::value && + hpx::traits::is_sentinel_for::value && + hpx::parallel::traits::is_projected::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_indirect_callable< + hpx::execution::sequenced_policy, Comp, + hpx::parallel::traits::projected, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend hpx::ranges::merge_result tag_invoke( + merge_t, Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2, + Iter3 dest, Comp&& comp = Comp(), Proj1&& proj1 = Proj1(), + Proj2&& proj2 = Proj2()) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires at least random access iterator."); + + using result_type = hpx::ranges::merge_result; + + return hpx::parallel::v1::detail::merge().call( + hpx::execution::seq, std::true_type(), first1, last1, first2, + last2, dest, std::forward(comp), + std::forward(proj1), std::forward(proj2)); + } + } merge{}; + + /////////////////////////////////////////////////////////////////////////// + // CPO for hpx::ranges::inplace_merge + HPX_INLINE_CONSTEXPR_VARIABLE struct inplace_merge_t final + : hpx::functional::tag + { + private: + // clang-format off + template ::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_projected::value && + hpx::parallel::traits::is_indirect_callable, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result::type + tag_invoke(inplace_merge_t, ExPolicy&& policy, Rng&& rng, Iter middle, + Comp&& comp = Comp(), Proj&& proj = Proj()) + { + using iterator_type = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + + return hpx::parallel::v1::detail::inplace_merge().call( + std::forward(policy), is_seq(), hpx::util::begin(rng), + middle, hpx::util::end(rng), std::forward(comp), + std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::traits::is_sentinel_for::value && + hpx::parallel::traits::is_projected::value && + hpx::parallel::traits::is_indirect_callable, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result::type + tag_invoke(inplace_merge_t, ExPolicy&& policy, Iter first, Iter middle, + Sent last, Comp&& comp = Comp(), Proj&& proj = Proj()) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + + return hpx::parallel::v1::detail::inplace_merge().call( + std::forward(policy), is_seq(), first, middle, last, + std::forward(comp), std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_projected_range::value && + hpx::traits::is_iterator::value && + hpx::parallel::traits::is_projected::value && + hpx::parallel::traits::is_indirect_callable< + hpx::execution::sequenced_policy, Comp, + hpx::parallel::traits::projected_range, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend Iter tag_invoke(inplace_merge_t, Rng&& rng, Iter middle, + Comp&& comp = Comp(), Proj&& proj = Proj()) + { + using iterator_type = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + static_assert(hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + + return hpx::parallel::v1::detail::inplace_merge().call( + hpx::execution::seq, std::true_type(), hpx::util::begin(rng), + middle, hpx::util::end(rng), std::forward(comp), + std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_projected::value && + hpx::parallel::traits::is_indirect_callable< + hpx::execution::sequenced_policy, Comp, + hpx::parallel::traits::projected, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend Iter tag_invoke(inplace_merge_t, Iter first, Iter middle, + Sent last, Comp&& comp = Comp(), Proj&& proj = Proj()) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Required at least random access iterator."); + + return hpx::parallel::v1::detail::inplace_merge().call( + hpx::execution::seq, std::true_type(), , first, middle, last, + std::forward(comp), std::forward(proj)); + } + } inplace_merge{}; + +}} // namespace hpx::ranges +#endif //DOXYGEN diff --git a/libs/parallelism/algorithms/include/hpx/parallel/util/projection_identity.hpp b/libs/parallelism/algorithms/include/hpx/parallel/util/projection_identity.hpp index d74be1aa0f16..867cdf9bd8b4 100644 --- a/libs/parallelism/algorithms/include/hpx/parallel/util/projection_identity.hpp +++ b/libs/parallelism/algorithms/include/hpx/parallel/util/projection_identity.hpp @@ -8,16 +8,26 @@ #include +#include #include namespace hpx { namespace parallel { namespace util { /////////////////////////////////////////////////////////////////////////// struct projection_identity { + using is_transparent = std::true_type; + template - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr T&& operator()(T&& val) const + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr T&& operator()( + T&& val) const noexcept { return std::forward(val); } }; }}} // namespace hpx::parallel::util + +namespace hpx { + + // C++20 introduces std::identity + using identity = hpx::parallel::util::projection_identity; +} // namespace hpx diff --git a/libs/parallelism/algorithms/tests/performance/benchmark_inplace_merge.cpp b/libs/parallelism/algorithms/tests/performance/benchmark_inplace_merge.cpp index 54e18feb13c2..290c11266f60 100644 --- a/libs/parallelism/algorithms/tests/performance/benchmark_inplace_merge.cpp +++ b/libs/parallelism/algorithms/tests/performance/benchmark_inplace_merge.cpp @@ -84,7 +84,7 @@ double run_inplace_merge_benchmark_hpx(int test_count, ExPolicy policy, hpx::copy(hpx::execution::par, org_first, org_last, first); std::uint64_t elapsed = hpx::util::high_resolution_clock::now(); - hpx::parallel::inplace_merge(policy, first, middle, last); + hpx::inplace_merge(policy, first, middle, last); time += hpx::util::high_resolution_clock::now() - elapsed; } diff --git a/libs/parallelism/algorithms/tests/performance/benchmark_merge.cpp b/libs/parallelism/algorithms/tests/performance/benchmark_merge.cpp index 8cc317ce356a..1b1d450640a3 100644 --- a/libs/parallelism/algorithms/tests/performance/benchmark_merge.cpp +++ b/libs/parallelism/algorithms/tests/performance/benchmark_merge.cpp @@ -75,7 +75,7 @@ double run_merge_benchmark_hpx(int test_count, ExPolicy policy, FwdIter1 first1, for (int i = 0; i < test_count; ++i) { - hpx::parallel::merge(policy, first1, last1, first2, last2, dest); + hpx::merge(policy, first1, last1, first2, last2, dest); } time = hpx::util::high_resolution_clock::now() - time; diff --git a/libs/parallelism/algorithms/tests/regressions/stable_merge_2964.cpp b/libs/parallelism/algorithms/tests/regressions/stable_merge_2964.cpp index fb31dff5228a..4a115d27411c 100644 --- a/libs/parallelism/algorithms/tests/regressions/stable_merge_2964.cpp +++ b/libs/parallelism/algorithms/tests/regressions/stable_merge_2964.cpp @@ -70,7 +70,7 @@ void test_merge_stable(ExPolicy policy, DataType, int rand_base) std::sort(std::begin(src1), std::end(src1)); std::sort(std::begin(src2), std::end(src2)); - hpx::parallel::merge( + hpx::ranges::merge( policy, std::begin(src1), std::end(src1), std::begin(src2), std::end(src2), std::begin(dest), [](DataType const& a, DataType const& b) -> bool { return a < b; }, @@ -127,7 +127,7 @@ int hpx_main(int argc, char** argv) // I expect a stable merge to order {3, 'a'} and {3, 'b'} before {3, 'c'} // because they come from the first sequence - hpx::parallel::merge(hpx::execution::par, a1.begin(), a1.end(), a2.begin(), + hpx::ranges::merge(hpx::execution::par, a1.begin(), a1.end(), a2.begin(), a2.end(), result.begin(), [](ElemType const& a, ElemType const& b) { return std::get<0>(a) < std::get<0>(b); }); @@ -139,7 +139,7 @@ int hpx_main(int argc, char** argv) HPX_TEST(result == solution); // Expect {3, 'c'}, {3, 'a'}, {3, 'b'} in order. - hpx::parallel::merge(hpx::execution::par, a2.begin(), a2.end(), a1.begin(), + hpx::ranges::merge(hpx::execution::par, a2.begin(), a2.end(), a1.begin(), a1.end(), result.begin(), [](ElemType const& a, ElemType const& b) { return std::get<0>(a) < std::get<0>(b); }); @@ -150,7 +150,7 @@ int hpx_main(int argc, char** argv) HPX_TEST(result == solution); - // Do moduler operation for avoiding overflow in ramdom_fill. (#2954) + // Do modulus operation for avoiding overflow in ramdom_fill. (#2954) std::uniform_int_distribution<> dis(0, 9999); int rand_base = dis(_rand); diff --git a/libs/parallelism/algorithms/tests/unit/container_algorithms/inplace_merge_range.cpp b/libs/parallelism/algorithms/tests/unit/container_algorithms/inplace_merge_range.cpp index c35a706340d9..4883ea083e9e 100644 --- a/libs/parallelism/algorithms/tests/unit/container_algorithms/inplace_merge_range.cpp +++ b/libs/parallelism/algorithms/tests/unit/container_algorithms/inplace_merge_range.cpp @@ -30,7 +30,7 @@ struct user_defined_type user_defined_type() = default; user_defined_type(int rand_no) : val(rand_no) - , name(name_list[std::rand() % name_list.size()]) + , name(name_list[_gen() % name_list.size()]) { } @@ -78,7 +78,7 @@ const std::vector user_defined_type::name_list{ struct random_fill { random_fill(int rand_base, int range) - : gen(std::rand()) + : gen(_gen()) , dist(rand_base - range / 2, rand_base + range / 2) { } @@ -166,9 +166,7 @@ template void test_inplace_merge_stable( ExPolicy&& policy, IteratorTag, DataType, int rand_base) { -#if defined(HPX_HAVE_STABLE_INPLACE_MERGE) - static_assert( - hpx::is_execution_policy::value, + static_assert(hpx::is_execution_policy::value, "hpx::is_execution_policy::value"); typedef typename std::pair ElemType; @@ -185,16 +183,16 @@ void test_inplace_merge_stable( int no = 0; auto rf = random_fill(rand_base, 6); std::generate(res_first, res_middle, [&no, &rf]() -> std::pair { - return { rf(), no++ }; - }); + return {rf(), no++}; + }); rf = random_fill(rand_base, 8); std::generate(res_middle, res_last, [&no, &rf]() -> std::pair { - return { rf(), no++ }; - }); + return {rf(), no++}; + }); std::sort(res_first, res_middle); std::sort(res_middle, res_last); - hpx::inplace_merge( + hpx::ranges::inplace_merge( policy, iterator(res_first), iterator(res_middle), iterator(res_last), [](DataType const& a, DataType const& b) -> bool { return a < b; }, [](ElemType const& elem) -> DataType const& { @@ -218,7 +216,6 @@ void test_inplace_merge_stable( HPX_TEST(test_is_meaningful); HPX_TEST(stable); -#endif } /////////////////////////////////////////////////////////////////////////////// @@ -241,28 +238,45 @@ void test_inplace_merge() test_inplace_merge(); } +template +void test_inplace_merge_stable() +{ + ////////// Test cases for checking whether the algorithm is stable. + using namespace hpx::execution; -////////// Test cases for checking whether the algorithm is stable. -test_inplace_merge_stable(seq, IteratorTag(), int(), rand_base); -test_inplace_merge_stable(par, IteratorTag(), int(), rand_base); -test_inplace_merge_stable(par_unseq, IteratorTag(), int(), rand_base); -test_inplace_merge_stable( - seq, IteratorTag(), user_defined_type(), rand_base); -test_inplace_merge_stable( - par, IteratorTag(), user_defined_type(), rand_base); -test_inplace_merge_stable( - par_unseq, IteratorTag(), user_defined_type(), rand_base); + int rand_base = _gen(); + + ////////// Test cases for checking whether the algorithm is stable. + test_inplace_merge_stable(seq, IteratorTag(), int(), rand_base); + test_inplace_merge_stable(par, IteratorTag(), int(), rand_base); + test_inplace_merge_stable(par_unseq, IteratorTag(), int(), rand_base); + test_inplace_merge_stable( + seq, IteratorTag(), user_defined_type(), rand_base); + test_inplace_merge_stable( + par, IteratorTag(), user_defined_type(), rand_base); + test_inplace_merge_stable( + par_unseq, IteratorTag(), user_defined_type(), rand_base); +} + +void test_inplace_merge_stable() +{ + test_inplace_merge_stable(); + test_inplace_merge_stable(); +} 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(); - + _gen.seed(seed); + } std::cout << "using seed: " << seed << std::endl; - std::srand(seed); test_inplace_merge(); + test_inplace_merge_stable(); + return hpx::finalize(); } diff --git a/libs/parallelism/algorithms/tests/unit/container_algorithms/merge_range.cpp b/libs/parallelism/algorithms/tests/unit/container_algorithms/merge_range.cpp index 724a5562c69f..34366ff6dbe1 100644 --- a/libs/parallelism/algorithms/tests/unit/container_algorithms/merge_range.cpp +++ b/libs/parallelism/algorithms/tests/unit/container_algorithms/merge_range.cpp @@ -93,14 +93,37 @@ struct random_fill }; //////////////////////////////////////////////////////////////////////////// +template +void test_merge(DataType) +{ + std::size_t const size1 = 300007, size2 = 123456; + std::vector src1(size1), src2(size2), dest_res(size1 + size2), + dest_sol(size1 + size2); + + std::generate(std::begin(src1), std::end(src1), random_fill(0, 6)); + std::generate(std::begin(src2), std::end(src2), random_fill(0, 8)); + std::sort(std::begin(src1), std::end(src1)); + std::sort(std::begin(src2), std::end(src2)); + + auto result = hpx::ranges::merge(src1, src2, std::begin(dest_res)); + auto solution = std::merge(std::begin(src1), std::end(src1), + std::begin(src2), std::end(src2), std::begin(dest_sol)); + + HPX_TEST(result.in1 == std::end(src1)); + HPX_TEST(result.in2 == std::end(src2)); + + bool equality = test::equal( + std::begin(dest_res), result.out, std::begin(dest_sol), solution); + + HPX_TEST(equality); +} + template -void test_merge(ExPolicy policy, DataType) +void test_merge(ExPolicy&& policy, DataType) { static_assert(hpx::is_execution_policy::value, "hpx::is_execution_policy::value"); - using hpx::get; - std::size_t const size1 = 300007, size2 = 123456; std::vector src1(size1), src2(size2), dest_res(size1 + size2), dest_sol(size1 + size2); @@ -110,28 +133,25 @@ void test_merge(ExPolicy policy, DataType) std::sort(std::begin(src1), std::end(src1)); std::sort(std::begin(src2), std::end(src2)); - auto result = - hpx::parallel::merge(policy, src1, src2, std::begin(dest_res)); + auto result = hpx::ranges::merge(policy, src1, src2, std::begin(dest_res)); auto solution = std::merge(std::begin(src1), std::end(src1), std::begin(src2), std::end(src2), std::begin(dest_sol)); - HPX_TEST(get<0>(result) == std::end(src1)); - HPX_TEST(get<1>(result) == std::end(src2)); + HPX_TEST(result.in1 == std::end(src1)); + HPX_TEST(result.in2 == std::end(src2)); bool equality = test::equal( - std::begin(dest_res), get<2>(result), std::begin(dest_sol), solution); + std::begin(dest_res), result.out, std::begin(dest_sol), solution); HPX_TEST(equality); } template -void test_merge_async(ExPolicy policy, DataType) +void test_merge_async(ExPolicy&& policy, DataType) { static_assert(hpx::is_execution_policy::value, "hpx::is_execution_policy::value"); - using hpx::get; - std::size_t const size1 = 300007, size2 = 123456; std::vector src1(size1), src2(size2), dest_res(size1 + size2), dest_sol(size1 + size2); @@ -141,16 +161,16 @@ void test_merge_async(ExPolicy policy, DataType) std::sort(std::begin(src1), std::end(src1)); std::sort(std::begin(src2), std::end(src2)); - auto f = hpx::parallel::merge(policy, src1, src2, std::begin(dest_res)); + auto f = hpx::ranges::merge(policy, src1, src2, std::begin(dest_res)); auto result = f.get(); auto solution = std::merge(std::begin(src1), std::end(src1), std::begin(src2), std::end(src2), std::begin(dest_sol)); - HPX_TEST(get<0>(result) == std::end(src1)); - HPX_TEST(get<1>(result) == std::end(src2)); + HPX_TEST(result.in1 == std::end(src1)); + HPX_TEST(result.in2 == std::end(src2)); bool equality = test::equal( - std::begin(dest_res), get<2>(result), std::begin(dest_sol), solution); + std::begin(dest_res), result.out, std::begin(dest_sol), solution); HPX_TEST(equality); } @@ -161,6 +181,8 @@ void test_merge() { using namespace hpx::execution; + test_merge(DataType()); + test_merge(seq, DataType()); test_merge(par, DataType()); test_merge(par_unseq, DataType()); @@ -222,8 +244,7 @@ void test_merge_etc(IteratorTag, DataType, int rand_base) template void test_merge_etc(ExPolicy&& policy, IteratorTag, DataType, int rand_base) { - static_assert( - hpx::is_execution_policy::value, + static_assert(hpx::is_execution_policy::value, "hpx::is_execution_policy::value"); typedef typename std::vector::iterator base_iterator; @@ -291,7 +312,7 @@ void test_merge_stable(IteratorTag, DataType, int rand_base) std::sort(std::begin(src1), std::end(src1)); std::sort(std::begin(src2), std::end(src2)); - hpx::merge( + hpx::ranges::merge( iterator(std::begin(src1)), iterator(std::end(src1)), iterator(std::begin(src2)), iterator(std::end(src2)), iterator(std::begin(dest)), @@ -326,8 +347,7 @@ void test_merge_stable(IteratorTag, DataType, int rand_base) template void test_merge_stable(ExPolicy&& policy, IteratorTag, DataType, int rand_base) { - static_assert( - hpx::is_execution_policy::value, + static_assert(hpx::is_execution_policy::value, "hpx::is_execution_policy::value"); typedef typename std::pair ElemType; @@ -351,7 +371,7 @@ void test_merge_stable(ExPolicy&& policy, IteratorTag, DataType, int rand_base) std::sort(std::begin(src1), std::end(src1)); std::sort(std::begin(src2), std::end(src2)); - hpx::merge( + hpx::ranges::merge( policy, iterator(std::begin(src1)), iterator(std::end(src1)), iterator(std::begin(src2)), iterator(std::end(src2)), iterator(std::begin(dest)),