/*
 * 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_XPR_H
#define TVMET_XPR_H

#include <tvmet/tvmet.h>
#include <tvmet/TypePromotion.h>

namespace tvmet {

/* forwards */
namespace meta {

template<std::size_t Rows1, std::size_t Cols1,
	 std::size_t Cols2,
	 std::size_t RowStride1, std::size_t ColStride1,
	 std::size_t RowStride2, std::size_t ColStride2,
	 std::size_t K>
class gemm;

template<std::size_t Rows, std::size_t Cols, std::size_t RowStride, std::size_t ColStride,
	 std::size_t VecStride, std::size_t J>
class gemv;

} // namespace meta

/**
 * \class XprVector Xpr.h "tvmet/Xpr.h"
 * \brief Represents the expression for vectors at any node in the parse tree.
 *
 * Specifically, XprVector is the class that wraps the expression, and the
 * expression itself is represented by the template parameter E. The
 * class XprVector is known as an anonymizing expression wrapper because
 * it can hold any subexpression of arbitrary complexity, allowing
 * clients to work with any expression by holding on to it via the
 * wrapper, without having to know the name of the type object that
 * actually implements the expression.
 * \note leave the Ctors non-explicit to allow implicit type conversation.
 *
 * \todo We have a const correctness problem!? It occoured on introducing the swap
 * function for vectors last. (The matrix hasn't a swap yet). Normally operator[]
 * for const and non const access should be enough. Unfortunally we go into
 * trouble for construction of vectors now. Affected is the Vector(value_type rhs) and
 * the CommaInitializer class. The Vector(value_tyoe) constructor problem is
 * easily to solve by writing a simple loop which doesn't call any fcnl_Assign.
 * The CommaInitializer problem seems to be more complicated, no solution yet.
 * The problem results from getting a reference non-const operator[] opposite
 * to the const operator[] which returns an object.
 * The solution (quick & dirty) is to use the operator() for non-const access.
 */
template<class E, std::size_t Sz>
class XprVector : public TvmetBase< XprVector<E, Sz> >
{
  XprVector();
  XprVector& operator=(const XprVector&);

public:
  typedef E						expr_type;
  typedef typename expr_type::value_type		value_type;

public:
  /** Constructor. */
#ifdef TVMET_OPTIMIZE_XPR_CTOR_ARGS_BY_VALUE
  explicit XprVector(expr_type expr) : m_expr(expr) { }
#else
  explicit XprVector(const expr_type& expr) : m_expr(expr) { }
#endif

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprVector(const XprVector<expr_type, Sz>& expr) : m_expr(expr.m_expr) { }
#endif

  /** const index operator for vectors. */
  value_type operator[](std::size_t i) const {
    //std::cout << "XprVector::operator[] const\n";
    return m_expr[i];
  }

  /** index operator for vectors. */
  value_type& operator()(std::size_t i) {
    //std::cout << "XprVector::operator[]\n";
    return m_expr[i];
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprVector<" << std::endl;
    m_expr.print_on(os, l);
    os << IndentLevel(l) << Sz << std::endl
       << IndentLevel(--l) << ">"
       << ((l != 0) ? "," : "") << std::endl;
  }

private:
  expr_type 						m_expr;
};

/**
 * \class XprMatrix Xpr.h "tvmet/Xpr.h"
 * \brief Represents the expression for vectors at any node in the parse tree.
 *
 * Specifically, XprMatrix is the class that wraps the expression, and the
 * expression itself is represented by the template parameter E. The
 * class XprMatrix is known as an anonymizing expression wrapper because
 * it can hold any subexpression of arbitrary complexity, allowing
 * clients to work with any expression by holding on to it via the
 * wrapper, without having to know the name of the type object that
 * actually implements the expression.
 * \note leave the Ctors non-explicit to allow implicit type conversation.
 */
template<class E, std::size_t Rows, std::size_t Cols>
class XprMatrix : public TvmetBase< XprMatrix<E, Rows, Cols> >
{
  XprMatrix();
  XprMatrix& operator=(const XprMatrix&);

public:
  typedef E						expr_type;
  typedef typename expr_type::value_type		value_type;

public:
  /** Constructor. */
#ifdef TVMET_OPTIMIZE_XPR_CTOR_ARGS_BY_VALUE
  explicit XprMatrix(expr_type expr) : m_expr(expr) { }
#else
  explicit XprMatrix(const expr_type& expr) : m_expr(expr) { }
#endif

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprMatrix(const XprMatrix<expr_type, Rows, Cols>& expr) : m_expr(expr.m_expr) { }
#endif

