/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This file contains the declaration and the implementation of a      //
// matrix class.                                                       //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
#ifndef __CMATRIX_HPP__
#define __CMATRIX_HPP__

#include <cstring> // memcpy


/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This class is a simple matrix class.                                //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
template <typename T, int M, int N>
class CMatrix
{
public:
  CMatrix();
  CMatrix(const CMatrix& m);

  inline T& operator()(int i, int j);
  inline T  operator()(int i, int j) const;

  inline CMatrix<T,M,N>& operator=(const CMatrix<T,M,N>& m);
  inline CMatrix<T,M,N>  operator+(const CMatrix<T,M,N>& m) const;
  inline CMatrix<T,M,N>  operator-(const CMatrix<T,M,N>& m) const;
  template <int O>
  inline CMatrix<T,M,O>  operator*(const CMatrix<T,N,O>& m) const;

  inline operator       T*();
  inline operator const T*() const;

protected:
  inline void Copy(const CMatrix<T,M,N>& m);

  T _values[M][N];
};




// Default constructor
template <typename T, int M, int N>
inline CMatrix<T,M,N>::CMatrix()
{
  for (int i = 0; i < M; ++i)
    for (int j = 0; j < N; ++j)
      (*this)(i,j) = 0;
}

// Copy constructor
template <typename T, int M, int N>
inline CMatrix<T,M,N>::CMatrix(const CMatrix& m)
{
  Copy(m);
}

// Access operator
template <typename T, int M, int N>
inline T& CMatrix<T,M,N>::operator()(int i, int j)
{
  return _values[i][j];
}

// Const access operator
template <typename T, int M, int N>
inline T  CMatrix<T,M,N>::operator()(int i, int j) const
{
  return _values[i][j];
}

// Assignment operator
template <typename T, int M, int N>
inline CMatrix<T,M,N>& CMatrix<T,M,N>::operator=(const CMatrix<T,M,N>& m)
{
  if (this != &m)
    Copy(m);

  return (*this);
};

// Matrix addition
template <typename T, int M, int N>
inline CMatrix<T,M,N> CMatrix<T,M,N>::operator+(const CMatrix<T,M,N>& m) const
{
  CMatrix<T,M,N> returnMatrix;

  for (int i = 0; i < M; ++i)
    for (int j = 0; j < N; ++j)
      returnMatrix(i,j) = ((*this)(i,j)) + m(i,j);

  return returnMatrix;
};

// Matrix subtraction
template <typename T, int M, int N>
inline CMatrix<T,M,N> CMatrix<T,M,N>::operator-(const CMatrix<T,M,N>& m) const
{
  CMatrix<T,M,N> returnMatrix;

  for (int i = 0; i < M; ++i)
    for (int j = 0; j < N; ++j)
      returnMatrix(i,j) = ((*this)(i,j)) - m(i,j);

  return returnMatrix;
};

// Matrix multiplication
template <typename T, int M, int N>
template <int O>
inline CMatrix<T,M,O> CMatrix<T,M,N>::operator*(const CMatrix<T,N,O>& m) const
{
  CMatrix<T,M,O> returnMatrix;

  for (int i = 0; i < M; ++i)
    for (int k = 0; k < O; ++k)
      {
        T sum = 0;

        for (int j = 0; j < N; ++j)
          sum += ((*this)(i,j)) * m(j,k);

        returnMatrix(i,k) = sum;
      }

  return returnMatrix;
};

// Direct access to constant matrix data
template <typename T, int M, int N>
inline CMatrix<T,M,N>::operator T*()
{
  return &_values[0][0];
}

// Direct access to matrix data
template <typename T, int M, int N>
inline CMatrix<T,M,N>::operator const T*() const
{
  return &_values[0][0];
}

// Matrix copy
template <typename T, int M, int N>
inline void CMatrix<T,M,N>::Copy(const CMatrix<T,M,N>& m)
{
  memcpy(_values, m._values, M*N*sizeof(T));
}


#endif // __CMATRIX_HPP__
