| ... | @@ -61,14 +61,41 @@ pub const Random = struct { | ... | @@ -61,14 +61,41 @@ pub const Random = struct { |
| 61 | } | 61 | } |
| 62 | | 62 | |
| 63 | /// Returns a random value from an enum, evenly distributed. | 63 | /// 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 { |
| 65 | comptime assert(@typeInfo(EnumType) == .Enum); | 83 | comptime assert(@typeInfo(EnumType) == .Enum); |
| 66 | | 84 | |
| 67 | // We won't use int -> enum casting because enum elements can have | 85 | // We won't use int -> enum casting because enum elements can have |
| 68 | // arbitrary values. Instead we'll randomly pick one of the type's values. | 86 | // arbitrary values. Instead we'll randomly pick one of the type's values. |
| 69 | const values = std.enums.values(EnumType); | 87 | const values = comptime std.enums.values(EnumType); |
| 70 | const index = r.uintLessThan(usize, values.len); | 88 | comptime assert(values.len > 0); // can't return anything |
| 71 | return values[index]; | 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)]; |
| 72 | } | 99 | } |
| 73 | | 100 | |
| 74 | /// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`. | 101 | /// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`. |
| ... | @@ -323,14 +350,37 @@ pub const Random = struct { | ... | @@ -323,14 +350,37 @@ pub const Random = struct { |
| 323 | } | 350 | } |
| 324 | | 351 | |
| 325 | /// Shuffle a slice into a random order. | 352 | /// 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); |
| 327 | if (buf.len < 2) { | 375 | if (buf.len < 2) { |
| 328 | return; | 376 | return; |
| 329 | } | 377 | } |
| 330 | | 378 | |
| 331 | var i: usize = 0; | 379 | // `i <= j < max <= maxInt(MinInt)` |
| 332 | while (i < buf.len - 1) : (i += 1) { | 380 | const max = @intCast(MinInt, buf.len); |
| 333 | const j = r.intRangeLessThan(usize, i, buf.len); | 381 | var i: MinInt = 0; |
| | 382 | while (i < max - 1) : (i += 1) { |
| | 383 | const j = @intCast(MinInt, r.intRangeLessThan(Index, i, max)); |
| 334 | mem.swap(T, &buf[i], &buf[j]); | 384 | mem.swap(T, &buf[i], &buf[j]); |
| 335 | } | 385 | } |
| 336 | } | 386 | } |
| ... | @@ -370,6 +420,13 @@ pub const Random = struct { | ... | @@ -370,6 +420,13 @@ pub const Random = struct { |
| 370 | | 420 | |
| 371 | unreachable; | 421 | unreachable; |
| 372 | } | 422 | } |
| | 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 | } |
| 373 | }; | 430 | }; |
| 374 | | 431 | |
| 375 | /// Convert a random integer 0 <= random_int <= maxValue(T), | 432 | /// Convert a random integer 0 <= random_int <= maxValue(T), |