viewof dist_type = {
const input = Inputs.select(["Chi-squared", "F distribution"], {label: "Distribution:"});
['pointerdown','touchstart','mousedown','click','wheel','pointermove','touchmove']
.forEach(e => input.addEventListener(e, ev => ev.stopPropagation()));
return input;
}
viewof df1 = {
const input = Inputs.range([1, 40], {value: 10, step: 1, label: "df₁ (or ν):"});
['pointerdown','touchstart','mousedown','click','wheel','pointermove','touchmove']
.forEach(e => input.addEventListener(e, ev => ev.stopPropagation()));
return input;
}
viewof df2 = {
const input = Inputs.range([1, 40], {value: 15, step: 1, label: "df₂ (F only):"});
['pointerdown','touchstart','mousedown','click','wheel','pointermove','touchmove']
.forEach(e => input.addEventListener(e, ev => ev.stopPropagation()));
return input;
}
viewof alpha_var = {
const input = Inputs.range([0.01, 0.10], {value: 0.05, step: 0.01, label: "α level:"});
['pointerdown','touchstart','mousedown','click','wheel','pointermove','touchmove']
.forEach(e => input.addEventListener(e, ev => ev.stopPropagation()));
return input;
}
// Gamma function approximation
lgamma = function(n) {
const c = [76.18009172947146, -86.50532032941677, 24.01409824083091,
-1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5];
let x = n, y = n, tmp = x + 5.5;
tmp -= (x + 0.5) * Math.log(tmp);
let ser = 1.000000000190015;
for (let j = 0; j < 6; j++) ser += c[j] / ++y;
return -tmp + Math.log(2.5066282746310005 * ser / x);
}
// Chi-squared PDF
chi2pdf = function(x, k) {
if (x <= 0) return 0;
return Math.exp((k/2-1)*Math.log(x) - x/2 - (k/2)*Math.log(2) - lgamma(k/2));
}
// F PDF approximation
fpdf = function(x, d1, d2) {
if (x <= 0) return 0;
const lnum = (d1/2)*Math.log(d1) + (d2/2)*Math.log(d2) + (d1/2-1)*Math.log(x);
const lden = lgamma(d1/2) + lgamma(d2/2) - lgamma((d1+d2)/2) + ((d1+d2)/2)*Math.log(d2+d1*x);
return Math.exp(lnum - lden);
}
// Approximate upper-tail critical value via bisection on CDF (numerical integration)
trapCDF = function(pdf, lo, hi, n) {
const h = (hi - lo) / n;
let s = 0;
for (let i = 0; i <= n; i++) {
const x = lo + i * h;
const w = (i === 0 || i === n) ? 0.5 : 1;
s += w * pdf(x);
}
return s * h;
}
critVal = {
const xmax = dist_type === "Chi-squared" ? Math.max(df1 * 4, 50) : 15;
const total = trapCDF(
dist_type === "Chi-squared" ? (x => chi2pdf(x, df1)) : (x => fpdf(x, df1, df2)),
0.001, xmax, 2000
);
// Bisect to find upper alpha critical value
let lo = 0.001, hi = xmax;
for (let i = 0; i < 60; i++) {
const mid = (lo + hi) / 2;
const tail = trapCDF(
dist_type === "Chi-squared" ? (x => chi2pdf(x, df1)) : (x => fpdf(x, df1, df2)),
mid, xmax, 1000
) / total;
tail > alpha_var ? (lo = mid) : (hi = mid);
}
return (lo + hi) / 2;
}
info_html2 = html`<div style="background:#f0f4ff; padding:8px; border-radius:6px; font-size:0.93em; line-height:1.6;">
<strong>Distribution:</strong> ${dist_type === "Chi-squared" ? `χ²(${df1})` : `F(${df1},${df2})`}<br>
<strong>α = ${alpha_var.toFixed(2)}</strong><br>
<strong>Critical value:</strong> ${critVal.toFixed(3)}<br>
<em style="color:#555;">Reject H₀ if statistic > ${critVal.toFixed(3)}</em>
</div>`;
md`${info_html2}`