/*
 * 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_VECTOR_FUNCTIONS_H
#define TVMET_VECTOR_FUNCTIONS_H

#include <tvmet/NumericTraits.h>
#include <tvmet/Extremum.h>

namespace tvmet {

/************************************************************************
 * vector specific functions
 ************************************************************************/

/**
 * \fn sum(const Vector<T, Sz>& v)
 * \brief Compute the sum of the vector.
 * \ingroup _unary_function
 *
 * Simply compute the sum of the given vector as:
 * \f[
 *  \sum_{i = 0}^{Sz-1} v[i]
 * \f]
 */
template<class T, std::size_t Sz>
inline
typename NumericTraits<T>::sum_type
sum(const Vector<T, Sz>& v) {
  return meta::Vector<Sz>::sum(v);
}

/**
 * \fn product(const Vector<T, Sz>& v)
 * \brief Compute the product of the vector.
 * \ingroup _unary_function
 *
 * Simply computer the product of the given vector as:
 * \f[
 * \prod_{i = 0}^{Sz - 1} v[i]
 * \f]
 */
template<class T, std::size_t Sz>
inline
typename NumericTraits<T>::sum_type
product(const Vector<T, Sz>& v) {
  return meta::Vector<Sz>::product(v);
}

/**
 * \fn dot(const Vector<T1, Sz>& lhs, const Vector<T2, Sz>& rhs)
 * \brief Compute the dot/inner product
 * \ingroup _binary_function
 *
 * Compute the dot product as:
 * \f[
 * \sum_{i = 0}^{Sz - 1} ( lhs[i] * rhs[i] )
 * \f]
 * where lhs is a column vector and rhs is a row vector, both vectors
 * have the same dimension.
 */
template<class T1, class T2, std::size_t Sz>
inline
typename PromoteTraits<T1, T2>::value_type
dot(const Vector<T1, Sz>& lhs, const Vector<T2, Sz>& rhs) {
  return meta::Vector<Sz>::dot(lhs, rhs);
}

/**
 * \fn cross(const Vector<T1, 3>& lhs, const Vector<T2, 3>& rhs)
 * \brief Compute the cross/outer product
 * \ingroup _binaray_function
 * \note working only for vectors of size = 3
 */
template<class T1, class T2>
inline
Vector<typename PromoteTraits<T1, T2>::value_type, 3>
cross(const Vector<T1, 3>& lhs, const Vector<T2, 3>& rhs) {
  typedef typename PromoteTraits<T1, T2>::value_type	value_type;
  return Vector<value_type, 3>(lhs[1]*rhs[2] - rhs[1]*lhs[2],
			       rhs[0]*lhs[2] - lhs[0]*rhs[2],
			       lhs[0]*rhs[1] - rhs[0]*lhs[1]);
}

/**
 * \fn norm(const Vector<T, Sz>& v)
 * \brief The norm of the vector
 * \ingroup _unary_function
 * The norm of any vector is just the square root of the dot product of
 * a vector with itself, or
 *
 * \f[
 * |Vector<T, Sz> v| = |v| = \sqrt{ v[i]^2 } \qquad ; i = 1 \ldots Sz
 * \f]
 *
 * The cast for Vector<int> avoids warnings on sqrt.
 */
template<class T, std::size_t Sz>
inline
typename NumericTraits<T>::sum_type
norm(const Vector<T, Sz>& v) {
  return static_cast<T>( std::sqrt(static_cast<typename NumericTraits<T>::float_type>(dot(v, v))) );
}

/**
 * \fn norm2(const Vector<T, Sz>& v)
 * \brief The norm squared.
 * \ingroup _unaray_function
 * \sa norm
 */
template<class T, std::size_t Sz>
inline
typename NumericTraits<T>::sum_type
norm2(const Vector<T, Sz>& v) {
  return meta::Vector<Sz>::dot(v, v);
}

/**
 * \fn normalize(const Vector<T, Sz>& v)
 * \brief Normalize the given vector.
 * \ingroup _unary_function
 * \sa norm
 *
 * using the equation:
 * \f[
 * \frac{Vector<T, Sz> v}{\sqrt{ v[i]^2 }} \qquad ; i = 1 \ldots Sz
 * \f]
 */
template<class T, std::size_t Sz>
inline
XprVector<
  XprBinOp<
    fcnl_Div<T, T>,
    VectorConstReference<T, Sz>,
    XprLiteral< T >
  >,
  Sz
