[libcxx-commits] [libcxx] db7d795 - [libc++][ranges] Implement `std::ranges::partial_sort_copy`.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 30 02:44:34 PDT 2022


Author: Konstantin Varlamov
Date: 2022-07-30T02:42:18-07:00
New Revision: db7d7959787ed68f037e2a6e5a70bb0d8c17ab27

URL: https://github.com/llvm/llvm-project/commit/db7d7959787ed68f037e2a6e5a70bb0d8c17ab27
DIFF: https://github.com/llvm/llvm-project/commit/db7d7959787ed68f037e2a6e5a70bb0d8c17ab27.diff

LOG: [libc++][ranges] Implement `std::ranges::partial_sort_copy`.

Differential Revision: https://reviews.llvm.org/D130532

Added: 
    

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/__algorithm/make_heap.h
    libcxx/include/__algorithm/make_projected.h
    libcxx/include/__algorithm/partial_sort.h
    libcxx/include/__algorithm/partial_sort_copy.h
    libcxx/include/__algorithm/pop_heap.h
    libcxx/include/__algorithm/push_heap.h
    libcxx/include/__algorithm/ranges_inplace_merge.h
    libcxx/include/__algorithm/ranges_is_heap.h
    libcxx/include/__algorithm/ranges_is_heap_until.h
    libcxx/include/__algorithm/ranges_make_heap.h
    libcxx/include/__algorithm/ranges_nth_element.h
    libcxx/include/__algorithm/ranges_partial_sort.h
    libcxx/include/__algorithm/ranges_partial_sort_copy.h
    libcxx/include/__algorithm/ranges_partition.h
    libcxx/include/__algorithm/ranges_pop_heap.h
    libcxx/include/__algorithm/ranges_push_heap.h
    libcxx/include/__algorithm/ranges_sort.h
    libcxx/include/__algorithm/ranges_sort_heap.h
    libcxx/include/__algorithm/ranges_stable_partition.h
    libcxx/include/__algorithm/ranges_stable_sort.h
    libcxx/include/__algorithm/ranges_unique.h
    libcxx/include/__algorithm/ranges_unique_copy.h
    libcxx/include/__algorithm/sift_down.h
    libcxx/include/__algorithm/sort_heap.h
    libcxx/include/algorithm
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort.copy/ranges_partial_sort_copy.pass.cpp
    libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_differing_projections.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.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 176ece9ae0473..f805d0e44369f 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -59,7 +59,7 @@ Write,rotate_copy,Nikolas Klauser,`D127211 <https://llvm.org/D127211>`_,✅
 Write,sample,Not assigned,n/a,Not started
 Write,unique_copy,Hui Xie,`D130404 <https://llvm.org/D130404>`,✅
 Write,partition_copy,Konstantin Varlamov,`D130070 <https://llvm.org/D130070>`_,✅
-Write,partial_sort_copy,Konstantin Varlamov,n/a,In progress
+Write,partial_sort_copy,Konstantin Varlamov,`D130532 <https://llvm.org/D130532>`_,✅
 Merge,merge,Hui Xie,`D128611 <https://llvm.org/D128611>`_,✅
 Merge,set_
diff erence,Hui Xie,`D128983 <https://llvm.org/D128983>`_,✅
 Merge,set_intersection,Hui Xie,`D129233 <https://llvm.org/D129233>`_,✅

diff  --git a/libcxx/include/__algorithm/make_heap.h b/libcxx/include/__algorithm/make_heap.h
index bf9dd96756af1..0aa67d18ed0ac 100644
--- a/libcxx/include/__algorithm/make_heap.h
+++ b/libcxx/include/__algorithm/make_heap.h
@@ -25,7 +25,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-void __make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare& __comp) {
+void __make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
   using _CompRef = typename __comp_ref_type<_Compare>::type;
   _CompRef __comp_ref = __comp;
 
@@ -34,7 +34,7 @@ void __make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _C
   if (__n > 1) {
     // start from the first parent, there is no need to consider children
     for (
diff erence_type __start = (__n - 2) / 2; __start >= 0; --__start) {
-        std::__sift_down<_AlgPolicy, _CompRef>(__first, __comp_ref, __n, __first + __start);
+        std::__sift_down<_AlgPolicy>(__first, __comp_ref, __n, __first + __start);
     }
   }
 }

diff  --git a/libcxx/include/__algorithm/make_projected.h b/libcxx/include/__algorithm/make_projected.h
index 64fc3dfb6a12c..6c1d156776675 100644
--- a/libcxx/include/__algorithm/make_projected.h
+++ b/libcxx/include/__algorithm/make_projected.h
@@ -14,51 +14,91 @@
 #include <__functional/identity.h>
 #include <__functional/invoke.h>
 #include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
 #include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_same.h>
+#include <__utility/declval.h>
 #include <__utility/forward.h>
 
 #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 _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;
+struct _ProjectedPred {
+  _Pred& __pred; // Can be a unary or a binary predicate.
+  _Proj& __proj;
+
+  _LIBCPP_CONSTEXPR _ProjectedPred(_Pred& __pred_arg, _Proj& __proj_arg) : __pred(__pred_arg), __proj(__proj_arg) {}
+
+  template <class _Tp>
+  typename __invoke_of<_Pred&,
+                       decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_Tp>()))
+  >::type
+  _LIBCPP_CONSTEXPR operator()(_Tp&& __v) const {
+    return std::__invoke(__pred, std::__invoke(__proj, std::forward<_Tp>(__v)));
+  }
 
-  } else {
-    return [&](auto&& __x) {
-      return std::invoke(__pred, std::invoke(__proj, std::forward<decltype(__x)>(__x)));
-    };
+  template <class _T1, class _T2>
+  typename __invoke_of<_Pred&,
+                       decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T1>())),
+                       decltype(std::__invoke(std::declval<_Proj&>(), std::declval<_T2>()))
+  >::type
+  _LIBCPP_CONSTEXPR operator()(_T1&& __lhs, _T2&& __rhs) const {
+    return std::__invoke(__pred,
+                      std::__invoke(__proj, std::forward<_T1>(__lhs)),
+                      std::__invoke(__proj, std::forward<_T2>(__rhs)));
   }
-}
 
