master
  1//! Xoroshiro128+ - http://xoroshiro.di.unimi.it/
  2//!
  3//! PRNG
  4
  5const std = @import("std");
  6const math = std.math;
  7const Xoroshiro128 = @This();
  8
  9s: [2]u64,
 10
 11pub fn init(init_s: u64) Xoroshiro128 {
 12    var x = Xoroshiro128{ .s = undefined };
 13
 14    x.seed(init_s);
 15    return x;
 16}
 17
 18pub fn random(self: *Xoroshiro128) std.Random {
 19    return std.Random.init(self, fill);
 20}
 21
 22pub fn next(self: *Xoroshiro128) u64 {
 23    const s0 = self.s[0];
 24    var s1 = self.s[1];
 25    const r = s0 +% s1;
 26
 27    s1 ^= s0;
 28    self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
 29    self.s[1] = math.rotl(u64, s1, @as(u8, 36));
 30
 31    return r;
 32}
 33
 34// Skip 2^64 places ahead in the sequence
 35pub fn jump(self: *Xoroshiro128) void {
 36    var s0: u64 = 0;
 37    var s1: u64 = 0;
 38
 39    const table = [_]u64{
 40        0xbeac0467eba5facb,
 41        0xd86b048b86aa9922,
 42    };
 43
 44    inline for (table) |entry| {
 45        var b: usize = 0;
 46        while (b < 64) : (b += 1) {
 47            if ((entry & (@as(u64, 1) << @as(u6, @intCast(b)))) != 0) {
 48                s0 ^= self.s[0];
 49                s1 ^= self.s[1];
 50            }
 51            _ = self.next();
 52        }
 53    }
 54
 55    self.s[0] = s0;
 56    self.s[1] = s1;
 57}
 58
 59pub fn seed(self: *Xoroshiro128, init_s: u64) void {
 60    // Xoroshiro requires 128-bits of seed.
 61    var gen = std.Random.SplitMix64.init(init_s);
 62
 63    self.s[0] = gen.next();
 64    self.s[1] = gen.next();
 65}
 66
 67pub fn fill(self: *Xoroshiro128, 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    var r = Xoroshiro128.init(0);
 93    r.s[0] = 0xaeecf86f7878dd75;
 94    r.s[1] = 0x01cd153642e72622;
 95
 96    const seq1 = [_]u64{
 97        0xb0ba0da5bb600397,
 98        0x18a08afde614dccc,
 99        0xa2635b956a31b929,
100        0xabe633c971efa045,
101        0x9ac19f9706ca3cac,
102        0xf62b426578c1e3fb,
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        0x95344a13556d3e22,
113        0xb4fb32dafa4d00df,
114        0xb2011d9ccdcfe2dd,
115        0x05679a9b2119b908,
116        0xa860a1da7c9cd8a0,
117        0x658a96efe3f86550,
118    };
119
120    for (seq2) |s| {
121        try std.testing.expect(s == r.next());
122    }
123}
124
125test fill {
126    var r = Xoroshiro128.init(0);
127    r.s[0] = 0xaeecf86f7878dd75;
128    r.s[1] = 0x01cd153642e72622;
129
130    const seq = [_]u64{
131        0xb0ba0da5bb600397,
132        0x18a08afde614dccc,
133        0xa2635b956a31b929,
134        0xabe633c971efa045,
135        0x9ac19f9706ca3cac,
136        0xf62b426578c1e3fb,
137    };
138
139    for (seq) |s| {
140        var buf0: [8]u8 = undefined;
141        var buf1: [7]u8 = undefined;
142        std.mem.writeInt(u64, &buf0, s, .little);
143        r.fill(&buf1);
144        try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
145    }
146}