mirror of
https://github.com/ziglang/zig.git
synced 2024-12-03 18:38:45 +00:00
235 lines
6.0 KiB
Zig
235 lines
6.0 KiB
Zig
//! ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
|
|
//!
|
|
//! Follows the general idea of the implementation from here with a few shortcuts.
|
|
//! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
|
|
|
|
const std = @import("std");
|
|
const mem = std.mem;
|
|
const Isaac64 = @This();
|
|
|
|
r: [256]u64,
|
|
m: [256]u64,
|
|
a: u64,
|
|
b: u64,
|
|
c: u64,
|
|
i: usize,
|
|
|
|
pub fn init(init_s: u64) Isaac64 {
|
|
var isaac = Isaac64{
|
|
.r = undefined,
|
|
.m = undefined,
|
|
.a = undefined,
|
|
.b = undefined,
|
|
.c = undefined,
|
|
.i = undefined,
|
|
};
|
|
|
|
// seed == 0 => same result as the unseeded reference implementation
|
|
isaac.seed(init_s, 1);
|
|
return isaac;
|
|
}
|
|
|
|
pub fn random(self: *Isaac64) std.Random {
|
|
return std.Random.init(self, fill);
|
|
}
|
|
|
|
fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
|
|
const x = self.m[base + m1];
|
|
self.a = mix +% self.m[base + m2];
|
|
|
|
const y = self.a +% self.b +% self.m[@as(usize, @intCast((x >> 3) % self.m.len))];
|
|
self.m[base + m1] = y;
|
|
|
|
self.b = x +% self.m[@as(usize, @intCast((y >> 11) % self.m.len))];
|
|
self.r[self.r.len - 1 - base - m1] = self.b;
|
|
}
|
|
|
|
fn refill(self: *Isaac64) void {
|
|
const midpoint = self.r.len / 2;
|
|
|
|
self.c +%= 1;
|
|
self.b +%= self.c;
|
|
|
|
{
|
|
var i: usize = 0;
|
|
while (i < midpoint) : (i += 4) {
|
|
self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
|
|
self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
|
|
self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
|
|
self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
|
|
}
|
|
}
|
|
|
|
{
|
|
var i: usize = 0;
|
|
while (i < midpoint) : (i += 4) {
|
|
self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
|
|
self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
|
|
self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
|
|
self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
|
|
}
|
|
}
|
|
|
|
self.i = 0;
|
|
}
|
|
|
|
fn next(self: *Isaac64) u64 {
|
|
if (self.i >= self.r.len) {
|
|
self.refill();
|
|
}
|
|
|
|
const value = self.r[self.i];
|
|
self.i += 1;
|
|
return value;
|
|
}
|
|
|
|
fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
|
|
// We ignore the multi-pass requirement since we don't currently expose full access to
|
|
// seeding the self.m array completely.
|
|
@memset(self.m[0..], 0);
|
|
self.m[0] = init_s;
|
|
|
|
// prescrambled golden ratio constants
|
|
var a = [_]u64{
|
|
0x647c4677a2884b7c,
|
|
0xb9f8b322c73ac862,
|
|
0x8c0ea5053d4712a0,
|
|
0xb29b2e824a595524,
|
|
0x82f053db8355e0ce,
|
|
0x48fe4a0fa5a09315,
|
|
0xae985bf2cbfc89ed,
|
|
0x98f5704f6c44c0ab,
|
|
};
|
|
|
|
comptime var i: usize = 0;
|
|
inline while (i < rounds) : (i += 1) {
|
|
var j: usize = 0;
|
|
while (j < self.m.len) : (j += 8) {
|
|
comptime var x1: usize = 0;
|
|
inline while (x1 < 8) : (x1 += 1) {
|
|
a[x1] +%= self.m[j + x1];
|
|
}
|
|
|
|
a[0] -%= a[4];
|
|
a[5] ^= a[7] >> 9;
|
|
a[7] +%= a[0];
|
|
a[1] -%= a[5];
|
|
a[6] ^= a[0] << 9;
|
|
a[0] +%= a[1];
|
|
a[2] -%= a[6];
|
|
a[7] ^= a[1] >> 23;
|
|
a[1] +%= a[2];
|
|
a[3] -%= a[7];
|
|
a[0] ^= a[2] << 15;
|
|
a[2] +%= a[3];
|
|
a[4] -%= a[0];
|
|
a[1] ^= a[3] >> 14;
|
|
a[3] +%= a[4];
|
|
a[5] -%= a[1];
|
|
a[2] ^= a[4] << 20;
|
|
a[4] +%= a[5];
|
|
a[6] -%= a[2];
|
|
a[3] ^= a[5] >> 17;
|
|
a[5] +%= a[6];
|
|
a[7] -%= a[3];
|
|
a[4] ^= a[6] << 14;
|
|
a[6] +%= a[7];
|
|
|
|
comptime var x2: usize = 0;
|
|
inline while (x2 < 8) : (x2 += 1) {
|
|
self.m[j + x2] = a[x2];
|
|
}
|
|
}
|
|
}
|
|
|
|
@memset(self.r[0..], 0);
|
|
self.a = 0;
|
|
self.b = 0;
|
|
self.c = 0;
|
|
self.i = self.r.len; // trigger refill on first value
|
|
}
|
|
|
|
pub fn fill(self: *Isaac64, buf: []u8) void {
|
|
var i: usize = 0;
|
|
const aligned_len = buf.len - (buf.len & 7);
|
|
|
|
// Fill complete 64-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] = @as(u8, @truncate(n));
|
|
n >>= 8;
|
|
}
|
|
}
|
|
|
|
// Fill trailing, ignoring excess (cut the stream).
|
|
if (i != buf.len) {
|
|
var n = self.next();
|
|
while (i < buf.len) : (i += 1) {
|
|
buf[i] = @as(u8, @truncate(n));
|
|
n >>= 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
test "sequence" {
|
|
var r = Isaac64.init(0);
|
|
|
|
// from reference implementation
|
|
const seq = [_]u64{
|
|
0xf67dfba498e4937c,
|
|
0x84a5066a9204f380,
|
|
0xfee34bd5f5514dbb,
|
|
0x4d1664739b8f80d6,
|
|
0x8607459ab52a14aa,
|
|
0x0e78bc5a98529e49,
|
|
0xfe5332822ad13777,
|
|
0x556c27525e33d01a,
|
|
0x08643ca615f3149f,
|
|
0xd0771faf3cb04714,
|
|
0x30e86f68a37b008d,
|
|
0x3074ebc0488a3adf,
|
|
0x270645ea7a2790bc,
|
|
0x5601a0a8d3763c6a,
|
|
0x2f83071f53f325dd,
|
|
0xb9090f3d42d2d2ea,
|
|
};
|
|
|
|
for (seq) |s| {
|
|
try std.testing.expect(s == r.next());
|
|
}
|
|
}
|
|
|
|
test fill {
|
|
var r = Isaac64.init(0);
|
|
|
|
// from reference implementation
|
|
const seq = [_]u64{
|
|
0xf67dfba498e4937c,
|
|
0x84a5066a9204f380,
|
|
0xfee34bd5f5514dbb,
|
|
0x4d1664739b8f80d6,
|
|
0x8607459ab52a14aa,
|
|
0x0e78bc5a98529e49,
|
|
0xfe5332822ad13777,
|
|
0x556c27525e33d01a,
|
|
0x08643ca615f3149f,
|
|
0xd0771faf3cb04714,
|
|
0x30e86f68a37b008d,
|
|
0x3074ebc0488a3adf,
|
|
0x270645ea7a2790bc,
|
|
0x5601a0a8d3763c6a,
|
|
0x2f83071f53f325dd,
|
|
0xb9090f3d42d2d2ea,
|
|
};
|
|
|
|
for (seq) |s| {
|
|
var buf0: [8]u8 = undefined;
|
|
var buf1: [7]u8 = undefined;
|
|
std.mem.writeInt(u64, &buf0, s, .little);
|
|
r.fill(&buf1);
|
|
try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
|
|
}
|
|
}
|