| ... | @@ -323,14 +323,38 @@ pub const Random = struct { | ... | @@ -323,14 +323,38 @@ pub const Random = struct { |
| 323 | } | 323 | } |
| 324 | | 324 | |
| 325 | /// Shuffle a slice into a random order. | 325 | /// Shuffle a slice into a random order. |
| 326 | pub fn shuffle(r: Random, comptime T: type, buf: []T) void { | 326 | /// |
| | 327 | /// Note that this will not yield consistent results across all targets |
| | 328 | /// due to dependence on the representation of `usize` as an index. |
| | 329 | /// See `shuffleWithIndex` for further commentary. |
| | 330 | pub inline fn shuffle(r: Random, comptime T: type, buf: []T) void { |
| | 331 | r.shuffleWithIndex(T, buf, usize); |
| | 332 | } |
| | 333 | |
| | 334 | /// Shuffle a slice into a random order, using an index of a |
| | 335 | /// specified type to maintain distribution across targets. |
| | 336 | /// Asserts the index type can represent `buf.len`. |
| | 337 | /// |
| | 338 | /// Indexes into the slice are generated using the specified `Index` |
| | 339 | /// type, which determines distribution properties. This allows for |
| | 340 | /// results to be independent of `usize` representation. |
| | 341 | /// |
| | 342 | /// Prefer `shuffle` if this isn't important. |
| | 343 | /// |
| | 344 | /// See `intRangeLessThan`, which this function uses, |
| | 345 | /// for commentary on the runtime of this function. |
| | 346 | pub fn shuffleWithIndex(r: Random, comptime T: type, buf: []T, comptime Index: type) void { |
| | 347 | comptime std.debug.assert(@typeInfo(Index).Int.signedness == .unsigned); |
| | 348 | const MinInt = std.meta.Int(.unsigned, @min(@typeInfo(Index).Int.bits, @typeInfo(usize).Int.bits)); |
| 327 | if (buf.len < 2) { | 349 | if (buf.len < 2) { |
| 328 | return; | 350 | return; |
| 329 | } | 351 | } |
| 330 | | 352 | |
| 331 | var i: usize = 0; | 353 | // `i <= j < max <= maxInt(MinInt)` |
| 332 | while (i < buf.len - 1) : (i += 1) { | 354 | const max = @intCast(MinInt, buf.len); |
| 333 | const j = r.intRangeLessThan(usize, i, buf.len); | 355 | var i: MinInt = 0; |
| | 356 | while (i < max - 1) : (i += 1) { |
| | 357 | const j = @intCast(MinInt, r.intRangeLessThan(Index, i, max)); |
| 334 | mem.swap(T, &buf[i], &buf[j]); | 358 | mem.swap(T, &buf[i], &buf[j]); |
| 335 | } | 359 | } |
| 336 | } | 360 | } |