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

[base] Ranges: Implement [alg.merge]

This change adds implementations of the algorithms in [alg.merge] to
//base/ranges/util.

Bug: 1071094
Change-Id: I7576ddd3c84738437959317b881c4883339359b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2339975
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796031}
parent 9b6e9c2a
...@@ -3503,7 +3503,161 @@ constexpr auto partition_point(Range&& range, Pred pred, Proj proj = {}) { ...@@ -3503,7 +3503,161 @@ constexpr auto partition_point(Range&& range, Pred pred, Proj proj = {}) {
// [alg.merge] Merge // [alg.merge] Merge
// Reference: https://wg21.link/alg.merge // Reference: https://wg21.link/alg.merge
// TODO(crbug.com/1071094): Implement. // Let `N` be `(last1 - first1) + (last2 - first2)`.
//
// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting
// range does not overlap with either of the original ranges.
//
// Effects: Copies all the elements of the two ranges `[first1, last1)` and
// `[first2, last2)` into the range `[result, result_last)`, where `result_last`
// is `result + N`. If an element `a` precedes `b` in an input range, `a` is
// copied into the output range before `b`. If `e1` is an element of
// `[first1, last1)` and `e2` of `[first2, last2)`, `e2` is copied into the
// output range before `e1` if and only if
// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`.
//
// Returns: `result_last`.
//
// Complexity: At most `N - 1` comparisons and applications of each projection.
//
// Remarks: Stable.
//
// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(I1
template <typename InputIterator1,
typename InputIterator2,
typename OutputIterator,
typename Comp = ranges::less,
typename Proj1 = identity,
typename Proj2 = identity,
typename = internal::iterator_category_t<InputIterator1>,
typename = internal::iterator_category_t<InputIterator2>,
typename = internal::iterator_category_t<OutputIterator>,
typename = indirect_result_t<Comp&,
projected<InputIterator1, Proj1>,
projected<InputIterator2, Proj2>>,
typename = indirect_result_t<Comp&,
projected<InputIterator2, Proj2>,
projected<InputIterator1, Proj1>>>
constexpr auto merge(InputIterator1 first1,
InputIterator1 last1,
InputIterator2 first2,
InputIterator2 last2,
OutputIterator result,
Comp comp = {},
Proj1 proj1 = {},
Proj2 proj2 = {}) {
// Needs to opt-in to all permutations, since std::merge expects
// comp(proj2(lhs), proj1(rhs)) to compile.
return std::merge(
first1, last1, first2, last2, result,
internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
}
// Let `N` be `size(range1) + size(range2)`.
//
// Preconditions: The ranges `range1` and `range2` are sorted with respect to
// `comp` and `proj1` or `proj2`, respectively. The resulting range does not
// overlap with either of the original ranges.
//
// Effects: Copies all the elements of the two ranges `range1` and `range2` into
// the range `[result, result_last)`, where `result_last` is `result + N`. If an
// element `a` precedes `b` in an input range, `a` is copied into the output
// range before `b`. If `e1` is an element of `range1` and `e2` of `range2`,
// `e2` is copied into the output range before `e1` if and only if
// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`.
//
// Returns: `result_last`.
//
// Complexity: At most `N - 1` comparisons and applications of each projection.
//
// Remarks: Stable.
//
// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(R1
template <typename Range1,
typename Range2,
typename OutputIterator,
typename Comp = ranges::less,
typename Proj1 = identity,
typename Proj2 = identity,
typename = internal::range_category_t<Range1>,
typename = internal::range_category_t<Range2>,
typename = internal::iterator_category_t<OutputIterator>,
typename = indirect_result_t<Comp&,
projected<iterator_t<Range1>, Proj1>,
projected<iterator_t<Range2>, Proj2>>,
typename = indirect_result_t<Comp&,
projected<iterator_t<Range2>, Proj2>,
projected<iterator_t<Range1>, Proj1>>>
constexpr auto merge(Range1&& range1,
Range2&& range2,
OutputIterator result,
Comp comp = {},
Proj1 proj1 = {},
Proj2 proj2 = {}) {
return ranges::merge(ranges::begin(range1), ranges::end(range1),
ranges::begin(range2), ranges::end(range2), result,
std::move(comp), std::move(proj1), std::move(proj2));
}
// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges sorted
// with respect to `comp` and `proj`.
//
// Effects: Merges two sorted consecutive ranges `[first, middle)` and
// `[middle, last)`, putting the result of the merge into the range
// `[first, last)`. The resulting range is sorted with respect to `comp` and
// `proj`.
//
// Returns: `last`.
//
// Complexity: Let `N = last - first`: If enough additional memory is available,
// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either
// case, twice as many projections as comparisons.
//
// Remarks: Stable.
//
// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(I
template <typename BidirectionalIterator,
typename Comp = ranges::less,
typename Proj = identity,
typename = internal::iterator_category_t<BidirectionalIterator>>
constexpr auto inplace_merge(BidirectionalIterator first,
BidirectionalIterator middle,
BidirectionalIterator last,
Comp comp = {},
Proj proj = {}) {
std::inplace_merge(first, middle, last,
internal::ProjectedBinaryPredicate(comp, proj, proj));
return last;
}
// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid
// ranges sorted with respect to `comp` and `proj`.
//
// Effects: Merges two sorted consecutive ranges `[begin(range), middle)` and
// `[middle, end(range))`, putting the result of the merge into `range`. The
// resulting range is sorted with respect to `comp` and `proj`.
//
// Returns: `end(range)`.
//
// Complexity: Let `N = size(range)`: If enough additional memory is available,
// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either
// case, twice as many projections as comparisons.
//
// Remarks: Stable.
//
// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(R
template <typename Range,
typename Comp = ranges::less,
typename Proj = identity,
typename = internal::range_category_t<Range>>
constexpr auto inplace_merge(Range&& range,
iterator_t<Range> middle,
Comp comp = {},
Proj proj = {}) {
return ranges::inplace_merge(ranges::begin(range), middle, ranges::end(range),
std::move(comp), std::move(proj));
}
// [alg.set.operations] Set operations on sorted structures // [alg.set.operations] Set operations on sorted structures
// Reference: https://wg21.link/alg.set.operations // Reference: https://wg21.link/alg.set.operations
......
...@@ -1099,4 +1099,37 @@ TEST(RangesTest, PartitionPoint) { ...@@ -1099,4 +1099,37 @@ TEST(RangesTest, PartitionPoint) {
EXPECT_EQ(ints + 2, ranges::partition_point(ints, lt_2, &Int::value)); EXPECT_EQ(ints + 2, ranges::partition_point(ints, lt_2, &Int::value));
} }
TEST(RangesTest, Merge) {
int input1[] = {0, 2, 4, 6, 8};
int input2[] = {1, 3, 5, 7, 9};
int output[10];
EXPECT_EQ(output + 10,
ranges::merge(input1, input1 + 5, input2, input2 + 5, output));
EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
Int ints1[] = {0, 2, 4, 6, 8};
Int ints2[] = {1, 3, 5, 7, 9};
Int outs[10];
EXPECT_EQ(outs + 10,
ranges::merge(ints1, ints2, outs, {}, &Int::value, &Int::value));
EXPECT_THAT(outs, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
EXPECT_EQ(outs + 10, ranges::merge(input1, ints1, outs, {}, {}, &Int::value));
EXPECT_THAT(outs, ElementsAre(0, 0, 2, 2, 4, 4, 6, 6, 8, 8));
EXPECT_EQ(outs + 10, ranges::merge(ints2, input2, outs, {}, &Int::value, {}));
EXPECT_THAT(outs, ElementsAre(1, 1, 3, 3, 5, 5, 7, 7, 9, 9));
}
TEST(RangesTest, InplaceMerge) {
int input[] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};
EXPECT_EQ(input + 10, ranges::inplace_merge(input, input + 5, input + 10));
EXPECT_THAT(input, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
Int ints[] = {8, 6, 4, 2, 0, 9, 7, 5, 3, 1};
EXPECT_EQ(ints + 10, ranges::inplace_merge(ints, ints + 5, ranges::greater(),
&Int::value));
EXPECT_THAT(ints, ElementsAre(9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
}
} // namespace util } // namespace util
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