Martin Donnelly e6e767bf26 some changes
2016-06-21 10:59:38 +01:00

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')
)