| author | |
| committer | |
| log | c1a5ff34f3f68a2a0bc32828ab483328cd436fea |
| tree | bad85387a89da38890f72696ccfc0ae3c416ab0f |
| parent | 9024f27d8f5cb651e2260348ce0ee6fd67fc2c32 |
These changes have been made to resolve issue #10037. The `Random`
interface was implemented in such a way that causes significant slowdown
when calling the `fill` function of the rng used.
The `Random` interface is no longer stored in a field of the rng, and is
instead returned by the child function `random()` of the rng. This
avoids the performance issues caused by the interface.18 files changed, 291 insertions(+), 244 deletions(-)
lib/std/atomic/queue.zig+3-2| ... | ... | @@ -242,10 +242,11 @@ test "std.atomic.Queue" { |
| 242 | 242 | |
| 243 | 243 | fn startPuts(ctx: *Context) u8 { |
| 244 | 244 | var put_count: usize = puts_per_thread; |
| 245 | var r = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 245 | var prng = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 246 | const random = prng.random(); | |
| 246 | 247 | while (put_count != 0) : (put_count -= 1) { |
| 247 | 248 | std.time.sleep(1); // let the os scheduler be our fuzz |
| 248 | const x = @bitCast(i32, r.random.int(u32)); | |
| 249 | const x = @bitCast(i32, random.int(u32)); | |
| 249 | 250 | const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; |
| 250 | 251 | node.* = .{ |
| 251 | 252 | .prev = undefined, |
lib/std/atomic/stack.zig+3-2| ... | ... | @@ -147,10 +147,11 @@ test "std.atomic.stack" { |
| 147 | 147 | |
| 148 | 148 | fn startPuts(ctx: *Context) u8 { |
| 149 | 149 | var put_count: usize = puts_per_thread; |
| 150 | var r = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 150 | var prng = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 151 | const random = prng.random(); | |
| 151 | 152 | while (put_count != 0) : (put_count -= 1) { |
| 152 | 153 | std.time.sleep(1); // let the os scheduler be our fuzz |
| 153 | const x = @bitCast(i32, r.random.int(u32)); | |
| 154 | const x = @bitCast(i32, random.int(u32)); | |
| 154 | 155 | const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; |
| 155 | 156 | node.* = Stack(i32).Node{ |
| 156 | 157 | .next = undefined, |
lib/std/crypto/benchmark.zig+11-10| ... | ... | @@ -11,6 +11,7 @@ const KiB = 1024; |
| 11 | 11 | const MiB = 1024 * KiB; |
| 12 | 12 | |
| 13 | 13 | var prng = std.rand.DefaultPrng.init(0); |
| 14 | const random = prng.random(); | |
| 14 | 15 | |
| 15 | 16 | const Crypto = struct { |
| 16 | 17 | ty: type, |
| ... | ... | @@ -34,7 +35,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 |
| 34 | 35 | var h = Hash.init(.{}); |
| 35 | 36 | |
| 36 | 37 | var block: [Hash.digest_length]u8 = undefined; |
| 37 | prng.random.bytes(block[0..]); | |
| 38 | random.bytes(block[0..]); | |
| 38 | 39 | |
| 39 | 40 | var offset: usize = 0; |
| 40 | 41 | var timer = try Timer.start(); |
| ... | ... | @@ -66,11 +67,11 @@ const macs = [_]Crypto{ |
| 66 | 67 | |
| 67 | 68 | pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { |
| 68 | 69 | var in: [512 * KiB]u8 = undefined; |
| 69 | prng.random.bytes(in[0..]); | |
| 70 | random.bytes(in[0..]); | |
| 70 | 71 | |
| 71 | 72 | const key_length = if (Mac.key_length == 0) 32 else Mac.key_length; |
| 72 | 73 | var key: [key_length]u8 = undefined; |
| 73 | prng.random.bytes(key[0..]); | |
| 74 | random.bytes(key[0..]); | |
| 74 | 75 | |
| 75 | 76 | var mac: [Mac.mac_length]u8 = undefined; |
| 76 | 77 | var offset: usize = 0; |
| ... | ... | @@ -94,10 +95,10 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c |
| 94 | 95 | std.debug.assert(DhKeyExchange.shared_length >= DhKeyExchange.secret_length); |
| 95 | 96 | |
| 96 | 97 | var secret: [DhKeyExchange.shared_length]u8 = undefined; |
| 97 | prng.random.bytes(secret[0..]); | |
| 98 | random.bytes(secret[0..]); | |
| 98 | 99 | |
| 99 | 100 | var public: [DhKeyExchange.shared_length]u8 = undefined; |
| 100 | prng.random.bytes(public[0..]); | |
| 101 | random.bytes(public[0..]); | |
| 101 | 102 | |
| 102 | 103 | var timer = try Timer.start(); |
| 103 | 104 | const start = timer.lap(); |
| ... | ... | @@ -211,15 +212,15 @@ const aeads = [_]Crypto{ |
| 211 | 212 | |
| 212 | 213 | pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 { |
| 213 | 214 | var in: [512 * KiB]u8 = undefined; |
| 214 | prng.random.bytes(in[0..]); | |
| 215 | random.bytes(in[0..]); | |
| 215 | 216 | |
| 216 | 217 | var tag: [Aead.tag_length]u8 = undefined; |
| 217 | 218 | |
| 218 | 219 | var key: [Aead.key_length]u8 = undefined; |
| 219 | prng.random.bytes(key[0..]); | |
| 220 | random.bytes(key[0..]); | |
| 220 | 221 | |
| 221 | 222 | var nonce: [Aead.nonce_length]u8 = undefined; |
| 222 | prng.random.bytes(nonce[0..]); | |
| 223 | random.bytes(nonce[0..]); | |
| 223 | 224 | |
| 224 | 225 | var offset: usize = 0; |
| 225 | 226 | var timer = try Timer.start(); |
| ... | ... | @@ -244,7 +245,7 @@ const aes = [_]Crypto{ |
| 244 | 245 | |
| 245 | 246 | pub fn benchmarkAes(comptime Aes: anytype, comptime count: comptime_int) !u64 { |
| 246 | 247 | var key: [Aes.key_bits / 8]u8 = undefined; |
| 247 | prng.random.bytes(key[0..]); | |
| 248 | random.bytes(key[0..]); | |
| 248 | 249 | const ctx = Aes.initEnc(key); |
| 249 | 250 | |
| 250 | 251 | var in = [_]u8{0} ** 16; |
| ... | ... | @@ -273,7 +274,7 @@ const aes8 = [_]Crypto{ |
| 273 | 274 | |
| 274 | 275 | pub fn benchmarkAes8(comptime Aes: anytype, comptime count: comptime_int) !u64 { |
| 275 | 276 | var key: [Aes.key_bits / 8]u8 = undefined; |
| 276 | prng.random.bytes(key[0..]); | |
| 277 | random.bytes(key[0..]); | |
| 277 | 278 | const ctx = Aes.initEnc(key); |
| 278 | 279 | |
| 279 | 280 | var in = [_]u8{0} ** (8 * 16); |
lib/std/crypto/tlcsprng.zig+5-2| ... | ... | @@ -11,7 +11,10 @@ const os = std.os; |
| 11 | 11 | |
| 12 | 12 | /// We use this as a layer of indirection because global const pointers cannot |
| 13 | 13 | /// point to thread-local variables. |
| 14 | pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill }; | |
| 14 | pub const interface = std.rand.Random{ | |
| 15 | .ptr = undefined, | |
| 16 | .fillFn = tlsCsprngFill, | |
| 17 | }; | |
| 15 | 18 | |
| 16 | 19 | const os_has_fork = switch (builtin.os.tag) { |
| 17 | 20 | .dragonfly, |
| ... | ... | @@ -55,7 +58,7 @@ var install_atfork_handler = std.once(struct { |
| 55 | 58 | |
| 56 | 59 | threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{}; |
| 57 | 60 | |
| 58 | fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void { | |
| 61 | fn tlsCsprngFill(_: *c_void, buffer: []u8) void { | |
| 59 | 62 | if (builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) { |
| 60 | 63 | // arc4random is already a thread-local CSPRNG. |
| 61 | 64 | return std.c.arc4random_buf(buffer.ptr, buffer.len); |
lib/std/hash/benchmark.zig+3-2| ... | ... | @@ -11,6 +11,7 @@ const MiB = 1024 * KiB; |
| 11 | 11 | const GiB = 1024 * MiB; |
| 12 | 12 | |
| 13 | 13 | var prng = std.rand.DefaultPrng.init(0); |
| 14 | const random = prng.random(); | |
| 14 | 15 | |
| 15 | 16 | const Hash = struct { |
| 16 | 17 | ty: type, |
| ... | ... | @@ -88,7 +89,7 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { |
| 88 | 89 | }; |
| 89 | 90 | |
| 90 | 91 | var block: [block_size]u8 = undefined; |
| 91 | prng.random.bytes(block[0..]); | |
| 92 | random.bytes(block[0..]); | |
| 92 | 93 | |
| 93 | 94 | var offset: usize = 0; |
| 94 | 95 | var timer = try Timer.start(); |
| ... | ... | @@ -110,7 +111,7 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { |
| 110 | 111 | pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize) !Result { |
| 111 | 112 | const key_count = bytes / key_size; |
| 112 | 113 | var block: [block_size]u8 = undefined; |
| 113 | prng.random.bytes(block[0..]); | |
| 114 | random.bytes(block[0..]); | |
| 114 | 115 | |
| 115 | 116 | var i: usize = 0; |
| 116 | 117 | var timer = try Timer.start(); |
lib/std/hash_map.zig+7-5| ... | ... | @@ -1795,10 +1795,11 @@ test "std.hash_map put and remove loop in random order" { |
| 1795 | 1795 | while (i < size) : (i += 1) { |
| 1796 | 1796 | try keys.append(i); |
| 1797 | 1797 | } |
| 1798 | var rng = std.rand.DefaultPrng.init(0); | |
| 1798 | var prng = std.rand.DefaultPrng.init(0); | |
| 1799 | const random = prng.random(); | |
| 1799 | 1800 | |
| 1800 | 1801 | while (i < iterations) : (i += 1) { |
| 1801 | std.rand.Random.shuffle(&rng.random, u32, keys.items); | |
| 1802 | random.shuffle(u32, keys.items); | |
| 1802 | 1803 | |
| 1803 | 1804 | for (keys.items) |key| { |
| 1804 | 1805 | try map.put(key, key); |
| ... | ... | @@ -1826,14 +1827,15 @@ test "std.hash_map remove one million elements in random order" { |
| 1826 | 1827 | keys.append(i) catch unreachable; |
| 1827 | 1828 | } |
| 1828 | 1829 | |
| 1829 | var rng = std.rand.DefaultPrng.init(0); | |
| 1830 | std.rand.Random.shuffle(&rng.random, u32, keys.items); | |
| 1830 | var prng = std.rand.DefaultPrng.init(0); | |
| 1831 | const random = prng.random(); | |
| 1832 | random.shuffle(u32, keys.items); | |
| 1831 | 1833 | |
| 1832 | 1834 | for (keys.items) |key| { |
| 1833 | 1835 | map.put(key, key) catch unreachable; |
| 1834 | 1836 | } |
| 1835 | 1837 | |
| 1836 | std.rand.Random.shuffle(&rng.random, u32, keys.items); | |
| 1838 | random.shuffle(u32, keys.items); | |
| 1837 | 1839 | i = 0; |
| 1838 | 1840 | while (i < n) : (i += 1) { |
| 1839 | 1841 | const key = keys.items[i]; |
lib/std/io/test.zig+2-1| ... | ... | @@ -20,7 +20,8 @@ test "write a file, read it, then delete it" { |
| 20 | 20 | |
| 21 | 21 | var data: [1024]u8 = undefined; |
| 22 | 22 | var prng = DefaultPrng.init(1234); |
| 23 | prng.random.bytes(data[0..]); | |
| 23 | const random = prng.random(); | |
| 24 | random.bytes(data[0..]); | |
| 24 | 25 | const tmp_file_name = "temp_test_file.txt"; |
| 25 | 26 | { |
| 26 | 27 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
lib/std/math/big/rational.zig+2-1| ... | ... | @@ -589,9 +589,10 @@ test "big.rational set/to Float round-trip" { |
| 589 | 589 | var a = try Rational.init(testing.allocator); |
| 590 | 590 | defer a.deinit(); |
| 591 | 591 | var prng = std.rand.DefaultPrng.init(0x5EED); |
| 592 | const random = prng.random(); | |
| 592 | 593 | var i: usize = 0; |
| 593 | 594 | while (i < 512) : (i += 1) { |
| 594 | const r = prng.random.float(f64); | |
| 595 | const r = random.float(f64); | |
| 595 | 596 | try a.setFloat(f64, r); |
| 596 | 597 | try testing.expect((try a.toFloat(f64)) == r); |
| 597 | 598 | } |
lib/std/priority_dequeue.zig+10-7| ... | ... | @@ -850,17 +850,18 @@ test "std.PriorityDequeue: shrinkAndFree" { |
| 850 | 850 | |
| 851 | 851 | test "std.PriorityDequeue: fuzz testing min" { |
| 852 | 852 | var prng = std.rand.DefaultPrng.init(0x12345678); |
| 853 | const random = prng.random(); | |
| 853 | 854 | |
| 854 | 855 | const test_case_count = 100; |
| 855 | 856 | const queue_size = 1_000; |
| 856 | 857 | |
| 857 | 858 | var i: usize = 0; |
| 858 | 859 | while (i < test_case_count) : (i += 1) { |
| 859 | try fuzzTestMin(&prng.random, queue_size); | |
| 860 | try fuzzTestMin(random, queue_size); | |
| 860 | 861 | } |
| 861 | 862 | } |
| 862 | 863 | |
| 863 | fn fuzzTestMin(rng: *std.rand.Random, comptime queue_size: usize) !void { | |
| 864 | fn fuzzTestMin(rng: std.rand.Random, comptime queue_size: usize) !void { | |
| 864 | 865 | const allocator = testing.allocator; |
| 865 | 866 | const items = try generateRandomSlice(allocator, rng, queue_size); |
| 866 | 867 | |
| ... | ... | @@ -878,17 +879,18 @@ fn fuzzTestMin(rng: *std.rand.Random, comptime queue_size: usize) !void { |
| 878 | 879 | |
| 879 | 880 | test "std.PriorityDequeue: fuzz testing max" { |
| 880 | 881 | var prng = std.rand.DefaultPrng.init(0x87654321); |
| 882 | const random = prng.random(); | |
| 881 | 883 | |
| 882 | 884 | const test_case_count = 100; |
| 883 | 885 | const queue_size = 1_000; |
| 884 | 886 | |
| 885 | 887 | var i: usize = 0; |
| 886 | 888 | while (i < test_case_count) : (i += 1) { |
| 887 | try fuzzTestMax(&prng.random, queue_size); | |
| 889 | try fuzzTestMax(random, queue_size); | |
| 888 | 890 | } |
| 889 | 891 | } |
| 890 | 892 | |
| 891 | fn fuzzTestMax(rng: *std.rand.Random, queue_size: usize) !void { | |
| 893 | fn fuzzTestMax(rng: std.rand.Random, queue_size: usize) !void { | |
| 892 | 894 | const allocator = testing.allocator; |
| 893 | 895 | const items = try generateRandomSlice(allocator, rng, queue_size); |
| 894 | 896 | |
| ... | ... | @@ -906,17 +908,18 @@ fn fuzzTestMax(rng: *std.rand.Random, queue_size: usize) !void { |
| 906 | 908 | |
| 907 | 909 | test "std.PriorityDequeue: fuzz testing min and max" { |
| 908 | 910 | var prng = std.rand.DefaultPrng.init(0x87654321); |
| 911 | const random = prng.random(); | |
| 909 | 912 | |
| 910 | 913 | const test_case_count = 100; |
| 911 | 914 | const queue_size = 1_000; |
| 912 | 915 | |
| 913 | 916 | var i: usize = 0; |
| 914 | 917 | while (i < test_case_count) : (i += 1) { |
| 915 | try fuzzTestMinMax(&prng.random, queue_size); | |
| 918 | try fuzzTestMinMax(random, queue_size); | |
| 916 | 919 | } |
| 917 | 920 | } |
| 918 | 921 | |
| 919 | fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void { | |
| 922 | fn fuzzTestMinMax(rng: std.rand.Random, queue_size: usize) !void { | |
| 920 | 923 | const allocator = testing.allocator; |
| 921 | 924 | const items = try generateRandomSlice(allocator, rng, queue_size); |
| 922 | 925 | |
| ... | ... | @@ -943,7 +946,7 @@ fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void { |
| 943 | 946 | } |
| 944 | 947 | } |
| 945 | 948 | |
| 946 | fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 { | |
| 949 | fn generateRandomSlice(allocator: *std.mem.Allocator, rng: std.rand.Random, size: usize) ![]u32 { | |
| 947 | 950 | var array = std.ArrayList(u32).init(allocator); |
| 948 | 951 | try array.ensureTotalCapacity(size); |
| 949 | 952 |
lib/std/rand.zig+195-159| ... | ... | @@ -29,19 +29,40 @@ pub const Xoshiro256 = @import("rand/Xoshiro256.zig"); |
| 29 | 29 | pub const Sfc64 = @import("rand/Sfc64.zig"); |
| 30 | 30 | |
| 31 | 31 | pub const Random = struct { |
| 32 | fillFn: fn (r: *Random, buf: []u8) void, | |
| 32 | ptr: *c_void, | |
| 33 | fillFn: fn (ptr: *c_void, buf: []u8) void, | |
| 34 | ||
| 35 | pub fn init(pointer: anytype) 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 | assert(std.meta.trait.hasFn("fill")(@typeInfo(Ptr).Pointer.child)); // Struct must provide the `fill` function | |
| 41 | const gen = struct { | |
| 42 | fn fill(ptr: *c_void, buf: []u8) void { | |
| 43 | const alignment = @typeInfo(Ptr).Pointer.alignment; | |
| 44 | const self = @ptrCast(Ptr, @alignCast(alignment, ptr)); | |
| 45 | self.fill(buf); | |
| 46 | } | |
| 47 | }; | |
| 48 | ||
| 49 | return .{ | |
| 50 | .ptr = pointer, | |
| 51 | .fillFn = gen.fill, | |
| 52 | }; | |
| 53 | } | |
| 33 | 54 | |
| 34 | 55 | /// Read random bytes into the specified buffer until full. |
| 35 | pub fn bytes(r: *Random, buf: []u8) void { | |
| 36 | r.fillFn(r, buf); | |
| 56 | pub fn bytes(r: Random, buf: []u8) void { | |
| 57 | r.fillFn(r.ptr, buf); | |
| 37 | 58 | } |
| 38 | 59 | |
| 39 | pub fn boolean(r: *Random) bool { | |
| 60 | pub fn boolean(r: Random) bool { | |
| 40 | 61 | return r.int(u1) != 0; |
| 41 | 62 | } |
| 42 | 63 | |
| 43 | 64 | /// Returns a random value from an enum, evenly distributed. |
| 44 | pub fn enumValue(r: *Random, comptime EnumType: type) EnumType { | |
| 65 | pub fn enumValue(r: Random, comptime EnumType: type) EnumType { | |
| 45 | 66 | if (comptime !std.meta.trait.is(.Enum)(EnumType)) { |
| 46 | 67 | @compileError("Random.enumValue requires an enum type, not a " ++ @typeName(EnumType)); |
| 47 | 68 | } |
| ... | ... | @@ -55,7 +76,7 @@ pub const Random = struct { |
| 55 | 76 | |
| 56 | 77 | /// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`. |
| 57 | 78 | /// `i` is evenly distributed. |
| 58 | pub fn int(r: *Random, comptime T: type) T { | |
| 79 | pub fn int(r: Random, comptime T: type) T { | |
| 59 | 80 | const bits = @typeInfo(T).Int.bits; |
| 60 | 81 | const UnsignedT = std.meta.Int(.unsigned, bits); |
| 61 | 82 | const ByteAlignedT = std.meta.Int(.unsigned, @divTrunc(bits + 7, 8) * 8); |
| ... | ... | @@ -73,7 +94,7 @@ pub const Random = struct { |
| 73 | 94 | |
| 74 | 95 | /// Constant-time implementation off `uintLessThan`. |
| 75 | 96 | /// The results of this function may be biased. |
| 76 | pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T { | |
| 97 | pub fn uintLessThanBiased(r: Random, comptime T: type, less_than: T) T { | |
| 77 | 98 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); |
| 78 | 99 | const bits = @typeInfo(T).Int.bits; |
| 79 | 100 | comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! |
| ... | ... | @@ -93,7 +114,7 @@ pub const Random = struct { |
| 93 | 114 | /// However, if `fillFn` is backed by any evenly distributed pseudo random number generator, |
| 94 | 115 | /// this function is guaranteed to return. |
| 95 | 116 | /// If you need deterministic runtime bounds, use `uintLessThanBiased`. |
| 96 | pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { | |
| 117 | pub fn uintLessThan(r: Random, comptime T: type, less_than: T) T { | |
| 97 | 118 | comptime assert(@typeInfo(T).Int.signedness == .unsigned); |
| 98 | 119 | const bits = @typeInfo(T).Int.bits; |
| 99 | 120 | comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! |
| ... | ... | @@ -130,7 +151,7 @@ pub const Random = struct { |
| 130 | 151 | |
| 131 | 152 | /// Constant-time implementation off `uintAtMost`. |
| 132 | 153 | /// The results of this function may be biased. |
| 133 | pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T { | |
| 154 | pub fn uintAtMostBiased(r: Random, comptime T: type, at_most: T) T { | |
| 134 | 155 | assert(@typeInfo(T).Int.signedness == .unsigned); |
| 135 | 156 | if (at_most == maxInt(T)) { |
| 136 | 157 | // have the full range |
| ... | ... | @@ -142,7 +163,7 @@ pub const Random = struct { |
| 142 | 163 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. |
| 143 | 164 | /// See `uintLessThan`, which this function uses in most cases, |
| 144 | 165 | /// for commentary on the runtime of this function. |
| 145 | pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T { | |
| 166 | pub fn uintAtMost(r: Random, comptime T: type, at_most: T) T { | |
| 146 | 167 | assert(@typeInfo(T).Int.signedness == .unsigned); |
| 147 | 168 | if (at_most == maxInt(T)) { |
| 148 | 169 | // have the full range |
| ... | ... | @@ -153,7 +174,7 @@ pub const Random = struct { |
| 153 | 174 | |
| 154 | 175 | /// Constant-time implementation off `intRangeLessThan`. |
| 155 | 176 | /// The results of this function may be biased. |
| 156 | pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T { | |
| 177 | pub fn intRangeLessThanBiased(r: Random, comptime T: type, at_least: T, less_than: T) T { | |
| 157 | 178 | assert(at_least < less_than); |
| 158 | 179 | const info = @typeInfo(T).Int; |
| 159 | 180 | if (info.signedness == .signed) { |
| ... | ... | @@ -172,7 +193,7 @@ pub const Random = struct { |
| 172 | 193 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. |
| 173 | 194 | /// See `uintLessThan`, which this function uses in most cases, |
| 174 | 195 | /// for commentary on the runtime of this function. |
| 175 | pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T { | |
| 196 | pub fn intRangeLessThan(r: Random, comptime T: type, at_least: T, less_than: T) T { | |
| 176 | 197 | assert(at_least < less_than); |
| 177 | 198 | const info = @typeInfo(T).Int; |
| 178 | 199 | if (info.signedness == .signed) { |
| ... | ... | @@ -190,7 +211,7 @@ pub const Random = struct { |
| 190 | 211 | |
| 191 | 212 | /// Constant-time implementation off `intRangeAtMostBiased`. |
| 192 | 213 | /// The results of this function may be biased. |
| 193 | pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T { | |
| 214 | pub fn intRangeAtMostBiased(r: Random, comptime T: type, at_least: T, at_most: T) T { | |
| 194 | 215 | assert(at_least <= at_most); |
| 195 | 216 | const info = @typeInfo(T).Int; |
| 196 | 217 | if (info.signedness == .signed) { |
| ... | ... | @@ -209,7 +230,7 @@ pub const Random = struct { |
| 209 | 230 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. |
| 210 | 231 | /// See `uintLessThan`, which this function uses in most cases, |
| 211 | 232 | /// for commentary on the runtime of this function. |
| 212 | pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T { | |
| 233 | pub fn intRangeAtMost(r: Random, comptime T: type, at_least: T, at_most: T) T { | |
| 213 | 234 | assert(at_least <= at_most); |
| 214 | 235 | const info = @typeInfo(T).Int; |
| 215 | 236 | if (info.signedness == .signed) { |
| ... | ... | @@ -230,7 +251,7 @@ pub const Random = struct { |
| 230 | 251 | pub const range = @compileError("deprecated; use intRangeLessThan()"); |
| 231 | 252 | |
| 232 | 253 | /// Return a floating point value evenly distributed in the range [0, 1). |
| 233 | pub fn float(r: *Random, comptime T: type) T { | |
| 254 | pub fn float(r: Random, comptime T: type) T { | |
| 234 | 255 | // Generate a uniform value between [1, 2) and scale down to [0, 1). |
| 235 | 256 | // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. |
| 236 | 257 | switch (T) { |
| ... | ... | @@ -251,7 +272,7 @@ pub const Random = struct { |
| 251 | 272 | /// Return a floating point value normally distributed with mean = 0, stddev = 1. |
| 252 | 273 | /// |
| 253 | 274 | /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. |
| 254 | pub fn floatNorm(r: *Random, comptime T: type) T { | |
| 275 | pub fn floatNorm(r: Random, comptime T: type) T { | |
| 255 | 276 | const value = ziggurat.next_f64(r, ziggurat.NormDist); |
| 256 | 277 | switch (T) { |
| 257 | 278 | f32 => return @floatCast(f32, value), |
| ... | ... | @@ -263,7 +284,7 @@ pub const Random = struct { |
| 263 | 284 | /// Return an exponentially distributed float with a rate parameter of 1. |
| 264 | 285 | /// |
| 265 | 286 | /// To use a different rate parameter, use: floatExp(...) / desiredRate. |
| 266 | pub fn floatExp(r: *Random, comptime T: type) T { | |
| 287 | pub fn floatExp(r: Random, comptime T: type) T { | |
| 267 | 288 | const value = ziggurat.next_f64(r, ziggurat.ExpDist); |
| 268 | 289 | switch (T) { |
| 269 | 290 | f32 => return @floatCast(f32, value), |
| ... | ... | @@ -273,7 +294,7 @@ pub const Random = struct { |
| 273 | 294 | } |
| 274 | 295 | |
| 275 | 296 | /// Shuffle a slice into a random order. |
| 276 | pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { | |
| 297 | pub fn shuffle(r: Random, comptime T: type, buf: []T) void { | |
| 277 | 298 | if (buf.len < 2) { |
| 278 | 299 | return; |
| 279 | 300 | } |
| ... | ... | @@ -303,18 +324,19 @@ pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { |
| 303 | 324 | |
| 304 | 325 | const SequentialPrng = struct { |
| 305 | 326 | const Self = @This(); |
| 306 | random: Random, | |
| 307 | 327 | next_value: u8, |
| 308 | 328 | |
| 309 | 329 | pub fn init() Self { |
| 310 | 330 | return Self{ |
| 311 | .random = Random{ .fillFn = fill }, | |
| 312 | 331 | .next_value = 0, |
| 313 | 332 | }; |
| 314 | 333 | } |
| 315 | 334 | |
| 316 | fn fill(r: *Random, buf: []u8) void { | |
| 317 | const self = @fieldParentPtr(Self, "random", r); | |
| 335 | pub fn random(self: *Self) Random { | |
| 336 | return Random.init(self); | |
| 337 | } | |
| 338 | ||
| 339 | pub fn fill(self: *Self, buf: []u8) void { | |
| 318 | 340 | for (buf) |*b| { |
| 319 | 341 | b.* = self.next_value; |
| 320 | 342 | } |
| ... | ... | @@ -327,45 +349,46 @@ test "Random int" { |
| 327 | 349 | comptime try testRandomInt(); |
| 328 | 350 | } |
| 329 | 351 | fn testRandomInt() !void { |
| 330 | var r = SequentialPrng.init(); | |
| 331 | ||
| 332 | try expect(r.random.int(u0) == 0); | |
| 333 | ||
| 334 | r.next_value = 0; | |
| 335 | try expect(r.random.int(u1) == 0); | |
| 336 | try expect(r.random.int(u1) == 1); | |
| 337 | try expect(r.random.int(u2) == 2); | |
| 338 | try expect(r.random.int(u2) == 3); | |
| 339 | try expect(r.random.int(u2) == 0); | |
| 340 | ||
| 341 | r.next_value = 0xff; | |
| 342 | try expect(r.random.int(u8) == 0xff); | |
| 343 | r.next_value = 0x11; | |
| 344 | try expect(r.random.int(u8) == 0x11); | |
| 345 | ||
| 346 | r.next_value = 0xff; | |
| 347 | try expect(r.random.int(u32) == 0xffffffff); | |
| 348 | r.next_value = 0x11; | |
| 349 | try expect(r.random.int(u32) == 0x11111111); | |
| 350 | ||
| 351 | r.next_value = 0xff; | |
| 352 | try expect(r.random.int(i32) == -1); | |
| 353 | r.next_value = 0x11; | |
| 354 | try expect(r.random.int(i32) == 0x11111111); | |
| 355 | ||
| 356 | r.next_value = 0xff; | |
| 357 | try expect(r.random.int(i8) == -1); | |
| 358 | r.next_value = 0x11; | |
| 359 | try expect(r.random.int(i8) == 0x11); | |
| 360 | ||
| 361 | r.next_value = 0xff; | |
| 362 | try expect(r.random.int(u33) == 0x1ffffffff); | |
| 363 | r.next_value = 0xff; | |
| 364 | try expect(r.random.int(i1) == -1); | |
| 365 | r.next_value = 0xff; | |
| 366 | try expect(r.random.int(i2) == -1); | |
| 367 | r.next_value = 0xff; | |
| 368 | try expect(r.random.int(i33) == -1); | |
| 352 | var rng = SequentialPrng.init(); | |
| 353 | const random = rng.random(); | |
| 354 | ||
| 355 | try expect(random.int(u0) == 0); | |
| 356 | ||
| 357 | rng.next_value = 0; | |
| 358 | try expect(random.int(u1) == 0); | |
| 359 | try expect(random.int(u1) == 1); | |
| 360 | try expect(random.int(u2) == 2); | |
| 361 | try expect(random.int(u2) == 3); | |
| 362 | try expect(random.int(u2) == 0); | |
| 363 | ||
| 364 | rng.next_value = 0xff; | |
| 365 | try expect(random.int(u8) == 0xff); | |
| 366 | rng.next_value = 0x11; | |
| 367 | try expect(random.int(u8) == 0x11); | |
| 368 | ||
| 369 | rng.next_value = 0xff; | |
| 370 | try expect(random.int(u32) == 0xffffffff); | |
| 371 | rng.next_value = 0x11; | |
| 372 | try expect(random.int(u32) == 0x11111111); | |
| 373 | ||
| 374 | rng.next_value = 0xff; | |
| 375 | try expect(random.int(i32) == -1); | |
| 376 | rng.next_value = 0x11; | |
| 377 | try expect(random.int(i32) == 0x11111111); | |
| 378 | ||
| 379 | rng.next_value = 0xff; | |
| 380 | try expect(random.int(i8) == -1); | |
| 381 | rng.next_value = 0x11; | |
| 382 | try expect(random.int(i8) == 0x11); | |
| 383 | ||
| 384 | rng.next_value = 0xff; | |
| 385 | try expect(random.int(u33) == 0x1ffffffff); | |
| 386 | rng.next_value = 0xff; | |
| 387 | try expect(random.int(i1) == -1); | |
| 388 | rng.next_value = 0xff; | |
| 389 | try expect(random.int(i2) == -1); | |
| 390 | rng.next_value = 0xff; | |
| 391 | try expect(random.int(i33) == -1); | |
| 369 | 392 | } |
| 370 | 393 | |
| 371 | 394 | test "Random boolean" { |
| ... | ... | @@ -373,11 +396,13 @@ test "Random boolean" { |
| 373 | 396 | comptime try testRandomBoolean(); |
| 374 | 397 | } |
| 375 | 398 | fn testRandomBoolean() !void { |
| 376 | var r = SequentialPrng.init(); | |
| 377 | try expect(r.random.boolean() == false); | |
| 378 | try expect(r.random.boolean() == true); | |
| 379 | try expect(r.random.boolean() == false); | |
| 380 | try expect(r.random.boolean() == true); | |
| 399 | var rng = SequentialPrng.init(); | |
| 400 | const random = rng.random(); | |
| 401 | ||
| 402 | try expect(random.boolean() == false); | |
| 403 | try expect(random.boolean() == true); | |
| 404 | try expect(random.boolean() == false); | |
| 405 | try expect(random.boolean() == true); | |
| 381 | 406 | } |
| 382 | 407 | |
| 383 | 408 | test "Random enum" { |
| ... | ... | @@ -390,11 +415,12 @@ fn testRandomEnumValue() !void { |
| 390 | 415 | Second, |
| 391 | 416 | Third, |
| 392 | 417 | }; |
| 393 | var r = SequentialPrng.init(); | |
| 394 | r.next_value = 0; | |
| 395 | try expect(r.random.enumValue(TestEnum) == TestEnum.First); | |
| 396 | try expect(r.random.enumValue(TestEnum) == TestEnum.First); | |
| 397 | try expect(r.random.enumValue(TestEnum) == TestEnum.First); | |
| 418 | var rng = SequentialPrng.init(); | |
| 419 | const random = rng.random(); | |
| 420 | rng.next_value = 0; | |
| 421 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 422 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 423 | try expect(random.enumValue(TestEnum) == TestEnum.First); | |
| 398 | 424 | } |
| 399 | 425 | |
| 400 | 426 | test "Random intLessThan" { |
| ... | ... | @@ -403,38 +429,40 @@ test "Random intLessThan" { |
| 403 | 429 | comptime try testRandomIntLessThan(); |
| 404 | 430 | } |
| 405 | 431 | fn testRandomIntLessThan() !void { |
| 406 | var r = SequentialPrng.init(); | |
| 407 | r.next_value = 0xff; | |
| 408 | try expect(r.random.uintLessThan(u8, 4) == 3); | |
| 409 | try expect(r.next_value == 0); | |
| 410 | try expect(r.random.uintLessThan(u8, 4) == 0); | |
| 411 | try expect(r.next_value == 1); | |
| 432 | var rng = SequentialPrng.init(); | |
| 433 | const random = rng.random(); | |
| 434 | ||
| 435 | rng.next_value = 0xff; | |
| 436 | try expect(random.uintLessThan(u8, 4) == 3); | |
| 437 | try expect(rng.next_value == 0); | |
| 438 | try expect(random.uintLessThan(u8, 4) == 0); | |
| 439 | try expect(rng.next_value == 1); | |
| 412 | 440 | |
| 413 | r.next_value = 0; | |
| 414 | try expect(r.random.uintLessThan(u64, 32) == 0); | |
| 441 | rng.next_value = 0; | |
| 442 | try expect(random.uintLessThan(u64, 32) == 0); | |
| 415 | 443 | |
| 416 | 444 | // trigger the bias rejection code path |
| 417 | r.next_value = 0; | |
| 418 | try expect(r.random.uintLessThan(u8, 3) == 0); | |
| 445 | rng.next_value = 0; | |
| 446 | try expect(random.uintLessThan(u8, 3) == 0); | |
| 419 | 447 | // verify we incremented twice |
| 420 | try expect(r.next_value == 2); | |
| 421 | ||
| 422 | r.next_value = 0xff; | |
| 423 | try expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); | |
| 424 | r.next_value = 0xff; | |
| 425 | try expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); | |
| 426 | ||
| 427 | r.next_value = 0xff; | |
| 428 | try expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); | |
| 429 | r.next_value = 0xff; | |
| 430 | try expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); | |
| 431 | r.next_value = 0xff; | |
| 432 | try expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1); | |
| 433 | ||
| 434 | r.next_value = 0xff; | |
| 435 | try expect(r.random.intRangeLessThan(i3, -4, 0) == -1); | |
| 436 | r.next_value = 0xff; | |
| 437 | try expect(r.random.intRangeLessThan(i3, -2, 2) == 1); | |
| 448 | try expect(rng.next_value == 2); | |
| 449 | ||
| 450 | rng.next_value = 0xff; | |
| 451 | try expect(random.intRangeLessThan(u8, 0, 0x80) == 0x7f); | |
| 452 | rng.next_value = 0xff; | |
| 453 | try expect(random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); | |
| 454 | ||
| 455 | rng.next_value = 0xff; | |
| 456 | try expect(random.intRangeLessThan(i8, 0, 0x40) == 0x3f); | |
| 457 | rng.next_value = 0xff; | |
| 458 | try expect(random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); | |
| 459 | rng.next_value = 0xff; | |
| 460 | try expect(random.intRangeLessThan(i8, -0x80, 0) == -1); | |
| 461 | ||
| 462 | rng.next_value = 0xff; | |
| 463 | try expect(random.intRangeLessThan(i3, -4, 0) == -1); | |
| 464 | rng.next_value = 0xff; | |
| 465 | try expect(random.intRangeLessThan(i3, -2, 2) == 1); | |
| 438 | 466 | } |
| 439 | 467 | |
| 440 | 468 | test "Random intAtMost" { |
| ... | ... | @@ -443,67 +471,70 @@ test "Random intAtMost" { |
| 443 | 471 | comptime try testRandomIntAtMost(); |
| 444 | 472 | } |
| 445 | 473 | fn testRandomIntAtMost() !void { |
| 446 | var r = SequentialPrng.init(); | |
| 447 | r.next_value = 0xff; | |
| 448 | try expect(r.random.uintAtMost(u8, 3) == 3); | |
| 449 | try expect(r.next_value == 0); | |
| 450 | try expect(r.random.uintAtMost(u8, 3) == 0); | |
| 474 | var rng = SequentialPrng.init(); | |
| 475 | const random = rng.random(); | |
| 476 | ||
| 477 | rng.next_value = 0xff; | |
| 478 | try expect(random.uintAtMost(u8, 3) == 3); | |
| 479 | try expect(rng.next_value == 0); | |
| 480 | try expect(random.uintAtMost(u8, 3) == 0); | |
| 451 | 481 | |
| 452 | 482 | // trigger the bias rejection code path |
| 453 | r.next_value = 0; | |
| 454 | try expect(r.random.uintAtMost(u8, 2) == 0); | |
| 483 | rng.next_value = 0; | |
| 484 | try expect(random.uintAtMost(u8, 2) == 0); | |
| 455 | 485 | // verify we incremented twice |
| 456 | try expect(r.next_value == 2); | |
| 457 | ||
| 458 | r.next_value = 0xff; | |
| 459 | try expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); | |
| 460 | r.next_value = 0xff; | |
| 461 | try expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); | |
| 462 | ||
| 463 | r.next_value = 0xff; | |
| 464 | try expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); | |
| 465 | r.next_value = 0xff; | |
| 466 | try expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); | |
| 467 | r.next_value = 0xff; | |
| 468 | try expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1); | |
| 469 | ||
| 470 | r.next_value = 0xff; | |
| 471 | try expect(r.random.intRangeAtMost(i3, -4, -1) == -1); | |
| 472 | r.next_value = 0xff; | |
| 473 | try expect(r.random.intRangeAtMost(i3, -2, 1) == 1); | |
| 474 | ||
| 475 | try expect(r.random.uintAtMost(u0, 0) == 0); | |
| 486 | try expect(rng.next_value == 2); | |
| 487 | ||
| 488 | rng.next_value = 0xff; | |
| 489 | try expect(random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); | |
| 490 | rng.next_value = 0xff; | |
| 491 | try expect(random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); | |
| 492 | ||
| 493 | rng.next_value = 0xff; | |
| 494 | try expect(random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); | |
| 495 | rng.next_value = 0xff; | |
| 496 | try expect(random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); | |
| 497 | rng.next_value = 0xff; | |
| 498 | try expect(random.intRangeAtMost(i8, -0x80, -1) == -1); | |
| 499 | ||
| 500 | rng.next_value = 0xff; | |
| 501 | try expect(random.intRangeAtMost(i3, -4, -1) == -1); | |
| 502 | rng.next_value = 0xff; | |
| 503 | try expect(random.intRangeAtMost(i3, -2, 1) == 1); | |
| 504 | ||
| 505 | try expect(random.uintAtMost(u0, 0) == 0); | |
| 476 | 506 | } |
| 477 | 507 | |
| 478 | 508 | test "Random Biased" { |
| 479 | var r = DefaultPrng.init(0); | |
| 509 | var prng = DefaultPrng.init(0); | |
| 510 | const random = prng.random(); | |
| 480 | 511 | // Not thoroughly checking the logic here. |
| 481 | 512 | // Just want to execute all the paths with different types. |
| 482 | 513 | |
| 483 | try expect(r.random.uintLessThanBiased(u1, 1) == 0); | |
| 484 | try expect(r.random.uintLessThanBiased(u32, 10) < 10); | |
| 485 | try expect(r.random.uintLessThanBiased(u64, 20) < 20); | |
| 514 | try expect(random.uintLessThanBiased(u1, 1) == 0); | |
| 515 | try expect(random.uintLessThanBiased(u32, 10) < 10); | |
| 516 | try expect(random.uintLessThanBiased(u64, 20) < 20); | |
| 486 | 517 | |
| 487 | try expect(r.random.uintAtMostBiased(u0, 0) == 0); | |
| 488 | try expect(r.random.uintAtMostBiased(u1, 0) <= 0); | |
| 489 | try expect(r.random.uintAtMostBiased(u32, 10) <= 10); | |
| 490 | try expect(r.random.uintAtMostBiased(u64, 20) <= 20); | |
| 518 | try expect(random.uintAtMostBiased(u0, 0) == 0); | |
| 519 | try expect(random.uintAtMostBiased(u1, 0) <= 0); | |
| 520 | try expect(random.uintAtMostBiased(u32, 10) <= 10); | |
| 521 | try expect(random.uintAtMostBiased(u64, 20) <= 20); | |
| 491 | 522 | |
| 492 | try expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); | |
| 493 | try expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); | |
| 494 | try expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); | |
| 495 | try expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); | |
| 496 | try expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); | |
| 497 | try expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); | |
| 523 | try expect(random.intRangeLessThanBiased(u1, 0, 1) == 0); | |
| 524 | try expect(random.intRangeLessThanBiased(i1, -1, 0) == -1); | |
| 525 | try expect(random.intRangeLessThanBiased(u32, 10, 20) >= 10); | |
| 526 | try expect(random.intRangeLessThanBiased(i32, 10, 20) >= 10); | |
| 527 | try expect(random.intRangeLessThanBiased(u64, 20, 40) >= 20); | |
| 528 | try expect(random.intRangeLessThanBiased(i64, 20, 40) >= 20); | |
| 498 | 529 | |
| 499 | 530 | // uncomment for broken module error: |
| 500 | //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); | |
| 501 | try expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); | |
| 502 | try expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); | |
| 503 | try expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); | |
| 504 | try expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); | |
| 505 | try expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); | |
| 506 | try expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); | |
| 531 | //expect(random.intRangeAtMostBiased(u0, 0, 0) == 0); | |
| 532 | try expect(random.intRangeAtMostBiased(u1, 0, 1) >= 0); | |
| 533 | try expect(random.intRangeAtMostBiased(i1, -1, 0) >= -1); | |
| 534 | try expect(random.intRangeAtMostBiased(u32, 10, 20) >= 10); | |
| 535 | try expect(random.intRangeAtMostBiased(i32, 10, 20) >= 10); | |
| 536 | try expect(random.intRangeAtMostBiased(u64, 20, 40) >= 20); | |
| 537 | try expect(random.intRangeAtMostBiased(i64, 20, 40) >= 20); | |
| 507 | 538 | } |
| 508 | 539 | |
| 509 | 540 | // Generator to extend 64-bit seed values into longer sequences. |
| ... | ... | @@ -547,14 +578,15 @@ test "splitmix64 sequence" { |
| 547 | 578 | // Actual Random helper function tests, pcg engine is assumed correct. |
| 548 | 579 | test "Random float" { |
| 549 | 580 | var prng = DefaultPrng.init(0); |
| 581 | const random = prng.random(); | |
| 550 | 582 | |
| 551 | 583 | var i: usize = 0; |
| 552 | 584 | while (i < 1000) : (i += 1) { |
| 553 | const val1 = prng.random.float(f32); | |
| 585 | const val1 = random.float(f32); | |
| 554 | 586 | try expect(val1 >= 0.0); |
| 555 | 587 | try expect(val1 < 1.0); |
| 556 | 588 | |
| 557 | const val2 = prng.random.float(f64); | |
| 589 | const val2 = random.float(f64); | |
| 558 | 590 | try expect(val2 >= 0.0); |
| 559 | 591 | try expect(val2 < 1.0); |
| 560 | 592 | } |
| ... | ... | @@ -562,13 +594,14 @@ test "Random float" { |
| 562 | 594 | |
| 563 | 595 | test "Random shuffle" { |
| 564 | 596 | var prng = DefaultPrng.init(0); |
| 597 | const random = prng.random(); | |
| 565 | 598 | |
| 566 | 599 | var seq = [_]u8{ 0, 1, 2, 3, 4 }; |
| 567 | 600 | var seen = [_]bool{false} ** 5; |
| 568 | 601 | |
| 569 | 602 | var i: usize = 0; |
| 570 | 603 | while (i < 1000) : (i += 1) { |
| 571 | prng.random.shuffle(u8, seq[0..]); | |
| 604 | random.shuffle(u8, seq[0..]); | |
| 572 | 605 | seen[seq[0]] = true; |
| 573 | 606 | try expect(sumArray(seq[0..]) == 10); |
| 574 | 607 | } |
| ... | ... | @@ -588,17 +621,19 @@ fn sumArray(s: []const u8) u32 { |
| 588 | 621 | |
| 589 | 622 | test "Random range" { |
| 590 | 623 | var prng = DefaultPrng.init(0); |
| 591 | try testRange(&prng.random, -4, 3); | |
| 592 | try testRange(&prng.random, -4, -1); | |
| 593 | try testRange(&prng.random, 10, 14); | |
| 594 | try testRange(&prng.random, -0x80, 0x7f); | |
| 624 | const random = prng.random(); | |
| 625 | ||
| 626 | try testRange(random, -4, 3); | |
| 627 | try testRange(random, -4, -1); | |
| 628 | try testRange(random, 10, 14); | |
| 629 | try testRange(random, -0x80, 0x7f); | |
| 595 | 630 | } |
| 596 | 631 | |
| 597 | fn testRange(r: *Random, start: i8, end: i8) !void { | |
| 632 | fn testRange(r: Random, start: i8, end: i8) !void { | |
| 598 | 633 | try testRangeBias(r, start, end, true); |
| 599 | 634 | try testRangeBias(r, start, end, false); |
| 600 | 635 | } |
| 601 | fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) !void { | |
| 636 | fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void { | |
| 602 | 637 | const count = @intCast(usize, @as(i32, end) - @as(i32, start)); |
| 603 | 638 | var values_buffer = [_]bool{false} ** 0x100; |
| 604 | 639 | const values = values_buffer[0..count]; |
| ... | ... | @@ -617,9 +652,10 @@ test "CSPRNG" { |
| 617 | 652 | var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined; |
| 618 | 653 | std.crypto.random.bytes(&secret_seed); |
| 619 | 654 | var csprng = DefaultCsprng.init(secret_seed); |
| 620 | const a = csprng.random.int(u64); | |
| 621 | const b = csprng.random.int(u64); | |
| 622 | const c = csprng.random.int(u64); | |
| 655 | const random = csprng.random(); | |
| 656 | const a = random.int(u64); | |
| 657 | const b = random.int(u64); | |
| 658 | const c = random.int(u64); | |
| 623 | 659 | try expect(a ^ b ^ c != 0); |
| 624 | 660 | } |
| 625 | 661 |
lib/std/rand/Gimli.zig+4-4| ... | ... | @@ -5,7 +5,6 @@ const Random = std.rand.Random; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const Gimli = @This(); |
| 7 | 7 | |
| 8 | random: Random, | |
| 9 | 8 | state: std.crypto.core.Gimli, |
| 10 | 9 | |
| 11 | 10 | pub const secret_seed_length = 32; |
| ... | ... | @@ -16,15 +15,16 @@ pub fn init(secret_seed: [secret_seed_length]u8) Gimli { |
| 16 | 15 | mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed); |
| 17 | 16 | mem.set(u8, initial_state[secret_seed_length..], 0); |
| 18 | 17 | var self = Gimli{ |
| 19 | .random = Random{ .fillFn = fill }, | |
| 20 | 18 | .state = std.crypto.core.Gimli.init(initial_state), |
| 21 | 19 | }; |
| 22 | 20 | return self; |
| 23 | 21 | } |
| 24 | 22 | |
| 25 | fn fill(r: *Random, buf: []u8) void { | |
| 26 | const self = @fieldParentPtr(Gimli, "random", r); | |
| 23 | pub fn random(self: *Gimli) Random { | |
| 24 | return Random.init(self); | |
| 25 | } | |
| 27 | 26 | |
| 27 | pub fn fill(self: *Gimli, buf: []u8) void { | |
| 28 | 28 | if (buf.len != 0) { |
| 29 | 29 | self.state.squeeze(buf); |
| 30 | 30 | } else { |
lib/std/rand/Isaac64.zig+6-7| ... | ... | @@ -8,8 +8,6 @@ const Random = std.rand.Random; |
| 8 | 8 | const mem = std.mem; |
| 9 | 9 | const Isaac64 = @This(); |
| 10 | 10 | |
| 11 | random: Random, | |
| 12 | ||
| 13 | 11 | r: [256]u64, |
| 14 | 12 | m: [256]u64, |
| 15 | 13 | a: u64, |
| ... | ... | @@ -19,7 +17,6 @@ i: usize, |
| 19 | 17 | |
| 20 | 18 | pub fn init(init_s: u64) Isaac64 { |
| 21 | 19 | var isaac = Isaac64{ |
| 22 | .random = Random{ .fillFn = fill }, | |
| 23 | 20 | .r = undefined, |
| 24 | 21 | .m = undefined, |
| 25 | 22 | .a = undefined, |
| ... | ... | @@ -33,6 +30,10 @@ pub fn init(init_s: u64) Isaac64 { |
| 33 | 30 | return isaac; |
| 34 | 31 | } |
| 35 | 32 | |
| 33 | pub fn random(self: *Isaac64) Random { | |
| 34 | return Random.init(self); | |
| 35 | } | |
| 36 | ||
| 36 | 37 | fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { |
| 37 | 38 | const x = self.m[base + m1]; |
| 38 | 39 | self.a = mix +% self.m[base + m2]; |
| ... | ... | @@ -149,9 +150,7 @@ fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { |
| 149 | 150 | self.i = self.r.len; // trigger refill on first value |
| 150 | 151 | } |
| 151 | 152 | |
| 152 | fn fill(r: *Random, buf: []u8) void { | |
| 153 | const self = @fieldParentPtr(Isaac64, "random", r); | |
| 154 | ||
| 153 | pub fn fill(self: *Isaac64, buf: []u8) void { | |
| 155 | 154 | var i: usize = 0; |
| 156 | 155 | const aligned_len = buf.len - (buf.len & 7); |
| 157 | 156 | |
| ... | ... | @@ -230,7 +229,7 @@ test "isaac64 fill" { |
| 230 | 229 | var buf0: [8]u8 = undefined; |
| 231 | 230 | var buf1: [7]u8 = undefined; |
| 232 | 231 | std.mem.writeIntLittle(u64, &buf0, s); |
| 233 | Isaac64.fill(&r.random, &buf1); | |
| 232 | r.fill(&buf1); | |
| 234 | 233 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); |
| 235 | 234 | } |
| 236 | 235 | } |
lib/std/rand/Pcg.zig+6-7| ... | ... | @@ -8,14 +8,11 @@ const Pcg = @This(); |
| 8 | 8 | |
| 9 | 9 | const default_multiplier = 6364136223846793005; |
| 10 | 10 | |
| 11 | random: Random, | |
| 12 | ||
| 13 | 11 | s: u64, |
| 14 | 12 | i: u64, |
| 15 | 13 | |
| 16 | 14 | pub fn init(init_s: u64) Pcg { |
| 17 | 15 | var pcg = Pcg{ |
| 18 | .random = Random{ .fillFn = fill }, | |
| 19 | 16 | .s = undefined, |
| 20 | 17 | .i = undefined, |
| 21 | 18 | }; |
| ... | ... | @@ -24,6 +21,10 @@ pub fn init(init_s: u64) Pcg { |
| 24 | 21 | return pcg; |
| 25 | 22 | } |
| 26 | 23 | |
| 24 | pub fn random(self: *Pcg) Random { | |
| 25 | return Random.init(self); | |
| 26 | } | |
| 27 | ||
| 27 | 28 | fn next(self: *Pcg) u32 { |
| 28 | 29 | const l = self.s; |
| 29 | 30 | self.s = l *% default_multiplier +% (self.i | 1); |
| ... | ... | @@ -48,9 +49,7 @@ fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { |
| 48 | 49 | self.s = self.s *% default_multiplier +% self.i; |
| 49 | 50 | } |
| 50 | 51 | |
| 51 | fn fill(r: *Random, buf: []u8) void { | |
| 52 | const self = @fieldParentPtr(Pcg, "random", r); | |
| 53 | ||
| 52 | pub fn fill(self: *Pcg, buf: []u8) void { | |
| 54 | 53 | var i: usize = 0; |
| 55 | 54 | const aligned_len = buf.len - (buf.len & 7); |
| 56 | 55 | |
| ... | ... | @@ -113,7 +112,7 @@ test "pcg fill" { |
| 113 | 112 | var buf0: [4]u8 = undefined; |
| 114 | 113 | var buf1: [3]u8 = undefined; |
| 115 | 114 | std.mem.writeIntLittle(u32, &buf0, s); |
| 116 | Pcg.fill(&r.random, &buf1); | |
| 115 | r.fill(&buf1); | |
| 117 | 116 | try std.testing.expect(std.mem.eql(u8, buf0[0..3], buf1[0..])); |
| 118 | 117 | } |
| 119 | 118 | } |
lib/std/rand/Sfc64.zig+7-9| ... | ... | @@ -7,8 +7,6 @@ const Random = std.rand.Random; |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const Sfc64 = @This(); |
| 9 | 9 | |
| 10 | random: Random, | |
| 11 | ||
| 12 | 10 | a: u64 = undefined, |
| 13 | 11 | b: u64 = undefined, |
| 14 | 12 | c: u64 = undefined, |
| ... | ... | @@ -19,14 +17,16 @@ const RightShift = 11; |
| 19 | 17 | const LeftShift = 3; |
| 20 | 18 | |
| 21 | 19 | pub fn init(init_s: u64) Sfc64 { |
| 22 | var x = Sfc64{ | |
| 23 | .random = Random{ .fillFn = fill }, | |
| 24 | }; | |
| 20 | var x = Sfc64{}; | |
| 25 | 21 | |
| 26 | 22 | x.seed(init_s); |
| 27 | 23 | return x; |
| 28 | 24 | } |
| 29 | 25 | |
| 26 | pub fn random(self: *Sfc64) Random { | |
| 27 | return Random.init(self); | |
| 28 | } | |
| 29 | ||
| 30 | 30 | fn next(self: *Sfc64) u64 { |
| 31 | 31 | const tmp = self.a +% self.b +% self.counter; |
| 32 | 32 | self.counter += 1; |
| ... | ... | @@ -47,9 +47,7 @@ fn seed(self: *Sfc64, init_s: u64) void { |
| 47 | 47 | } |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | fn fill(r: *Random, buf: []u8) void { | |
| 51 | const self = @fieldParentPtr(Sfc64, "random", r); | |
| 52 | ||
| 50 | pub fn fill(self: *Sfc64, buf: []u8) void { | |
| 53 | 51 | var i: usize = 0; |
| 54 | 52 | const aligned_len = buf.len - (buf.len & 7); |
| 55 | 53 | |
| ... | ... | @@ -128,7 +126,7 @@ test "Sfc64 fill" { |
| 128 | 126 | var buf0: [8]u8 = undefined; |
| 129 | 127 | var buf1: [7]u8 = undefined; |
| 130 | 128 | std.mem.writeIntLittle(u64, &buf0, s); |
| 131 | Sfc64.fill(&r.random, &buf1); | |
| 129 | r.fill(&buf1); | |
| 132 | 130 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); |
| 133 | 131 | } |
| 134 | 132 | } |
lib/std/rand/Xoroshiro128.zig+7-10| ... | ... | @@ -7,20 +7,19 @@ const Random = std.rand.Random; |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const Xoroshiro128 = @This(); |
| 9 | 9 | |
| 10 | random: Random, | |
| 11 | ||
| 12 | 10 | s: [2]u64, |
| 13 | 11 | |
| 14 | 12 | pub fn init(init_s: u64) Xoroshiro128 { |
| 15 | var x = Xoroshiro128{ | |
| 16 | .random = Random{ .fillFn = fill }, | |
| 17 | .s = undefined, | |
| 18 | }; | |
| 13 | var x = Xoroshiro128{ .s = undefined }; | |
| 19 | 14 | |
| 20 | 15 | x.seed(init_s); |
| 21 | 16 | return x; |
| 22 | 17 | } |
| 23 | 18 | |
| 19 | pub fn random(self: *Xoroshiro128) Random { | |
| 20 | return Random.init(self); | |
| 21 | } | |
| 22 | ||
| 24 | 23 | fn next(self: *Xoroshiro128) u64 { |
| 25 | 24 | const s0 = self.s[0]; |
| 26 | 25 | var s1 = self.s[1]; |
| ... | ... | @@ -66,9 +65,7 @@ pub fn seed(self: *Xoroshiro128, init_s: u64) void { |
| 66 | 65 | self.s[1] = gen.next(); |
| 67 | 66 | } |
| 68 | 67 | |
| 69 | fn fill(r: *Random, buf: []u8) void { | |
| 70 | const self = @fieldParentPtr(Xoroshiro128, "random", r); | |
| 71 | ||
| 68 | pub fn fill(self: *Xoroshiro128, buf: []u8) void { | |
| 72 | 69 | var i: usize = 0; |
| 73 | 70 | const aligned_len = buf.len - (buf.len & 7); |
| 74 | 71 | |
| ... | ... | @@ -144,7 +141,7 @@ test "xoroshiro fill" { |
| 144 | 141 | var buf0: [8]u8 = undefined; |
| 145 | 142 | var buf1: [7]u8 = undefined; |
| 146 | 143 | std.mem.writeIntLittle(u64, &buf0, s); |
| 147 | Xoroshiro128.fill(&r.random, &buf1); | |
| 144 | r.fill(&buf1); | |
| 148 | 145 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); |
| 149 | 146 | } |
| 150 | 147 | } |
lib/std/rand/Xoshiro256.zig+6-7| ... | ... | @@ -7,13 +7,10 @@ const Random = std.rand.Random; |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const Xoshiro256 = @This(); |
| 9 | 9 | |
| 10 | random: Random, | |
| 11 | ||
| 12 | 10 | s: [4]u64, |
| 13 | 11 | |
| 14 | 12 | pub fn init(init_s: u64) Xoshiro256 { |
| 15 | 13 | var x = Xoshiro256{ |
| 16 | .random = Random{ .fillFn = fill }, | |
| 17 | 14 | .s = undefined, |
| 18 | 15 | }; |
| 19 | 16 | |
| ... | ... | @@ -21,6 +18,10 @@ pub fn init(init_s: u64) Xoshiro256 { |
| 21 | 18 | return x; |
| 22 | 19 | } |
| 23 | 20 | |
| 21 | pub fn random(self: *Xoshiro256) Random { | |
| 22 | return Random.init(self); | |
| 23 | } | |
| 24 | ||
| 24 | 25 | fn next(self: *Xoshiro256) u64 { |
| 25 | 26 | const r = math.rotl(u64, self.s[0] +% self.s[3], 23) +% self.s[0]; |
| 26 | 27 | |
| ... | ... | @@ -64,9 +65,7 @@ pub fn seed(self: *Xoshiro256, init_s: u64) void { |
| 64 | 65 | self.s[3] = gen.next(); |
| 65 | 66 | } |
| 66 | 67 | |
| 67 | fn fill(r: *Random, buf: []u8) void { | |
| 68 | const self = @fieldParentPtr(Xoshiro256, "random", r); | |
| 69 | ||
| 68 | pub fn fill(self: *Xoshiro256, buf: []u8) void { | |
| 70 | 69 | var i: usize = 0; |
| 71 | 70 | const aligned_len = buf.len - (buf.len & 7); |
| 72 | 71 | |
| ... | ... | @@ -138,7 +137,7 @@ test "xoroshiro fill" { |
| 138 | 137 | var buf0: [8]u8 = undefined; |
| 139 | 138 | var buf1: [7]u8 = undefined; |
| 140 | 139 | std.mem.writeIntLittle(u64, &buf0, s); |
| 141 | Xoshiro256.fill(&r.random, &buf1); | |
| 140 | r.fill(&buf1); | |
| 142 | 141 | try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); |
| 143 | 142 | } |
| 144 | 143 | } |
lib/std/rand/ziggurat.zig+11-7| ... | ... | @@ -13,7 +13,7 @@ const builtin = @import("builtin"); |
| 13 | 13 | const math = std.math; |
| 14 | 14 | const Random = std.rand.Random; |
| 15 | 15 | |
| 16 | pub fn next_f64(random: *Random, comptime tables: ZigTable) f64 { | |
| 16 | pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { | |
| 17 | 17 | while (true) { |
| 18 | 18 | // We manually construct a float from parts as we can avoid an extra random lookup here by |
| 19 | 19 | // using the unused exponent for the lookup table entry. |
| ... | ... | @@ -61,7 +61,7 @@ pub const ZigTable = struct { |
| 61 | 61 | // whether the distribution is symmetric |
| 62 | 62 | is_symmetric: bool, |
| 63 | 63 | // fallback calculation in the case we are in the 0 block |
| 64 | zero_case: fn (*Random, f64) f64, | |
| 64 | zero_case: fn (Random, f64) f64, | |
| 65 | 65 | }; |
| 66 | 66 | |
| 67 | 67 | // zigNorInit |
| ... | ... | @@ -71,7 +71,7 @@ fn ZigTableGen( |
| 71 | 71 | comptime v: f64, |
| 72 | 72 | comptime f: fn (f64) f64, |
| 73 | 73 | comptime f_inv: fn (f64) f64, |
| 74 | comptime zero_case: fn (*Random, f64) f64, | |
| 74 | comptime zero_case: fn (Random, f64) f64, | |
| 75 | 75 | ) ZigTable { |
| 76 | 76 | var tables: ZigTable = undefined; |
| 77 | 77 | |
| ... | ... | @@ -111,7 +111,7 @@ fn norm_f(x: f64) f64 { |
| 111 | 111 | fn norm_f_inv(y: f64) f64 { |
| 112 | 112 | return math.sqrt(-2.0 * math.ln(y)); |
| 113 | 113 | } |
| 114 | fn norm_zero_case(random: *Random, u: f64) f64 { | |
| 114 | fn norm_zero_case(random: Random, u: f64) f64 { | |
| 115 | 115 | var x: f64 = 1; |
| 116 | 116 | var y: f64 = 0; |
| 117 | 117 | |
| ... | ... | @@ -133,9 +133,11 @@ test "normal dist sanity" { |
| 133 | 133 | if (please_windows_dont_oom) return error.SkipZigTest; |
| 134 | 134 | |
| 135 | 135 | var prng = std.rand.DefaultPrng.init(0); |
| 136 | const random = prng.random(); | |
| 137 | ||
| 136 | 138 | var i: usize = 0; |
| 137 | 139 | while (i < 1000) : (i += 1) { |
| 138 | _ = prng.random.floatNorm(f64); | |
| 140 | _ = random.floatNorm(f64); | |
| 139 | 141 | } |
| 140 | 142 | } |
| 141 | 143 | |
| ... | ... | @@ -154,7 +156,7 @@ fn exp_f(x: f64) f64 { |
| 154 | 156 | fn exp_f_inv(y: f64) f64 { |
| 155 | 157 | return -math.ln(y); |
| 156 | 158 | } |
| 157 | fn exp_zero_case(random: *Random, _: f64) f64 { | |
| 159 | fn exp_zero_case(random: Random, _: f64) f64 { | |
| 158 | 160 | return exp_r - math.ln(random.float(f64)); |
| 159 | 161 | } |
| 160 | 162 | |
| ... | ... | @@ -162,9 +164,11 @@ test "exp dist sanity" { |
| 162 | 164 | if (please_windows_dont_oom) return error.SkipZigTest; |
| 163 | 165 | |
| 164 | 166 | var prng = std.rand.DefaultPrng.init(0); |
| 167 | const random = prng.random(); | |
| 168 | ||
| 165 | 169 | var i: usize = 0; |
| 166 | 170 | while (i < 1000) : (i += 1) { |
| 167 | _ = prng.random.floatExp(f64); | |
| 171 | _ = random.floatExp(f64); | |
| 168 | 172 | } |
| 169 | 173 | } |
| 170 | 174 |
lib/std/sort.zig+3-2| ... | ... | @@ -1328,16 +1328,17 @@ test "another sort case" { |
| 1328 | 1328 | |
| 1329 | 1329 | test "sort fuzz testing" { |
| 1330 | 1330 | var prng = std.rand.DefaultPrng.init(0x12345678); |
| 1331 | const random = prng.random(); | |
| 1331 | 1332 | const test_case_count = 10; |
| 1332 | 1333 | var i: usize = 0; |
| 1333 | 1334 | while (i < test_case_count) : (i += 1) { |
| 1334 | try fuzzTest(&prng.random); | |
| 1335 | try fuzzTest(random); | |
| 1335 | 1336 | } |
| 1336 | 1337 | } |
| 1337 | 1338 | |
| 1338 | 1339 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 1339 | 1340 | |
| 1340 | fn fuzzTest(rng: *std.rand.Random) !void { | |
| 1341 | fn fuzzTest(rng: std.rand.Random) !void { | |
| 1341 | 1342 | const array_size = rng.intRangeLessThan(usize, 0, 1000); |
| 1342 | 1343 | var array = try testing.allocator.alloc(IdAndValue, array_size); |
| 1343 | 1344 | defer testing.allocator.free(array); |