#ifndef TINY_VECTOR_HPP
#define TINY_VECTOR_HPP

#include <cassert>
#include <iostream>

template <size_t N, typename T=double>
class TinyVector
{
private:
  T m_values[N];
  static_assert((N>0),"TinyVector size must be strictly positive");

public:
  KOKKOS_INLINE_FUNCTION
  T operator,(const TinyVector& v)
  {
    T t = m_values[0]*v.m_values[0];
    for (size_t i=1; i<N; ++i) {
      t += m_values[i]*v.m_values[i];
    }
    return std::move(t);
  }

  KOKKOS_INLINE_FUNCTION
  friend TinyVector operator*(const T& t, const TinyVector& v)
  {
    TinyVector tv;
    for (size_t i=0; i<N; ++i) {
      tv.m_values[i] = t * v.m_values[i];
    }
    return std::move(tv);
  }

  KOKKOS_INLINE_FUNCTION
  friend std::ostream& operator<<(std::ostream& os, const TinyVector& v)
  {
    os << '(' << v.m_values[0];
    for (size_t i=1; i<N; ++i) {
      os << ',' << v.m_values[i];
    }
    os << ')';
    return os;
  }
  
  KOKKOS_INLINE_FUNCTION
  TinyVector operator+(const TinyVector& v) const
  {
    TinyVector sum;
    for (size_t i=0; i<N; ++i) {
      sum[i] = m_values[i]+v.m_values[i];
    }
    return std::move(sum);
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector operator-(const TinyVector& v) const
  {
    TinyVector difference;
    for (size_t i=0; i<N; ++i) {
      difference[i] = m_values[i]-v.m_values[i];
    }
    return std::move(difference);
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector& operator+=(const TinyVector& v)
  {
    for (size_t i=0; i<N; ++i) {
      m_values[i] += v.m_values[i];
    }
    return *this;
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector& operator-=(const TinyVector& v)
  {
    for (size_t i=0; i<N; ++i) {
      m_values[i] -= v.m_values[i];
    }
    return *this;
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector& operator=(const T& t)
  {
    for (size_t i=0; i<N; ++i) {
      m_values[i] = t;
    }
    return *this;
  }

  KOKKOS_INLINE_FUNCTION
  T& operator[](const size_t& i)
  {
    assert(i<N);
    return m_values[i];
  }

  KOKKOS_INLINE_FUNCTION
  const T& operator[](const size_t& i) const
  {
    assert(i<N);
    return m_values[i];
  }

  KOKKOS_INLINE_FUNCTION
  const TinyVector& operator=(const TinyVector& v)
  {
    for (size_t i=0; i<N; ++i) {
      m_values[i] = v.m_values[i];
    }
    return *this;
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector& operator=(TinyVector&& v) = default;

  KOKKOS_INLINE_FUNCTION
  TinyVector()
  {
    ;
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector(const T& t)
  {
    for (size_t i=0; i<N; ++i) {
      m_values[i] = t;
    }
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector(const TinyVector& v)
  {
    for (size_t i=0; i<N; ++i) {
      m_values[i] = v.m_values[i];
    }
  }

  KOKKOS_INLINE_FUNCTION
  TinyVector(TinyVector&& v) = default;
  
  KOKKOS_INLINE_FUNCTION
  ~TinyVector()
  {
    ;
  }
};

#endif // TINYVECTOR_HPP