'use strict'; const { test } = require('node:test'); const assert = require('node:assert/strict'); const { mean, stdDev, median, mad, lerp } = require('../../src/stats'); const EPS = 1e-9; function near(a, b, eps = EPS) { assert.ok(Math.abs(a - b) <= eps, `expected ${a} ≈ ${b} (eps ${eps})`); } test('mean: basic and empty', () => { assert.equal(mean([1, 2, 3, 4]), 2.5); assert.equal(mean([]), 0); }); test('stdDev: zero-variance, classic sample, single-element, empty', () => { assert.equal(stdDev([1, 1, 1, 1]), 0); near(stdDev([1, 2, 3, 4, 5]), 1.5811388300841898); assert.equal(stdDev([5]), 0); assert.equal(stdDev([]), 0); }); test('median: odd, even, empty', () => { assert.equal(median([1, 2, 3, 4, 5]), 3); assert.equal(median([1, 2, 3, 4]), 2.5); assert.equal(median([]), 0); }); test('mad: hand-checked sample and constant array', () => { // [1,1,2,2,4,6,9] -> median 2 -> |dev| [1,1,0,0,2,4,7] -> sorted // [0,0,1,1,2,4,7] -> mad = 1. assert.equal(mad([1, 1, 2, 2, 4, 6, 9]), 1); assert.equal(mad([5, 5, 5]), 0); assert.equal(mad([]), 0); }); test('lerp: in-range mapping and degenerate pass-through', () => { assert.equal(lerp(2, 0, 4, 0, 100), 50); assert.equal(lerp(2, 0, 0, 0, 100), 2); // iMin > iMax also degenerate (defensive against swapped bounds). assert.equal(lerp(2, 4, 0, 0, 100), 2); }); test('lerp: float arithmetic stays within epsilon', () => { near(lerp(0.1, 0, 1, 0, 10), 1); near(lerp(1 / 3, 0, 1, 0, 30), 10); });