master
  1//! Sfc64 pseudo-random number generator from Practically Random.
  2//! Fastest engine of pracrand and smallest footprint.
  3//! See http://pracrand.sourceforge.net/
  4
  5const std = @import("std");
  6const math = std.math;
  7const Sfc64 = @This();
  8
  9a: u64 = undefined,
 10b: u64 = undefined,
 11c: u64 = undefined,
 12counter: u64 = undefined,
 13
 14const Rotation = 24;
 15const RightShift = 11;
 16const LeftShift = 3;
 17
 18pub fn init(init_s: u64) Sfc64 {
 19    var x = Sfc64{};
 20
 21    x.seed(init_s);
 22    return x;
 23}
 24
 25pub fn random(self: *Sfc64) std.Random {
 26    return std.Random.init(self, fill);
 27}
 28
 29fn next(self: *Sfc64) u64 {
 30    const tmp = self.a +% self.b +% self.counter;
 31    self.counter += 1;
 32    self.a = self.b ^ (self.b >> RightShift);
 33    self.b = self.c +% (self.c << LeftShift);
 34    self.c = math.rotl(u64, self.c, Rotation) +% tmp;
 35    return tmp;
 36}
 37
 38fn seed(self: *Sfc64, init_s: u64) void {
 39    self.a = init_s;
 40    self.b = init_s;
 41    self.c = init_s;
 42    self.counter = 1;
 43    var i: u32 = 0;
 44    while (i < 12) : (i += 1) {
 45        _ = self.next();
 46    }
 47}
 48
 49pub fn fill(self: *Sfc64, buf: []u8) void {
 50    var i: usize = 0;
 51    const aligned_len = buf.len - (buf.len & 7);
 52
 53    // Complete 8 byte segments.
 54    while (i < aligned_len) : (i += 8) {
 55        var n = self.next();
 56        comptime var j: usize = 0;
 57        inline while (j < 8) : (j += 1) {
 58            buf[i + j] = @as(u8, @truncate(n));
 59            n >>= 8;
 60        }
 61    }
 62
 63    // Remaining. (cuts the stream)
 64    if (i != buf.len) {
 65        var n = self.next();
 66        while (i < buf.len) : (i += 1) {
 67            buf[i] = @as(u8, @truncate(n));
 68            n >>= 8;
 69        }
 70    }
 71}
 72
 73test "sequence" {
 74    // Unfortunately there does not seem to be an official test sequence.
 75    var r = Sfc64.init(0);
 76
 77    const seq = [_]u64{
 78        0x3acfa029e3cc6041,
 79        0xf5b6515bf2ee419c,
 80        0x1259635894a29b61,
 81        0xb6ae75395f8ebd6,
 82        0x225622285ce302e2,
 83        0x520d28611395cb21,
 84        0xdb909c818901599d,
 85        0x8ffd195365216f57,
 86        0xe8c4ad5e258ac04a,
 87        0x8f8ef2c89fdb63ca,
 88        0xf9865b01d98d8e2f,
 89        0x46555871a65d08ba,
 90        0x66868677c6298fcd,
 91        0x2ce15a7e6329f57d,
 92        0xb2f1833ca91ca79,
 93        0x4b0890ac9bf453ca,
 94    };
 95
 96    for (seq) |s| {
 97        try std.testing.expectEqual(s, r.next());
 98    }
 99}
100
101test fill {
102    // Unfortunately there does not seem to be an official test sequence.
103    var r = Sfc64.init(0);
104
105    const seq = [_]u64{
106        0x3acfa029e3cc6041,
107        0xf5b6515bf2ee419c,
108        0x1259635894a29b61,
109        0xb6ae75395f8ebd6,
110        0x225622285ce302e2,
111        0x520d28611395cb21,
112        0xdb909c818901599d,
113        0x8ffd195365216f57,
114        0xe8c4ad5e258ac04a,
115        0x8f8ef2c89fdb63ca,
116        0xf9865b01d98d8e2f,
117        0x46555871a65d08ba,
118        0x66868677c6298fcd,
119        0x2ce15a7e6329f57d,
120        0xb2f1833ca91ca79,
121        0x4b0890ac9bf453ca,
122    };
123
124    for (seq) |s| {
125        var buf0: [8]u8 = undefined;
126        var buf1: [7]u8 = undefined;
127        std.mem.writeInt(u64, &buf0, s, .little);
128        r.fill(&buf1);
129        try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
130    }
131}