#ifndef ARRAY2D_H_
#define ARRAY2D_H_

#include <iostream>
#include <algorithm>
#include <cassert>
#include <vector>
#include <string>

template<typename T>
class Array2D {
 public:
  typedef typename std::vector<T>::reference reference;
  typedef typename std::vector<T>::const_reference const_reference;
  typedef typename std::vector<T>::iterator iterator;
  typedef typename std::vector<T>::const_iterator const_iterator;
  Array2D() : width_(0), height_(0) {}
  Array2D(int w, int h, const T& d = T()) :
    width_(w), height_(h), data_(w*h, d) {}
  Array2D(const Array2D& rhs) :
    width_(rhs.width_), height_(rhs.height_), data_(rhs.data_) {}
  bool empty() const { return data_.empty(); }
  void resize(int w, int h, const T& d = T()) {
    data_.resize(w * h, d);
    width_ = w;
    height_ = h;
  }
  const Array2D& operator=(const Array2D& rhs) {
    data_ = rhs.data_;
    width_ = rhs.width_;
    height_ = rhs.height_;
    return *this;
  }
  void fill(const T& v) { data_.assign(data_.size(), v); }
  int width() const { return width_; }
  int height() const { return height_; }
  reference operator()(int i, int j) {
    return data_[offset(i, j)];
  }
  void clear() { data_.clear(); width_=0; height_=0; }
  const_reference operator()(int i, int j) const {
    return data_[offset(i, j)];
  }
  iterator begin_col(int j) {
    return data_.begin() + offset(0,j);
  }
  const_iterator begin_col(int j) const {
    return data_.begin() + offset(0,j);
  }
  iterator end_col(int j) {
    return data_.begin() + offset(0,j) + width_;
  }
  const_iterator end_col(int j) const {
    return data_.begin() + offset(0,j) + width_;
  }
  iterator end() { return data_.end(); }
  const_iterator end() const { return data_.end(); }
  const Array2D<T>& operator*=(const T& x) {
    std::transform(data_.begin(), data_.end(), data_.begin(),
        std::bind2nd(std::multiplies<T>(), x));
  }
  const Array2D<T>& operator/=(const T& x) {
    std::transform(data_.begin(), data_.end(), data_.begin(),
        std::bind2nd(std::divides<T>(), x));
  }
  const Array2D<T>& operator+=(const Array2D<T>& m) {
    std::transform(m.data_.begin(), m.data_.end(), data_.begin(), data_.begin(), std::plus<T>());
  }
  const Array2D<T>& operator-=(const Array2D<T>& m) {
    std::transform(m.data_.begin(), m.data_.end(), data_.begin(), data_.begin(), std::minus<T>());
  }

 private:
  inline int offset(int i, int j) const {
    assert(i<width_);
    assert(j<height_);
    return i + j * width_;
  }

  int width_;
  int height_;

  std::vector<T> data_;
};

template <typename T>
Array2D<T> operator*(const Array2D<T>& l, const T& scalar) {
  Array2D<T> res(l);
  res *= scalar;
  return res;
}

template <typename T>
Array2D<T> operator*(const T& scalar, const Array2D<T>& l) {
  Array2D<T> res(l);
  res *= scalar;
  return res;
}

template <typename T>
Array2D<T> operator/(const Array2D<T>& l, const T& scalar) {
  Array2D<T> res(l);
  res /= scalar;
  return res;
}

template <typename T>
Array2D<T> operator+(const Array2D<T>& l, const Array2D<T>& r) {
  Array2D<T> res(l);
  res += r;
  return res;
}

template <typename T>
Array2D<T> operator-(const Array2D<T>& l, const Array2D<T>& r) {
  Array2D<T> res(l);
  res -= r;
  return res;
}

template <typename T>
inline std::ostream& operator<<(std::ostream& os, const Array2D<T>& m) {
  for (int i=0; i<m.width(); ++i) {
    for (int j=0; j<m.height(); ++j)
      os << '\t' << m(i,j);
    os << '\n';
  }
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const Array2D<bool>& m) {
  os << ' ';
  for (int j=0; j<m.height(); ++j)
    os << (j%10);
  os << "\n";
  for (int i=0; i<m.width(); ++i) {
    os << (i%10);
    for (int j=0; j<m.height(); ++j)
      os << (m(i,j) ? '*' : '.');
    os << (i%10) << "\n";
  }
  os << ' ';
  for (int j=0; j<m.height(); ++j)
    os << (j%10);
  os << "\n";
  return os;
}

inline std::ostream& operator<<(std::ostream& os, const Array2D<std::vector<bool> >& m) {
  os << ' ';
  for (int j=0; j<m.height(); ++j)
    os << (j%10) << "\t";
  os << "\n";
  for (int i=0; i<m.width(); ++i) {
    os << (i%10);
    for (int j=0; j<m.height(); ++j) {
      const std::vector<bool>& ar = m(i,j);
      for (int k=0; k<ar.size(); ++k)
        os << (ar[k] ? '*' : '.');
    }
    os << "\t";
    os << (i%10) << "\n";
  }
  os << ' ';
  for (int j=0; j<m.height(); ++j)
    os << (j%10) << "\t";
  os << "\n";
  return os;
}

#endif