  /** index operator for arrays/matrizes */
  value_type operator()(std::size_t i, std::size_t j) const { return m_expr(i, j); }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprMatrix<" << std::endl;
    m_expr.print_on(os, l);
    os << IndentLevel(l) << Rows << ", " << Cols << std::endl
       << IndentLevel(--l) << ">"
       << ((l != 0) ? "," : "") << std::endl;
  }

private:
  expr_type 						m_expr;
};

#if 0 // unused
/**
 * \class XprConstReference Xpr.h "tvmet/Xpr.h"
 * \brief Wrapper class for avoiding temporary objects used by operator
 * expressions.
 * \internal UNUSED
 */
template<class E>
class XprConstReference : public TvmetBase< XprConstReference<E> >
{
  XprConstReference();
  XprConstReference& operator=(const XprConstReference&);

 public:
  typedef E						expr_type;
  typedef typename expr_type::value_type		value_type;

 public:
  /** Constructor. */
  explicit XprConstReference(const expr_type& e) : m_expr(e) { }

  /** Copy Constructor. Not explicit! */
  XprConstReference(const XprConstReference<expr_type>& rhs) : m_expr(rhs.m_expr) { }

  /** index operator, returns the expression by index. */
  value_type operator[](std::size_t i) const { return m_expr[i]; }

  /** index operator for arrays/matrices */
  value_type operator()(std::size_t i, std::size_t j) const { return m_expr(i, j); }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprConstReference<" << std::endl;
    m_expr.print_on(os, l);
    os << IndentLevel(l) << ">," << std::endl;
  }

private:
  const E& _tvmet_restrict 				m_expr;
};
#endif

/**
 * \class XprLiteral Xpr.h "tvmet/Xpr.h"
 * \brief Specify literals like scalars into the expression.
 */
template<class T>
class XprLiteral : public TvmetBase< XprLiteral<T> >
{
  XprLiteral();
  XprLiteral& operator=(const XprLiteral&);

public:
  typedef T						value_type;

public:
  /** Constructor by value for literals . */
  explicit XprLiteral(value_type rhs) : m_data(rhs) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprLiteral(const  XprLiteral<value_type>& rhs) : m_data(rhs.m_data) { }
#endif

  /** Index operator, gives the value inside. */
  value_type operator[](std::size_t) const { return m_data; }

  /** index operator for arrays/matrices */
  value_type operator()(std::size_t, std::size_t) const { return m_data; }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprLiteral<"
       << m_data
       << ">," << std::endl;
  }

private:
  const value_type 					m_data;
};

/**
 * \class XprNull Xpr.h "tvmet/Xpr.h"
 * \brief Null object design pattern
 */
class XprNull : public TvmetBase< XprNull >
{
  XprNull& operator=(const XprNull&);

public:
  explicit XprNull() { }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l) << "XprNull" << std::endl;
  }
};

#define TVMET_BINARY_OPERATOR(OP)                   			\
template <class T>                    					\
inline                     						\
T operator OP (const T& lhs, XprNull) { return lhs; }

TVMET_BINARY_OPERATOR(+)
TVMET_BINARY_OPERATOR(-)
TVMET_BINARY_OPERATOR(*)
TVMET_BINARY_OPERATOR(/)
#undef TVMET_BINARY_OPERATOR

/**
 * \class XprBinOp Xpr.h "tvmet/Xpr.h"
 * \brief Binary operators working on two sub expressions.
 *
 * On acessing using the index operator[] the binary operation will be
 * evaluated at compile time.
 */
template<class BinOp, class E1, class E2>
class XprBinOp  : public TvmetBase< XprBinOp<BinOp, E1, E2> >
{
  XprBinOp();
  XprBinOp& operator=(const XprBinOp&);

public:
  typedef typename
  PromoteTraits<typename E1::value_type,
		typename E2::value_type>::value_type	value_type;
  typedef E1						expr1_type;
  typedef E2						expr2_type;

public:
  /** Constructor for two expressions. */
#ifdef TVMET_OPTIMIZE_XPR_CTOR_ARGS_BY_VALUE
  explicit XprBinOp(expr1_type lhs, expr2_type rhs)
    : m_expr1(lhs), m_expr2(rhs) { }
#else
  explicit XprBinOp(const expr1_type& lhs, const expr2_type& rhs)
    : m_expr1(lhs), m_expr2(rhs) { }
#endif

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprBinOp(const XprBinOp<BinOp, expr1_type, expr2_type>& bop)
    : m_expr1(bop.m_expr1), m_expr2(bop.m_expr2) { }
#endif

  /** Index operator, evaluates the expression inside. */
  value_type operator[](std::size_t i) const {
    return BinOp::applyOn(m_expr1[i], m_expr2[i]);
  }

