From 5fd733837b7a9625871e9d3d6c489be347f2f0df Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Thu, 16 Apr 2026 10:56:15 +0530 Subject: [PATCH 01/16] [parallel] Fix hpx::nth_element to correctly support projections and sentinels This PR fixes a regression where hpx::nth_element ignored user-provided projections in its sequential and parallel paths. It also adds full support for sentinels and aligns the algorithm with C++20 Ranges standards. --- .../hpx/parallel/algorithms/nth_element.hpp | 122 ++++++++++++++---- .../tests/unit/algorithms/CMakeLists.txt | 1 + .../algorithms/nth_element_projection.cpp | 87 +++++++++++++ 3 files changed, 187 insertions(+), 23 deletions(-) create mode 100644 libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 7f4c6077a28d..38308da525de 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -139,11 +139,14 @@ namespace hpx { #include #include #include +#include #include #include +#include #include #include #include +#include #include #include @@ -186,9 +189,11 @@ namespace hpx::parallel { // Check the special conditions if (nth == first) { + using wrapped_comp_type = hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; RandomIt it = detail::min_element().call( - hpx::execution::seq, first, end, HPX_FORWARD(Compare, comp), - HPX_FORWARD(Proj, proj)); + hpx::execution::seq, first, end, + wrapped_comp_type(comp, proj), hpx::identity_v); if (it != first) { @@ -204,19 +209,27 @@ namespace hpx::parallel { if (nelem < nmin_sort) { + using wrapped_comp_type = hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; detail::sort().call(hpx::execution::seq, first, end, - HPX_FORWARD(Compare, comp), HPX_FORWARD(Proj, proj)); + wrapped_comp_type(comp, proj), hpx::identity_v); return; } if (level == 0) { - std::make_heap(first, end, comp); - std::sort_heap(first, nth, comp); + using wrapped_comp_type = hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; + hpx::parallel::detail::make_heap().call( + hpx::execution::seq, first, end, + wrapped_comp_type(comp, proj), hpx::identity_v); + std::sort_heap(first, nth, wrapped_comp_type(comp, proj)); return; } // Filter the range and check which part contains the nth element - RandomIt c_last = filter(first, end, comp); + using wrapped_comp_type = hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; + RandomIt c_last = filter(first, end, wrapped_comp_type(comp, proj)); if (c_last == nth) return; @@ -267,8 +280,6 @@ namespace hpx::parallel { parallel(ExPolicy&& policy, RandomIt first, RandomIt nth, Sent last, Pred&& pred, Proj&& proj) { - using value_type = - typename std::iterator_traits::value_type; RandomIt partition_iter, return_last; @@ -292,17 +303,21 @@ namespace hpx::parallel { while (first != last_iter) { - detail::pivot9(first, last_iter, pred); + detail::pivot9(first, last_iter, + hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>( + pred, proj)); partition_iter = hpx::parallel::detail::partition().call( policy(hpx::execution::non_task), first + 1, last_iter, - [val = HPX_INVOKE(proj, *first), &pred]( - value_type const& elem) { - return HPX_INVOKE(pred, elem, val); + [val = HPX_INVOKE(proj, *first), &pred, + &proj](auto const& elem) { + return HPX_INVOKE( + pred, HPX_INVOKE(proj, elem), val); }, - proj); + hpx::identity_v); --partition_iter; @@ -355,42 +370,103 @@ namespace hpx { : hpx::detail::tag_parallel_algorithm { template + typename Pred = hpx::parallel::detail::less, + typename Proj = hpx::identity> + // clang-format off + requires ( + hpx::traits::is_iterator_v && + hpx::is_invocable_v::type, + typename hpx::parallel::traits::projected_result_of::type + > + ) + // clang-format on + friend void tag_fallback_invoke(hpx::nth_element_t, RandomIt first, + RandomIt nth, RandomIt last, Pred pred = Pred(), Proj proj = Proj()) + { + static_assert(std::random_access_iterator, + "Requires at least random iterator."); + + hpx::parallel::detail::nth_element().call( + hpx::execution::seq, first, nth, last, HPX_MOVE(pred), + HPX_MOVE(proj)); + } + + template // clang-format off requires ( hpx::traits::is_iterator_v && hpx::is_invocable_v::value_type, - typename std::iterator_traits::value_type + typename hpx::parallel::traits::projected_result_of::type, + typename hpx::parallel::traits::projected_result_of::type > ) // clang-format on friend void tag_fallback_invoke(hpx::nth_element_t, RandomIt first, - RandomIt nth, RandomIt last, Pred pred = Pred()) + RandomIt nth, Sent last, Pred pred = Pred(), Proj proj = Proj()) { static_assert(std::random_access_iterator, "Requires at least random iterator."); hpx::parallel::detail::nth_element().call( hpx::execution::seq, first, nth, last, HPX_MOVE(pred), - hpx::identity_v); + HPX_MOVE(proj)); } template + typename Pred = hpx::parallel::detail::less, + typename Proj = hpx::identity> + // clang-format off + requires ( + hpx::is_execution_policy_v && + hpx::traits::is_iterator_v && + hpx::is_invocable_v::type, + typename hpx::parallel::traits::projected_result_of::type + > + ) + // clang-format on + friend parallel::util::detail::algorithm_result_t + tag_fallback_invoke(hpx::nth_element_t, ExPolicy&& policy, + RandomIt first, RandomIt nth, RandomIt last, Pred pred = Pred(), + Proj proj = Proj()) + { + static_assert(std::random_access_iterator, + "Requires at least random iterator."); + + using result_type = + hpx::parallel::util::detail::algorithm_result_t; + + return hpx::util::void_guard(), + hpx::parallel::detail::nth_element().call( + HPX_FORWARD(ExPolicy, policy), first, nth, last, + HPX_MOVE(pred), HPX_MOVE(proj)); + } + + template // clang-format off requires ( hpx::is_execution_policy_v && hpx::traits::is_iterator_v && hpx::is_invocable_v::value_type, - typename std::iterator_traits::value_type + typename hpx::parallel::traits::projected_result_of::type, + typename hpx::parallel::traits::projected_result_of::type > ) // clang-format on friend parallel::util::detail::algorithm_result_t tag_fallback_invoke(hpx::nth_element_t, ExPolicy&& policy, - RandomIt first, RandomIt nth, RandomIt last, Pred pred = Pred()) + RandomIt first, RandomIt nth, Sent last, Pred pred = Pred(), + Proj proj = Proj()) { static_assert(std::random_access_iterator, "Requires at least random iterator."); @@ -401,7 +477,7 @@ namespace hpx { return hpx::util::void_guard(), hpx::parallel::detail::nth_element().call( HPX_FORWARD(ExPolicy, policy), first, nth, last, - HPX_MOVE(pred), hpx::identity_v); + HPX_MOVE(pred), HPX_MOVE(proj)); } } nth_element{}; } // namespace hpx diff --git a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index 44ebddbcd306..3511e0229827 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -90,6 +90,7 @@ set(tests mismatch_binary move nth_element + nth_element_projection none_of parallel_sort partial_sort diff --git a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp new file mode 100644 index 000000000000..cb2d0fb5d1ac --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2014 Grant Mercer +// Copyright (c) 2017-2024 Hartmut Kaiser +// +// SPDX-License-Identifier: BSL-1.0 +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include +#include +#include +#include + +struct S +{ + int val; +}; + +template +void test_nth_element_projection(ExPolicy policy) +{ + std::vector v(100); + for (int i = 0; i < 100; ++i) + v[i].val = 100 - i; + + auto nth = v.begin() + 50; + + hpx::nth_element(policy, v.begin(), nth, v.end(), std::less{}, &S::val); + + // After nth_element, the element at nth should be the 51st smallest element (which is 51) + HPX_TEST_EQ(v[50].val, 51); + + // All elements before nth should be <= v[50].val + for (auto it = v.begin(); it != nth; ++it) + { + HPX_TEST_LTE(it->val, v[50].val); + } + + // All elements after nth should be >= v[50].val + for (auto it = nth + 1; it != v.end(); ++it) + { + HPX_TEST(it->val >= v[50].val); + } +} + +template +void test_nth_element_projection_type_change(ExPolicy policy) +{ + std::vector v = {"abc", "a", "abcd", "ab", "abcde"}; + auto nth = v.begin() + 2; + + // Sort by string length + hpx::nth_element(policy, v.begin(), nth, v.end(), std::less{}, + &std::string::length); + + HPX_TEST_EQ(v[2].length(), std::size_t(3)); +} + +int hpx_main() +{ + test_nth_element_projection(hpx::execution::seq); + test_nth_element_projection(hpx::execution::par); + test_nth_element_projection(hpx::execution::par_unseq); + + test_nth_element_projection_type_change(hpx::execution::seq); + test_nth_element_projection_type_change(hpx::execution::par); + test_nth_element_projection_type_change(hpx::execution::par_unseq); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + 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(); +} From 181ec0aaf6548e45b04bffa542581ba0f635a72f Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Thu, 16 Apr 2026 11:14:36 +0530 Subject: [PATCH 02/16] Apply clang-format fixes --- .../hpx/parallel/algorithms/nth_element.hpp | 28 +++++++++++-------- .../algorithms/nth_element_projection.cpp | 3 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 38308da525de..ff19849e1bbe 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -189,8 +189,9 @@ namespace hpx::parallel { // Check the special conditions if (nth == first) { - using wrapped_comp_type = hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; + using wrapped_comp_type = + hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; RandomIt it = detail::min_element().call( hpx::execution::seq, first, end, wrapped_comp_type(comp, proj), hpx::identity_v); @@ -209,16 +210,18 @@ namespace hpx::parallel { if (nelem < nmin_sort) { - using wrapped_comp_type = hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; + using wrapped_comp_type = + hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; detail::sort().call(hpx::execution::seq, first, end, wrapped_comp_type(comp, proj), hpx::identity_v); return; } if (level == 0) { - using wrapped_comp_type = hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; + using wrapped_comp_type = + hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; hpx::parallel::detail::make_heap().call( hpx::execution::seq, first, end, wrapped_comp_type(comp, proj), hpx::identity_v); @@ -227,8 +230,9 @@ namespace hpx::parallel { } // Filter the range and check which part contains the nth element - using wrapped_comp_type = hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; + using wrapped_comp_type = + hpx::parallel::util::compare_projected, + std::decay_t>; RandomIt c_last = filter(first, end, wrapped_comp_type(comp, proj)); if (c_last == nth) @@ -280,7 +284,6 @@ namespace hpx::parallel { parallel(ExPolicy&& policy, RandomIt first, RandomIt nth, Sent last, Pred&& pred, Proj&& proj) { - RandomIt partition_iter, return_last; if (first == last) @@ -312,8 +315,8 @@ namespace hpx::parallel { hpx::parallel::detail::partition().call( policy(hpx::execution::non_task), first + 1, last_iter, - [val = HPX_INVOKE(proj, *first), &pred, - &proj](auto const& elem) { + [val = HPX_INVOKE(proj, *first), &pred, &proj]( + auto const& elem) { return HPX_INVOKE( pred, HPX_INVOKE(proj, elem), val); }, @@ -394,7 +397,8 @@ namespace hpx { HPX_MOVE(proj)); } - template + template // clang-format off requires ( hpx::traits::is_iterator_v && diff --git a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp index cb2d0fb5d1ac..80a73bffa747 100644 --- a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp @@ -29,7 +29,8 @@ void test_nth_element_projection(ExPolicy policy) auto nth = v.begin() + 50; - hpx::nth_element(policy, v.begin(), nth, v.end(), std::less{}, &S::val); + hpx::nth_element( + policy, v.begin(), nth, v.end(), std::less{}, &S::val); // After nth_element, the element at nth should be the 51st smallest element (which is 51) HPX_TEST_EQ(v[50].val, 51); From b0ebd6271006e51f414b104507bf9630038743c0 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Thu, 16 Apr 2026 11:49:37 +0530 Subject: [PATCH 03/16] Remove inclusion to fix hpxinspect failure --- .../algorithms/tests/unit/algorithms/nth_element_projection.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp index 80a73bffa747..230733cddbe3 100644 --- a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include From 9740280f421e43ad44e0e2c19f46bbba4525db92 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Fri, 17 Apr 2026 01:54:55 +0530 Subject: [PATCH 04/16] Revert hpx::nth_element to standard compliance, move projections/sentinels to ranges API --- .../hpx/parallel/algorithms/nth_element.hpp | 82 +++-------------- .../tests/unit/algorithms/CMakeLists.txt | 1 - .../algorithms/nth_element_projection.cpp | 87 ------------------- .../nth_element_range.cpp | 72 +++++++++++++++ 4 files changed, 82 insertions(+), 160 deletions(-) delete mode 100644 libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index b78838f87e32..0c3207f07a83 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -365,104 +365,42 @@ namespace hpx { : hpx::detail::tag_parallel_algorithm { template + typename Pred = hpx::parallel::detail::less> // clang-format off requires ( hpx::traits::is_iterator_v && hpx::is_invocable_v::type, - typename hpx::parallel::traits::projected_result_of::type + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type > ) // clang-format on friend void tag_fallback_invoke(hpx::nth_element_t, RandomIt first, - RandomIt nth, RandomIt last, Pred pred = Pred(), Proj proj = Proj()) + RandomIt nth, RandomIt last, Pred pred = Pred()) { static_assert(std::random_access_iterator, "Requires at least random iterator."); hpx::parallel::detail::nth_element().call( hpx::execution::seq, first, nth, last, HPX_MOVE(pred), - HPX_MOVE(proj)); - } - - template - // clang-format off - requires ( - hpx::traits::is_iterator_v && - hpx::is_invocable_v::type, - typename hpx::parallel::traits::projected_result_of::type - > - ) - // clang-format on - friend void tag_fallback_invoke(hpx::nth_element_t, RandomIt first, - RandomIt nth, Sent last, Pred pred = Pred(), Proj proj = Proj()) - { - static_assert(std::random_access_iterator, - "Requires at least random iterator."); - - hpx::parallel::detail::nth_element().call( - hpx::execution::seq, first, nth, last, HPX_MOVE(pred), - HPX_MOVE(proj)); + hpx::identity_v); } template - // clang-format off - requires ( - hpx::is_execution_policy_v && - hpx::traits::is_iterator_v && - hpx::is_invocable_v::type, - typename hpx::parallel::traits::projected_result_of::type - > - ) - // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::nth_element_t, ExPolicy&& policy, - RandomIt first, RandomIt nth, RandomIt last, Pred pred = Pred(), - Proj proj = Proj()) - { - static_assert(std::random_access_iterator, - "Requires at least random iterator."); - - using result_type = - hpx::parallel::util::detail::algorithm_result_t; - - return hpx::util::void_guard(), - hpx::parallel::detail::nth_element().call( - HPX_FORWARD(ExPolicy, policy), first, nth, last, - HPX_MOVE(pred), HPX_MOVE(proj)); - } - - template + typename Pred = hpx::parallel::detail::less> // clang-format off requires ( hpx::is_execution_policy_v && hpx::traits::is_iterator_v && hpx::is_invocable_v::type, - typename hpx::parallel::traits::projected_result_of::type + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type > ) // clang-format on friend parallel::util::detail::algorithm_result_t tag_fallback_invoke(hpx::nth_element_t, ExPolicy&& policy, - RandomIt first, RandomIt nth, Sent last, Pred pred = Pred(), - Proj proj = Proj()) + RandomIt first, RandomIt nth, RandomIt last, Pred pred = Pred()) { static_assert(std::random_access_iterator, "Requires at least random iterator."); @@ -473,7 +411,7 @@ namespace hpx { return hpx::util::void_guard(), hpx::parallel::detail::nth_element().call( HPX_FORWARD(ExPolicy, policy), first, nth, last, - HPX_MOVE(pred), HPX_MOVE(proj)); + HPX_MOVE(pred), hpx::identity_v); } } nth_element{}; } // namespace hpx diff --git a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index aefb84efee46..efbc335eaa27 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -90,7 +90,6 @@ set(tests mismatch_binary move nth_element - nth_element_projection none_of parallel_sort partial_sort diff --git a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp b/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp deleted file mode 100644 index 230733cddbe3..000000000000 --- a/libs/core/algorithms/tests/unit/algorithms/nth_element_projection.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2014 Grant Mercer -// Copyright (c) 2017-2024 Hartmut Kaiser -// -// SPDX-License-Identifier: BSL-1.0 -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -#include -#include -#include -#include - -struct S -{ - int val; -}; - -template -void test_nth_element_projection(ExPolicy policy) -{ - std::vector v(100); - for (int i = 0; i < 100; ++i) - v[i].val = 100 - i; - - auto nth = v.begin() + 50; - - hpx::nth_element( - policy, v.begin(), nth, v.end(), std::less{}, &S::val); - - // After nth_element, the element at nth should be the 51st smallest element (which is 51) - HPX_TEST_EQ(v[50].val, 51); - - // All elements before nth should be <= v[50].val - for (auto it = v.begin(); it != nth; ++it) - { - HPX_TEST_LTE(it->val, v[50].val); - } - - // All elements after nth should be >= v[50].val - for (auto it = nth + 1; it != v.end(); ++it) - { - HPX_TEST(it->val >= v[50].val); - } -} - -template -void test_nth_element_projection_type_change(ExPolicy policy) -{ - std::vector v = {"abc", "a", "abcd", "ab", "abcde"}; - auto nth = v.begin() + 2; - - // Sort by string length - hpx::nth_element(policy, v.begin(), nth, v.end(), std::less{}, - &std::string::length); - - HPX_TEST_EQ(v[2].length(), std::size_t(3)); -} - -int hpx_main() -{ - test_nth_element_projection(hpx::execution::seq); - test_nth_element_projection(hpx::execution::par); - test_nth_element_projection(hpx::execution::par_unseq); - - test_nth_element_projection_type_change(hpx::execution::seq); - test_nth_element_projection_type_change(hpx::execution::par); - test_nth_element_projection_type_change(hpx::execution::par_unseq); - - return hpx::local::finalize(); -} - -int main(int argc, char* argv[]) -{ - std::vector const cfg = {"hpx.os_threads=all"}; - - hpx::local::init_params init_args; - 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/nth_element_range.cpp b/libs/core/algorithms/tests/unit/container_algorithms/nth_element_range.cpp index 29dc4132fbaf..a0683ee2f849 100644 --- a/libs/core/algorithms/tests/unit/container_algorithms/nth_element_range.cpp +++ b/libs/core/algorithms/tests/unit/container_algorithms/nth_element_range.cpp @@ -23,6 +23,11 @@ #include "test_utils.hpp" +struct S +{ + std::size_t val; +}; + //////////////////////////////////////////////////////////////////////////// #define SIZE 10007 @@ -198,6 +203,65 @@ void test_nth_element_async(ExPolicy policy, IteratorTag) } } +template +void test_nth_element_projection(ExPolicy policy) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + std::vector c(SIZE); + for (std::size_t i = 0; i < SIZE; ++i) + c[i].val = SIZE - i; + + auto rand_index = std::rand() % SIZE; + auto nth = std::begin(c) + rand_index; + + hpx::ranges::nth_element(policy, c, nth, std::less{}, &S::val); + + for (int k = 0; k < rand_index; k++) + { + HPX_TEST(c[k].val <= c[rand_index].val); + } + + for (int k = rand_index + 1; k < SIZE; k++) + { + HPX_TEST(c[k].val >= c[rand_index].val); + } +} + +template +void test_nth_element_sent_projection(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + using sentinel = test::sentinel_from_iterator; + + std::vector c(SIZE); + for (std::size_t i = 0; i < SIZE; ++i) + c[i].val = SIZE - i; + + auto rand_index = std::rand() % SIZE; + + auto result = hpx::ranges::nth_element(policy, iterator(std::begin(c)), + iterator(std::begin(c) + rand_index), + sentinel(iterator(std::end(c) - 1)), std::less{}, &S::val); + + HPX_TEST(result == iterator(std::end(c) - 1)); + + for (int k = 0; k < rand_index; k++) + { + HPX_TEST(c[k].val <= c[rand_index].val); + } + + for (int k = rand_index + 1; k < SIZE - 1; k++) + { + HPX_TEST(c[k].val >= c[rand_index].val); + } +} + template void test_nth_element() { @@ -215,6 +279,14 @@ void test_nth_element() test_nth_element_sent(seq, IteratorTag()); test_nth_element_sent(par, IteratorTag()); test_nth_element_sent(par_unseq, IteratorTag()); + + test_nth_element_projection(seq); + test_nth_element_projection(par); + test_nth_element_projection(par_unseq); + + test_nth_element_sent_projection(seq, IteratorTag()); + test_nth_element_sent_projection(par, IteratorTag()); + test_nth_element_sent_projection(par_unseq, IteratorTag()); } void nth_element_test() From e58f6aa80e8d8757392bf31c709fb540413ee90b Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Fri, 17 Apr 2026 03:20:46 +0530 Subject: [PATCH 05/16] Address maintainer feedback: consolidate wrapped_comp_type and use std::make_heap --- .../hpx/parallel/algorithms/nth_element.hpp | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 0c3207f07a83..08400b479d67 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -183,18 +183,18 @@ namespace hpx::parallel { constexpr void nth_element_seq(RandomIt first, RandomIt nth, RandomIt end, std::uint32_t level, Compare&& comp, Proj&& proj) { + using wrapped_comp_type = hpx::parallel::util::compare_projected< + std::decay_t, std::decay_t>; + constexpr std::uint32_t nmin_sort = 24; auto nelem = end - first; // Check the special conditions if (nth == first) { - using wrapped_comp_type = - hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; RandomIt it = detail::min_element().call( - hpx::execution::seq, first, end, - wrapped_comp_type(comp, proj), hpx::identity_v); + hpx::execution::seq, first, end, HPX_FORWARD(Compare, comp), + HPX_FORWARD(Proj, proj)); if (it != first) { @@ -206,29 +206,18 @@ namespace hpx::parallel { if (nelem < nmin_sort) { - using wrapped_comp_type = - hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; detail::sort().call(hpx::execution::seq, first, end, wrapped_comp_type(comp, proj), hpx::identity_v); return; } if (level == 0) { - using wrapped_comp_type = - hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; - hpx::parallel::detail::make_heap().call( - hpx::execution::seq, first, end, - wrapped_comp_type(comp, proj), hpx::identity_v); + std::make_heap(first, end, wrapped_comp_type(comp, proj)); std::sort_heap(first, nth, wrapped_comp_type(comp, proj)); return; } // Filter the range and check which part contains the nth element - using wrapped_comp_type = - hpx::parallel::util::compare_projected, - std::decay_t>; RandomIt c_last = filter(first, end, wrapped_comp_type(comp, proj)); if (c_last == nth) From 819905858b979368539a4720c0c620633952b822 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Fri, 17 Apr 2026 03:26:14 +0530 Subject: [PATCH 06/16] Apply clang-format fixes --- .../include/hpx/parallel/algorithms/nth_element.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 08400b479d67..2043a0b7498f 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -183,8 +183,9 @@ namespace hpx::parallel { constexpr void nth_element_seq(RandomIt first, RandomIt nth, RandomIt end, std::uint32_t level, Compare&& comp, Proj&& proj) { - using wrapped_comp_type = hpx::parallel::util::compare_projected< - std::decay_t, std::decay_t>; + using wrapped_comp_type = + hpx::parallel::util::compare_projected, + std::decay_t>; constexpr std::uint32_t nmin_sort = 24; auto nelem = end - first; From 5590bc1c764bc28c8d2f45f565757be3e2de8470 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Fri, 17 Apr 2026 10:13:06 +0530 Subject: [PATCH 07/16] Fix macOS CI: avoid detail::min_element type-matching issue with projections --- .../include/hpx/parallel/algorithms/nth_element.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 2043a0b7498f..55d7c4449848 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -193,9 +193,13 @@ namespace hpx::parallel { // Check the special conditions if (nth == first) { + // We use wrapped_comp_type here to avoid an issue in the + // underlying detail::min_element implementation which + // incorrectly fails when projections change types on some + // platforms. RandomIt it = detail::min_element().call( - hpx::execution::seq, first, end, HPX_FORWARD(Compare, comp), - HPX_FORWARD(Proj, proj)); + hpx::execution::seq, first, end, + wrapped_comp_type(comp, proj), hpx::identity_v); if (it != first) { From 5f9a80b9567ada4a845c0c84319ce4c32db06c40 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sat, 18 Apr 2026 02:46:26 +0530 Subject: [PATCH 08/16] Fix type deduction in minmax.hpp and remove workaround in nth_element.hpp --- .../hpx/parallel/algorithms/minmax.hpp | 46 +++++++++++-------- .../hpx/parallel/algorithms/nth_element.hpp | 10 ++-- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 1329bce1d0c6..7f4e355a54cb 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -403,8 +403,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::reference>>; auto smallest = it; @@ -443,8 +444,9 @@ namespace hpx::parallel { auto smallest = *it; using element_type = - hpx::traits::proxy_value_t::value_type>; + std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -473,8 +475,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::reference>>; auto smallest = first; @@ -556,8 +559,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::reference>>; auto largest = it; @@ -595,8 +599,9 @@ namespace hpx::parallel { auto largest = *it; using element_type = - hpx::traits::proxy_value_t::value_type>; + std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -625,8 +630,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::reference>>; auto largest = first; @@ -710,8 +716,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return result; - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -751,8 +758,10 @@ namespace hpx::parallel { if (count == 1) return *it; - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::value_type>::reference>>; auto result = *it; @@ -799,8 +808,9 @@ namespace hpx::parallel { return minmax_element_result{min, max}; } - using element_type = hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type>; + using element_type = + std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 55d7c4449848..950a8fc672b7 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -193,13 +193,9 @@ namespace hpx::parallel { // Check the special conditions if (nth == first) { - // We use wrapped_comp_type here to avoid an issue in the - // underlying detail::min_element implementation which - // incorrectly fails when projections change types on some - // platforms. RandomIt it = detail::min_element().call( - hpx::execution::seq, first, end, - wrapped_comp_type(comp, proj), hpx::identity_v); + hpx::execution::seq, first, end, HPX_FORWARD(Compare, comp), + HPX_FORWARD(Proj, proj)); if (it != first) { @@ -212,7 +208,7 @@ namespace hpx::parallel { if (nelem < nmin_sort) { detail::sort().call(hpx::execution::seq, first, end, - wrapped_comp_type(comp, proj), hpx::identity_v); + HPX_FORWARD(Compare, comp), HPX_FORWARD(Proj, proj)); return; } if (level == 0) From e61e9748878f901faf89969e36b34bf9c956ddd6 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sat, 18 Apr 2026 02:54:47 +0530 Subject: [PATCH 09/16] Apply clang-format corrections to minmax.hpp --- .../hpx/parallel/algorithms/minmax.hpp | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 7f4e355a54cb..5790dd797859 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -403,9 +403,8 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto smallest = it; @@ -443,10 +442,8 @@ namespace hpx::parallel { auto smallest = *it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -475,9 +472,8 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto smallest = first; @@ -559,9 +555,8 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto largest = it; @@ -598,10 +593,8 @@ namespace hpx::parallel { auto largest = *it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -630,9 +623,8 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto largest = first; @@ -716,9 +708,8 @@ namespace hpx::parallel { if (count == 0 || count == 1) return result; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -758,10 +749,8 @@ namespace hpx::parallel { if (count == 1) return *it; - using element_type = - std::decay_t::value_type>::reference>>; + using element_type = std::decay_t::reference>>; auto result = *it; @@ -808,9 +797,8 @@ namespace hpx::parallel { return minmax_element_result{min, max}; } - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From 3ad02bef0f4b398b0cab9c1f11ea1a8d122fe00d Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sat, 18 Apr 2026 03:03:14 +0530 Subject: [PATCH 10/16] Fix clang-format in minmax.hpp after CI failure --- .../hpx/parallel/algorithms/minmax.hpp | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 5790dd797859..b06a82f7000b 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -403,8 +403,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto smallest = it; @@ -442,8 +443,9 @@ namespace hpx::parallel { auto smallest = *it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -472,8 +474,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto smallest = first; @@ -555,8 +558,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto largest = it; @@ -593,8 +597,9 @@ namespace hpx::parallel { auto largest = *it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -623,8 +628,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto largest = first; @@ -708,8 +714,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return result; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -749,8 +756,9 @@ namespace hpx::parallel { if (count == 1) return *it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto result = *it; @@ -797,8 +805,9 @@ namespace hpx::parallel { return minmax_element_result{min, max}; } - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From c86066360cdfd6701d3112376b23548b127886c5 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sat, 18 Apr 2026 03:13:51 +0530 Subject: [PATCH 11/16] Fix clang-format in minmax.hpp (final attempt) --- .../hpx/parallel/algorithms/minmax.hpp | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index b06a82f7000b..ea7e2a666757 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -403,9 +403,8 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto smallest = it; @@ -443,9 +442,8 @@ namespace hpx::parallel { auto smallest = *it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -558,9 +556,8 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto largest = it; @@ -597,9 +594,8 @@ namespace hpx::parallel { auto largest = *it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -628,9 +624,8 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto largest = first; @@ -714,9 +709,8 @@ namespace hpx::parallel { if (count == 0 || count == 1) return result; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -756,9 +750,8 @@ namespace hpx::parallel { if (count == 1) return *it; - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; auto result = *it; @@ -805,9 +798,8 @@ namespace hpx::parallel { return minmax_element_result{min, max}; } - using element_type = - std::decay_t::reference>>; + using element_type = std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From e9938176f87f58dec3c7b806c9289616ac9c87a6 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sat, 18 Apr 2026 03:27:47 +0530 Subject: [PATCH 12/16] Automated clang-format fix using opt/homebrew/bin/clang-format --- .../hpx/parallel/algorithms/minmax.hpp | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index ea7e2a666757..8fbb9576926b 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -442,8 +442,9 @@ namespace hpx::parallel { auto smallest = *it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -594,8 +595,9 @@ namespace hpx::parallel { auto largest = *it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -624,8 +626,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto largest = first; @@ -750,8 +753,9 @@ namespace hpx::parallel { if (count == 1) return *it; - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; auto result = *it; @@ -798,8 +802,9 @@ namespace hpx::parallel { return minmax_element_result{min, max}; } - using element_type = std::decay_t::reference>>; + using element_type = + std::decay_t::reference>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From 30cf465a9d21e86b5b13808184cb103b7cc26957 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sat, 18 Apr 2026 15:58:25 +0530 Subject: [PATCH 13/16] Refine element_type deduction to handle HPX proxies using proxy_value_t --- .../hpx/parallel/algorithms/minmax.hpp | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 8fbb9576926b..09f4286d3c70 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -403,8 +403,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = std::decay_t::reference>>; + using element_type = hpx::traits::proxy_value_t< + std::decay_t::reference>>>; auto smallest = it; @@ -442,9 +443,9 @@ namespace hpx::parallel { auto smallest = *it; - using element_type = + using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>; + typename std::iterator_traits::reference>>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -473,9 +474,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = + using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>; + typename std::iterator_traits::reference>>>; auto smallest = first; @@ -557,8 +558,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return it; - using element_type = std::decay_t::reference>>; + using element_type = hpx::traits::proxy_value_t< + std::decay_t::reference>>>; auto largest = it; @@ -595,9 +597,9 @@ namespace hpx::parallel { auto largest = *it; - using element_type = + using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>; + typename std::iterator_traits::reference>>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -626,9 +628,9 @@ namespace hpx::parallel { if (first == last) return first; - using element_type = + using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>; + typename std::iterator_traits::reference>>>; auto largest = first; @@ -712,8 +714,9 @@ namespace hpx::parallel { if (count == 0 || count == 1) return result; - using element_type = std::decay_t::reference>>; + using element_type = hpx::traits::proxy_value_t< + std::decay_t::reference>>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -753,9 +756,9 @@ namespace hpx::parallel { if (count == 1) return *it; - using element_type = + using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>; + typename std::iterator_traits::reference>>>; auto result = *it; @@ -802,9 +805,9 @@ namespace hpx::parallel { return minmax_element_result{min, max}; } - using element_type = + using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>; + typename std::iterator_traits::reference>>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From 0e2e53e664004cdc298a5a35d422030bd3e40fe5 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sun, 19 Apr 2026 19:35:56 +0530 Subject: [PATCH 14/16] Remove redundant typename keywords as requested by maintainer --- .../include/hpx/parallel/algorithms/minmax.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 09f4286d3c70..c3906f99bc6c 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -405,7 +405,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; auto smallest = it; @@ -445,7 +445,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -476,7 +476,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; auto smallest = first; @@ -560,7 +560,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; auto largest = it; @@ -599,7 +599,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -630,7 +630,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; auto largest = first; @@ -716,7 +716,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -758,7 +758,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; auto result = *it; @@ -807,7 +807,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + std::iterator_traits::reference>>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From 865d4180fd173bcfaea5bd2c4a64af2d31af679d Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Sun, 19 Apr 2026 19:45:35 +0530 Subject: [PATCH 15/16] Restore typename keywords (strictly required for CI builds) --- .../include/hpx/parallel/algorithms/minmax.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index c3906f99bc6c..09f4286d3c70 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -405,7 +405,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; auto smallest = it; @@ -445,7 +445,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -476,7 +476,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; auto smallest = first; @@ -560,7 +560,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; auto largest = it; @@ -599,7 +599,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -630,7 +630,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; auto largest = first; @@ -716,7 +716,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -758,7 +758,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; auto result = *it; @@ -807,7 +807,7 @@ namespace hpx::parallel { using element_type = hpx::traits::proxy_value_t< std::decay_t::reference>>>; + typename std::iterator_traits::reference>>>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); From a4b4a86938b67aab000062b5d8d87a9065e83927 Mon Sep 17 00:00:00 2001 From: Aneek22112007 Date: Mon, 20 Apr 2026 00:24:20 +0530 Subject: [PATCH 16/16] Separate core fix into independent PR and restore temporary workaround --- .../hpx/parallel/algorithms/minmax.hpp | 33 ++++++++----------- .../hpx/parallel/algorithms/nth_element.hpp | 4 +-- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 09f4286d3c70..1329bce1d0c6 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -404,8 +404,7 @@ namespace hpx::parallel { return it; using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; auto smallest = it; @@ -443,9 +442,9 @@ namespace hpx::parallel { auto smallest = *it; - using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + using element_type = + hpx::traits::proxy_value_t::value_type>; element_type value = HPX_INVOKE(proj, *smallest); util::loop_n>( @@ -475,8 +474,7 @@ namespace hpx::parallel { return first; using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; auto smallest = first; @@ -559,8 +557,7 @@ namespace hpx::parallel { return it; using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; auto largest = it; @@ -597,9 +594,9 @@ namespace hpx::parallel { auto largest = *it; - using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + using element_type = + hpx::traits::proxy_value_t::value_type>; element_type value = HPX_INVOKE(proj, *largest); util::loop_n>( @@ -629,8 +626,7 @@ namespace hpx::parallel { return first; using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; auto largest = first; @@ -715,8 +711,7 @@ namespace hpx::parallel { return result; using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; element_type min_value = HPX_INVOKE(proj, *it); element_type max_value = min_value; @@ -757,8 +752,7 @@ namespace hpx::parallel { return *it; using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; auto result = *it; @@ -806,8 +800,7 @@ namespace hpx::parallel { } using element_type = hpx::traits::proxy_value_t< - std::decay_t::reference>>>; + typename std::iterator_traits::value_type>; element_type min_value = HPX_INVOKE(proj, *min); element_type max_value = HPX_INVOKE(proj, *max); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp index 950a8fc672b7..a8c31f63dcbc 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/nth_element.hpp @@ -194,8 +194,8 @@ namespace hpx::parallel { if (nth == first) { RandomIt it = detail::min_element().call( - hpx::execution::seq, first, end, HPX_FORWARD(Compare, comp), - HPX_FORWARD(Proj, proj)); + hpx::execution::seq, first, end, + wrapped_comp_type(comp, proj), hpx::identity_v); if (it != first) {