mirror of
https://asciireactor.com/otho/psdlag-agn.git
synced 2024-11-22 08:55:08 +00:00
3555 lines
116 KiB
C++
3555 lines
116 KiB
C++
/*************************************************************************
|
|
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<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(f.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(f.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(s.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(r.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(s.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(r.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(a.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(signal.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(pattern.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(signal.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(pattern.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(signal.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(pattern.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(signal.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(pattern.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(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<alglib_impl::ae_vector*>(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)<N!", _state);
|
|
ae_assert(isfinitecvector(a, n, _state), "FFTC1D: A 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_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* convert input array to the more convinient format
|
|
*/
|
|
ae_vector_set_length(&buf, 2*n, _state);
|
|
for(i=0; i<=n-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;
|
|
}
|
|
|
|
/*
|
|
* 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)<N!", _state);
|
|
ae_assert(isfinitecvector(a, n, _state), "FFTC1DInv: A contains infinite or NAN values!", _state);
|
|
|
|
/*
|
|
* Inverse DFT can be expressed in terms of the DFT as
|
|
*
|
|
* invfft(x) = fft(x')'/N
|
|
*
|
|
* here x' means conj(x).
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
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)<N!", _state);
|
|
ae_assert(isfinitevector(a, n, _state), "FFTR1D: A contains infinite or NAN values!", _state);
|
|
|
|
/*
|
|
* Special cases:
|
|
* * N=1, FFT is just identity transform.
|
|
* * N=2, FFT is simple too
|
|
*
|
|
* After this block we assume that N is strictly greater than 2
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
ae_vector_set_length(f, 1, _state);
|
|
f->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)<Floor(N/2)+1!", _state);
|
|
ae_assert(ae_isfinite(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( m<n )
|
|
{
|
|
convc1d(b, n, a, m, r, _state);
|
|
return;
|
|
}
|
|
convc1dx(a, m, b, n, ae_false, -1, 0, r, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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(/* 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 p;
|
|
ae_vector buf;
|
|
ae_vector buf2;
|
|
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);
|
|
_fasttransformplan_init(&plan, _state, ae_true);
|
|
|
|
ae_assert((n>0&&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( m<n )
|
|
{
|
|
ae_vector_set_length(&buf, m, _state);
|
|
for(i1=0; i1<=m-1; i1++)
|
|
{
|
|
buf.ptr.p_complex[i1] = ae_complex_from_d(0);
|
|
}
|
|
i1 = 0;
|
|
while(i1<n)
|
|
{
|
|
i2 = ae_minint(i1+m-1, n-1, _state);
|
|
j2 = i2-i1;
|
|
ae_v_cadd(&buf.ptr.p_complex[0], 1, &r->ptr.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( m<n )
|
|
{
|
|
ae_vector_set_length(&cbuf, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
cbuf.ptr.p_complex[i] = ae_complex_from_d(0);
|
|
}
|
|
i1 = 0;
|
|
while(i1<n)
|
|
{
|
|
i2 = ae_minint(i1+m-1, n-1, _state);
|
|
j2 = i2-i1;
|
|
ae_v_cadd(&cbuf.ptr.p_complex[0], 1, &b->ptr.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( m<n )
|
|
{
|
|
convr1d(b, n, a, m, r, _state);
|
|
return;
|
|
}
|
|
convr1dx(a, m, b, n, ae_false, -1, 0, r, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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(/* 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 p;
|
|
ae_vector buf;
|
|
ae_vector buf2;
|
|
ae_vector buf3;
|
|
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);
|
|
_fasttransformplan_init(&plan, _state, ae_true);
|
|
|
|
ae_assert((n>0&&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( m<n )
|
|
{
|
|
ae_vector_set_length(&buf, m, _state);
|
|
for(i1=0; i1<=m-1; i1++)
|
|
{
|
|
buf.ptr.p_double[i1] = 0;
|
|
}
|
|
i1 = 0;
|
|
while(i1<n)
|
|
{
|
|
i2 = ae_minint(i1+m-1, n-1, _state);
|
|
j2 = i2-i1;
|
|
ae_v_add(&buf.ptr.p_double[0], 1, &r->ptr.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( m<n )
|
|
{
|
|
ae_vector_set_length(&buf, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
buf.ptr.p_double[i] = 0;
|
|
}
|
|
i1 = 0;
|
|
while(i1<n)
|
|
{
|
|
i2 = ae_minint(i1+m-1, n-1, _state);
|
|
j2 = i2-i1;
|
|
ae_v_add(&buf.ptr.p_double[0], 1, &b->ptr.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: N<M assumption is false!", _state);
|
|
|
|
/*
|
|
* Auto-select
|
|
*/
|
|
if( alg==-1||alg==-2 )
|
|
{
|
|
|
|
/*
|
|
* Initial candidate: straightforward implementation.
|
|
*
|
|
* If we want to use auto-fitted overlap-add,
|
|
* flop count is initialized by large real number - to force
|
|
* another algorithm selection
|
|
*/
|
|
algbest = 0;
|
|
if( alg==-1 )
|
|
{
|
|
flopbest = 2*m*n;
|
|
}
|
|
else
|
|
{
|
|
flopbest = ae_maxrealnumber;
|
|
}
|
|
|
|
/*
|
|
* Another candidate - generic FFT code
|
|
*/
|
|
if( alg==-1 )
|
|
{
|
|
if( circular&&ftbaseissmooth(m, _state) )
|
|
{
|
|
|
|
/*
|
|
* special code for circular convolution of a sequence with a smooth length
|
|
*/
|
|
flopcand = 3*ftbasegetflopestimate(m, _state)+6*m;
|
|
if( ae_fp_less(flopcand,flopbest) )
|
|
{
|
|
algbest = 1;
|
|
flopbest = flopcand;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* general cyclic/non-cyclic convolution
|
|
*/
|
|
p = ftbasefindsmooth(m+n-1, _state);
|
|
flopcand = 3*ftbasegetflopestimate(p, _state)+6*p;
|
|
if( ae_fp_less(flopcand,flopbest) )
|
|
{
|
|
algbest = 1;
|
|
flopbest = flopcand;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Another candidate - overlap-add
|
|
*/
|
|
q = 1;
|
|
ptotal = 1;
|
|
while(ptotal<n)
|
|
{
|
|
ptotal = ptotal*2;
|
|
}
|
|
while(ptotal<=m+n-1)
|
|
{
|
|
p = ptotal-n+1;
|
|
flopcand = ae_iceil((double)m/(double)p, _state)*(2*ftbasegetflopestimate(ptotal, _state)+8*ptotal);
|
|
if( ae_fp_less(flopcand,flopbest) )
|
|
{
|
|
flopbest = flopcand;
|
|
algbest = 2;
|
|
q = p;
|
|
}
|
|
ptotal = ptotal*2;
|
|
}
|
|
alg = algbest;
|
|
convc1dx(a, m, b, n, circular, alg, q, r, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* straightforward formula for
|
|
* circular and non-circular convolutions.
|
|
*
|
|
* Very simple code, no further comments needed.
|
|
*/
|
|
if( alg==0 )
|
|
{
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
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);
|
|
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: N<M assumption is false!", _state);
|
|
|
|
/*
|
|
* handle special cases
|
|
*/
|
|
if( ae_minint(m, n, _state)<=2 )
|
|
{
|
|
alg = 0;
|
|
}
|
|
|
|
/*
|
|
* Auto-select
|
|
*/
|
|
if( alg<0 )
|
|
{
|
|
|
|
/*
|
|
* Initial candidate: straightforward implementation.
|
|
*
|
|
* If we want to use auto-fitted overlap-add,
|
|
* flop count is initialized by large real number - to force
|
|
* another algorithm selection
|
|
*/
|
|
algbest = 0;
|
|
if( alg==-1 )
|
|
{
|
|
flopbest = 0.15*m*n;
|
|
}
|
|
else
|
|
{
|
|
flopbest = ae_maxrealnumber;
|
|
}
|
|
|
|
/*
|
|
* Another candidate - generic FFT code
|
|
*/
|
|
if( alg==-1 )
|
|
{
|
|
if( (circular&&ftbaseissmooth(m, _state))&&m%2==0 )
|
|
{
|
|
|
|
/*
|
|
* special code for circular convolution of a sequence with a smooth length
|
|
*/
|
|
flopcand = 3*ftbasegetflopestimate(m/2, _state)+(double)(6*m)/(double)2;
|
|
if( ae_fp_less(flopcand,flopbest) )
|
|
{
|
|
algbest = 1;
|
|
flopbest = flopcand;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* general cyclic/non-cyclic convolution
|
|
*/
|
|
p = ftbasefindsmootheven(m+n-1, _state);
|
|
flopcand = 3*ftbasegetflopestimate(p/2, _state)+(double)(6*p)/(double)2;
|
|
if( ae_fp_less(flopcand,flopbest) )
|
|
{
|
|
algbest = 1;
|
|
flopbest = flopcand;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Another candidate - overlap-add
|
|
*/
|
|
q = 1;
|
|
ptotal = 1;
|
|
while(ptotal<n)
|
|
{
|
|
ptotal = ptotal*2;
|
|
}
|
|
while(ptotal<=m+n-1)
|
|
{
|
|
p = ptotal-n+1;
|
|
flopcand = ae_iceil((double)m/(double)p, _state)*(2*ftbasegetflopestimate(ptotal/2, _state)+1*(ptotal/2));
|
|
if( ae_fp_less(flopcand,flopbest) )
|
|
{
|
|
flopbest = flopcand;
|
|
algbest = 2;
|
|
q = p;
|
|
}
|
|
ptotal = ptotal*2;
|
|
}
|
|
alg = algbest;
|
|
convr1dx(a, m, b, n, circular, alg, q, r, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* straightforward formula for
|
|
* circular and non-circular convolutions.
|
|
*
|
|
* Very simple code, no further comments needed.
|
|
*/
|
|
if( alg==0 )
|
|
{
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
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);
|
|
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( m<n )
|
|
{
|
|
ae_vector_set_length(&b, m, _state);
|
|
for(i1=0; i1<=m-1; i1++)
|
|
{
|
|
b.ptr.p_complex[i1] = ae_complex_from_d(0);
|
|
}
|
|
i1 = 0;
|
|
while(i1<n)
|
|
{
|
|
i2 = ae_minint(i1+m-1, n-1, _state);
|
|
j2 = i2-i1;
|
|
ae_v_cadd(&b.ptr.p_complex[0], 1, &pattern->ptr.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( m<n )
|
|
{
|
|
ae_vector_set_length(&b, m, _state);
|
|
for(i1=0; i1<=m-1; i1++)
|
|
{
|
|
b.ptr.p_double[i1] = 0;
|
|
}
|
|
i1 = 0;
|
|
while(i1<n)
|
|
{
|
|
i2 = ae_minint(i1+m-1, n-1, _state);
|
|
j2 = i2-i1;
|
|
ae_v_add(&b.ptr.p_double[0], 1, &pattern->ptr.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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|