[libcxx-commits] [libcxx] 0f6364b - [libc++][ranges] implement `std::ranges::equal_range`

Hui Xie via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 22 02:27:34 PDT 2022


Author: Hui Xie
Date: 2022-07-22T10:24:08+01:00
New Revision: 0f6364b8a1007e9cfbde331321115f9385bd6b17

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

LOG: [libc++][ranges] implement `std::ranges::equal_range`

implement `std::ranges::equal_range` which delegates to
`std::equal_range`

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

Added: 
    

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/__algorithm/equal_range.h
    libcxx/include/__algorithm/inplace_merge.h
    libcxx/include/__algorithm/iterator_operations.h
    libcxx/include/__algorithm/ranges_equal_range.h
    libcxx/include/__algorithm/upper_bound.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.binary.search/equal.range/ranges_equal_range.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/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 849a9a2a2b4df..e433266729cef 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -13,7 +13,7 @@ Search,lexicographical_compare,Nikolas Klauser,`D127130 <https://llvm.org/D12713
 Search,partition_point,Konstantin Varlamov,`D130070 <https://llvm.org/D130070>`_,✅
 Search,lower_bound,Nikolas Klauser,`D121964 <https://llvm.org/D121964>`_,✅
 Search,upper_bound,Nikolas Klauser,`D121964 <https://llvm.org/D121964>`_,✅
-Search,equal_range,Christopher Di Bella,n/a,Not started
+Search,equal_range,Hui Xie,`D129796 <https://llvm.org/D129796>`,✅
 Search,binary_search,Nikolas Klauser,`D121964 <https://llvm.org/D121964>`_,✅
 Search,min,Nikolas Klauser,`D119589 <https://llvm.org/D119589>`_,✅
 Search,max,Nikolas Klauser,`D122002 <https://llvm.org/D122002>`_,✅

diff  --git a/libcxx/include/__algorithm/equal_range.h b/libcxx/include/__algorithm/equal_range.h
index f30f55be64fc7..42d009ebbc0f4 100644
--- a/libcxx/include/__algorithm/equal_range.h
+++ b/libcxx/include/__algorithm/equal_range.h
@@ -17,9 +17,13 @@
 #include <__algorithm/upper_bound.h>
 #include <__config>
 #include <__functional/identity.h>
+#include <__functional/invoke.h>
 #include <__iterator/advance.h>
 #include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
+#include <__type_traits/is_callable.h>
+#include <__type_traits/is_copy_constructible.h>
+#include <__utility/move.h>
 #include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -28,59 +32,50 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _ForwardIterator, class _Tp>
