/*
 * 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_OPERATORS_H
#define TVMET_MATRIX_OPERATORS_H

/* forwards */
#ifdef TVMET_HAVE_COMPLEX
template<class T> class std::complex;
#endif // TVMET_HAVE_COMPLEX

namespace tvmet {

/**
 * \fn operator<<(std::ostream& os, const Matrix<T, Rows, Cols>& rhs)
 * \brief Overload operator for i/o
 * \ingroup _binary_operator
 */
template<class T, std::size_t Rows, std::size_t Cols>
inline
std::ostream& operator<<(std::ostream& os, const Matrix<T, Rows, Cols>& rhs) {
  return rhs.print_on(os);
}

/***********************************************************************
 * global binary math, bitops and logical
 * documented by doc/operators_doc.cc
 ***********************************************************************/

/*
 * Matrix<T1, Rows1, Cols1> and Matrix<T2, Rows2, Cols2>
 * Row2 has to be equal to Col1
 */
#define TVMET_BINARY_OPERATOR(FUNCTIONAL, OP)                   	\
template<class T1, std::size_t Rows1, std::size_t Cols1,		\
	 class T2, std::size_t Cols2>					\
inline									\
XprMatrix<								\
  XprBinOp<								\
    FUNCTIONAL<T1, T2>,							\
    MatrixConstReference<T1, Rows1, Cols1>,				\
    MatrixConstReference<T2, Cols1, Cols2>				\
  >, 									\
  Rows1, Cols2 								\
>									\
operator OP (const Matrix<T1, Rows1, Cols1>& lhs, 			\
	     const Matrix<T2, Cols1, Cols2>& rhs) {			\
  typedef XprBinOp <							\
    FUNCTIONAL<T1, T2>,							\
    MatrixConstReference<T1, Rows1, Cols1>,				\
    MatrixConstReference<T2, Cols1, Cols2>				\
  >							expr_type;	\
  return XprMatrix<expr_type, Rows1, Cols2>(expr_type(lhs.cref(), rhs.cref())); \
}

TVMET_BINARY_OPERATOR(fcnl_Add, +)
TVMET_BINARY_OPERATOR(fcnl_Sub, -)
namespace element_wise { // overloaded by product(matrix, matrix)
TVMET_BINARY_OPERATOR(fcnl_Mul, *)
} // namespace element_wise
TVMET_BINARY_OPERATOR(fcnl_Div, /)
TVMET_BINARY_OPERATOR(fcnl_Mod, %)
TVMET_BINARY_OPERATOR(fcnl_BitwiseXor, ^)
TVMET_BINARY_OPERATOR(fcnl_BitwiseAnd, &)
TVMET_BINARY_OPERATOR(fcnl_BitwiseOr, |)
TVMET_BINARY_OPERATOR(fcnl_Shl, <<)
TVMET_BINARY_OPERATOR(fcnl_Shr, >>)
TVMET_BINARY_OPERATOR(fcnl_Greater, >)
TVMET_BINARY_OPERATOR(fcnl_Less, <)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||)
#undef TVMET_BINARY_OPERATOR

/*
 * Matrix<T, Rows, Cols> and POD's
 * POD's and Matrix<T, Rows, Cols>
 */
#define TVMET_BINARY_OPERATOR(FUNCTIONAL, OP, TP)               	\
template<class T, std::size_t Rows, std::size_t Cols>         		\
inline                 			      			      	\
XprMatrix<                  						\
  XprBinOp<          							\
    FUNCTIONAL<T, TP >,          					\
    MatrixConstReference<T, Rows, Cols>,       				\
    XprLiteral<TP >          						\
  >,          								\
  Rows, Cols 								\
>          								\
operator OP (const Matrix<T, Rows, Cols>& lhs, TP rhs) {		\
  typedef XprBinOp<          						\
    FUNCTIONAL<T, TP >,          					\
    MatrixConstReference<T, Rows, Cols>,       				\
    XprLiteral< TP >							\
  >							expr_type;	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(lhs.cref(), XprLiteral< TP >(rhs))); \
}          								\
          								\
template<class T, std::size_t Rows, std::size_t Cols>          		\
inline                 			      			      	\
XprMatrix<     								\
  XprBinOp<          							\
    FUNCTIONAL< TP, T>,          					\
    XprLiteral< TP >,         						\
    MatrixConstReference<T, Rows, Cols>        				\
  >,          								\
  Rows, Cols 								\
>          								\
operator OP (TP lhs, const Matrix<T, Rows, Cols>& rhs) {      		\
  typedef XprBinOp<         						\
    FUNCTIONAL< TP, T>,         					\
    XprLiteral< TP >,         						\
    MatrixConstReference<T, Rows, Cols>					\
  >							expr_type;	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(XprLiteral< TP >(lhs), rhs.cref())); \
}

