#include <iterator>
#include <algorithm>
#include <tuple>
#include <functional>

using std::size_t;

template <typename TypeA, typename... Types>
class ZipIterator : public std::iterator<std::random_access_iterator_tag, std::tuple<decltype(*TypeA::begin()), decltype(*Types::begin())...>> {
public:
  typedef decltype(TypeA::begin()) TypeAit;
  typedef std::tuple<TypeAit, decltype(Types::begin())...> ItT;
  typedef std::tuple<decltype(*TypeA::begin()), decltype(*Types::begin())...> DataT;
  typedef std::tuple<TypeA*, Types*...> ContainerT;

  ZipIterator(TypeA first, Types... rest) :
      m_index(0) {
    if (sizeof...(rest) != 0) {
      ZipIterator leftover(rest...);
      m_its = std::tuple_cat(std::tuple<TypeAit>(first.begin()), leftover.m_its);
      m_containers = std::tuple<TypeA*, Types*...>(first.begin(), leftover.m_containers);
    } else {
      m_its = std::tuple<TypeAit>(first.begin());
      m_containers = std::tuple<TypeA*>(first.begin());
    }
  }

  ZipIterator(ZipIterator const& rhs) {
    copy(rhs);
  }

  inline ZipIterator& operator=(ZipIterator const& rhs) {
    copy(rhs);
  }
  
  inline ZipIterator& operator+=(int steps) const {
    additionImpl(m_its, steps);
    return *this;
  }

  inline ZipIterator& operator-=(int steps) const {
    subtractionImpl(m_its, steps);
    return *this;
  }

  inline DataT operator*() const {
    return construct(m_its);
  }

  inline DataT* operator->() const {
    return &construct(m_its);
  }

  inline DataT operator[](int rhs) const {
    return constructAt(rhs);
  }

  inline ZipIterator& operator++() {
    incrementImpl(m_its);
    return *this;
  }

  inline ZipIterator& operator--() {
    decrementImpl(m_its);
    return *this;
  }

  inline ZipIterator operator++(int) {
    ZipIterator tmp(*this);
    ++this;
    return tmp;
  }

  inline ZipIterator operator--(int) {
    ZipIterator tmp(*this);
    --this;
    return tmp;
  }

  inline ZipIterator operator+(int steps) const {
    ZipIterator copy(*this);
    copy.additionImpl(copy.m_its, steps);
    return copy;
  }

  inline ZipIterator operator-(int steps) const {
    ZipIterator copy(*this);
    copy.subtractionImpl(copy.m_its, steps);
    return copy;
  }

  inline int operator-(ZipIterator const& rhs) {
    return std::get<0>(m_its) - std::get<0>(rhs.m_its);
  }

  friend inline ZipIterator operator+(int lhs, ZipIterator const& rhs) {
    return rhs + lhs;
  }

  friend inline ZipIterator operator-(int lhs, ZipIterator const& rhs) {
    return rhs - lhs;
  }

  inline bool operator==(ZipIterator const& rhs) const {
    if (valid() || rhs.valid())
      return m_its == rhs.m_its;
    return true;
  }

  inline bool operator!=(ZipIterator const& rhs) const {
    return !(*this == rhs);
  }

  inline bool operator<(ZipIterator const& rhs) const {
    return m_its < rhs.m_its;
  }

  inline bool operator<=(ZipIterator const& rhs) const {
    return (*this == rhs) || (*this < rhs);
  }

  inline bool operator>=(ZipIterator const& rhs) const {
    return !(*this < rhs);
  }

  inline bool operator>(ZipIterator const& rhs) const {
    return !(*this <= rhs);
  }

  inline ZipIterator begin() const {
    ZipIterator copy(*this);
    copy.m_its = beginImpl(copy);
    return copy;
  }

  inline ZipIterator end() const {
    ZipIterator copy(*this);
    copy.m_its = endImpl(copy);
    return copy;
  }

private:
  inline void copy(ZipIterator const& copy) {
    m_its = copy.m_its;
    m_containers = copy.m_containers;
    m_index = copy.m_index;
  }