-_LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_ForwardIterator, _ForwardIterator>
-__equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp)
-{
-    typedef typename iterator_traits<_ForwardIterator>::
diff erence_type 
diff erence_type;
-    
diff erence_type __len = _VSTD::distance(__first, __last);
-    while (__len != 0)
-    {
-        
diff erence_type __l2 = _VSTD::__half_positive(__len);
-        _ForwardIterator __m = __first;
-        _VSTD::advance(__m, __l2);
-        if (__comp(*__m, __value))
-        {
-            __first = ++__m;
-            __len -= __l2 + 1;
-        }
-        else if (__comp(__value, *__m))
-        {
-            __last = __m;
-            __len = __l2;
-        }
-        else
-        {
-            auto __proj = std::__identity();
-            _ForwardIterator __mp1 = __m;
-            return pair<_ForwardIterator, _ForwardIterator>
-                   (
-                      _VSTD::__lower_bound_impl<_ClassicAlgPolicy>(__first, __m, __value, __comp, __proj),
-                      _VSTD::__upper_bound<_Compare>(++__mp1, __last, __value, __comp)
-                   );
-        }
+template <class _AlgPolicy, class _Compare, class _Iter, class _Sent, class _Tp, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_Iter, _Iter>
+__equal_range(_Iter __first, _Sent __last, const _Tp& __value, _Compare&& __comp, _Proj&& __proj) {
+  auto __len  = _IterOps<_AlgPolicy>::distance(__first, __last);
+  _Iter __end = _IterOps<_AlgPolicy>::next(__first, __last);
+  while (__len != 0) {
+    auto __half_len = std::__half_positive(__len);
+    _Iter __mid     = _IterOps<_AlgPolicy>::next(__first, __half_len);
+    if (std::__invoke(__comp, std::__invoke(__proj, *__mid), __value)) {
+      __first = ++__mid;
+      __len -= __half_len + 1;
+    } else if (std::__invoke(__comp, __value, std::__invoke(__proj, *__mid))) {
+      __end = __mid;
+      __len = __half_len;
+    } else {
+      _Iter __mp1 = __mid;
+      return pair<_Iter, _Iter>(
+          std::__lower_bound_impl<_AlgPolicy>(__first, __mid, __value, __comp, __proj),
+          std::__upper_bound<_AlgPolicy>(++__mp1, __end, __value, __comp, __proj));
     }
-    return pair<_ForwardIterator, _ForwardIterator>(__first, __first);
+  }
+  return pair<_Iter, _Iter>(__first, __first);
 }
 
 template <class _ForwardIterator, class _Tp, class _Compare>
-_LIBCPP_NODISCARD_EXT inline
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-pair<_ForwardIterator, _ForwardIterator>
-equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp)
-{
-    typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
-    return _VSTD::__equal_range<_Comp_ref>(__first, __last, __value, __comp);
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_ForwardIterator, _ForwardIterator>
+equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp) {
+  static_assert(__is_callable<_Compare, decltype(*__first), const _Tp&>::value,
+                "The comparator has to be callable");
+  static_assert(is_copy_constructible<_ForwardIterator>::value,
+                "Iterator has to be copy constructible");
+  typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
+  return std::__equal_range<_ClassicAlgPolicy>(
+      std::move(__first), std::move(__last), __value, static_cast<_Comp_ref>(__comp), std::__identity());
 }
 
 template <class _ForwardIterator, class _Tp>
-_LIBCPP_NODISCARD_EXT inline
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-pair<_ForwardIterator, _ForwardIterator>
-equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
-{
-    return _VSTD::equal_range(__first, __last, __value,
-                             __less<typename iterator_traits<_ForwardIterator>::value_type, _Tp>());
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 pair<_ForwardIterator, _ForwardIterator>
+equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) {
+  return std::equal_range(
+      std::move(__first),
+      std::move(__last),
+      __value,
+      __less<typename iterator_traits<_ForwardIterator>::value_type, _Tp>());
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h
index 7369786eeb060..f4364969b8f9f 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -18,6 +18,7 @@
 #include <__algorithm/rotate.h>
 #include <__algorithm/upper_bound.h>
 #include <__config>
+#include <__functional/identity.h>
 #include <__iterator/advance.h>
 #include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
@@ -157,7 +158,8 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
             __len21 = __len2 / 2;
             __m2 = __middle;
             _Ops::advance(__m2, __len21);
-            __m1 = _VSTD::__upper_bound<_Compare>(__first, __middle, *__m2, __comp);
+            // TODO: replace _ClassicAlgPolicy and __identity with _AlgPolicy and projection
+            __m1 = std::__upper_bound<_ClassicAlgPolicy>(__first, __middle, *__m2, __comp, std::__identity());
             __len11 = _Ops::distance(__first, __m1);
         }
         else

diff  --git a/libcxx/include/__algorithm/iterator_operations.h b/libcxx/include/__algorithm/iterator_operations.h
index b27217d5d8680..8307d71214e58 100644
--- a/libcxx/include/__algorithm/iterator_operations.h
+++ b/libcxx/include/__algorithm/iterator_operations.h
@@ -100,7 +100,7 @@ struct _IterOps<_ClassicAlgPolicy> {
 
   template <class _Iter>
   _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
-  __uncvref_t<_Iter> next(_Iter&& __it,
+  __uncvref_t<_Iter> next(_Iter&& __it, 
                           typename iterator_traits<__uncvref_t<_Iter> >::
diff erence_type __n = 1){
     return std::next(std::forward<_Iter>(__it), __n);
   }

diff  --git a/libcxx/include/__algorithm/ranges_equal_range.h b/libcxx/include/__algorithm/ranges_equal_range.h
index 28d721530bdad..dd4b377df1a11 100644
--- a/libcxx/include/__algorithm/ranges_equal_range.h
+++ b/libcxx/include/__algorithm/ranges_equal_range.h
@@ -10,7 +10,7 @@
 #define _LIBCPP___ALGORITHM_RANGES_EQUAL_RANGE_H
 
 #include <__algorithm/equal_range.h>
-#include <__algorithm/make_projected.h>
+#include <__algorithm/iterator_operations.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/invoke.h>
@@ -37,27 +37,30 @@ namespace ranges {
 namespace __equal_range {
 
 struct __fn {
-
-  template <forward_iterator _Iter, sentinel_for<_Iter> _Sent, class _Tp, class _Proj = identity,
-            indirect_strict_weak_order<const _Tp*, projected<_Iter, _Proj>> _Comp = ranges::less>
-  _LIBCPP_HIDE_FROM_ABI constexpr
-  subrange<_Iter> operator()(_Iter __first, _Sent __last, const _Tp& __value, _Comp __comp = {},
-                             _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__first; (void)__last; (void)__value; (void)__comp; (void)__proj;
-    return {};
+  template <
+      forward_iterator _Iter,
+      sentinel_for<_Iter> _Sent,
+      class _Tp,
+      class _Proj                                                           = identity,
+      indirect_strict_weak_order<const _Tp*, projected<_Iter, _Proj>> _Comp = ranges::less>
+  _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
+  operator()(_Iter __first, _Sent __last, const _Tp& __value, _Comp __comp = {}, _Proj __proj = {}) const {
+    auto __ret = std::__equal_range<_RangeAlgPolicy>(
+        std::move(__first), std::move(__last), __value, __comp, __proj);
+    return {std::move(__ret.first), std::move(__ret.second)};
   }
 
-  template <forward_range _Range, class _Tp, class _Proj = identity,
-            indirect_strict_weak_order<const _Tp*, projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
-  _LIBCPP_HIDE_FROM_ABI constexpr
-  borrowed_subrange_t<_Range> operator()(_Range&& __range, const _Tp& __value, _Comp __comp = {},
-                                         _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__range; (void)__value; (void)__comp; (void)__proj;
-    return {};
+  template <
+      forward_range _Range,
+      class _Tp,
+      class _Proj                                                                        = identity,
+      indirect_strict_weak_order<const _Tp*, projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
+  _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
+  operator()(_Range&& __range, const _Tp& __value, _Comp __comp = {}, _Proj __proj = {}) const {
+    auto __ret = std::__equal_range<_RangeAlgPolicy>(
+        ranges::begin(__range), ranges::end(__range), __value, __comp, __proj);
+    return {std::move(__ret.first), std::move(__ret.second)};
   }
-
 };
 
 } // namespace __equal_range

diff  --git a/libcxx/include/__algorithm/upper_bound.h b/libcxx/include/__algorithm/upper_bound.h
index 3fc254873532b..1045380bc84e3 100644
--- a/libcxx/include/__algorithm/upper_bound.h
+++ b/libcxx/include/__algorithm/upper_bound.h
@@ -11,10 +11,15 @@
 
 #include <__algorithm/comp.h>
 #include <__algorithm/half_positive.h>
+#include <__algorithm/iterator_operations.h>
 #include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
 #include <__iterator/advance.h>
 #include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
+#include <__type_traits/is_copy_constructible.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -22,45 +27,40 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Compare, class _ForwardIterator, class _Tp>
-_LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
-__upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp)
-{
-    typedef typename iterator_traits<_ForwardIterator>::
diff erence_type 
diff erence_type;
-    
diff erence_type __len = _VSTD::distance(__first, __last);
-    while (__len != 0)
-    {
-        
diff erence_type __l2 = _VSTD::__half_positive(__len);
-        _ForwardIterator __m = __first;
-        _VSTD::advance(__m, __l2);
-        if (__comp(__value, *__m))
-            __len = __l2;
-        else
-        {
-            __first = ++__m;
-            __len -= __l2 + 1;
-        }
+template <class _AlgPolicy, class _Compare, class _Iter, class _Sent, class _Tp, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _Iter
+__upper_bound(_Iter __first, _Sent __last, const _Tp& __value, _Compare&& __comp, _Proj&& __proj) {
+  auto __len = _IterOps<_AlgPolicy>::distance(__first, __last);
+  while (__len != 0) {
+    auto __half_len = std::__half_positive(__len);
+    auto __mid      = _IterOps<_AlgPolicy>::next(__first, __half_len);
+    if (std::__invoke(__comp, __value, std::__invoke(__proj, *__mid)))
+      __len = __half_len;
+    else {
+      __first = ++__mid;
+      __len -= __half_len + 1;
     }
-    return __first;
+  }
+  return __first;
 }
 
 template <class _ForwardIterator, class _Tp, class _Compare>
-_LIBCPP_NODISCARD_EXT inline
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-_ForwardIterator
-upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp)
-{
-    return _VSTD::__upper_bound<_Compare&>(__first, __last, __value, __comp);
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
+upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp) {
+  static_assert(is_copy_constructible<_ForwardIterator>::value,
+                "Iterator has to be copy constructible");
+  return std::__upper_bound<_ClassicAlgPolicy>(
+      std::move(__first), std::move(__last), __value, std::move(__comp), std::__identity());
 }
 
 template <class _ForwardIterator, class _Tp>
-_LIBCPP_NODISCARD_EXT inline
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-_ForwardIterator
-upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
-{
-    return _VSTD::upper_bound(__first, __last, __value,
-                             __less<_Tp, typename iterator_traits<_ForwardIterator>::value_type>());
+_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
+upper_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) {
+  return std::upper_bound(
+      std::move(__first),
+      std::move(__last),
+      __value,
+      __less<_Tp, typename iterator_traits<_ForwardIterator>::value_type>());
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 88c7665d712b3..19cf3b8fc4aae 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -772,6 +772,17 @@ namespace ranges {
                                                       borrowed_iterator_t<R2>, O>
       set_symmetric_
diff erence(R1&& r1, R2&& r2, O result, Comp comp = {},
                                Proj1 proj1 = {}, Proj2 proj2 = {});                                 // since C++20
+
+  template<forward_iterator I, sentinel_for<I> S, class T, class Proj = identity,
+           indirect_strict_weak_order<const T*, projected<I, Proj>> Comp = ranges::less>
+    constexpr subrange<I>
+      equal_range(I first, S last, const T& value, Comp comp = {}, Proj proj = {});                 // since C++20
+  
+  template<forward_range R, class T, class Proj = identity,
+           indirect_strict_weak_order<const T*, projected<iterator_t<R>, Proj>> Comp =
+             ranges::less>
+    constexpr borrowed_subrange_t<R>
+      equal_range(R&& r, const T& value, Comp comp = {}, Proj proj = {});                           // since C++20
   
   template<class I1, class I2, class O>
     using set_union_result = in_in_out_result<I1, I2, O>;                                           // since C++20
@@ -1530,6 +1541,7 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/ranges_count.h>
 #include <__algorithm/ranges_count_if.h>
 #include <__algorithm/ranges_equal.h>
+#include <__algorithm/ranges_equal_range.h>
 #include <__algorithm/ranges_fill.h>
 #include <__algorithm/ranges_fill_n.h>
 #include <__algorithm/ranges_find.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 51e4386969fb9..4ac4c4f3909c5 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
@@ -108,8 +108,8 @@ constexpr bool all_the_algorithms()
 #endif
     (void)std::ranges::equal(first, last, first2, last2, Equal(&copies)); assert(copies == 0);
     (void)std::ranges::equal(a, b, Equal(&copies)); assert(copies == 0);
-    //(void)std::ranges::equal_range(first, last, value, Less(&copies)); assert(copies == 0);
-    //(void)std::ranges::equal_range(a, value, Less(&copies)); assert(copies == 0);
+    (void)std::ranges::equal_range(first, last, value, Less(&copies)); assert(copies == 0);
+    (void)std::ranges::equal_range(a, value, Less(&copies)); assert(copies == 0);
     (void)std::ranges::find_end(first, last, first2, mid2, Equal(&copies)); assert(copies == 0);
     (void)std::ranges::find_end(a, b, Equal(&copies)); assert(copies == 0);
     (void)std::ranges::find_first_of(first, last, first2, last2, Equal(&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 b5bc34029efb5..62950e0230389 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
@@ -92,8 +92,8 @@ constexpr bool all_the_algorithms()
 #endif
     (void)std::ranges::equal(first, last, first2, last2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::equal(a, b, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::equal_range(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
-    //(void)std::ranges::equal_range(a, value, Less(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::equal_range(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
+    (void)std::ranges::equal_range(a, value, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::find(first, last, value, Proj(&copies)); assert(copies == 0);
     (void)std::ranges::find(a, value, Proj(&copies)); assert(copies == 0);
     (void)std::ranges::find_end(first, last, first2, mid2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/equal.range/ranges_equal_range.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/equal.range/ranges_equal_range.pass.cpp
index 32f4954796f39..87b9de477a4e2 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/equal.range/ranges_equal_range.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.binary.search/equal.range/ranges_equal_range.pass.cpp
@@ -30,14 +30,194 @@
 
 #include "almost_satisfies_types.h"
 #include "test_iterators.h"
+#include "../../sortable_helpers.h"
 
-// TODO: SFINAE tests.
+struct Foo {};
+
+template <class Iter, class Sent, class T, class Proj = std::identity, class Comp = std::ranges::less>
+concept HasEqualRangeIter =
+    requires(Iter&& iter, Sent&& sent, const T& value, Comp&& comp, Proj&& proj) {
+      std::ranges::equal_range(
+          std::forward<Iter>(iter),
+          std::forward<Sent>(sent),
+          value,
+          std::forward<Comp>(comp),
+          std::forward<Proj>(proj));
+    };
+
+static_assert(HasEqualRangeIter<int*, int*, int>);
+
+// !forward_iterator<I>
+static_assert(!HasEqualRangeIter<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>, int>);
+
+// !sentinel_for<S, I>
+static_assert(!HasEqualRangeIter<int*, SentinelForNotSemiregular, int>);
+
+// !indirect_strict_weak_order<Comp, const T*, projected<I, Proj>>
+static_assert(!HasEqualRangeIter<int*, int*, Foo>);
+
+template <class R, class T, class Proj = std::identity, class Comp = std::ranges::less>
+concept HasEqualRangeForRange =
+    requires(R&& r, const T& value, Comp&& comp, Proj&& proj) {
+      std::ranges::equal_range(std::forward<R>(r), value, comp, proj);
+    };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasEqualRangeForRange<R<int*>, int>);
+
+// !forward_range<R>
+static_assert(!HasEqualRangeForRange<R<cpp20_input_iterator<int*>>, int>);
+
+// !indirect_strict_weak_order<Comp, const T*, projected<ranges::iterator_t<R>, Proj>>
+static_assert(!HasEqualRangeForRange<R<int*>, Foo>);
+
+template <class InIter, std::size_t N>
+constexpr void testEqualRangeImpl(std::array<int, N>& in, int value, std::ranges::subrange<int*> expected) {
+  using Sent = sentinel_wrapper<InIter>;
+
+  // iterator overload
+  {
+    std::same_as<std::ranges::subrange<InIter, InIter>> decltype(auto) result =
+        std::ranges::equal_range(InIter{in.data()}, Sent{InIter{in.data() + in.size()}}, value);
+
+    assert(base(result.begin()) == expected.begin());
+    assert(base(result.end()) == expected.end());
+  }
+
+  // range overload
+  {
+    std::ranges::subrange r{InIter{in.data()}, Sent{InIter{in.data() + in.size()}}};
+    std::same_as<std::ranges::subrange<InIter, InIter>> decltype(auto) result = std::ranges::equal_range(r, value);
+
+    assert(base(result.begin()) == expected.begin());
+    assert(base(result.end()) == expected.end());
+  }
+}
+
+template <class InIter>
+constexpr void testImpl() {
+  // no match and the searched value would be in the middle
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    int value = 7;
+    std::ranges::subrange<int*> expected{in.data() + 4, in.data() + 4};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // value smaller than all the elements
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    int value = -1;
+    std::ranges::subrange<int*> expected{in.data(), in.data()};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // value bigger than all the elements
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    int value = 20;
+    std::ranges::subrange<int*> expected{in.data() + in.size(), in.data() + in.size()};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // exactly one match
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    int value = 5;
+    std::ranges::subrange<int*> expected{in.data() + 2, in.data() + 3};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // multiple matches
+  {
+    std::array in{0, 1, 5, 6, 6, 6, 9, 10};
+    int value = 6;
+    std::ranges::subrange<int*> expected{in.data() + 3, in.data() + 6};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // all matches
+  {
+    std::array in{6, 6, 6, 6, 6};
+    int value = 6;
+    std::ranges::subrange<int*> expected{in.data(), in.data() + in.size()};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // empty range
+  {
+    std::array<int, 0> in{};
+    int value = 6;
+    std::ranges::subrange<int*> expected{in.data(), in.data()};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+
+  // partially sorted
+  {
+    std::array in{3, 1, 5, 2, 6, 6, 10, 7, 8};
+    int value = 6;
+    std::ranges::subrange<int*> expected{in.data() + 4, in.data() + 6};
+    testEqualRangeImpl<InIter>(in, value, expected);
+  }
+}
 
 constexpr bool test() {
-  // TODO: main tests.
-  // TODO: A custom comparator works.
-  // TODO: A custom projection works.
+  testImpl<forward_iterator<int*>>();
+  testImpl<bidirectional_iterator<int*>>();
+  testImpl<random_access_iterator<int*>>();
+  testImpl<contiguous_iterator<int*>>();
+
+  struct Data {
+    int data;
+  };
+
+  // Test custom comparator
+  {
+    std::array<Data, 10> in{{{2}, {1}, {3}, {3}, {3}, {10}, {9}, {5}, {5}, {7}}};
+    Data value{3};
+
+    const auto comp = [](const Data& x, const Data& y) { return x.data < y.data; };
+    // iterator overload
+    {
+      auto result = std::ranges::equal_range(in.begin(), in.end(), value, comp);
+
+      assert(result.begin() == in.begin() + 2);
+      assert(result.end() == in.begin() + 5);
+    }
+
+    // range overload
+    {
+      auto result = std::ranges::equal_range(in, value, comp);
+
+      assert(result.begin() == in.begin() + 2);
+      assert(result.end() == in.begin() + 5);
+    }
+  }
+
+  // Test custom projection
+  {
+    std::array<Data, 10> in{{{2}, {1}, {3}, {3}, {3}, {10}, {9}, {5}, {5}, {7}}};
+    int value = 3;
+
+    const auto proj = [](const Data& d) { return d.data; };
+    // iterator overload
+    {
+      auto result = std::ranges::equal_range(in.begin(), in.end(), value, {}, proj);
+
+      assert(result.begin() == in.begin() + 2);
+      assert(result.end() == in.begin() + 5);
+    }
+
+    // range overload
+    {
+      auto result = std::ranges::equal_range(in, value, {}, proj);
 
+      assert(result.begin() == in.begin() + 2);
+      assert(result.end() == in.begin() + 5);
+    }
+  }
   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 9fc9c1f8d0f28..18c3a02979474 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
@@ -80,7 +80,7 @@ constexpr bool test_all() {
   test(std::ranges::partition_point, in, unary_pred);
   test(std::ranges::lower_bound, in, x, binary_pred);
   test(std::ranges::upper_bound, in, x, binary_pred);
-  //test(std::ranges::equal_range, in, x, binary_pred);
+  test(std::ranges::equal_range, in, x, binary_pred);
   test(std::ranges::binary_search, in, x, binary_pred);
 
   // min

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 e70ae510889c8..39cefeced4ab6 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
@@ -86,7 +86,7 @@ constexpr bool test_all() {
   test(std::ranges::partition_point, in, &Foo::unary_pred, &Bar::val);
   test(std::ranges::lower_bound, in, x, &Foo::binary_pred, &Bar::val);
   test(std::ranges::upper_bound, in, x, &Foo::binary_pred, &Bar::val);
-  //test(std::ranges::equal_range, in, x, &Foo::binary_pred, &Bar::val);
+  test(std::ranges::equal_range, in, x, &Foo::binary_pred, &Bar::val);
   test(std::ranges::binary_search, in, x, &Foo::binary_pred, &Bar::val);
 
   // min

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 6ac3ca96669e1..f71732db12ab9 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
@@ -74,7 +74,7 @@ static_assert(test(std::ranges::count, a, 42));
 static_assert(test(std::ranges::count_if, a, odd));
 //static_assert(test(std::ranges::ends_with, a, a));
 static_assert(test(std::ranges::equal, a, a));
-//static_assert(test(std::ranges::equal_range, a, 42));
+static_assert(test(std::ranges::equal_range, a, 42));
 static_assert(test(std::ranges::fill, a, 42));
 static_assert(test(std::ranges::fill_n, a, 10, 42));
 static_assert(test(std::ranges::find, a, 42));


        


More information about the libcxx-commits mailing list