[libcxx-commits] [libcxx] d406c64 - [libc++][ranges] Implement `ranges::is_heap{, _until}`.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jul 26 16:11:34 PDT 2022
Author: Konstantin Varlamov
Date: 2022-07-26T16:11:24-07:00
New Revision: d406c6493e9e0e6334e16e8fd22b0ae613653704
URL: https://github.com/llvm/llvm-project/commit/d406c6493e9e0e6334e16e8fd22b0ae613653704
DIFF: https://github.com/llvm/llvm-project/commit/d406c6493e9e0e6334e16e8fd22b0ae613653704.diff
LOG: [libc++][ranges] Implement `ranges::is_heap{,_until}`.
Differential Revision: https://reviews.llvm.org/D130547
Added:
Modified:
libcxx/docs/Status/RangesAlgorithms.csv
libcxx/include/__algorithm/is_heap.h
libcxx/include/__algorithm/is_heap_until.h
libcxx/include/__algorithm/ranges_is_heap.h
libcxx/include/__algorithm/ranges_is_heap_until.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.heap.operations/is.heap/ranges_is_heap.pass.cpp
libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
libcxx/test/std/algorithms/ranges_robust_against_dangling.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 35db0ad64ae81..ecbfb3329c536 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -30,8 +30,8 @@ Read-only,is_partitioned,Nikolas Klauser,`D124440 <https://llvm.org/D124440>`_,
Read-only,is_sorted,Nikolas Klauser,`D125608 <https://llvm.org/D125608>`_,✅
Read-only,is_sorted_until,Nikolas Klauser,`D125608 <https://llvm.org/D125608>`_,✅
Read-only,includes,Hui Xie,`D130116 <https://llvm.org/D130116>`,✅
-Read-only,is_heap,Nikolas Klauser,n/a,Not started
-Read-only,is_heap_until,Nikolas Klauser,n/a,Not started
+Read-only,is_heap,Konstantin Varlamov,`D130547 <https://llvm.org/D130547>`_,✅
+Read-only,is_heap_until,Konstantin Varlamov,`D130547 <https://llvm.org/D130547>`_,✅
Read-only,clamp,Nikolas Klauser,`D126193 <https://llvm.org/D126193>`_,Under review
Read-only,is_permutation,Nikolas Klauser,`D127194 <https://llvm.org/D127194>`_,Under review
Read-only,for_each,Nikolas Klauser,`D124332 <https://llvm.org/D124332>`_,✅
diff --git a/libcxx/include/__algorithm/is_heap.h b/libcxx/include/__algorithm/is_heap.h
index fe44e634f6ddd..0d399a09b8576 100644
--- a/libcxx/include/__algorithm/is_heap.h
+++ b/libcxx/include/__algorithm/is_heap.h
@@ -28,7 +28,7 @@ bool
is_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
{
typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
- return _VSTD::__is_heap_until<_Comp_ref>(__first, __last, __comp) == __last;
+ return std::__is_heap_until(__first, __last, static_cast<_Comp_ref>(__comp)) == __last;
}
template<class _RandomAccessIterator>
diff --git a/libcxx/include/__algorithm/is_heap_until.h b/libcxx/include/__algorithm/is_heap_until.h
index 39f313eb0d3fc..adb35af887fba 100644
--- a/libcxx/include/__algorithm/is_heap_until.h
+++ b/libcxx/include/__algorithm/is_heap_until.h
@@ -22,7 +22,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Compare, class _RandomAccessIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _RandomAccessIterator
-__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
+__is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp)
{
typedef typename iterator_traits<_RandomAccessIterator>::
diff erence_type
diff erence_type;
diff erence_type __len = __last - __first;
@@ -52,7 +52,7 @@ _LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
{
typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
- return _VSTD::__is_heap_until<_Comp_ref>(__first, __last, __comp);
+ return std::__is_heap_until(__first, __last, static_cast<_Comp_ref>(__comp));
}
template<class _RandomAccessIterator>
diff --git a/libcxx/include/__algorithm/ranges_is_heap.h b/libcxx/include/__algorithm/ranges_is_heap.h
index 00105189fed7d..a3e86d1a8d724 100644
--- a/libcxx/include/__algorithm/ranges_is_heap.h
+++ b/libcxx/include/__algorithm/ranges_is_heap.h
@@ -9,18 +9,17 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_IS_HEAP_H
#define _LIBCPP___ALGORITHM_RANGES_IS_HEAP_H
-#include <__algorithm/is_heap.h>
+#include <__algorithm/is_heap_until.h>
#include <__algorithm/make_projected.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>
+#include <__iterator/next.h>
#include <__iterator/projected.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
-#include <__utility/forward.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -36,22 +35,28 @@ namespace __is_heap {
struct __fn {
+ template <class _Iter, class _Sent, class _Proj, class _Comp>
+ _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 __result = std::__is_heap_until(std::move(__first), std::move(__last_iter), __projected_comp);
+ return __result == __last;
+ }
+
template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
indirect_strict_weak_order<projected<_Iter, _Proj>> _Comp = ranges::less>
_LIBCPP_HIDE_FROM_ABI constexpr
bool operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
- // TODO: implement
- (void)__first; (void)__last; (void)__comp; (void)__proj;
- return {};
+ return __is_heap_fn_impl(std::move(__first), std::move(__last), __comp, __proj);
}
template <random_access_range _Range, class _Proj = identity,
indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
_LIBCPP_HIDE_FROM_ABI constexpr
bool operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
- // TODO: implement
- (void)__range; (void)__comp; (void)__proj;
- return {};
+ return __is_heap_fn_impl(ranges::begin(__range), ranges::end(__range), __comp, __proj);
}
};
diff --git a/libcxx/include/__algorithm/ranges_is_heap_until.h b/libcxx/include/__algorithm/ranges_is_heap_until.h
index ad021d6f2525e..bcd33ad404e88 100644
--- a/libcxx/include/__algorithm/ranges_is_heap_until.h
+++ b/libcxx/include/__algorithm/ranges_is_heap_until.h
@@ -13,15 +13,14 @@
#include <__algorithm/make_projected.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>
+#include <__iterator/next.h>
#include <__iterator/projected.h>
#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)
@@ -37,22 +36,27 @@ namespace __is_heap_until {
struct __fn {
+ template <class _Iter, class _Sent, class _Proj, class _Comp>
+ _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);
+
+ return std::__is_heap_until(std::move(__first), std::move(__last_iter), __projected_comp);
+ }
+
template <random_access_iterator _Iter, sentinel_for<_Iter> _Sent, class _Proj = identity,
indirect_strict_weak_order<projected<_Iter, _Proj>> _Comp = ranges::less>
_LIBCPP_HIDE_FROM_ABI constexpr
_Iter operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
- // TODO: implement
- (void)__first; (void)__last; (void)__comp; (void)__proj;
- return {};
+ return __is_heap_until_fn_impl(std::move(__first), std::move(__last), __comp, __proj);
}
template <random_access_range _Range, class _Proj = identity,
indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> _Comp = ranges::less>
_LIBCPP_HIDE_FROM_ABI constexpr
borrowed_iterator_t<_Range> operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
- // TODO: implement
- (void)__range; (void)__comp; (void)__proj;
- return {};
+ return __is_heap_until_fn_impl(ranges::begin(__range), ranges::end(__range), __comp, __proj);
}
};
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 30b04e155bac0..197b3b1043bbe 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -331,6 +331,23 @@ namespace ranges {
constexpr borrowed_iterator_t<R>
ranges::sort_heap(R&& r, Comp comp = {}, Proj proj = {}); // since C++20
+ template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
+ indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+ constexpr bool is_heap(I first, S last, Comp comp = {}, Proj proj = {}); // Since C++20
+
+ template<random_access_range R, class Proj = identity,
+ indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+ constexpr bool is_heap(R&& r, Comp comp = {}, Proj proj = {}); // Since C++20
+
+ template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
+ indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+ constexpr I is_heap_until(I first, S last, Comp comp = {}, Proj proj = {}); // Since C++20
+
+ template<random_access_range R, class Proj = identity,
+ indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+ constexpr borrowed_iterator_t<R>
+ is_heap_until(R&& r, Comp comp = {}, Proj proj = {}); // Since C++20
+
template<bidirectional_iterator I, sentinel_for<I> S>
requires permutable<I>
constexpr I ranges::reverse(I first, S last); // since C++20
@@ -1590,6 +1607,8 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_generate.h>
#include <__algorithm/ranges_generate_n.h>
#include <__algorithm/ranges_includes.h>
+#include <__algorithm/ranges_is_heap.h>
+#include <__algorithm/ranges_is_heap_until.h>
#include <__algorithm/ranges_is_partitioned.h>
#include <__algorithm/ranges_is_sorted.h>
#include <__algorithm/ranges_is_sorted_until.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 6fe0cfc177ef6..48eeb01b57587 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
@@ -126,10 +126,10 @@ constexpr bool all_the_algorithms()
//(void)std::ranges::generate_n(first, count, NullaryValue(&copies)); assert(copies == 0);
(void)std::ranges::includes(first, last, first2, last2, Less(&copies)); assert(copies == 0);
(void)std::ranges::includes(a, b, Less(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap(first, last, Less(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap(a, Less(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap_until(first, last, Less(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap_until(a, Less(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap(first, last, Less(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap(a, Less(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap_until(first, last, Less(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap_until(a, Less(&copies)); assert(copies == 0);
(void)std::ranges::is_partitioned(first, last, UnaryTrue(&copies)); assert(copies == 0);
(void)std::ranges::is_partitioned(a, UnaryTrue(&copies)); assert(copies == 0);
//(void)std::ranges::is_permutation(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 56e7471c73f98..80b8a5ec4d836 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
@@ -109,10 +109,10 @@ constexpr bool all_the_algorithms()
(void)std::ranges::for_each_n(first, count, UnaryVoid(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::includes(first, last, first2, last2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
(void)std::ranges::includes(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap(a, Less(), Proj(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
- //(void)std::ranges::is_heap_until(a, Less(), Proj(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap(a, Less(), Proj(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
+ (void)std::ranges::is_heap_until(a, Less(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::is_partitioned(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::is_partitioned(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
//(void)std::ranges::is_permutation(first, last, first2, last2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp
index 28b1c4af4ee91..8d58009a70fd5 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap.pass.cpp
@@ -12,28 +12,158 @@
// <algorithm>
// template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
-// indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
-// constexpr bool is_heap(I first, S last, Comp comp = {}, Proj proj = {}); // Since C++20
+// indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+// constexpr bool is_heap(I first, S last, Comp comp = {}, Proj proj = {}); // Since C++20
//
-// template<random_access_range R, class Proj = identity,
-// indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
-// constexpr bool is_heap(R&& r, Comp comp = {}, Proj proj = {}); // Since C++20
+// template<random_access_range R, class Proj = identity,
+// indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+// constexpr bool is_heap(R&& r, Comp comp = {}, Proj proj = {}); // Since C++20
#include <algorithm>
#include <array>
#include <concepts>
#include <functional>
#include <ranges>
+#include <utility>
#include "almost_satisfies_types.h"
#include "test_iterators.h"
-// TODO: SFINAE tests.
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter = int*, class Sent = int*, class Comp = std::ranges::less>
+concept HasIsHeapIter =
+ requires(Iter&& iter, Sent&& sent, Comp&& comp) {
+ std::ranges::is_heap(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Comp>(comp));
+ };
+
+static_assert(HasIsHeapIter<int*, int*, std::ranges::less>);
+
+// !random_access_iterator<I>
+static_assert(!HasIsHeapIter<RandomAccessIteratorNotDerivedFrom>);
+static_assert(!HasIsHeapIter<RandomAccessIteratorBadIndex>);
+
+// !sentinel_for<S, I>
+static_assert(!HasIsHeapIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasIsHeapIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+struct NoComparator {};
+// !indirect_strict_weak_order<Comp, projected<I, Proj>>
+static_assert(!HasIsHeapIter<NoComparator*, NoComparator*>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range, class Comp = std::ranges::less>
+concept HasIsHeapRange =
+ requires(Range&& range, Comp&& comp) {
+ std::ranges::is_heap(std::forward<Range>(range), std::forward<Comp>(comp));
+ };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasIsHeapRange<R<int*>>);
+
+// !random_access_range<R>
+static_assert(!HasIsHeapRange<RandomAccessRangeNotDerivedFrom>);
+static_assert(!HasIsHeapRange<RandomAccessRangeBadIndex>);
+
+// !indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>
+static_assert(!HasIsHeapRange<R<NoComparator*>>);
+
+template <class Iter, class Sent, size_t N>
+constexpr void test_one(std::array<int, N> input, bool expected) {
+ auto begin = Iter(input.data());
+ auto end = Sent(Iter(input.data() + input.size()));
+
+ { // (iterator, sentinel) overload.
+ std::same_as<bool> decltype(auto) result = std::ranges::is_heap(begin, end);
+ assert(result == expected);
+ }
+
+ { // (range) overload.
+ auto range = std::ranges::subrange(begin, end);
+ std::same_as<bool> decltype(auto) result = std::ranges::is_heap(range);
+ assert(result == expected);
+ }
+}
+
+template <class Iter, class Sent>
+constexpr void test_iter_sent() {
+ // Empty sequence.
+ test_one<Iter, Sent, 0>({}, true);
+ // 1-element sequence.
+ test_one<Iter, Sent>(std::array{1}, true);
+ // 2-element sequence, a heap.
+ test_one<Iter, Sent>(std::array{2, 1}, true);
+ // 2-element sequence, not a heap.
+ test_one<Iter, Sent>(std::array{1, 2}, false);
+ // Longer sequence, a heap.
+ test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, true);
+ // Longer sequence, not a heap.
+ test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 2, 5}, false);
+ // Longer sequence with duplicates, a heap.
+ test_one<Iter, Sent>(std::array{8, 7, 5, 5, 6, 4, 1, 2, 3, 2}, true);
+ // Longer sequence with duplicates, not a heap.
+ test_one<Iter, Sent>(std::array{7, 5, 5, 6, 4, 1, 2, 3, 2, 8}, false);
+ // All elements are the same.
+ test_one<Iter, Sent>(std::array{1, 1, 1}, true);
+}
+
+template <class Iter>
+constexpr void test_iter() {
+ test_iter_sent<Iter, Iter>();
+ test_iter_sent<Iter, sentinel_wrapper<Iter>>();
+}
+
+constexpr void test_iterators() {
+ test_iter<random_access_iterator<int*>>();
+ test_iter<contiguous_iterator<int*>>();
+ test_iter<int*>();
+ test_iter<const int*>();
+}
constexpr bool test() {
- // TODO: main tests.
- // TODO: A custom comparator works.
- // TODO: A custom projection works.
+ test_iterators();
+
+ { // A custom comparator works.
+ std::ranges::less ls;
+ std::ranges::greater gt;
+ std::array in = {1, 3, 2, 5, 4, 7, 8, 6};
+
+ { // (iterator, sentinel) overload.
+ assert(!std::ranges::is_heap(in.begin(), in.end(), ls));
+ assert(std::ranges::is_heap(in.begin(), in.end(), gt));
+ }
+
+ { // (range) overload.
+ assert(!std::ranges::is_heap(in, ls));
+ assert(std::ranges::is_heap(in, gt));
+ }
+ }
+
+ { // A custom projection works.
+ struct A {
+ int x;
+ constexpr auto operator<=>(const A&) const = default;
+ };
+
+ std::array in = {A{-8}, A{-6}, A{-7}, A{-3}, A{-4}, A{-1}, A{-5}, A{-2}};
+ auto negate = [](A a) { return a.x * -1; };
+
+ { // (iterator, sentinel) overload.
+ assert(!std::ranges::is_heap(in.begin(), in.end(), {}));
+ assert(std::ranges::is_heap(in.begin(), in.end(), {}, negate));
+ }
+
+ { // (range) overload.
+ assert(!std::ranges::is_heap(in, {}));
+ assert(std::ranges::is_heap(in, {}, negate));
+ }
+ }
+
return true;
}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
index 86da5ab54653d..8ce43657af30e 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.heap.operations/is.heap/ranges_is_heap_until.pass.cpp
@@ -12,13 +12,13 @@
// <algorithm>
// template<random_access_iterator I, sentinel_for<I> S, class Proj = identity,
-// indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
-// constexpr I is_heap_until(I first, S last, Comp comp = {}, Proj proj = {}); // Since C++20
+// indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
+// constexpr I is_heap_until(I first, S last, Comp comp = {}, Proj proj = {}); // Since C++20
//
-// template<random_access_range R, class Proj = identity,
-// indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
-// constexpr borrowed_iterator_t<R>
-// is_heap_until(R&& r, Comp comp = {}, Proj proj = {}); // Since C++20
+// template<random_access_range R, class Proj = identity,
+// indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
+// constexpr borrowed_iterator_t<R>
+// is_heap_until(R&& r, Comp comp = {}, Proj proj = {}); // Since C++20
#include <algorithm>
#include <array>
@@ -29,12 +29,149 @@
#include "almost_satisfies_types.h"
#include "test_iterators.h"
-// TODO: SFINAE tests.
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter = int*, class Sent = int*, class Comp = std::ranges::less>
+concept HasIsHeapUntilIter =
+ requires(Iter&& iter, Sent&& sent, Comp&& comp) {
+ std::ranges::is_heap_until(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Comp>(comp));
+ };
+
+static_assert(HasIsHeapUntilIter<int*, int*, std::ranges::less>);
+
+// !random_access_iterator<I>
+static_assert(!HasIsHeapUntilIter<RandomAccessIteratorNotDerivedFrom>);
+static_assert(!HasIsHeapUntilIter<RandomAccessIteratorBadIndex>);
+
+// !sentinel_for<S, I>
+static_assert(!HasIsHeapUntilIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasIsHeapUntilIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+struct NoComparator {};
+// !indirect_strict_weak_order<Comp, projected<I, Proj>>
+static_assert(!HasIsHeapUntilIter<NoComparator*, NoComparator*>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range, class Comp = std::ranges::less>
+concept HasIsHeapUntilRange =
+ requires(Range&& range, Comp&& comp) {
+ std::ranges::is_heap_until(std::forward<Range>(range), std::forward<Comp>(comp));
+ };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasIsHeapUntilRange<R<int*>>);
+
+// !random_access_range<R>
+static_assert(!HasIsHeapUntilRange<RandomAccessRangeNotDerivedFrom>);
+static_assert(!HasIsHeapUntilRange<RandomAccessRangeBadIndex>);
+
+// !indirect_strict_weak_order<Comp, projected<iterator_t<R>, Proj>>
+static_assert(!HasIsHeapUntilRange<R<NoComparator*>>);
+
+template <class Iter, class Sent, size_t N>
+constexpr void test_one(std::array<int, N> input, size_t until_index) {
+ auto begin = Iter(input.data());
+ auto end = Sent(Iter(input.data() + input.size()));
+
+ { // (iterator, sentinel) overload.
+ std::same_as<Iter> decltype(auto) result = std::ranges::is_heap_until(begin, end);
+ assert(base(result) == input.data() + until_index);
+ }
+
+ { // (range) overload.
+ auto range = std::ranges::subrange(begin, end);
+ std::same_as<Iter> decltype(auto) result = std::ranges::is_heap_until(range);
+ assert(base(result) == input.data() + until_index);
+ }
+}
+
+template <class Iter, class Sent>
+constexpr void test_iter_sent() {
+ // Empty sequence.
+ test_one<Iter, Sent, 0>({}, 0);
+ // 1-element sequence.
+ test_one<Iter, Sent>(std::array{1}, 1);
+ // 2-element sequence, a heap.
+ test_one<Iter, Sent>(std::array{2, 1}, 2);
+ // 2-element sequence, not a heap.
+ test_one<Iter, Sent>(std::array{1, 2}, 1);
+ // Longer sequence, a heap.
+ test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 5, 2}, 8);
+ // Longer sequence, not a heap.
+ test_one<Iter, Sent>(std::array{8, 6, 7, 3, 4, 1, 2, 5}, 7);
+ // Longer sequence with duplicates, a heap.
+ test_one<Iter, Sent>(std::array{8, 7, 5, 5, 6, 4, 1, 2, 3, 2}, 10);
+ // Longer sequence with duplicates, not a heap.
+ test_one<Iter, Sent>(std::array{7, 5, 5, 6, 4, 1, 2, 3, 2, 8}, 3);
+ // All elements are the same.
+ test_one<Iter, Sent>(std::array{1, 1, 1}, 3);
+}
+
+template <class Iter>
+constexpr void test_iter() {
+ test_iter_sent<Iter, Iter>();
+ test_iter_sent<Iter, sentinel_wrapper<Iter>>();
+}
+
+constexpr void test_iterators() {
+ test_iter<random_access_iterator<int*>>();
+ test_iter<contiguous_iterator<int*>>();
+ test_iter<int*>();
+ test_iter<const int*>();
+}
constexpr bool test() {
- // TODO: main tests.
- // TODO: A custom comparator works.
- // TODO: A custom projection works.
+ test_iterators();
+
+ { // A custom comparator works.
+ std::ranges::less ls;
+ std::ranges::greater gt;
+ std::array in = {1, 3, 2, 5, 4, 7, 8, 6};
+
+ { // (iterator, sentinel) overload.
+ auto result_default_comp = std::ranges::is_heap_until(in.begin(), in.end(), ls);
+ assert(result_default_comp == in.begin() + 1);
+ auto result_custom_comp = std::ranges::is_heap_until(in.begin(), in.end(), gt);
+ assert(result_custom_comp == in.end());
+ }
+
+ { // (range) overload.
+ auto result_default_comp = std::ranges::is_heap_until(in, ls);
+ assert(result_default_comp == in.begin() + 1);
+ auto result_custom_comp = std::ranges::is_heap_until(in, gt);
+ assert(result_custom_comp == in.end());
+ }
+ }
+
+ { // A custom projection works.
+ struct A {
+ int x;
+ constexpr auto operator<=>(const A&) const = default;
+ };
+
+ std::array in = {A{-8}, A{-6}, A{-7}, A{-3}, A{-4}, A{-1}, A{-5}, A{-2}};
+ auto negate = [](A a) { return a.x * -1; };
+
+ { // (iterator, sentinel) overload.
+ auto result_default_comp = std::ranges::is_heap_until(in.begin(), in.end(), {});
+ assert(result_default_comp == in.begin() + 1);
+ auto result_custom_comp = std::ranges::is_heap_until(in.begin(), in.end(), {}, negate);
+ assert(result_custom_comp == in.end());
+ }
+
+ { // (range) overload.
+ auto result_default_comp = std::ranges::is_heap_until(in, {});
+ assert(result_default_comp == in.begin() + 1);
+ auto result_custom_comp = std::ranges::is_heap_until(in, {}, negate);
+ assert(result_custom_comp == in.end());
+ }
+ }
+
return true;
}
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 7e92e15d69010..40d1043d4b1b4 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -125,7 +125,7 @@ constexpr bool test_all() {
dangling_1st(std::ranges::search_n, in, count, x);
dangling_1st(std::ranges::find_end, in, in2);
dangling_1st(std::ranges::is_sorted_until, in);
- //dangling_1st(std::ranges::is_heap_until, in);
+ dangling_1st(std::ranges::is_heap_until, in);
dangling_1st<for_each_result<dangling, decltype(unary_pred)>>(std::ranges::for_each, in, unary_pred);
dangling_1st<copy_result<dangling, int*>>(std::ranges::copy, in, out);
// TODO: uncomment `copy_backward` once https://reviews.llvm.org/D128864 lands.
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 7448f552364f2..f5f7160869368 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
@@ -107,8 +107,8 @@ constexpr bool test_all() {
test(std::ranges::is_sorted, in, binary_pred);
test(std::ranges::is_sorted_until, in, binary_pred);
test(std::ranges::includes, in, in2, binary_pred);
- //test(std::ranges::is_heap, in, binary_pred);
- //test(std::ranges::is_heap_until, in, binary_pred);
+ test(std::ranges::is_heap, in, binary_pred);
+ test(std::ranges::is_heap_until, in, binary_pred);
//std::ranges::clamp(2, 1, 3, binary_pred);
//test(std::ranges::is_permutation, in, in2, binary_pred);
test(std::ranges::copy_if, in, out, unary_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 38ca7c2522ff2..2da3db405c324 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
@@ -114,8 +114,8 @@ constexpr bool test_all() {
test(std::ranges::is_sorted, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::is_sorted_until, in, &Foo::binary_pred, &Bar::val);
test(std::ranges::includes, in, in2, &Foo::binary_pred, &Bar::val, &Bar::val);
- //test(std::ranges::is_heap, in, &Foo::binary_pred, &Bar::val);
- //test(std::ranges::is_heap_until, in, &Foo::binary_pred, &Bar::val);
+ test(std::ranges::is_heap, in, &Foo::binary_pred, &Bar::val);
+ test(std::ranges::is_heap_until, in, &Foo::binary_pred, &Bar::val);
//std::ranges::clamp(b, a, c, &Foo::binary_pred);
//test(std::ranges::is_permutation, in, in2, &Foo::binary_pred, &Bar::val, &Bar::val);
test(std::ranges::for_each, in, &Foo::unary_pred, &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 c4aa662978b93..aba570160899a 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
@@ -101,8 +101,8 @@ constexpr void run_tests() {
test(std::ranges::is_sorted, in);
test(std::ranges::is_sorted_until, in);
test(std::ranges::includes, in, in2);
- //test(std::ranges::is_heap, in);
- //test(std::ranges::is_heap_until, in);
+ test(std::ranges::is_heap, in);
+ test(std::ranges::is_heap_until, in);
//test(std::ranges::is_permutation, in, in2);
test(std::ranges::for_each, in, std::identity{});
std::ranges::for_each_n(in.begin(), count, std::identity{});
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 9aa8024d86305..ebe78c1742216 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
@@ -88,8 +88,8 @@ static_assert(test(std::ranges::generate, a, gen));
static_assert(test(std::ranges::generate_n, a, 10, gen));
static_assert(test(std::ranges::includes, a, a));
//static_assert(test(std::ranges::inplace_merge, a, a+5));
-//static_assert(test(std::ranges::is_heap, a));
-//static_assert(test(std::ranges::is_heap_until, a));
+static_assert(test(std::ranges::is_heap, a));
+static_assert(test(std::ranges::is_heap_until, a));
static_assert(test(std::ranges::is_partitioned, a, odd));
//static_assert(test(std::ranges::is_permutation, a, a));
static_assert(test(std::ranges::is_sorted, a));
More information about the libcxx-commits
mailing list