-template <class _Comp, class _Proj>
-_LIBCPP_HIDE_FROM_ABI constexpr static
-decltype(auto) __make_projected_comp(_Comp& __comp, _Proj& __proj) {
-  if constexpr (same_as<decay_t<_Proj>, identity> && !is_member_pointer_v<decay_t<_Comp>>) {
-    // Avoid creating the lambda and just use the pristine comparator -- for certain algorithms, this would enable
-    // optimizations that rely on the type of the comparator.
-    return __comp;
+};
 
-  } else {
-    return [&](auto&& __lhs, auto&& __rhs) {
-      return std::invoke(__comp,
-                        std::invoke(__proj, std::forward<decltype(__lhs)>(__lhs)),
-                        std::invoke(__proj, std::forward<decltype(__rhs)>(__rhs)));
-    };
-  }
+template <class _Pred, class _Proj, class = void>
+struct __can_use_pristine_comp : false_type {};
+
+template <class _Pred, class _Proj>
+struct __can_use_pristine_comp<_Pred, _Proj, __enable_if_t<
+    !is_member_pointer<typename decay<_Pred>::type>::value && (
+#if _LIBCPP_STD_VER > 17
+      is_same<typename decay<_Proj>::type, identity>::value ||
+#endif
+      is_same<typename decay<_Proj>::type, __identity>::value
+    )
+> > : true_type {};
+
+template <class _Pred, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static
+__enable_if_t<
+    !__can_use_pristine_comp<_Pred, _Proj>::value,
+    _ProjectedPred<_Pred, _Proj>
+>
+__make_projected(_Pred& __pred, _Proj& __proj) {
+  return _ProjectedPred<_Pred, _Proj>(__pred, __proj);
+}
+
+// Avoid creating the functor and just use the pristine comparator -- for certain algorithms, this would enable
+// optimizations that rely on the type of the comparator. Additionally, this results in less layers of indirection in
+// the call stack when the comparator is invoked, even in an unoptimized build.
+template <class _Pred, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static
+__enable_if_t<
+    __can_use_pristine_comp<_Pred, _Proj>::value,
+    _Pred&
+>
+__make_projected(_Pred& __pred, _Proj&) {
+  return __pred;
 }
 
