| 1 | //! PCG32 - http://www.pcg-random.org/ |
| 2 | //! |
| 3 | //! PRNG |
| 4 | |
| 5 | const std = @import("std"); |
| 6 | const Pcg = @This(); |
| 7 | |
| 8 | const default_multiplier = 6364136223846793005; |
| 9 | |
| 10 | s: u64, |
| 11 | i: u64, |
| 12 | |
| 13 | pub fn init(init_s: u64) Pcg { |
| 14 | var pcg = Pcg{ |
| 15 | .s = undefined, |
| 16 | .i = undefined, |
| 17 | }; |
| 18 | |
| 19 | pcg.seed(init_s); |
| 20 | return pcg; |
| 21 | } |
| 22 | |
| 23 | pub fn random(self: *Pcg) std.Random { |
| 24 | return std.Random.init(self, fill); |
| 25 | } |
| 26 | |
| 27 | fn next(self: *Pcg) u32 { |
| 28 | const l = self.s; |
| 29 | self.s = l *% default_multiplier +% (self.i | 1); |
| 30 | |
| 31 | const xor_s: u32 = @truncate(((l >> 18) ^ l) >> 27); |
| 32 | const rot: u32 = @intCast(l >> 59); |
| 33 | |
| 34 | return (xor_s >> @as(u5, @intCast(rot))) | (xor_s << @as(u5, @intCast((0 -% rot) & 31))); |
| 35 | } |
| 36 | |
| 37 | fn seed(self: *Pcg, init_s: u64) void { |
| 38 | // Pcg requires 128-bits of seed. |
| 39 | var gen = std.Random.SplitMix64.init(init_s); |
| 40 | self.seedTwo(gen.next(), gen.next()); |
| 41 | } |
| 42 | |
| 43 | fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { |
| 44 | self.s = 0; |
| 45 | self.i = (init_s << 1) | 1; |
| 46 | self.s = self.s *% default_multiplier +% self.i; |
| 47 | self.s +%= init_i; |
| 48 | self.s = self.s *% default_multiplier +% self.i; |
| 49 | } |
| 50 | |
| 51 | pub fn fill(self: *Pcg, buf: []u8) void { |
| 52 | var i: usize = 0; |
| 53 | const aligned_len = buf.len - (buf.len & 3); |
| 54 | |
| 55 | // Complete 4 byte segments. |
| 56 | while (i < aligned_len) : (i += 4) { |
| 57 | var n = self.next(); |
| 58 | comptime var j: usize = 0; |
| 59 | inline while (j < 4) : (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 | |
| 75 | test "sequence" { |
| 76 | var r = Pcg.init(0); |
| 77 | const s0: u64 = 0x9394bf54ce5d79de; |
| 78 | const s1: u64 = 0x84e9c579ef59bbf7; |
| 79 | r.seedTwo(s0, s1); |
| 80 | |
| 81 | const seq = [_]u32{ |
| 82 | 2881561918, |
| 83 | 3063928540, |
| 84 | 1199791034, |
| 85 | 2487695858, |
| 86 | 1479648952, |
| 87 | 3247963454, |
| 88 | }; |
| 89 | |
| 90 | for (seq) |s| { |
| 91 | try std.testing.expect(s == r.next()); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | test fill { |
| 96 | var r = Pcg.init(0); |
| 97 | const s0: u64 = 0x9394bf54ce5d79de; |
| 98 | const s1: u64 = 0x84e9c579ef59bbf7; |
| 99 | r.seedTwo(s0, s1); |
| 100 | |
| 101 | const seq = [_]u32{ |
| 102 | 2881561918, |
| 103 | 3063928540, |
| 104 | 1199791034, |
| 105 | 2487695858, |
| 106 | 1479648952, |
| 107 | 3247963454, |
| 108 | }; |
| 109 | |
| 110 | var i: u32 = 0; |
| 111 | while (i < seq.len) : (i += 2) { |
| 112 | var buf0: [8]u8 = undefined; |
| 113 | std.mem.writeInt(u32, buf0[0..4], seq[i], .little); |
| 114 | std.mem.writeInt(u32, buf0[4..8], seq[i + 1], .little); |
| 115 | |
| 116 | var buf1: [7]u8 = undefined; |
| 117 | r.fill(&buf1); |
| 118 | |
| 119 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); |
| 120 | } |
| 121 | } |