1/**
2 * Deterministic Gaussian sample stream.
3 *
4 * Seeded so that the compression block is reproducible for a given parameter
5 * set — the reported sizes then only move when the model moves, not between
6 * recomputes. mulberry32 for the uniforms, Box–Muller for the normals.
7 */
8export class GaussianStream {
9 private state: number
10 private spare: number | null = null
12 constructor(seed: number) {
13 this.state = seed >>> 0
14 }
16 private uniform(): number {
17 this.state = (this.state + 0x6d2b79f5) >>> 0
18 let t = this.state
19 t = Math.imul(t ^ (t >>> 15), t | 1)
20 t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
21 return ((t ^ (t >>> 14)) >>> 0) / 4294967296
22 }
24 /** Uniform on [-1/2, 1/2), for dither. */
25 uniformCentered(): number {
26 return this.uniform() - 0.5
27 }
29 /** Standard normal. */
30 normal(): number {
31 if (this.spare !== null) {
32 const v = this.spare
33 this.spare = null
34 return v
35 }
36 let u = 0
37 while (u === 0) u = this.uniform()
38 const r = Math.sqrt(-2 * Math.log(u))
39 const theta = 2 * Math.PI * this.uniform()
40 this.spare = r * Math.sin(theta)
41 return r * Math.cos(theta)
42 }
43}