diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 2b28ff0c42a7..9847038449c2 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -126,6 +126,7 @@ set(doxygen_dependencies "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/replace.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/reverse.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/rotate.hpp" + "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/search.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/sort.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/transform.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/unique.hpp" diff --git a/hpx/include/parallel_search.hpp b/hpx/include/parallel_search.hpp index 8f4f6eb3ed75..5066cb637a24 100644 --- a/hpx/include/parallel_search.hpp +++ b/hpx/include/parallel_search.hpp @@ -8,6 +8,7 @@ #define HPX_PARALLEL_SEARCH_OCT_25_130PM #include +#include #endif diff --git a/hpx/parallel/algorithms/search.hpp b/hpx/parallel/algorithms/search.hpp index 157dac67c147..1b4aec5b53c9 100644 --- a/hpx/parallel/algorithms/search.hpp +++ b/hpx/parallel/algorithms/search.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -42,27 +43,36 @@ namespace hpx {namespace parallel { inline namespace v1 : search::algorithm("search") {} - template + template static FwdIter sequential(ExPolicy, FwdIter first, FwdIter last, - FwdIter2 s_first, FwdIter2 s_last, Pred && op) + FwdIter2 s_first, FwdIter2 s_last, Pred && op, + Proj1 && proj1, Proj2 && proj2) { - return std::search(first, last, s_first, s_last, op); + return std::search(first, last, s_first, s_last, + util::compare_projected(op, + proj1, proj2)); } - template + template static typename util::detail::algorithm_result< ExPolicy, FwdIter >::type parallel(ExPolicy && policy, FwdIter first, FwdIter last, - FwdIter2 s_first, FwdIter2 s_last, Pred && op) + FwdIter2 s_first, FwdIter2 s_last, Pred && op, + Proj1 && proj1, Proj2 && proj2) { - typedef typename std::iterator_traits::reference - reference; - typedef typename std::iterator_traits::difference_type - difference_type; - typedef typename std::iterator_traits::difference_type - s_difference_type; + using reference = + typename std::iterator_traits::reference; + + using difference_type = + typename std::iterator_traits::difference_type; + + using s_difference_type = + typename std::iterator_traits::difference_type; + typedef util::detail::algorithm_result result; s_difference_type diff = std::distance(s_first, s_last); @@ -78,17 +88,27 @@ namespace hpx {namespace parallel { inline namespace v1 util::cancellation_token tok(count); return partitioner::call_with_index( std::forward(policy), first, count-(diff-1), 1, - [=](FwdIter it, std::size_t part_size, + [=, + HPX_CAPTURE_FORWARD(op), + HPX_CAPTURE_FORWARD(proj1), + HPX_CAPTURE_FORWARD(proj2) + ](FwdIter it, std::size_t part_size, std::size_t base_idx) mutable -> void { FwdIter curr = it; util::loop_idx_n( base_idx, it, part_size, tok, - [=, &tok, &curr](reference v, std::size_t i) -> void + [=, &tok, &curr, + HPX_CAPTURE_FORWARD(op), + HPX_CAPTURE_FORWARD(proj1), + HPX_CAPTURE_FORWARD(proj2) + ](reference v, std::size_t i) -> void { ++curr; - if (op(v, *s_first)) + if (hpx::util::invoke(op, + hpx::util::invoke(proj1, v), + hpx::util::invoke(proj2, *s_first))) { difference_type local_count = 1; FwdIter2 needle = s_first; @@ -98,7 +118,9 @@ namespace hpx {namespace parallel { inline namespace v1 local_count != diff && len != count; ++local_count, ++len, ++mid) { - if(*mid != *++needle) + if(!hpx::util::invoke(op, + hpx::util::invoke(proj1, *mid), + hpx::util::invoke(proj2, *++needle))) break; } @@ -133,11 +155,11 @@ namespace hpx {namespace parallel { inline namespace v1 /// It describes the manner in which the execution /// of the algorithm may be parallelized and the manner /// in which it executes the assignments. - /// \tparam FwdIter The type of the source iterators used for the + /// \tparam FwdIter The type of the source iterators used for the /// first range (deduced). /// This iterator type must meet the requirements of an /// input iterator. - /// \tparam FwdIter2 The type of the source iterators used for the + /// \tparam FwdIter2 The type of the source iterators used for the /// second range (deduced). /// This iterator type must meet the requirements of an /// forward iterator. @@ -146,6 +168,12 @@ namespace hpx {namespace parallel { inline namespace v1 /// overload of \a adjacent_find requires \a Pred to meet the /// requirements of \a CopyConstructible. This defaults /// to std::equal_to<> + /// \tparam Proj1 The type of an optional projection function. This + /// defaults to \a util::projection_identity and is applied + /// to the elements of type dereferenced \a FwdIter. + /// \tparam Proj2 The type of an optional projection function. This + /// defaults to \a util::projection_identity and is applied + /// to the elements of type dereferenced \a FwdIter2. /// /// \param policy The execution policy to use for the scheduling of /// the iterations. @@ -169,6 +197,14 @@ namespace hpx {namespace parallel { inline namespace v1 /// that objects of types \a FwdIter1 and \a FwdIter2 can /// be dereferenced and then implicitly converted to /// \a Type1 and \a Type2 respectively + /// param proj1 Specifies the function (or function object) which + /// will be invoked for each of the elements of type + /// dereferenced \a FwdIter1 as a projection operation + /// before the actual predicate \a is invoked. + /// \param proj2 Specifies the function (or function object) which + /// will be invoked for each of the elements of type + /// dereferenced \a FwdIter2 as a projection operation + /// before the actual predicate \a is invoked. /// /// The comparison operations in the parallel \a search algorithm invoked /// with an execution policy object of type \a sequenced_policy @@ -191,13 +227,25 @@ namespace hpx {namespace parallel { inline namespace v1 /// returned. If no subsequence is found, \a last is returned. /// template - inline typename std::enable_if< - execution::is_execution_policy::value, - typename util::detail::algorithm_result::type - >::type + typename Pred = detail::equal_to, + typename Proj1 = util::projection_identity, + typename Proj2 = util::projection_identity, + HPX_CONCEPT_REQUIRES_( + execution::is_execution_policy::value && + hpx::traits::is_iterator::value && + traits::is_projected::value && + hpx::traits::is_iterator::value && + traits::is_projected::value && + traits::is_indirect_callable< + ExPolicy, Pred, + traits::projected, + traits::projected + >::value + )> + typename util::detail::algorithm_result::type search(ExPolicy && policy, FwdIter first, FwdIter last, - FwdIter2 s_first, FwdIter2 s_last, Pred && op = Pred()) + FwdIter2 s_first, FwdIter2 s_last, Pred && op = Pred(), + Proj1 && proj1 = Proj1(), Proj2 && proj2 = Proj2()) { static_assert( (hpx::traits::is_forward_iterator::value), @@ -210,7 +258,8 @@ namespace hpx {namespace parallel { inline namespace v1 return detail::search().call( std::forward(policy), is_seq(), - first, last, s_first, s_last, std::forward(op)); + first, last, s_first, s_last, std::forward(op), + std::forward(proj1), std::forward(proj2)); } /////////////////////////////////////////////////////////////////////////// @@ -225,21 +274,27 @@ namespace hpx {namespace parallel { inline namespace v1 : search_n::algorithm("search_n") {} - template + template static FwdIter sequential(ExPolicy, FwdIter first, std::size_t count, - FwdIter2 s_first, FwdIter2 s_last, Pred && op) + FwdIter2 s_first, FwdIter2 s_last, Pred && op, + Proj1 && proj1, Proj2 && proj2) { return std::search(first, std::next(first, count), - s_first, s_last, op); + s_first, s_last, + util::compare_projected(op, + proj1, proj2)); } - template + template static typename util::detail::algorithm_result< ExPolicy, FwdIter >::type parallel(ExPolicy && policy, FwdIter first, std::size_t count, - FwdIter2 s_first, FwdIter2 s_last, Pred && op) + FwdIter2 s_first, FwdIter2 s_last, Pred && op, + Proj1 && proj1, Proj2 && proj2) { typedef typename std::iterator_traits::reference reference; @@ -261,17 +316,27 @@ namespace hpx {namespace parallel { inline namespace v1 util::cancellation_token tok(count); return partitioner::call_with_index( std::forward(policy), first, count-(diff-1), 1, - [=](FwdIter it, std::size_t part_size, + [=, + HPX_CAPTURE_FORWARD(op), + HPX_CAPTURE_FORWARD(proj1), + HPX_CAPTURE_FORWARD(proj2) + ](FwdIter it, std::size_t part_size, std::size_t base_idx) mutable -> void { FwdIter curr = it; util::loop_idx_n( base_idx, it, part_size, tok, - [=, &tok, &curr](reference v, std::size_t i) -> void + [=, &tok, &curr, + HPX_CAPTURE_FORWARD(op), + HPX_CAPTURE_FORWARD(proj1), + HPX_CAPTURE_FORWARD(proj2) + ](reference v, std::size_t i) -> void { ++curr; - if (op(v, *s_first)) + if (hpx::util::invoke(op, + hpx::util::invoke(proj1, v), + hpx::util::invoke(proj2, *s_first))) { difference_type local_count = 1; FwdIter2 needle = s_first; @@ -282,8 +347,10 @@ namespace hpx {namespace parallel { inline namespace v1 len != difference_type(count); ++local_count, ++len, ++mid) { - if(*mid != *++needle) - break; + if(!hpx::util::invoke(op, + hpx::util::invoke(proj1, *mid), + hpx::util::invoke(proj2, *++needle))) + break; } if(local_count == diff) @@ -315,11 +382,11 @@ namespace hpx {namespace parallel { inline namespace v1 /// It describes the manner in which the execution /// of the algorithm may be parallelized and the manner /// in which it executes the assignments. - /// \tparam FwdIter The type of the source iterators used for the + /// \tparam FwdIter The type of the source iterators used for the /// first range (deduced). /// This iterator type must meet the requirements of an /// input iterator. - /// \tparam FwdIter2 The type of the source iterators used for the + /// \tparam FwdIter2 The type of the source iterators used for the /// second range (deduced). /// This iterator type must meet the requirements of an /// forward iterator. @@ -375,13 +442,25 @@ namespace hpx {namespace parallel { inline namespace v1 /// is found, \a first is also returned. /// template - inline typename std::enable_if< - execution::is_execution_policy::value, - typename util::detail::algorithm_result::type - >::type + typename Pred = detail::equal_to, + typename Proj1 = util::projection_identity, + typename Proj2 = util::projection_identity, + HPX_CONCEPT_REQUIRES_( + execution::is_execution_policy::value && + hpx::traits::is_iterator::value && + traits::is_projected::value && + hpx::traits::is_iterator::value && + traits::is_projected::value && + traits::is_indirect_callable< + ExPolicy, Pred, + traits::projected, + traits::projected + >::value + )> + typename util::detail::algorithm_result::type search_n(ExPolicy && policy, FwdIter first, std::size_t count, - FwdIter2 s_first, FwdIter2 s_last, Pred && op = Pred()) + FwdIter2 s_first, FwdIter2 s_last, Pred && op = Pred(), + Proj1 && proj1 = Proj1(), Proj2 && proj2 = Proj2()) { static_assert( (hpx::traits::is_forward_iterator::value), @@ -394,7 +473,8 @@ namespace hpx {namespace parallel { inline namespace v1 return detail::search_n().call( std::forward(policy), is_seq(), - first, count, s_first, s_last, std::forward(op)); + first, count, s_first, s_last, std::forward(op), + std::forward(proj1), std::forward(proj2)); } }}} diff --git a/hpx/parallel/container_algorithms.hpp b/hpx/parallel/container_algorithms.hpp index fedfc3805545..54320072115a 100644 --- a/hpx/parallel/container_algorithms.hpp +++ b/hpx/parallel/container_algorithms.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/hpx/parallel/container_algorithms/search.hpp b/hpx/parallel/container_algorithms/search.hpp new file mode 100644 index 000000000000..74d203c78917 --- /dev/null +++ b/hpx/parallel/container_algorithms/search.hpp @@ -0,0 +1,252 @@ +// Copyright (c) 2018 Christopher Ogle +// +// 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/container_algorithms/search.hpp + +#if !defined(HPX_PARALLEL_CONTAINER_ALGORITHM_SEARCH_FEB_28_2018_0007AM) +#define HPX_PARALLEL_CONTAINER_ALGORITHM_SEARCH_FEB_28_2018_0007AM + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 +{ + /// Searches the range [first, last) for any elements in the range [s_first, s_last). + /// Uses a provided predicate to compare elements. + /// + /// \note Complexity: at most (S*N) comparisons where + /// \a S = distance(s_first, s_last) and + /// \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 Rng1 The type of the examine range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam Rng2 The type of the search range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam Pred The type of an optional function/function object to use. + /// Unlike its sequential form, the parallel + /// overload of \a adjacent_find requires \a Pred to meet the + /// requirements of \a CopyConstructible. This defaults + /// to std::equal_to<> + /// \tparam Proj1 The type of an optional projection function. This + /// defaults to \a util::projection_identity and is applied + /// to the elements of \a Rng1. + /// \tparam Proj2 The type of an optional projection function. This + /// defaults to \a util::projection_identity and is applied + /// to the elements of \a Rng2. + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first Refers to the beginning of the sequence of elements + /// of the first range the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements of + /// the first range the algorithm will be applied to. + /// \param rng1 Refers to the sequence of elements the algorithm + /// will be examining. + /// \param rng2 Refers to the sequence of elements the algorithm + /// will be searching for. + /// \param op Refers to the binary predicate which returns true if the + /// elements should be treated as equal. the signature of + /// the function should be equivalent to + /// \code + /// bool pred(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 FwdIter1 and \a FwdIter2 can + /// be dereferenced and then implicitly converted to + /// \a Type1 and \a Type2 respectively + /// \param proj1 Specifies the function (or function object) which + /// will be invoked for each of the elements of \a rng1 + /// as a projection operation before the actual + /// predicate \a is invoked. + /// \param proj2 Specifies the function (or function object) which + /// will be invoked for each of the elements of \a rng2 + /// as a projection operation before the actual + /// predicate \a is invoked. + /// + /// The comparison operations in the parallel \a search algorithm invoked + /// with an execution policy object of type \a sequenced_policy + /// execute in sequential order in the calling thread. + /// + /// The comparison operations in the parallel \a search 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 search algorithm returns a \a hpx::future if the + /// execution policy is of type \a task_execution_policy and + /// returns \a FwdIter otherwise. + /// The \a search algorithm returns an iterator to the beginning of + /// the first subsequence [s_first, s_last) in range [first, last). + /// If the length of the subsequence [s_first, s_last) is greater + /// than the length of the range [first, last), \a last is returned. + /// Additionally if the size of the subsequence is empty \a first is + /// returned. If no subsequence is found, \a last is returned. + /// + template ::value && + hpx::traits::is_range::value && + traits::is_projected_range::value && + hpx::traits::is_range::value && + traits::is_projected_range::value && + traits::is_indirect_callable< + ExPolicy, Pred, + traits::projected_range, + traits::projected_range + >::value + )> + typename util::detail::algorithm_result< + ExPolicy, + typename hpx::traits::range_iterator::type + >::type + search(ExPolicy && policy, Rng1 && rng1, + Rng2 && rng2, Pred && op = Pred(), + Proj1 && proj1 = Proj1(), Proj2 && proj2 = Proj2()) + { + return search(std::forward(policy), + hpx::util::begin(rng1), hpx::util::end(rng1), + hpx::util::begin(rng2), hpx::util::end(rng2), + std::forward(op), + std::forward(proj1), + std::forward(proj2)); + } + + /// Searches the range [first, last) for any elements in the range [s_first, s_last). + /// Uses a provided predicate to compare elements. + /// + /// \note Complexity: at most (S*N) comparisons where + /// \a S = distance(s_first, s_last) and + /// \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 Rng1 The type of the examine range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam Rng2 The type of the search range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam Pred The type of an optional function/function object to use. + /// Unlike its sequential form, the parallel + /// overload of \a adjacent_find requires \a Pred to meet the + /// requirements of \a CopyConstructible. This defaults + /// to std::equal_to<> + /// \tparam Proj1 The type of an optional projection function. This + /// defaults to \a util::projection_identity and is applied + /// to the elements of \a Rng1. + /// \tparam Proj2 The type of an optional projection function. This + /// defaults to \a util::projection_identity and is applied + /// to the elements of \a Rng2. + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first Refers to the beginning of the sequence of elements + /// of the first range the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements of + /// the first range the algorithm will be applied to. + /// \param rng1 Refers to the sequence of elements the algorithm + /// will be examining. + /// \param rng2 Refers to the sequence of elements the algorithm + /// will be searching for. + /// \param op Refers to the binary predicate which returns true if the + /// elements should be treated as equal. the signature of + /// the function should be equivalent to + /// \code + /// bool pred(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 FwdIter1 and \a FwdIter2 can + /// be dereferenced and then implicitly converted to + /// \a Type1 and \a Type2 respectively + /// \param proj1 Specifies the function (or function object) which + /// will be invoked for each of the elements of \a rng1 + /// as a projection operation before the actual + /// predicate \a is invoked. + /// \param proj2 Specifies the function (or function object) which + /// will be invoked for each of the elements of \a rng2 + /// as a projection operation before the actual + /// predicate \a is invoked. + /// + /// The comparison operations in the parallel \a search algorithm invoked + /// with an execution policy object of type \a sequenced_policy + /// execute in sequential order in the calling thread. + /// + /// The comparison operations in the parallel \a search 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 search algorithm returns a \a hpx::future if the + /// execution policy is of type \a task_execution_policy and + /// returns \a FwdIter otherwise. + /// The \a search algorithm returns an iterator to the beginning of + /// the first subsequence [s_first, s_last) in range [first, last). + /// If the length of the subsequence [s_first, s_last) is greater + /// than the length of the range [first, last), \a last is returned. + /// Additionally if the size of the subsequence is empty \a first is + /// returned. If no subsequence is found, \a last is returned. + /// + template ::value && + hpx::traits::is_range::value && + traits::is_projected_range::value && + hpx::traits::is_range::value && + traits::is_projected_range::value && + traits::is_indirect_callable< + ExPolicy, Pred, + traits::projected_range, + traits::projected_range + >::value + )> + typename util::detail::algorithm_result< + ExPolicy, + typename hpx::traits::range_iterator::type + >::type + search_n(ExPolicy && policy, Rng1 && rng1, + std::size_t count, Rng2 && rng2, + Pred && op = Pred(), Proj1 && proj1 = Proj1(), + Proj2 && proj2 = Proj2()) + { + return search_n(std::forward(policy), + hpx::util::begin(rng1), count, + hpx::util::begin(rng2), hpx::util::end(rng2), + std::forward(op), + std::forward(proj1), + std::forward(proj2)); + } +}}} + +#endif diff --git a/hpx/parallel/util/compare_projected.hpp b/hpx/parallel/util/compare_projected.hpp index 9e6160a1e1f5..cbd271d86c00 100644 --- a/hpx/parallel/util/compare_projected.hpp +++ b/hpx/parallel/util/compare_projected.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2016-2017 Hartmut Kaiser +// Copyright (c) 2018 Christopher Ogle // // 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) @@ -14,8 +15,11 @@ namespace hpx { namespace parallel { namespace util { /////////////////////////////////////////////////////////////////////////// + template + struct compare_projected; + template - struct compare_projected + struct compare_projected { template compare_projected(Compare_ && comp, Proj_ && proj) @@ -34,6 +38,29 @@ namespace hpx { namespace parallel { namespace util Compare comp_; Proj proj_; }; + + template + struct compare_projected + { + template + compare_projected(Compare_ && comp, Proj1_ && proj1, Proj2_ && proj2) + : comp_(std::forward(comp)), + proj1_(std::forward(proj1)), + proj2_(std::forward(proj2)) + {} + + template + inline bool operator()(T1 && t1, T2 && t2) const + { + return hpx::util::invoke(comp_, + hpx::util::invoke(proj1_, std::forward(t1)), + hpx::util::invoke(proj2_, std::forward(t2))); + } + + Compare comp_; + Proj1 proj1_; + Proj2 proj2_; + }; }}} #endif diff --git a/tests/unit/parallel/container_algorithms/CMakeLists.txt b/tests/unit/parallel/container_algorithms/CMakeLists.txt index bb68be50ff85..59dbb7abc768 100644 --- a/tests/unit/parallel/container_algorithms/CMakeLists.txt +++ b/tests/unit/parallel/container_algorithms/CMakeLists.txt @@ -36,6 +36,8 @@ set(tests reverse_copy_range rotate_range rotate_copy_range + search_range + searchn_range sort_range transform_range transform_range_binary diff --git a/tests/unit/parallel/container_algorithms/search_range.cpp b/tests/unit/parallel/container_algorithms/search_range.cpp new file mode 100644 index 000000000000..4dc7b68d3b8f --- /dev/null +++ b/tests/unit/parallel/container_algorithms/search_range.cpp @@ -0,0 +1,500 @@ +// Copyright (c) 2018 Christopher Ogle +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + + +struct user_defined_type_1 +{ + user_defined_type_1() = default; + user_defined_type_1(int v) : val(v){} + unsigned int val; +}; + +struct user_defined_type_2 +{ + user_defined_type_2() = default; + user_defined_type_2(int v) : val(v){} + std::size_t val; +}; + +//////////////////////////////////////////////////////////////////////////// +template +void test_search1(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto index = hpx::parallel::search(policy, c, h); + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(index == test_index); +} + +template +void test_search1_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto f = hpx::parallel::search(p, c, h); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(f.get() == test_index); +} + +template +void test_search1() +{ + using namespace hpx::parallel; + test_search1(execution::seq, IteratorTag()); + test_search1(execution::par, IteratorTag()); + test_search1(execution::par_unseq, IteratorTag()); + + test_search1_async(execution::seq(execution::task), IteratorTag()); + test_search1_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search1(execution_policy(execution::seq), IteratorTag()); + test_search1(execution_policy(execution::par), IteratorTag()); + test_search1(execution_policy(execution::par_unseq), IteratorTag()); + test_search1(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search1(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test1() +{ + test_search1(); + test_search1(); +} + +template +void test_search2(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values about 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence at start and end + c[0] = 1; + c[1] = 2; + c[c.size()-1] = 2; + c[c.size()-2] = 1; + + std::size_t h[] = { 1, 2 }; + + auto index = hpx::parallel::search(policy, c, h); + + auto test_index = std::begin(c); + + HPX_TEST(index == test_index); +} + +template +void test_search2_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence at start and end + c[0] = 1; + c[1] = 2; + c[c.size()-1] = 2; + c[c.size()-2] = 1; + + std::size_t h[] = { 1, 2 }; + + auto f = hpx::parallel::search(p, c, h); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c); + + HPX_TEST(f.get() == test_index); +} + +template +void test_search2() +{ + using namespace hpx::parallel; + test_search2(execution::seq, IteratorTag()); + test_search2(execution::par, IteratorTag()); + test_search2(execution::par_unseq, IteratorTag()); + + test_search2_async(execution::seq(execution::task), IteratorTag()); + test_search2_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search2(execution_policy(execution::seq), IteratorTag()); + test_search2(execution_policy(execution::par), IteratorTag()); + test_search2(execution_policy(execution::par_unseq), IteratorTag()); + + test_search2(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search2(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test2() +{ + test_search2(); + test_search2(); +} + +template +void test_search3(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence large enough to always be split into multiple partitions + std::iota(std::begin(c), std::begin(c) + c.size()/16+1, 1); + std::size_t sub_size = c.size()/16 + 1; + std::vector h(sub_size); + std::iota(std::begin(h), std::end(h), 1); + + auto index = hpx::parallel::search(policy, c, h); + + auto test_index = std::begin(c); + + HPX_TEST(index == test_index); +} + +template +void test_search3_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 6 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 7); + // create subsequence large enough to always be split into multiple partitions + std::iota(std::begin(c), std::begin(c) + c.size()/16+1, 1); + std::size_t sub_size = c.size()/16 + 1; + std::vector h(sub_size); + std::iota(std::begin(h), std::end(h), 1); + + // create only two partitions, splitting the desired sub sequence into + // separate partitions. + auto f = hpx::parallel::search(p, c, h); + f.wait(); + + //create iterator at position of value to be found + auto test_index = std::begin(c); + + HPX_TEST(f.get() == test_index); +} + +template +void test_search3() +{ + using namespace hpx::parallel; + test_search3(execution::seq, IteratorTag()); + test_search3(execution::par, IteratorTag()); + test_search3(execution::par_unseq, IteratorTag()); + + test_search3_async(execution::seq(execution::task), IteratorTag()); + test_search3_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search3(execution_policy(execution::seq), IteratorTag()); + test_search3(execution_policy(execution::par), IteratorTag()); + test_search3(execution_policy(execution::par_unseq), IteratorTag()); + + test_search3(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search3(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test3() +{ + test_search3(); + test_search3(); +} + +template +void test_search4(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto op = + [](std::size_t a, std::size_t b) + { + return !(a != b); + }; + + auto index = hpx::parallel::search(policy, c, h, op); + + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(index == test_index); +} + +template +void test_search4_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector, provide custom predicate + // for search + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto op = + [](std::size_t a, std::size_t b) + { + return !(a != b); + }; + + auto f = hpx::parallel::search(p, c, h, op); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(f.get() == test_index); +} + +template +void test_search4() +{ + using namespace hpx::parallel; + test_search4(execution::seq, IteratorTag()); + test_search4(execution::par, IteratorTag()); + test_search4(execution::par_unseq, IteratorTag()); + + test_search4_async(execution::seq(execution::task), IteratorTag()); + test_search4_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search4(execution_policy(execution::seq), IteratorTag()); + test_search4(execution_policy(execution::par), IteratorTag()); + test_search4(execution_policy(execution::par_unseq), IteratorTag()); + test_search4(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search4(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test4() +{ + test_search4(); + test_search4(); +} + +template +void test_search5(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + + // fill vector with random values above 2 + std::for_each(std::begin(c), std::end(c), [](user_defined_type_1 & ut1){ + ut1.val = (std::rand() % 100) + 3; + }); + + c[c.size()/2].val = 1; + c[c.size()/2 + 1].val = 2; + + user_defined_type_2 h[] = { user_defined_type_2(1), user_defined_type_2(2) }; + + auto op = + [](std::size_t a, std::size_t b) + { + return (a == b); + }; + + //Provide custom projections + auto proj1 = + [](const user_defined_type_1 & ut1) + { + return ut1.val; + }; + + auto proj2 = + [](const user_defined_type_2 & ut2) + { + return ut2.val; + }; + + auto index = hpx::parallel::search(policy, c, h, op, proj1, proj2); + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(index == test_index); +} + +template +void test_search5_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 2 + std::for_each(std::begin(c), std::end(c), [](user_defined_type_1 & ut1){ + ut1.val = (std::rand() % 100) + 3; + }); + // create subsequence in middle of vector, + c[c.size()/2].val = 1; + c[c.size()/2 + 1].val = 2; + + user_defined_type_2 h[] = { user_defined_type_2(1), user_defined_type_2(2) }; + + auto op = + [](std::size_t a, std::size_t b) + { + return !(a != b); + }; + + //Provide custom projections + auto proj1 = + [](const user_defined_type_1 & ut1) + { + return ut1.val; + }; + + auto proj2 = + [](const user_defined_type_2 & ut2) + { + return ut2.val; + }; + + + auto f = hpx::parallel::search(p, c, h, op, proj1, proj2); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(f.get() == test_index); +} + +template +void test_search5() +{ + using namespace hpx::parallel; + test_search5(execution::seq, IteratorTag()); + test_search5(execution::par, IteratorTag()); + test_search5(execution::par_unseq, IteratorTag()); + + test_search5_async(execution::seq(execution::task), IteratorTag()); + test_search5_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search5(execution_policy(execution::seq), IteratorTag()); + test_search5(execution_policy(execution::par), IteratorTag()); + test_search5(execution_policy(execution::par_unseq), IteratorTag()); + test_search5(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search5(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test5() +{ + test_search5(); + test_search5(); +} + + +//////////////////////////////////////////////////////////////////////////// +int hpx_main(boost::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int)std::time(nullptr); + if(vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + search_test1(); + search_test2(); + search_test3(); + search_test4(); + search_test5(); + return hpx::finalize(); +} + +int main(int argc, char* argv[]) +{ + using namespace boost::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") + ; + + std::vector const cfg = { + "hpx.os_threads=all" + }; + + HPX_TEST_EQ_MSG(hpx::init(desc_commandline, argc, argv, cfg), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/tests/unit/parallel/container_algorithms/searchn_range.cpp b/tests/unit/parallel/container_algorithms/searchn_range.cpp new file mode 100644 index 000000000000..a8d4663e10d6 --- /dev/null +++ b/tests/unit/parallel/container_algorithms/searchn_range.cpp @@ -0,0 +1,505 @@ +// Copyright (c) 2018 Christopher Ogle +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + + +struct user_defined_type_1 +{ + user_defined_type_1() = default; + user_defined_type_1(int v) : val(v){} + unsigned int val; +}; + +struct user_defined_type_2 +{ + user_defined_type_2() = default; + user_defined_type_2(int v) : val(v){} + std::size_t val; +}; + +//////////////////////////////////////////////////////////////////////////// +template +void test_search_n1(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto index = hpx::parallel::search_n(policy, c, c.size(), h); + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(index == test_index); +} + +template +void test_search_n1_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto f = hpx::parallel::search_n(p, c, c.size(), h); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(f.get() == test_index); +} + +template +void test_search_n1() +{ + using namespace hpx::parallel; + test_search_n1(execution::seq, IteratorTag()); + test_search_n1(execution::par, IteratorTag()); + test_search_n1(execution::par_unseq, IteratorTag()); + + test_search_n1_async(execution::seq(execution::task), IteratorTag()); + test_search_n1_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search_n1(execution_policy(execution::seq), IteratorTag()); + test_search_n1(execution_policy(execution::par), IteratorTag()); + test_search_n1(execution_policy(execution::par_unseq), IteratorTag()); + test_search_n1(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search_n1(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test_n1() +{ + test_search_n1(); + test_search_n1(); +} + +template +void test_search_n2(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values about 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence at start and end + + c[0] = 1; + c[1] = 2; + c[c.size()-1] = 2; + c[c.size()-2] = 1; + + std::size_t h[] = { 1, 2 }; + + auto index = hpx::parallel::search_n(policy, c, c.size(), h); + + auto test_index = std::begin(c); + + HPX_TEST(index == test_index); +} + +template +void test_search_n2_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence at start and end + c[0] = 1; + c[1] = 2; + c[c.size()-1] = 2; + c[c.size()-2] = 1; + + std::size_t h[] = { 1, 2 }; + + auto f = hpx::parallel::search_n(p, c, c.size(), h); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c); + + HPX_TEST(f.get() == test_index); +} + +template +void test_search_n2() +{ + using namespace hpx::parallel; + test_search_n2(execution::seq, IteratorTag()); + test_search_n2(execution::par, IteratorTag()); + test_search_n2(execution::par_unseq, IteratorTag()); + + test_search_n2_async(execution::seq(execution::task), IteratorTag()); + test_search_n2_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search_n2(execution_policy(execution::seq), IteratorTag()); + test_search_n2(execution_policy(execution::par), IteratorTag()); + test_search_n2(execution_policy(execution::par_unseq), IteratorTag()); + + test_search_n2(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search_n2(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test_n2() +{ + test_search_n2(); + test_search_n2(); +} + +template +void test_search_n3(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence large enough to always be split into multiple partitions + std::iota(std::begin(c), std::begin(c) + c.size()/16+1, 1); + std::size_t sub_size = c.size()/16 + 1; + std::vector h(sub_size); + std::iota(std::begin(h), std::end(h), 1); + + auto index = hpx::parallel::search_n(policy, c, c.size(), h); + + auto test_index = std::begin(c); + + HPX_TEST(index == test_index); +} + +template +void test_search_n3_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 6 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 7); + // create subsequence large enough to always be split into multiple partitions + std::iota(std::begin(c), std::begin(c) + c.size()/16+1, 1); + std::size_t sub_size = c.size()/16 + 1; + std::vector h(sub_size); + std::iota(std::begin(h), std::end(h), 1); + + // create only two partitions, splitting the desired sub sequence into + // separate partitions. + auto f = hpx::parallel::search_n(p, c, c.size(), h); + f.wait(); + + //create iterator at position of value to be found + auto test_index = std::begin(c); + + HPX_TEST(f.get() == test_index); +} + +template +void test_search_n3() +{ + using namespace hpx::parallel; + test_search_n3(execution::seq, IteratorTag()); + test_search_n3(execution::par, IteratorTag()); + test_search_n3(execution::par_unseq, IteratorTag()); + + test_search_n3_async(execution::seq(execution::task), IteratorTag()); + test_search_n3_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search_n3(execution_policy(execution::seq), IteratorTag()); + test_search_n3(execution_policy(execution::par), IteratorTag()); + test_search_n3(execution_policy(execution::par_unseq), IteratorTag()); + + test_search_n3(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search_n3(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test_n3() +{ + test_search_n3(); + test_search_n3(); +} + +template +void test_search_n4(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto op = + [](std::size_t a, std::size_t b) + { + return !(a != b); + }; + + auto index = hpx::parallel::search_n(policy, c, c.size(), h, op); + + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(index == test_index); +} + +template +void test_search_n4_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector, provide custom predicate + // for search + c[c.size()/2] = 1; + c[c.size()/2 + 1] = 2; + + std::size_t h[] = { 1, 2 }; + + auto op = + [](std::size_t a, std::size_t b) + { + return !(a != b); + }; + + auto f = hpx::parallel::search_n(p, c, c.size(), h, op); + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(f.get() == test_index); +} + +template +void test_search_n4() +{ + using namespace hpx::parallel; + test_search_n4(execution::seq, IteratorTag()); + test_search_n4(execution::par, IteratorTag()); + test_search_n4(execution::par_unseq, IteratorTag()); + + test_search_n4_async(execution::seq(execution::task), IteratorTag()); + test_search_n4_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search_n4(execution_policy(execution::seq), IteratorTag()); + test_search_n4(execution_policy(execution::par), IteratorTag()); + test_search_n4(execution_policy(execution::par_unseq), IteratorTag()); + test_search_n4(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search_n4(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test_n4() +{ + test_search_n4(); + test_search_n4(); +} + +template +void test_search_n5(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + std::vector c(10007); + + // fill vector with random values above 2 + std::for_each(std::begin(c), std::end(c), [](user_defined_type_1 & ut1){ + ut1.val = (std::rand() % 100) + 3; + }); + + c[c.size()/2].val = 1; + c[c.size()/2 + 1].val = 2; + + user_defined_type_2 h[] = { user_defined_type_2(1), user_defined_type_2(2) }; + + auto op = + [](std::size_t a, std::size_t b) + { + return (a == b); + }; + + //Provide custom projections + auto proj1 = + [](const user_defined_type_1 & ut1) + { + return ut1.val; + }; + + auto proj2 = + [](const user_defined_type_2 & ut2) + { + return ut2.val; + }; + + auto index = hpx::parallel::search_n(policy, c, c.size(), h, + op, proj1, proj2); + + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(index == test_index); +} + +template +void test_search_n5_async(ExPolicy p, IteratorTag) +{ + std::vector c(10007); + // fill vector with random values above 2 + std::for_each(std::begin(c), std::end(c), [](user_defined_type_1 & ut1){ + ut1.val = (std::rand() % 100) + 3; + }); + // create subsequence in middle of vector, + c[c.size()/2].val = 1; + c[c.size()/2 + 1].val = 2; + + user_defined_type_2 h[] = { user_defined_type_2(1), user_defined_type_2(2) }; + + auto op = + [](std::size_t a, std::size_t b) + { + return !(a != b); + }; + + //Provide custom projections + auto proj1 = + [](const user_defined_type_1 & ut1) + { + return ut1.val; + }; + + auto proj2 = + [](const user_defined_type_2 & ut2) + { + return ut2.val; + }; + + + auto f = hpx::parallel::search_n(p, c, c.size(), h, + op, proj1, proj2); + + f.wait(); + + // create iterator at position of value to be found + auto test_index = std::begin(c) + c.size()/2; + + HPX_TEST(f.get() == test_index); +} + +template +void test_search_n5() +{ + using namespace hpx::parallel; + test_search_n5(execution::seq, IteratorTag()); + test_search_n5(execution::par, IteratorTag()); + test_search_n5(execution::par_unseq, IteratorTag()); + + test_search_n5_async(execution::seq(execution::task), IteratorTag()); + test_search_n5_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_search_n5(execution_policy(execution::seq), IteratorTag()); + test_search_n5(execution_policy(execution::par), IteratorTag()); + test_search_n5(execution_policy(execution::par_unseq), IteratorTag()); + test_search_n5(execution_policy(execution::seq(execution::task)), + IteratorTag()); + test_search_n5(execution_policy(execution::par(execution::task)), + IteratorTag()); +#endif +} + +void search_test_n5() +{ + test_search_n5(); + test_search_n5(); +} + + +//////////////////////////////////////////////////////////////////////////// +int hpx_main(boost::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int)std::time(nullptr); + if(vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + search_test_n1(); + search_test_n2(); + search_test_n3(); + search_test_n4(); + search_test_n5(); + return hpx::finalize(); +} + +int main(int argc, char* argv[]) +{ + using namespace boost::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") + ; + + std::vector const cfg = { + "hpx.os_threads=all" + }; + + HPX_TEST_EQ_MSG(hpx::init(desc_commandline, argc, argv, cfg), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +}