Chris@25
|
1 'use strict';
|
Chris@25
|
2
|
Chris@25
|
3 !function(exports, complex_array) {
|
Chris@25
|
4
|
Chris@25
|
5 var
|
Chris@25
|
6 ComplexArray = complex_array.ComplexArray,
|
Chris@25
|
7 // Math constants and functions we need.
|
Chris@25
|
8 PI = Math.PI,
|
Chris@25
|
9 SQRT1_2 = Math.SQRT1_2,
|
Chris@25
|
10 sqrt = Math.sqrt,
|
Chris@25
|
11 cos = Math.cos,
|
Chris@25
|
12 sin = Math.sin
|
Chris@25
|
13
|
Chris@25
|
14 ComplexArray.prototype.FFT = function() {
|
Chris@25
|
15 return FFT(this, false)
|
Chris@25
|
16 }
|
Chris@25
|
17
|
Chris@25
|
18 exports.FFT = function(input) {
|
Chris@25
|
19 return ensureComplexArray(input).FFT()
|
Chris@25
|
20 }
|
Chris@25
|
21
|
Chris@25
|
22 ComplexArray.prototype.InvFFT = function() {
|
Chris@25
|
23 return FFT(this, true)
|
Chris@25
|
24 }
|
Chris@25
|
25
|
Chris@25
|
26 exports.InvFFT = function(input) {
|
Chris@25
|
27 return ensureComplexArray(input).InvFFT()
|
Chris@25
|
28 }
|
Chris@25
|
29
|
Chris@25
|
30 // Applies a frequency-space filter to input, and returns the real-space
|
Chris@25
|
31 // filtered input.
|
Chris@25
|
32 // filterer accepts freq, i, n and modifies freq.real and freq.imag.
|
Chris@25
|
33 ComplexArray.prototype.frequencyMap = function(filterer) {
|
Chris@25
|
34 return this.FFT().map(filterer).InvFFT()
|
Chris@25
|
35 }
|
Chris@25
|
36
|
Chris@25
|
37 exports.frequencyMap = function(input, filterer) {
|
Chris@25
|
38 return ensureComplexArray(input).frequencyMap(filterer)
|
Chris@25
|
39 }
|
Chris@25
|
40
|
Chris@25
|
41 function ensureComplexArray(input) {
|
Chris@25
|
42 return complex_array.isComplexArray(input) && input ||
|
Chris@25
|
43 new ComplexArray(input)
|
Chris@25
|
44 }
|
Chris@25
|
45
|
Chris@25
|
46 function FFT(input, inverse) {
|
Chris@25
|
47 var n = input.length
|
Chris@25
|
48
|
Chris@25
|
49 if (n & (n - 1)) {
|
Chris@25
|
50 return FFT_Recursive(input, inverse)
|
Chris@25
|
51 } else {
|
Chris@25
|
52 return FFT_2_Iterative(input, inverse)
|
Chris@25
|
53 }
|
Chris@25
|
54 }
|
Chris@25
|
55
|
Chris@25
|
56 function FFT_Recursive(input, inverse) {
|
Chris@25
|
57 var
|
Chris@25
|
58 n = input.length,
|
Chris@25
|
59 // Counters.
|
Chris@25
|
60 i, j,
|
Chris@25
|
61 output,
|
Chris@25
|
62 // Complex multiplier and its delta.
|
Chris@25
|
63 f_r, f_i, del_f_r, del_f_i,
|
Chris@25
|
64 // Lowest divisor and remainder.
|
Chris@25
|
65 p, m,
|
Chris@25
|
66 normalisation,
|
Chris@25
|
67 recursive_result,
|
Chris@25
|
68 _swap, _real, _imag
|
Chris@25
|
69
|
Chris@25
|
70 if (n === 1) {
|
Chris@25
|
71 return input
|
Chris@25
|
72 }
|
Chris@25
|
73
|
Chris@25
|
74 output = new ComplexArray(n, input.ArrayType)
|
Chris@25
|
75
|
Chris@25
|
76 // Use the lowest odd factor, so we are able to use FFT_2_Iterative in the
|
Chris@25
|
77 // recursive transforms optimally.
|
Chris@25
|
78 p = LowestOddFactor(n)
|
Chris@25
|
79 m = n / p
|
Chris@25
|
80 normalisation = 1 / sqrt(p)
|
Chris@25
|
81 recursive_result = new ComplexArray(m, input.ArrayType)
|
Chris@25
|
82
|
Chris@25
|
83 // Loops go like O(n Σ p_i), where p_i are the prime factors of n.
|
Chris@25
|
84 // for a power of a prime, p, this reduces to O(n p log_p n)
|
Chris@25
|
85 for(j = 0; j < p; j++) {
|
Chris@25
|
86 for(i = 0; i < m; i++) {
|
Chris@25
|
87 recursive_result.real[i] = input.real[i * p + j]
|
Chris@25
|
88 recursive_result.imag[i] = input.imag[i * p + j]
|
Chris@25
|
89 }
|
Chris@25
|
90 // Don't go deeper unless necessary to save allocs.
|
Chris@25
|
91 if (m > 1) {
|
Chris@25
|
92 recursive_result = FFT(recursive_result, inverse)
|
Chris@25
|
93 }
|
Chris@25
|
94
|
Chris@25
|
95 del_f_r = cos(2*PI*j/n)
|
Chris@25
|
96 del_f_i = (inverse ? -1 : 1) * sin(2*PI*j/n)
|
Chris@25
|
97 f_r = 1
|
Chris@25
|
98 f_i = 0
|
Chris@25
|
99
|
Chris@25
|
100 for(i = 0; i < n; i++) {
|
Chris@25
|
101 _real = recursive_result.real[i % m]
|
Chris@25
|
102 _imag = recursive_result.imag[i % m]
|
Chris@25
|
103
|
Chris@25
|
104 output.real[i] += f_r * _real - f_i * _imag
|
Chris@25
|
105 output.imag[i] += f_r * _imag + f_i * _real
|
Chris@25
|
106
|
Chris@25
|
107 _swap = f_r * del_f_r - f_i * del_f_i
|
Chris@25
|
108 f_i = f_r * del_f_i + f_i * del_f_r
|
Chris@25
|
109 f_r = _swap
|
Chris@25
|
110 }
|
Chris@25
|
111 }
|
Chris@25
|
112
|
Chris@25
|
113 // Copy back to input to match FFT_2_Iterative in-placeness
|
Chris@25
|
114 // TODO: faster way of making this in-place?
|
Chris@25
|
115 for(i = 0; i < n; i++) {
|
Chris@25
|
116 input.real[i] = normalisation * output.real[i]
|
Chris@25
|
117 input.imag[i] = normalisation * output.imag[i]
|
Chris@25
|
118 }
|
Chris@25
|
119
|
Chris@25
|
120 return input
|
Chris@25
|
121 }
|
Chris@25
|
122
|
Chris@25
|
123 function FFT_2_Iterative(input, inverse) {
|
Chris@25
|
124 var
|
Chris@25
|
125 n = input.length,
|
Chris@25
|
126 // Counters.
|
Chris@25
|
127 i, j,
|
Chris@25
|
128 output, output_r, output_i,
|
Chris@25
|
129 // Complex multiplier and its delta.
|
Chris@25
|
130 f_r, f_i, del_f_r, del_f_i, temp,
|
Chris@25
|
131 // Temporary loop variables.
|
Chris@25
|
132 l_index, r_index,
|
Chris@25
|
133 left_r, left_i, right_r, right_i,
|
Chris@25
|
134 // width of each sub-array for which we're iteratively calculating FFT.
|
Chris@25
|
135 width
|
Chris@25
|
136
|
Chris@25
|
137 output = BitReverseComplexArray(input)
|
Chris@25
|
138 output_r = output.real
|
Chris@25
|
139 output_i = output.imag
|
Chris@25
|
140 // Loops go like O(n log n):
|
Chris@25
|
141 // width ~ log n; i,j ~ n
|
Chris@25
|
142 width = 1
|
Chris@25
|
143 while (width < n) {
|
Chris@25
|
144 del_f_r = cos(PI/width)
|
Chris@25
|
145 del_f_i = (inverse ? -1 : 1) * sin(PI/width)
|
Chris@25
|
146 for (i = 0; i < n/(2*width); i++) {
|
Chris@25
|
147 f_r = 1
|
Chris@25
|
148 f_i = 0
|
Chris@25
|
149 for (j = 0; j < width; j++) {
|
Chris@25
|
150 l_index = 2*i*width + j
|
Chris@25
|
151 r_index = l_index + width
|
Chris@25
|
152
|
Chris@25
|
153 left_r = output_r[l_index]
|
Chris@25
|
154 left_i = output_i[l_index]
|
Chris@25
|
155 right_r = f_r * output_r[r_index] - f_i * output_i[r_index]
|
Chris@25
|
156 right_i = f_i * output_r[r_index] + f_r * output_i[r_index]
|
Chris@25
|
157
|
Chris@25
|
158 output_r[l_index] = SQRT1_2 * (left_r + right_r)
|
Chris@25
|
159 output_i[l_index] = SQRT1_2 * (left_i + right_i)
|
Chris@25
|
160 output_r[r_index] = SQRT1_2 * (left_r - right_r)
|
Chris@25
|
161 output_i[r_index] = SQRT1_2 * (left_i - right_i)
|
Chris@25
|
162 temp = f_r * del_f_r - f_i * del_f_i
|
Chris@25
|
163 f_i = f_r * del_f_i + f_i * del_f_r
|
Chris@25
|
164 f_r = temp
|
Chris@25
|
165 }
|
Chris@25
|
166 }
|
Chris@25
|
167 width <<= 1
|
Chris@25
|
168 }
|
Chris@25
|
169
|
Chris@25
|
170 return output
|
Chris@25
|
171 }
|
Chris@25
|
172
|
Chris@25
|
173 function BitReverseIndex(index, n) {
|
Chris@25
|
174 var bitreversed_index = 0
|
Chris@25
|
175
|
Chris@25
|
176 while (n > 1) {
|
Chris@25
|
177 bitreversed_index <<= 1
|
Chris@25
|
178 bitreversed_index += index & 1
|
Chris@25
|
179 index >>= 1
|
Chris@25
|
180 n >>= 1
|
Chris@25
|
181 }
|
Chris@25
|
182 return bitreversed_index
|
Chris@25
|
183 }
|
Chris@25
|
184
|
Chris@25
|
185 function BitReverseComplexArray(array) {
|
Chris@25
|
186 var n = array.length,
|
Chris@25
|
187 flips = {},
|
Chris@25
|
188 swap,
|
Chris@25
|
189 i
|
Chris@25
|
190
|
Chris@25
|
191 for(i = 0; i < n; i++) {
|
Chris@25
|
192 var r_i = BitReverseIndex(i, n)
|
Chris@25
|
193
|
Chris@25
|
194 if (flips.hasOwnProperty(i) || flips.hasOwnProperty(r_i)) continue
|
Chris@25
|
195
|
Chris@25
|
196 swap = array.real[r_i]
|
Chris@25
|
197 array.real[r_i] = array.real[i]
|
Chris@25
|
198 array.real[i] = swap
|
Chris@25
|
199
|
Chris@25
|
200 swap = array.imag[r_i]
|
Chris@25
|
201 array.imag[r_i] = array.imag[i]
|
Chris@25
|
202 array.imag[i] = swap
|
Chris@25
|
203
|
Chris@25
|
204 flips[i] = flips[r_i] = true
|
Chris@25
|
205 }
|
Chris@25
|
206
|
Chris@25
|
207 return array
|
Chris@25
|
208 }
|
Chris@25
|
209
|
Chris@25
|
210 function LowestOddFactor(n) {
|
Chris@25
|
211 var factor = 3,
|
Chris@25
|
212 sqrt_n = sqrt(n)
|
Chris@25
|
213
|
Chris@25
|
214 while(factor <= sqrt_n) {
|
Chris@25
|
215 if (n % factor === 0) return factor
|
Chris@25
|
216 factor = factor + 2
|
Chris@25
|
217 }
|
Chris@25
|
218 return n
|
Chris@25
|
219 }
|
Chris@25
|
220
|
Chris@25
|
221 }(
|
Chris@25
|
222 typeof exports === 'undefined' && (this.fft = {}) || exports,
|
Chris@25
|
223 typeof require === 'undefined' && (this.complex_array) ||
|
Chris@25
|
224 require('./complex_array')
|
Chris@25
|
225 )
|