mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #18867 from e4m2/random
std.rand: Move to std.Random
This commit is contained in:
commit
32f30399e5
@ -299,7 +299,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/Progress.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/pdb.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/process.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/rand.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/Random.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/sort.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/compiler_rt.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/absv.zig"
|
||||
|
||||
@ -420,7 +420,7 @@ fn runStepNames(
|
||||
|
||||
const starting_steps = try arena.dupe(*Step, step_stack.keys());
|
||||
|
||||
var rng = std.rand.DefaultPrng.init(seed);
|
||||
var rng = std.Random.DefaultPrng.init(seed);
|
||||
const rand = rng.random();
|
||||
rand.shuffle(*Step, starting_steps);
|
||||
|
||||
@ -836,7 +836,7 @@ fn constructGraphAndCheckForDependencyLoop(
|
||||
b: *std.Build,
|
||||
s: *Step,
|
||||
step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
|
||||
rand: std.rand.Random,
|
||||
rand: std.Random,
|
||||
) !void {
|
||||
switch (s.state) {
|
||||
.precheck_started => {
|
||||
|
||||
@ -26,7 +26,7 @@ test "paritydi2" {
|
||||
try test__paritydi2(@bitCast(@as(u64, 0xffffffff_fffffffe)));
|
||||
try test__paritydi2(@bitCast(@as(u64, 0xffffffff_ffffffff)));
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: u32 = 0;
|
||||
while (i < 10_000) : (i += 1) {
|
||||
|
||||
@ -26,7 +26,7 @@ test "paritysi2" {
|
||||
try test__paritysi2(@bitCast(@as(u32, 0xfffffffe)));
|
||||
try test__paritysi2(@bitCast(@as(u32, 0xffffffff)));
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: u32 = 0;
|
||||
while (i < 10_000) : (i += 1) {
|
||||
|
||||
@ -26,7 +26,7 @@ test "parityti2" {
|
||||
try test__parityti2(@bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_fffffffe)));
|
||||
try test__parityti2(@bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_ffffffff)));
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: u32 = 0;
|
||||
while (i < 10_000) : (i += 1) {
|
||||
|
||||
@ -25,7 +25,7 @@ test "popcountdi2" {
|
||||
try test__popcountdi2(@as(i64, @bitCast(@as(u64, 0xffffffff_fffffffe))));
|
||||
try test__popcountdi2(@as(i64, @bitCast(@as(u64, 0xffffffff_ffffffff))));
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: u32 = 0;
|
||||
while (i < 10_000) : (i += 1) {
|
||||
|
||||
@ -25,7 +25,7 @@ test "popcountsi2" {
|
||||
try test__popcountsi2(@as(i32, @bitCast(@as(u32, 0xfffffffe))));
|
||||
try test__popcountsi2(@as(i32, @bitCast(@as(u32, 0xffffffff))));
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: u32 = 0;
|
||||
while (i < 10_000) : (i += 1) {
|
||||
|
||||
@ -25,7 +25,7 @@ test "popcountti2" {
|
||||
try test__popcountti2(@as(i128, @bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_fffffffe))));
|
||||
try test__popcountti2(@as(i128, @bitCast(@as(u128, 0xffffffff_ffffffff_ffffffff_ffffffff))));
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: u32 = 0;
|
||||
while (i < 10_000) : (i += 1) {
|
||||
|
||||
@ -132,7 +132,7 @@ test "__udivei4/__umodei4" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
var rnd = RndGen.init(42);
|
||||
var i: usize = 10000;
|
||||
while (i > 0) : (i -= 1) {
|
||||
|
||||
438
lib/std/Random.zig
Normal file
438
lib/std/Random.zig
Normal file
@ -0,0 +1,438 @@
|
||||
//! The engines provided here should be initialized from an external source.
|
||||
//! For a thread-local cryptographically secure pseudo random number generator,
|
||||
//! use `std.crypto.random`.
|
||||
//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
|
||||
//! be faster and use substantially less stack space.
|
||||
|
||||
const std = @import("std.zig");
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
const maxInt = std.math.maxInt;
|
||||
pub const Random = @This(); // Remove pub when `std.rand` namespace is removed.
|
||||
|
||||
/// Fast unbiased random numbers.
|
||||
pub const DefaultPrng = Xoshiro256;
|
||||
|
||||
/// Cryptographically secure random numbers.
|
||||
pub const DefaultCsprng = ChaCha;
|
||||
|
||||
pub const Ascon = @import("Random/Ascon.zig");
|
||||
pub const ChaCha = @import("Random/ChaCha.zig");
|
||||
|
||||
pub const Isaac64 = @import("Random/Isaac64.zig");
|
||||
pub const Pcg = @import("Random/Pcg.zig");
|
||||
pub const Xoroshiro128 = @import("Random/Xoroshiro128.zig");
|
||||
pub const Xoshiro256 = @import("Random/Xoshiro256.zig");
|
||||
pub const Sfc64 = @import("Random/Sfc64.zig");
|
||||
pub const RomuTrio = @import("Random/RomuTrio.zig");
|
||||
pub const SplitMix64 = @import("Random/SplitMix64.zig");
|
||||
pub const ziggurat = @import("Random/ziggurat.zig");
|
||||
|
||||
ptr: *anyopaque,
|
||||
fillFn: *const fn (ptr: *anyopaque, buf: []u8) void,
|
||||
|
||||
pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random {
|
||||
const Ptr = @TypeOf(pointer);
|
||||
assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer
|
||||
assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer
|
||||
assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct
|
||||
const gen = struct {
|
||||
fn fill(ptr: *anyopaque, buf: []u8) void {
|
||||
const self: Ptr = @ptrCast(@alignCast(ptr));
|
||||
fillFn(self, buf);
|
||||
}
|
||||
};
|
||||
|
||||
return .{
|
||||
.ptr = pointer,
|
||||
.fillFn = gen.fill,
|
||||
};
|
||||
}
|
||||
|
||||
/// Read random bytes into the specified buffer until full.
|
||||
pub fn bytes(r: Random, buf: []u8) void {
|
||||
r.fillFn(r.ptr, buf);
|
||||
}
|
||||
|
||||
pub fn boolean(r: Random) bool {
|
||||
return r.int(u1) != 0;
|
||||
}
|
||||
|
||||
/// Returns a random value from an enum, evenly distributed.
|
||||
///
|
||||
/// Note that this will not yield consistent results across all targets
|
||||
/// due to dependence on the representation of `usize` as an index.
|
||||
/// See `enumValueWithIndex` for further commentary.
|
||||
pub inline fn enumValue(r: Random, comptime EnumType: type) EnumType {
|
||||
return r.enumValueWithIndex(EnumType, usize);
|
||||
}
|
||||
|
||||
/// Returns a random value from an enum, evenly distributed.
|
||||
///
|
||||
/// An index into an array of all named values is generated using the
|
||||
/// specified `Index` type to determine the return value.
|
||||
/// This allows for results to be independent of `usize` representation.
|
||||
///
|
||||
/// Prefer `enumValue` if this isn't important.
|
||||
///
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: type) EnumType {
|
||||
comptime assert(@typeInfo(EnumType) == .Enum);
|
||||
|
||||
// We won't use int -> enum casting because enum elements can have
|
||||
// arbitrary values. Instead we'll randomly pick one of the type's values.
|
||||
const values = comptime std.enums.values(EnumType);
|
||||
comptime assert(values.len > 0); // can't return anything
|
||||
comptime assert(maxInt(Index) >= values.len - 1); // can't access all values
|
||||
comptime if (values.len == 1) return values[0];
|
||||
|
||||
const index = if (comptime values.len - 1 == maxInt(Index))
|
||||
r.int(Index)
|
||||
else
|
||||
r.uintLessThan(Index, values.len);
|
||||
|
||||
const MinInt = MinArrayIndex(Index);
|
||||
return values[@as(MinInt, @intCast(index))];
|
||||
}
|
||||
|
||||
/// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`.
|
||||
/// `i` is evenly distributed.
|
||||
pub fn int(r: Random, comptime T: type) T {
|
||||
const bits = @typeInfo(T).Int.bits;
|
||||
const UnsignedT = std.meta.Int(.unsigned, bits);
|
||||
const ceil_bytes = comptime std.math.divCeil(u16, bits, 8) catch unreachable;
|
||||
const ByteAlignedT = std.meta.Int(.unsigned, ceil_bytes * 8);
|
||||
|
||||
var rand_bytes: [ceil_bytes]u8 = undefined;
|
||||
r.bytes(&rand_bytes);
|
||||
|
||||
// use LE instead of native endian for better portability maybe?
|
||||
// TODO: endian portability is pointless if the underlying prng isn't endian portable.
|
||||
// TODO: document the endian portability of this library.
|
||||
const byte_aligned_result = mem.readInt(ByteAlignedT, &rand_bytes, .little);
|
||||
const unsigned_result: UnsignedT = @truncate(byte_aligned_result);
|
||||
return @bitCast(unsigned_result);
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `uintLessThan`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn uintLessThanBiased(r: Random, comptime T: type, less_than: T) T {
|
||||
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
assert(0 < less_than);
|
||||
return limitRangeBiased(T, r.int(T), less_than);
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random unsigned integer `0 <= i < less_than`.
|
||||
/// This function assumes that the underlying `fillFn` produces evenly distributed values.
|
||||
/// Within this assumption, the runtime of this function is exponentially distributed.
|
||||
/// If `fillFn` were backed by a true random generator,
|
||||
/// the runtime of this function would technically be unbounded.
|
||||
/// However, if `fillFn` is backed by any evenly distributed pseudo random number generator,
|
||||
/// this function is guaranteed to return.
|
||||
/// If you need deterministic runtime bounds, use `uintLessThanBiased`.
|
||||
pub fn uintLessThan(r: Random, comptime T: type, less_than: T) T {
|
||||
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
const bits = @typeInfo(T).Int.bits;
|
||||
assert(0 < less_than);
|
||||
|
||||
// adapted from:
|
||||
// http://www.pcg-random.org/posts/bounded-rands.html
|
||||
// "Lemire's (with an extra tweak from me)"
|
||||
var x = r.int(T);
|
||||
var m = math.mulWide(T, x, less_than);
|
||||
var l: T = @truncate(m);
|
||||
if (l < less_than) {
|
||||
var t = -%less_than;
|
||||
|
||||
if (t >= less_than) {
|
||||
t -= less_than;
|
||||
if (t >= less_than) {
|
||||
t %= less_than;
|
||||
}
|
||||
}
|
||||
while (l < t) {
|
||||
x = r.int(T);
|
||||
m = math.mulWide(T, x, less_than);
|
||||
l = @truncate(m);
|
||||
}
|
||||
}
|
||||
return @intCast(m >> bits);
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `uintAtMost`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn uintAtMostBiased(r: Random, comptime T: type, at_most: T) T {
|
||||
assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
if (at_most == maxInt(T)) {
|
||||
// have the full range
|
||||
return r.int(T);
|
||||
}
|
||||
return r.uintLessThanBiased(T, at_most + 1);
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`.
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn uintAtMost(r: Random, comptime T: type, at_most: T) T {
|
||||
assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
if (at_most == maxInt(T)) {
|
||||
// have the full range
|
||||
return r.int(T);
|
||||
}
|
||||
return r.uintLessThan(T, at_most + 1);
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `intRangeLessThan`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn intRangeLessThanBiased(r: Random, comptime T: type, at_least: T, less_than: T) T {
|
||||
assert(at_least < less_than);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(less_than);
|
||||
const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintLessThanBiased(T, less_than - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random integer `at_least <= i < less_than`.
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn intRangeLessThan(r: Random, comptime T: type, at_least: T, less_than: T) T {
|
||||
assert(at_least < less_than);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(less_than);
|
||||
const result = lo +% r.uintLessThan(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintLessThan(T, less_than - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `intRangeAtMostBiased`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn intRangeAtMostBiased(r: Random, comptime T: type, at_least: T, at_most: T) T {
|
||||
assert(at_least <= at_most);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(at_most);
|
||||
const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintAtMostBiased(T, at_most - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random integer `at_least <= i <= at_most`.
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn intRangeAtMost(r: Random, comptime T: type, at_least: T, at_most: T) T {
|
||||
assert(at_least <= at_most);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(at_most);
|
||||
const result = lo +% r.uintAtMost(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintAtMost(T, at_most - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value evenly distributed in the range [0, 1).
|
||||
pub fn float(r: Random, comptime T: type) T {
|
||||
// Generate a uniformly random value for the mantissa.
|
||||
// Then generate an exponentially biased random value for the exponent.
|
||||
// This covers every possible value in the range.
|
||||
switch (T) {
|
||||
f32 => {
|
||||
// Use 23 random bits for the mantissa, and the rest for the exponent.
|
||||
// If all 41 bits are zero, generate additional random bits, until a
|
||||
// set bit is found, or 126 bits have been generated.
|
||||
const rand = r.int(u64);
|
||||
var rand_lz = @clz(rand);
|
||||
if (rand_lz >= 41) {
|
||||
// TODO: when #5177 or #489 is implemented,
|
||||
// tell the compiler it is unlikely (1/2^41) to reach this point.
|
||||
// (Same for the if branch and the f64 calculations below.)
|
||||
rand_lz = 41 + @clz(r.int(u64));
|
||||
if (rand_lz == 41 + 64) {
|
||||
// It is astronomically unlikely to reach this point.
|
||||
rand_lz += @clz(r.int(u32) | 0x7FF);
|
||||
}
|
||||
}
|
||||
const mantissa: u23 = @truncate(rand);
|
||||
const exponent = @as(u32, 126 - rand_lz) << 23;
|
||||
return @bitCast(exponent | mantissa);
|
||||
},
|
||||
f64 => {
|
||||
// Use 52 random bits for the mantissa, and the rest for the exponent.
|
||||
// If all 12 bits are zero, generate additional random bits, until a
|
||||
// set bit is found, or 1022 bits have been generated.
|
||||
const rand = r.int(u64);
|
||||
var rand_lz: u64 = @clz(rand);
|
||||
if (rand_lz >= 12) {
|
||||
rand_lz = 12;
|
||||
while (true) {
|
||||
// It is astronomically unlikely for this loop to execute more than once.
|
||||
const addl_rand_lz = @clz(r.int(u64));
|
||||
rand_lz += addl_rand_lz;
|
||||
if (addl_rand_lz != 64) {
|
||||
break;
|
||||
}
|
||||
if (rand_lz >= 1022) {
|
||||
rand_lz = 1022;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const mantissa = rand & 0xFFFFFFFFFFFFF;
|
||||
const exponent = (1022 - rand_lz) << 52;
|
||||
return @bitCast(exponent | mantissa);
|
||||
},
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value normally distributed with mean = 0, stddev = 1.
|
||||
///
|
||||
/// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
|
||||
pub fn floatNorm(r: Random, comptime T: type) T {
|
||||
const value = ziggurat.next_f64(r, ziggurat.NormDist);
|
||||
switch (T) {
|
||||
f32 => return @floatCast(value),
|
||||
f64 => return value,
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an exponentially distributed float with a rate parameter of 1.
|
||||
///
|
||||
/// To use a different rate parameter, use: floatExp(...) / desiredRate.
|
||||
pub fn floatExp(r: Random, comptime T: type) T {
|
||||
const value = ziggurat.next_f64(r, ziggurat.ExpDist);
|
||||
switch (T) {
|
||||
f32 => return @floatCast(value),
|
||||
f64 => return value,
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shuffle a slice into a random order.
|
||||
///
|
||||
/// Note that this will not yield consistent results across all targets
|
||||
/// due to dependence on the representation of `usize` as an index.
|
||||
/// See `shuffleWithIndex` for further commentary.
|
||||
pub inline fn shuffle(r: Random, comptime T: type, buf: []T) void {
|
||||
r.shuffleWithIndex(T, buf, usize);
|
||||
}
|
||||
|
||||
/// Shuffle a slice into a random order, using an index of a
|
||||
/// specified type to maintain distribution across targets.
|
||||
/// Asserts the index type can represent `buf.len`.
|
||||
///
|
||||
/// Indexes into the slice are generated using the specified `Index`
|
||||
/// type, which determines distribution properties. This allows for
|
||||
/// results to be independent of `usize` representation.
|
||||
///
|
||||
/// Prefer `shuffle` if this isn't important.
|
||||
///
|
||||
/// See `intRangeLessThan`, which this function uses,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn shuffleWithIndex(r: Random, comptime T: type, buf: []T, comptime Index: type) void {
|
||||
const MinInt = MinArrayIndex(Index);
|
||||
if (buf.len < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// `i <= j < max <= maxInt(MinInt)`
|
||||
const max: MinInt = @intCast(buf.len);
|
||||
var i: MinInt = 0;
|
||||
while (i < max - 1) : (i += 1) {
|
||||
const j: MinInt = @intCast(r.intRangeLessThan(Index, i, max));
|
||||
mem.swap(T, &buf[i], &buf[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Randomly selects an index into `proportions`, where the likelihood of each
|
||||
/// index is weighted by that proportion.
|
||||
/// It is more likely for the index of the last proportion to be returned
|
||||
/// than the index of the first proportion in the slice, and vice versa.
|
||||
///
|
||||
/// This is useful for selecting an item from a slice where weights are not equal.
|
||||
/// `T` must be a numeric type capable of holding the sum of `proportions`.
|
||||
pub fn weightedIndex(r: Random, comptime T: type, proportions: []const T) usize {
|
||||
// This implementation works by summing the proportions and picking a
|
||||
// random point in [0, sum). We then loop over the proportions,
|
||||
// accumulating until our accumulator is greater than the random point.
|
||||
|
||||
const sum = s: {
|
||||
var sum: T = 0;
|
||||
for (proportions) |v| sum += v;
|
||||
break :s sum;
|
||||
};
|
||||
|
||||
const point = switch (@typeInfo(T)) {
|
||||
.Int => |int_info| switch (int_info.signedness) {
|
||||
.signed => r.intRangeLessThan(T, 0, sum),
|
||||
.unsigned => r.uintLessThan(T, sum),
|
||||
},
|
||||
// take care that imprecision doesn't lead to a value slightly greater than sum
|
||||
.Float => @min(r.float(T) * sum, sum - std.math.floatEps(T)),
|
||||
else => @compileError("weightedIndex does not support proportions of type " ++
|
||||
@typeName(T)),
|
||||
};
|
||||
|
||||
assert(point < sum);
|
||||
|
||||
var accumulator: T = 0;
|
||||
for (proportions, 0..) |p, index| {
|
||||
accumulator += p;
|
||||
if (point < accumulator) return index;
|
||||
} else unreachable;
|
||||
}
|
||||
|
||||
/// Convert a random integer 0 <= random_int <= maxValue(T),
|
||||
/// into an integer 0 <= result < less_than.
|
||||
/// This function introduces a minor bias.
|
||||
pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T {
|
||||
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
const bits = @typeInfo(T).Int.bits;
|
||||
|
||||
// adapted from:
|
||||
// http://www.pcg-random.org/posts/bounded-rands.html
|
||||
// "Integer Multiplication (Biased)"
|
||||
const m = math.mulWide(T, random_int, less_than);
|
||||
return @intCast(m >> bits);
|
||||
}
|
||||
|
||||
/// Returns the smallest of `Index` and `usize`.
|
||||
fn MinArrayIndex(comptime Index: type) type {
|
||||
const index_info = @typeInfo(Index).Int;
|
||||
assert(index_info.signedness == .unsigned);
|
||||
return if (index_info.bits >= @typeInfo(usize).Int.bits) usize else Index;
|
||||
}
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
_ = @import("Random/test.zig");
|
||||
}
|
||||
@ -10,7 +10,6 @@
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Random = std.rand.Random;
|
||||
const Self = @This();
|
||||
|
||||
const Ascon = std.crypto.core.Ascon(.little);
|
||||
@ -39,9 +38,9 @@ pub fn addEntropy(self: *Self, bytes: []const u8) void {
|
||||
self.state.permute();
|
||||
}
|
||||
|
||||
/// Returns a `std.rand.Random` structure backed by the current RNG.
|
||||
pub fn random(self: *Self) Random {
|
||||
return Random.init(self, fill);
|
||||
/// Returns a `std.Random` structure backed by the current RNG.
|
||||
pub fn random(self: *Self) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
/// Fills the buffer with random bytes.
|
||||
@ -5,7 +5,6 @@
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Random = std.rand.Random;
|
||||
const Self = @This();
|
||||
|
||||
const Cipher = std.crypto.stream.chacha.ChaCha8IETF;
|
||||
@ -53,9 +52,9 @@ pub fn addEntropy(self: *Self, bytes: []const u8) void {
|
||||
self.refill();
|
||||
}
|
||||
|
||||
/// Returns a `std.rand.Random` structure backed by the current RNG.
|
||||
pub fn random(self: *Self) Random {
|
||||
return Random.init(self, fill);
|
||||
/// Returns a `std.Random` structure backed by the current RNG.
|
||||
pub fn random(self: *Self) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
// Refills the buffer with random bytes, overwriting the previous key.
|
||||
@ -4,7 +4,6 @@
|
||||
//! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
|
||||
|
||||
const std = @import("std");
|
||||
const Random = std.rand.Random;
|
||||
const mem = std.mem;
|
||||
const Isaac64 = @This();
|
||||
|
||||
@ -30,8 +29,8 @@ pub fn init(init_s: u64) Isaac64 {
|
||||
return isaac;
|
||||
}
|
||||
|
||||
pub fn random(self: *Isaac64) Random {
|
||||
return Random.init(self, fill);
|
||||
pub fn random(self: *Isaac64) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
|
||||
@ -3,7 +3,6 @@
|
||||
//! PRNG
|
||||
|
||||
const std = @import("std");
|
||||
const Random = std.rand.Random;
|
||||
const Pcg = @This();
|
||||
|
||||
const default_multiplier = 6364136223846793005;
|
||||
@ -21,8 +20,8 @@ pub fn init(init_s: u64) Pcg {
|
||||
return pcg;
|
||||
}
|
||||
|
||||
pub fn random(self: *Pcg) Random {
|
||||
return Random.init(self, fill);
|
||||
pub fn random(self: *Pcg) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
fn next(self: *Pcg) u32 {
|
||||
@ -37,7 +36,7 @@ fn next(self: *Pcg) u32 {
|
||||
|
||||
fn seed(self: *Pcg, init_s: u64) void {
|
||||
// Pcg requires 128-bits of seed.
|
||||
var gen = std.rand.SplitMix64.init(init_s);
|
||||
var gen = std.Random.SplitMix64.init(init_s);
|
||||
self.seedTwo(gen.next(), gen.next());
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
// Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes.
|
||||
|
||||
const std = @import("std");
|
||||
const Random = std.rand.Random;
|
||||
const math = std.math;
|
||||
const RomuTrio = @This();
|
||||
|
||||
@ -17,8 +16,8 @@ pub fn init(init_s: u64) RomuTrio {
|
||||
return x;
|
||||
}
|
||||
|
||||
pub fn random(self: *RomuTrio) Random {
|
||||
return Random.init(self, fill);
|
||||
pub fn random(self: *RomuTrio) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
fn next(self: *RomuTrio) u64 {
|
||||
@ -42,7 +41,7 @@ pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void {
|
||||
|
||||
pub fn seed(self: *RomuTrio, init_s: u64) void {
|
||||
// RomuTrio requires 192-bits of seed.
|
||||
var gen = std.rand.SplitMix64.init(init_s);
|
||||
var gen = std.Random.SplitMix64.init(init_s);
|
||||
|
||||
self.x_state = gen.next();
|
||||
self.y_state = gen.next();
|
||||
@ -3,7 +3,6 @@
|
||||
//! See http://pracrand.sourceforge.net/
|
||||
|
||||
const std = @import("std");
|
||||
const Random = std.rand.Random;
|
||||
const math = std.math;
|
||||
const Sfc64 = @This();
|
||||
|
||||
@ -23,8 +22,8 @@ pub fn init(init_s: u64) Sfc64 {
|
||||
return x;
|
||||
}
|
||||
|
||||
pub fn random(self: *Sfc64) Random {
|
||||
return Random.init(self, fill);
|
||||
pub fn random(self: *Sfc64) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
fn next(self: *Sfc64) u64 {
|
||||
21
lib/std/Random/SplitMix64.zig
Normal file
21
lib/std/Random/SplitMix64.zig
Normal file
@ -0,0 +1,21 @@
|
||||
//! Generator to extend 64-bit seed values into longer sequences.
|
||||
//!
|
||||
//! The number of cycles is thus limited to 64-bits regardless of the engine, but this
|
||||
//! is still plenty for practical purposes.
|
||||
|
||||
const SplitMix64 = @This();
|
||||
|
||||
s: u64,
|
||||
|
||||
pub fn init(seed: u64) SplitMix64 {
|
||||
return SplitMix64{ .s = seed };
|
||||
}
|
||||
|
||||
pub fn next(self: *SplitMix64) u64 {
|
||||
self.s +%= 0x9e3779b97f4a7c15;
|
||||
|
||||
var z = self.s;
|
||||
z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) *% 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
//! PRNG
|
||||
|
||||
const std = @import("std");
|
||||
const Random = std.rand.Random;
|
||||
const math = std.math;
|
||||
const Xoroshiro128 = @This();
|
||||
|
||||
@ -16,8 +15,8 @@ pub fn init(init_s: u64) Xoroshiro128 {
|
||||
return x;
|
||||
}
|
||||
|
||||
pub fn random(self: *Xoroshiro128) Random {
|
||||
return Random.init(self, fill);
|
||||
pub fn random(self: *Xoroshiro128) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
pub fn next(self: *Xoroshiro128) u64 {
|
||||
@ -59,7 +58,7 @@ pub fn jump(self: *Xoroshiro128) void {
|
||||
|
||||
pub fn seed(self: *Xoroshiro128, init_s: u64) void {
|
||||
// Xoroshiro requires 128-bits of seed.
|
||||
var gen = std.rand.SplitMix64.init(init_s);
|
||||
var gen = std.Random.SplitMix64.init(init_s);
|
||||
|
||||
self.s[0] = gen.next();
|
||||
self.s[1] = gen.next();
|
||||
@ -3,7 +3,6 @@
|
||||
//! PRNG
|
||||
|
||||
const std = @import("std");
|
||||
const Random = std.rand.Random;
|
||||
const math = std.math;
|
||||
const Xoshiro256 = @This();
|
||||
|
||||
@ -18,8 +17,8 @@ pub fn init(init_s: u64) Xoshiro256 {
|
||||
return x;
|
||||
}
|
||||
|
||||
pub fn random(self: *Xoshiro256) Random {
|
||||
return Random.init(self, fill);
|
||||
pub fn random(self: *Xoshiro256) std.Random {
|
||||
return std.Random.init(self, fill);
|
||||
}
|
||||
|
||||
pub fn next(self: *Xoshiro256) u64 {
|
||||
@ -57,7 +56,7 @@ pub fn jump(self: *Xoshiro256) void {
|
||||
|
||||
pub fn seed(self: *Xoshiro256, init_s: u64) void {
|
||||
// Xoshiro requires 256-bits of seed.
|
||||
var gen = std.rand.SplitMix64.init(init_s);
|
||||
var gen = std.Random.SplitMix64.init(init_s);
|
||||
|
||||
self.s[0] = gen.next();
|
||||
self.s[1] = gen.next();
|
||||
@ -4,7 +4,7 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const time = std.time;
|
||||
const Timer = time.Timer;
|
||||
const rand = std.rand;
|
||||
const Random = std.Random;
|
||||
|
||||
const KiB = 1024;
|
||||
const MiB = 1024 * KiB;
|
||||
@ -19,32 +19,32 @@ const Rng = struct {
|
||||
|
||||
const prngs = [_]Rng{
|
||||
Rng{
|
||||
.ty = rand.Isaac64,
|
||||
.ty = Random.Isaac64,
|
||||
.name = "isaac64",
|
||||
.init_u64 = 0,
|
||||
},
|
||||
Rng{
|
||||
.ty = rand.Pcg,
|
||||
.ty = Random.Pcg,
|
||||
.name = "pcg",
|
||||
.init_u64 = 0,
|
||||
},
|
||||
Rng{
|
||||
.ty = rand.RomuTrio,
|
||||
.ty = Random.RomuTrio,
|
||||
.name = "romutrio",
|
||||
.init_u64 = 0,
|
||||
},
|
||||
Rng{
|
||||
.ty = std.rand.Sfc64,
|
||||
.ty = Random.Sfc64,
|
||||
.name = "sfc64",
|
||||
.init_u64 = 0,
|
||||
},
|
||||
Rng{
|
||||
.ty = std.rand.Xoroshiro128,
|
||||
.ty = Random.Xoroshiro128,
|
||||
.name = "xoroshiro128",
|
||||
.init_u64 = 0,
|
||||
},
|
||||
Rng{
|
||||
.ty = std.rand.Xoshiro256,
|
||||
.ty = Random.Xoshiro256,
|
||||
.name = "xoshiro256",
|
||||
.init_u64 = 0,
|
||||
},
|
||||
@ -52,12 +52,12 @@ const prngs = [_]Rng{
|
||||
|
||||
const csprngs = [_]Rng{
|
||||
Rng{
|
||||
.ty = rand.Ascon,
|
||||
.ty = Random.Ascon,
|
||||
.name = "ascon",
|
||||
.init_u8s = &[_]u8{0} ** 32,
|
||||
},
|
||||
Rng{
|
||||
.ty = rand.ChaCha,
|
||||
.ty = Random.ChaCha,
|
||||
.name = "chacha",
|
||||
.init_u8s = &[_]u8{0} ** 32,
|
||||
},
|
||||
@ -1,9 +1,9 @@
|
||||
const std = @import("../std.zig");
|
||||
const math = std.math;
|
||||
const DefaultPrng = std.rand.DefaultPrng;
|
||||
const Random = std.rand.Random;
|
||||
const SplitMix64 = std.rand.SplitMix64;
|
||||
const DefaultCsprng = std.rand.DefaultCsprng;
|
||||
const Random = std.Random;
|
||||
const DefaultPrng = Random.DefaultPrng;
|
||||
const SplitMix64 = Random.SplitMix64;
|
||||
const DefaultCsprng = Random.DefaultCsprng;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const math = std.math;
|
||||
const Random = std.rand.Random;
|
||||
const Random = std.Random;
|
||||
|
||||
pub fn next_f64(random: Random, comptime tables: ZigTable) f64 {
|
||||
while (true) {
|
||||
@ -127,7 +127,7 @@ pub fn norm_zero_case(random: Random, u: f64) f64 {
|
||||
}
|
||||
|
||||
test "normal dist sanity" {
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = Random.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
|
||||
var i: usize = 0;
|
||||
@ -156,7 +156,7 @@ pub fn exp_zero_case(random: Random, _: f64) f64 {
|
||||
}
|
||||
|
||||
test "exp dist smoke test" {
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = Random.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
|
||||
var i: usize = 0;
|
||||
@ -328,7 +328,7 @@ test "RwLock - concurrent access" {
|
||||
}
|
||||
|
||||
fn writer(self: *Self, thread_idx: usize) !void {
|
||||
var prng = std.rand.DefaultPrng.init(thread_idx);
|
||||
var prng = std.Random.DefaultPrng.init(thread_idx);
|
||||
var rnd = prng.random();
|
||||
|
||||
while (true) {
|
||||
|
||||
@ -10,7 +10,7 @@ const crypto = std.crypto;
|
||||
const KiB = 1024;
|
||||
const MiB = 1024 * KiB;
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = std.Random.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
|
||||
const Crypto = struct {
|
||||
|
||||
@ -110,7 +110,7 @@ const assert = std.debug.assert;
|
||||
const crypto = std.crypto;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const RndGen = std.rand.DefaultPrng;
|
||||
const RndGen = std.Random.DefaultPrng;
|
||||
const sha3 = crypto.hash.sha3;
|
||||
|
||||
// Q is the parameter q ≡ 3329 = 2¹¹ + 2¹⁰ + 2⁸ + 1.
|
||||
|
||||
@ -10,7 +10,7 @@ const os = std.os;
|
||||
|
||||
/// We use this as a layer of indirection because global const pointers cannot
|
||||
/// point to thread-local variables.
|
||||
pub const interface = std.rand.Random{
|
||||
pub const interface = std.Random{
|
||||
.ptr = undefined,
|
||||
.fillFn = tlsCsprngFill,
|
||||
};
|
||||
@ -43,7 +43,7 @@ const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{
|
||||
}) orelse true;
|
||||
const is_haiku = builtin.os.tag == .haiku;
|
||||
|
||||
const Rng = std.rand.DefaultCsprng;
|
||||
const Rng = std.Random.DefaultCsprng;
|
||||
|
||||
const Context = struct {
|
||||
init_state: enum(u8) { uninitialized = 0, initialized, failed },
|
||||
|
||||
@ -10,7 +10,7 @@ const KiB = 1024;
|
||||
const MiB = 1024 * KiB;
|
||||
const GiB = 1024 * MiB;
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = std.Random.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
|
||||
const Hash = struct {
|
||||
|
||||
@ -1884,7 +1884,7 @@ test "std.hash_map put and remove loop in random order" {
|
||||
while (i < size) : (i += 1) {
|
||||
try keys.append(i);
|
||||
}
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = std.Random.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
|
||||
while (i < iterations) : (i += 1) {
|
||||
@ -1916,7 +1916,7 @@ test "std.hash_map remove one million elements in random order" {
|
||||
keys.append(i) catch unreachable;
|
||||
}
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = std.Random.DefaultPrng.init(0);
|
||||
const random = prng.random();
|
||||
random.shuffle(u32, keys.items);
|
||||
|
||||
|
||||
@ -250,7 +250,7 @@ test "ArenaAllocator (reset with preheating)" {
|
||||
var arena_allocator = ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena_allocator.deinit();
|
||||
// provides some variance in the allocated data
|
||||
var rng_src = std.rand.DefaultPrng.init(19930913);
|
||||
var rng_src = std.Random.DefaultPrng.init(19930913);
|
||||
const random = rng_src.random();
|
||||
var rounds: usize = 25;
|
||||
while (rounds > 0) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const DefaultPrng = std.rand.DefaultPrng;
|
||||
const DefaultPrng = std.Random.DefaultPrng;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const expectError = std.testing.expectError;
|
||||
|
||||
@ -594,7 +594,7 @@ test "big.rational toFloat" {
|
||||
test "big.rational set/to Float round-trip" {
|
||||
var a = try Rational.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
var prng = std.rand.DefaultPrng.init(0x5EED);
|
||||
var prng = std.Random.DefaultPrng.init(0x5EED);
|
||||
const random = prng.random();
|
||||
var i: usize = 0;
|
||||
while (i < 512) : (i += 1) {
|
||||
|
||||
@ -4423,7 +4423,7 @@ test "read/write(Var)PackedInt" {
|
||||
|
||||
const foreign_endian: Endian = if (native_endian == .big) .little else .big;
|
||||
const expect = std.testing.expect;
|
||||
var prng = std.rand.DefaultPrng.init(1234);
|
||||
var prng = std.Random.DefaultPrng.init(1234);
|
||||
const random = prng.random();
|
||||
|
||||
@setEvalBranchQuota(10_000);
|
||||
|
||||
@ -866,7 +866,7 @@ test "std.PriorityDequeue: shrinkAndFree" {
|
||||
}
|
||||
|
||||
test "std.PriorityDequeue: fuzz testing min" {
|
||||
var prng = std.rand.DefaultPrng.init(0x12345678);
|
||||
var prng = std.Random.DefaultPrng.init(0x12345678);
|
||||
const random = prng.random();
|
||||
|
||||
const test_case_count = 100;
|
||||
@ -878,7 +878,7 @@ test "std.PriorityDequeue: fuzz testing min" {
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzzTestMin(rng: std.rand.Random, comptime queue_size: usize) !void {
|
||||
fn fuzzTestMin(rng: std.Random, comptime queue_size: usize) !void {
|
||||
const allocator = testing.allocator;
|
||||
const items = try generateRandomSlice(allocator, rng, queue_size);
|
||||
|
||||
@ -895,7 +895,7 @@ fn fuzzTestMin(rng: std.rand.Random, comptime queue_size: usize) !void {
|
||||
}
|
||||
|
||||
test "std.PriorityDequeue: fuzz testing max" {
|
||||
var prng = std.rand.DefaultPrng.init(0x87654321);
|
||||
var prng = std.Random.DefaultPrng.init(0x87654321);
|
||||
const random = prng.random();
|
||||
|
||||
const test_case_count = 100;
|
||||
@ -907,7 +907,7 @@ test "std.PriorityDequeue: fuzz testing max" {
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzzTestMax(rng: std.rand.Random, queue_size: usize) !void {
|
||||
fn fuzzTestMax(rng: std.Random, queue_size: usize) !void {
|
||||
const allocator = testing.allocator;
|
||||
const items = try generateRandomSlice(allocator, rng, queue_size);
|
||||
|
||||
@ -924,7 +924,7 @@ fn fuzzTestMax(rng: std.rand.Random, queue_size: usize) !void {
|
||||
}
|
||||
|
||||
test "std.PriorityDequeue: fuzz testing min and max" {
|
||||
var prng = std.rand.DefaultPrng.init(0x87654321);
|
||||
var prng = std.Random.DefaultPrng.init(0x87654321);
|
||||
const random = prng.random();
|
||||
|
||||
const test_case_count = 100;
|
||||
@ -936,7 +936,7 @@ test "std.PriorityDequeue: fuzz testing min and max" {
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzzTestMinMax(rng: std.rand.Random, queue_size: usize) !void {
|
||||
fn fuzzTestMinMax(rng: std.Random, queue_size: usize) !void {
|
||||
const allocator = testing.allocator;
|
||||
const items = try generateRandomSlice(allocator, rng, queue_size);
|
||||
|
||||
@ -963,7 +963,7 @@ fn fuzzTestMinMax(rng: std.rand.Random, queue_size: usize) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn generateRandomSlice(allocator: std.mem.Allocator, rng: std.rand.Random, size: usize) ![]u32 {
|
||||
fn generateRandomSlice(allocator: std.mem.Allocator, rng: std.Random, size: usize) ![]u32 {
|
||||
var array = std.ArrayList(u32).init(allocator);
|
||||
try array.ensureTotalCapacity(size);
|
||||
|
||||
|
||||
460
lib/std/rand.zig
460
lib/std/rand.zig
@ -1,460 +0,0 @@
|
||||
//! The engines provided here should be initialized from an external source.
|
||||
//! For a thread-local cryptographically secure pseudo random number generator,
|
||||
//! use `std.crypto.random`.
|
||||
//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
|
||||
//! be faster and use substantially less stack space.
|
||||
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
/// Fast unbiased random numbers.
|
||||
pub const DefaultPrng = Xoshiro256;
|
||||
|
||||
/// Cryptographically secure random numbers.
|
||||
pub const DefaultCsprng = ChaCha;
|
||||
|
||||
pub const Ascon = @import("rand/Ascon.zig");
|
||||
pub const ChaCha = @import("rand/ChaCha.zig");
|
||||
|
||||
pub const Isaac64 = @import("rand/Isaac64.zig");
|
||||
pub const Pcg = @import("rand/Pcg.zig");
|
||||
pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig");
|
||||
pub const Xoshiro256 = @import("rand/Xoshiro256.zig");
|
||||
pub const Sfc64 = @import("rand/Sfc64.zig");
|
||||
pub const RomuTrio = @import("rand/RomuTrio.zig");
|
||||
pub const ziggurat = @import("rand/ziggurat.zig");
|
||||
|
||||
pub const Random = struct {
|
||||
ptr: *anyopaque,
|
||||
fillFn: *const fn (ptr: *anyopaque, buf: []u8) void,
|
||||
|
||||
pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random {
|
||||
const Ptr = @TypeOf(pointer);
|
||||
assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer
|
||||
assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer
|
||||
assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct
|
||||
const gen = struct {
|
||||
fn fill(ptr: *anyopaque, buf: []u8) void {
|
||||
const self: Ptr = @ptrCast(@alignCast(ptr));
|
||||
fillFn(self, buf);
|
||||
}
|
||||
};
|
||||
|
||||
return .{
|
||||
.ptr = pointer,
|
||||
.fillFn = gen.fill,
|
||||
};
|
||||
}
|
||||
|
||||
/// Read random bytes into the specified buffer until full.
|
||||
pub fn bytes(r: Random, buf: []u8) void {
|
||||
r.fillFn(r.ptr, buf);
|
||||
}
|
||||
|
||||
pub fn boolean(r: Random) bool {
|
||||
return r.int(u1) != 0;
|
||||
}
|
||||
|
||||
/// Returns a random value from an enum, evenly distributed.
|
||||
///
|
||||
/// Note that this will not yield consistent results across all targets
|
||||
/// due to dependence on the representation of `usize` as an index.
|
||||
/// See `enumValueWithIndex` for further commentary.
|
||||
pub inline fn enumValue(r: Random, comptime EnumType: type) EnumType {
|
||||
return r.enumValueWithIndex(EnumType, usize);
|
||||
}
|
||||
|
||||
/// Returns a random value from an enum, evenly distributed.
|
||||
///
|
||||
/// An index into an array of all named values is generated using the
|
||||
/// specified `Index` type to determine the return value.
|
||||
/// This allows for results to be independent of `usize` representation.
|
||||
///
|
||||
/// Prefer `enumValue` if this isn't important.
|
||||
///
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn enumValueWithIndex(r: Random, comptime EnumType: type, comptime Index: type) EnumType {
|
||||
comptime assert(@typeInfo(EnumType) == .Enum);
|
||||
|
||||
// We won't use int -> enum casting because enum elements can have
|
||||
// arbitrary values. Instead we'll randomly pick one of the type's values.
|
||||
const values = comptime std.enums.values(EnumType);
|
||||
comptime assert(values.len > 0); // can't return anything
|
||||
comptime assert(maxInt(Index) >= values.len - 1); // can't access all values
|
||||
comptime if (values.len == 1) return values[0];
|
||||
|
||||
const index = if (comptime values.len - 1 == maxInt(Index))
|
||||
r.int(Index)
|
||||
else
|
||||
r.uintLessThan(Index, values.len);
|
||||
|
||||
const MinInt = MinArrayIndex(Index);
|
||||
return values[@as(MinInt, @intCast(index))];
|
||||
}
|
||||
|
||||
/// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`.
|
||||
/// `i` is evenly distributed.
|
||||
pub fn int(r: Random, comptime T: type) T {
|
||||
const bits = @typeInfo(T).Int.bits;
|
||||
const UnsignedT = std.meta.Int(.unsigned, bits);
|
||||
const ceil_bytes = comptime std.math.divCeil(u16, bits, 8) catch unreachable;
|
||||
const ByteAlignedT = std.meta.Int(.unsigned, ceil_bytes * 8);
|
||||
|
||||
var rand_bytes: [ceil_bytes]u8 = undefined;
|
||||
r.bytes(&rand_bytes);
|
||||
|
||||
// use LE instead of native endian for better portability maybe?
|
||||
// TODO: endian portability is pointless if the underlying prng isn't endian portable.
|
||||
// TODO: document the endian portability of this library.
|
||||
const byte_aligned_result = mem.readInt(ByteAlignedT, &rand_bytes, .little);
|
||||
const unsigned_result: UnsignedT = @truncate(byte_aligned_result);
|
||||
return @bitCast(unsigned_result);
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `uintLessThan`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn uintLessThanBiased(r: Random, comptime T: type, less_than: T) T {
|
||||
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
assert(0 < less_than);
|
||||
return limitRangeBiased(T, r.int(T), less_than);
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random unsigned integer `0 <= i < less_than`.
|
||||
/// This function assumes that the underlying `fillFn` produces evenly distributed values.
|
||||
/// Within this assumption, the runtime of this function is exponentially distributed.
|
||||
/// If `fillFn` were backed by a true random generator,
|
||||
/// the runtime of this function would technically be unbounded.
|
||||
/// However, if `fillFn` is backed by any evenly distributed pseudo random number generator,
|
||||
/// this function is guaranteed to return.
|
||||
/// If you need deterministic runtime bounds, use `uintLessThanBiased`.
|
||||
pub fn uintLessThan(r: Random, comptime T: type, less_than: T) T {
|
||||
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
const bits = @typeInfo(T).Int.bits;
|
||||
assert(0 < less_than);
|
||||
|
||||
// adapted from:
|
||||
// http://www.pcg-random.org/posts/bounded-rands.html
|
||||
// "Lemire's (with an extra tweak from me)"
|
||||
var x = r.int(T);
|
||||
var m = math.mulWide(T, x, less_than);
|
||||
var l: T = @truncate(m);
|
||||
if (l < less_than) {
|
||||
var t = -%less_than;
|
||||
|
||||
if (t >= less_than) {
|
||||
t -= less_than;
|
||||
if (t >= less_than) {
|
||||
t %= less_than;
|
||||
}
|
||||
}
|
||||
while (l < t) {
|
||||
x = r.int(T);
|
||||
m = math.mulWide(T, x, less_than);
|
||||
l = @truncate(m);
|
||||
}
|
||||
}
|
||||
return @intCast(m >> bits);
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `uintAtMost`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn uintAtMostBiased(r: Random, comptime T: type, at_most: T) T {
|
||||
assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
if (at_most == maxInt(T)) {
|
||||
// have the full range
|
||||
return r.int(T);
|
||||
}
|
||||
return r.uintLessThanBiased(T, at_most + 1);
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`.
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn uintAtMost(r: Random, comptime T: type, at_most: T) T {
|
||||
assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
if (at_most == maxInt(T)) {
|
||||
// have the full range
|
||||
return r.int(T);
|
||||
}
|
||||
return r.uintLessThan(T, at_most + 1);
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `intRangeLessThan`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn intRangeLessThanBiased(r: Random, comptime T: type, at_least: T, less_than: T) T {
|
||||
assert(at_least < less_than);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(less_than);
|
||||
const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintLessThanBiased(T, less_than - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random integer `at_least <= i < less_than`.
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn intRangeLessThan(r: Random, comptime T: type, at_least: T, less_than: T) T {
|
||||
assert(at_least < less_than);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(less_than);
|
||||
const result = lo +% r.uintLessThan(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintLessThan(T, less_than - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Constant-time implementation off `intRangeAtMostBiased`.
|
||||
/// The results of this function may be biased.
|
||||
pub fn intRangeAtMostBiased(r: Random, comptime T: type, at_least: T, at_most: T) T {
|
||||
assert(at_least <= at_most);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(at_most);
|
||||
const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintAtMostBiased(T, at_most - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an evenly distributed random integer `at_least <= i <= at_most`.
|
||||
/// See `uintLessThan`, which this function uses in most cases,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn intRangeAtMost(r: Random, comptime T: type, at_least: T, at_most: T) T {
|
||||
assert(at_least <= at_most);
|
||||
const info = @typeInfo(T).Int;
|
||||
if (info.signedness == .signed) {
|
||||
// Two's complement makes this math pretty easy.
|
||||
const UnsignedT = std.meta.Int(.unsigned, info.bits);
|
||||
const lo: UnsignedT = @bitCast(at_least);
|
||||
const hi: UnsignedT = @bitCast(at_most);
|
||||
const result = lo +% r.uintAtMost(UnsignedT, hi -% lo);
|
||||
return @bitCast(result);
|
||||
} else {
|
||||
// The signed implementation would work fine, but we can use stricter arithmetic operators here.
|
||||
return at_least + r.uintAtMost(T, at_most - at_least);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value evenly distributed in the range [0, 1).
|
||||
pub fn float(r: Random, comptime T: type) T {
|
||||
// Generate a uniformly random value for the mantissa.
|
||||
// Then generate an exponentially biased random value for the exponent.
|
||||
// This covers every possible value in the range.
|
||||
switch (T) {
|
||||
f32 => {
|
||||
// Use 23 random bits for the mantissa, and the rest for the exponent.
|
||||
// If all 41 bits are zero, generate additional random bits, until a
|
||||
// set bit is found, or 126 bits have been generated.
|
||||
const rand = r.int(u64);
|
||||
var rand_lz = @clz(rand);
|
||||
if (rand_lz >= 41) {
|
||||
// TODO: when #5177 or #489 is implemented,
|
||||
// tell the compiler it is unlikely (1/2^41) to reach this point.
|
||||
// (Same for the if branch and the f64 calculations below.)
|
||||
rand_lz = 41 + @clz(r.int(u64));
|
||||
if (rand_lz == 41 + 64) {
|
||||
// It is astronomically unlikely to reach this point.
|
||||
rand_lz += @clz(r.int(u32) | 0x7FF);
|
||||
}
|
||||
}
|
||||
const mantissa: u23 = @truncate(rand);
|
||||
const exponent = @as(u32, 126 - rand_lz) << 23;
|
||||
return @bitCast(exponent | mantissa);
|
||||
},
|
||||
f64 => {
|
||||
// Use 52 random bits for the mantissa, and the rest for the exponent.
|
||||
// If all 12 bits are zero, generate additional random bits, until a
|
||||
// set bit is found, or 1022 bits have been generated.
|
||||
const rand = r.int(u64);
|
||||
var rand_lz: u64 = @clz(rand);
|
||||
if (rand_lz >= 12) {
|
||||
rand_lz = 12;
|
||||
while (true) {
|
||||
// It is astronomically unlikely for this loop to execute more than once.
|
||||
const addl_rand_lz = @clz(r.int(u64));
|
||||
rand_lz += addl_rand_lz;
|
||||
if (addl_rand_lz != 64) {
|
||||
break;
|
||||
}
|
||||
if (rand_lz >= 1022) {
|
||||
rand_lz = 1022;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const mantissa = rand & 0xFFFFFFFFFFFFF;
|
||||
const exponent = (1022 - rand_lz) << 52;
|
||||
return @bitCast(exponent | mantissa);
|
||||
},
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a floating point value normally distributed with mean = 0, stddev = 1.
|
||||
///
|
||||
/// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
|
||||
pub fn floatNorm(r: Random, comptime T: type) T {
|
||||
const value = ziggurat.next_f64(r, ziggurat.NormDist);
|
||||
switch (T) {
|
||||
f32 => return @floatCast(value),
|
||||
f64 => return value,
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an exponentially distributed float with a rate parameter of 1.
|
||||
///
|
||||
/// To use a different rate parameter, use: floatExp(...) / desiredRate.
|
||||
pub fn floatExp(r: Random, comptime T: type) T {
|
||||
const value = ziggurat.next_f64(r, ziggurat.ExpDist);
|
||||
switch (T) {
|
||||
f32 => return @floatCast(value),
|
||||
f64 => return value,
|
||||
else => @compileError("unknown floating point type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shuffle a slice into a random order.
|
||||
///
|
||||
/// Note that this will not yield consistent results across all targets
|
||||
/// due to dependence on the representation of `usize` as an index.
|
||||
/// See `shuffleWithIndex` for further commentary.
|
||||
pub inline fn shuffle(r: Random, comptime T: type, buf: []T) void {
|
||||
r.shuffleWithIndex(T, buf, usize);
|
||||
}
|
||||
|
||||
/// Shuffle a slice into a random order, using an index of a
|
||||
/// specified type to maintain distribution across targets.
|
||||
/// Asserts the index type can represent `buf.len`.
|
||||
///
|
||||
/// Indexes into the slice are generated using the specified `Index`
|
||||
/// type, which determines distribution properties. This allows for
|
||||
/// results to be independent of `usize` representation.
|
||||
///
|
||||
/// Prefer `shuffle` if this isn't important.
|
||||
///
|
||||
/// See `intRangeLessThan`, which this function uses,
|
||||
/// for commentary on the runtime of this function.
|
||||
pub fn shuffleWithIndex(r: Random, comptime T: type, buf: []T, comptime Index: type) void {
|
||||
const MinInt = MinArrayIndex(Index);
|
||||
if (buf.len < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// `i <= j < max <= maxInt(MinInt)`
|
||||
const max: MinInt = @intCast(buf.len);
|
||||
var i: MinInt = 0;
|
||||
while (i < max - 1) : (i += 1) {
|
||||
const j: MinInt = @intCast(r.intRangeLessThan(Index, i, max));
|
||||
mem.swap(T, &buf[i], &buf[j]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Randomly selects an index into `proportions`, where the likelihood of each
|
||||
/// index is weighted by that proportion.
|
||||
/// It is more likely for the index of the last proportion to be returned
|
||||
/// than the index of the first proportion in the slice, and vice versa.
|
||||
///
|
||||
/// This is useful for selecting an item from a slice where weights are not equal.
|
||||
/// `T` must be a numeric type capable of holding the sum of `proportions`.
|
||||
pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []const T) usize {
|
||||
// This implementation works by summing the proportions and picking a
|
||||
// random point in [0, sum). We then loop over the proportions,
|
||||
// accumulating until our accumulator is greater than the random point.
|
||||
|
||||
const sum = s: {
|
||||
var sum: T = 0;
|
||||
for (proportions) |v| sum += v;
|
||||
break :s sum;
|
||||
};
|
||||
|
||||
const point = switch (@typeInfo(T)) {
|
||||
.Int => |int_info| switch (int_info.signedness) {
|
||||
.signed => r.intRangeLessThan(T, 0, sum),
|
||||
.unsigned => r.uintLessThan(T, sum),
|
||||
},
|
||||
// take care that imprecision doesn't lead to a value slightly greater than sum
|
||||
.Float => @min(r.float(T) * sum, sum - std.math.floatEps(T)),
|
||||
else => @compileError("weightedIndex does not support proportions of type " ++
|
||||
@typeName(T)),
|
||||
};
|
||||
|
||||
assert(point < sum);
|
||||
|
||||
var accumulator: T = 0;
|
||||
for (proportions, 0..) |p, index| {
|
||||
accumulator += p;
|
||||
if (point < accumulator) return index;
|
||||
} else unreachable;
|
||||
}
|
||||
|
||||
/// Returns the smallest of `Index` and `usize`.
|
||||
fn MinArrayIndex(comptime Index: type) type {
|
||||
const index_info = @typeInfo(Index).Int;
|
||||
assert(index_info.signedness == .unsigned);
|
||||
return if (index_info.bits >= @typeInfo(usize).Int.bits) usize else Index;
|
||||
}
|
||||
};
|
||||
|
||||
/// Convert a random integer 0 <= random_int <= maxValue(T),
|
||||
/// into an integer 0 <= result < less_than.
|
||||
/// This function introduces a minor bias.
|
||||
pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T {
|
||||
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
|
||||
const bits = @typeInfo(T).Int.bits;
|
||||
|
||||
// adapted from:
|
||||
// http://www.pcg-random.org/posts/bounded-rands.html
|
||||
// "Integer Multiplication (Biased)"
|
||||
const m = math.mulWide(T, random_int, less_than);
|
||||
return @intCast(m >> bits);
|
||||
}
|
||||
|
||||
// Generator to extend 64-bit seed values into longer sequences.
|
||||
//
|
||||
// The number of cycles is thus limited to 64-bits regardless of the engine, but this
|
||||
// is still plenty for practical purposes.
|
||||
pub const SplitMix64 = struct {
|
||||
s: u64,
|
||||
|
||||
pub fn init(seed: u64) SplitMix64 {
|
||||
return SplitMix64{ .s = seed };
|
||||
}
|
||||
|
||||
pub fn next(self: *SplitMix64) u64 {
|
||||
self.s +%= 0x9e3779b97f4a7c15;
|
||||
|
||||
var z = self.s;
|
||||
z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) *% 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
_ = @import("rand/test.zig");
|
||||
}
|
||||
@ -379,7 +379,7 @@ test "sort with context in the middle of a slice" {
|
||||
}
|
||||
|
||||
test "sort fuzz testing" {
|
||||
var prng = std.rand.DefaultPrng.init(0x12345678);
|
||||
var prng = std.Random.DefaultPrng.init(0x12345678);
|
||||
const random = prng.random();
|
||||
const test_case_count = 10;
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE
|
||||
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
|
||||
pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue;
|
||||
pub const Progress = @import("Progress.zig");
|
||||
pub const Random = @import("Random.zig");
|
||||
pub const RingBuffer = @import("RingBuffer.zig");
|
||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||
pub const SemanticVersion = @import("SemanticVersion.zig");
|
||||
@ -156,8 +157,8 @@ pub const pdb = @import("pdb.zig");
|
||||
/// and spawning of child processes.
|
||||
pub const process = @import("process.zig");
|
||||
|
||||
/// Fast pseudo-random number generators (i.e. not cryptographically secure).
|
||||
pub const rand = @import("rand.zig");
|
||||
/// Deprecated: use `Random` instead.
|
||||
pub const rand = Random;
|
||||
|
||||
/// Sorting.
|
||||
pub const sort = @import("sort.zig");
|
||||
|
||||
@ -18,7 +18,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type {
|
||||
|
||||
/// A customized pseudo random number generator for the treap.
|
||||
/// This just helps reducing the memory size of the treap itself
|
||||
/// as std.rand.DefaultPrng requires larger state (while producing better entropy for randomness to be fair).
|
||||
/// as std.Random.DefaultPrng requires larger state (while producing better entropy for randomness to be fair).
|
||||
const Prng = struct {
|
||||
xorshift: usize = 0,
|
||||
|
||||
@ -305,7 +305,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type {
|
||||
// https://lemire.me/blog/2017/09/18/visiting-all-values-in-an-array-exactly-once-in-random-order/
|
||||
fn SliceIterRandomOrder(comptime T: type) type {
|
||||
return struct {
|
||||
rng: std.rand.Random,
|
||||
rng: std.Random,
|
||||
slice: []T,
|
||||
index: usize = undefined,
|
||||
offset: usize = undefined,
|
||||
@ -313,7 +313,7 @@ fn SliceIterRandomOrder(comptime T: type) type {
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(slice: []T, rng: std.rand.Random) Self {
|
||||
pub fn init(slice: []T, rng: std.Random) Self {
|
||||
return Self{
|
||||
.rng = rng,
|
||||
.slice = slice,
|
||||
@ -353,7 +353,7 @@ test "std.Treap: insert, find, replace, remove" {
|
||||
var treap = TestTreap{};
|
||||
var nodes: [10]TestNode = undefined;
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0xdeadbeef);
|
||||
var prng = std.Random.DefaultPrng.init(0xdeadbeef);
|
||||
var iter = SliceIterRandomOrder(TestNode).init(&nodes, prng.random());
|
||||
|
||||
// insert check
|
||||
|
||||
@ -136,7 +136,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
var more_fixups: Ast.Fixups = .{};
|
||||
defer more_fixups.deinit(gpa);
|
||||
|
||||
var rng = std.rand.DefaultPrng.init(seed);
|
||||
var rng = std.Random.DefaultPrng.init(seed);
|
||||
|
||||
// 1. Walk the AST of the source file looking for independent
|
||||
// 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 {
|
||||
return std.process.cleanExit();
|
||||
}
|
||||
|
||||
fn sortTransformations(transformations: []Walk.Transformation, rng: std.rand.Random) void {
|
||||
fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) void {
|
||||
rng.shuffle(Walk.Transformation, transformations);
|
||||
// Stable sort based on priority to keep randomness as the secondary sort.
|
||||
// TODO: introduce transformation priorities
|
||||
|
||||
@ -3356,7 +3356,7 @@ test "StringTable" {
|
||||
}
|
||||
break :ids buf;
|
||||
};
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
var prng = std.Random.DefaultPrng.init(0);
|
||||
var random = prng.random();
|
||||
random.shuffle(u16, &ids);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user