/*
 * 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_FUNCTION_H
#define TVMET_MATRIX_FUNCTIONS_H

#include <tvmet/Extremum.h>
#include <tvmet/meta/Gemm.h>
#include <tvmet/meta/Gemv.h>

namespace tvmet {

/* forwards */
template<class T, std::size_t Sz> class Vector;

/**
 * \fn product(const Matrix<T1, Rows1, Cols1>& lhs, const Matrix<T2, Cols1, Cols2>& rhs)
 * \brief Function for the matrix-matrix-product.
 * \ingroup _binaray_function
 * \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
>
product(const Matrix<T1, Rows1, Cols1>& lhs, const Matrix<T2, Cols1, Cols2>& rhs) {
  typedef XprMMProduct<
    T1, Rows1, Cols1,
    T2, Cols2, Cols1,
    1, Cols2, 1
  >							expr_type;
  return XprMatrix<expr_type, Rows1, Cols2>(expr_type(lhs.data(), rhs.data()));
}

/**
 * \fn product(const Matrix<T1, Rows, Cols>& lhs,  const Vector<T2, Cols>& v)
 * \brief Function for the matrix-vector-product
 * \ingroup _binary_function
 */
template<class T1, std::size_t Rows, std::size_t Cols, class T2>
inline
XprVector<
  XprMVProduct<T1, Rows, Cols, Cols, 1, T2, 1>,
  Rows
>
product(const Matrix<T1, Rows, Cols>& m, const Vector<T2, Cols>& v) {
  typedef XprMVProduct<T1, Rows, Cols, Cols, 1, T2, 1> 	expr_type;
  return XprVector<expr_type, Rows>(expr_type(m.data(), v.data()));
}

#if 0 // doesn't work without temporaries yet
/*
 * \fn product(const XprMatrix<E, Rows, Cols>& lhs, const Vector<T, Cols>& v)
 * \brief Compute the product of an XprMatrix with a Vector.
 * \ingroup _binary_function
 */
// XXX shit temporaries
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
>
product(XprMatrix<E, Rows, Cols> lhs, const Vector<T, Cols>& rhs) {
  Matrix<T, Rows, Cols> m;
  m = lhs;
  typedef XprMVProduct<typename E::value_type, Rows, Cols, Cols, 1, T, 1> expr_type;
  return XprVector<expr_type, Rows>(expr_type(m.data(), rhs.data()));
}
#endif

/**
 * \fn productTransposed(const Matrix<T1, Rows, Cols>& matrix, const Vector<T2, Rows>& vector)
 * \brief Function for the transpose(matrix)-vector-product
 * \ingroup _binary_function
 */
template<class T1, class T2, std::size_t Rows, std::size_t Cols>
inline
XprVector<
  XprMVProduct<T1, Cols, Rows, 1, Cols, T2, 1>,
  Rows
>
productTransposed(const Matrix<T1, Rows, Cols>& matrix, const Vector<T2, Rows>& vector) {
  typedef XprMVProduct<T1, Cols, Rows, 1, Cols, T2, 1> expr_type;
  return XprVector<expr_type, Rows>(expr_type(matrix.data(), vector.data()));
}


/***********************************************************************
 * global binary math functions
 * documented by doc/functions_doc.cc
 ***********************************************************************/

