/************************************************************************* Copyright (c) Sergey Bochkanov (ALGLIB project). >>> SOURCE LICENSE >>> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation (www.fsf.org); either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. A copy of the GNU General Public License is available at http://www.fsf.org/licensing/licenses >>> END OF LICENSE >>> *************************************************************************/ #include "stdafx.h" #include "fasttransforms.h" // disable some irrelevant warnings #if (AE_COMPILER==AE_MSVC) #pragma warning(disable:4100) #pragma warning(disable:4127) #pragma warning(disable:4702) #pragma warning(disable:4996) #endif using namespace std; ///////////////////////////////////////////////////////////////////////// // // THIS SECTION CONTAINS IMPLEMENTATION OF C++ INTERFACE // ///////////////////////////////////////////////////////////////////////// namespace alglib { /************************************************************************* 1-dimensional complex FFT. Array size N may be arbitrary number (composite or prime). Composite N's are handled with cache-oblivious variation of a Cooley-Tukey algorithm. Small prime-factors are transformed using hard coded codelets (similar to FFTW codelets, but without low-level optimization), large prime-factors are handled with Bluestein's algorithm. Fastests transforms are for smooth N's (prime factors are 2, 3, 5 only), most fast for powers of 2. When N have prime factors larger than these, but orders of magnitude smaller than N, computations will be about 4 times slower than for nearby highly composite N's. When N itself is prime, speed will be 6 times lower. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - complex function to be transformed N - problem size OUTPUT PARAMETERS A - DFT of a input array, array[0..N-1] A_out[j] = SUM(A_in[k]*exp(-2*pi*sqrt(-1)*j*k/N), k = 0..N-1) -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fftc1d(complex_1d_array &a, const ae_int_t n) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftc1d(const_cast(a.c_ptr()), n, &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex FFT. Array size N may be arbitrary number (composite or prime). Composite N's are handled with cache-oblivious variation of a Cooley-Tukey algorithm. Small prime-factors are transformed using hard coded codelets (similar to FFTW codelets, but without low-level optimization), large prime-factors are handled with Bluestein's algorithm. Fastests transforms are for smooth N's (prime factors are 2, 3, 5 only), most fast for powers of 2. When N have prime factors larger than these, but orders of magnitude smaller than N, computations will be about 4 times slower than for nearby highly composite N's. When N itself is prime, speed will be 6 times lower. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - complex function to be transformed N - problem size OUTPUT PARAMETERS A - DFT of a input array, array[0..N-1] A_out[j] = SUM(A_in[k]*exp(-2*pi*sqrt(-1)*j*k/N), k = 0..N-1) -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fftc1d(complex_1d_array &a) { alglib_impl::ae_state _alglib_env_state; ae_int_t n; n = a.length(); alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftc1d(const_cast(a.c_ptr()), n, &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex inverse FFT. Array size N may be arbitrary number (composite or prime). Algorithm has O(N*logN) complexity for any N (composite or prime). See FFTC1D() description for more information about algorithm performance. INPUT PARAMETERS A - array[0..N-1] - complex array to be transformed N - problem size OUTPUT PARAMETERS A - inverse DFT of a input array, array[0..N-1] A_out[j] = SUM(A_in[k]/N*exp(+2*pi*sqrt(-1)*j*k/N), k = 0..N-1) -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fftc1dinv(complex_1d_array &a, const ae_int_t n) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftc1dinv(const_cast(a.c_ptr()), n, &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex inverse FFT. Array size N may be arbitrary number (composite or prime). Algorithm has O(N*logN) complexity for any N (composite or prime). See FFTC1D() description for more information about algorithm performance. INPUT PARAMETERS A - array[0..N-1] - complex array to be transformed N - problem size OUTPUT PARAMETERS A - inverse DFT of a input array, array[0..N-1] A_out[j] = SUM(A_in[k]/N*exp(+2*pi*sqrt(-1)*j*k/N), k = 0..N-1) -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fftc1dinv(complex_1d_array &a) { alglib_impl::ae_state _alglib_env_state; ae_int_t n; n = a.length(); alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftc1dinv(const_cast(a.c_ptr()), n, &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real FFT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS F - DFT of a input array, array[0..N-1] F[j] = SUM(A[k]*exp(-2*pi*sqrt(-1)*j*k/N), k = 0..N-1) NOTE: F[] satisfies symmetry property F[k] = conj(F[N-k]), so just one half of array is usually needed. But for convinience subroutine returns full complex array (with frequencies above N/2), so its result may be used by other FFT-related subroutines. -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1d(const real_1d_array &a, const ae_int_t n, complex_1d_array &f) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftr1d(const_cast(a.c_ptr()), n, const_cast(f.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real FFT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS F - DFT of a input array, array[0..N-1] F[j] = SUM(A[k]*exp(-2*pi*sqrt(-1)*j*k/N), k = 0..N-1) NOTE: F[] satisfies symmetry property F[k] = conj(F[N-k]), so just one half of array is usually needed. But for convinience subroutine returns full complex array (with frequencies above N/2), so its result may be used by other FFT-related subroutines. -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1d(const real_1d_array &a, complex_1d_array &f) { alglib_impl::ae_state _alglib_env_state; ae_int_t n; n = a.length(); alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftr1d(const_cast(a.c_ptr()), n, const_cast(f.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real inverse FFT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS F - array[0..floor(N/2)] - frequencies from forward real FFT N - problem size OUTPUT PARAMETERS A - inverse DFT of a input array, array[0..N-1] NOTE: F[] should satisfy symmetry property F[k] = conj(F[N-k]), so just one half of frequencies array is needed - elements from 0 to floor(N/2). F[0] is ALWAYS real. If N is even F[floor(N/2)] is real too. If N is odd, then F[floor(N/2)] has no special properties. Relying on properties noted above, FFTR1DInv subroutine uses only elements from 0th to floor(N/2)-th. It ignores imaginary part of F[0], and in case N is even it ignores imaginary part of F[floor(N/2)] too. When you call this function using full arguments list - "FFTR1DInv(F,N,A)" - you can pass either either frequencies array with N elements or reduced array with roughly N/2 elements - subroutine will successfully transform both. If you call this function using reduced arguments list - "FFTR1DInv(F,A)" - you must pass FULL array with N elements (although higher N/2 are still not used) because array size is used to automatically determine FFT length -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1dinv(const complex_1d_array &f, const ae_int_t n, real_1d_array &a) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftr1dinv(const_cast(f.c_ptr()), n, const_cast(a.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real inverse FFT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS F - array[0..floor(N/2)] - frequencies from forward real FFT N - problem size OUTPUT PARAMETERS A - inverse DFT of a input array, array[0..N-1] NOTE: F[] should satisfy symmetry property F[k] = conj(F[N-k]), so just one half of frequencies array is needed - elements from 0 to floor(N/2). F[0] is ALWAYS real. If N is even F[floor(N/2)] is real too. If N is odd, then F[floor(N/2)] has no special properties. Relying on properties noted above, FFTR1DInv subroutine uses only elements from 0th to floor(N/2)-th. It ignores imaginary part of F[0], and in case N is even it ignores imaginary part of F[floor(N/2)] too. When you call this function using full arguments list - "FFTR1DInv(F,N,A)" - you can pass either either frequencies array with N elements or reduced array with roughly N/2 elements - subroutine will successfully transform both. If you call this function using reduced arguments list - "FFTR1DInv(F,A)" - you must pass FULL array with N elements (although higher N/2 are still not used) because array size is used to automatically determine FFT length -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1dinv(const complex_1d_array &f, real_1d_array &a) { alglib_impl::ae_state _alglib_env_state; ae_int_t n; n = f.length(); alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fftr1dinv(const_cast(f.c_ptr()), n, const_cast(a.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex convolution. For given A/B returns conv(A,B) (non-circular). Subroutine can automatically choose between three implementations: straightforward O(M*N) formula for very small N (or M), overlap-add algorithm for cases where max(M,N) is significantly larger than min(M,N), but O(M*N) algorithm is too slow, and general FFT-based formula for cases where two previois algorithms are too slow. Algorithm has max(M,N)*log(max(M,N)) complexity for any M/N. INPUT PARAMETERS A - array[0..M-1] - complex function to be transformed M - problem size B - array[0..N-1] - complex function to be transformed N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..N+M-2]. NOTE: It is assumed that A is zero at T<0, B is zero too. If one or both functions have non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1d(const complex_1d_array &a, const ae_int_t m, const complex_1d_array &b, const ae_int_t n, complex_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convc1d(const_cast(a.c_ptr()), m, const_cast(b.c_ptr()), n, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex non-circular deconvolution (inverse of ConvC1D()). Algorithm has M*log(M)) complexity for any M (composite or prime). INPUT PARAMETERS A - array[0..M-1] - convolved signal, A = conv(R, B) M - convolved signal length B - array[0..N-1] - response N - response length, N<=M OUTPUT PARAMETERS R - deconvolved signal. array[0..M-N]. NOTE: deconvolution is unstable process and may result in division by zero (if your response function is degenerate, i.e. has zero Fourier coefficient). NOTE: It is assumed that A is zero at T<0, B is zero too. If one or both functions have non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1dinv(const complex_1d_array &a, const ae_int_t m, const complex_1d_array &b, const ae_int_t n, complex_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convc1dinv(const_cast(a.c_ptr()), m, const_cast(b.c_ptr()), n, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional circular complex convolution. For given S/R returns conv(S,R) (circular). Algorithm has linearithmic complexity for any M/N. IMPORTANT: normal convolution is commutative, i.e. it is symmetric - conv(A,B)=conv(B,A). Cyclic convolution IS NOT. One function - S - is a signal, periodic function, and another - R - is a response, non-periodic function with limited length. INPUT PARAMETERS S - array[0..M-1] - complex periodic signal M - problem size B - array[0..N-1] - complex non-periodic response N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1dcircular(const complex_1d_array &s, const ae_int_t m, const complex_1d_array &r, const ae_int_t n, complex_1d_array &c) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convc1dcircular(const_cast(s.c_ptr()), m, const_cast(r.c_ptr()), n, const_cast(c.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional circular complex deconvolution (inverse of ConvC1DCircular()). Algorithm has M*log(M)) complexity for any M (composite or prime). INPUT PARAMETERS A - array[0..M-1] - convolved periodic signal, A = conv(R, B) M - convolved signal length B - array[0..N-1] - non-periodic response N - response length OUTPUT PARAMETERS R - deconvolved signal. array[0..M-1]. NOTE: deconvolution is unstable process and may result in division by zero (if your response function is degenerate, i.e. has zero Fourier coefficient). NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1dcircularinv(const complex_1d_array &a, const ae_int_t m, const complex_1d_array &b, const ae_int_t n, complex_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convc1dcircularinv(const_cast(a.c_ptr()), m, const_cast(b.c_ptr()), n, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real convolution. Analogous to ConvC1D(), see ConvC1D() comments for more details. INPUT PARAMETERS A - array[0..M-1] - real function to be transformed M - problem size B - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..N+M-2]. NOTE: It is assumed that A is zero at T<0, B is zero too. If one or both functions have non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1d(const real_1d_array &a, const ae_int_t m, const real_1d_array &b, const ae_int_t n, real_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convr1d(const_cast(a.c_ptr()), m, const_cast(b.c_ptr()), n, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real deconvolution (inverse of ConvC1D()). Algorithm has M*log(M)) complexity for any M (composite or prime). INPUT PARAMETERS A - array[0..M-1] - convolved signal, A = conv(R, B) M - convolved signal length B - array[0..N-1] - response N - response length, N<=M OUTPUT PARAMETERS R - deconvolved signal. array[0..M-N]. NOTE: deconvolution is unstable process and may result in division by zero (if your response function is degenerate, i.e. has zero Fourier coefficient). NOTE: It is assumed that A is zero at T<0, B is zero too. If one or both functions have non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1dinv(const real_1d_array &a, const ae_int_t m, const real_1d_array &b, const ae_int_t n, real_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convr1dinv(const_cast(a.c_ptr()), m, const_cast(b.c_ptr()), n, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional circular real convolution. Analogous to ConvC1DCircular(), see ConvC1DCircular() comments for more details. INPUT PARAMETERS S - array[0..M-1] - real signal M - problem size B - array[0..N-1] - real response N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1dcircular(const real_1d_array &s, const ae_int_t m, const real_1d_array &r, const ae_int_t n, real_1d_array &c) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convr1dcircular(const_cast(s.c_ptr()), m, const_cast(r.c_ptr()), n, const_cast(c.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex deconvolution (inverse of ConvC1D()). Algorithm has M*log(M)) complexity for any M (composite or prime). INPUT PARAMETERS A - array[0..M-1] - convolved signal, A = conv(R, B) M - convolved signal length B - array[0..N-1] - response N - response length OUTPUT PARAMETERS R - deconvolved signal. array[0..M-N]. NOTE: deconvolution is unstable process and may result in division by zero (if your response function is degenerate, i.e. has zero Fourier coefficient). NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1dcircularinv(const real_1d_array &a, const ae_int_t m, const real_1d_array &b, const ae_int_t n, real_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::convr1dcircularinv(const_cast(a.c_ptr()), m, const_cast(b.c_ptr()), n, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional complex cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (non-circular). Correlation is calculated using reduction to convolution. Algorithm with max(N,N)*log(max(N,N)) complexity is used (see ConvC1D() for more info about performance). IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrC1D(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - complex function to be transformed, signal containing pattern N - problem size Pattern - array[0..M-1] - complex function to be transformed, pattern to search withing signal M - problem size OUTPUT PARAMETERS R - cross-correlation, array[0..N+M-2]: * positive lags are stored in R[0..N-1], R[i] = sum(conj(pattern[j])*signal[i+j] * negative lags are stored in R[N..N+M-2], R[N+M-1-i] = sum(conj(pattern[j])*signal[-i+j] NOTE: It is assumed that pattern domain is [0..M-1]. If Pattern is non-zero on [-K..M-1], you can still use this subroutine, just shift result by K. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrc1d(const complex_1d_array &signal, const ae_int_t n, const complex_1d_array &pattern, const ae_int_t m, complex_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::corrc1d(const_cast(signal.c_ptr()), n, const_cast(pattern.c_ptr()), m, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional circular complex cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (circular). Algorithm has linearithmic complexity for any M/N. IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrC1DCircular(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - complex function to be transformed, periodic signal containing pattern N - problem size Pattern - array[0..M-1] - complex function to be transformed, non-periodic pattern to search withing signal M - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrc1dcircular(const complex_1d_array &signal, const ae_int_t m, const complex_1d_array &pattern, const ae_int_t n, complex_1d_array &c) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::corrc1dcircular(const_cast(signal.c_ptr()), m, const_cast(pattern.c_ptr()), n, const_cast(c.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional real cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (non-circular). Correlation is calculated using reduction to convolution. Algorithm with max(N,N)*log(max(N,N)) complexity is used (see ConvC1D() for more info about performance). IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrR1D(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - real function to be transformed, signal containing pattern N - problem size Pattern - array[0..M-1] - real function to be transformed, pattern to search withing signal M - problem size OUTPUT PARAMETERS R - cross-correlation, array[0..N+M-2]: * positive lags are stored in R[0..N-1], R[i] = sum(pattern[j]*signal[i+j] * negative lags are stored in R[N..N+M-2], R[N+M-1-i] = sum(pattern[j]*signal[-i+j] NOTE: It is assumed that pattern domain is [0..M-1]. If Pattern is non-zero on [-K..M-1], you can still use this subroutine, just shift result by K. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrr1d(const real_1d_array &signal, const ae_int_t n, const real_1d_array &pattern, const ae_int_t m, real_1d_array &r) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::corrr1d(const_cast(signal.c_ptr()), n, const_cast(pattern.c_ptr()), m, const_cast(r.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional circular real cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (circular). Algorithm has linearithmic complexity for any M/N. IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrR1DCircular(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - real function to be transformed, periodic signal containing pattern N - problem size Pattern - array[0..M-1] - real function to be transformed, non-periodic pattern to search withing signal M - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrr1dcircular(const real_1d_array &signal, const ae_int_t m, const real_1d_array &pattern, const ae_int_t n, real_1d_array &c) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::corrr1dcircular(const_cast(signal.c_ptr()), m, const_cast(pattern.c_ptr()), n, const_cast(c.c_ptr()), &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional Fast Hartley Transform. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS A - FHT of a input array, array[0..N-1], A_out[k] = sum(A_in[j]*(cos(2*pi*j*k/N)+sin(2*pi*j*k/N)), j=0..N-1) -- ALGLIB -- Copyright 04.06.2009 by Bochkanov Sergey *************************************************************************/ void fhtr1d(real_1d_array &a, const ae_int_t n) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fhtr1d(const_cast(a.c_ptr()), n, &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } /************************************************************************* 1-dimensional inverse FHT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - complex array to be transformed N - problem size OUTPUT PARAMETERS A - inverse FHT of a input array, array[0..N-1] -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fhtr1dinv(real_1d_array &a, const ae_int_t n) { alglib_impl::ae_state _alglib_env_state; alglib_impl::ae_state_init(&_alglib_env_state); try { alglib_impl::fhtr1dinv(const_cast(a.c_ptr()), n, &_alglib_env_state); alglib_impl::ae_state_clear(&_alglib_env_state); return; } catch(alglib_impl::ae_error_type) { throw ap_error(_alglib_env_state.error_msg); } } } ///////////////////////////////////////////////////////////////////////// // // THIS SECTION CONTAINS IMPLEMENTATION OF COMPUTATIONAL CORE // ///////////////////////////////////////////////////////////////////////// namespace alglib_impl { /************************************************************************* 1-dimensional complex FFT. Array size N may be arbitrary number (composite or prime). Composite N's are handled with cache-oblivious variation of a Cooley-Tukey algorithm. Small prime-factors are transformed using hard coded codelets (similar to FFTW codelets, but without low-level optimization), large prime-factors are handled with Bluestein's algorithm. Fastests transforms are for smooth N's (prime factors are 2, 3, 5 only), most fast for powers of 2. When N have prime factors larger than these, but orders of magnitude smaller than N, computations will be about 4 times slower than for nearby highly composite N's. When N itself is prime, speed will be 6 times lower. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - complex function to be transformed N - problem size OUTPUT PARAMETERS A - DFT of a input array, array[0..N-1] A_out[j] = SUM(A_in[k]*exp(-2*pi*sqrt(-1)*j*k/N), k = 0..N-1) -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fftc1d(/* Complex */ ae_vector* a, ae_int_t n, ae_state *_state) { ae_frame _frame_block; fasttransformplan plan; ae_int_t i; ae_vector buf; ae_frame_make(_state, &_frame_block); _fasttransformplan_init(&plan, _state, ae_true); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); ae_assert(n>0, "FFTC1D: incorrect N!", _state); ae_assert(a->cnt>=n, "FFTC1D: Length(A)ptr.p_complex[i].x; buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y; } /* * Generate plan and execute it. * * Plan is a combination of a successive factorizations of N and * precomputed data. It is much like a FFTW plan, but is not stored * between subroutine calls and is much simpler. */ ftcomplexfftplan(n, 1, &plan, _state); ftapplyplan(&plan, &buf, 0, 1, _state); /* * result */ for(i=0; i<=n-1; i++) { a->ptr.p_complex[i].x = buf.ptr.p_double[2*i+0]; a->ptr.p_complex[i].y = buf.ptr.p_double[2*i+1]; } ae_frame_leave(_state); } /************************************************************************* 1-dimensional complex inverse FFT. Array size N may be arbitrary number (composite or prime). Algorithm has O(N*logN) complexity for any N (composite or prime). See FFTC1D() description for more information about algorithm performance. INPUT PARAMETERS A - array[0..N-1] - complex array to be transformed N - problem size OUTPUT PARAMETERS A - inverse DFT of a input array, array[0..N-1] A_out[j] = SUM(A_in[k]/N*exp(+2*pi*sqrt(-1)*j*k/N), k = 0..N-1) -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fftc1dinv(/* Complex */ ae_vector* a, ae_int_t n, ae_state *_state) { ae_int_t i; ae_assert(n>0, "FFTC1DInv: incorrect N!", _state); ae_assert(a->cnt>=n, "FFTC1DInv: Length(A)ptr.p_complex[i].y = -a->ptr.p_complex[i].y; } fftc1d(a, n, _state); for(i=0; i<=n-1; i++) { a->ptr.p_complex[i].x = a->ptr.p_complex[i].x/n; a->ptr.p_complex[i].y = -a->ptr.p_complex[i].y/n; } } /************************************************************************* 1-dimensional real FFT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS F - DFT of a input array, array[0..N-1] F[j] = SUM(A[k]*exp(-2*pi*sqrt(-1)*j*k/N), k = 0..N-1) NOTE: F[] satisfies symmetry property F[k] = conj(F[N-k]), so just one half of array is usually needed. But for convinience subroutine returns full complex array (with frequencies above N/2), so its result may be used by other FFT-related subroutines. -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1d(/* Real */ ae_vector* a, ae_int_t n, /* Complex */ ae_vector* f, ae_state *_state) { ae_frame _frame_block; ae_int_t i; ae_int_t n2; ae_int_t idx; ae_complex hn; ae_complex hmnc; ae_complex v; ae_vector buf; fasttransformplan plan; ae_frame_make(_state, &_frame_block); ae_vector_clear(f); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); _fasttransformplan_init(&plan, _state, ae_true); ae_assert(n>0, "FFTR1D: incorrect N!", _state); ae_assert(a->cnt>=n, "FFTR1D: Length(A)ptr.p_complex[0] = ae_complex_from_d(a->ptr.p_double[0]); ae_frame_leave(_state); return; } if( n==2 ) { ae_vector_set_length(f, 2, _state); f->ptr.p_complex[0].x = a->ptr.p_double[0]+a->ptr.p_double[1]; f->ptr.p_complex[0].y = 0; f->ptr.p_complex[1].x = a->ptr.p_double[0]-a->ptr.p_double[1]; f->ptr.p_complex[1].y = 0; ae_frame_leave(_state); return; } /* * Choose between odd-size and even-size FFTs */ if( n%2==0 ) { /* * even-size real FFT, use reduction to the complex task */ n2 = n/2; ae_vector_set_length(&buf, n, _state); ae_v_move(&buf.ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,n-1)); ftcomplexfftplan(n2, 1, &plan, _state); ftapplyplan(&plan, &buf, 0, 1, _state); ae_vector_set_length(f, n, _state); for(i=0; i<=n2; i++) { idx = 2*(i%n2); hn.x = buf.ptr.p_double[idx+0]; hn.y = buf.ptr.p_double[idx+1]; idx = 2*((n2-i)%n2); hmnc.x = buf.ptr.p_double[idx+0]; hmnc.y = -buf.ptr.p_double[idx+1]; v.x = -ae_sin(-2*ae_pi*i/n, _state); v.y = ae_cos(-2*ae_pi*i/n, _state); f->ptr.p_complex[i] = ae_c_sub(ae_c_add(hn,hmnc),ae_c_mul(v,ae_c_sub(hn,hmnc))); f->ptr.p_complex[i].x = 0.5*f->ptr.p_complex[i].x; f->ptr.p_complex[i].y = 0.5*f->ptr.p_complex[i].y; } for(i=n2+1; i<=n-1; i++) { f->ptr.p_complex[i] = ae_c_conj(f->ptr.p_complex[n-i], _state); } } else { /* * use complex FFT */ ae_vector_set_length(f, n, _state); for(i=0; i<=n-1; i++) { f->ptr.p_complex[i] = ae_complex_from_d(a->ptr.p_double[i]); } fftc1d(f, n, _state); } ae_frame_leave(_state); } /************************************************************************* 1-dimensional real inverse FFT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS F - array[0..floor(N/2)] - frequencies from forward real FFT N - problem size OUTPUT PARAMETERS A - inverse DFT of a input array, array[0..N-1] NOTE: F[] should satisfy symmetry property F[k] = conj(F[N-k]), so just one half of frequencies array is needed - elements from 0 to floor(N/2). F[0] is ALWAYS real. If N is even F[floor(N/2)] is real too. If N is odd, then F[floor(N/2)] has no special properties. Relying on properties noted above, FFTR1DInv subroutine uses only elements from 0th to floor(N/2)-th. It ignores imaginary part of F[0], and in case N is even it ignores imaginary part of F[floor(N/2)] too. When you call this function using full arguments list - "FFTR1DInv(F,N,A)" - you can pass either either frequencies array with N elements or reduced array with roughly N/2 elements - subroutine will successfully transform both. If you call this function using reduced arguments list - "FFTR1DInv(F,A)" - you must pass FULL array with N elements (although higher N/2 are still not used) because array size is used to automatically determine FFT length -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1dinv(/* Complex */ ae_vector* f, ae_int_t n, /* Real */ ae_vector* a, ae_state *_state) { ae_frame _frame_block; ae_int_t i; ae_vector h; ae_vector fh; ae_frame_make(_state, &_frame_block); ae_vector_clear(a); ae_vector_init(&h, 0, DT_REAL, _state, ae_true); ae_vector_init(&fh, 0, DT_COMPLEX, _state, ae_true); ae_assert(n>0, "FFTR1DInv: incorrect N!", _state); ae_assert(f->cnt>=ae_ifloor((double)n/(double)2, _state)+1, "FFTR1DInv: Length(F)ptr.p_complex[0].x, _state), "FFTR1DInv: F contains infinite or NAN values!", _state); for(i=1; i<=ae_ifloor((double)n/(double)2, _state)-1; i++) { ae_assert(ae_isfinite(f->ptr.p_complex[i].x, _state)&&ae_isfinite(f->ptr.p_complex[i].y, _state), "FFTR1DInv: F contains infinite or NAN values!", _state); } ae_assert(ae_isfinite(f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].x, _state), "FFTR1DInv: F contains infinite or NAN values!", _state); if( n%2!=0 ) { ae_assert(ae_isfinite(f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].y, _state), "FFTR1DInv: F contains infinite or NAN values!", _state); } /* * Special case: N=1, FFT is just identity transform. * After this block we assume that N is strictly greater than 1. */ if( n==1 ) { ae_vector_set_length(a, 1, _state); a->ptr.p_double[0] = f->ptr.p_complex[0].x; ae_frame_leave(_state); return; } /* * inverse real FFT is reduced to the inverse real FHT, * which is reduced to the forward real FHT, * which is reduced to the forward real FFT. * * Don't worry, it is really compact and efficient reduction :) */ ae_vector_set_length(&h, n, _state); ae_vector_set_length(a, n, _state); h.ptr.p_double[0] = f->ptr.p_complex[0].x; for(i=1; i<=ae_ifloor((double)n/(double)2, _state)-1; i++) { h.ptr.p_double[i] = f->ptr.p_complex[i].x-f->ptr.p_complex[i].y; h.ptr.p_double[n-i] = f->ptr.p_complex[i].x+f->ptr.p_complex[i].y; } if( n%2==0 ) { h.ptr.p_double[ae_ifloor((double)n/(double)2, _state)] = f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].x; } else { h.ptr.p_double[ae_ifloor((double)n/(double)2, _state)] = f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].x-f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].y; h.ptr.p_double[ae_ifloor((double)n/(double)2, _state)+1] = f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].x+f->ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].y; } fftr1d(&h, n, &fh, _state); for(i=0; i<=n-1; i++) { a->ptr.p_double[i] = (fh.ptr.p_complex[i].x-fh.ptr.p_complex[i].y)/n; } ae_frame_leave(_state); } /************************************************************************* Internal subroutine. Never call it directly! -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1dinternaleven(/* Real */ ae_vector* a, ae_int_t n, /* Real */ ae_vector* buf, fasttransformplan* plan, ae_state *_state) { double x; double y; ae_int_t i; ae_int_t n2; ae_int_t idx; ae_complex hn; ae_complex hmnc; ae_complex v; ae_assert(n>0&&n%2==0, "FFTR1DEvenInplace: incorrect N!", _state); /* * Special cases: * * N=2 * * After this block we assume that N is strictly greater than 2 */ if( n==2 ) { x = a->ptr.p_double[0]+a->ptr.p_double[1]; y = a->ptr.p_double[0]-a->ptr.p_double[1]; a->ptr.p_double[0] = x; a->ptr.p_double[1] = y; return; } /* * even-size real FFT, use reduction to the complex task */ n2 = n/2; ae_v_move(&buf->ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,n-1)); ftapplyplan(plan, buf, 0, 1, _state); a->ptr.p_double[0] = buf->ptr.p_double[0]+buf->ptr.p_double[1]; for(i=1; i<=n2-1; i++) { idx = 2*(i%n2); hn.x = buf->ptr.p_double[idx+0]; hn.y = buf->ptr.p_double[idx+1]; idx = 2*(n2-i); hmnc.x = buf->ptr.p_double[idx+0]; hmnc.y = -buf->ptr.p_double[idx+1]; v.x = -ae_sin(-2*ae_pi*i/n, _state); v.y = ae_cos(-2*ae_pi*i/n, _state); v = ae_c_sub(ae_c_add(hn,hmnc),ae_c_mul(v,ae_c_sub(hn,hmnc))); a->ptr.p_double[2*i+0] = 0.5*v.x; a->ptr.p_double[2*i+1] = 0.5*v.y; } a->ptr.p_double[1] = buf->ptr.p_double[0]-buf->ptr.p_double[1]; } /************************************************************************* Internal subroutine. Never call it directly! -- ALGLIB -- Copyright 01.06.2009 by Bochkanov Sergey *************************************************************************/ void fftr1dinvinternaleven(/* Real */ ae_vector* a, ae_int_t n, /* Real */ ae_vector* buf, fasttransformplan* plan, ae_state *_state) { double x; double y; double t; ae_int_t i; ae_int_t n2; ae_assert(n>0&&n%2==0, "FFTR1DInvInternalEven: incorrect N!", _state); /* * Special cases: * * N=2 * * After this block we assume that N is strictly greater than 2 */ if( n==2 ) { x = 0.5*(a->ptr.p_double[0]+a->ptr.p_double[1]); y = 0.5*(a->ptr.p_double[0]-a->ptr.p_double[1]); a->ptr.p_double[0] = x; a->ptr.p_double[1] = y; return; } /* * inverse real FFT is reduced to the inverse real FHT, * which is reduced to the forward real FHT, * which is reduced to the forward real FFT. * * Don't worry, it is really compact and efficient reduction :) */ n2 = n/2; buf->ptr.p_double[0] = a->ptr.p_double[0]; for(i=1; i<=n2-1; i++) { x = a->ptr.p_double[2*i+0]; y = a->ptr.p_double[2*i+1]; buf->ptr.p_double[i] = x-y; buf->ptr.p_double[n-i] = x+y; } buf->ptr.p_double[n2] = a->ptr.p_double[1]; fftr1dinternaleven(buf, n, a, plan, _state); a->ptr.p_double[0] = buf->ptr.p_double[0]/n; t = (double)1/(double)n; for(i=1; i<=n2-1; i++) { x = buf->ptr.p_double[2*i+0]; y = buf->ptr.p_double[2*i+1]; a->ptr.p_double[i] = t*(x-y); a->ptr.p_double[n-i] = t*(x+y); } a->ptr.p_double[n2] = buf->ptr.p_double[1]/n; } /************************************************************************* 1-dimensional complex convolution. For given A/B returns conv(A,B) (non-circular). Subroutine can automatically choose between three implementations: straightforward O(M*N) formula for very small N (or M), overlap-add algorithm for cases where max(M,N) is significantly larger than min(M,N), but O(M*N) algorithm is too slow, and general FFT-based formula for cases where two previois algorithms are too slow. Algorithm has max(M,N)*log(max(M,N)) complexity for any M/N. INPUT PARAMETERS A - array[0..M-1] - complex function to be transformed M - problem size B - array[0..N-1] - complex function to be transformed N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..N+M-2]. NOTE: It is assumed that A is zero at T<0, B is zero too. If one or both functions have non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1d(/* Complex */ ae_vector* a, ae_int_t m, /* Complex */ ae_vector* b, ae_int_t n, /* Complex */ ae_vector* r, ae_state *_state) { ae_vector_clear(r); ae_assert(n>0&&m>0, "ConvC1D: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer that B. */ if( m0&&m>0)&&n<=m, "ConvC1DInv: incorrect N or M!", _state); p = ftbasefindsmooth(m, _state); ftcomplexfftplan(p, 1, &plan, _state); ae_vector_set_length(&buf, 2*p, _state); for(i=0; i<=m-1; i++) { buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x; buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y; } for(i=m; i<=p-1; i++) { buf.ptr.p_double[2*i+0] = 0; buf.ptr.p_double[2*i+1] = 0; } ae_vector_set_length(&buf2, 2*p, _state); for(i=0; i<=n-1; i++) { buf2.ptr.p_double[2*i+0] = b->ptr.p_complex[i].x; buf2.ptr.p_double[2*i+1] = b->ptr.p_complex[i].y; } for(i=n; i<=p-1; i++) { buf2.ptr.p_double[2*i+0] = 0; buf2.ptr.p_double[2*i+1] = 0; } ftapplyplan(&plan, &buf, 0, 1, _state); ftapplyplan(&plan, &buf2, 0, 1, _state); for(i=0; i<=p-1; i++) { c1.x = buf.ptr.p_double[2*i+0]; c1.y = buf.ptr.p_double[2*i+1]; c2.x = buf2.ptr.p_double[2*i+0]; c2.y = buf2.ptr.p_double[2*i+1]; c3 = ae_c_div(c1,c2); buf.ptr.p_double[2*i+0] = c3.x; buf.ptr.p_double[2*i+1] = -c3.y; } ftapplyplan(&plan, &buf, 0, 1, _state); t = (double)1/(double)p; ae_vector_set_length(r, m-n+1, _state); for(i=0; i<=m-n; i++) { r->ptr.p_complex[i].x = t*buf.ptr.p_double[2*i+0]; r->ptr.p_complex[i].y = -t*buf.ptr.p_double[2*i+1]; } ae_frame_leave(_state); } /************************************************************************* 1-dimensional circular complex convolution. For given S/R returns conv(S,R) (circular). Algorithm has linearithmic complexity for any M/N. IMPORTANT: normal convolution is commutative, i.e. it is symmetric - conv(A,B)=conv(B,A). Cyclic convolution IS NOT. One function - S - is a signal, periodic function, and another - R - is a response, non-periodic function with limited length. INPUT PARAMETERS S - array[0..M-1] - complex periodic signal M - problem size B - array[0..N-1] - complex non-periodic response N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1dcircular(/* Complex */ ae_vector* s, ae_int_t m, /* Complex */ ae_vector* r, ae_int_t n, /* Complex */ ae_vector* c, ae_state *_state) { ae_frame _frame_block; ae_vector buf; ae_int_t i1; ae_int_t i2; ae_int_t j2; ae_frame_make(_state, &_frame_block); ae_vector_clear(c); ae_vector_init(&buf, 0, DT_COMPLEX, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DCircular: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer (at least - not shorter) that B. */ if( mptr.p_complex[i1], 1, "N", ae_v_len(0,j2)); i1 = i1+m; } convc1dcircular(s, m, &buf, m, c, _state); ae_frame_leave(_state); return; } convc1dx(s, m, r, n, ae_true, -1, 0, c, _state); ae_frame_leave(_state); } /************************************************************************* 1-dimensional circular complex deconvolution (inverse of ConvC1DCircular()). Algorithm has M*log(M)) complexity for any M (composite or prime). INPUT PARAMETERS A - array[0..M-1] - convolved periodic signal, A = conv(R, B) M - convolved signal length B - array[0..N-1] - non-periodic response N - response length OUTPUT PARAMETERS R - deconvolved signal. array[0..M-1]. NOTE: deconvolution is unstable process and may result in division by zero (if your response function is degenerate, i.e. has zero Fourier coefficient). NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1dcircularinv(/* Complex */ ae_vector* a, ae_int_t m, /* Complex */ ae_vector* b, ae_int_t n, /* Complex */ ae_vector* r, ae_state *_state) { ae_frame _frame_block; ae_int_t i; ae_int_t i1; ae_int_t i2; ae_int_t j2; ae_vector buf; ae_vector buf2; ae_vector cbuf; fasttransformplan plan; ae_complex c1; ae_complex c2; ae_complex c3; double t; ae_frame_make(_state, &_frame_block); ae_vector_clear(r); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); ae_vector_init(&buf2, 0, DT_REAL, _state, ae_true); ae_vector_init(&cbuf, 0, DT_COMPLEX, _state, ae_true); _fasttransformplan_init(&plan, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DCircularInv: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer (at least - not shorter) that B. */ if( mptr.p_complex[i1], 1, "N", ae_v_len(0,j2)); i1 = i1+m; } convc1dcircularinv(a, m, &cbuf, m, r, _state); ae_frame_leave(_state); return; } /* * Task is normalized */ ftcomplexfftplan(m, 1, &plan, _state); ae_vector_set_length(&buf, 2*m, _state); for(i=0; i<=m-1; i++) { buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x; buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y; } ae_vector_set_length(&buf2, 2*m, _state); for(i=0; i<=n-1; i++) { buf2.ptr.p_double[2*i+0] = b->ptr.p_complex[i].x; buf2.ptr.p_double[2*i+1] = b->ptr.p_complex[i].y; } for(i=n; i<=m-1; i++) { buf2.ptr.p_double[2*i+0] = 0; buf2.ptr.p_double[2*i+1] = 0; } ftapplyplan(&plan, &buf, 0, 1, _state); ftapplyplan(&plan, &buf2, 0, 1, _state); for(i=0; i<=m-1; i++) { c1.x = buf.ptr.p_double[2*i+0]; c1.y = buf.ptr.p_double[2*i+1]; c2.x = buf2.ptr.p_double[2*i+0]; c2.y = buf2.ptr.p_double[2*i+1]; c3 = ae_c_div(c1,c2); buf.ptr.p_double[2*i+0] = c3.x; buf.ptr.p_double[2*i+1] = -c3.y; } ftapplyplan(&plan, &buf, 0, 1, _state); t = (double)1/(double)m; ae_vector_set_length(r, m, _state); for(i=0; i<=m-1; i++) { r->ptr.p_complex[i].x = t*buf.ptr.p_double[2*i+0]; r->ptr.p_complex[i].y = -t*buf.ptr.p_double[2*i+1]; } ae_frame_leave(_state); } /************************************************************************* 1-dimensional real convolution. Analogous to ConvC1D(), see ConvC1D() comments for more details. INPUT PARAMETERS A - array[0..M-1] - real function to be transformed M - problem size B - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..N+M-2]. NOTE: It is assumed that A is zero at T<0, B is zero too. If one or both functions have non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1d(/* Real */ ae_vector* a, ae_int_t m, /* Real */ ae_vector* b, ae_int_t n, /* Real */ ae_vector* r, ae_state *_state) { ae_vector_clear(r); ae_assert(n>0&&m>0, "ConvR1D: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer that B. */ if( m0&&m>0)&&n<=m, "ConvR1DInv: incorrect N or M!", _state); p = ftbasefindsmootheven(m, _state); ae_vector_set_length(&buf, p, _state); ae_v_move(&buf.ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,m-1)); for(i=m; i<=p-1; i++) { buf.ptr.p_double[i] = 0; } ae_vector_set_length(&buf2, p, _state); ae_v_move(&buf2.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,n-1)); for(i=n; i<=p-1; i++) { buf2.ptr.p_double[i] = 0; } ae_vector_set_length(&buf3, p, _state); ftcomplexfftplan(p/2, 1, &plan, _state); fftr1dinternaleven(&buf, p, &buf3, &plan, _state); fftr1dinternaleven(&buf2, p, &buf3, &plan, _state); buf.ptr.p_double[0] = buf.ptr.p_double[0]/buf2.ptr.p_double[0]; buf.ptr.p_double[1] = buf.ptr.p_double[1]/buf2.ptr.p_double[1]; for(i=1; i<=p/2-1; i++) { c1.x = buf.ptr.p_double[2*i+0]; c1.y = buf.ptr.p_double[2*i+1]; c2.x = buf2.ptr.p_double[2*i+0]; c2.y = buf2.ptr.p_double[2*i+1]; c3 = ae_c_div(c1,c2); buf.ptr.p_double[2*i+0] = c3.x; buf.ptr.p_double[2*i+1] = c3.y; } fftr1dinvinternaleven(&buf, p, &buf3, &plan, _state); ae_vector_set_length(r, m-n+1, _state); ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-n)); ae_frame_leave(_state); } /************************************************************************* 1-dimensional circular real convolution. Analogous to ConvC1DCircular(), see ConvC1DCircular() comments for more details. INPUT PARAMETERS S - array[0..M-1] - real signal M - problem size B - array[0..N-1] - real response N - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1dcircular(/* Real */ ae_vector* s, ae_int_t m, /* Real */ ae_vector* r, ae_int_t n, /* Real */ ae_vector* c, ae_state *_state) { ae_frame _frame_block; ae_vector buf; ae_int_t i1; ae_int_t i2; ae_int_t j2; ae_frame_make(_state, &_frame_block); ae_vector_clear(c); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DCircular: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer (at least - not shorter) that B. */ if( mptr.p_double[i1], 1, ae_v_len(0,j2)); i1 = i1+m; } convr1dcircular(s, m, &buf, m, c, _state); ae_frame_leave(_state); return; } /* * reduce to usual convolution */ convr1dx(s, m, r, n, ae_true, -1, 0, c, _state); ae_frame_leave(_state); } /************************************************************************* 1-dimensional complex deconvolution (inverse of ConvC1D()). Algorithm has M*log(M)) complexity for any M (composite or prime). INPUT PARAMETERS A - array[0..M-1] - convolved signal, A = conv(R, B) M - convolved signal length B - array[0..N-1] - response N - response length OUTPUT PARAMETERS R - deconvolved signal. array[0..M-N]. NOTE: deconvolution is unstable process and may result in division by zero (if your response function is degenerate, i.e. has zero Fourier coefficient). NOTE: It is assumed that B is zero at T<0. If it has non-zero values at negative T's, you can still use this subroutine - just shift its result correspondingly. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1dcircularinv(/* Real */ ae_vector* a, ae_int_t m, /* Real */ ae_vector* b, ae_int_t n, /* Real */ ae_vector* r, ae_state *_state) { ae_frame _frame_block; ae_int_t i; ae_int_t i1; ae_int_t i2; ae_int_t j2; ae_vector buf; ae_vector buf2; ae_vector buf3; ae_vector cbuf; ae_vector cbuf2; fasttransformplan plan; ae_complex c1; ae_complex c2; ae_complex c3; ae_frame_make(_state, &_frame_block); ae_vector_clear(r); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); ae_vector_init(&buf2, 0, DT_REAL, _state, ae_true); ae_vector_init(&buf3, 0, DT_REAL, _state, ae_true); ae_vector_init(&cbuf, 0, DT_COMPLEX, _state, ae_true); ae_vector_init(&cbuf2, 0, DT_COMPLEX, _state, ae_true); _fasttransformplan_init(&plan, _state, ae_true); ae_assert(n>0&&m>0, "ConvR1DCircularInv: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer (at least - not shorter) that B. */ if( mptr.p_double[i1], 1, ae_v_len(0,j2)); i1 = i1+m; } convr1dcircularinv(a, m, &buf, m, r, _state); ae_frame_leave(_state); return; } /* * Task is normalized */ if( m%2==0 ) { /* * size is even, use fast even-size FFT */ ae_vector_set_length(&buf, m, _state); ae_v_move(&buf.ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,m-1)); ae_vector_set_length(&buf2, m, _state); ae_v_move(&buf2.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,n-1)); for(i=n; i<=m-1; i++) { buf2.ptr.p_double[i] = 0; } ae_vector_set_length(&buf3, m, _state); ftcomplexfftplan(m/2, 1, &plan, _state); fftr1dinternaleven(&buf, m, &buf3, &plan, _state); fftr1dinternaleven(&buf2, m, &buf3, &plan, _state); buf.ptr.p_double[0] = buf.ptr.p_double[0]/buf2.ptr.p_double[0]; buf.ptr.p_double[1] = buf.ptr.p_double[1]/buf2.ptr.p_double[1]; for(i=1; i<=m/2-1; i++) { c1.x = buf.ptr.p_double[2*i+0]; c1.y = buf.ptr.p_double[2*i+1]; c2.x = buf2.ptr.p_double[2*i+0]; c2.y = buf2.ptr.p_double[2*i+1]; c3 = ae_c_div(c1,c2); buf.ptr.p_double[2*i+0] = c3.x; buf.ptr.p_double[2*i+1] = c3.y; } fftr1dinvinternaleven(&buf, m, &buf3, &plan, _state); ae_vector_set_length(r, m, _state); ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-1)); } else { /* * odd-size, use general real FFT */ fftr1d(a, m, &cbuf, _state); ae_vector_set_length(&buf2, m, _state); ae_v_move(&buf2.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,n-1)); for(i=n; i<=m-1; i++) { buf2.ptr.p_double[i] = 0; } fftr1d(&buf2, m, &cbuf2, _state); for(i=0; i<=ae_ifloor((double)m/(double)2, _state); i++) { cbuf.ptr.p_complex[i] = ae_c_div(cbuf.ptr.p_complex[i],cbuf2.ptr.p_complex[i]); } fftr1dinv(&cbuf, m, r, _state); } ae_frame_leave(_state); } /************************************************************************* 1-dimensional complex convolution. Extended subroutine which allows to choose convolution algorithm. Intended for internal use, ALGLIB users should call ConvC1D()/ConvC1DCircular(). INPUT PARAMETERS A - array[0..M-1] - complex function to be transformed M - problem size B - array[0..N-1] - complex function to be transformed N - problem size, N<=M Alg - algorithm type: *-2 auto-select Q for overlap-add *-1 auto-select algorithm and parameters * 0 straightforward formula for small N's * 1 general FFT-based code * 2 overlap-add with length Q Q - length for overlap-add OUTPUT PARAMETERS R - convolution: A*B. array[0..N+M-1]. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convc1dx(/* Complex */ ae_vector* a, ae_int_t m, /* Complex */ ae_vector* b, ae_int_t n, ae_bool circular, ae_int_t alg, ae_int_t q, /* Complex */ ae_vector* r, ae_state *_state) { ae_frame _frame_block; ae_int_t i; ae_int_t j; ae_int_t p; ae_int_t ptotal; ae_int_t i1; ae_int_t i2; ae_int_t j1; ae_int_t j2; ae_vector bbuf; ae_complex v; double ax; double ay; double bx; double by; double t; double tx; double ty; double flopcand; double flopbest; ae_int_t algbest; fasttransformplan plan; ae_vector buf; ae_vector buf2; ae_frame_make(_state, &_frame_block); ae_vector_clear(r); ae_vector_init(&bbuf, 0, DT_COMPLEX, _state, ae_true); _fasttransformplan_init(&plan, _state, ae_true); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); ae_vector_init(&buf2, 0, DT_REAL, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DX: incorrect N or M!", _state); ae_assert(n<=m, "ConvC1DX: Nptr.p_complex[0]; ae_v_cmovec(&r->ptr.p_complex[0], 1, &a->ptr.p_complex[0], 1, "N", ae_v_len(0,m-1), v); ae_frame_leave(_state); return; } /* * use straightforward formula */ if( circular ) { /* * circular convolution */ ae_vector_set_length(r, m, _state); v = b->ptr.p_complex[0]; ae_v_cmovec(&r->ptr.p_complex[0], 1, &a->ptr.p_complex[0], 1, "N", ae_v_len(0,m-1), v); for(i=1; i<=n-1; i++) { v = b->ptr.p_complex[i]; i1 = 0; i2 = i-1; j1 = m-i; j2 = m-1; ae_v_caddc(&r->ptr.p_complex[i1], 1, &a->ptr.p_complex[j1], 1, "N", ae_v_len(i1,i2), v); i1 = i; i2 = m-1; j1 = 0; j2 = m-i-1; ae_v_caddc(&r->ptr.p_complex[i1], 1, &a->ptr.p_complex[j1], 1, "N", ae_v_len(i1,i2), v); } } else { /* * non-circular convolution */ ae_vector_set_length(r, m+n-1, _state); for(i=0; i<=m+n-2; i++) { r->ptr.p_complex[i] = ae_complex_from_d(0); } for(i=0; i<=n-1; i++) { v = b->ptr.p_complex[i]; ae_v_caddc(&r->ptr.p_complex[i], 1, &a->ptr.p_complex[0], 1, "N", ae_v_len(i,i+m-1), v); } } ae_frame_leave(_state); return; } /* * general FFT-based code for * circular and non-circular convolutions. * * First, if convolution is circular, we test whether M is smooth or not. * If it is smooth, we just use M-length FFT to calculate convolution. * If it is not, we calculate non-circular convolution and wrap it arount. * * IF convolution is non-circular, we use zero-padding + FFT. */ if( alg==1 ) { if( circular&&ftbaseissmooth(m, _state) ) { /* * special code for circular convolution with smooth M */ ftcomplexfftplan(m, 1, &plan, _state); ae_vector_set_length(&buf, 2*m, _state); for(i=0; i<=m-1; i++) { buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x; buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y; } ae_vector_set_length(&buf2, 2*m, _state); for(i=0; i<=n-1; i++) { buf2.ptr.p_double[2*i+0] = b->ptr.p_complex[i].x; buf2.ptr.p_double[2*i+1] = b->ptr.p_complex[i].y; } for(i=n; i<=m-1; i++) { buf2.ptr.p_double[2*i+0] = 0; buf2.ptr.p_double[2*i+1] = 0; } ftapplyplan(&plan, &buf, 0, 1, _state); ftapplyplan(&plan, &buf2, 0, 1, _state); for(i=0; i<=m-1; i++) { ax = buf.ptr.p_double[2*i+0]; ay = buf.ptr.p_double[2*i+1]; bx = buf2.ptr.p_double[2*i+0]; by = buf2.ptr.p_double[2*i+1]; tx = ax*bx-ay*by; ty = ax*by+ay*bx; buf.ptr.p_double[2*i+0] = tx; buf.ptr.p_double[2*i+1] = -ty; } ftapplyplan(&plan, &buf, 0, 1, _state); t = (double)1/(double)m; ae_vector_set_length(r, m, _state); for(i=0; i<=m-1; i++) { r->ptr.p_complex[i].x = t*buf.ptr.p_double[2*i+0]; r->ptr.p_complex[i].y = -t*buf.ptr.p_double[2*i+1]; } } else { /* * M is non-smooth, general code (circular/non-circular): * * first part is the same for circular and non-circular * convolutions. zero padding, FFTs, inverse FFTs * * second part differs: * * for non-circular convolution we just copy array * * for circular convolution we add array tail to its head */ p = ftbasefindsmooth(m+n-1, _state); ftcomplexfftplan(p, 1, &plan, _state); ae_vector_set_length(&buf, 2*p, _state); for(i=0; i<=m-1; i++) { buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x; buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y; } for(i=m; i<=p-1; i++) { buf.ptr.p_double[2*i+0] = 0; buf.ptr.p_double[2*i+1] = 0; } ae_vector_set_length(&buf2, 2*p, _state); for(i=0; i<=n-1; i++) { buf2.ptr.p_double[2*i+0] = b->ptr.p_complex[i].x; buf2.ptr.p_double[2*i+1] = b->ptr.p_complex[i].y; } for(i=n; i<=p-1; i++) { buf2.ptr.p_double[2*i+0] = 0; buf2.ptr.p_double[2*i+1] = 0; } ftapplyplan(&plan, &buf, 0, 1, _state); ftapplyplan(&plan, &buf2, 0, 1, _state); for(i=0; i<=p-1; i++) { ax = buf.ptr.p_double[2*i+0]; ay = buf.ptr.p_double[2*i+1]; bx = buf2.ptr.p_double[2*i+0]; by = buf2.ptr.p_double[2*i+1]; tx = ax*bx-ay*by; ty = ax*by+ay*bx; buf.ptr.p_double[2*i+0] = tx; buf.ptr.p_double[2*i+1] = -ty; } ftapplyplan(&plan, &buf, 0, 1, _state); t = (double)1/(double)p; if( circular ) { /* * circular, add tail to head */ ae_vector_set_length(r, m, _state); for(i=0; i<=m-1; i++) { r->ptr.p_complex[i].x = t*buf.ptr.p_double[2*i+0]; r->ptr.p_complex[i].y = -t*buf.ptr.p_double[2*i+1]; } for(i=m; i<=m+n-2; i++) { r->ptr.p_complex[i-m].x = r->ptr.p_complex[i-m].x+t*buf.ptr.p_double[2*i+0]; r->ptr.p_complex[i-m].y = r->ptr.p_complex[i-m].y-t*buf.ptr.p_double[2*i+1]; } } else { /* * non-circular, just copy */ ae_vector_set_length(r, m+n-1, _state); for(i=0; i<=m+n-2; i++) { r->ptr.p_complex[i].x = t*buf.ptr.p_double[2*i+0]; r->ptr.p_complex[i].y = -t*buf.ptr.p_double[2*i+1]; } } } ae_frame_leave(_state); return; } /* * overlap-add method for * circular and non-circular convolutions. * * First part of code (separate FFTs of input blocks) is the same * for all types of convolution. Second part (overlapping outputs) * differs for different types of convolution. We just copy output * when convolution is non-circular. We wrap it around, if it is * circular. */ if( alg==2 ) { ae_vector_set_length(&buf, 2*(q+n-1), _state); /* * prepare R */ if( circular ) { ae_vector_set_length(r, m, _state); for(i=0; i<=m-1; i++) { r->ptr.p_complex[i] = ae_complex_from_d(0); } } else { ae_vector_set_length(r, m+n-1, _state); for(i=0; i<=m+n-2; i++) { r->ptr.p_complex[i] = ae_complex_from_d(0); } } /* * pre-calculated FFT(B) */ ae_vector_set_length(&bbuf, q+n-1, _state); ae_v_cmove(&bbuf.ptr.p_complex[0], 1, &b->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1)); for(j=n; j<=q+n-2; j++) { bbuf.ptr.p_complex[j] = ae_complex_from_d(0); } fftc1d(&bbuf, q+n-1, _state); /* * prepare FFT plan for chunks of A */ ftcomplexfftplan(q+n-1, 1, &plan, _state); /* * main overlap-add cycle */ i = 0; while(i<=m-1) { p = ae_minint(q, m-i, _state); for(j=0; j<=p-1; j++) { buf.ptr.p_double[2*j+0] = a->ptr.p_complex[i+j].x; buf.ptr.p_double[2*j+1] = a->ptr.p_complex[i+j].y; } for(j=p; j<=q+n-2; j++) { buf.ptr.p_double[2*j+0] = 0; buf.ptr.p_double[2*j+1] = 0; } ftapplyplan(&plan, &buf, 0, 1, _state); for(j=0; j<=q+n-2; j++) { ax = buf.ptr.p_double[2*j+0]; ay = buf.ptr.p_double[2*j+1]; bx = bbuf.ptr.p_complex[j].x; by = bbuf.ptr.p_complex[j].y; tx = ax*bx-ay*by; ty = ax*by+ay*bx; buf.ptr.p_double[2*j+0] = tx; buf.ptr.p_double[2*j+1] = -ty; } ftapplyplan(&plan, &buf, 0, 1, _state); t = (double)1/(double)(q+n-1); if( circular ) { j1 = ae_minint(i+p+n-2, m-1, _state)-i; j2 = j1+1; } else { j1 = p+n-2; j2 = j1+1; } for(j=0; j<=j1; j++) { r->ptr.p_complex[i+j].x = r->ptr.p_complex[i+j].x+buf.ptr.p_double[2*j+0]*t; r->ptr.p_complex[i+j].y = r->ptr.p_complex[i+j].y-buf.ptr.p_double[2*j+1]*t; } for(j=j2; j<=p+n-2; j++) { r->ptr.p_complex[j-j2].x = r->ptr.p_complex[j-j2].x+buf.ptr.p_double[2*j+0]*t; r->ptr.p_complex[j-j2].y = r->ptr.p_complex[j-j2].y-buf.ptr.p_double[2*j+1]*t; } i = i+p; } ae_frame_leave(_state); return; } ae_frame_leave(_state); } /************************************************************************* 1-dimensional real convolution. Extended subroutine which allows to choose convolution algorithm. Intended for internal use, ALGLIB users should call ConvR1D(). INPUT PARAMETERS A - array[0..M-1] - complex function to be transformed M - problem size B - array[0..N-1] - complex function to be transformed N - problem size, N<=M Alg - algorithm type: *-2 auto-select Q for overlap-add *-1 auto-select algorithm and parameters * 0 straightforward formula for small N's * 1 general FFT-based code * 2 overlap-add with length Q Q - length for overlap-add OUTPUT PARAMETERS R - convolution: A*B. array[0..N+M-1]. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void convr1dx(/* Real */ ae_vector* a, ae_int_t m, /* Real */ ae_vector* b, ae_int_t n, ae_bool circular, ae_int_t alg, ae_int_t q, /* Real */ ae_vector* r, ae_state *_state) { ae_frame _frame_block; double v; ae_int_t i; ae_int_t j; ae_int_t p; ae_int_t ptotal; ae_int_t i1; ae_int_t i2; ae_int_t j1; ae_int_t j2; double ax; double ay; double bx; double by; double tx; double ty; double flopcand; double flopbest; ae_int_t algbest; fasttransformplan plan; ae_vector buf; ae_vector buf2; ae_vector buf3; ae_frame_make(_state, &_frame_block); ae_vector_clear(r); _fasttransformplan_init(&plan, _state, ae_true); ae_vector_init(&buf, 0, DT_REAL, _state, ae_true); ae_vector_init(&buf2, 0, DT_REAL, _state, ae_true); ae_vector_init(&buf3, 0, DT_REAL, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DX: incorrect N or M!", _state); ae_assert(n<=m, "ConvC1DX: Nptr.p_double[0]; ae_v_moved(&r->ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,m-1), v); ae_frame_leave(_state); return; } /* * use straightforward formula */ if( circular ) { /* * circular convolution */ ae_vector_set_length(r, m, _state); v = b->ptr.p_double[0]; ae_v_moved(&r->ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,m-1), v); for(i=1; i<=n-1; i++) { v = b->ptr.p_double[i]; i1 = 0; i2 = i-1; j1 = m-i; j2 = m-1; ae_v_addd(&r->ptr.p_double[i1], 1, &a->ptr.p_double[j1], 1, ae_v_len(i1,i2), v); i1 = i; i2 = m-1; j1 = 0; j2 = m-i-1; ae_v_addd(&r->ptr.p_double[i1], 1, &a->ptr.p_double[j1], 1, ae_v_len(i1,i2), v); } } else { /* * non-circular convolution */ ae_vector_set_length(r, m+n-1, _state); for(i=0; i<=m+n-2; i++) { r->ptr.p_double[i] = 0; } for(i=0; i<=n-1; i++) { v = b->ptr.p_double[i]; ae_v_addd(&r->ptr.p_double[i], 1, &a->ptr.p_double[0], 1, ae_v_len(i,i+m-1), v); } } ae_frame_leave(_state); return; } /* * general FFT-based code for * circular and non-circular convolutions. * * First, if convolution is circular, we test whether M is smooth or not. * If it is smooth, we just use M-length FFT to calculate convolution. * If it is not, we calculate non-circular convolution and wrap it arount. * * If convolution is non-circular, we use zero-padding + FFT. * * We assume that M+N-1>2 - we should call small case code otherwise */ if( alg==1 ) { ae_assert(m+n-1>2, "ConvR1DX: internal error!", _state); if( (circular&&ftbaseissmooth(m, _state))&&m%2==0 ) { /* * special code for circular convolution with smooth even M */ ae_vector_set_length(&buf, m, _state); ae_v_move(&buf.ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,m-1)); ae_vector_set_length(&buf2, m, _state); ae_v_move(&buf2.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,n-1)); for(i=n; i<=m-1; i++) { buf2.ptr.p_double[i] = 0; } ae_vector_set_length(&buf3, m, _state); ftcomplexfftplan(m/2, 1, &plan, _state); fftr1dinternaleven(&buf, m, &buf3, &plan, _state); fftr1dinternaleven(&buf2, m, &buf3, &plan, _state); buf.ptr.p_double[0] = buf.ptr.p_double[0]*buf2.ptr.p_double[0]; buf.ptr.p_double[1] = buf.ptr.p_double[1]*buf2.ptr.p_double[1]; for(i=1; i<=m/2-1; i++) { ax = buf.ptr.p_double[2*i+0]; ay = buf.ptr.p_double[2*i+1]; bx = buf2.ptr.p_double[2*i+0]; by = buf2.ptr.p_double[2*i+1]; tx = ax*bx-ay*by; ty = ax*by+ay*bx; buf.ptr.p_double[2*i+0] = tx; buf.ptr.p_double[2*i+1] = ty; } fftr1dinvinternaleven(&buf, m, &buf3, &plan, _state); ae_vector_set_length(r, m, _state); ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-1)); } else { /* * M is non-smooth or non-even, general code (circular/non-circular): * * first part is the same for circular and non-circular * convolutions. zero padding, FFTs, inverse FFTs * * second part differs: * * for non-circular convolution we just copy array * * for circular convolution we add array tail to its head */ p = ftbasefindsmootheven(m+n-1, _state); ae_vector_set_length(&buf, p, _state); ae_v_move(&buf.ptr.p_double[0], 1, &a->ptr.p_double[0], 1, ae_v_len(0,m-1)); for(i=m; i<=p-1; i++) { buf.ptr.p_double[i] = 0; } ae_vector_set_length(&buf2, p, _state); ae_v_move(&buf2.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,n-1)); for(i=n; i<=p-1; i++) { buf2.ptr.p_double[i] = 0; } ae_vector_set_length(&buf3, p, _state); ftcomplexfftplan(p/2, 1, &plan, _state); fftr1dinternaleven(&buf, p, &buf3, &plan, _state); fftr1dinternaleven(&buf2, p, &buf3, &plan, _state); buf.ptr.p_double[0] = buf.ptr.p_double[0]*buf2.ptr.p_double[0]; buf.ptr.p_double[1] = buf.ptr.p_double[1]*buf2.ptr.p_double[1]; for(i=1; i<=p/2-1; i++) { ax = buf.ptr.p_double[2*i+0]; ay = buf.ptr.p_double[2*i+1]; bx = buf2.ptr.p_double[2*i+0]; by = buf2.ptr.p_double[2*i+1]; tx = ax*bx-ay*by; ty = ax*by+ay*bx; buf.ptr.p_double[2*i+0] = tx; buf.ptr.p_double[2*i+1] = ty; } fftr1dinvinternaleven(&buf, p, &buf3, &plan, _state); if( circular ) { /* * circular, add tail to head */ ae_vector_set_length(r, m, _state); ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-1)); if( n>=2 ) { ae_v_add(&r->ptr.p_double[0], 1, &buf.ptr.p_double[m], 1, ae_v_len(0,n-2)); } } else { /* * non-circular, just copy */ ae_vector_set_length(r, m+n-1, _state); ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m+n-2)); } } ae_frame_leave(_state); return; } /* * overlap-add method */ if( alg==2 ) { ae_assert((q+n-1)%2==0, "ConvR1DX: internal error!", _state); ae_vector_set_length(&buf, q+n-1, _state); ae_vector_set_length(&buf2, q+n-1, _state); ae_vector_set_length(&buf3, q+n-1, _state); ftcomplexfftplan((q+n-1)/2, 1, &plan, _state); /* * prepare R */ if( circular ) { ae_vector_set_length(r, m, _state); for(i=0; i<=m-1; i++) { r->ptr.p_double[i] = 0; } } else { ae_vector_set_length(r, m+n-1, _state); for(i=0; i<=m+n-2; i++) { r->ptr.p_double[i] = 0; } } /* * pre-calculated FFT(B) */ ae_v_move(&buf2.ptr.p_double[0], 1, &b->ptr.p_double[0], 1, ae_v_len(0,n-1)); for(j=n; j<=q+n-2; j++) { buf2.ptr.p_double[j] = 0; } fftr1dinternaleven(&buf2, q+n-1, &buf3, &plan, _state); /* * main overlap-add cycle */ i = 0; while(i<=m-1) { p = ae_minint(q, m-i, _state); ae_v_move(&buf.ptr.p_double[0], 1, &a->ptr.p_double[i], 1, ae_v_len(0,p-1)); for(j=p; j<=q+n-2; j++) { buf.ptr.p_double[j] = 0; } fftr1dinternaleven(&buf, q+n-1, &buf3, &plan, _state); buf.ptr.p_double[0] = buf.ptr.p_double[0]*buf2.ptr.p_double[0]; buf.ptr.p_double[1] = buf.ptr.p_double[1]*buf2.ptr.p_double[1]; for(j=1; j<=(q+n-1)/2-1; j++) { ax = buf.ptr.p_double[2*j+0]; ay = buf.ptr.p_double[2*j+1]; bx = buf2.ptr.p_double[2*j+0]; by = buf2.ptr.p_double[2*j+1]; tx = ax*bx-ay*by; ty = ax*by+ay*bx; buf.ptr.p_double[2*j+0] = tx; buf.ptr.p_double[2*j+1] = ty; } fftr1dinvinternaleven(&buf, q+n-1, &buf3, &plan, _state); if( circular ) { j1 = ae_minint(i+p+n-2, m-1, _state)-i; j2 = j1+1; } else { j1 = p+n-2; j2 = j1+1; } ae_v_add(&r->ptr.p_double[i], 1, &buf.ptr.p_double[0], 1, ae_v_len(i,i+j1)); if( p+n-2>=j2 ) { ae_v_add(&r->ptr.p_double[0], 1, &buf.ptr.p_double[j2], 1, ae_v_len(0,p+n-2-j2)); } i = i+p; } ae_frame_leave(_state); return; } ae_frame_leave(_state); } /************************************************************************* 1-dimensional complex cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (non-circular). Correlation is calculated using reduction to convolution. Algorithm with max(N,N)*log(max(N,N)) complexity is used (see ConvC1D() for more info about performance). IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrC1D(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - complex function to be transformed, signal containing pattern N - problem size Pattern - array[0..M-1] - complex function to be transformed, pattern to search withing signal M - problem size OUTPUT PARAMETERS R - cross-correlation, array[0..N+M-2]: * positive lags are stored in R[0..N-1], R[i] = sum(conj(pattern[j])*signal[i+j] * negative lags are stored in R[N..N+M-2], R[N+M-1-i] = sum(conj(pattern[j])*signal[-i+j] NOTE: It is assumed that pattern domain is [0..M-1]. If Pattern is non-zero on [-K..M-1], you can still use this subroutine, just shift result by K. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrc1d(/* Complex */ ae_vector* signal, ae_int_t n, /* Complex */ ae_vector* pattern, ae_int_t m, /* Complex */ ae_vector* r, ae_state *_state) { ae_frame _frame_block; ae_vector p; ae_vector b; ae_int_t i; ae_frame_make(_state, &_frame_block); ae_vector_clear(r); ae_vector_init(&p, 0, DT_COMPLEX, _state, ae_true); ae_vector_init(&b, 0, DT_COMPLEX, _state, ae_true); ae_assert(n>0&&m>0, "CorrC1D: incorrect N or M!", _state); ae_vector_set_length(&p, m, _state); for(i=0; i<=m-1; i++) { p.ptr.p_complex[m-1-i] = ae_c_conj(pattern->ptr.p_complex[i], _state); } convc1d(&p, m, signal, n, &b, _state); ae_vector_set_length(r, m+n-1, _state); ae_v_cmove(&r->ptr.p_complex[0], 1, &b.ptr.p_complex[m-1], 1, "N", ae_v_len(0,n-1)); if( m+n-2>=n ) { ae_v_cmove(&r->ptr.p_complex[n], 1, &b.ptr.p_complex[0], 1, "N", ae_v_len(n,m+n-2)); } ae_frame_leave(_state); } /************************************************************************* 1-dimensional circular complex cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (circular). Algorithm has linearithmic complexity for any M/N. IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrC1DCircular(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - complex function to be transformed, periodic signal containing pattern N - problem size Pattern - array[0..M-1] - complex function to be transformed, non-periodic pattern to search withing signal M - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrc1dcircular(/* Complex */ ae_vector* signal, ae_int_t m, /* Complex */ ae_vector* pattern, ae_int_t n, /* Complex */ ae_vector* c, ae_state *_state) { ae_frame _frame_block; ae_vector p; ae_vector b; ae_int_t i1; ae_int_t i2; ae_int_t i; ae_int_t j2; ae_frame_make(_state, &_frame_block); ae_vector_clear(c); ae_vector_init(&p, 0, DT_COMPLEX, _state, ae_true); ae_vector_init(&b, 0, DT_COMPLEX, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DCircular: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer (at least - not shorter) that B. */ if( mptr.p_complex[i1], 1, "N", ae_v_len(0,j2)); i1 = i1+m; } corrc1dcircular(signal, m, &b, m, c, _state); ae_frame_leave(_state); return; } /* * Task is normalized */ ae_vector_set_length(&p, n, _state); for(i=0; i<=n-1; i++) { p.ptr.p_complex[n-1-i] = ae_c_conj(pattern->ptr.p_complex[i], _state); } convc1dcircular(signal, m, &p, n, &b, _state); ae_vector_set_length(c, m, _state); ae_v_cmove(&c->ptr.p_complex[0], 1, &b.ptr.p_complex[n-1], 1, "N", ae_v_len(0,m-n)); if( m-n+1<=m-1 ) { ae_v_cmove(&c->ptr.p_complex[m-n+1], 1, &b.ptr.p_complex[0], 1, "N", ae_v_len(m-n+1,m-1)); } ae_frame_leave(_state); } /************************************************************************* 1-dimensional real cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (non-circular). Correlation is calculated using reduction to convolution. Algorithm with max(N,N)*log(max(N,N)) complexity is used (see ConvC1D() for more info about performance). IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrR1D(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - real function to be transformed, signal containing pattern N - problem size Pattern - array[0..M-1] - real function to be transformed, pattern to search withing signal M - problem size OUTPUT PARAMETERS R - cross-correlation, array[0..N+M-2]: * positive lags are stored in R[0..N-1], R[i] = sum(pattern[j]*signal[i+j] * negative lags are stored in R[N..N+M-2], R[N+M-1-i] = sum(pattern[j]*signal[-i+j] NOTE: It is assumed that pattern domain is [0..M-1]. If Pattern is non-zero on [-K..M-1], you can still use this subroutine, just shift result by K. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrr1d(/* Real */ ae_vector* signal, ae_int_t n, /* Real */ ae_vector* pattern, ae_int_t m, /* Real */ ae_vector* r, ae_state *_state) { ae_frame _frame_block; ae_vector p; ae_vector b; ae_int_t i; ae_frame_make(_state, &_frame_block); ae_vector_clear(r); ae_vector_init(&p, 0, DT_REAL, _state, ae_true); ae_vector_init(&b, 0, DT_REAL, _state, ae_true); ae_assert(n>0&&m>0, "CorrR1D: incorrect N or M!", _state); ae_vector_set_length(&p, m, _state); for(i=0; i<=m-1; i++) { p.ptr.p_double[m-1-i] = pattern->ptr.p_double[i]; } convr1d(&p, m, signal, n, &b, _state); ae_vector_set_length(r, m+n-1, _state); ae_v_move(&r->ptr.p_double[0], 1, &b.ptr.p_double[m-1], 1, ae_v_len(0,n-1)); if( m+n-2>=n ) { ae_v_move(&r->ptr.p_double[n], 1, &b.ptr.p_double[0], 1, ae_v_len(n,m+n-2)); } ae_frame_leave(_state); } /************************************************************************* 1-dimensional circular real cross-correlation. For given Pattern/Signal returns corr(Pattern,Signal) (circular). Algorithm has linearithmic complexity for any M/N. IMPORTANT: for historical reasons subroutine accepts its parameters in reversed order: CorrR1DCircular(Signal, Pattern) = Pattern x Signal (using traditional definition of cross-correlation, denoting cross-correlation as "x"). INPUT PARAMETERS Signal - array[0..N-1] - real function to be transformed, periodic signal containing pattern N - problem size Pattern - array[0..M-1] - real function to be transformed, non-periodic pattern to search withing signal M - problem size OUTPUT PARAMETERS R - convolution: A*B. array[0..M-1]. -- ALGLIB -- Copyright 21.07.2009 by Bochkanov Sergey *************************************************************************/ void corrr1dcircular(/* Real */ ae_vector* signal, ae_int_t m, /* Real */ ae_vector* pattern, ae_int_t n, /* Real */ ae_vector* c, ae_state *_state) { ae_frame _frame_block; ae_vector p; ae_vector b; ae_int_t i1; ae_int_t i2; ae_int_t i; ae_int_t j2; ae_frame_make(_state, &_frame_block); ae_vector_clear(c); ae_vector_init(&p, 0, DT_REAL, _state, ae_true); ae_vector_init(&b, 0, DT_REAL, _state, ae_true); ae_assert(n>0&&m>0, "ConvC1DCircular: incorrect N or M!", _state); /* * normalize task: make M>=N, * so A will be longer (at least - not shorter) that B. */ if( mptr.p_double[i1], 1, ae_v_len(0,j2)); i1 = i1+m; } corrr1dcircular(signal, m, &b, m, c, _state); ae_frame_leave(_state); return; } /* * Task is normalized */ ae_vector_set_length(&p, n, _state); for(i=0; i<=n-1; i++) { p.ptr.p_double[n-1-i] = pattern->ptr.p_double[i]; } convr1dcircular(signal, m, &p, n, &b, _state); ae_vector_set_length(c, m, _state); ae_v_move(&c->ptr.p_double[0], 1, &b.ptr.p_double[n-1], 1, ae_v_len(0,m-n)); if( m-n+1<=m-1 ) { ae_v_move(&c->ptr.p_double[m-n+1], 1, &b.ptr.p_double[0], 1, ae_v_len(m-n+1,m-1)); } ae_frame_leave(_state); } /************************************************************************* 1-dimensional Fast Hartley Transform. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - real function to be transformed N - problem size OUTPUT PARAMETERS A - FHT of a input array, array[0..N-1], A_out[k] = sum(A_in[j]*(cos(2*pi*j*k/N)+sin(2*pi*j*k/N)), j=0..N-1) -- ALGLIB -- Copyright 04.06.2009 by Bochkanov Sergey *************************************************************************/ void fhtr1d(/* Real */ ae_vector* a, ae_int_t n, ae_state *_state) { ae_frame _frame_block; ae_int_t i; ae_vector fa; ae_frame_make(_state, &_frame_block); ae_vector_init(&fa, 0, DT_COMPLEX, _state, ae_true); ae_assert(n>0, "FHTR1D: incorrect N!", _state); /* * Special case: N=1, FHT is just identity transform. * After this block we assume that N is strictly greater than 1. */ if( n==1 ) { ae_frame_leave(_state); return; } /* * Reduce FHt to real FFT */ fftr1d(a, n, &fa, _state); for(i=0; i<=n-1; i++) { a->ptr.p_double[i] = fa.ptr.p_complex[i].x-fa.ptr.p_complex[i].y; } ae_frame_leave(_state); } /************************************************************************* 1-dimensional inverse FHT. Algorithm has O(N*logN) complexity for any N (composite or prime). INPUT PARAMETERS A - array[0..N-1] - complex array to be transformed N - problem size OUTPUT PARAMETERS A - inverse FHT of a input array, array[0..N-1] -- ALGLIB -- Copyright 29.05.2009 by Bochkanov Sergey *************************************************************************/ void fhtr1dinv(/* Real */ ae_vector* a, ae_int_t n, ae_state *_state) { ae_int_t i; ae_assert(n>0, "FHTR1DInv: incorrect N!", _state); /* * Special case: N=1, iFHT is just identity transform. * After this block we assume that N is strictly greater than 1. */ if( n==1 ) { return; } /* * Inverse FHT can be expressed in terms of the FHT as * * invfht(x) = fht(x)/N */ fhtr1d(a, n, _state); for(i=0; i<=n-1; i++) { a->ptr.p_double[i] = a->ptr.p_double[i]/n; } } }