mirror of
https://github.com/ziglang/zig.git
synced 2024-11-16 00:57:04 +00:00
0ccdc511ce
I specifically needed jump for an application and it doesn't appear to be exposed in any way.
148 lines
3.2 KiB
Zig
148 lines
3.2 KiB
Zig
//! Xoroshiro128+ - http://xoroshiro.di.unimi.it/
|
|
//!
|
|
//! PRNG
|
|
|
|
const std = @import("std");
|
|
const Random = std.rand.Random;
|
|
const math = std.math;
|
|
const Xoroshiro128 = @This();
|
|
|
|
s: [2]u64,
|
|
|
|
pub fn init(init_s: u64) Xoroshiro128 {
|
|
var x = Xoroshiro128{ .s = undefined };
|
|
|
|
x.seed(init_s);
|
|
return x;
|
|
}
|
|
|
|
pub fn random(self: *Xoroshiro128) Random {
|
|
return Random.init(self, fill);
|
|
}
|
|
|
|
pub fn next(self: *Xoroshiro128) u64 {
|
|
const s0 = self.s[0];
|
|
var s1 = self.s[1];
|
|
const r = s0 +% s1;
|
|
|
|
s1 ^= s0;
|
|
self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
|
|
self.s[1] = math.rotl(u64, s1, @as(u8, 36));
|
|
|
|
return r;
|
|
}
|
|
|
|
// Skip 2^64 places ahead in the sequence
|
|
pub fn jump(self: *Xoroshiro128) void {
|
|
var s0: u64 = 0;
|
|
var s1: u64 = 0;
|
|
|
|
const table = [_]u64{
|
|
0xbeac0467eba5facb,
|
|
0xd86b048b86aa9922,
|
|
};
|
|
|
|
inline for (table) |entry| {
|
|
var b: usize = 0;
|
|
while (b < 64) : (b += 1) {
|
|
if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
|
|
s0 ^= self.s[0];
|
|
s1 ^= self.s[1];
|
|
}
|
|
_ = self.next();
|
|
}
|
|
}
|
|
|
|
self.s[0] = s0;
|
|
self.s[1] = s1;
|
|
}
|
|
|
|
pub fn seed(self: *Xoroshiro128, init_s: u64) void {
|
|
// Xoroshiro requires 128-bits of seed.
|
|
var gen = std.rand.SplitMix64.init(init_s);
|
|
|
|
self.s[0] = gen.next();
|
|
self.s[1] = gen.next();
|
|
}
|
|
|
|
pub fn fill(self: *Xoroshiro128, buf: []u8) void {
|
|
var i: usize = 0;
|
|
const aligned_len = buf.len - (buf.len & 7);
|
|
|
|
// Complete 8 byte segments.
|
|
while (i < aligned_len) : (i += 8) {
|
|
var n = self.next();
|
|
comptime var j: usize = 0;
|
|
inline while (j < 8) : (j += 1) {
|
|
buf[i + j] = @truncate(u8, n);
|
|
n >>= 8;
|
|
}
|
|
}
|
|
|
|
// Remaining. (cuts the stream)
|
|
if (i != buf.len) {
|
|
var n = self.next();
|
|
while (i < buf.len) : (i += 1) {
|
|
buf[i] = @truncate(u8, n);
|
|
n >>= 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
test "xoroshiro sequence" {
|
|
var r = Xoroshiro128.init(0);
|
|
r.s[0] = 0xaeecf86f7878dd75;
|
|
r.s[1] = 0x01cd153642e72622;
|
|
|
|
const seq1 = [_]u64{
|
|
0xb0ba0da5bb600397,
|
|
0x18a08afde614dccc,
|
|
0xa2635b956a31b929,
|
|
0xabe633c971efa045,
|
|
0x9ac19f9706ca3cac,
|
|
0xf62b426578c1e3fb,
|
|
};
|
|
|
|
for (seq1) |s| {
|
|
try std.testing.expect(s == r.next());
|
|
}
|
|
|
|
r.jump();
|
|
|
|
const seq2 = [_]u64{
|
|
0x95344a13556d3e22,
|
|
0xb4fb32dafa4d00df,
|
|
0xb2011d9ccdcfe2dd,
|
|
0x05679a9b2119b908,
|
|
0xa860a1da7c9cd8a0,
|
|
0x658a96efe3f86550,
|
|
};
|
|
|
|
for (seq2) |s| {
|
|
try std.testing.expect(s == r.next());
|
|
}
|
|
}
|
|
|
|
test "xoroshiro fill" {
|
|
var r = Xoroshiro128.init(0);
|
|
r.s[0] = 0xaeecf86f7878dd75;
|
|
r.s[1] = 0x01cd153642e72622;
|
|
|
|
const seq = [_]u64{
|
|
0xb0ba0da5bb600397,
|
|
0x18a08afde614dccc,
|
|
0xa2635b956a31b929,
|
|
0xabe633c971efa045,
|
|
0x9ac19f9706ca3cac,
|
|
0xf62b426578c1e3fb,
|
|
};
|
|
|
|
for (seq) |s| {
|
|
var buf0: [8]u8 = undefined;
|
|
var buf1: [7]u8 = undefined;
|
|
std.mem.writeIntLittle(u64, &buf0, s);
|
|
r.fill(&buf1);
|
|
try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
|
|
}
|
|
}
|