#ifndef VECTOR_HH
#define VECTOR_HH VECTOR_HH

#include <iostream>
#include <vector>
#include <initializer_list>
#include <cstdarg>
#include <cmath>

template <int dim>
class Vector {
public:
  Vector() {
    std::fill(data,data+dim,0.0);
  }

  Vector(std::vector<double> a) {
    std::copy(a.begin(),a.end(),data);
  }

  Vector(std::initializer_list<double> a) {
    std::copy(a.begin(),a.end(),data);
  }

  typedef Vector<dim> VectorType;

  VectorType & operator+=(const VectorType &other) {
    for (int i=0;i<dim;++i) {
      data[i] += other.data[i];
    }
    return *this;
  }
  // Add two vectors
  friend Vector operator+(const Vector<dim>& u,
                          const Vector<dim>& v) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i] = u.data[i] + v.data[i];
    }
    return w;
  }

  // Add a double to a vector
  friend Vector operator+(const Vector<dim>& u,
                          const double v) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
    w.data[i] = u.data[i] + v;
    }
    return w;
  }

  // Add a vector to a double
  friend Vector operator+(const double v,
                          const Vector<dim>& u) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
    w.data[i] = u.data[i] + v;
    }
    return w;
  }

  VectorType & operator-=(const VectorType &other) {
    for (int i=0;i<dim;++i) {
      data[i] -= other.data[i];
    }
    return *this;
  }

  // Subtract two vectors
  friend Vector operator-(const Vector<dim>& u,
                          const Vector<dim>& v) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i] = u.data[i] - v.data[i];
    }
    return w;
  }

  // Subtract a double from a vector
  friend Vector operator-(const Vector<dim>& u,
                          const double v) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i] = u.data[i] - v;
    }
    return w;
  }

  // Subtract a vector from a double
  friend Vector operator-(const double v,
                          const Vector<dim>& u) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i] = v - u.data[i];
    }
    return w;
  }

  VectorType & operator*=(const double v) {
    for (int i=0;i<dim;++i) {
      data[i]*=v;
    }
    return *this;
  }

  // Multilpy a double with a vector
  friend Vector operator*(const double v,
                          const Vector<dim>& u) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i]=v*u.data[i];
    }
    return w;
  }

  // Multiply a vector with a double
  friend Vector operator*(const Vector<dim>& u,
                          const double v) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i] = u.data[i]*v;
    }
    return w;
  }

  VectorType & operator/=(const double v) {
    for (int i=0;i<dim;++i) {
      data[i]/=v;
    }
    return *this;
  }

  // Divide a vector by a double
  friend Vector operator/(const Vector<dim>& u,
                          const double v) {
    Vector<dim> w;
    for (int i=0;i<dim;++i) {
      w.data[i] = u.data[i]/v;
    }
    return w;
  }

  VectorType & operator=(const VectorType &other) {
    for (int i=0;i<dim;++i) {
      data[i] = other.data[i];
    }
    return *this;
  }

  // Return dot product
  double dot(const VectorType &other) {
    double dot_product = 0.0;
    for (int i=0;i<dim;++i) {
      dot_product += data[i]*other.data[i];
    }
    return dot_product;
  }

  // Return vector norm
  double two_norm() {
    double two_norm = 0.0;
    for (int i=0;i<dim;++i) {
      two_norm += data[i]*data[i];
    }
    return sqrt(two_norm);
  }

  // Return as std::vector
  std::vector<double> as_vector() const {
    std::vector<double> v(dim);
    std::copy(data,data+dim,v.begin());
    return v;
  }

  friend std::ostream & operator<<(std::ostream & out,Vector<dim> & v) {
    for(int i = 0; i<dim; ++i) {
      out << " " << v.data[i];
    }
    return out;
  }

  int size() const { return dim; }

private:
  double data[dim];
};

#endif // VECTOR_HH
