import { describe, expect, it } from "@suaveplan/testing/runner"; import { PseudoRandomError } from "../errors/pseudo-random-error.js"; import { Sfc32Rng } from "./sfc32.js"; describe("Sfc32Rng", () => { it("is deterministic", { timeout: 5000 }, () => { const a = new Sfc32Rng("abc"); const b = new Sfc32Rng("abc"); for (let i = 0; i < 100; i += 1) { expect(a.nextUint32()).toBe(b.nextUint32()); } }); it("produces uint32 in range", { timeout: 5000 }, () => { const r = new Sfc32Rng(1); for (let i = 0; i < 1000; i += 1) { const v = r.nextUint32(); expect(v).toBeGreaterThanOrEqual(0); expect(v).toBeLessThan(2 ** 32); } }); it("skip === repeated nextUint32", { timeout: 5000 }, () => { const a = new Sfc32Rng(2); const b = new Sfc32Rng(2); a.skip(50); for (let i = 0; i < 50; i += 1) b.nextUint32(); expect(a.nextUint32()).toBe(b.nextUint32()); }); it("at doesn't mutate self", { timeout: 5000 }, () => { const a = new Sfc32Rng(3); const ahead = a.at(10); expect(a.position).toBe(0); expect(ahead.position).toBe(10); }); it("checkpoint/restore round-trip", { timeout: 5000 }, () => { const r = new Sfc32Rng(4); r.skip(7); const cp = r.checkpoint(); const exp = [r.nextUint32(), r.nextUint32()]; r.restore(cp); expect([r.nextUint32(), r.nextUint32()]).toEqual(exp); }); it("restore rejects foreign algorithm", { timeout: 5000 }, () => { const r = new Sfc32Rng(5); expect(() => r.restore({ algorithm: "mulberry32", state: [0], counter: 0 }) ).toThrow(PseudoRandomError); }); it("restore rejects bad state length or non-finite words", { timeout: 5000, }, () => { const r = new Sfc32Rng(6); expect(() => r.restore({ algorithm: "sfc32", state: [1, 2], counter: 0 }) ).toThrow(PseudoRandomError); expect(() => r.restore({ algorithm: "sfc32", state: [1, 2, 3, Number.NaN], counter: 0, }) ).toThrow(PseudoRandomError); }); it("rejects invalid skip/at", { timeout: 5000 }, () => { const r = new Sfc32Rng(7); expect(() => r.skip(-1)).toThrow(PseudoRandomError); expect(() => r.at(-1)).toThrow(PseudoRandomError); }); it("fork yields distinct stream", { timeout: 5000 }, () => { const parent = new Sfc32Rng(8); const child = parent.fork("x"); const a = [parent.nextUint32(), parent.nextUint32()]; const b = [child.nextUint32(), child.nextUint32()]; expect(a).not.toEqual(b); }); it("nextFloat is in [0,1)", { timeout: 5000 }, () => { const r = new Sfc32Rng(9); for (let i = 0; i < 200; i += 1) { const f = r.nextFloat(); expect(f).toBeGreaterThanOrEqual(0); expect(f).toBeLessThan(1); } }); });