  /** index operator for arrays/matrices */
  value_type operator()(std::size_t i, std::size_t j) const {
    return BinOp::applyOn(m_expr1(i, j), m_expr2(i, j));
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprBinOp<" << std::endl;
    BinOp::print_on(os, l);
    m_expr1.print_on(os, l);
    m_expr2.print_on(os, l);
    os << IndentLevel(--l) << ">," << std::endl;
  }

private:
  expr1_type 						m_expr1;
  expr2_type 						m_expr2;
};

/**
 * \class XprUnOp Xpr.h "tvmet/Xpr.h"
 * \brief Unary operator working on one subexpression.
 *
 * Using the access operator[] the unary operation will be evaluated.
 */
template<class UnOp, class E>
class XprUnOp  : public TvmetBase< XprUnOp<UnOp, E> >
{
  XprUnOp();
  XprUnOp& operator=(const XprUnOp&);

public:
  typedef typename UnOp::value_type			value_type;
  typedef E						expr_type;

public:
  /** Constructor for an expressions. */
#ifdef TVMET_OPTIMIZE_XPR_CTOR_ARGS_BY_VALUE
  explicit XprUnOp(expr_type expr) : m_expr(expr) { }
#else
  explicit XprUnOp(const expr_type& expr) : m_expr(expr) { }
#endif

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprUnOp(const XprUnOp<UnOp, expr_type>& uop) : m_expr(uop.m_expr) { }
#endif

  /** Index operator, evaluates the expression inside. */
  value_type operator[](std::size_t i) const {
    return UnOp::applyOn(m_expr[i]);
  }

  /** index operator for arrays/matrices */
  value_type operator()(std::size_t i, std::size_t j) const {
    return UnOp::applyOn(m_expr(i, j));
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprUnOp<" << std::endl;
    UnOp::print_on(os, l);
    m_expr.print_on(os, l);
    os << IndentLevel(--l) << ">," << std::endl;
  }

private:
  expr_type 						m_expr;
};

/**
 * \class XprMMProduct Xpr.h "tvmet/Xpr.h"
 * \brief Expression for matrix-matrix product.
 * \note The Rows2 has to be  equal to Cols1.
 */
template<class T1, std::size_t Rows1, std::size_t Cols1,
	 class T2, std::size_t Cols2,
	 std::size_t RowStride1, std::size_t ColStride1,
	 std::size_t RowStride2, std::size_t ColStride2>
class XprMMProduct
  : public TvmetBase< XprMMProduct<T1, Rows1, Cols1, T2, Cols2, RowStride1, ColStride1, RowStride2, ColStride2> >
{
private:
  XprMMProduct();
  XprMMProduct& operator=(const XprMMProduct&);

public:
  typedef typename PromoteTraits<T1, T2>::value_type	value_type;

public:
  /** Constructor. */
  explicit XprMMProduct(const T1* lhs,const T2* rhs)
    : m_lhs(lhs), m_rhs(rhs) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprMMProduct(const XprMMProduct<T1, Rows1, Cols1,
	       T2, Cols2,
	       RowStride1, ColStride1,
	       RowStride2, ColStride2>& mmp)
    : m_lhs(mmp.m_lhs), m_rhs(mmp.m_rhs) { }
#endif

  /** index operator for arrays/matrices */
  value_type operator()(std::size_t i, std::size_t j) const {
    return meta::gemm<Rows1, Cols1, Cols2,
                      RowStride1, ColStride1,
                      RowStride2, ColStride2, 0>::product(m_lhs, m_rhs, i, j);
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprMMProduct<" << std::endl;
    os << IndentLevel(l) << typeid(T1).name() << ", " << Rows1 << ", " << Cols1 << ", ";
    os << typeid(T2).name() << ", " << Cols2 << ", ";
    os << RowStride1 << ", " << ColStride1 << ", ";
    os << RowStride2 << ", " << ColStride2 << std::endl;
    os << IndentLevel(--l) << ">," << std::endl;
  }

private:
  const T1* _tvmet_restrict 				m_lhs;
  const T2* _tvmet_restrict 				m_rhs;
};

/**
 * \class XprMVProduct Xpr.h "tvmet/Xpr.h"
 * \brief Expression for matrix-vector product
 */
template<class T1, std::size_t Rows, std::size_t Cols,
	 std::size_t RowStride, std::size_t ColStride,
	 class T2, std::size_t VecStride>
