From 19c352276211106884078495a2244fe454f92cbe Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Mon, 31 Aug 2026 16:27:17 +0200 Subject: Fix test for uniformity of distribution for generation of integers This lead me into a rabbit hole to understand how one should assert the shape of the distribution of some empirical data w.r.t. to the "true" expected distribution which uses the Pearson coefficient test. Unfortunately for very large number of samples deviations sum up quickly to a point where the computed p-value is basically 0, thus rejecting the null hypothesis. While this is theoretically true, there are a lot of ways to introduce biases that will lead to rejection, one of them being the use of modulus to select numbers from the generator's output! --- pbt/ts/src/gen.ts | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) (limited to 'pbt/ts/src/gen.ts') diff --git a/pbt/ts/src/gen.ts b/pbt/ts/src/gen.ts index 531a118..c27cfec 100644 --- a/pbt/ts/src/gen.ts +++ b/pbt/ts/src/gen.ts @@ -1,4 +1,4 @@ -import {Option, none, some} from 'fp-ts/lib/Option.js'; +import { Option, none, some } from 'fp-ts/lib/Option.js'; import Prando from 'prando'; // a shrinkable generator needs to maintain some state, @@ -71,3 +71,60 @@ export class ReplayBytes implements GenBytes { return chunk; } } + +class GenInt implements Gen { + private bytes: BytesGen; + + constructor(seed: number | undefined = undefined) { + this.bytes = new BytesGen(new Prando(seed ? seed : Math.random() * 1000)); + } + + generate(): number { + const bytes = this.bytes.generate(1); + return bytes[0]; + } + + generateN(n: number): number[] { + const result = []; + for (let i = 0; i < n; i++) { + result.push(this.bytes.generate(1)[0]); + } + + return result; + } +} + +class GenIntWithin implements Gen { + private bytes: BytesGen; + + constructor(private min: number, private max: number, seed: number | undefined = undefined) { + this.bytes = new BytesGen(new Prando(seed ? seed : Math.random() * 1000000000)); + this.min = min; + this.max = max; + } + + generate(): number { + const range = this.max - this.min; + const numBytes = Math.ceil(Math.log2(range) / 8); + const bytes = this.bytes.generate(numBytes); + let value = 0; + for (let i = 0; i < bytes.length; i++) { + value = (value << 8) | bytes[i]; + } + return this.min + (value % range); + } + + generateN(n: number): number[] { + const result = []; + for (let i = 0; i < n; i++) { + result.push(this.bytes.generate(1)[0]); + } + + return result; + } + +} + +export const genInt = (seed : number | undefined = undefined) => new GenInt(seed); + +export const genIntWithin = (min: number, max: number, seed : number | undefined = undefined) => new GenIntWithin(min, max, seed); -- cgit v1.3.1