373 lines
13 KiB
C++
373 lines
13 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// <algorithm>
|
|
|
|
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
|
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=9000000
|
|
|
|
// template<permutable I, sentinel_for<I> S>
|
|
// constexpr subrange<I> ranges::shift_left(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_left(R&& r, range_difference_t<R> n)
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <ranges>
|
|
#include <iterator>
|
|
#include <vector>
|
|
|
|
#include "almost_satisfies_types.h"
|
|
#include "MoveOnly.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 HasShiftLeftIt = requires(Iter iter, Sent sent, N n) { std::ranges::shift_left(iter, sent, n); };
|
|
|
|
static_assert(HasShiftLeftIt<int*>);
|
|
static_assert(HasShiftLeftIt<int*, sentinel_wrapper<int*>>);
|
|
static_assert(HasShiftLeftIt<int*, sized_sentinel<int*>>);
|
|
static_assert(!HasShiftLeftIt<int*, int*, InvalidDifferenceT>);
|
|
static_assert(!HasShiftLeftIt<int*, int, int>);
|
|
|
|
static_assert(!HasShiftLeftIt<ForwardIteratorNotDerivedFrom>);
|
|
static_assert(!HasShiftLeftIt<PermutableNotForwardIterator>);
|
|
static_assert(!HasShiftLeftIt<PermutableNotSwappable>);
|
|
|
|
template <class Range, class N = std::ranges::range_difference_t<Range>>
|
|
concept HasShiftLeftR = requires(Range range, N n) { std::ranges::shift_left(range, n); };
|
|
|
|
static_assert(HasShiftLeftR<UncheckedRange<int*>>);
|
|
static_assert(!HasShiftLeftR<UncheckedRange<int*>, InvalidDifferenceT>);
|
|
|
|
static_assert(!HasShiftLeftR<ForwardRangeNotDerivedFrom>);
|
|
static_assert(!HasShiftLeftR<PermutableRangeNotForwardIterator>);
|
|
static_assert(!HasShiftLeftR<PermutableRangeNotSwappable>);
|
|
|
|
// An iterator whose iterator_traits::difference_type is not the same as
|
|
// std::iter_difference_t<It>.
|
|
struct DiffTypeIter {
|
|
int* it_;
|
|
|
|
using difference_type = InvalidDifferenceT;
|
|
using value_type = int;
|
|
using reference = int&;
|
|
using pointer = int*;
|
|
using iterator_category = std::random_access_iterator_tag;
|
|
|
|
constexpr DiffTypeIter() : it_() {}
|
|
constexpr explicit DiffTypeIter(int* it) : it_(it) {}
|
|
|
|
constexpr reference operator*() const { return *it_; }
|
|
constexpr reference operator[](int n) const { return it_[n]; }
|
|
|
|
constexpr DiffTypeIter& operator++() {
|
|
++it_;
|
|
return *this;
|
|
}
|
|
constexpr DiffTypeIter& operator--() {
|
|
--it_;
|
|
return *this;
|
|
}
|
|
constexpr DiffTypeIter operator++(int) { return DiffTypeIter(it_++); }
|
|
constexpr DiffTypeIter operator--(int) { return DiffTypeIter(it_--); }
|
|
|
|
constexpr DiffTypeIter& operator+=(int n) {
|
|
it_ += n;
|
|
return *this;
|
|
}
|
|
constexpr DiffTypeIter& operator-=(int n) {
|
|
it_ -= n;
|
|
return *this;
|
|
}
|
|
friend constexpr DiffTypeIter operator+(DiffTypeIter x, int n) {
|
|
x += n;
|
|
return x;
|
|
}
|
|
friend constexpr DiffTypeIter operator+(int n, DiffTypeIter x) {
|
|
x += n;
|
|
return x;
|
|
}
|
|
friend constexpr DiffTypeIter operator-(DiffTypeIter x, int n) {
|
|
x -= n;
|
|
return x;
|
|
}
|
|
friend constexpr int operator-(DiffTypeIter x, DiffTypeIter y) { return x.it_ - y.it_; }
|
|
|
|
friend constexpr bool operator==(const DiffTypeIter& x, const DiffTypeIter& y) { return x.it_ == y.it_; }
|
|
friend constexpr bool operator!=(const DiffTypeIter& x, const DiffTypeIter& y) { return x.it_ != y.it_; }
|
|
friend constexpr bool operator<(const DiffTypeIter& x, const DiffTypeIter& y) { return x.it_ < y.it_; }
|
|
friend constexpr bool operator<=(const DiffTypeIter& x, const DiffTypeIter& y) { return x.it_ <= y.it_; }
|
|
friend constexpr bool operator>(const DiffTypeIter& x, const DiffTypeIter& y) { return x.it_ > y.it_; }
|
|
friend constexpr bool operator>=(const DiffTypeIter& x, const DiffTypeIter& y) { return x.it_ >= y.it_; }
|
|
};
|
|
|
|
template <>
|
|
struct std::incrementable_traits<DiffTypeIter> {
|
|
using difference_type = std::ptrdiff_t;
|
|
};
|
|
|
|
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::array<int, 8> scratch;
|
|
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_left(begin, end, k);
|
|
|
|
assert(result.begin() == begin);
|
|
if (k < n) {
|
|
assert(result.end() == Iter(scratch.data() + n - k));
|
|
assert(std::ranges::equal(original.begin() + k, original.begin() + n, result.begin(), result.end()));
|
|
} else {
|
|
assert(result.end() == begin);
|
|
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::array<int, 8> scratch;
|
|
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_left(range, k);
|
|
|
|
assert(result.begin() == begin);
|
|
if (k < n) {
|
|
assert(result.end() == Iter(scratch.data() + n - k));
|
|
assert(std::ranges::equal(original.begin() + k, original.begin() + n, begin, result.end()));
|
|
} else {
|
|
assert(result.end() == begin);
|
|
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_left(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_left(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_left(begin, end, input.size());
|
|
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_left(range, input.size());
|
|
assert(std::ranges::equal(expected, input));
|
|
assert(result.begin() == begin);
|
|
assert(result.end() == begin);
|
|
}
|
|
}
|
|
|
|
// 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_left(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_left(range, input.size() + 1);
|
|
assert(std::ranges::equal(expected, input));
|
|
assert(result.begin() == begin);
|
|
assert(result.end() == begin);
|
|
}
|
|
}
|
|
|
|
// 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_left(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_left(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>>();
|
|
});
|
|
test_iter_sent<DiffTypeIter, DiffTypeIter>();
|
|
|
|
// Complexity: At most (last - first) - n assignments
|
|
{
|
|
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_left(input.begin(), input.end(), n);
|
|
assert(result.begin() == input.begin());
|
|
assert(result.end() == input.begin() + (length - n));
|
|
|
|
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_left(input, n);
|
|
assert(result.begin() == input.begin());
|
|
assert(result.end() == input.begin() + (length - n));
|
|
|
|
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;
|
|
}
|