[libcxx-commits] [libcxx] 8ed702b - [libc++][ranges] Implement `ranges::{, stable_}partition`.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 18 21:06:58 PDT 2022
Author: Konstantin Varlamov
Date: 2022-07-18T21:06:17-07:00
New Revision: 8ed702b83f20d0ec117264a8e7776098ae956250
URL: https://github.com/llvm/llvm-project/commit/8ed702b83f20d0ec117264a8e7776098ae956250
DIFF: https://github.com/llvm/llvm-project/commit/8ed702b83f20d0ec117264a8e7776098ae956250.diff
LOG: [libc++][ranges] Implement `ranges::{,stable_}partition`.
Differential Revision: https://reviews.llvm.org/D129624
Added:
libcxx/include/__algorithm/ranges_iterator_concept.h
Modified:
libcxx/docs/Status/RangesAlgorithms.csv
libcxx/include/CMakeLists.txt
libcxx/include/__algorithm/iterator_operations.h
libcxx/include/__algorithm/make_projected.h
libcxx/include/__algorithm/partition.h
libcxx/include/__algorithm/ranges_find_end.h
libcxx/include/__algorithm/ranges_partition.h
libcxx/include/__algorithm/ranges_stable_partition.h
libcxx/include/__algorithm/rotate.h
libcxx/include/__algorithm/stable_partition.h
libcxx/include/algorithm
libcxx/include/module.modulemap.in
libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
libcxx/test/libcxx/private_headers.verify.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_partition.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 7a73504e4ba9f..3376b6a8522df 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -10,7 +10,7 @@ Search,adjacent_find,Nikolas Klauser,`D126610 <https://llvm.org/D126610>`_,✅
Search,mismatch,Nikolas Klauser,`D117817 <https://llvm.org/D117817>`_,✅
Search,equal,Nikolas Klauser,`D123681 <https://llvm.org/D123681>`_,✅
Search,lexicographical_compare,Nikolas Klauser,`D127130 <https://llvm.org/D127130>`_,✅
-Search,partition_point,Christopher Di Bella,`D105794 <https://llvm.org/D105794>`_,Under review
+Search,partition_point,Konstantin Varlamov,n/a,In progress
Search,lower_bound,Nikolas Klauser,`D121964 <https://llvm.org/D121964>`_,✅
Search,upper_bound,Nikolas Klauser,`D121964 <https://llvm.org/D121964>`_,✅
Search,equal_range,Christopher Di Bella,n/a,Not started
@@ -58,7 +58,7 @@ Write,reverse_copy,Nikolas Klauser,`D127211 <https://llvm.org/D127211>`_,✅
Write,rotate_copy,Nikolas Klauser,`D127211 <https://llvm.org/D127211>`_,✅
Write,sample,Not assigned,n/a,Not started
Write,unique_copy,Not assigned,n/a,Not started
-Write,partition_copy,Not assigned,n/a,Not started
+Write,partition_copy,Konstantin Varlamov,n/a,In progress
Write,partial_sort_copy,Not assigned,n/a,Not started
Merge,merge,Hui Xie,`D128611 <https://llvm.org/D128611>`_,✅
Merge,set_
diff erence,Hui Xie,`D128983 <https://llvm.org/D128983>`_,✅
@@ -71,8 +71,8 @@ Permutation,reverse,Nikolas Klauser,`D125752 <https://llvm.org/D125752>`_,✅
Permutation,rotate,Nikolas Klauser,`D124122 <https://llvm.org/D124122>`_,Under review
Permutation,shuffle,Not assigned,n/a,Not started
Permutation,unique,Not assigned,n/a,Not started
-Permutation,partition,Not assigned,n/a,Not started
-Permutation,stable_partition,Not assigned,n/a,Not started
+Permutation,partition,Konstantin Varlamov,`D129624 <https://llvm.org/D129624>`_,✅
+Permutation,stable_partition,Konstantin Varlamov,`D129624 <https://llvm.org/D129624>`_,✅
Permutation,sort,Konstantin Varlamov,`D127557 <https://llvm.org/D127557>`_,✅
Permutation,stable_sort,Konstantin Varlamov,`D127834 <https://llvm.org/D127834>`_,✅
Permutation,partial_sort,Konstantin Varlamov,n/a,In progress
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index e69b0516b372f..0193f6162cf72 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -98,6 +98,7 @@ set(files
__algorithm/ranges_is_partitioned.h
__algorithm/ranges_is_sorted.h
__algorithm/ranges_is_sorted_until.h
+ __algorithm/ranges_iterator_concept.h
__algorithm/ranges_lexicographical_compare.h
__algorithm/ranges_lower_bound.h
__algorithm/ranges_make_heap.h
diff --git a/libcxx/include/__algorithm/iterator_operations.h b/libcxx/include/__algorithm/iterator_operations.h
index de9c8a130ecb8..03dd2c41e94af 100644
--- a/libcxx/include/__algorithm/iterator_operations.h
+++ b/libcxx/include/__algorithm/iterator_operations.h
@@ -85,6 +85,13 @@ struct _IterOps<_ClassicAlgPolicy> {
return __last;
}
+ template <class _Iter>
+ _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
+ __uncvref_t<_Iter> next(_Iter&& __it,
+ typename iterator_traits<__uncvref_t<_Iter> >::
diff erence_type __n = 1){
+ return std::next(std::forward<_Iter>(__it), __n);
+ }
+
template <class _Iter>
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
void __advance_to(_Iter& __first, _Iter __last) {
diff --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h
index 6d8ebfd3d90e8..64fc3dfb6a12c 100644
--- a/libcxx/include/__algorithm/make_projected.h
+++ b/libcxx/include/__algorithm/make_projected.h
@@ -27,6 +27,21 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
+template <class _Pred, class _Proj>
+_LIBCPP_HIDE_FROM_ABI constexpr static
+decltype(auto) __make_projected_pred(_Pred& __pred, _Proj& __proj) {
+ if constexpr (same_as<decay_t<_Proj>, identity> && !is_member_pointer_v<decay_t<_Pred>>) {
+ // Avoid creating the lambda and just use the pristine predicate -- for certain algorithms, this would enable
+ // optimizations that rely on the type of the predicate.
+ return __pred;
+
+ } else {
+ return [&](auto&& __x) {
+ return std::invoke(__pred, std::invoke(__proj, std::forward<decltype(__x)>(__x)));
+ };
+ }
+}
+
template <class _Comp, class _Proj>
_LIBCPP_HIDE_FROM_ABI constexpr static
decltype(auto) __make_projected_comp(_Comp& __comp, _Proj& __proj) {
diff --git a/libcxx/include/__algorithm/partition.h b/libcxx/include/__algorithm/partition.h
index 73d94831ed876..60b4e290ebebc 100644
--- a/libcxx/include/__algorithm/partition.h
+++ b/libcxx/include/__algorithm/partition.h
@@ -9,9 +9,12 @@
#ifndef _LIBCPP___ALGORITHM_PARTITION_H
#define _LIBCPP___ALGORITHM_PARTITION_H
+#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__iterator/iterator_traits.h>
-#include <__utility/swap.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
+#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -19,40 +22,45 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _Predicate, class _ForwardIterator>
-_LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
-__partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred, forward_iterator_tag)
+template <class _Predicate, class _AlgPolicy, class _ForwardIterator, class _Sentinel>
+_LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_ForwardIterator, _ForwardIterator>
+__partition_impl(_ForwardIterator __first, _Sentinel __last, _Predicate __pred, forward_iterator_tag)
{
while (true)
{
if (__first == __last)
- return __first;
+ return std::make_pair(std::move(__first), std::move(__first));
if (!__pred(*__first))
break;
++__first;
}
- for (_ForwardIterator __p = __first; ++__p != __last;)
+
+ _ForwardIterator __p = __first;
+ while (++__p != __last)
{
if (__pred(*__p))
{
- swap(*__first, *__p);
+ _IterOps<_AlgPolicy>::iter_swap(__first, __p);
++__first;
}
}
- return __first;
+ return std::make_pair(std::move(__first), std::move(__p));
}
-template <class _Predicate, class _BidirectionalIterator>
-_LIBCPP_CONSTEXPR_AFTER_CXX17 _BidirectionalIterator
-__partition(_BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred,
+template <class _Predicate, class _AlgPolicy, class _BidirectionalIterator, class _Sentinel>
+_LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_BidirectionalIterator, _BidirectionalIterator>
+__partition_impl(_BidirectionalIterator __first, _Sentinel __sentinel, _Predicate __pred,
bidirectional_iterator_tag)
{
+ _BidirectionalIterator __original_last = _IterOps<_AlgPolicy>::next(__first, __sentinel);
+ _BidirectionalIterator __last = __original_last;
+
while (true)
{
while (true)
{
if (__first == __last)
- return __first;
+ return std::make_pair(std::move(__first), std::move(__original_last));
if (!__pred(*__first))
break;
++__first;
@@ -60,20 +68,29 @@ __partition(_BidirectionalIterator __first, _BidirectionalIterator __last, _Pred
do
{
if (__first == --__last)
- return __first;
+ return std::make_pair(std::move(__first), std::move(__original_last));
} while (!__pred(*__last));
- swap(*__first, *__last);
+ _IterOps<_AlgPolicy>::iter_swap(__first, __last);
++__first;
}
}
+template <class _AlgPolicy, class _ForwardIterator, class _Sentinel, class _Predicate, class _IterCategory>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
+pair<_ForwardIterator, _ForwardIterator> __partition(
+ _ForwardIterator __first, _Sentinel __last, _Predicate&& __pred, _IterCategory __iter_category) {
+ return std::__partition_impl<__uncvref_t<_Predicate>&, _AlgPolicy>(
+ std::move(__first), std::move(__last), __pred, __iter_category);
+}
+
template <class _ForwardIterator, class _Predicate>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_ForwardIterator
partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred)
{
- return _VSTD::__partition<_Predicate&>(
- __first, __last, __pred, typename iterator_traits<_ForwardIterator>::iterator_category());
+ using _IterCategory = typename iterator_traits<_ForwardIterator>::iterator_category;
+ auto __result = std::__partition<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred, _IterCategory());
+ return __result.first;
}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/ranges_find_end.h b/libcxx/include/__algorithm/ranges_find_end.h
index fec709e79f5ab..270b006498486 100644
--- a/libcxx/include/__algorithm/ranges_find_end.h
+++ b/libcxx/include/__algorithm/ranges_find_end.h
@@ -11,6 +11,7 @@
#include <__algorithm/find_end.h>
#include <__algorithm/iterator_operations.h>
+#include <__algorithm/ranges_iterator_concept.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/ranges_operations.h>
@@ -29,23 +30,6 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _Iter>
-consteval auto __get_iterator_concept() {
- if constexpr (contiguous_iterator<_Iter>)
- return contiguous_iterator_tag();
- else if constexpr (random_access_iterator<_Iter>)
- return random_access_iterator_tag();
- else if constexpr (bidirectional_iterator<_Iter>)
- return bidirectional_iterator_tag();
- else if constexpr (forward_iterator<_Iter>)
- return forward_iterator_tag();
- else if constexpr (input_iterator<_Iter>)
- return input_iterator_tag();
-}
-
-template <class _Iter>
-using __iterator_concept = decltype(__get_iterator_concept<_Iter>());
-
namespace ranges {
namespace __find_end {
struct __fn {
diff --git a/libcxx/include/__algorithm/ranges_iterator_concept.h b/libcxx/include/__algorithm/ranges_iterator_concept.h
new file mode 100644
index 0000000000000..3323119317aec
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_iterator_concept.h
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ALGORITHM_RANGES_ITERATOR_CONCEPT_H
+#define _LIBCPP___ALGORITHM_RANGES_ITERATOR_CONCEPT_H
+
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+
+template <class _IterMaybeQualified>
+consteval auto __get_iterator_concept() {
+ using _Iter = __uncvref_t<_IterMaybeQualified>;
+
+ if constexpr (contiguous_iterator<_Iter>)
+ return contiguous_iterator_tag();
+ else if constexpr (random_access_iterator<_Iter>)
+ return random_access_iterator_tag();
+ else if constexpr (bidirectional_iterator<_Iter>)
+ return bidirectional_iterator_tag();
+ else if constexpr (forward_iterator<_Iter>)
+ return forward_iterator_tag();
+ else if constexpr (input_iterator<_Iter>)
+ return input_iterator_tag();
+}
+
+template <class _Iter>
+using __iterator_concept = decltype(__get_iterator_concept<_Iter>());
+
+} // namespace ranges
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_ITERATOR_CONCEPT_H
diff --git a/libcxx/include/__algorithm/ranges_partition.h b/libcxx/include/__algorithm/ranges_partition.h
index c145e7bdb4a23..60bee699d90e2 100644
--- a/libcxx/include/__algorithm/ranges_partition.h
+++ b/libcxx/include/__algorithm/ranges_partition.h
@@ -9,8 +9,10 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_PARTITION_H
#define _LIBCPP___ALGORITHM_RANGES_PARTITION_H
+#include <__algorithm/iterator_operations.h>
#include <__algorithm/make_projected.h>
#include <__algorithm/partition.h>
+#include <__algorithm/ranges_iterator_concept.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
@@ -21,10 +23,10 @@
#include <__iterator/projected.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
-#include <__ranges/dangling.h>
#include <__ranges/subrange.h>
#include <__utility/forward.h>
#include <__utility/move.h>
+#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -39,13 +41,21 @@ namespace __partition {
struct __fn {
+ template <class _Iter, class _Sent, class _Proj, class _Pred>
+ _LIBCPP_HIDE_FROM_ABI static constexpr
+ subrange<__uncvref_t<_Iter>> __partition_fn_impl(_Iter&& __first, _Sent&& __last, _Pred&& __pred, _Proj&& __proj) {
+ auto&& __projected_pred = ranges::__make_projected_pred(__pred, __proj);
+ auto __result = std::__partition<_RangeAlgPolicy>(
+ std::move(__first), std::move(__last), __projected_pred, __iterator_concept<_Iter>());
+
+ return {std::move(__result.first), std::move(__result.second)};
+ }
+
template <permutable _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
_LIBCPP_HIDE_FROM_ABI constexpr
subrange<_Iter> operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const {
- // TODO: implement
- (void)__first; (void)__last; (void)__pred; (void)__proj;
- return {};
+ return __partition_fn_impl(__first, __last, __pred, __proj);
}
template <forward_range _Range, class _Proj = identity,
@@ -53,9 +63,7 @@ struct __fn {
requires permutable<iterator_t<_Range>>
_LIBCPP_HIDE_FROM_ABI constexpr
borrowed_subrange_t<_Range> operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) const {
- // TODO: implement
- (void)__range; (void)__pred; (void)__proj;
- return {};
+ return __partition_fn_impl(ranges::begin(__range), ranges::end(__range), __pred, __proj);
}
};
diff --git a/libcxx/include/__algorithm/ranges_stable_partition.h b/libcxx/include/__algorithm/ranges_stable_partition.h
index 178c953ebdae2..27957db8829f1 100644
--- a/libcxx/include/__algorithm/ranges_stable_partition.h
+++ b/libcxx/include/__algorithm/ranges_stable_partition.h
@@ -9,7 +9,9 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_STABLE_PARTITION_H
#define _LIBCPP___ALGORITHM_RANGES_STABLE_PARTITION_H
+#include <__algorithm/iterator_operations.h>
#include <__algorithm/make_projected.h>
+#include <__algorithm/ranges_iterator_concept.h>
#include <__algorithm/stable_partition.h>
#include <__config>
#include <__functional/identity.h>
@@ -17,6 +19,7 @@
#include <__functional/ranges_operations.h>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
#include <__iterator/permutable.h>
#include <__iterator/projected.h>
#include <__ranges/access.h>
@@ -25,6 +28,7 @@
#include <__ranges/subrange.h>
#include <__utility/forward.h>
#include <__utility/move.h>
+#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -39,14 +43,25 @@ namespace __stable_partition {
struct __fn {
+ template <class _Iter, class _Sent, class _Proj, class _Pred>
+ _LIBCPP_HIDE_FROM_ABI static
+ subrange<__uncvref_t<_Iter>> __stable_partition_fn_impl(
+ _Iter&& __first, _Sent&& __last, _Pred&& __pred, _Proj&& __proj) {
+ auto __last_iter = ranges::next(__first, __last);
+
+ auto&& __projected_pred = ranges::__make_projected_pred(__pred, __proj);
+ auto __result = std::__stable_partition<_RangeAlgPolicy>(
+ std::move(__first), __last_iter, __projected_pred, __iterator_concept<_Iter>());
+
+ return {std::move(__result), std::move(__last_iter)};
+ }
+
template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
requires permutable<_Iter>
_LIBCPP_HIDE_FROM_ABI
subrange<_Iter> operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const {
- // TODO: implement
- (void)__first; (void)__last; (void)__pred; (void)__proj;
- return {};
+ return __stable_partition_fn_impl(__first, __last, __pred, __proj);
}
template <bidirectional_range _Range, class _Proj = identity,
@@ -54,9 +69,7 @@ struct __fn {
requires permutable<iterator_t<_Range>>
_LIBCPP_HIDE_FROM_ABI
borrowed_subrange_t<_Range> operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) const {
- // TODO: implement
- (void)__range; (void)__pred; (void)__proj;
- return {};
+ return __stable_partition_fn_impl(ranges::begin(__range), ranges::end(__range), __pred, __proj);
}
};
diff --git a/libcxx/include/__algorithm/rotate.h b/libcxx/include/__algorithm/rotate.h
index c9ea5bad4c5ad..fcf8444a65a08 100644
--- a/libcxx/include/__algorithm/rotate.h
+++ b/libcxx/include/__algorithm/rotate.h
@@ -9,6 +9,7 @@
#ifndef _LIBCPP___ALGORITHM_ROTATE_H
#define _LIBCPP___ALGORITHM_ROTATE_H
+#include <__algorithm/iterator_operations.h>
#include <__algorithm/move.h>
#include <__algorithm/move_backward.h>
#include <__algorithm/swap_ranges.h>
@@ -26,37 +27,40 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _ForwardIterator>
+template <class _AlgPolicy, class _ForwardIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX11 _ForwardIterator
__rotate_left(_ForwardIterator __first, _ForwardIterator __last)
{
typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
- value_type __tmp = _VSTD::move(*__first);
+ value_type __tmp = _IterOps<_AlgPolicy>::__iter_move(__first);
+ // TODO(ranges): pass `_AlgPolicy` to `move`.
_ForwardIterator __lm1 = _VSTD::move(_VSTD::next(__first), __last, __first);
*__lm1 = _VSTD::move(__tmp);
return __lm1;
}
-template <class _BidirectionalIterator>
+template <class _AlgPolicy, class _BidirectionalIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX11 _BidirectionalIterator
__rotate_right(_BidirectionalIterator __first, _BidirectionalIterator __last)
{
typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
+ // TODO(ranges): pass `_AlgPolicy` to `prev`.
_BidirectionalIterator __lm1 = _VSTD::prev(__last);
- value_type __tmp = _VSTD::move(*__lm1);
+ value_type __tmp = _IterOps<_AlgPolicy>::__iter_move(__lm1);
+ // TODO(ranges): pass `_AlgPolicy` to `move_backward`.
_BidirectionalIterator __fp1 = _VSTD::move_backward(__first, __lm1, __last);
*__first = _VSTD::move(__tmp);
return __fp1;
}
-template <class _ForwardIterator>
+template <class _AlgPolicy, class _ForwardIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX14 _ForwardIterator
__rotate_forward(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIterator __last)
{
_ForwardIterator __i = __middle;
while (true)
{
- swap(*__first, *__i);
+ _IterOps<_AlgPolicy>::iter_swap(__first, __i);
++__first;
if (++__i == __last)
break;
@@ -69,7 +73,7 @@ __rotate_forward(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIt
__i = __middle;
while (true)
{
- swap(*__first, *__i);
+ _IterOps<_AlgPolicy>::iter_swap(__first, __i);
++__first;
if (++__i == __last)
{
@@ -98,7 +102,7 @@ __algo_gcd(_Integral __x, _Integral __y)
return __x;
}
-template<typename _RandomAccessIterator>
+template <class _AlgPolicy, typename _RandomAccessIterator>
_LIBCPP_CONSTEXPR_AFTER_CXX14 _RandomAccessIterator
__rotate_gcd(_RandomAccessIterator __first, _RandomAccessIterator __middle, _RandomAccessIterator __last)
{
@@ -109,18 +113,19 @@ __rotate_gcd(_RandomAccessIterator __first, _RandomAccessIterator __middle, _Ran
const
diff erence_type __m2 = __last - __middle;
if (__m1 == __m2)
{
+ // TODO(ranges): pass `_AlgPolicy` to `swap_ranges`.
_VSTD::swap_ranges(__first, __middle, __middle);
return __middle;
}
const
diff erence_type __g = _VSTD::__algo_gcd(__m1, __m2);
for (_RandomAccessIterator __p = __first + __g; __p != __first;)
{
- value_type __t(_VSTD::move(*--__p));
+ value_type __t(_IterOps<_AlgPolicy>::__iter_move(--__p));
_RandomAccessIterator __p1 = __p;
_RandomAccessIterator __p2 = __p1 + __m1;
do
{
- *__p1 = _VSTD::move(*__p2);
+ *__p1 = _IterOps<_AlgPolicy>::__iter_move(__p2);
__p1 = __p2;
const
diff erence_type __d = __last - __p2;
if (__m1 < __d)
@@ -133,54 +138,66 @@ __rotate_gcd(_RandomAccessIterator __first, _RandomAccessIterator __middle, _Ran
return __first + __m2;
}
-template <class _ForwardIterator>
+template <class _AlgPolicy, class _ForwardIterator>
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR_AFTER_CXX11 _ForwardIterator
-__rotate(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIterator __last,
+__rotate_impl(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIterator __last,
_VSTD::forward_iterator_tag)
{
typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
if (is_trivially_move_assignable<value_type>::value)
{
- if (_VSTD::next(__first) == __middle)
- return _VSTD::__rotate_left(__first, __last);
+ if (_IterOps<_AlgPolicy>::next(__first) == __middle)
+ return std::__rotate_left<_AlgPolicy>(__first, __last);
}
- return _VSTD::__rotate_forward(__first, __middle, __last);
+ return std::__rotate_forward<_AlgPolicy>(__first, __middle, __last);
}
-template <class _BidirectionalIterator>
+template <class _AlgPolicy, class _BidirectionalIterator>
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR_AFTER_CXX11 _BidirectionalIterator
-__rotate(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
+__rotate_impl(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
bidirectional_iterator_tag)
{
typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
if (is_trivially_move_assignable<value_type>::value)
{
- if (_VSTD::next(__first) == __middle)
- return _VSTD::__rotate_left(__first, __last);
- if (_VSTD::next(__middle) == __last)
- return _VSTD::__rotate_right(__first, __last);
+ if (_IterOps<_AlgPolicy>::next(__first) == __middle)
+ return std::__rotate_left<_AlgPolicy>(__first, __last);
+ if (_IterOps<_AlgPolicy>::next(__middle) == __last)
+ return std::__rotate_right<_AlgPolicy>(__first, __last);
}
- return _VSTD::__rotate_forward(__first, __middle, __last);
+ return std::__rotate_forward<_AlgPolicy>(__first, __middle, __last);
}
-template <class _RandomAccessIterator>
+template <class _AlgPolicy, class _RandomAccessIterator>
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR_AFTER_CXX11 _RandomAccessIterator
-__rotate(_RandomAccessIterator __first, _RandomAccessIterator __middle, _RandomAccessIterator __last,
+__rotate_impl(_RandomAccessIterator __first, _RandomAccessIterator __middle, _RandomAccessIterator __last,
random_access_iterator_tag)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
if (is_trivially_move_assignable<value_type>::value)
{
- if (_VSTD::next(__first) == __middle)
- return _VSTD::__rotate_left(__first, __last);
- if (_VSTD::next(__middle) == __last)
- return _VSTD::__rotate_right(__first, __last);
- return _VSTD::__rotate_gcd(__first, __middle, __last);
+ if (_IterOps<_AlgPolicy>::next(__first) == __middle)
+ return std::__rotate_left<_AlgPolicy>(__first, __last);
+ if (_IterOps<_AlgPolicy>::next(__middle) == __last)
+ return std::__rotate_right<_AlgPolicy>(__first, __last);
+ return std::__rotate_gcd<_AlgPolicy>(__first, __middle, __last);
}
- return _VSTD::__rotate_forward(__first, __middle, __last);
+ return std::__rotate_forward<_AlgPolicy>(__first, __middle, __last);
+}
+
+template <class _AlgPolicy, class _RandomAccessIterator, class _IterCategory>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+_RandomAccessIterator __rotate(_RandomAccessIterator __first, _RandomAccessIterator __middle,
+ _RandomAccessIterator __last, _IterCategory __iter_category) {
+ if (__first == __middle)
+ return __last;
+ if (__middle == __last)
+ return __first;
+
+ return std::__rotate_impl<_AlgPolicy>(std::move(__first), std::move(__middle), std::move(__last), __iter_category);
}
template <class _ForwardIterator>
@@ -188,12 +205,8 @@ inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
rotate(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIterator __last)
{
- if (__first == __middle)
- return __last;
- if (__middle == __last)
- return __first;
- return _VSTD::__rotate(__first, __middle, __last,
- typename iterator_traits<_ForwardIterator>::iterator_category());
+ return std::__rotate<_ClassicAlgPolicy>(__first, __middle, __last,
+ typename iterator_traits<_ForwardIterator>::iterator_category());
}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/stable_partition.h b/libcxx/include/__algorithm/stable_partition.h
index 969ac7a6173e6..e5ad48b2ed516 100644
--- a/libcxx/include/__algorithm/stable_partition.h
+++ b/libcxx/include/__algorithm/stable_partition.h
@@ -9,13 +9,14 @@
#ifndef _LIBCPP___ALGORITHM_STABLE_PARTITION_H
#define _LIBCPP___ALGORITHM_STABLE_PARTITION_H
+#include <__algorithm/iterator_operations.h>
#include <__algorithm/rotate.h>
#include <__config>
#include <__iterator/advance.h>
#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
-#include <__utility/swap.h>
#include <memory>
+#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -23,11 +24,13 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _Predicate, class _ForwardIterator, class _Distance, class _Pair>
+template <class _AlgPolicy, class _Predicate, class _ForwardIterator, class _Distance, class _Pair>
_ForwardIterator
-__stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred,
+__stable_partition_impl(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred,
_Distance __len, _Pair __p, forward_iterator_tag __fit)
{
+ using _Ops = _IterOps<_AlgPolicy>;
+
// *__first is known to be false
// __len >= 1
if (__len == 1)
@@ -37,7 +40,7 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
_ForwardIterator __m = __first;
if (__pred(*++__m))
{
- swap(*__first, *__m);
+ _Ops::iter_swap(__first, __m);
return __m;
}
return __first;
@@ -50,7 +53,7 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
// Move the falses into the temporary buffer, and the trues to the front of the line
// Update __first to always point to the end of the trues
value_type* __t = __p.first;
- ::new ((void*)__t) value_type(_VSTD::move(*__first));
+ ::new ((void*)__t) value_type(_Ops::__iter_move(__first));
__d.template __incr<value_type>();
++__t;
_ForwardIterator __i = __first;
@@ -58,12 +61,12 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
{
if (__pred(*__i))
{
- *__first = _VSTD::move(*__i);
+ *__first = _Ops::__iter_move(__i);
++__first;
}
else
{
- ::new ((void*)__t) value_type(_VSTD::move(*__i));
+ ::new ((void*)__t) value_type(_Ops::__iter_move(__i));
__d.template __incr<value_type>();
++__t;
}
@@ -72,7 +75,7 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
// Move falses back into range, but don't mess up __first which points to first false
__i = __first;
for (value_type* __t2 = __p.first; __t2 < __t; ++__t2, (void) ++__i)
- *__i = _VSTD::move(*__t2);
+ *__i = _Ops::__iter_move(__t2);
// __h destructs moved-from values out of the temp buffer, but doesn't deallocate buffer
return __first;
}
@@ -80,11 +83,12 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
// __len >= 3
_ForwardIterator __m = __first;
_Distance __len2 = __len / 2; // __len2 >= 2
- _VSTD::advance(__m, __len2);
+ _Ops::advance(__m, __len2);
// recurse on [__first, __m), *__first know to be false
// F?????????????????
// f m l
- _ForwardIterator __first_false = _VSTD::__stable_partition<_Predicate&>(__first, __m, __pred, __len2, __p, __fit);
+ _ForwardIterator __first_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
+ __first, __m, __pred, __len2, __p, __fit);
// TTTFFFFF??????????
// f ff m l
// recurse on [__m, __last], except increase __m until *(__m) is false, *__last know to be true
@@ -99,18 +103,19 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
}
// TTTFFFFFTTTF??????
// f ff m m1 l
- __second_false = _VSTD::__stable_partition<_Predicate&>(__m1, __last, __pred, __len_half, __p, __fit);
+ __second_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
+ __m1, __last, __pred, __len_half, __p, __fit);
__second_half_done:
// TTTFFFFFTTTTTFFFFF
// f ff m sf l
- return _VSTD::rotate(__first_false, __m, __second_false);
+ return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false, __fit);
// TTTTTTTTFFFFFFFFFF
// |
}
-template <class _Predicate, class _ForwardIterator>
+template <class _AlgPolicy, class _Predicate, class _ForwardIterator>
_ForwardIterator
-__stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred,
+__stable_partition_impl(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred,
forward_iterator_tag)
{
const unsigned __alloc_limit = 3; // might want to make this a function of trivial assignment
@@ -127,7 +132,7 @@ __stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate
// *__first is known to be false
typedef typename iterator_traits<_ForwardIterator>::
diff erence_type
diff erence_type;
typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
-
diff erence_type __len = _VSTD::distance(__first, __last);
+
diff erence_type __len = _IterOps<_AlgPolicy>::distance(__first, __last);
pair<value_type*, ptr
diff _t> __p(0, 0);
unique_ptr<value_type, __return_temporary_buffer> __h;
if (__len >= __alloc_limit)
@@ -138,20 +143,23 @@ _LIBCPP_SUPPRESS_DEPRECATED_PUSH
_LIBCPP_SUPPRESS_DEPRECATED_POP
__h.reset(__p.first);
}
- return _VSTD::__stable_partition<_Predicate&>(__first, __last, __pred, __len, __p, forward_iterator_tag());
+ return std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
+ std::move(__first), std::move(__last), __pred, __len, __p, forward_iterator_tag());
}
-template <class _Predicate, class _BidirectionalIterator, class _Distance, class _Pair>
+template <class _AlgPolicy, class _Predicate, class _BidirectionalIterator, class _Distance, class _Pair>
_BidirectionalIterator
-__stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred,
+__stable_partition_impl(_BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred,
_Distance __len, _Pair __p, bidirectional_iterator_tag __bit)
{
+ using _Ops = _IterOps<_AlgPolicy>;
+
// *__first is known to be false
// *__last is known to be true
// __len >= 2
if (__len == 2)
{
- swap(*__first, *__last);
+ _Ops::iter_swap(__first, __last);
return __last;
}
if (__len == 3)
@@ -159,12 +167,12 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
_BidirectionalIterator __m = __first;
if (__pred(*++__m))
{
- swap(*__first, *__m);
- swap(*__m, *__last);
+ _Ops::iter_swap(__first, __m);
+ _Ops::iter_swap(__m, __last);
return __last;
}
- swap(*__m, *__last);
- swap(*__first, *__m);
+ _Ops::iter_swap(__m, __last);
+ _Ops::iter_swap(__first, __m);
return __m;
}
if (__len <= __p.second)
@@ -175,7 +183,7 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
// Move the falses into the temporary buffer, and the trues to the front of the line
// Update __first to always point to the end of the trues
value_type* __t = __p.first;
- ::new ((void*)__t) value_type(_VSTD::move(*__first));
+ ::new ((void*)__t) value_type(_Ops::__iter_move(__first));
__d.template __incr<value_type>();
++__t;
_BidirectionalIterator __i = __first;
@@ -183,23 +191,23 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
{
if (__pred(*__i))
{
- *__first = _VSTD::move(*__i);
+ *__first = _Ops::__iter_move(__i);
++__first;
}
else
{
- ::new ((void*)__t) value_type(_VSTD::move(*__i));
+ ::new ((void*)__t) value_type(_Ops::__iter_move(__i));
__d.template __incr<value_type>();
++__t;
}
}
// move *__last, known to be true
- *__first = _VSTD::move(*__i);
+ *__first = _Ops::__iter_move(__i);
__i = ++__first;
// All trues now at start of range, all falses in buffer
// Move falses back into range, but don't mess up __first which points to first false
for (value_type* __t2 = __p.first; __t2 < __t; ++__t2, (void) ++__i)
- *__i = _VSTD::move(*__t2);
+ *__i = _Ops::__iter_move(__t2);
// __h destructs moved-from values out of the temp buffer, but doesn't deallocate buffer
return __first;
}
@@ -207,7 +215,7 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
// __len >= 4
_BidirectionalIterator __m = __first;
_Distance __len2 = __len / 2; // __len2 >= 2
- _VSTD::advance(__m, __len2);
+ _Ops::advance(__m, __len2);
// recurse on [__first, __m-1], except reduce __m-1 until *(__m-1) is true, *__first know to be false
// F????????????????T
// f m l
@@ -222,7 +230,8 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
}
// F???TFFF?????????T
// f m1 m l
- __first_false = _VSTD::__stable_partition<_Predicate&>(__first, __m1, __pred, __len_half, __p, __bit);
+ __first_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
+ __first, __m1, __pred, __len_half, __p, __bit);
__first_half_done:
// TTTFFFFF?????????T
// f ff m l
@@ -239,18 +248,19 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
}
// TTTFFFFFTTTF?????T
// f ff m m1 l
- __second_false = _VSTD::__stable_partition<_Predicate&>(__m1, __last, __pred, __len_half, __p, __bit);
+ __second_false = std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
+ __m1, __last, __pred, __len_half, __p, __bit);
__second_half_done:
// TTTFFFFFTTTTTFFFFF
// f ff m sf l
- return _VSTD::rotate(__first_false, __m, __second_false);
+ return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false, __bit);
// TTTTTTTTFFFFFFFFFF
// |
}
-template <class _Predicate, class _BidirectionalIterator>
+template <class _AlgPolicy, class _Predicate, class _BidirectionalIterator>
_BidirectionalIterator
-__stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred,
+__stable_partition_impl(_BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred,
bidirectional_iterator_tag)
{
typedef typename iterator_traits<_BidirectionalIterator>::
diff erence_type
diff erence_type;
@@ -276,7 +286,7 @@ __stable_partition(_BidirectionalIterator __first, _BidirectionalIterator __last
// *__first is known to be false
// *__last is known to be true
// __len >= 2
-
diff erence_type __len = _VSTD::distance(__first, __last) + 1;
+
diff erence_type __len = _IterOps<_AlgPolicy>::distance(__first, __last) + 1;
pair<value_type*, ptr
diff _t> __p(0, 0);
unique_ptr<value_type, __return_temporary_buffer> __h;
if (__len >= __alloc_limit)
@@ -287,7 +297,16 @@ _LIBCPP_SUPPRESS_DEPRECATED_PUSH
_LIBCPP_SUPPRESS_DEPRECATED_POP
__h.reset(__p.first);
}
- return _VSTD::__stable_partition<_Predicate&>(__first, __last, __pred, __len, __p, bidirectional_iterator_tag());
+ return std::__stable_partition_impl<_AlgPolicy, _Predicate&>(
+ std::move(__first), std::move(__last), __pred, __len, __p, bidirectional_iterator_tag());
+}
+
+template <class _AlgPolicy, class _Predicate, class _ForwardIterator, class _IterCategory>
+_LIBCPP_HIDE_FROM_ABI
+_ForwardIterator __stable_partition(
+ _ForwardIterator __first, _ForwardIterator __last, _Predicate&& __pred, _IterCategory __iter_category) {
+ return std::__stable_partition_impl<_AlgPolicy, __uncvref_t<_Predicate>&>(
+ std::move(__first), std::move(__last), __pred, __iter_category);
}
template <class _ForwardIterator, class _Predicate>
@@ -295,7 +314,9 @@ inline _LIBCPP_INLINE_VISIBILITY
_ForwardIterator
stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred)
{
- return _VSTD::__stable_partition<_Predicate&>(__first, __last, __pred, typename iterator_traits<_ForwardIterator>::iterator_category());
+ using _IterCategory = typename iterator_traits<_ForwardIterator>::iterator_category;
+ return std::__stable_partition<_ClassicAlgPolicy, _Predicate&>(
+ std::move(__first), std::move(__last), __pred, _IterCategory());
}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index ff700cf29a2bf..cc2c468c2976e 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -464,6 +464,28 @@ namespace ranges {
ranges::less>
constexpr bool binary_search(R&& r, const T& value, Comp comp = {},
Proj proj = {}); // since C++20
+
+ template<permutable I, sentinel_for<I> S, class Proj = identity,
+ indirect_unary_predicate<projected<I, Proj>> Pred>
+ constexpr subrange<I>
+ partition(I first, S last, Pred pred, Proj proj = {}); // Since C++20
+
+ template<forward_range R, class Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+ requires permutable<iterator_t<R>>
+ constexpr borrowed_subrange_t<R>
+ partition(R&& r, Pred pred, Proj proj = {}); // Since C++20
+
+ template<bidirectional_iterator I, sentinel_for<I> S, class Proj = identity,
+ indirect_unary_predicate<projected<I, Proj>> Pred>
+ requires permutable<I>
+ subrange<I> stable_partition(I first, S last, Pred pred, Proj proj = {}); // Since C++20
+
+ template<bidirectional_range R, class Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+ requires permutable<iterator_t<R>>
+ borrowed_subrange_t<R> stable_partition(R&& r, Pred pred, Proj proj = {}); // Since C++20
+
template<input_iterator I1, sentinel_for<I1> S1, forward_iterator I2, sentinel_for<I2> S2,
class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity>
requires indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
@@ -1496,6 +1518,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_move_backward.h>
#include <__algorithm/ranges_none_of.h>
#include <__algorithm/ranges_nth_element.h>
+#include <__algorithm/ranges_partition.h>
#include <__algorithm/ranges_pop_heap.h>
#include <__algorithm/ranges_push_heap.h>
#include <__algorithm/ranges_remove.h>
@@ -1513,6 +1536,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_set_union.h>
#include <__algorithm/ranges_sort.h>
#include <__algorithm/ranges_sort_heap.h>
+#include <__algorithm/ranges_stable_partition.h>
#include <__algorithm/ranges_stable_sort.h>
#include <__algorithm/ranges_swap_ranges.h>
#include <__algorithm/ranges_transform.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 25e1c6e6d0e2b..91ac0d2c753d7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -337,6 +337,7 @@ module std [system] {
module ranges_is_partitioned { private header "__algorithm/ranges_is_partitioned.h" }
module ranges_is_sorted { private header "__algorithm/ranges_is_sorted.h" }
module ranges_is_sorted_until { private header "__algorithm/ranges_is_sorted_until.h" }
+ module ranges_iterator_concept { private header "__algorithm/ranges_iterator_concept.h" }
module ranges_lexicographical_compare { private header "__algorithm/ranges_lexicographical_compare.h" }
module ranges_lower_bound { private header "__algorithm/ranges_lower_bound.h" }
module ranges_make_heap { private header "__algorithm/ranges_make_heap.h" }
diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
index c43ad9c7145d3..d980ba32efc0c 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
@@ -175,8 +175,8 @@ constexpr bool all_the_algorithms()
//(void)std::ranges::partial_sort(a, mid, Less(&copies)); assert(copies == 0);
//(void)std::ranges::partial_sort_copy(first, last, first2, mid2, Less(&copies)); assert(copies == 0);
//(void)std::ranges::partial_sort_copy(a, b, Less(&copies)); assert(copies == 0);
- //(void)std::ranges::partition(first, last, UnaryTrue(&copies)); assert(copies == 0);
- //(void)std::ranges::partition(a, UnaryTrue(&copies)); assert(copies == 0);
+ (void)std::ranges::partition(first, last, UnaryTrue(&copies)); assert(copies == 0);
+ (void)std::ranges::partition(a, UnaryTrue(&copies)); assert(copies == 0);
//(void)std::ranges::partition_copy(first, last, first2, last2, UnaryTrue(&copies)); assert(copies == 0);
//(void)std::ranges::partition_copy(a, first2, last2, UnaryTrue(&copies)); assert(copies == 0);
//(void)std::ranges::partition_point(first, last, UnaryTrue(&copies)); assert(copies == 0);
@@ -211,8 +211,8 @@ constexpr bool all_the_algorithms()
(void)std::ranges::sort(a, Less(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(first, last, Less(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(a, Less(&copies)); assert(copies == 0);
- //if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); }
- //if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(&copies)); assert(copies == 0); }
+ if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); }
+ if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(&copies)); assert(copies == 0); }
#if TEST_STD_VER > 20
diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
index b0740eaae2114..0a5758699e4a3 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
@@ -158,8 +158,8 @@ constexpr bool all_the_algorithms()
//(void)std::ranges::partial_sort(a, mid, Less(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::partial_sort_copy(first, last, first2, mid2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::partial_sort_copy(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
- //(void)std::ranges::partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
- //(void)std::ranges::partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
+ (void)std::ranges::partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
+ (void)std::ranges::partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::partition_copy(first, last, first2, last2, UnaryTrue(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::partition_copy(a, first2, last2, UnaryTrue(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::partition_point(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
@@ -202,8 +202,8 @@ constexpr bool all_the_algorithms()
(void)std::ranges::sort(a, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::sort_heap(a, Less(), Proj(&copies)); assert(copies == 0);
- //if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
- //if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
+ if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
+ if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(), Proj(&copies)); assert(copies == 0); }
if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(), Proj(&copies)); assert(copies == 0); }
#if TEST_STD_VER > 20
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 8e2c43bfbd3a0..dfed6560a7d3d 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -135,6 +135,7 @@ END-SCRIPT
#include <__algorithm/ranges_is_partitioned.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_is_partitioned.h'}}
#include <__algorithm/ranges_is_sorted.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_is_sorted.h'}}
#include <__algorithm/ranges_is_sorted_until.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_is_sorted_until.h'}}
+#include <__algorithm/ranges_iterator_concept.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_iterator_concept.h'}}
#include <__algorithm/ranges_lexicographical_compare.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_lexicographical_compare.h'}}
#include <__algorithm/ranges_lower_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_lower_bound.h'}}
#include <__algorithm/ranges_make_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_make_heap.h'}}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_partition.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_partition.pass.cpp
index 1863bade51426..4938f5f52e972 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_partition.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_partition.pass.cpp
@@ -31,12 +31,174 @@
#include "almost_satisfies_types.h"
#include "test_iterators.h"
-// TODO: SFINAE tests.
+struct UnaryPred { bool operator()(int) const; };
+
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter = int*, class Sent = int*, class Pred = UnaryPred>
+concept HasPartitionIter =
+ requires(Iter&& iter, Sent&& sent, Pred&& pred) {
+ std::ranges::partition(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Pred>(pred));
+ };
+
+static_assert(HasPartitionIter<int*, int*, UnaryPred>);
+
+// !permutable<I>
+static_assert(!HasPartitionIter<PermutableNotForwardIterator>);
+static_assert(!HasPartitionIter<PermutableNotSwappable>);
+
+// !sentinel_for<S, I>
+static_assert(!HasPartitionIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasPartitionIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+// !indirect_unary_predicate<projected<I, Proj>>
+static_assert(!HasPartitionIter<int*, int*, IndirectUnaryPredicateNotPredicate>);
+static_assert(!HasPartitionIter<int*, int*, IndirectUnaryPredicateNotCopyConstructible>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range, class Pred>
+concept HasPartitionRange =
+ requires(Range&& range, Pred&& pred) {
+ std::ranges::partition(std::forward<Range>(range), std::forward<Pred>(pred));
+ };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasPartitionRange<R<int*>, UnaryPred>);
+
+// !forward_range<R>
+static_assert(!HasPartitionRange<ForwardRangeNotDerivedFrom, UnaryPred>);
+static_assert(!HasPartitionRange<ForwardIteratorNotIncrementable, UnaryPred>);
+
+// !indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+static_assert(!HasPartitionRange<R<int*>, IndirectUnaryPredicateNotPredicate>);
+static_assert(!HasPartitionRange<R<int*>, IndirectUnaryPredicateNotCopyConstructible>);
+
+// !permutable<iterator_t<R>>
+static_assert(!HasPartitionRange<R<PermutableNotForwardIterator>, UnaryPred>);
+static_assert(!HasPartitionRange<R<PermutableNotSwappable>, UnaryPred>);
+
+// `partition` isn't a stable algorithm so this function cannot test the exact output.
+template <class Iter, class Sent, size_t N, class Pred>
+constexpr void test_one(std::array<int, N> input, Pred pred, size_t partition_point) {
+ auto neg_pred = [&](int x) { return !pred(x); };
+
+ { // (iterator, sentinel) overload.
+ auto partitioned = input;
+ auto b = Iter(partitioned.data());
+ auto e = Sent(Iter(partitioned.data() + partitioned.size()));
+
+ std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::partition(b, e, pred);
+
+ assert(base(result.begin()) == partitioned.data() + partition_point);
+ assert(base(result.end()) == partitioned.data() + partitioned.size());
+
+ assert(std::ranges::all_of(b, result.begin(), pred));
+ assert(std::ranges::all_of(result.begin(), e, neg_pred));
+ }
+
+ { // (range) overload.
+ auto partitioned = input;
+ auto b = Iter(partitioned.data());
+ auto e = Sent(Iter(partitioned.data() + partitioned.size()));
+ auto range = std::ranges::subrange(b, e);
+
+ std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::partition(range, pred);
+
+ assert(base(result.begin()) == partitioned.data() + partition_point);
+ assert(base(result.end()) == partitioned.data() + partitioned.size());
+
+ assert(std::ranges::all_of(b, result.begin(), pred));
+ assert(std::ranges::all_of(result.begin(), e, neg_pred));
+ }
+}
+
+template <class Iter, class Sent>
+constexpr void test_iterators_2() {
+ auto is_odd = [](int x) { return x % 2 != 0; };
+
+ // Empty sequence.
+ test_one<Iter, Sent, 0>({}, is_odd, 0);
+ // 1-element sequence, the element satisfies the predicate.
+ test_one<Iter, Sent, 1>({1}, is_odd, 1);
+ // 1-element sequence, the element doesn't satisfy the predicate.
+ test_one<Iter, Sent, 1>({2}, is_odd, 0);
+ // 2-element sequence, not in order.
+ test_one<Iter, Sent, 2>({2, 1}, is_odd, 1);
+ // 2-element sequence, already in order.
+ test_one<Iter, Sent, 2>({1, 2}, is_odd, 1);
+ // 3-element sequence.
+ test_one<Iter, Sent, 3>({2, 1, 3}, is_odd, 2);
+ // Longer sequence.
+ test_one<Iter, Sent, 8>({2, 1, 3, 6, 8, 4, 11, 5}, is_odd, 4);
+ // Longer sequence with duplicates.
+ test_one<Iter, Sent, 8>({2, 1, 3, 6, 2, 8, 1, 6}, is_odd, 3);
+ // All elements are the same and satisfy the predicate.
+ test_one<Iter, Sent, 3>({1, 1, 1}, is_odd, 3);
+ // All elements are the same and don't satisfy the predicate.
+ test_one<Iter, Sent, 3>({2, 2, 2}, is_odd, 0);
+ // Already partitioned.
+ test_one<Iter, Sent, 6>({1, 3, 5, 4, 6, 8}, is_odd, 3);
+ // Reverse-partitioned.
+ test_one<Iter, Sent, 6>({4, 6, 8, 1, 3, 5}, is_odd, 3);
+ // Repeating pattern.
+ test_one<Iter, Sent, 6>({1, 2, 1, 2, 1, 2}, is_odd, 3);
+}
+
+template <class Iter>
+constexpr void test_iterators_1() {
+ test_iterators_2<Iter, Iter>();
+ test_iterators_2<Iter, sentinel_wrapper<Iter>>();
+}
+
+constexpr void test_iterators() {
+ test_iterators_1<forward_iterator<int*>>();
+ test_iterators_1<bidirectional_iterator<int*>>();
+ test_iterators_1<random_access_iterator<int*>>();
+ test_iterators_1<contiguous_iterator<int*>>();
+ test_iterators_1<int*>();
+}
constexpr bool test() {
- // TODO: main tests.
- // TODO: A custom comparator works.
- // TODO: A custom projection works.
+ test_iterators();
+
+ { // A custom projection works.
+ const std::array input = {1, -1};
+ auto is_negative = [](int x) { return x < 0; };
+ auto negate = [](int x) { return -x; };
+ const std::array expected_no_proj = {-1, 1};
+ const std::array expected_with_proj = {1, -1};
+
+ { // (iterator, sentinel) overload.
+ {
+ auto in = input;
+ std::ranges::partition(in.begin(), in.end(), is_negative);
+ assert(in == expected_no_proj);
+ }
+ {
+ auto in = input;
+ std::ranges::partition(in.begin(), in.end(), is_negative, negate);
+ assert(in == expected_with_proj);
+ }
+ }
+
+ { // (range) overload.
+ {
+ auto in = input;
+ std::ranges::partition(in, is_negative);
+ assert(in == expected_no_proj);
+ }
+ {
+ auto in = input;
+ std::ranges::partition(in, is_negative, negate);
+ assert(in == expected_with_proj);
+ }
+ }
+ }
return true;
}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
index e5a9bcb08ac93..6fcaa8e621cb2 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/ranges_stable_partition.pass.cpp
@@ -30,19 +30,216 @@
#include "almost_satisfies_types.h"
#include "test_iterators.h"
-// TODO: SFINAE tests.
+struct UnaryPred { bool operator()(int) const; };
-constexpr bool test() {
- // TODO: main tests.
- // TODO: A custom comparator works.
- // TODO: A custom projection works.
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
- return true;
+template <class Iter = int*, class Sent = int*, class Pred = UnaryPred>
+concept HasStablePartitionIter =
+ requires(Iter&& iter, Sent&& sent, Pred&& pred) {
+ std::ranges::stable_partition(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Pred>(pred));
+ };
+
+static_assert(HasStablePartitionIter<int*, int*, UnaryPred>);
+
+// !bidirectional_iterator<I>
+static_assert(!HasStablePartitionIter<BidirectionalIteratorNotDerivedFrom>);
+static_assert(!HasStablePartitionIter<BidirectionalIteratorNotDecrementable>);
+
+// !sentinel_for<S, I>
+static_assert(!HasStablePartitionIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasStablePartitionIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+// !indirect_unary_predicate<projected<I, Proj>>
+static_assert(!HasStablePartitionIter<int*, int*, IndirectUnaryPredicateNotPredicate>);
+static_assert(!HasStablePartitionIter<int*, int*, IndirectUnaryPredicateNotCopyConstructible>);
+
+// !permutable<I>
+static_assert(!HasStablePartitionIter<PermutableNotForwardIterator>);
+static_assert(!HasStablePartitionIter<PermutableNotSwappable>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range, class Pred>
+concept HasStablePartitionRange =
+ requires(Range&& range, Pred&& pred) {
+ std::ranges::stable_partition(std::forward<Range>(range), std::forward<Pred>(pred));
+ };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasStablePartitionRange<R<int*>, UnaryPred>);
+
+// !bidirectional_range<R>
+static_assert(!HasStablePartitionRange<BidirectionalRangeNotDerivedFrom, UnaryPred>);
+static_assert(!HasStablePartitionRange<BidirectionalRangeNotDecrementable, UnaryPred>);
+
+// !indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+static_assert(!HasStablePartitionRange<R<int*>, IndirectUnaryPredicateNotPredicate>);
+static_assert(!HasStablePartitionRange<R<int*>, IndirectUnaryPredicateNotCopyConstructible>);
+
+// !permutable<iterator_t<R>>
+static_assert(!HasStablePartitionRange<R<PermutableNotForwardIterator>, UnaryPred>);
+static_assert(!HasStablePartitionRange<R<PermutableNotSwappable>, UnaryPred>);
+
+template <class Iter, class Sent, size_t N, class Pred>
+void test_one(std::array<int, N> input, Pred pred, size_t partition_point, std::array<int, N> expected) {
+ auto neg_pred = [&](int x) { return !pred(x); };
+
+ { // (iterator, sentinel) overload.
+ auto partitioned = input;
+ auto b = Iter(partitioned.data());
+ auto e = Sent(Iter(partitioned.data() + partitioned.size()));
+
+ std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::stable_partition(b, e, pred);
+
+ assert(partitioned == expected);
+ assert(base(result.begin()) == partitioned.data() + partition_point);
+ assert(base(result.end()) == partitioned.data() + partitioned.size());
+
+ assert(std::ranges::all_of(b, result.begin(), pred));
+ assert(std::ranges::all_of(result.begin(), e, neg_pred));
+ }
+
+ { // (range) overload.
+ auto partitioned = input;
+ auto b = Iter(partitioned.data());
+ auto e = Sent(Iter(partitioned.data() + partitioned.size()));
+ auto range = std::ranges::subrange(b, e);
+
+ std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::stable_partition(range, pred);
+
+ assert(partitioned == expected);
+ assert(base(result.begin()) == partitioned.data() + partition_point);
+ assert(base(result.end()) == partitioned.data() + partitioned.size());
+
+ assert(std::ranges::all_of(b, result.begin(), pred));
+ assert(std::ranges::all_of(result.begin(), e, neg_pred));
+ }
+}
+
+template <class Iter, class Sent>
+void test_iterators_2() {
+ auto is_odd = [](int x) { return x % 2 != 0; };
+
+ // Empty sequence.
+ test_one<Iter, Sent, 0>({}, is_odd, 0, {});
+ // 1-element sequence, the element satisfies the predicate.
+ test_one<Iter, Sent, 1>({1}, is_odd, 1, {1});
+ // 1-element sequence, the element doesn't satisfy the predicate.
+ test_one<Iter, Sent, 1>({2}, is_odd, 0, {2});
+ // 2-element sequence, not in order.
+ test_one<Iter, Sent, 2>({2, 1}, is_odd, 1, {1, 2});
+ // 2-element sequence, already in order.
+ test_one<Iter, Sent, 2>({1, 2}, is_odd, 1, {1, 2});
+ // 3-element sequence.
+ test_one<Iter, Sent, 3>({2, 1, 3}, is_odd, 2, {1, 3, 2});
+ // Longer sequence.
+ test_one<Iter, Sent, 8>({2, 1, 3, 6, 8, 4, 11, 5}, is_odd, 4, {1, 3, 11, 5, 2, 6, 8, 4});
+ // Longer sequence with duplicates.
+ test_one<Iter, Sent, 8>({2, 1, 3, 6, 2, 8, 1, 6}, is_odd, 3, {1, 3, 1, 2, 6, 2, 8, 6});
+ // All elements are the same and satisfy the predicate.
+ test_one<Iter, Sent, 3>({1, 1, 1}, is_odd, 3, {1, 1, 1});
+ // All elements are the same and don't satisfy the predicate.
+ test_one<Iter, Sent, 3>({2, 2, 2}, is_odd, 0, {2, 2, 2});
+ // Already partitioned.
+ test_one<Iter, Sent, 6>({1, 3, 5, 4, 6, 8}, is_odd, 3, {1, 3, 5, 4, 6, 8});
+ // Reverse-partitioned.
+ test_one<Iter, Sent, 6>({4, 6, 8, 1, 3, 5}, is_odd, 3, {1, 3, 5, 4, 6, 8});
+ // Repeating pattern.
+ test_one<Iter, Sent, 6>({1, 2, 1, 2, 1, 2}, is_odd, 3, {1, 1, 1, 2, 2, 2});
+}
+
+template <class Iter>
+void test_iterators_1() {
+ test_iterators_2<Iter, Iter>();
+ test_iterators_2<Iter, sentinel_wrapper<Iter>>();
+}
+
+void test_iterators() {
+ test_iterators_1<bidirectional_iterator<int*>>();
+ test_iterators_1<random_access_iterator<int*>>();
+ test_iterators_1<contiguous_iterator<int*>>();
+ test_iterators_1<int*>();
+}
+
+void test() {
+ test_iterators();
+
+ { // The algorithm is stable (equivalent elements remain in the same order).
+ struct OrderedValue {
+ int value;
+ double original_order;
+ bool operator==(const OrderedValue&) const = default;
+ };
+
+ auto is_odd = [](OrderedValue x) { return x.value % 2 != 0; };
+
+ using V = OrderedValue;
+ using Array = std::array<V, 20>;
+ Array orig_in = {
+ V{10, 2.1}, {12, 2.2}, {3, 1.1}, {5, 1.2}, {3, 1.3}, {3, 1.4}, {11, 1.5}, {12, 2.3}, {4, 2.4}, {4, 2.5},
+ {4, 2.6}, {1, 1.6}, {6, 2.7}, {3, 1.7}, {10, 2.8}, {8, 2.9}, {12, 2.10}, {1, 1.8}, {1, 1.9}, {5, 1.10}
+ };
+ Array expected = {
+ V{3, 1.1}, {5, 1.2}, {3, 1.3}, {3, 1.4}, {11, 1.5}, {1, 1.6}, {3, 1.7}, {1, 1.8}, {1, 1.9}, {5, 1.10},
+ {10, 2.1}, {12, 2.2}, {12, 2.3}, {4, 2.4}, {4, 2.5}, {4, 2.6}, {6, 2.7}, {10, 2.8}, {8, 2.9}, {12, 2.10}
+ };
+
+ {
+ auto in = orig_in;
+ std::ranges::stable_partition(in.begin(), in.end(), is_odd);
+ assert(in == expected);
+ }
+
+ {
+ auto in = orig_in;
+ std::ranges::stable_partition(in, is_odd);
+ assert(in == expected);
+ }
+ }
+
+ { // A custom projection works.
+ const std::array input = {1, -1};
+ auto is_negative = [](int x) { return x < 0; };
+ auto negate = [](int x) { return -x; };
+ const std::array expected_no_proj = {-1, 1};
+ const std::array expected_with_proj = {1, -1};
+
+ { // (iterator, sentinel) overload.
+ {
+ auto in = input;
+ std::ranges::partition(in.begin(), in.end(), is_negative);
+ assert(in == expected_no_proj);
+ }
+ {
+ auto in = input;
+ std::ranges::partition(in.begin(), in.end(), is_negative, negate);
+ assert(in == expected_with_proj);
+ }
+ }
+
+ { // (range) overload.
+ {
+ auto in = input;
+ std::ranges::partition(in, is_negative);
+ assert(in == expected_no_proj);
+ }
+ {
+ auto in = input;
+ std::ranges::partition(in, is_negative, negate);
+ assert(in == expected_with_proj);
+ }
+ }
+ }
}
int main(int, char**) {
test();
- static_assert(test());
+ // Note: `stable_partition` is not `constexpr`.
return 0;
}
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp
index cd8bde50b58ac..439a16b103cec 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.compile.pass.cpp
@@ -156,8 +156,8 @@ void test_all() {
in2_out_pred(std::ranges::set_union, in, in2, out, binary_pred);
in_pred(std::ranges::remove_if, in, unary_pred);
//in_pred(std::ranges::unique, in, binary_pred);
- //in_pred(std::ranges::partition, in, binary_pred);
- //in_pred(std::ranges::stable_partition, in, binary_pred);
+ in_pred(std::ranges::partition, in, unary_pred);
+ in_pred(std::ranges::stable_partition, in, unary_pred);
in_pred(std::ranges::sort, in, binary_pred);
in_pred(std::ranges::stable_sort, in, binary_pred);
//in_mid_pred(std::ranges::partial_sort, in, mid, binary_pred);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp
index 48b20348a295a..7709b88536bbd 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.compile.pass.cpp
@@ -201,8 +201,8 @@ void test_all() {
// `rotate` has neither a projection nor a predicate.
// `shuffle` has neither a projection nor a predicate.
//in_pred(std::ranges::unique, in, &Foo::binary_pred, &Bar::val);
- //in_pred(std::ranges::partition, in, &Foo::binary_pred, &Bar::val);
- //in_pred(std::ranges::stable_partition, in, &Foo::binary_pred, &Bar::val);
+ in_pred(std::ranges::partition, in, &Foo::unary_pred, &Bar::val);
+ in_pred(std::ranges::stable_partition, in, &Foo::unary_pred, &Bar::val);
in_pred(std::ranges::sort, in, &Foo::binary_pred, &Bar::val);
in_pred(std::ranges::stable_sort, in, &Foo::binary_pred, &Bar::val);
//in_mid_pred(std::ranges::partial_sort, in, mid, binary_pred);
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
index f6eaf15862e3c..fda245d5a6700 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
@@ -138,9 +138,9 @@ constexpr bool test_all() {
//test_mid(std::ranges::rotate, in, mid);
//test(std::ranges::shuffle, in, rand_gen);
//test(std::ranges::unique, in);
- //test(std::ranges::partition, in, binary_pred);
- //if (!std::is_constant_evaluated())
- // test(std::ranges::stable_partition, in, binary_pred);
+ test(std::ranges::partition, in, unary_pred);
+ if (!std::is_constant_evaluated())
+ test(std::ranges::stable_partition, in, unary_pred);
test(std::ranges::sort, in);
// TODO: `stable_sort` requires `ranges::rotate` to be implemented.
//if (!std::is_constant_evaluated())
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index fec9bfe6a0fb9..84fec22ca9d63 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -112,7 +112,7 @@ static_assert(test(std::ranges::none_of, a, odd));
static_assert(test(std::ranges::nth_element, a, a+5));
//static_assert(test(std::ranges::partial_sort, a, a+5));
//static_assert(test(std::ranges::partial_sort_copy, a, a));
-//static_assert(test(std::ranges::partition, a, odd));
+static_assert(test(std::ranges::partition, a, odd));
//static_assert(test(std::ranges::partition_copy, a, a, a, odd));
//static_assert(test(std::ranges::partition_point, a, odd));
static_assert(test(std::ranges::pop_heap, a));
@@ -140,7 +140,7 @@ static_assert(test(std::ranges::set_union, a, a, a));
//static_assert(test(std::ranges::shuffle, a, g));
static_assert(test(std::ranges::sort, a));
static_assert(test(std::ranges::sort_heap, a));
-//static_assert(test(std::ranges::stable_partition, a, odd));
+static_assert(test(std::ranges::stable_partition, a, odd));
static_assert(test(std::ranges::stable_sort, a));
//static_assert(test(std::ranges::starts_with, a, a));
static_assert(test(std::ranges::swap_ranges, a, a));
More information about the libcxx-commits
mailing list