[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel std::min_element() based on parallel reduce (PR #219876)

Michael G. Kazakov via libcxx-commits libcxx-commits at lists.llvm.org
Thu Sep 3 23:39:51 PDT 2026


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/219876

>From a02053e1685e99fe393f8fa0bfec7f76ec02fba6 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 31 Aug 2026 09:32:10 +0500
Subject: [PATCH 1/4] Implementation of min_element and max_element

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__algorithm/pstl.h             |  50 ++++++++
 libcxx/include/__pstl/backend_fwd.h           |  12 ++
 libcxx/include/__pstl/backends/default.h      |  24 ++++
 libcxx/include/__pstl/backends/libdispatch.h  |   5 +
 libcxx/include/__pstl/backends/serial.h       |  10 ++
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/min_element.h |  84 +++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 .../pstl.iterator-requirements.verify.cpp     |   7 ++
 .../algorithms/pstl.nodiscard.verify.cpp      |   8 ++
 .../alg.min.max/pstl.max_element.pass.cpp     | 111 +++++++++++++++++
 .../pstl.max_element_comp.pass.cpp            | 113 +++++++++++++++++
 .../alg.min.max/pstl.min_element.pass.cpp     | 111 +++++++++++++++++
 .../pstl.min_element_comp.pass.cpp            | 114 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  20 +++
 16 files changed, 678 insertions(+)
 create mode 100644 libcxx/include/__pstl/cpu_algos/min_element.h
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f0caccdc94194..40fd1554d7443 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -698,6 +698,7 @@ set(files
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
+  __pstl/cpu_algos/min_element.h
   __pstl/cpu_algos/mismatch.h
   __pstl/cpu_algos/reverse.h
   __pstl/cpu_algos/search.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index a8ef6ddae9b63..2ecfb687e8c38 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -139,6 +139,56 @@ count(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+max_element(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "max_element requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__max_element, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), less<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+max_element(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "max_element requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__max_element, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+min_element(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "min_element requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__min_element, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), less<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+min_element(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "min_element requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__min_element, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator1,
           class _ForwardIterator2,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 45e570c51aed9..c7e6acb07144f 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -194,6 +194,18 @@ struct __merge;
 //                       _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                       _ForwardOutIterator __result, _Comp __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __max_element;
+// template <class _Policy, class _ForwardIterator, class _Compare>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __min_element;
+// template <class _Policy, class _ForwardIterator, class _Compare>
+// optional<_ForwardIterator>
+// operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __search_n;
 // template <class _Policy, class _ForwardIterator, class _Size, class _T, class _Predicate>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index ecf5b3bc3d9eb..ee076cd47c993 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -70,6 +70,10 @@ namespace __pstl {
 // - is_partitioned
 // - find_first_of
 //
+// min_element family
+// ---------------
+// - max_element
+//
 // mismatch family
 // ---------------
 // - adjacent_find
@@ -246,6 +250,26 @@ struct __find_first_of<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+//////////////////////////////////////////////////////////////
+// min_element family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __max_element<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _Compare>
+  optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) const noexcept {
+    using _MinElement = __dispatch<__min_element, __current_configuration, _ExecutionPolicy>;
+    using _Ref        = __iterator_reference<_ForwardIterator>;
+    // Express max_element via min_element by replacing the comparison
+    // "lhs OP rhs" with "rhs OP lhs".
+    return _MinElement()(
+        __policy, std::move(__first), std::move(__last), [__comp = std::move(__comp)](_Ref __lhs, _Ref __rhs) {
+          return __comp(__rhs, __lhs);
+        });
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // mismatch family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index c5efbd46298fb..858666058b191 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -40,6 +40,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/min_element.h>
 #include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/reverse.h>
 #include <__pstl/cpu_algos/search.h>
@@ -374,6 +375,10 @@ template <class _ExecutionPolicy>
 struct __merge<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__libdispatch_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __min_element<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_min_element<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __mismatch<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_mismatch<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index c67f0b3331ac2..d5a89c66e33e4 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
+#include <__algorithm/min_element.h>
 #include <__algorithm/mismatch.h>
 #include <__algorithm/reverse.h>
 #include <__algorithm/search.h>
@@ -109,6 +110,15 @@ struct __merge<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __min_element<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _Compare>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) const noexcept {
+    return std::min_element(std::move(__first), std::move(__last), std::move(__comp));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __reverse<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _BidirectionalIterator>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index 06beb04e4d885..99f52658578bd 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -19,6 +19,7 @@
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
+#include <__pstl/cpu_algos/min_element.h>
 #include <__pstl/cpu_algos/mismatch.h>
 #include <__pstl/cpu_algos/reverse.h>
 #include <__pstl/cpu_algos/search.h>
@@ -105,6 +106,10 @@ template <class _ExecutionPolicy>
 struct __merge<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_merge<__std_thread_backend_tag, _ExecutionPolicy> {};
 
+template <class _ExecutionPolicy>
+struct __min_element<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_min_element<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __mismatch<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_mismatch<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/min_element.h b/libcxx/include/__pstl/cpu_algos/min_element.h
new file mode 100644
index 0000000000000..af136c10c5469
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/min_element.h
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___PSTL_CPU_ALGOS_MIN_ELEMENT_H
+#define _LIBCPP___PSTL_CPU_ALGOS_MIN_ELEMENT_H
+
+#include <__algorithm/min_element.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__optional/optional.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_min_element {
+  template <class _Policy, class _ForwardIterator, class _Compare>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last, _Compare __comp) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+      if (__first == __last) {
+        return __last; // nothing to do
+      }
+
+      _ForwardIterator __init = __first;
+      ++__first;
+      if (__first == __last) {
+        return __init; // the only element is the minimum
+      }
+
+      // A reduction that returns an iterator pointing to the lowest element, left bias in case of a tie
+      auto __iter_reduce = [&__comp](_ForwardIterator __lhs, _ForwardIterator __rhs) {
+        return __comp(*__rhs, *__lhs) ? __rhs : __lhs;
+      };
+
+      // Perform a parallel reduction of iterators [first+1, last) with 'first' as init.
+      return __cpu_traits<_Backend>::__transform_reduce(
+          std::move(__first),
+          std::move(__last),
+          __identity{},      // No transformations
+          std::move(__init), // Use the first iterator as the init element
+          __iter_reduce,     // Reduction of 2 elements
+          [&__iter_reduce, &__comp](auto __brick_first, auto __brick_last, auto __brick_init) {
+            // Reduction of an iterage range + init element: use the serial version to find the minimum among
+            // the iterators and then reduce with the init element.
+            return __iter_reduce(__brick_init, std::min_element(__brick_first, __brick_last, __comp));
+          });
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      return std::min_element(std::move(__first), std::move(__last), std::move(__comp));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_MIN_ELEMENT_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9ce0c49b8b2b3..e12a6888ca468 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2438,6 +2438,9 @@ module std {
       module merge {
         header "__pstl/cpu_algos/merge.h"
       }
+      module min_element {
+        header "__pstl/cpu_algos/min_element.h"
+      }
       module mismatch {
         header "__pstl/cpu_algos/mismatch.h"
       }
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 7e9fa5cfacc30..32a454ce2f89c 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -168,6 +168,13 @@ void f(non_forward_iterator non_fwd,
     (void)std::merge(pol, it, it, it, it, non_output, pred);    // expected-error@*:* {{static assertion failed: merge}}
   }
 
+  {
+    (void)std::max_element(pol, non_fwd, non_fwd);       // expected-error@*:* {{static assertion failed: max_element}}
+    (void)std::max_element(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: max_element}}
+    (void)std::min_element(pol, non_fwd, non_fwd);       // expected-error@*:* {{static assertion failed: min_element}}
+    (void)std::min_element(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: min_element}}
+  }
+
   {
     (void)std::mismatch(pol, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: mismatch}}
     (void)std::mismatch(pol, it, it, non_fwd);      // expected-error@*:* {{static assertion failed: mismatch}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 61758770102f3..d163d268a60dd 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -68,6 +68,14 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::max_element(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::max_element(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::min_element(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::min_element(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::mismatch(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
new file mode 100644
index 0000000000000..cba23411f6743
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator>
+//   ForwardIterator
+//   max_element(ExecutionPolicy&& exec,
+//               ForwardIterator first, ForwardIterator last);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(max_element);
+
+static_assert(sfinae_test_max_element<int, int*, int*>);
+static_assert(!sfinae_test_max_element<std::execution::parallel_policy, int*, int*>);
+
+std::mt19937 randomness;
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      int a[]  = {0};
+      auto res = std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    { // Empty
+      int a[] = {0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+    }
+    { // Single
+      int a[] = {0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Two elements, second max
+      int a[] = {0, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Two elements, first max
+      int a[] = {1, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Check for maximum among iotaed and shuffled elements
+      auto verify_max = [&](Iter first, Iter last) {
+        Iter i = std::max_element(policy, first, last);
+        if (first != last) {
+          for (Iter j = first; j != last; ++j)
+            assert(!(*j > *i));
+        } else {
+          assert(i == last);
+        }
+      };
+      auto test_n_elem = [&](int N) {
+        std::vector<int> a(N);
+        std::iota(a.begin(), a.end(), 0);
+        std::shuffle(a.begin(), a.end(), randomness);
+        verify_max(Iter(a.data()), Iter(a.data() + N));
+      };
+      test_n_elem(3);
+      test_n_elem(4);
+      test_n_elem(5);
+      test_n_elem(10);
+      test_n_elem(100);
+      test_n_elem(1000);
+    }
+    { // Check that the first one among equals is returned
+      int a[1073];
+      // Fill with 0 0 0 0 .... 2 1 2 1 ...
+      for (std::size_t i = 0; i < std::size(a); ++i)
+        a[i] = (i < 473) ? 0 : (i % 2 ? 2 : 1);
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 473));
+    }
+    { // Check that the maximum at sampled locations is found
+      int a[1073];
+      std::fill(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](size_t i) {
+        a[i] = 1;
+        assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i));
+        a[i] = 0;
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
new file mode 100644
index 0000000000000..ba5a980f9becb
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator, class Compare>
+//   ForwardIterator
+//   max_element(ExecutionPolicy&& exec,
+//               ForwardIterator first, ForwardIterator last,
+//               Compare comp);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(max_element);
+
+static_assert(sfinae_test_max_element<int, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_max_element<std::execution::parallel_policy, int*, int*, bool (*)(int, int)>);
+
+std::mt19937 randomness;
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    std::greater<int> comp;
+    { // Check the return type
+      int a[]  = {0};
+      auto res = std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp);
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    { // Empty
+      int a[] = {0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::begin(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Single
+      int a[] = {0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Two elements, first max
+      int a[] = {0, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Two elements, second max
+      int a[] = {1, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Check for maximum among iotaed and shuffled elements
+      auto verify_max = [&](Iter first, Iter last) {
+        Iter i = std::max_element(policy, first, last, comp);
+        if (first != last) {
+          for (Iter j = first; j != last; ++j)
+            assert(!(*j < *i));
+        } else {
+          assert(i == last);
+        }
+      };
+      auto test_n_elem = [&](int N) {
+        std::vector<int> a(N);
+        std::iota(a.begin(), a.end(), 0);
+        std::shuffle(a.begin(), a.end(), randomness);
+        verify_max(Iter(a.data()), Iter(a.data() + N));
+      };
+      test_n_elem(3);
+      test_n_elem(4);
+      test_n_elem(5);
+      test_n_elem(10);
+      test_n_elem(100);
+      test_n_elem(1000);
+    }
+    { // Check that the first one among equals is returned
+      int a[1073];
+      // Fill with 2 2 2 2 .... 0 1 0 1 ...
+      for (std::size_t i = 0; i < std::size(a); ++i)
+        a[i] = (i < 473) ? 2 : (i % 2 ? 0 : 1);
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 473));
+    }
+    { // Check that the maximum at sampled locations is found
+      int a[1073];
+      std::fill(std::begin(a), std::end(a), 1);
+      runway_sample(std::size(a), [&](size_t i) {
+        a[i] = 0;
+        assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + i));
+        a[i] = 1;
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
new file mode 100644
index 0000000000000..0aaeddcdfa19d
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator>
+//   ForwardIterator
+//   min_element(ExecutionPolicy&& exec,
+//               ForwardIterator first, ForwardIterator last);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(min_element);
+
+static_assert(sfinae_test_min_element<int, int*, int*>);
+static_assert(!sfinae_test_min_element<std::execution::parallel_policy, int*, int*>);
+
+std::mt19937 randomness;
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      int a[]  = {0};
+      auto res = std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)));
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    { // Empty
+      int a[] = {0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::begin(a))) == Iter(std::begin(a)));
+    }
+    { // Single
+      int a[] = {0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Two elements, first min
+      int a[] = {0, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Two elements, second min
+      int a[] = {1, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Check for minimum among iotaed and shuffled elements
+      auto verify_min = [&](Iter first, Iter last) {
+        Iter i = std::min_element(policy, first, last);
+        if (first != last) {
+          for (Iter j = first; j != last; ++j)
+            assert(!(*j < *i));
+        } else {
+          assert(i == last);
+        }
+      };
+      auto test_n_elem = [&](int N) {
+        std::vector<int> a(N);
+        std::iota(a.begin(), a.end(), 0);
+        std::shuffle(a.begin(), a.end(), randomness);
+        verify_min(Iter(a.data()), Iter(a.data() + N));
+      };
+      test_n_elem(3);
+      test_n_elem(4);
+      test_n_elem(5);
+      test_n_elem(10);
+      test_n_elem(100);
+      test_n_elem(1000);
+    }
+    { // Check that the first one among equals is returned
+      int a[1073];
+      // Fill with 2 2 2 2 .... 0 1 0 1 ...
+      for (std::size_t i = 0; i < std::size(a); ++i)
+        a[i] = (i < 473) ? 2 : (i % 2 ? 0 : 1);
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 473));
+    }
+    { // Check that the minimum at sampled locations is found
+      int a[1073];
+      std::fill(std::begin(a), std::end(a), 1);
+      runway_sample(std::size(a), [&](size_t i) {
+        a[i] = 0;
+        assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i));
+        a[i] = 1;
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
new file mode 100644
index 0000000000000..7391b941425ed
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator, class Compare>
+//   ForwardIterator
+//   min_element(ExecutionPolicy&& exec,
+//               ForwardIterator first, ForwardIterator last,
+//               Compare comp);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(min_element);
+
+static_assert(sfinae_test_min_element<int, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_min_element<std::execution::parallel_policy, int*, int*, bool (*)(int, int)>);
+
+std::mt19937 randomness;
+
+template <class Iter>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    std::greater<int> comp;
+    { // Check the return type
+      int a[]  = {0};
+      auto res = std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp);
+      static_assert(std::is_same_v<decltype(res), Iter>);
+    }
+    { // Empty
+      int a[] = {0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::begin(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Single
+      int a[] = {0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Two elements, second min
+      int a[] = {0, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Two elements, first min
+      int a[] = {1, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Check for minimum among iotaed and shuffled elements
+      auto verify_min = [&](Iter first, Iter last) {
+        Iter i = std::min_element(policy, first, last, comp);
+        if (first != last) {
+          for (Iter j = first; j != last; ++j)
+            assert(!(*j > *i));
+        } else {
+          assert(i == last);
+        }
+      };
+      auto test_n_elem = [&](int N) {
+        std::vector<int> a(N);
+        std::iota(a.begin(), a.end(), 0);
+        std::shuffle(a.begin(), a.end(), randomness);
+        verify_min(Iter(a.data()), Iter(a.data() + N));
+      };
+      test_n_elem(3);
+      test_n_elem(4);
+      test_n_elem(5);
+      test_n_elem(10);
+      test_n_elem(100);
+      test_n_elem(1000);
+    }
+    { // Check that the first one among equals is returned
+      int a[1073];
+      // Fill with 0 0 0 0 .... 2 1 2 1 ...
+      for (std::size_t i = 0; i < std::size(a); ++i)
+        a[i] = (i < 473) ? 0 : (i % 2 ? 2 : 1);
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 473));
+    }
+    { // Check that the minimum at sampled locations is found
+      int a[1073];
+      std::fill(std::begin(a), std::end(a), 0);
+      runway_sample(std::size(a), [&](size_t i) {
+        a[i] = 1;
+        assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + i));
+        a[i] = 0;
+      });
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, TestIteratorWithPolicies<Test>{});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index b1d6d831ef61f..6f173f1cbaa0e 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -256,6 +256,26 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // max_element(first, last)
+        assert_non_throwing([=, &policy] { (void)std::max_element(policy, std::move(first1), std::move(last1)); });
+
+        // max_element(first, last, comp)
+        assert_non_throwing([=, &policy] {
+          (void)std::max_element(policy, std::move(first1), std::move(last1), compare);
+        });
+
+        // min_element(first, last)
+        assert_non_throwing([=, &policy] { (void)std::min_element(policy, std::move(first1), std::move(last1)); });
+
+        // min_element(first, last, comp)
+        assert_non_throwing([=, &policy] {
+          (void)std::min_element(policy, std::move(first1), std::move(last1), compare);
+        });
+      }
+
       {
         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
 

>From be86de1daa4c0cd244128f8d27ca6a3496b6c138 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 31 Aug 2026 11:26:33 +0500
Subject: [PATCH 2/4] Missing include

---
 .../alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp       | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
index ba5a980f9becb..f112a04372dfd 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
@@ -21,6 +21,7 @@
 #include <cstddef>
 #include <algorithm>
 #include <cassert>
+#include <functional>
 #include <iterator>
 #include <numeric>
 #include <random>

>From 9c9b56867d319d71c466d7051093119cfee9d352 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Fri, 4 Sep 2026 06:44:57 +0100
Subject: [PATCH 3/4] Moved mt19937 into the function bodies

---
 .../alg.sorting/alg.min.max/pstl.max_element.pass.cpp          | 3 +--
 .../alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp     | 3 +--
 .../alg.sorting/alg.min.max/pstl.min_element.pass.cpp          | 3 +--
 .../alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp     | 3 +--
 4 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
index cba23411f6743..d0b7c32fa9c63 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
@@ -36,8 +36,6 @@ EXECUTION_POLICY_SFINAE_TEST(max_element);
 static_assert(sfinae_test_max_element<int, int*, int*>);
 static_assert(!sfinae_test_max_element<std::execution::parallel_policy, int*, int*>);
 
-std::mt19937 randomness;
-
 template <class Iter>
 struct Test {
   template <class ExecutionPolicy>
@@ -64,6 +62,7 @@ struct Test {
       assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
     }
     { // Check for maximum among iotaed and shuffled elements
+      std::mt19937 randomness;
       auto verify_max = [&](Iter first, Iter last) {
         Iter i = std::max_element(policy, first, last);
         if (first != last) {
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
index f112a04372dfd..df97f03cd4ca2 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
@@ -38,8 +38,6 @@ EXECUTION_POLICY_SFINAE_TEST(max_element);
 static_assert(sfinae_test_max_element<int, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_max_element<std::execution::parallel_policy, int*, int*, bool (*)(int, int)>);
 
-std::mt19937 randomness;
-
 template <class Iter>
 struct Test {
   template <class ExecutionPolicy>
@@ -67,6 +65,7 @@ struct Test {
       assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
     }
     { // Check for maximum among iotaed and shuffled elements
+      std::mt19937 randomness;
       auto verify_max = [&](Iter first, Iter last) {
         Iter i = std::max_element(policy, first, last, comp);
         if (first != last) {
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
index 0aaeddcdfa19d..3f528930840fb 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
@@ -36,8 +36,6 @@ EXECUTION_POLICY_SFINAE_TEST(min_element);
 static_assert(sfinae_test_min_element<int, int*, int*>);
 static_assert(!sfinae_test_min_element<std::execution::parallel_policy, int*, int*>);
 
-std::mt19937 randomness;
-
 template <class Iter>
 struct Test {
   template <class ExecutionPolicy>
@@ -64,6 +62,7 @@ struct Test {
       assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
     }
     { // Check for minimum among iotaed and shuffled elements
+      std::mt19937 randomness;
       auto verify_min = [&](Iter first, Iter last) {
         Iter i = std::min_element(policy, first, last);
         if (first != last) {
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
index 7391b941425ed..12ec46a71ff21 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
@@ -38,8 +38,6 @@ EXECUTION_POLICY_SFINAE_TEST(min_element);
 static_assert(sfinae_test_min_element<int, int*, int*, bool (*)(int, int)>);
 static_assert(!sfinae_test_min_element<std::execution::parallel_policy, int*, int*, bool (*)(int, int)>);
 
-std::mt19937 randomness;
-
 template <class Iter>
 struct Test {
   template <class ExecutionPolicy>
@@ -67,6 +65,7 @@ struct Test {
       assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
     }
     { // Check for minimum among iotaed and shuffled elements
+      std::mt19937 randomness;
       auto verify_min = [&](Iter first, Iter last) {
         Iter i = std::min_element(policy, first, last, comp);
         if (first != last) {

>From 9d19d728568d6bd99742fc10dea1b5afb9684c0b Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Fri, 4 Sep 2026 07:39:34 +0100
Subject: [PATCH 4/4] Added more test cases, explicitly handle an edge case of
 empty bricks for reduction

---
 libcxx/include/__pstl/cpu_algos/min_element.h |  6 +++-
 .../alg.min.max/pstl.max_element.pass.cpp     | 36 +++++++++++++++++++
 .../pstl.max_element_comp.pass.cpp            | 36 +++++++++++++++++++
 .../alg.min.max/pstl.min_element.pass.cpp     | 36 +++++++++++++++++++
 .../pstl.min_element_comp.pass.cpp            | 36 +++++++++++++++++++
 5 files changed, 149 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__pstl/cpu_algos/min_element.h b/libcxx/include/__pstl/cpu_algos/min_element.h
index af136c10c5469..3f61d8c2b4b42 100644
--- a/libcxx/include/__pstl/cpu_algos/min_element.h
+++ b/libcxx/include/__pstl/cpu_algos/min_element.h
@@ -65,7 +65,11 @@ struct __cpu_parallel_min_element {
           [&__iter_reduce, &__comp](auto __brick_first, auto __brick_last, auto __brick_init) {
             // Reduction of an iterage range + init element: use the serial version to find the minimum among
             // the iterators and then reduce with the init element.
-            return __iter_reduce(__brick_init, std::min_element(__brick_first, __brick_last, __comp));
+            // Atm __transform_reduce can give empty bricks in edge cases, handle them explicitly until the contract is
+            // tightened.
+            return __brick_first == __brick_last
+                     ? __brick_init
+                     : __iter_reduce(__brick_init, std::min_element(__brick_first, __brick_last, __comp));
           });
     } else {
       // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
index d0b7c32fa9c63..188f5d9e5f6b4 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element.pass.cpp
@@ -61,6 +61,42 @@ struct Test {
       int a[] = {1, 0};
       assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
     }
+    { // Three elements, first max
+      int a[] = {2, 1, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Three elements, middle max
+      int a[] = {0, 2, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Three elements, last max
+      int a[] = {1, 0, 2};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    { // Four elements, first max
+      int a[] = {3, 0, 2, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Four elements, all equal
+      int a[] = {1, 1, 1, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Four elements, middle max
+      int a[] = {0, 3, 2, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, middle max
+      int a[] = {0, 2, 3, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    { // Four elements, middle max and equal to consequent elements
+      int a[] = {0, 3, 3, 3};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, last max
+      int a[] = {0, 1, 2, 3};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+    }
     { // Check for maximum among iotaed and shuffled elements
       std::mt19937 randomness;
       auto verify_max = [&](Iter first, Iter last) {
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
index df97f03cd4ca2..4f0e0b475e4bf 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.max_element_comp.pass.cpp
@@ -64,6 +64,42 @@ struct Test {
       int a[] = {1, 0};
       assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
     }
+    { // Three elements, first max
+      int a[] = {0, 1, 2};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Three elements, middle max
+      int a[] = {1, 0, 2};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Three elements, middle max and equal to last element
+      int a[] = {1, 0, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Three elements, last max
+      int a[] = {2, 1, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 2));
+    }
+    { // Four elements, first max
+      int a[] = {0, 1, 2, 3};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Four elements, all equal
+      int a[] = {1, 1, 1, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Four elements, middle max
+      int a[] = {3, 0, 2, 1};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, middle max and equal to consequent elements
+      int a[] = {3, 0, 0, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, last max
+      int a[] = {3, 2, 1, 0};
+      assert(std::max_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 3));
+    }
     { // Check for maximum among iotaed and shuffled elements
       std::mt19937 randomness;
       auto verify_max = [&](Iter first, Iter last) {
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
index 3f528930840fb..59587f65a1efd 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element.pass.cpp
@@ -61,6 +61,42 @@ struct Test {
       int a[] = {1, 0};
       assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
     }
+    { // Three elements, first min
+      int a[] = {0, 1, 2};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Three elements, middle min
+      int a[] = {1, 0, 2};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Three elements, middle min and equal to last element
+      int a[] = {1, 0, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Three elements, last min
+      int a[] = {2, 1, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+    }
+    { // Four elements, first min
+      int a[] = {0, 1, 2, 3};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Four elements, all equal
+      int a[] = {1, 1, 1, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a)));
+    }
+    { // Four elements, middle min
+      int a[] = {3, 0, 2, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, middle min and equal to consequent elements
+      int a[] = {3, 0, 0, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, last min
+      int a[] = {3, 2, 1, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+    }
     { // Check for minimum among iotaed and shuffled elements
       std::mt19937 randomness;
       auto verify_min = [&](Iter first, Iter last) {
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
index 12ec46a71ff21..f24a37e08e0ad 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.min.max/pstl.min_element_comp.pass.cpp
@@ -64,6 +64,42 @@ struct Test {
       int a[] = {1, 0};
       assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
     }
+    { // Three elements, first min
+      int a[] = {2, 1, 0};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Three elements, middle min
+      int a[] = {0, 2, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Three elements, last min
+      int a[] = {1, 0, 2};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 2));
+    }
+    { // Four elements, first min
+      int a[] = {3, 0, 2, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Four elements, all equal
+      int a[] = {1, 1, 1, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a)));
+    }
+    { // Four elements, middle min
+      int a[] = {0, 3, 2, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, middle min
+      int a[] = {0, 2, 3, 1};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 2));
+    }
+    { // Four elements, middle min and equal to consequent elements
+      int a[] = {0, 3, 3, 3};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 1));
+    }
+    { // Four elements, last min
+      int a[] = {0, 1, 2, 3};
+      assert(std::min_element(policy, Iter(std::begin(a)), Iter(std::end(a)), comp) == Iter(std::begin(a) + 3));
+    }
     { // Check for minimum among iotaed and shuffled elements
       std::mt19937 randomness;
       auto verify_min = [&](Iter first, Iter last) {



More information about the libcxx-commits mailing list