// Copyright (C) 2006  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_MATRIx_MATH_FUNCTIONS_ABSTRACT_
#ifdef DLIB_MATRIx_MATH_FUNCTIONS_ABSTRACT_

#include "matrix_abstract.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
//                          Exponential Functions 
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    const matrix_exp exp (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::exp(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp log10 (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::log10(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp log (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::log(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp sqrt (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == sqrt(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    template <typename T>
    const matrix_exp pow (
        const matrix_exp& m,
        const T& e
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == pow(m(r,c),e) 
    !*/

// ----------------------------------------------------------------------------------------

    template <typename T>
    const matrix_exp pow (
        const T& b,
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == pow(b, m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp squared (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == m(r,c)*m(r,c)
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp cubed (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == m(r,c)*m(r,c)*m(r,c)
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
//                               Miscellaneous
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    const matrix_exp sign (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix that tells the sign of each element in m. In particular:
              returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                    - if (m(r,c) >= 0) then
                        - R(r,c) == +1
                    - else
                        - R(r,c) == -1
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp sigmoid (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == 1/(1 + exp(-m(r,c))) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp abs (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - if (m contains std::complex<T> objects) then
                    - R::type == T 
                - else
                    - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::abs(m(r,c)) 
                  (note that if m is complex then std::abs(val) performs sqrt(std::norm(val))
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp reciprocal (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, long double, std::complex<float>,
              std::complex<double>, or std::complex<long double>
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                    - if (m(r,c) != 0) then
                        - R(r,c) == 1.0/m(r,c) 
                    - else
                        - R(r,c) == 0
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp reciprocal_max (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, long double
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                    - if (m(r,c) != 0) then
                        - R(r,c) == 1.0/m(r,c) 
                    - else
                        - R(r,c) == std::numeric_limits<R::type>::max() 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp normalize (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - if (sqrt(sum(squared(m))) != 0) then
                - returns m/sqrt(sum(squared(m)))
            - else
                - returns m
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
//                          Rounding numbers one way or another
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    const matrix_exp round (
        const matrix_exp& m
    );
    /*!
        requires
            - is_built_in_scalar_type<matrix_exp::type>::value == true
              (i.e. m must contain a type like int, float, double, long, etc.)
        ensures
            - if (m contains integers) then
                - returns m unmodified
            - else
                - returns a matrix R such that:
                    - R::type == the same type that was in m
                    - R has the same dimensions as m
                    - for all valid r and c:
                      R(r,c) == m(r,c) rounded to the nearest integral value
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp ceil (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::ceil(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp floor (
        const matrix_exp& m
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::floor(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp round_zeros (
        const matrix_exp& m
    );
    /*!
        requires
            - is_built_in_scalar_type<matrix_exp::type>::value == true
              (i.e. m must contain a type like int, float, double, long, etc.)
        ensures
            - if (m contains integers) then
                - returns m unmodified
            - else
                - returns a matrix R such that:
                    - R::type == the same type that was in m
                    - R has the same dimensions as m
                    - let eps == 10*std::numeric_limits<matrix_exp::type>::epsilon()
                    - for all valid r and c:
                        - if (abs(m(r,c)) >= eps) then
                            - R(r,c) == m(r,c)
                        - else
                            - R(r,c) == 0
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp round_zeros (
        const matrix_exp& m,
        matrix_exp::type eps 
    );
    /*!
        requires
            - is_built_in_scalar_type<matrix_exp::type>::value == true
              (i.e. m must contain a type like int, float, double, long, etc.)
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                    - if (abs(m(r,c)) >= eps) then
                        - R(r,c) == m(r,c)
                    - else
                        - R(r,c) == 0
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
//                              Complex number utility functions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    const matrix_exp conj (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == std::complex<T>
        ensures
            - returns a matrix R such that:
                - R::type == std::complex<T>
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::conj(m(r,c))
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp norm (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == std::complex<T>
        ensures
            - returns a matrix R such that:
                - R::type == T
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::norm(m(r,c))
                  (note that std::norm(val) == val.real()*val.real() + val.imag()*val.imag())
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp imag (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == std::complex<T>
        ensures
            - returns a matrix R such that:
                - R::type == T
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::imag(m(r,c))
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp real (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == std::complex<T>
        ensures
            - returns a matrix R such that:
                - R::type == T
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::real(m(r,c))
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp complex_matrix (
        const matrix_exp& real_part
    );
    /*!
        ensures
            - returns a matrix R such that:
                - R::type == std::complex<T> where T is whatever type real_part used.
                - R has the same dimensions as real_part. 
                - for all valid r and c:
                  R(r,c) == std::complex(real_part(r,c), 0)
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp complex_matrix (
        const matrix_exp& real_part,
        const matrix_exp& imag_part
    );
    /*!
        requires
            - real_part.nr() == imag_part.nr()
            - real_part.nc() == imag_part.nc()
            - real_part and imag_part both contain the same type of element
        ensures
            - returns a matrix R such that:
                - R::type == std::complex<T> where T is whatever type real_part and imag_part used.
                - R has the same dimensions as real_part and imag_part
                - for all valid r and c:
                  R(r,c) == std::complex(real_part(r,c),imag_part(r,c))
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
//                              Trigonometric Functions 
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    const matrix_exp sin (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::sin(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp cos (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::cos(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp tan (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::tan(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp asin (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::asin(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp acos (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::acos(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp atan (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::atan(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp sinh (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::sinh(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp cosh (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::cosh(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp tanh (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double 
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::tanh(m(r,c)) 
    !*/

// ----------------------------------------------------------------------------------------

    const matrix_exp soft_max (
        const matrix_exp& m
    );
    /*!
        requires
            - matrix_exp::type == float, double, or long double
        ensures
            - returns a matrix R such that:
                - R::type == the same type that was in m
                - R has the same dimensions as m
                - for all valid r and c:
                  R(r,c) == std::exp(m(r,c)) / sum(std::exp(m))
    !*/

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_MATRIx_MATH_FUNCTIONS_ABSTRACT_