>
normalize(const Vector<T, Sz>& v) {
  typedef XprBinOp<
    fcnl_Div<T, T>,
    VectorConstReference<T, Sz>,
    XprLiteral< T >
  >							expr_type;
  return XprVector<expr_type, Sz>(expr_type(v.cref(), XprLiteral< T >(norm(v))));
}


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

/*
 * Functions on Vector<T1, Sz> and Vector<T1, Sz>)
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL)                   	\
template<class T1, class T2, std::size_t Sz> 	                  	\
inline                 			      			      	\
XprVector<                   						\
  XprBinOp<                   						\
    FUNCTIONAL<T1, T2>,                   				\
    VectorConstReference<T1, Sz>,              				\
    VectorConstReference<T2, Sz>               				\
  >,                   			              			\
  Sz									\
>                   			              			\
FUNC(const Vector<T1, Sz>& lhs, const Vector<T2, Sz>& rhs) { 		\
  typedef XprBinOp <                    				\
    FUNCTIONAL<T1, T2>,  	                 			\
    VectorConstReference<T1, Sz>,              				\
    VectorConstReference<T2, Sz>	      				\
  >							expr_type;	\
  return XprVector<expr_type, Sz>(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

/*
 * Vector<T, Sz> and  TP's
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL, TP)                 	\
template<class T, std::size_t Sz>              				\
inline                 			      			      	\
XprVector<                						\
  XprBinOp<          							\
    FUNCTIONAL<T, TP >,          					\
    VectorConstReference<T, Sz>,       					\
    XprLiteral< TP >          						\
  >,          								\
  Sz									\
>          								\
FUNC(const Vector<T, Sz>& lhs, TP rhs) {				\
  typedef XprBinOp<          						\
    FUNCTIONAL<T, TP >,          					\
    VectorConstReference<T, Sz>,      					\
    XprLiteral< TP >							\
  >							expr_type;	\
  return XprVector<expr_type, Sz>(expr_type(lhs.cref(), XprLiteral< TP >(rhs))); \
}          								\
          								\
template<class T, std::size_t Sz>     					\
inline                 			      			      	\
XprVector<          							\
  XprBinOp<          							\
    FUNCTIONAL< TP, T>,          					\
    XprLiteral< TP >,          						\
    VectorConstReference<T, Sz>        					\
  >,          								\
  Sz									\
>          								\
FUNC(TP lhs, const Vector<T, Sz>& rhs) {	         		\
  typedef XprBinOp<         						\
    FUNCTIONAL< TP, T>,         					\
    XprLiteral< TP >,         						\
    VectorConstReference<T, Sz>						\
   >							expr_type;	\
  return XprVector<expr_type, Sz>(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
/*
 * Vector<T, Sz> and std::complex<>
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL)                 	\
template<class T, std::size_t Sz>              				\
inline                 			      			      	\
XprVector<                						\
  XprBinOp<          							\
    FUNCTIONAL<T, std::complex<T> >,   					\
    VectorConstReference<T, Sz>,       					\
    XprLiteral< std::complex<T> >					\
  >,          								\
  Sz									\
>          								\
FUNC(const Vector<T, Sz>& lhs, const std::complex<T>& rhs) {		\
  typedef XprBinOp<          						\
    FUNCTIONAL<T, std::complex<T> >,   					\
    VectorConstReference<T, Sz>,      					\
    XprLiteral< std::complex<T> >					\
  >							expr_type;	\
  return XprVector<expr_type, Sz>(expr_type(lhs.cref(), XprLiteral< std::complex<T> >(rhs))); \
}          								\
          								\
template<class T, std::size_t Sz>     					\
inline                 			      			      	\
XprVector<          							\
  XprBinOp<          							\
    FUNCTIONAL< std::complex<T>, T>,          				\
    XprLiteral< std::complex<T> >,          				\
    VectorConstReference<T, Sz>        					\
  >,          								\
  Sz									\
>          								\
FUNC(const std::complex<T>& lhs, const Vector<T, Sz>& rhs) {   		\
  typedef XprBinOp<         						\
    FUNCTIONAL< std::complex<T>, T>,   					\
    XprLiteral< std::complex<T> >,					\
    VectorConstReference<T, Sz>						\
   >							expr_type;	\
  return XprVector<expr_type, Sz>(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

/*
 * Vector<T, Sz> and  XprVector<E>
 */
