Skip to content

Commit

Permalink
Merge 24662f5 into master
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Mar 15, 2022
2 parents 7e673c4 + 24662f5 commit 79d16c7
Show file tree
Hide file tree
Showing 20 changed files with 112 additions and 78 deletions.
12 changes: 8 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# 0.7.2

- Fixed: `Xoshiro256pp`, `Xoshiro256pp` and `Muberry32` were not throwing `Unsupported64Error` when
created in JS

# 0.7.1

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

# 0.7.0+1

- Added Xoshiro256**

- fixed: for 64-bit generators `.nextInt32` was throwing an assertion error
- Fixed: for 64-bit generators `.nextInt32` was throwing an assertion error
instead of returning 0 as a random result


Expand Down
7 changes: 3 additions & 4 deletions lib/src/21_base32.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ abstract class RandomBase32 implements Random {
/// the range from 0, inclusive, to [max], exclusive.
@override
int nextInt(int max) {
// almost the same as https://bit.ly/35OH1Vh
// almost the same as https://bit.ly/35OH1Vh by Dart authors, BSD

if (max <= 0 || max > _POW2_32) {
throw RangeError.range(
max, 1, _POW2_32, 'max', 'Must be positive and <= 2^32');
throw RangeError.range(max, 1, _POW2_32, 'max', 'Must be positive and <= 2^32');
}

if ((max & -max) == max) {
// Fast case for powers of two.
return nextRaw32() & (max - 1);
Expand Down Expand Up @@ -155,4 +155,3 @@ abstract class RandomBase32 implements Random {
@protected
int boolCache_prevShift = 0;
}

7 changes: 7 additions & 0 deletions lib/src/60_mulberry32.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@

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

import '00_errors.dart';
import '00_ints.dart';

/// Random number generator based on **mulberry32** algorithm by T. Ettinger.
///
/// [reference](https://git.io/JmoUq)
class Mulberry32 extends RandomBase32 {
Mulberry32([int? seed32]) {
// although it's "32", it still needs long values
if (!INT64_SUPPORTED) {
throw Unsupported64Error();
}
if (seed32 != null) {
RangeError.checkValueInInterval(seed32, 0, 0xFFFFFFFF);
_state = seed32;
Expand Down
5 changes: 5 additions & 0 deletions lib/src/60_xoshiro256.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@

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

import '00_errors.dart';
import '00_ints.dart';
import '21_base64.dart';

/// Base class for Xoshiro256++ and Xoshiro256**
abstract class Xoshiro256 extends RandomBase64 {
Xoshiro256([int? seed64a, int? seed64b, int? seed64c, int? seed64d]) {
if (!INT64_SUPPORTED) {
throw Unsupported64Error();
}
if (seed64a != null || seed64b != null || seed64c != null || seed64d != null) {
_S0 = seed64a!;
_S1 = seed64b!;
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: xrandom
description: "Random number generators library focused on the consistency,
performance and reproducibility"
version: 0.7.1
version: 0.7.2
homepage: https://github.com/rtmigo/xrandom

environment:
Expand Down
33 changes: 10 additions & 23 deletions test/aliases_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,19 @@ import 'package:test/test.dart';
import 'package:xrandom/xrandom.dart'; // no imports for src should be here

void main() {



test('Xrandom', () {
expect(Xrandom(), isA<Xorshift32>());
expect(Xrandom(1), isA<Xorshift32>());
expect(Xrandom.expected(), isA<Xorshift32>());
expect(Xrandom.expected(), isA<Xrandom>());
expect(
List.generate(3, (_) => Xrandom.expected().nextRaw32()),
expect(List.generate(3, (_) => Xrandom.expected().nextRaw32()),
List.generate(3, (_) => Xorshift32.seeded().nextRaw32()));
expect(
List.generate(3, (_) => Xrandom(777).nextRaw32()),
expect(List.generate(3, (_) => Xrandom(777).nextRaw32()),
List.generate(3, (_) => Xorshift32(777).nextRaw32()));

final r1 = Xrandom();
final r2 = Xrandom();
expect(
List.generate(3, (_) => r1.nextRaw32()),
isNot(List.generate(3, (_) => r2.nextRaw32())));
expect(List.generate(3, (_) => r1.nextRaw32()), isNot(List.generate(3, (_) => r2.nextRaw32())));
});

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



void checkRespectsSeed(RandomBase32 Function(int seed) create) {
expect( List.generate(3, (_) => create(123).nextRaw32()),
List.generate(3, (_) => create(123).nextRaw32()) );
expect( List.generate(3, (_) => create(123).nextRaw32()),
isNot(List.generate(3, (_) => create(321).nextRaw32())) );
expect(List.generate(3, (_) => create(123).nextRaw32()),
List.generate(3, (_) => create(123).nextRaw32()));
expect(List.generate(3, (_) => create(123).nextRaw32()),
isNot(List.generate(3, (_) => create(321).nextRaw32())));
}

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


test('XrandomHq returns constant values from seed', () {
final random = Qrandom(10);
expect( List.generate(3, (_) => random.nextRaw32()),
[1282276250, 3989185767, 2009065675] );
expect(List.generate(3, (_) => random.nextRaw32()), [1282276250, 3989185767, 2009065675]);
});

test('XrandomHq range checking', () {
expect(()=>Qrandom(0), throwsRangeError);
expect(()=>Qrandom(0xFFFFFFFF+1), throwsRangeError);
expect(() => Qrandom(0), throwsRangeError);
expect(() => Qrandom(0xFFFFFFFF + 1), throwsRangeError);
});


}
68 changes: 62 additions & 6 deletions test/helper.dart → test/common.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: (c) 2021 Art Galkin <github.com/rtmigo>
// SPDX-FileCopyrightText: (c) 2021-2022 Art Galkin <github.com/rtmigo>
// SPDX-License-Identifier: MIT

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

test('bools', () => checkBooleans(createRandom()));
test('ints', () => checkIntegers(createRandom()));
test('ints', () => check_nextInt_bounds(createRandom()));

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

test('large ints uniformity', () {
test('nextInt returns uniform result for max > 1<<32', () {
final r = createRandom();
checkUniformityForLargeInts(r);
check_nextInt_is_uniform_for_large_max(r);
});

var r = Random();
for (int i=0; i<10; ++i) {
// generating max from range 1000..(1<<32)
int max = 0;
while ((max = r.nextInt(0xFFFFFFFF+1)+1)<1000) {}

test('nextInt returns uniform results for max=$max', () {
check_nextInt_is_uniform(createRandom(), max);
});
}
});
}

Expand Down Expand Up @@ -289,7 +300,9 @@ void checkBooleans(Random r) {
expect(countTrue, lessThan(N * 0.6));
}

void checkUniformityForLargeInts(Random random) {
void check_nextInt_is_uniform_for_large_max(Random random) {
// checking whether nextInt results are uniform for max exceeding 31<<1
//
// eliminating the issue:
// https://github.com/rtmigo/xrandom_dart/issues/3

Expand Down Expand Up @@ -318,8 +331,51 @@ void checkUniformityForLargeInts(Random random) {
expect(upper, lessThan(expected + delta));
}

void check_nextInt_is_uniform(Random random, int max) {
// we will split range (0..max) to three equal bins: (0..a) (a..b) (b..max)
// Then we generate random ints from (0..max), and counting how many results correspond
// to particular bin. If the distribution is uniform, we'll get roughly the same count
// of results in each bin.

int a = (max * (1 / 3)).round();
int b = (max * (2 / 3)).round();

assert (0 < a);
assert (a < b);
assert (b < max);

int countA=0, countB=0, countC=0;

const N = 10000000;

for (int i=0; i<N; ++i) {

var x = random.nextInt(max);
if (x<a) {
countA++;
} else if (x<b) {
countB++;
} else {
countC++;
}
}

final int expected = (N/3).round();
final int delta = (expected*0.1).round();

expect(countA, greaterThan(expected - delta));
expect(countA, lessThan(expected + delta));

expect(countB, greaterThan(expected - delta));
expect(countB, lessThan(expected + delta));

expect(countC, greaterThan(expected - delta));
expect(countC, lessThan(expected + delta));
}


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

Expand Down
2 changes: 1 addition & 1 deletion test/helper_test.dart → test/common_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import 'package:test/test.dart';

import 'helper.dart';
import 'common.dart';

void main() {
test('test', () {
Expand Down
2 changes: 1 addition & 1 deletion test/exp_lemire_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:quiver/iterables.dart';
import 'package:test/test.dart';

import 'experimental/70_exp_lemire_o_neill.dart';
import 'helper.dart';
import 'common.dart';

void main() {

Expand Down
14 changes: 1 addition & 13 deletions test/int_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ import 'package:test/test.dart';
import 'package:xrandom/src/00_ints.dart';

void main() {

//print(1420068584772107736.truncateToDouble());

//return;

test('string to int', () {
// the problem with BigInt.toInt():
expect(BigInt.parse('0xf7d3b43bed078fa3').toInt().toHexUint64(), '7fffffffffffffff');
Expand Down Expand Up @@ -79,8 +74,6 @@ void main() {
}
});



test('toInt32', () {
// C99:
// int64_t src = rand_uint64();
Expand Down Expand Up @@ -111,20 +104,15 @@ void main() {
}
});



test('uint32 to int32', () {



// 0x7ffffffd 2147483645 -> 2147483645
// 0x7ffffffe 2147483646 -> 2147483646
// 0x7fffffff 2147483647 -> 2147483647
// 0x80000000 2147483648 -> -2147483648
// 0x80000001 2147483649 -> -2147483647
// 0x80000002 2147483650 -> -2147483646

for (final pair in[
for (final pair in [
[0x7ffffffd, 2147483645],
[0x7ffffffe, 2147483646],
[0x7fffffff, 2147483647],
Expand Down
3 changes: 2 additions & 1 deletion test/mulberry32_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import 'package:test/test.dart';
import 'package:xrandom/src/60_mulberry32.dart';

import 'helper.dart';
import 'common.dart';

void main() {
testCommonRandom(() => Mulberry32(), () => Mulberry32.seeded());
Expand Down Expand Up @@ -33,3 +33,4 @@ void main() {
expect(List.generate(10, (_) => random.nextRaw32()), exp);
});
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import 'package:xrandom/xrandom.dart';

void main() {
test('64', () {
Xorshift32(); // no errors
expect(() => Xorshift32().nextRaw64(), throwsA(isA<Unsupported64Error>()));
expect(() => Xorshift64.seeded(), throwsA(isA<Unsupported64Error>()));
expect(() => Xorshift128p.seeded(), throwsA(isA<Unsupported64Error>()));
expect(() => Xoshiro256pp.seeded(), throwsA(isA<Unsupported64Error>()));
expect(() => Xoshiro256ss.seeded(), throwsA(isA<Unsupported64Error>()));
expect(() => Splitmix64.seeded(), throwsA(isA<Unsupported64Error>()));
expect(() => Mulberry32.seeded(), throwsA(isA<Unsupported64Error>()));
});
}
6 changes: 1 addition & 5 deletions test/splitmix64_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@ import 'package:test/test.dart';
import 'package:xrandom/src/50_splitmix64.dart';
import 'package:xrandom/xrandom.dart';

import 'helper.dart';
import 'common.dart';

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

//print(-6562126107>>1);
//return;

checkReferenceFiles(() => Splitmix64(1), 'a');
checkReferenceFiles(() => Splitmix64(0), 'b');
checkReferenceFiles(() => Splitmix64(777), 'c');
checkReferenceFiles(() => Splitmix64(int.parse('0xf7d3b43bed078fa3')), 'd');
// checkReferenceFiles(() => Splitmix64(3141592653589793238), 'c');

test('expected values', () {
expect(expectedList(Splitmix64.seeded()), [
Expand Down
2 changes: 1 addition & 1 deletion test/xorshift128_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import 'package:test/test.dart';
import 'package:xrandom/src/60_xorshift128.dart';

import 'helper.dart';
import 'common.dart';

void main() {
testCommonRandom(() => Xorshift128(), ()=>Xorshift128.seeded());
Expand Down
2 changes: 1 addition & 1 deletion test/xorshift128plus_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import 'package:test/test.dart';
import 'package:xrandom/src/60_xorshift128plus.dart';

import 'helper.dart';
import 'common.dart';
import 'madsen.dart';

void main() {
Expand Down
2 changes: 1 addition & 1 deletion test/xorshift32_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import 'package:test/test.dart';
import 'package:xrandom/src/60_xorshift32.dart';

import 'helper.dart';
import 'common.dart';

void main() {
testCommonRandom(() => Xorshift32(), ()=>Xorshift32.seeded());
Expand Down
Loading

0 comments on commit 79d16c7

Please sign in to comment.