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,
ForwardIterator last,
Pred pred = {},
Proj proj = {}) {
return std::adjacent_find(
first, last, internal::ProjectedBinaryPredicate(pred, proj, proj));
// Implementation inspired by cppreference.com:
// 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))))`.
......@@ -2890,12 +2905,12 @@ constexpr auto partial_sort_copy(Range1&& range,
// [is.sorted] is_sorted
// Reference: https://wg21.link/is.sorted
// Returns: Whether the range `[first, last)` is sorted with respect to `comp`
// and `proj`.
// Returns: The last iterator `i` in `[first, last]` for which the range
// `[first, i)` is sorted with respect to `comp` and `proj`.
//
// 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,
typename Comp = ranges::less,
typename Proj = identity,
......@@ -2903,19 +2918,35 @@ template <typename ForwardIterator,
typename = indirect_result_t<Comp&,
projected<ForwardIterator, Proj>,
projected<ForwardIterator, Proj>>>
constexpr auto is_sorted(ForwardIterator first,
ForwardIterator last,
Comp comp = {},
Proj proj = {}) {
return std::is_sorted(first, last,
internal::ProjectedBinaryPredicate(comp, proj, proj));
constexpr auto is_sorted_until(ForwardIterator first,
ForwardIterator last,
Comp comp = {},
Proj proj = {}) {
// Implementation inspired by cppreference.com:
// 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.
//
// 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,
typename Comp = ranges::less,
typename Proj = identity,
......@@ -2923,17 +2954,17 @@ template <typename Range,
typename = indirect_result_t<Comp&,
projected<iterator_t<Range>, Proj>,
projected<iterator_t<Range>, Proj>>>
constexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) {
return ranges::is_sorted(ranges::begin(range), ranges::end(range),
std::move(comp), std::move(proj));
constexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) {
return ranges::is_sorted_until(ranges::begin(range), ranges::end(range),
std::move(comp), std::move(proj));
}
// Returns: The last iterator `i` in `[first, last]` for which the range
// `[first, i)` is sorted with respect to `comp` and `proj`.
// Returns: Whether the range `[first, last)` is sorted with respect to `comp`
// and `proj`.
//
// 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,
typename Comp = ranges::less,
typename Proj = identity,
......@@ -2941,20 +2972,19 @@ template <typename ForwardIterator,
typename = indirect_result_t<Comp&,
projected<ForwardIterator, Proj>,
projected<ForwardIterator, Proj>>>
constexpr auto is_sorted_until(ForwardIterator first,
ForwardIterator last,
Comp comp = {},
Proj proj = {}) {
return std::is_sorted_until(
first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));
constexpr auto is_sorted(ForwardIterator first,
ForwardIterator last,
Comp comp = {},
Proj proj = {}) {
return ranges::is_sorted_until(first, last, std::move(comp),
std::move(proj)) == last;
}
// 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`.
// Returns: Whether `range` is sorted with respect to `comp` and `proj`.
//
// 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,
typename Comp = ranges::less,
typename Proj = identity,
......@@ -2962,9 +2992,9 @@ template <typename Range,
typename = indirect_result_t<Comp&,
projected<iterator_t<Range>, Proj>,
projected<iterator_t<Range>, Proj>>>
constexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) {
return ranges::is_sorted_until(ranges::begin(range), ranges::end(range),
std::move(comp), std::move(proj));
constexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) {
return ranges::is_sorted(ranges::begin(range), ranges::end(range),
std::move(comp), std::move(proj));
}
// [alg.nth.element] Nth element
......
......@@ -255,15 +255,19 @@ TEST(RangesTest, FindFirstOf) {
}
TEST(RangesTest, AdjacentFind) {
int array[] = {1, 2, 3, 3};
EXPECT_EQ(array + 2, ranges::adjacent_find(array, ranges::end(array)));
EXPECT_EQ(array,
ranges::adjacent_find(array, ranges::end(array), ranges::less{}));
constexpr int array[] = {1, 2, 3, 3};
static_assert(array + 2 == ranges::adjacent_find(array, ranges::end(array)),
"");
static_assert(
array == ranges::adjacent_find(array, ranges::end(array), ranges::less{}),
"");
Int ints[] = {{6}, {6}, {5}, {4}};
EXPECT_EQ(ints, ranges::adjacent_find(ints, ranges::equal_to{}, &Int::value));
EXPECT_EQ(ranges::end(ints),
ranges::adjacent_find(ints, ranges::less{}, &Int::value));
constexpr Int ints[] = {{6}, {6}, {5}, {4}};
static_assert(
ints == ranges::adjacent_find(ints, ranges::equal_to{}, &Int::value), "");
static_assert(ranges::end(ints) ==
ranges::adjacent_find(ints, ranges::less{}, &Int::value),
"");
}
TEST(RangesTest, Count) {
......@@ -899,27 +903,29 @@ TEST(RangesTest, PartialSortCopy) {
}
TEST(RangesTest, IsSorted) {
int input[] = {3, 1, 2, 0, 4};
EXPECT_TRUE(ranges::is_sorted(input + 1, input + 3));
EXPECT_FALSE(ranges::is_sorted(input + 1, input + 4));
EXPECT_TRUE(ranges::is_sorted(input, input + 2, ranges::greater()));
constexpr int input[] = {3, 1, 2, 0, 4};
static_assert(ranges::is_sorted(input + 1, input + 3), "");
static_assert(!ranges::is_sorted(input + 1, input + 4), "");
static_assert(ranges::is_sorted(input, input + 2, ranges::greater()), "");
Int ints[] = {0, 1, 2, 3, 4};
EXPECT_TRUE(ranges::is_sorted(ints, {}, &Int::value));
EXPECT_FALSE(ranges::is_sorted(ints, ranges::greater(), &Int::value));
constexpr Int ints[] = {0, 1, 2, 3, 4};
static_assert(ranges::is_sorted(ints, {}, &Int::value), "");
static_assert(!ranges::is_sorted(ints, ranges::greater(), &Int::value), "");
}
TEST(RangesTest, IsSortedUntil) {
int input[] = {3, 1, 2, 0, 4};
EXPECT_EQ(input + 3, ranges::is_sorted_until(input + 1, input + 3));
EXPECT_EQ(input + 3, ranges::is_sorted_until(input + 1, input + 4));
EXPECT_EQ(input + 2,
ranges::is_sorted_until(input, input + 2, ranges::greater()));
constexpr int input[] = {3, 1, 2, 0, 4};
static_assert(input + 3 == ranges::is_sorted_until(input + 1, input + 3), "");
static_assert(input + 3 == ranges::is_sorted_until(input + 1, input + 4), "");
static_assert(
input + 2 == ranges::is_sorted_until(input, input + 2, ranges::greater()),
"");
Int ints[] = {0, 1, 2, 3, 4};
EXPECT_EQ(ints + 5, ranges::is_sorted_until(ints, {}, &Int::value));
EXPECT_EQ(ints + 1,
ranges::is_sorted_until(ints, ranges::greater(), &Int::value));
constexpr Int ints[] = {0, 1, 2, 3, 4};
static_assert(ints + 5 == ranges::is_sorted_until(ints, {}, &Int::value), "");
static_assert(
ints + 1 == ranges::is_sorted_until(ints, ranges::greater(), &Int::value),
"");
}
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