Skip to content

Commit a7e19a8

Browse files
authored
🔥 Clean internals of uniform distribution (#515)
1 parent 520cca7 commit a7e19a8

File tree

2 files changed

+8
-68
lines changed

2 files changed

+8
-68
lines changed

src/distribution/internals/UnsafeUniformIntDistributionInternal.ts

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,19 @@ import { RandomGenerator } from '../../generator/RandomGenerator';
22

33
/**
44
* Uniformly generate number in range [0 ; rangeSize[
5+
* With rangeSize <= 0x100000000
56
* @internal
67
*/
78
export function unsafeUniformIntDistributionInternal(rangeSize: number, rng: RandomGenerator): number {
89
const MinRng = -0x80000000;
910
const NumValues = 0x100000000;
1011

11-
// Range provided by the RandomGenerator is large enough
12-
if (rangeSize <= NumValues) {
13-
const MaxAllowed = NumValues - (NumValues % rangeSize);
14-
let deltaV = rng.unsafeNext() - MinRng;
15-
while (deltaV >= MaxAllowed) {
16-
deltaV = rng.unsafeNext() - MinRng;
17-
}
18-
return deltaV % rangeSize; // Warning: we expect NumValues <= 2**32, so diff too
12+
// Range provided by the RandomGenerator is large enough,
13+
// given rangeSize <= 0x100000000 and RandomGenerator is uniform on 0x100000000 values
14+
const MaxAllowed = NumValues - (NumValues % rangeSize);
15+
let deltaV = rng.unsafeNext() - MinRng;
16+
while (deltaV >= MaxAllowed) {
17+
deltaV = rng.unsafeNext() - MinRng;
1918
}
20-
21-
// Compute number of iterations required to have enough random
22-
// to build uniform entries in the asked range
23-
let FinalNumValues = NumValues * NumValues;
24-
let NumIterations = 2; // At least 2 (at this point in the code)
25-
while (FinalNumValues < rangeSize) {
26-
FinalNumValues *= NumValues;
27-
++NumIterations;
28-
}
29-
const MaxAcceptedRandom = rangeSize * Math.floor((1 * FinalNumValues) / rangeSize);
30-
31-
let largeDeltaV = unsafeComputeValue(rng, NumIterations, NumValues, MinRng);
32-
while (largeDeltaV >= MaxAcceptedRandom) {
33-
largeDeltaV = unsafeComputeValue(rng, NumIterations, NumValues, MinRng);
34-
}
35-
const inDiff = largeDeltaV - rangeSize * Math.floor((1 * largeDeltaV) / rangeSize);
36-
return inDiff;
37-
}
38-
39-
/**
40-
* Aggregate multiple calls to next() into a single random value
41-
*/
42-
function unsafeComputeValue(rng: RandomGenerator, NumIterations: number, NumValues: number, MinRng: number): number {
43-
let value = 0;
44-
for (let num = 0; num !== NumIterations; ++num) {
45-
const out = rng.unsafeNext();
46-
value = NumValues * value + (out - MinRng); // Warning: we overflow may when diff > max_safe (eg: =max_safe-min_safe+1)
47-
}
48-
return value;
19+
return deltaV % rangeSize; // Warning: we expect NumValues <= 2**32, so diff too
4920
}

test/unit/distribution/internals/UnsafeUniformIntDistributionInternal.spec.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as fc from 'fast-check';
22

33
import { unsafeUniformIntDistributionInternal } from '../../../../src/distribution/internals/UnsafeUniformIntDistributionInternal';
4-
import mersenne from '../../../../src/generator/MersenneTwister';
54
import { RandomGenerator } from '../../../../src/generator/RandomGenerator';
65

76
class NatGenerator implements RandomGenerator {
@@ -22,22 +21,6 @@ class NatGenerator implements RandomGenerator {
2221
}
2322
}
2423

25-
class ModNatGenerator implements RandomGenerator {
26-
constructor(private current: RandomGenerator, private mod: number) {}
27-
clone(): RandomGenerator {
28-
return new ModNatGenerator(this.current, this.mod);
29-
}
30-
next(): [number, RandomGenerator] {
31-
const nextRng = this.clone();
32-
return [nextRng.unsafeNext(), nextRng];
33-
}
34-
unsafeNext(): number {
35-
const [v, nrng] = this.current.next();
36-
this.current = nrng;
37-
return Math.abs(v % this.mod);
38-
}
39-
}
40-
4124
const MAX_RANGE: number = 1000;
4225

4326
describe('unsafeUniformIntDistributionInternal', () => {
@@ -84,18 +67,4 @@ describe('unsafeUniformIntDistributionInternal', () => {
8467
}
8568
)
8669
));
87-
it('Should be able to generate values larger than the RandomGenerator and outside bitwise operations', () =>
88-
fc.assert(
89-
fc.property(fc.integer(), fc.integer({ min: 2, max: 0x7fffffff }), (seed, mod) => {
90-
// kept it for legacy reasons, but could probably go without it
91-
let rng: RandomGenerator = new ModNatGenerator(mersenne(seed), mod);
92-
for (let numTries = 0; numTries < 100; ++numTries) {
93-
const v = unsafeUniformIntDistributionInternal(Number.MAX_SAFE_INTEGER, rng);
94-
if (v > 0xffffffff) {
95-
return true;
96-
}
97-
}
98-
return false;
99-
})
100-
));
10170
});

0 commit comments

Comments
 (0)