class XprMVProduct
  : public TvmetBase< XprMVProduct<T1, Rows, Cols, RowStride, ColStride, T2, VecStride> >
{
  XprMVProduct();
  XprMVProduct& operator=(const XprMVProduct&);

public:
  typedef typename PromoteTraits<T1, T2>::value_type	value_type;

public:
  /** Constructor. */
  explicit XprMVProduct(const T1* mtx, const T2* vec)
    : m_mtx(mtx), m_vec(vec) { }

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprMVProduct(const XprMVProduct<T1, Rows, Cols, RowStride, ColStride, T2, VecStride>& e)
    : m_mtx(e.m_mtx), m_vec(e.m_vec) { }
#endif

  /** index operator, returns the expression by index. This is the vector
      style since a matrix*vector gives a vector. */
  value_type operator[](std::size_t i) const {
    return meta::gemv<Rows, Cols, RowStride,
                      ColStride, VecStride, 0>::product(m_mtx, m_vec, i);
  }

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

private:
  const T1* _tvmet_restrict 				m_mtx;
  const T2* _tvmet_restrict 				m_vec;
};

/**
 * \class XprMatrixTranspose Xpr.h "tvmet/Xpr.h"
 * \brief Expression for transpose matrix
 */
template<class E>
class XprMatrixTranspose : public TvmetBase< XprMatrixTranspose<E> >
{
  XprMatrixTranspose();
  XprMatrixTranspose& operator=(const XprMatrixTranspose&);

public:
  typedef E						expr_type;
  typedef typename expr_type::value_type		value_type;

public:
  /** Constructor. */
#ifdef TVMET_OPTIMIZE_XPR_CTOR_ARGS_BY_VALUE
  explicit XprMatrixTranspose(expr_type expr)
    : m_expr(expr) { }
#else
  explicit XprMatrixTranspose(const expr_type& expr)
    : m_expr(expr) { }
#endif

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprMatrixTranspose(const XprMatrixTranspose<expr_type>& expr) : m_expr(expr.m_expr) { }
#endif

  /** index operator for arrays/matrices. This simple swap the index
      access for transpose. */
  value_type operator()(std::size_t i, std::size_t j) const { return m_expr(j, i); }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprMatrixTranspose<" << std::endl;
    m_expr.print_on(os, l);
    os << IndentLevel(--l) << ">," << std::endl;
  }

private:
  expr_type 						m_expr;
};

/**
 * \class XprEval Xpr.h "tvmet/Xpr.h"
 * \brief evaluate the expression
 *
 * Since we can't overwrite the ? operator we have to write a wrapper
 * for expression like return v1>v2 ? true : false
 */
template<class E1, class E2, class E3>
class XprEval : public TvmetBase< XprEval<E1, E2, E3> >
{
public:
  typedef E1 						expr1_type;
  typedef E2 						expr2_type;
  typedef E3 						expr3_type;

  typedef typename expr2_type::value_type 		value2_type;
  typedef typename expr3_type::value_type 		value3_type;

  typedef typename
  PromoteTraits<value2_type, value3_type>::value_type 	value_type;

private:
  XprEval();
  XprEval& operator=(const XprEval<expr1_type, expr2_type, expr3_type>&);

public:
  /** Constructor. */
#ifdef TVMET_OPTIMIZE_XPR_CTOR_ARGS_BY_VALUE
  explicit XprEval(expr1_type expr1, expr2_type expr2, expr3_type expr3)
    : m_expr1(expr1), m_expr2(expr2), m_expr3(expr3) { }
#else
  explicit XprEval(const expr1_type& expr1, const expr2_type& expr2, const expr3_type& expr3)
    : m_expr1(expr1), m_expr2(expr2), m_expr3(expr3) { }
#endif

  /** Copy Constructor. Not explicit! */
#ifdef TVMET_OPTIMIZE_XPR_MANUAL_CCTOR
  XprEval(const XprEval<expr1_type, expr2_type, expr3_type>& expr)
    : m_expr1(expr.m_expr1), m_expr2(expr.m_expr2), m_expr3(expr.m_expr3) { }
#endif

public: //access
  /** index operator for vectors. */
  value_type operator[](std::size_t i) const {
    return m_expr1[i] ? m_expr2[i] : m_expr3[i];
  }

  /** index operator for matrizes. */
  value_type operator()(std::size_t i, std::size_t j) const {
    return m_expr1(i, j) ? m_expr2(i, j) : m_expr3(i, j);
  }

public: // debugging Xpr parse tree
  void print_on(std::ostream& os, std::size_t l=0) const {
    os << IndentLevel(l++) << "XprEval<" << std::endl;
    m_expr1.print_on(os, l);
    m_expr2.print_on(os, l);
    m_expr3.print_on(os, l);
    os << IndentLevel(--l) << ">," << std::endl;
  }

private:
  expr1_type 						m_expr1;
  expr2_type 						m_expr2;
  expr3_type 						m_expr3;
};

} // namespace tvmet

#include <tvmet/XprOperators.h>
#include <tvmet/XprFunctions.h>

#endif // TVMET_XPR_H

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