import {Option, none, some} from 'fp-ts/lib/Option.js'; import Prando from 'prando'; // a shrinkable generator needs to maintain some state, // namely the sequence of generated values in order to be // able to backtrack and shrink. type GenMode = 'gen' | 'shrink'; export interface Gen { generate(): A; generateN(n: number): A[]; } export interface GenBytes { generate(n: number): number[]; } export class BytesGen implements GenBytes { prng: Prando; bytes: number[][]; // the internal sequence of generated values constructor(prng: Prando) { this.prng = prng; this.bytes = []; } generate(n: number): number[] { if (n <= 0) { throw new Error('n must be greater than 0'); } return this.generateBytes(n); } generateBytes(n: number): number[] { const gens = []; for (let i = 0; i < n; i++) { gens.push(this.prng.nextInt(0, 255)); } this.bytes.push([...gens]); return gens; } replay(): GenBytes { return new ReplayBytes(this.bytes); } } export class ReplayBytes implements GenBytes { bytes: number[][]; // the internal sequence of generated values index: number = 0; // the current index in the sequence of generated values constructor(bytes: number[][]) { this.bytes = bytes; this.index = 0; } generate(n: number): number[] { if (this.index >= this.bytes.length) { throw new Error('No more bytes to shrink'); } const chunk = [...this.bytes[this.index++]]; if (chunk.length < n) { const zeros = new Array(n - chunk.length).fill(0); chunk.unshift(...zeros); } return chunk; } }