master
  1//! Xoshiro256++ - http://xoroshiro.di.unimi.it/
  2//!
  3//! PRNG
  4
  5const std = @import("std");
  6const math = std.math;
  7const Xoshiro256 = @This();
  8
  9s: [4]u64,
 10
 11pub fn init(init_s: u64) Xoshiro256 {
 12    var x = Xoshiro256{
 13        .s = undefined,
 14    };
 15
 16    x.seed(init_s);
 17    return x;
 18}
 19
 20pub fn random(self: *Xoshiro256) std.Random {
 21    return std.Random.init(self, fill);
 22}
 23
 24pub fn next(self: *Xoshiro256) u64 {
 25    const r = math.rotl(u64, self.s[0] +% self.s[3], 23) +% self.s[0];
 26
 27    const t = self.s[1] << 17;
 28
 29    self.s[2] ^= self.s[0];
 30    self.s[3] ^= self.s[1];
 31    self.s[1] ^= self.s[2];
 32    self.s[0] ^= self.s[3];
 33
 34    self.s[2] ^= t;
 35
 36    self.s[3] = math.rotl(u64, self.s[3], 45);
 37
 38    return r;
 39}
 40
 41// Skip 2^128 places ahead in the sequence
 42pub fn jump(self: *Xoshiro256) void {
 43    var s: u256 = 0;
 44
 45    var table: u256 = 0x39abdc4529b1661ca9582618e03fc9aad5a61266f0c9392c180ec6d33cfd0aba;
 46
 47    while (table != 0) : (table >>= 1) {
 48        if (@as(u1, @truncate(table)) != 0) {
 49            s ^= @as(u256, @bitCast(self.s));
 50        }
 51        _ = self.next();
 52    }
 53
 54    self.s = @as([4]u64, @bitCast(s));
 55}
 56
 57pub fn seed(self: *Xoshiro256, init_s: u64) void {
 58    // Xoshiro requires 256-bits of seed.
 59    var gen = std.Random.SplitMix64.init(init_s);
 60
 61    self.s[0] = gen.next();
 62    self.s[1] = gen.next();
 63    self.s[2] = gen.next();
 64    self.s[3] = gen.next();
 65}
 66
 67pub fn fill(self: *Xoshiro256, buf: []u8) void {
 68    var i: usize = 0;
 69    const aligned_len = buf.len - (buf.len & 7);
 70
 71    // Complete 8 byte segments.
 72    while (i < aligned_len) : (i += 8) {
 73        var n = self.next();
 74        comptime var j: usize = 0;
 75        inline while (j < 8) : (j += 1) {
 76            buf[i + j] = @as(u8, @truncate(n));
 77            n >>= 8;
 78        }
 79    }
 80
 81    // Remaining. (cuts the stream)
 82    if (i != buf.len) {
 83        var n = self.next();
 84        while (i < buf.len) : (i += 1) {
 85            buf[i] = @as(u8, @truncate(n));
 86            n >>= 8;
 87        }
 88    }
 89}
 90
 91test "sequence" {
 92    if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest;
 93
 94    var r = Xoshiro256.init(0);
 95
 96    const seq1 = [_]u64{
 97        0x53175d61490b23df,
 98        0x61da6f3dc380d507,
 99        0x5c0fdf91ec9a7bfc,
100        0x02eebf8c3bbe5e1a,
101        0x7eca04ebaf4a5eea,
102        0x0543c37757f08d9a,
103    };
104
105    for (seq1) |s| {
106        try std.testing.expect(s == r.next());
107    }
108
109    r.jump();
110
111    const seq2 = [_]u64{
112        0xae1db5c5e27807be,
113        0xb584c6a7fd8709fe,
114        0xc46a0ee9330fb6e,
115        0xdc0c9606f49ed76e,
116        0x1f5bb6540f6651fb,
117        0x72fa2ca734601488,
118    };
119
120    for (seq2) |s| {
121        try std.testing.expect(s == r.next());
122    }
123}
124
125test fill {
126    var r = Xoshiro256.init(0);
127
128    const seq = [_]u64{
129        0x53175d61490b23df,
130        0x61da6f3dc380d507,
131        0x5c0fdf91ec9a7bfc,
132        0x02eebf8c3bbe5e1a,
133        0x7eca04ebaf4a5eea,
134        0x0543c37757f08d9a,
135    };
136
137    for (seq) |s| {
138        var buf0: [8]u8 = undefined;
139        var buf1: [7]u8 = undefined;
140        std.mem.writeInt(u64, &buf0, s, .little);
141        r.fill(&buf1);
142        try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
143    }
144}