1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.transform;
18
19 import java.util.Arrays;
20 import java.util.function.DoubleUnaryOperator;
21
22 import org.apache.commons.numbers.core.ArithmeticUtils;
23 import org.apache.commons.numbers.complex.Complex;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class FastFourierTransform implements ComplexTransform {
43
44 private static final int NUM_PARTS = 2;
45
46
47
48
49
50 private static final double[] W_SUB_N_R = {
51 0x1.0p0, -0x1.0p0, 0x1.1a62633145c07p-54, 0x1.6a09e667f3bcdp-1,
52 0x1.d906bcf328d46p-1, 0x1.f6297cff75cbp-1, 0x1.fd88da3d12526p-1, 0x1.ff621e3796d7ep-1,
53 0x1.ffd886084cd0dp-1, 0x1.fff62169b92dbp-1, 0x1.fffd8858e8a92p-1, 0x1.ffff621621d02p-1,
54 0x1.ffffd88586ee6p-1, 0x1.fffff62161a34p-1, 0x1.fffffd8858675p-1, 0x1.ffffff621619cp-1,
55 0x1.ffffffd885867p-1, 0x1.fffffff62161ap-1, 0x1.fffffffd88586p-1, 0x1.ffffffff62162p-1,
56 0x1.ffffffffd8858p-1, 0x1.fffffffff6216p-1, 0x1.fffffffffd886p-1, 0x1.ffffffffff621p-1,
57 0x1.ffffffffffd88p-1, 0x1.fffffffffff62p-1, 0x1.fffffffffffd9p-1, 0x1.ffffffffffff6p-1,
58 0x1.ffffffffffffep-1, 0x1.fffffffffffffp-1, 0x1.0p0, 0x1.0p0,
59 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
60 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
61 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
62 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
63 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
64 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
65 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0,
66 0x1.0p0, 0x1.0p0, 0x1.0p0 };
67
68
69
70
71
72
73 private static final double[] W_SUB_N_I = {
74 0x1.1a62633145c07p-52, -0x1.1a62633145c07p-53, -0x1.0p0, -0x1.6a09e667f3bccp-1,
75 -0x1.87de2a6aea963p-2, -0x1.8f8b83c69a60ap-3, -0x1.917a6bc29b42cp-4, -0x1.91f65f10dd814p-5,
76 -0x1.92155f7a3667ep-6, -0x1.921d1fcdec784p-7, -0x1.921f0fe670071p-8, -0x1.921f8becca4bap-9,
77 -0x1.921faaee6472dp-10, -0x1.921fb2aecb36p-11, -0x1.921fb49ee4ea6p-12, -0x1.921fb51aeb57bp-13,
78 -0x1.921fb539ecf31p-14, -0x1.921fb541ad59ep-15, -0x1.921fb5439d73ap-16, -0x1.921fb544197ap-17,
79 -0x1.921fb544387bap-18, -0x1.921fb544403c1p-19, -0x1.921fb544422c2p-20, -0x1.921fb54442a83p-21,
80 -0x1.921fb54442c73p-22, -0x1.921fb54442cefp-23, -0x1.921fb54442d0ep-24, -0x1.921fb54442d15p-25,
81 -0x1.921fb54442d17p-26, -0x1.921fb54442d18p-27, -0x1.921fb54442d18p-28, -0x1.921fb54442d18p-29,
82 -0x1.921fb54442d18p-30, -0x1.921fb54442d18p-31, -0x1.921fb54442d18p-32, -0x1.921fb54442d18p-33,
83 -0x1.921fb54442d18p-34, -0x1.921fb54442d18p-35, -0x1.921fb54442d18p-36, -0x1.921fb54442d18p-37,
84 -0x1.921fb54442d18p-38, -0x1.921fb54442d18p-39, -0x1.921fb54442d18p-40, -0x1.921fb54442d18p-41,
85 -0x1.921fb54442d18p-42, -0x1.921fb54442d18p-43, -0x1.921fb54442d18p-44, -0x1.921fb54442d18p-45,
86 -0x1.921fb54442d18p-46, -0x1.921fb54442d18p-47, -0x1.921fb54442d18p-48, -0x1.921fb54442d18p-49,
87 -0x1.921fb54442d18p-50, -0x1.921fb54442d18p-51, -0x1.921fb54442d18p-52, -0x1.921fb54442d18p-53,
88 -0x1.921fb54442d18p-54, -0x1.921fb54442d18p-55, -0x1.921fb54442d18p-56, -0x1.921fb54442d18p-57,
89 -0x1.921fb54442d18p-58, -0x1.921fb54442d18p-59, -0x1.921fb54442d18p-60 };
90
91
92 private final Norm normalization;
93
94 private final boolean inverse;
95
96
97
98
99
100
101 public FastFourierTransform(final Norm normalization,
102 final boolean inverse) {
103 this.normalization = normalization;
104 this.inverse = inverse;
105 }
106
107
108
109
110
111 public FastFourierTransform(final Norm normalization) {
112 this(normalization, false);
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 public void transformInPlace(final double[][] dataRI) {
130 if (dataRI.length != NUM_PARTS) {
131 throw new TransformException(TransformException.SIZE_MISMATCH,
132 dataRI.length, NUM_PARTS);
133 }
134 final double[] dataR = dataRI[0];
135 final double[] dataI = dataRI[1];
136 if (dataR.length != dataI.length) {
137 throw new TransformException(TransformException.SIZE_MISMATCH,
138 dataI.length, dataR.length);
139 }
140 final int n = dataR.length;
141 if (!ArithmeticUtils.isPowerOfTwo(n)) {
142 throw new TransformException(TransformException.NOT_POWER_OF_TWO,
143 Integer.valueOf(n));
144 }
145
146 if (n == 1) {
147 return;
148 } else if (n == 2) {
149 final double srcR0 = dataR[0];
150 final double srcI0 = dataI[0];
151 final double srcR1 = dataR[1];
152 final double srcI1 = dataI[1];
153
154
155 dataR[0] = srcR0 + srcR1;
156 dataI[0] = srcI0 + srcI1;
157
158 dataR[1] = srcR0 - srcR1;
159 dataI[1] = srcI0 - srcI1;
160
161 normalizeTransformedData(dataRI);
162 return;
163 }
164
165 bitReversalShuffle2(dataR, dataI);
166
167
168 if (inverse) {
169 for (int i0 = 0; i0 < n; i0 += 4) {
170 final int i1 = i0 + 1;
171 final int i2 = i0 + 2;
172 final int i3 = i0 + 3;
173
174 final double srcR0 = dataR[i0];
175 final double srcI0 = dataI[i0];
176 final double srcR1 = dataR[i2];
177 final double srcI1 = dataI[i2];
178 final double srcR2 = dataR[i1];
179 final double srcI2 = dataI[i1];
180 final double srcR3 = dataR[i3];
181 final double srcI3 = dataI[i3];
182
183
184
185 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
186 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
187
188 dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1);
189 dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3);
190
191 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
192 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
193
194 dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3);
195 dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1);
196 }
197 } else {
198 for (int i0 = 0; i0 < n; i0 += 4) {
199 final int i1 = i0 + 1;
200 final int i2 = i0 + 2;
201 final int i3 = i0 + 3;
202
203 final double srcR0 = dataR[i0];
204 final double srcI0 = dataI[i0];
205 final double srcR1 = dataR[i2];
206 final double srcI1 = dataI[i2];
207 final double srcR2 = dataR[i1];
208 final double srcI2 = dataI[i1];
209 final double srcR3 = dataR[i3];
210 final double srcI3 = dataI[i3];
211
212
213
214 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
215 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
216
217 dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3);
218 dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1);
219
220 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
221 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
222
223 dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1);
224 dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3);
225 }
226 }
227
228 int lastN0 = 4;
229 int lastLogN0 = 2;
230 while (lastN0 < n) {
231 final int n0 = lastN0 << 1;
232 final int logN0 = lastLogN0 + 1;
233 final double wSubN0R = W_SUB_N_R[logN0];
234 double wSubN0I = W_SUB_N_I[logN0];
235 if (inverse) {
236 wSubN0I = -wSubN0I;
237 }
238
239
240 for (int destEvenStartIndex = 0; destEvenStartIndex < n; destEvenStartIndex += n0) {
241 final int destOddStartIndex = destEvenStartIndex + lastN0;
242
243 double wSubN0ToRR = 1;
244 double wSubN0ToRI = 0;
245
246 for (int r = 0; r < lastN0; r++) {
247 final int destEvenStartIndexPlusR = destEvenStartIndex + r;
248 final int destOddStartIndexPlusR = destOddStartIndex + r;
249
250 final double grR = dataR[destEvenStartIndexPlusR];
251 final double grI = dataI[destEvenStartIndexPlusR];
252 final double hrR = dataR[destOddStartIndexPlusR];
253 final double hrI = dataI[destOddStartIndexPlusR];
254
255 final double a = wSubN0ToRR * hrR - wSubN0ToRI * hrI;
256 final double b = wSubN0ToRR * hrI + wSubN0ToRI * hrR;
257
258 dataR[destEvenStartIndexPlusR] = grR + a;
259 dataI[destEvenStartIndexPlusR] = grI + b;
260
261 dataR[destOddStartIndexPlusR] = grR - a;
262 dataI[destOddStartIndexPlusR] = grI - b;
263
264
265 final double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I;
266 final double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R;
267 wSubN0ToRR = nextWsubN0ToRR;
268 wSubN0ToRI = nextWsubN0ToRI;
269 }
270 }
271
272 lastN0 = n0;
273 lastLogN0 = logN0;
274 }
275
276 normalizeTransformedData(dataRI);
277 }
278
279
280
281
282
283
284 @Override
285 public Complex[] apply(final double[] f) {
286 final double[][] dataRI = {
287 Arrays.copyOf(f, f.length),
288 new double[f.length]
289 };
290 transformInPlace(dataRI);
291 return TransformUtils.createComplex(dataRI);
292 }
293
294
295
296
297
298
299
300
301
302 @Override
303 public Complex[] apply(final DoubleUnaryOperator f,
304 final double min,
305 final double max,
306 final int n) {
307 return apply(TransformUtils.sample(f, min, max, n));
308 }
309
310
311
312
313
314
315
316 @Override
317 public Complex[] apply(final Complex[] f) {
318 final double[][] dataRI = TransformUtils.createRealImaginary(f);
319 transformInPlace(dataRI);
320 return TransformUtils.createComplex(dataRI);
321 }
322
323
324
325
326
327
328 private void normalizeTransformedData(final double[][] dataRI) {
329 final double[] dataR = dataRI[0];
330 final double[] dataI = dataRI[1];
331 final int n = dataR.length;
332
333 switch (normalization) {
334 case STD:
335 if (inverse) {
336 final double scaleFactor = 1d / n;
337 for (int i = 0; i < n; i++) {
338 dataR[i] *= scaleFactor;
339 dataI[i] *= scaleFactor;
340 }
341 }
342
343 break;
344
345 case UNIT:
346 final double scaleFactor = 1d / Math.sqrt(n);
347 for (int i = 0; i < n; i++) {
348 dataR[i] *= scaleFactor;
349 dataI[i] *= scaleFactor;
350 }
351
352 break;
353
354 default:
355 throw new IllegalStateException();
356 }
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370
371 private static void bitReversalShuffle2(double[] a,
372 double[] b) {
373 final int n = a.length;
374 final int halfOfN = n >> 1;
375
376 int j = 0;
377 for (int i = 0; i < n; i++) {
378 if (i < j) {
379
380 double temp = a[i];
381 a[i] = a[j];
382 a[j] = temp;
383
384 temp = b[i];
385 b[i] = b[j];
386 b[j] = temp;
387 }
388
389 int k = halfOfN;
390 while (k <= j && k > 0) {
391 j -= k;
392 k >>= 1;
393 }
394 j += k;
395 }
396 }
397
398
399
400
401 public enum Norm {
402
403
404
405
406
407
408
409
410
411
412 STD,
413
414
415
416
417
418
419
420
421
422
423
424 UNIT;
425 }
426 }