import { xCheck } from "./xCheck.js";
/**
 * Downsample an array by averaging consecutive non-overlapping points (binning).
 * Either specify a fixed `binSize` or a target `numberOfPoints`.
 * @param array - input array.
 * @param options - options.
 * @returns downsampled array.
 */
export function xBinning(array, options = {}) {
    xCheck(array);
    const { binSize, numberOfPoints, keepFirstAndLast = true } = options;
    const { length } = array;
    if (numberOfPoints !== undefined) {
        if (binSize !== undefined) {
            throw new RangeError('binSize and numberOfPoints are mutually exclusive');
        }
        if (!Number.isInteger(numberOfPoints) || numberOfPoints <= 0) {
            throw new RangeError('numberOfPoints must be a positive integer');
        }
        if (numberOfPoints > length) {
            throw new RangeError('numberOfPoints must be <= array.length');
        }
        return binByNumberOfPoints(array, numberOfPoints, keepFirstAndLast);
    }
    const effectiveBinSize = binSize ?? 10;
    if (!Number.isInteger(effectiveBinSize) || effectiveBinSize <= 0) {
        throw new RangeError('binSize must be a positive integer');
    }
    if (effectiveBinSize === 1) {
        return Float64Array.from(array);
    }
    if (keepFirstAndLast) {
        if (length <= 2) {
            return Float64Array.from(array);
        }
        const innerLength = length - 2;
        const innerBinCount = Math.ceil(innerLength / effectiveBinSize);
        const output = new Float64Array(innerBinCount + 2);
        output[0] = array[0];
        output[output.length - 1] = array[length - 1];
        for (let i = 0, j = 1; i < innerLength; i += effectiveBinSize, j++) {
            const start = i + 1;
            const end = Math.min(start + effectiveBinSize, length - 1);
            let sum = 0;
            for (let k = start; k < end; k++) {
                sum += array[k];
            }
            output[j] = sum / (end - start);
        }
        return output;
    }
    const outputLength = Math.ceil(length / effectiveBinSize);
    const output = new Float64Array(outputLength);
    for (let i = 0, j = 0; i < length; i += effectiveBinSize, j++) {
        const end = Math.min(i + effectiveBinSize, length);
        let sum = 0;
        for (let k = i; k < end; k++) {
            sum += array[k];
        }
        output[j] = sum / (end - i);
    }
    return output;
}
function binByNumberOfPoints(array, numberOfPoints, keepFirstAndLast) {
    const { length } = array;
    const output = new Float64Array(numberOfPoints);
    if (keepFirstAndLast) {
        if (numberOfPoints < 2) {
            throw new RangeError('numberOfPoints must be >= 2 when keepFirstAndLast is true');
        }
        output[0] = array[0];
        output[numberOfPoints - 1] = array[length - 1];
        if (numberOfPoints === 2) {
            return output;
        }
        const innerLength = length - 2;
        const innerBins = numberOfPoints - 2;
        for (let j = 0; j < innerBins; j++) {
            const start = 1 + Math.floor((j * innerLength) / innerBins);
            const end = 1 + Math.floor(((j + 1) * innerLength) / innerBins);
            let sum = 0;
            for (let k = start; k < end; k++) {
                sum += array[k];
            }
            output[j + 1] = sum / (end - start);
        }
        return output;
    }
    for (let j = 0; j < numberOfPoints; j++) {
        const start = Math.floor((j * length) / numberOfPoints);
        const end = Math.floor(((j + 1) * length) / numberOfPoints);
        let sum = 0;
        for (let k = start; k < end; k++) {
            sum += array[k];
        }
        output[j] = sum / (end - start);
    }
    return output;
}
//# sourceMappingURL=xBinning.js.map