//+------------------------------------------------------------------+ //| fasttransforms.mqh | //| Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) | //| Copyright 2012-2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ //| Implementation of ALGLIB library in MetaQuotes Language 5 | //| | //| The features of the library include: | //| - Linear algebra (direct algorithms, EVD, SVD) | //| - Solving systems of linear and non-linear equations | //| - Interpolation | //| - Optimization | //| - FFT (Fast Fourier Transform) | //| - Numerical integration | //| - Linear and nonlinear least-squares fitting | //| - Ordinary differential equations | //| - Computation of special functions | //| - Descriptive statistics and hypothesis testing | //| - Data analysis - classification, regression | //| - Implementing linear algebra algorithms, interpolation, etc. | //| in high-precision arithmetic (using MPFR) | //| | //| This file 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. | //+------------------------------------------------------------------+ #include "alglibinternal.mqh" //+------------------------------------------------------------------+ //| Fast Fourier Transform | //+------------------------------------------------------------------+ class CFastFourierTransform { public: static void FFTC1D(complex &a[],const int n); static void FFTC1DInv(complex &a[],const int n); static void FFTR1D(double &a[],const int n,complex &f[]); static void FFTR1DInv(complex &f[],const int n,double &a[]); static void FFTR1DInternalEven(double &a[],const int n,double &buf[],CFtPlan &plan); static void FFTR1DInvInternalEven(double &a[],const int n,double &buf[],CFtPlan &plan); static void FFTC1D(CRowComplex &a,const int n); static void FFTC1DInv(CRowComplex &a,const int n); static void FFTR1D(CRowDouble &a,const int n,CRowComplex &f); static void FFTR1DInv(CRowComplex &f,const int n,CRowDouble &a); static void FFTR1DInternalEven(CRowDouble &a,const int n,CRowDouble &buf,CFtPlan &plan); static void FFTR1DInvInternalEven(CRowDouble &a,const int n,CRowDouble &buf,CFtPlan &plan); }; //+------------------------------------------------------------------+ //| 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) | //+------------------------------------------------------------------+ void CFastFourierTransform::FFTC1D(complex &a[],const int n) { //--- create a variable int i=0; //--- create array CRowDouble buf; //--- object of class CFtPlan plan; //--- check if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(f)>=(int)MathFloor((double)n/2.0)+1,__FUNCTION__+": Length(F)0,__FUNCTION__+": incorrect N!")) return; //--- check if(!CAp::Assert(CAp::Len(f)>=(int)MathFloor((double)n/2.0)+1,__FUNCTION__+": Length(F)0 && n%2==0,__FUNCTION__+": incorrect N!")) return; //--- Special cases: //--- * N=2 //--- After this block we assume that N is strictly greater than 2 if(n==2) { //--- change values x=a[0]+a[1]; y=a[0]-a[1]; a.Set(0,x); a.Set(1,y); //--- exit the function return; } //--- even-size real FFT,use reduction to the complex task n2=n/2; //--- copy buf=a; buf.Resize(n); //--- function call CFtBase::FtApplyPlan(plan,buf,0,1); //--- change value a.Set(0,buf[0]+buf[1]); for(i=1; i<=n2-1; i++) { //--- calculation idx=2*(i%n2); hn.real=buf[idx+0]; hn.imag=buf[idx+1]; idx=2*(n2-i); hmnc.real=buf[idx+0]; hmnc.imag=-buf[idx+1]; v.real=-MathSin(-(2*M_PI*i/n)); v.imag=MathCos(-(2*M_PI*i/n)); v=hn+hmnc-v*(hn-hmnc); a.Set(2*i+0,0.5*v.real); a.Set(2*i+1,0.5*v.imag); } //--- change value a.Set(1,buf[0]-buf[1]); } //+------------------------------------------------------------------+ //| Internal subroutine. Never call it directly! | //+------------------------------------------------------------------+ void CFastFourierTransform::FFTR1DInvInternalEven(double &a[],const int n, double &buf[],CFtPlan &plan) { //--- create variables double x=0; double y=0; double t=0; int i=0; int n2=0; //--- check if(!CAp::Assert(n>0 && n%2==0,__FUNCTION__+": incorrect N!")) return; //--- Special cases: //--- * N=2 //--- After this block we assume that N is strictly greater than 2 if(n==2) { //--- change values x=0.5*(a[0]+a[1]); y=0.5*(a[0]-a[1]); a[0]=x; a[1]=y; //--- exit the function 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[0]=a[0]; //--- calculation for(i=1; i<=n2-1; i++) { x=a[2*i+0]; y=a[2*i+1]; buf[i]=x-y; buf[n-i]=x+y; } buf[n2]=a[1]; //--- function call FFTR1DInternalEven(buf,n,a,plan); //--- change values a[0]=buf[0]/n; t=1.0/(double)n; //--- calculation for(i=1; i<=n2-1; i++) { x=buf[2*i+0]; y=buf[2*i+1]; a[i]=t*(x-y); a[n-i]=t*(x+y); } //--- change value a[n2]=buf[1]/n; } //+------------------------------------------------------------------+ //| Internal subroutine. Never call it directly! | //+------------------------------------------------------------------+ void CFastFourierTransform::FFTR1DInvInternalEven(CRowDouble &a,const int n, CRowDouble &buf,CFtPlan &plan) { //--- create variables double x=0; double y=0; double t=0; int i=0; int n2=0; //--- check if(!CAp::Assert(n>0 && n%2==0,__FUNCTION__+": incorrect N!")) return; //--- Special cases: //--- * N=2 //--- After this block we assume that N is strictly greater than 2 if(n==2) { //--- change values x=0.5*(a[0]+a[1]); y=0.5*(a[0]-a[1]); a.Set(0,x); a.Set(1,y); //--- exit the function 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.Set(0,a[0]); //--- calculation for(i=1; i<=n2-1; i++) { x=a[2*i+0]; y=a[2*i+1]; buf.Set(i,x-y); buf.Set(n-i,x+y); } buf.Set(n2,a[1]); //--- function call FFTR1DInternalEven(buf,n,a,plan); //--- change values a.Set(0,buf[0]/n); t=1.0/(double)n; //--- calculation for(i=1; i<=n2-1; i++) { x=buf[2*i+0]; y=buf[2*i+1]; a.Set(i,t*(x-y)); a.Set(n-i,t*(x+y)); } //--- change value a.Set(n2,buf[1]/n); } //+------------------------------------------------------------------+ //| Convolution class | //+------------------------------------------------------------------+ class CConv { public: static void ConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void ConvC1DInv(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void ConvC1DCircular(complex &s[],const int m,complex &r[],const int n,complex &c[]); static void ConvC1DCircularInv(complex &a[],const int m,complex &b[],const int n,complex &r[]); static void ConvR1D(double &a[],const int m,double &b[],const int n,double &r[]); static void ConvR1DInv(double &a[],const int m,double &b[],const int n,double &r[]); static void ConvR1DCircular(double &s[],const int m,double &r[],const int n,double &c[]); static void ConvR1DCircularInv(double &a[],const int m,double &b[],const int n,double &r[]); static void ConvC1DX(complex &a[],const int m,complex &b[],const int n,const bool circular,int alg,int q,complex &r[]); static void ConvR1DX(double &a[],const int m,double &b[],const int n,const bool circular,int alg,int q,double &r[]); }; //+------------------------------------------------------------------+ //| 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), | //| 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. | //+------------------------------------------------------------------+ void CConv::ConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]) { //--- check if(!CAp::Assert(n>0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer that B. if(m0 && m>0) && n<=m,__FUNCTION__+": incorrect N or M!")) return; //--- function call p=CFtBase::FtBaseFindSmooth(m); CFtBase::FtComplexFFTPlan(p,1,plan); //--- allocation ArrayResize(buf,2*p); //--- copy for(i=0; i<=m-1; i++) { buf[2*i+0]=a[i].real; buf[2*i+1]=a[i].imag; } //--- make zero for(i=m; i<=p-1; i++) { buf[2*i+0]=0; buf[2*i+1]=0; } //--- allocation ArrayResize(buf2,2*p); //--- copy for(i=0; i<=n-1; i++) { buf2[2*i+0]=b[i].real; buf2[2*i+1]=b[i].imag; } //--- make zero for(i=n; i<=p-1; i++) { buf2[2*i+0]=0; buf2[2*i+1]=0; } //--- function call CFtBase::FtApplyPlan(plan,buf,0,1); CFtBase::FtApplyPlan(plan,buf2,0,1); //--- calculation for(i=0; i<=p-1; i++) { c1.real=buf[2*i+0]; c1.imag=buf[2*i+1]; c2.real=buf2[2*i+0]; c2.imag=buf2[2*i+1]; c3=c1/c2; buf[2*i+0]=c3.real; buf[2*i+1]=-c3.imag; } //--- function call CFtBase::FtApplyPlan(plan,buf,0,1); t=1.0/(double)p; //--- allocation ArrayResize(r,m-n+1); //--- change values for(i=0; i<=m-n; i++) { r[i].real=t*buf[2*i+0]; r[i].imag=-(t*buf[2*i+1]); } } //+------------------------------------------------------------------+ //| 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. | //+------------------------------------------------------------------+ void CConv::ConvC1DCircular(complex &s[],const int m,complex &r[], const int n,complex &c[]) { //--- create variables int i1=0; int i2=0; int j2=0; int i_=0; int i1_=0; //--- create array complex buf[]; //--- check if(!CAp::Assert(n>0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer (at least - not shorter) that B. if(m0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer (at least - not shorter) that B. if(m0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer that B. if(m0 && m>0) && n<=m,__FUNCTION__+": incorrect N or M!")) return; //--- function call p=CFtBase::FtBaseFindSmoothEven(m); //--- allocation ArrayResize(buf,p); //--- copy for(i_=0; i_<=m-1; i_++) buf[i_]=a[i_]; //--- make zero for(i=m; i<=p-1; i++) buf[i]=0; //--- allocation ArrayResize(buf2,p); for(i_=0; i_<=n-1; i_++) buf2[i_]=b[i_]; //--- make zero for(i=n; i<=p-1; i++) buf2[i]=0; //--- allocation ArrayResize(buf3,p); //--- function call CFtBase::FtComplexFFTPlan(p/2,1,plan); //--- function call CFastFourierTransform::FFTR1DInternalEven(buf,p,buf3,plan); //--- function call CFastFourierTransform::FFTR1DInternalEven(buf2,p,buf3,plan); //--- change values buf[0]=buf[0]/buf2[0]; buf[1]=buf[1]/buf2[1]; //--- calculation for(i=1; i<=p/2-1; i++) { c1.real=buf[2*i+0]; c1.imag=buf[2*i+1]; c2.real=buf2[2*i+0]; c2.imag=buf2[2*i+1]; c3=c1/c2; buf[2*i+0]=c3.real; buf[2*i+1]=c3.imag; } //--- function call CFastFourierTransform::FFTR1DInvInternalEven(buf,p,buf3,plan); //--- allocation ArrayResize(r,m-n+1); //--- copy for(i_=0; i_<=m-n; i_++) r[i_]=buf[i_]; } //+------------------------------------------------------------------+ //| 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. | //+------------------------------------------------------------------+ void CConv::ConvR1DCircular(double &s[],const int m,double &r[], const int n,double &c[]) { //--- create variables int i1=0; int i2=0; int j2=0; int i_=0; int i1_=0; //--- create array double buf[]; //--- check if(!CAp::Assert(n>0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer (at least - not shorter) that B. if(m0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer (at least - not shorter) that B. if(m0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- check if(!CAp::Assert(n<=m,__FUNCTION__+": N0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- check if(!CAp::Assert(n<=m,__FUNCTION__+": N2 - we should call small case code otherwise if(alg==1) { //--- check if(!CAp::Assert(m+n-1>2,__FUNCTION__+": internal error!")) return; //--- check if((circular && CFtBase::FtBaseIsSmooth(m)) && m%2==0) { //--- special code for circular convolution with smooth even M ArrayResize(buf,m); for(i_=0; i_<=m-1; i_++) buf[i_]=a[i_]; //--- allocation ArrayResize(buf2,m); for(i_=0; i_<=n-1; i_++) buf2[i_]=b[i_]; for(i=n; i<=m-1; i++) buf2[i]=0; //--- allocation ArrayResize(buf3,m); //--- function call CFtBase::FtComplexFFTPlan(m/2,1,plan); //--- function call CFastFourierTransform::FFTR1DInternalEven(buf,m,buf3,plan); //--- function call CFastFourierTransform::FFTR1DInternalEven(buf2,m,buf3,plan); //--- change values buf[0]=buf[0]*buf2[0]; buf[1]=buf[1]*buf2[1]; //--- calculation for(i=1; i<=m/2-1; i++) { ax=buf[2*i+0]; ay=buf[2*i+1]; bx=buf2[2*i+0]; by=buf2[2*i+1]; tx=ax*bx-ay*by; ty=ax*by+ay*bx; buf[2*i+0]=tx; buf[2*i+1]=ty; } //--- function call CFastFourierTransform::FFTR1DInvInternalEven(buf,m,buf3,plan); //--- allocation ArrayResize(r,m); //--- copy for(i_=0; i_<=m-1; i_++) r[i_]=buf[i_]; } 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=CFtBase::FtBaseFindSmoothEven(m+n-1); //--- allocation ArrayResize(buf,p); for(i_=0; i_<=m-1; i_++) buf[i_]=a[i_]; for(i=m; i<=p-1; i++) buf[i]=0; //--- allocation ArrayResize(buf2,p); for(i_=0; i_<=n-1; i_++) buf2[i_]=b[i_]; for(i=n; i<=p-1; i++) buf2[i]=0; //--- allocation ArrayResize(buf3,p); //--- function call CFtBase::FtComplexFFTPlan(p/2,1,plan); //--- function call CFastFourierTransform::FFTR1DInternalEven(buf,p,buf3,plan); //--- function call CFastFourierTransform::FFTR1DInternalEven(buf2,p,buf3,plan); //--- change values buf[0]=buf[0]*buf2[0]; buf[1]=buf[1]*buf2[1]; //--- calculation for(i=1; i<=p/2-1; i++) { ax=buf[2*i+0]; ay=buf[2*i+1]; bx=buf2[2*i+0]; by=buf2[2*i+1]; tx=ax*bx-ay*by; ty=ax*by+ay*bx; buf[2*i+0]=tx; buf[2*i+1]=ty; } //--- function call CFastFourierTransform::FFTR1DInvInternalEven(buf,p,buf3,plan); //--- check if(circular) { //--- circular,add tail to head ArrayResize(r,m); for(i_=0; i_<=m-1; i_++) r[i_]=buf[i_]; //--- check if(n>=2) { i1_=m; for(i_=0; i_<=n-2; i_++) r[i_]=r[i_]+buf[i_+i1_]; } } else { //--- non-circular,just copy ArrayResize(r,m+n-1); for(i_=0; i_<=m+n-2; i_++) r[i_]=buf[i_]; } } //--- exit the function return; } //--- overlap-add method if(alg==2) { //--- check if(!CAp::Assert((q+n-1)%2==0,__FUNCTION__+": internal error!")) return; //--- allocation ArrayResize(buf,q+n-1); ArrayResize(buf2,q+n-1); ArrayResize(buf3,q+n-1); //--- function call CFtBase::FtComplexFFTPlan((q+n-1)/2,1,plan); //--- prepare R if(circular) { //--- allocation ArrayResize(r,m); for(i=0; i<=m-1; i++) r[i]=0; } else { //--- allocation ArrayResize(r,m+n-1); for(i=0; i<=m+n-2; i++) r[i]=0; } //--- pre-calculated FFT(B) for(i_=0; i_<=n-1; i_++) buf2[i_]=b[i_]; for(j=n; j<=q+n-2; j++) buf2[j]=0; //--- function call CFastFourierTransform::FFTR1DInternalEven(buf2,q+n-1,buf3,plan); //--- main overlap-add cycle i=0; //--- calculation while(i<=m-1) { p=MathMin(q,m-i); i1_=i; //--- copy for(i_=0; i_<=p-1; i_++) buf[i_]=a[i_+i1_]; //--- make zero for(j=p; j<=q+n-2; j++) buf[j]=0; //--- function call CFastFourierTransform::FFTR1DInternalEven(buf,q+n-1,buf3,plan); //--- change values buf[0]=buf[0]*buf2[0]; buf[1]=buf[1]*buf2[1]; //--- calculation for(j=1; j<=(q+n-1)/2-1; j++) { ax=buf[2*j+0]; ay=buf[2*j+1]; bx=buf2[2*j+0]; by=buf2[2*j+1]; tx=ax*bx-ay*by; ty=ax*by+ay*bx; buf[2*j+0]=tx; buf[2*j+1]=ty; } //--- function call CFastFourierTransform::FFTR1DInvInternalEven(buf,q+n-1,buf3,plan); //--- check if(circular) { j1=MathMin(i+p+n-2,m-1)-i; j2=j1+1; } else { j1=p+n-2; j2=j1+1; } //--- change values i1_=-i; for(i_=i; i_<=i+j1; i_++) r[i_]=r[i_]+buf[i_+i1_]; //--- check if(p+n-2>=j2) { i1_=j2; for(i_=0; i_<=p+n-2-j2; i_++) r[i_]=r[i_]+buf[i_+i1_]; } i=i+p; } //--- exit the function return; } } //+------------------------------------------------------------------+ //| Cross-correlation class | //+------------------------------------------------------------------+ class CCorr { public: static void CorrC1D(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]); static void CorrC1DCircular(complex &signal[],const int m,complex &pattern[],const int n,complex &c[]); static void CorrR1D(double &signal[],const int n,double &pattern[],const int m,double &r[]); static void CorrR1DCircular(double &signal[],const int m,double &pattern[],const int n,double &c[]); }; //+------------------------------------------------------------------+ //| 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. | //+------------------------------------------------------------------+ void CCorr::CorrC1D(complex &signal[],const int n,complex &pattern[], const int m,complex &r[]) { //--- create variables int i=0; int i_=0; int i1_=0; //--- create arrays complex p[]; complex b[]; //--- check if(!CAp::Assert(n>0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- allocation ArrayResize(p,m); for(i=0; i<=m-1; i++) p[m-1-i]=CMath::Conj(pattern[i]); //--- function call CConv::ConvC1D(p,m,signal,n,b); //--- allocation ArrayResize(r,m+n-1); i1_=m-1; for(i_=0; i_<=n-1; i_++) r[i_]=b[i_+i1_]; //--- check if(m+n-2>=n) { i1_=-n; for(i_=n; i_<=m+n-2; i_++) r[i_]=b[i_+i1_]; } } //+------------------------------------------------------------------+ //| 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]. | //+------------------------------------------------------------------+ void CCorr::CorrC1DCircular(complex &signal[],const int m,complex &pattern[], const int n,complex &c[]) { //--- create variables int i1=0; int i2=0; int i=0; int j2=0; int i_=0; int i1_=0; //--- create arrays complex p[]; complex b[]; //--- check if(!CAp::Assert(n>0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer (at least - not shorter) that B. if(m0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- allocation ArrayResize(p,m); //--- copy for(i=0; i<=m-1; i++) p[m-1-i]=pattern[i]; //--- function call CConv::ConvR1D(p,m,signal,n,b); //--- allocation ArrayResize(r,m+n-1); i1_=m-1; //--- copy for(i_=0; i_<=n-1; i_++) r[i_]=b[i_+i1_]; //--- check if(m+n-2>=n) { i1_=-n; for(i_=n; i_<=m+n-2; i_++) r[i_]=b[i_+i1_]; } } //+------------------------------------------------------------------+ //| 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]. | //+------------------------------------------------------------------+ void CCorr::CorrR1DCircular(double &signal[],const int m,double &pattern[], const int n,double &c[]) { //--- create variables int i1=0; int i2=0; int i=0; int j2=0; int i_=0; int i1_=0; //--- create arrays double p[]; double b[]; //--- check if(!CAp::Assert(n>0 && m>0,__FUNCTION__+": incorrect N or M!")) return; //--- normalize task: make M>=N, //--- so A will be longer (at least - not shorter) that B. if(m0,__FUNCTION__+": incorrect N!")) return; //--- Special case: N=1,FHT is just identity transform. //--- After this block we assume that N is strictly greater than 1. if(n==1) return; //--- Reduce FHt to real FFT CFastFourierTransform::FFTR1D(a,n,fa); //--- change values for(i=0; i<=n-1; i++) a[i]=fa[i].real-fa[i].imag; } //+------------------------------------------------------------------+ //| 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] | //+------------------------------------------------------------------+ void CFastHartleyTransform::FHTR1DInv(double &a[],const int n) { //--- create a variable int i=0; //--- check if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!")) return; //--- 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); //--- change values for(i=0; i<=n-1; i++) a[i]=a[i]/n; } //+------------------------------------------------------------------+