authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-18 14:48:51+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-18 14:48:51+02:00
log3c0c0f899bfd9965e92dd1821b72150e9ec023ab
treee456a72feb7d8f9ffb462015637373df70c2c95e
parent8082323dfd65c20328991d9a0d6740b779b26670
parent1f0e9e4e1104dc1140de0f68778536d4f49a5cfa
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13417 from InKryption/rand-deterministic-indexing

std.Random: add functions with explicit index type

1 files changed, 65 insertions(+), 8 deletions(-)

lib/std/rand.zig+65-8
......@@ -61,14 +61,41 @@ pub const Random = struct {
6161 }
6262
6363 /// Returns a random value from an enum, evenly distributed.
64 pub fn enumValue(r: Random, comptime EnumType: type) EnumType {
64 ///
65 /// Note that this will not yield consistent results across all targets
66 /// due to dependence on the representation of `usize` as an index.
67 /// See `enumValueWithIndex` for further commentary.
68 pub inline fn enumValue(r: Random, comptime EnumType: type) EnumType {
69 return r.enumValueWithIndex(EnumType, usize);
70 }
71
72 /// Returns a random value from an enum, evenly distributed.
73 ///
74 /// An index into an array of all named values is generated using the
75 /// specified `Index` type to determine the return value.
76 /// This allows for results to be independent of `usize` representation.
77 ///
78 /// Prefer `enumValue` if this isn't important.
79 ///
80 /// See `uintLessThan`, which this function uses in most cases,
81 /// for commentary on the runtime of this function.
82 pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: type) EnumType {
6583 comptime assert(@typeInfo(EnumType) == .Enum);
6684
6785 // We won't use int -> enum casting because enum elements can have
6886 // arbitrary values. Instead we'll randomly pick one of the type's values.
69 const values = std.enums.values(EnumType);
70 const index = r.uintLessThan(usize, values.len);
71 return values[index];
87 const values = comptime std.enums.values(EnumType);
88 comptime assert(values.len > 0); // can't return anything
89 comptime assert(maxInt(Index) >= values.len - 1); // can't access all values
90 comptime if (values.len == 1) return values[0];
91
92 const index = if (comptime values.len - 1 == maxInt(Index))
93 r.int(Index)
94 else
95 r.uintLessThan(Index, values.len);
96
97 const MinInt = MinArrayIndex(Index);
98 return values[@intCast(MinInt, index)];
7299 }
73100
74101 /// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`.
......@@ -323,14 +350,37 @@ pub const Random = struct {
323350 }
324351
325352 /// Shuffle a slice into a random order.
326 pub fn shuffle(r: Random, comptime T: type, buf: []T) void {
353 ///
354 /// Note that this will not yield consistent results across all targets
355 /// due to dependence on the representation of `usize` as an index.
356 /// See `shuffleWithIndex` for further commentary.
357 pub inline fn shuffle(r: Random, comptime T: type, buf: []T) void {
358 r.shuffleWithIndex(T, buf, usize);
359 }
360
361 /// Shuffle a slice into a random order, using an index of a
362 /// specified type to maintain distribution across targets.
363 /// Asserts the index type can represent `buf.len`.
364 ///
365 /// Indexes into the slice are generated using the specified `Index`
366 /// type, which determines distribution properties. This allows for
367 /// results to be independent of `usize` representation.
368 ///
369 /// Prefer `shuffle` if this isn't important.
370 ///
371 /// See `intRangeLessThan`, which this function uses,
372 /// for commentary on the runtime of this function.
373 pub fn shuffleWithIndex(r: Random, comptime T: type, buf: []T, comptime Index: type) void {
374 const MinInt = MinArrayIndex(Index);
327375 if (buf.len < 2) {
328376 return;
329377 }
330378
331 var i: usize = 0;
332 while (i < buf.len - 1) : (i += 1) {
333 const j = r.intRangeLessThan(usize, i, buf.len);
379 // `i <= j < max <= maxInt(MinInt)`
380 const max = @intCast(MinInt, buf.len);
381 var i: MinInt = 0;
382 while (i < max - 1) : (i += 1) {
383 const j = @intCast(MinInt, r.intRangeLessThan(Index, i, max));
334384 mem.swap(T, &buf[i], &buf[j]);
335385 }
336386 }
......@@ -370,6 +420,13 @@ pub const Random = struct {
370420
371421 unreachable;
372422 }
423
424 /// Returns the smallest of `Index` and `usize`.
425 fn MinArrayIndex(comptime Index: type) type {
426 const index_info = @typeInfo(Index).Int;
427 assert(index_info.signedness == .unsigned);
428 return if (index_info.bits >= @typeInfo(usize).Int.bits) usize else Index;
429 }
373430};
374431
375432/// Convert a random integer 0 <= random_int <= maxValue(T),