| 1 | //! Sfc64 pseudo-random number generator from Practically Random. |
| 2 | //! Fastest engine of pracrand and smallest footprint. |
| 3 | //! See http://pracrand.sourceforge.net/ |
| 4 | |
| 5 | const std = @import("std"); |
| 6 | const math = std.math; |
| 7 | const Sfc64 = @This(); |
| 8 | |
| 9 | a: u64 = undefined, |
| 10 | b: u64 = undefined, |
| 11 | c: u64 = undefined, |
| 12 | counter: u64 = undefined, |
| 13 | |
| 14 | const Rotation = 24; |
| 15 | const RightShift = 11; |
| 16 | const LeftShift = 3; |
| 17 | |
| 18 | pub fn init(init_s: u64) Sfc64 { |
| 19 | var x = Sfc64{}; |
| 20 | |
| 21 | x.seed(init_s); |
| 22 | return x; |
| 23 | } |
| 24 | |
| 25 | pub fn random(self: *Sfc64) std.Random { |
| 26 | return std.Random.init(self, fill); |
| 27 | } |
| 28 | |
| 29 | fn 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 | |
| 38 | fn 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 | |
| 49 | pub 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 | |
| 73 | test "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 | |
| 101 | test 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 | } |