TVMET_BINARY_OPERATOR(fcnl_Add, +, int)
TVMET_BINARY_OPERATOR(fcnl_Sub, -, int)
TVMET_BINARY_OPERATOR(fcnl_Mul, *, int)
TVMET_BINARY_OPERATOR(fcnl_Div, /, int)
TVMET_BINARY_OPERATOR(fcnl_Mod, %, int)
TVMET_BINARY_OPERATOR(fcnl_BitwiseXor, ^, int)
TVMET_BINARY_OPERATOR(fcnl_BitwiseAnd, &, int)
TVMET_BINARY_OPERATOR(fcnl_BitwiseOr, |, int)
TVMET_BINARY_OPERATOR(fcnl_Shl, <<, int)
TVMET_BINARY_OPERATOR(fcnl_Shr, >>, int)
TVMET_BINARY_OPERATOR(fcnl_Greater, >, int)
TVMET_BINARY_OPERATOR(fcnl_Less, <, int)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=, int)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=, int)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==, int)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=, int)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&, int)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||, int)

#ifdef TVMET_HAVE_LONG_LONG
TVMET_BINARY_OPERATOR(fcnl_Add, +, long long int)
TVMET_BINARY_OPERATOR(fcnl_Sub, -, long long int)
TVMET_BINARY_OPERATOR(fcnl_Mul, *, long long int)
TVMET_BINARY_OPERATOR(fcnl_Div, /, long long int)
TVMET_BINARY_OPERATOR(fcnl_Mod, %, long long int)
TVMET_BINARY_OPERATOR(fcnl_BitwiseXor, ^, long long int)
TVMET_BINARY_OPERATOR(fcnl_BitwiseAnd, &, long long int)
TVMET_BINARY_OPERATOR(fcnl_BitwiseOr, |, long long int)
TVMET_BINARY_OPERATOR(fcnl_Shl, <<, long long int)
TVMET_BINARY_OPERATOR(fcnl_Shr, >>, long long int)
TVMET_BINARY_OPERATOR(fcnl_Greater, >, long long int)
TVMET_BINARY_OPERATOR(fcnl_Less, <, long long int)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=, long long int)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=, long long int)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==, long long int)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=, long long int)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&, long long int)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||, long long int)
#endif // TVMET_HAVE_LONG_LONG

TVMET_BINARY_OPERATOR(fcnl_Add, +, float)
TVMET_BINARY_OPERATOR(fcnl_Sub, -, float)
TVMET_BINARY_OPERATOR(fcnl_Mul, *, float)
TVMET_BINARY_OPERATOR(fcnl_Div, /, float)
TVMET_BINARY_OPERATOR(fcnl_Greater, >, float)
TVMET_BINARY_OPERATOR(fcnl_Less, <, float)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=, float)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=, float)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==, float)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=, float)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&, float)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||, float)

TVMET_BINARY_OPERATOR(fcnl_Add, +, double)
TVMET_BINARY_OPERATOR(fcnl_Sub, -, double)
TVMET_BINARY_OPERATOR(fcnl_Mul, *, double)
TVMET_BINARY_OPERATOR(fcnl_Div, /, double)
TVMET_BINARY_OPERATOR(fcnl_Greater, >, double)
TVMET_BINARY_OPERATOR(fcnl_Less, <, double)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=, double)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=, double)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==, double)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=, double)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&, double)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||, double)

#ifdef TVMET_HAVE_LONG_DOUBLE
TVMET_BINARY_OPERATOR(fcnl_Add, +, long double)
TVMET_BINARY_OPERATOR(fcnl_Sub, -, long double)
TVMET_BINARY_OPERATOR(fcnl_Mul, *, long double)
TVMET_BINARY_OPERATOR(fcnl_Div, /, long double)
TVMET_BINARY_OPERATOR(fcnl_Greater, >, long double)
TVMET_BINARY_OPERATOR(fcnl_Less, <, long double)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=, long double)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=, long double)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==, long double)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=, long double)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&, long double)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||, long double)
#endif // TVMET_HAVE_LONG_DOUBLE

#undef TVMET_BINARY_OPERATOR

#ifdef TVMET_HAVE_COMPLEX
/*
 * Matrix<T, Rows, Cols> and complex<T>
 * complex<T> and Matrix<T, Rows, Cols>
 * \todo type promotion
 */
