Commit ec6889b6 authored by Jan Wilken Dörrie's avatar Jan Wilken Dörrie Committed by Commit Bot

[ranges] constexpr is_sorted, is_sorted_until and adjacent_find

This change adds constexpr implementations of base::ranges::is_sorted,
base::ranges::is_sorted_until and base::ranges::adjacent_find.

Since the regular std:: implementations are not constexpr prior to C++20
the algorithms are now implemented manually.

Bug: 1071094
Change-Id: Id25b4bde77451c6fcc17f4c07179d68de4af3981
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2494872
Commit-Queue: Jan Wilken Dörrie <jdoerrie@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820829}
parent e2ad5083
...@@ -694,8 +694,23 @@ constexpr auto adjacent_find(ForwardIterator first, ...@@ -694,8 +694,23 @@ constexpr auto adjacent_find(ForwardIterator first,
ForwardIterator last, ForwardIterator last,
Pred pred = {}, Pred pred = {},
Proj proj = {}) { Proj proj = {}) {
return std::adjacent_find( // Implementation inspired by cppreference.com:
first, last, internal::ProjectedBinaryPredicate(pred, proj, proj)); // https://en.cppreference.com/w/cpp/algorithm/adjacent_find
//
// A reimplementation is required, because std::adjacent_find is not constexpr
// prior to C++20. Once we have C++20, we should switch to standard library
// implementation.
if (first == last)
return last;
for (ForwardIterator next = first; ++next != last; ++first) {
if (base::invoke(pred, base::invoke(proj, *first),
base::invoke(proj, *next))) {
return first;
}
}
return last;
} }
// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`. // Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`.
...@@ -2890,12 +2905,12 @@ constexpr auto partial_sort_copy(Range1&& range, ...@@ -2890,12 +2905,12 @@ constexpr auto partial_sort_copy(Range1&& range,
// [is.sorted] is_sorted // [is.sorted] is_sorted
// Reference: https://wg21.link/is.sorted // Reference: https://wg21.link/is.sorted
// Returns: Whether the range `[first, last)` is sorted with respect to `comp` // Returns: The last iterator `i` in `[first, last]` for which the range
// and `proj`. // `[first, i)` is sorted with respect to `comp` and `proj`.
// //
// Complexity: Linear. // Complexity: Linear.
// //
// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(I // Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(I
template <typename ForwardIterator, template <typename ForwardIterator,
typename Comp = ranges::less, typename Comp = ranges::less,
typename Proj = identity, typename Proj = identity,
...@@ -2903,19 +2918,35 @@ template <typename ForwardIterator, ...@@ -2903,19 +2918,35 @@ template <typename ForwardIterator,
typename = indirect_result_t<Comp&, typename = indirect_result_t<Comp&,
projected<ForwardIterator, Proj>, projected<ForwardIterator, Proj>,
projected<ForwardIterator, Proj>>> projected<ForwardIterator, Proj>>>
constexpr auto is_sorted(ForwardIterator first, constexpr auto is_sorted_until(ForwardIterator first,
ForwardIterator last, ForwardIterator last,
Comp comp = {}, Comp comp = {},
Proj proj = {}) { Proj proj = {}) {
return std::is_sorted(first, last, // Implementation inspired by cppreference.com:
internal::ProjectedBinaryPredicate(comp, proj, proj)); // https://en.cppreference.com/w/cpp/algorithm/is_sorted_until
//
// A reimplementation is required, because std::is_sorted_until is not
// constexpr prior to C++20. Once we have C++20, we should switch to standard
// library implementation.
if (first == last)
return last;
for (ForwardIterator next = first; ++next != last; ++first) {
if (base::invoke(comp, base::invoke(proj, *next),
base::invoke(proj, *first))) {
return next;
}
}
return last;
} }
// Returns: Whether `range` is sorted with respect to `comp` and `proj`. // Returns: The last iterator `i` in `[begin(range), end(range)]` for which the
// range `[begin(range), i)` is sorted with respect to `comp` and `proj`.
// //
// Complexity: Linear. // Complexity: Linear.
// //
// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(R // Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(R
template <typename Range, template <typename Range,
typename Comp = ranges::less, typename Comp = ranges::less,
typename Proj = identity, typename Proj = identity,
...@@ -2923,17 +2954,17 @@ template <typename Range, ...@@ -2923,17 +2954,17 @@ template <typename Range,
typename = indirect_result_t<Comp&, typename = indirect_result_t<Comp&,
projected<iterator_t<Range>, Proj>, projected<iterator_t<Range>, Proj>,
projected<iterator_t<Range>, Proj>>> projected<iterator_t<Range>, Proj>>>
constexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) { constexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) {
return ranges::is_sorted(ranges::begin(range), ranges::end(range), return ranges::is_sorted_until(ranges::begin(range), ranges::end(range),
std::move(comp), std::move(proj)); std::move(comp), std::move(proj));
} }
// Returns: The last iterator `i` in `[first, last]` for which the range // Returns: Whether the range `[first, last)` is sorted with respect to `comp`
// `[first, i)` is sorted with respect to `comp` and `proj`. // and `proj`.
// //
// Complexity: Linear. // Complexity: Linear.
// //
// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(I // Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(I
template <typename ForwardIterator, template <typename ForwardIterator,
typename Comp = ranges::less, typename Comp = ranges::less,
typename Proj = identity, typename Proj = identity,
...@@ -2941,20 +2972,19 @@ template <typename ForwardIterator, ...@@ -2941,20 +2972,19 @@ template <typename ForwardIterator,
typename = indirect_result_t<Comp&, typename = indirect_result_t<Comp&,
projected<ForwardIterator, Proj>, projected<ForwardIterator, Proj>,
projected<ForwardIterator, Proj>>> projected<ForwardIterator, Proj>>>
constexpr auto is_sorted_until(ForwardIterator first, constexpr auto is_sorted(ForwardIterator first,
ForwardIterator last, ForwardIterator last,
Comp comp = {}, Comp comp = {},
Proj proj = {}) { Proj proj = {}) {
return std::is_sorted_until( return ranges::is_sorted_until(first, last, std::move(comp),
first, last, internal::ProjectedBinaryPredicate(comp, proj, proj)); std::move(proj)) == last;
} }
// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the // Returns: Whether `range` is sorted with respect to `comp` and `proj`.
// range `[begin(range), i)` is sorted with respect to `comp` and `proj`.
// //
// Complexity: Linear. // Complexity: Linear.
// //
// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(R // Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(R
template <typename Range, template <typename Range,
typename Comp = ranges::less, typename Comp = ranges::less,
typename Proj = identity, typename Proj = identity,
...@@ -2962,9 +2992,9 @@ template <typename Range, ...@@ -2962,9 +2992,9 @@ template <typename Range,
typename = indirect_result_t<Comp&, typename = indirect_result_t<Comp&,
projected<iterator_t<Range>, Proj>, projected<iterator_t<Range>, Proj>,
projected<iterator_t<Range>, Proj>>> projected<iterator_t<Range>, Proj>>>
constexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) { constexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) {
return ranges::is_sorted_until(ranges::begin(range), ranges::end(range), return ranges::is_sorted(ranges::begin(range), ranges::end(range),
std::move(comp), std::move(proj)); std::move(comp), std::move(proj));
} }
// [alg.nth.element] Nth element // [alg.nth.element] Nth element
......
...@@ -255,15 +255,19 @@ TEST(RangesTest, FindFirstOf) { ...@@ -255,15 +255,19 @@ TEST(RangesTest, FindFirstOf) {
} }
TEST(RangesTest, AdjacentFind) { TEST(RangesTest, AdjacentFind) {
int array[] = {1, 2, 3, 3}; constexpr int array[] = {1, 2, 3, 3};
EXPECT_EQ(array + 2, ranges::adjacent_find(array, ranges::end(array))); static_assert(array + 2 == ranges::adjacent_find(array, ranges::end(array)),
EXPECT_EQ(array, "");
ranges::adjacent_find(array, ranges::end(array), ranges::less{})); static_assert(
array == ranges::adjacent_find(array, ranges::end(array), ranges::less{}),
"");
Int ints[] = {{6}, {6}, {5}, {4}}; constexpr Int ints[] = {{6}, {6}, {5}, {4}};
EXPECT_EQ(ints, ranges::adjacent_find(ints, ranges::equal_to{}, &Int::value)); static_assert(
EXPECT_EQ(ranges::end(ints), ints == ranges::adjacent_find(ints, ranges::equal_to{}, &Int::value), "");
ranges::adjacent_find(ints, ranges::less{}, &Int::value)); static_assert(ranges::end(ints) ==
ranges::adjacent_find(ints, ranges::less{}, &Int::value),
"");
} }
TEST(RangesTest, Count) { TEST(RangesTest, Count) {
...@@ -899,27 +903,29 @@ TEST(RangesTest, PartialSortCopy) { ...@@ -899,27 +903,29 @@ TEST(RangesTest, PartialSortCopy) {
} }
TEST(RangesTest, IsSorted) { TEST(RangesTest, IsSorted) {
int input[] = {3, 1, 2, 0, 4}; constexpr int input[] = {3, 1, 2, 0, 4};
EXPECT_TRUE(ranges::is_sorted(input + 1, input + 3)); static_assert(ranges::is_sorted(input + 1, input + 3), "");
EXPECT_FALSE(ranges::is_sorted(input + 1, input + 4)); static_assert(!ranges::is_sorted(input + 1, input + 4), "");
EXPECT_TRUE(ranges::is_sorted(input, input + 2, ranges::greater())); static_assert(ranges::is_sorted(input, input + 2, ranges::greater()), "");
Int ints[] = {0, 1, 2, 3, 4}; constexpr Int ints[] = {0, 1, 2, 3, 4};
EXPECT_TRUE(ranges::is_sorted(ints, {}, &Int::value)); static_assert(ranges::is_sorted(ints, {}, &Int::value), "");
EXPECT_FALSE(ranges::is_sorted(ints, ranges::greater(), &Int::value)); static_assert(!ranges::is_sorted(ints, ranges::greater(), &Int::value), "");
} }
TEST(RangesTest, IsSortedUntil) { TEST(RangesTest, IsSortedUntil) {
int input[] = {3, 1, 2, 0, 4}; constexpr int input[] = {3, 1, 2, 0, 4};
EXPECT_EQ(input + 3, ranges::is_sorted_until(input + 1, input + 3)); static_assert(input + 3 == ranges::is_sorted_until(input + 1, input + 3), "");
EXPECT_EQ(input + 3, ranges::is_sorted_until(input + 1, input + 4)); static_assert(input + 3 == ranges::is_sorted_until(input + 1, input + 4), "");
EXPECT_EQ(input + 2, static_assert(
ranges::is_sorted_until(input, input + 2, ranges::greater())); input + 2 == ranges::is_sorted_until(input, input + 2, ranges::greater()),
"");
Int ints[] = {0, 1, 2, 3, 4}; constexpr Int ints[] = {0, 1, 2, 3, 4};
EXPECT_EQ(ints + 5, ranges::is_sorted_until(ints, {}, &Int::value)); static_assert(ints + 5 == ranges::is_sorted_until(ints, {}, &Int::value), "");
EXPECT_EQ(ints + 1, static_assert(
ranges::is_sorted_until(ints, ranges::greater(), &Int::value)); ints + 1 == ranges::is_sorted_until(ints, ranges::greater(), &Int::value),
"");
} }
TEST(RangesTest, NthElement) { TEST(RangesTest, NthElement) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment