Skip to content

Commit a0775fd

Browse files
francescoalemannoominitay
authored andcommitted
Add std.rand.RomuTrio
Co-authored-by: ominitay <[email protected]>
1 parent 60f0acd commit a0775fd

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

lib/std/rand.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub const Pcg = @import("rand/Pcg.zig");
2626
pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig");
2727
pub const Xoshiro256 = @import("rand/Xoshiro256.zig");
2828
pub const Sfc64 = @import("rand/Sfc64.zig");
29+
pub const RomuTrio = @import("rand/RomuTrio.zig");
2930

3031
pub const Random = struct {
3132
ptr: *anyopaque,

lib/std/rand/RomuTrio.zig

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

0 commit comments

Comments
 (0)