93 lines
3.0 KiB
TypeScript
93 lines
3.0 KiB
TypeScript
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);
|
|
}
|
|
});
|
|
});
|