[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel std::is_sorted_until() based on std::adjacent_find() (PR #213445)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Aug 1 06:25:02 PDT 2026
https://github.com/mikekazakov created https://github.com/llvm/llvm-project/pull/213445
Part of #99938.
>From 95fc8431f4654a3622223301c1e89bd5999e8062 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 1 Aug 2026 14:22:52 +0100
Subject: [PATCH] Implemented is_sorted_until and rebased is_sorted onto it
---
libcxx/include/__algorithm/pstl.h | 25 ++
libcxx/include/__pstl/backend_fwd.h | 6 +
libcxx/include/__pstl/backends/default.h | 68 ++---
.../pstl.iterator-requirements.verify.cpp | 12 +
.../algorithms/pstl.nodiscard.verify.cpp | 4 +
.../is.sorted/pstl.is_sorted_until.pass.cpp | 245 +++++++++++++++++
.../pstl.is_sorted_until_comp.pass.cpp | 252 ++++++++++++++++++
.../pstl.exception_handling.pass.cpp | 8 +
8 files changed, 590 insertions(+), 30 deletions(-)
create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index bf6c2a030d348..532f62ad3047a 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -909,6 +909,31 @@ is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterato
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
+is_sorted_until(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "is_sorted_until requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__is_sorted_until, __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 _Comp,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+is_sorted_until(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp __comp) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "is_sorted_until requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__is_sorted_until, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
+}
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index eaa987a2a3c94..48d68a9650d70 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -317,6 +317,12 @@ struct __is_sorted;
// optional<bool>
// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __is_sorted_until;
+// template <class _Policy, class _ForwardIterator, class _Comp>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __adjacent_difference;
// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryOperation>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index df287bdb67dc1..809f9351b25cd 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -69,6 +69,8 @@ namespace __pstl {
// mismatch family
// ---------------
// - adjacent_find
+// - is_sorted
+// - is_sorted_until
// - lexicographical_compare
// - mismatch_3leg
//
@@ -96,7 +98,6 @@ namespace __pstl {
// - count
// - equal(3 legs)
// - equal
-// - is_sorted
// - reduce
//
// transform and transform_binary family
@@ -322,6 +323,42 @@ struct __adjacent_find<__default_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __is_sorted_until<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Comp>
+ optional<_ForwardIterator>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
+ using _AdjacentFind = __dispatch<__adjacent_find, __current_configuration, _ExecutionPolicy>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ // Find the first pair of adjacent elements that are not in sorted order (i.e. __comp(__rhs, __lhs) is true).
+ auto __res = _AdjacentFind()(__policy, std::move(__first), __last, [&](_Ref __lhs, _Ref __rhs) {
+ return __comp(__rhs, __lhs);
+ });
+ if (!__res) {
+ return nullopt; // Failed to run the algorithm, propagate the error.
+ }
+ if (*__res == __last) {
+ return __last; // Range is sorted, return __last.
+ }
+ ++*__res; // Advance the iterator to the first unsorted element.
+ return *__res;
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Comp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
+ using _IsSortedUntil = __dispatch<__is_sorted_until, __current_configuration, _ExecutionPolicy>;
+ auto __res = _IsSortedUntil()(__policy, std::move(__first), __last, std::forward<_Comp>(__comp));
+ if (!__res) {
+ return nullopt; // Failed to run the algorithm, propagate the error.
+ }
+ return *__res == __last; // If the first unsorted element is the end of the range, the range is sorted.
+ }
+};
+
//////////////////////////////////////////////////////////////
// for_each family
//////////////////////////////////////////////////////////////
@@ -532,35 +569,6 @@ struct __reduce<__default_backend_tag, _ExecutionPolicy> {
}
};
-template <class _ExecutionPolicy>
-struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
- template <class _Policy, class _ForwardIterator, class _Comp>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
- operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
- if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
- if (__first == __last)
- return true; // Empty, sorted by definition
- _ForwardIterator __first2 = std::next(__first);
- if (__first2 == __last)
- return true; // Only one element, sorted by definition
- --__last; // Make two iterator ranges: [__first, __first + n - 1) and [__first + 1, __first + n)
- using _TransformReduce = __dispatch<__transform_reduce_binary, __current_configuration, _ExecutionPolicy>;
- using _Ref = __iterator_reference<_ForwardIterator>;
- return _TransformReduce()(
- __policy,
- std::move(__first),
- std::move(__last),
- std::move(__first2),
- true,
- std::logical_and{},
- [&](_Ref __left, _Ref __right) -> bool { return !__comp(__right, __left); });
- } else {
- // Currently anything outside bidirectional iterators has to be processed serially
- return std::is_sorted(std::move(__first), std::move(__last), std::forward<_Comp>(__comp));
- }
- }
-};
-
//////////////////////////////////////////////////////////////
// transform family
//////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 636c4cf84ab2d..c6a507a331726 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -136,6 +136,18 @@ void f(non_forward_iterator non_fwd,
pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
}
+ {
+ (void)std::is_sorted(pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: is_sorted}}
+ (void)std::is_sorted(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_sorted}}
+ }
+
+ {
+ (void)std::is_sorted_until(
+ pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: is_sorted_until}}
+ (void)std::is_sorted_until(
+ pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_sorted_until}}
+ }
+
{
(void)std::merge(pol, non_fwd, non_fwd, it, it, out); // expected-error@*:* {{static assertion failed: merge}}
(void)std::merge(pol, it, it, non_fwd, non_fwd, out); // expected-error@*:* {{static assertion failed: merge}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 0efb6ffe6b0cd..37e843eb90fe1 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -58,6 +58,10 @@ void test() {
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::is_sorted_until(std::execution::par, std::begin(a), std::end(a));
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::is_sorted_until(std::execution::par, std::begin(a), std::end(a), pred2);
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::reverse_copy(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::adjacent_difference(std::execution::par, std::begin(a), std::end(a), std::begin(b));
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
new file mode 100644
index 0000000000000..66d036f9f6773
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
@@ -0,0 +1,245 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class ExecutionPolicy, class ForwardIterator>
+// ForwardIterator is_sorted_until(ExecutionPolicy&& exec,
+// ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(is_sorted_until);
+
+static_assert(sfinae_test_is_sorted_until<int, int*, int*>);
+static_assert(!sfinae_test_is_sorted_until<std::execution::parallel_policy, int*, int*>);
+
+// The type X is provided to test that the is_sorted_until algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+ X() = delete;
+ explicit X(int i) : i_(i) {}
+ X(const X&) = delete;
+ X(X&&) = delete;
+ X& operator=(const X&) = delete;
+ X& operator=(X&&) = delete;
+ bool operator<(const X& other) const { return i_ < other.i_; }
+
+private:
+ int i_;
+};
+
+template <class Iter>
+struct Test {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // check the return types
+ int a[] = {0};
+ auto res = std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+ static_assert(std::is_same_v<decltype(res), Iter>);
+ }
+ { // empty range
+ int a[] = {0};
+ auto res = std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::begin(a)));
+ assert(res == Iter(std::begin(a)));
+ }
+ { // single element range
+ int a[] = {0};
+ auto res = std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)));
+ assert(res == Iter(std::end(a)));
+ }
+ { // two element range, equal - sorted
+ int a[] = {0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // two element range, ascending - sorted
+ int a[] = {0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // two element range, descending - not sorted
+ int a[] = {1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // two element range, equal - sorted
+ int a[] = {1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // three element range, equal - sorted
+ int a[] = {0, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // three element range, ascending - sorted
+ int a[] = {0, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // three element range, not sorted
+ int a[] = {0, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // three element range, ascending - sorted
+ int a[] = {0, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // three element range, descending - not sorted
+ int a[] = {1, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // three element range, not sorted
+ int a[] = {1, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // three element range, not sorted
+ int a[] = {1, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // three element range, equal - sorted
+ int a[] = {1, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, equal - sorted
+ int a[] = {0, 0, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, ascending - sorted
+ int a[] = {0, 0, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 0, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+ }
+ { // four element range, ascending - sorted
+ int a[] = {0, 0, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 1, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 1, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 1, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+ }
+ { // four element range, ascending - sorted
+ int a[] = {0, 1, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, descending - not sorted
+ int a[] = {1, 0, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 0, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 0, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 0, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, descending - not sorted
+ int a[] = {1, 1, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 1, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, descending - not sorted
+ int a[] = {1, 1, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+ }
+ { // four element range, equal - sorted
+ int a[] = {1, 1, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, min/max - sorted
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // four element range, min/max - not sorted
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 2));
+ }
+ { // seven element range, min/max - sorted
+ int a[] = {
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min() / 2,
+ -1,
+ 0,
+ 1,
+ std::numeric_limits<int>::max() / 2,
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::end(a)));
+ }
+ { // seven element range, min/max - not sorted
+ int a[] = {
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min() / 2,
+ 1,
+ 0,
+ -1,
+ std::numeric_limits<int>::max() / 2,
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+ }
+ { // many elements, ordering breaks at various sampled positions
+ int a[1073];
+ std::iota(std::begin(a), std::end(a), 0);
+ runway_sample(std::size(a), [&](size_t i) {
+ if (i == 0) {
+ return; // sorted range cannot be broken at the first element
+ }
+ a[i] = -1;
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + i));
+ a[i] = static_cast<int>(i);
+ });
+ }
+ }
+};
+
+template <class Iter>
+struct TestX {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ // Four elements, unsorted
+ X a[] = {X(0), X(1), X(2), X(0)};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+ assert(std::is_sorted_until(policy, Iter(std::begin(a) + 1), Iter(std::end(a))) == Iter(std::begin(a) + 3));
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::begin(a) + 3)) == Iter(std::begin(a) + 3));
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<Test>{});
+ types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+ return 0;
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
new file mode 100644
index 0000000000000..114fa8043b197
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
@@ -0,0 +1,252 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class ExecutionPolicy, class ForwardIterator, class Comp>
+// ForwardIterator is_sorted_until(ExecutionPolicy&& exec,
+// ForwardIterator first, ForwardIterator last,
+// Comp comp);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <limits>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(is_sorted_until);
+
+static_assert(sfinae_test_is_sorted_until<int, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_is_sorted_until<std::execution::parallel_policy, int*, int*, bool (*)(int, int)>);
+
+// The type X is provided to test that the is_sorted_until algorithm can be used with custom non-movable/non-copyable types.
+struct X {
+ X() = delete;
+ explicit X(int i) : i_(i) {}
+ X(const X&) = delete;
+ X(X&&) = delete;
+ X& operator=(const X&) = delete;
+ X& operator=(X&&) = delete;
+ int value() const { return i_; }
+
+private:
+ int i_;
+};
+
+struct Comp {
+ bool operator()(int lhs, int rhs) const { return lhs > rhs; }
+ bool operator()(const X& lhs, const X& rhs) const { return lhs.value() > rhs.value(); }
+};
+
+template <class Iter>
+struct Test {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ { // check the return types
+ int a[] = {0};
+ auto res = std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::begin(a)), Comp{});
+ static_assert(std::is_same_v<decltype(res), Iter>);
+ }
+ { // empty range
+ int a[] = {0};
+ auto res = std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::begin(a)), Comp{});
+ assert(res == Iter(std::begin(a)));
+ }
+ { // single element range
+ int a[] = {0};
+ auto res = std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{});
+ assert(res == Iter(std::end(a)));
+ }
+ { // two element range, equal - sorted
+ int a[] = {0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // two element range, ascending - unsorted
+ int a[] = {0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // two element range, descending - sorted
+ int a[] = {1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // two element range, equal - sorted
+ int a[] = {1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // three element range, equal - sorted
+ int a[] = {0, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // three element range, ascending - unsorted
+ int a[] = {0, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // three element range, not sorted
+ int a[] = {0, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // three element range, ascending - unsorted
+ int a[] = {0, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // three element range, descending - sorted
+ int a[] = {1, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // three element range, not sorted
+ int a[] = {1, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // three element range, sorted
+ int a[] = {1, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // three element range, equal - sorted
+ int a[] = {1, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // four element range, equal - sorted
+ int a[] = {0, 0, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // four element range, ascending - unsorted
+ int a[] = {0, 0, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 3));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 0, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, ascending - unsorted
+ int a[] = {0, 0, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 1, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 1, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, not sorted
+ int a[] = {0, 1, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, ascending - unsorted
+ int a[] = {0, 1, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // four element range, descending - sorted
+ int a[] = {1, 0, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 0, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 3));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 0, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 0, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, descending - sorted
+ int a[] = {1, 1, 0, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // four element range, not sorted
+ int a[] = {1, 1, 0, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 3));
+ }
+ { // four element range, descending - sorted
+ int a[] = {1, 1, 1, 0};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // four element range, equal - sorted
+ int a[] = {1, 1, 1, 1};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::end(a)));
+ }
+ { // four element range, min/max - unsorted
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 2));
+ }
+ { // four element range, min/max - not sorted
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // seven element range, min/max - sorted
+ int a[] = {
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min() / 2,
+ -1,
+ 0,
+ 1,
+ std::numeric_limits<int>::max() / 2,
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // seven element range, min/max - not sorted
+ int a[] = {
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min() / 2,
+ 1,
+ 0,
+ -1,
+ std::numeric_limits<int>::max() / 2,
+ std::numeric_limits<int>::max()};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 1));
+ }
+ { // many elements, ordering breaks at various sampled positions
+ int a[1073];
+ std::generate(std::begin(a), std::end(a), [n = 0]() mutable { return n--; });
+ runway_sample(std::size(a), [&](size_t i) {
+ if (i == 0) {
+ return; // sorted range cannot be broken at the first element
+ }
+ a[i] = 1;
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + i));
+ a[i] = -static_cast<int>(i);
+ });
+ }
+ }
+};
+
+template <class Iter>
+struct TestX {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ // Four elements, unsorted
+ X a[] = {X(2), X(2), X(1), X(3)};
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 3));
+ assert(std::is_sorted_until(policy, Iter(std::begin(a) + 1), Iter(std::end(a)), Comp{}) == Iter(std::begin(a) + 3));
+ assert(std::is_sorted_until(policy, Iter(std::begin(a)), Iter(std::begin(a) + 3), Comp{}) ==
+ Iter(std::begin(a) + 3));
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<Test>{});
+ types::for_each(types::forward_iterator_list<const X*>{}, TestIteratorWithPolicies<TestX>{});
+ return 0;
+}
\ No newline at end of file
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a505e1441d2a9..5dbeaafc93929 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -365,6 +365,14 @@ int main(int, char**) {
assert_non_throwing([=, &policy] {
(void)std::is_sorted(policy, std::move(first1), std::move(last1), compare);
});
+
+ // is_sorted_until(first, last)
+ assert_non_throwing([=, &policy] { (void)std::is_sorted_until(policy, std::move(first1), std::move(last1)); });
+
+ // is_sorted_until(first, last, comp)
+ assert_non_throwing([=, &policy] {
+ (void)std::is_sorted_until(policy, std::move(first1), std::move(last1), compare);
+ });
}
{
More information about the libcxx-commits
mailing list