// Bucketed value distribution for one parametric test — generalized from
// tsmap's own charts/aggregate.ts. Pure math, no DOM.
//
// Two builders, mirroring tsmap's own split: `buildTestHistogramData` is the
// single-population view (used ungrouped, or restricted to one item), and
// `buildTestHistogramSeries` is the *faceted* view — one count-series per
// group over a shared set of bucket ranges, so the series overlay and
// compare directly on one axis. Unlike boxplot/capability, wmap's Analysis
// tab hands this one the `groups` list directly (it already computes it),
// rather than a `groupBy` callback over a flat wafer list like tsmap's
// version — same result, one fewer indirection (see WMAP_ISSUES.md).
//
// Deliberate exception to the "prefer StatsSummary.stats.perTestStats over
// raw Die[]" dedup pattern used elsewhere in this package (boxplot.ts,
// capability.ts's mean/stddev, binPareto.ts, summaryPanel.ts, etc.):
// bucket assignment needs every individual value (`Math.floor((v - min) /
// bucketWidth)`), which a five-number-summary/mean/stddev cannot
// reconstruct. A histogram is exactly the shape of information
// `perTestStats` intentionally does not carry, so this module always reads
// raw `Die[]` and always will.
/**
 * Histogram of one test's values across `items`, divided into `bucketCount`
 * equal-width buckets. If `limitLow`/`limitHigh` are given, the axis range
 * is expanded to include them so limit lines always draw.
 */
export function buildTestHistogramData(items, testNumber, bucketCount = 16, limitLow, limitHigh) {
    const values = [];
    for (const item of items) {
        for (const die of item.dies ?? []) {
            const v = die.testValues?.[testNumber];
            if (v !== undefined && Number.isFinite(v))
                values.push(v);
        }
    }
    if (values.length === 0)
        return [];
    let dataMin = values[0], dataMax = values[0];
    for (let i = 1; i < values.length; i++) {
        if (values[i] < dataMin)
            dataMin = values[i];
        if (values[i] > dataMax)
            dataMax = values[i];
    }
    const min = limitLow !== undefined ? Math.min(dataMin, limitLow) : dataMin;
    const max = limitHigh !== undefined ? Math.max(dataMax, limitHigh) : dataMax;
    const span = max - min || 1;
    const width = span / bucketCount;
    const buckets = Array.from({ length: bucketCount }, (_, i) => ({
        rangeLow: min + i * width,
        rangeHigh: min + (i + 1) * width,
        count: 0,
    }));
    for (const v of values) {
        const index = Math.min(bucketCount - 1, Math.floor((v - min) / width));
        buckets[index].count++;
    }
    return buckets;
}
/**
 * Faceted histogram: one count-series per group over a *shared* set of
 * buckets, so the series overlay and compare directly. The bucket range
 * spans every group's dies (and the limits when given) so all series align
 * on one axis. Groups are returned in the order given; empty groups (no
 * valid values for this test) are omitted.
 */
export function buildTestHistogramSeries(groups, testNumber, bucketCount = 16, limitLow, limitHigh) {
    const byGroup = new Map();
    let dataMin = Infinity, dataMax = -Infinity;
    for (const g of groups) {
        const vals = [];
        for (const item of g.items) {
            for (const die of item.dies ?? []) {
                const v = die.testValues?.[testNumber];
                if (v !== undefined && Number.isFinite(v)) {
                    vals.push(v);
                    if (v < dataMin)
                        dataMin = v;
                    if (v > dataMax)
                        dataMax = v;
                }
            }
        }
        byGroup.set(g.key, vals);
    }
    const nonEmpty = groups.map(g => g.key).filter(k => byGroup.get(k).length > 0);
    if (nonEmpty.length === 0)
        return { ranges: [], series: [] };
    const min = limitLow !== undefined ? Math.min(dataMin, limitLow) : dataMin;
    const max = limitHigh !== undefined ? Math.max(dataMax, limitHigh) : dataMax;
    const span = max - min || 1;
    const width = span / bucketCount;
    const ranges = Array.from({ length: bucketCount }, (_, i) => ({
        rangeLow: min + i * width,
        rangeHigh: min + (i + 1) * width,
    }));
    const series = nonEmpty.map(groupKey => {
        const counts = new Array(bucketCount).fill(0);
        for (const v of byGroup.get(groupKey)) {
            const index = Math.min(bucketCount - 1, Math.floor((v - min) / width));
            counts[index]++;
        }
        return { groupKey, counts };
    });
    return { ranges, series };
}
//# sourceMappingURL=histogram.js.map