#define TVMET_BINARY_FUNCTION(FUNC, FUNCTIONAL)                   	\
template<class E, class T, std::size_t Sz>             			\
inline                 			      			      	\
XprVector<                    	      					\
  XprBinOp<                     	      				\
    FUNCTIONAL<typename E::value_type, T>,                    	      	\
    VectorConstReference<T, Sz>,               	      			\
    XprVector<E, Sz>                   	      				\
  >,                     	      					\
  Sz									\
>                      	      						\
FUNC(XprVector<E, Sz> lhs, const Vector<T, Sz>& rhs) {			\
  typedef XprBinOp<                     	      			\
    FUNCTIONAL<typename E::value_type, T>,                    	      	\
    XprVector<E, Sz>,                  	      				\
    VectorConstReference<T, Sz>                	      			\
  > 							 expr_type; 	\
  return XprVector<expr_type, Sz>(expr_type(lhs, rhs.cref()));		\
}                    	      						\
                    	      						\
template<class E, class T, std::size_t Sz>             			\
inline                 			      			      	\
XprVector<                    	      					\
  XprBinOp<                     	      				\
    FUNCTIONAL<T, typename E::value_type>,                    	      	\
    VectorConstReference<T, Sz>,               	      			\
    XprVector<E, Sz>                   	      				\
  >,                     	      					\
  Sz									\
>                      	      						\
FUNC(const Vector<T, Sz>& lhs, XprVector<E, Sz> rhs) { 	                \
  typedef XprBinOp<                     	      			\
    FUNCTIONAL<T, typename E::value_type>,                    	      	\
    VectorConstReference<T, Sz>,      	      				\
    XprVector<E, Sz>         	      					\
  > 						 	expr_type; 	\
  return XprVector<expr_type, Sz>(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 Vector<T, Sz>
 */
#define TVMET_UNARY_FUNCTION(FUNC, FUNCTIONAL)				\
template<class T, std::size_t Sz>					\
inline									\
XprVector<								\
  XprUnOp<								\
    FUNCTIONAL<T>, 							\
    VectorConstReference<T, Sz>						\
  >, 									\
  Sz									\
>									\
FUNC(const Vector<T, Sz>& rhs) {					\
  typedef XprUnOp<							\
      FUNCTIONAL<T>, 							\
      VectorConstReference<T, Sz>					\
    > 							expr_type;	\
    return XprVector<expr_type, Sz>(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

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

/**
 * \fn Extremum<typename E::value_type, std::size_t, vector_tag> max(const XprVector<E, Sz>& e)
 * \brief Find the maximum of a vector expression
 * \ingroup _unary_function
 */
template<class E, std::size_t Sz>
inline
Extremum<typename E::value_type, std::size_t, vector_tag>
max(const XprVector<E, Sz>& e) {
  typedef typename E::value_type value_type;

  value_type max(e[0]);
  std::size_t idx(0);

  // this loop is faster than meta templates!
  for(std::size_t i = 1; i < Sz; ++i) {
    if(e[i] > max) {
      max = e[i];
      idx = i;
    }
  }

  return Extremum<value_type, std::size_t, vector_tag>(max, idx);
}

/**
 * \fn Extremum<T, std::size_t, vector_tag> max(const Vector<T, Sz>& v)
 * \brief Find the maximum of a vector
 * \ingroup _unary_function
 */
template<class T, std::size_t Sz>
inline
Extremum<T, std::size_t, vector_tag>
max(const Vector<T, Sz>& v) { return max(v.as_expr()); }

/**
 * \fn Extremum<typename E::value_type, std::size_t, vector_tag> min(const XprVector<E, Sz>& e)
 * \brief Find the minimum of a vector expression
 * \ingroup _unary_function
 */
template<class E, std::size_t Sz>
inline
Extremum<typename E::value_type, std::size_t, vector_tag>
min(const XprVector<E, Sz>& e) {
  typedef typename E::value_type value_type;

  value_type min(e[0]);
  std::size_t idx(0);

  // this loop is faster than meta templates!
  for(std::size_t i = 1; i < Sz; ++i) {
    if(e[i] < min) {
      min = e[i];
      idx = i;
    }
  }

  return Extremum<value_type, std::size_t, vector_tag>(min, idx);
}

/**
 * \fn Extremum<T, std::size_t, vector_tag> min(const Vector<T, Sz>& v)
 * \brief Find the minimum of a vector
 * \ingroup _unary_function
 */
template<class T, std::size_t Sz>
inline
Extremum<T, std::size_t, vector_tag>
min(const Vector<T, Sz>& v) { return min(v.as_expr()); }

} // namespace tvmet

#endif // TVMET_VECTOR_FUNCTIONS_H

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