-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrfft.hpp
110 lines (94 loc) · 2.99 KB
/
rfft.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#ifndef RFFT_HPP
#define RFFT_HPP
// Reasonably Fast Fourier Transform (in public domain)
// Perform the FFT in place for an array of size 2^k.
// No normalization is done.
void fft_transform_radix2(std::vector<std::complex<double>>& vec, bool inverse);
// Perform the FFT for an arbitrary array using the Bluestein's algorithm.
// The code needs to allocate two supplementary buffers (of size < 4 * n - 3).
// No normalization is done.
void fft_transform_bluestein(std::vector<std::complex<double>>& vec, bool inverse);
// Perform the FFT, choosing the suitable algorithm from the two above.
void fft_transform(std::vector<std::complex<double>>& vec, bool inverse);
#endif // RFFT_HPP
#ifdef RFFT_IMPLEMENTATION
#include <complex>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void fft_transform_radix2(std::vector<std::complex<double>>& vec, bool inverse) {
size_t n = vec.size();
int levels = 0; // Compute levels = floor(log2(n))
for (size_t k = 1; (k & n) == 0; k <<= 1)
levels++;
// Permute vec by reversing the bits of addresses
for (size_t i = 0; i < n; i++) {
// Reverse the bits of i
size_t j = 0, ii = i;
for (int k = 0; k < levels; k++) {
j = (j << 1) | (ii & 1);
ii >>= 1;
}
if (j > i) {
std::complex<double> tmp = vec[i];
vec[i] = vec[j];
vec[j] = tmp;
}
}
// Cooley-Tukey in place
for (size_t half = 1; half < n; half *= 2) {
size_t size = 2 * half;
size_t step = n / size;
for (size_t j = 0, k = 0; j < half; j++, k += step) {
double angle = (inverse ? 2 : -2) * M_PI * k / n;
std::complex<double> omega = std::polar(1.0, angle);
for (size_t i = 0; i < n; i += size) {
std::complex<double> tmp = vec[i + j + half] * omega;
vec[i + j + half] = vec[i + j] - tmp;
vec[i + j] += tmp;
}
}
}
}
void fft_transform_bluestein(std::vector<std::complex<double>>& vec, bool inverse) {
size_t n = vec.size();
// Find m = 2^k such that m >= 2 * n + 1
size_t m = 1;
while (m <= 2 * n) {
m *= 2;
}
// Extended vectors of size m
std::vector<std::complex<double>> avec(m), bvec(m);
avec[0] = vec[0];
bvec[0] = 1.0;
for (size_t i = 1; i < n; i++) {
size_t k = (i * i) % (2 * n);
double angle = (inverse ? M_PI : -M_PI) * k / n;
std::complex<double> omega = std::polar(1.0, angle);
avec[i] = vec[i] * omega;
bvec[i] = bvec[m - i] = std::conj(omega);
}
// Convolution
fft_transform_radix2(avec, false);
fft_transform_radix2(bvec, false);
for (size_t i = 0; i < m; i++) {
avec[i] *= bvec[i];
}
fft_transform_radix2(avec, true);
for (size_t i = 0; i < n; i++) {
size_t k = (i * i) % (2 * n);
double angle = (inverse ? M_PI : -M_PI) * k / n;
std::complex<double> omega = std::polar(1.0, angle);
vec[i] = avec[i] * omega / double(m);
}
}
void fft_transform(std::vector<std::complex<double>>& vec, bool inverse) {
size_t n = vec.size();
if (n <= 1)
return;
else if ((n & (n - 1)) == 0) // Power of 2
fft_transform_radix2(vec, inverse);
else
fft_transform_bluestein(vec, inverse);
}
#endif // RFFT_IMPLEMENTATION