blob: 130af2d31c2b121d9dacac49a05579e9426ba21d [file]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=9000000
// <algorithm>
// template<permutable I, sentinel_for<I> S>
// constexpr subrange<I> ranges::shift_right(I first, S last, iter_difference_t<I> n);
// template<forward_range R>
// requires permutable<iterator_t<R>>
// constexpr borrowed_subrange_t<R> ranges::shift_right(R&& r, range_difference_t<R> n)
#include <algorithm>
#include <array>
#include <cassert>
#include <ranges>
#include <vector>
#include "almost_satisfies_types.h"
#include "MoveOnly.h"
#include "test_macros.h"
#include "test_iterators.h"
#include "type_algorithms.h"
struct InvalidDifferenceT {};
template <class Iter, class Sent = Iter, class N = std::iter_difference_t<Iter>>
concept HasShiftRightIt = requires(Iter iter, Sent sent, N n) { std::ranges::shift_right(iter, sent, n); };
static_assert(HasShiftRightIt<int*>);
static_assert(HasShiftRightIt<int*, sentinel_wrapper<int*>>);
static_assert(HasShiftRightIt<int*, sized_sentinel<int*>>);
static_assert(!HasShiftRightIt<int*, int*, InvalidDifferenceT>);
static_assert(!HasShiftRightIt<int*, int, int>);
static_assert(!HasShiftRightIt<ForwardIteratorNotDerivedFrom>);
static_assert(!HasShiftRightIt<PermutableNotForwardIterator>);
static_assert(!HasShiftRightIt<PermutableNotSwappable>);
template <class Range, class N = std::ranges::range_difference_t<Range>>
concept HasShiftRightR = requires(Range range, N n) { std::ranges::shift_right(range, n); };
static_assert(HasShiftRightR<UncheckedRange<int*>>);
static_assert(!HasShiftRightR<UncheckedRange<int*>, InvalidDifferenceT>);
static_assert(!HasShiftRightR<ForwardRangeNotDerivedFrom>);
static_assert(!HasShiftRightR<PermutableRangeNotForwardIterator>);
static_assert(!HasShiftRightR<PermutableRangeNotSwappable>);
struct TrackCopyMove {
mutable int copy_count = 0;
int move_count = 0;
constexpr TrackCopyMove() = default;
constexpr TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
++copy_count;
++other.copy_count;
}
constexpr TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
++move_count;
++other.move_count;
}
constexpr TrackCopyMove& operator=(const TrackCopyMove& other) {
++copy_count;
++other.copy_count;
return *this;
}
constexpr TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
++move_count;
++other.move_count;
return *this;
}
};
template <class Iter, class Sent>
constexpr void test_iter_sent() {
{
const std::array<int, 8> original = {3, 1, 4, 1, 5, 9, 2, 6};
// (iterator, sentinel) overload
for (size_t n = 0; n <= original.size(); ++n) {
for (size_t k = 0; k <= n + 2; ++k) {
std::vector<int> scratch(n);
auto begin = Iter(scratch.data());
auto end = Sent(Iter(scratch.data() + n));
std::ranges::copy(original.begin(), original.begin() + n, begin);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(begin, end, k);
assert(result.end() == end);
if (k < n) {
assert(result.begin() == Iter(scratch.data() + k));
assert(std::ranges::equal(original.begin(), original.begin() + n - k, result.begin(), result.end()));
} else {
assert(result.begin() == end);
assert(std::ranges::equal(original.begin(), original.begin() + n, begin, end));
}
}
}
// (range) overload
for (size_t n = 0; n <= original.size(); ++n) {
for (size_t k = 0; k <= n + 2; ++k) {
std::vector<int> scratch(n);
auto begin = Iter(scratch.data());
auto end = Sent(Iter(scratch.data() + n));
std::ranges::copy(original.begin(), original.begin() + n, begin);
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(range, k);
assert(result.end() == end);
if (k < n) {
assert(result.begin() == Iter(scratch.data() + k));
assert(std::ranges::equal(original.begin(), original.begin() + n - k, result.begin(), result.end()));
} else {
assert(result.begin() == end);
assert(std::ranges::equal(original.begin(), original.begin() + n, begin, end));
}
}
}
}
// n == 0
{
std::array<int, 3> input = {0, 1, 2};
const std::array<int, 3> expected = {0, 1, 2};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(begin, end, 0);
assert(std::ranges::equal(expected, result));
assert(result.begin() == begin);
assert(result.end() == end);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(range, 0);
assert(std::ranges::equal(expected, result));
assert(result.begin() == begin);
assert(result.end() == end);
}
}
// n == len
{
std::array<int, 3> input = {0, 1, 2};
const std::array<int, 3> expected = {0, 1, 2};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(begin, end, input.size());
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(range, input.size());
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
}
// n > len
{
std::array<int, 3> input = {0, 1, 2};
const std::array<int, 3> expected = {0, 1, 2};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(begin, end, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(range, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
}
// empty range
{
std::vector<int> input = {};
const std::vector<int> expected = {};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(begin, end, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == begin);
assert(result.end() == begin);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(range, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == begin);
assert(result.end() == begin);
}
}
}
constexpr bool test() {
types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
test_iter_sent<Iter, Iter>();
test_iter_sent<Iter, sentinel_wrapper<Iter>>();
test_iter_sent<Iter, sized_sentinel<Iter>>();
});
// Complexity: At most (last - first) - n assignments or swaps.
{
constexpr int length = 100;
constexpr int n = length / 2;
auto make_vec = []() {
std::vector<TrackCopyMove> vec;
vec.reserve(length);
for (int i = 0; i < length; ++i) {
vec.emplace_back();
}
return vec;
};
{ // (iterator, sentinel) overload
auto input = make_vec();
auto result = std::ranges::shift_right(input.begin(), input.end(), n);
assert(result.begin() == input.begin() + length - n);
assert(result.end() == input.end());
auto total_copies = 0;
auto total_moves = 0;
for (auto it = result.begin(); it != result.end(); ++it) {
const auto& item = *it;
total_copies += item.copy_count;
total_moves += item.move_count;
}
assert(total_copies == 0);
assert(total_moves <= length - n);
}
{ // (range) overload
auto input = make_vec();
auto result = std::ranges::shift_right(input, n);
assert(result.begin() == input.begin() + length - n);
assert(result.end() == input.end());
auto total_copies = 0;
auto total_moves = 0;
for (auto it = result.begin(); it != result.end(); ++it) {
const auto& item = *it;
total_copies += item.copy_count;
total_moves += item.move_count;
}
assert(total_copies == 0);
assert(total_moves <= length - n);
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}