/************************************************************************* Copyright (c) 2009, 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 >>> *************************************************************************/ using System; namespace alglib { public class conv { /************************************************************************* 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 *************************************************************************/ public static void convc1d(ref AP.Complex[] a, int m, ref AP.Complex[] b, int n, ref AP.Complex[] r) { System.Diagnostics.Debug.Assert(n>0 & m>0, "ConvC1D: incorrect N or M!"); // // normalize task: make M>=N, // so A will be longer that B. // if( m0 & m>0 & n<=m, "ConvC1DInv: incorrect N or M!"); p = ftbase.ftbasefindsmooth(m); ftbase.ftbasegeneratecomplexfftplan(p, ref plan); buf = new double[2*p]; for(i=0; i<=m-1; i++) { buf[2*i+0] = a[i].x; buf[2*i+1] = a[i].y; } for(i=m; i<=p-1; i++) { buf[2*i+0] = 0; buf[2*i+1] = 0; } buf2 = new double[2*p]; for(i=0; i<=n-1; i++) { buf2[2*i+0] = b[i].x; buf2[2*i+1] = b[i].y; } for(i=n; i<=p-1; i++) { buf2[2*i+0] = 0; buf2[2*i+1] = 0; } ftbase.ftbaseexecuteplan(ref buf, 0, p, ref plan); ftbase.ftbaseexecuteplan(ref buf2, 0, p, ref plan); for(i=0; i<=p-1; i++) { c1.x = buf[2*i+0]; c1.y = buf[2*i+1]; c2.x = buf2[2*i+0]; c2.y = buf2[2*i+1]; c3 = c1/c2; buf[2*i+0] = c3.x; buf[2*i+1] = -c3.y; } ftbase.ftbaseexecuteplan(ref buf, 0, p, ref plan); t = (double)(1)/(double)(p); r = new AP.Complex[m-n+1]; for(i=0; i<=m-n; i++) { r[i].x = +(t*buf[2*i+0]); r[i].y = -(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 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 *************************************************************************/ public static void convc1dcircular(ref AP.Complex[] s, int m, ref AP.Complex[] r, int n, ref AP.Complex[] c) { AP.Complex[] buf = new AP.Complex[0]; int i1 = 0; int i2 = 0; int j2 = 0; int i_ = 0; int i1_ = 0; System.Diagnostics.Debug.Assert(n>0 & m>0, "ConvC1DCircular: incorrect N or M!"); // // normalize task: make M>=N, // so A will be longer (at least - not shorter) that B. // if( m0 & m>0, "ConvC1DCircularInv: incorrect N or M!"); // // normalize task: make M>=N, // so A will be longer (at least - not shorter) that B. // if( m0 & m>0, "ConvR1D: incorrect N or M!"); // // normalize task: make M>=N, // so A will be longer that B. // if( m0 & m>0 & n<=m, "ConvR1DInv: incorrect N or M!"); p = ftbase.ftbasefindsmootheven(m); buf = new double[p]; for(i_=0; i_<=m-1;i_++) { buf[i_] = a[i_]; } for(i=m; i<=p-1; i++) { buf[i] = 0; } buf2 = new double[p]; for(i_=0; i_<=n-1;i_++) { buf2[i_] = b[i_]; } for(i=n; i<=p-1; i++) { buf2[i] = 0; } buf3 = new double[p]; ftbase.ftbasegeneratecomplexfftplan(p/2, ref plan); fft.fftr1dinternaleven(ref buf, p, ref buf3, ref plan); fft.fftr1dinternaleven(ref buf2, p, ref buf3, ref plan); buf[0] = buf[0]/buf2[0]; buf[1] = buf[1]/buf2[1]; for(i=1; i<=p/2-1; i++) { c1.x = buf[2*i+0]; c1.y = buf[2*i+1]; c2.x = buf2[2*i+0]; c2.y = buf2[2*i+1]; c3 = c1/c2; buf[2*i+0] = c3.x; buf[2*i+1] = c3.y; } fft.fftr1dinvinternaleven(ref buf, p, ref buf3, ref plan); r = new double[m-n+1]; 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 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 *************************************************************************/ public static void convr1dcircular(ref double[] s, int m, ref double[] r, int n, ref double[] c) { double[] buf = new double[0]; int i1 = 0; int i2 = 0; int j2 = 0; int i_ = 0; int i1_ = 0; System.Diagnostics.Debug.Assert(n>0 & m>0, "ConvC1DCircular: incorrect N or M!"); // // normalize task: make M>=N, // so A will be longer (at least - not shorter) that B. // if( m0 & m>0, "ConvR1DCircularInv: incorrect N or M!"); // // normalize task: make M>=N, // so A will be longer (at least - not shorter) that B. // if( m0 & m>0, "ConvC1DX: incorrect N or M!"); System.Diagnostics.Debug.Assert(n<=m, "ConvC1DX: N0 & m>0, "ConvC1DX: incorrect N or M!"); System.Diagnostics.Debug.Assert(n<=m, "ConvC1DX: N2 - we should call small case code otherwise // if( alg==1 ) { System.Diagnostics.Debug.Assert(m+n-1>2, "ConvR1DX: internal error!"); if( circular & ftbase.ftbaseissmooth(m) & m%2==0 ) { // // special code for circular convolution with smooth even M // buf = new double[m]; for(i_=0; i_<=m-1;i_++) { buf[i_] = a[i_]; } buf2 = new double[m]; for(i_=0; i_<=n-1;i_++) { buf2[i_] = b[i_]; } for(i=n; i<=m-1; i++) { buf2[i] = 0; } buf3 = new double[m]; ftbase.ftbasegeneratecomplexfftplan(m/2, ref plan); fft.fftr1dinternaleven(ref buf, m, ref buf3, ref plan); fft.fftr1dinternaleven(ref buf2, m, ref buf3, ref plan); buf[0] = buf[0]*buf2[0]; buf[1] = buf[1]*buf2[1]; 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; } fft.fftr1dinvinternaleven(ref buf, m, ref buf3, ref plan); r = new double[m]; 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 = ftbase.ftbasefindsmootheven(m+n-1); buf = new double[p]; for(i_=0; i_<=m-1;i_++) { buf[i_] = a[i_]; } for(i=m; i<=p-1; i++) { buf[i] = 0; } buf2 = new double[p]; for(i_=0; i_<=n-1;i_++) { buf2[i_] = b[i_]; } for(i=n; i<=p-1; i++) { buf2[i] = 0; } buf3 = new double[p]; ftbase.ftbasegeneratecomplexfftplan(p/2, ref plan); fft.fftr1dinternaleven(ref buf, p, ref buf3, ref plan); fft.fftr1dinternaleven(ref buf2, p, ref buf3, ref plan); buf[0] = buf[0]*buf2[0]; buf[1] = buf[1]*buf2[1]; 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; } fft.fftr1dinvinternaleven(ref buf, p, ref buf3, ref plan); if( circular ) { // // circular, add tail to head // r = new double[m]; for(i_=0; i_<=m-1;i_++) { r[i_] = buf[i_]; } if( n>=2 ) { i1_ = (m) - (0); for(i_=0; i_<=n-2;i_++) { r[i_] = r[i_] + buf[i_+i1_]; } } } else { // // non-circular, just copy // r = new double[m+n-1]; for(i_=0; i_<=m+n-2;i_++) { r[i_] = buf[i_]; } } } return; } // // overlap-add method // if( alg==2 ) { System.Diagnostics.Debug.Assert((q+n-1)%2==0, "ConvR1DX: internal error!"); buf = new double[q+n-1]; buf2 = new double[q+n-1]; buf3 = new double[q+n-1]; ftbase.ftbasegeneratecomplexfftplan((q+n-1)/2, ref plan); // // prepare R // if( circular ) { r = new double[m]; for(i=0; i<=m-1; i++) { r[i] = 0; } } else { r = new double[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; } fft.fftr1dinternaleven(ref buf2, q+n-1, ref buf3, ref plan); // // main overlap-add cycle // i = 0; while( i<=m-1 ) { p = Math.Min(q, m-i); i1_ = (i) - (0); for(i_=0; i_<=p-1;i_++) { buf[i_] = a[i_+i1_]; } for(j=p; j<=q+n-2; j++) { buf[j] = 0; } fft.fftr1dinternaleven(ref buf, q+n-1, ref buf3, ref plan); buf[0] = buf[0]*buf2[0]; buf[1] = buf[1]*buf2[1]; 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; } fft.fftr1dinvinternaleven(ref buf, q+n-1, ref buf3, ref plan); if( circular ) { j1 = Math.Min(i+p+n-2, m-1)-i; j2 = j1+1; } else { j1 = p+n-2; j2 = j1+1; } i1_ = (0) - (i); for(i_=i; i_<=i+j1;i_++) { r[i_] = r[i_] + buf[i_+i1_]; } if( p+n-2>=j2 ) { i1_ = (j2) - (0); for(i_=0; i_<=p+n-2-j2;i_++) { r[i_] = r[i_] + buf[i_+i1_]; } } i = i+p; } return; } } } }