#define TVMET_BINARY_OPERATOR(FUNCTIONAL, OP)		               	\
template<class T, std::size_t Rows, std::size_t Cols>         		\
inline                 			      			      	\
XprMatrix<                  						\
  XprBinOp<          							\
    FUNCTIONAL< std::complex<T>, std::complex<T> >,			\
    MatrixConstReference< std::complex<T>, Rows, Cols>,			\
    XprLiteral<std::complex<T> > 					\
  >,          								\
  Rows, Cols 								\
>          								\
operator OP (const Matrix< std::complex<T>, Rows, Cols>& lhs, const std::complex<T>& rhs) { \
  typedef XprBinOp<          						\
    FUNCTIONAL< std::complex<T>, std::complex<T> >,			\
    MatrixConstReference< std::complex<T>, Rows, Cols>,			\
    XprLiteral< std::complex<T> >					\
  >							expr_type;	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(lhs.cref(), XprLiteral< std::complex<T> >(rhs))); \
}          								\
          								\
template<class T, std::size_t Rows, std::size_t Cols>          		\
inline                 			      			      	\
XprMatrix<     								\
  XprBinOp<          							\
    FUNCTIONAL< std::complex<T>, std::complex<T> >,			\
    XprLiteral< std::complex<T> >,  					\
    MatrixConstReference< std::complex<T>, Rows, Cols>			\
  >,          								\
  Rows, Cols 								\
>          								\
operator OP (const std::complex<T>& lhs, const Matrix< std::complex<T>, Rows, Cols>& rhs) { \
  typedef XprBinOp<         						\
    FUNCTIONAL< std::complex<T>, std::complex<T> >,			\
    XprLiteral< std::complex<T> >, 					\
    MatrixConstReference<T, Rows, Cols>					\
  >							expr_type;	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(XprLiteral< std::complex<T> >(lhs), rhs.cref())); \
}

TVMET_BINARY_OPERATOR(fcnl_Add, +)
TVMET_BINARY_OPERATOR(fcnl_Sub, -)
TVMET_BINARY_OPERATOR(fcnl_Mul, *)
TVMET_BINARY_OPERATOR(fcnl_Div, /)
TVMET_BINARY_OPERATOR(fcnl_Mod, %)
TVMET_BINARY_OPERATOR(fcnl_BitwiseXor, ^)
TVMET_BINARY_OPERATOR(fcnl_BitwiseAnd, &)
TVMET_BINARY_OPERATOR(fcnl_BitwiseOr, |)
TVMET_BINARY_OPERATOR(fcnl_Shl, <<)
TVMET_BINARY_OPERATOR(fcnl_Shr, >>)
TVMET_BINARY_OPERATOR(fcnl_Greater, >)
TVMET_BINARY_OPERATOR(fcnl_Less, <)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||)
#undef TVMET_BINARY_OPERATOR

#endif // TVMET_HAVE_COMPLEX

/*
 * XprMatrix<E> and Matrix<T, Rows, Cols>
 * Matrix<T, Rows, Cols> and XprMatrix<E>
 */
#define TVMET_BINARY_OPERATOR(FUNCTIONAL, OP)                   	\
template<class E,                   					\
	 class T, std::size_t Rows, std::size_t Cols>         		\
inline                                 					\
XprMatrix<                                     				\
  XprBinOp<                     	      				\
    FUNCTIONAL<typename E::value_type, T>,				\
    XprMatrix<E, Rows, Cols>,                  				\
    MatrixConstReference<T, Rows, Cols>                			\
  >,                     						\
  Rows, Cols 								\
>                      							\
operator OP (XprMatrix<E, Rows, Cols> lhs, const Matrix<T, Rows, Cols>& rhs) { \
  typedef XprBinOp<                     	      			\
    FUNCTIONAL<typename E::value_type, T>,				\
    XprMatrix<E, Rows, Cols>,						\
    MatrixConstReference<T, Rows, Cols>					\
  > 							 expr_type; 	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(lhs, rhs.cref()));	\
}                    	      						\
                    	      						\
template<class T, std::size_t Rows, std::size_t Cols, 			\
	 class E>			                   		\
inline                 			      			      	\
XprMatrix<                    	      					\
  XprBinOp<                     	      				\
    FUNCTIONAL<typename E::value_type, T>,				\
    MatrixConstReference<T, Rows, Cols>,               			\
    XprMatrix<E, Rows, Cols>                   				\
  >,                     	      					\
  Rows, Cols 								\
>                      	      						\
operator OP (const Matrix<T, Rows, Cols>& lhs, XprMatrix<E, Rows, Cols> rhs) { \
  typedef XprBinOp<                     	      			\
    FUNCTIONAL<T, typename E::value_type>,				\
    MatrixConstReference<T, Rows, Cols>,               			\
    XprMatrix<E, Rows, Cols>						\
  >	 						 expr_type; 	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(lhs.cref(), rhs));	\
}

