| ... | ... | @@ -18,6 +18,7 @@ const std = @import("std.zig"); |
| 18 | 18 | const builtin = @import("builtin"); |
| 19 | 19 | const assert = std.debug.assert; |
| 20 | 20 | const expect = std.testing.expect; |
| 21 | const expectEqual = std.testing.expectEqual; |
| 21 | 22 | const mem = std.mem; |
| 22 | 23 | const math = std.math; |
| 23 | 24 | const ziggurat = @import("rand/ziggurat.zig"); |
| ... | ... | @@ -935,6 +936,105 @@ test "isaac64 sequence" { |
| 935 | 936 | } |
| 936 | 937 | } |
| 937 | 938 | |
| 939 | /// Sfc64 pseudo-random number generator from Practically Random. |
| 940 | /// Fastest engine of pracrand and smallest footprint. |
| 941 | /// See http://pracrand.sourceforge.net/ |
| 942 | pub const Sfc64 = struct { |
| 943 | random: Random, |
| 944 | |
| 945 | a: u64 = undefined, |
| 946 | b: u64 = undefined, |
| 947 | c: u64 = undefined, |
| 948 | counter: u64 = undefined, |
| 949 | |
| 950 | const Rotation = 24; |
| 951 | const RightShift = 11; |
| 952 | const LeftShift = 3; |
| 953 | |
| 954 | pub fn init(init_s: u64) Sfc64 { |
| 955 | var x = Sfc64{ |
| 956 | .random = Random{ .fillFn = fill }, |
| 957 | }; |
| 958 | |
| 959 | x.seed(init_s); |
| 960 | return x; |
| 961 | } |
| 962 | |
| 963 | fn next(self: *Sfc64) u64 { |
| 964 | const tmp = self.a +% self.b +% self.counter; |
| 965 | self.counter += 1; |
| 966 | self.a = self.b ^ (self.b >> RightShift); |
| 967 | self.b = self.c +% (self.c << LeftShift); |
| 968 | self.c = math.rotl(u64, self.c, Rotation) +% tmp; |
| 969 | return tmp; |
| 970 | } |
| 971 | |
| 972 | fn seed(self: *Sfc64, init_s: u64) void { |
| 973 | self.a = init_s; |
| 974 | self.b = init_s; |
| 975 | self.c = init_s; |
| 976 | self.counter = 1; |
| 977 | var i: u32 = 0; |
| 978 | while (i < 12) : (i += 1) { |
| 979 | _ = self.next(); |
| 980 | } |
| 981 | } |
| 982 | |
| 983 | fn fill(r: *Random, buf: []u8) void { |
| 984 | const self = @fieldParentPtr(Sfc64, "random", r); |
| 985 | |
| 986 | var i: usize = 0; |
| 987 | const aligned_len = buf.len - (buf.len & 7); |
| 988 | |
| 989 | // Complete 8 byte segments. |
| 990 | while (i < aligned_len) : (i += 8) { |
| 991 | var n = self.next(); |
| 992 | comptime var j: usize = 0; |
| 993 | inline while (j < 8) : (j += 1) { |
| 994 | buf[i + j] = @truncate(u8, n); |
| 995 | n >>= 8; |
| 996 | } |
| 997 | } |
| 998 | |
| 999 | // Remaining. (cuts the stream) |
| 1000 | if (i != buf.len) { |
| 1001 | var n = self.next(); |
| 1002 | while (i < buf.len) : (i += 1) { |
| 1003 | buf[i] = @truncate(u8, n); |
| 1004 | n >>= 8; |
| 1005 | } |
| 1006 | } |
| 1007 | } |
| 1008 | }; |
| 1009 | |
| 1010 | test "Sfc64 sequence" { |
| 1011 | // Unfortunately there does not seem to be an official test sequence. |
| 1012 | var r = Sfc64.init(0); |
| 1013 | |
| 1014 | const seq = []const u64{ |
| 1015 | 0x3acfa029e3cc6041, |
| 1016 | 0xf5b6515bf2ee419c, |
| 1017 | 0x1259635894a29b61, |
| 1018 | 0xb6ae75395f8ebd6, |
| 1019 | 0x225622285ce302e2, |
| 1020 | 0x520d28611395cb21, |
| 1021 | 0xdb909c818901599d, |
| 1022 | 0x8ffd195365216f57, |
| 1023 | 0xe8c4ad5e258ac04a, |
| 1024 | 0x8f8ef2c89fdb63ca, |
| 1025 | 0xf9865b01d98d8e2f, |
| 1026 | 0x46555871a65d08ba, |
| 1027 | 0x66868677c6298fcd, |
| 1028 | 0x2ce15a7e6329f57d, |
| 1029 | 0xb2f1833ca91ca79, |
| 1030 | 0x4b0890ac9bf453ca, |
| 1031 | }; |
| 1032 | |
| 1033 | for (seq) |s| { |
| 1034 | expectEqual(s, r.next()); |
| 1035 | } |
| 1036 | } |
| 1037 | |
| 938 | 1038 | // Actual Random helper function tests, pcg engine is assumed correct. |
| 939 | 1039 | test "Random float" { |
| 940 | 1040 | var prng = DefaultPrng.init(0); |