From 67cb28cb7994f205261482e060eeb3880c88ecf5 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Fri, 27 Feb 2026 22:56:31 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=A5=20Make=20unsafe=20the=20default=20?= =?UTF-8?q?(#846)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop pure versions, recommend passing through `purify` to create them back if needed. --- README.md | 10 ++- .../internals/uniformIntInternal.ts | 4 +- src/distribution/uniformBigInt.ts | 4 +- src/generator/LinearCongruential.ts | 7 +- src/generator/MersenneTwister.ts | 7 +- src/generator/XorShift.ts | 16 +--- src/generator/XoroShiro.ts | 16 +--- src/types/Distribution.ts | 8 -- src/types/RandomGenerator.ts | 11 +-- src/utils/generateN.ts | 4 +- src/utils/skipN.ts | 4 +- .../internals/uniformIntInternal.spec.ts | 6 +- test/unit/distribution/uniformBigInt.spec.ts | 3 +- .../unit/generator/LinearCongruencial.spec.ts | 12 ++- test/unit/generator/MersenneTwister.spec.ts | 11 +-- .../generator/RandomGenerator.properties.ts | 90 +++++++------------ test/unit/generator/XorShift.spec.ts | 25 +++--- test/unit/generator/XoroShiro.spec.ts | 25 +++--- test/unit/utils/purify.spec.ts | 4 - 19 files changed, 93 insertions(+), 174 deletions(-) delete mode 100644 src/types/Distribution.ts diff --git a/README.md b/README.md index 589c266..cd7280a 100644 --- a/README.md +++ b/README.md @@ -65,11 +65,15 @@ In order to produce independent simulations it can be tempting to instanciate se ```javascript import { uniformInt } from 'pure-rand/distribution/uniformInt'; import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; +import { purify } from 'pure-rand/utils/purify'; + +const pureJump = purify((rng) => rng.jump()); +const createAnotherSimulation = (rng) => pureJump(rng)[1]; const seed = 42; const rngSimulation1 = xoroshiro128plus(seed); -const rngSimulation2 = rngSimulation1.jump(); // not in-place, creates a new instance -const rngSimulation3 = rngSimulation2.jump(); // not in-place, creates a new instance +const rngSimulation2 = createAnotherSimulation(rngSimulation1); // not in-place, creates a new instance +const rngSimulation3 = createAnotherSimulation(rngSimulation2); // not in-place, creates a new instance const diceSim1Value = uniformInt(rngSimulation1, 1, 6); // value in {1..6}, here: 2 const diceSim2Value = uniformInt(rngSimulation2, 1, 6); // value in {1..6}, here: 5 @@ -86,7 +90,7 @@ import { xoroshiro128plus } from 'pure-rand/generator/XoroShiro'; const seed = 42; const rng = xoroshiro128plus(seed); const rand = (min, max) => { - const out = (rng.unsafeNext() >>> 0) / 0x100000000; + const out = (rng.next() >>> 0) / 0x100000000; return min + Math.floor(out * (max - min + 1)); }; const firstDiceValue = rand(1, 6); // value in {1..6}, here: 6 diff --git a/src/distribution/internals/uniformIntInternal.ts b/src/distribution/internals/uniformIntInternal.ts index 8aadaf0..6f46050 100644 --- a/src/distribution/internals/uniformIntInternal.ts +++ b/src/distribution/internals/uniformIntInternal.ts @@ -9,9 +9,9 @@ export function uniformIntInternal(rng: RandomGenerator, rangeSize: number): num // Range provided by the RandomGenerator is large enough, // given rangeSize <= 0x100000000 and RandomGenerator is uniform on 0x100000000 values const MaxAllowed = rangeSize > 2 ? ~~(0x100000000 / rangeSize) * rangeSize : 0x100000000; - let deltaV = rng.unsafeNext() + 0x80000000; + let deltaV = rng.next() + 0x80000000; while (deltaV >= MaxAllowed) { - deltaV = rng.unsafeNext() + 0x80000000; + deltaV = rng.next() + 0x80000000; } return deltaV % rangeSize; } diff --git a/src/distribution/uniformBigInt.ts b/src/distribution/uniformBigInt.ts index ed94180..a76272b 100644 --- a/src/distribution/uniformBigInt.ts +++ b/src/distribution/uniformBigInt.ts @@ -44,9 +44,9 @@ export function uniformBigInt(rng: RandomGenerator, from: bigint, to: bigint): b function generateNext(NumIterations: number, rng: RandomGenerator): bigint { // Aggregate mutiple calls to next() into a single random value - let value = SBigInt(rng.unsafeNext() + 0x80000000); + let value = SBigInt(rng.next() + 0x80000000); for (let num = 1; num < NumIterations; ++num) { - const out = rng.unsafeNext(); + const out = rng.next(); value = (value << ThirtyTwo) + SBigInt(out + 0x80000000); // <>> U; y ^= (y << S) & B; diff --git a/src/generator/XorShift.ts b/src/generator/XorShift.ts index 3a04d0d..eb80a6c 100644 --- a/src/generator/XorShift.ts +++ b/src/generator/XorShift.ts @@ -17,12 +17,7 @@ class XorShift128Plus implements RandomGenerator { clone(): XorShift128Plus { return new XorShift128Plus(this.s01, this.s00, this.s11, this.s10); } - next(): [number, XorShift128Plus] { - const nextRng = new XorShift128Plus(this.s01, this.s00, this.s11, this.s10); - const out = nextRng.unsafeNext(); - return [out, nextRng]; - } - unsafeNext(): number { + next(): number { const a0 = this.s00 ^ (this.s00 << 23); const a1 = this.s01 ^ ((this.s01 << 23) | (this.s00 >>> 9)); const b0 = a0 ^ this.s10 ^ ((a0 >>> 18) | (a1 << 14)) ^ ((this.s10 >>> 5) | (this.s11 << 27)); @@ -34,12 +29,7 @@ class XorShift128Plus implements RandomGenerator { this.s10 = b0; return out; } - jump(): XorShift128Plus { - const nextRng = new XorShift128Plus(this.s01, this.s00, this.s11, this.s10); - nextRng.unsafeJump(); - return nextRng; - } - unsafeJump() { + jump() { // equivalent to 2^64 calls to next() // can be used to generate 2^64 non-overlapping subsequences let ns01 = 0; @@ -56,7 +46,7 @@ class XorShift128Plus implements RandomGenerator { ns11 ^= this.s11; ns10 ^= this.s10; } - this.unsafeNext(); + this.next(); } } this.s01 = ns01; diff --git a/src/generator/XoroShiro.ts b/src/generator/XoroShiro.ts index 01e53eb..cd3d80b 100644 --- a/src/generator/XoroShiro.ts +++ b/src/generator/XoroShiro.ts @@ -13,12 +13,7 @@ class XoroShiro128Plus implements RandomGenerator { clone(): XoroShiro128Plus { return new XoroShiro128Plus(this.s01, this.s00, this.s11, this.s10); } - next(): [number, XoroShiro128Plus] { - const nextRng = new XoroShiro128Plus(this.s01, this.s00, this.s11, this.s10); - const out = nextRng.unsafeNext(); - return [out, nextRng]; - } - unsafeNext(): number { + next(): number { const out = (this.s00 + this.s10) | 0; // a = s0[n] ^ s1[n] const a0 = this.s10 ^ this.s00; @@ -33,12 +28,7 @@ class XoroShiro128Plus implements RandomGenerator { this.s11 = (a0 << 5) ^ (a1 >>> 27); return out; } - jump(): XoroShiro128Plus { - const nextRng = new XoroShiro128Plus(this.s01, this.s00, this.s11, this.s10); - nextRng.unsafeJump(); - return nextRng; - } - unsafeJump(): void { + jump(): void { // equivalent to 2^64 calls to next() // can be used to generate 2^64 non-overlapping subsequences let ns01 = 0; @@ -55,7 +45,7 @@ class XoroShiro128Plus implements RandomGenerator { ns11 ^= this.s11; ns10 ^= this.s10; } - this.unsafeNext(); + this.next(); } } this.s01 = ns01; diff --git a/src/types/Distribution.ts b/src/types/Distribution.ts deleted file mode 100644 index 682bd61..0000000 --- a/src/types/Distribution.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { RandomGenerator } from './RandomGenerator'; - -/** - * Generate random value based on a given RandomGenerator. - * Return the generated value and an offsetted version of the RandomGenerator (never alters the source rng). - * @public - */ -export type Distribution = (rng: RandomGenerator) => [T, RandomGenerator]; diff --git a/src/types/RandomGenerator.ts b/src/types/RandomGenerator.ts index d82b5ec..2a80b61 100644 --- a/src/types/RandomGenerator.ts +++ b/src/types/RandomGenerator.ts @@ -1,20 +1,13 @@ export interface RandomGenerator { /** Produce a fully independent clone of the current instance */ clone(): RandomGenerator; - /** - * Generate next random value along with the next generator (does not impact current instance). - * Values uniform in range -0x8000_0000 (included) to 0x7fff_ffff (included) - */ - next(): [number, RandomGenerator]; - /** Compute the jumped generator (does not impact current instance) */ - jump?(): RandomGenerator; /** * Generate next value BUT alters current generator. * Values uniform in range -0x8000_0000 (included) to 0x7fff_ffff (included) */ - unsafeNext(): number; + next(): number; /** Jump current generator */ - unsafeJump?(): void; + jump?(): void; /** Access to the internal state of a RandomGenerator in a read-only fashion */ getState(): readonly number[]; } diff --git a/src/utils/generateN.ts b/src/utils/generateN.ts index 98a02a6..9888176 100644 --- a/src/utils/generateN.ts +++ b/src/utils/generateN.ts @@ -2,8 +2,8 @@ import type { RandomGenerator } from '../types/RandomGenerator'; export function generateN(rng: RandomGenerator, num: number): number[] { const out: number[] = []; - for (let idx = 0; idx != num; ++idx) { - out.push(rng.unsafeNext()); + for (let idx = 0; idx !== num; ++idx) { + out.push(rng.next()); } return out; } diff --git a/src/utils/skipN.ts b/src/utils/skipN.ts index f62b9b8..c842044 100644 --- a/src/utils/skipN.ts +++ b/src/utils/skipN.ts @@ -1,7 +1,7 @@ import type { RandomGenerator } from '../types/RandomGenerator'; export function skipN(rng: RandomGenerator, num: number): void { - for (let idx = 0; idx != num; ++idx) { - rng.unsafeNext(); + for (let idx = 0; idx !== num; ++idx) { + rng.next(); } } diff --git a/test/unit/distribution/internals/uniformIntInternal.spec.ts b/test/unit/distribution/internals/uniformIntInternal.spec.ts index 7706e56..24127de 100644 --- a/test/unit/distribution/internals/uniformIntInternal.spec.ts +++ b/test/unit/distribution/internals/uniformIntInternal.spec.ts @@ -11,11 +11,7 @@ class NatGenerator implements RandomGenerator { clone(): RandomGenerator { return new NatGenerator(this.current); } - next(): [number, RandomGenerator] { - const nextRng = this.clone(); - return [nextRng.unsafeNext(), nextRng]; - } - unsafeNext(): number { + next(): number { const previousCurrent = this.current; this.current = (this.current + 1) | 0; return previousCurrent; diff --git a/test/unit/distribution/uniformBigInt.spec.ts b/test/unit/distribution/uniformBigInt.spec.ts index 99cf583..fa66dae 100644 --- a/test/unit/distribution/uniformBigInt.spec.ts +++ b/test/unit/distribution/uniformBigInt.spec.ts @@ -46,7 +46,8 @@ describe('uniformBigInt', () => { const rngBigInt = mersenne(seed); const vBigInt = uniformBigInt(rngBigInt, BigInt(minV), BigInt(maxV)); assert.strictEqual(Number(vBigInt), vInt); // same values - assert.strictEqual(rngBigInt.next()[0], rngInt.next()[0]); // same state within generators + expect(rngBigInt.getState()).toEqual(rngInt.getState()); // same state within generators + assert.strictEqual(rngBigInt.next(), rngInt.next()); // same next value (this check is optional given we already checked for equal states, it just plays the role of a confirmation) }, ), )); diff --git a/test/unit/generator/LinearCongruencial.spec.ts b/test/unit/generator/LinearCongruencial.spec.ts index 7d5028a..dc14972 100644 --- a/test/unit/generator/LinearCongruencial.spec.ts +++ b/test/unit/generator/LinearCongruencial.spec.ts @@ -7,12 +7,11 @@ import * as p from './RandomGenerator.properties'; describe('congruential32', () => { it('Should produce the right sequence for seed=42', () => { - let g = congruential32(42); + const g = congruential32(42); const data = []; for (let idx = 0; idx !== 1000; ++idx) { - const [v, nextG] = g.next(); + const v = g.next(); data.push(v); - g = nextG; } assert.deepEqual( data, @@ -136,8 +135,7 @@ describe('congruential32', () => { fc.assert(p.clonedFromStateSameSequences(congruential32))); it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(congruential32))); it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(congruential32))); - it('Should impact itself with unsafeNext', () => fc.assert(p.changeSelfWithUnsafeNext(congruential32))); - it('Should not impact itself with next', () => fc.assert(p.noChangeSelfWithNext(congruential32))); - it('Should not impact clones when impacting itself on unsafeNext', () => - fc.assert(p.noChangeOnClonedWithUnsafeNext(congruential32))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(congruential32))); + it('Should not impact clones when impacting itself on next', () => + fc.assert(p.noChangeOnClonedWithNext(congruential32))); }); diff --git a/test/unit/generator/MersenneTwister.spec.ts b/test/unit/generator/MersenneTwister.spec.ts index 2e2b09a..6d53182 100644 --- a/test/unit/generator/MersenneTwister.spec.ts +++ b/test/unit/generator/MersenneTwister.spec.ts @@ -7,12 +7,11 @@ import * as p from './RandomGenerator.properties'; describe('mersenne', () => { it('Should produce the right sequence for seed=42', () => { - let g = mersenne(42); + const g = mersenne(42); const data = []; for (let idx = 0; idx !== 1000; ++idx) { - const [v, nextG] = g.next(); + const v = g.next(); data.push(v); - g = nextG; } // should be equivalent to the following Python code: // from numpy.random import MT19937 @@ -141,8 +140,6 @@ describe('mersenne', () => { fc.assert(p.clonedFromStateSameSequences(mersenne))); it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(mersenne))); it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(mersenne))); - it('Should impact itself with unsafeNext', () => fc.assert(p.changeSelfWithUnsafeNext(mersenne))); - it('Should not impact itself with next', () => fc.assert(p.noChangeSelfWithNext(mersenne))); - it('Should not impact clones when impacting itself on unsafeNext', () => - fc.assert(p.noChangeOnClonedWithUnsafeNext(mersenne))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(mersenne))); + it('Should not impact clones when impacting itself on next', () => fc.assert(p.noChangeOnClonedWithNext(mersenne))); }); diff --git a/test/unit/generator/RandomGenerator.properties.ts b/test/unit/generator/RandomGenerator.properties.ts index c91ca0a..526e1d1 100644 --- a/test/unit/generator/RandomGenerator.properties.ts +++ b/test/unit/generator/RandomGenerator.properties.ts @@ -33,7 +33,8 @@ export function sameSequencesIfCallTwice(rng_for: (seed: number) => RandomGenera export function valuesInRange(rng_for: (seed: number) => RandomGenerator) { return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { const rng = rng_for(seed); - const value = pureSkipN(rng, offset).next()[0]; + skipN(rng, offset); + const value = rng.next(); assert.ok(value >= -0x80000000); assert.ok(value <= 0x7fffffff); }); @@ -41,87 +42,64 @@ export function valuesInRange(rng_for: (seed: number) => RandomGenerator) { export function noOrderNextJump(rng_for: (seed: number) => RandomGenerator) { return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { - const rng = rng_for(seed); + const rngNextFirst = rng_for(seed); + const rngJumpFirst = rng_for(seed); // rngNextFirst = rng.next.next..(offset times)..next.jump - const rngNextFirst = pureSkipN(rng, offset).jump!(); + skipN(rngNextFirst, offset); + rngNextFirst.jump!(); // rngJumpFirst = rng.jump.next.next..(offset times)..next - const rngJumpFirst = pureSkipN(rng.jump!(), offset); - expect(rngNextFirst.next()[0]).toBe(rngJumpFirst.next()[0]); + rngJumpFirst.jump!(); + skipN(rngJumpFirst, offset); + // check same state and consequently same next value + expect(rngNextFirst.getState()).toEqual(rngJumpFirst.getState()); + expect(rngNextFirst.next()).toBe(rngJumpFirst.next()); }); } -export function changeSelfWithUnsafeNext(rng_for: (seed: number) => RandomGenerator) { +export function changeSelfWithNext(rng_for: (seed: number) => RandomGenerator) { return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { // Arrange - const [expectedValue, expectedNextRng] = pureSkipN(rng_for(seed), offset).next(); + const expectedRng = rng_for(seed); + skipN(expectedRng, offset); + const expectedValue = expectedRng.next(); const rng = rng_for(seed); skipN(rng, offset); - const rngReprBefore = JSON.stringify(rng); - const expectedRngReprAfter = JSON.stringify(expectedNextRng); + const rngStateBefore = rng.getState(); + const expectedRngStateAfter = expectedRng.getState(); // Act - const value = rng.unsafeNext(); - const rngReprAfter = JSON.stringify(rng); + const value = rng.next(); + const rngStateAfter = rng.getState(); // Assert expect(value).toBe(expectedValue); - expect(rngReprAfter).not.toBe(rngReprBefore); - expect(rngReprAfter).toBe(expectedRngReprAfter); - }); -} - -export function changeSelfWithUnsafeJump(rng_for: (seed: number) => RandomGenerator) { - return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { - // Arrange - const expectedJumpRng = pureSkipN(rng_for(seed), offset).jump!(); - const rng = rng_for(seed); - skipN(rng, offset); - const rngReprBefore = JSON.stringify(rng); - const expectedRngReprAfter = JSON.stringify(expectedJumpRng); - - // Act - rng.unsafeJump!(); - const rngReprAfter = JSON.stringify(rng); - - // Assert - expect(rngReprAfter).not.toBe(rngReprBefore); - expect(rngReprAfter).toBe(expectedRngReprAfter); + expect(rngStateAfter).not.toEqual(rngStateBefore); + expect(rngStateAfter).toEqual(expectedRngStateAfter); }); } -export function noChangeSelfWithNext(rng_for: (seed: number) => RandomGenerator) { +export function changeSelfWithJump(rng_for: (seed: number) => RandomGenerator) { return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { // Arrange + const expectedRng = rng_for(seed); + skipN(expectedRng, offset); + expectedRng.jump!(); const rng = rng_for(seed); skipN(rng, offset); - const rngReprBefore = JSON.stringify(rng); - - // Act - rng.next(); - const rngReprAfter = JSON.stringify(rng); - - // Assert - expect(rngReprAfter).toBe(rngReprBefore); - }); -} - -export function noChangeSelfWithJump(rng_for: (seed: number) => RandomGenerator) { - return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { - // Arrange - const rng = rng_for(seed); - skipN(rng, offset); - const rngReprBefore = JSON.stringify(rng); + const rngStateBefore = rng.getState(); + const expectedRngStateAfter = expectedRng.getState(); // Act rng.jump!(); - const rngReprAfter = JSON.stringify(rng); + const rngStateAfter = rng.getState(); // Assert - expect(rngReprAfter).toBe(rngReprBefore); + expect(rngStateAfter).not.toEqual(rngStateBefore); + expect(rngStateAfter).toEqual(expectedRngStateAfter); }); } -export function noChangeOnClonedWithUnsafeNext(rng_for: (seed: number) => RandomGenerator) { +export function noChangeOnClonedWithNext(rng_for: (seed: number) => RandomGenerator) { return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { // Arrange const rng = rng_for(seed); @@ -131,7 +109,7 @@ export function noChangeOnClonedWithUnsafeNext(rng_for: (seed: number) => Random const rngClonedReprBefore = JSON.stringify(rngCloned); // Act - rng.unsafeNext(); + rng.next(); const rngReprAfter = JSON.stringify(rng); const rngClonedReprAfter = JSON.stringify(rngCloned); @@ -142,7 +120,7 @@ export function noChangeOnClonedWithUnsafeNext(rng_for: (seed: number) => Random }); } -export function noChangeOnClonedWithUnsafeJump(rng_for: (seed: number) => RandomGenerator) { +export function noChangeOnClonedWithJump(rng_for: (seed: number) => RandomGenerator) { return fc.property(fc.integer(), fc.nat(MAX_SIZE), (seed, offset) => { // Arrange const rng = rng_for(seed); @@ -152,7 +130,7 @@ export function noChangeOnClonedWithUnsafeJump(rng_for: (seed: number) => Random const rngClonedReprBefore = JSON.stringify(rngCloned); // Act - rng.unsafeJump!(); + rng.jump!(); const rngReprAfter = JSON.stringify(rng); const rngClonedReprAfter = JSON.stringify(rngCloned); diff --git a/test/unit/generator/XorShift.spec.ts b/test/unit/generator/XorShift.spec.ts index 3dc9e01..7b1f653 100644 --- a/test/unit/generator/XorShift.spec.ts +++ b/test/unit/generator/XorShift.spec.ts @@ -7,12 +7,11 @@ import * as p from './RandomGenerator.properties'; describe('xorshift128plus', () => { it('Should produce the right sequence for seed=42', () => { - let g = xorshift128plus(42); + const g = xorshift128plus(42); let data = []; for (let idx = 0; idx !== 100; ++idx) { - const [v, nextG] = g.next(); + const v = g.next(); data.push(v); - g = nextG; } // should be equivalent to the following C code: // uint64_t s[] = { (uint64_t) (~42), ((uint64_t) 42) << 32 }; @@ -47,12 +46,12 @@ describe('xorshift128plus', () => { ); }); it('Should produce the right sequence after jump for seed=42', () => { - let g = xorshift128plus(42).jump!(); + const g = xorshift128plus(42); + g.jump!(); let data = []; for (let idx = 0; idx !== 100; ++idx) { - const [v, nextG] = g.next(); + const v = g.next(); data.push(v); - g = nextG; } // should be equivalent to the following C++ code (+previous): // void jump() { @@ -98,12 +97,10 @@ describe('xorshift128plus', () => { it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(xorshift128plus))); it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(xorshift128plus))); it('Should not depend on ordering between jump and next', () => fc.assert(p.noOrderNextJump(xorshift128plus))); - it('Should impact itself with unsafeNext', () => fc.assert(p.changeSelfWithUnsafeNext(xorshift128plus))); - it('Should impact itself with unsafeJump', () => fc.assert(p.changeSelfWithUnsafeJump(xorshift128plus))); - it('Should not impact itself with next', () => fc.assert(p.noChangeSelfWithNext(xorshift128plus))); - it('Should not impact itself with jump', () => fc.assert(p.noChangeSelfWithJump(xorshift128plus))); - it('Should not impact clones when impacting itself on unsafeNext', () => - fc.assert(p.noChangeOnClonedWithUnsafeNext(xorshift128plus))); - it('Should not impact clones when impacting itself on unsafeJump', () => - fc.assert(p.noChangeOnClonedWithUnsafeJump(xorshift128plus))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(xorshift128plus))); + it('Should impact itself with jump', () => fc.assert(p.changeSelfWithJump(xorshift128plus))); + it('Should not impact clones when impacting itself on next', () => + fc.assert(p.noChangeOnClonedWithNext(xorshift128plus))); + it('Should not impact clones when impacting itself on jump', () => + fc.assert(p.noChangeOnClonedWithJump(xorshift128plus))); }); diff --git a/test/unit/generator/XoroShiro.spec.ts b/test/unit/generator/XoroShiro.spec.ts index 51e49eb..40d6d66 100644 --- a/test/unit/generator/XoroShiro.spec.ts +++ b/test/unit/generator/XoroShiro.spec.ts @@ -7,12 +7,11 @@ import * as p from './RandomGenerator.properties'; describe('xoroshiro128plus', () => { it('Should produce the right sequence for seed=42', () => { - let g = xoroshiro128plus(42); + const g = xoroshiro128plus(42); let data = []; for (let idx = 0; idx !== 100; ++idx) { - const [v, nextG] = g.next(); + const v = g.next(); data.push(v); - g = nextG; } // should be equivalent to the following C code: // uint64_t s[] = { (uint64_t) (~42), ((uint64_t) 42) << 32 }; @@ -47,12 +46,12 @@ describe('xoroshiro128plus', () => { ); }); it('Should produce the right sequence after jump for seed=42', () => { - let g = xoroshiro128plus(42).jump!(); + const g = xoroshiro128plus(42); + g.jump!(); let data = []; for (let idx = 0; idx !== 100; ++idx) { - const [v, nextG] = g.next(); + const v = g.next(); data.push(v); - g = nextG; } // should be equivalent to the following C code (+previous): // void jump() { @@ -95,12 +94,10 @@ describe('xoroshiro128plus', () => { it('Should return the same sequence if called twice', () => fc.assert(p.sameSequencesIfCallTwice(xoroshiro128plus))); it('Should generate values between -2**31 and 2**31 -1', () => fc.assert(p.valuesInRange(xoroshiro128plus))); it('Should not depend on ordering between jump and next', () => fc.assert(p.noOrderNextJump(xoroshiro128plus))); - it('Should impact itself with unsafeNext', () => fc.assert(p.changeSelfWithUnsafeNext(xoroshiro128plus))); - it('Should impact itself with unsafeJump', () => fc.assert(p.changeSelfWithUnsafeJump(xoroshiro128plus))); - it('Should not impact itself with next', () => fc.assert(p.noChangeSelfWithNext(xoroshiro128plus))); - it('Should not impact itself with jump', () => fc.assert(p.noChangeSelfWithJump(xoroshiro128plus))); - it('Should not impact clones when impacting itself on unsafeNext', () => - fc.assert(p.noChangeOnClonedWithUnsafeNext(xoroshiro128plus))); - it('Should not impact clones when impacting itself on unsafeJump', () => - fc.assert(p.noChangeOnClonedWithUnsafeJump(xoroshiro128plus))); + it('Should impact itself with next', () => fc.assert(p.changeSelfWithNext(xoroshiro128plus))); + it('Should impact itself with jump', () => fc.assert(p.changeSelfWithJump(xoroshiro128plus))); + it('Should not impact clones when impacting itself on next', () => + fc.assert(p.noChangeOnClonedWithNext(xoroshiro128plus))); + it('Should not impact clones when impacting itself on jump', () => + fc.assert(p.noChangeOnClonedWithJump(xoroshiro128plus))); }); diff --git a/test/unit/utils/purify.spec.ts b/test/unit/utils/purify.spec.ts index 5621c49..8d8375c 100644 --- a/test/unit/utils/purify.spec.ts +++ b/test/unit/utils/purify.spec.ts @@ -10,17 +10,13 @@ describe('purify', () => { clone: notImplemented, getState: notImplemented, next: notImplemented, - unsafeNext: notImplemented, jump: notImplemented, - unsafeJump: notImplemented, }; const rng: RandomGenerator = { clone: () => clonedRng, getState: notImplemented, next: notImplemented, - unsafeNext: notImplemented, jump: notImplemented, - unsafeJump: notImplemented, }; const action = vi.fn<(rng: RandomGenerator, label: string, num: number) => string>( (_, label, num) => `${label}::${num}`, -- 2.51.2