[libcxx-commits] [libcxx] [libc++][ranges] implement `ranges::shift_left` (PR #83231)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Feb 29 04:18:41 PST 2024


================
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// 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 <ranges>
+#include <iterator>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+#include "MoveOnly.h"
+
+template <class Iter, class Sent = Iter, class Count = std::size_t>
+concept HasShiftLeftIt = requires(Iter iter, Sent sent, Count n) { std::ranges::shift_left(iter, sent, n); };
+
+static_assert(HasShiftLeftIt<int*>);
+static_assert(!HasShiftLeftIt<ForwardIteratorNotDerivedFrom>);
+static_assert(!HasShiftLeftIt<PermutableNotForwardIterator>);
+static_assert(!HasShiftLeftIt<PermutableNotSwappable>);
+
+template <class Range, class Count = std::size_t>
+concept HasShiftLeftR = requires(Range range, Count n) { std::ranges::shift_left(range, n); };
+
+static_assert(HasShiftLeftR<UncheckedRange<int*>>);
+static_assert(!HasShiftLeftR<ForwardRangeNotDerivedFrom>);
+static_assert(!HasShiftLeftR<PermutableRangeNotForwardIterator>);
+static_assert(!HasShiftLeftR<PermutableRangeNotSwappable>);
+
+template <class Iter, class Sent>
+constexpr void test_iter_sent() {
+  {
+    const std::array<int, 8> original = {3, 1, 4, 1, 5, 9, 2, 6};
+    std::array<int, 8> scratch;
+
+    // (iterator, sentinel) overload
+    for (size_t n = 0; n <= original.size(); ++n) {
+      for (size_t k = 0; k <= n + 2; ++k) {
+        auto begin = Iter(scratch.data());
+        auto end   = Sent(Iter(scratch.data() + n));
+        std::ranges::copy(original.begin(), original.begin() + n, begin);
+        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) {
+        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);
+        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()));
+      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);
+      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()));
+      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);
+      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()));
+      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);
+      auto result = std::ranges::shift_left(range, input.size() + 1);
+      assert(std::ranges::equal(expected, input));
+      assert(result.begin() == begin);
+      assert(result.end() == begin);
+    }
+  }
+}
+
+template <class Iter>
+constexpr void test_iter() {
+  test_iter_sent<Iter, Iter>();
+  test_iter_sent<Iter, sentinel_wrapper<Iter>>();
+  test_iter_sent<Iter, sized_sentinel<Iter>>();
+}
+
+constexpr bool test() {
+  test_iter<forward_iterator<int*>>();
+  test_iter<bidirectional_iterator<int*>>();
+  test_iter<random_access_iterator<int*>>();
+  test_iter<contiguous_iterator<int*>>();
+  test_iter<int*>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
----------------
philnik777 wrote:

```suggestion
int main(int, char**) {
  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>>();
  });
  static_assert(test());

  return 0;
}
```

https://github.com/llvm/llvm-project/pull/83231


More information about the libcxx-commits mailing list