Skip to content

Commit 79d16c7

Browse files
Merge 24662f5 into master
2 parents 7e673c4 + 24662f5 commit 79d16c7

20 files changed

+112
-78
lines changed

CHANGELOG.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
# 0.7.2
2+
3+
- Fixed: `Xoshiro256pp`, `Xoshiro256pp` and `Muberry32` were not throwing `Unsupported64Error` when
4+
created in JS
5+
16
# 0.7.1
27

3-
- fixed: `nextInt` results for `max >= 0x80000000` were not uniformly distributed
4-
- narrowed the range of possible `max` values for `Drandom`
8+
- Fixed: `nextInt` results for `max >= 0x80000000` were not uniformly distributed
9+
- Narrowed the range of possible `max` values for `Drandom`
510

611
# 0.7.0+1
712

813
- Added Xoshiro256**
9-
10-
- fixed: for 64-bit generators `.nextInt32` was throwing an assertion error
14+
- Fixed: for 64-bit generators `.nextInt32` was throwing an assertion error
1115
instead of returning 0 as a random result
1216

1317

lib/src/21_base32.dart

+3-4
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ abstract class RandomBase32 implements Random {
7676
/// the range from 0, inclusive, to [max], exclusive.
7777
@override
7878
int nextInt(int max) {
79-
// almost the same as https://bit.ly/35OH1Vh
79+
// almost the same as https://bit.ly/35OH1Vh by Dart authors, BSD
8080

8181
if (max <= 0 || max > _POW2_32) {
82-
throw RangeError.range(
83-
max, 1, _POW2_32, 'max', 'Must be positive and <= 2^32');
82+
throw RangeError.range(max, 1, _POW2_32, 'max', 'Must be positive and <= 2^32');
8483
}
84+
8585
if ((max & -max) == max) {
8686
// Fast case for powers of two.
8787
return nextRaw32() & (max - 1);
@@ -155,4 +155,3 @@ abstract class RandomBase32 implements Random {
155155
@protected
156156
int boolCache_prevShift = 0;
157157
}
158-

lib/src/60_mulberry32.dart

+7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33

44
import 'package:xrandom/src/21_base32.dart';
55

6+
import '00_errors.dart';
7+
import '00_ints.dart';
8+
69
/// Random number generator based on **mulberry32** algorithm by T. Ettinger.
710
///
811
/// [reference](https://git.io/JmoUq)
912
class Mulberry32 extends RandomBase32 {
1013
Mulberry32([int? seed32]) {
14+
// although it's "32", it still needs long values
15+
if (!INT64_SUPPORTED) {
16+
throw Unsupported64Error();
17+
}
1118
if (seed32 != null) {
1219
RangeError.checkValueInInterval(seed32, 0, 0xFFFFFFFF);
1320
_state = seed32;

lib/src/60_xoshiro256.dart

+5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33

44
import 'package:xrandom/src/50_splitmix64.dart';
55

6+
import '00_errors.dart';
7+
import '00_ints.dart';
68
import '21_base64.dart';
79

810
/// Base class for Xoshiro256++ and Xoshiro256**
911
abstract class Xoshiro256 extends RandomBase64 {
1012
Xoshiro256([int? seed64a, int? seed64b, int? seed64c, int? seed64d]) {
13+
if (!INT64_SUPPORTED) {
14+
throw Unsupported64Error();
15+
}
1116
if (seed64a != null || seed64b != null || seed64c != null || seed64d != null) {
1217
_S0 = seed64a!;
1318
_S1 = seed64b!;

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: xrandom
22
description: "Random number generators library focused on the consistency,
33
performance and reproducibility"
4-
version: 0.7.1
4+
version: 0.7.2
55
homepage: https://github.com/rtmigo/xrandom
66

77
environment:

test/aliases_test.dart

+10-23
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,19 @@ import 'package:test/test.dart';
55
import 'package:xrandom/xrandom.dart'; // no imports for src should be here
66

77
void main() {
8-
9-
10-
118
test('Xrandom', () {
129
expect(Xrandom(), isA<Xorshift32>());
1310
expect(Xrandom(1), isA<Xorshift32>());
1411
expect(Xrandom.expected(), isA<Xorshift32>());
1512
expect(Xrandom.expected(), isA<Xrandom>());
16-
expect(
17-
List.generate(3, (_) => Xrandom.expected().nextRaw32()),
13+
expect(List.generate(3, (_) => Xrandom.expected().nextRaw32()),
1814
List.generate(3, (_) => Xorshift32.seeded().nextRaw32()));
19-
expect(
20-
List.generate(3, (_) => Xrandom(777).nextRaw32()),
15+
expect(List.generate(3, (_) => Xrandom(777).nextRaw32()),
2116
List.generate(3, (_) => Xorshift32(777).nextRaw32()));
2217

2318
final r1 = Xrandom();
2419
final r2 = Xrandom();
25-
expect(
26-
List.generate(3, (_) => r1.nextRaw32()),
27-
isNot(List.generate(3, (_) => r2.nextRaw32())));
20+
expect(List.generate(3, (_) => r1.nextRaw32()), isNot(List.generate(3, (_) => r2.nextRaw32())));
2821
});
2922

3023
test('Xrandom readme expected', () {
@@ -43,13 +36,11 @@ void main() {
4336
expect(random.nextInt(1000), 904);
4437
});
4538

46-
47-
4839
void checkRespectsSeed(RandomBase32 Function(int seed) create) {
49-
expect( List.generate(3, (_) => create(123).nextRaw32()),
50-
List.generate(3, (_) => create(123).nextRaw32()) );
51-
expect( List.generate(3, (_) => create(123).nextRaw32()),
52-
isNot(List.generate(3, (_) => create(321).nextRaw32())) );
40+
expect(List.generate(3, (_) => create(123).nextRaw32()),
41+
List.generate(3, (_) => create(123).nextRaw32()));
42+
expect(List.generate(3, (_) => create(123).nextRaw32()),
43+
isNot(List.generate(3, (_) => create(321).nextRaw32())));
5344
}
5445

5546
test('XrandomHq respects seed argument', () {
@@ -60,17 +51,13 @@ void main() {
6051
checkRespectsSeed((seed) => Xrandom(seed));
6152
});
6253

63-
6454
test('XrandomHq returns constant values from seed', () {
6555
final random = Qrandom(10);
66-
expect( List.generate(3, (_) => random.nextRaw32()),
67-
[1282276250, 3989185767, 2009065675] );
56+
expect(List.generate(3, (_) => random.nextRaw32()), [1282276250, 3989185767, 2009065675]);
6857
});
6958

7059
test('XrandomHq range checking', () {
71-
expect(()=>Qrandom(0), throwsRangeError);
72-
expect(()=>Qrandom(0xFFFFFFFF+1), throwsRangeError);
60+
expect(() => Qrandom(0), throwsRangeError);
61+
expect(() => Qrandom(0xFFFFFFFF + 1), throwsRangeError);
7362
});
74-
75-
7663
}

test/helper.dart test/common.dart

+62-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: (c) 2021 Art Galkin <github.com/rtmigo>
1+
// SPDX-FileCopyrightText: (c) 2021-2022 Art Galkin <github.com/rtmigo>
22
// SPDX-License-Identifier: MIT
33

44
import 'dart:math';
@@ -146,7 +146,7 @@ void testCommonRandom(RandomBase32 Function() createRandom,
146146
test('nextFloat', () => checkDoubles(createRandom(), false));
147147

148148
test('bools', () => checkBooleans(createRandom()));
149-
test('ints', () => checkIntegers(createRandom()));
149+
test('ints', () => check_nextInt_bounds(createRandom()));
150150

151151
test('ints when power of two', () {
152152
final r = createExpectedRandom();
@@ -243,10 +243,21 @@ void testCommonRandom(RandomBase32 Function() createRandom,
243243
expect(random2.nextRaw32(), b64.lower32());
244244
});
245245

246-
test('large ints uniformity', () {
246+
test('nextInt returns uniform result for max > 1<<32', () {
247247
final r = createRandom();
248-
checkUniformityForLargeInts(r);
248+
check_nextInt_is_uniform_for_large_max(r);
249249
});
250+
251+
var r = Random();
252+
for (int i=0; i<10; ++i) {
253+
// generating max from range 1000..(1<<32)
254+
int max = 0;
255+
while ((max = r.nextInt(0xFFFFFFFF+1)+1)<1000) {}
256+
257+
test('nextInt returns uniform results for max=$max', () {
258+
check_nextInt_is_uniform(createRandom(), max);
259+
});
260+
}
250261
});
251262
}
252263

@@ -289,7 +300,9 @@ void checkBooleans(Random r) {
289300
expect(countTrue, lessThan(N * 0.6));
290301
}
291302

292-
void checkUniformityForLargeInts(Random random) {
303+
void check_nextInt_is_uniform_for_large_max(Random random) {
304+
// checking whether nextInt results are uniform for max exceeding 31<<1
305+
//
293306
// eliminating the issue:
294307
// https://github.com/rtmigo/xrandom_dart/issues/3
295308

@@ -318,8 +331,51 @@ void checkUniformityForLargeInts(Random random) {
318331
expect(upper, lessThan(expected + delta));
319332
}
320333

334+
void check_nextInt_is_uniform(Random random, int max) {
335+
// we will split range (0..max) to three equal bins: (0..a) (a..b) (b..max)
336+
// Then we generate random ints from (0..max), and counting how many results correspond
337+
// to particular bin. If the distribution is uniform, we'll get roughly the same count
338+
// of results in each bin.
339+
340+
int a = (max * (1 / 3)).round();
341+
int b = (max * (2 / 3)).round();
342+
343+
assert (0 < a);
344+
assert (a < b);
345+
assert (b < max);
346+
347+
int countA=0, countB=0, countC=0;
348+
349+
const N = 10000000;
350+
351+
for (int i=0; i<N; ++i) {
352+
353+
var x = random.nextInt(max);
354+
if (x<a) {
355+
countA++;
356+
} else if (x<b) {
357+
countB++;
358+
} else {
359+
countC++;
360+
}
361+
}
362+
363+
final int expected = (N/3).round();
364+
final int delta = (expected*0.1).round();
365+
366+
expect(countA, greaterThan(expected - delta));
367+
expect(countA, lessThan(expected + delta));
368+
369+
expect(countB, greaterThan(expected - delta));
370+
expect(countB, lessThan(expected + delta));
371+
372+
expect(countC, greaterThan(expected - delta));
373+
expect(countC, lessThan(expected + delta));
374+
}
375+
321376

322-
void checkIntegers(Random r) {
377+
/// Check that `nextInt(max)` returns only values from the `0 < x < max`, including `0` and `max-1`.
378+
void check_nextInt_bounds(Random r) {
323379
int countMin = 0;
324380
int countMax = 0;
325381

test/helper_test.dart test/common_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import 'package:test/test.dart';
66

7-
import 'helper.dart';
7+
import 'common.dart';
88

99
void main() {
1010
test('test', () {

test/exp_lemire_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'package:quiver/iterables.dart';
88
import 'package:test/test.dart';
99

1010
import 'experimental/70_exp_lemire_o_neill.dart';
11-
import 'helper.dart';
11+
import 'common.dart';
1212

1313
void main() {
1414

test/int_test.dart

+1-13
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ import 'package:test/test.dart';
77
import 'package:xrandom/src/00_ints.dart';
88

99
void main() {
10-
11-
//print(1420068584772107736.truncateToDouble());
12-
13-
//return;
14-
1510
test('string to int', () {
1611
// the problem with BigInt.toInt():
1712
expect(BigInt.parse('0xf7d3b43bed078fa3').toInt().toHexUint64(), '7fffffffffffffff');
@@ -79,8 +74,6 @@ void main() {
7974
}
8075
});
8176

82-
83-
8477
test('toInt32', () {
8578
// C99:
8679
// int64_t src = rand_uint64();
@@ -111,20 +104,15 @@ void main() {
111104
}
112105
});
113106

114-
115-
116107
test('uint32 to int32', () {
117-
118-
119-
120108
// 0x7ffffffd 2147483645 -> 2147483645
121109
// 0x7ffffffe 2147483646 -> 2147483646
122110
// 0x7fffffff 2147483647 -> 2147483647
123111
// 0x80000000 2147483648 -> -2147483648
124112
// 0x80000001 2147483649 -> -2147483647
125113
// 0x80000002 2147483650 -> -2147483646
126114

127-
for (final pair in[
115+
for (final pair in [
128116
[0x7ffffffd, 2147483645],
129117
[0x7ffffffe, 2147483646],
130118
[0x7fffffff, 2147483647],

test/mulberry32_test.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'package:test/test.dart';
66
import 'package:xrandom/src/60_mulberry32.dart';
77

8-
import 'helper.dart';
8+
import 'common.dart';
99

1010
void main() {
1111
testCommonRandom(() => Mulberry32(), () => Mulberry32.seeded());
@@ -33,3 +33,4 @@ void main() {
3333
expect(List.generate(10, (_) => random.nextRaw32()), exp);
3434
});
3535
}
36+

test/node_unsuported_tests.dart test/node_unsuported_test.dart

+3
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import 'package:xrandom/xrandom.dart';
88

99
void main() {
1010
test('64', () {
11+
Xorshift32(); // no errors
1112
expect(() => Xorshift32().nextRaw64(), throwsA(isA<Unsupported64Error>()));
1213
expect(() => Xorshift64.seeded(), throwsA(isA<Unsupported64Error>()));
1314
expect(() => Xorshift128p.seeded(), throwsA(isA<Unsupported64Error>()));
1415
expect(() => Xoshiro256pp.seeded(), throwsA(isA<Unsupported64Error>()));
16+
expect(() => Xoshiro256ss.seeded(), throwsA(isA<Unsupported64Error>()));
1517
expect(() => Splitmix64.seeded(), throwsA(isA<Unsupported64Error>()));
18+
expect(() => Mulberry32.seeded(), throwsA(isA<Unsupported64Error>()));
1619
});
1720
}

test/splitmix64_test.dart

+1-5
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,15 @@ import 'package:test/test.dart';
66
import 'package:xrandom/src/50_splitmix64.dart';
77
import 'package:xrandom/xrandom.dart';
88

9-
import 'helper.dart';
9+
import 'common.dart';
1010

1111
void main() {
1212
testCommonRandom(() => Splitmix64(), ()=>Splitmix64.seeded());
1313

14-
//print(-6562126107>>1);
15-
//return;
16-
1714
checkReferenceFiles(() => Splitmix64(1), 'a');
1815
checkReferenceFiles(() => Splitmix64(0), 'b');
1916
checkReferenceFiles(() => Splitmix64(777), 'c');
2017
checkReferenceFiles(() => Splitmix64(int.parse('0xf7d3b43bed078fa3')), 'd');
21-
// checkReferenceFiles(() => Splitmix64(3141592653589793238), 'c');
2218

2319
test('expected values', () {
2420
expect(expectedList(Splitmix64.seeded()), [

test/xorshift128_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import 'package:test/test.dart';
55
import 'package:xrandom/src/60_xorshift128.dart';
66

7-
import 'helper.dart';
7+
import 'common.dart';
88

99
void main() {
1010
testCommonRandom(() => Xorshift128(), ()=>Xorshift128.seeded());

test/xorshift128plus_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import 'package:test/test.dart';
66
import 'package:xrandom/src/60_xorshift128plus.dart';
77

8-
import 'helper.dart';
8+
import 'common.dart';
99
import 'madsen.dart';
1010

1111
void main() {

test/xorshift32_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import 'package:test/test.dart';
55
import 'package:xrandom/src/60_xorshift32.dart';
66

7-
import 'helper.dart';
7+
import 'common.dart';
88

99
void main() {
1010
testCommonRandom(() => Xorshift32(), ()=>Xorshift32.seeded());

0 commit comments

Comments
 (0)