2016-07-26 23:51:58 -07:00
|
|
|
const assert = @import("debug.zig").assert;
|
2016-07-28 20:14:57 -07:00
|
|
|
const rand_test = @import("rand_test.zig");
|
2016-07-26 23:51:58 -07:00
|
|
|
|
|
|
|
pub const MT19937_32 = MersenneTwister(
|
|
|
|
u32, 624, 397, 31,
|
|
|
|
0x9908B0DF,
|
|
|
|
11, 0xFFFFFFFF,
|
|
|
|
7, 0x9D2C5680,
|
|
|
|
15, 0xEFC60000,
|
|
|
|
18, 1812433253);
|
|
|
|
|
|
|
|
pub const MT19937_64 = MersenneTwister(
|
|
|
|
u64, 312, 156, 31,
|
|
|
|
0xB5026F5AA96619E9,
|
|
|
|
29, 0x5555555555555555,
|
|
|
|
17, 0x71D67FFFEDA60000,
|
|
|
|
37, 0xFFF7EEE000000000,
|
|
|
|
43, 6364136223846793005);
|
2016-01-02 23:30:41 -08:00
|
|
|
|
2016-04-07 10:34:54 -07:00
|
|
|
/// Use `init` to initialize this state.
|
2016-12-18 16:40:26 -08:00
|
|
|
pub const Rand = struct {
|
2016-08-16 22:42:50 -07:00
|
|
|
const Rng = if (@sizeOf(usize) >= 8) MT19937_64 else MT19937_32;
|
2016-07-26 23:51:58 -07:00
|
|
|
|
|
|
|
rng: Rng,
|
2016-01-02 23:30:41 -08:00
|
|
|
|
2016-04-07 10:34:54 -07:00
|
|
|
/// Initialize random state with the given seed.
|
2016-08-16 22:42:50 -07:00
|
|
|
pub fn init(r: &Rand, seed: usize) {
|
|
|
|
r.rng.init(seed);
|
2016-04-07 10:34:54 -07:00
|
|
|
}
|
|
|
|
|
2016-07-26 23:51:58 -07:00
|
|
|
/// Get an integer with random bits.
|
|
|
|
pub fn scalar(r: &Rand, inline T: type) -> T {
|
|
|
|
if (T == usize) {
|
|
|
|
return r.rng.get();
|
|
|
|
} else {
|
2016-08-16 22:42:50 -07:00
|
|
|
var result: [@sizeOf(T)]u8 = undefined;
|
|
|
|
r.fillBytes(result);
|
2016-07-27 19:52:38 -07:00
|
|
|
return ([]T)(result)[0];
|
2016-01-02 23:30:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-05 21:47:47 -08:00
|
|
|
/// Fill `buf` with randomness.
|
2016-08-16 22:42:50 -07:00
|
|
|
pub fn fillBytes(r: &Rand, buf: []u8) {
|
2016-07-26 23:51:58 -07:00
|
|
|
var bytes_left = buf.len;
|
2016-08-16 22:42:50 -07:00
|
|
|
while (bytes_left >= @sizeOf(usize)) {
|
2016-07-27 19:52:38 -07:00
|
|
|
([]usize)(buf[buf.len - bytes_left...])[0] = r.rng.get();
|
2016-08-16 22:42:50 -07:00
|
|
|
bytes_left -= @sizeOf(usize);
|
2016-07-26 23:51:58 -07:00
|
|
|
}
|
2016-01-02 23:30:41 -08:00
|
|
|
if (bytes_left > 0) {
|
2016-08-16 22:42:50 -07:00
|
|
|
var rand_val_array : [@sizeOf(usize)]u8 = undefined;
|
2016-07-27 19:52:38 -07:00
|
|
|
([]usize)(rand_val_array)[0] = r.rng.get();
|
2016-01-02 23:30:41 -08:00
|
|
|
while (bytes_left > 0) {
|
2016-08-16 22:42:50 -07:00
|
|
|
buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left];
|
2016-01-02 23:30:41 -08:00
|
|
|
bytes_left -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a random unsigned integer with even distribution between `start`
|
|
|
|
/// inclusive and `end` exclusive.
|
2016-07-26 23:51:58 -07:00
|
|
|
// TODO support signed integers and then rename to "range"
|
2016-08-16 22:42:50 -07:00
|
|
|
pub fn rangeUnsigned(r: &Rand, inline T: type, start: T, end: T) -> T {
|
2016-01-02 23:30:41 -08:00
|
|
|
const range = end - start;
|
2016-08-16 22:42:50 -07:00
|
|
|
const leftover = @maxValue(T) % range;
|
|
|
|
const upper_bound = @maxValue(T) - leftover;
|
|
|
|
var rand_val_array : [@sizeOf(T)]u8 = undefined;
|
2016-01-02 23:30:41 -08:00
|
|
|
|
|
|
|
while (true) {
|
2016-08-16 22:42:50 -07:00
|
|
|
r.fillBytes(rand_val_array);
|
2016-07-26 23:51:58 -07:00
|
|
|
const rand_val = ([]T)(rand_val_array)[0];
|
2016-01-02 23:30:41 -08:00
|
|
|
if (rand_val < upper_bound) {
|
|
|
|
return start + (rand_val % range);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-26 23:51:58 -07:00
|
|
|
/// Get a floating point value in the range 0.0..1.0.
|
|
|
|
pub fn float(r: &Rand, inline T: type) -> T {
|
2016-07-27 23:46:20 -07:00
|
|
|
// TODO Implement this way instead:
|
2016-08-16 22:42:50 -07:00
|
|
|
// const int = @int_type(false, @sizeOf(T) * 8);
|
2016-07-27 23:46:20 -07:00
|
|
|
// const mask = ((1 << @float_mantissa_bit_count(T)) - 1);
|
|
|
|
// const rand_bits = r.rng.scalar(int) & mask;
|
|
|
|
// return @float_compose(T, false, 0, rand_bits) - 1.0
|
2016-08-16 22:42:50 -07:00
|
|
|
const int_type = @intType(false, @sizeOf(T) * 8);
|
2016-07-26 23:51:58 -07:00
|
|
|
const precision = if (T == f32) {
|
|
|
|
16777216
|
|
|
|
} else if (T == f64) {
|
|
|
|
9007199254740992
|
|
|
|
} else {
|
2016-12-31 14:10:29 -08:00
|
|
|
@compileError("unknown floating point type")
|
2016-07-26 23:51:58 -07:00
|
|
|
};
|
2016-08-16 22:42:50 -07:00
|
|
|
return T(r.rangeUnsigned(int_type, 0, precision)) / T(precision);
|
2016-02-05 03:30:19 -08:00
|
|
|
}
|
2016-12-18 16:40:26 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
fn MersenneTwister(
|
|
|
|
inline int: type, inline n: usize, inline m: usize, inline r: int,
|
|
|
|
inline a: int,
|
|
|
|
inline u: int, inline d: int,
|
|
|
|
inline s: int, inline b: int,
|
|
|
|
inline t: int, inline c: int,
|
|
|
|
inline l: int, inline f: int) -> type
|
2016-07-26 23:51:58 -07:00
|
|
|
{
|
2016-12-18 16:40:26 -08:00
|
|
|
struct {
|
|
|
|
const Self = this;
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
array: [n]int,
|
|
|
|
index: usize,
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
pub fn init(mt: &Self, seed: int) {
|
|
|
|
mt.index = n;
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
var prev_value = seed;
|
|
|
|
mt.array[0] = prev_value;
|
|
|
|
{var i: usize = 1; while (i < n; i += 1) {
|
|
|
|
prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2)));
|
|
|
|
mt.array[i] = prev_value;
|
|
|
|
}};
|
|
|
|
}
|
2016-02-07 11:38:51 -08:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
pub fn get(mt: &Self) -> int {
|
|
|
|
const mag01 = []int{0, a};
|
|
|
|
const LM: int = (1 << r) - 1;
|
|
|
|
const UM = ~LM;
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
if (mt.index >= n) {
|
|
|
|
var i: usize = 0;
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
while (i < n - m; i += 1) {
|
|
|
|
const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
|
|
|
|
mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[x & 0x1];
|
|
|
|
}
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
while (i < n - 1; i += 1) {
|
|
|
|
const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
|
|
|
|
mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[x & 0x1];
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
}
|
|
|
|
const x = (mt.array[i] & UM) | (mt.array[0] & LM);
|
|
|
|
mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[x & 0x1];
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
mt.index = 0;
|
|
|
|
}
|
2016-01-02 23:30:41 -08:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
var x = mt.array[mt.index];
|
|
|
|
mt.index += 1;
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
x ^= ((x >> u) & d);
|
|
|
|
x ^= ((x <<% s) & b);
|
|
|
|
x ^= ((x <<% t) & c);
|
|
|
|
x ^= (x >> l);
|
2016-07-26 23:51:58 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
return x;
|
|
|
|
}
|
2016-07-26 23:51:58 -07:00
|
|
|
}
|
2016-01-24 18:27:12 -08:00
|
|
|
}
|
2016-02-05 03:30:19 -08:00
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
fn testFloat32() {
|
2016-12-31 14:10:29 -08:00
|
|
|
@setFnTest(this);
|
2016-09-27 23:33:32 -07:00
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
var r: Rand = undefined;
|
|
|
|
r.init(42);
|
2016-02-05 03:30:19 -08:00
|
|
|
|
2016-07-26 23:51:58 -07:00
|
|
|
{var i: usize = 0; while (i < 1000; i += 1) {
|
|
|
|
const val = r.float(f32);
|
2016-07-28 20:14:57 -07:00
|
|
|
assert(val >= 0.0);
|
|
|
|
assert(val < 1.0);
|
2016-04-19 20:28:44 -07:00
|
|
|
}}
|
2016-02-05 03:30:19 -08:00
|
|
|
}
|
2016-07-28 20:14:57 -07:00
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
fn testMT19937_64() {
|
2016-12-31 14:10:29 -08:00
|
|
|
@setFnTest(this);
|
2016-09-27 23:33:32 -07:00
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
var rng: MT19937_64 = undefined;
|
|
|
|
rng.init(rand_test.mt64_seed);
|
2016-07-28 20:14:57 -07:00
|
|
|
for (rand_test.mt64_data) |value| {
|
|
|
|
assert(value == rng.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
fn testMT19937_32() {
|
2016-12-31 14:10:29 -08:00
|
|
|
@setFnTest(this);
|
2016-09-27 23:33:32 -07:00
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
var rng: MT19937_32 = undefined;
|
|
|
|
rng.init(rand_test.mt32_seed);
|
2016-07-28 20:14:57 -07:00
|
|
|
for (rand_test.mt32_data) |value| {
|
|
|
|
assert(value == rng.get());
|
|
|
|
}
|
|
|
|
}
|