+_LIBCPP_END_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+
 template <class _Comp, class _Proj1, class _Proj2>
 _LIBCPP_HIDE_FROM_ABI constexpr static
 decltype(auto) __make_projected_comp(_Comp& __comp, _Proj1& __proj1, _Proj2& __proj2) {

diff  --git a/libcxx/include/__algorithm/partial_sort.h b/libcxx/include/__algorithm/partial_sort.h
index 24016e5cf5a5d..dff0cd01f35a1 100644
--- a/libcxx/include/__algorithm/partial_sort.h
+++ b/libcxx/include/__algorithm/partial_sort.h
@@ -31,12 +31,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _AlgPolicy, class _Compare, class _RandomAccessIterator, class _Sentinel>
 _LIBCPP_CONSTEXPR_AFTER_CXX17
 _RandomAccessIterator __partial_sort_impl(
-    _RandomAccessIterator __first, _RandomAccessIterator __middle, _Sentinel __last, _Compare __comp) {
+    _RandomAccessIterator __first, _RandomAccessIterator __middle, _Sentinel __last, _Compare&& __comp) {
   if (__first == __middle) {
     return _IterOps<_AlgPolicy>::next(__middle, __last);
   }
 
-  std::__make_heap<_AlgPolicy, _Compare>(__first, __middle, __comp);
+  std::__make_heap<_AlgPolicy>(__first, __middle, __comp);
 
   typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len = __middle - __first;
   _RandomAccessIterator __i = __middle;
@@ -45,11 +45,11 @@ _RandomAccessIterator __partial_sort_impl(
       if (__comp(*__i, *__first))
       {
           _IterOps<_AlgPolicy>::iter_swap(__i, __first);
-          std::__sift_down<_AlgPolicy, _Compare>(__first, __comp, __len, __first);
+          std::__sift_down<_AlgPolicy>(__first, __comp, __len, __first);
       }
 
   }
-  std::__sort_heap<_AlgPolicy, _Compare>(std::move(__first), std::move(__middle), __comp);
+  std::__sort_heap<_AlgPolicy>(std::move(__first), std::move(__middle), __comp);
 
   return __i;
 }
@@ -64,7 +64,7 @@ _RandomAccessIterator __partial_sort(_RandomAccessIterator __first, _RandomAcces
   std::__debug_randomize_range<_AlgPolicy>(__first, __last);
 
   using _Comp_ref = typename __comp_ref_type<_Compare>::type;
-  auto __last_iter = std::__partial_sort_impl<_AlgPolicy, _Comp_ref>(__first, __middle, __last, __comp);
+  auto __last_iter = std::__partial_sort_impl<_AlgPolicy>(__first, __middle, __last, static_cast<_Comp_ref>(__comp));
 
   std::__debug_randomize_range<_AlgPolicy>(__middle, __last);
 

diff  --git a/libcxx/include/__algorithm/partial_sort_copy.h b/libcxx/include/__algorithm/partial_sort_copy.h
index 3556764e652d3..55edf31b0f921 100644
--- a/libcxx/include/__algorithm/partial_sort_copy.h
+++ b/libcxx/include/__algorithm/partial_sort_copy.h
@@ -13,10 +13,16 @@
 #include <__algorithm/comp_ref_type.h>
 #include <__algorithm/iterator_operations.h>
 #include <__algorithm/make_heap.h>
+#include <__algorithm/make_projected.h>
 #include <__algorithm/sift_down.h>
 #include <__algorithm/sort_heap.h>
 #include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
 #include <__iterator/iterator_traits.h>
+#include <__type_traits/is_callable.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -24,27 +30,33 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _AlgPolicy, class _Compare, class _InputIterator, class _RandomAccessIterator>
-_LIBCPP_CONSTEXPR_AFTER_CXX17 _RandomAccessIterator
-__partial_sort_copy(_InputIterator __first, _InputIterator __last,
-                    _RandomAccessIterator __result_first, _RandomAccessIterator __result_last, _Compare __comp)
+template <class _AlgPolicy, class _Compare,
+          class _InputIterator, class _Sentinel1, class _RandomAccessIterator, class _Sentinel2,
+          class _Proj1, class _Proj2>
+_LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_InputIterator, _RandomAccessIterator>
+__partial_sort_copy(_InputIterator __first, _Sentinel1 __last,
+                    _RandomAccessIterator __result_first, _Sentinel2 __result_last,
+                    _Compare&& __comp, _Proj1&& __proj1, _Proj2&& __proj2)
 {
     _RandomAccessIterator __r = __result_first;
+    auto&& __projected_comp = std::__make_projected(__comp, __proj2);
+
     if (__r != __result_last)
     {
         for (; __first != __last && __r != __result_last; ++__first, (void) ++__r)
             *__r = *__first;
-        std::__make_heap<_AlgPolicy, _Compare>(__result_first, __r, __comp);
+        std::__make_heap<_AlgPolicy>(__result_first, __r, __projected_comp);
         typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len = __r - __result_first;
         for (; __first != __last; ++__first)
-            if (__comp(*__first, *__result_first))
-            {
+            if (std::__invoke(__comp, std::__invoke(__proj1, *__first), std::__invoke(__proj2, *__result_first))) {
                 *__result_first = *__first;
-                std::__sift_down<_AlgPolicy, _Compare>(__result_first, __comp, __len, __result_first);
+                std::__sift_down<_AlgPolicy>(__result_first, __projected_comp, __len, __result_first);
             }
-        std::__sort_heap<_AlgPolicy, _Compare>(__result_first, __r, __comp);
+        std::__sort_heap<_AlgPolicy>(__result_first, __r, __projected_comp);
     }
-    return __r;
+
+    return pair<_InputIterator, _RandomAccessIterator>(
+        _IterOps<_AlgPolicy>::next(std::move(__first), std::move(__last)), std::move(__r));
 }
 
 template <class _InputIterator, class _RandomAccessIterator, class _Compare>
@@ -53,9 +65,13 @@ _RandomAccessIterator
 partial_sort_copy(_InputIterator __first, _InputIterator __last,
                   _RandomAccessIterator __result_first, _RandomAccessIterator __result_last, _Compare __comp)
 {
-    typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
-    return std::__partial_sort_copy<_ClassicAlgPolicy, _Comp_ref>(
-        __first, __last, __result_first, __result_last, __comp);
+  static_assert(__is_callable<_Compare, decltype(*__first), decltype(*__result_first)>::value,
+                "Comparator has to be callable");
+
+  using _Comp_ref = typename __comp_ref_type<_Compare>::type;
+  auto __result = std::__partial_sort_copy<_ClassicAlgPolicy>(__first, __last, __result_first, __result_last,
+      static_cast<_Comp_ref>(__comp), __identity(), __identity());
+  return __result.second;
 }
 
 template <class _InputIterator, class _RandomAccessIterator>

diff  --git a/libcxx/include/__algorithm/pop_heap.h b/libcxx/include/__algorithm/pop_heap.h
index 870af50c133e4..44d5d39726058 100644
--- a/libcxx/include/__algorithm/pop_heap.h
+++ b/libcxx/include/__algorithm/pop_heap.h
@@ -38,7 +38,7 @@ void __pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Co
   using value_type = typename iterator_traits<_RandomAccessIterator>::value_type;
   if (__len > 1) {
     value_type __top = _IterOps<_AlgPolicy>::__iter_move(__first);  // create a hole at __first
-    _RandomAccessIterator __hole = std::__floyd_sift_down<_AlgPolicy, _CompRef>(__first, __comp_ref, __len);
+    _RandomAccessIterator __hole = std::__floyd_sift_down<_AlgPolicy>(__first, __comp_ref, __len);
     --__last;
 
     if (__hole == __last) {
@@ -47,7 +47,7 @@ void __pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Co
       *__hole = _IterOps<_AlgPolicy>::__iter_move(__last);
       ++__hole;
       *__last = std::move(__top);
-      std::__sift_up<_AlgPolicy, _CompRef>(__first, __hole, __comp_ref, __hole - __first);
+      std::__sift_up<_AlgPolicy>(__first, __hole, __comp_ref, __hole - __first);
     }
   }
 }

diff  --git a/libcxx/include/__algorithm/push_heap.h b/libcxx/include/__algorithm/push_heap.h
index 716670b767887..72ad51e1a887f 100644
--- a/libcxx/include/__algorithm/push_heap.h
+++ b/libcxx/include/__algorithm/push_heap.h
@@ -25,7 +25,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-void __sift_up(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp,
+void __sift_up(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp,
         typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len) {
   using value_type = typename iterator_traits<_RandomAccessIterator>::value_type;
 

diff  --git a/libcxx/include/__algorithm/ranges_inplace_merge.h b/libcxx/include/__algorithm/ranges_inplace_merge.h
index 2152e6648c350..12c90908c2105 100644
--- a/libcxx/include/__algorithm/ranges_inplace_merge.h
+++ b/libcxx/include/__algorithm/ranges_inplace_merge.h
@@ -44,7 +44,7 @@ namespace __inplace_merge {
     __inplace_merge_impl(_Iter __first, _Iter __middle, _Sent __last, _Comp&& __comp, _Proj&& __proj) {
       auto __last_iter = ranges::next(__middle, __last);
       std::__inplace_merge<_RangeAlgPolicy>(
-          std::move(__first), std::move(__middle), __last_iter, ranges::__make_projected_comp(__comp, __proj));
+          std::move(__first), std::move(__middle), __last_iter, std::__make_projected(__comp, __proj));
       return __last_iter;
     }
 

diff  --git a/libcxx/include/__algorithm/ranges_is_heap.h b/libcxx/include/__algorithm/ranges_is_heap.h
index a3e86d1a8d724..0bb1dcda0e341 100644
--- a/libcxx/include/__algorithm/ranges_is_heap.h
+++ b/libcxx/include/__algorithm/ranges_is_heap.h
@@ -39,7 +39,7 @@ struct __fn {
   _LIBCPP_HIDE_FROM_ABI constexpr
   static bool __is_heap_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
 
     auto __result = std::__is_heap_until(std::move(__first), std::move(__last_iter), __projected_comp);
     return __result == __last;

diff  --git a/libcxx/include/__algorithm/ranges_is_heap_until.h b/libcxx/include/__algorithm/ranges_is_heap_until.h
index bcd33ad404e88..8a751fcc51302 100644
--- a/libcxx/include/__algorithm/ranges_is_heap_until.h
+++ b/libcxx/include/__algorithm/ranges_is_heap_until.h
@@ -40,7 +40,7 @@ struct __fn {
   _LIBCPP_HIDE_FROM_ABI constexpr
   static _Iter __is_heap_until_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
 
     return std::__is_heap_until(std::move(__first), std::move(__last_iter), __projected_comp);
   }

diff  --git a/libcxx/include/__algorithm/ranges_make_heap.h b/libcxx/include/__algorithm/ranges_make_heap.h
index 8eabdd12cd2ff..b114286a85cc8 100644
--- a/libcxx/include/__algorithm/ranges_make_heap.h
+++ b/libcxx/include/__algorithm/ranges_make_heap.h
@@ -45,7 +45,7 @@ struct __fn {
   _Iter __make_heap_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__make_heap<_RangeAlgPolicy>(std::move(__first), __last_iter, __projected_comp);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_nth_element.h b/libcxx/include/__algorithm/ranges_nth_element.h
index b15eb816b9189..ad63bd20fb477 100644
--- a/libcxx/include/__algorithm/ranges_nth_element.h
+++ b/libcxx/include/__algorithm/ranges_nth_element.h
@@ -44,7 +44,7 @@ struct __fn {
   _Iter __nth_element_fn_impl(_Iter __first, _Iter __nth, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__nth_element_impl<_RangeAlgPolicy>(std::move(__first), std::move(__nth), __last_iter, __projected_comp);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_partial_sort.h b/libcxx/include/__algorithm/ranges_partial_sort.h
index 5e82bc6fcc325..020e34925df7b 100644
--- a/libcxx/include/__algorithm/ranges_partial_sort.h
+++ b/libcxx/include/__algorithm/ranges_partial_sort.h
@@ -43,7 +43,7 @@ struct __fn {
   template <class _Iter, class _Sent, class _Comp, class _Proj>
   _LIBCPP_HIDE_FROM_ABI constexpr static
   _Iter __partial_sort_fn_impl(_Iter __first, _Iter __middle, _Sent __last, _Comp& __comp, _Proj& __proj) {
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     return std::__partial_sort<_RangeAlgPolicy>(std::move(__first), std::move(__middle), __last, __projected_comp);
   }
 

diff  --git a/libcxx/include/__algorithm/ranges_partial_sort_copy.h b/libcxx/include/__algorithm/ranges_partial_sort_copy.h
index 55ad2ca4e686b..271c347b7aa28 100644
--- a/libcxx/include/__algorithm/ranges_partial_sort_copy.h
+++ b/libcxx/include/__algorithm/ranges_partial_sort_copy.h
@@ -10,11 +10,11 @@
 #define _LIBCPP___ALGORITHM_RANGES_PARTIAL_SORT_COPY_H
 
 #include <__algorithm/in_out_result.h>
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/make_projected.h>
 #include <__algorithm/partial_sort_copy.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__functional/invoke.h>
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
@@ -23,7 +23,6 @@
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/dangling.h>
-#include <__utility/forward.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -52,9 +51,11 @@ struct __fn {
   partial_sort_copy_result<_Iter1, _Iter2>
   operator()(_Iter1 __first, _Sent1 __last, _Iter2 __result_first, _Sent2 __result_last,
              _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
-    // TODO: implement
-    (void)__first; (void)__last; (void)__result_first; (void)__result_last; (void)__comp; (void)__proj1; (void)__proj2;
-    return {};
+    auto __result = std::__partial_sort_copy<_RangeAlgPolicy>(
+        std::move(__first), std::move(__last), std::move(__result_first), std::move(__result_last),
+        __comp, __proj1, __proj2
+    );
+    return {std::move(__result.first), std::move(__result.second)};
   }
 
   template <input_range _Range1, random_access_range _Range2, class _Comp = ranges::less,
@@ -67,9 +68,11 @@ struct __fn {
   partial_sort_copy_result<borrowed_iterator_t<_Range1>, borrowed_iterator_t<_Range2>>
   operator()(_Range1&& __range, _Range2&& __result_range, _Comp __comp = {},
              _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
-    // TODO: implement
-    (void)__range; (void)__result_range; (void)__comp; (void)__proj1; (void)__proj2;
-    return {};
+    auto __result = std::__partial_sort_copy<_RangeAlgPolicy>(
+        ranges::begin(__range), ranges::end(__range), ranges::begin(__result_range), ranges::end(__result_range),
+        __comp, __proj1, __proj2
+    );
+    return {std::move(__result.first), std::move(__result.second)};
   }
 
 };

diff  --git a/libcxx/include/__algorithm/ranges_partition.h b/libcxx/include/__algorithm/ranges_partition.h
index 60bee699d90e2..6a53933f37aa3 100644
--- a/libcxx/include/__algorithm/ranges_partition.h
+++ b/libcxx/include/__algorithm/ranges_partition.h
@@ -44,7 +44,7 @@ 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&& __projected_pred = std::__make_projected(__pred, __proj);
     auto __result = std::__partition<_RangeAlgPolicy>(
         std::move(__first), std::move(__last), __projected_pred, __iterator_concept<_Iter>());
 

diff  --git a/libcxx/include/__algorithm/ranges_pop_heap.h b/libcxx/include/__algorithm/ranges_pop_heap.h
index 92df6119d34a0..fc7554fb0733a 100644
--- a/libcxx/include/__algorithm/ranges_pop_heap.h
+++ b/libcxx/include/__algorithm/ranges_pop_heap.h
@@ -46,7 +46,7 @@ struct __fn {
     auto __last_iter = ranges::next(__first, __last);
     auto __len = __last_iter - __first;
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__pop_heap<_RangeAlgPolicy>(std::move(__first), __last_iter, __projected_comp, __len);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_push_heap.h b/libcxx/include/__algorithm/ranges_push_heap.h
index 4c41b00128ded..3436b39e12cc7 100644
--- a/libcxx/include/__algorithm/ranges_push_heap.h
+++ b/libcxx/include/__algorithm/ranges_push_heap.h
@@ -45,7 +45,7 @@ struct __fn {
   _Iter __push_heap_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__push_heap<_RangeAlgPolicy>(std::move(__first), __last_iter, __projected_comp);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_sort.h b/libcxx/include/__algorithm/ranges_sort.h
index ef14db64295d8..c3f3cbff007c9 100644
--- a/libcxx/include/__algorithm/ranges_sort.h
+++ b/libcxx/include/__algorithm/ranges_sort.h
@@ -44,7 +44,7 @@ struct __fn {
   _Iter __sort_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__sort_impl<_RangeAlgPolicy>(std::move(__first), __last_iter, __projected_comp);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_sort_heap.h b/libcxx/include/__algorithm/ranges_sort_heap.h
index eb6a30dcd3d06..f6e4dcb43ddf5 100644
--- a/libcxx/include/__algorithm/ranges_sort_heap.h
+++ b/libcxx/include/__algorithm/ranges_sort_heap.h
@@ -45,7 +45,7 @@ struct __fn {
   _Iter __sort_heap_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__sort_heap<_RangeAlgPolicy>(std::move(__first), __last_iter, __projected_comp);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_stable_partition.h b/libcxx/include/__algorithm/ranges_stable_partition.h
index 27957db8829f1..b20dfa3a8bfc5 100644
--- a/libcxx/include/__algorithm/ranges_stable_partition.h
+++ b/libcxx/include/__algorithm/ranges_stable_partition.h
@@ -49,7 +49,7 @@ struct __fn {
       _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&& __projected_pred = std::__make_projected(__pred, __proj);
     auto __result = std::__stable_partition<_RangeAlgPolicy>(
         std::move(__first), __last_iter, __projected_pred, __iterator_concept<_Iter>());
 

diff  --git a/libcxx/include/__algorithm/ranges_stable_sort.h b/libcxx/include/__algorithm/ranges_stable_sort.h
index de48416a41bef..7ecffefc19da9 100644
--- a/libcxx/include/__algorithm/ranges_stable_sort.h
+++ b/libcxx/include/__algorithm/ranges_stable_sort.h
@@ -44,7 +44,7 @@ struct __fn {
   static _Iter __stable_sort_fn_impl(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
     auto __last_iter = ranges::next(__first, __last);
 
-    auto&& __projected_comp = ranges::__make_projected_comp(__comp, __proj);
+    auto&& __projected_comp = std::__make_projected(__comp, __proj);
     std::__stable_sort_impl<_RangeAlgPolicy>(std::move(__first), __last_iter, __projected_comp);
 
     return __last_iter;

diff  --git a/libcxx/include/__algorithm/ranges_unique.h b/libcxx/include/__algorithm/ranges_unique.h
index 85185280724b6..11370aeccd24a 100644
--- a/libcxx/include/__algorithm/ranges_unique.h
+++ b/libcxx/include/__algorithm/ranges_unique.h
@@ -47,7 +47,7 @@ namespace __unique {
     _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
     operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
       auto __ret = std::__unique<_RangeAlgPolicy>(
-          std::move(__first), std::move(__last), ranges::__make_projected_comp(__comp, __proj));
+          std::move(__first), std::move(__last), std::__make_projected(__comp, __proj));
       return {std::move(__ret.first), std::move(__ret.second)};
     }
 
@@ -59,7 +59,7 @@ namespace __unique {
     _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
     operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
       auto __ret = std::__unique<_RangeAlgPolicy>(
-          ranges::begin(__range), ranges::end(__range), ranges::__make_projected_comp(__comp, __proj));
+          ranges::begin(__range), ranges::end(__range), std::__make_projected(__comp, __proj));
       return {std::move(__ret.first), std::move(__ret.second)};
     }
   };

diff  --git a/libcxx/include/__algorithm/ranges_unique_copy.h b/libcxx/include/__algorithm/ranges_unique_copy.h
index d047b444aa5e7..8c0b970d043fb 100644
--- a/libcxx/include/__algorithm/ranges_unique_copy.h
+++ b/libcxx/include/__algorithm/ranges_unique_copy.h
@@ -76,7 +76,7 @@ struct __fn {
         std::move(__first),
         std::move(__last),
         std::move(__result),
-        __make_projected_comp(__comp, __proj),
+        std::__make_projected(__comp, __proj),
         __algo_tag_t<_InIter, _OutIter>());
     return {std::move(__ret.first), std::move(__ret.second)};
   }
@@ -95,7 +95,7 @@ struct __fn {
         ranges::begin(__range),
         ranges::end(__range),
         std::move(__result),
-        __make_projected_comp(__comp, __proj),
+        std::__make_projected(__comp, __proj),
         __algo_tag_t<iterator_t<_Range>, _OutIter>());
     return {std::move(__ret.first), std::move(__ret.second)};
   }

diff  --git a/libcxx/include/__algorithm/sift_down.h b/libcxx/include/__algorithm/sift_down.h
index be2eb29dd53a3..06811239f84fd 100644
--- a/libcxx/include/__algorithm/sift_down.h
+++ b/libcxx/include/__algorithm/sift_down.h
@@ -23,7 +23,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 void
-__sift_down(_RandomAccessIterator __first, _Compare __comp,
+__sift_down(_RandomAccessIterator __first, _Compare&& __comp,
             typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len,
             _RandomAccessIterator __start)
 {
@@ -79,7 +79,7 @@ __sift_down(_RandomAccessIterator __first, _Compare __comp,
 
 template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
 _LIBCPP_CONSTEXPR_AFTER_CXX11 _RandomAccessIterator
-__floyd_sift_down(_RandomAccessIterator __first, _Compare __comp,
+__floyd_sift_down(_RandomAccessIterator __first, _Compare&& __comp,
                   typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len)
 {
     using 
diff erence_type = typename iterator_traits<_RandomAccessIterator>::
diff erence_type;

diff  --git a/libcxx/include/__algorithm/sort_heap.h b/libcxx/include/__algorithm/sort_heap.h
index b9f0b2c9690d2..7713b766f913e 100644
--- a/libcxx/include/__algorithm/sort_heap.h
+++ b/libcxx/include/__algorithm/sort_heap.h
@@ -26,13 +26,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-void __sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare& __comp) {
+void __sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
   using _CompRef = typename __comp_ref_type<_Compare>::type;
   _CompRef __comp_ref = __comp;
 
   using 
diff erence_type = typename iterator_traits<_RandomAccessIterator>::
diff erence_type;
   for (
diff erence_type __n = __last - __first; __n > 1; --__last, (void) --__n)
-    std::__pop_heap<_AlgPolicy, _CompRef>(__first, __last, __comp_ref, __n);
+    std::__pop_heap<_AlgPolicy>(__first, __last, __comp_ref, __n);
 }
 
 template <class _RandomAccessIterator, class _Compare>

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 7b510b5096afd..92e9327442ff4 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -446,6 +446,25 @@ namespace ranges {
            indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
     constexpr bool ranges::none_of(R&& r, Pred pred, Proj proj = {});               // since C++20
 
+  template<input_iterator I1, sentinel_for<I1> S1,
+          random_access_iterator I2, sentinel_for<I2> S2,
+          class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
+    requires indirectly_copyable<I1, I2> && sortable<I2, Comp, Proj2> &&
+            indirect_strict_weak_order<Comp, projected<I1, Proj1>, projected<I2, Proj2>>
+    constexpr partial_sort_copy_result<I1, I2>
+      partial_sort_copy(I1 first, S1 last, I2 result_first, S2 result_last,
+                        Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});        // Since C++20
+
+  template<input_range R1, random_access_range R2, class Comp = ranges::less,
+          class Proj1 = identity, class Proj2 = identity>
+    requires indirectly_copyable<iterator_t<R1>, iterator_t<R2>> &&
+            sortable<iterator_t<R2>, Comp, Proj2> &&
+            indirect_strict_weak_order<Comp, projected<iterator_t<R1>, Proj1>,
+                                        projected<iterator_t<R2>, Proj2>>
+    constexpr partial_sort_copy_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>>
+      partial_sort_copy(R1&& r, R2&& result_r, Comp comp = {},
+                        Proj1 proj1 = {}, Proj2 proj2 = {});                        // Since C++20
+
   template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
            indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
     constexpr bool ranges::is_sorted(I first, S last, Comp comp = {}, Proj proj = {});      // since C++20
@@ -1668,6 +1687,7 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/ranges_none_of.h>
 #include <__algorithm/ranges_nth_element.h>
 #include <__algorithm/ranges_partial_sort.h>
+#include <__algorithm/ranges_partial_sort_copy.h>
 #include <__algorithm/ranges_partition.h>
 #include <__algorithm/ranges_partition_copy.h>
 #include <__algorithm/ranges_partition_point.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 b4328006539f7..6e64454358b1f 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
@@ -173,8 +173,8 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::nth_element(a, mid, Less(&copies)); assert(copies == 0);
     (void)std::ranges::partial_sort(first, mid, last, Less(&copies)); assert(copies == 0);
     (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::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_copy(first, last, first2, last2, UnaryTrue(&copies)); assert(copies == 0);

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 4eadbcc97a65c..7f0015456d758 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
@@ -156,8 +156,8 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::nth_element(a, mid, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::partial_sort(first, mid, last, Less(), Proj(&copies)); assert(copies == 0);
     (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::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_copy(first, last, first2, last2, UnaryTrue(), Proj(&copies)); assert(copies == 0);

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort.copy/ranges_partial_sort_copy.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort.copy/ranges_partial_sort_copy.pass.cpp
index 783d37464f7f7..15c0b06c7e022 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort.copy/ranges_partial_sort_copy.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/partial.sort.copy/ranges_partial_sort_copy.pass.cpp
@@ -12,39 +12,286 @@
 // <algorithm>
 
 // template<input_iterator I1, sentinel_for<I1> S1,
-//              random_access_iterator I2, sentinel_for<I2> S2,
-//              class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
-//       requires indirectly_copyable<I1, I2> && sortable<I2, Comp, Proj2> &&
-//                indirect_strict_weak_order<Comp, projected<I1, Proj1>, projected<I2, Proj2>>
-//       constexpr partial_sort_copy_result<I1, I2>
-//         partial_sort_copy(I1 first, S1 last, I2 result_first, S2 result_last,
-//                           Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});                   // Since C++20
+//          random_access_iterator I2, sentinel_for<I2> S2,
+//          class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
+//   requires indirectly_copyable<I1, I2> && sortable<I2, Comp, Proj2> &&
+//            indirect_strict_weak_order<Comp, projected<I1, Proj1>, projected<I2, Proj2>>
+//   constexpr partial_sort_copy_result<I1, I2>
+//     partial_sort_copy(I1 first, S1 last, I2 result_first, S2 result_last,
+//                       Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});               // Since C++20
 //
-//     template<input_range R1, random_access_range R2, class Comp = ranges::less,
-//              class Proj1 = identity, class Proj2 = identity>
-//       requires indirectly_copyable<iterator_t<R1>, iterator_t<R2>> &&
-//                sortable<iterator_t<R2>, Comp, Proj2> &&
-//                indirect_strict_weak_order<Comp, projected<iterator_t<R1>, Proj1>,
-//                                           projected<iterator_t<R2>, Proj2>>
-//       constexpr partial_sort_copy_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>>
-//         partial_sort_copy(R1&& r, R2&& result_r, Comp comp = {},
-//                           Proj1 proj1 = {}, Proj2 proj2 = {});                                   // Since C++20
+// template<input_range R1, random_access_range R2, class Comp = ranges::less,
+//          class Proj1 = identity, class Proj2 = identity>
+//   requires indirectly_copyable<iterator_t<R1>, iterator_t<R2>> &&
+//            sortable<iterator_t<R2>, Comp, Proj2> &&
+//            indirect_strict_weak_order<Comp, projected<iterator_t<R1>, Proj1>,
+//                                       projected<iterator_t<R2>, Proj2>>
+//   constexpr partial_sort_copy_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>>
+//     partial_sort_copy(R1&& r, R2&& result_r, Comp comp = {},
+//                       Proj1 proj1 = {}, Proj2 proj2 = {});                               // Since C++20
 
 #include <algorithm>
 #include <array>
 #include <concepts>
 #include <functional>
 #include <ranges>
+#include <utility>
 
+#include "MoveOnly.h"
 #include "almost_satisfies_types.h"
 #include "test_iterators.h"
 
-// TODO: SFINAE tests.
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter1 = int*, class Sent1 = int*, class Iter2 = int*, class Sent2 = int*,
+          class Comp = std::ranges::less>
+concept HasPartialSortCopyIter =
+    requires(Iter1&& first1, Sent1&& last1, Iter2&& first2, Sent2&& last2, Comp&& comp) {
+      std::ranges::partial_sort_copy(std::forward<Iter1>(first1), std::forward<Sent1>(last1),
+          std::forward<Iter2>(first2), std::forward<Sent2>(last2), std::forward<Comp>(comp));
+    };
+
+static_assert(HasPartialSortCopyIter<int*, int*, int*, int*, std::ranges::less>);
+
+// !input_iterator<I1>
+static_assert(!HasPartialSortCopyIter<InputIteratorNotDerivedFrom>);
+static_assert(!HasPartialSortCopyIter<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasPartialSortCopyIter<InputIteratorNotInputOrOutputIterator>);
+
+// !sentinel_for<S1, I1>
+static_assert(!HasPartialSortCopyIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasPartialSortCopyIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+// !random_access_iterator<I2>
+static_assert(!HasPartialSortCopyIter<int*, int*, RandomAccessIteratorNotDerivedFrom>);
+static_assert(!HasPartialSortCopyIter<int*, int*, RandomAccessIteratorBadIndex>);
+
+// !sentinel_for<S2, I2>
+static_assert(!HasPartialSortCopyIter<int*, int*, int*, SentinelForNotSemiregular>);
+static_assert(!HasPartialSortCopyIter<int*, int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+// !indirect_unary_predicate<projected<I, Proj>>
+static_assert(!HasPartialSortCopyIter<int*, int*, int*, int*, IndirectUnaryPredicateNotPredicate>);
+static_assert(!HasPartialSortCopyIter<int*, int*, int*, int*, IndirectUnaryPredicateNotCopyConstructible>);
+
+// !indirectly_copyable<I1, I2>
+static_assert(!HasPartialSortCopyIter<int*, int*, MoveOnly*>);
+
+// !sortable<I2, Comp, Proj2>
+static_assert(!HasPartialSortCopyIter<int*, int*, const int*, const int*>);
+
+struct NoComparator {};
+// !indirect_strict_weak_order<Comp, projected<I1, Proj1>, projected<I2, Proj2>>
+static_assert(!HasPartialSortCopyIter<NoComparator*, NoComparator*, NoComparator*, NoComparator*>);
+
+// Test constraints of the (range) overload.
+// ======================================================
+
+template <class T>
+using R = UncheckedRange<T>;
+
+template <class Range1 = R<int*>, class Range2 = R<int*>, class Comp = std::ranges::less>
+concept HasPartialSortCopyRange =
+    requires(Range1&& range1, Range2&& range2, Comp&& comp) {
+      std::ranges::partial_sort_copy(std::forward<Range1>(range1), std::forward<Range2>(range2),
+          std::forward<Comp>(comp));
+    };
+
+static_assert(HasPartialSortCopyRange<R<int*>, R<int*>, std::ranges::less>);
+
+// !input_range<R1>
+static_assert(!HasPartialSortCopyRange<InputRangeNotDerivedFrom>);
+static_assert(!HasPartialSortCopyRange<InputRangeNotIndirectlyReadable>);
+static_assert(!HasPartialSortCopyRange<InputRangeNotInputOrOutputIterator>);
+
+// !random_access_range<R2>
+static_assert(!HasPartialSortCopyRange<R<int*>, RandomAccessRangeNotDerivedFrom>);
+static_assert(!HasPartialSortCopyRange<R<int*>, RandomAccessRangeBadIndex>);
+
+// !indirectly_copyable<iterator_t<R1>, iterator_t<R2>>
+static_assert(!HasPartialSortCopyRange<R<int*>, R<MoveOnly*>>);
+
+// !sortable<iterator_t<R2>, Comp, Proj2>
+static_assert(!HasPartialSortCopyRange<R<int*>, R<const int*>>);
+
+// !indirect_strict_weak_order<Comp, projected<iterator_t<R1>, Proj1>, projected<iterator_t<R2>, Proj2>>
+static_assert(!HasPartialSortCopyRange<R<NoComparator*>, R<NoComparator*>>);
+
+static_assert(std::is_same_v<std::ranges::partial_sort_copy_result<int, int>, std::ranges::in_out_result<int, int>>);
+
+template <class Iter, class Sent, class OutIter, class OutSent, size_t N>
+constexpr void test_one(
+    std::array<int, N> input, size_t input_size, size_t output_size, std::array<int, N> sorted) {
+  assert(input_size <= N);
+  assert(output_size <= N + 1); // To support testing the case where output size exceeds input size.
+
+  using ResultT = std::ranges::partial_sort_copy_result<Iter, OutIter>;
+  // To support testing the case where output size exceeds input size; also makes sure calling `out.data() + int()` is
+  // valid.
+  constexpr size_t OutputSize = N + 1;
+  size_t result_size = std::ranges::min(input_size, output_size);
+
+  auto begin = input.data();
+  auto end = input.data() + input_size;
+
+  { // (iterator, sentinel) overload.
+    std::array<int, OutputSize> out;
+    auto out_begin = out.data();
+    auto out_end = out.data() + output_size;
+
+    std::same_as<ResultT> decltype(auto) result = std::ranges::partial_sort_copy(
+        Iter(begin), Sent(Iter(end)), OutIter(out_begin), OutSent(OutIter(out_end)));
+
+    assert(base(result.in) == input.data() + input_size);
+    assert(base(result.out) == out.data() + result_size);
+
+    assert(std::ranges::equal(std::ranges::subrange(out.begin(), out.begin() + result_size),
+           std::ranges::subrange(sorted.begin(), sorted.begin() + result_size)));
+  }
+
+  { // (range) overload.
+    std::array<int, OutputSize> out;
+    auto out_begin = out.data();
+    auto out_end = out.data() + output_size;
+    auto in_range = std::ranges::subrange(Iter(begin), Sent(Iter(end)));
+    auto out_range = std::ranges::subrange(OutIter(out_begin), OutSent(OutIter(out_end)));
+
+    std::same_as<ResultT> decltype(auto) result = std::ranges::partial_sort_copy(in_range, out_range);
+
+    assert(base(result.in) == input.data() + input_size);
+    assert(base(result.out) == out.data() + result_size);
+
+    assert(std::ranges::equal(std::ranges::subrange(out.begin(), out.begin() + result_size),
+           std::ranges::subrange(sorted.begin(), sorted.begin() + result_size)));
+  }
+
+}
+
+template <class Iter, class Sent, class OutIter, class OutSent, size_t N>
+constexpr void test_all_subsequences(const std::array<int, N> input) {
+  auto sorted = input;
+  std::sort(sorted.begin(), sorted.end());
+
+  // Whole input, increasing output size. Also check the case when `output_size` exceeds input size.
+  for (size_t out_size = 0; out_size <= N + 1; ++out_size) {
+    test_one<Iter, Sent, OutIter, OutSent>(input, N, out_size, sorted);
+  }
+}
+
+template <class InIter, class Sent1, class OutIter, class Sent2>
+constexpr void test_iterators_in_sent1_out_sent2() {
+  // Empty sequence.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2, 0>({});
+
+  // 1-element sequence.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{1});
+
+  // 2-element sequence.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{2, 1});
+
+  // 3-element sequence.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{2, 1, 3});
+
+  // Longer sequence.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{2, 1, 3, 6, 8, 4, 11, 5});
+
+  // Longer sequence with duplicates.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{2, 1, 3, 6, 2, 8, 6});
+
+  // All elements are the same.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{1, 1, 1});
+
+  // Already sorted.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{1, 2, 3, 4, 5});
+
+  // Descending.
+  test_all_subsequences<InIter, Sent1, OutIter, Sent2>(std::array{5, 4, 3, 2, 1});
+}
+
+template <class InIter, class Sent1, class OutIter>
+constexpr void test_iterators_in_sent1_out() {
+  test_iterators_in_sent1_out_sent2<InIter, Sent1, OutIter, OutIter>();
+  test_iterators_in_sent1_out_sent2<InIter, Sent1, OutIter, sentinel_wrapper<OutIter>>();
+}
+
+template <class InIter, class Sent1>
+constexpr void test_iterators_in_sent1() {
+  test_iterators_in_sent1_out<InIter, Sent1, random_access_iterator<int*>>();
+}
+
+template <class InIter>
+constexpr void test_iterators_in() {
+  if constexpr (std::sentinel_for<InIter, InIter>) {
+    test_iterators_in_sent1<InIter, InIter>();
+  }
+  test_iterators_in_sent1<InIter, sentinel_wrapper<InIter>>();
+}
+
+constexpr void test_iterators() {
+  test_iterators_in<cpp20_input_iterator<int*>>();
+  // Omitting other iterator types to reduce the combinatorial explosion.
+  test_iterators_in<random_access_iterator<int*>>();
+  test_iterators_in<const int*>();
+}
 
 constexpr bool test() {
-  // TODO: main tests.
-  // TODO: A custom comparator works.
-  // TODO: A custom projection works.
+  test_iterators();
+
+  { // A custom comparator works.
+    const std::array in = {1, 2, 3, 4, 5};
+    std::ranges::greater comp;
+
+    {
+      std::array<int, 2> out;
+
+      auto result = std::ranges::partial_sort_copy(in.begin(), in.end(), out.begin(), out.end(), comp);
+      assert(std::ranges::equal(std::ranges::subrange(out.begin(), result.out), std::array{5, 4}));
+    }
+
+    {
+      std::array<int, 2> out;
+
+      auto result = std::ranges::partial_sort_copy(in, out, comp);
+      assert(std::ranges::equal(std::ranges::subrange(out.begin(), result.out), std::array{5, 4}));
+    }
+  }
+
+  { // A custom projection works.
+    struct A {
+      int a;
+
+      constexpr A() = default;
+      constexpr A(int value) : a(value) {}
+      constexpr auto operator<=>(const A&) const = default;
+    };
+
+    const std::array in = {2, 1, 3};
+    auto proj1 = [](int value) { return value * -1; };
+    auto proj2 = [](A value) { return value.a * -1; };
+    // The projections negate the argument, so the array will appear to be sorted in descending order: [3, 2, 1]
+    // (the projections make it appear to the algorithm as [-3, -2, -1]).
+
+    {
+      std::array<A, 2> out;
+
+      // No projections: ascending order.
+      auto result = std::ranges::partial_sort_copy(in.begin(), in.end(), out.begin(), out.end(), {});
+      assert(std::ranges::equal(std::ranges::subrange(out.begin(), result.out), std::array{1, 2}));
+      // Using projections: descending order.
+      result = std::ranges::partial_sort_copy(in.begin(), in.end(), out.begin(), out.end(), {}, proj1, proj2);
+      assert(std::ranges::equal(std::ranges::subrange(out.begin(), result.out), std::array{3, 2}));
+    }
+
+    {
+      std::array<int, 2> out;
+
+      auto result = std::ranges::partial_sort_copy(in, out, {});
+      assert(std::ranges::equal(std::ranges::subrange(out.begin(), result.out), std::array{1, 2}));
+      result = std::ranges::partial_sort_copy(in, out, {}, proj1, proj2);
+      assert(std::ranges::equal(std::ranges::subrange(out.begin(), result.out), std::array{3, 2}));
+    }
+  }
 
   return true;
 }

diff  --git a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
index 875ca35b1265a..870fe1a9cf13a 100644
--- a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
@@ -30,7 +30,7 @@ static_assert(std::is_same_v<in_out_result<int, long>, copy_if_result<int, long>
 static_assert(std::is_same_v<in_out_result<int, long>, copy_backward_result<int, long>>);
 static_assert(std::is_same_v<in_out_result<int, long>, move_result<int, long>>);
 static_assert(std::is_same_v<in_out_result<int, long>, move_backward_result<int, long>>);
-// static_assert(std::is_same_v<in_out_result<int, long>, partial_sort_copy_result<int, long>>);
+static_assert(std::is_same_v<in_out_result<int, long>, partial_sort_copy_result<int, long>>);
 // static_assert(std::is_same_v<in_out_result<int, long>, remove_copy_result<int, long>>);
 // static_assert(std::is_same_v<in_out_result<int, long>, remove_copy_if_result<int, long>>);
 // static_assert(std::is_same_v<in_out_result<int, long>, replace_copy_result<int, long>>);

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
index b8625f5a3f449..c0468de8365c5 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -76,7 +76,7 @@ constexpr bool test_all() {
   using std::ranges::mismatch_result;
   using std::ranges::move_result;
   //using std::ranges::move_backward_result;
-  //using std::ranges::partial_sort_copy_result;
+  using std::ranges::partial_sort_copy_result;
   using std::ranges::partition_copy_result;
   //using std::ranges::remove_copy_result;
   //using std::ranges::remove_copy_if_result;
@@ -159,9 +159,9 @@ constexpr bool test_all() {
   dangling_1st<rotate_copy_result<dangling, int*>>(std::ranges::rotate_copy, in, mid, out);
   dangling_1st<unique_copy_result<dangling, int*>>(std::ranges::unique_copy, in, out);
   dangling_1st<partition_copy_result<dangling, int*, int*>>(std::ranges::partition_copy, in, out, out2, unary_pred);
-  //dangling_1st<partial_sort_copy_result<dangling, int*>>(std::ranges::partial_sort_copy, in, in2);
-  //dangling_2nd<partial_sort_copy_result<int*, dangling>>(std::ranges::partial_sort_copy, in, in2);
-  //dangling_both<partial_sort_copy_result<dangling, dangling>>(std::ranges::partial_sort_copy, in, in2);
+  dangling_1st<partial_sort_copy_result<dangling, int*>>(std::ranges::partial_sort_copy, in, in2);
+  dangling_2nd<partial_sort_copy_result<int*, dangling>>(std::ranges::partial_sort_copy, in, in2);
+  dangling_both<partial_sort_copy_result<dangling, dangling>>(std::ranges::partial_sort_copy, in, in2);
   dangling_1st<merge_result<dangling, int*, int*>>(std::ranges::merge, in, in2, out);
   dangling_2nd<merge_result<int*, dangling, int*>>(std::ranges::merge, in, in2, out);
   dangling_both<merge_result<dangling, dangling, int*>>(std::ranges::merge, in, in2, out);

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_
diff ering_projections.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_
diff ering_projections.pass.cpp
index b116b78691fda..5293fab120f67 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_
diff ering_projections.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_
diff ering_projections.pass.cpp
@@ -64,7 +64,7 @@ constexpr bool test_all() {
   test(std::ranges::find_end, in, in2, eq, proj1, proj2);
   test(std::ranges::transform, in, in2, out, sum, proj1, proj2);
   test(std::ranges::transform, in, in2, out2, sum, proj1, proj2);
-  //test(std::ranges::partial_sort_copy, in, in2, output2.begin(), output2.end(), less, proj1, proj2);
+  test(std::ranges::partial_sort_copy, in, in2, less, proj1, proj2);
   test(std::ranges::merge, in, in2, out, less, proj1, proj2);
   test(std::ranges::merge, in, in2, out2, less, proj1, proj2);
   test(std::ranges::set_intersection, in, in2, out, less, proj1, proj2);
@@ -75,6 +75,8 @@ constexpr bool test_all() {
   test(std::ranges::set_symmetric_
diff erence, in, in2, out2, less, proj1, proj2);
   test(std::ranges::set_union, in, in2, out, less, proj1, proj2);
   test(std::ranges::set_union, in, in2, out2, less, proj1, proj2);
+  //test(std::ranges::starts_with, in, in2, eq, proj1, proj2);
+  //test(std::ranges::ends_with, in, in2, eq, proj1, proj2);
 
   return true;
 }

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
index 0d1e3990b3b18..a38891792a82e 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
@@ -117,7 +117,7 @@ constexpr bool test_all() {
   //test(std::ranges::replace_copy_if, in, out, unary_pred, x);
   test(std::ranges::unique_copy, in, out, binary_pred);
   test(std::ranges::partition_copy, in, out, out2, unary_pred);
-  //test(std::ranges::partial_sort_copy, in, in2, binary_pred);
+  test(std::ranges::partial_sort_copy, in, in2, binary_pred);
   test(std::ranges::merge, in, in2, out, binary_pred);
   test(std::ranges::set_
diff erence, in, in2, out, binary_pred);
   test(std::ranges::set_intersection, in, in2, out, binary_pred);

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
index c6fa8598e6136..0c39e901f275c 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
@@ -142,7 +142,7 @@ constexpr bool test_all() {
   // `sample` has no requirement that the given generator be invoked via `std::invoke`.
   test(std::ranges::unique_copy, in, out, &Foo::binary_pred, &Bar::val);
   test(std::ranges::partition_copy, in, out, out2, &Foo::unary_pred, &Bar::val);
-  //test(std::ranges::partial_sort_copy, in, in2, &Foo::binary_pred, &Bar::val);
+  test(std::ranges::partial_sort_copy, in, in2, &Foo::binary_pred, &Bar::val, &Bar::val);
   test(std::ranges::merge, in, in2, out, &Foo::binary_pred, &Bar::val, &Bar::val);
   test(std::ranges::set_
diff erence, in, in2, out, &Foo::binary_pred, &Bar::val, &Bar::val);
   test(std::ranges::set_intersection, in, in2, out, &Foo::binary_pred, &Bar::val, &Bar::val);

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 191b4953f025f..e77153ae828c5 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,7 +138,7 @@ constexpr void run_tests() {
     test_mid(std::ranges::rotate_copy, in, mid, out);
     test(std::ranges::unique_copy, in, out);
     test(std::ranges::partition_copy, in, out, out2, unary_pred);
-    //test_mid(std::ranges::partial_sort_copy, in, in2);
+    test(std::ranges::partial_sort_copy, in, in2);
     test(std::ranges::merge, in, in2, out);
     test(std::ranges::set_
diff erence, in, in2, out);
     test(std::ranges::set_intersection, in, in2, out);

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 eac6eece9ac77..5e9d9f27ff2ec 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
@@ -111,7 +111,7 @@ static_assert(test(std::ranges::move_backward, a, a));
 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::partial_sort_copy, a, a));
 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));


        


More information about the libcxx-commits mailing list