import { getFFT } from "../utils/fftCache.js";
import { isPowerOfTwo, nextPowerOfTwo } from "../utils/index.js";
import { xCheck } from "./xCheck.js";
/**
 * Performs the Hilbert transform
 * @link https://en.wikipedia.org/wiki/Hilbert_transform
 * @param array - array containing values.
 * @param options - options.
 * @returns a new vector with 90 degree shift regarding the phase of the original function.
 */
export function xHilbertTransform(array, options = {}) {
    xCheck(array);
    const { forceFFT = false } = options;
    const length = array.length;
    if (isPowerOfTwo(length)) {
        return hilbertTransformWithFFT(array);
    }
    else if (forceFFT) {
        return resampling(hilbertTransformWithFFT(resampling(array, nextPowerOfTwo(length))), length);
    }
    else {
        return hilbertTransform(array);
    }
}
/**
 * Performs the discrete Hilbert transform using fast Fourier transform
 * @param array - array containing values.
 * @returns a new vector with 90 degree shift regarding the phase of the original function.
 * @see DOI: 10.1109/TAU.1970.1162139 "Discrete Hilbert transform"
 */
function hilbertTransformWithFFT(array) {
    const length = array.length;
    const fft = getFFT(length);
    // Single reusable buffer for FFT spectrum
    const spectrum = new Float64Array(length * 2);
    // Forward FFT
    fft.realTransform(spectrum, array);
    fft.completeSpectrum(spectrum);
    const half = length >> 1;
    // Zero Nyquist
    const j = half << 1;
    spectrum[j] = 0;
    spectrum[j + 1] = 0;
    // Negate negative frequencies
    for (let j = (half + 1) << 1; j < spectrum.length; j += 2) {
        spectrum[j] = -spectrum[j];
        spectrum[j + 1] = -spectrum[j + 1];
    }
    const hilbertSignal = new Float64Array(length * 2);
    fft.inverseTransform(hilbertSignal, spectrum);
    // Extract imaginary part directly into output
    const result = new Float64Array(length);
    for (let i = 0; i < length; i++) {
        result[i] = hilbertSignal[i * 2 + 1];
    }
    return result;
}
/**
 * Performs the discrete Hilbert transform
 * @param array - array containing values.
 * @param options - options.
 * @param options.inClockwise - whether to apply clockwise rotation.
 * @returns a new vector with 90 degree shift regarding the phase of the original function.
 */
function hilbertTransform(array, options = {}) {
    const { inClockwise = true } = options;
    const input = [0, ...array, 0];
    const result = new Float64Array(array.length);
    for (let k = 1; k < input.length - 1; k++) {
        let aSum = 0;
        for (let i = 0; i < k - 1; i++) {
            const log = Math.log((k - i) / (k - i - 1));
            aSum += input[i] * log + (input[i + 1] - input[i]) * (-1 + (k - i) * log);
        }
        const b = input[k - 1] - input[k + 1];
        let cSum = 0;
        for (let i = k + 1; i < input.length - 1; i++) {
            const log = Math.log((i - k) / (i - k + 1));
            cSum += input[i] * log + (input[i - 1] - input[i]) * (1 + (i - k) * log);
        }
        result[k - 1] = ((inClockwise ? 1 : -1) * (aSum + b + cSum)) / Math.PI;
    }
    return result;
}
/**
 * Performs resampling of an input array to the desired length employing linear interpolation.
 * @param array - array containing values.
 * @param length - the length of the resulting array.
 * @returns a new array of the desired length.
 * @link https://en.wikipedia.org/wiki/Sample-rate_conversion
 */
function resampling(array, length) {
    xCheck(array);
    const oldLength = array.length;
    const ratio = (oldLength - 1) / (length - 1);
    const result = new Float64Array(length);
    let currentIndex = 0;
    let floor = Math.floor(currentIndex);
    let ceil = Math.min(Math.ceil(currentIndex), oldLength - 1);
    let diff = currentIndex - floor;
    for (let i = 0; i < length; i++) {
        result[i] = array[floor] * (1 - diff) + array[ceil] * diff;
        currentIndex += ratio;
        floor = Math.floor(currentIndex);
        ceil = Math.min(Math.ceil(currentIndex), oldLength - 1);
        diff = currentIndex - floor;
    }
    return result;
}
//# sourceMappingURL=xHilbertTransform.js.map