/*
 * FUNCTION(Matrix<T1, Rows, Cols>, Matrix<T2, Rows, Cols>)
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL)                   	\
template<class T1, class T2, std::size_t Rows, std::size_t Cols>	\
inline                 			      			      	\
XprMatrix<                   						\
  XprBinOp<                   						\
    FUNCTIONAL<T1, T2>,                   				\
    MatrixConstReference<T1, Rows, Cols>,      				\
    MatrixConstReference<T2, Rows, Cols>      				\
  >,                   			              			\
  Rows, Cols				      			      	\
>                   			              			\
FUNC(const Matrix<T1, Rows, Cols>& lhs,           			\
     const Matrix<T2, Cols, Cols>& rhs) {           			\
  typedef XprBinOp <                    				\
    FUNCTIONAL<T1, T2>,  	                 			\
    MatrixConstReference<T1, Rows, Cols>,      				\
    MatrixConstReference<T2, Rows, Cols>       				\
  >							expr_type;	\
  return XprMatrix<expr_type, Rows, Cols>(expr_type(lhs.cref(), rhs.cref())); \
}

TVMET_BINARY_FUNCTION(atan2, fcnl_atan2)
TVMET_BINARY_FUNCTION(drem, fcnl_drem)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot)
TVMET_BINARY_FUNCTION(jn, fcnl_jn)
TVMET_BINARY_FUNCTION(yn, fcnl_yn)
TVMET_BINARY_FUNCTION(pow, fcnl_pow)
#undef TVMET_BINARY_FUNCTION


/*
 * Matrix<T, Rows, Cols> and  POD's
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL, 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				      			      	\
>          								\
FUNC(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				      			      	\
>          								\
FUNC(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_FUNCTION(atan2, fcnl_atan2, int)
TVMET_BINARY_FUNCTION(drem, fcnl_drem, int)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod, int)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot, int)
TVMET_BINARY_FUNCTION(jn, fcnl_jn, int)
TVMET_BINARY_FUNCTION(yn, fcnl_yn, int)
TVMET_BINARY_FUNCTION(pow, fcnl_pow, int)

TVMET_BINARY_FUNCTION(atan2, fcnl_atan2, float)
TVMET_BINARY_FUNCTION(drem, fcnl_drem, float)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod, float)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot, float)
TVMET_BINARY_FUNCTION(jn, fcnl_jn, float)
TVMET_BINARY_FUNCTION(yn, fcnl_yn, float)
TVMET_BINARY_FUNCTION(pow, fcnl_pow, float)

TVMET_BINARY_FUNCTION(atan2, fcnl_atan2, double)
TVMET_BINARY_FUNCTION(drem, fcnl_drem, double)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod, double)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot, double)
TVMET_BINARY_FUNCTION(jn, fcnl_jn, double)
TVMET_BINARY_FUNCTION(yn, fcnl_yn, double)
TVMET_BINARY_FUNCTION(pow, fcnl_pow, double)

#ifdef TVMET_HAVE_LONG_DOUBLE
TVMET_BINARY_FUNCTION(atan2, fcnl_atan2, long double)
TVMET_BINARY_FUNCTION(drem, fcnl_drem, long double)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod, long double)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot, long double)
TVMET_BINARY_FUNCTION(jn, fcnl_jn, long double)
TVMET_BINARY_FUNCTION(yn, fcnl_yn, long double)
TVMET_BINARY_FUNCTION(pow, fcnl_pow, long double)
#endif // TVMET_HAVE_LONG_DOUBLE

#undef TVMET_BINARY_FUNCTION


#ifdef TVMET_HAVE_COMPLEX
/*
 * Matrix<T, Rows, Cols> and  std::complex<>
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL)                 	\
template<class T, std::size_t Rows, std::size_t Cols> 			\
inline                 			      			      	\
XprMatrix<                						\
  XprBinOp<          							\
    FUNCTIONAL<T, std::complex<T> >,          				\
    MatrixConstReference<T, Rows, Cols>,				\
    XprLiteral< std::complex<T> >          				\
  >,          								\
  Rows, Cols				      			      	\
>          								\
FUNC(const Matrix<T, Rows, Cols>& lhs, const std::complex<T>& rhs) {	\
  typedef XprBinOp<          						\
    FUNCTIONAL<T, std::complex<T> >,          				\
    MatrixConstReference<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>, T>,          				\
    XprLiteral< std::complex<T> >,          				\
    MatrixConstReference<T, Rows, Cols>					\
  >,          								\
  Rows, Cols				      			      	\
>          								\
FUNC(const std::complex<T>& lhs, const Matrix<T, Rows, Cols>& rhs) {	\
  typedef XprBinOp<         						\
    FUNCTIONAL< std::complex<T>, 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_FUNCTION(atan2, fcnl_atan2)
TVMET_BINARY_FUNCTION(drem, fcnl_drem)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot)
TVMET_BINARY_FUNCTION(jn, fcnl_jn)
TVMET_BINARY_FUNCTION(yn, fcnl_yn)
TVMET_BINARY_FUNCTION(pow, fcnl_pow)
#undef TVMET_BINARY_FUNCTION

#endif // TVMET_HAVE_COMPLEX

/*
 * Matrix<T, Rows, Cols> and  XprMatrix<E, Rows, Cols>
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL)                   	\
template<class E, class T, std::size_t Rows, std::size_t Cols> 		\
inline                 			      			      	\
XprMatrix<                    	      					\
  XprBinOp<                     	      				\
    FUNCTIONAL<typename E::value_type, T>,                    	      	\
    MatrixConstReference<T, Rows, Cols>,       	      			\
    XprMatrix<E, Rows, Cols>						\
  >,                     	      					\
  Rows, Cols				      			      	\
>                      	      						\
FUNC(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 E, class T, std::size_t Rows, std::size_t Cols> 		\
inline                 			      			      	\
XprMatrix<                    	      					\
  XprBinOp<                     	      				\
    FUNCTIONAL<T, typename E::value_type>,				\
    MatrixConstReference<T, Rows, Cols>,       	      			\
    XprMatrix<E, Rows, Cols>						\
  >,                     	      					\
  Rows, Cols				      			      	\
>                      	      						\
FUNC(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_FUNCTION(atan2, fcnl_atan2)
TVMET_BINARY_FUNCTION(drem, fcnl_drem)
TVMET_BINARY_FUNCTION(fmod, fcnl_fmod)
TVMET_BINARY_FUNCTION(hypot, fcnl_hypot)
TVMET_BINARY_FUNCTION(jn, fcnl_jn)
TVMET_BINARY_FUNCTION(yn, fcnl_yn)
TVMET_BINARY_FUNCTION(pow, fcnl_pow)
#undef TVMET_BINARY_FUNCTION

/***********************************************************************
 * global unary math functions
 * documented by doc/functions_doc.cc
 ***********************************************************************/

