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}