226 lines
5.7 KiB
JavaScript
226 lines
5.7 KiB
JavaScript
'use strict';
|
|
|
|
!function(exports, complex_array) {
|
|
|
|
var
|
|
ComplexArray = complex_array.ComplexArray,
|
|
// Math constants and functions we need.
|
|
PI = Math.PI,
|
|
SQRT1_2 = Math.SQRT1_2,
|
|
sqrt = Math.sqrt,
|
|
cos = Math.cos,
|
|
sin = Math.sin
|
|
|
|
ComplexArray.prototype.FFT = function() {
|
|
return FFT(this, false)
|
|
}
|
|
|
|
exports.FFT = function(input) {
|
|
return ensureComplexArray(input).FFT()
|
|
}
|
|
|
|
ComplexArray.prototype.InvFFT = function() {
|
|
return FFT(this, true)
|
|
}
|
|
|
|
exports.InvFFT = function(input) {
|
|
return ensureComplexArray(input).InvFFT()
|
|
}
|
|
|
|
// Applies a frequency-space filter to input, and returns the real-space
|
|
// filtered input.
|
|
// filterer accepts freq, i, n and modifies freq.real and freq.imag.
|
|
ComplexArray.prototype.frequencyMap = function(filterer) {
|
|
return this.FFT().map(filterer).InvFFT()
|
|
}
|
|
|
|
exports.frequencyMap = function(input, filterer) {
|
|
return ensureComplexArray(input).frequencyMap(filterer)
|
|
}
|
|
|
|
function ensureComplexArray(input) {
|
|
return complex_array.isComplexArray(input) && input ||
|
|
new ComplexArray(input)
|
|
}
|
|
|
|
function FFT(input, inverse) {
|
|
var n = input.length
|
|
|
|
if (n & (n - 1)) {
|
|
return FFT_Recursive(input, inverse)
|
|
} else {
|
|
return FFT_2_Iterative(input, inverse)
|
|
}
|
|
}
|
|
|
|
function FFT_Recursive(input, inverse) {
|
|
var
|
|
n = input.length,
|
|
// Counters.
|
|
i, j,
|
|
output,
|
|
// Complex multiplier and its delta.
|
|
f_r, f_i, del_f_r, del_f_i,
|
|
// Lowest divisor and remainder.
|
|
p, m,
|
|
normalisation,
|
|
recursive_result,
|
|
_swap, _real, _imag
|
|
|
|
if (n === 1) {
|
|
return input
|
|
}
|
|
|
|
output = new ComplexArray(n, input.ArrayType)
|
|
|
|
// Use the lowest odd factor, so we are able to use FFT_2_Iterative in the
|
|
// recursive transforms optimally.
|
|
p = LowestOddFactor(n)
|
|
m = n / p
|
|
normalisation = 1 / sqrt(p)
|
|
recursive_result = new ComplexArray(m, input.ArrayType)
|
|
|
|
// Loops go like O(n Σ p_i), where p_i are the prime factors of n.
|
|
// for a power of a prime, p, this reduces to O(n p log_p n)
|
|
for(j = 0; j < p; j++) {
|
|
for(i = 0; i < m; i++) {
|
|
recursive_result.real[i] = input.real[i * p + j]
|
|
recursive_result.imag[i] = input.imag[i * p + j]
|
|
}
|
|
// Don't go deeper unless necessary to save allocs.
|
|
if (m > 1) {
|
|
recursive_result = FFT(recursive_result, inverse)
|
|
}
|
|
|
|
del_f_r = cos(2*PI*j/n)
|
|
del_f_i = (inverse ? -1 : 1) * sin(2*PI*j/n)
|
|
f_r = 1
|
|
f_i = 0
|
|
|
|
for(i = 0; i < n; i++) {
|
|
_real = recursive_result.real[i % m]
|
|
_imag = recursive_result.imag[i % m]
|
|
|
|
output.real[i] += f_r * _real - f_i * _imag
|
|
output.imag[i] += f_r * _imag + f_i * _real
|
|
|
|
_swap = f_r * del_f_r - f_i * del_f_i
|
|
f_i = f_r * del_f_i + f_i * del_f_r
|
|
f_r = _swap
|
|
}
|
|
}
|
|
|
|
// Copy back to input to match FFT_2_Iterative in-placeness
|
|
// TODO: faster way of making this in-place?
|
|
for(i = 0; i < n; i++) {
|
|
input.real[i] = normalisation * output.real[i]
|
|
input.imag[i] = normalisation * output.imag[i]
|
|
}
|
|
|
|
return input
|
|
}
|
|
|
|
function FFT_2_Iterative(input, inverse) {
|
|
var
|
|
n = input.length,
|
|
// Counters.
|
|
i, j,
|
|
output, output_r, output_i,
|
|
// Complex multiplier and its delta.
|
|
f_r, f_i, del_f_r, del_f_i, temp,
|
|
// Temporary loop variables.
|
|
l_index, r_index,
|
|
left_r, left_i, right_r, right_i,
|
|
// width of each sub-array for which we're iteratively calculating FFT.
|
|
width
|
|
|
|
output = BitReverseComplexArray(input)
|
|
output_r = output.real
|
|
output_i = output.imag
|
|
// Loops go like O(n log n):
|
|
// width ~ log n; i,j ~ n
|
|
width = 1
|
|
while (width < n) {
|
|
del_f_r = cos(PI/width)
|
|
del_f_i = (inverse ? -1 : 1) * sin(PI/width)
|
|
for (i = 0; i < n/(2*width); i++) {
|
|
f_r = 1
|
|
f_i = 0
|
|
for (j = 0; j < width; j++) {
|
|
l_index = 2*i*width + j
|
|
r_index = l_index + width
|
|
|
|
left_r = output_r[l_index]
|
|
left_i = output_i[l_index]
|
|
right_r = f_r * output_r[r_index] - f_i * output_i[r_index]
|
|
right_i = f_i * output_r[r_index] + f_r * output_i[r_index]
|
|
|
|
output_r[l_index] = SQRT1_2 * (left_r + right_r)
|
|
output_i[l_index] = SQRT1_2 * (left_i + right_i)
|
|
output_r[r_index] = SQRT1_2 * (left_r - right_r)
|
|
output_i[r_index] = SQRT1_2 * (left_i - right_i)
|
|
temp = f_r * del_f_r - f_i * del_f_i
|
|
f_i = f_r * del_f_i + f_i * del_f_r
|
|
f_r = temp
|
|
}
|
|
}
|
|
width <<= 1
|
|
}
|
|
|
|
return output
|
|
}
|
|
|
|
function BitReverseIndex(index, n) {
|
|
var bitreversed_index = 0
|
|
|
|
while (n > 1) {
|
|
bitreversed_index <<= 1
|
|
bitreversed_index += index & 1
|
|
index >>= 1
|
|
n >>= 1
|
|
}
|
|
return bitreversed_index
|
|
}
|
|
|
|
function BitReverseComplexArray(array) {
|
|
var n = array.length,
|
|
flips = {},
|
|
swap,
|
|
i
|
|
|
|
for(i = 0; i < n; i++) {
|
|
var r_i = BitReverseIndex(i, n)
|
|
|
|
if (flips.hasOwnProperty(i) || flips.hasOwnProperty(r_i)) continue
|
|
|
|
swap = array.real[r_i]
|
|
array.real[r_i] = array.real[i]
|
|
array.real[i] = swap
|
|
|
|
swap = array.imag[r_i]
|
|
array.imag[r_i] = array.imag[i]
|
|
array.imag[i] = swap
|
|
|
|
flips[i] = flips[r_i] = true
|
|
}
|
|
|
|
return array
|
|
}
|
|
|
|
function LowestOddFactor(n) {
|
|
var factor = 3,
|
|
sqrt_n = sqrt(n)
|
|
|
|
while(factor <= sqrt_n) {
|
|
if (n % factor === 0) return factor
|
|
factor = factor + 2
|
|
}
|
|
return n
|
|
}
|
|
|
|
}(
|
|
typeof exports === 'undefined' && (this.fft = {}) || exports,
|
|
typeof require === 'undefined' && (this.complex_array) ||
|
|
require('./complex_array')
|
|
)
|