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}