diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -10,7 +10,7 @@ Search,mismatch,Nikolas Klauser,`D117817 `_,✅ Search,equal,Nikolas Klauser,`D123681 `_,✅ Search,lexicographical_compare,Nikolas Klauser,`D127130 `_,✅ -Search,partition_point,Christopher Di Bella,`D105794 `_,Under review +Search,partition_point,Konstantin Varlamov,n/a,In progress Search,lower_bound,Nikolas Klauser,`D121964 `_,✅ Search,upper_bound,Nikolas Klauser,`D121964 `_,✅ Search,equal_range,Christopher Di Bella,n/a,Not started @@ -58,7 +58,7 @@ Write,rotate_copy,Nikolas Klauser,`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 `_,✅ Merge,set_difference,Hui Xie,`D128983 `,✅ @@ -71,8 +71,8 @@ Permutation,rotate,Nikolas Klauser,`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 `_,✅ +Permutation,stable_partition,Konstantin Varlamov,`D129624 `_,✅ Permutation,sort,Konstantin Varlamov,`D127557 `_,✅ Permutation,stable_sort,Konstantin Varlamov,`D127834 `_,✅ Permutation,partial_sort,Konstantin Varlamov,n/a,In progress diff --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h --- a/libcxx/include/__algorithm/make_projected.h +++ b/libcxx/include/__algorithm/make_projected.h @@ -27,6 +27,21 @@ namespace ranges { +template +_LIBCPP_HIDE_FROM_ABI constexpr static +decltype(auto) __make_projected_pred(_Pred& __pred, _Proj& __proj) { + if constexpr (same_as, identity> && !is_member_pointer_v>) { + // 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(__x))); + }; + } +} + template _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 --- 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 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -19,40 +22,45 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template -_LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator -__partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred, forward_iterator_tag) +template +_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::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::move(__first), std::move(__p)}; } -template -_LIBCPP_CONSTEXPR_AFTER_CXX17 _BidirectionalIterator -__partition(_BidirectionalIterator __first, _BidirectionalIterator __last, _Predicate __pred, +template +_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::move(__first), std::move(__original_last)}; if (!__pred(*__first)) break; ++__first; @@ -60,20 +68,27 @@ do { if (__first == --__last) - return __first; + return {std::move(__first), std::move(__original_last)}; } while (!__pred(*__last)); - swap(*__first, *__last); + _IterOps<_AlgPolicy>::iter_swap(__first, __last); ++__first; } } +template +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 +pair<_ForwardIterator, _ForwardIterator> __partition(_ForwardIterator __first, _Sentinel __last, _Predicate&& __pred) { + return std::__partition_impl<__uncvref_t<_Predicate>&, _AlgPolicy>( + std::move(__first), std::move(__last), __pred, typename iterator_traits<_ForwardIterator>::iterator_category()); +} + template 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()); + auto __result = std::__partition<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred); + return __result.first; } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__algorithm/ranges_partition.h b/libcxx/include/__algorithm/ranges_partition.h --- a/libcxx/include/__algorithm/ranges_partition.h +++ b/libcxx/include/__algorithm/ranges_partition.h @@ -25,6 +25,7 @@ #include <__ranges/subrange.h> #include <__utility/forward.h> #include <__utility/move.h> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -39,13 +40,20 @@ struct __fn { + template + _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); + + return {std::move(__result.first), std::move(__result.second)}; + } + template _Sent, class _Proj = identity, indirect_unary_predicate> _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 > _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 --- a/libcxx/include/__algorithm/ranges_stable_partition.h +++ b/libcxx/include/__algorithm/ranges_stable_partition.h @@ -17,6 +17,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 +26,7 @@ #include <__ranges/subrange.h> #include <__utility/forward.h> #include <__utility/move.h> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -39,14 +41,24 @@ struct __fn { + template + _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); + + return {std::move(__result), std::move(__last_iter)}; + } + template _Sent, class _Proj = identity, indirect_unary_predicate> _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 > _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/stable_partition.h b/libcxx/include/__algorithm/stable_partition.h --- 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 +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -23,9 +24,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template +template _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) { // *__first is known to be false @@ -50,7 +51,7 @@ // 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(_IterOps<_AlgPolicy>::__iter_move(__first)); __d.template __incr(); ++__t; _ForwardIterator __i = __first; @@ -58,12 +59,12 @@ { if (__pred(*__i)) { - *__first = _VSTD::move(*__i); + *__first = _IterOps<_AlgPolicy>::__iter_move(__i); ++__first; } else { - ::new ((void*)__t) value_type(_VSTD::move(*__i)); + ::new ((void*)__t) value_type(_IterOps<_AlgPolicy>::__iter_move(__i)); __d.template __incr(); ++__t; } @@ -72,7 +73,7 @@ // 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 = _IterOps<_AlgPolicy>::__iter_move(__t2); // __h destructs moved-from values out of the temp buffer, but doesn't deallocate buffer return __first; } @@ -80,11 +81,12 @@ // __len >= 3 _ForwardIterator __m = __first; _Distance __len2 = __len / 2; // __len2 >= 2 - _VSTD::advance(__m, __len2); + _IterOps<_AlgPolicy>::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,7 +101,8 @@ } // 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 @@ -108,9 +111,9 @@ // | } -template +template _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 +130,7 @@ // *__first is known to be false typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type; typedef typename iterator_traits<_ForwardIterator>::value_type value_type; - difference_type __len = _VSTD::distance(__first, __last); + difference_type __len = _IterOps<_AlgPolicy>::distance(__first, __last); pair __p(0, 0); unique_ptr __h; if (__len >= __alloc_limit) @@ -138,12 +141,13 @@ _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 +template _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) { // *__first is known to be false @@ -175,7 +179,7 @@ // 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(_IterOps<_AlgPolicy>::__iter_move(__first)); __d.template __incr(); ++__t; _BidirectionalIterator __i = __first; @@ -183,23 +187,23 @@ { if (__pred(*__i)) { - *__first = _VSTD::move(*__i); + *__first = _IterOps<_AlgPolicy>::__iter_move(__i); ++__first; } else { - ::new ((void*)__t) value_type(_VSTD::move(*__i)); + ::new ((void*)__t) value_type(_IterOps<_AlgPolicy>::__iter_move(__i)); __d.template __incr(); ++__t; } } // move *__last, known to be true - *__first = _VSTD::move(*__i); + *__first = _IterOps<_AlgPolicy>::__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 = _IterOps<_AlgPolicy>::__iter_move(__t2); // __h destructs moved-from values out of the temp buffer, but doesn't deallocate buffer return __first; } @@ -207,7 +211,7 @@ // __len >= 4 _BidirectionalIterator __m = __first; _Distance __len2 = __len / 2; // __len2 >= 2 - _VSTD::advance(__m, __len2); + _IterOps<_AlgPolicy>::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 +226,8 @@ } // 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,7 +244,8 @@ } // 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 @@ -248,9 +254,9 @@ // | } -template +template _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>::difference_type difference_type; @@ -276,7 +282,7 @@ // *__first is known to be false // *__last is known to be true // __len >= 2 - difference_type __len = _VSTD::distance(__first, __last) + 1; + difference_type __len = _IterOps<_AlgPolicy>::distance(__first, __last) + 1; pair __p(0, 0); unique_ptr __h; if (__len >= __alloc_limit) @@ -287,7 +293,15 @@ _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 +_LIBCPP_HIDE_FROM_ABI _ForwardIterator +__stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate&& __pred) { + return std::__stable_partition_impl<_AlgPolicy, __uncvref_t<_Predicate>&>( + std::move(__first), std::move(__last), __pred, typename iterator_traits<_ForwardIterator>::iterator_category()); } template @@ -295,7 +309,7 @@ _ForwardIterator stable_partition(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred) { - return _VSTD::__stable_partition<_Predicate&>(__first, __last, __pred, typename iterator_traits<_ForwardIterator>::iterator_category()); + return std::__stable_partition<_ClassicAlgPolicy, _Predicate&>(std::move(__first), std::move(__last), __pred); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -464,6 +464,28 @@ ranges::less> constexpr bool binary_search(R&& r, const T& value, Comp comp = {}, Proj proj = {}); // since C++20 + + template S, class Proj = identity, + indirect_unary_predicate> Pred> + constexpr subrange + partition(I first, S last, Pred pred, Proj proj = {}); // Since C++20 + + template, Proj>> Pred> + requires permutable> + constexpr borrowed_subrange_t + partition(R&& r, Pred pred, Proj proj = {}); // Since C++20 + + template S, class Proj = identity, + indirect_unary_predicate> Pred> + requires permutable + subrange stable_partition(I first, S last, Pred pred, Proj proj = {}); // Since C++20 + + template, Proj>> Pred> + requires permutable> + borrowed_subrange_t stable_partition(R&& r, Pred pred, Proj proj = {}); // Since C++20 + template S1, forward_iterator I2, sentinel_for S2, class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity> requires indirectly_comparable @@ -1415,6 +1437,7 @@ #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> @@ -1428,6 +1451,7 @@ #include <__algorithm/ranges_set_intersection.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/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp --- 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 @@ //(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 @@ (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 --- 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 @@ //(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 @@ (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/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 --- 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 +concept HasPartitionIter = + requires(Iter&& iter, Sent&& sent, Pred&& pred) { + std::ranges::partition(std::forward(iter), std::forward(sent), std::forward(pred)); + }; + +static_assert(HasPartitionIter); + +// !permutable +static_assert(!HasPartitionIter); +static_assert(!HasPartitionIter); + +// !sentinel_for +static_assert(!HasPartitionIter); +static_assert(!HasPartitionIter); + +// !indirect_unary_predicate> +static_assert(!HasPartitionIter); +static_assert(!HasPartitionIter); + +// Test constraints of the (range) overload. +// ========================================= + +template +concept HasPartitionRange = + requires(Range&& range, Pred&& pred) { + std::ranges::partition(std::forward(range), std::forward(pred)); + }; + +template +using R = UncheckedRange; + +static_assert(HasPartitionRange, UnaryPred>); + +// !forward_range +static_assert(!HasPartitionRange); +static_assert(!HasPartitionRange); + +// !indirect_unary_predicate, Proj>> Pred> +static_assert(!HasPartitionRange, IndirectUnaryPredicateNotPredicate>); +static_assert(!HasPartitionRange, IndirectUnaryPredicateNotCopyConstructible>); + +// !permutable> +static_assert(!HasPartitionRange, UnaryPred>); +static_assert(!HasPartitionRange, UnaryPred>); + +// `partition` isn't a stable algorithm so this function cannot test the exact output. +template +constexpr void test_one(std::array 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> 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> 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 +constexpr void test_iterators_2() { + auto is_odd = [](int x) { return x % 2 != 0; }; + + // Empty sequence. + test_one({}, is_odd, 0); + // 1-element sequence, the element satisfies the predicate. + test_one({1}, is_odd, 1); + // 1-element sequence, the element doesn't satisfy the predicate. + test_one({2}, is_odd, 0); + // 2-element sequence, not in order. + test_one({2, 1}, is_odd, 1); + // 2-element sequence, already in order. + test_one({1, 2}, is_odd, 1); + // 3-element sequence. + test_one({2, 1, 3}, is_odd, 2); + // Longer sequence. + test_one({2, 1, 3, 6, 8, 4, 11, 5}, is_odd, 4); + // Longer sequence with duplicates. + test_one({2, 1, 3, 6, 2, 8, 1, 6}, is_odd, 3); + // All elements are the same and satisfy the predicate. + test_one({1, 1, 1}, is_odd, 3); + // All elements are the same and don't satisfy the predicate. + test_one({2, 2, 2}, is_odd, 0); + // Already partitioned. + test_one({1, 3, 5, 4, 6, 8}, is_odd, 3); + // Reverse-partitioned. + test_one({4, 6, 8, 1, 3, 5}, is_odd, 3); + // Repeating pattern. + test_one({1, 2, 1, 2, 1, 2}, is_odd, 3); +} + +template +constexpr void test_iterators_1() { + test_iterators_2(); + test_iterators_2>(); +} + +constexpr void test_iterators() { + test_iterators_1>(); + test_iterators_1>(); + test_iterators_1>(); + test_iterators_1>(); + test_iterators_1(); +} 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 --- 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 +concept HasStablePartitionIter = + requires(Iter&& iter, Sent&& sent, Pred&& pred) { + std::ranges::stable_partition(std::forward(iter), std::forward(sent), std::forward(pred)); + }; + +static_assert(HasStablePartitionIter); + +// !bidirectional_iterator +static_assert(!HasStablePartitionIter); +static_assert(!HasStablePartitionIter); + +// !sentinel_for +static_assert(!HasStablePartitionIter); +static_assert(!HasStablePartitionIter); + +// !indirect_unary_predicate> +static_assert(!HasStablePartitionIter); +static_assert(!HasStablePartitionIter); + +// !permutable +static_assert(!HasStablePartitionIter); +static_assert(!HasStablePartitionIter); + +// Test constraints of the (range) overload. +// ========================================= + +template +concept HasStablePartitionRange = + requires(Range&& range, Pred&& pred) { + std::ranges::stable_partition(std::forward(range), std::forward(pred)); + }; + +template +using R = UncheckedRange; + +static_assert(HasStablePartitionRange, UnaryPred>); + +// !bidirectional_range +static_assert(!HasStablePartitionRange); +static_assert(!HasStablePartitionRange); + +// !indirect_unary_predicate, Proj>> Pred> +static_assert(!HasStablePartitionRange, IndirectUnaryPredicateNotPredicate>); +static_assert(!HasStablePartitionRange, IndirectUnaryPredicateNotCopyConstructible>); + +// !permutable> +static_assert(!HasStablePartitionRange, UnaryPred>); +static_assert(!HasStablePartitionRange, UnaryPred>); + +template +void test_one(std::array input, Pred pred, size_t partition_point, std::array 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> 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> 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 +void test_iterators_2() { + auto is_odd = [](int x) { return x % 2 != 0; }; + + // Empty sequence. + test_one({}, is_odd, 0, {}); + // 1-element sequence, the element satisfies the predicate. + test_one({1}, is_odd, 1, {1}); + // 1-element sequence, the element doesn't satisfy the predicate. + test_one({2}, is_odd, 0, {2}); + // 2-element sequence, not in order. + test_one({2, 1}, is_odd, 1, {1, 2}); + // 2-element sequence, already in order. + test_one({1, 2}, is_odd, 1, {1, 2}); + // 3-element sequence. + test_one({2, 1, 3}, is_odd, 2, {1, 3, 2}); + // Longer sequence. + test_one({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({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({1, 1, 1}, is_odd, 3, {1, 1, 1}); + // All elements are the same and don't satisfy the predicate. + test_one({2, 2, 2}, is_odd, 0, {2, 2, 2}); + // Already partitioned. + test_one({1, 3, 5, 4, 6, 8}, is_odd, 3, {1, 3, 5, 4, 6, 8}); + // Reverse-partitioned. + test_one({4, 6, 8, 1, 3, 5}, is_odd, 3, {1, 3, 5, 4, 6, 8}); + // Repeating pattern. + test_one({1, 2, 1, 2, 1, 2}, is_odd, 3, {1, 1, 1, 2, 2, 2}); +} + +template +void test_iterators_1() { + test_iterators_2(); + test_iterators_2>(); +} + +void test_iterators() { + test_iterators_1>(); + test_iterators_1>(); + test_iterators_1>(); + test_iterators_1(); +} + +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; + 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 --- 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 @@ //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 --- 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 @@ // `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/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp --- 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::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::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));