#pragma once

#include <vector>

template<typename It>
class IteratorRange {
 public:
  IteratorRange(It b, It e) :
  begin_(b), end_(e), size_(std::distance(b, e)) {}

  [[nodiscard]] It begin() const {
    return begin_;
  }

  [[nodiscard]] It end() const {
    return end_;
  }

  [[nodiscard]] std::size_t size() const {
    return size_;
  }

 private:
  It begin_, end_;
  std::size_t size_;
};

template <typename It>
class Slicer {
 public:
  Slicer(It b, It e, std::size_t page_size) {
    std::size_t i = 0;
    It begin = b, end = b;
    for (; end != e;) {
      ++i;
      ++end;
      if (i == page_size) {
        pages_.push_back(IteratorRange<It>(begin, end));
        i = 0;
        begin = end;
        continue;
      }
    }
    if (i > 0) {
      pages_.push_back(IteratorRange<It>(begin, end));
    }
  }

  [[nodiscard]] auto begin() const {
    return pages_.begin();
  }

  [[nodiscard]] auto end() const {
    return pages_.end();
  }

  std::size_t size() {
    return pages_.size();
  }

 private:
  // It begin_, end_;
  std::vector<IteratorRange<It>> pages_;
};

template<typename C>
auto Slice(C &c, std::size_t page_size) {
  return Slicer<typename C::iterator>(c.begin(), c.end(), page_size);
}

template<typename C>
auto Slice(const C &c, std::size_t page_size) {
  return Slicer<typename C::const_iterator>(c.begin(), c.end(), page_size);
}

template<>
auto Slice(const IteratorRange<std::vector<int>::iterator> &c, std::size_t page_size) {
  return Slicer<std::vector<int>::iterator>(c.begin(), c.end(), page_size);
}