TVMET_BINARY_OPERATOR(fcnl_Add, +)
TVMET_BINARY_OPERATOR(fcnl_Sub, -)
namespace element_wise { // overloaded by product(matrix, matrix)
TVMET_BINARY_OPERATOR(fcnl_Mul, *)
} // namespace element_wise
TVMET_BINARY_OPERATOR(fcnl_Div, /)
TVMET_BINARY_OPERATOR(fcnl_Mod, %)
TVMET_BINARY_OPERATOR(fcnl_BitwiseXor, ^)
TVMET_BINARY_OPERATOR(fcnl_BitwiseAnd, &)
TVMET_BINARY_OPERATOR(fcnl_BitwiseOr, |)
TVMET_BINARY_OPERATOR(fcnl_Shl, <<)
TVMET_BINARY_OPERATOR(fcnl_Shr, >>)
TVMET_BINARY_OPERATOR(fcnl_Greater, >)
TVMET_BINARY_OPERATOR(fcnl_Less, <)
TVMET_BINARY_OPERATOR(fcnl_GreaterOrEqual, >=)
TVMET_BINARY_OPERATOR(fcnl_LessOrEqual, <=)
TVMET_BINARY_OPERATOR(fcnl_Equal, ==)
TVMET_BINARY_OPERATOR(fcnl_NotEqual, !=)
TVMET_BINARY_OPERATOR(fcnl_LogicalAnd, &&)
TVMET_BINARY_OPERATOR(fcnl_LogicalOr, ||)
#undef TVMET_BINARY_OPERATOR

/*
 * XprMatrix<E, Rows, Cols> and Vector<T, Cols>
 * XXX return value equal to MatrixFunctions.h: product(...)
 */
#if 0
template<class E, std::size_t Rows, std::size_t Cols, class T>
inline
XprVector<
  XprMVProduct<typename E::value_type, Rows, Cols, Cols, 1, T, 1>,
  Rows
>
operator*(XprMatrix<E, Rows, Cols> lhs, const Vector<T, Cols>& rhs) {
  return product(lhs, rhs);
}
#endif

/**
 * \fn operator*(const Matrix<T1, Rows1, Cols1>& lhs, const Matrix<T2, Cols1, Cols2>& rhs)
 * \brief Multiply two Matrices.
 * \ingroup _binaray_operator
 * \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>
inline
XprMatrix<
  XprMMProduct<
    T1, Rows1, Cols1,
    T2, Cols2, Cols1,
    1, Cols2, 1
  >,
  Rows1, Cols2
>
operator*(const Matrix<T1, Rows1, Cols1>& lhs,
	  const Matrix<T2, Cols1, Cols2>& rhs) {
  return product(lhs, rhs);
}

/**
 * \fn operator*(const Matrix<T1, Rows, Cols>& matrix, const Vector<T2, Cols>& vector)
 * \brief Multiply a Matrix with a Vector.
 * \ingroup _binaray_operator
 * \note The length of the Vector has to be equal to the number of Columns.
 */
template<class T1, std::size_t Rows, std::size_t Cols, class T2>
inline
XprVector<
  XprMVProduct<T1, Rows, Cols, Cols, 1, T2, 1>,
  Rows
>
operator*(const Matrix<T1, Rows, Cols>& matrix,
	  const Vector<T2, Cols>& vector) {
  return product(matrix, vector);
}

/***********************************************************************
 * global unary operators
 * documented by doc/operators_doc.cc
 ***********************************************************************/

/*
 * Operators on Matrix<T, Rows, Cols>
 */
#define TVMET_UNARY_OPERATOR(FUNCTIONAL, OP)                   		\
template <class T, std::size_t Rows, std::size_t Cols>                  \
inline                                                        		\
XprMatrix<                                                       	\
  XprUnOp<                                                        	\
    FUNCTIONAL<T>,                                                      \
    MatrixConstReference<T, Rows, Cols>                                	\
  >,                                                        		\
  Rows, Cols 								\
>                                                        		\
operator OP (const Matrix<T, Rows, Cols>& rhs) {                        \
  typedef XprUnOp<                                                      \
    FUNCTIONAL<T>,                                                      \
    MatrixConstReference<T, Rows, Cols>                                	\
  >  							 expr_type;	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(rhs.cref()));	\
}

TVMET_UNARY_OPERATOR(fcnl_LogicalNot, !)
TVMET_UNARY_OPERATOR(fcnl_BitwiseNot, ~)
TVMET_UNARY_OPERATOR(fcnl_Negate, -)
#undef TVMET_UNARY_OPERATOR

} // namespace tvmet

#endif // TVMET_MATRIX_OPERATORS_H

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