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

    #include "matrix_abstract.h"
    #include "../algs.h"

    namespace dlib
    {

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

        template <typename EXP>
        typename EXP::matrix_type fft (
            const matrix_exp<EXP>& data
        );
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - Computes the 1 or 2 dimensional discrete Fourier transform of the given data
                  matrix and returns it.  In particular, we return a matrix D such that:
                    - D.nr() == data.nr()
                    - D.nc() == data.nc()
                    - D(0,0) == the DC term of the Fourier transform.
                    - starting with D(0,0), D contains progressively higher frequency components
                      of the input data.
                    - ifft(D) == data
        !*/

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

        template < typename T, typename Alloc >
        matrix<std::complex<T>,0,1> fft (
            const std::vector<std::complex<T>, Alloc>& data
        );
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - Computes the 1 dimensional discrete Fourier transform of the given data
                  vector and returns it.  In particular, we return a matrix D such that:
                    - D.nr() == data.size()
                    - D.nc() == 1
                    - D(0,0) == the DC term of the Fourier transform.
                    - starting with D(0,0), D contains progressively higher frequency components
                      of the input data.
                    - ifft(D) == data
        !*/

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

        template <typename EXP>
        typename EXP::matrix_type ifft (
            const matrix_exp<EXP>& data
        );
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - Computes the 1 or 2 dimensional inverse discrete Fourier transform of the
                  given data vector and returns it.  In particular, we return a matrix D such
                  that:
                    - D.nr() == data.nr()
                    - D.nc() == data.nc()
                    - fft(D) == data
        !*/

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

        template < typename T, typename Alloc >
        matrix<std::complex<T>,0,1> ifft (
            const std::vector<std::complex<T>, Alloc>& data
        )
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - Computes the 1 dimensional inverse discrete Fourier transform of the
                  given data vector and returns it.  In particular, we return a matrix D such
                  that:
                    - D.nr() == data.size()
                    - D.nc() == 1
                    - fft(D) == data
        !*/

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

        template <typename EXP>
        matrix<add_complex_t<typename EXP::type>> fftr (
            const matrix_exp<EXP>& data
        );
        /*!
            requires
                - data contains elements of type double, float, or long double.
                - data.nc() is even
            ensures
                - Computes the 1 or 2 dimensional real discrete Fourier transform of the given data
                  matrix and returns it.  In particular, we return a matrix D such that:
                    - D.nr() == data.nr()
                    - D.nc() == fftr_nc_size(data.nc())
                    - D(0,0) == the DC term of the Fourier transform.
                    - starting with D(0,0), D contains progressively higher frequency components
                      of the input data.
                    - ifftr(D) == data
        !*/

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

        template <typename EXP>
        matrix<remove_complex_t<typename EXP::type>> ifftr (
            const matrix_exp<EXP>& data
        );
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - Computes the 1 or 2 dimensional inverse real discrete Fourier transform of the
                  given data vector and returns it.  In particular, we return a matrix D such
                  that:
                    - D.nr() == data.nr()
                    - D.nc() == ifftr_nc_size(data.nc())
                    - fftr(D) == data
        !*/

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

        template <
            typename T,
            long NR,
            long NC,
            typename MM,
            typename L
            >
        void fft_inplace (
            matrix<std::complex<T>,NR,NC,MM,L>& data
        );
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - This function is identical to fft() except that it does the FFT in-place.
                  That is, after this function executes we will have:
                    - #data == fft(data)
        !*/

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

        template <
            typename T,
            long NR,
            long NC,
            typename MM,
            typename L
            >
        void ifft_inplace (
            matrix<std::complex<T>,NR,NC,MM,L>& data
        );
        /*!
            requires
                - data contains elements of type std::complex<> that itself contains double, float, or long double.
            ensures
                - This function is identical to ifft() except that it does the inverse FFT
                  in-place.  That is, after this function executes we will have:
                    - #data == ifft(data)*data.size()
                    - Note that the output needs to be divided by data.size() to complete the
                      inverse transformation.
        !*/

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

        // These return function objects with signature double(size_t i, size_t wlen)
        // defining PERIODIC window functions suitable for passing to STFT functions

        inline function_object make_hann();
        inline function_object make_blackman();
        inline function_object make_blackman_nuttall();
        inline function_object make_blackman_harris();
        inline function_object make_blackman_harris7();
        inline function_object make_kaiser(beta_t beta);

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

        template <typename EXP, typename WINDOW>
        matrix<complex_type> stft (
            const matrix_exp<EXP>& signal,
            const WINDOW& w,
            std::size_t fftsize,
            std::size_t wlen,
            std::size_t hoplen
        );
        /*!
            requires
                - is_vector(signal) == true, i.e. signal has rank 1
                - signal.size() >= wlen
                - w is a function object with signature double(size_t i, size_t wlen) that defines a PERIODIC window,
                  e.g. the output of make_hann().
                - fftsize >= wlen
                - wlen >= hoplen
                - EXP::type is a floating point type (float, double or long double), real or complex
            ensures
                - Performs a Short-Time-Fourier-Transform (STFT) on 1D data.
                - Returns a matrix D where first dimension correponds to time and second dimension corresponds to frequency.
                - Dimensions of D are:
                    - D.nr() == (signal.size() + hoplen) / hoplen and corresponds to the number of time frames.
                    - D.nc() == fftsize
                - The type of D is add_complex_t<EXP::type>
                - Each time frame t (equivalently, each row t) is centered on signal(t*hoplen)
                - This is equivalent to calling the following in python
                  (provided w is converted into a string representation which scipy can interpret)
                    win     = scipy.signal.get_window(w, wlen)
                    scale   = win.sum()
                    _, _, Z = scipy.signal.stft(signal, nfft=fftsize, nperseg=wlen, noverlap=(wlen-hoplen), window=win, return_onesided=False)
                    Z       *= scale

        !*/

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

        template <typename T, typename Alloc, typename WINDOW>
        matrix<complex_type> stft (
            const std::vector<T, Alloc>& signal,
            const WINDOW& w,
            std::size_t fftsize,
            std::size_t wlen,
            std::size_t hoplen
        );
        /*!
            ensures
                - This is a shortcut to calling stft(dlib::mat(signal), w, fftsize, wlen, hoplen)
        !*/

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

        template <typename EXP,typename WINDOW>
        matrix<complex_type> istft (
            const matrix_exp<EXP>& stft,
            const WINDOW& w,
            std::size_t wlen,
            std::size_t hoplen
        );
        /*!
            requires
                - m has rank 2 where 1st dimension corresponds to time and second dimension corresponds to frequency
                - w is a function object with signature double(size_t i, size_t wlen) that defines a PERIODIC window,
                  e.g. the output of make_hann().
                - wlen >= hoplen
                - EXP::type is a complex floating point type (complex<float>, complex<double> or complex<long double>)
                - If you wish to satisfy istft(stft(x, ...), ...) == x then:
                    - w is the same as what was used with stft()
                    - wlen is the same as what was used with stft()
                    - hoplen is the same as what was used with stft()
            ensures
                - Performs an inverse Short-Time-Fourier-Transform (STFT)
                - istft(stft(x, w, wlen, wlen, hoplen), w, wlen, hoplen)) == x
                - istft(stft(x, w, fftsize, wlen, hoplen), w, wlen, hoplen)) == x
        !*/

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

        template <typename EXP, typename WINDOW>
        matrix<complex_type> stftr (
            const matrix_exp<EXP>& signal,
            const WINDOW& w,
            std::size_t fftsize,
            std::size_t wlen,
            std::size_t hoplen
        );
        /*!
            requires
                - is_vector(signal) == true, i.e. signal has rank 1
                - signal.size() >= wlen
                - w is a function object with signature double(size_t i, size_t wlen) that defines a PERIODIC window,
                  e.g. the output of make_hann().
                - fftsize >= wlen
                - wlen >= hoplen
                - EXP::type is a floating point type (float, double or long double) and must be real
            ensures
                - Performs a real Short-Time-Fourier-Transform (STFTr) on 1D data.
                - Returns a matrix D where first dimension correponds to time and second dimension corresponds to frequency.
                - Dimensions of D are:
                    - D.nr() == (signal.size() + hoplen) / hoplen and corresponds to the number of time frames.
                    - D.nc() == fftsize/2 + 1
                - The type of D is add_complex_t<EXP::type>
                - Each time frame t (equivalently each row t) is centered on signal(t*hoplen)
                - This is equivalent to calling the follwoing in python
                  (provided w is converted into a string representation which scipy can interpret)
                    win     = scipy.signal.get_window(w, wlen)
                    scale   = win.sum()
                    _, _, Z = scipy.signal.stft(signal, nfft=fftsize, nperseg=wlen, noverlap=(wlen-hoplen), window=win, return_onesided=True)
                    Z       *= scale

        !*/

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

        template <typename T, typename Alloc, typename WINDOW>
        matrix<complex_type> stftr (
            const std::vector<T, Alloc>& signal,
            const WINDOW& w,
            std::size_t fftsize,
            std::size_t wlen,
            std::size_t hoplen
        );
        /*!
            ensures
                - This is a shortcut to calling istft(dlib::mat(signal), w, wlen, hoplen)
        !*/

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

        template <typename EXP, typename WINDOW>
        matrix<real_type> istftr (
            const matrix_exp<EXP>& stft,
            const WINDOW& w,
            std::size_t wlen,
            std::size_t hoplen
        );
        /*!
            requires
                - m has rank 2 where 1st dimension corresponds to time and second dimension corresponds to frequency
                - w is a function object with signature double(size_t i, size_t wlen) that defines a PERIODIC window,
                  e.g. the output of make_hann().
                - wlen >= hoplen
                - EXP::type is a complex floating point type (complex<float>, complex<double> or complex<long double>)
                - If you wish to satisfy istftr(stftr(x, ...), ...) == x then:
                    - w is the same as what was used with stftr()
                    - wlen is the same as what was used with stftr()
                    - hoplen is the same as what was used with stftr()
            ensures
                - Performs an inverse Short-Time-Fourier-Transform (STFT)
                - istftr(stftr(x, w, wlen, wlen, hoplen), w, wlen, hoplen)) == x
                - istftr(stftr(x, w, fftsize, wlen, hoplen), w, wlen, hoplen)) == x
        !*/
    // ----------------------------------------------------------------------------------------
    }

    #endif // DLIB_FFt_ABSTRACT_Hh_