/*
 * Tiny Vector Matrix Library
 * Dense Vector Matrix Libary of Tiny size using Expression Templates
 *
 * Copyright (C) 2001, 2002 Olaf Petzold <opetzold@wit.regiocom.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * "$Id:"
 */

#ifndef TVMET_MATRIX_H
#define TVMET_MATRIX_H

#include <iterator>					// reverse_iterator

#include <tvmet/tvmet.h>
#include <tvmet/Xpr.h>
#include <tvmet/TypePromotion.h>
#include <tvmet/NumericTraits.h>
#include <tvmet/CommaInitializer.h>
#include <tvmet/CompileTimeError.h>
#include <tvmet/RunTimeError.h>
#include <tvmet/meta/Matrix.h>

namespace tvmet {

/* forwards */
template<class T, std::size_t Rows, std::size_t Cols> class Matrix;
template<class T, std::size_t Rows, std::size_t Cols,
	 std::size_t RowStride, std::size_t ColStride> class MatrixConstReference;

/**
 * \class MatrixConstReference Matrix.h "tvmet/Matrix.h"
 * \brief value iterator for ET
 */
template<class T,
	 std::size_t Rows, std::size_t Cols,
	 std::size_t RowStride=Cols, std::size_t ColStride=1>
class MatrixConstReference
  : public TvmetBase < MatrixConstReference<T, Rows, Cols, RowStride, ColStride> >
{
public: // types
  typedef T						value_type;
  typedef MatrixConstReference<T, Rows, Cols,
		          RowStride, ColStride>		this_type;

private:
  MatrixConstReference();
  MatrixConstReference& operator=(const this_type&);

public:
  /** Constructor. */
  explicit MatrixConstReference(const Matrix<T, Rows, Cols>& rhs)
    : m_data(rhs.data()) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  MatrixConstReference(const this_type& rhs) : m_data(rhs.m_data) { }
#endif

public: // access operators
  /** access by index. */
  value_type operator()(std::size_t i, std::size_t j) const {
    RT_CONDITION((i < Rows) && (j < Cols), "[tvmet] Matrix Bounce Violation")
    return m_data[i * RowStride + j * ColStride];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l)
       << "MatrixConstReference<" << typeid(T).name() << ", "
       << Rows << ", " << Cols << ", " << RowStride << ", " << ColStride << ">,"
       << std::endl;
  }

private:
  const value_type* _tvmet_restrict 			m_data;
};

/**
 * \class MatrixReference Matrix.h "tvmet/Matrix.h"
 * \brief value iterator for ET
 */
template<class T,
	 std::size_t Rows, std::size_t Cols,
	 std::size_t RowStride=Cols, std::size_t ColStride=1>
class MatrixReference
  : public TvmetBase < MatrixReference<T, Rows, Cols, RowStride, ColStride> >
{
public: // types
  typedef T						value_type;
  typedef MatrixReference<T, Rows, Cols,
		          RowStride, ColStride>		this_type;

private:
  MatrixReference();
  MatrixReference& operator=(const this_type&);

public:
  /** Constructor. */
  explicit MatrixReference(const Matrix<T, Rows, Cols>& rhs)
    : m_data(rhs.data()) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  MatrixReference(const this_type& rhs) : m_data(rhs.m_data) { }
#endif

public: // access operators
  /** access by index. */
  value_type& _tvmet_restrict operator()(std::size_t i, std::size_t j) {
    RT_CONDITION((i < Rows) && (j < Cols), "[tvmet] Matrix Bounce Violation")
    return m_data[i * RowStride + j * ColStride];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l)
       << "MatrixReference<" << typeid(T).name() << ", "
       << Rows << ", " << Cols << ", " << RowStride << ", " << ColStride << ">,"
       << std::endl;
  }

private:
  value_type* _tvmet_restrict 				m_data;
};

/**
 * \class MatrixRowVectorReference Matrix.h "tvmet/Matrix.h"
 * \brief Reference to matrix used for access on the row vector.
 */
template<class T, std::size_t Rows, std::size_t Cols>
class MatrixRowVectorReference
  : public TvmetBase< MatrixRowVectorReference<T, Rows, Cols> >
{
  MatrixRowVectorReference();
  MatrixRowVectorReference& operator=(const MatrixRowVectorReference<T, Rows, Cols>&);

public:
  typedef T						value_type;

public:
  /** Constructor. */
  explicit MatrixRowVectorReference(value_type* m, std::size_t no)
    : m_matrix(m), m_row(no) { }

  value_type& _tvmet_restrict operator[](std::size_t j) {
    return m_matrix[m_row*Cols + j];
  }

  value_type operator[](std::size_t j) const {
    return m_matrix[m_row*Cols + j];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++)
       << "MatrixRowVectorReference<" << typeid(T).name() << ", "
       << Rows << ", " << Cols << ">" << std::endl;
  }

