master
  1// Website: romu-random.org
  2// Reference paper:   http://arxiv.org/abs/2002.11331
  3// Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes.
  4
  5const std = @import("std");
  6const math = std.math;
  7const RomuTrio = @This();
  8
  9x_state: u64,
 10y_state: u64,
 11z_state: u64, // set to nonzero seed
 12
 13pub fn init(init_s: u64) RomuTrio {
 14    var x = RomuTrio{ .x_state = undefined, .y_state = undefined, .z_state = undefined };
 15    x.seed(init_s);
 16    return x;
 17}
 18
 19pub fn random(self: *RomuTrio) std.Random {
 20    return std.Random.init(self, fill);
 21}
 22
 23fn next(self: *RomuTrio) u64 {
 24    const xp = self.x_state;
 25    const yp = self.y_state;
 26    const zp = self.z_state;
 27    self.x_state = 15241094284759029579 *% zp;
 28    self.y_state = yp -% xp;
 29    self.y_state = std.math.rotl(u64, self.y_state, 12);
 30    self.z_state = zp -% yp;
 31    self.z_state = std.math.rotl(u64, self.z_state, 44);
 32    return xp;
 33}
 34
 35pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void {
 36    const seed_buf = @as([3]u64, @bitCast(buf));
 37    self.x_state = seed_buf[0];
 38    self.y_state = seed_buf[1];
 39    self.z_state = seed_buf[2];
 40}
 41
 42pub fn seed(self: *RomuTrio, init_s: u64) void {
 43    // RomuTrio requires 192-bits of seed.
 44    var gen = std.Random.SplitMix64.init(init_s);
 45
 46    self.x_state = gen.next();
 47    self.y_state = gen.next();
 48    self.z_state = gen.next();
 49}
 50
 51pub fn fill(self: *RomuTrio, buf: []u8) void {
 52    var i: usize = 0;
 53    const aligned_len = buf.len - (buf.len & 7);
 54
 55    // Complete 8 byte segments.
 56    while (i < aligned_len) : (i += 8) {
 57        var n = self.next();
 58        comptime var j: usize = 0;
 59        inline while (j < 8) : (j += 1) {
 60            buf[i + j] = @as(u8, @truncate(n));
 61            n >>= 8;
 62        }
 63    }
 64
 65    // Remaining. (cuts the stream)
 66    if (i != buf.len) {
 67        var n = self.next();
 68        while (i < buf.len) : (i += 1) {
 69            buf[i] = @as(u8, @truncate(n));
 70            n >>= 8;
 71        }
 72    }
 73}
 74
 75test "sequence" {
 76    // Unfortunately there does not seem to be an official test sequence.
 77    var r = RomuTrio.init(0);
 78
 79    const seq = [_]u64{
 80        16294208416658607535,
 81        13964609475759908645,
 82        4703697494102998476,
 83        3425221541186733346,
 84        2285772463536419399,
 85        9454187757529463048,
 86        13695907680080547496,
 87        8328236714879408626,
 88        12323357569716880909,
 89        12375466223337721820,
 90    };
 91
 92    for (seq) |s| {
 93        try std.testing.expectEqual(s, r.next());
 94    }
 95}
 96
 97test fill {
 98    // Unfortunately there does not seem to be an official test sequence.
 99    var r = RomuTrio.init(0);
100
101    const seq = [_]u64{
102        16294208416658607535,
103        13964609475759908645,
104        4703697494102998476,
105        3425221541186733346,
106        2285772463536419399,
107        9454187757529463048,
108        13695907680080547496,
109        8328236714879408626,
110        12323357569716880909,
111        12375466223337721820,
112    };
113
114    for (seq) |s| {
115        var buf0: [8]u8 = undefined;
116        var buf1: [7]u8 = undefined;
117        std.mem.writeInt(u64, &buf0, s, .little);
118        r.fill(&buf1);
119        try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
120    }
121}
122
123test "buf seeding test" {
124    const buf0 = @as([24]u8, @bitCast([3]u64{ 16294208416658607535, 13964609475759908645, 4703697494102998476 }));
125    const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 };
126    var r = RomuTrio.init(0);
127    r.seedWithBuf(buf0);
128    try std.testing.expect(r.x_state == resulting_state.x);
129    try std.testing.expect(r.y_state == resulting_state.y);
130    try std.testing.expect(r.z_state == resulting_state.z);
131}