Repository URL to install this package:
Version:
3.0.0 ▾
|
(*************************************************************************
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 >>>
*************************************************************************)
unit u_fft;
interface
uses Math, Sysutils, u_ap, u_ftbase;
procedure FFTC1D(var A : TComplex1DArray; N : Integer);
procedure FFTC1DInv(var A : TComplex1DArray; N : Integer);
procedure FFTR1D(const A : TReal1DArray; N : Integer; var F : TComplex1DArray);
procedure FFTR1DInv(const F : TComplex1DArray;
N : Integer;
var A : TReal1DArray);
procedure FFTR1DInternalEven(var A : TReal1DArray;
N : Integer;
var Buf : TReal1DArray;
var Plan : FTPlan);
procedure FFTR1DInvInternalEven(var A : TReal1DArray;
N : Integer;
var Buf : TReal1DArray;
var Plan : FTPlan);
implementation
(*************************************************************************
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
*************************************************************************)
procedure FFTC1D(var A : TComplex1DArray; N : Integer);
var
Plan : FTPlan;
I : Integer;
Buf : TReal1DArray;
begin
Assert(N>0, 'FFTC1D: incorrect N!');
//
// Special case: N=1, FFT is just identity transform.
// After this block we assume that N is strictly greater than 1.
//
if N=1 then
begin
Exit;
end;
//
// convert input array to the more convinient format
//
SetLength(Buf, 2*N);
I:=0;
while I<=N-1 do
begin
Buf[2*I+0] := A[I].X;
Buf[2*I+1] := A[I].Y;
Inc(I);
end;
//
// 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.
//
FTBaseGenerateComplexFFTPlan(N, Plan);
FTBaseExecutePlan(Buf, 0, N, Plan);
//
// result
//
I:=0;
while I<=N-1 do
begin
A[I].X := Buf[2*I+0];
A[I].Y := Buf[2*I+1];
Inc(I);
end;
end;
(*************************************************************************
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
*************************************************************************)
procedure FFTC1DInv(var A : TComplex1DArray; N : Integer);
var
I : Integer;
begin
Assert(N>0, 'FFTC1DInv: incorrect N!');
//
// Inverse DFT can be expressed in terms of the DFT as
//
// invfft(x) = fft(x')'/N
//
// here x' means conj(x).
//
I:=0;
while I<=N-1 do
begin
A[I].Y := -A[I].Y;
Inc(I);
end;
FFTC1D(A, N);
I:=0;
while I<=N-1 do
begin
A[I].X := A[I].X/N;
A[I].Y := -A[I].Y/N;
Inc(I);
end;
end;
(*************************************************************************
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
*************************************************************************)
procedure FFTR1D(const A : TReal1DArray; N : Integer; var F : TComplex1DArray);
var
I : Integer;
N2 : Integer;
Idx : Integer;
Hn : Complex;
HmnC : Complex;
V : Complex;
Buf : TReal1DArray;
Plan : FTPlan;
begin
Assert(N>0, 'FFTR1D: incorrect N!');
//
// 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 then
begin
SetLength(F, 1);
F[0] := C_Complex(A[0]);
Exit;
end;
if N=2 then
begin
SetLength(F, 2);
F[0].X := A[0]+A[1];
F[0].Y := 0;
F[1].X := A[0]-A[1];
F[1].Y := 0;
Exit;
end;
//
// Choose between odd-size and even-size FFTs
//
if N mod 2=0 then
begin
//
// even-size real FFT, use reduction to the complex task
//
N2 := N div 2;
SetLength(Buf, N);
APVMove(@Buf[0], 0, N-1, @A[0], 0, N-1);
FTBaseGenerateComplexFFTPlan(N2, Plan);
FTBaseExecutePlan(Buf, 0, N2, Plan);
SetLength(F, N);
I:=0;
while I<=N2 do
begin
Idx := 2*(I mod N2);
Hn.X := Buf[Idx+0];
Hn.Y := Buf[Idx+1];
Idx := 2*((N2-I) mod N2);
HmnC.X := Buf[Idx+0];
HmnC.Y := -Buf[Idx+1];
V.X := -Sin(-2*Pi*I/N);
V.Y := Cos(-2*Pi*I/N);
F[I] := C_Sub(C_Add(Hn,HmnC),C_Mul(V,C_Sub(Hn,HmnC)));
F[I].X := Double(0.5)*F[I].X;
F[I].Y := Double(0.5)*F[I].Y;
Inc(I);
end;
I:=N2+1;
while I<=N-1 do
begin
F[I] := Conj(F[N-I]);
Inc(I);
end;
Exit;
end
else
begin
//
// use complex FFT
//
SetLength(F, N);
I:=0;
while I<=N-1 do
begin
F[I] := C_Complex(A[I]);
Inc(I);
end;
FFTC1D(F, N);
Exit;
end;
end;
(*************************************************************************
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. So you can pass
either frequencies array with N elements or reduced array with roughly N/2
elements - subroutine will successfully transform both.
-- ALGLIB --
Copyright 01.06.2009 by Bochkanov Sergey
*************************************************************************)
procedure FFTR1DInv(const F : TComplex1DArray;
N : Integer;
var A : TReal1DArray);
var
I : Integer;
H : TReal1DArray;
FH : TComplex1DArray;
begin
Assert(N>0, 'FFTR1DInv: incorrect N!');
//
// Special case: N=1, FFT is just identity transform.
// After this block we assume that N is strictly greater than 1.
//
if N=1 then
begin
SetLength(A, 1);
A[0] := F[0].X;
Exit;
end;
//
// 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 :)
//
SetLength(H, N);
SetLength(A, N);
H[0] := F[0].X;
I:=1;
while I<=Floor(AP_Double(N)/2)-1 do
begin
H[I] := F[I].X-F[I].Y;
H[N-I] := F[I].X+F[I].Y;
Inc(I);
end;
if N mod 2=0 then
begin
H[Floor(AP_Double(N)/2)] := F[Floor(AP_Double(N)/2)].X;
end
else
begin
H[Floor(AP_Double(N)/2)] := F[Floor(AP_Double(N)/2)].X-F[Floor(AP_Double(N)/2)].Y;
H[Floor(AP_Double(N)/2)+1] := F[Floor(AP_Double(N)/2)].X+F[Floor(AP_Double(N)/2)].Y;
end;
FFTR1D(H, N, FH);
I:=0;
while I<=N-1 do
begin
A[I] := (FH[I].X-FH[I].Y)/N;
Inc(I);
end;
end;
(*************************************************************************
Internal subroutine. Never call it directly!
-- ALGLIB --
Copyright 01.06.2009 by Bochkanov Sergey
*************************************************************************)
procedure FFTR1DInternalEven(var A : TReal1DArray;
N : Integer;
var Buf : TReal1DArray;
var Plan : FTPlan);
var
X : Double;
Y : Double;
I : Integer;
N2 : Integer;
Idx : Integer;
Hn : Complex;
HmnC : Complex;
V : Complex;
begin
Assert((N>0) and (N mod 2=0), 'FFTR1DEvenInplace: incorrect N!');
//
// Special cases:
// * N=2
//
// After this block we assume that N is strictly greater than 2
//
if N=2 then
begin
X := A[0]+A[1];
Y := A[0]-A[1];
A[0] := X;
A[1] := Y;
Exit;
end;
//
// even-size real FFT, use reduction to the complex task
//
N2 := N div 2;
APVMove(@Buf[0], 0, N-1, @A[0], 0, N-1);
FTBaseExecutePlan(Buf, 0, N2, Plan);
A[0] := Buf[0]+Buf[1];
I:=1;
while I<=N2-1 do
begin
Idx := 2*(I mod N2);
Hn.X := Buf[Idx+0];
Hn.Y := Buf[Idx+1];
Idx := 2*(N2-I);
HmnC.X := Buf[Idx+0];
HmnC.Y := -Buf[Idx+1];
V.X := -Sin(-2*Pi*I/N);
V.Y := Cos(-2*Pi*I/N);
V := C_Sub(C_Add(Hn,HmnC),C_Mul(V,C_Sub(Hn,HmnC)));
A[2*I+0] := Double(0.5)*V.X;
A[2*I+1] := Double(0.5)*V.Y;
Inc(I);
end;
A[1] := Buf[0]-Buf[1];
end;
(*************************************************************************
Internal subroutine. Never call it directly!
-- ALGLIB --
Copyright 01.06.2009 by Bochkanov Sergey
*************************************************************************)
procedure FFTR1DInvInternalEven(var A : TReal1DArray;
N : Integer;
var Buf : TReal1DArray;
var Plan : FTPlan);
var
X : Double;
Y : Double;
T : Double;
I : Integer;
N2 : Integer;
begin
Assert((N>0) and (N mod 2=0), 'FFTR1DInvInternalEven: incorrect N!');
//
// Special cases:
// * N=2
//
// After this block we assume that N is strictly greater than 2
//
if N=2 then
begin
X := Double(0.5)*(A[0]+A[1]);
Y := Double(0.5)*(A[0]-A[1]);
A[0] := X;
A[1] := Y;
Exit;
end;
//
// 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 div 2;
Buf[0] := A[0];
I:=1;
while I<=N2-1 do
begin
X := A[2*I+0];
Y := A[2*I+1];
Buf[I] := X-Y;
Buf[N-I] := X+Y;
Inc(I);
end;
Buf[N2] := A[1];
FFTR1DInternalEven(Buf, N, A, Plan);
A[0] := Buf[0]/N;
T := AP_Double(1)/N;
I:=1;
while I<=N2-1 do
begin
X := Buf[2*I+0];
Y := Buf[2*I+1];
A[I] := T*(X-Y);
A[N-I] := T*(X+Y);
Inc(I);
end;
A[N2] := Buf[1]/N;
end;
end.