diff options
| author | e4m2 <git@e4m2.com> | 2024-02-08 14:43:20 +0100 |
|---|---|---|
| committer | e4m2 <git@e4m2.com> | 2024-02-08 14:43:20 +0100 |
| commit | 9af077d71eaf2c004d63405fc5c017e237cb9c6c (patch) | |
| tree | e9a82339e59f49ae2b6fc57c893b3e16afaed9aa /lib/std/Random/Pcg.zig | |
| parent | 919a3bae1c5f2024b09e127a15c752d9dc0aa9a6 (diff) | |
| download | zig-9af077d71eaf2c004d63405fc5c017e237cb9c6c.tar.gz zig-9af077d71eaf2c004d63405fc5c017e237cb9c6c.zip | |
std.rand: Move to std.Random
Diffstat (limited to 'lib/std/Random/Pcg.zig')
| -rw-r--r-- | lib/std/Random/Pcg.zig | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/lib/std/Random/Pcg.zig b/lib/std/Random/Pcg.zig new file mode 100644 index 0000000000..d7d233659f --- /dev/null +++ b/lib/std/Random/Pcg.zig @@ -0,0 +1,122 @@ +//! PCG32 - http://www.pcg-random.org/ +//! +//! PRNG + +const std = @import("std"); +const Random = std.rand.Random; +const Pcg = @This(); + +const default_multiplier = 6364136223846793005; + +s: u64, +i: u64, + +pub fn init(init_s: u64) Pcg { + var pcg = Pcg{ + .s = undefined, + .i = undefined, + }; + + pcg.seed(init_s); + return pcg; +} + +pub fn random(self: *Pcg) Random { + return Random.init(self, fill); +} + +fn next(self: *Pcg) u32 { + const l = self.s; + self.s = l *% default_multiplier +% (self.i | 1); + + const xor_s: u32 = @truncate(((l >> 18) ^ l) >> 27); + const rot: u32 = @intCast(l >> 59); + + return (xor_s >> @as(u5, @intCast(rot))) | (xor_s << @as(u5, @intCast((0 -% rot) & 31))); +} + +fn seed(self: *Pcg, init_s: u64) void { + // Pcg requires 128-bits of seed. + var gen = std.rand.SplitMix64.init(init_s); + self.seedTwo(gen.next(), gen.next()); +} + +fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { + self.s = 0; + self.i = (init_s << 1) | 1; + self.s = self.s *% default_multiplier +% self.i; + self.s +%= init_i; + self.s = self.s *% default_multiplier +% self.i; +} + +pub fn fill(self: *Pcg, buf: []u8) void { + var i: usize = 0; + const aligned_len = buf.len - (buf.len & 3); + + // Complete 4 byte segments. + while (i < aligned_len) : (i += 4) { + var n = self.next(); + comptime var j: usize = 0; + inline while (j < 4) : (j += 1) { + buf[i + j] = @as(u8, @truncate(n)); + n >>= 8; + } + } + + // Remaining. (cuts the stream) + if (i != buf.len) { + var n = self.next(); + while (i < buf.len) : (i += 1) { + buf[i] = @as(u8, @truncate(n)); + n >>= 8; + } + } +} + +test "pcg sequence" { + var r = Pcg.init(0); + const s0: u64 = 0x9394bf54ce5d79de; + const s1: u64 = 0x84e9c579ef59bbf7; + r.seedTwo(s0, s1); + + const seq = [_]u32{ + 2881561918, + 3063928540, + 1199791034, + 2487695858, + 1479648952, + 3247963454, + }; + + for (seq) |s| { + try std.testing.expect(s == r.next()); + } +} + +test "pcg fill" { + var r = Pcg.init(0); + const s0: u64 = 0x9394bf54ce5d79de; + const s1: u64 = 0x84e9c579ef59bbf7; + r.seedTwo(s0, s1); + + const seq = [_]u32{ + 2881561918, + 3063928540, + 1199791034, + 2487695858, + 1479648952, + 3247963454, + }; + + var i: u32 = 0; + while (i < seq.len) : (i += 2) { + var buf0: [8]u8 = undefined; + std.mem.writeInt(u32, buf0[0..4], seq[i], .little); + std.mem.writeInt(u32, buf0[4..8], seq[i + 1], .little); + + var buf1: [7]u8 = undefined; + r.fill(&buf1); + + try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); + } +} |
