import test from 'node:test' import assert from 'node:assert/strict' import { nextUint32, pure, map, flatMap, intBelow, run } from '../src/random.js' import { loadFixture, freeze } from './helpers.js' const fixtureTests = (name, f) => { const fx = loadFixture(name) fx.cases.forEach((c) => test(`m04 ${fx.function}: ${c.name}`, () => assert.deepStrictEqual(f(...freeze(c.args)), c.expected))) } fixtureTests('m04-random', (seed) => run(nextUint32, seed)) fixtureTests('m04-int-below', (n, seed) => run(intBelow(n), seed)) test('m04 the recorded steps chain: each step starts from the previous step\'s seed', () => { const cases = loadFixture('m04-random').cases cases.forEach((c, i) => { if (!c.name.endsWith('-step-1')) assert.equal(c.args[0], cases[i - 1].expected[1]) }) }) test('m04 pure returns its value and leaves the seed alone', () => assert.deepStrictEqual(run(pure('x'), 99), ['x', 99])) test('m04 map applies f to the value and keeps the seed from the underlying step', () => { const [v, s] = run(nextUint32, 5) assert.deepStrictEqual(run(map(nextUint32, (x) => x + 1), 5), [v + 1, s]) }) test('m04 map with the identity function changes nothing', () => assert.deepStrictEqual(run(map(nextUint32, (x) => x), 5), run(nextUint32, 5))) test('m04 flatMap threads the seed through both steps', () => { const [a, s1] = run(nextUint32, 5) const [b, s2] = run(nextUint32, s1) assert.deepStrictEqual(run(flatMap(nextUint32, (x) => map(nextUint32, (y) => [x, y])), 5), [[a, b], s2]) }) test('m04 flatMap with pure on the left or right changes nothing', () => { assert.deepStrictEqual(run(flatMap(pure(3), (x) => map(nextUint32, (v) => v + x)), 5), run(map(nextUint32, (v) => v + 3), 5)) assert.deepStrictEqual(run(flatMap(nextUint32, pure), 5), run(nextUint32, 5)) }) test('m04 intBelow always lands in [0, n)', () => { const draws = (n, seed, count) => count === 0 ? [] : ((([v, s]) => [v, ...draws(n, s, count - 1)])(run(intBelow(n), seed))) ;[1, 2, 7, 400].forEach((n) => draws(n, 123, 50).forEach((v) => assert.ok(Number.isInteger(v) && v >= 0 && v < n, `${v} not in [0, ${n})`))) }) test('m04 running a Random does not mutate anything: same seed, same answer', () => assert.deepStrictEqual(run(nextUint32, 8), run(nextUint32, 8)))