diff --git a/libs/full/include/CMakeLists.txt b/libs/full/include/CMakeLists.txt index 5a3387e4ca83..f3051c1f9102 100644 --- a/libs/full/include/CMakeLists.txt +++ b/libs/full/include/CMakeLists.txt @@ -61,6 +61,7 @@ set(include_headers hpx/include/parallel_is_partitioned.hpp hpx/include/parallel_is_sorted.hpp hpx/include/parallel_lexicographical_compare.hpp + hpx/include/parallel_make_heap.hpp hpx/include/parallel_memory.hpp hpx/include/parallel_merge.hpp hpx/include/parallel_minmax.hpp diff --git a/libs/full/include/include/hpx/include/parallel_make_heap.hpp b/libs/full/include/include/hpx/include/parallel_make_heap.hpp new file mode 100644 index 000000000000..d61678da779d --- /dev/null +++ b/libs/full/include/include/hpx/include/parallel_make_heap.hpp @@ -0,0 +1,10 @@ +// Copyright (c) 2015 Grant Mercer +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include diff --git a/libs/parallelism/algorithms/CMakeLists.txt b/libs/parallelism/algorithms/CMakeLists.txt index c347bbf5f1da..31307544513c 100644 --- a/libs/parallelism/algorithms/CMakeLists.txt +++ b/libs/parallelism/algorithms/CMakeLists.txt @@ -47,6 +47,7 @@ set(algorithms_headers hpx/parallel/algorithms/is_partitioned.hpp hpx/parallel/algorithms/is_sorted.hpp hpx/parallel/algorithms/lexicographical_compare.hpp + hpx/parallel/algorithms/make_heap.hpp hpx/parallel/algorithms/merge.hpp hpx/parallel/algorithms/minmax.hpp hpx/parallel/algorithms/mismatch.hpp @@ -90,6 +91,7 @@ set(algorithms_headers hpx/parallel/container_algorithms/generate.hpp hpx/parallel/container_algorithms.hpp hpx/parallel/container_algorithms/is_heap.hpp + hpx/parallel/container_algorithms/make_heap.hpp hpx/parallel/container_algorithms/merge.hpp hpx/parallel/container_algorithms/minmax.hpp hpx/parallel/container_algorithms/mismatch.hpp diff --git a/libs/parallelism/algorithms/include/hpx/parallel/algorithm.hpp b/libs/parallelism/algorithms/include/hpx/parallel/algorithm.hpp index 8bac4f1f16e8..5fd43e7772ec 100644 --- a/libs/parallelism/algorithms/include/hpx/parallel/algorithm.hpp +++ b/libs/parallelism/algorithms/include/hpx/parallel/algorithm.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2016 Hartmut Kaiser +// Copyright (c) 2007-2020 Hartmut Kaiser // Copyright (c) 2014 Grant Mercer // Copyright (c) 2017 Taeguk Kwon // @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/parallelism/algorithms/include/hpx/parallel/algorithms/make_heap.hpp b/libs/parallelism/algorithms/include/hpx/parallel/algorithms/make_heap.hpp new file mode 100644 index 000000000000..507e9235722a --- /dev/null +++ b/libs/parallelism/algorithms/include/hpx/parallel/algorithms/make_heap.hpp @@ -0,0 +1,525 @@ +// Copyright (c) 2015 Grant Mercer +// +// 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) + +/// \file parallel/algorithms/make_heap.hpp + +#pragma once + +#if defined(DOXYGEN) +namespace hpx { + // clang-format off + + /// Constructs a \a max \a heap in the range [first, last). + /// + /// \note Complexity: at most (3*N) comparisons where + /// \a N = distance(first, last). + /// + /// \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 RndIter The type of the source iterators used for algorithm. + /// This iterator must meet the requirements for a + /// random access iterator. + /// \param first Refers to the beginning of the sequence of elements + /// of that the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements of + /// that the algorithm will be applied to. + /// \param comp Refers to the binary predicate which returns true + /// if the first argument should be treated as less than + /// the second. The signature of the function should be + /// equivalent to + /// \code + /// bool comp(const Type &a, const Type &b); + /// \endcode \n + /// The signature does not need to have const &, but + /// the function must not modify the objects passed to + /// it. The type \a Type must be such that objects of + /// types \a RndIter can be dereferenced and then + /// implicitly converted to Type. + /// + /// The predicate operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a sequential_execution_policy + /// executes in sequential order in the calling thread. + /// + /// The comparison operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a parallel_execution_policy + /// or \a parallel_task_execution_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a make_heap algorithm returns a \a hpx::future + /// if the execution policy is of type \a task_execution_policy + /// and returns \a void otherwise. + /// + template + typename util::detail::algorithm_result::type make_heap( + ExPolicy&& policy, RndIter first, RndIter last, Comp&& comp); + + /// Constructs a \a max \a heap in the range [first, last). Uses the + /// operator \a < for comparisons. + /// + /// \note Complexity: at most (3*N) comparisons where + /// \a N = distance(first, last). + /// + /// \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 RndIter The type of the source iterators used for algorithm. + /// This iterator must meet the requirements for a + /// random access iterator. + /// \param first Refers to the beginning of the sequence of elements + /// of that the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements of + /// that the algorithm will be applied to. + /// + /// The predicate operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a sequential_execution_policy + /// executes in sequential order in the calling thread. + /// + /// The comparison operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a parallel_execution_policy + /// or \a parallel_task_execution_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a make_heap algorithm returns a \a hpx::future + /// if the execution policy is of type \a task_execution_policy + /// and returns \a void otherwise. + /// + template + typename hpx::parallel::util::detail::algorithm_result::type + make_heap(ExPolicy&& policy, RndIter first, RndIter last); + + // clang-format on +} // namespace hpx + +#else // DOXYGEN + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 { + + ////////////////////////////////////////////////////////////////////// + // make_heap + namespace detail { + + // Perform bottom up heap construction given a range of elements. + // sift_down_range will take a range from [start,start-count) and + // apply sift_down to each element in the range + template + void sift_down(RndIter first, Comp&& comp, Proj&& proj, + typename std::iterator_traits::difference_type len, + RndIter start) + { + typename std::iterator_traits::difference_type child = + start - first; + + if (len < 2 || (len - 2) / 2 < child) + return; + + child = 2 * child + 1; + RndIter child_i = first + child; + + if ((child + 1) < len && + hpx::util::invoke(comp, hpx::util::invoke(proj, *child_i), + hpx::util::invoke(proj, *(child_i + 1)))) + { + ++child_i; + ++child; + } + + if (hpx::util::invoke(comp, hpx::util::invoke(proj, *child_i), + hpx::util::invoke(proj, *start))) + return; + + typename std::iterator_traits::value_type top = *start; + + do + { + *start = *child_i; + start = child_i; + + if ((len - 2) / 2 < child) + break; + + child = 2 * child + 1; + child_i = first + child; + + if ((child + 1) < len && + hpx::util::invoke(comp, hpx::util::invoke(proj, *child_i), + hpx::util::invoke(proj, *(child_i + 1)))) + { + ++child_i; + ++child; + } + + } while (!hpx::util::invoke(comp, hpx::util::invoke(proj, *child_i), + hpx::util::invoke(proj, top))); + + *start = top; + } + + template + void sift_down_range(RndIter first, Comp&& comp, Proj&& proj, + typename std::iterator_traits::difference_type len, + RndIter start, std::size_t count) + { + for (std::size_t i = 0; i != count; ++i) + { + sift_down(first, comp, proj, len, start - i); + } + } + + template + Iter sequential_make_heap( + Iter first, Sent last, Comp&& comp, Proj&& proj) + { + using difference_type = + typename std::iterator_traits::difference_type; + + difference_type n = last - first; + if (n > 1) + { + for (difference_type start = (n - 2) / 2; start >= 0; --start) + { + sift_down(first, comp, proj, n, first + start); + } + return first + n; + } + return first; + } + + ////////////////////////////////////////////////////////////////////// + template + struct make_heap : public detail::algorithm, Iter> + { + make_heap() + : make_heap::algorithm("make_heap") + { + } + + template + static RndIter sequential( + ExPolicy, RndIter first, Sent last, Comp&& comp, Proj&& proj) + { + return sequential_make_heap(first, last, + std::forward(comp), std::forward(proj)); + } + + template + static + typename util::detail::algorithm_result::type + make_heap_thread(ExPolicy&& policy, RndIter first, Sent last, + Comp&& comp, Proj&& proj) + { + typename std::iterator_traits::difference_type n = + last - first; + if (n <= 1) + { + return util::detail::algorithm_result::get(std::move(first)); + } + + using execution_policy = typename std::decay::type; + using parameters_type = + typename execution_policy::executor_parameters_type; + using executor_type = typename execution_policy::executor_type; + + using scoped_executor_parameters = + util::detail::scoped_executor_parameters_ref< + parameters_type, executor_type>; + + scoped_executor_parameters scoped_params( + policy.parameters(), policy.executor()); + + std::vector> workitems; + std::list errors; + + using tuple_type = hpx::tuple; + + auto op = [=](tuple_type const& t) { + sift_down_range(first, comp, proj, (std::size_t) n, + hpx::get<0>(t), hpx::get<1>(t)); + }; + + std::size_t const cores = execution::processing_units_count( + policy.parameters(), policy.executor()); + + // Take a standard chunk size (amount of work / cores), and only + // take a quarter of that. If our chunk size is too large a LOT + // of the work will be done sequentially due to the level + // barrier of heap parallelism. + // 1/4 of the standard chunk size is an estimate to lower the + // average number of levels done sequentially + std::size_t chunk_size = execution::get_chunk_size( + policy.parameters(), policy.executor(), + [](std::size_t) { return 0; }, cores, n); + chunk_size /= 4; + + std::size_t max_chunks = execution::maximal_number_of_chunks( + policy.parameters(), policy.executor(), cores, n); + + util::detail::adjust_chunk_size_and_max_chunks( + cores, n, chunk_size, max_chunks); + + try + { + // Get workitems that are to be run in parallel + std::size_t start = (n - 2) / 2; + while (start > 0) + { + // Index of start of level, and amount of items in level + std::size_t end_exclusive = + (std::size_t) std::pow( + 2, std::floor(std::log2(start))) - + 2; + std::size_t level_items = (start - end_exclusive); + + // If we can't at least run two chunks in parallel, + // don't bother parallelizing and simply run sequentially + if (chunk_size * 2 > level_items) + { + op(hpx::make_tuple(first + start, level_items)); + } + else + { + std::vector shapes; + shapes.reserve(level_items / chunk_size + 1); + + std::size_t cnt = 0; + while (cnt + chunk_size < level_items) + { + shapes.push_back(hpx::make_tuple( + first + start - cnt, chunk_size)); + cnt += chunk_size; + } + + // Schedule any left-over work + if (cnt < level_items) + { + shapes.push_back(hpx::make_tuple( + first + start - cnt, level_items - cnt)); + } + + // Reserve items/chunk_size spaces for async calls + workitems = execution::bulk_async_execute( + policy.executor(), op, shapes); + + // Required synchronization per level + hpx::wait_all(workitems); + + // collect exceptions + util::detail::handle_local_exceptions< + ExPolicy>::call(workitems, errors, false); + workitems.clear(); + } + + if (!errors.empty()) + break; // stop on errors + + start = end_exclusive; + } + + scoped_params.mark_end_of_scheduling(); + + // Perform sift down for the head node + sift_down(first, comp = std::forward(comp), + proj = std::forward(proj), n, first); + } + catch (...) + { + util::detail::handle_local_exceptions::call( + std::current_exception(), errors); + } + + // rethrow exceptions, if any + util::detail::handle_local_exceptions::call( + workitems, errors); + + std::advance(first, n); + return util::detail::algorithm_result::get( + std::move(first)); + } + + template + static + typename util::detail::algorithm_result::type + parallel(ExPolicy&& policy, RndIter first, Sent last, + Comp&& comp, Proj&& proj) + { + return make_heap_thread(std::forward(policy), first, + last, std::forward(comp), std::forward(proj)); + } + + template + static typename util::detail::algorithm_result< + execution::parallel_task_policy, RndIter>::type + parallel(execution::parallel_task_policy policy, RndIter first, + Sent last, Comp&& comp, Proj&& proj) + { + return execution::async_execute(policy.executor(), + [=, comp = std::forward(comp), + proj = std::forward(proj)]() mutable { + return make_heap_thread(policy, first, last, + std::forward(comp), std::forward(proj)); + }); + } + }; + } // namespace detail +}}} // namespace hpx::parallel::v1 + +namespace hpx { + + /////////////////////////////////////////////////////////////////////////// + // CPO for hpx::make_heap + HPX_INLINE_CONSTEXPR_VARIABLE struct make_heap_t final + : hpx::functional::tag + { + private: + // clang-format off + template ::value && + hpx::traits::is_iterator::value && + hpx::traits::is_invocable::value_type, + typename std::iterator_traits::value_type + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result< + ExPolicy>::type + tag_invoke(make_heap_t, ExPolicy&& policy, RndIter first, RndIter last, + Comp&& comp) + { + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + + return hpx::parallel::util::detail::algorithm_result::get( + hpx::parallel::v1::detail::make_heap().call( + std::forward(policy), is_seq{}, first, last, + std::forward(comp), + hpx::parallel::util::projection_identity{})); + } + + // clang-format off + template ::value && + hpx::traits::is_iterator::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result< + ExPolicy>::type + tag_invoke(make_heap_t, ExPolicy&& policy, RndIter first, RndIter last) + { + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + using value_type = + typename std::iterator_traits::value_type; + + return hpx::parallel::util::detail::algorithm_result::get( + hpx::parallel::v1::detail::make_heap().call( + std::forward(policy), is_seq{}, first, last, + std::less(), + hpx::parallel::util::projection_identity{})); + } + + // clang-format off + template ::value && + hpx::traits::is_invocable::value_type, + typename std::iterator_traits::value_type + >::value + )> + // clang-format on + friend void tag_invoke( + make_heap_t, RndIter first, RndIter last, Comp&& comp) + { + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + hpx::parallel::v1::detail::make_heap().call( + hpx::parallel::execution::seq, std::true_type{}, first, last, + std::forward(comp), + hpx::parallel::util::projection_identity{}); + } + + // clang-format off + template ::value + )> + // clang-format on + friend void tag_invoke(make_heap_t, RndIter first, RndIter last) + { + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using value_type = + typename std::iterator_traits::value_type; + + hpx::parallel::v1::detail::make_heap().call( + hpx::parallel::execution::seq, std::true_type{}, first, last, + std::less(), + hpx::parallel::util::projection_identity{}); + } + } make_heap{}; +} // namespace hpx + +#endif // DOXYGEN diff --git a/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms.hpp b/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms.hpp index 31e16bcbf964..07a26e280d68 100644 --- a/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms.hpp +++ b/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/make_heap.hpp b/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/make_heap.hpp new file mode 100644 index 000000000000..81ae8408c173 --- /dev/null +++ b/libs/parallelism/algorithms/include/hpx/parallel/container_algorithms/make_heap.hpp @@ -0,0 +1,396 @@ +// Copyright (c) 2020 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/// \file parallel/algorithms/make_heap.hpp + +#pragma once + +#if defined(DOXYGEN) +namespace hpx { namespace ranges { + // clang-format off + + /// Constructs a \a max \a heap in the range [first, last). + /// + /// \note Complexity: at most (3*N) comparisons where + /// \a N = distance(first, last). + /// + /// \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 Rng The type of the source range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam Comp The type of the function/function object to use + /// (deduced). + /// \tparam Proj The type of an optional projection function. This + /// defaults to \a util::projection_identity + /// + /// \param rng Refers to the sequence of elements the algorithm + /// will be applied to. + /// \param comp Refers to the binary predicate which returns true + /// if the first argument should be treated as less than + /// the second. The signature of the function should be + /// equivalent to + /// \code + /// bool comp(const Type &a, const Type &b); + /// \endcode \n + /// The signature does not need to have const &, but + /// the function must not modify the objects passed to + /// it. The type \a Type must be such that objects of + /// types \a RndIter can be dereferenced and then + /// implicitly converted to Type. + /// \param proj Specifies the function (or function object) which + /// will be invoked for each pair of elements as a + /// projection operation before the actual predicate + /// \a comp is invoked. + /// + /// The predicate operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a sequential_execution_policy + /// executes in sequential order in the calling thread. + /// + /// The comparison operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a parallel_execution_policy + /// or \a parallel_task_execution_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a make_heap 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. + /// It returns \a last. + /// + template + typename util::detail::algorithm_result::type>::type + make_heap(ExPolicy&& policy, Rng&& rng, Comp&& comp, Proj&& proj = Proj{}); + + /// Constructs a \a max \a heap in the range [first, last). Uses the + /// operator \a < for comparisons. + /// + /// \note Complexity: at most (3*N) comparisons where + /// \a N = distance(first, last). + /// + /// \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 Rng The type of the source range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam Proj The type of an optional projection function. This + /// defaults to \a util::projection_identity + /// + /// \param rng Refers to the sequence of elements the algorithm + /// will be applied to. + /// \param proj Specifies the function (or function object) which + /// will be invoked for each pair of elements as a + /// projection operation before the actual predicate + /// \a comp is invoked. + /// + /// The predicate operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a sequential_execution_policy + /// executes in sequential order in the calling thread. + /// + /// The comparison operations in the parallel \a make_heap algorithm invoked + /// with an execution policy object of type \a parallel_execution_policy + /// or \a parallel_task_execution_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a make_heap algorithm returns a \a hpx::future + /// if the execution policy is of type \a task_execution_policy + /// and returns \a void otherwise. + /// + template + typename util::detail::algorithm_result::type>::type + make_heap(ExPolicy&& policy, Rng&& rng, 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 +#include + +namespace hpx { namespace ranges { + + /////////////////////////////////////////////////////////////////////////// + // CPO for hpx::ranges::make_heap + HPX_INLINE_CONSTEXPR_VARIABLE struct make_heap_t final + : hpx::functional::tag + { + private: + // clang-format off + template ::value && + hpx::traits::is_sentinel_for::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(make_heap_t, ExPolicy&& policy, Iter first, Sent last, + Comp&& comp, Proj&& proj = Proj{}) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + + return hpx::parallel::v1::detail::make_heap().call( + std::forward(policy), is_seq{}, first, last, + std::forward(comp), std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::traits::is_range::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>::type + tag_invoke(make_heap_t, ExPolicy&& policy, Rng& rng, Comp&& comp, + Proj&& proj = Proj{}) + { + using iterator_type = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + + return hpx::parallel::v1::detail::make_heap().call( + std::forward(policy), is_seq{}, hpx::util::begin(rng), + 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_indirect_callable::value_type>, + hpx::parallel::traits::projected, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result::type + tag_invoke(make_heap_t, ExPolicy&& policy, Iter first, Sent last, + Proj&& proj = Proj{}) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + using value_type = typename std::iterator_traits::value_type; + + return hpx::parallel::v1::detail::make_heap().call( + std::forward(policy), is_seq{}, first, last, + std::less(), std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::traits::is_range::value && + hpx::parallel::traits::is_indirect_callable::type + >::value_type>, + hpx::parallel::traits::projected_range, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend typename hpx::parallel::util::detail::algorithm_result::type>::type + tag_invoke( + make_heap_t, ExPolicy&& policy, Rng&& rng, Proj&& proj = Proj{}) + { + using iterator_type = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using is_seq = + hpx::parallel::execution::is_sequenced_execution_policy< + ExPolicy>; + using value_type = + typename std::iterator_traits::value_type; + + return hpx::parallel::v1::detail::make_heap().call( + std::forward(policy), is_seq{}, hpx::util::begin(rng), + hpx::util::end(rng), std::less(), + std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_indirect_callable< + hpx::parallel::execution::sequenced_policy, Comp, + hpx::parallel::traits::projected, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend Iter tag_invoke(make_heap_t, Iter first, Sent last, Comp&& comp, + Proj&& proj = Proj{}) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + return hpx::parallel::v1::detail::make_heap().call( + hpx::parallel::execution::seq, std::true_type{}, first, last, + std::forward(comp), std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_indirect_callable< + hpx::parallel::execution::sequenced_policy, Comp, + hpx::parallel::traits::projected_range, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend typename hpx::traits::range_iterator::type tag_invoke( + make_heap_t, Rng& rng, Comp&& comp, Proj&& proj = Proj{}) + { + using iterator_type = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + return hpx::parallel::v1::detail::make_heap().call( + hpx::parallel::execution::seq, std::true_type{}, + hpx::util::begin(rng), hpx::util::end(rng), + std::forward(comp), std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_indirect_callable< + hpx::parallel::execution::sequenced_policy, + std::less::value_type>, + hpx::parallel::traits::projected, + hpx::parallel::traits::projected + >::value + )> + // clang-format on + friend Iter tag_invoke( + make_heap_t, Iter first, Sent last, Proj&& proj = Proj{}) + { + static_assert(hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using value_type = typename std::iterator_traits::value_type; + + return hpx::parallel::v1::detail::make_heap().call( + hpx::parallel::execution::seq, std::true_type{}, first, last, + std::less(), std::forward(proj)); + } + + // clang-format off + template ::value && + hpx::parallel::traits::is_indirect_callable< + hpx::parallel::execution::sequenced_policy, + std::less::type + >::value_type>, + hpx::parallel::traits::projected_range, + hpx::parallel::traits::projected_range + >::value + )> + // clang-format on + friend typename hpx::traits::range_iterator::type tag_invoke( + make_heap_t, Rng&& rng, Proj&& proj = Proj{}) + { + using iterator_type = + typename hpx::traits::range_iterator::type; + + static_assert( + hpx::traits::is_random_access_iterator::value, + "Requires random access iterator."); + + using value_type = + typename std::iterator_traits::value_type; + + return hpx::parallel::v1::detail::make_heap().call( + hpx::parallel::execution::seq, std::true_type{}, + hpx::util::begin(rng), hpx::util::end(rng), + std::less(), std::forward(proj)); + } + } make_heap{}; +}} // namespace hpx::ranges + +#endif // DOXYGEN diff --git a/libs/parallelism/algorithms/include/hpx/parallel/util/detail/handle_local_exceptions.hpp b/libs/parallelism/algorithms/include/hpx/parallel/util/detail/handle_local_exceptions.hpp index b5774581fdcc..91523b8fe4a0 100644 --- a/libs/parallelism/algorithms/include/hpx/parallel/util/detail/handle_local_exceptions.hpp +++ b/libs/parallelism/algorithms/include/hpx/parallel/util/detail/handle_local_exceptions.hpp @@ -109,7 +109,7 @@ namespace hpx { namespace parallel { namespace util { namespace detail { } template - static void call(std::vector>& workitems, + static void call_with_cleanup(std::vector>& workitems, std::list& errors, Cleanup&& cleanup, bool throw_errors = true) { @@ -221,7 +221,8 @@ namespace hpx { namespace parallel { namespace util { namespace detail { } template - static void call(std::vector> const& workitems, + static void call_with_cleanup( + std::vector> const& workitems, std::list&, Cleanup&&, bool = true) { #if defined(HPX_COMPUTE_DEVICE_CODE) diff --git a/libs/parallelism/algorithms/include/hpx/parallel/util/partitioner_with_cleanup.hpp b/libs/parallelism/algorithms/include/hpx/parallel/util/partitioner_with_cleanup.hpp index 707f03e409b4..9e1ef11d8497 100644 --- a/libs/parallelism/algorithms/include/hpx/parallel/util/partitioner_with_cleanup.hpp +++ b/libs/parallelism/algorithms/include/hpx/parallel/util/partitioner_with_cleanup.hpp @@ -95,7 +95,7 @@ namespace hpx { namespace parallel { namespace util { // always rethrow if 'errors' is not empty or workitems has // exceptional future - handle_local_exceptions::call( + handle_local_exceptions::call_with_cleanup( workitems, errors, std::forward(cleanup)); try @@ -180,7 +180,7 @@ namespace hpx { namespace parallel { namespace util { std::vector>&& r) mutable -> R { HPX_UNUSED(scoped_params); - handle_local_exceptions::call( + handle_local_exceptions::call_with_cleanup( r, errors, std::forward(cleanup)); return f(std::move(r)); }, diff --git a/libs/parallelism/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/parallelism/algorithms/tests/unit/algorithms/CMakeLists.txt index b5c99aa3c149..2b1bc9d728c9 100644 --- a/libs/parallelism/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/parallelism/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -86,6 +86,7 @@ set(tests is_sorted is_sorted_until lexicographical_compare + make_heap max_element merge min_element diff --git a/libs/parallelism/algorithms/tests/unit/algorithms/make_heap.cpp b/libs/parallelism/algorithms/tests/unit/algorithms/make_heap.cpp new file mode 100644 index 000000000000..09939ad947fa --- /dev/null +++ b/libs/parallelism/algorithms/tests/unit/algorithms/make_heap.cpp @@ -0,0 +1,448 @@ +// Copyright (c) 2015 Grant Mercer +// +// 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 +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); + +/////////////////////////////////////////////////////////////////////////// +template +void test_make_heap1(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::make_heap(iterator(hpx::util::begin(c)), iterator(hpx::util::end(c))); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c)), true); +} + +template +void test_make_heap1(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::make_heap( + policy, iterator(hpx::util::begin(c)), iterator(hpx::util::end(c))); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c)), true); +} + +template +void test_make_heap_async1(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::future test = hpx::make_heap( + p, iterator(hpx::util::begin(c)), iterator(hpx::util::end(c))); + + test.wait(); + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c)), true); +} + +template +void test_make_heap1() +{ + using namespace hpx::parallel::execution; + + test_make_heap1(IteratorTag()); + + test_make_heap1(seq, IteratorTag()); + test_make_heap1(par, IteratorTag()); + test_make_heap1(par_unseq, IteratorTag()); + + test_make_heap_async1(seq(task), IteratorTag()); + test_make_heap_async1(par(task), IteratorTag()); +} + +void make_heap_test1() +{ + test_make_heap1(); +} + +/////////////////////////////////////////////////////////////////////////// +template +void test_make_heap2(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(25); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::make_heap(iterator(hpx::util::begin(c)), iterator(hpx::util::end(c)), + std::greater()); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c), + std::greater()), + true); +} + +template +void test_make_heap2(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(25); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::make_heap(policy, iterator(hpx::util::begin(c)), + iterator(hpx::util::end(c)), std::greater()); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c), + std::greater()), + true); +} + +template +void test_make_heap_async2(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::future test = hpx::make_heap(p, iterator(hpx::util::begin(c)), + iterator(hpx::util::end(c)), std::greater()); + + test.wait(); + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c), + std::greater()), + true); +} + +template +void test_make_heap2() +{ + using namespace hpx::parallel::execution; + + test_make_heap2(IteratorTag()); + + test_make_heap2(seq, IteratorTag()); + test_make_heap2(par, IteratorTag()); + test_make_heap2(par_unseq, IteratorTag()); + + test_make_heap_async2(seq(task), IteratorTag()); + test_make_heap_async2(par(task), IteratorTag()); +} + +void make_heap_test2() +{ + test_make_heap2(); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_make_heap_exception(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_exception = false; + try + { + hpx::make_heap(decorated_iterator(hpx::util::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_make_heap_exception(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_exception = false; + try + { + hpx::make_heap(policy, + decorated_iterator(hpx::util::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_make_heap_exception_async(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_exception = false; + bool returned_from_algorithm = false; + try + { + hpx::future f = hpx::make_heap(p, + decorated_iterator(hpx::util::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(hpx::util::end(c))); + returned_from_algorithm = true; + f.get(); + + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + } + catch (...) + { + HPX_TEST(false); + } + HPX_TEST(caught_exception); + HPX_TEST(returned_from_algorithm); +} + +template +void test_make_heap_exception() +{ + using namespace hpx::parallel::execution; + + test_make_heap_exception(IteratorTag()); + + // If the execution policy object is of type vector_execution_policy, + // std::terminate shall be called. therefore we do not test exceptions + // with a vector execution policy + test_make_heap_exception(seq, IteratorTag()); + test_make_heap_exception(par, IteratorTag()); + + test_make_heap_exception_async(seq(task), IteratorTag()); + test_make_heap_exception_async(par(task), IteratorTag()); +} + +void make_heap_exception_test() +{ + test_make_heap_exception(); +} + +////////////////////////////////////////////////////////////////////////////// +template +void test_make_heap_bad_alloc(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(100007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_bad_alloc = false; + try + { + hpx::make_heap(decorated_iterator(hpx::util::begin(c), + []() { throw std::bad_alloc(); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_make_heap_bad_alloc(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(100007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_bad_alloc = false; + try + { + hpx::make_heap(policy, + decorated_iterator( + hpx::util::begin(c), []() { throw std::bad_alloc(); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_make_heap_bad_alloc_async(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_bad_alloc = false; + bool returned_from_algorithm = false; + try + { + hpx::future f = hpx::make_heap(p, + decorated_iterator( + hpx::util::begin(c), []() { throw std::bad_alloc(); }), + decorated_iterator(hpx::util::end(c))); + returned_from_algorithm = true; + f.get(); + + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); + HPX_TEST(returned_from_algorithm); +} + +template +void test_make_heap_bad_alloc() +{ + using namespace hpx::parallel::execution; + + test_make_heap_bad_alloc(IteratorTag()); + + // If the execution policy object is of type vector_execution_policy, + // std::terminate shall be called. therefore we do not test exceptions + // with a vector execution policy + test_make_heap_bad_alloc(seq, IteratorTag()); + test_make_heap_bad_alloc(par, IteratorTag()); + + test_make_heap_bad_alloc_async(seq(task), IteratorTag()); + test_make_heap_bad_alloc_async(par(task), IteratorTag()); +} + +void make_heap_bad_alloc_test() +{ + test_make_heap_bad_alloc(); +} + +int hpx_main(boost::program_options::variables_map& vm) +{ + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + gen.seed(seed); + + make_heap_test1(); + make_heap_test2(); + make_heap_exception_test(); + make_heap_bad_alloc_test(); + + return hpx::finalize(); +} + +int main(int argc, char* argv[]) +{ + 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"); + + HPX_TEST_EQ_MSG(hpx::init(desc_commandline, argc, argv), 0, + "HPX main exited with a non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/parallelism/algorithms/tests/unit/container_algorithms/CMakeLists.txt b/libs/parallelism/algorithms/tests/unit/container_algorithms/CMakeLists.txt index 30fd38a865c2..6a4f249b0020 100644 --- a/libs/parallelism/algorithms/tests/unit/container_algorithms/CMakeLists.txt +++ b/libs/parallelism/algorithms/tests/unit/container_algorithms/CMakeLists.txt @@ -33,6 +33,7 @@ set(tests inplace_merge_range is_heap_range is_heap_until_range + make_heap_range max_element_range merge_range min_element_range diff --git a/libs/parallelism/algorithms/tests/unit/container_algorithms/make_heap_range.cpp b/libs/parallelism/algorithms/tests/unit/container_algorithms/make_heap_range.cpp new file mode 100644 index 000000000000..095e76b2ae0f --- /dev/null +++ b/libs/parallelism/algorithms/tests/unit/container_algorithms/make_heap_range.cpp @@ -0,0 +1,454 @@ +// Copyright (c) 2015 Grant Mercer +// +// 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 +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); + +/////////////////////////////////////////////////////////////////////////// +template +void test_make_heap1(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::ranges::make_heap( + iterator(hpx::util::begin(c)), iterator(hpx::util::end(c))); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c)), true); +} + +template +void test_make_heap1(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::ranges::make_heap( + policy, iterator(hpx::util::begin(c)), iterator(hpx::util::end(c))); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c)), true); +} + +template +void test_make_heap_async1(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + auto test = hpx::ranges::make_heap( + p, iterator(hpx::util::begin(c)), iterator(hpx::util::end(c))); + + auto result = test.get(); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c)), true); +} + +template +void test_make_heap1() +{ + using namespace hpx::parallel::execution; + + test_make_heap1(IteratorTag()); + + test_make_heap1(seq, IteratorTag()); + test_make_heap1(par, IteratorTag()); + test_make_heap1(par_unseq, IteratorTag()); + + test_make_heap_async1(seq(task), IteratorTag()); + test_make_heap_async1(par(task), IteratorTag()); +} + +void make_heap_test1() +{ + test_make_heap1(); +} + +/////////////////////////////////////////////////////////////////////////// +template +void test_make_heap2(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(25); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::ranges::make_heap(iterator(hpx::util::begin(c)), + iterator(hpx::util::end(c)), std::greater()); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c), + std::greater()), + true); +} + +template +void test_make_heap2(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(25); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::ranges::make_heap(policy, iterator(hpx::util::begin(c)), + iterator(hpx::util::end(c)), std::greater()); + + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c), + std::greater()), + true); +} + +template +void test_make_heap_async2(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), 0); + + hpx::future test = + hpx::ranges::make_heap(p, iterator(hpx::util::begin(c)), + iterator(hpx::util::end(c)), std::greater()); + + test.wait(); + HPX_TEST_EQ(std::is_heap(hpx::util::begin(c), hpx::util::end(c), + std::greater()), + true); +} + +template +void test_make_heap2() +{ + using namespace hpx::parallel::execution; + + test_make_heap2(IteratorTag()); + + test_make_heap2(seq, IteratorTag()); + test_make_heap2(par, IteratorTag()); + test_make_heap2(par_unseq, IteratorTag()); + + test_make_heap_async2(seq(task), IteratorTag()); + test_make_heap_async2(par(task), IteratorTag()); +} + +void make_heap_test2() +{ + test_make_heap2(); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_make_heap_exception(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_exception = false; + try + { + hpx::ranges::make_heap(decorated_iterator(hpx::util::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + //test::test_num_exceptions::call(policy, e); + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_make_heap_exception(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_exception = false; + try + { + hpx::ranges::make_heap(policy, + decorated_iterator(hpx::util::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + //test::test_num_exceptions::call(policy, e); + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_make_heap_exception_async(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_exception = false; + bool returned_from_algorithm = false; + try + { + hpx::future f = hpx::ranges::make_heap(p, + decorated_iterator(hpx::util::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(hpx::util::end(c))); + returned_from_algorithm = true; + f.get(); + + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + //test::test_num_exceptions::call(policy, e); + } + catch (...) + { + HPX_TEST(false); + } + HPX_TEST(caught_exception); + HPX_TEST(returned_from_algorithm); +} + +template +void test_make_heap_exception() +{ + using namespace hpx::parallel::execution; + + test_make_heap_exception(IteratorTag()); + + // If the execution policy object is of type vector_execution_policy, + // std::terminate shall be called. therefore we do not test exceptions + // with a vector execution policy + test_make_heap_exception(seq, IteratorTag()); + test_make_heap_exception(par, IteratorTag()); + + test_make_heap_exception_async(seq(task), IteratorTag()); + test_make_heap_exception_async(par(task), IteratorTag()); +} + +void make_heap_exception_test() +{ + test_make_heap_exception(); +} + +////////////////////////////////////////////////////////////////////////////// +template +void test_make_heap_bad_alloc(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(100007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_bad_alloc = false; + try + { + hpx::ranges::make_heap(decorated_iterator(hpx::util::begin(c), + []() { throw std::bad_alloc(); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_make_heap_bad_alloc(ExPolicy&& policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(100007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_bad_alloc = false; + try + { + hpx::ranges::make_heap(policy, + decorated_iterator( + hpx::util::begin(c), []() { throw std::bad_alloc(); }), + decorated_iterator(hpx::util::end(c))); + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_make_heap_bad_alloc_async(ExPolicy&& p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::iota(hpx::util::begin(c), hpx::util::end(c), gen()); + + bool caught_bad_alloc = false; + bool returned_from_algorithm = false; + try + { + hpx::future f = hpx::ranges::make_heap(p, + decorated_iterator( + hpx::util::begin(c), []() { throw std::bad_alloc(); }), + decorated_iterator(hpx::util::end(c))); + returned_from_algorithm = true; + f.get(); + + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); + HPX_TEST(returned_from_algorithm); +} + +template +void test_make_heap_bad_alloc() +{ + using namespace hpx::parallel::execution; + + test_make_heap_bad_alloc(IteratorTag()); + + // If the execution policy object is of type vector_execution_policy, + // std::terminate shall be called. therefore we do not test exceptions + // with a vector execution policy + test_make_heap_bad_alloc(seq, IteratorTag()); + test_make_heap_bad_alloc(par, IteratorTag()); + + test_make_heap_bad_alloc_async(seq(task), IteratorTag()); + test_make_heap_bad_alloc_async(par(task), IteratorTag()); +} + +void make_heap_bad_alloc_test() +{ + test_make_heap_bad_alloc(); +} + +int hpx_main(boost::program_options::variables_map& vm) +{ + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + gen.seed(seed); + + make_heap_test1(); + make_heap_test2(); + make_heap_exception_test(); + make_heap_bad_alloc_test(); + + return hpx::finalize(); +} + +int main(int argc, char* argv[]) +{ + 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"); + + HPX_TEST_EQ_MSG(hpx::init(desc_commandline, argc, argv), 0, + "HPX main exited with a non-zero status"); + + return hpx::util::report_errors(); +}