[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