| author | |
| committer | |
| log | 32f30399e5cd42af2d670321979aa424803b1819 |
| tree | 97963ae96b09538a4dc21dd964b955c807666842 |
| parent | 54bbc73f8502fe073d385361ddb34a43d12eec39 |
| parent | 60639ec83d0ccb7a26a19ae64452b00141d48354 |
| signature |
std.rand: Move to std.Random50 files changed, 2422 insertions(+), 2430 deletions(-)
CMakeLists.txt+1-1| ... | ... | @@ -299,7 +299,7 @@ set(ZIG_STAGE2_SOURCES |
| 299 | 299 | "${CMAKE_SOURCE_DIR}/lib/std/Progress.zig" |
| 300 | 300 | "${CMAKE_SOURCE_DIR}/lib/std/pdb.zig" |
| 301 | 301 | "${CMAKE_SOURCE_DIR}/lib/std/process.zig" |
| 302 | "${CMAKE_SOURCE_DIR}/lib/std/rand.zig" | |
| 302 | "${CMAKE_SOURCE_DIR}/lib/std/Random.zig" | |
| 303 | 303 | "${CMAKE_SOURCE_DIR}/lib/std/sort.zig" |
| 304 | 304 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt.zig" |
| 305 | 305 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absv.zig" |
lib/build_runner.zig+2-2| ... | ... | @@ -420,7 +420,7 @@ fn runStepNames( |
| 420 | 420 | |
| 421 | 421 | const starting_steps = try arena.dupe(*Step, step_stack.keys()); |
| 422 | 422 | |
| 423 | var rng = std.rand.DefaultPrng.init(seed); | |
| 423 | var rng = std.Random.DefaultPrng.init(seed); | |
| 424 | 424 | const rand = rng.random(); |
| 425 | 425 | rand.shuffle(*Step, starting_steps); |
| 426 | 426 | |
| ... | ... | @@ -836,7 +836,7 @@ fn constructGraphAndCheckForDependencyLoop( |
| 836 | 836 | b: *std.Build, |
| 837 | 837 | s: *Step, |
| 838 | 838 | step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), |
| 839 | rand: std.rand.Random, | |
| 839 | rand: std.Random, | |
| 840 | 840 | ) !void { |
| 841 | 841 | switch (s.state) { |
| 842 | 842 | .precheck_started => { |
lib/compiler_rt/paritydi2_test.zig+1-1| ... | ... | @@ -26,7 +26,7 @@ test "paritydi2" { |
| 26 | 26 | try test__paritydi2(@bitCast(@as(u64, 0xffffffff_fffffffe))); |
| 27 | 27 | try test__paritydi2(@bitCast(@as(u64, 0xffffffff_ffffffff))); |
| 28 | 28 | |
| 29 | const RndGen = std.rand.DefaultPrng; | |
| 29 | const RndGen = std.Random.DefaultPrng; | |
| 30 | 30 | var rnd = RndGen.init(42); |
| 31 | 31 | var i: u32 = 0; |
| 32 | 32 | while (i < 10_000) : (i += 1) { |
lib/compiler_rt/paritysi2_test.zig+1-1| ... | ... | @@ -26,7 +26,7 @@ test "paritysi2" { |
| 26 | 26 | try test__paritysi2(@bitCast(@as(u32, 0xfffffffe))); |
| 27 | 27 | try test__paritysi2(@bitCast(@as(u32, 0xffffffff))); |
| 28 | 28 | |
| 29 | const RndGen = std.rand.DefaultPrng; | |
| 29 | const RndGen = std.Random.DefaultPrng; | |
| 30 | 30 | var rnd = RndGen.init(42); |
| 31 | 31 | var i: u32 = 0; |
| 32 | 32 | while (i < 10_000) : (i += 1) { |
lib/compiler_rt/parityti2_test.zig+1-1| ... | ... | @@ -26,7 +26,7 @@ test "parityti2" { |
| 26 | 26 | try test__parityti2(@bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_fffffffe))); |
| 27 | 27 | try test__parityti2(@bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_ffffffff))); |
| 28 | 28 | |
| 29 | const RndGen = std.rand.DefaultPrng; | |
| 29 | const RndGen = std.Random.DefaultPrng; | |
| 30 | 30 | var rnd = RndGen.init(42); |
| 31 | 31 | var i: u32 = 0; |
| 32 | 32 | while (i < 10_000) : (i += 1) { |
lib/compiler_rt/popcountdi2_test.zig+1-1| ... | ... | @@ -25,7 +25,7 @@ test "popcountdi2" { |
| 25 | 25 | try test__popcountdi2(@as(i64, @bitCast(@as(u64, 0xffffffff_fffffffe)))); |
| 26 | 26 | try test__popcountdi2(@as(i64, @bitCast(@as(u64, 0xffffffff_ffffffff)))); |
| 27 | 27 | |
| 28 | const RndGen = std.rand.DefaultPrng; | |
| 28 | const RndGen = std.Random.DefaultPrng; | |
| 29 | 29 | var rnd = RndGen.init(42); |
| 30 | 30 | var i: u32 = 0; |
| 31 | 31 | while (i < 10_000) : (i += 1) { |
lib/compiler_rt/popcountsi2_test.zig+1-1| ... | ... | @@ -25,7 +25,7 @@ test "popcountsi2" { |
| 25 | 25 | try test__popcountsi2(@as(i32, @bitCast(@as(u32, 0xfffffffe)))); |
| 26 | 26 | try test__popcountsi2(@as(i32, @bitCast(@as(u32, 0xffffffff)))); |
| 27 | 27 | |
| 28 | const RndGen = std.rand.DefaultPrng; | |
| 28 | const RndGen = std.Random.DefaultPrng; | |
| 29 | 29 | var rnd = RndGen.init(42); |
| 30 | 30 | var i: u32 = 0; |
| 31 | 31 | while (i < 10_000) : (i += 1) { |
lib/compiler_rt/popcountti2_test.zig+1-1| ... | ... | @@ -25,7 +25,7 @@ test "popcountti2" { |
| 25 | 25 | try test__popcountti2(@as(i128, @bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_fffffffe)))); |
| 26 | 26 | try test__popcountti2(@as(i128, @bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_ffffffff)))); |
| 27 | 27 | |
| 28 | const RndGen = std.rand.DefaultPrng; | |
| 28 | const RndGen = std.Random.DefaultPrng; | |
| 29 | 29 | var rnd = RndGen.init(42); |
| 30 | 30 | var i: u32 = 0; |
| 31 | 31 | while (i < 10_000) : (i += 1) { |
lib/compiler_rt/udivmodei4.zig+1-1| ... | ... | @@ -132,7 +132,7 @@ test "__udivei4/__umodei4" { |
| 132 | 132 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 133 | 133 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 134 | 134 | |
| 135 | const RndGen = std.rand.DefaultPrng; | |
| 135 | const RndGen = std.Random.DefaultPrng; | |
| 136 | 136 | var rnd = RndGen.init(42); |
| 137 | 137 | var i: usize = 10000; |
| 138 | 138 | while (i > 0) : (i -= 1) { |
lib/std/Random.zig created+438| ... | ... | @@ -0,0 +1,438 @@ |
| 1 | //! The engines provided here should be initialized from an external source. | |
| 2 | //! For a thread-local cryptographically secure pseudo random number generator, | |
| 3 | //! use `std.crypto.random`. | |
| 4 | //! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will | |
| 5 | //! be faster and use substantially less stack space. | |
| 6 | ||
| 7 | const std = @import("std.zig"); | |
| 8 | const math = std.math; | |
| 9 | const mem = std.mem; | |
| 10 | const assert = std.debug.assert; | |
| 11 | const maxInt = std.math.maxInt; | |
| 12 | pub const Random = @This(); // Remove pub when `std.rand` namespace is removed. | |
| 13 | ||
| 14 | /// Fast unbiased random numbers. | |
| 15 | pub const DefaultPrng = Xoshiro256; | |
| 16 | ||
| 17 | /// Cryptographically secure random numbers. | |
| 18 | pub const DefaultCsprng = ChaCha; | |
| 19 | ||
| 20 | pub const Ascon = @import("Random/Ascon.zig"); | |
| 21 | pub const ChaCha = @import("Random/ChaCha.zig"); | |
| 22 | ||
| 23 | pub const Isaac64 = @import("Random/Isaac64.zig"); | |
| 24 | pub const Pcg = @import("Random/Pcg.zig"); | |
| 25 | pub const Xoroshiro128 = @import("Random/Xoroshiro128.zig"); | |
| 26 | pub const Xoshiro256 = @import("Random/Xoshiro256.zig"); | |
| 27 | pub const Sfc64 = @import("Random/Sfc64.zig"); | |
| 28 | pub const RomuTrio = @import("Random/RomuTrio.zig"); | |
| 29 | pub const SplitMix64 = @import("Random/SplitMix64.zig"); | |
| 30 | pub const ziggurat = @import("Random/ziggurat.zig"); | |
| 31 | ||
| 32 | ptr: *anyopaque, | |
| 33 | fillFn: *const fn (ptr: *anyopaque, buf: []u8) void, | |
| 34 | ||
| 35 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { | |
| 36 | const Ptr = @TypeOf(pointer); | |
| 37 | assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer | |
| 38 | assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer | |
| 39 | assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct | |
| 40 | const gen = struct { | |
| 41 | fn fill(ptr: *anyopaque, buf: []u8) void { | |
| 42 | const self: Ptr = @ptrCast(@alignCast(ptr)); | |
| 43 | fillFn(self, buf); | |
| 44 | } | |
| 45 | }; | |
| 46 | ||
| 47 | return .{ | |
| 48 | .ptr = pointer, | |
| 49 | .fillFn = gen.fill, | |
| 50 | }; | |
| 51 | } | |
| 52 | ||
| 53 | /// Read random bytes into the specified buffer until full. | |
| 54 | pub fn bytes(r: Random, buf: []u8) void { | |
| 55 | r.fillFn(r.ptr, buf); | |
| 56 | } | |
| 57 | ||
| 58 | pub fn boolean(r: Random) bool { | |
| 59 | return r.int(u1) != 0; | |
| 60 | } | |
| 61 | ||
| 62 | /// Returns a random value from an enum, evenly distributed. | |
| 63 | /// | |
| 64 | /// Note that this will not yield consistent results across all targets | |
| 65 | /// due to dependence on the representation of `usize` as an index. | |
| 66 | /// See `enumValueWithIndex` for further commentary. | |
| 67 | pub inline fn enumValue(r: Random, comptime EnumType: type) EnumType { | |
| 68 | return r.enumValueWithIndex(EnumType, usize); | |
| 69 | } | |
| 70 | ||
| 71 | /// Returns a random value from an enum, evenly distributed. | |
| 72 | /// | |
| 73 | /// An index into an array of all named values is generated using the | |
| 74 | /// specified `Index` type to determine the return value. | |
| 75 | /// This allows for results to be independent of `usize` representation. | |
| 76 | /// | |
| 77 | /// Prefer `enumValue` if this isn't important. | |
| 78 | /// | |
| 79 | /// See `uintLessThan`, which this function uses in most cases, | |
| 80 | /// for commentary on the runtime of this function. | |
| 81 | pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: type) EnumType { | |
| 82 | comptime assert(@typeInfo(EnumType) == .Enum); | |
| 83 | ||
| 84 | // We won't use int -> enum casting because enum elements can have | |
| 85 | // arbitrary values. Instead we'll randomly pick one of the type's values. | |
| 86 | const values = comptime std.enums.values(EnumType); | |
| 87 | comptime assert(values.len > 0); // can't return anything | |
| 88 | comptime assert(maxInt(Index) >= values.len - 1); // can't access all values | |
| 89 | comptime if (values.len == 1) return values[0]; | |
| 90 | ||
| 91 | const index = if (comptime values.len - 1 == maxInt(Index)) | |
| 92 | r.int(Index) | |
| 93 | else | |
| 94 | r.uintLessThan(Index, values.len); | |
| 95 | ||
| 96 | const MinInt = MinArrayIndex(Index); | |
| 97 | return values[@as(MinInt, @intCast(index))]; | |
| 98 | } | |
| 99 | ||
| 100 | /// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`. | |
| 101 | /// `i` is evenly distributed. | |
| 102 | pub fn int(r: Random, comptime T: type) T { | |
| 103 | const bits = @typeInfo(T).Int.bits; | |
| 104 | const UnsignedT = std.meta.Int(.unsigned, bits); | |
| 105 | const ceil_bytes = comptime std.math.divCeil(u16, bits, 8) catch unreachable; | |
| 106 | const ByteAlignedT = std.meta.Int(.unsigned, ceil_bytes * 8); | |
| 107 | ||
| 108 | var rand_bytes: [ceil_bytes]u8 = undefined; | |
| 109 | r.bytes(&rand_bytes); | |
| 110 | ||
| 111 | // use LE instead of native endian for better portability maybe? | |
| 112 | // TODO: endian portability is pointless if the underlying prng isn't endian portable. | |
| 113 | // TODO: document the endian portability of this library. | |
| 114 | const byte_aligned_result = mem.readInt(ByteAlignedT, &rand_bytes, .little); | |
| 115 | const unsigned_result: UnsignedT = @truncate(byte_aligned_result); | |
| 116 | return @bitCast(unsigned_result); | |
| 117 | } | |
| 118 | ||
| 119 | /// Constant-time implementation off `uintLessThan`. | |
| 120 | /// The results of this function may be biased. | |
| 121 | pub fn uintLessThanBiased(r: Random, comptime T: type, less_than: T) T { | |
| 122 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 123 | assert(0 < less_than); | |
| 124 | return limitRangeBiased(T, r.int(T), less_than); | |
| 125 | } | |
| 126 | ||
| 127 | /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. | |
| 128 | /// This function assumes that the underlying `fillFn` produces evenly distributed values. | |
| 129 | /// Within this assumption, the runtime of this function is exponentially distributed. | |
| 130 | /// If `fillFn` were backed by a true random generator, | |
| 131 | /// the runtime of this function would technically be unbounded. | |
| 132 | /// However, if `fillFn` is backed by any evenly distributed pseudo random number generator, | |
| 133 | /// this function is guaranteed to return. | |
| 134 | /// If you need deterministic runtime bounds, use `uintLessThanBiased`. | |
| 135 | pub fn uintLessThan(r: Random, comptime T: type, less_than: T) T { | |
| 136 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 137 | const bits = @typeInfo(T).Int.bits; | |
| 138 | assert(0 < less_than); | |
| 139 | ||
| 140 | // adapted from: | |
| 141 | // http://www.pcg-random.org/posts/bounded-rands.html | |
| 142 | // "Lemire's (with an extra tweak from me)" | |
| 143 | var x = r.int(T); | |
| 144 | var m = math.mulWide(T, x, less_than); | |
| 145 | var l: T = @truncate(m); | |
| 146 | if (l < less_than) { | |
| 147 | var t = -%less_than; | |
| 148 | ||
| 149 | if (t >= less_than) { | |
| 150 | t -= less_than; | |
| 151 | if (t >= less_than) { | |
| 152 | t %= less_than; | |
| 153 | } | |
| 154 | } | |
| 155 | while (l < t) { | |
| 156 | x = r.int(T); | |
| 157 | m = math.mulWide(T, x, less_than); | |
| 158 | l = @truncate(m); | |
| 159 | } | |
| 160 | } | |
| 161 | return @intCast(m >> bits); | |
| 162 | } | |
| 163 | ||
| 164 | /// Constant-time implementation off `uintAtMost`. | |
| 165 | /// The results of this function may be biased. | |
| 166 | pub fn uintAtMostBiased(r: Random, comptime T: type, at_most: T) T { | |
| 167 | assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 168 | if (at_most == maxInt(T)) { | |
| 169 | // have the full range | |
| 170 | return r.int(T); | |
| 171 | } | |
| 172 | return r.uintLessThanBiased(T, at_most + 1); | |
| 173 | } | |
| 174 | ||
| 175 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. | |
| 176 | /// See `uintLessThan`, which this function uses in most cases, | |
| 177 | /// for commentary on the runtime of this function. | |
| 178 | pub fn uintAtMost(r: Random, comptime T: type, at_most: T) T { | |
| 179 | assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 180 | if (at_most == maxInt(T)) { | |
| 181 | // have the full range | |
| 182 | return r.int(T); | |
| 183 | } | |
| 184 | return r.uintLessThan(T, at_most + 1); | |
| 185 | } | |
| 186 | ||
| 187 | /// Constant-time implementation off `intRangeLessThan`. | |
| 188 | /// The results of this function may be biased. | |
| 189 | pub fn intRangeLessThanBiased(r: Random, comptime T: type, at_least: T, less_than: T) T { | |
| 190 | assert(at_least < less_than); | |
| 191 | const info = @typeInfo(T).Int; | |
| 192 | if (info.signedness == .signed) { | |
| 193 | // Two's complement makes this math pretty easy. | |
| 194 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 195 | const lo: UnsignedT = @bitCast(at_least); | |
| 196 | const hi: UnsignedT = @bitCast(less_than); | |
| 197 | const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); | |
| 198 | return @bitCast(result); | |
| 199 | } else { | |
| 200 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 201 | return at_least + r.uintLessThanBiased(T, less_than - at_least); | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. | |
| 206 | /// See `uintLessThan`, which this function uses in most cases, | |
| 207 | /// for commentary on the runtime of this function. | |
| 208 | pub fn intRangeLessThan(r: Random, comptime T: type, at_least: T, less_than: T) T { | |
| 209 | assert(at_least < less_than); | |
| 210 | const info = @typeInfo(T).Int; | |
| 211 | if (info.signedness == .signed) { | |
| 212 | // Two's complement makes this math pretty easy. | |
| 213 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 214 | const lo: UnsignedT = @bitCast(at_least); | |
| 215 | const hi: UnsignedT = @bitCast(less_than); | |
| 216 | const result = lo +% r.uintLessThan(UnsignedT, hi -% lo); | |
| 217 | return @bitCast(result); | |
| 218 | } else { | |
| 219 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 220 | return at_least + r.uintLessThan(T, less_than - at_least); | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | /// Constant-time implementation off `intRangeAtMostBiased`. | |
| 225 | /// The results of this function may be biased. | |
| 226 | pub fn intRangeAtMostBiased(r: Random, comptime T: type, at_least: T, at_most: T) T { | |
| 227 | assert(at_least <= at_most); | |
| 228 | const info = @typeInfo(T).Int; | |
| 229 | if (info.signedness == .signed) { | |
| 230 | // Two's complement makes this math pretty easy. | |
| 231 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 232 | const lo: UnsignedT = @bitCast(at_least); | |
| 233 | const hi: UnsignedT = @bitCast(at_most); | |
| 234 | const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); | |
| 235 | return @bitCast(result); | |
| 236 | } else { | |
| 237 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 238 | return at_least + r.uintAtMostBiased(T, at_most - at_least); | |
| 239 | } | |
| 240 | } | |
| 241 | ||
| 242 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. | |
| 243 | /// See `uintLessThan`, which this function uses in most cases, | |
| 244 | /// for commentary on the runtime of this function. | |
| 245 | pub fn intRangeAtMost(r: Random, comptime T: type, at_least: T, at_most: T) T { | |
| 246 | assert(at_least <= at_most); | |
| 247 | const info = @typeInfo(T).Int; | |
| 248 | if (info.signedness == .signed) { | |
| 249 | // Two's complement makes this math pretty easy. | |
| 250 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 251 | const lo: UnsignedT = @bitCast(at_least); | |
| 252 | const hi: UnsignedT = @bitCast(at_most); | |
| 253 | const result = lo +% r.uintAtMost(UnsignedT, hi -% lo); | |
| 254 | return @bitCast(result); | |
| 255 | } else { | |
| 256 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 257 | return at_least + r.uintAtMost(T, at_most - at_least); | |
| 258 | } | |
| 259 | } | |
| 260 | ||
| 261 | /// Return a floating point value evenly distributed in the range [0, 1). | |
| 262 | pub fn float(r: Random, comptime T: type) T { | |
| 263 | // Generate a uniformly random value for the mantissa. | |
| 264 | // Then generate an exponentially biased random value for the exponent. | |
| 265 | // This covers every possible value in the range. | |
| 266 | switch (T) { | |
| 267 | f32 => { | |
| 268 | // Use 23 random bits for the mantissa, and the rest for the exponent. | |
| 269 | // If all 41 bits are zero, generate additional random bits, until a | |
| 270 | // set bit is found, or 126 bits have been generated. | |
| 271 | const rand = r.int(u64); | |
| 272 | var rand_lz = @clz(rand); | |
| 273 | if (rand_lz >= 41) { | |
| 274 | // TODO: when #5177 or #489 is implemented, | |
| 275 | // tell the compiler it is unlikely (1/2^41) to reach this point. | |
| 276 | // (Same for the if branch and the f64 calculations below.) | |
| 277 | rand_lz = 41 + @clz(r.int(u64)); | |
| 278 | if (rand_lz == 41 + 64) { | |
| 279 | // It is astronomically unlikely to reach this point. | |
| 280 | rand_lz += @clz(r.int(u32) | 0x7FF); | |
| 281 | } | |
| 282 | } | |
| 283 | const mantissa: u23 = @truncate(rand); | |
| 284 | const exponent = @as(u32, 126 - rand_lz) << 23; | |
| 285 | return @bitCast(exponent | mantissa); | |
| 286 | }, | |
| 287 | f64 => { | |
| 288 | // Use 52 random bits for the mantissa, and the rest for the exponent. | |
| 289 | // If all 12 bits are zero, generate additional random bits, until a | |
| 290 | // set bit is found, or 1022 bits have been generated. | |
| 291 | const rand = r.int(u64); | |
| 292 | var rand_lz: u64 = @clz(rand); | |
| 293 | if (rand_lz >= 12) { | |
| 294 | rand_lz = 12; | |
| 295 | while (true) { | |
| 296 | // It is astronomically unlikely for this loop to execute more than once. | |
| 297 | const addl_rand_lz = @clz(r.int(u64)); | |
| 298 | rand_lz += addl_rand_lz; | |
| 299 | if (addl_rand_lz != 64) { | |
| 300 | break; | |
| 301 | } | |
| 302 | if (rand_lz >= 1022) { | |
| 303 | rand_lz = 1022; | |
| 304 | break; | |
| 305 | } | |
| 306 | } | |
| 307 | } | |
| 308 | const mantissa = rand & 0xFFFFFFFFFFFFF; | |
| 309 | const exponent = (1022 - rand_lz) << 52; | |
| 310 | return @bitCast(exponent | mantissa); | |
| 311 | }, | |
| 312 | else => @compileError("unknown floating point type"), | |
| 313 | } | |
| 314 | } | |
| 315 | ||
| 316 | /// Return a floating point value normally distributed with mean = 0, stddev = 1. | |
| 317 | /// | |
| 318 | /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. | |
| 319 | pub fn floatNorm(r: Random, comptime T: type) T { | |
| 320 | const value = ziggurat.next_f64(r, ziggurat.NormDist); | |
| 321 | switch (T) { | |
| 322 | f32 => return @floatCast(value), | |
| 323 | f64 => return value, | |
| 324 | else => @compileError("unknown floating point type"), | |
| 325 | } | |
| 326 | } | |
| 327 | ||
| 328 | /// Return an exponentially distributed float with a rate parameter of 1. | |
| 329 | /// | |
| 330 | /// To use a different rate parameter, use: floatExp(...) / desiredRate. | |
| 331 | pub fn floatExp(r: Random, comptime T: type) T { | |
| 332 | const value = ziggurat.next_f64(r, ziggurat.ExpDist); | |
| 333 | switch (T) { | |
| 334 | f32 => return @floatCast(value), | |
| 335 | f64 => return value, | |
| 336 | else => @compileError("unknown floating point type"), | |
| 337 | } | |
| 338 | } | |
| 339 | ||
| 340 | /// Shuffle a slice into a random order. | |
| 341 | /// | |
| 342 | /// Note that this will not yield consistent results across all targets | |
| 343 | /// due to dependence on the representation of `usize` as an index. | |
| 344 | /// See `shuffleWithIndex` for further commentary. | |
| 345 | pub inline fn shuffle(r: Random, comptime T: type, buf: []T) void { | |
| 346 | r.shuffleWithIndex(T, buf, usize); | |
| 347 | } | |
| 348 | ||
| 349 | /// Shuffle a slice into a random order, using an index of a | |
| 350 | /// specified type to maintain distribution across targets. | |
| 351 | /// Asserts the index type can represent `buf.len`. | |
| 352 | /// | |
| 353 | /// Indexes into the slice are generated using the specified `Index` | |
| 354 | /// type, which determines distribution properties. This allows for | |
| 355 | /// results to be independent of `usize` representation. | |
| 356 | /// | |
| 357 | /// Prefer `shuffle` if this isn't important. | |
| 358 | /// | |
| 359 | /// See `intRangeLessThan`, which this function uses, | |
| 360 | /// for commentary on the runtime of this function. | |
| 361 | pub fn shuffleWithIndex(r: Random, comptime T: type, buf: []T, comptime Index: type) void { | |
| 362 | const MinInt = MinArrayIndex(Index); | |
| 363 | if (buf.len < 2) { | |
| 364 | return; | |
| 365 | } | |
| 366 | ||
| 367 | // `i <= j < max <= maxInt(MinInt)` | |
| 368 | const max: MinInt = @intCast(buf.len); | |
| 369 | var i: MinInt = 0; | |
| 370 | while (i < max - 1) : (i += 1) { | |
| 371 | const j: MinInt = @intCast(r.intRangeLessThan(Index, i, max)); | |
| 372 | mem.swap(T, &buf[i], &buf[j]); | |
| 373 | } | |
| 374 | } | |
| 375 | ||
| 376 | /// Randomly selects an index into `proportions`, where the likelihood of each | |
| 377 | /// index is weighted by that proportion. | |
| 378 | /// It is more likely for the index of the last proportion to be returned | |
| 379 | /// than the index of the first proportion in the slice, and vice versa. | |
| 380 | /// | |
| 381 | /// This is useful for selecting an item from a slice where weights are not equal. | |
| 382 | /// `T` must be a numeric type capable of holding the sum of `proportions`. | |
| 383 | pub fn weightedIndex(r: Random, comptime T: type, proportions: []const T) usize { | |
| 384 | // This implementation works by summing the proportions and picking a | |
| 385 | // random point in [0, sum). We then loop over the proportions, | |
| 386 | // accumulating until our accumulator is greater than the random point. | |
| 387 | ||
| 388 | const sum = s: { | |
| 389 | var sum: T = 0; | |
| 390 | for (proportions) |v| sum += v; | |
| 391 | break :s sum; | |
| 392 | }; | |
| 393 | ||
| 394 | const point = switch (@typeInfo(T)) { | |
| 395 | .Int => |int_info| switch (int_info.signedness) { | |
| 396 | .signed => r.intRangeLessThan(T, 0, sum), | |
| 397 | .unsigned => r.uintLessThan(T, sum), | |
| 398 | }, | |
| 399 | // take care that imprecision doesn't lead to a value slightly greater than sum | |
| 400 | .Float => @min(r.float(T) * sum, sum - std.math.floatEps(T)), | |
| 401 | else => @compileError("weightedIndex does not support proportions of type " ++ | |
| 402 | @typeName(T)), | |
| 403 | }; | |
| 404 | ||
| 405 | assert(point < sum); | |
| 406 | ||
| 407 | var accumulator: T = 0; | |
| 408 | for (proportions, 0..) |p, index| { | |
| 409 | accumulator += p; | |
| 410 | if (point < accumulator) return index; | |
| 411 | } else unreachable; | |
| 412 | } | |
| 413 | ||
| 414 | /// Convert a random integer 0 <= random_int <= maxValue(T), | |
| 415 | /// into an integer 0 <= result < less_than. | |
| 416 | /// This function introduces a minor bias. | |
| 417 | pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { | |
| 418 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 419 | const bits = @typeInfo(T).Int.bits; | |
| 420 | ||
| 421 | // adapted from: | |
| 422 | // http://www.pcg-random.org/posts/bounded-rands.html | |
| 423 | // "Integer Multiplication (Biased)" | |
| 424 | const m = math.mulWide(T, random_int, less_than); | |
| 425 | return @intCast(m >> bits); | |
| 426 | } | |
| 427 | ||
| 428 | /// Returns the smallest of `Index` and `usize`. | |
| 429 | fn MinArrayIndex(comptime Index: type) type { | |
| 430 | const index_info = @typeInfo(Index).Int; | |
| 431 | assert(index_info.signedness == .unsigned); | |
| 432 | return if (index_info.bits >= @typeInfo(usize).Int.bits) usize else Index; | |
| 433 | } | |
| 434 | ||
| 435 | test { | |
| 436 | std.testing.refAllDecls(@This()); | |
| 437 | _ = @import("Random/test.zig"); | |
| 438 | } |
lib/std/Random/Ascon.zig created+58| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | //! CSPRNG based on the Reverie construction, a permutation-based PRNG | |
| 2 | //! with forward security, instantiated with the Ascon(128,12,8) permutation. | |
| 3 | //! | |
| 4 | //! Compared to ChaCha, this PRNG has a much smaller state, and can be | |
| 5 | //! a better choice for constrained environments. | |
| 6 | //! | |
| 7 | //! References: | |
| 8 | //! - A Robust and Sponge-Like PRNG with Improved Efficiency https://eprint.iacr.org/2016/886.pdf | |
| 9 | //! - Ascon https://ascon.iaik.tugraz.at/files/asconv12-nist.pdf | |
| 10 | ||
| 11 | const std = @import("std"); | |
| 12 | const mem = std.mem; | |
| 13 | const Self = @This(); | |
| 14 | ||
| 15 | const Ascon = std.crypto.core.Ascon(.little); | |
| 16 | ||
| 17 | state: Ascon, | |
| 18 | ||
| 19 | const rate = 16; | |
| 20 | pub const secret_seed_length = 32; | |
| 21 | ||
| 22 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. | |
| 23 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | |
| 24 | var self = Self{ .state = Ascon.initXof() }; | |
| 25 | self.addEntropy(&secret_seed); | |
| 26 | return self; | |
| 27 | } | |
| 28 | ||
| 29 | /// Inserts entropy to refresh the internal state. | |
| 30 | pub fn addEntropy(self: *Self, bytes: []const u8) void { | |
| 31 | comptime std.debug.assert(secret_seed_length % rate == 0); | |
| 32 | var i: usize = 0; | |
| 33 | while (i + rate < bytes.len) : (i += rate) { | |
| 34 | self.state.addBytes(bytes[i..][0..rate]); | |
| 35 | self.state.permuteR(8); | |
| 36 | } | |
| 37 | if (i != bytes.len) self.state.addBytes(bytes[i..]); | |
| 38 | self.state.permute(); | |
| 39 | } | |
| 40 | ||
| 41 | /// Returns a `std.Random` structure backed by the current RNG. | |
| 42 | pub fn random(self: *Self) std.Random { | |
| 43 | return std.Random.init(self, fill); | |
| 44 | } | |
| 45 | ||
| 46 | /// Fills the buffer with random bytes. | |
| 47 | pub fn fill(self: *Self, buf: []u8) void { | |
| 48 | var i: usize = 0; | |
| 49 | while (true) { | |
| 50 | const left = buf.len - i; | |
| 51 | const n = @min(left, rate); | |
| 52 | self.state.extractBytes(buf[i..][0..n]); | |
| 53 | if (left == 0) break; | |
| 54 | self.state.permuteR(8); | |
| 55 | i += n; | |
| 56 | } | |
| 57 | self.state.permuteRatchet(6, rate); | |
| 58 | } |
lib/std/Random/ChaCha.zig created+97| ... | ... | @@ -0,0 +1,97 @@ |
| 1 | //! CSPRNG based on the ChaCha8 stream cipher, with forward security. | |
| 2 | //! | |
| 3 | //! References: | |
| 4 | //! - Fast-key-erasure random-number generators https://blog.cr.yp.to/20170723-random.html | |
| 5 | ||
| 6 | const std = @import("std"); | |
| 7 | const mem = std.mem; | |
| 8 | const Self = @This(); | |
| 9 | ||
| 10 | const Cipher = std.crypto.stream.chacha.ChaCha8IETF; | |
| 11 | ||
| 12 | const State = [8 * Cipher.block_length]u8; | |
| 13 | ||
| 14 | state: State, | |
| 15 | offset: usize, | |
| 16 | ||
| 17 | const nonce = [_]u8{0} ** Cipher.nonce_length; | |
| 18 | ||
| 19 | pub const secret_seed_length = Cipher.key_length; | |
| 20 | ||
| 21 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. | |
| 22 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | |
| 23 | var self = Self{ .state = undefined, .offset = 0 }; | |
| 24 | Cipher.stream(&self.state, 0, secret_seed, nonce); | |
| 25 | return self; | |
| 26 | } | |
| 27 | ||
| 28 | /// Inserts entropy to refresh the internal state. | |
| 29 | pub fn addEntropy(self: *Self, bytes: []const u8) void { | |
| 30 | var i: usize = 0; | |
| 31 | while (i + Cipher.key_length <= bytes.len) : (i += Cipher.key_length) { | |
| 32 | Cipher.xor( | |
| 33 | self.state[0..Cipher.key_length], | |
| 34 | self.state[0..Cipher.key_length], | |
| 35 | 0, | |
| 36 | bytes[i..][0..Cipher.key_length].*, | |
| 37 | nonce, | |
| 38 | ); | |
| 39 | } | |
| 40 | if (i < bytes.len) { | |
| 41 | var k = [_]u8{0} ** Cipher.key_length; | |
| 42 | const src = bytes[i..]; | |
| 43 | @memcpy(k[0..src.len], src); | |
| 44 | Cipher.xor( | |
| 45 | self.state[0..Cipher.key_length], | |
| 46 | self.state[0..Cipher.key_length], | |
| 47 | 0, | |
| 48 | k, | |
| 49 | nonce, | |
| 50 | ); | |
| 51 | } | |
| 52 | self.refill(); | |
| 53 | } | |
| 54 | ||
| 55 | /// Returns a `std.Random` structure backed by the current RNG. | |
| 56 | pub fn random(self: *Self) std.Random { | |
| 57 | return std.Random.init(self, fill); | |
| 58 | } | |
| 59 | ||
| 60 | // Refills the buffer with random bytes, overwriting the previous key. | |
| 61 | fn refill(self: *Self) void { | |
| 62 | Cipher.stream(&self.state, 0, self.state[0..Cipher.key_length].*, nonce); | |
| 63 | self.offset = 0; | |
| 64 | } | |
| 65 | ||
| 66 | /// Fills the buffer with random bytes. | |
| 67 | pub fn fill(self: *Self, buf_: []u8) void { | |
| 68 | const bytes = self.state[Cipher.key_length..]; | |
| 69 | var buf = buf_; | |
| 70 | ||
| 71 | const avail = bytes.len - self.offset; | |
| 72 | if (avail > 0) { | |
| 73 | // Bytes from the current block | |
| 74 | const n = @min(avail, buf.len); | |
| 75 | @memcpy(buf[0..n], bytes[self.offset..][0..n]); | |
| 76 | @memset(bytes[self.offset..][0..n], 0); | |
| 77 | buf = buf[n..]; | |
| 78 | self.offset += n; | |
| 79 | } | |
| 80 | if (buf.len == 0) return; | |
| 81 | ||
| 82 | self.refill(); | |
| 83 | ||
| 84 | // Full blocks | |
| 85 | while (buf.len >= bytes.len) { | |
| 86 | @memcpy(buf[0..bytes.len], bytes); | |
| 87 | buf = buf[bytes.len..]; | |
| 88 | self.refill(); | |
| 89 | } | |
| 90 | ||
| 91 | // Remaining bytes | |
| 92 | if (buf.len > 0) { | |
| 93 | @memcpy(buf, bytes[0..buf.len]); | |
| 94 | @memset(bytes[0..buf.len], 0); | |
| 95 | self.offset = buf.len; | |
| 96 | } | |
| 97 | } |
lib/std/Random/Isaac64.zig created+234| ... | ... | @@ -0,0 +1,234 @@ |
| 1 | //! ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html | |
| 2 | //! | |
| 3 | //! Follows the general idea of the implementation from here with a few shortcuts. | |
| 4 | //! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html | |
| 5 | ||
| 6 | const std = @import("std"); | |
| 7 | const mem = std.mem; | |
| 8 | const Isaac64 = @This(); | |
| 9 | ||
| 10 | r: [256]u64, | |
| 11 | m: [256]u64, | |
| 12 | a: u64, | |
| 13 | b: u64, | |
| 14 | c: u64, | |
| 15 | i: usize, | |
| 16 | ||
| 17 | pub fn init(init_s: u64) Isaac64 { | |
| 18 | var isaac = Isaac64{ | |
| 19 | .r = undefined, | |
| 20 | .m = undefined, | |
| 21 | .a = undefined, | |
| 22 | .b = undefined, | |
| 23 | .c = undefined, | |
| 24 | .i = undefined, | |
| 25 | }; | |
| 26 | ||
| 27 | // seed == 0 => same result as the unseeded reference implementation | |
| 28 | isaac.seed(init_s, 1); | |
| 29 | return isaac; | |
| 30 | } | |
| 31 | ||
| 32 | pub fn random(self: *Isaac64) std.Random { | |
| 33 | return std.Random.init(self, fill); | |
| 34 | } | |
| 35 | ||
| 36 | fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { | |
| 37 | const x = self.m[base + m1]; | |
| 38 | self.a = mix +% self.m[base + m2]; | |
| 39 | ||
| 40 | const y = self.a +% self.b +% self.m[@as(usize, @intCast((x >> 3) % self.m.len))]; | |
| 41 | self.m[base + m1] = y; | |
| 42 | ||
| 43 | self.b = x +% self.m[@as(usize, @intCast((y >> 11) % self.m.len))]; | |
| 44 | self.r[self.r.len - 1 - base - m1] = self.b; | |
| 45 | } | |
| 46 | ||
| 47 | fn refill(self: *Isaac64) void { | |
| 48 | const midpoint = self.r.len / 2; | |
| 49 | ||
| 50 | self.c +%= 1; | |
| 51 | self.b +%= self.c; | |
| 52 | ||
| 53 | { | |
| 54 | var i: usize = 0; | |
| 55 | while (i < midpoint) : (i += 4) { | |
| 56 | self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); | |
| 57 | self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); | |
| 58 | self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); | |
| 59 | self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); | |
| 60 | } | |
| 61 | } | |
| 62 | ||
| 63 | { | |
| 64 | var i: usize = 0; | |
| 65 | while (i < midpoint) : (i += 4) { | |
| 66 | self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); | |
| 67 | self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); | |
| 68 | self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); | |
| 69 | self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | self.i = 0; | |
| 74 | } | |
| 75 | ||
| 76 | fn next(self: *Isaac64) u64 { | |
| 77 | if (self.i >= self.r.len) { | |
| 78 | self.refill(); | |
| 79 | } | |
| 80 | ||
| 81 | const value = self.r[self.i]; | |
| 82 | self.i += 1; | |
| 83 | return value; | |
| 84 | } | |
| 85 | ||
| 86 | fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { | |
| 87 | // We ignore the multi-pass requirement since we don't currently expose full access to | |
| 88 | // seeding the self.m array completely. | |
| 89 | @memset(self.m[0..], 0); | |
| 90 | self.m[0] = init_s; | |
| 91 | ||
| 92 | // prescrambled golden ratio constants | |
| 93 | var a = [_]u64{ | |
| 94 | 0x647c4677a2884b7c, | |
| 95 | 0xb9f8b322c73ac862, | |
| 96 | 0x8c0ea5053d4712a0, | |
| 97 | 0xb29b2e824a595524, | |
| 98 | 0x82f053db8355e0ce, | |
| 99 | 0x48fe4a0fa5a09315, | |
| 100 | 0xae985bf2cbfc89ed, | |
| 101 | 0x98f5704f6c44c0ab, | |
| 102 | }; | |
| 103 | ||
| 104 | comptime var i: usize = 0; | |
| 105 | inline while (i < rounds) : (i += 1) { | |
| 106 | var j: usize = 0; | |
| 107 | while (j < self.m.len) : (j += 8) { | |
| 108 | comptime var x1: usize = 0; | |
| 109 | inline while (x1 < 8) : (x1 += 1) { | |
| 110 | a[x1] +%= self.m[j + x1]; | |
| 111 | } | |
| 112 | ||
| 113 | a[0] -%= a[4]; | |
| 114 | a[5] ^= a[7] >> 9; | |
| 115 | a[7] +%= a[0]; | |
| 116 | a[1] -%= a[5]; | |
| 117 | a[6] ^= a[0] << 9; | |
| 118 | a[0] +%= a[1]; | |
| 119 | a[2] -%= a[6]; | |
| 120 | a[7] ^= a[1] >> 23; | |
| 121 | a[1] +%= a[2]; | |
| 122 | a[3] -%= a[7]; | |
| 123 | a[0] ^= a[2] << 15; | |
| 124 | a[2] +%= a[3]; | |
| 125 | a[4] -%= a[0]; | |
| 126 | a[1] ^= a[3] >> 14; | |
| 127 | a[3] +%= a[4]; | |
| 128 | a[5] -%= a[1]; | |
| 129 | a[2] ^= a[4] << 20; | |
| 130 | a[4] +%= a[5]; | |
| 131 | a[6] -%= a[2]; | |
| 132 | a[3] ^= a[5] >> 17; | |
| 133 | a[5] +%= a[6]; | |
| 134 | a[7] -%= a[3]; | |
| 135 | a[4] ^= a[6] << 14; | |
| 136 | a[6] +%= a[7]; | |
| 137 | ||
| 138 | comptime var x2: usize = 0; | |
| 139 | inline while (x2 < 8) : (x2 += 1) { | |
| 140 | self.m[j + x2] = a[x2]; | |
| 141 | } | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | @memset(self.r[0..], 0); | |
| 146 | self.a = 0; | |
| 147 | self.b = 0; | |
| 148 | self.c = 0; | |
| 149 | self.i = self.r.len; // trigger refill on first value | |
| 150 | } | |
| 151 | ||
| 152 | pub fn fill(self: *Isaac64, buf: []u8) void { | |
| 153 | var i: usize = 0; | |
| 154 | const aligned_len = buf.len - (buf.len & 7); | |
| 155 | ||
| 156 | // Fill complete 64-byte segments | |
| 157 | while (i < aligned_len) : (i += 8) { | |
| 158 | var n = self.next(); | |
| 159 | comptime var j: usize = 0; | |
| 160 | inline while (j < 8) : (j += 1) { | |
| 161 | buf[i + j] = @as(u8, @truncate(n)); | |
| 162 | n >>= 8; | |
| 163 | } | |
| 164 | } | |
| 165 | ||
| 166 | // Fill trailing, ignoring excess (cut the stream). | |
| 167 | if (i != buf.len) { | |
| 168 | var n = self.next(); | |
| 169 | while (i < buf.len) : (i += 1) { | |
| 170 | buf[i] = @as(u8, @truncate(n)); | |
| 171 | n >>= 8; | |
| 172 | } | |
| 173 | } | |
| 174 | } | |
| 175 | ||
| 176 | test "isaac64 sequence" { | |
| 177 | var r = Isaac64.init(0); | |
| 178 | ||
| 179 | // from reference implementation | |
| 180 | const seq = [_]u64{ | |
| 181 | 0xf67dfba498e4937c, | |
| 182 | 0x84a5066a9204f380, | |
| 183 | 0xfee34bd5f5514dbb, | |
| 184 | 0x4d1664739b8f80d6, | |
| 185 | 0x8607459ab52a14aa, | |
| 186 | 0x0e78bc5a98529e49, | |
| 187 | 0xfe5332822ad13777, | |
| 188 | 0x556c27525e33d01a, | |
| 189 | 0x08643ca615f3149f, | |
| 190 | 0xd0771faf3cb04714, | |
| 191 | 0x30e86f68a37b008d, | |
| 192 | 0x3074ebc0488a3adf, | |
| 193 | 0x270645ea7a2790bc, | |
| 194 | 0x5601a0a8d3763c6a, | |
| 195 | 0x2f83071f53f325dd, | |
| 196 | 0xb9090f3d42d2d2ea, | |
| 197 | }; | |
| 198 | ||
| 199 | for (seq) |s| { | |
| 200 | try std.testing.expect(s == r.next()); | |
| 201 | } | |
| 202 | } | |
| 203 | ||
| 204 | test "isaac64 fill" { | |
| 205 | var r = Isaac64.init(0); | |
| 206 | ||
| 207 | // from reference implementation | |
| 208 | const seq = [_]u64{ | |
| 209 | 0xf67dfba498e4937c, | |
| 210 | 0x84a5066a9204f380, | |
| 211 | 0xfee34bd5f5514dbb, | |
| 212 | 0x4d1664739b8f80d6, | |
| 213 | 0x8607459ab52a14aa, | |
| 214 | 0x0e78bc5a98529e49, | |
| 215 | 0xfe5332822ad13777, | |
| 216 | 0x556c27525e33d01a, | |
| 217 | 0x08643ca615f3149f, | |
| 218 | 0xd0771faf3cb04714, | |
| 219 | 0x30e86f68a37b008d, | |
| 220 | 0x3074ebc0488a3adf, | |
| 221 | 0x270645ea7a2790bc, | |
| 222 | 0x5601a0a8d3763c6a, | |
| 223 | 0x2f83071f53f325dd, | |
| 224 | 0xb9090f3d42d2d2ea, | |
| 225 | }; | |
| 226 | ||
| 227 | for (seq) |s| { | |
| 228 | var buf0: [8]u8 = undefined; | |
| 229 | var buf1: [7]u8 = undefined; | |
| 230 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 231 | r.fill(&buf1); | |
| 232 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 233 | } | |
| 234 | } |
lib/std/Random/Pcg.zig created+121| ... | ... | @@ -0,0 +1,121 @@ |
| 1 | //! PCG32 - http://www.pcg-random.org/ | |
| 2 | //! | |
| 3 | //! PRNG | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const Pcg = @This(); | |
| 7 | ||
| 8 | const default_multiplier = 6364136223846793005; | |
| 9 | ||
| 10 | s: u64, | |
| 11 | i: u64, | |
| 12 | ||
| 13 | pub fn init(init_s: u64) Pcg { | |
| 14 | var pcg = Pcg{ | |
| 15 | .s = undefined, | |
| 16 | .i = undefined, | |
| 17 | }; | |
| 18 | ||
| 19 | pcg.seed(init_s); | |
| 20 | return pcg; | |
| 21 | } | |
| 22 | ||
| 23 | pub fn random(self: *Pcg) std.Random { | |
| 24 | return std.Random.init(self, fill); | |
| 25 | } | |
| 26 | ||
| 27 | fn next(self: *Pcg) u32 { | |
| 28 | const l = self.s; | |
| 29 | self.s = l *% default_multiplier +% (self.i | 1); | |
| 30 | ||
| 31 | const xor_s: u32 = @truncate(((l >> 18) ^ l) >> 27); | |
| 32 | const rot: u32 = @intCast(l >> 59); | |
| 33 | ||
| 34 | return (xor_s >> @as(u5, @intCast(rot))) | (xor_s << @as(u5, @intCast((0 -% rot) & 31))); | |
| 35 | } | |
| 36 | ||
| 37 | fn seed(self: *Pcg, init_s: u64) void { | |
| 38 | // Pcg requires 128-bits of seed. | |
| 39 | var gen = std.Random.SplitMix64.init(init_s); | |
| 40 | self.seedTwo(gen.next(), gen.next()); | |
| 41 | } | |
| 42 | ||
| 43 | fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { | |
| 44 | self.s = 0; | |
| 45 | self.i = (init_s << 1) | 1; | |
| 46 | self.s = self.s *% default_multiplier +% self.i; | |
| 47 | self.s +%= init_i; | |
| 48 | self.s = self.s *% default_multiplier +% self.i; | |
| 49 | } | |
| 50 | ||
| 51 | pub fn fill(self: *Pcg, buf: []u8) void { | |
| 52 | var i: usize = 0; | |
| 53 | const aligned_len = buf.len - (buf.len & 3); | |
| 54 | ||
| 55 | // Complete 4 byte segments. | |
| 56 | while (i < aligned_len) : (i += 4) { | |
| 57 | var n = self.next(); | |
| 58 | comptime var j: usize = 0; | |
| 59 | inline while (j < 4) : (j += 1) { | |
| 60 | buf[i + j] = @as(u8, @truncate(n)); | |
| 61 | n >>= 8; | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | // Remaining. (cuts the stream) | |
| 66 | if (i != buf.len) { | |
| 67 | var n = self.next(); | |
| 68 | while (i < buf.len) : (i += 1) { | |
| 69 | buf[i] = @as(u8, @truncate(n)); | |
| 70 | n >>= 8; | |
| 71 | } | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | test "pcg sequence" { | |
| 76 | var r = Pcg.init(0); | |
| 77 | const s0: u64 = 0x9394bf54ce5d79de; | |
| 78 | const s1: u64 = 0x84e9c579ef59bbf7; | |
| 79 | r.seedTwo(s0, s1); | |
| 80 | ||
| 81 | const seq = [_]u32{ | |
| 82 | 2881561918, | |
| 83 | 3063928540, | |
| 84 | 1199791034, | |
| 85 | 2487695858, | |
| 86 | 1479648952, | |
| 87 | 3247963454, | |
| 88 | }; | |
| 89 | ||
| 90 | for (seq) |s| { | |
| 91 | try std.testing.expect(s == r.next()); | |
| 92 | } | |
| 93 | } | |
| 94 | ||
| 95 | test "pcg fill" { | |
| 96 | var r = Pcg.init(0); | |
| 97 | const s0: u64 = 0x9394bf54ce5d79de; | |
| 98 | const s1: u64 = 0x84e9c579ef59bbf7; | |
| 99 | r.seedTwo(s0, s1); | |
| 100 | ||
| 101 | const seq = [_]u32{ | |
| 102 | 2881561918, | |
| 103 | 3063928540, | |
| 104 | 1199791034, | |
| 105 | 2487695858, | |
| 106 | 1479648952, | |
| 107 | 3247963454, | |
| 108 | }; | |
| 109 | ||
| 110 | var i: u32 = 0; | |
| 111 | while (i < seq.len) : (i += 2) { | |
| 112 | var buf0: [8]u8 = undefined; | |
| 113 | std.mem.writeInt(u32, buf0[0..4], seq[i], .little); | |
| 114 | std.mem.writeInt(u32, buf0[4..8], seq[i + 1], .little); | |
| 115 | ||
| 116 | var buf1: [7]u8 = undefined; | |
| 117 | r.fill(&buf1); | |
| 118 | ||
| 119 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 120 | } | |
| 121 | } |
lib/std/Random/RomuTrio.zig created+131| ... | ... | @@ -0,0 +1,131 @@ |
| 1 | // Website: romu-random.org | |
| 2 | // Reference paper: http://arxiv.org/abs/2002.11331 | |
| 3 | // Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes. | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const math = std.math; | |
| 7 | const RomuTrio = @This(); | |
| 8 | ||
| 9 | x_state: u64, | |
| 10 | y_state: u64, | |
| 11 | z_state: u64, // set to nonzero seed | |
| 12 | ||
| 13 | pub fn init(init_s: u64) RomuTrio { | |
| 14 | var x = RomuTrio{ .x_state = undefined, .y_state = undefined, .z_state = undefined }; | |
| 15 | x.seed(init_s); | |
| 16 | return x; | |
| 17 | } | |
| 18 | ||
| 19 | pub fn random(self: *RomuTrio) std.Random { | |
| 20 | return std.Random.init(self, fill); | |
| 21 | } | |
| 22 | ||
| 23 | fn next(self: *RomuTrio) u64 { | |
| 24 | const xp = self.x_state; | |
| 25 | const yp = self.y_state; | |
| 26 | const zp = self.z_state; | |
| 27 | self.x_state = 15241094284759029579 *% zp; | |
| 28 | self.y_state = yp -% xp; | |
| 29 | self.y_state = std.math.rotl(u64, self.y_state, 12); | |
| 30 | self.z_state = zp -% yp; | |
| 31 | self.z_state = std.math.rotl(u64, self.z_state, 44); | |
| 32 | return xp; | |
| 33 | } | |
| 34 | ||
| 35 | pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void { | |
| 36 | const seed_buf = @as([3]u64, @bitCast(buf)); | |
| 37 | self.x_state = seed_buf[0]; | |
| 38 | self.y_state = seed_buf[1]; | |
| 39 | self.z_state = seed_buf[2]; | |
| 40 | } | |
| 41 | ||
| 42 | pub fn seed(self: *RomuTrio, init_s: u64) void { | |
| 43 | // RomuTrio requires 192-bits of seed. | |
| 44 | var gen = std.Random.SplitMix64.init(init_s); | |
| 45 | ||
| 46 | self.x_state = gen.next(); | |
| 47 | self.y_state = gen.next(); | |
| 48 | self.z_state = gen.next(); | |
| 49 | } | |
| 50 | ||
| 51 | pub fn fill(self: *RomuTrio, buf: []u8) void { | |
| 52 | var i: usize = 0; | |
| 53 | const aligned_len = buf.len - (buf.len & 7); | |
| 54 | ||
| 55 | // Complete 8 byte segments. | |
| 56 | while (i < aligned_len) : (i += 8) { | |
| 57 | var n = self.next(); | |
| 58 | comptime var j: usize = 0; | |
| 59 | inline while (j < 8) : (j += 1) { | |
| 60 | buf[i + j] = @as(u8, @truncate(n)); | |
| 61 | n >>= 8; | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | // Remaining. (cuts the stream) | |
| 66 | if (i != buf.len) { | |
| 67 | var n = self.next(); | |
| 68 | while (i < buf.len) : (i += 1) { | |
| 69 | buf[i] = @as(u8, @truncate(n)); | |
| 70 | n >>= 8; | |
| 71 | } | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | test "RomuTrio sequence" { | |
| 76 | // Unfortunately there does not seem to be an official test sequence. | |
| 77 | var r = RomuTrio.init(0); | |
| 78 | ||
| 79 | const seq = [_]u64{ | |
| 80 | 16294208416658607535, | |
| 81 | 13964609475759908645, | |
| 82 | 4703697494102998476, | |
| 83 | 3425221541186733346, | |
| 84 | 2285772463536419399, | |
| 85 | 9454187757529463048, | |
| 86 | 13695907680080547496, | |
| 87 | 8328236714879408626, | |
| 88 | 12323357569716880909, | |
| 89 | 12375466223337721820, | |
| 90 | }; | |
| 91 | ||
| 92 | for (seq) |s| { | |
| 93 | try std.testing.expectEqual(s, r.next()); | |
| 94 | } | |
| 95 | } | |
| 96 | ||
| 97 | test "RomuTrio fill" { | |
| 98 | // Unfortunately there does not seem to be an official test sequence. | |
| 99 | var r = RomuTrio.init(0); | |
| 100 | ||
| 101 | const seq = [_]u64{ | |
| 102 | 16294208416658607535, | |
| 103 | 13964609475759908645, | |
| 104 | 4703697494102998476, | |
| 105 | 3425221541186733346, | |
| 106 | 2285772463536419399, | |
| 107 | 9454187757529463048, | |
| 108 | 13695907680080547496, | |
| 109 | 8328236714879408626, | |
| 110 | 12323357569716880909, | |
| 111 | 12375466223337721820, | |
| 112 | }; | |
| 113 | ||
| 114 | for (seq) |s| { | |
| 115 | var buf0: [8]u8 = undefined; | |
| 116 | var buf1: [7]u8 = undefined; | |
| 117 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 118 | r.fill(&buf1); | |
| 119 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | test "RomuTrio buf seeding test" { | |
| 124 | const buf0 = @as([24]u8, @bitCast([3]u64{ 16294208416658607535, 13964609475759908645, 4703697494102998476 })); | |
| 125 | const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 }; | |
| 126 | var r = RomuTrio.init(0); | |
| 127 | r.seedWithBuf(buf0); | |
| 128 | try std.testing.expect(r.x_state == resulting_state.x); | |
| 129 | try std.testing.expect(r.y_state == resulting_state.y); | |
| 130 | try std.testing.expect(r.z_state == resulting_state.z); | |
| 131 | } |
lib/std/Random/Sfc64.zig created+131| ... | ... | @@ -0,0 +1,131 @@ |
| 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 "Sfc64 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 "Sfc64 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 | } |
lib/std/Random/SplitMix64.zig created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //! Generator to extend 64-bit seed values into longer sequences. | |
| 2 | //! | |
| 3 | //! The number of cycles is thus limited to 64-bits regardless of the engine, but this | |
| 4 | //! is still plenty for practical purposes. | |
| 5 | ||
| 6 | const SplitMix64 = @This(); | |
| 7 | ||
| 8 | s: u64, | |
| 9 | ||
| 10 | pub fn init(seed: u64) SplitMix64 { | |
| 11 | return SplitMix64{ .s = seed }; | |
| 12 | } | |
| 13 | ||
| 14 | pub fn next(self: *SplitMix64) u64 { | |
| 15 | self.s +%= 0x9e3779b97f4a7c15; | |
| 16 | ||
| 17 | var z = self.s; | |
| 18 | z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9; | |
| 19 | z = (z ^ (z >> 27)) *% 0x94d049bb133111eb; | |
| 20 | return z ^ (z >> 31); | |
| 21 | } |
lib/std/Random/Xoroshiro128.zig created+146| ... | ... | @@ -0,0 +1,146 @@ |
| 1 | //! Xoroshiro128+ - http://xoroshiro.di.unimi.it/ | |
| 2 | //! | |
| 3 | //! PRNG | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const math = std.math; | |
| 7 | const Xoroshiro128 = @This(); | |
| 8 | ||
| 9 | s: [2]u64, | |
| 10 | ||
| 11 | pub fn init(init_s: u64) Xoroshiro128 { | |
| 12 | var x = Xoroshiro128{ .s = undefined }; | |
| 13 | ||
| 14 | x.seed(init_s); | |
| 15 | return x; | |
| 16 | } | |
| 17 | ||
| 18 | pub fn random(self: *Xoroshiro128) std.Random { | |
| 19 | return std.Random.init(self, fill); | |
| 20 | } | |
| 21 | ||
| 22 | pub fn next(self: *Xoroshiro128) u64 { | |
| 23 | const s0 = self.s[0]; | |
| 24 | var s1 = self.s[1]; | |
| 25 | const r = s0 +% s1; | |
| 26 | ||
| 27 | s1 ^= s0; | |
| 28 | self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14); | |
| 29 | self.s[1] = math.rotl(u64, s1, @as(u8, 36)); | |
| 30 | ||
| 31 | return r; | |
| 32 | } | |
| 33 | ||
| 34 | // Skip 2^64 places ahead in the sequence | |
| 35 | pub fn jump(self: *Xoroshiro128) void { | |
| 36 | var s0: u64 = 0; | |
| 37 | var s1: u64 = 0; | |
| 38 | ||
| 39 | const table = [_]u64{ | |
| 40 | 0xbeac0467eba5facb, | |
| 41 | 0xd86b048b86aa9922, | |
| 42 | }; | |
| 43 | ||
| 44 | inline for (table) |entry| { | |
| 45 | var b: usize = 0; | |
| 46 | while (b < 64) : (b += 1) { | |
| 47 | if ((entry & (@as(u64, 1) << @as(u6, @intCast(b)))) != 0) { | |
| 48 | s0 ^= self.s[0]; | |
| 49 | s1 ^= self.s[1]; | |
| 50 | } | |
| 51 | _ = self.next(); | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 55 | self.s[0] = s0; | |
| 56 | self.s[1] = s1; | |
| 57 | } | |
| 58 | ||
| 59 | pub fn seed(self: *Xoroshiro128, init_s: u64) void { | |
| 60 | // Xoroshiro requires 128-bits of seed. | |
| 61 | var gen = std.Random.SplitMix64.init(init_s); | |
| 62 | ||
| 63 | self.s[0] = gen.next(); | |
| 64 | self.s[1] = gen.next(); | |
| 65 | } | |
| 66 | ||
| 67 | pub fn fill(self: *Xoroshiro128, buf: []u8) void { | |
| 68 | var i: usize = 0; | |
| 69 | const aligned_len = buf.len - (buf.len & 7); | |
| 70 | ||
| 71 | // Complete 8 byte segments. | |
| 72 | while (i < aligned_len) : (i += 8) { | |
| 73 | var n = self.next(); | |
| 74 | comptime var j: usize = 0; | |
| 75 | inline while (j < 8) : (j += 1) { | |
| 76 | buf[i + j] = @as(u8, @truncate(n)); | |
| 77 | n >>= 8; | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | // Remaining. (cuts the stream) | |
| 82 | if (i != buf.len) { | |
| 83 | var n = self.next(); | |
| 84 | while (i < buf.len) : (i += 1) { | |
| 85 | buf[i] = @as(u8, @truncate(n)); | |
| 86 | n >>= 8; | |
| 87 | } | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | test "xoroshiro sequence" { | |
| 92 | var r = Xoroshiro128.init(0); | |
| 93 | r.s[0] = 0xaeecf86f7878dd75; | |
| 94 | r.s[1] = 0x01cd153642e72622; | |
| 95 | ||
| 96 | const seq1 = [_]u64{ | |
| 97 | 0xb0ba0da5bb600397, | |
| 98 | 0x18a08afde614dccc, | |
| 99 | 0xa2635b956a31b929, | |
| 100 | 0xabe633c971efa045, | |
| 101 | 0x9ac19f9706ca3cac, | |
| 102 | 0xf62b426578c1e3fb, | |
| 103 | }; | |
| 104 | ||
| 105 | for (seq1) |s| { | |
| 106 | try std.testing.expect(s == r.next()); | |
| 107 | } | |
| 108 | ||
| 109 | r.jump(); | |
| 110 | ||
| 111 | const seq2 = [_]u64{ | |
| 112 | 0x95344a13556d3e22, | |
| 113 | 0xb4fb32dafa4d00df, | |
| 114 | 0xb2011d9ccdcfe2dd, | |
| 115 | 0x05679a9b2119b908, | |
| 116 | 0xa860a1da7c9cd8a0, | |
| 117 | 0x658a96efe3f86550, | |
| 118 | }; | |
| 119 | ||
| 120 | for (seq2) |s| { | |
| 121 | try std.testing.expect(s == r.next()); | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | test "xoroshiro fill" { | |
| 126 | var r = Xoroshiro128.init(0); | |
| 127 | r.s[0] = 0xaeecf86f7878dd75; | |
| 128 | r.s[1] = 0x01cd153642e72622; | |
| 129 | ||
| 130 | const seq = [_]u64{ | |
| 131 | 0xb0ba0da5bb600397, | |
| 132 | 0x18a08afde614dccc, | |
| 133 | 0xa2635b956a31b929, | |
| 134 | 0xabe633c971efa045, | |
| 135 | 0x9ac19f9706ca3cac, | |
| 136 | 0xf62b426578c1e3fb, | |
| 137 | }; | |
| 138 | ||
| 139 | for (seq) |s| { | |
| 140 | var buf0: [8]u8 = undefined; | |
| 141 | var buf1: [7]u8 = undefined; | |
| 142 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 143 | r.fill(&buf1); | |
| 144 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 145 | } | |
| 146 | } |
lib/std/Random/Xoshiro256.zig created+145| ... | ... | @@ -0,0 +1,145 @@ |
| 1 | //! Xoshiro256++ - http://xoroshiro.di.unimi.it/ | |
| 2 | //! | |
| 3 | //! PRNG | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const math = std.math; | |
| 7 | const Xoshiro256 = @This(); | |
| 8 | ||
| 9 | s: [4]u64, | |
| 10 | ||
| 11 | pub fn init(init_s: u64) Xoshiro256 { | |
| 12 | var x = Xoshiro256{ | |
| 13 | .s = undefined, | |
| 14 | }; | |
| 15 | ||
| 16 | x.seed(init_s); | |
| 17 | return x; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn random(self: *Xoshiro256) std.Random { | |
| 21 | return std.Random.init(self, fill); | |
| 22 | } | |
| 23 | ||
| 24 | pub fn next(self: *Xoshiro256) u64 { | |
| 25 | const r = math.rotl(u64, self.s[0] +% self.s[3], 23) +% self.s[0]; | |
| 26 | ||
| 27 | const t = self.s[1] << 17; | |
| 28 | ||
| 29 | self.s[2] ^= self.s[0]; | |
| 30 | self.s[3] ^= self.s[1]; | |
| 31 | self.s[1] ^= self.s[2]; | |
| 32 | self.s[0] ^= self.s[3]; | |
| 33 | ||
| 34 | self.s[2] ^= t; | |
| 35 | ||
| 36 | self.s[3] = math.rotl(u64, self.s[3], 45); | |
| 37 | ||
| 38 | return r; | |
| 39 | } | |
| 40 | ||
| 41 | // Skip 2^128 places ahead in the sequence | |
| 42 | pub fn jump(self: *Xoshiro256) void { | |
| 43 | var s: u256 = 0; | |
| 44 | ||
| 45 | var table: u256 = 0x39abdc4529b1661ca9582618e03fc9aad5a61266f0c9392c180ec6d33cfd0aba; | |
| 46 | ||
| 47 | while (table != 0) : (table >>= 1) { | |
| 48 | if (@as(u1, @truncate(table)) != 0) { | |
| 49 | s ^= @as(u256, @bitCast(self.s)); | |
| 50 | } | |
| 51 | _ = self.next(); | |
| 52 | } | |
| 53 | ||
| 54 | self.s = @as([4]u64, @bitCast(s)); | |
| 55 | } | |
| 56 | ||
| 57 | pub fn seed(self: *Xoshiro256, init_s: u64) void { | |
| 58 | // Xoshiro requires 256-bits of seed. | |
| 59 | var gen = std.Random.SplitMix64.init(init_s); | |
| 60 | ||
| 61 | self.s[0] = gen.next(); | |
| 62 | self.s[1] = gen.next(); | |
| 63 | self.s[2] = gen.next(); | |
| 64 | self.s[3] = gen.next(); | |
| 65 | } | |
| 66 | ||
| 67 | pub fn fill(self: *Xoshiro256, buf: []u8) void { | |
| 68 | var i: usize = 0; | |
| 69 | const aligned_len = buf.len - (buf.len & 7); | |
| 70 | ||
| 71 | // Complete 8 byte segments. | |
| 72 | while (i < aligned_len) : (i += 8) { | |
| 73 | var n = self.next(); | |
| 74 | comptime var j: usize = 0; | |
| 75 | inline while (j < 8) : (j += 1) { | |
| 76 | buf[i + j] = @as(u8, @truncate(n)); | |
| 77 | n >>= 8; | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | // Remaining. (cuts the stream) | |
| 82 | if (i != buf.len) { | |
| 83 | var n = self.next(); | |
| 84 | while (i < buf.len) : (i += 1) { | |
| 85 | buf[i] = @as(u8, @truncate(n)); | |
| 86 | n >>= 8; | |
| 87 | } | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | test "xoroshiro sequence" { | |
| 92 | if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; | |
| 93 | if (@import("builtin").zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 94 | ||
| 95 | var r = Xoshiro256.init(0); | |
| 96 | ||
| 97 | const seq1 = [_]u64{ | |
| 98 | 0x53175d61490b23df, | |
| 99 | 0x61da6f3dc380d507, | |
| 100 | 0x5c0fdf91ec9a7bfc, | |
| 101 | 0x02eebf8c3bbe5e1a, | |
| 102 | 0x7eca04ebaf4a5eea, | |
| 103 | 0x0543c37757f08d9a, | |
| 104 | }; | |
| 105 | ||
| 106 | for (seq1) |s| { | |
| 107 | try std.testing.expect(s == r.next()); | |
| 108 | } | |
| 109 | ||
| 110 | r.jump(); | |
| 111 | ||
| 112 | const seq2 = [_]u64{ | |
| 113 | 0xae1db5c5e27807be, | |
| 114 | 0xb584c6a7fd8709fe, | |
| 115 | 0xc46a0ee9330fb6e, | |
| 116 | 0xdc0c9606f49ed76e, | |
| 117 | 0x1f5bb6540f6651fb, | |
| 118 | 0x72fa2ca734601488, | |
| 119 | }; | |
| 120 | ||
| 121 | for (seq2) |s| { | |
| 122 | try std.testing.expect(s == r.next()); | |
| 123 | } | |
| 124 | } | |
| 125 | ||
| 126 | test "xoroshiro fill" { | |
| 127 | var r = Xoshiro256.init(0); | |
| 128 | ||
| 129 | const seq = [_]u64{ | |
| 130 | 0x53175d61490b23df, | |
| 131 | 0x61da6f3dc380d507, | |
| 132 | 0x5c0fdf91ec9a7bfc, | |
| 133 | 0x02eebf8c3bbe5e1a, | |
| 134 | 0x7eca04ebaf4a5eea, | |
| 135 | 0x0543c37757f08d9a, | |
| 136 | }; | |
| 137 | ||
| 138 | for (seq) |s| { | |
| 139 | var buf0: [8]u8 = undefined; | |
| 140 | var buf1: [7]u8 = undefined; | |
| 141 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 142 | r.fill(&buf1); | |
| 143 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 144 | } | |
| 145 | } |
lib/std/Random/benchmark.zig created+217| ... | ... | @@ -0,0 +1,217 @@ |
| 1 | // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const builtin = @import("builtin"); | |
| 5 | const time = std.time; | |
| 6 | const Timer = time.Timer; | |
| 7 | const Random = std.Random; | |
| 8 | ||
| 9 | const KiB = 1024; | |
| 10 | const MiB = 1024 * KiB; | |
| 11 | const GiB = 1024 * MiB; | |
| 12 | ||
| 13 | const Rng = struct { | |
| 14 | ty: type, | |
| 15 | name: []const u8, | |
| 16 | init_u8s: ?[]const u8 = null, | |
| 17 | init_u64: ?u64 = null, | |
| 18 | }; | |
| 19 | ||
| 20 | const prngs = [_]Rng{ | |
| 21 | Rng{ | |
| 22 | .ty = Random.Isaac64, | |
| 23 | .name = "isaac64", | |
| 24 | .init_u64 = 0, | |
| 25 | }, | |
| 26 | Rng{ | |
| 27 | .ty = Random.Pcg, | |
| 28 | .name = "pcg", | |
| 29 | .init_u64 = 0, | |
| 30 | }, | |
| 31 | Rng{ | |
| 32 | .ty = Random.RomuTrio, | |
| 33 | .name = "romutrio", | |
| 34 | .init_u64 = 0, | |
| 35 | }, | |
| 36 | Rng{ | |
| 37 | .ty = Random.Sfc64, | |
| 38 | .name = "sfc64", | |
| 39 | .init_u64 = 0, | |
| 40 | }, | |
| 41 | Rng{ | |
| 42 | .ty = Random.Xoroshiro128, | |
| 43 | .name = "xoroshiro128", | |
| 44 | .init_u64 = 0, | |
| 45 | }, | |
| 46 | Rng{ | |
| 47 | .ty = Random.Xoshiro256, | |
| 48 | .name = "xoshiro256", | |
| 49 | .init_u64 = 0, | |
| 50 | }, | |
| 51 | }; | |
| 52 | ||
| 53 | const csprngs = [_]Rng{ | |
| 54 | Rng{ | |
| 55 | .ty = Random.Ascon, | |
| 56 | .name = "ascon", | |
| 57 | .init_u8s = &[_]u8{0} ** 32, | |
| 58 | }, | |
| 59 | Rng{ | |
| 60 | .ty = Random.ChaCha, | |
| 61 | .name = "chacha", | |
| 62 | .init_u8s = &[_]u8{0} ** 32, | |
| 63 | }, | |
| 64 | }; | |
| 65 | ||
| 66 | const Result = struct { | |
| 67 | throughput: u64, | |
| 68 | }; | |
| 69 | ||
| 70 | const long_block_size: usize = 8 * 8192; | |
| 71 | const short_block_size: usize = 8; | |
| 72 | ||
| 73 | pub fn benchmark(comptime H: anytype, bytes: usize, comptime block_size: usize) !Result { | |
| 74 | var rng = blk: { | |
| 75 | if (H.init_u8s) |init| { | |
| 76 | break :blk H.ty.init(init[0..].*); | |
| 77 | } | |
| 78 | if (H.init_u64) |init| { | |
| 79 | break :blk H.ty.init(init); | |
| 80 | } | |
| 81 | break :blk H.ty.init(); | |
| 82 | }; | |
| 83 | ||
| 84 | var block: [block_size]u8 = undefined; | |
| 85 | ||
| 86 | var offset: usize = 0; | |
| 87 | var timer = try Timer.start(); | |
| 88 | const start = timer.lap(); | |
| 89 | while (offset < bytes) : (offset += block.len) { | |
| 90 | rng.fill(block[0..]); | |
| 91 | } | |
| 92 | const end = timer.read(); | |
| 93 | ||
| 94 | const elapsed_s = @as(f64, @floatFromInt(end - start)) / time.ns_per_s; | |
| 95 | const throughput = @as(u64, @intFromFloat(@as(f64, @floatFromInt(bytes)) / elapsed_s)); | |
| 96 | ||
| 97 | std.debug.assert(rng.random().int(u64) != 0); | |
| 98 | ||
| 99 | return Result{ | |
| 100 | .throughput = throughput, | |
| 101 | }; | |
| 102 | } | |
| 103 | ||
| 104 | fn usage() void { | |
| 105 | std.debug.print( | |
| 106 | \\throughput_test [options] | |
| 107 | \\ | |
| 108 | \\Options: | |
| 109 | \\ --filter [test-name] | |
| 110 | \\ --count [int] | |
| 111 | \\ --prngs-only | |
| 112 | \\ --csprngs-only | |
| 113 | \\ --short-only | |
| 114 | \\ --long-only | |
| 115 | \\ --help | |
| 116 | \\ | |
| 117 | , .{}); | |
| 118 | } | |
| 119 | ||
| 120 | fn mode(comptime x: comptime_int) comptime_int { | |
| 121 | return if (builtin.mode == .Debug) x / 64 else x; | |
| 122 | } | |
| 123 | ||
| 124 | pub fn main() !void { | |
| 125 | const stdout = std.io.getStdOut().writer(); | |
| 126 | ||
| 127 | var buffer: [1024]u8 = undefined; | |
| 128 | var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); | |
| 129 | const args = try std.process.argsAlloc(fixed.allocator()); | |
| 130 | ||
| 131 | var filter: ?[]u8 = ""; | |
| 132 | var count: usize = mode(128 * MiB); | |
| 133 | var bench_prngs = true; | |
| 134 | var bench_csprngs = true; | |
| 135 | var bench_long = true; | |
| 136 | var bench_short = true; | |
| 137 | ||
| 138 | var i: usize = 1; | |
| 139 | while (i < args.len) : (i += 1) { | |
| 140 | if (std.mem.eql(u8, args[i], "--mode")) { | |
| 141 | try stdout.print("{}\n", .{builtin.mode}); | |
| 142 | return; | |
| 143 | } else if (std.mem.eql(u8, args[i], "--filter")) { | |
| 144 | i += 1; | |
| 145 | if (i == args.len) { | |
| 146 | usage(); | |
| 147 | std.os.exit(1); | |
| 148 | } | |
| 149 | ||
| 150 | filter = args[i]; | |
| 151 | } else if (std.mem.eql(u8, args[i], "--count")) { | |
| 152 | i += 1; | |
| 153 | if (i == args.len) { | |
| 154 | usage(); | |
| 155 | std.os.exit(1); | |
| 156 | } | |
| 157 | ||
| 158 | const c = try std.fmt.parseUnsigned(usize, args[i], 10); | |
| 159 | count = c * MiB; | |
| 160 | } else if (std.mem.eql(u8, args[i], "--csprngs-only")) { | |
| 161 | bench_prngs = false; | |
| 162 | } else if (std.mem.eql(u8, args[i], "--prngs-only")) { | |
| 163 | bench_csprngs = false; | |
| 164 | } else if (std.mem.eql(u8, args[i], "--short-only")) { | |
| 165 | bench_long = false; | |
| 166 | } else if (std.mem.eql(u8, args[i], "--long-only")) { | |
| 167 | bench_short = false; | |
| 168 | } else if (std.mem.eql(u8, args[i], "--help")) { | |
| 169 | usage(); | |
| 170 | return; | |
| 171 | } else { | |
| 172 | usage(); | |
| 173 | std.os.exit(1); | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | if (bench_prngs) { | |
| 178 | if (bench_long) { | |
| 179 | inline for (prngs) |R| { | |
| 180 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 181 | try stdout.print("{s} (long outputs)\n", .{R.name}); | |
| 182 | const result_long = try benchmark(R, count, long_block_size); | |
| 183 | try stdout.print(" {:5} MiB/s\n", .{result_long.throughput / (1 * MiB)}); | |
| 184 | } | |
| 185 | } | |
| 186 | } | |
| 187 | if (bench_short) { | |
| 188 | inline for (prngs) |R| { | |
| 189 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 190 | try stdout.print("{s} (short outputs)\n", .{R.name}); | |
| 191 | const result_short = try benchmark(R, count, short_block_size); | |
| 192 | try stdout.print(" {:5} MiB/s\n", .{result_short.throughput / (1 * MiB)}); | |
| 193 | } | |
| 194 | } | |
| 195 | } | |
| 196 | } | |
| 197 | if (bench_csprngs) { | |
| 198 | if (bench_long) { | |
| 199 | inline for (csprngs) |R| { | |
| 200 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 201 | try stdout.print("{s} (cryptographic, long outputs)\n", .{R.name}); | |
| 202 | const result_long = try benchmark(R, count, long_block_size); | |
| 203 | try stdout.print(" {:5} MiB/s\n", .{result_long.throughput / (1 * MiB)}); | |
| 204 | } | |
| 205 | } | |
| 206 | } | |
| 207 | if (bench_short) { | |
| 208 | inline for (csprngs) |R| { | |
| 209 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 210 | try stdout.print("{s} (cryptographic, short outputs)\n", .{R.name}); | |
| 211 | const result_short = try benchmark(R, count, short_block_size); | |
| 212 | try stdout.print(" {:5} MiB/s\n", .{result_short.throughput / (1 * MiB)}); | |
| 213 | } | |
| 214 | } | |
| 215 | } | |
| 216 | } | |
| 217 | } |
lib/std/Random/test.zig created+473| ... | ... | @@ -0,0 +1,473 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const math = std.math; | |
| 3 | const Random = std.Random; | |
| 4 | const DefaultPrng = Random.DefaultPrng; | |
| 5 | const SplitMix64 = Random.SplitMix64; | |
| 6 | const DefaultCsprng = Random.DefaultCsprng; | |
| 7 | const expect = std.testing.expect; | |
| 8 | const expectEqual = std.testing.expectEqual; | |
| 9 | ||
| 10 | const SequentialPrng = struct { | |
| 11 | const Self = @This(); | |
| 12 | next_value: u8, | |
| 13 | ||
| 14 | pub fn init() Self { | |
| 15 | return Self{ | |
| 16 | .next_value = 0, | |
| 17 | }; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn random(self: *Self) Random { | |
| 21 | return Random.init(self, fill); | |
| 22 | } | |
| 23 | ||
| 24 | pub fn fill(self: *Self, buf: []u8) void { | |
| 25 | for (buf) |*b| { | |
| 26 | b.* = self.next_value; | |
| 27 | } | |
| 28 | self.next_value +%= 1; | |
| 29 | } | |
| 30 | }; | |
| 31 | ||
| 32 | /// Do not use this PRNG! It is meant to be predictable, for the purposes of test reproducibility and coverage. | |
| 33 | /// Its output is just a repeat of a user-specified byte pattern. | |
| 34 | /// Name is a reference to this comic: https://dilbert.com/strip/2001-10-25 | |
| 35 | const Dilbert = struct { | |
| 36 | pattern: []const u8 = undefined, | |
| 37 | curr_idx: usize = 0, | |
| 38 | ||
| 39 | pub fn init(pattern: []const u8) !Dilbert { | |
| 40 | if (pattern.len == 0) | |
| 41 | return error.EmptyPattern; | |
| 42 | var self = Dilbert{}; | |
| 43 | self.pattern = pattern; | |
| 44 | self.curr_idx = 0; | |
| 45 | return self; | |
| 46 | } | |
| 47 | ||
| 48 | pub fn random(self: *Dilbert) Random { | |
| 49 | return Random.init(self, fill); | |
| 50 | } | |
| 51 | ||
| 52 | pub fn fill(self: *Dilbert, buf: []u8) void { | |
| 53 | for (buf) |*byte| { | |
| 54 | byte.* = self.pattern[self.curr_idx]; | |
| 55 | self.curr_idx = (self.curr_idx + 1) % self.pattern.len; | |
| 56 | } | |
| 57 | } | |
| 58 | ||
| 59 | test "Dilbert fill" { | |
| 60 | var r = try Dilbert.init("9nine"); | |
| 61 | ||
| 62 | const seq = [_]u64{ | |
| 63 | 0x396E696E65396E69, | |
| 64 | 0x6E65396E696E6539, | |
| 65 | 0x6E696E65396E696E, | |
| 66 | 0x65396E696E65396E, | |
| 67 | 0x696E65396E696E65, | |
| 68 | }; | |
| 69 | ||
| 70 | for (seq) |s| { | |
| 71 | var buf0: [8]u8 = undefined; | |
| 72 | var buf1: [8]u8 = undefined; | |
| 73 | std.mem.writeInt(u64, &buf0, s, .big); | |
| 74 | r.fill(&buf1); | |
| 75 | try std.testing.expect(std.mem.eql(u8, buf0[0..], buf1[0..])); | |
| 76 | } | |
| 77 | } | |
| 78 | }; | |
| 79 | ||
| 80 | test "Random int" { | |
| 81 | try testRandomInt(); | |
| 82 | try comptime testRandomInt(); | |
| 83 | } | |
| 84 | fn testRandomInt() !void { | |
| 85 | var rng = SequentialPrng.init(); | |
| 86 | const random = rng.random(); | |
| 87 | ||
| 88 | try expect(random.int(u0) == 0); | |
| 89 | ||
| 90 | rng.next_value = 0; | |
| 91 | try expect(random.int(u1) == 0); | |
| 92 | try expect(random.int(u1) == 1); | |
| 93 | try expect(random.int(u2) == 2); | |
| 94 | try expect(random.int(u2) == 3); | |
| 95 | try expect(random.int(u2) == 0); | |
| 96 | ||
| 97 | rng.next_value = 0xff; | |
| 98 | try expect(random.int(u8) == 0xff); | |
| 99 | rng.next_value = 0x11; | |
| 100 | try expect(random.int(u8) == 0x11); | |
| 101 | ||
| 102 | rng.next_value = 0xff; | |
| 103 | try expect(random.int(u32) == 0xffffffff); | |
| 104 | rng.next_value = 0x11; | |
| 105 | try expect(random.int(u32) == 0x11111111); | |
| 106 | ||
| 107 | rng.next_value = 0xff; | |
| 108 | try expect(random.int(i32) == -1); | |
| 109 | rng.next_value = 0x11; | |
| 110 | try expect(random.int(i32) == 0x11111111); | |
| 111 | ||
| 112 | rng.next_value = 0xff; | |
| 113 | try expect(random.int(i8) == -1); | |
| 114 | rng.next_value = 0x11; | |
| 115 | try expect(random.int(i8) == 0x11); | |
| 116 | ||
| 117 | rng.next_value = 0xff; | |
| 118 | try expect(random.int(u33) == 0x1ffffffff); | |
| 119 | rng.next_value = 0xff; | |
| 120 | try expect(random.int(i1) == -1); | |
| 121 | rng.next_value = 0xff; | |
| 122 | try expect(random.int(i2) == -1); | |
| 123 | rng.next_value = 0xff; | |
| 124 | try expect(random.int(i33) == -1); | |
| 125 | } | |
| 126 | ||
| 127 | test "Random boolean" { | |
| 128 | try testRandomBoolean(); | |
| 129 | try comptime testRandomBoolean(); | |
| 130 | } | |
| 131 | fn testRandomBoolean() !void { | |
| 132 | var rng = SequentialPrng.init(); | |
| 133 | const random = rng.random(); | |
| 134 | ||
| 135 | try expect(random.boolean() == false); | |
| 136 | try expect(random.boolean() == true); | |
| 137 | try expect(random.boolean() == false); | |
| 138 | try expect(random.boolean() == true); | |
| 139 | } | |
| 140 | ||
| 141 | test "Random enum" { | |
| 142 | try testRandomEnumValue(); | |
| 143 | try comptime testRandomEnumValue(); | |
| 144 | } | |
| 145 | fn testRandomEnumValue() !void { | |
| 146 | const TestEnum = enum { | |
| 147 | First, | |
| 148 | Second, | |
| 149 | Third, | |
| 150 | }; | |
| 151 | var rng = SequentialPrng.init(); | |
| 152 | const random = rng.random(); | |
| 153 | rng.next_value = 0; | |
| 154 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 155 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 156 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 157 | } | |
| 158 | ||
| 159 | test "Random intLessThan" { | |
| 160 | @setEvalBranchQuota(10000); | |
| 161 | try testRandomIntLessThan(); | |
| 162 | try comptime testRandomIntLessThan(); | |
| 163 | } | |
| 164 | fn testRandomIntLessThan() !void { | |
| 165 | var rng = SequentialPrng.init(); | |
| 166 | const random = rng.random(); | |
| 167 | ||
| 168 | rng.next_value = 0xff; | |
| 169 | try expect(random.uintLessThan(u8, 4) == 3); | |
| 170 | try expect(rng.next_value == 0); | |
| 171 | try expect(random.uintLessThan(u8, 4) == 0); | |
| 172 | try expect(rng.next_value == 1); | |
| 173 | ||
| 174 | rng.next_value = 0; | |
| 175 | try expect(random.uintLessThan(u64, 32) == 0); | |
| 176 | ||
| 177 | // trigger the bias rejection code path | |
| 178 | rng.next_value = 0; | |
| 179 | try expect(random.uintLessThan(u8, 3) == 0); | |
| 180 | // verify we incremented twice | |
| 181 | try expect(rng.next_value == 2); | |
| 182 | ||
| 183 | rng.next_value = 0xff; | |
| 184 | try expect(random.intRangeLessThan(u8, 0, 0x80) == 0x7f); | |
| 185 | rng.next_value = 0xff; | |
| 186 | try expect(random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); | |
| 187 | ||
| 188 | rng.next_value = 0xff; | |
| 189 | try expect(random.intRangeLessThan(i8, 0, 0x40) == 0x3f); | |
| 190 | rng.next_value = 0xff; | |
| 191 | try expect(random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); | |
| 192 | rng.next_value = 0xff; | |
| 193 | try expect(random.intRangeLessThan(i8, -0x80, 0) == -1); | |
| 194 | ||
| 195 | rng.next_value = 0xff; | |
| 196 | try expect(random.intRangeLessThan(i3, -4, 0) == -1); | |
| 197 | rng.next_value = 0xff; | |
| 198 | try expect(random.intRangeLessThan(i3, -2, 2) == 1); | |
| 199 | } | |
| 200 | ||
| 201 | test "Random intAtMost" { | |
| 202 | @setEvalBranchQuota(10000); | |
| 203 | try testRandomIntAtMost(); | |
| 204 | try comptime testRandomIntAtMost(); | |
| 205 | } | |
| 206 | fn testRandomIntAtMost() !void { | |
| 207 | var rng = SequentialPrng.init(); | |
| 208 | const random = rng.random(); | |
| 209 | ||
| 210 | rng.next_value = 0xff; | |
| 211 | try expect(random.uintAtMost(u8, 3) == 3); | |
| 212 | try expect(rng.next_value == 0); | |
| 213 | try expect(random.uintAtMost(u8, 3) == 0); | |
| 214 | ||
| 215 | // trigger the bias rejection code path | |
| 216 | rng.next_value = 0; | |
| 217 | try expect(random.uintAtMost(u8, 2) == 0); | |
| 218 | // verify we incremented twice | |
| 219 | try expect(rng.next_value == 2); | |
| 220 | ||
| 221 | rng.next_value = 0xff; | |
| 222 | try expect(random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); | |
| 223 | rng.next_value = 0xff; | |
| 224 | try expect(random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); | |
| 225 | ||
| 226 | rng.next_value = 0xff; | |
| 227 | try expect(random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); | |
| 228 | rng.next_value = 0xff; | |
| 229 | try expect(random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); | |
| 230 | rng.next_value = 0xff; | |
| 231 | try expect(random.intRangeAtMost(i8, -0x80, -1) == -1); | |
| 232 | ||
| 233 | rng.next_value = 0xff; | |
| 234 | try expect(random.intRangeAtMost(i3, -4, -1) == -1); | |
| 235 | rng.next_value = 0xff; | |
| 236 | try expect(random.intRangeAtMost(i3, -2, 1) == 1); | |
| 237 | ||
| 238 | try expect(random.uintAtMost(u0, 0) == 0); | |
| 239 | } | |
| 240 | ||
| 241 | test "Random Biased" { | |
| 242 | var prng = DefaultPrng.init(0); | |
| 243 | const random = prng.random(); | |
| 244 | // Not thoroughly checking the logic here. | |
| 245 | // Just want to execute all the paths with different types. | |
| 246 | ||
| 247 | try expect(random.uintLessThanBiased(u1, 1) == 0); | |
| 248 | try expect(random.uintLessThanBiased(u32, 10) < 10); | |
| 249 | try expect(random.uintLessThanBiased(u64, 20) < 20); | |
| 250 | ||
| 251 | try expect(random.uintAtMostBiased(u0, 0) == 0); | |
| 252 | try expect(random.uintAtMostBiased(u1, 0) <= 0); | |
| 253 | try expect(random.uintAtMostBiased(u32, 10) <= 10); | |
| 254 | try expect(random.uintAtMostBiased(u64, 20) <= 20); | |
| 255 | ||
| 256 | try expect(random.intRangeLessThanBiased(u1, 0, 1) == 0); | |
| 257 | try expect(random.intRangeLessThanBiased(i1, -1, 0) == -1); | |
| 258 | try expect(random.intRangeLessThanBiased(u32, 10, 20) >= 10); | |
| 259 | try expect(random.intRangeLessThanBiased(i32, 10, 20) >= 10); | |
| 260 | try expect(random.intRangeLessThanBiased(u64, 20, 40) >= 20); | |
| 261 | try expect(random.intRangeLessThanBiased(i64, 20, 40) >= 20); | |
| 262 | ||
| 263 | // uncomment for broken module error: | |
| 264 | //expect(random.intRangeAtMostBiased(u0, 0, 0) == 0); | |
| 265 | try expect(random.intRangeAtMostBiased(u1, 0, 1) >= 0); | |
| 266 | try expect(random.intRangeAtMostBiased(i1, -1, 0) >= -1); | |
| 267 | try expect(random.intRangeAtMostBiased(u32, 10, 20) >= 10); | |
| 268 | try expect(random.intRangeAtMostBiased(i32, 10, 20) >= 10); | |
| 269 | try expect(random.intRangeAtMostBiased(u64, 20, 40) >= 20); | |
| 270 | try expect(random.intRangeAtMostBiased(i64, 20, 40) >= 20); | |
| 271 | } | |
| 272 | ||
| 273 | test "splitmix64 sequence" { | |
| 274 | var r = SplitMix64.init(0xaeecf86f7878dd75); | |
| 275 | ||
| 276 | const seq = [_]u64{ | |
| 277 | 0x5dbd39db0178eb44, | |
| 278 | 0xa9900fb66b397da3, | |
| 279 | 0x5c1a28b1aeebcf5c, | |
| 280 | 0x64a963238f776912, | |
| 281 | 0xc6d4177b21d1c0ab, | |
| 282 | 0xb2cbdbdb5ea35394, | |
| 283 | }; | |
| 284 | ||
| 285 | for (seq) |s| { | |
| 286 | try expect(s == r.next()); | |
| 287 | } | |
| 288 | } | |
| 289 | ||
| 290 | // Actual Random helper function tests, pcg engine is assumed correct. | |
| 291 | test "Random float correctness" { | |
| 292 | var prng = DefaultPrng.init(0); | |
| 293 | const random = prng.random(); | |
| 294 | ||
| 295 | var i: usize = 0; | |
| 296 | while (i < 1000) : (i += 1) { | |
| 297 | const val1 = random.float(f32); | |
| 298 | try expect(val1 >= 0.0); | |
| 299 | try expect(val1 < 1.0); | |
| 300 | ||
| 301 | const val2 = random.float(f64); | |
| 302 | try expect(val2 >= 0.0); | |
| 303 | try expect(val2 < 1.0); | |
| 304 | } | |
| 305 | } | |
| 306 | ||
| 307 | // Check the "astronomically unlikely" code paths. | |
| 308 | test "Random float coverage" { | |
| 309 | var prng = try Dilbert.init(&[_]u8{0}); | |
| 310 | const random = prng.random(); | |
| 311 | ||
| 312 | const rand_f64 = random.float(f64); | |
| 313 | const rand_f32 = random.float(f32); | |
| 314 | ||
| 315 | try expect(rand_f32 == 0.0); | |
| 316 | try expect(rand_f64 == 0.0); | |
| 317 | } | |
| 318 | ||
| 319 | test "Random float chi-square goodness of fit" { | |
| 320 | const num_numbers = 100000; | |
| 321 | const num_buckets = 1000; | |
| 322 | ||
| 323 | var f32_hist = std.AutoHashMap(u32, u32).init(std.testing.allocator); | |
| 324 | defer f32_hist.deinit(); | |
| 325 | var f64_hist = std.AutoHashMap(u64, u32).init(std.testing.allocator); | |
| 326 | defer f64_hist.deinit(); | |
| 327 | ||
| 328 | var prng = DefaultPrng.init(0); | |
| 329 | const random = prng.random(); | |
| 330 | ||
| 331 | var i: usize = 0; | |
| 332 | while (i < num_numbers) : (i += 1) { | |
| 333 | const rand_f32 = random.float(f32); | |
| 334 | const rand_f64 = random.float(f64); | |
| 335 | const f32_put = try f32_hist.getOrPut(@as(u32, @intFromFloat(rand_f32 * @as(f32, @floatFromInt(num_buckets))))); | |
| 336 | if (f32_put.found_existing) { | |
| 337 | f32_put.value_ptr.* += 1; | |
| 338 | } else { | |
| 339 | f32_put.value_ptr.* = 1; | |
| 340 | } | |
| 341 | const f64_put = try f64_hist.getOrPut(@as(u32, @intFromFloat(rand_f64 * @as(f64, @floatFromInt(num_buckets))))); | |
| 342 | if (f64_put.found_existing) { | |
| 343 | f64_put.value_ptr.* += 1; | |
| 344 | } else { | |
| 345 | f64_put.value_ptr.* = 1; | |
| 346 | } | |
| 347 | } | |
| 348 | ||
| 349 | var f32_total_variance: f64 = 0; | |
| 350 | var f64_total_variance: f64 = 0; | |
| 351 | ||
| 352 | { | |
| 353 | var j: u32 = 0; | |
| 354 | while (j < num_buckets) : (j += 1) { | |
| 355 | const count = @as(f64, @floatFromInt((if (f32_hist.get(j)) |v| v else 0))); | |
| 356 | const expected = @as(f64, @floatFromInt(num_numbers)) / @as(f64, @floatFromInt(num_buckets)); | |
| 357 | const delta = count - expected; | |
| 358 | const variance = (delta * delta) / expected; | |
| 359 | f32_total_variance += variance; | |
| 360 | } | |
| 361 | } | |
| 362 | ||
| 363 | { | |
| 364 | var j: u64 = 0; | |
| 365 | while (j < num_buckets) : (j += 1) { | |
| 366 | const count = @as(f64, @floatFromInt((if (f64_hist.get(j)) |v| v else 0))); | |
| 367 | const expected = @as(f64, @floatFromInt(num_numbers)) / @as(f64, @floatFromInt(num_buckets)); | |
| 368 | const delta = count - expected; | |
| 369 | const variance = (delta * delta) / expected; | |
| 370 | f64_total_variance += variance; | |
| 371 | } | |
| 372 | } | |
| 373 | ||
| 374 | // Accept p-values >= 0.05. | |
| 375 | // Critical value is calculated by opening a Python interpreter and running: | |
| 376 | // scipy.stats.chi2.isf(0.05, num_buckets - 1) | |
| 377 | const critical_value = 1073.6426506574246; | |
| 378 | try expect(f32_total_variance < critical_value); | |
| 379 | try expect(f64_total_variance < critical_value); | |
| 380 | } | |
| 381 | ||
| 382 | test "Random shuffle" { | |
| 383 | var prng = DefaultPrng.init(0); | |
| 384 | const random = prng.random(); | |
| 385 | ||
| 386 | var seq = [_]u8{ 0, 1, 2, 3, 4 }; | |
| 387 | var seen = [_]bool{false} ** 5; | |
| 388 | ||
| 389 | var i: usize = 0; | |
| 390 | while (i < 1000) : (i += 1) { | |
| 391 | random.shuffle(u8, seq[0..]); | |
| 392 | seen[seq[0]] = true; | |
| 393 | try expect(sumArray(seq[0..]) == 10); | |
| 394 | } | |
| 395 | ||
| 396 | // we should see every entry at the head at least once | |
| 397 | for (seen) |e| { | |
| 398 | try expect(e == true); | |
| 399 | } | |
| 400 | } | |
| 401 | ||
| 402 | fn sumArray(s: []const u8) u32 { | |
| 403 | var r: u32 = 0; | |
| 404 | for (s) |e| | |
| 405 | r += e; | |
| 406 | return r; | |
| 407 | } | |
| 408 | ||
| 409 | test "Random range" { | |
| 410 | var prng = DefaultPrng.init(0); | |
| 411 | const random = prng.random(); | |
| 412 | ||
| 413 | try testRange(random, -4, 3); | |
| 414 | try testRange(random, -4, -1); | |
| 415 | try testRange(random, 10, 14); | |
| 416 | try testRange(random, -0x80, 0x7f); | |
| 417 | } | |
| 418 | ||
| 419 | fn testRange(r: Random, start: i8, end: i8) !void { | |
| 420 | try testRangeBias(r, start, end, true); | |
| 421 | try testRangeBias(r, start, end, false); | |
| 422 | } | |
| 423 | fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void { | |
| 424 | const count = @as(usize, @intCast(@as(i32, end) - @as(i32, start))); | |
| 425 | var values_buffer = [_]bool{false} ** 0x100; | |
| 426 | const values = values_buffer[0..count]; | |
| 427 | var i: usize = 0; | |
| 428 | while (i < count) { | |
| 429 | const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); | |
| 430 | const index = @as(usize, @intCast(value - start)); | |
| 431 | if (!values[index]) { | |
| 432 | i += 1; | |
| 433 | values[index] = true; | |
| 434 | } | |
| 435 | } | |
| 436 | } | |
| 437 | ||
| 438 | test "CSPRNG" { | |
| 439 | var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined; | |
| 440 | std.crypto.random.bytes(&secret_seed); | |
| 441 | var csprng = DefaultCsprng.init(secret_seed); | |
| 442 | const random = csprng.random(); | |
| 443 | const a = random.int(u64); | |
| 444 | const b = random.int(u64); | |
| 445 | const c = random.int(u64); | |
| 446 | try expect(a ^ b ^ c != 0); | |
| 447 | } | |
| 448 | ||
| 449 | test "Random weightedIndex" { | |
| 450 | // Make sure weightedIndex works for various integers and floats | |
| 451 | inline for (.{ u64, i4, f32, f64 }) |T| { | |
| 452 | var prng = DefaultPrng.init(0); | |
| 453 | const random = prng.random(); | |
| 454 | ||
| 455 | const proportions = [_]T{ 2, 1, 1, 2 }; | |
| 456 | var counts = [_]f64{ 0, 0, 0, 0 }; | |
| 457 | ||
| 458 | const n_trials: u64 = 10_000; | |
| 459 | var i: usize = 0; | |
| 460 | while (i < n_trials) : (i += 1) { | |
| 461 | const pick = random.weightedIndex(T, &proportions); | |
| 462 | counts[pick] += 1; | |
| 463 | } | |
| 464 | ||
| 465 | // We expect the first and last counts to be roughly 2x the second and third | |
| 466 | const approxEqRel = std.math.approxEqRel; | |
| 467 | // Define "roughly" to be within 10% | |
| 468 | const tolerance = 0.1; | |
| 469 | try std.testing.expect(approxEqRel(f64, counts[0], counts[1] * 2, tolerance)); | |
| 470 | try std.testing.expect(approxEqRel(f64, counts[1], counts[2], tolerance)); | |
| 471 | try std.testing.expect(approxEqRel(f64, counts[2] * 2, counts[3], tolerance)); | |
| 472 | } | |
| 473 | } |
lib/std/Random/ziggurat.zig created+170| ... | ... | @@ -0,0 +1,170 @@ |
| 1 | //! Implements [ZIGNOR][1] (Jurgen A. Doornik, 2005, Nuffield College, Oxford). | |
| 2 | //! | |
| 3 | //! [1]: https://www.doornik.com/research/ziggurat.pdf | |
| 4 | //! | |
| 5 | //! rust/rand used as a reference; | |
| 6 | //! | |
| 7 | //! NOTE: This seems interesting but reference code is a bit hard to grok: | |
| 8 | //! https://sbarral.github.io/etf. | |
| 9 | ||
| 10 | const std = @import("../std.zig"); | |
| 11 | const builtin = @import("builtin"); | |
| 12 | const math = std.math; | |
| 13 | const Random = std.Random; | |
| 14 | ||
| 15 | pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { | |
| 16 | while (true) { | |
| 17 | // We manually construct a float from parts as we can avoid an extra random lookup here by | |
| 18 | // using the unused exponent for the lookup table entry. | |
| 19 | const bits = random.int(u64); | |
| 20 | const i = @as(usize, @as(u8, @truncate(bits))); | |
| 21 | ||
| 22 | const u = blk: { | |
| 23 | if (tables.is_symmetric) { | |
| 24 | // Generate a value in the range [2, 4) and scale into [-1, 1) | |
| 25 | const repr = ((0x3ff + 1) << 52) | (bits >> 12); | |
| 26 | break :blk @as(f64, @bitCast(repr)) - 3.0; | |
| 27 | } else { | |
| 28 | // Generate a value in the range [1, 2) and scale into (0, 1) | |
| 29 | const repr = (0x3ff << 52) | (bits >> 12); | |
| 30 | break :blk @as(f64, @bitCast(repr)) - (1.0 - math.floatEps(f64) / 2.0); | |
| 31 | } | |
| 32 | }; | |
| 33 | ||
| 34 | const x = u * tables.x[i]; | |
| 35 | const test_x = if (tables.is_symmetric) @abs(x) else x; | |
| 36 | ||
| 37 | // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i]) | |
| 38 | if (test_x < tables.x[i + 1]) { | |
| 39 | return x; | |
| 40 | } | |
| 41 | ||
| 42 | if (i == 0) { | |
| 43 | return tables.zero_case(random, u); | |
| 44 | } | |
| 45 | ||
| 46 | // equivalent to f1 + DRanU() * (f0 - f1) < 1 | |
| 47 | if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) { | |
| 48 | return x; | |
| 49 | } | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | pub const ZigTable = struct { | |
| 54 | r: f64, | |
| 55 | x: [257]f64, | |
| 56 | f: [257]f64, | |
| 57 | ||
| 58 | // probability density function used as a fallback | |
| 59 | pdf: fn (f64) f64, | |
| 60 | // whether the distribution is symmetric | |
| 61 | is_symmetric: bool, | |
| 62 | // fallback calculation in the case we are in the 0 block | |
| 63 | zero_case: fn (Random, f64) f64, | |
| 64 | }; | |
| 65 | ||
| 66 | // zigNorInit | |
| 67 | pub fn ZigTableGen( | |
| 68 | comptime is_symmetric: bool, | |
| 69 | comptime r: f64, | |
| 70 | comptime v: f64, | |
| 71 | comptime f: fn (f64) f64, | |
| 72 | comptime f_inv: fn (f64) f64, | |
| 73 | comptime zero_case: fn (Random, f64) f64, | |
| 74 | ) ZigTable { | |
| 75 | var tables: ZigTable = undefined; | |
| 76 | ||
| 77 | tables.is_symmetric = is_symmetric; | |
| 78 | tables.r = r; | |
| 79 | tables.pdf = f; | |
| 80 | tables.zero_case = zero_case; | |
| 81 | ||
| 82 | tables.x[0] = v / f(r); | |
| 83 | tables.x[1] = r; | |
| 84 | ||
| 85 | for (tables.x[2..256], 0..) |*entry, i| { | |
| 86 | const last = tables.x[2 + i - 1]; | |
| 87 | entry.* = f_inv(v / last + f(last)); | |
| 88 | } | |
| 89 | tables.x[256] = 0; | |
| 90 | ||
| 91 | for (tables.f[0..], 0..) |*entry, i| { | |
| 92 | entry.* = f(tables.x[i]); | |
| 93 | } | |
| 94 | ||
| 95 | return tables; | |
| 96 | } | |
| 97 | ||
| 98 | // N(0, 1) | |
| 99 | pub const NormDist = blk: { | |
| 100 | @setEvalBranchQuota(30000); | |
| 101 | break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); | |
| 102 | }; | |
| 103 | ||
| 104 | pub const norm_r = 3.6541528853610088; | |
| 105 | pub const norm_v = 0.00492867323399; | |
| 106 | ||
| 107 | pub fn norm_f(x: f64) f64 { | |
| 108 | return @exp(-x * x / 2.0); | |
| 109 | } | |
| 110 | pub fn norm_f_inv(y: f64) f64 { | |
| 111 | return @sqrt(-2.0 * @log(y)); | |
| 112 | } | |
| 113 | pub fn norm_zero_case(random: Random, u: f64) f64 { | |
| 114 | var x: f64 = 1; | |
| 115 | var y: f64 = 0; | |
| 116 | ||
| 117 | while (-2.0 * y < x * x) { | |
| 118 | x = @log(random.float(f64)) / norm_r; | |
| 119 | y = @log(random.float(f64)); | |
| 120 | } | |
| 121 | ||
| 122 | if (u < 0) { | |
| 123 | return x - norm_r; | |
| 124 | } else { | |
| 125 | return norm_r - x; | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | test "normal dist sanity" { | |
| 130 | var prng = Random.DefaultPrng.init(0); | |
| 131 | const random = prng.random(); | |
| 132 | ||
| 133 | var i: usize = 0; | |
| 134 | while (i < 1000) : (i += 1) { | |
| 135 | _ = random.floatNorm(f64); | |
| 136 | } | |
| 137 | } | |
| 138 | ||
| 139 | // Exp(1) | |
| 140 | pub const ExpDist = blk: { | |
| 141 | @setEvalBranchQuota(30000); | |
| 142 | break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case); | |
| 143 | }; | |
| 144 | ||
| 145 | pub const exp_r = 7.69711747013104972; | |
| 146 | pub const exp_v = 0.0039496598225815571993; | |
| 147 | ||
| 148 | pub fn exp_f(x: f64) f64 { | |
| 149 | return @exp(-x); | |
| 150 | } | |
| 151 | pub fn exp_f_inv(y: f64) f64 { | |
| 152 | return -@log(y); | |
| 153 | } | |
| 154 | pub fn exp_zero_case(random: Random, _: f64) f64 { | |
| 155 | return exp_r - @log(random.float(f64)); | |
| 156 | } | |
| 157 | ||
| 158 | test "exp dist smoke test" { | |
| 159 | var prng = Random.DefaultPrng.init(0); | |
| 160 | const random = prng.random(); | |
| 161 | ||
| 162 | var i: usize = 0; | |
| 163 | while (i < 1000) : (i += 1) { | |
| 164 | _ = random.floatExp(f64); | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | test { | |
| 169 | _ = NormDist; | |
| 170 | } |
lib/std/Thread/RwLock.zig+1-1| ... | ... | @@ -328,7 +328,7 @@ test "RwLock - concurrent access" { |
| 328 | 328 | } |
| 329 | 329 | |
| 330 | 330 | fn writer(self: *Self, thread_idx: usize) !void { |
| 331 | var prng = std.rand.DefaultPrng.init(thread_idx); | |
| 331 | var prng = std.Random.DefaultPrng.init(thread_idx); | |
| 332 | 332 | var rnd = prng.random(); |
| 333 | 333 | |
| 334 | 334 | while (true) { |
lib/std/crypto/benchmark.zig+1-1| ... | ... | @@ -10,7 +10,7 @@ const crypto = std.crypto; |
| 10 | 10 | const KiB = 1024; |
| 11 | 11 | const MiB = 1024 * KiB; |
| 12 | 12 | |
| 13 | var prng = std.rand.DefaultPrng.init(0); | |
| 13 | var prng = std.Random.DefaultPrng.init(0); | |
| 14 | 14 | const random = prng.random(); |
| 15 | 15 | |
| 16 | 16 | const Crypto = struct { |
lib/std/crypto/kyber_d00.zig+1-1| ... | ... | @@ -110,7 +110,7 @@ const assert = std.debug.assert; |
| 110 | 110 | const crypto = std.crypto; |
| 111 | 111 | const math = std.math; |
| 112 | 112 | const mem = std.mem; |
| 113 | const RndGen = std.rand.DefaultPrng; | |
| 113 | const RndGen = std.Random.DefaultPrng; | |
| 114 | 114 | const sha3 = crypto.hash.sha3; |
| 115 | 115 | |
| 116 | 116 | // Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1. |
lib/std/crypto/tlcsprng.zig+2-2| ... | ... | @@ -10,7 +10,7 @@ const os = std.os; |
| 10 | 10 | |
| 11 | 11 | /// We use this as a layer of indirection because global const pointers cannot |
| 12 | 12 | /// point to thread-local variables. |
| 13 | pub const interface = std.rand.Random{ | |
| 13 | pub const interface = std.Random{ | |
| 14 | 14 | .ptr = undefined, |
| 15 | 15 | .fillFn = tlsCsprngFill, |
| 16 | 16 | }; |
| ... | ... | @@ -43,7 +43,7 @@ const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{ |
| 43 | 43 | }) orelse true; |
| 44 | 44 | const is_haiku = builtin.os.tag == .haiku; |
| 45 | 45 | |
| 46 | const Rng = std.rand.DefaultCsprng; | |
| 46 | const Rng = std.Random.DefaultCsprng; | |
| 47 | 47 | |
| 48 | 48 | const Context = struct { |
| 49 | 49 | init_state: enum(u8) { uninitialized = 0, initialized, failed }, |
lib/std/hash/benchmark.zig+1-1| ... | ... | @@ -10,7 +10,7 @@ const KiB = 1024; |
| 10 | 10 | const MiB = 1024 * KiB; |
| 11 | 11 | const GiB = 1024 * MiB; |
| 12 | 12 | |
| 13 | var prng = std.rand.DefaultPrng.init(0); | |
| 13 | var prng = std.Random.DefaultPrng.init(0); | |
| 14 | 14 | const random = prng.random(); |
| 15 | 15 | |
| 16 | 16 | const Hash = struct { |
lib/std/hash_map.zig+2-2| ... | ... | @@ -1884,7 +1884,7 @@ test "std.hash_map put and remove loop in random order" { |
| 1884 | 1884 | while (i < size) : (i += 1) { |
| 1885 | 1885 | try keys.append(i); |
| 1886 | 1886 | } |
| 1887 | var prng = std.rand.DefaultPrng.init(0); | |
| 1887 | var prng = std.Random.DefaultPrng.init(0); | |
| 1888 | 1888 | const random = prng.random(); |
| 1889 | 1889 | |
| 1890 | 1890 | while (i < iterations) : (i += 1) { |
| ... | ... | @@ -1916,7 +1916,7 @@ test "std.hash_map remove one million elements in random order" { |
| 1916 | 1916 | keys.append(i) catch unreachable; |
| 1917 | 1917 | } |
| 1918 | 1918 | |
| 1919 | var prng = std.rand.DefaultPrng.init(0); | |
| 1919 | var prng = std.Random.DefaultPrng.init(0); | |
| 1920 | 1920 | const random = prng.random(); |
| 1921 | 1921 | random.shuffle(u32, keys.items); |
| 1922 | 1922 |
lib/std/heap/arena_allocator.zig+1-1| ... | ... | @@ -250,7 +250,7 @@ test "ArenaAllocator (reset with preheating)" { |
| 250 | 250 | var arena_allocator = ArenaAllocator.init(std.testing.allocator); |
| 251 | 251 | defer arena_allocator.deinit(); |
| 252 | 252 | // provides some variance in the allocated data |
| 253 | var rng_src = std.rand.DefaultPrng.init(19930913); | |
| 253 | var rng_src = std.Random.DefaultPrng.init(19930913); | |
| 254 | 254 | const random = rng_src.random(); |
| 255 | 255 | var rounds: usize = 25; |
| 256 | 256 | while (rounds > 0) { |
lib/std/io/test.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const io = std.io; |
| 3 | const DefaultPrng = std.rand.DefaultPrng; | |
| 3 | const DefaultPrng = std.Random.DefaultPrng; | |
| 4 | 4 | const expect = std.testing.expect; |
| 5 | 5 | const expectEqual = std.testing.expectEqual; |
| 6 | 6 | const expectError = std.testing.expectError; |
lib/std/math/big/rational.zig+1-1| ... | ... | @@ -594,7 +594,7 @@ test "big.rational toFloat" { |
| 594 | 594 | test "big.rational set/to Float round-trip" { |
| 595 | 595 | var a = try Rational.init(testing.allocator); |
| 596 | 596 | defer a.deinit(); |
| 597 | var prng = std.rand.DefaultPrng.init(0x5EED); | |
| 597 | var prng = std.Random.DefaultPrng.init(0x5EED); | |
| 598 | 598 | const random = prng.random(); |
| 599 | 599 | var i: usize = 0; |
| 600 | 600 | while (i < 512) : (i += 1) { |
lib/std/mem.zig+1-1| ... | ... | @@ -4423,7 +4423,7 @@ test "read/write(Var)PackedInt" { |
| 4423 | 4423 | |
| 4424 | 4424 | const foreign_endian: Endian = if (native_endian == .big) .little else .big; |
| 4425 | 4425 | const expect = std.testing.expect; |
| 4426 | var prng = std.rand.DefaultPrng.init(1234); | |
| 4426 | var prng = std.Random.DefaultPrng.init(1234); | |
| 4427 | 4427 | const random = prng.random(); |
| 4428 | 4428 | |
| 4429 | 4429 | @setEvalBranchQuota(10_000); |
lib/std/priority_dequeue.zig+7-7| ... | ... | @@ -866,7 +866,7 @@ test "std.PriorityDequeue: shrinkAndFree" { |
| 866 | 866 | } |
| 867 | 867 | |
| 868 | 868 | test "std.PriorityDequeue: fuzz testing min" { |
| 869 | var prng = std.rand.DefaultPrng.init(0x12345678); | |
| 869 | var prng = std.Random.DefaultPrng.init(0x12345678); | |
| 870 | 870 | const random = prng.random(); |
| 871 | 871 | |
| 872 | 872 | const test_case_count = 100; |
| ... | ... | @@ -878,7 +878,7 @@ test "std.PriorityDequeue: fuzz testing min" { |
| 878 | 878 | } |
| 879 | 879 | } |
| 880 | 880 | |
| 881 | fn fuzzTestMin(rng: std.rand.Random, comptime queue_size: usize) !void { | |
| 881 | fn fuzzTestMin(rng: std.Random, comptime queue_size: usize) !void { | |
| 882 | 882 | const allocator = testing.allocator; |
| 883 | 883 | const items = try generateRandomSlice(allocator, rng, queue_size); |
| 884 | 884 | |
| ... | ... | @@ -895,7 +895,7 @@ fn fuzzTestMin(rng: std.rand.Random, comptime queue_size: usize) !void { |
| 895 | 895 | } |
| 896 | 896 | |
| 897 | 897 | test "std.PriorityDequeue: fuzz testing max" { |
| 898 | var prng = std.rand.DefaultPrng.init(0x87654321); | |
| 898 | var prng = std.Random.DefaultPrng.init(0x87654321); | |
| 899 | 899 | const random = prng.random(); |
| 900 | 900 | |
| 901 | 901 | const test_case_count = 100; |
| ... | ... | @@ -907,7 +907,7 @@ test "std.PriorityDequeue: fuzz testing max" { |
| 907 | 907 | } |
| 908 | 908 | } |
| 909 | 909 | |
| 910 | fn fuzzTestMax(rng: std.rand.Random, queue_size: usize) !void { | |
| 910 | fn fuzzTestMax(rng: std.Random, queue_size: usize) !void { | |
| 911 | 911 | const allocator = testing.allocator; |
| 912 | 912 | const items = try generateRandomSlice(allocator, rng, queue_size); |
| 913 | 913 | |
| ... | ... | @@ -924,7 +924,7 @@ fn fuzzTestMax(rng: std.rand.Random, queue_size: usize) !void { |
| 924 | 924 | } |
| 925 | 925 | |
| 926 | 926 | test "std.PriorityDequeue: fuzz testing min and max" { |
| 927 | var prng = std.rand.DefaultPrng.init(0x87654321); | |
| 927 | var prng = std.Random.DefaultPrng.init(0x87654321); | |
| 928 | 928 | const random = prng.random(); |
| 929 | 929 | |
| 930 | 930 | const test_case_count = 100; |
| ... | ... | @@ -936,7 +936,7 @@ test "std.PriorityDequeue: fuzz testing min and max" { |
| 936 | 936 | } |
| 937 | 937 | } |
| 938 | 938 | |
| 939 | fn fuzzTestMinMax(rng: std.rand.Random, queue_size: usize) !void { | |
| 939 | fn fuzzTestMinMax(rng: std.Random, queue_size: usize) !void { | |
| 940 | 940 | const allocator = testing.allocator; |
| 941 | 941 | const items = try generateRandomSlice(allocator, rng, queue_size); |
| 942 | 942 | |
| ... | ... | @@ -963,7 +963,7 @@ fn fuzzTestMinMax(rng: std.rand.Random, queue_size: usize) !void { |
| 963 | 963 | } |
| 964 | 964 | } |
| 965 | 965 | |
| 966 | fn generateRandomSlice(allocator: std.mem.Allocator, rng: std.rand.Random, size: usize) ![]u32 { | |
| 966 | fn generateRandomSlice(allocator: std.mem.Allocator, rng: std.Random, size: usize) ![]u32 { | |
| 967 | 967 | var array = std.ArrayList(u32).init(allocator); |
| 968 | 968 | try array.ensureTotalCapacity(size); |
| 969 | 969 |
lib/std/rand.zig deleted-460| ... | ... | @@ -1,460 +0,0 @@ |
| 1 | //! The engines provided here should be initialized from an external source. | |
| 2 | //! For a thread-local cryptographically secure pseudo random number generator, | |
| 3 | //! use `std.crypto.random`. | |
| 4 | //! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will | |
| 5 | //! be faster and use substantially less stack space. | |
| 6 | ||
| 7 | const std = @import("std.zig"); | |
| 8 | const builtin = @import("builtin"); | |
| 9 | const assert = std.debug.assert; | |
| 10 | const mem = std.mem; | |
| 11 | const math = std.math; | |
| 12 | const maxInt = std.math.maxInt; | |
| 13 | ||
| 14 | /// Fast unbiased random numbers. | |
| 15 | pub const DefaultPrng = Xoshiro256; | |
| 16 | ||
| 17 | /// Cryptographically secure random numbers. | |
| 18 | pub const DefaultCsprng = ChaCha; | |
| 19 | ||
| 20 | pub const Ascon = @import("rand/Ascon.zig"); | |
| 21 | pub const ChaCha = @import("rand/ChaCha.zig"); | |
| 22 | ||
| 23 | pub const Isaac64 = @import("rand/Isaac64.zig"); | |
| 24 | pub const Pcg = @import("rand/Pcg.zig"); | |
| 25 | pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig"); | |
| 26 | pub const Xoshiro256 = @import("rand/Xoshiro256.zig"); | |
| 27 | pub const Sfc64 = @import("rand/Sfc64.zig"); | |
| 28 | pub const RomuTrio = @import("rand/RomuTrio.zig"); | |
| 29 | pub const ziggurat = @import("rand/ziggurat.zig"); | |
| 30 | ||
| 31 | pub const Random = struct { | |
| 32 | ptr: *anyopaque, | |
| 33 | fillFn: *const fn (ptr: *anyopaque, buf: []u8) void, | |
| 34 | ||
| 35 | pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random { | |
| 36 | const Ptr = @TypeOf(pointer); | |
| 37 | assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer | |
| 38 | assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer | |
| 39 | assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct | |
| 40 | const gen = struct { | |
| 41 | fn fill(ptr: *anyopaque, buf: []u8) void { | |
| 42 | const self: Ptr = @ptrCast(@alignCast(ptr)); | |
| 43 | fillFn(self, buf); | |
| 44 | } | |
| 45 | }; | |
| 46 | ||
| 47 | return .{ | |
| 48 | .ptr = pointer, | |
| 49 | .fillFn = gen.fill, | |
| 50 | }; | |
| 51 | } | |
| 52 | ||
| 53 | /// Read random bytes into the specified buffer until full. | |
| 54 | pub fn bytes(r: Random, buf: []u8) void { | |
| 55 | r.fillFn(r.ptr, buf); | |
| 56 | } | |
| 57 | ||
| 58 | pub fn boolean(r: Random) bool { | |
| 59 | return r.int(u1) != 0; | |
| 60 | } | |
| 61 | ||
| 62 | /// Returns a random value from an enum, evenly distributed. | |
| 63 | /// | |
| 64 | /// Note that this will not yield consistent results across all targets | |
| 65 | /// due to dependence on the representation of `usize` as an index. | |
| 66 | /// See `enumValueWithIndex` for further commentary. | |
| 67 | pub inline fn enumValue(r: Random, comptime EnumType: type) EnumType { | |
| 68 | return r.enumValueWithIndex(EnumType, usize); | |
| 69 | } | |
| 70 | ||
| 71 | /// Returns a random value from an enum, evenly distributed. | |
| 72 | /// | |
| 73 | /// An index into an array of all named values is generated using the | |
| 74 | /// specified `Index` type to determine the return value. | |
| 75 | /// This allows for results to be independent of `usize` representation. | |
| 76 | /// | |
| 77 | /// Prefer `enumValue` if this isn't important. | |
| 78 | /// | |
| 79 | /// See `uintLessThan`, which this function uses in most cases, | |
| 80 | /// for commentary on the runtime of this function. | |
| 81 | pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: type) EnumType { | |
| 82 | comptime assert(@typeInfo(EnumType) == .Enum); | |
| 83 | ||
| 84 | // We won't use int -> enum casting because enum elements can have | |
| 85 | // arbitrary values. Instead we'll randomly pick one of the type's values. | |
| 86 | const values = comptime std.enums.values(EnumType); | |
| 87 | comptime assert(values.len > 0); // can't return anything | |
| 88 | comptime assert(maxInt(Index) >= values.len - 1); // can't access all values | |
| 89 | comptime if (values.len == 1) return values[0]; | |
| 90 | ||
| 91 | const index = if (comptime values.len - 1 == maxInt(Index)) | |
| 92 | r.int(Index) | |
| 93 | else | |
| 94 | r.uintLessThan(Index, values.len); | |
| 95 | ||
| 96 | const MinInt = MinArrayIndex(Index); | |
| 97 | return values[@as(MinInt, @intCast(index))]; | |
| 98 | } | |
| 99 | ||
| 100 | /// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`. | |
| 101 | /// `i` is evenly distributed. | |
| 102 | pub fn int(r: Random, comptime T: type) T { | |
| 103 | const bits = @typeInfo(T).Int.bits; | |
| 104 | const UnsignedT = std.meta.Int(.unsigned, bits); | |
| 105 | const ceil_bytes = comptime std.math.divCeil(u16, bits, 8) catch unreachable; | |
| 106 | const ByteAlignedT = std.meta.Int(.unsigned, ceil_bytes * 8); | |
| 107 | ||
| 108 | var rand_bytes: [ceil_bytes]u8 = undefined; | |
| 109 | r.bytes(&rand_bytes); | |
| 110 | ||
| 111 | // use LE instead of native endian for better portability maybe? | |
| 112 | // TODO: endian portability is pointless if the underlying prng isn't endian portable. | |
| 113 | // TODO: document the endian portability of this library. | |
| 114 | const byte_aligned_result = mem.readInt(ByteAlignedT, &rand_bytes, .little); | |
| 115 | const unsigned_result: UnsignedT = @truncate(byte_aligned_result); | |
| 116 | return @bitCast(unsigned_result); | |
| 117 | } | |
| 118 | ||
| 119 | /// Constant-time implementation off `uintLessThan`. | |
| 120 | /// The results of this function may be biased. | |
| 121 | pub fn uintLessThanBiased(r: Random, comptime T: type, less_than: T) T { | |
| 122 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 123 | assert(0 < less_than); | |
| 124 | return limitRangeBiased(T, r.int(T), less_than); | |
| 125 | } | |
| 126 | ||
| 127 | /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. | |
| 128 | /// This function assumes that the underlying `fillFn` produces evenly distributed values. | |
| 129 | /// Within this assumption, the runtime of this function is exponentially distributed. | |
| 130 | /// If `fillFn` were backed by a true random generator, | |
| 131 | /// the runtime of this function would technically be unbounded. | |
| 132 | /// However, if `fillFn` is backed by any evenly distributed pseudo random number generator, | |
| 133 | /// this function is guaranteed to return. | |
| 134 | /// If you need deterministic runtime bounds, use `uintLessThanBiased`. | |
| 135 | pub fn uintLessThan(r: Random, comptime T: type, less_than: T) T { | |
| 136 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 137 | const bits = @typeInfo(T).Int.bits; | |
| 138 | assert(0 < less_than); | |
| 139 | ||
| 140 | // adapted from: | |
| 141 | // http://www.pcg-random.org/posts/bounded-rands.html | |
| 142 | // "Lemire's (with an extra tweak from me)" | |
| 143 | var x = r.int(T); | |
| 144 | var m = math.mulWide(T, x, less_than); | |
| 145 | var l: T = @truncate(m); | |
| 146 | if (l < less_than) { | |
| 147 | var t = -%less_than; | |
| 148 | ||
| 149 | if (t >= less_than) { | |
| 150 | t -= less_than; | |
| 151 | if (t >= less_than) { | |
| 152 | t %= less_than; | |
| 153 | } | |
| 154 | } | |
| 155 | while (l < t) { | |
| 156 | x = r.int(T); | |
| 157 | m = math.mulWide(T, x, less_than); | |
| 158 | l = @truncate(m); | |
| 159 | } | |
| 160 | } | |
| 161 | return @intCast(m >> bits); | |
| 162 | } | |
| 163 | ||
| 164 | /// Constant-time implementation off `uintAtMost`. | |
| 165 | /// The results of this function may be biased. | |
| 166 | pub fn uintAtMostBiased(r: Random, comptime T: type, at_most: T) T { | |
| 167 | assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 168 | if (at_most == maxInt(T)) { | |
| 169 | // have the full range | |
| 170 | return r.int(T); | |
| 171 | } | |
| 172 | return r.uintLessThanBiased(T, at_most + 1); | |
| 173 | } | |
| 174 | ||
| 175 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. | |
| 176 | /// See `uintLessThan`, which this function uses in most cases, | |
| 177 | /// for commentary on the runtime of this function. | |
| 178 | pub fn uintAtMost(r: Random, comptime T: type, at_most: T) T { | |
| 179 | assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 180 | if (at_most == maxInt(T)) { | |
| 181 | // have the full range | |
| 182 | return r.int(T); | |
| 183 | } | |
| 184 | return r.uintLessThan(T, at_most + 1); | |
| 185 | } | |
| 186 | ||
| 187 | /// Constant-time implementation off `intRangeLessThan`. | |
| 188 | /// The results of this function may be biased. | |
| 189 | pub fn intRangeLessThanBiased(r: Random, comptime T: type, at_least: T, less_than: T) T { | |
| 190 | assert(at_least < less_than); | |
| 191 | const info = @typeInfo(T).Int; | |
| 192 | if (info.signedness == .signed) { | |
| 193 | // Two's complement makes this math pretty easy. | |
| 194 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 195 | const lo: UnsignedT = @bitCast(at_least); | |
| 196 | const hi: UnsignedT = @bitCast(less_than); | |
| 197 | const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); | |
| 198 | return @bitCast(result); | |
| 199 | } else { | |
| 200 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 201 | return at_least + r.uintLessThanBiased(T, less_than - at_least); | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. | |
| 206 | /// See `uintLessThan`, which this function uses in most cases, | |
| 207 | /// for commentary on the runtime of this function. | |
| 208 | pub fn intRangeLessThan(r: Random, comptime T: type, at_least: T, less_than: T) T { | |
| 209 | assert(at_least < less_than); | |
| 210 | const info = @typeInfo(T).Int; | |
| 211 | if (info.signedness == .signed) { | |
| 212 | // Two's complement makes this math pretty easy. | |
| 213 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 214 | const lo: UnsignedT = @bitCast(at_least); | |
| 215 | const hi: UnsignedT = @bitCast(less_than); | |
| 216 | const result = lo +% r.uintLessThan(UnsignedT, hi -% lo); | |
| 217 | return @bitCast(result); | |
| 218 | } else { | |
| 219 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 220 | return at_least + r.uintLessThan(T, less_than - at_least); | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | /// Constant-time implementation off `intRangeAtMostBiased`. | |
| 225 | /// The results of this function may be biased. | |
| 226 | pub fn intRangeAtMostBiased(r: Random, comptime T: type, at_least: T, at_most: T) T { | |
| 227 | assert(at_least <= at_most); | |
| 228 | const info = @typeInfo(T).Int; | |
| 229 | if (info.signedness == .signed) { | |
| 230 | // Two's complement makes this math pretty easy. | |
| 231 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 232 | const lo: UnsignedT = @bitCast(at_least); | |
| 233 | const hi: UnsignedT = @bitCast(at_most); | |
| 234 | const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); | |
| 235 | return @bitCast(result); | |
| 236 | } else { | |
| 237 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 238 | return at_least + r.uintAtMostBiased(T, at_most - at_least); | |
| 239 | } | |
| 240 | } | |
| 241 | ||
| 242 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. | |
| 243 | /// See `uintLessThan`, which this function uses in most cases, | |
| 244 | /// for commentary on the runtime of this function. | |
| 245 | pub fn intRangeAtMost(r: Random, comptime T: type, at_least: T, at_most: T) T { | |
| 246 | assert(at_least <= at_most); | |
| 247 | const info = @typeInfo(T).Int; | |
| 248 | if (info.signedness == .signed) { | |
| 249 | // Two's complement makes this math pretty easy. | |
| 250 | const UnsignedT = std.meta.Int(.unsigned, info.bits); | |
| 251 | const lo: UnsignedT = @bitCast(at_least); | |
| 252 | const hi: UnsignedT = @bitCast(at_most); | |
| 253 | const result = lo +% r.uintAtMost(UnsignedT, hi -% lo); | |
| 254 | return @bitCast(result); | |
| 255 | } else { | |
| 256 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | |
| 257 | return at_least + r.uintAtMost(T, at_most - at_least); | |
| 258 | } | |
| 259 | } | |
| 260 | ||
| 261 | /// Return a floating point value evenly distributed in the range [0, 1). | |
| 262 | pub fn float(r: Random, comptime T: type) T { | |
| 263 | // Generate a uniformly random value for the mantissa. | |
| 264 | // Then generate an exponentially biased random value for the exponent. | |
| 265 | // This covers every possible value in the range. | |
| 266 | switch (T) { | |
| 267 | f32 => { | |
| 268 | // Use 23 random bits for the mantissa, and the rest for the exponent. | |
| 269 | // If all 41 bits are zero, generate additional random bits, until a | |
| 270 | // set bit is found, or 126 bits have been generated. | |
| 271 | const rand = r.int(u64); | |
| 272 | var rand_lz = @clz(rand); | |
| 273 | if (rand_lz >= 41) { | |
| 274 | // TODO: when #5177 or #489 is implemented, | |
| 275 | // tell the compiler it is unlikely (1/2^41) to reach this point. | |
| 276 | // (Same for the if branch and the f64 calculations below.) | |
| 277 | rand_lz = 41 + @clz(r.int(u64)); | |
| 278 | if (rand_lz == 41 + 64) { | |
| 279 | // It is astronomically unlikely to reach this point. | |
| 280 | rand_lz += @clz(r.int(u32) | 0x7FF); | |
| 281 | } | |
| 282 | } | |
| 283 | const mantissa: u23 = @truncate(rand); | |
| 284 | const exponent = @as(u32, 126 - rand_lz) << 23; | |
| 285 | return @bitCast(exponent | mantissa); | |
| 286 | }, | |
| 287 | f64 => { | |
| 288 | // Use 52 random bits for the mantissa, and the rest for the exponent. | |
| 289 | // If all 12 bits are zero, generate additional random bits, until a | |
| 290 | // set bit is found, or 1022 bits have been generated. | |
| 291 | const rand = r.int(u64); | |
| 292 | var rand_lz: u64 = @clz(rand); | |
| 293 | if (rand_lz >= 12) { | |
| 294 | rand_lz = 12; | |
| 295 | while (true) { | |
| 296 | // It is astronomically unlikely for this loop to execute more than once. | |
| 297 | const addl_rand_lz = @clz(r.int(u64)); | |
| 298 | rand_lz += addl_rand_lz; | |
| 299 | if (addl_rand_lz != 64) { | |
| 300 | break; | |
| 301 | } | |
| 302 | if (rand_lz >= 1022) { | |
| 303 | rand_lz = 1022; | |
| 304 | break; | |
| 305 | } | |
| 306 | } | |
| 307 | } | |
| 308 | const mantissa = rand & 0xFFFFFFFFFFFFF; | |
| 309 | const exponent = (1022 - rand_lz) << 52; | |
| 310 | return @bitCast(exponent | mantissa); | |
| 311 | }, | |
| 312 | else => @compileError("unknown floating point type"), | |
| 313 | } | |
| 314 | } | |
| 315 | ||
| 316 | /// Return a floating point value normally distributed with mean = 0, stddev = 1. | |
| 317 | /// | |
| 318 | /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. | |
| 319 | pub fn floatNorm(r: Random, comptime T: type) T { | |
| 320 | const value = ziggurat.next_f64(r, ziggurat.NormDist); | |
| 321 | switch (T) { | |
| 322 | f32 => return @floatCast(value), | |
| 323 | f64 => return value, | |
| 324 | else => @compileError("unknown floating point type"), | |
| 325 | } | |
| 326 | } | |
| 327 | ||
| 328 | /// Return an exponentially distributed float with a rate parameter of 1. | |
| 329 | /// | |
| 330 | /// To use a different rate parameter, use: floatExp(...) / desiredRate. | |
| 331 | pub fn floatExp(r: Random, comptime T: type) T { | |
| 332 | const value = ziggurat.next_f64(r, ziggurat.ExpDist); | |
| 333 | switch (T) { | |
| 334 | f32 => return @floatCast(value), | |
| 335 | f64 => return value, | |
| 336 | else => @compileError("unknown floating point type"), | |
| 337 | } | |
| 338 | } | |
| 339 | ||
| 340 | /// Shuffle a slice into a random order. | |
| 341 | /// | |
| 342 | /// Note that this will not yield consistent results across all targets | |
| 343 | /// due to dependence on the representation of `usize` as an index. | |
| 344 | /// See `shuffleWithIndex` for further commentary. | |
| 345 | pub inline fn shuffle(r: Random, comptime T: type, buf: []T) void { | |
| 346 | r.shuffleWithIndex(T, buf, usize); | |
| 347 | } | |
| 348 | ||
| 349 | /// Shuffle a slice into a random order, using an index of a | |
| 350 | /// specified type to maintain distribution across targets. | |
| 351 | /// Asserts the index type can represent `buf.len`. | |
| 352 | /// | |
| 353 | /// Indexes into the slice are generated using the specified `Index` | |
| 354 | /// type, which determines distribution properties. This allows for | |
| 355 | /// results to be independent of `usize` representation. | |
| 356 | /// | |
| 357 | /// Prefer `shuffle` if this isn't important. | |
| 358 | /// | |
| 359 | /// See `intRangeLessThan`, which this function uses, | |
| 360 | /// for commentary on the runtime of this function. | |
| 361 | pub fn shuffleWithIndex(r: Random, comptime T: type, buf: []T, comptime Index: type) void { | |
| 362 | const MinInt = MinArrayIndex(Index); | |
| 363 | if (buf.len < 2) { | |
| 364 | return; | |
| 365 | } | |
| 366 | ||
| 367 | // `i <= j < max <= maxInt(MinInt)` | |
| 368 | const max: MinInt = @intCast(buf.len); | |
| 369 | var i: MinInt = 0; | |
| 370 | while (i < max - 1) : (i += 1) { | |
| 371 | const j: MinInt = @intCast(r.intRangeLessThan(Index, i, max)); | |
| 372 | mem.swap(T, &buf[i], &buf[j]); | |
| 373 | } | |
| 374 | } | |
| 375 | ||
| 376 | /// Randomly selects an index into `proportions`, where the likelihood of each | |
| 377 | /// index is weighted by that proportion. | |
| 378 | /// It is more likely for the index of the last proportion to be returned | |
| 379 | /// than the index of the first proportion in the slice, and vice versa. | |
| 380 | /// | |
| 381 | /// This is useful for selecting an item from a slice where weights are not equal. | |
| 382 | /// `T` must be a numeric type capable of holding the sum of `proportions`. | |
| 383 | pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []const T) usize { | |
| 384 | // This implementation works by summing the proportions and picking a | |
| 385 | // random point in [0, sum). We then loop over the proportions, | |
| 386 | // accumulating until our accumulator is greater than the random point. | |
| 387 | ||
| 388 | const sum = s: { | |
| 389 | var sum: T = 0; | |
| 390 | for (proportions) |v| sum += v; | |
| 391 | break :s sum; | |
| 392 | }; | |
| 393 | ||
| 394 | const point = switch (@typeInfo(T)) { | |
| 395 | .Int => |int_info| switch (int_info.signedness) { | |
| 396 | .signed => r.intRangeLessThan(T, 0, sum), | |
| 397 | .unsigned => r.uintLessThan(T, sum), | |
| 398 | }, | |
| 399 | // take care that imprecision doesn't lead to a value slightly greater than sum | |
| 400 | .Float => @min(r.float(T) * sum, sum - std.math.floatEps(T)), | |
| 401 | else => @compileError("weightedIndex does not support proportions of type " ++ | |
| 402 | @typeName(T)), | |
| 403 | }; | |
| 404 | ||
| 405 | assert(point < sum); | |
| 406 | ||
| 407 | var accumulator: T = 0; | |
| 408 | for (proportions, 0..) |p, index| { | |
| 409 | accumulator += p; | |
| 410 | if (point < accumulator) return index; | |
| 411 | } else unreachable; | |
| 412 | } | |
| 413 | ||
| 414 | /// Returns the smallest of `Index` and `usize`. | |
| 415 | fn MinArrayIndex(comptime Index: type) type { | |
| 416 | const index_info = @typeInfo(Index).Int; | |
| 417 | assert(index_info.signedness == .unsigned); | |
| 418 | return if (index_info.bits >= @typeInfo(usize).Int.bits) usize else Index; | |
| 419 | } | |
| 420 | }; | |
| 421 | ||
| 422 | /// Convert a random integer 0 <= random_int <= maxValue(T), | |
| 423 | /// into an integer 0 <= result < less_than. | |
| 424 | /// This function introduces a minor bias. | |
| 425 | pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { | |
| 426 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); | |
| 427 | const bits = @typeInfo(T).Int.bits; | |
| 428 | ||
| 429 | // adapted from: | |
| 430 | // http://www.pcg-random.org/posts/bounded-rands.html | |
| 431 | // "Integer Multiplication (Biased)" | |
| 432 | const m = math.mulWide(T, random_int, less_than); | |
| 433 | return @intCast(m >> bits); | |
| 434 | } | |
| 435 | ||
| 436 | // Generator to extend 64-bit seed values into longer sequences. | |
| 437 | // | |
| 438 | // The number of cycles is thus limited to 64-bits regardless of the engine, but this | |
| 439 | // is still plenty for practical purposes. | |
| 440 | pub const SplitMix64 = struct { | |
| 441 | s: u64, | |
| 442 | ||
| 443 | pub fn init(seed: u64) SplitMix64 { | |
| 444 | return SplitMix64{ .s = seed }; | |
| 445 | } | |
| 446 | ||
| 447 | pub fn next(self: *SplitMix64) u64 { | |
| 448 | self.s +%= 0x9e3779b97f4a7c15; | |
| 449 | ||
| 450 | var z = self.s; | |
| 451 | z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9; | |
| 452 | z = (z ^ (z >> 27)) *% 0x94d049bb133111eb; | |
| 453 | return z ^ (z >> 31); | |
| 454 | } | |
| 455 | }; | |
| 456 | ||
| 457 | test { | |
| 458 | std.testing.refAllDecls(@This()); | |
| 459 | _ = @import("rand/test.zig"); | |
| 460 | } |
lib/std/rand/Ascon.zig deleted-59| ... | ... | @@ -1,59 +0,0 @@ |
| 1 | //! CSPRNG based on the Reverie construction, a permutation-based PRNG | |
| 2 | //! with forward security, instantiated with the Ascon(128,12,8) permutation. | |
| 3 | //! | |
| 4 | //! Compared to ChaCha, this PRNG has a much smaller state, and can be | |
| 5 | //! a better choice for constrained environments. | |
| 6 | //! | |
| 7 | //! References: | |
| 8 | //! - A Robust and Sponge-Like PRNG with Improved Efficiency https://eprint.iacr.org/2016/886.pdf | |
| 9 | //! - Ascon https://ascon.iaik.tugraz.at/files/asconv12-nist.pdf | |
| 10 | ||
| 11 | const std = @import("std"); | |
| 12 | const mem = std.mem; | |
| 13 | const Random = std.rand.Random; | |
| 14 | const Self = @This(); | |
| 15 | ||
| 16 | const Ascon = std.crypto.core.Ascon(.little); | |
| 17 | ||
| 18 | state: Ascon, | |
| 19 | ||
| 20 | const rate = 16; | |
| 21 | pub const secret_seed_length = 32; | |
| 22 | ||
| 23 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. | |
| 24 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | |
| 25 | var self = Self{ .state = Ascon.initXof() }; | |
| 26 | self.addEntropy(&secret_seed); | |
| 27 | return self; | |
| 28 | } | |
| 29 | ||
| 30 | /// Inserts entropy to refresh the internal state. | |
| 31 | pub fn addEntropy(self: *Self, bytes: []const u8) void { | |
| 32 | comptime std.debug.assert(secret_seed_length % rate == 0); | |
| 33 | var i: usize = 0; | |
| 34 | while (i + rate < bytes.len) : (i += rate) { | |
| 35 | self.state.addBytes(bytes[i..][0..rate]); | |
| 36 | self.state.permuteR(8); | |
| 37 | } | |
| 38 | if (i != bytes.len) self.state.addBytes(bytes[i..]); | |
| 39 | self.state.permute(); | |
| 40 | } | |
| 41 | ||
| 42 | /// Returns a `std.rand.Random` structure backed by the current RNG. | |
| 43 | pub fn random(self: *Self) Random { | |
| 44 | return Random.init(self, fill); | |
| 45 | } | |
| 46 | ||
| 47 | /// Fills the buffer with random bytes. | |
| 48 | pub fn fill(self: *Self, buf: []u8) void { | |
| 49 | var i: usize = 0; | |
| 50 | while (true) { | |
| 51 | const left = buf.len - i; | |
| 52 | const n = @min(left, rate); | |
| 53 | self.state.extractBytes(buf[i..][0..n]); | |
| 54 | if (left == 0) break; | |
| 55 | self.state.permuteR(8); | |
| 56 | i += n; | |
| 57 | } | |
| 58 | self.state.permuteRatchet(6, rate); | |
| 59 | } |
lib/std/rand/ChaCha.zig deleted-98| ... | ... | @@ -1,98 +0,0 @@ |
| 1 | //! CSPRNG based on the ChaCha8 stream cipher, with forward security. | |
| 2 | //! | |
| 3 | //! References: | |
| 4 | //! - Fast-key-erasure random-number generators https://blog.cr.yp.to/20170723-random.html | |
| 5 | ||
| 6 | const std = @import("std"); | |
| 7 | const mem = std.mem; | |
| 8 | const Random = std.rand.Random; | |
| 9 | const Self = @This(); | |
| 10 | ||
| 11 | const Cipher = std.crypto.stream.chacha.ChaCha8IETF; | |
| 12 | ||
| 13 | const State = [8 * Cipher.block_length]u8; | |
| 14 | ||
| 15 | state: State, | |
| 16 | offset: usize, | |
| 17 | ||
| 18 | const nonce = [_]u8{0} ** Cipher.nonce_length; | |
| 19 | ||
| 20 | pub const secret_seed_length = Cipher.key_length; | |
| 21 | ||
| 22 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. | |
| 23 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | |
| 24 | var self = Self{ .state = undefined, .offset = 0 }; | |
| 25 | Cipher.stream(&self.state, 0, secret_seed, nonce); | |
| 26 | return self; | |
| 27 | } | |
| 28 | ||
| 29 | /// Inserts entropy to refresh the internal state. | |
| 30 | pub fn addEntropy(self: *Self, bytes: []const u8) void { | |
| 31 | var i: usize = 0; | |
| 32 | while (i + Cipher.key_length <= bytes.len) : (i += Cipher.key_length) { | |
| 33 | Cipher.xor( | |
| 34 | self.state[0..Cipher.key_length], | |
| 35 | self.state[0..Cipher.key_length], | |
| 36 | 0, | |
| 37 | bytes[i..][0..Cipher.key_length].*, | |
| 38 | nonce, | |
| 39 | ); | |
| 40 | } | |
| 41 | if (i < bytes.len) { | |
| 42 | var k = [_]u8{0} ** Cipher.key_length; | |
| 43 | const src = bytes[i..]; | |
| 44 | @memcpy(k[0..src.len], src); | |
| 45 | Cipher.xor( | |
| 46 | self.state[0..Cipher.key_length], | |
| 47 | self.state[0..Cipher.key_length], | |
| 48 | 0, | |
| 49 | k, | |
| 50 | nonce, | |
| 51 | ); | |
| 52 | } | |
| 53 | self.refill(); | |
| 54 | } | |
| 55 | ||
| 56 | /// Returns a `std.rand.Random` structure backed by the current RNG. | |
| 57 | pub fn random(self: *Self) Random { | |
| 58 | return Random.init(self, fill); | |
| 59 | } | |
| 60 | ||
| 61 | // Refills the buffer with random bytes, overwriting the previous key. | |
| 62 | fn refill(self: *Self) void { | |
| 63 | Cipher.stream(&self.state, 0, self.state[0..Cipher.key_length].*, nonce); | |
| 64 | self.offset = 0; | |
| 65 | } | |
| 66 | ||
| 67 | /// Fills the buffer with random bytes. | |
| 68 | pub fn fill(self: *Self, buf_: []u8) void { | |
| 69 | const bytes = self.state[Cipher.key_length..]; | |
| 70 | var buf = buf_; | |
| 71 | ||
| 72 | const avail = bytes.len - self.offset; | |
| 73 | if (avail > 0) { | |
| 74 | // Bytes from the current block | |
| 75 | const n = @min(avail, buf.len); | |
| 76 | @memcpy(buf[0..n], bytes[self.offset..][0..n]); | |
| 77 | @memset(bytes[self.offset..][0..n], 0); | |
| 78 | buf = buf[n..]; | |
| 79 | self.offset += n; | |
| 80 | } | |
| 81 | if (buf.len == 0) return; | |
| 82 | ||
| 83 | self.refill(); | |
| 84 | ||
| 85 | // Full blocks | |
| 86 | while (buf.len >= bytes.len) { | |
| 87 | @memcpy(buf[0..bytes.len], bytes); | |
| 88 | buf = buf[bytes.len..]; | |
| 89 | self.refill(); | |
| 90 | } | |
| 91 | ||
| 92 | // Remaining bytes | |
| 93 | if (buf.len > 0) { | |
| 94 | @memcpy(buf, bytes[0..buf.len]); | |
| 95 | @memset(bytes[0..buf.len], 0); | |
| 96 | self.offset = buf.len; | |
| 97 | } | |
| 98 | } |
lib/std/rand/Isaac64.zig deleted-235| ... | ... | @@ -1,235 +0,0 @@ |
| 1 | //! ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html | |
| 2 | //! | |
| 3 | //! Follows the general idea of the implementation from here with a few shortcuts. | |
| 4 | //! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html | |
| 5 | ||
| 6 | const std = @import("std"); | |
| 7 | const Random = std.rand.Random; | |
| 8 | const mem = std.mem; | |
| 9 | const Isaac64 = @This(); | |
| 10 | ||
| 11 | r: [256]u64, | |
| 12 | m: [256]u64, | |
| 13 | a: u64, | |
| 14 | b: u64, | |
| 15 | c: u64, | |
| 16 | i: usize, | |
| 17 | ||
| 18 | pub fn init(init_s: u64) Isaac64 { | |
| 19 | var isaac = Isaac64{ | |
| 20 | .r = undefined, | |
| 21 | .m = undefined, | |
| 22 | .a = undefined, | |
| 23 | .b = undefined, | |
| 24 | .c = undefined, | |
| 25 | .i = undefined, | |
| 26 | }; | |
| 27 | ||
| 28 | // seed == 0 => same result as the unseeded reference implementation | |
| 29 | isaac.seed(init_s, 1); | |
| 30 | return isaac; | |
| 31 | } | |
| 32 | ||
| 33 | pub fn random(self: *Isaac64) Random { | |
| 34 | return Random.init(self, fill); | |
| 35 | } | |
| 36 | ||
| 37 | fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { | |
| 38 | const x = self.m[base + m1]; | |
| 39 | self.a = mix +% self.m[base + m2]; | |
| 40 | ||
| 41 | const y = self.a +% self.b +% self.m[@as(usize, @intCast((x >> 3) % self.m.len))]; | |
| 42 | self.m[base + m1] = y; | |
| 43 | ||
| 44 | self.b = x +% self.m[@as(usize, @intCast((y >> 11) % self.m.len))]; | |
| 45 | self.r[self.r.len - 1 - base - m1] = self.b; | |
| 46 | } | |
| 47 | ||
| 48 | fn refill(self: *Isaac64) void { | |
| 49 | const midpoint = self.r.len / 2; | |
| 50 | ||
| 51 | self.c +%= 1; | |
| 52 | self.b +%= self.c; | |
| 53 | ||
| 54 | { | |
| 55 | var i: usize = 0; | |
| 56 | while (i < midpoint) : (i += 4) { | |
| 57 | self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); | |
| 58 | self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); | |
| 59 | self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); | |
| 60 | self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | { | |
| 65 | var i: usize = 0; | |
| 66 | while (i < midpoint) : (i += 4) { | |
| 67 | self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); | |
| 68 | self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); | |
| 69 | self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); | |
| 70 | self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | self.i = 0; | |
| 75 | } | |
| 76 | ||
| 77 | fn next(self: *Isaac64) u64 { | |
| 78 | if (self.i >= self.r.len) { | |
| 79 | self.refill(); | |
| 80 | } | |
| 81 | ||
| 82 | const value = self.r[self.i]; | |
| 83 | self.i += 1; | |
| 84 | return value; | |
| 85 | } | |
| 86 | ||
| 87 | fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { | |
| 88 | // We ignore the multi-pass requirement since we don't currently expose full access to | |
| 89 | // seeding the self.m array completely. | |
| 90 | @memset(self.m[0..], 0); | |
| 91 | self.m[0] = init_s; | |
| 92 | ||
| 93 | // prescrambled golden ratio constants | |
| 94 | var a = [_]u64{ | |
| 95 | 0x647c4677a2884b7c, | |
| 96 | 0xb9f8b322c73ac862, | |
| 97 | 0x8c0ea5053d4712a0, | |
| 98 | 0xb29b2e824a595524, | |
| 99 | 0x82f053db8355e0ce, | |
| 100 | 0x48fe4a0fa5a09315, | |
| 101 | 0xae985bf2cbfc89ed, | |
| 102 | 0x98f5704f6c44c0ab, | |
| 103 | }; | |
| 104 | ||
| 105 | comptime var i: usize = 0; | |
| 106 | inline while (i < rounds) : (i += 1) { | |
| 107 | var j: usize = 0; | |
| 108 | while (j < self.m.len) : (j += 8) { | |
| 109 | comptime var x1: usize = 0; | |
| 110 | inline while (x1 < 8) : (x1 += 1) { | |
| 111 | a[x1] +%= self.m[j + x1]; | |
| 112 | } | |
| 113 | ||
| 114 | a[0] -%= a[4]; | |
| 115 | a[5] ^= a[7] >> 9; | |
| 116 | a[7] +%= a[0]; | |
| 117 | a[1] -%= a[5]; | |
| 118 | a[6] ^= a[0] << 9; | |
| 119 | a[0] +%= a[1]; | |
| 120 | a[2] -%= a[6]; | |
| 121 | a[7] ^= a[1] >> 23; | |
| 122 | a[1] +%= a[2]; | |
| 123 | a[3] -%= a[7]; | |
| 124 | a[0] ^= a[2] << 15; | |
| 125 | a[2] +%= a[3]; | |
| 126 | a[4] -%= a[0]; | |
| 127 | a[1] ^= a[3] >> 14; | |
| 128 | a[3] +%= a[4]; | |
| 129 | a[5] -%= a[1]; | |
| 130 | a[2] ^= a[4] << 20; | |
| 131 | a[4] +%= a[5]; | |
| 132 | a[6] -%= a[2]; | |
| 133 | a[3] ^= a[5] >> 17; | |
| 134 | a[5] +%= a[6]; | |
| 135 | a[7] -%= a[3]; | |
| 136 | a[4] ^= a[6] << 14; | |
| 137 | a[6] +%= a[7]; | |
| 138 | ||
| 139 | comptime var x2: usize = 0; | |
| 140 | inline while (x2 < 8) : (x2 += 1) { | |
| 141 | self.m[j + x2] = a[x2]; | |
| 142 | } | |
| 143 | } | |
| 144 | } | |
| 145 | ||
| 146 | @memset(self.r[0..], 0); | |
| 147 | self.a = 0; | |
| 148 | self.b = 0; | |
| 149 | self.c = 0; | |
| 150 | self.i = self.r.len; // trigger refill on first value | |
| 151 | } | |
| 152 | ||
| 153 | pub fn fill(self: *Isaac64, buf: []u8) void { | |
| 154 | var i: usize = 0; | |
| 155 | const aligned_len = buf.len - (buf.len & 7); | |
| 156 | ||
| 157 | // Fill complete 64-byte segments | |
| 158 | while (i < aligned_len) : (i += 8) { | |
| 159 | var n = self.next(); | |
| 160 | comptime var j: usize = 0; | |
| 161 | inline while (j < 8) : (j += 1) { | |
| 162 | buf[i + j] = @as(u8, @truncate(n)); | |
| 163 | n >>= 8; | |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | // Fill trailing, ignoring excess (cut the stream). | |
| 168 | if (i != buf.len) { | |
| 169 | var n = self.next(); | |
| 170 | while (i < buf.len) : (i += 1) { | |
| 171 | buf[i] = @as(u8, @truncate(n)); | |
| 172 | n >>= 8; | |
| 173 | } | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | test "isaac64 sequence" { | |
| 178 | var r = Isaac64.init(0); | |
| 179 | ||
| 180 | // from reference implementation | |
| 181 | const seq = [_]u64{ | |
| 182 | 0xf67dfba498e4937c, | |
| 183 | 0x84a5066a9204f380, | |
| 184 | 0xfee34bd5f5514dbb, | |
| 185 | 0x4d1664739b8f80d6, | |
| 186 | 0x8607459ab52a14aa, | |
| 187 | 0x0e78bc5a98529e49, | |
| 188 | 0xfe5332822ad13777, | |
| 189 | 0x556c27525e33d01a, | |
| 190 | 0x08643ca615f3149f, | |
| 191 | 0xd0771faf3cb04714, | |
| 192 | 0x30e86f68a37b008d, | |
| 193 | 0x3074ebc0488a3adf, | |
| 194 | 0x270645ea7a2790bc, | |
| 195 | 0x5601a0a8d3763c6a, | |
| 196 | 0x2f83071f53f325dd, | |
| 197 | 0xb9090f3d42d2d2ea, | |
| 198 | }; | |
| 199 | ||
| 200 | for (seq) |s| { | |
| 201 | try std.testing.expect(s == r.next()); | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | test "isaac64 fill" { | |
| 206 | var r = Isaac64.init(0); | |
| 207 | ||
| 208 | // from reference implementation | |
| 209 | const seq = [_]u64{ | |
| 210 | 0xf67dfba498e4937c, | |
| 211 | 0x84a5066a9204f380, | |
| 212 | 0xfee34bd5f5514dbb, | |
| 213 | 0x4d1664739b8f80d6, | |
| 214 | 0x8607459ab52a14aa, | |
| 215 | 0x0e78bc5a98529e49, | |
| 216 | 0xfe5332822ad13777, | |
| 217 | 0x556c27525e33d01a, | |
| 218 | 0x08643ca615f3149f, | |
| 219 | 0xd0771faf3cb04714, | |
| 220 | 0x30e86f68a37b008d, | |
| 221 | 0x3074ebc0488a3adf, | |
| 222 | 0x270645ea7a2790bc, | |
| 223 | 0x5601a0a8d3763c6a, | |
| 224 | 0x2f83071f53f325dd, | |
| 225 | 0xb9090f3d42d2d2ea, | |
| 226 | }; | |
| 227 | ||
| 228 | for (seq) |s| { | |
| 229 | var buf0: [8]u8 = undefined; | |
| 230 | var buf1: [7]u8 = undefined; | |
| 231 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 232 | r.fill(&buf1); | |
| 233 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 234 | } | |
| 235 | } |
lib/std/rand/Pcg.zig deleted-122| ... | ... | @@ -1,122 +0,0 @@ |
| 1 | //! PCG32 - http://www.pcg-random.org/ | |
| 2 | //! | |
| 3 | //! PRNG | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const Random = std.rand.Random; | |
| 7 | const Pcg = @This(); | |
| 8 | ||
| 9 | const default_multiplier = 6364136223846793005; | |
| 10 | ||
| 11 | s: u64, | |
| 12 | i: u64, | |
| 13 | ||
| 14 | pub fn init(init_s: u64) Pcg { | |
| 15 | var pcg = Pcg{ | |
| 16 | .s = undefined, | |
| 17 | .i = undefined, | |
| 18 | }; | |
| 19 | ||
| 20 | pcg.seed(init_s); | |
| 21 | return pcg; | |
| 22 | } | |
| 23 | ||
| 24 | pub fn random(self: *Pcg) Random { | |
| 25 | return Random.init(self, fill); | |
| 26 | } | |
| 27 | ||
| 28 | fn next(self: *Pcg) u32 { | |
| 29 | const l = self.s; | |
| 30 | self.s = l *% default_multiplier +% (self.i | 1); | |
| 31 | ||
| 32 | const xor_s: u32 = @truncate(((l >> 18) ^ l) >> 27); | |
| 33 | const rot: u32 = @intCast(l >> 59); | |
| 34 | ||
| 35 | return (xor_s >> @as(u5, @intCast(rot))) | (xor_s << @as(u5, @intCast((0 -% rot) & 31))); | |
| 36 | } | |
| 37 | ||
| 38 | fn seed(self: *Pcg, init_s: u64) void { | |
| 39 | // Pcg requires 128-bits of seed. | |
| 40 | var gen = std.rand.SplitMix64.init(init_s); | |
| 41 | self.seedTwo(gen.next(), gen.next()); | |
| 42 | } | |
| 43 | ||
| 44 | fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { | |
| 45 | self.s = 0; | |
| 46 | self.i = (init_s << 1) | 1; | |
| 47 | self.s = self.s *% default_multiplier +% self.i; | |
| 48 | self.s +%= init_i; | |
| 49 | self.s = self.s *% default_multiplier +% self.i; | |
| 50 | } | |
| 51 | ||
| 52 | pub fn fill(self: *Pcg, buf: []u8) void { | |
| 53 | var i: usize = 0; | |
| 54 | const aligned_len = buf.len - (buf.len & 3); | |
| 55 | ||
| 56 | // Complete 4 byte segments. | |
| 57 | while (i < aligned_len) : (i += 4) { | |
| 58 | var n = self.next(); | |
| 59 | comptime var j: usize = 0; | |
| 60 | inline while (j < 4) : (j += 1) { | |
| 61 | buf[i + j] = @as(u8, @truncate(n)); | |
| 62 | n >>= 8; | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | // Remaining. (cuts the stream) | |
| 67 | if (i != buf.len) { | |
| 68 | var n = self.next(); | |
| 69 | while (i < buf.len) : (i += 1) { | |
| 70 | buf[i] = @as(u8, @truncate(n)); | |
| 71 | n >>= 8; | |
| 72 | } | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | test "pcg sequence" { | |
| 77 | var r = Pcg.init(0); | |
| 78 | const s0: u64 = 0x9394bf54ce5d79de; | |
| 79 | const s1: u64 = 0x84e9c579ef59bbf7; | |
| 80 | r.seedTwo(s0, s1); | |
| 81 | ||
| 82 | const seq = [_]u32{ | |
| 83 | 2881561918, | |
| 84 | 3063928540, | |
| 85 | 1199791034, | |
| 86 | 2487695858, | |
| 87 | 1479648952, | |
| 88 | 3247963454, | |
| 89 | }; | |
| 90 | ||
| 91 | for (seq) |s| { | |
| 92 | try std.testing.expect(s == r.next()); | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | test "pcg fill" { | |
| 97 | var r = Pcg.init(0); | |
| 98 | const s0: u64 = 0x9394bf54ce5d79de; | |
| 99 | const s1: u64 = 0x84e9c579ef59bbf7; | |
| 100 | r.seedTwo(s0, s1); | |
| 101 | ||
| 102 | const seq = [_]u32{ | |
| 103 | 2881561918, | |
| 104 | 3063928540, | |
| 105 | 1199791034, | |
| 106 | 2487695858, | |
| 107 | 1479648952, | |
| 108 | 3247963454, | |
| 109 | }; | |
| 110 | ||
| 111 | var i: u32 = 0; | |
| 112 | while (i < seq.len) : (i += 2) { | |
| 113 | var buf0: [8]u8 = undefined; | |
| 114 | std.mem.writeInt(u32, buf0[0..4], seq[i], .little); | |
| 115 | std.mem.writeInt(u32, buf0[4..8], seq[i + 1], .little); | |
| 116 | ||
| 117 | var buf1: [7]u8 = undefined; | |
| 118 | r.fill(&buf1); | |
| 119 | ||
| 120 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 121 | } | |
| 122 | } |
lib/std/rand/RomuTrio.zig deleted-132| ... | ... | @@ -1,132 +0,0 @@ |
| 1 | // Website: romu-random.org | |
| 2 | // Reference paper: http://arxiv.org/abs/2002.11331 | |
| 3 | // Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes. | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const Random = std.rand.Random; | |
| 7 | const math = std.math; | |
| 8 | const RomuTrio = @This(); | |
| 9 | ||
| 10 | x_state: u64, | |
| 11 | y_state: u64, | |
| 12 | z_state: u64, // set to nonzero seed | |
| 13 | ||
| 14 | pub fn init(init_s: u64) RomuTrio { | |
| 15 | var x = RomuTrio{ .x_state = undefined, .y_state = undefined, .z_state = undefined }; | |
| 16 | x.seed(init_s); | |
| 17 | return x; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn random(self: *RomuTrio) Random { | |
| 21 | return Random.init(self, fill); | |
| 22 | } | |
| 23 | ||
| 24 | fn next(self: *RomuTrio) u64 { | |
| 25 | const xp = self.x_state; | |
| 26 | const yp = self.y_state; | |
| 27 | const zp = self.z_state; | |
| 28 | self.x_state = 15241094284759029579 *% zp; | |
| 29 | self.y_state = yp -% xp; | |
| 30 | self.y_state = std.math.rotl(u64, self.y_state, 12); | |
| 31 | self.z_state = zp -% yp; | |
| 32 | self.z_state = std.math.rotl(u64, self.z_state, 44); | |
| 33 | return xp; | |
| 34 | } | |
| 35 | ||
| 36 | pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void { | |
| 37 | const seed_buf = @as([3]u64, @bitCast(buf)); | |
| 38 | self.x_state = seed_buf[0]; | |
| 39 | self.y_state = seed_buf[1]; | |
| 40 | self.z_state = seed_buf[2]; | |
| 41 | } | |
| 42 | ||
| 43 | pub fn seed(self: *RomuTrio, init_s: u64) void { | |
| 44 | // RomuTrio requires 192-bits of seed. | |
| 45 | var gen = std.rand.SplitMix64.init(init_s); | |
| 46 | ||
| 47 | self.x_state = gen.next(); | |
| 48 | self.y_state = gen.next(); | |
| 49 | self.z_state = gen.next(); | |
| 50 | } | |
| 51 | ||
| 52 | pub fn fill(self: *RomuTrio, buf: []u8) void { | |
| 53 | var i: usize = 0; | |
| 54 | const aligned_len = buf.len - (buf.len & 7); | |
| 55 | ||
| 56 | // Complete 8 byte segments. | |
| 57 | while (i < aligned_len) : (i += 8) { | |
| 58 | var n = self.next(); | |
| 59 | comptime var j: usize = 0; | |
| 60 | inline while (j < 8) : (j += 1) { | |
| 61 | buf[i + j] = @as(u8, @truncate(n)); | |
| 62 | n >>= 8; | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | // Remaining. (cuts the stream) | |
| 67 | if (i != buf.len) { | |
| 68 | var n = self.next(); | |
| 69 | while (i < buf.len) : (i += 1) { | |
| 70 | buf[i] = @as(u8, @truncate(n)); | |
| 71 | n >>= 8; | |
| 72 | } | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | test "RomuTrio sequence" { | |
| 77 | // Unfortunately there does not seem to be an official test sequence. | |
| 78 | var r = RomuTrio.init(0); | |
| 79 | ||
| 80 | const seq = [_]u64{ | |
| 81 | 16294208416658607535, | |
| 82 | 13964609475759908645, | |
| 83 | 4703697494102998476, | |
| 84 | 3425221541186733346, | |
| 85 | 2285772463536419399, | |
| 86 | 9454187757529463048, | |
| 87 | 13695907680080547496, | |
| 88 | 8328236714879408626, | |
| 89 | 12323357569716880909, | |
| 90 | 12375466223337721820, | |
| 91 | }; | |
| 92 | ||
| 93 | for (seq) |s| { | |
| 94 | try std.testing.expectEqual(s, r.next()); | |
| 95 | } | |
| 96 | } | |
| 97 | ||
| 98 | test "RomuTrio fill" { | |
| 99 | // Unfortunately there does not seem to be an official test sequence. | |
| 100 | var r = RomuTrio.init(0); | |
| 101 | ||
| 102 | const seq = [_]u64{ | |
| 103 | 16294208416658607535, | |
| 104 | 13964609475759908645, | |
| 105 | 4703697494102998476, | |
| 106 | 3425221541186733346, | |
| 107 | 2285772463536419399, | |
| 108 | 9454187757529463048, | |
| 109 | 13695907680080547496, | |
| 110 | 8328236714879408626, | |
| 111 | 12323357569716880909, | |
| 112 | 12375466223337721820, | |
| 113 | }; | |
| 114 | ||
| 115 | for (seq) |s| { | |
| 116 | var buf0: [8]u8 = undefined; | |
| 117 | var buf1: [7]u8 = undefined; | |
| 118 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 119 | r.fill(&buf1); | |
| 120 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | test "RomuTrio buf seeding test" { | |
| 125 | const buf0 = @as([24]u8, @bitCast([3]u64{ 16294208416658607535, 13964609475759908645, 4703697494102998476 })); | |
| 126 | const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 }; | |
| 127 | var r = RomuTrio.init(0); | |
| 128 | r.seedWithBuf(buf0); | |
| 129 | try std.testing.expect(r.x_state == resulting_state.x); | |
| 130 | try std.testing.expect(r.y_state == resulting_state.y); | |
| 131 | try std.testing.expect(r.z_state == resulting_state.z); | |
| 132 | } |
lib/std/rand/Sfc64.zig deleted-132| ... | ... | @@ -1,132 +0,0 @@ |
| 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 Random = std.rand.Random; | |
| 7 | const math = std.math; | |
| 8 | const Sfc64 = @This(); | |
| 9 | ||
| 10 | a: u64 = undefined, | |
| 11 | b: u64 = undefined, | |
| 12 | c: u64 = undefined, | |
| 13 | counter: u64 = undefined, | |
| 14 | ||
| 15 | const Rotation = 24; | |
| 16 | const RightShift = 11; | |
| 17 | const LeftShift = 3; | |
| 18 | ||
| 19 | pub fn init(init_s: u64) Sfc64 { | |
| 20 | var x = Sfc64{}; | |
| 21 | ||
| 22 | x.seed(init_s); | |
| 23 | return x; | |
| 24 | } | |
| 25 | ||
| 26 | pub fn random(self: *Sfc64) Random { | |
| 27 | return Random.init(self, fill); | |
| 28 | } | |
| 29 | ||
| 30 | fn next(self: *Sfc64) u64 { | |
| 31 | const tmp = self.a +% self.b +% self.counter; | |
| 32 | self.counter += 1; | |
| 33 | self.a = self.b ^ (self.b >> RightShift); | |
| 34 | self.b = self.c +% (self.c << LeftShift); | |
| 35 | self.c = math.rotl(u64, self.c, Rotation) +% tmp; | |
| 36 | return tmp; | |
| 37 | } | |
| 38 | ||
| 39 | fn seed(self: *Sfc64, init_s: u64) void { | |
| 40 | self.a = init_s; | |
| 41 | self.b = init_s; | |
| 42 | self.c = init_s; | |
| 43 | self.counter = 1; | |
| 44 | var i: u32 = 0; | |
| 45 | while (i < 12) : (i += 1) { | |
| 46 | _ = self.next(); | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | pub fn fill(self: *Sfc64, buf: []u8) void { | |
| 51 | var i: usize = 0; | |
| 52 | const aligned_len = buf.len - (buf.len & 7); | |
| 53 | ||
| 54 | // Complete 8 byte segments. | |
| 55 | while (i < aligned_len) : (i += 8) { | |
| 56 | var n = self.next(); | |
| 57 | comptime var j: usize = 0; | |
| 58 | inline while (j < 8) : (j += 1) { | |
| 59 | buf[i + j] = @as(u8, @truncate(n)); | |
| 60 | n >>= 8; | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | // Remaining. (cuts the stream) | |
| 65 | if (i != buf.len) { | |
| 66 | var n = self.next(); | |
| 67 | while (i < buf.len) : (i += 1) { | |
| 68 | buf[i] = @as(u8, @truncate(n)); | |
| 69 | n >>= 8; | |
| 70 | } | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | test "Sfc64 sequence" { | |
| 75 | // Unfortunately there does not seem to be an official test sequence. | |
| 76 | var r = Sfc64.init(0); | |
| 77 | ||
| 78 | const seq = [_]u64{ | |
| 79 | 0x3acfa029e3cc6041, | |
| 80 | 0xf5b6515bf2ee419c, | |
| 81 | 0x1259635894a29b61, | |
| 82 | 0xb6ae75395f8ebd6, | |
| 83 | 0x225622285ce302e2, | |
| 84 | 0x520d28611395cb21, | |
| 85 | 0xdb909c818901599d, | |
| 86 | 0x8ffd195365216f57, | |
| 87 | 0xe8c4ad5e258ac04a, | |
| 88 | 0x8f8ef2c89fdb63ca, | |
| 89 | 0xf9865b01d98d8e2f, | |
| 90 | 0x46555871a65d08ba, | |
| 91 | 0x66868677c6298fcd, | |
| 92 | 0x2ce15a7e6329f57d, | |
| 93 | 0xb2f1833ca91ca79, | |
| 94 | 0x4b0890ac9bf453ca, | |
| 95 | }; | |
| 96 | ||
| 97 | for (seq) |s| { | |
| 98 | try std.testing.expectEqual(s, r.next()); | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | test "Sfc64 fill" { | |
| 103 | // Unfortunately there does not seem to be an official test sequence. | |
| 104 | var r = Sfc64.init(0); | |
| 105 | ||
| 106 | const seq = [_]u64{ | |
| 107 | 0x3acfa029e3cc6041, | |
| 108 | 0xf5b6515bf2ee419c, | |
| 109 | 0x1259635894a29b61, | |
| 110 | 0xb6ae75395f8ebd6, | |
| 111 | 0x225622285ce302e2, | |
| 112 | 0x520d28611395cb21, | |
| 113 | 0xdb909c818901599d, | |
| 114 | 0x8ffd195365216f57, | |
| 115 | 0xe8c4ad5e258ac04a, | |
| 116 | 0x8f8ef2c89fdb63ca, | |
| 117 | 0xf9865b01d98d8e2f, | |
| 118 | 0x46555871a65d08ba, | |
| 119 | 0x66868677c6298fcd, | |
| 120 | 0x2ce15a7e6329f57d, | |
| 121 | 0xb2f1833ca91ca79, | |
| 122 | 0x4b0890ac9bf453ca, | |
| 123 | }; | |
| 124 | ||
| 125 | for (seq) |s| { | |
| 126 | var buf0: [8]u8 = undefined; | |
| 127 | var buf1: [7]u8 = undefined; | |
| 128 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 129 | r.fill(&buf1); | |
| 130 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 131 | } | |
| 132 | } |
lib/std/rand/Xoroshiro128.zig deleted-147| ... | ... | @@ -1,147 +0,0 @@ |
| 1 | //! Xoroshiro128+ - http://xoroshiro.di.unimi.it/ | |
| 2 | //! | |
| 3 | //! PRNG | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const Random = std.rand.Random; | |
| 7 | const math = std.math; | |
| 8 | const Xoroshiro128 = @This(); | |
| 9 | ||
| 10 | s: [2]u64, | |
| 11 | ||
| 12 | pub fn init(init_s: u64) Xoroshiro128 { | |
| 13 | var x = Xoroshiro128{ .s = undefined }; | |
| 14 | ||
| 15 | x.seed(init_s); | |
| 16 | return x; | |
| 17 | } | |
| 18 | ||
| 19 | pub fn random(self: *Xoroshiro128) Random { | |
| 20 | return Random.init(self, fill); | |
| 21 | } | |
| 22 | ||
| 23 | pub fn next(self: *Xoroshiro128) u64 { | |
| 24 | const s0 = self.s[0]; | |
| 25 | var s1 = self.s[1]; | |
| 26 | const r = s0 +% s1; | |
| 27 | ||
| 28 | s1 ^= s0; | |
| 29 | self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14); | |
| 30 | self.s[1] = math.rotl(u64, s1, @as(u8, 36)); | |
| 31 | ||
| 32 | return r; | |
| 33 | } | |
| 34 | ||
| 35 | // Skip 2^64 places ahead in the sequence | |
| 36 | pub fn jump(self: *Xoroshiro128) void { | |
| 37 | var s0: u64 = 0; | |
| 38 | var s1: u64 = 0; | |
| 39 | ||
| 40 | const table = [_]u64{ | |
| 41 | 0xbeac0467eba5facb, | |
| 42 | 0xd86b048b86aa9922, | |
| 43 | }; | |
| 44 | ||
| 45 | inline for (table) |entry| { | |
| 46 | var b: usize = 0; | |
| 47 | while (b < 64) : (b += 1) { | |
| 48 | if ((entry & (@as(u64, 1) << @as(u6, @intCast(b)))) != 0) { | |
| 49 | s0 ^= self.s[0]; | |
| 50 | s1 ^= self.s[1]; | |
| 51 | } | |
| 52 | _ = self.next(); | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | self.s[0] = s0; | |
| 57 | self.s[1] = s1; | |
| 58 | } | |
| 59 | ||
| 60 | pub fn seed(self: *Xoroshiro128, init_s: u64) void { | |
| 61 | // Xoroshiro requires 128-bits of seed. | |
| 62 | var gen = std.rand.SplitMix64.init(init_s); | |
| 63 | ||
| 64 | self.s[0] = gen.next(); | |
| 65 | self.s[1] = gen.next(); | |
| 66 | } | |
| 67 | ||
| 68 | pub fn fill(self: *Xoroshiro128, buf: []u8) void { | |
| 69 | var i: usize = 0; | |
| 70 | const aligned_len = buf.len - (buf.len & 7); | |
| 71 | ||
| 72 | // Complete 8 byte segments. | |
| 73 | while (i < aligned_len) : (i += 8) { | |
| 74 | var n = self.next(); | |
| 75 | comptime var j: usize = 0; | |
| 76 | inline while (j < 8) : (j += 1) { | |
| 77 | buf[i + j] = @as(u8, @truncate(n)); | |
| 78 | n >>= 8; | |
| 79 | } | |
| 80 | } | |
| 81 | ||
| 82 | // Remaining. (cuts the stream) | |
| 83 | if (i != buf.len) { | |
| 84 | var n = self.next(); | |
| 85 | while (i < buf.len) : (i += 1) { | |
| 86 | buf[i] = @as(u8, @truncate(n)); | |
| 87 | n >>= 8; | |
| 88 | } | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | test "xoroshiro sequence" { | |
| 93 | var r = Xoroshiro128.init(0); | |
| 94 | r.s[0] = 0xaeecf86f7878dd75; | |
| 95 | r.s[1] = 0x01cd153642e72622; | |
| 96 | ||
| 97 | const seq1 = [_]u64{ | |
| 98 | 0xb0ba0da5bb600397, | |
| 99 | 0x18a08afde614dccc, | |
| 100 | 0xa2635b956a31b929, | |
| 101 | 0xabe633c971efa045, | |
| 102 | 0x9ac19f9706ca3cac, | |
| 103 | 0xf62b426578c1e3fb, | |
| 104 | }; | |
| 105 | ||
| 106 | for (seq1) |s| { | |
| 107 | try std.testing.expect(s == r.next()); | |
| 108 | } | |
| 109 | ||
| 110 | r.jump(); | |
| 111 | ||
| 112 | const seq2 = [_]u64{ | |
| 113 | 0x95344a13556d3e22, | |
| 114 | 0xb4fb32dafa4d00df, | |
| 115 | 0xb2011d9ccdcfe2dd, | |
| 116 | 0x05679a9b2119b908, | |
| 117 | 0xa860a1da7c9cd8a0, | |
| 118 | 0x658a96efe3f86550, | |
| 119 | }; | |
| 120 | ||
| 121 | for (seq2) |s| { | |
| 122 | try std.testing.expect(s == r.next()); | |
| 123 | } | |
| 124 | } | |
| 125 | ||
| 126 | test "xoroshiro fill" { | |
| 127 | var r = Xoroshiro128.init(0); | |
| 128 | r.s[0] = 0xaeecf86f7878dd75; | |
| 129 | r.s[1] = 0x01cd153642e72622; | |
| 130 | ||
| 131 | const seq = [_]u64{ | |
| 132 | 0xb0ba0da5bb600397, | |
| 133 | 0x18a08afde614dccc, | |
| 134 | 0xa2635b956a31b929, | |
| 135 | 0xabe633c971efa045, | |
| 136 | 0x9ac19f9706ca3cac, | |
| 137 | 0xf62b426578c1e3fb, | |
| 138 | }; | |
| 139 | ||
| 140 | for (seq) |s| { | |
| 141 | var buf0: [8]u8 = undefined; | |
| 142 | var buf1: [7]u8 = undefined; | |
| 143 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 144 | r.fill(&buf1); | |
| 145 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 146 | } | |
| 147 | } |
lib/std/rand/Xoshiro256.zig deleted-146| ... | ... | @@ -1,146 +0,0 @@ |
| 1 | //! Xoshiro256++ - http://xoroshiro.di.unimi.it/ | |
| 2 | //! | |
| 3 | //! PRNG | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const Random = std.rand.Random; | |
| 7 | const math = std.math; | |
| 8 | const Xoshiro256 = @This(); | |
| 9 | ||
| 10 | s: [4]u64, | |
| 11 | ||
| 12 | pub fn init(init_s: u64) Xoshiro256 { | |
| 13 | var x = Xoshiro256{ | |
| 14 | .s = undefined, | |
| 15 | }; | |
| 16 | ||
| 17 | x.seed(init_s); | |
| 18 | return x; | |
| 19 | } | |
| 20 | ||
| 21 | pub fn random(self: *Xoshiro256) Random { | |
| 22 | return Random.init(self, fill); | |
| 23 | } | |
| 24 | ||
| 25 | pub fn next(self: *Xoshiro256) u64 { | |
| 26 | const r = math.rotl(u64, self.s[0] +% self.s[3], 23) +% self.s[0]; | |
| 27 | ||
| 28 | const t = self.s[1] << 17; | |
| 29 | ||
| 30 | self.s[2] ^= self.s[0]; | |
| 31 | self.s[3] ^= self.s[1]; | |
| 32 | self.s[1] ^= self.s[2]; | |
| 33 | self.s[0] ^= self.s[3]; | |
| 34 | ||
| 35 | self.s[2] ^= t; | |
| 36 | ||
| 37 | self.s[3] = math.rotl(u64, self.s[3], 45); | |
| 38 | ||
| 39 | return r; | |
| 40 | } | |
| 41 | ||
| 42 | // Skip 2^128 places ahead in the sequence | |
| 43 | pub fn jump(self: *Xoshiro256) void { | |
| 44 | var s: u256 = 0; | |
| 45 | ||
| 46 | var table: u256 = 0x39abdc4529b1661ca9582618e03fc9aad5a61266f0c9392c180ec6d33cfd0aba; | |
| 47 | ||
| 48 | while (table != 0) : (table >>= 1) { | |
| 49 | if (@as(u1, @truncate(table)) != 0) { | |
| 50 | s ^= @as(u256, @bitCast(self.s)); | |
| 51 | } | |
| 52 | _ = self.next(); | |
| 53 | } | |
| 54 | ||
| 55 | self.s = @as([4]u64, @bitCast(s)); | |
| 56 | } | |
| 57 | ||
| 58 | pub fn seed(self: *Xoshiro256, init_s: u64) void { | |
| 59 | // Xoshiro requires 256-bits of seed. | |
| 60 | var gen = std.rand.SplitMix64.init(init_s); | |
| 61 | ||
| 62 | self.s[0] = gen.next(); | |
| 63 | self.s[1] = gen.next(); | |
| 64 | self.s[2] = gen.next(); | |
| 65 | self.s[3] = gen.next(); | |
| 66 | } | |
| 67 | ||
| 68 | pub fn fill(self: *Xoshiro256, buf: []u8) void { | |
| 69 | var i: usize = 0; | |
| 70 | const aligned_len = buf.len - (buf.len & 7); | |
| 71 | ||
| 72 | // Complete 8 byte segments. | |
| 73 | while (i < aligned_len) : (i += 8) { | |
| 74 | var n = self.next(); | |
| 75 | comptime var j: usize = 0; | |
| 76 | inline while (j < 8) : (j += 1) { | |
| 77 | buf[i + j] = @as(u8, @truncate(n)); | |
| 78 | n >>= 8; | |
| 79 | } | |
| 80 | } | |
| 81 | ||
| 82 | // Remaining. (cuts the stream) | |
| 83 | if (i != buf.len) { | |
| 84 | var n = self.next(); | |
| 85 | while (i < buf.len) : (i += 1) { | |
| 86 | buf[i] = @as(u8, @truncate(n)); | |
| 87 | n >>= 8; | |
| 88 | } | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | test "xoroshiro sequence" { | |
| 93 | if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; | |
| 94 | if (@import("builtin").zig_backend == .stage2_x86_64) return error.SkipZigTest; | |
| 95 | ||
| 96 | var r = Xoshiro256.init(0); | |
| 97 | ||
| 98 | const seq1 = [_]u64{ | |
| 99 | 0x53175d61490b23df, | |
| 100 | 0x61da6f3dc380d507, | |
| 101 | 0x5c0fdf91ec9a7bfc, | |
| 102 | 0x02eebf8c3bbe5e1a, | |
| 103 | 0x7eca04ebaf4a5eea, | |
| 104 | 0x0543c37757f08d9a, | |
| 105 | }; | |
| 106 | ||
| 107 | for (seq1) |s| { | |
| 108 | try std.testing.expect(s == r.next()); | |
| 109 | } | |
| 110 | ||
| 111 | r.jump(); | |
| 112 | ||
| 113 | const seq2 = [_]u64{ | |
| 114 | 0xae1db5c5e27807be, | |
| 115 | 0xb584c6a7fd8709fe, | |
| 116 | 0xc46a0ee9330fb6e, | |
| 117 | 0xdc0c9606f49ed76e, | |
| 118 | 0x1f5bb6540f6651fb, | |
| 119 | 0x72fa2ca734601488, | |
| 120 | }; | |
| 121 | ||
| 122 | for (seq2) |s| { | |
| 123 | try std.testing.expect(s == r.next()); | |
| 124 | } | |
| 125 | } | |
| 126 | ||
| 127 | test "xoroshiro fill" { | |
| 128 | var r = Xoshiro256.init(0); | |
| 129 | ||
| 130 | const seq = [_]u64{ | |
| 131 | 0x53175d61490b23df, | |
| 132 | 0x61da6f3dc380d507, | |
| 133 | 0x5c0fdf91ec9a7bfc, | |
| 134 | 0x02eebf8c3bbe5e1a, | |
| 135 | 0x7eca04ebaf4a5eea, | |
| 136 | 0x0543c37757f08d9a, | |
| 137 | }; | |
| 138 | ||
| 139 | for (seq) |s| { | |
| 140 | var buf0: [8]u8 = undefined; | |
| 141 | var buf1: [7]u8 = undefined; | |
| 142 | std.mem.writeInt(u64, &buf0, s, .little); | |
| 143 | r.fill(&buf1); | |
| 144 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); | |
| 145 | } | |
| 146 | } |
lib/std/rand/benchmark.zig deleted-217| ... | ... | @@ -1,217 +0,0 @@ |
| 1 | // zig run -O ReleaseFast --zig-lib-dir ../.. benchmark.zig | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const builtin = @import("builtin"); | |
| 5 | const time = std.time; | |
| 6 | const Timer = time.Timer; | |
| 7 | const rand = std.rand; | |
| 8 | ||
| 9 | const KiB = 1024; | |
| 10 | const MiB = 1024 * KiB; | |
| 11 | const GiB = 1024 * MiB; | |
| 12 | ||
| 13 | const Rng = struct { | |
| 14 | ty: type, | |
| 15 | name: []const u8, | |
| 16 | init_u8s: ?[]const u8 = null, | |
| 17 | init_u64: ?u64 = null, | |
| 18 | }; | |
| 19 | ||
| 20 | const prngs = [_]Rng{ | |
| 21 | Rng{ | |
| 22 | .ty = rand.Isaac64, | |
| 23 | .name = "isaac64", | |
| 24 | .init_u64 = 0, | |
| 25 | }, | |
| 26 | Rng{ | |
| 27 | .ty = rand.Pcg, | |
| 28 | .name = "pcg", | |
| 29 | .init_u64 = 0, | |
| 30 | }, | |
| 31 | Rng{ | |
| 32 | .ty = rand.RomuTrio, | |
| 33 | .name = "romutrio", | |
| 34 | .init_u64 = 0, | |
| 35 | }, | |
| 36 | Rng{ | |
| 37 | .ty = std.rand.Sfc64, | |
| 38 | .name = "sfc64", | |
| 39 | .init_u64 = 0, | |
| 40 | }, | |
| 41 | Rng{ | |
| 42 | .ty = std.rand.Xoroshiro128, | |
| 43 | .name = "xoroshiro128", | |
| 44 | .init_u64 = 0, | |
| 45 | }, | |
| 46 | Rng{ | |
| 47 | .ty = std.rand.Xoshiro256, | |
| 48 | .name = "xoshiro256", | |
| 49 | .init_u64 = 0, | |
| 50 | }, | |
| 51 | }; | |
| 52 | ||
| 53 | const csprngs = [_]Rng{ | |
| 54 | Rng{ | |
| 55 | .ty = rand.Ascon, | |
| 56 | .name = "ascon", | |
| 57 | .init_u8s = &[_]u8{0} ** 32, | |
| 58 | }, | |
| 59 | Rng{ | |
| 60 | .ty = rand.ChaCha, | |
| 61 | .name = "chacha", | |
| 62 | .init_u8s = &[_]u8{0} ** 32, | |
| 63 | }, | |
| 64 | }; | |
| 65 | ||
| 66 | const Result = struct { | |
| 67 | throughput: u64, | |
| 68 | }; | |
| 69 | ||
| 70 | const long_block_size: usize = 8 * 8192; | |
| 71 | const short_block_size: usize = 8; | |
| 72 | ||
| 73 | pub fn benchmark(comptime H: anytype, bytes: usize, comptime block_size: usize) !Result { | |
| 74 | var rng = blk: { | |
| 75 | if (H.init_u8s) |init| { | |
| 76 | break :blk H.ty.init(init[0..].*); | |
| 77 | } | |
| 78 | if (H.init_u64) |init| { | |
| 79 | break :blk H.ty.init(init); | |
| 80 | } | |
| 81 | break :blk H.ty.init(); | |
| 82 | }; | |
| 83 | ||
| 84 | var block: [block_size]u8 = undefined; | |
| 85 | ||
| 86 | var offset: usize = 0; | |
| 87 | var timer = try Timer.start(); | |
| 88 | const start = timer.lap(); | |
| 89 | while (offset < bytes) : (offset += block.len) { | |
| 90 | rng.fill(block[0..]); | |
| 91 | } | |
| 92 | const end = timer.read(); | |
| 93 | ||
| 94 | const elapsed_s = @as(f64, @floatFromInt(end - start)) / time.ns_per_s; | |
| 95 | const throughput = @as(u64, @intFromFloat(@as(f64, @floatFromInt(bytes)) / elapsed_s)); | |
| 96 | ||
| 97 | std.debug.assert(rng.random().int(u64) != 0); | |
| 98 | ||
| 99 | return Result{ | |
| 100 | .throughput = throughput, | |
| 101 | }; | |
| 102 | } | |
| 103 | ||
| 104 | fn usage() void { | |
| 105 | std.debug.print( | |
| 106 | \\throughput_test [options] | |
| 107 | \\ | |
| 108 | \\Options: | |
| 109 | \\ --filter [test-name] | |
| 110 | \\ --count [int] | |
| 111 | \\ --prngs-only | |
| 112 | \\ --csprngs-only | |
| 113 | \\ --short-only | |
| 114 | \\ --long-only | |
| 115 | \\ --help | |
| 116 | \\ | |
| 117 | , .{}); | |
| 118 | } | |
| 119 | ||
| 120 | fn mode(comptime x: comptime_int) comptime_int { | |
| 121 | return if (builtin.mode == .Debug) x / 64 else x; | |
| 122 | } | |
| 123 | ||
| 124 | pub fn main() !void { | |
| 125 | const stdout = std.io.getStdOut().writer(); | |
| 126 | ||
| 127 | var buffer: [1024]u8 = undefined; | |
| 128 | var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); | |
| 129 | const args = try std.process.argsAlloc(fixed.allocator()); | |
| 130 | ||
| 131 | var filter: ?[]u8 = ""; | |
| 132 | var count: usize = mode(128 * MiB); | |
| 133 | var bench_prngs = true; | |
| 134 | var bench_csprngs = true; | |
| 135 | var bench_long = true; | |
| 136 | var bench_short = true; | |
| 137 | ||
| 138 | var i: usize = 1; | |
| 139 | while (i < args.len) : (i += 1) { | |
| 140 | if (std.mem.eql(u8, args[i], "--mode")) { | |
| 141 | try stdout.print("{}\n", .{builtin.mode}); | |
| 142 | return; | |
| 143 | } else if (std.mem.eql(u8, args[i], "--filter")) { | |
| 144 | i += 1; | |
| 145 | if (i == args.len) { | |
| 146 | usage(); | |
| 147 | std.os.exit(1); | |
| 148 | } | |
| 149 | ||
| 150 | filter = args[i]; | |
| 151 | } else if (std.mem.eql(u8, args[i], "--count")) { | |
| 152 | i += 1; | |
| 153 | if (i == args.len) { | |
| 154 | usage(); | |
| 155 | std.os.exit(1); | |
| 156 | } | |
| 157 | ||
| 158 | const c = try std.fmt.parseUnsigned(usize, args[i], 10); | |
| 159 | count = c * MiB; | |
| 160 | } else if (std.mem.eql(u8, args[i], "--csprngs-only")) { | |
| 161 | bench_prngs = false; | |
| 162 | } else if (std.mem.eql(u8, args[i], "--prngs-only")) { | |
| 163 | bench_csprngs = false; | |
| 164 | } else if (std.mem.eql(u8, args[i], "--short-only")) { | |
| 165 | bench_long = false; | |
| 166 | } else if (std.mem.eql(u8, args[i], "--long-only")) { | |
| 167 | bench_short = false; | |
| 168 | } else if (std.mem.eql(u8, args[i], "--help")) { | |
| 169 | usage(); | |
| 170 | return; | |
| 171 | } else { | |
| 172 | usage(); | |
| 173 | std.os.exit(1); | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | if (bench_prngs) { | |
| 178 | if (bench_long) { | |
| 179 | inline for (prngs) |R| { | |
| 180 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 181 | try stdout.print("{s} (long outputs)\n", .{R.name}); | |
| 182 | const result_long = try benchmark(R, count, long_block_size); | |
| 183 | try stdout.print(" {:5} MiB/s\n", .{result_long.throughput / (1 * MiB)}); | |
| 184 | } | |
| 185 | } | |
| 186 | } | |
| 187 | if (bench_short) { | |
| 188 | inline for (prngs) |R| { | |
| 189 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 190 | try stdout.print("{s} (short outputs)\n", .{R.name}); | |
| 191 | const result_short = try benchmark(R, count, short_block_size); | |
| 192 | try stdout.print(" {:5} MiB/s\n", .{result_short.throughput / (1 * MiB)}); | |
| 193 | } | |
| 194 | } | |
| 195 | } | |
| 196 | } | |
| 197 | if (bench_csprngs) { | |
| 198 | if (bench_long) { | |
| 199 | inline for (csprngs) |R| { | |
| 200 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 201 | try stdout.print("{s} (cryptographic, long outputs)\n", .{R.name}); | |
| 202 | const result_long = try benchmark(R, count, long_block_size); | |
| 203 | try stdout.print(" {:5} MiB/s\n", .{result_long.throughput / (1 * MiB)}); | |
| 204 | } | |
| 205 | } | |
| 206 | } | |
| 207 | if (bench_short) { | |
| 208 | inline for (csprngs) |R| { | |
| 209 | if (filter == null or std.mem.indexOf(u8, R.name, filter.?) != null) { | |
| 210 | try stdout.print("{s} (cryptographic, short outputs)\n", .{R.name}); | |
| 211 | const result_short = try benchmark(R, count, short_block_size); | |
| 212 | try stdout.print(" {:5} MiB/s\n", .{result_short.throughput / (1 * MiB)}); | |
| 213 | } | |
| 214 | } | |
| 215 | } | |
| 216 | } | |
| 217 | } |
lib/std/rand/test.zig deleted-473| ... | ... | @@ -1,473 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const math = std.math; | |
| 3 | const DefaultPrng = std.rand.DefaultPrng; | |
| 4 | const Random = std.rand.Random; | |
| 5 | const SplitMix64 = std.rand.SplitMix64; | |
| 6 | const DefaultCsprng = std.rand.DefaultCsprng; | |
| 7 | const expect = std.testing.expect; | |
| 8 | const expectEqual = std.testing.expectEqual; | |
| 9 | ||
| 10 | const SequentialPrng = struct { | |
| 11 | const Self = @This(); | |
| 12 | next_value: u8, | |
| 13 | ||
| 14 | pub fn init() Self { | |
| 15 | return Self{ | |
| 16 | .next_value = 0, | |
| 17 | }; | |
| 18 | } | |
| 19 | ||
| 20 | pub fn random(self: *Self) Random { | |
| 21 | return Random.init(self, fill); | |
| 22 | } | |
| 23 | ||
| 24 | pub fn fill(self: *Self, buf: []u8) void { | |
| 25 | for (buf) |*b| { | |
| 26 | b.* = self.next_value; | |
| 27 | } | |
| 28 | self.next_value +%= 1; | |
| 29 | } | |
| 30 | }; | |
| 31 | ||
| 32 | /// Do not use this PRNG! It is meant to be predictable, for the purposes of test reproducibility and coverage. | |
| 33 | /// Its output is just a repeat of a user-specified byte pattern. | |
| 34 | /// Name is a reference to this comic: https://dilbert.com/strip/2001-10-25 | |
| 35 | const Dilbert = struct { | |
| 36 | pattern: []const u8 = undefined, | |
| 37 | curr_idx: usize = 0, | |
| 38 | ||
| 39 | pub fn init(pattern: []const u8) !Dilbert { | |
| 40 | if (pattern.len == 0) | |
| 41 | return error.EmptyPattern; | |
| 42 | var self = Dilbert{}; | |
| 43 | self.pattern = pattern; | |
| 44 | self.curr_idx = 0; | |
| 45 | return self; | |
| 46 | } | |
| 47 | ||
| 48 | pub fn random(self: *Dilbert) Random { | |
| 49 | return Random.init(self, fill); | |
| 50 | } | |
| 51 | ||
| 52 | pub fn fill(self: *Dilbert, buf: []u8) void { | |
| 53 | for (buf) |*byte| { | |
| 54 | byte.* = self.pattern[self.curr_idx]; | |
| 55 | self.curr_idx = (self.curr_idx + 1) % self.pattern.len; | |
| 56 | } | |
| 57 | } | |
| 58 | ||
| 59 | test "Dilbert fill" { | |
| 60 | var r = try Dilbert.init("9nine"); | |
| 61 | ||
| 62 | const seq = [_]u64{ | |
| 63 | 0x396E696E65396E69, | |
| 64 | 0x6E65396E696E6539, | |
| 65 | 0x6E696E65396E696E, | |
| 66 | 0x65396E696E65396E, | |
| 67 | 0x696E65396E696E65, | |
| 68 | }; | |
| 69 | ||
| 70 | for (seq) |s| { | |
| 71 | var buf0: [8]u8 = undefined; | |
| 72 | var buf1: [8]u8 = undefined; | |
| 73 | std.mem.writeInt(u64, &buf0, s, .big); | |
| 74 | r.fill(&buf1); | |
| 75 | try std.testing.expect(std.mem.eql(u8, buf0[0..], buf1[0..])); | |
| 76 | } | |
| 77 | } | |
| 78 | }; | |
| 79 | ||
| 80 | test "Random int" { | |
| 81 | try testRandomInt(); | |
| 82 | try comptime testRandomInt(); | |
| 83 | } | |
| 84 | fn testRandomInt() !void { | |
| 85 | var rng = SequentialPrng.init(); | |
| 86 | const random = rng.random(); | |
| 87 | ||
| 88 | try expect(random.int(u0) == 0); | |
| 89 | ||
| 90 | rng.next_value = 0; | |
| 91 | try expect(random.int(u1) == 0); | |
| 92 | try expect(random.int(u1) == 1); | |
| 93 | try expect(random.int(u2) == 2); | |
| 94 | try expect(random.int(u2) == 3); | |
| 95 | try expect(random.int(u2) == 0); | |
| 96 | ||
| 97 | rng.next_value = 0xff; | |
| 98 | try expect(random.int(u8) == 0xff); | |
| 99 | rng.next_value = 0x11; | |
| 100 | try expect(random.int(u8) == 0x11); | |
| 101 | ||
| 102 | rng.next_value = 0xff; | |
| 103 | try expect(random.int(u32) == 0xffffffff); | |
| 104 | rng.next_value = 0x11; | |
| 105 | try expect(random.int(u32) == 0x11111111); | |
| 106 | ||
| 107 | rng.next_value = 0xff; | |
| 108 | try expect(random.int(i32) == -1); | |
| 109 | rng.next_value = 0x11; | |
| 110 | try expect(random.int(i32) == 0x11111111); | |
| 111 | ||
| 112 | rng.next_value = 0xff; | |
| 113 | try expect(random.int(i8) == -1); | |
| 114 | rng.next_value = 0x11; | |
| 115 | try expect(random.int(i8) == 0x11); | |
| 116 | ||
| 117 | rng.next_value = 0xff; | |
| 118 | try expect(random.int(u33) == 0x1ffffffff); | |
| 119 | rng.next_value = 0xff; | |
| 120 | try expect(random.int(i1) == -1); | |
| 121 | rng.next_value = 0xff; | |
| 122 | try expect(random.int(i2) == -1); | |
| 123 | rng.next_value = 0xff; | |
| 124 | try expect(random.int(i33) == -1); | |
| 125 | } | |
| 126 | ||
| 127 | test "Random boolean" { | |
| 128 | try testRandomBoolean(); | |
| 129 | try comptime testRandomBoolean(); | |
| 130 | } | |
| 131 | fn testRandomBoolean() !void { | |
| 132 | var rng = SequentialPrng.init(); | |
| 133 | const random = rng.random(); | |
| 134 | ||
| 135 | try expect(random.boolean() == false); | |
| 136 | try expect(random.boolean() == true); | |
| 137 | try expect(random.boolean() == false); | |
| 138 | try expect(random.boolean() == true); | |
| 139 | } | |
| 140 | ||
| 141 | test "Random enum" { | |
| 142 | try testRandomEnumValue(); | |
| 143 | try comptime testRandomEnumValue(); | |
| 144 | } | |
| 145 | fn testRandomEnumValue() !void { | |
| 146 | const TestEnum = enum { | |
| 147 | First, | |
| 148 | Second, | |
| 149 | Third, | |
| 150 | }; | |
| 151 | var rng = SequentialPrng.init(); | |
| 152 | const random = rng.random(); | |
| 153 | rng.next_value = 0; | |
| 154 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 155 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 156 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 157 | } | |
| 158 | ||
| 159 | test "Random intLessThan" { | |
| 160 | @setEvalBranchQuota(10000); | |
| 161 | try testRandomIntLessThan(); | |
| 162 | try comptime testRandomIntLessThan(); | |
| 163 | } | |
| 164 | fn testRandomIntLessThan() !void { | |
| 165 | var rng = SequentialPrng.init(); | |
| 166 | const random = rng.random(); | |
| 167 | ||
| 168 | rng.next_value = 0xff; | |
| 169 | try expect(random.uintLessThan(u8, 4) == 3); | |
| 170 | try expect(rng.next_value == 0); | |
| 171 | try expect(random.uintLessThan(u8, 4) == 0); | |
| 172 | try expect(rng.next_value == 1); | |
| 173 | ||
| 174 | rng.next_value = 0; | |
| 175 | try expect(random.uintLessThan(u64, 32) == 0); | |
| 176 | ||
| 177 | // trigger the bias rejection code path | |
| 178 | rng.next_value = 0; | |
| 179 | try expect(random.uintLessThan(u8, 3) == 0); | |
| 180 | // verify we incremented twice | |
| 181 | try expect(rng.next_value == 2); | |
| 182 | ||
| 183 | rng.next_value = 0xff; | |
| 184 | try expect(random.intRangeLessThan(u8, 0, 0x80) == 0x7f); | |
| 185 | rng.next_value = 0xff; | |
| 186 | try expect(random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); | |
| 187 | ||
| 188 | rng.next_value = 0xff; | |
| 189 | try expect(random.intRangeLessThan(i8, 0, 0x40) == 0x3f); | |
| 190 | rng.next_value = 0xff; | |
| 191 | try expect(random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); | |
| 192 | rng.next_value = 0xff; | |
| 193 | try expect(random.intRangeLessThan(i8, -0x80, 0) == -1); | |
| 194 | ||
| 195 | rng.next_value = 0xff; | |
| 196 | try expect(random.intRangeLessThan(i3, -4, 0) == -1); | |
| 197 | rng.next_value = 0xff; | |
| 198 | try expect(random.intRangeLessThan(i3, -2, 2) == 1); | |
| 199 | } | |
| 200 | ||
| 201 | test "Random intAtMost" { | |
| 202 | @setEvalBranchQuota(10000); | |
| 203 | try testRandomIntAtMost(); | |
| 204 | try comptime testRandomIntAtMost(); | |
| 205 | } | |
| 206 | fn testRandomIntAtMost() !void { | |
| 207 | var rng = SequentialPrng.init(); | |
| 208 | const random = rng.random(); | |
| 209 | ||
| 210 | rng.next_value = 0xff; | |
| 211 | try expect(random.uintAtMost(u8, 3) == 3); | |
| 212 | try expect(rng.next_value == 0); | |
| 213 | try expect(random.uintAtMost(u8, 3) == 0); | |
| 214 | ||
| 215 | // trigger the bias rejection code path | |
| 216 | rng.next_value = 0; | |
| 217 | try expect(random.uintAtMost(u8, 2) == 0); | |
| 218 | // verify we incremented twice | |
| 219 | try expect(rng.next_value == 2); | |
| 220 | ||
| 221 | rng.next_value = 0xff; | |
| 222 | try expect(random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); | |
| 223 | rng.next_value = 0xff; | |
| 224 | try expect(random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); | |
| 225 | ||
| 226 | rng.next_value = 0xff; | |
| 227 | try expect(random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); | |
| 228 | rng.next_value = 0xff; | |
| 229 | try expect(random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); | |
| 230 | rng.next_value = 0xff; | |
| 231 | try expect(random.intRangeAtMost(i8, -0x80, -1) == -1); | |
| 232 | ||
| 233 | rng.next_value = 0xff; | |
| 234 | try expect(random.intRangeAtMost(i3, -4, -1) == -1); | |
| 235 | rng.next_value = 0xff; | |
| 236 | try expect(random.intRangeAtMost(i3, -2, 1) == 1); | |
| 237 | ||
| 238 | try expect(random.uintAtMost(u0, 0) == 0); | |
| 239 | } | |
| 240 | ||
| 241 | test "Random Biased" { | |
| 242 | var prng = DefaultPrng.init(0); | |
| 243 | const random = prng.random(); | |
| 244 | // Not thoroughly checking the logic here. | |
| 245 | // Just want to execute all the paths with different types. | |
| 246 | ||
| 247 | try expect(random.uintLessThanBiased(u1, 1) == 0); | |
| 248 | try expect(random.uintLessThanBiased(u32, 10) < 10); | |
| 249 | try expect(random.uintLessThanBiased(u64, 20) < 20); | |
| 250 | ||
| 251 | try expect(random.uintAtMostBiased(u0, 0) == 0); | |
| 252 | try expect(random.uintAtMostBiased(u1, 0) <= 0); | |
| 253 | try expect(random.uintAtMostBiased(u32, 10) <= 10); | |
| 254 | try expect(random.uintAtMostBiased(u64, 20) <= 20); | |
| 255 | ||
| 256 | try expect(random.intRangeLessThanBiased(u1, 0, 1) == 0); | |
| 257 | try expect(random.intRangeLessThanBiased(i1, -1, 0) == -1); | |
| 258 | try expect(random.intRangeLessThanBiased(u32, 10, 20) >= 10); | |
| 259 | try expect(random.intRangeLessThanBiased(i32, 10, 20) >= 10); | |
| 260 | try expect(random.intRangeLessThanBiased(u64, 20, 40) >= 20); | |
| 261 | try expect(random.intRangeLessThanBiased(i64, 20, 40) >= 20); | |
| 262 | ||
| 263 | // uncomment for broken module error: | |
| 264 | //expect(random.intRangeAtMostBiased(u0, 0, 0) == 0); | |
| 265 | try expect(random.intRangeAtMostBiased(u1, 0, 1) >= 0); | |
| 266 | try expect(random.intRangeAtMostBiased(i1, -1, 0) >= -1); | |
| 267 | try expect(random.intRangeAtMostBiased(u32, 10, 20) >= 10); | |
| 268 | try expect(random.intRangeAtMostBiased(i32, 10, 20) >= 10); | |
| 269 | try expect(random.intRangeAtMostBiased(u64, 20, 40) >= 20); | |
| 270 | try expect(random.intRangeAtMostBiased(i64, 20, 40) >= 20); | |
| 271 | } | |
| 272 | ||
| 273 | test "splitmix64 sequence" { | |
| 274 | var r = SplitMix64.init(0xaeecf86f7878dd75); | |
| 275 | ||
| 276 | const seq = [_]u64{ | |
| 277 | 0x5dbd39db0178eb44, | |
| 278 | 0xa9900fb66b397da3, | |
| 279 | 0x5c1a28b1aeebcf5c, | |
| 280 | 0x64a963238f776912, | |
| 281 | 0xc6d4177b21d1c0ab, | |
| 282 | 0xb2cbdbdb5ea35394, | |
| 283 | }; | |
| 284 | ||
| 285 | for (seq) |s| { | |
| 286 | try expect(s == r.next()); | |
| 287 | } | |
| 288 | } | |
| 289 | ||
| 290 | // Actual Random helper function tests, pcg engine is assumed correct. | |
| 291 | test "Random float correctness" { | |
| 292 | var prng = DefaultPrng.init(0); | |
| 293 | const random = prng.random(); | |
| 294 | ||
| 295 | var i: usize = 0; | |
| 296 | while (i < 1000) : (i += 1) { | |
| 297 | const val1 = random.float(f32); | |
| 298 | try expect(val1 >= 0.0); | |
| 299 | try expect(val1 < 1.0); | |
| 300 | ||
| 301 | const val2 = random.float(f64); | |
| 302 | try expect(val2 >= 0.0); | |
| 303 | try expect(val2 < 1.0); | |
| 304 | } | |
| 305 | } | |
| 306 | ||
| 307 | // Check the "astronomically unlikely" code paths. | |
| 308 | test "Random float coverage" { | |
| 309 | var prng = try Dilbert.init(&[_]u8{0}); | |
| 310 | const random = prng.random(); | |
| 311 | ||
| 312 | const rand_f64 = random.float(f64); | |
| 313 | const rand_f32 = random.float(f32); | |
| 314 | ||
| 315 | try expect(rand_f32 == 0.0); | |
| 316 | try expect(rand_f64 == 0.0); | |
| 317 | } | |
| 318 | ||
| 319 | test "Random float chi-square goodness of fit" { | |
| 320 | const num_numbers = 100000; | |
| 321 | const num_buckets = 1000; | |
| 322 | ||
| 323 | var f32_hist = std.AutoHashMap(u32, u32).init(std.testing.allocator); | |
| 324 | defer f32_hist.deinit(); | |
| 325 | var f64_hist = std.AutoHashMap(u64, u32).init(std.testing.allocator); | |
| 326 | defer f64_hist.deinit(); | |
| 327 | ||
| 328 | var prng = DefaultPrng.init(0); | |
| 329 | const random = prng.random(); | |
| 330 | ||
| 331 | var i: usize = 0; | |
| 332 | while (i < num_numbers) : (i += 1) { | |
| 333 | const rand_f32 = random.float(f32); | |
| 334 | const rand_f64 = random.float(f64); | |
| 335 | const f32_put = try f32_hist.getOrPut(@as(u32, @intFromFloat(rand_f32 * @as(f32, @floatFromInt(num_buckets))))); | |
| 336 | if (f32_put.found_existing) { | |
| 337 | f32_put.value_ptr.* += 1; | |
| 338 | } else { | |
| 339 | f32_put.value_ptr.* = 1; | |
| 340 | } | |
| 341 | const f64_put = try f64_hist.getOrPut(@as(u32, @intFromFloat(rand_f64 * @as(f64, @floatFromInt(num_buckets))))); | |
| 342 | if (f64_put.found_existing) { | |
| 343 | f64_put.value_ptr.* += 1; | |
| 344 | } else { | |
| 345 | f64_put.value_ptr.* = 1; | |
| 346 | } | |
| 347 | } | |
| 348 | ||
| 349 | var f32_total_variance: f64 = 0; | |
| 350 | var f64_total_variance: f64 = 0; | |
| 351 | ||
| 352 | { | |
| 353 | var j: u32 = 0; | |
| 354 | while (j < num_buckets) : (j += 1) { | |
| 355 | const count = @as(f64, @floatFromInt((if (f32_hist.get(j)) |v| v else 0))); | |
| 356 | const expected = @as(f64, @floatFromInt(num_numbers)) / @as(f64, @floatFromInt(num_buckets)); | |
| 357 | const delta = count - expected; | |
| 358 | const variance = (delta * delta) / expected; | |
| 359 | f32_total_variance += variance; | |
| 360 | } | |
| 361 | } | |
| 362 | ||
| 363 | { | |
| 364 | var j: u64 = 0; | |
| 365 | while (j < num_buckets) : (j += 1) { | |
| 366 | const count = @as(f64, @floatFromInt((if (f64_hist.get(j)) |v| v else 0))); | |
| 367 | const expected = @as(f64, @floatFromInt(num_numbers)) / @as(f64, @floatFromInt(num_buckets)); | |
| 368 | const delta = count - expected; | |
| 369 | const variance = (delta * delta) / expected; | |
| 370 | f64_total_variance += variance; | |
| 371 | } | |
| 372 | } | |
| 373 | ||
| 374 | // Accept p-values >= 0.05. | |
| 375 | // Critical value is calculated by opening a Python interpreter and running: | |
| 376 | // scipy.stats.chi2.isf(0.05, num_buckets - 1) | |
| 377 | const critical_value = 1073.6426506574246; | |
| 378 | try expect(f32_total_variance < critical_value); | |
| 379 | try expect(f64_total_variance < critical_value); | |
| 380 | } | |
| 381 | ||
| 382 | test "Random shuffle" { | |
| 383 | var prng = DefaultPrng.init(0); | |
| 384 | const random = prng.random(); | |
| 385 | ||
| 386 | var seq = [_]u8{ 0, 1, 2, 3, 4 }; | |
| 387 | var seen = [_]bool{false} ** 5; | |
| 388 | ||
| 389 | var i: usize = 0; | |
| 390 | while (i < 1000) : (i += 1) { | |
| 391 | random.shuffle(u8, seq[0..]); | |
| 392 | seen[seq[0]] = true; | |
| 393 | try expect(sumArray(seq[0..]) == 10); | |
| 394 | } | |
| 395 | ||
| 396 | // we should see every entry at the head at least once | |
| 397 | for (seen) |e| { | |
| 398 | try expect(e == true); | |
| 399 | } | |
| 400 | } | |
| 401 | ||
| 402 | fn sumArray(s: []const u8) u32 { | |
| 403 | var r: u32 = 0; | |
| 404 | for (s) |e| | |
| 405 | r += e; | |
| 406 | return r; | |
| 407 | } | |
| 408 | ||
| 409 | test "Random range" { | |
| 410 | var prng = DefaultPrng.init(0); | |
| 411 | const random = prng.random(); | |
| 412 | ||
| 413 | try testRange(random, -4, 3); | |
| 414 | try testRange(random, -4, -1); | |
| 415 | try testRange(random, 10, 14); | |
| 416 | try testRange(random, -0x80, 0x7f); | |
| 417 | } | |
| 418 | ||
| 419 | fn testRange(r: Random, start: i8, end: i8) !void { | |
| 420 | try testRangeBias(r, start, end, true); | |
| 421 | try testRangeBias(r, start, end, false); | |
| 422 | } | |
| 423 | fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void { | |
| 424 | const count = @as(usize, @intCast(@as(i32, end) - @as(i32, start))); | |
| 425 | var values_buffer = [_]bool{false} ** 0x100; | |
| 426 | const values = values_buffer[0..count]; | |
| 427 | var i: usize = 0; | |
| 428 | while (i < count) { | |
| 429 | const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); | |
| 430 | const index = @as(usize, @intCast(value - start)); | |
| 431 | if (!values[index]) { | |
| 432 | i += 1; | |
| 433 | values[index] = true; | |
| 434 | } | |
| 435 | } | |
| 436 | } | |
| 437 | ||
| 438 | test "CSPRNG" { | |
| 439 | var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined; | |
| 440 | std.crypto.random.bytes(&secret_seed); | |
| 441 | var csprng = DefaultCsprng.init(secret_seed); | |
| 442 | const random = csprng.random(); | |
| 443 | const a = random.int(u64); | |
| 444 | const b = random.int(u64); | |
| 445 | const c = random.int(u64); | |
| 446 | try expect(a ^ b ^ c != 0); | |
| 447 | } | |
| 448 | ||
| 449 | test "Random weightedIndex" { | |
| 450 | // Make sure weightedIndex works for various integers and floats | |
| 451 | inline for (.{ u64, i4, f32, f64 }) |T| { | |
| 452 | var prng = DefaultPrng.init(0); | |
| 453 | const random = prng.random(); | |
| 454 | ||
| 455 | const proportions = [_]T{ 2, 1, 1, 2 }; | |
| 456 | var counts = [_]f64{ 0, 0, 0, 0 }; | |
| 457 | ||
| 458 | const n_trials: u64 = 10_000; | |
| 459 | var i: usize = 0; | |
| 460 | while (i < n_trials) : (i += 1) { | |
| 461 | const pick = random.weightedIndex(T, &proportions); | |
| 462 | counts[pick] += 1; | |
| 463 | } | |
| 464 | ||
| 465 | // We expect the first and last counts to be roughly 2x the second and third | |
| 466 | const approxEqRel = std.math.approxEqRel; | |
| 467 | // Define "roughly" to be within 10% | |
| 468 | const tolerance = 0.1; | |
| 469 | try std.testing.expect(approxEqRel(f64, counts[0], counts[1] * 2, tolerance)); | |
| 470 | try std.testing.expect(approxEqRel(f64, counts[1], counts[2], tolerance)); | |
| 471 | try std.testing.expect(approxEqRel(f64, counts[2] * 2, counts[3], tolerance)); | |
| 472 | } | |
| 473 | } |
lib/std/rand/ziggurat.zig deleted-170| ... | ... | @@ -1,170 +0,0 @@ |
| 1 | //! Implements [ZIGNOR][1] (Jurgen A. Doornik, 2005, Nuffield College, Oxford). | |
| 2 | //! | |
| 3 | //! [1]: https://www.doornik.com/research/ziggurat.pdf | |
| 4 | //! | |
| 5 | //! rust/rand used as a reference; | |
| 6 | //! | |
| 7 | //! NOTE: This seems interesting but reference code is a bit hard to grok: | |
| 8 | //! https://sbarral.github.io/etf. | |
| 9 | ||
| 10 | const std = @import("../std.zig"); | |
| 11 | const builtin = @import("builtin"); | |
| 12 | const math = std.math; | |
| 13 | const Random = std.rand.Random; | |
| 14 | ||
| 15 | pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { | |
| 16 | while (true) { | |
| 17 | // We manually construct a float from parts as we can avoid an extra random lookup here by | |
| 18 | // using the unused exponent for the lookup table entry. | |
| 19 | const bits = random.int(u64); | |
| 20 | const i = @as(usize, @as(u8, @truncate(bits))); | |
| 21 | ||
| 22 | const u = blk: { | |
| 23 | if (tables.is_symmetric) { | |
| 24 | // Generate a value in the range [2, 4) and scale into [-1, 1) | |
| 25 | const repr = ((0x3ff + 1) << 52) | (bits >> 12); | |
| 26 | break :blk @as(f64, @bitCast(repr)) - 3.0; | |
| 27 | } else { | |
| 28 | // Generate a value in the range [1, 2) and scale into (0, 1) | |
| 29 | const repr = (0x3ff << 52) | (bits >> 12); | |
| 30 | break :blk @as(f64, @bitCast(repr)) - (1.0 - math.floatEps(f64) / 2.0); | |
| 31 | } | |
| 32 | }; | |
| 33 | ||
| 34 | const x = u * tables.x[i]; | |
| 35 | const test_x = if (tables.is_symmetric) @abs(x) else x; | |
| 36 | ||
| 37 | // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i]) | |
| 38 | if (test_x < tables.x[i + 1]) { | |
| 39 | return x; | |
| 40 | } | |
| 41 | ||
| 42 | if (i == 0) { | |
| 43 | return tables.zero_case(random, u); | |
| 44 | } | |
| 45 | ||
| 46 | // equivalent to f1 + DRanU() * (f0 - f1) < 1 | |
| 47 | if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) { | |
| 48 | return x; | |
| 49 | } | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | pub const ZigTable = struct { | |
| 54 | r: f64, | |
| 55 | x: [257]f64, | |
| 56 | f: [257]f64, | |
| 57 | ||
| 58 | // probability density function used as a fallback | |
| 59 | pdf: fn (f64) f64, | |
| 60 | // whether the distribution is symmetric | |
| 61 | is_symmetric: bool, | |
| 62 | // fallback calculation in the case we are in the 0 block | |
| 63 | zero_case: fn (Random, f64) f64, | |
| 64 | }; | |
| 65 | ||
| 66 | // zigNorInit | |
| 67 | pub fn ZigTableGen( | |
| 68 | comptime is_symmetric: bool, | |
| 69 | comptime r: f64, | |
| 70 | comptime v: f64, | |
| 71 | comptime f: fn (f64) f64, | |
| 72 | comptime f_inv: fn (f64) f64, | |
| 73 | comptime zero_case: fn (Random, f64) f64, | |
| 74 | ) ZigTable { | |
| 75 | var tables: ZigTable = undefined; | |
| 76 | ||
| 77 | tables.is_symmetric = is_symmetric; | |
| 78 | tables.r = r; | |
| 79 | tables.pdf = f; | |
| 80 | tables.zero_case = zero_case; | |
| 81 | ||
| 82 | tables.x[0] = v / f(r); | |
| 83 | tables.x[1] = r; | |
| 84 | ||
| 85 | for (tables.x[2..256], 0..) |*entry, i| { | |
| 86 | const last = tables.x[2 + i - 1]; | |
| 87 | entry.* = f_inv(v / last + f(last)); | |
| 88 | } | |
| 89 | tables.x[256] = 0; | |
| 90 | ||
| 91 | for (tables.f[0..], 0..) |*entry, i| { | |
| 92 | entry.* = f(tables.x[i]); | |
| 93 | } | |
| 94 | ||
| 95 | return tables; | |
| 96 | } | |
| 97 | ||
| 98 | // N(0, 1) | |
| 99 | pub const NormDist = blk: { | |
| 100 | @setEvalBranchQuota(30000); | |
| 101 | break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); | |
| 102 | }; | |
| 103 | ||
| 104 | pub const norm_r = 3.6541528853610088; | |
| 105 | pub const norm_v = 0.00492867323399; | |
| 106 | ||
| 107 | pub fn norm_f(x: f64) f64 { | |
| 108 | return @exp(-x * x / 2.0); | |
| 109 | } | |
| 110 | pub fn norm_f_inv(y: f64) f64 { | |
| 111 | return @sqrt(-2.0 * @log(y)); | |
| 112 | } | |
| 113 | pub fn norm_zero_case(random: Random, u: f64) f64 { | |
| 114 | var x: f64 = 1; | |
| 115 | var y: f64 = 0; | |
| 116 | ||
| 117 | while (-2.0 * y < x * x) { | |
| 118 | x = @log(random.float(f64)) / norm_r; | |
| 119 | y = @log(random.float(f64)); | |
| 120 | } | |
| 121 | ||
| 122 | if (u < 0) { | |
| 123 | return x - norm_r; | |
| 124 | } else { | |
| 125 | return norm_r - x; | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | test "normal dist sanity" { | |
| 130 | var prng = std.rand.DefaultPrng.init(0); | |
| 131 | const random = prng.random(); | |
| 132 | ||
| 133 | var i: usize = 0; | |
| 134 | while (i < 1000) : (i += 1) { | |
| 135 | _ = random.floatNorm(f64); | |
| 136 | } | |
| 137 | } | |
| 138 | ||
| 139 | // Exp(1) | |
| 140 | pub const ExpDist = blk: { | |
| 141 | @setEvalBranchQuota(30000); | |
| 142 | break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case); | |
| 143 | }; | |
| 144 | ||
| 145 | pub const exp_r = 7.69711747013104972; | |
| 146 | pub const exp_v = 0.0039496598225815571993; | |
| 147 | ||
| 148 | pub fn exp_f(x: f64) f64 { | |
| 149 | return @exp(-x); | |
| 150 | } | |
| 151 | pub fn exp_f_inv(y: f64) f64 { | |
| 152 | return -@log(y); | |
| 153 | } | |
| 154 | pub fn exp_zero_case(random: Random, _: f64) f64 { | |
| 155 | return exp_r - @log(random.float(f64)); | |
| 156 | } | |
| 157 | ||
| 158 | test "exp dist smoke test" { | |
| 159 | var prng = std.rand.DefaultPrng.init(0); | |
| 160 | const random = prng.random(); | |
| 161 | ||
| 162 | var i: usize = 0; | |
| 163 | while (i < 1000) : (i += 1) { | |
| 164 | _ = random.floatExp(f64); | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | test { | |
| 169 | _ = NormDist; | |
| 170 | } |
lib/std/sort.zig+1-1| ... | ... | @@ -379,7 +379,7 @@ test "sort with context in the middle of a slice" { |
| 379 | 379 | } |
| 380 | 380 | |
| 381 | 381 | test "sort fuzz testing" { |
| 382 | var prng = std.rand.DefaultPrng.init(0x12345678); | |
| 382 | var prng = std.Random.DefaultPrng.init(0x12345678); | |
| 383 | 383 | const random = prng.random(); |
| 384 | 384 | const test_case_count = 10; |
| 385 | 385 |
lib/std/std.zig+3-2| ... | ... | @@ -36,6 +36,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE |
| 36 | 36 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; |
| 37 | 37 | pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; |
| 38 | 38 | pub const Progress = @import("Progress.zig"); |
| 39 | pub const Random = @import("Random.zig"); | |
| 39 | 40 | pub const RingBuffer = @import("RingBuffer.zig"); |
| 40 | 41 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 41 | 42 | pub const SemanticVersion = @import("SemanticVersion.zig"); |
| ... | ... | @@ -156,8 +157,8 @@ pub const pdb = @import("pdb.zig"); |
| 156 | 157 | /// and spawning of child processes. |
| 157 | 158 | pub const process = @import("process.zig"); |
| 158 | 159 | |
| 159 | /// Fast pseudo-random number generators (i.e. not cryptographically secure). | |
| 160 | pub const rand = @import("rand.zig"); | |
| 160 | /// Deprecated: use `Random` instead. | |
| 161 | pub const rand = Random; | |
| 161 | 162 | |
| 162 | 163 | /// Sorting. |
| 163 | 164 | pub const sort = @import("sort.zig"); |
lib/std/treap.zig+4-4| ... | ... | @@ -18,7 +18,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { |
| 18 | 18 | |
| 19 | 19 | /// A customized pseudo random number generator for the treap. |
| 20 | 20 | /// This just helps reducing the memory size of the treap itself |
| 21 | /// as std.rand.DefaultPrng requires larger state (while producing better entropy for randomness to be fair). | |
| 21 | /// as std.Random.DefaultPrng requires larger state (while producing better entropy for randomness to be fair). | |
| 22 | 22 | const Prng = struct { |
| 23 | 23 | xorshift: usize = 0, |
| 24 | 24 | |
| ... | ... | @@ -305,7 +305,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { |
| 305 | 305 | // https://lemire.me/blog/2017/09/18/visiting-all-values-in-an-array-exactly-once-in-random-order/ |
| 306 | 306 | fn SliceIterRandomOrder(comptime T: type) type { |
| 307 | 307 | return struct { |
| 308 | rng: std.rand.Random, | |
| 308 | rng: std.Random, | |
| 309 | 309 | slice: []T, |
| 310 | 310 | index: usize = undefined, |
| 311 | 311 | offset: usize = undefined, |
| ... | ... | @@ -313,7 +313,7 @@ fn SliceIterRandomOrder(comptime T: type) type { |
| 313 | 313 | |
| 314 | 314 | const Self = @This(); |
| 315 | 315 | |
| 316 | pub fn init(slice: []T, rng: std.rand.Random) Self { | |
| 316 | pub fn init(slice: []T, rng: std.Random) Self { | |
| 317 | 317 | return Self{ |
| 318 | 318 | .rng = rng, |
| 319 | 319 | .slice = slice, |
| ... | ... | @@ -353,7 +353,7 @@ test "std.Treap: insert, find, replace, remove" { |
| 353 | 353 | var treap = TestTreap{}; |
| 354 | 354 | var nodes: [10]TestNode = undefined; |
| 355 | 355 | |
| 356 | var prng = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 356 | var prng = std.Random.DefaultPrng.init(0xdeadbeef); | |
| 357 | 357 | var iter = SliceIterRandomOrder(TestNode).init(&nodes, prng.random()); |
| 358 | 358 | |
| 359 | 359 | // insert check |
src/reduce.zig+2-2| ... | ... | @@ -136,7 +136,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 136 | 136 | var more_fixups: Ast.Fixups = .{}; |
| 137 | 137 | defer more_fixups.deinit(gpa); |
| 138 | 138 | |
| 139 | var rng = std.rand.DefaultPrng.init(seed); | |
| 139 | var rng = std.Random.DefaultPrng.init(seed); | |
| 140 | 140 | |
| 141 | 141 | // 1. Walk the AST of the source file looking for independent |
| 142 | 142 | // reductions and collecting them all into an array list. |
| ... | ... | @@ -274,7 +274,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 274 | 274 | return std.process.cleanExit(); |
| 275 | 275 | } |
| 276 | 276 | |
| 277 | fn sortTransformations(transformations: []Walk.Transformation, rng: std.rand.Random) void { | |
| 277 | fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) void { | |
| 278 | 278 | rng.shuffle(Walk.Transformation, transformations); |
| 279 | 279 | // Stable sort based on priority to keep randomness as the secondary sort. |
| 280 | 280 | // TODO: introduce transformation priorities |
src/resinator/compile.zig+1-1| ... | ... | @@ -3356,7 +3356,7 @@ test "StringTable" { |
| 3356 | 3356 | } |
| 3357 | 3357 | break :ids buf; |
| 3358 | 3358 | }; |
| 3359 | var prng = std.rand.DefaultPrng.init(0); | |
| 3359 | var prng = std.Random.DefaultPrng.init(0); | |
| 3360 | 3360 | var random = prng.random(); |
| 3361 | 3361 | random.shuffle(u16, &ids); |
| 3362 | 3362 |