summaryrefslogtreecommitdiff
path: root/pbt/ts/src/gen.ts
diff options
context:
space:
mode:
authorArnaud Bailly <arnaud@pankzsoft.com>2026-08-31 16:27:17 +0200
committerArnaud Bailly <arnaud@pankzsoft.com>2026-09-01 07:59:01 +0200
commit19c352276211106884078495a2244fe454f92cbe (patch)
tree915d36b906acbde5b5fb29e48a647590b2dca91e /pbt/ts/src/gen.ts
parent32f9be77d18d3ad0cb86e7290c6df7c51ab99ca5 (diff)
downloadlambda-nantes-19c352276211106884078495a2244fe454f92cbe.tar.gz
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!
Diffstat (limited to 'pbt/ts/src/gen.ts')
-rw-r--r--pbt/ts/src/gen.ts59
1 files changed, 58 insertions, 1 deletions
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<number> {
+ 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<number> {
+ 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);