/*
 * Functions on  Matrix<T, Rows, Cols>
 */
#define TVMET_UNARY_FUNCTION(FUNC, FUNCTIONAL)				\
template<class T, std::size_t Rows, std::size_t Cols>			\
inline									\
XprMatrix<								\
  XprUnOp<								\
    FUNCTIONAL<T>, 							\
    MatrixConstReference<T, Rows, Cols>					\
  >, 									\
  Rows, Cols				      			      	\
>									\
FUNC(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_FUNCTION(abs, fcnl_abs)
TVMET_UNARY_FUNCTION(cbrt, fcnl_cbrt)
TVMET_UNARY_FUNCTION(ceil, fcnl_ceil)
TVMET_UNARY_FUNCTION(floor, fcnl_floor)
TVMET_UNARY_FUNCTION(rint, fcnl_rint)
TVMET_UNARY_FUNCTION(sin, fcnl_sin)
TVMET_UNARY_FUNCTION(cos, fcnl_cos)
TVMET_UNARY_FUNCTION(tan, fcnl_tan)
TVMET_UNARY_FUNCTION(sinh, fcnl_sinh)
TVMET_UNARY_FUNCTION(cosh, fcnl_cosh)
TVMET_UNARY_FUNCTION(tanh, fcnl_tanh)
TVMET_UNARY_FUNCTION(asin, fcnl_asin)
TVMET_UNARY_FUNCTION(acos, fcnl_acos)
TVMET_UNARY_FUNCTION(atan, fcnl_atan)
TVMET_UNARY_FUNCTION(exp, fcnl_exp)
TVMET_UNARY_FUNCTION(log, fcnl_log)
TVMET_UNARY_FUNCTION(log10, fcnl_log10)
TVMET_UNARY_FUNCTION(sqrt, fcnl_sqrt)

#ifdef TVMET_HAVE_IEEE_MATH
TVMET_UNARY_FUNCTION(asinh, fcnl_asinh)
TVMET_UNARY_FUNCTION(acosh, fcnl_acosh)
TVMET_UNARY_FUNCTION(atanh, fcnl_atanh)
TVMET_UNARY_FUNCTION(expm1, fcnl_expm1)
TVMET_UNARY_FUNCTION(log1p, fcnl_log1p)
TVMET_UNARY_FUNCTION(erf, fcnl_erf)
TVMET_UNARY_FUNCTION(erfc, fcnl_erfc)
TVMET_UNARY_FUNCTION(j0, fcnl_j0)
TVMET_UNARY_FUNCTION(j1, fcnl_j1)
TVMET_UNARY_FUNCTION(y0, fcnl_y0)
TVMET_UNARY_FUNCTION(y1, fcnl_y1)
TVMET_UNARY_FUNCTION(lgamma, fcnl_lgamma)
// XXX default return is only an int!
TVMET_UNARY_FUNCTION(isnan, fcnl_isnan)
TVMET_UNARY_FUNCTION(isinf, fcnl_isinf)
TVMET_UNARY_FUNCTION(finite, fcnl_finite)
#endif // TVMET_HAVE_IEEE_MATH

#undef TVMET_UNARY_FUNCTION


/***********************************************************************
 * misc unary/binary matrix functions
 ***********************************************************************/

/**
 * \fn transpose(const Matrix<T, Rows, Cols>& rhs)
 * \brief Transpose the matrix
 * \ingroup _unary_function
 */
template<class T, std::size_t Rows, std::size_t Cols>
inline
XprMatrix<
  XprMatrixTranspose<
    MatrixConstReference<T, Rows, Cols>
  >,
  Cols, Rows
>
transpose(const Matrix<T, Rows, Cols>& rhs) {
  typedef XprMatrixTranspose<
    MatrixConstReference<T, Rows, Cols>
  >							expr_type;
  return XprMatrix<expr_type, Cols, Rows>(expr_type(rhs.cref()));
}

/*
 * \fn trace(const Matrix<T, Dim, Dim>& m)
 * \brief Compute the trace of a square matrix.
 * \ingroup _unary_function
 *
 * Simply compute the trace of the given matrix as:
 * \f[
 *  \sum_{k = 0}^{Sz-1} m(k, k)
 * \f]
 */
template<class T, std::size_t Dim>
inline
typename NumericTraits<T>::sum_type
trace(const Matrix<T, Dim, Dim>& m) {
  return meta::Matrix<Dim, Dim, 0, 0>::trace(m);
}

/**
 * \fn row(Matrix<T, Rows, Cols>& m, std::size_t no)
 * \brief Returns a row vector of the given matrix.
 * \ingroup _binary_function
 */
template<class T, std::size_t Rows, std::size_t Cols>
inline
XprVector<
  MatrixRowVectorReference<T, Rows, Cols>,
  Cols
>
row(Matrix<T, Rows, Cols>& m, std::size_t no) {
  typedef MatrixRowVectorReference<T, Rows, Cols>	expr_type;
  return XprVector<expr_type, Cols>(expr_type(m.data(), no));
}

/**
 * \fn col(Matrix<T, Rows, Cols>& m, std::size_t no)
 * \brief Returns a column vector of the given matrix.
 * \ingroup _binary_function
 */
template<class T, std::size_t Rows, std::size_t Cols>
inline
XprVector<
  MatrixColVectorReference<T, Rows, Cols>,
  Rows
>
col(Matrix<T, Rows, Cols>& m, std::size_t col) {
  typedef MatrixColVectorReference<T, Rows, Cols>	expr_type;
  return XprVector<expr_type, Rows>(expr_type(m.data(), col));
}

/***********************************************************************
 * min/max unary functions
 ***********************************************************************/

/**
 * \fn max(const XprMatrix<E, Rows, Cols>& e)
 * \brief Find the maximum of a matrix expression
 * \ingroup _unary_function
 */
template<class E, std::size_t Rows, std::size_t Cols>
inline
Extremum<typename E::value_type, std::size_t, matrix_tag>
max(const XprMatrix<E, Rows, Cols>& e) {
  typedef typename E::value_type value_type;

  value_type max( e(0, 0) );
  std::size_t row(0), col(0);

  // this loop is faster than meta templates!
  for(std::size_t i = 1; i < Rows; ++i) {
    for(std::size_t j = 1; j < Cols; ++j) {
      if(e(i, j) > max) {
	max = e(i, j);
	row = i;
	col = j;
      }
    }
  }

  return Extremum<value_type, std::size_t, matrix_tag>(max, row, col);
}

/**
 * \fn max(const Matrix<T, Rows, Cols>& m)
 * \brief Find the maximum of a matrix
 * \ingroup _unary_function
 */
template<class T, std::size_t Rows, std::size_t Cols>
inline
Extremum<T, std::size_t, matrix_tag>
max(const Matrix<T, Rows, Cols>& m) { return max(m.as_expr()); }

/**
 * \fn min(const XprMatrix<E, Rows, Cols>& e)
 * \brief Find the minimum of a matrix expression
 * \ingroup _unary_function
 */
template<class E, std::size_t Rows, std::size_t Cols>
inline
Extremum<typename E::value_type, std::size_t, matrix_tag>
min(const XprMatrix<E, Rows, Cols>& e) {
  typedef typename E::value_type value_type;

  value_type min( e(0, 0) );
  std::size_t row(0), col(0);

  // this loop is faster than meta templates!
  for(std::size_t i = 1; i < Rows; ++i) {
    for(std::size_t j = 1; j < Cols; ++j) {
      if(e(i, j) < min) {
	min = e(i, j);
	row = i;
	col = j;
      }
    }
  }

  return Extremum<value_type, std::size_t, matrix_tag>(min, row, col);
}

/**
 * \fn min(const Matrix<T, Rows, Cols>& m)
 * \brief Find the minimum of a matrix
 * \ingroup _unary_function
 */
template<class T, std::size_t Rows, std::size_t Cols>
inline
Extremum<T, std::size_t, matrix_tag>
min(const Matrix<T, Rows, Cols>& m) { return min(m.as_expr()); }


} // namespace tvmet

#endif // TVMET_MATRIX_FUNCTIONS_H

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