private:
  value_type* _tvmet_restrict 				m_matrix;
  std::size_t 						m_row;
};

/**
 * \class MatrixColVectorReference Matrix.h "tvmet/Matrix.h"
 * \brief Reference to matrix used for access on the column vector.
 */
template<class T, std::size_t Rows, std::size_t Cols>
class MatrixColVectorReference
  : public TvmetBase< MatrixColVectorReference<T, Rows, Cols> >
{
  MatrixColVectorReference();
  MatrixColVectorReference& operator=(const MatrixColVectorReference<T, Rows, Cols>&);

public:
  typedef T						value_type;

public:
  /** Constructor. */
  explicit MatrixColVectorReference(value_type* m, std::size_t no)
    : m_matrix(m), m_col(no) { }

  value_type& _tvmet_restrict operator[](std::size_t i) {
    return m_matrix[i*Cols + m_col];
  }

  value_type operator[](std::size_t i) const {
    return m_matrix[i*Cols + m_col];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++)
       << "MatrixColVectorReference<" << typeid(T).name() << ", "
       << Rows << ", " << Cols << ">" << std::endl;
  }

private:
  value_type* _tvmet_restrict 				m_matrix;
  std::size_t 						m_col;
};

/**
 * \class Matrix Matrix.h "tvmet/Matrix.h"
 * \brief A tiny matrix class.
 *
 * The array syntax A[j][j] isn't supported here. The reason is that
 * operator[] always takes exactly one parameter, but operator() can
 * take any number of parameters (in the case of a rectangular matrix,
 * two paramters are needed). Therefore the cleanest way to do it is
 * with operator() rather than with operator[]. \see C++ FAQ Lite 13.8
 */
template<class T, std::size_t Rows, std::size_t Cols>
class Matrix
{
public:
  /** Data type of the tvmet::Matrix. */
  typedef T						value_type;

  /** Reference type of the tvmet::Matrix data elements. */
  typedef T&     					reference;

  /** const reference type of the tvmet::Matrix data elements. */
  typedef const T&     					const_reference;

  /** STL iterator interface. */
  typedef T*     					iterator;

  /** STL const_iterator interface. */
  typedef const T*     					const_iterator;

  /** STL reverse iterator interface. */
  typedef std::reverse_iterator<iterator> 		reverse_iterator;

  /** STL const reverse iterator interface. */
  typedef std::reverse_iterator<const_iterator> 	const_reverse_iterator;

  /** This type of tvmet::Matrix, used to simplify member wrote. */
  typedef Matrix<T, Rows, Cols>				this_type;

public: // STL  interface
  /** STL iterator interface. */
  iterator begin() { return m_data; }

  /** STL iterator interface. */
  iterator end() { return m_data + Rows*Cols; }

  /** STL const_iterator interface. */
  const_iterator begin() const { return m_data; }

  /** STL const_iterator interface. */
  const_iterator end() const { return m_data + Rows*Cols; }

  /** STL reverse iterator interface reverse begin. */
  reverse_iterator rbegin() { return reverse_iterator( end() ); }

  /** STL const reverse iterator interface reverse begin. */
  const_reverse_iterator rbegin() const {
    return const_reverse_iterator( end() );
  }

  /** STL reverse iterator interface reverse end. */
  reverse_iterator rend() { return reverse_iterator( begin() ); }

  /** STL const reverse iterator interface reverse end. */
  const_reverse_iterator rend() const {
    return const_reverse_iterator( begin() );
  }

  /** The size of the matrix. */
  static std::size_t size() { return Rows*Cols; }

  /** STL vector max_size() - returns allways rows()*cols(). */
  static std::size_t max_size() { return Rows*Cols; }

  /** STL vector empty() - returns allways false. */
  static bool empty() { return false; }

public:
  /** The number of rows of matrix. */
  std::size_t rows() const { return Rows; }

  /** The number of columns of matrix. */
  std::size_t cols() const { return Cols; }

public:
  /** Default Destructor */
  ~Matrix() { }

  /** Default Constructor. The allocated memory region isn't cleared. If you want
   a clean use the constructor argument zero. */
  explicit Matrix() { }

  /** Copy Constructor, not explicit! */
  Matrix(const this_type& rhs) {
    *this = XprMatrix<typename this_type::ConstReference, Rows, Cols>(rhs.cref());
  }

  /** Constructor with STL iterator interface. */
  template<class InputIterator>
  explicit Matrix(InputIterator first, InputIterator last) {
    // XXX CT_CONDITION(std::distance(first, last) <= Rows*Cols, Iterator_distance_is_too_long)
    for(std::size_t i = 0 ; first != last; ++first, ++i)
      m_data[i] = *first;
  }

