import {
  ValidationError
} from "./chunk-ONWEPEDO.js";

// src/statistics.ts
var normalizeScores = (scores) => scores;
function weightedMean(scores) {
  if (scores.length === 0) return 0;
  let totalWeight = 0;
  let weightedSum = 0;
  for (const { score, weight } of scores) {
    const w = weight ?? 1;
    weightedSum += score * w;
    totalWeight += w;
  }
  return totalWeight > 0 ? weightedSum / totalWeight : 0;
}
function confidenceInterval(scores, confidence = 0.95, opts = {}) {
  if (scores.length === 0) return { mean: 0, lower: 0, upper: 0 };
  if (scores.length === 1) return { mean: scores[0], lower: scores[0], upper: scores[0] };
  const n = scores.length;
  const mean = scores.reduce((a, b) => a + b, 0) / n;
  const B = opts.resamples ?? 1e3;
  const rng = makeRng(opts.seed);
  const bootstrapMeans = [];
  for (let i = 0; i < B; i++) {
    let sum = 0;
    for (let j = 0; j < n; j++) {
      sum += scores[Math.floor(rng() * n)];
    }
    bootstrapMeans.push(sum / n);
  }
  bootstrapMeans.sort((a, b) => a - b);
  const alpha = 1 - confidence;
  const lowerIdx = Math.floor(alpha / 2 * B);
  const upperIdx = Math.floor((1 - alpha / 2) * B) - 1;
  return {
    mean,
    lower: bootstrapMeans[lowerIdx],
    upper: bootstrapMeans[Math.min(upperIdx, B - 1)]
  };
}
function interRaterReliability(judgeScores) {
  if (judgeScores.length < 2) return 1;
  const dimensionMap = /* @__PURE__ */ new Map();
  for (const judgeSet of judgeScores) {
    for (const s of judgeSet) {
      if (!dimensionMap.has(s.dimension)) dimensionMap.set(s.dimension, []);
      const arr = dimensionMap.get(s.dimension);
      if (arr.length === 0 || arr[arr.length - 1].length >= judgeScores.length) {
        arr.push([s.score]);
      } else {
        arr[arr.length - 1].push(s.score);
      }
    }
  }
  const allValues = [];
  const pairDiffs = [];
  for (const items of dimensionMap.values()) {
    for (const ratings of items) {
      if (ratings.length < 2) continue;
      for (const v of ratings) allValues.push(v);
      for (let i = 0; i < ratings.length; i++) {
        for (let j = i + 1; j < ratings.length; j++) {
          pairDiffs.push((ratings[i] - ratings[j]) ** 2);
        }
      }
    }
  }
  if (pairDiffs.length === 0 || allValues.length < 2) return 1;
  const observedDisagreement = pairDiffs.reduce((a, b) => a + b, 0) / pairDiffs.length;
  let expectedDisagreement = 0;
  let expectedCount = 0;
  for (let i = 0; i < allValues.length; i++) {
    for (let j = i + 1; j < allValues.length; j++) {
      expectedDisagreement += (allValues[i] - allValues[j]) ** 2;
      expectedCount++;
    }
  }
  expectedDisagreement = expectedCount > 0 ? expectedDisagreement / expectedCount : 0;
  if (expectedDisagreement === 0) return 1;
  return 1 - observedDisagreement / expectedDisagreement;
}
function mannWhitneyU(a, b) {
  if (a.length === 0 || b.length === 0) return { u: 0, p: 1 };
  const n1 = a.length;
  const n2 = b.length;
  const combined = [
    ...a.map((v) => ({ v, group: "a" })),
    ...b.map((v) => ({ v, group: "b" }))
  ].sort((x, y) => x.v - y.v);
  const ranks2 = new Array(combined.length);
  let i = 0;
  while (i < combined.length) {
    let j = i;
    while (j < combined.length && combined[j].v === combined[i].v) j++;
    const avgRank = (i + 1 + j) / 2;
    for (let k = i; k < j; k++) ranks2[k] = avgRank;
    i = j;
  }
  let r1 = 0;
  for (let k = 0; k < combined.length; k++) {
    if (combined[k].group === "a") r1 += ranks2[k];
  }
  const u1 = r1 - n1 * (n1 + 1) / 2;
  const u2 = n1 * n2 - u1;
  const u = Math.min(u1, u2);
  const mu = n1 * n2 / 2;
  const sigma = Math.sqrt(n1 * n2 * (n1 + n2 + 1) / 12);
  if (sigma === 0) return { u, p: 1 };
  const z = Math.abs(u - mu) / sigma;
  const p = 2 * (1 - normalCdf(z));
  return { u, p };
}
function partialCredit(current, target) {
  if (target <= 0) return 1;
  return Math.min(1, Math.max(0, current / target));
}
function pairedTTest(before, after) {
  if (before.length !== after.length) {
    throw new ValidationError(
      `pairedTTest: unequal sample sizes (${before.length} vs ${after.length})`
    );
  }
  const n = before.length;
  if (n < 2) return { t: 0, df: 0, p: 1 };
  const diffs = before.map((b, i) => after[i] - b);
  const mean = diffs.reduce((a, b) => a + b, 0) / n;
  const variance = diffs.reduce((acc, d) => acc + (d - mean) ** 2, 0) / (n - 1);
  const se = Math.sqrt(variance / n);
  if (se === 0) return { t: mean === 0 ? 0 : Infinity, df: n - 1, p: mean === 0 ? 1 : 0 };
  const t = mean / se;
  const df = n - 1;
  const p = 2 * (1 - studentTCdf(Math.abs(t), df));
  return { t, df, p };
}
function wilcoxonSignedRank(before, after) {
  if (before.length !== after.length) {
    throw new ValidationError(
      `wilcoxonSignedRank: unequal sample sizes (${before.length} vs ${after.length})`
    );
  }
  const diffs = before.map((b, i2) => after[i2] - b).filter((d) => d !== 0);
  const n = diffs.length;
  if (n < 6) return { w: 0, p: 1 };
  const absRanks = diffs.map((d, i2) => ({ abs: Math.abs(d), sign: Math.sign(d), i: i2 })).sort((a, b) => a.abs - b.abs);
  const ranks2 = new Array(n);
  let i = 0;
  while (i < n) {
    let j = i;
    while (j < n && absRanks[j].abs === absRanks[i].abs) j++;
    const avg = (i + 1 + j) / 2;
    for (let k = i; k < j; k++) ranks2[absRanks[k].i] = avg;
    i = j;
  }
  let wPlus = 0;
  for (let k = 0; k < n; k++) if (diffs[k] > 0) wPlus += ranks2[k];
  const mean = n * (n + 1) / 4;
  const variance = n * (n + 1) * (2 * n + 1) / 24;
  const z = (wPlus - mean) / Math.sqrt(variance);
  const p = 2 * (1 - normalCdf(Math.abs(z)));
  return { w: wPlus, p };
}
function cohensD(a, b) {
  if (a.length < 2 || b.length < 2) return 0;
  const meanA = a.reduce((x, y) => x + y, 0) / a.length;
  const meanB = b.reduce((x, y) => x + y, 0) / b.length;
  const varA = a.reduce((acc, x) => acc + (x - meanA) ** 2, 0) / (a.length - 1);
  const varB = b.reduce((acc, x) => acc + (x - meanB) ** 2, 0) / (b.length - 1);
  const pooled = Math.sqrt(
    ((a.length - 1) * varA + (b.length - 1) * varB) / (a.length + b.length - 2)
  );
  if (pooled === 0) return 0;
  return (meanB - meanA) / pooled;
}
function cliffsDelta(before, after) {
  const n = before.length * after.length;
  if (n === 0) return 0;
  let dominance = 0;
  for (const a of after) {
    for (const b of before) {
      if (a > b) dominance += 1;
      else if (a < b) dominance -= 1;
    }
  }
  return dominance / n;
}
function interpretCliffs(delta) {
  const d = Math.abs(delta);
  if (d < 0.147) return "negligible";
  if (d < 0.33) return "small";
  if (d < 0.474) return "medium";
  return "large";
}
function ranks(xs) {
  const indexed = xs.map((v, i2) => ({ v, i: i2 })).sort((a, b) => a.v - b.v);
  const r = new Array(xs.length);
  let i = 0;
  while (i < indexed.length) {
    let j = i;
    while (j + 1 < indexed.length && indexed[j + 1].v === indexed[i].v) j++;
    const avg = (i + j) / 2 + 1;
    for (let k = i; k <= j; k++) r[indexed[k].i] = avg;
    i = j + 1;
  }
  return r;
}
function pearsonR(a, b) {
  if (a.length !== b.length || a.length < 2) return Number.NaN;
  const n = a.length;
  const meanA = a.reduce((s, v) => s + v, 0) / n;
  const meanB = b.reduce((s, v) => s + v, 0) / n;
  let num = 0;
  let varA = 0;
  let varB = 0;
  for (let i = 0; i < n; i++) {
    const da = a[i] - meanA;
    const db = b[i] - meanB;
    num += da * db;
    varA += da * da;
    varB += db * db;
  }
  if (varA === 0 || varB === 0) return varA === 0 && varB === 0 ? 1 : 0;
  return num / Math.sqrt(varA * varB);
}
function spearmanR(a, b) {
  if (a.length !== b.length || a.length < 2) return Number.NaN;
  return pearsonR(ranks(a), ranks(b));
}
function weightedComposite(input) {
  const entries = Object.entries(input.weights);
  if (entries.length === 0) {
    throw new Error("weightedComposite: `weights` is empty \u2014 nothing to combine");
  }
  let weightedSum = 0;
  let weightTotal = 0;
  for (const [dim, weight] of entries) {
    if (weight < 0) {
      throw new Error(`weightedComposite: weight for '${dim}' is negative (${weight})`);
    }
    if (!(dim in input.dims)) {
      throw new Error(
        `weightedComposite: weighted dimension '${dim}' is absent from \`dims\` \u2014 refusing to renormalise onto a different denominator`
      );
    }
    weightedSum += input.dims[dim] * weight;
    weightTotal += weight;
  }
  if (weightTotal === 0) {
    throw new Error("weightedComposite: weights sum to 0 \u2014 composite is undefined");
  }
  const composite = weightedSum / weightTotal;
  return input.threshold === void 0 ? { composite } : { composite, pass: composite >= input.threshold };
}
function corpusInterRaterAgreement(records, opts = {}) {
  if (records.length === 0) {
    throw new ValidationError("corpusInterRaterAgreement: no score records supplied");
  }
  const judgesSeen = /* @__PURE__ */ new Set();
  const dimsSeen = /* @__PURE__ */ new Set();
  const grid = /* @__PURE__ */ new Map();
  for (const r of records) {
    if (!Number.isFinite(r.score)) {
      throw new ValidationError(
        `corpusInterRaterAgreement: non-finite score for (item=${r.itemId}, judge=${r.judgeName}, dim=${r.dimension})`
      );
    }
    judgesSeen.add(r.judgeName);
    dimsSeen.add(r.dimension);
    const byJudge = grid.get(r.dimension) ?? /* @__PURE__ */ new Map();
    const byItem = byJudge.get(r.judgeName) ?? /* @__PURE__ */ new Map();
    if (byItem.has(r.itemId)) {
      throw new ValidationError(
        `corpusInterRaterAgreement: duplicate record for (item=${r.itemId}, judge=${r.judgeName}, dim=${r.dimension})`
      );
    }
    byItem.set(r.itemId, r.score);
    byJudge.set(r.judgeName, byItem);
    grid.set(r.dimension, byJudge);
  }
  const targetDims = opts.dimensions ?? [...dimsSeen].sort();
  for (const d of targetDims) {
    if (!dimsSeen.has(d)) {
      throw new ValidationError(
        `corpusInterRaterAgreement: dimension '${d}' was requested but no records carry it`
      );
    }
  }
  const targetJudges = opts.judges ? [...opts.judges] : [...judgesSeen].sort();
  for (const j of targetJudges) {
    if (!judgesSeen.has(j)) {
      throw new ValidationError(
        `corpusInterRaterAgreement: judge '${j}' was requested but produced no records`
      );
    }
  }
  if (targetJudges.length < 2) {
    throw new ValidationError(
      `corpusInterRaterAgreement: need \u22652 judges, got ${targetJudges.length}`
    );
  }
  const perDimension = [];
  const iccs = [];
  const kappas = [];
  for (const dim of targetDims) {
    const byJudge = grid.get(dim);
    const judgeItemCounts = {};
    for (const j of targetJudges) {
      const m = byJudge.get(j);
      judgeItemCounts[j] = m?.size ?? 0;
    }
    const emptyJudges = targetJudges.filter((j) => judgeItemCounts[j] === 0);
    if (emptyJudges.length > 0) {
      throw new ValidationError(
        `corpusInterRaterAgreement: dimension '${dim}' has no scores from judge(s) ${emptyJudges.join(", ")} (counts: ${JSON.stringify(judgeItemCounts)})`
      );
    }
    let commonItems = null;
    for (const j of targetJudges) {
      const ids = new Set(byJudge.get(j).keys());
      if (commonItems === null) {
        commonItems = ids;
      } else {
        const prev = commonItems;
        commonItems = new Set([...prev].filter((x) => ids.has(x)));
      }
    }
    const sortedItems = [...commonItems ?? /* @__PURE__ */ new Set()].sort();
    if (sortedItems.length < 2) {
      throw new ValidationError(
        `corpusInterRaterAgreement: dimension '${dim}' has ${sortedItems.length} item(s) rated by all ${targetJudges.length} judges (need \u22652)`
      );
    }
    const matrix = sortedItems.map(
      (itemId) => targetJudges.map((j) => byJudge.get(j).get(itemId))
    );
    const agreement = continuousAgreement(matrix, opts);
    perDimension.push({
      ...agreement,
      dimension: dim,
      itemIds: sortedItems,
      judgeIds: [...targetJudges]
    });
    if (Number.isFinite(agreement.icc)) iccs.push(agreement.icc);
    if (Number.isFinite(agreement.weightedKappa)) kappas.push(agreement.weightedKappa);
  }
  const mean = (xs) => xs.length === 0 ? Number.NaN : xs.reduce((a, b) => a + b, 0) / xs.length;
  return {
    perDimension,
    overallIcc: mean(iccs),
    overallWeightedKappa: mean(kappas),
    dimensions: targetDims,
    judgeIds: targetJudges
  };
}
function corpusInterRaterAgreementFromJudgeScores(itemsScores, opts = {}) {
  const records = [];
  const seen = /* @__PURE__ */ new Set();
  for (const { itemId, scores } of itemsScores) {
    if (seen.has(itemId)) {
      throw new ValidationError(
        `corpusInterRaterAgreementFromJudgeScores: duplicate itemId '${itemId}'`
      );
    }
    seen.add(itemId);
    for (const s of scores) {
      records.push({
        itemId,
        judgeName: s.judgeName,
        dimension: s.dimension,
        score: s.score
      });
    }
  }
  return corpusInterRaterAgreement(records, opts);
}
function studentTCdf(t, df) {
  if (df <= 0) return 0.5;
  if (df > 100) return normalCdf(t);
  const x = df / (df + t * t);
  const a = df / 2;
  const b = 0.5;
  const ib = incompleteBeta(x, a, b);
  return t >= 0 ? 1 - 0.5 * ib : 0.5 * ib;
}
function incompleteBeta(x, a, b) {
  if (x <= 0) return 0;
  if (x >= 1) return 1;
  const lnBeta = lnGamma(a) + lnGamma(b) - lnGamma(a + b);
  const front = Math.exp(Math.log(x) * a + Math.log(1 - x) * b - lnBeta) / a;
  const maxIter = 200;
  const eps = 3e-7;
  let c = 1;
  let d = 1 - (a + b) * x / (a + 1);
  if (Math.abs(d) < 1e-30) d = 1e-30;
  d = 1 / d;
  let f = d;
  for (let m = 1; m <= maxIter; m++) {
    const m2 = 2 * m;
    let num = m * (b - m) * x / ((a + m2 - 1) * (a + m2));
    d = 1 + num * d;
    if (Math.abs(d) < 1e-30) d = 1e-30;
    c = 1 + num / c;
    if (Math.abs(c) < 1e-30) c = 1e-30;
    d = 1 / d;
    f *= d * c;
    num = -((a + m) * (a + b + m) * x) / ((a + m2) * (a + m2 + 1));
    d = 1 + num * d;
    if (Math.abs(d) < 1e-30) d = 1e-30;
    c = 1 + num / c;
    if (Math.abs(c) < 1e-30) c = 1e-30;
    d = 1 / d;
    const delta = d * c;
    f *= delta;
    if (Math.abs(delta - 1) < eps) break;
  }
  return front * f;
}
function lnGamma(z) {
  const g = 7;
  const coefs = [
    0.9999999999998099,
    676.5203681218851,
    -1259.1392167224028,
    771.3234287776531,
    -176.6150291621406,
    12.507343278686905,
    -0.13857109526572012,
    9984369578019572e-21,
    15056327351493116e-23
  ];
  if (z < 0.5) {
    return Math.log(Math.PI / Math.sin(Math.PI * z)) - lnGamma(1 - z);
  }
  z -= 1;
  let x = coefs[0];
  for (let i = 1; i < g + 2; i++) x += coefs[i] / (z + i);
  const t = z + g + 0.5;
  return 0.5 * Math.log(2 * Math.PI) + (z + 0.5) * Math.log(t) - t + Math.log(x);
}
function normalCdf(x) {
  const a1 = 0.254829592;
  const a2 = -0.284496736;
  const a3 = 1.421413741;
  const a4 = -1.453152027;
  const a5 = 1.061405429;
  const p = 0.3275911;
  const sign = x < 0 ? -1 : 1;
  const absX = Math.abs(x);
  const t = 1 / (1 + p * absX);
  const y = 1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-absX * absX / 2);
  return 0.5 * (1 + sign * y);
}
function requiredSampleSize(opts) {
  const effect = opts.effect;
  if (!Number.isFinite(effect) || effect <= 0) return Infinity;
  const alpha = opts.alpha ?? 0.05;
  const power = opts.power ?? 0.8;
  const twoSided = opts.twoSided ?? true;
  const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha);
  const zBeta = zQuantile(power);
  const n = 2 * ((zAlpha + zBeta) / effect) ** 2;
  return Math.ceil(n);
}
function pairedMde(opts) {
  if (!Number.isFinite(opts.nPaired) || opts.nPaired <= 0) return Infinity;
  const alpha = opts.alpha ?? 0.05;
  const power = opts.power ?? 0.8;
  const twoSided = opts.twoSided ?? true;
  const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha);
  const zBeta = zQuantile(power);
  return (zAlpha + zBeta) / Math.sqrt(opts.nPaired);
}
function mcnemarRequiredN(opts) {
  const { p10, p01 } = opts;
  if (p10 < 0 || p01 < 0 || p10 + p01 > 1) {
    throw new Error(`mcnemarRequiredN: require p10,p01 \u2265 0 and p10+p01 \u2264 1 (got ${p10}, ${p01})`);
  }
  const delta = p10 - p01;
  if (delta === 0) return Infinity;
  const alpha = opts.alpha ?? 0.05;
  const power = opts.power ?? 0.8;
  const twoSided = opts.twoSided ?? true;
  const pDisc = p10 + p01;
  const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha);
  const zBeta = zQuantile(power);
  const n = (zAlpha * Math.sqrt(pDisc) + zBeta * Math.sqrt(Math.max(0, pDisc - delta * delta))) ** 2 / (delta * delta);
  return Math.ceil(n);
}
function mcnemarPower(opts) {
  const { p10, p01, nPairs } = opts;
  if (p10 < 0 || p01 < 0 || p10 + p01 > 1) {
    throw new Error(`mcnemarPower: require p10,p01 \u2265 0 and p10+p01 \u2264 1 (got ${p10}, ${p01})`);
  }
  const alpha = opts.alpha ?? 0.05;
  const twoSided = opts.twoSided ?? true;
  const delta = p10 - p01;
  if (delta === 0 || nPairs <= 0) return alpha;
  const pDisc = p10 + p01;
  const zAlpha = zQuantile(twoSided ? 1 - alpha / 2 : 1 - alpha);
  const denom = Math.sqrt(Math.max(1e-12, pDisc - delta * delta));
  const zBeta = (Math.sqrt(nPairs) * Math.abs(delta) - zAlpha * Math.sqrt(pDisc)) / denom;
  return Math.min(1, Math.max(0, normalCdf(zBeta)));
}
function bonferroni(pValues, alpha = 0.05) {
  const k = pValues.length;
  const adjusted = pValues.map((p) => Math.min(1, p * k));
  const significant = adjusted.map((p) => p < alpha);
  return { adjusted, significant };
}
function holm(pValues, alpha = 0.05) {
  if (!Number.isFinite(alpha) || alpha <= 0 || alpha >= 1) {
    throw new ValidationError(`holm: alpha must be in (0,1), got ${alpha}`);
  }
  for (const [index, pValue] of pValues.entries()) {
    if (!Number.isFinite(pValue) || pValue < 0 || pValue > 1) {
      throw new ValidationError(`holm: pValues[${index}] must be in [0,1], got ${pValue}`);
    }
  }
  const count = pValues.length;
  if (count === 0) return { adjusted: [], significant: [] };
  const ordered = pValues.map((pValue, index) => ({ pValue, index })).sort((a, b) => a.pValue - b.pValue || a.index - b.index);
  const adjusted = new Array(count);
  let previous = 0;
  for (let rank = 0; rank < count; rank++) {
    const entry = ordered[rank];
    const stepAdjusted = Math.min(1, entry.pValue * (count - rank));
    previous = Math.max(previous, stepAdjusted);
    adjusted[entry.index] = previous;
  }
  return { adjusted, significant: adjusted.map((pValue) => pValue <= alpha) };
}
function benjaminiHochberg(pValues, fdr = 0.05) {
  const n = pValues.length;
  if (n === 0) return { qValues: [], significant: [] };
  const indexed = pValues.map((p, i) => ({ p, i })).sort((a, b) => a.p - b.p);
  const q = new Array(n);
  let minRight = 1;
  for (let k = n - 1; k >= 0; k--) {
    const rank = k + 1;
    const entry = indexed[k];
    const raw = entry.p * n / rank;
    const bounded = Math.min(minRight, raw);
    minRight = bounded;
    q[entry.i] = Math.min(1, bounded);
  }
  const significant = q.map((v) => v < fdr);
  return { qValues: q, significant };
}
function pairedBootstrap(before, after, opts = {}) {
  if (before.length !== after.length) {
    throw new Error(`pairedBootstrap: unequal sample sizes (${before.length} vs ${after.length})`);
  }
  const confidence = opts.confidence ?? 0.95;
  const resamples = opts.resamples ?? 2e3;
  const statistic = opts.statistic ?? "median";
  if (confidence <= 0 || confidence >= 1) {
    throw new Error(`pairedBootstrap: confidence must be in (0,1), got ${confidence}`);
  }
  const n = before.length;
  const deltas = before.map((b, i) => after[i] - b);
  if (n === 0) {
    return { n: 0, median: 0, mean: 0, low: 0, high: 0, confidence, resamples };
  }
  if (n === 1) {
    const d = deltas[0];
    return { n: 1, median: d, mean: d, low: d, high: d, confidence, resamples };
  }
  const rng = makeRng(opts.seed);
  const samples = new Array(resamples);
  for (let b = 0; b < resamples; b++) {
    if (statistic === "mean") {
      let sum = 0;
      for (let k = 0; k < n; k++) {
        sum += deltas[Math.floor(rng() * n)];
      }
      samples[b] = sum / n;
    } else {
      const acc = new Array(n);
      for (let k = 0; k < n; k++) {
        acc[k] = deltas[Math.floor(rng() * n)];
      }
      samples[b] = medianInPlace(acc);
    }
  }
  samples.sort((a, b) => a - b);
  const alpha = 1 - confidence;
  const lowIdx = Math.floor(alpha / 2 * resamples);
  const highIdx = Math.min(resamples - 1, Math.ceil((1 - alpha / 2) * resamples) - 1);
  return {
    n,
    median: medianInPlace([...deltas]),
    mean: deltas.reduce((s, x) => s + x, 0) / n,
    low: samples[lowIdx],
    high: samples[Math.max(highIdx, lowIdx)],
    confidence,
    resamples
  };
}
function pairedSignTest(differences, alternative) {
  if (alternative !== "greater" && alternative !== "less") {
    throw new ValidationError(
      `pairedSignTest: alternative must be 'greater' or 'less', got ${alternative}`
    );
  }
  let positive = 0;
  let negative = 0;
  let ties = 0;
  for (let i = 0; i < differences.length; i++) {
    const difference = differences[i];
    if (!Number.isFinite(difference)) {
      throw new ValidationError(
        `pairedSignTest: difference at index ${i} must be finite, got ${difference}`
      );
    }
    if (difference > 0) positive++;
    else if (difference < 0) negative++;
    else ties++;
  }
  const nNonTies = positive + negative;
  const successes = alternative === "greater" ? positive : negative;
  return {
    n: differences.length,
    positive,
    negative,
    ties,
    nNonTies,
    alternative,
    pValue: binomialHalfUpperTail(successes, nNonTies)
  };
}
function wilson(successes, n, confidence = 0.95) {
  if (n <= 0) return { estimate: 0, lower: 0, upper: 0 };
  if (successes < 0 || successes > n) {
    throw new Error(`wilson: successes (${successes}) must be in [0, ${n}]`);
  }
  const z = zQuantile(1 - (1 - confidence) / 2);
  const p = successes / n;
  const z2 = z * z;
  const denom = 1 + z2 / n;
  const center = (p + z2 / (2 * n)) / denom;
  const half = z * Math.sqrt((p * (1 - p) + z2 / (4 * n)) / n) / denom;
  return {
    estimate: p,
    lower: Math.max(0, center - half),
    upper: Math.min(1, center + half)
  };
}
function mcnemar(control, treatment) {
  if (control.length !== treatment.length) {
    throw new Error(`mcnemar: unequal sample sizes (${control.length} vs ${treatment.length})`);
  }
  const n = control.length;
  let b = 0;
  let c = 0;
  for (let i = 0; i < n; i++) {
    const ctrl = control[i] ? 1 : 0;
    const treat = treatment[i] ? 1 : 0;
    if (treat === 1 && ctrl === 0) b++;
    else if (treat === 0 && ctrl === 1) c++;
  }
  const nDiscordant = b + c;
  const statistic = nDiscordant === 0 ? 0 : (Math.abs(b - c) - 1) ** 2 / nDiscordant;
  return { n, nDiscordant, b, c, statistic, pValue: binomialSignTwoSided(b, c) };
}
function pairedRiskDifference(control, treatment, confidence = 0.95) {
  if (control.length !== treatment.length) {
    throw new Error(
      `pairedRiskDifference: unequal sample sizes (${control.length} vs ${treatment.length})`
    );
  }
  const n = control.length;
  if (n === 0) return { n: 0, b: 0, c: 0, riskDifference: 0, lower: 0, upper: 0, confidence };
  let b = 0;
  let c = 0;
  for (let i = 0; i < n; i++) {
    const ctrl = control[i] ? 1 : 0;
    const treat = treatment[i] ? 1 : 0;
    if (treat === 1 && ctrl === 0) b++;
    else if (treat === 0 && ctrl === 1) c++;
  }
  const rd = (b - c) / n;
  const variance = (b + c - (b - c) ** 2 / n) / (n * n);
  const z = zQuantile(1 - (1 - confidence) / 2);
  const half = z * Math.sqrt(Math.max(0, variance));
  return {
    n,
    b,
    c,
    riskDifference: rd,
    lower: Math.max(-1, rd - half),
    upper: Math.min(1, rd + half),
    confidence
  };
}
function passAtK(n, c, k) {
  if (!Number.isInteger(n) || !Number.isInteger(c) || !Number.isInteger(k)) {
    throw new Error(`passAtK: n, c, k must be integers (got n=${n}, c=${c}, k=${k})`);
  }
  if (k < 1 || k > n || c < 0 || c > n) {
    throw new Error(`passAtK: require 1 \u2264 k \u2264 n and 0 \u2264 c \u2264 n (got n=${n}, c=${c}, k=${k})`);
  }
  if (n - c < k) return 1;
  let prob = 1;
  for (let i = n - c + 1; i <= n; i++) prob *= 1 - k / i;
  return 1 - prob;
}
function binomialSignTwoSided(b, c) {
  const nd = b + c;
  if (nd === 0) return 1;
  return Math.min(1, 2 * binomialHalfLowerTail(Math.min(b, c), nd));
}
function binomialHalfUpperTail(successes, n) {
  if (successes <= 0) return 1;
  if (successes > n) return 0;
  if (successes <= n / 2) {
    return Math.max(0, 1 - binomialHalfLowerTail(successes - 1, n));
  }
  return binomialHalfLowerTail(n - successes, n);
}
function binomialHalfLowerTail(maxSuccesses, n) {
  if (maxSuccesses < 0) return 0;
  if (maxSuccesses >= n) return 1;
  if (maxSuccesses === 0) return 2 ** -n;
  const logHalfN = n * Math.log(0.5);
  let logTail = Number.NEGATIVE_INFINITY;
  for (let i = 0; i <= maxSuccesses; i++) {
    const logChoose = lnGamma(n + 1) - lnGamma(i + 1) - lnGamma(n - i + 1);
    logTail = logAddExp(logTail, logChoose + logHalfN);
  }
  return Math.min(1, Math.exp(logTail));
}
function logAddExp(a, b) {
  if (a === Number.NEGATIVE_INFINITY) return b;
  if (b === Number.NEGATIVE_INFINITY) return a;
  const max = Math.max(a, b);
  const min = Math.min(a, b);
  return max + Math.log1p(Math.exp(min - max));
}
function eProcess(opts = {}) {
  const alpha = opts.alpha ?? 0.05;
  const maxBet = opts.maxBet ?? 0.5;
  const nullMean = opts.nullMean ?? 0.5;
  if (!Number.isFinite(alpha) || alpha <= 0 || alpha >= 1) {
    throw new ValidationError(`eProcess: alpha must be in (0,1), got ${alpha}`);
  }
  if (!Number.isFinite(nullMean) || nullMean <= 0 || nullMean >= 1) {
    throw new ValidationError(`eProcess: nullMean must be in (0,1), got ${nullMean}`);
  }
  if (!Number.isFinite(maxBet) || maxBet <= 0 || maxBet >= 1 / nullMean) {
    throw new ValidationError(
      `eProcess: maxBet must be in (0, 1/nullMean=${(1 / nullMean).toFixed(4)}) so wealth factors stay positive, got ${maxBet}`
    );
  }
  const threshold = 1 / alpha;
  let wealth = 1;
  let n = 0;
  let decided = false;
  let decidedAtN;
  let sumX = 0;
  let varSum = 0;
  return {
    update(x) {
      if (typeof x !== "number" || !Number.isFinite(x) || x < 0 || x > 1) {
        throw new ValidationError(
          `eProcess: observation must be a finite number in [0,1], got ${x}`
        );
      }
      const muPrev = (0.5 + sumX) / (n + 1);
      const varPrev = (0.25 + varSum) / (n + 1);
      const edge = muPrev - nullMean;
      const lambda = Math.min(maxBet, Math.max(0, edge / (varPrev + edge * edge)));
      wealth *= 1 + lambda * (x - nullMean);
      n += 1;
      sumX += x;
      const muNow = (0.5 + sumX) / (n + 1);
      varSum += (x - muNow) ** 2;
      if (!decided && wealth >= threshold) {
        decided = true;
        decidedAtN = n;
      }
      return { wealth, n, decided };
    },
    state() {
      return { wealth, n, decided, alpha, maxBet, nullMean, threshold, decidedAtN };
    }
  };
}
function zQuantile(p) {
  if (p <= 0 || p >= 1) {
    if (p === 0) return -Infinity;
    if (p === 1) return Infinity;
    return NaN;
  }
  const a = [
    -39.69683028665376,
    220.9460984245205,
    -275.9285104469687,
    138.357751867269,
    -30.66479806614716,
    2.506628277459239
  ];
  const b = [
    -54.47609879822406,
    161.5858368580409,
    -155.6989798598866,
    66.80131188771972,
    -13.28068155288572
  ];
  const c = [
    -0.007784894002430293,
    -0.3223964580411365,
    -2.400758277161838,
    -2.549732539343734,
    4.374664141464968,
    2.938163982698783
  ];
  const d = [0.007784695709041462, 0.3224671290700398, 2.445134137142996, 3.754408661907416];
  const pLow = 0.02425;
  const pHigh = 1 - pLow;
  let q;
  let r;
  if (p < pLow) {
    q = Math.sqrt(-2 * Math.log(p));
    return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
  }
  if (p <= pHigh) {
    q = p - 0.5;
    r = q * q;
    return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
  }
  q = Math.sqrt(-2 * Math.log(1 - p));
  return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
}
function medianInPlace(xs) {
  if (xs.length === 0) return 0;
  xs.sort((a, b) => a - b);
  const mid = Math.floor(xs.length / 2);
  return xs.length % 2 === 0 ? (xs[mid - 1] + xs[mid]) / 2 : xs[mid];
}
function makeRng(seed) {
  if (seed === void 0) return Math.random;
  return mulberry32(seed);
}
function mulberry32(seed) {
  let s = seed | 0 || 2654435769;
  return () => {
    s = s + 1831565813 | 0;
    let t = s;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}

// src/judge-calibration.ts
function calibrateJudge(golden, candidate) {
  const map = /* @__PURE__ */ new Map();
  for (const g of golden) map.set(g.itemId, { h: g.humanScore, j: NaN });
  for (const c of candidate) {
    const entry = map.get(c.itemId);
    if (entry) entry.j = c.score;
  }
  const common = [...map.values()].filter((v) => Number.isFinite(v.j));
  const n = common.length;
  if (n < 2) {
    return { n, pearson: NaN, kappa: NaN, mae: NaN, worstItems: [] };
  }
  const humans = common.map((c) => c.h);
  const judges = common.map((c) => c.j);
  const pearson = pearsonR(humans, judges);
  const kappa = weightedKappa(humans.map(Math.round), judges.map(Math.round));
  const absDiffs = common.map((c) => Math.abs(c.j - c.h));
  const mae = absDiffs.reduce((a, b) => a + b, 0) / n;
  const worst = [...map.entries()].filter(([, v]) => Number.isFinite(v.j)).map(([itemId, v]) => ({ itemId, judge: v.j, human: v.h, delta: Math.abs(v.j - v.h) })).sort((a, b) => b.delta - a.delta).slice(0, 5);
  return { n, pearson, kappa, mae, worstItems: worst };
}
function positionalBias(scores) {
  const pairs = /* @__PURE__ */ new Map();
  for (const s of scores) {
    const slot = pairs.get(s.itemId) ?? {};
    if (s.positionOfAInput === "first") slot.first = s.score;
    else if (s.positionOfAInput === "second") slot.second = s.score;
    pairs.set(s.itemId, slot);
  }
  const deltas = [];
  for (const { first, second } of pairs.values()) {
    if (first !== void 0 && second !== void 0) deltas.push(first - second);
  }
  if (deltas.length === 0) return { avgDelta: 0, n: 0 };
  return { avgDelta: deltas.reduce((a, b) => a + b, 0) / deltas.length, n: deltas.length };
}
function verbosityBias(samples) {
  const n = samples.length;
  if (n < 3) return { pearson: NaN, n };
  return {
    pearson: pearsonR(
      samples.map((s) => s.outputLen),
      samples.map((s) => s.score)
    ),
    n
  };
}
function selfPreference(samples) {
  const inF = samples.filter((s) => s.inFamily).map((s) => s.score);
  const outF = samples.filter((s) => !s.inFamily).map((s) => s.score);
  if (inF.length === 0 || outF.length === 0)
    return { inFamilyMean: 0, outOfFamilyMean: 0, deltaMean: 0, n: 0 };
  const inMean = inF.reduce((a, b) => a + b, 0) / inF.length;
  const outMean = outF.reduce((a, b) => a + b, 0) / outF.length;
  return {
    inFamilyMean: inMean,
    outOfFamilyMean: outMean,
    deltaMean: inMean - outMean,
    n: samples.length
  };
}
function weightedKappa(a, b) {
  if (a.length !== b.length || a.length === 0) return NaN;
  const min = Math.min(...a, ...b);
  const max = Math.max(...a, ...b);
  const K = max - min + 1;
  if (K < 2) return 1;
  const observed = Array.from({ length: K }, () => new Array(K).fill(0));
  const rowMarg = new Array(K).fill(0);
  const colMarg = new Array(K).fill(0);
  for (let i = 0; i < a.length; i++) {
    const ai = a[i] - min;
    const bi = b[i] - min;
    const row = observed[ai];
    row[bi] = (row[bi] ?? 0) + 1;
    rowMarg[ai]++;
    colMarg[bi]++;
  }
  let num = 0;
  let den = 0;
  for (let i = 0; i < K; i++) {
    for (let j = 0; j < K; j++) {
      const w = (i - j) ** 2 / (K - 1) ** 2;
      const expected = rowMarg[i] * colMarg[j] / a.length;
      num += w * observed[i][j];
      den += w * expected;
    }
  }
  if (den === 0) return 1;
  return 1 - num / den;
}
function continuousAgreement(scores, opts = {}) {
  const bootstrap = opts.bootstrap ?? 1e3;
  const weights = opts.weights ?? "quadratic";
  const seed = opts.seed ?? 12648430;
  const ciLevel = opts.ciLevel ?? 0.95;
  const matrix = scores.filter((row) => row.length >= 2 && row.every((v) => Number.isFinite(v)));
  const raters = matrix[0]?.length ?? 0;
  const clean = matrix.filter((row) => row.length === raters);
  const nClean = clean.length;
  if (nClean < 2 || raters < 2) {
    return {
      weightedKappa: NaN,
      icc: NaN,
      pearson: NaN,
      spearman: NaN,
      ci: { icc: [NaN, NaN], weightedKappa: [NaN, NaN] },
      n: nClean,
      raters
    };
  }
  const kappa = continuousWeightedKappa(clean, weights);
  const icc = icc21(clean);
  const pearson = avgPairwise(clean, pearsonR);
  const spearman = avgPairwise(clean, spearmanR);
  const ciIcc = [NaN, NaN];
  const ciKappa = [NaN, NaN];
  if (bootstrap > 0) {
    const rng = mulberry322(seed);
    const iccs = [];
    const kappas = [];
    for (let b = 0; b < bootstrap; b++) {
      const sample = new Array(nClean);
      for (let i = 0; i < nClean; i++) {
        sample[i] = clean[Math.floor(rng() * nClean)];
      }
      const iccB = icc21(sample);
      const kB = continuousWeightedKappa(sample, weights);
      if (Number.isFinite(iccB)) iccs.push(iccB);
      if (Number.isFinite(kB)) kappas.push(kB);
    }
    const [lo, hi] = percentileBounds(ciLevel);
    if (iccs.length > 0) {
      iccs.sort((a, b) => a - b);
      ciIcc[0] = quantile(iccs, lo);
      ciIcc[1] = quantile(iccs, hi);
    }
    if (kappas.length > 0) {
      kappas.sort((a, b) => a - b);
      ciKappa[0] = quantile(kappas, lo);
      ciKappa[1] = quantile(kappas, hi);
    }
  }
  return {
    weightedKappa: kappa,
    icc,
    pearson,
    spearman,
    ci: { icc: ciIcc, weightedKappa: ciKappa },
    n: nClean,
    raters
  };
}
function calibrateJudgeContinuous(golden, candidate, opts = {}) {
  const base = calibrateJudge(golden, candidate);
  const map = /* @__PURE__ */ new Map();
  for (const g of golden) map.set(g.itemId, { h: g.humanScore, j: NaN });
  for (const c of candidate) {
    const entry = map.get(c.itemId);
    if (entry) entry.j = c.score;
  }
  const rows = [];
  for (const v of map.values()) {
    if (Number.isFinite(v.j)) rows.push([v.h, v.j]);
  }
  const agreement = continuousAgreement(rows, opts);
  return {
    ...base,
    weightedKappaContinuous: agreement.weightedKappa,
    icc: agreement.icc,
    spearman: agreement.spearman,
    ci: agreement.ci
  };
}
function continuousWeightedKappa(rows, scheme) {
  if (rows.length === 0) return NaN;
  const raters = rows[0].length;
  if (raters < 2) return NaN;
  const wFn = scheme === "linear" ? (x, y) => Math.abs(x - y) : (x, y) => (x - y) ** 2;
  let sum = 0;
  let pairs = 0;
  for (let r1 = 0; r1 < raters; r1++) {
    for (let r2 = r1 + 1; r2 < raters; r2++) {
      const a = rows.map((row) => row[r1]);
      const b = rows.map((row) => row[r2]);
      const n = a.length;
      let obs = 0;
      for (let i = 0; i < n; i++) obs += wFn(a[i], b[i]);
      obs /= n;
      let exp = 0;
      for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) exp += wFn(a[i], b[j]);
      }
      exp /= n * n;
      if (exp === 0) {
        sum += obs === 0 ? 1 : 0;
      } else {
        sum += 1 - obs / exp;
      }
      pairs++;
    }
  }
  return pairs === 0 ? NaN : sum / pairs;
}
function icc21(rows) {
  const n = rows.length;
  if (n < 2) return NaN;
  const k = rows[0].length;
  if (k < 2) return NaN;
  const rowMeans = rows.map((row) => row.reduce((s, v) => s + v, 0) / k);
  const colMeans = new Array(k).fill(0);
  for (let j = 0; j < k; j++) {
    let s = 0;
    for (let i = 0; i < n; i++) s += rows[i][j];
    colMeans[j] = s / n;
  }
  let grand = 0;
  for (let i = 0; i < n; i++) grand += rowMeans[i];
  grand /= n;
  let ssR = 0;
  for (let i = 0; i < n; i++) ssR += (rowMeans[i] - grand) ** 2;
  ssR *= k;
  let ssC = 0;
  for (let j = 0; j < k; j++) ssC += (colMeans[j] - grand) ** 2;
  ssC *= n;
  let ssT = 0;
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < k; j++) ssT += (rows[i][j] - grand) ** 2;
  }
  const ssE = ssT - ssR - ssC;
  const dfR = n - 1;
  const dfC = k - 1;
  const dfE = (n - 1) * (k - 1);
  const msR = ssR / dfR;
  const msC = ssC / dfC;
  const msE = dfE > 0 ? ssE / dfE : 0;
  const denom = msR + (k - 1) * msE + k * (msC - msE) / n;
  if (denom === 0) {
    return msR === 0 && msE === 0 ? 1 : 0;
  }
  return (msR - msE) / denom;
}
function avgPairwise(rows, fn) {
  const k = rows[0]?.length ?? 0;
  if (k < 2) return NaN;
  let sum = 0;
  let pairs = 0;
  for (let i = 0; i < k; i++) {
    for (let j = i + 1; j < k; j++) {
      const a = rows.map((row) => row[i]);
      const b = rows.map((row) => row[j]);
      const r = fn(a, b);
      if (Number.isFinite(r)) {
        sum += r;
        pairs++;
      }
    }
  }
  return pairs === 0 ? NaN : sum / pairs;
}
function mulberry322(seed) {
  let a = seed >>> 0;
  return () => {
    a = a + 1831565813 >>> 0;
    let t = a;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
function percentileBounds(ciLevel) {
  const tail = (1 - ciLevel) / 2;
  return [tail, 1 - tail];
}
function quantile(sorted, q) {
  if (sorted.length === 0) return NaN;
  if (sorted.length === 1) return sorted[0];
  const pos = q * (sorted.length - 1);
  const lo = Math.floor(pos);
  const hi = Math.ceil(pos);
  if (lo === hi) return sorted[lo];
  const frac = pos - lo;
  return sorted[lo] * (1 - frac) + sorted[hi] * frac;
}

export {
  calibrateJudge,
  positionalBias,
  verbosityBias,
  selfPreference,
  continuousAgreement,
  calibrateJudgeContinuous,
  normalizeScores,
  weightedMean,
  confidenceInterval,
  interRaterReliability,
  mannWhitneyU,
  partialCredit,
  pairedTTest,
  wilcoxonSignedRank,
  cohensD,
  cliffsDelta,
  interpretCliffs,
  ranks,
  pearsonR,
  spearmanR,
  weightedComposite,
  corpusInterRaterAgreement,
  corpusInterRaterAgreementFromJudgeScores,
  requiredSampleSize,
  pairedMde,
  mcnemarRequiredN,
  mcnemarPower,
  bonferroni,
  holm,
  benjaminiHochberg,
  pairedBootstrap,
  pairedSignTest,
  wilson,
  mcnemar,
  pairedRiskDifference,
  passAtK,
  eProcess,
  mulberry32
};
//# sourceMappingURL=chunk-PJQFMIOX.js.map