diff --git a/pkg/spec/test/pcg.test.ts b/pkg/spec/test/pcg.test.ts new file mode 100644 index 0000000..c3e8cd3 --- /dev/null +++ b/pkg/spec/test/pcg.test.ts @@ -0,0 +1,58 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { Pcg } from "../src/pcg.ts"; + +const here = dirname(fileURLToPath(import.meta.url)); +const fixturePath = join(here, "fixtures", "pcg-golden.json"); + +interface GoldenCase { + seed: { hi: string; lo: string }; + uint64: string[]; + float64: number[]; + intN: Record; +} + +const cases: GoldenCase[] = JSON.parse(readFileSync(fixturePath, "utf8")); + +function seedLabel(c: GoldenCase): string { + return `seed(${c.seed.hi}, ${c.seed.lo})`; +} + +test("golden fixture has the expected shape", () => { + assert.ok(cases.length >= 5, "expected several seed cases"); + for (const c of cases) { + assert.equal(c.uint64.length, 40); + assert.equal(c.float64.length, 40); + for (const draws of Object.values(c.intN)) { + assert.equal(draws.length, 40); + } + } +}); + +for (const c of cases) { + test(`${seedLabel(c)} uint64 sequence is bit-exact`, () => { + const pcg = new Pcg(BigInt(c.seed.hi), BigInt(c.seed.lo)); + c.uint64.forEach((want, i) => { + assert.equal(pcg.uint64(), BigInt(want), `uint64 draw ${i}`); + }); + }); + + test(`${seedLabel(c)} float64 sequence is bit-exact`, () => { + const pcg = new Pcg(BigInt(c.seed.hi), BigInt(c.seed.lo)); + c.float64.forEach((want, i) => { + assert.equal(pcg.float64(), want, `float64 draw ${i}`); + }); + }); + + for (const [n, expected] of Object.entries(c.intN)) { + test(`${seedLabel(c)} intN(${n}) sequence is bit-exact`, () => { + const pcg = new Pcg(BigInt(c.seed.hi), BigInt(c.seed.lo)); + expected.forEach((want, i) => { + assert.equal(pcg.intN(Number(n)), want, `intN(${n}) draw ${i}`); + }); + }); + } +}