  /** Constructor with STL iterator interface. */
  template<class InputIterator>
  explicit Matrix(InputIterator first, std::size_t sz) {
    // XXX CT_CONDITION(sz <= Rows*Cols, Iterator_distance_is_too_long)
    for(std::size_t i = 0; i < Rows*Cols; ++first, ++i)
      m_data[i] = *first;
  }

  /** Construct the matrix by value. */
  explicit Matrix(value_type rhs) {
    typedef XprLiteral<value_type> expr_type;
    *this = XprMatrix<expr_type, Rows, Cols>(expr_type(rhs));
  }

  /** Construct a matrix by expression. */
  template<class E>
  explicit Matrix(XprMatrix<E, Rows, Cols> expr) {
    assign(expr, fcnl_Assign<value_type, typename E::value_type>());
  }

  /** Assign a value_type on array, this can be used for a single value
      or a comma separeted list of values. */
  CommaInitializer<this_type, Rows*Cols> operator=(value_type rhs) {
    return CommaInitializer<this_type, Rows*Cols>(*this, rhs);
  }

public: // access operators
  value_type* _tvmet_restrict data() { return m_data; }
  const value_type* _tvmet_restrict data() const { return m_data; }

public: // index access operators
  value_type& _tvmet_restrict operator()(std::size_t i, std::size_t j) {
    RT_CONDITION((i < Rows) && (j < Cols), "[tvmet] Matrix Bounce Violation")
    return m_data[i * Cols + j];
  }

  value_type operator()(std::size_t i, std::size_t j) const {
    RT_CONDITION((i < Rows) && (j < Cols), "[tvmet] Matrix Bounce Violation")
    return m_data[i * Cols + j];
  }

public: // ET interface
  typedef MatrixConstReference<T, Rows, Cols>   	ConstReference;
  typedef MatrixReference<T, Rows, Cols>   		Reference;

  /** Return a const Reference of the internal data */
  ConstReference cref() const { return ConstReference(*this); }

  /** Return a Reference of the internal data */
  Reference ref() { return Reference(*this); }

  /** Return the vector as const expression. */
  XprMatrix<ConstReference, Rows, Cols> as_expr() const {
    return XprMatrix<ConstReference, Rows, Cols>(this->cref());
  }

  /** Return the vector as expression. */
  XprMatrix<Reference, Rows, Cols> as_expr() {
    return XprMatrix<Reference, Rows, Cols>(this->ref());
  }

private: // ET helper
  template<class E, class Op>
  void assign(E expr, Op op) {
    meta::Matrix<Rows, Cols>::assign(*this, expr, op);
  }

private:
  template<class Obj, std::size_t LEN> friend class CommaInitializer;

  /** This is a helper for assigning a comma separated initializer
      list. It's equal to this_type& operator=(value_type) which does
      replace it. */
  this_type& assignValue(value_type rhs) {
    typedef XprLiteral<value_type> 			expr_type;
    *this = XprMatrix<expr_type, Rows, Cols>(expr_type(rhs));
    return *this;
  }

public: // math operators with scalars
  this_type& operator+=(value_type);
  this_type& operator-=(value_type);
  this_type& operator*=(value_type);
  this_type& operator/=(value_type);

  this_type& operator%=(value_type);
  this_type& operator^=(value_type);
  this_type& operator&=(value_type);
  this_type& operator|=(value_type);
  this_type& operator<<=(std::size_t);
  this_type& operator>>=(std::size_t);

public: // math operators with matrixs
  template<class T2> this_type& operator=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator+=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator-=(const Matrix<T2, Rows, Cols>&);
#ifdef KICK_MATH_SENSE // not inside regression tests
  /**
     \todo operator*=(const Matrix<T2, Rows, Cols>&) should do matrix multiplication. This
     could get into trouble if KICK_MATH_SENSE is defined. Therfore move it into a
     namespace oder sub struct.
  */
  template<class T2> this_type& operator*=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator/=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator%=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator^=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator&=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator|=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator<<=(const Matrix<T2, Rows, Cols>&);
  template<class T2> this_type& operator>>=(const Matrix<T2, Rows, Cols>&);
#endif

public: // math operators with expressions
  template <class E> this_type& operator=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator+=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator-=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator*=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator/=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator%=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator^=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator&=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator|=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator<<=(XprMatrix<E, Rows, Cols>);
  template <class E> this_type& operator>>=(XprMatrix<E, Rows, Cols>);

public: // io
  std::ostream& print_on(std::ostream& os) const;

private:
  value_type 						m_data[Rows * Cols];
};

} // namespace tvmet

#include <tvmet/MatrixImpl.h>
#include <tvmet/MatrixFunctions.h>
#include <tvmet/MatrixOperators.h>
#include <tvmet/MatrixEval.h>

#endif // TVMET_MATRIX_H

// Local Variables:
// mode:C++
// End:
