diff --git a/libs/core/algorithms/CMakeLists.txt b/libs/core/algorithms/CMakeLists.txt index 5c7e17ef726..6fcfed897e2 100644 --- a/libs/core/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/CMakeLists.txt @@ -23,6 +23,7 @@ set(algorithms_headers hpx/parallel/algorithms/detail/accumulate.hpp hpx/parallel/algorithms/detail/advance_and_get_distance.hpp hpx/parallel/algorithms/detail/advance_to_sentinel.hpp + hpx/parallel/algorithms/detail/contains.hpp hpx/parallel/algorithms/detail/dispatch.hpp hpx/parallel/algorithms/detail/distance.hpp hpx/parallel/algorithms/detail/equal.hpp @@ -103,6 +104,7 @@ set(algorithms_headers hpx/parallel/container_algorithms/adjacent_difference.hpp hpx/parallel/container_algorithms/adjacent_find.hpp hpx/parallel/container_algorithms/all_any_none.hpp + hpx/parallel/container_algorithms/contains.hpp hpx/parallel/container_algorithms/copy.hpp hpx/parallel/container_algorithms/count.hpp hpx/parallel/container_algorithms/destroy.hpp diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/contains.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/contains.hpp new file mode 100644 index 00000000000..1e7dfed5001 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/contains.hpp @@ -0,0 +1,63 @@ +// Copyright (c) 2024 Zakaria Abdi +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx::parallel::detail { + + template + struct sequential_contains_t final + : hpx::functional::detail::tag_fallback> + { + private: + template + friend constexpr bool tag_fallback_invoke(sequential_contains_t, + Iterator first, Sentinel last, const T& val, Proj&& proj) + { + using difference_type = + typename std::iterator_traits::difference_type; + difference_type distance = detail::distance(first, last); + if (distance <= 0) + return false; + + const auto itr = + util::loop_pred>( + first, last, [&val, &proj](const auto& cur) { + return HPX_INVOKE(proj, *cur) == val; + }); + + return itr != last; + } + + template + friend constexpr void tag_fallback_invoke(sequential_contains_t, + Iterator first, T const& val, std::size_t count, Token& tok, + Proj&& proj) + { + util::loop_n( + first, count, tok, [&val, &tok, &proj](const auto& cur) { + if (HPX_INVOKE(proj, *cur) == val) + { + tok.cancel(); + return; + } + }); + } + }; + template + inline constexpr sequential_contains_t sequential_contains = + sequential_contains_t{}; +} //namespace hpx::parallel::detail diff --git a/libs/core/algorithms/include/hpx/parallel/container_algorithms.hpp b/libs/core/algorithms/include/hpx/parallel/container_algorithms.hpp index 543047ef35d..8c302dde948 100644 --- a/libs/core/algorithms/include/hpx/parallel/container_algorithms.hpp +++ b/libs/core/algorithms/include/hpx/parallel/container_algorithms.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/container_algorithms/contains.hpp b/libs/core/algorithms/include/hpx/parallel/container_algorithms/contains.hpp new file mode 100644 index 00000000000..241cd0eeeb6 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/container_algorithms/contains.hpp @@ -0,0 +1,367 @@ +// Copyright (c) 2024 Zakaria Abdi +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hpx::parallel { namespace detail { + struct contains : public algorithm + { + constexpr contains() noexcept + : algorithm("contains") + { + } + + template + static constexpr bool sequential( + ExPolicy, Iterator first, Sentinel last, const T& val, Proj&& proj) + { + return sequential_contains>( + first, last, val, HPX_FORWARD(Proj, proj)); + } + + template + static util::detail::algorithm_result_t parallel( + ExPolicy&& orgpolicy, Iterator first, Sentinel last, const T& val, + Proj&& proj) + { + using difference_type = + typename std::iterator_traits::difference_type; + difference_type count = detail::distance(first, last); + if (count <= 0) + return util::detail::algorithm_result::get( + false); + + decltype(auto) policy = parallel::util::adapt_placement_mode( + HPX_FORWARD(ExPolicy, orgpolicy), + hpx::threads::thread_placement_hint::breadth_first); + + using policy_type = std::decay_t; + util::cancellation_token<> tok; + auto f1 = [val, tok, proj](Iterator first, std::size_t count) { + sequential_contains(first, val, count, tok, proj); + return tok.was_cancelled(); + }; + + auto f2 = [](auto&& results) { + return std::any_of(hpx::util::begin(results), + hpx::util::end(results), + [](hpx::future& val) { return val.get(); }); + }; + + return util::partitioner::call( + HPX_FORWARD(decltype(policy), policy), first, count, + HPX_MOVE(f1), HPX_MOVE(f2)); + } + }; + + template + struct contains_subrange_helper; + + template + struct contains_subrange_helper>> + { + static bool get_result(T& itr, T& last) + { + return itr != last; + } + }; + + template + struct contains_subrange_helper>> + { + static hpx::future get_result(hpx::future& itr, T& last) + { + return itr.then( + [last](hpx::future it) { return it.get() != last; }); + } + }; + + struct contains_subrange : public algorithm + { + constexpr contains_subrange() noexcept + : algorithm("contains_subrange") + { + } + + template + static bool sequential(ExPolicy, FwdIter1 first1, Sent1 last1, + FwdIter2 first2, Sent2 last2, Pred pred, Proj1&& proj1, + Proj2&& proj2) + { + auto itr = hpx::ranges::search(hpx::execution::seq, first1, last1, + first2, last2, HPX_MOVE(pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); + + return itr != last1; + } + + template + static constexpr util::detail::algorithm_result_t + parallel(ExPolicy&& policy, FwdIter1 first1, Sent1 last1, + FwdIter2 first2, Sent2 last2, Pred pred, Proj1&& proj1, + Proj2&& proj2) + { + auto itr = hpx::ranges::search(policy, first1, last1, first2, last2, + HPX_MOVE(pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); + + return contains_subrange_helper().get_result( + itr, last1); + } + }; + +}} // namespace hpx::parallel::detail + +namespace hpx::ranges { + + inline constexpr struct contains_t final + : hpx::functional::detail::tag_fallback + { + private: + // clang-format off + template && + hpx::traits::is_iterator_v && + hpx::is_invocable_v::value_type> + )> + + // clang-format on + friend bool tag_fallback_invoke(hpx::ranges::contains_t, Iterator first, + Sentinel last, const T& val, Proj&& proj = Proj()) + { + static_assert(hpx::traits::is_input_iterator_v, + "Required at least input iterator."); + + static_assert(hpx::traits::is_input_iterator_v, + "Required at least input iterator."); + + return hpx::parallel::detail::contains().call( + hpx::execution::seq, first, last, val, proj); + } + + // clang-format off + template && + hpx::parallel::traits::is_projected_range_v + )> + + // clang-format on + friend bool tag_fallback_invoke( + hpx::ranges::contains_t, Rng&& rng, const T& t, Proj proj = Proj()) + { + return parallel::detail::contains().call(hpx::execution::seq, + hpx::util::begin(rng), hpx::util::end(rng), t, HPX_MOVE(proj)); + } + + // clang-format off + template && + hpx::traits::is_iterator_v&& hpx::traits:: + is_iterator_v && + hpx::is_invocable_v::value_type> + )> + + // clang-format on + friend typename parallel::util::detail::algorithm_result::type + tag_fallback_invoke(hpx::ranges::contains_t, ExPolicy&& policy, + Iterator first, Sentinel last, T const& val, Proj&& proj = Proj()) + { + static_assert(hpx::traits::is_iterator_v, + "Required at least iterator."); + + static_assert(hpx::traits::is_iterator_v, + "Required at least iterator."); + + return hpx::parallel::detail::contains().call( + HPX_FORWARD(ExPolicy, policy), first, last, val, + HPX_FORWARD(Proj, proj)); + } + + // clang-format off + template && + hpx::traits::is_range_v && + hpx::parallel::traits::is_projected_range_v + )> + + // clang-format on + friend typename parallel::util::detail::algorithm_result::type + tag_fallback_invoke(hpx::ranges::contains_t, ExPolicy&& policy, + Rng&& rng, const T& t, Proj proj = Proj()) + { + return parallel::detail::contains().call( + HPX_FORWARD(ExPolicy, policy), hpx::util::begin(rng), + hpx::util::end(rng), t, HPX_MOVE(proj)); + } + + } contains{}; + + inline constexpr struct contains_subrange_t final + : hpx::functional::detail::tag_fallback + { + private: + // clang-format off + template && + hpx::traits::is_sentinel_for_v && + hpx::traits::is_iterator_v && + hpx::traits::is_sentinel_for_v && + hpx::is_invocable_v::value_type, + typename std::iterator_traits::value_type> && + hpx::is_invocable_v::value_type> && + hpx::is_invocable_v::value_type> + )> + + // clang-format on + friend bool tag_fallback_invoke(hpx::ranges::contains_subrange_t, + FwdIter1 first1, Sent1 last1, FwdIter2 first2, Sent2 last2, + Pred pred = Pred(), Proj1&& proj1 = Proj1(), + Proj2&& proj2 = Proj2()) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Required at least forward iterator."); + + static_assert(hpx::traits::is_forward_iterator_v, + "Required at least forward iterator."); + + return hpx::parallel::detail::contains_subrange().call( + hpx::execution::seq, first1, last1, first2, last2, + HPX_MOVE(pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); + } + + // clang-format off + template && + hpx::parallel::traits::is_projected_range_v && + hpx::traits::is_range_v && + hpx::parallel::traits::is_projected_range_v && + hpx::parallel::traits::is_indirect_callable_v< + hpx::execution::sequenced_policy, Pred, + hpx::parallel::traits::projected_range, + hpx::parallel::traits::projected_range + > + )> + + // clang-format on + friend bool tag_fallback_invoke(hpx::ranges::contains_subrange_t, + Rng1&& rng1, Rng2&& rng2, Pred pred = Pred(), Proj1 proj1 = Proj1(), + Proj2 proj2 = Proj2()) + { + return hpx::parallel::detail::contains_subrange().call( + hpx::execution::seq, hpx::util::begin(rng1), + hpx::util::end(rng1), hpx::util::begin(rng2), + hpx::util::end(rng2), HPX_MOVE(pred), HPX_MOVE(proj1), + HPX_MOVE(proj2)); + } + + // clang-format off + template && + hpx::traits::is_iterator_v && + hpx::traits::is_sentinel_for_v && + hpx::traits::is_iterator_v && + hpx::traits::is_sentinel_for_v && + hpx::is_invocable_v::value_type, + typename std::iterator_traits::value_type> + )> + + // clang-format on + friend typename parallel::util::detail::algorithm_result::type + tag_fallback_invoke(hpx::ranges::contains_subrange_t, ExPolicy&& policy, + FwdIter1 first1, Sent1 last1, FwdIter2 first2, Sent2 last2, + Pred pred = Pred(), Proj1&& proj1 = Proj1(), + Proj2&& proj2 = Proj2()) + { + static_assert(hpx::traits::is_forward_iterator_v, + "Required at least forward iterator."); + + static_assert(hpx::traits::is_forward_iterator_v, + "Required at least forward iterator."); + + return hpx::parallel::detail::contains_subrange().call( + HPX_FORWARD(ExPolicy, policy), first1, last1, first2, last2, + HPX_MOVE(pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); + } + + // clang-format off + template && + hpx::parallel::traits::is_projected_range_v && + hpx::traits::is_range_v && + hpx::parallel::traits::is_projected_range_v && + hpx::parallel::traits::is_indirect_callable_v, + hpx::parallel::traits::projected_range + > + )> + + // clang-format on + friend typename parallel::util::detail::algorithm_result::type + tag_fallback_invoke(hpx::ranges::contains_subrange_t, ExPolicy&& policy, + Rng1&& rng1, Rng2&& rng2, Pred pred = Pred(), Proj1 proj1 = Proj1(), + Proj2 proj2 = Proj2()) + { + return hpx::parallel::detail::contains_subrange().call( + HPX_FORWARD(ExPolicy, policy), hpx::util::begin(rng1), + hpx::util::end(rng1), hpx::util::begin(rng2), + hpx::util::end(rng2), HPX_MOVE(pred), HPX_MOVE(proj1), + HPX_MOVE(proj2)); + } + + } contains_subrange{}; +} // namespace hpx::ranges diff --git a/libs/core/algorithms/tests/unit/algorithms/search.cpp b/libs/core/algorithms/tests/unit/algorithms/search.cpp index 8c57de4b9ae..3c47dd0ec2b 100644 --- a/libs/core/algorithms/tests/unit/algorithms/search.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/search.cpp @@ -39,6 +39,12 @@ void test_search1_without_expolicy(IteratorTag) base_iterator test_index = std::begin(c) + c.size() / 2; HPX_TEST(index == iterator(test_index)); + + h[0] = 2; + iterator end_index = hpx::search(iterator(std::begin(c)), + iterator(std::end(c)), std::begin(h), std::end(h)); + + HPX_TEST(end_index == iterator(std::end(c))); } template diff --git a/libs/core/algorithms/tests/unit/container_algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/container_algorithms/CMakeLists.txt index 6015a548c8c..c5a619d65d6 100644 --- a/libs/core/algorithms/tests/unit/container_algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/container_algorithms/CMakeLists.txt @@ -16,6 +16,8 @@ set(tests adjacentfind_exception_range all_of_range any_of_range + contains_range + contains_subrange_range copy_range copyn_range copyif_range diff --git a/libs/core/algorithms/tests/unit/container_algorithms/contains_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/contains_range.cpp new file mode 100644 index 00000000000..ab006cfd46f --- /dev/null +++ b/libs/core/algorithms/tests/unit/container_algorithms/contains_range.cpp @@ -0,0 +1,382 @@ +// Copyright (c) 2024 Zakaria Abdi +// 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" + +////////////////////////////////////////////////////////////////////////////// +unsigned int seed = std::random_device{}(); +std::mt19937 gen(seed); +std::uniform_int_distribution<> dis(2, 101); + +template +void test_contains(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::fill(std::begin(c), std::end(c), dis(gen)); + const std::size_t n = c.size(); + c.at(n / 2) = 1; + + bool result1 = hpx::ranges::contains( + iterator(std::begin(c)), iterator(std::end(c)), int(1)); + HPX_TEST_EQ(result1, true); +} + +template +void test_contains(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + std::fill(std::begin(c), std::end(c), dis(gen)); + const std::size_t n = c.size(); + c.at(n / 2) = 1; + + bool result1 = hpx::ranges::contains( + policy, iterator(std::begin(c)), iterator(std::end(c)), int(1)); + HPX_TEST_EQ(result1, true); + + bool result2 = hpx::ranges::contains( + policy, iterator(std::begin(c)), iterator(std::end(c)), int(110)); + HPX_TEST_EQ(result2, false); +} + +template +void test_contains_async(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + const std::size_t n = c.size(); + std::fill(std::begin(c), std::end(c), dis(gen)); + c.at(n / 2) = 1; + + hpx::future result1 = hpx::ranges::contains( + policy, iterator(std::begin(c)), iterator(std::end(c)), int(1)); + result1.wait(); + HPX_TEST_EQ(result1.get(), true); + + hpx::future result2 = hpx::ranges::contains( + policy, iterator(std::begin(c)), iterator(std::end(c)), int(110)); + result2.wait(); + HPX_TEST_EQ(result2.get(), false); +} + +template +void test_contains() +{ + using namespace hpx::execution; + + test_contains(IteratorTag()); + + test_contains(seq, IteratorTag()); + test_contains(par, IteratorTag()); + test_contains(par_unseq, IteratorTag()); + + test_contains_async(seq(task), IteratorTag()); + test_contains_async(par(task), IteratorTag()); + test_contains_async(par_unseq(task), IteratorTag()); +} + +void contains_test() +{ + test_contains(); + test_contains(); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_contains_exception(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c(10007); + std::iota(std::begin(c), std::end(c), gen() + 1); + const std::size_t n = c.size(); + c.at(n / 2) = 0; + bool caught_exception = false; + try + { + hpx::ranges::contains(decorated_iterator(std::begin(c), + []() { throw std::runtime_error("test"); }), + decorated_iterator(std::end(c)), int(0)); + + HPX_TEST(false); + } + + catch (hpx::exception_list const& e) + { + caught_exception = true; + test::test_num_exceptions::call(hpx::execution::seq, e); + } + + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_contains_exception(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + const std::size_t n = c.size(); + std::iota(std::begin(c), std::end(c), gen() + 1); + c.at(n / 2) = 0; + + bool caught_exception = false; + try + { + hpx::ranges::contains(policy, + decorated_iterator( + std::begin(c), []() { throw std::runtime_error("test"); }), + decorated_iterator(std::end(c)), int(0)); + + 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_contains_exception_async(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + const std::size_t n = c.size(); + std::iota(std::begin(c), std::end(c), gen() + 1); + c.at(n / 2) = 0; + + bool caught_exception = false; + bool returned_from_algorithm = false; + try + { + hpx::future result = hpx::ranges::contains(policy, + decorated_iterator( + std::begin(c), []() { throw std::runtime_error("test"); }), + decorated_iterator(std::end(c)), int(0)); + returned_from_algorithm = true; + result.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_contains_exception() +{ + using namespace hpx::execution; + + test_contains_exception(IteratorTag()); + + test_contains_exception(seq, IteratorTag()); + test_contains_exception(par, IteratorTag()); + + test_contains_exception_async(seq(task), IteratorTag()); + test_contains_exception_async(par(task), IteratorTag()); +} + +void contains_exception_test() +{ + test_contains_exception(); + test_contains_exception(); +} + +////////////////////////////////////////////////////////////////////////////// + +template +void test_contains_bad_alloc(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_b"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + const std::size_t n = c.size(); + std::iota(std::begin(c), std::end(c), gen() + 1); + c.at(n / 2) = 0; + bool caught_bad_alloc = false; + try + { + hpx::ranges::contains(policy, + decorated_iterator(std::begin(c), []() { throw std::bad_alloc(); }), + decorated_iterator(std::end(c)), int(0)); + HPX_TEST(false); + } + + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_contains_bad_alloc_async(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + const std::size_t n = c.size(); + std::iota(std::begin(c), std::end(c), gen() + 1); + c.at(n / 2) = 0; + bool caught_bad_alloc = false; + bool returned_from_algorithm = false; + try + { + hpx::future result = hpx::ranges::contains(policy, + decorated_iterator(std::begin(c), []() { throw std::bad_alloc(); }), + decorated_iterator(std::end(c)), int(0)); + returned_from_algorithm = true; + result.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_contains_bad_alloc() +{ + using namespace hpx::execution; + + test_contains_bad_alloc(seq, IteratorTag()); + test_contains_bad_alloc(par, IteratorTag()); + + test_contains_bad_alloc_async(seq(task), IteratorTag()); + test_contains_bad_alloc_async(par(task), IteratorTag()); +} + +void contains_bad_alloc_test() +{ + test_contains_bad_alloc(); + test_contains_bad_alloc(); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "Using seed as: " << seed << std::endl; + gen.seed(seed); + + contains_test(); + contains_exception_test(); + contains_bad_alloc_test(); + + return hpx::local::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 to use for this run"); + + std::vector cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/container_algorithms/contains_subrange_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/contains_subrange_range.cpp new file mode 100644 index 00000000000..2a53fa4caee --- /dev/null +++ b/libs/core/algorithms/tests/unit/container_algorithms/contains_subrange_range.cpp @@ -0,0 +1,415 @@ +// Copyright (c) 2024 Zakaria Abdi +// 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" + +unsigned int seed = std::random_device{}(); +std::mt19937 gen(seed); +std::uniform_int_distribution<> dis(2, 101); + +template +void test_contains_subrange(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c1(10007), c2 = {1, 2, 3}; + std::fill(std::begin(c1), std::end(c1), dis(gen)); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool result = hpx::ranges::contains_subrange(iterator(std::begin(c1)), + iterator(std::end(c1)), iterator(std::begin(c2)), + iterator(std::end(c2))); + HPX_TEST_EQ(result, true); +} + +template +void test_contains_subrange(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c1(10007), c2 = {1, 2, 3}; + std::fill(std::begin(c1), std::end(c1), dis(gen)); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool result1 = hpx::ranges::contains_subrange(policy, + iterator(std::begin(c1)), iterator(std::end(c1)), + iterator(std::begin(c2)), iterator(std::end(c2))); + HPX_TEST_EQ(result1, true); +} + +template +void test_contains_subrange_async(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c1(10007), c2 = {1, 2, 3}; + std::fill(std::begin(c1), std::end(c1), dis(gen)); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + hpx::future result = hpx::ranges::contains_subrange(policy, + iterator(std::begin(c1)), iterator(std::end(c1)), + iterator(std::begin(c2)), iterator(std::end(c2))); + result.wait(); + HPX_TEST_EQ(result.get(), true); +} + +template +void test_contains_subrange() +{ + using namespace hpx::execution; + + test_contains_subrange(IteratorTag()); + + test_contains_subrange(seq, IteratorTag()); + test_contains_subrange(par, IteratorTag()); + test_contains_subrange(par_unseq, IteratorTag()); + + test_contains_subrange_async(seq(task), IteratorTag()); + test_contains_subrange_async(par(task), IteratorTag()); + test_contains_subrange_async(par_unseq(task), IteratorTag()); +} + +void contains_subrange_test() +{ + test_contains_subrange(); + test_contains_subrange(); +} + +//////////////////////////////////////////////////////////////////////////// +template +void test_contains_subrange_exception(IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c1(10007), c2 = {1, 2, 3}; + std::iota(std::begin(c1), std::end(c1), gen() + 1); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool caught_exception = false; + try + { + hpx::ranges::contains_subrange( + decorated_iterator( + std::begin(c1), []() { throw std::runtime_error("test"); }), + decorated_iterator( + std::end(c1), []() { throw std::runtime_error("test"); }), + decorated_iterator(std::begin(c2)), + decorated_iterator(std::end(c2))); + HPX_TEST(false); + } + + catch (hpx::exception_list const& e) + { + caught_exception = true; + test::test_num_exceptions::call(hpx::execution::seq, e); + } + + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_contains_subrange_exception(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c1(10007), c2 = {1, 2, 3}; + std::iota(std::begin(c1), std::end(c1), gen() + 1); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool caught_exception = false; + try + { + hpx::ranges::contains_subrange(policy, + decorated_iterator( + std::begin(c1), []() { throw std::runtime_error("test"); }), + decorated_iterator( + std::end(c1), []() { throw std::runtime_error("test"); }), + decorated_iterator(std::begin(c2)), + decorated_iterator(std::end(c2))); + + 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_contains_subrange_exception_async(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c1(10007), c2 = {1, 2, 3}; + std::iota(std::begin(c1), std::end(c1), gen() + 1); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool caught_exception = false; + try + { + hpx::future result = hpx::ranges::contains_subrange(policy, + decorated_iterator( + std::begin(c1), []() { throw std::runtime_error("test"); }), + decorated_iterator( + std::end(c1), []() { throw std::runtime_error("test"); }), + decorated_iterator(std::begin(c2)), + decorated_iterator(std::end(c2))); + result.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); +} + +template +void test_contains_subrange_exception() +{ + using namespace hpx::execution; + + test_contains_subrange_exception(IteratorTag()); + + test_contains_subrange_exception(seq, IteratorTag()); + test_contains_subrange_exception(par, IteratorTag()); + + test_contains_subrange_exception_async(seq(task), IteratorTag()); + test_contains_subrange_exception_async(par(task), IteratorTag()); +} + +void contains_subrange_exception_test() +{ + test_contains_subrange_exception(); + test_contains_subrange_exception(); +} + +////////////////////////////////////////////////////////////////////////////// +template +void test_contains_subrange_bad_alloc(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c1(10007), c2 = {1, 2, 3}; + std::iota(std::begin(c1), std::end(c1), gen() + 1); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool caught_bad_alloc = false; + try + { + hpx::ranges::contains_subrange(policy, + decorated_iterator( + std::begin(c1), []() { throw std::bad_alloc(); }), + decorated_iterator(std::end(c1), []() { throw std::bad_alloc(); }), + decorated_iterator(std::begin(c2)), + decorated_iterator(std::end(c2))); + HPX_TEST(false); + } + + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_contains_subrange_bad_alloc_async(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy_v, + "hpx::is_execution_policy_v"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c1(10007), c2 = {1, 2, 3}; + std::iota(std::begin(c1), std::end(c1), gen() + 1); + const std::size_t n = c1.size(); + const std::size_t mid = n / 2; + + c1.at(mid) = 1; + c1.at(mid + 1) = 2; + c1.at(mid + 2) = 3; + + bool caught_bad_alloc = false; + try + { + hpx::future result = hpx::ranges::contains_subrange(policy, + decorated_iterator( + std::begin(c1), []() { throw std::bad_alloc(); }), + decorated_iterator(std::end(c1), []() { throw std::bad_alloc(); }), + decorated_iterator(std::begin(c2)), + decorated_iterator(std::end(c2))); + result.get(); + HPX_TEST(false); + } + + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_contains_subrange_bad_alloc() +{ + using namespace hpx::execution; + + test_contains_subrange_bad_alloc(seq, IteratorTag()); + test_contains_subrange_bad_alloc(par, IteratorTag()); + + test_contains_subrange_bad_alloc_async(seq(task), IteratorTag()); + test_contains_subrange_bad_alloc_async(par(task), IteratorTag()); +} + +void contains_subrange_bad_alloc_test() +{ + test_contains_subrange_bad_alloc(); + test_contains_subrange_bad_alloc(); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "Using seed as " << seed << std::endl; + gen.seed(seed); + contains_subrange_test(); + contains_subrange_exception_test(); + contains_subrange_bad_alloc_test(); + + return hpx::local::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 to use for this run"); + + std::vector cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +}