| ... | @@ -18,6 +18,7 @@ const std = @import("std.zig"); | ... | @@ -18,6 +18,7 @@ const std = @import("std.zig"); |
| 18 | const builtin = @import("builtin"); | 18 | const builtin = @import("builtin"); |
| 19 | const assert = std.debug.assert; | 19 | const assert = std.debug.assert; |
| 20 | const expect = std.testing.expect; | 20 | const expect = std.testing.expect; |
| | 21 | const expectEqual = std.testing.expectEqual; |
| 21 | const mem = std.mem; | 22 | const mem = std.mem; |
| 22 | const math = std.math; | 23 | const math = std.math; |
| 23 | const ziggurat = @import("rand/ziggurat.zig"); | 24 | const ziggurat = @import("rand/ziggurat.zig"); |
| ... | @@ -935,6 +936,105 @@ test "isaac64 sequence" { | ... | @@ -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 | // Actual Random helper function tests, pcg engine is assumed correct. | 1038 | // Actual Random helper function tests, pcg engine is assumed correct. |
| 939 | test "Random float" { | 1039 | test "Random float" { |
| 940 | var prng = DefaultPrng.init(0); | 1040 | var prng = DefaultPrng.init(0); |