  //Begin

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), DataT>::type
  beginImpl(std::tuple<Tp...> const& its) {
    return std::tuple<>();
  }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), DataT>::type
  beginImpl(std::tuple<Tp...> const& its) {
    return std::tuple_cat(
             std::tuple<decltype(*std::get<I>(its))>(
               std::get<I>(m_containers).begin();
             ),
             constructImpl<I + 1>(its)
           );
  }
 
  //End

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), DataT>::type
  endImpl(std::tuple<Tp...> const& its) {
    return std::tuple<>();
  }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), DataT>::type
  endImpl(std::tuple<Tp...> const& its) {
    return std::tuple_cat(
             std::tuple<decltype(*std::get<I>(its))>(
               std::get<I>(m_containers).end();
             ),
             constructImpl<I + 1>(its)
           );
  }

  //Increment

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), void>::type
  incrementImpl(std::tuple<Tp...> const& its) { }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
  incrementImpl(std::tuple<Tp...> const& its) {
    ++(std::get<I>(its));
    incrementImpl<I + 1>(its);
  }

  //Decrement

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), void>::type
  decrementImpl(std::tuple<Tp...> const& its) { }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
  decrementImpl(std::tuple<Tp...> const& its) {
    --(std::get<I>(its));
    decrementImpl<I + 1>(its);
  }
  
  //Addition

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), void>::type
  additionImpl(std::tuple<Tp...> const& its, int steps) { }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
  additionImpl(std::tuple<Tp...> const& its, int steps) {
    std::get<I>(its) + steps;
    incrementImpl<I + 1>(its);
  }

  //Subtraction

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), void>::type
  subtractionImpl(std::tuple<Tp...> const& its, int steps) { }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
  subtractionImpl(std::tuple<Tp...> const& its, int steps) {
    std::get<I>(its) + steps;
    decrementImpl<I + 1>(its);
  }

  //Construct

  template<size_t I = 0, typename... Tp>
  inline auto constructImpl(std::tuple<Tp...> const& its) ->
  typename std::enable_if<(I == sizeof...(Tp)), typename std::tuple<decltype(*Tp)...>>::type {
    return std::tuple<>();
  }

  template<size_t I = 0, typename... Tp>
  inline auto constructImpl(std::tuple<Tp...> const& its) ->
  typename std::enable_if<(I < sizeof...(Tp)), typename std::tuple<decltype(*Tp)...>>::type {
    return std::tuple_cat(
             std::tuple<decltype(*std::get<I>(its))>(
               *std::get<I>(its)
             ),
             constructImpl<I + 1>(its)
           );
  }

  inline DataT construct() {
    if (valid()) {
      return constructImpl(m_its);
    }
    throw StarAlgorithm("Attempted to construct zipped tuple beyond end.");
  }

  //ConstructAt

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), typename std::tuple<decltype(*Tp)...>>::type
  constructAtImpl(std::tuple<Tp...> const& its, size_t index) {
    return std::tuple<>();
  }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), typename std::tuple<decltype(*Tp)...>>::type
  constructImpl(std::tuple<Tp...> const& its, size_t index) {
    return std::tuple_cat(
             std::tuple<decltype(*std::get<I>(its))>(
               std::get<I>(its)[index]
             ),
             constructAtImpl<I + 1>(its, index)
           );
  }

  inline DataT constructAt(size_t index) {
    if (validAt(index)) {
      constructAtImpl(m_its, index);
    }
    throw StarAlgorithm("Attempting to construct zipped tuple beyond end.");
  }

  //Valid

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), bool>::type
  validImpl(std::tuple<Tp...> const& its) {
    return true;
  }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I < sizeof...(Tp)), bool>::type
  validImpl(std::tuple<Tp...> const& its) {
    return std::get<I>(its) != std::get<I>(m_containers).end() &&
           validImpl<I + 1>(its);
  }

  inline bool valid() {
    validImpl(m_its);
  }

  //ValidAt

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<(I == sizeof...(Tp)), bool>::type
  validAtImpl(std::tuple<Tp...> const& its, size_t index) {
    return true;
  }

  template<size_t I = 0, typename... Tp>
  inline typename std::enable_if<I < sizeof...(Tp), bool>::type
  validAtImpl(std::tuple<Tp...> const& its, size_t index) {
    try {
      std::get<I>(its)[index];
    } catch (AlgorithmException const& e) {
      return false;
    }
    return validAtImpl<I + 1>(its, index);
  }

  inline bool validAt(size_t index) {
    validAtImpl(m_its, index);
  }

  ItT m_its;
  ContainerT m_containers;
};
