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}