blob: 50e6cdb953d0754cdbbeefbdb65b0d6496f95602 [file]
#include <cassert>
#include <cstdint>
#include <random>
class Rand
{
public:
void seed(uint64_t x) { m_impl.seed(x); }
uint64_t u64() { return m_impl(); }
uint32_t u32() { return static_cast<uint32_t>(m_impl()); }
float f32(float start, float end)
{
// 24-bit mantissa drawn from the top bits of u64. Not strictly
// unbiased: the affine map onto [start, end] introduces tiny
// rounding non-uniformity when (end - start) isn't a dyadic
// fraction, and for very wide intervals the result can round up
// to exactly `end`. Acceptable for tests — the bias is below
// float epsilon, and std::uniform_real_distribution had the same
// boundary quirk anyway.
float u = (m_impl() >> 40) * (1.0f / (1 << 24));
return start + u * (end - start);
}
float f32(float end = 1) { return f32(0, end); }
bool boolean() { return m_impl() & 1; }
// This is based on Lemire's nearly-divisonless unbiased bounded random
// integer algorithm:
// https://lemire.me/blog/2019/06/06/nearly-divisionless-random-integer-generation-on-various-systems/
uint32_t u32(uint32_t start, uint32_t end)
{
assert(start <= end);
if (start == 0 && end == UINT32_MAX)
{
return u32();
}
uint32_t range = end - start + 1;
uint64_t m = uint64_t(u32()) * range;
uint32_t l = uint32_t(m);
if (l < range)
{
uint32_t threshold =
(0u - range) % range; // == (1ull << 32) % range
while (l < threshold)
{
m = uint64_t(u32()) * range;
l = uint32_t(m);
}
}
return start + uint32_t(m >> 32);
}
private:
std::mt19937_64 m_impl;
};