ts-scikit
Version:
A scientific toolkit written in Typescript
1,185 lines • 40.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Fft = void 0;
const fft_real_1 = require("./fft-real");
const fft_complex_1 = require("./fft-complex");
const sampling_1 = require("./sampling");
const utils_1 = require("../utils");
/**
* An easy-to-use fast Fourier transform.
* <p>
* <em>WARNING: NOT YET TESTED!</em>
* This class is less flexible than {@link FftComplex} and {@link FftReal}.
* For example, the user has less control over the sampling of frequency.
* However, for many applications this class may be simpler to use.
* <p>
* For example, the following program shows how to use this class to
* filter a real-valued sequence in the frequency domain.
* <pre>
* const fft = new Fft(nx); // nx = number of samples of f(x)
* const sk = fft.getFrequencySampling1();
* const nk = sk.count; // number of frequencies sampled.
* const f: number[] = ... // nx real samples of input f(x)
* const g: number[] = fft.applyForward(f);
* for (let kk=0, kr=0, ki=kr + 1; kk < nk; ++kk, kr+=2, ki+=2 ) {
* let k = sk.getValue(kk); // frequency k in cycles/sample.
* // modify g[kr], the real part of g(k)
* // modify g[ki], the imag part of g(k)
* }
* const h[] = fft.applyInverse(g); // nk real samples of output h(x)
* </pre>
* <p>
* This example is almost as simple for multi-dimensional transforms.
* <p>
* A forward transform computes an output array of complex values g(x)
* from an input array of real or complex values f(x). An inverse
* transform computes the corresponding real of complex values f(x)
* from g(k). For definiteness, in this documentation, the variable x
* represents spatial coordinates and the variable k represents spatial
* frequencies (or wavenumbers). For functions of time, simply replace
* the word "space" with "time" in this documentation.
* <p>
* This class enables transforms of 1D, 2D, and 3D arrays. For example,
* a 2D array f[nx2][nx1] represents nx2*nx1 samples of a function
* f(x1, x2) of two spatial coordinates x1 and x2. In addition to
* numbers of samples nx1 and nx2, sampling intervals dx1 and dx2
* and first sampled coordinates fx1 and fx2 may also be specified.
* (The default sampling interval is 1.0 and the default first sample
* coordinate is 0.0.) These sampling parameters may be specified with
* samplings sx1 and sx2.
* <p>
* For each specified spatial sampling sx, this class defines a
* corresponding frequency sampling sk, in which units of frequency
* are cycles per unit distance. The number of frequencies sampled
* is computed so that the Fourier transform is fast, but the number
* of frequency samples is never less than the number of space samples.
* Arrays to be transformed may be padded with zeros to obtain the
* required frequency sampling. Optional additional padding may be
* specified to sample frequency more finely.
* <p>
* A frequency sampling sk may be centered. Such a centered frequency
* sampling always has an odd number of samples, and zero frequency
* corresponds to the middle sample in the array of complex transformed
* values. The default is not centered, so that zero frequency corresponds
* to the first sample, the one with index 0.
* <p>
* Arrays input to forward transforms may contain either real or
* complex values. If complex, values are packed sequentially as
* (real, imag) pairs of consecutive numbers. The default input
* type is real.
* <p>
* Signs of the exponents in the complex exponentials used in forward
* transforms may be specified. The opposite signs are used for inverse
* transforms. The default signs are -1 for forward transforms and 1 for
* inverse transforms.
*/
class Fft {
constructor(n1, n2, n3, complex) {
this._padding1 = 0;
this._padding2 = 0;
this._padding3 = 0;
if (n1 instanceof sampling_1.Sampling && n2 instanceof sampling_1.Sampling && n3 instanceof sampling_1.Sampling) {
this._init(n1, n2, n3);
}
else if (n1 instanceof sampling_1.Sampling && n2 instanceof sampling_1.Sampling) {
this._init(n1, n2);
}
else if (n1 instanceof sampling_1.Sampling) {
this._init(n1);
if (typeof n1 === 'number' && typeof n2 === 'number' && typeof n3 === 'number') {
this._init(new sampling_1.Sampling(n1), new sampling_1.Sampling(n2), new sampling_1.Sampling(n3));
}
else if (typeof n1 === 'number' && typeof n2 === 'number') {
this._init(new sampling_1.Sampling(n1), new sampling_1.Sampling(n2));
}
else if (typeof n1 === 'number') {
this._init(new sampling_1.Sampling(n1));
}
else if (n1[0] instanceof Array) {
if (n1[0][0] instanceof Array) {
n1 = n1;
this._init(new sampling_1.Sampling(n1[0][0].length / 2), new sampling_1.Sampling(n1[0].length), new sampling_1.Sampling(n1.length));
}
else {
n1 = n1;
this._init(new sampling_1.Sampling(n1[0].length / 2), new sampling_1.Sampling(n1.length));
}
}
else if (n1 instanceof Array) {
this._init(new sampling_1.Sampling(n1.length / 2));
}
}
if (complex) {
this.complex = complex;
}
;
}
static FromData(f, complex = false) {
switch (utils_1.arrayDimensions(f)) {
case 1:
f = f;
return new Fft(f.length, complex);
case 2:
f = f;
return new Fft(f[0].length, f.length, complex);
default:
f = f;
return new Fft(f[0][0].length, f[0].length, f.length, complex);
}
}
/**
* Sets the type of input (output) values for forward (inverse) transforms.
* <p>
* The default type is real.
* @param complex true, for complex values; false, for real values.
*/
set complex(complex) {
if (this._complex !== complex) {
this._complex = complex;
this._updateSampling1();
}
}
/**
* Sets the ability of this transform to overwrite specified arrays.
* <p>
* The array specified in an inverse transform is either copied or
* overwritten internally by the inverse transform. Copying preserves
* the values in the specified array, but wastes memory in the case
* when those values are no longer needed. If overwrite is true, then
* the inverse transform will be performed in place, so that no copy
* is necessary. The default is false.
* @param overwrite true, to overwrite; false, to copy.
*/
set overwrite(overwrite) {
if (this._overwrite !== overwrite) {
this._overwrite = overwrite;
}
}
/**
* Sets the centering of frequency samplings for all dimensions.
* <p>
* If centered, the number of frequency samples is always odd,
* and zero frequency corresponds to the middle sample. The
* default center is false, so that zero frequency corresponds to
* the sample with index zero in the output transformed array.
* @param center true, for centering; false, otherwise.
*/
set center(center) {
this.center1 = center;
this.center2 = center;
this.center3 = center;
}
/**
* Sets the centering of frequency sampling for the 1st dimension.
* <p>
* If centered, the number of frequency samples is always odd,
* and zero frequency corresponds to the middle sample. The
* default center is false, so that zero frequency corresponds to
* the sample with index zero in the output transformed array.
* @param center true, for centering; false, otherwise.
*/
set center1(center) {
if (this._center1 !== center) {
this._center1 = center;
this._updateSampling1();
}
}
/**
* Sets the centering of frequency sampling for the 2nd dimension.
* <p>
* If centered, the number of frequency samples is always odd,
* and zero frequency corresponds to the middle sample. The
* default center is false, so that zero frequency corresponds to
* the sample with index zero in the output transformed array.
* @param center true, for centering; false, otherwise.
*/
set center2(center) {
if (this._center2 !== center) {
this._center2 = center;
this._updateSampling2();
}
}
/**
* Sets the centering of frequency sampling for the 3rd dimension.
* <p>
* If centered, the number of frequency samples is always odd,
* and zero frequency corresponds to the middle sample. The
* default center is false, so that zero frequency corresponds to
* the sample with index zero in the output transformed array.
* @param center true, for centering; false, otherwise.
*/
set center3(center) {
if (this._center3 !== center) {
this._center3 = center;
this._updateSampling3();
}
}
/**
* Sets the minimum padding with zeros for all array dimensions.
* <p>
* The default minimum is zero. However, some amount of padding
* may be required by the FFT.
* @param padding the minimum padding.
*/
set padding(padding) {
this.padding1 = padding;
this.padding2 = padding;
this.padding3 = padding;
}
/**
* Sets the minimum padding with zeros for the 1st array dimension.
* <p>
* The default minimum is zero. However, some amount of padding may
* be required by the FFT.
* @param padding the minimum padding.
*/
set padding1(padding) {
if (this._padding1 !== padding) {
this._padding1 = padding;
this._updateSampling1();
}
}
/**
* Sets the minimum padding with zeros for the 2nd array dimension.
* <p>
* The default minimum is zero. However, some amount of padding may
* be required by the FFT.
* @param padding the minimum padding.
*/
set padding2(padding) {
if (this._padding2 !== padding) {
this._padding2 = padding;
this._updateSampling2();
}
}
/**
* Sets the minimum padding with zeros for the 3rd array dimension.
* <p>
* The default minimum is zero. However, some amount of padding may
* be required by the FFT.
* @param padding the minimum padding.
*/
set padding3(padding) {
if (this._padding3 !== padding) {
this._padding3 = padding;
this._updateSampling3();
}
}
/**
* Gets the frequency sampling for the 1st dimension.
* @returns the frequency sampling.
*/
get frequencySampling1() { return this._sk1; }
/**
* Gets the frequency sampling for the 2nd dimension.
* @returns the frequency sampling.
*/
get frequencySampling2() { return this._sk2; }
/**
* Gets the frequency sampling for the 3rd dimension.
* @returns the frequency sampling.
*/
get frequencySampling3() { return this._sk3; }
/**
* Sets the sign used for forward transforms in all dimensions.
* <p>
* The opposite sign is used for inverse transforms.
* The default sign is -1.
* @param sign the sign, -1 or 1.
*/
set sign(sign) {
this.sign1 = sign;
this.sign2 = sign;
this.sign3 = sign;
}
/**
* Sets the sign used for forward transforms in the 1st dimension.
* <p>
* The opposite sign is used for inverse transforms.
* The default sign is -1.
* @param sign the sign, -1 or 1.
*/
set sign1(sign) {
this._sign1 = (sign > 0) ? 1 : -1;
}
/**
* Sets the sign used for forward transforms in the 2nd dimension.
* <p>
* The opposite sign is used for inverse transforms.
* The default sign is -1.
* @param sign the sign, -1 or 1.
*/
set sign2(sign) {
this._sign2 = (sign > 0) ? 1 : -1;
}
/**
* Sets the sign used for forward transforms in the 3rd dimension.
* <p>
* The opposite sign is used for inverse transforms.
* The default sign is -1.
* @param sign the sign, -1 or 1.
*/
set sign3(sign) {
this._sign3 = (sign > 0) ? 1 : -1;
}
applyForward(f) {
const dim = utils_1.arrayDimensions(f);
switch (dim) {
case 1:
return this._applyForward1(f);
case 2:
return this._applyForward2(f);
default:
return this._applyForward3(f);
}
}
applyInverse(g) {
const dim = utils_1.arrayDimensions(g);
switch (dim) {
case 1:
return this._applyInverse1(g);
case 2:
return this._applyInverse2(g);
default:
return this._applyInverse3(g);
}
}
/** @internal */
_applyInverse1(g) {
this._ensureSamplingK1(g);
const nx1 = this._sx1.count;
const gpad = (this._overwrite) ? g : utils_1.copy(g);
this._uncenter(gpad, 1);
this._unphase(gpad, 1);
if (this._complex) {
this._fft1c.complexToComplex(-this._sign1, gpad, gpad);
this._fft1c.scale(gpad, nx1);
return utils_1.ccopy(gpad, nx1);
}
else {
this._fft1r.complexToReal(-this._sign1, gpad, gpad);
this._fft1r.scale(gpad, nx1);
return utils_1.copy(gpad, nx1);
}
}
/** @internal */
_applyInverse2(g) {
this._ensureSamplingK2(g);
const gpad = (this._overwrite) ? g : utils_1.copy(g);
const nx1 = this._sx1.count;
const nx2 = this._sx2.count;
this._uncenter(gpad, 2);
this._unphase(gpad, 2);
if (this._complex) {
this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1);
this._fft2.scale(gpad, this._nfft1, nx2);
this._fft1c.complexToComplex1(-this._sign1, gpad, gpad, nx2);
this._fft1c.scale(gpad, nx1, nx2);
return utils_1.ccopy(gpad, nx1, nx2);
}
else {
this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1 / 2 + 1);
this._fft2.scale(gpad, this._nfft1 / 2 + 1, nx2);
this._fft1r.complexToReal1(-this._sign1, gpad, gpad, nx2);
this._fft1r.scale(gpad, nx1, nx2);
return utils_1.copy(gpad, nx1, nx2);
}
}
/** @internal */
_applyInverse3(g) {
this._ensureSamplingK3(g);
const gpad = (this._overwrite) ? g : utils_1.copy(g);
const nx1 = this._sx1.count;
const nx2 = this._sx2.count;
const nx3 = this._sx3.count;
this._uncenter(gpad, 3);
this._unphase(gpad, 3);
if (this._complex) {
this._fft3.complexToComplex3(-this._sign3, gpad, gpad, this._nfft1, this._nfft2);
this._fft3.scale(gpad, this._nfft1, this._nfft2, nx3);
this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1, nx3);
this._fft2.scale(gpad, this._nfft1, nx2, nx3);
this._fft1c.complexToComplex1(-this._sign1, gpad, gpad, nx2, nx3);
this._fft1c.scale(gpad, nx1, nx2, nx3);
return utils_1.ccopy(gpad, nx1, nx2, nx3);
}
else {
this._fft3.complexToComplex3(-this._sign3, gpad, gpad, this._nfft1 / 2 + 1, this._nfft2);
this._fft3.scale(gpad, this._nfft1 / 2 + 1, this._nfft2, nx3);
this._fft2.complexToComplex2(-this._sign2, gpad, gpad, this._nfft1 / 2 + 1, nx3);
this._fft2.scale(gpad, this._nfft1 / 2 + 1, nx2, nx3);
this._fft1r.complexToReal1(-this._sign1, gpad, gpad, nx2, nx3);
this._fft1r.scale(gpad, nx1, nx2, nx3);
return utils_1.copy(gpad, nx1, nx2, nx3);
}
}
/** @internal */
_applyForward1(f) {
this._ensureSamplingX1(f);
const fpad = this._pad1(f);
if (this._complex) {
this._fft1c.complexToComplex(this._sign1, fpad, fpad);
}
else {
this._fft1r.realToComplex(this._sign1, fpad, fpad);
}
this._phase(fpad, 1);
this._center(fpad, 1);
return fpad;
}
/** @interal */
_applyForward2(f) {
this._ensureSamplingX2(f);
const fpad = this._pad2(f);
const nx2 = this._sx2.count;
if (this._complex) {
this._fft1c.complexToComplex1(this._sign1, fpad, fpad, nx2);
this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1);
}
else {
this._fft1r.realToComplex1(this._sign1, fpad, fpad, nx2);
this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1 / 2 + 1);
}
this._phase(fpad, 2);
this._center(fpad, 2);
return fpad;
}
/** @internal */
_applyForward3(f) {
this._ensureSamplingX3(f);
const fpad = this._pad3(f);
const nx2 = this._sx2.count;
const nx3 = this._sx3.count;
if (this._complex) {
this._fft1c.complexToComplex1(this._sign1, fpad, fpad, nx2, nx3);
this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1, nx3);
this._fft3.complexToComplex3(this._sign3, fpad, fpad, this._nfft1, this._nfft2);
}
else {
this._fft1r.realToComplex1(this._sign1, fpad, fpad, nx2, nx3);
this._fft2.complexToComplex2(this._sign2, fpad, fpad, this._nfft1 / 2 + 1, nx3);
this._fft3.complexToComplex3(this._sign3, fpad, fpad, this._nfft1 / 2 + 1, this._nfft2);
}
this._phase(fpad, 3);
this._center(fpad, 3);
return fpad;
}
/** @internal */
_cswap(f, n, i, j, dim) {
switch (dim) {
case 1:
this._doCswap1(f, n, i, j);
break;
case 2:
this._doCswap2(f, n, i, j);
break;
default:
this._doCswap3(f, n, i, j);
}
}
/** @internal */
_doCswap1(f, n, i, j) {
let ir = 2 * i, ii = ir + 1;
let jr = 2 * j, ji = jr + 1;
for (const k = 0; k < n; ++i, ir += 2, ii += 2, jr += 2, ji += 2) {
const fir = f[ir];
f[ir] = f[jr];
f[jr] = fir;
const fii = f[ii];
f[ii] = f[ji];
f[ji] = fii;
}
}
/** @internal */
_doCswap2(f, n, i, j) {
for (let k = 0; k < n; ++k, ++i, ++j) {
const fi = f[i];
f[i] = f[j];
f[j] = fi;
}
}
/** @internal */
_doCswap3(f, n, i, j) {
for (let k = 0; k < n; ++k, ++i, ++j) {
const fi = f[i];
f[i] = f[j];
f[j] = fi;
}
}
/** @internal */
_cshift(f, n, i, j) {
if (i < j) {
let ir = 2 * (i + n - 1), ii = ir + 1;
let jr = 2 * (j + n - 1), ji = jr + 1;
for (let k = 0; k < n; ++k, ir -= 2, ii -= 2, jr -= 2, ji -= 2) {
f[jr] = f[ir];
f[ji] = f[ii];
}
}
else {
let ir = 2 * i, ii = ir + 1;
let jr = 2 * j, ji = jr + 1;
for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr += 2, ji += 2) {
f[jr] = f[ir];
f[ji] = f[ii];
}
}
}
/** @internal */
_crotateLeft(f, n, j, dim) {
switch (dim) {
case 1:
this._doCrotateLeft1(f, n, j);
break;
case 2:
this._doCrotateLeft2(f, n, j);
break;
default:
this._doCrotateLeft3(f, n, j);
}
}
/** @internal */
_doCrotateLeft1(f, n, j) {
const fjr = f[j * 2];
const fji = f[j * 2 + 1];
const i = j + 1;
let ir = 2 * i;
let ii = ir + 1;
for (let k = 1; k < n; ++k, ir += 2, ii += 2) {
f[ir - 2] = f[ir];
f[ii - 2] = f[ii];
}
f[ir - 2] = fjr;
f[ii - 2] = fji;
}
/** @internal */
_doCrotateLeft2(f, n, j) {
// nfft odd
// 4 5 6 3 0 1 2
// 4 5 6 0 1 2 3
// _doCrotateLeft(f,n=4,j=3);
const fj = f[j];
const m = j + n;
let i = j + 1;
for (; i < m; ++i)
f[i - 1] = f[i];
f[i - 1] = fj;
}
/** @internal */
_doCrotateLeft3(f, n, j) {
// nfft odd
// 4 5 6 3 0 1 2
// 4 5 6 0 1 2 3
// _doCrotateLeft(f, n=4,j=3);
const fj = f[j];
const m = j + n;
let i = j + 1;
for (; i < m; ++i)
f[i - 1] = f[i];
f[i - 1] = fj;
}
/** @internal */
_crotateRight(f, n, j, dim) {
switch (dim) {
case 1:
this._doCrotateRight1(f, n, j);
break;
case 2:
this._doCrotateRight2(f, n, j);
break;
default:
this._doCrotateRight3(f, n, j);
}
}
/** @internal */
_doCrotateRight1(f, n, j) {
const m = j + n - 1;
const fmr = f[m * 2];
const fmi = f[m * 2 + 1];
const i = m;
let ir = 2 * i;
let ii = ir + 1;
for (let k = 1; k < n; ++k, ir -= 2, ii -= 2) {
f[ir] = f[ir - 2];
f[ii] = f[ii - 2];
}
f[ir] = fmr;
f[ii] = fmi;
}
/** @internal */
_doCrotateRight2(f, n, j) {
const m = j + n - 1;
const fm = f[m];
let i = m;
for (; i > j; --i) {
f[i] = f[i - 1];
}
f[i] = fm;
}
/** @internal */
_doCrotateRight3(f, n, j) {
const m = j + n - 1;
const fm = f[m];
let i = m;
for (; i > j; --i) {
f[i] = f[i - 1];
}
f[i] = fm;
}
/** @internal */
_creflect(f, n, i, dim) {
switch (dim) {
case 1:
this._doCreflect1(f, n, i);
break;
case 2:
this._doCreflect2(f, n, i);
break;
default:
this._doCreflect3(f, n, i);
}
}
/** @internal */
_doCreflect1(f, n, i) {
let ir = 2 * (i + 1), ii = ir + 1;
let jr = 2 * (i - 1), ji = jr + 1;
for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr -= 2, ji -= 2) {
f[jr] = f[ir];
f[ji] = -f[ii];
}
}
/** @internal */
_doCreflect2(f, n, i) {
const n2 = f.length;
for (let i2 = 0, j2 = n2 - 1; i2 < n2; ++i2, --j2) {
let ir = 2 * (i + 1), ii = ir + 1;
let jr = 2 * (i - 1), ji = jr + 1;
const fj2 = f[j2];
const fi2 = f[i2];
for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr -= 2, ji -= 2) {
fj2[jr] = fi2[ir];
fj2[ji] = -fi2[ii];
}
}
}
/** @internal */
_doCreflect3(f, n, i) {
const n2 = f[0].length;
const n3 = f.length;
for (let i3 = 0, j3 = n3 - 1; i3 < n3; ++i3, --j3) {
for (let i2 = 0, j2 = n2 - 1; i2 < n2; ++i2, --j2) {
let ir = 2 * (i + 1), ii = ir + 1;
let jr = 2 * (i - 1), ji = jr + 1;
const fj3j2 = f[j3][j2];
const fi3i2 = f[i3][i2];
for (let k = 0; k < n; ++k, ir += 2, ii += 2, jr -= 2, ji -= 2) {
fj3j2[jr] = fi3i2[ir];
fj3j2[ji] = -fi3i2[ii];
}
}
}
}
/** @internal */
_init(sx1, sx2, sx3) {
this._sx1 = sx1;
this._sx2 = sx2;
this._sx3 = sx3;
this._sign1 = -1;
this._sign2 = -1;
this._sign3 = -1;
this._updateSampling1();
this._updateSampling2();
this._updateSampling3();
}
_updateSampling1() {
if (!this._sx1) {
return;
}
const nx = this._sx1.count;
const dx = this._sx1.delta;
const npad = nx + this._padding1;
let nfft, nk, dk, fk;
if (this._complex) {
nfft = fft_complex_1.FftComplex.SmallNFFT(npad);
dk = 1.0 / (nfft * dx);
if (this._center1) {
const even = nfft % 2 === 0;
nk = even ? nfft + 1 : nfft;
fk = even ? -0.5 / dx : -0.5 / dx + 0.5 * dk;
}
else {
nk = nfft;
fk = 0.0;
}
if (!this._fft1c || this._nfft1 !== nfft) {
this._fft1c = new fft_complex_1.FftComplex(nfft);
this._fft1r = null;
this._nfft1 = nfft;
}
}
else {
nfft = fft_real_1.FftReal.SmallNFFT(npad);
dk = 1.0 / (nfft * dx);
if (this._center1) {
nk = nfft + 1;
fk = -0.5 / dx;
}
else {
nk = nfft / 2 + 1;
fk = 0.0;
}
if (!this._fft1r || this._nfft1 !== nfft) {
this._fft1r = new fft_real_1.FftReal(nfft);
this._fft1c = null;
this._nfft1 = nfft;
}
}
this._sk1 = new sampling_1.Sampling(nk, dk, fk);
}
_updateSampling2() {
if (!this._sx2) {
return;
}
const nx = this._sx2.count;
const dx = this._sx2.delta;
const npad = nx + this._padding2;
const nfft = fft_complex_1.FftComplex.SmallNFFT(npad);
const dk = 1.0 / (nfft * dx);
let fk, nk;
if (this._center2) {
const even = nfft % 2 === 0;
nk = even ? nfft + 1 : nfft;
fk = even ? -0.5 / dx : -0.5 / dx + 0.5 * dk;
}
else {
nk = nfft;
fk = 0.0;
}
if (!this._fft2 || this._nfft2 !== nfft) {
this._fft2 = new fft_complex_1.FftComplex(nfft);
this._nfft2 = nfft;
}
this._sx2 = new sampling_1.Sampling(nk, dk, fk);
}
_updateSampling3() {
if (!this._sx3) {
return;
}
const nx = this._sx3.count;
const dx = this._sx3.delta;
const npad = nx + this._padding3;
const nfft = fft_complex_1.FftComplex.SmallNFFT(npad);
const dk = 1.0 / (nfft * dx);
let fk, nk;
if (this._center3) {
const even = nfft % 2 === 0;
nk = even ? nfft + 1 : nfft;
fk = even ? -0.5 / dx : -0.5 / dx + 0.5 * dk;
}
else {
nk = nfft;
fk = 0.0;
}
if (!this._fft3 || this._nfft3 !== nfft) {
this._fft3 = new fft_complex_1.FftComplex(nfft);
this._nfft3 = nfft;
}
this._sk3 = new sampling_1.Sampling(nk, dk, fk);
}
_pad1(f) {
const nk1 = this._sk1.count;
return (this._complex)
? utils_1.copy(f, f.length).concat(Array(nk1).fill(0.0))
: utils_1.ccopy(f, f.length / 2).concat(Array(nk1).fill(0.0));
}
_pad2(f) {
const nk2 = this._sk2.count;
const g = utils_1.copy(f);
for (let i2 = 0; i2 < nk2; ++i2) {
g[i2] = this._pad1(g[i2]);
}
return g;
}
_pad3(f) {
const nk3 = this._sk3.count;
const g = utils_1.copy(f);
for (let i3 = 0; i3 < nk3; ++i3) {
g[i3] = this._pad2(g[i3]);
}
return g;
}
_ensureSamplingX1(f) {
utils_1.Check.state(this._sx1 !== null, 'sampling sx1 exists for 1st dimension');
const l1 = f.length;
let n1 = this._sx1.count;
if (this._complex) {
n1 *= 2;
}
utils_1.Check.argument(n1 === l1, 'array length consistent with sampling sx1');
}
_ensureSamplingX2(f) {
utils_1.Check.state(this._sx2 !== null, 'sampling sx2 exists for 2nd dimension');
this._ensureSamplingX1(f[0]);
const l2 = f.length;
const n2 = this._sx2.count;
utils_1.Check.argument(n2 === l2, 'array length consistent with sampling sx2');
}
_ensureSamplingX3(f) {
utils_1.Check.state(this._sx3 !== null, 'sampling sx3 exists for 3rd dimension');
this._ensureSamplingX2(f[0]);
const l3 = f.length;
const n3 = this._sx3.count;
utils_1.Check.argument(n3 === l3, 'array length consistent with sampling sx3');
}
_ensureSamplingK1(f) {
utils_1.Check.state(this._sk1 !== null, 'sampling sk1 exists for 1st dimension');
const l1 = f.length;
const n1 = this._sk1.count;
console.log(l1, n1);
utils_1.Check.argument(2 * n1 === l1, 'array length consistent with sampling sk1');
}
_ensureSamplingK2(f) {
utils_1.Check.state(this._sk2 !== null, 'sampling sk2 exists for 2nd dimension');
const l2 = f.length;
const n2 = this._sk2.count;
utils_1.Check.argument(n2 === l2, 'array length consistent with sampling sk2');
}
_ensureSamplingK3(f) {
utils_1.Check.state(this._sk3 !== null, 'sampling sk3 exists for 3rd dimension');
this._ensureSamplingK2(f[0]);
const l3 = f.length;
const n3 = this._sk3.count;
utils_1.Check.argument(n3 === l3, 'array length consistent with sampling sk3');
}
/** @ignore */
_center(f, dim) {
switch (dim) {
case 1:
this._doCenter1(f);
break;
case 2:
this._doCenter2(f);
break;
default:
this._doCenter3(f);
}
}
/** @internal */
_doCenter1(f) {
this._center1d(f);
if (this._center1 && !this._complex) {
// real, nfft = 8
// x x x x 0 1 2 3 4
// 4 3 2 1 0 1 2 3 4
this._creflect(f, this._nfft1 / 2, this._nfft1 / 2, 1);
}
}
/** @ignore */
_doCenter2(f) {
if (this._center1) {
for (let i2 = 0; i2 < this._nfft2; ++i2) {
this._center1d(f[i2]);
}
this._center2d(f);
if (this._center1 && !this._complex) {
this._creflect(f, this._nfft1 / 2, this._nfft1 / 2, 2);
}
}
}
/** @ignore */
_doCenter3(f) {
if (this._center3) {
return;
}
const nk3 = this._sk3.count;
const nfft3 = this._nfft3;
const even3 = nfft3 % 2 === 0;
if (even3) {
// nfft even
// 0 1 2 3 4 5 6 7 | 8
// 4 5 6 7 0 1 2 3 | 4
this._cswap(f, nfft3 / 2, 0, nfft3 / 2, 3);
f[nk3 - 1] = utils_1.ccopy(f[0]);
}
else {
// nfft odd
// 0 1 2 3 4 5 6
// 4 5 6 3 0 1 2
// 4 5 6 0 1 2 3
this._cswap(f, (nfft3 - 1) / 2, 0, (nfft3 + 1) / 2, 3);
this._crotateLeft(f, (nfft3 + 1) / 2, (nfft3 - 1) / 2, 3);
}
}
/** @internal */
_phase(f, dim) {
switch (dim) {
case 1:
this._doPhase1(f, this._sign1);
break;
case 2:
this._doPhase2(f, this._sign1, this._sign2);
break;
default:
this._doPhase3(f, this._sign1, this._sign2, this._sign3);
}
}
/** @internal */
_unphase(f, dim) {
switch (dim) {
case 1:
this._doPhase1(f, -this._sign1);
break;
case 2:
this._doPhase2(f, -this._sign1, -this._sign2);
break;
default:
this._doPhase3(f, -this._sign1, -this._sign2, -this._sign3);
}
}
/** @internal */
_doPhase1(f, sign1 = this._sign1) {
const fx = this._sx1.first;
if (fx === 0.0) {
return;
}
const nk = (this._complex) ? this._nfft1 : this._nfft1 / 2 + 1;
const dp = sign1 * 2.0 * Math.PI * this._sk1.delta * fx;
let p, cosp, sinp, fr, fi;
for (let i = 0, ir = 0, ii = 1; i < nk; ++i, ir += 2, ii += 2) {
p = i * dp;
cosp = Math.cos(p);
sinp = Math.sin(p);
fr = f[ir];
fi = f[ii];
f[ir] = fr * cosp - fi * sinp;
f[ii] = fi * cosp + fr * sinp;
}
}
/** @internal */
_doPhase2(f, sign1 = this._sign1, sign2 = this._sign2) {
const fx1 = this._sx1.first;
const fx2 = this._sx2.first;
let p, cosp, sinp, fr, fi;
let p2, f2;
if (fx1 === 0.0 && fx2 === 0.0) {
return;
}
const nk1 = (this._complex) ? this._nfft1 : this._nfft1 / 2 + 1;
const nk2 = this._nfft2;
const dp1 = sign1 * 2.0 * Math.PI * this._sk1.delta * fx1;
const dp2 = sign2 * 2.0 * Math.PI * this._sk1.delta * fx2;
for (let i2 = 0; i2 < nk2; ++i2) {
p2 = i2 * dp2;
f2 = f[i2];
for (let i1 = 0, ir = 0, ii = 1; i1 < nk1; ++i1, ir += 2, ii += 2) {
p = i1 * dp1 * p2;
cosp = Math.cos(p);
sinp = Math.sin(p);
fr = f2[ir];
fi = f2[ii];
f2[ir] = fr * cosp - fi * sinp;
f2[ii] = fi * cosp + fr * sinp;
}
}
}
/** @internal */
_doPhase3(f, sign1 = this._sign1, sign2 = this._sign2, sign3 = this._sign3) {
const fx1 = this._sx1.first;
const fx2 = this._sx2.first;
const fx3 = this._sx3.first;
let p, cosp, sinp, fr, fi;
if (fx1 === 0.0 && fx2 === 0.0 && fx3 === 0.0) {
return;
}
const nk1 = (this._complex) ? this._nfft1 : this._nfft1 / 2 + 1;
const nk2 = this._nfft2;
const nk3 = this._nfft3;
const dp1 = sign1 * 2.0 * Math.PI * this._sk1.delta * fx1;
const dp2 = sign2 * 2.0 * Math.PI * this._sk2.delta * fx2;
const dp3 = sign3 * 2.0 * Math.PI * this._sk3.delta * fx3;
for (let i3 = 0; i3 < nk3; ++i3) {
for (let i2 = 0; i2 < nk2; ++i2) {
const p23 = i2 * dp2 + i3 * dp3;
const f32 = f[i3][i2];
for (let i1 = 0, ir = 0, ii = 1; i1 < nk1; ++i1, ir += 2, ii += 2) {
p = i1 * dp1 * p23;
cosp = Math.cos(p);
sinp = Math.sin(p);
fr = f32[ir];
fi = f32[ii];
f32[ir] = fr * cosp - fi * sinp;
f32[ii] = fi * cosp + fr * sinp;
}
}
}
}
/** @internal */
_center1d(f) {
if (!this._center1) {
return;
}
const nk1 = this._sk1.count;
const nfft1 = this._nfft1;
const even1 = nfft1 % 2 === 0;
if (this._complex) {
if (even1) {
// complex, nfft = 8
// 0 1 2 3 4 5 6 7 | 8
// 4 5 6 7 0 1 2 3 | 4
this._cswap(f, nfft1 / 2, 0, nfft1 / 2, 1);
f[2 * (nk1 - 1)] = f[0];
f[2 * (nk1 - 1) + 1] = f[1];
}
else {
// complex, nfft = 7
// 0 1 2 3 4 5 6
// 4 5 6 3 0 1 2
// 4 5 6 0 1 2 3
this._cswap(f, (nfft1 - 1) / 2, 0, (nfft1 + 1) / 2, 1);
this._crotateLeft(f, (nfft1 + 1) / 2, (nfft1 - 1) / 2, 1);
}
}
else {
// real, nfft = 8
// 0 1 2 3 4
// 0 1 2 3 0 1 2 3 4
this._cshift(f, nfft1 / 2 + 1, 0, nfft1 / 2);
}
}
/** @internal */
_center2d(f) {
if (!this._center2) {
return;
}
const nk2 = this._sk2.count;
const nfft2 = this._nfft2;
const even2 = nfft2 % 2 === 0;
if (even2) {
// nfft even
// 0 1 2 3 4 5 6 7 | 8
// 4 5 6 7 0 1 2 3 | 4
this._cswap(f, nfft2 / 2, 0, nfft2 / 2, 2);
f[nk2 - 1] = utils_1.ccopy(f[0]);
}
else {
// nfft odd
// 0 1 2 3 4 5 6
// 4 5 6 3 0 1 2
// 4 5 6 0 1 2 3
this._cswap(f, (nfft2 - 1) / 2, 0, (nfft2 + 1) / 2, 2);
this._crotateLeft(f, (nfft2 + 1) / 2, (nfft2 - 1) / 2, 2);
}
}
/** @ignore */
_center3d(f) {
if (!this._center3) {
return;
}
const nk3 = this._sk3.count;
const nfft3 = this._nfft3;
const even3 = nfft3 % 2 === 0;
if (even3) {
// nfft even
// 0 1 2 3 4 5 6 7 | 8
// 4 5 6 7 0 1 2 3 | 4
this._cswap(f, nfft3 / 2, 0, nfft3 / 2, 3);
f[nk3 - 1] = utils_1.ccopy(f[0]);
}
else {
// nfft odd
// 0 1 2 3 4 5 6
// 4 5 6 3 0 1 2
// 4 5 6 0 1 2 3
this._cswap(f, (nfft3 - 1) / 2, 0, (nfft3 + 1) / 2, 3);
this._crotateLeft(f, (nfft3 + 1) / 2, (nfft3 - 1) / 2, 3);
}
}
/** @internal */
_uncenter(f, dims) {
switch (dims) {
case 1:
this._uncenter1(f);
case 2:
this._uncenter2(f);
default:
this._uncenter3(f);
}
}
;
/** @internal */
_uncenter1(f) {
if (!this._center1) {
return;
}
const nfft1 = this._nfft1;
const even1 = nfft1 % 2 === 0;
if (this._complex) {
if (even1) {
// complex, nfft = 8
// 4 5 6 7 0 1 2 3 | 8
// 0 1 2 3 4 5 6 7 | 8
this._cswap(f, nfft1 / 2, 0, nfft1 / 2, 1);
}
else {
// complex, nfft = 7
// 4 5 6 0 1 2 3
// 4 5 6 3 0 1 2
// 0 1 2 3 4 5 6
this._crotateRight(f, (nfft1 + 1) / 2, (nfft1 - 1) / 2, 1);
this._cswap(f, (nfft1 - 1) / 2, 0, (nfft1 + 1) / 2, 1);
}
}
else {
// real, nfft = 8
// 4 3 2 1 0 1 2 3 4
// 0 1 2 3 4 1 2 3 4
this._cshift(f, nfft1 / 2 + 1, nfft1 / 2, 0);
}
}
/** @internal */
_uncenter2(f) {
if (!this._center2) {
return;
}
const nfft2 = this._nfft2;
const even2 = nfft2 % 2 === 0;
if (even2) {
// nfft even
// 4 5 6 7 0 1 2 3 | 8
// 0 1 2 3 4 5 6 7 | 8
this._cswap(f, nfft2 / 2, 0, nfft2 / 2, 2);
}
else {
// nfft odd
// 4 5 6 0 1 2 3
// 4 5 6 3 0 1 2
// 0 1 2 3 4 5 6
this._crotateRight(f, (nfft2 + 1) / 2, (nfft2 - 1) / 2, 2);
this._cswap(f, (nfft2 - 1) / 2, 0, (nfft2 + 1) / 2, 2);
}
}
/** @internal */
_uncenter3(f) {
if (!this._center3) {
return;
}
const nfft3 = this._nfft3;
const even3 = nfft3 % 2 === 0;
if (even3) {
// nfft even
// 4 5 6 7 0 1 2 3 | 8
// 0 1 2 3 4 5 6 7 | 8
this._cswap(f, nfft3 / 2, 0, nfft3 / 2, 3);
}
else {
// nfft odd
// 4 5 6 0 1 2 3
// 4 5 6 3 0 1 2
// 0 1 2 3 4 5 6
this._crotateRight(f, (nfft3 + 1) / 2, (nfft3 - 1) / 2, 3);
this._cswap(f, (nfft3 - 1) / 2, 0, (nfft3 + 1) / 2, 3);
}
}
}
exports.Fft = Fft;
//# sourceMappingURL=fft.js.map