/* //@HEADER // ************************************************************************ // // Kokkos v. 3.0 // Copyright (2020) National Technology & Engineering // Solutions of Sandia, LLC (NTESS). // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the Corporation nor the names of the // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY NTESS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NTESS OR THE // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Questions? Contact Christian R. Trott (crtrott@sandia.gov) // // ************************************************************************ //@HEADER */ #include #include namespace KE = Kokkos::Experimental; namespace Test { namespace stdalgos { struct CustomValueType { KOKKOS_INLINE_FUNCTION CustomValueType(){}; KOKKOS_INLINE_FUNCTION CustomValueType(value_type val) : value(val){}; KOKKOS_INLINE_FUNCTION CustomValueType(const CustomValueType& other) { this->value = other.value; } KOKKOS_INLINE_FUNCTION value_type& operator()() { return value; } KOKKOS_INLINE_FUNCTION const value_type& operator()() const { return value; } KOKKOS_INLINE_FUNCTION CustomValueType& operator+=(const CustomValueType& other) { this->value += other.value; return *this; } KOKKOS_INLINE_FUNCTION CustomValueType& operator=(const CustomValueType& other) { this->value = other.value; return *this; } KOKKOS_INLINE_FUNCTION CustomValueType operator+(const CustomValueType& other) const { CustomValueType result; result.value = this->value + other.value; return result; } KOKKOS_INLINE_FUNCTION CustomValueType operator*(const CustomValueType& other) const { CustomValueType result; result.value = this->value * other.value; return result; } KOKKOS_INLINE_FUNCTION bool operator==(const CustomValueType& other) const { return this->value == other.value; } // // volatile overloads needed for the kokkos reductions // // note the void return KOKKOS_INLINE_FUNCTION void operator+=(const volatile CustomValueType& other) volatile { this->value += other.value; } // note the void return KOKKOS_INLINE_FUNCTION void operator=(const CustomValueType& other) volatile { this->value = other.value; } KOKKOS_INLINE_FUNCTION CustomValueType operator+(const volatile CustomValueType& other) const volatile { CustomValueType result; result.value = this->value + other.value; return result; } private: value_type value = {}; }; template struct TimesTwoUnaryTransformFunctor { KOKKOS_INLINE_FUNCTION ValueType operator()(const ValueType& a) const { return (a * 2.); } }; template struct MultiplyAndHalveBinaryTransformFunctor { KOKKOS_INLINE_FUNCTION ValueType operator()(const ValueType& a, const ValueType& b) const { return (a * b) * 0.5; } }; template struct SumJoinFunctor { KOKKOS_INLINE_FUNCTION ValueType operator()(const ValueType& a, const ValueType& b) const { return a + b; } KOKKOS_INLINE_FUNCTION ValueType operator()(const volatile ValueType& a, const volatile ValueType& b) const { return a + b; } }; struct std_algorithms_numerics_test : public ::testing::Test { Kokkos::LayoutStride layout{20, 2}; // value_type using static_view_t = Kokkos::View; using dyn_view_t = Kokkos::View; using strided_view_t = Kokkos::View; static_view_t m_static_view{"std-algo-test-1D-contiguous-view-static"}; dyn_view_t m_dynamic_view{"std-algo-test-1D-contiguous-view-dyn", 20}; strided_view_t m_strided_view{"std-algo-test-1D-strided-view", layout}; // custom scalar (cs) using static_view_cs_t = Kokkos::View; using dyn_view_cs_t = Kokkos::View; using strided_view_cs_t = Kokkos::View; static_view_cs_t m_static_view_cs{ "std-algo-test-1D-contiguous-view-static-custom-scalar"}; dyn_view_cs_t m_dynamic_view_cs{ "std-algo-test-1D-contiguous-view-dyn-custom_scalar", 20}; strided_view_cs_t m_strided_view_cs{ "std-algo-test-1D-strided-view-custom-scalar", layout}; template void copyPodViewToCustom(ViewFromType v_from, ViewToType v_to) { for (std::size_t i = 0; i < v_from.extent(0); ++i) { v_to(i)() = v_from(i); } } void fillFixtureViews() { static_view_t tmpView("tmpView"); static_view_cs_t tmpViewCs("tmpViewCs"); auto tmp_view_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), tmpView); auto tmp_view_cs_h = Kokkos::create_mirror_view(Kokkos::HostSpace(), tmpViewCs); tmp_view_h(0) = 0.; tmp_view_h(1) = 0.; tmp_view_h(2) = 0.; tmp_view_h(3) = 2.; tmp_view_h(4) = 2.; tmp_view_h(5) = 1.; tmp_view_h(6) = 1.; tmp_view_h(7) = 1.; tmp_view_h(8) = 1.; tmp_view_h(9) = 0.; tmp_view_h(10) = -2.; tmp_view_h(11) = -2.; tmp_view_h(12) = 0.; tmp_view_h(13) = 2.; tmp_view_h(14) = 2.; tmp_view_h(15) = 1.; tmp_view_h(16) = 1.; tmp_view_h(17) = 1.; tmp_view_h(18) = 1.; tmp_view_h(19) = 0.; copyPodViewToCustom(tmp_view_h, tmp_view_cs_h); Kokkos::deep_copy(tmpView, tmp_view_h); Kokkos::deep_copy(tmpViewCs, tmp_view_cs_h); CopyFunctor F1(tmpView, m_static_view); Kokkos::parallel_for("_std_algo_copy1", 20, F1); CopyFunctor F2(tmpView, m_dynamic_view); Kokkos::parallel_for("_std_algo_copy2", 20, F2); CopyFunctor F3(tmpView, m_strided_view); Kokkos::parallel_for("_std_algo_copy3", 20, F3); CopyFunctor F4(tmpViewCs, m_static_view_cs); Kokkos::parallel_for("_std_algo_copy4", 20, F4); CopyFunctor F5(tmpViewCs, m_dynamic_view_cs); Kokkos::parallel_for("_std_algo_copy5", 20, F5); CopyFunctor F6(tmpViewCs, m_strided_view_cs); Kokkos::parallel_for("_std_algo_copy6", 20, F6); } }; #if not defined KOKKOS_ENABLE_OPENMPTARGET // ------------------------------------------------------------------- // test default case of transform_reduce // // test for both POD types and custom scalar types // ------------------------------------------------------------------- template void run_and_check_transform_reduce_default(ViewType1 first_view, ViewType2 second_view, ValueType init_value, ValueType result_value) { // trivial cases const auto r1 = KE::transform_reduce(ExecutionSpace(), KE::cbegin(first_view), KE::cbegin(first_view), KE::cbegin(second_view), init_value); const auto r2 = KE::transform_reduce( "MYLABEL", ExecutionSpace(), KE::cbegin(first_view), KE::cbegin(first_view), KE::cbegin(second_view), init_value); EXPECT_TRUE(r1 == init_value); EXPECT_TRUE(r2 == init_value); // non-trivial cases const auto r3 = KE::transform_reduce(ExecutionSpace(), KE::cbegin(first_view), KE::cend(first_view), KE::cbegin(second_view), init_value); const auto r4 = KE::transform_reduce( "MYLABEL", ExecutionSpace(), KE::cbegin(first_view), KE::cend(first_view), KE::cbegin(second_view), init_value); const auto r5 = KE::transform_reduce(ExecutionSpace(), first_view, second_view, init_value); const auto r6 = KE::transform_reduce("MYLABEL", ExecutionSpace(), first_view, second_view, init_value); EXPECT_TRUE(r3 == result_value); EXPECT_TRUE(r4 == result_value); EXPECT_TRUE(r5 == result_value); EXPECT_TRUE(r6 == result_value); } TEST_F(std_algorithms_numerics_test, transform_reduce_default_functors_using_pod_value_type) { fillFixtureViews(); const value_type init0 = 0.; const value_type init5 = 5.; const value_type gold0 = 32.; const value_type gold5 = 37.; run_and_check_transform_reduce_default( m_static_view, m_dynamic_view, init0, gold0); run_and_check_transform_reduce_default( m_static_view, m_dynamic_view, init5, gold5); run_and_check_transform_reduce_default( m_static_view, m_strided_view, init0, gold0); run_and_check_transform_reduce_default( m_static_view, m_strided_view, init5, gold5); run_and_check_transform_reduce_default( m_dynamic_view, m_strided_view, init0, gold0); run_and_check_transform_reduce_default( m_dynamic_view, m_strided_view, init5, gold5); } TEST_F(std_algorithms_numerics_test, transform_reduce_default_functors_using_custom_value_type) { fillFixtureViews(); const CustomValueType init0{0.}; const CustomValueType init5{5.}; const CustomValueType gold0{32.}; const CustomValueType gold5{37.}; run_and_check_transform_reduce_default( m_static_view_cs, m_dynamic_view_cs, init0, gold0); run_and_check_transform_reduce_default( m_static_view_cs, m_dynamic_view_cs, init5, gold5); run_and_check_transform_reduce_default( m_static_view_cs, m_strided_view_cs, init0, gold0); run_and_check_transform_reduce_default( m_static_view_cs, m_strided_view_cs, init5, gold5); run_and_check_transform_reduce_default( m_dynamic_view_cs, m_strided_view_cs, init0, gold0); run_and_check_transform_reduce_default( m_dynamic_view_cs, m_strided_view_cs, init5, gold5); } // ------------------------------------------------------------------- // transform_reduce for custom joiner and custom transform op // test for both POD types and custom scalar types // // test overload1 accepting two intervals // // Note that in the std, the reducer is called BinaryReductionOp // but in the Kokkos naming convention, it corresponds to a "joiner" // that knows how to join two values. // the "joiner" is assumed to be commutative: // // https://en.cppreference.com/w/cpp/algorithm/transform_reduce // // ------------------------------------------------------------------- template void run_and_check_transform_reduce_overloadA(ViewType1 first_view, ViewType2 second_view, ValueType init_value, ValueType result_value, Args&&... args) { // trivial cases const auto r1 = KE::transform_reduce( ExecutionSpace(), KE::cbegin(first_view), KE::cbegin(first_view), KE::cbegin(second_view), init_value, std::forward(args)...); const auto r2 = KE::transform_reduce("MYLABEL", ExecutionSpace(), KE::cbegin(first_view), KE::cbegin(first_view), KE::cbegin(second_view), init_value, std::forward(args)...); EXPECT_TRUE(r1 == init_value); EXPECT_TRUE(r2 == init_value); // non trivial cases const auto r3 = KE::transform_reduce( ExecutionSpace(), KE::cbegin(first_view), KE::cend(first_view), KE::cbegin(second_view), init_value, std::forward(args)...); const auto r4 = KE::transform_reduce( "MYLABEL", ExecutionSpace(), KE::cbegin(first_view), KE::cend(first_view), KE::cbegin(second_view), init_value, std::forward(args)...); const auto r5 = KE::transform_reduce(ExecutionSpace(), first_view, second_view, init_value, std::forward(args)...); const auto r6 = KE::transform_reduce("MYLABEL", ExecutionSpace(), first_view, second_view, init_value, std::forward(args)...); EXPECT_TRUE(r3 == result_value); EXPECT_TRUE(r4 == result_value); EXPECT_TRUE(r5 == result_value); EXPECT_TRUE(r6 == result_value); } TEST_F(std_algorithms_numerics_test, transform_reduce_custom_functors_overloadA_using_pod_value_type) { using joiner_type = SumJoinFunctor; using transf_type = MultiplyAndHalveBinaryTransformFunctor; const value_type init0 = 0.; const value_type init5 = 5.; const value_type gold0 = 16.; const value_type gold5 = 21.; fillFixtureViews(); run_and_check_transform_reduce_overloadA( m_static_view, m_dynamic_view, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_static_view, m_dynamic_view, init5, gold5, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_static_view, m_strided_view, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_static_view, m_strided_view, init5, gold5, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_dynamic_view, m_strided_view, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_dynamic_view, m_strided_view, init5, gold5, joiner_type(), transf_type()); } TEST_F(std_algorithms_numerics_test, transform_reduce_custom_functors_overloadA_using_custom_value_type) { using joiner_type = SumJoinFunctor; using transf_type = MultiplyAndHalveBinaryTransformFunctor; const CustomValueType init0{0.}; const CustomValueType init5{5.}; const CustomValueType gold0{16.}; const CustomValueType gold5{21.}; fillFixtureViews(); run_and_check_transform_reduce_overloadA( m_static_view_cs, m_dynamic_view_cs, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_static_view_cs, m_dynamic_view_cs, init5, gold5, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_static_view_cs, m_strided_view_cs, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_static_view_cs, m_strided_view_cs, init5, gold5, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_dynamic_view_cs, m_strided_view_cs, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadA( m_dynamic_view_cs, m_strided_view_cs, init5, gold5, joiner_type(), transf_type()); } // ------------------------------------------------------------------- // transform_reduce for custom joiner and custom transform op // test for both POD types and custom scalar types // // test overload1 accepting single interval/view // // Note that in the std, the reducer is called BinaryReductionOp // but in the Kokkos naming convention, it corresponds to a "joiner" // that knows how to join two values. // the "joiner" is assumed to be commutative: // // https://en.cppreference.com/w/cpp/algorithm/transform_reduce // // ------------------------------------------------------------------- template void run_and_check_transform_reduce_overloadB(ViewType view, ValueType init_value, ValueType result_value, Args&&... args) { // trivial const auto r1 = KE::transform_reduce(ExecutionSpace(), KE::cbegin(view), KE::cbegin(view), init_value, std::forward(args)...); const auto r2 = KE::transform_reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cbegin(view), init_value, std::forward(args)...); EXPECT_TRUE(r1 == init_value); EXPECT_TRUE(r2 == init_value); // non trivial const auto r3 = KE::transform_reduce(ExecutionSpace(), KE::cbegin(view), KE::cend(view), init_value, std::forward(args)...); const auto r4 = KE::transform_reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cend(view), init_value, std::forward(args)...); const auto r5 = KE::transform_reduce(ExecutionSpace(), view, init_value, std::forward(args)...); const auto r6 = KE::transform_reduce("MYLABEL", ExecutionSpace(), view, init_value, std::forward(args)...); EXPECT_TRUE(r3 == result_value); EXPECT_TRUE(r4 == result_value); EXPECT_TRUE(r5 == result_value); EXPECT_TRUE(r6 == result_value); } TEST_F(std_algorithms_numerics_test, transform_reduce_custom_functors_overloadB_using_pod_value_type) { using joiner_type = SumJoinFunctor; using transf_type = TimesTwoUnaryTransformFunctor; const value_type init0 = 0.; const value_type init5 = 5.; const value_type gold0 = 24.; const value_type gold5 = 29.; fillFixtureViews(); run_and_check_transform_reduce_overloadB( m_static_view, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadB( m_dynamic_view, init5, gold5, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadB( m_strided_view, init0, gold0, joiner_type(), transf_type()); } TEST_F(std_algorithms_numerics_test, transform_reduce_custom_functors_overloadB_using_custom_value_type) { using joiner_type = SumJoinFunctor; using transf_type = TimesTwoUnaryTransformFunctor; const CustomValueType init0{0.}; const CustomValueType init5{5.}; const CustomValueType gold0{24.}; const CustomValueType gold5{29.}; fillFixtureViews(); run_and_check_transform_reduce_overloadB( m_static_view_cs, init0, gold0, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadB( m_dynamic_view_cs, init5, gold5, joiner_type(), transf_type()); run_and_check_transform_reduce_overloadB( m_strided_view_cs, init0, gold0, joiner_type(), transf_type()); } // ------------------------------------------------------------------- // test reduce overload1 // // test for both POD types and custom scalar types // ------------------------------------------------------------------- template void run_and_check_reduce_overloadA(ViewType view, ValueType non_trivial_result, ValueType trivial_result) { // trivial cases const auto r1 = KE::reduce(ExecutionSpace(), KE::cbegin(view), KE::cbegin(view)); const auto r2 = KE::reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cbegin(view)); EXPECT_TRUE(r1 == trivial_result); EXPECT_TRUE(r2 == trivial_result); // non trivial cases const auto r3 = KE::reduce(ExecutionSpace(), KE::cbegin(view), KE::cend(view)); const auto r4 = KE::reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cend(view)); const auto r5 = KE::reduce(ExecutionSpace(), view); const auto r6 = KE::reduce("MYLABEL", ExecutionSpace(), view); EXPECT_TRUE(r3 == non_trivial_result); EXPECT_TRUE(r4 == non_trivial_result); EXPECT_TRUE(r5 == non_trivial_result); EXPECT_TRUE(r6 == non_trivial_result); } TEST_F(std_algorithms_numerics_test, reduce_default_functors_overloadA_using_pod_value_type) { fillFixtureViews(); const value_type trivial_gold = 0.; const value_type non_trivial_gold = 12.; run_and_check_reduce_overloadA(m_static_view, non_trivial_gold, trivial_gold); run_and_check_reduce_overloadA(m_dynamic_view, non_trivial_gold, trivial_gold); run_and_check_reduce_overloadA(m_strided_view, non_trivial_gold, trivial_gold); } TEST_F(std_algorithms_numerics_test, reduce_default_functors_overloadA_using_custom_value_type) { fillFixtureViews(); const CustomValueType trivial_gold{0.}; const CustomValueType non_trivial_gold{12.}; run_and_check_reduce_overloadA(m_static_view_cs, non_trivial_gold, trivial_gold); run_and_check_reduce_overloadA(m_dynamic_view_cs, non_trivial_gold, trivial_gold); run_and_check_reduce_overloadA(m_strided_view_cs, non_trivial_gold, trivial_gold); } // ------------------------------------------------------------------- // test reduce overload2 with init value // // test for both POD types and custom scalar types // ------------------------------------------------------------------- template void run_and_check_reduce_overloadB(ViewType view, ValueType result_value, ValueType init_value) { // trivial cases const auto r1 = KE::reduce(ExecutionSpace(), KE::cbegin(view), KE::cbegin(view), init_value); const auto r2 = KE::reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cbegin(view), init_value); EXPECT_TRUE(r1 == init_value); EXPECT_TRUE(r2 == init_value); // non trivial cases const auto r3 = KE::reduce(ExecutionSpace(), KE::cbegin(view), KE::cend(view), init_value); const auto r4 = KE::reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cend(view), init_value); const auto r5 = KE::reduce(ExecutionSpace(), view, init_value); const auto r6 = KE::reduce("MYLABEL", ExecutionSpace(), view, init_value); EXPECT_TRUE(r3 == result_value); EXPECT_TRUE(r4 == result_value); EXPECT_TRUE(r5 == result_value); EXPECT_TRUE(r6 == result_value); } TEST_F(std_algorithms_numerics_test, reduce_default_functors_overloadB_using_pod_value_type) { fillFixtureViews(); const value_type init = 5.; const value_type gold = 17.; run_and_check_reduce_overloadB(m_static_view, gold, init); run_and_check_reduce_overloadB(m_dynamic_view, gold, init); run_and_check_reduce_overloadB(m_strided_view, gold, init); } TEST_F(std_algorithms_numerics_test, reduce_default_functors_overloadB_using_custom_value_type) { fillFixtureViews(); const CustomValueType init{5.}; const CustomValueType gold{17.}; run_and_check_reduce_overloadB(m_static_view_cs, gold, init); run_and_check_reduce_overloadB(m_dynamic_view_cs, gold, init); run_and_check_reduce_overloadB(m_strided_view_cs, gold, init); } // ------------------------------------------------------------------- // test reduce overload3 with init value // // test for both POD types and custom scalar types // ------------------------------------------------------------------- template void run_and_check_reduce_overloadC(ViewType view, ValueType result_value, ValueType init_value, BinaryOp joiner) { // trivial cases const auto r1 = KE::reduce(ExecutionSpace(), KE::cbegin(view), KE::cbegin(view), init_value, joiner); const auto r2 = KE::reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cbegin(view), init_value, joiner); EXPECT_TRUE(r1 == init_value); EXPECT_TRUE(r2 == init_value); // non trivial cases const auto r3 = KE::reduce(ExecutionSpace(), KE::cbegin(view), KE::cend(view), init_value, joiner); const auto r4 = KE::reduce("MYLABEL", ExecutionSpace(), KE::cbegin(view), KE::cend(view), init_value, joiner); const auto r5 = KE::reduce(ExecutionSpace(), view, init_value, joiner); const auto r6 = KE::reduce("MYLABEL", ExecutionSpace(), view, init_value, joiner); EXPECT_TRUE(r3 == result_value); EXPECT_TRUE(r4 == result_value); EXPECT_TRUE(r5 == result_value); EXPECT_TRUE(r6 == result_value); } TEST_F(std_algorithms_numerics_test, reduce_custom_functors_using_pod_value_type) { using joiner_type = SumJoinFunctor; fillFixtureViews(); const value_type init = 5.; const value_type gold = 17.; run_and_check_reduce_overloadC(m_static_view, gold, init, joiner_type()); run_and_check_reduce_overloadC(m_dynamic_view, gold, init, joiner_type()); run_and_check_reduce_overloadC(m_strided_view, gold, init, joiner_type()); } TEST_F(std_algorithms_numerics_test, reduce_custom_functors_using_custom_value_type) { using joiner_type = SumJoinFunctor; fillFixtureViews(); const CustomValueType init{5.}; const CustomValueType gold{17.}; run_and_check_reduce_overloadC(m_static_view_cs, gold, init, joiner_type()); run_and_check_reduce_overloadC(m_dynamic_view_cs, gold, init, joiner_type()); run_and_check_reduce_overloadC(m_strided_view_cs, gold, init, joiner_type()); } #endif // not defined KOKKOS_ENABLE_OPENMPTARGET } // namespace stdalgos } // namespace Test