/* * Copyright (C) 2008, Chris Lomont * 2021-2020, Rocks * Public Domain * * http://lomont.org/papers/2008/Lomont_PRNG_2008.pdf */ #include #include "WELL.hpp" #include "mtwister.hpp" // WELL should provide us reasonably 'secure' and, more importantly, fast RNG for // general purpose use. It isn't fundamentally broken unlike MT, I dont think 512 // or 16 samples is going to break anything unlike MT with 624 or so. WELL has // demonstrable better statistical properties and 32-bit performance. I think it // makes for a better defacto backend than MT. Generic xorshiftrots might be alright // for improved speed at the cost of what little reliability PRNGs have. // Redunced seed entropy should not matter to the extent that MT can make up for it // WELL is used in the fast rng backend, and i feel as though it would be unreasonable // to go above UInt64 seeds. For toy purposes, U32/64 seeds are probably fine. Treat // WELL like MT without 624 prediction and performance issues. static auline void WELL_SeedRand64(WELLRand *rand, AuUInt64 seed) { MTRand mtrand = MT_SeedRand(seed & 0xffffffff); MTRand mtrand2 = MT_SeedRand(seed >> 32); AuMemset(rand->state, 0, sizeof(rand->state)); for (unsigned int i = 0; i < AuArraySize(rand->state); i++) { rand->state[i] = MT_NextLong(&mtrand) ^ MT_NextLong(&mtrand2); } } static auline void WELL_SeedRand(WELLRand *rand, AuUInt32 seed) { MTRand mtrand = MT_SeedRand(seed); AuMemset(rand->state, 0, sizeof(rand->state)); for (unsigned int i = 0; i < AuArraySize(rand->state); i++) { rand->state[i] = MT_NextLong(&mtrand); } } /** * Creates a new random number generator from a given seed. */ WELLRand WELL_SeedRand(AuUInt32 seed) { WELLRand rand {}; WELL_SeedRand(&rand, seed); return rand; } WELLRand WELL_SeedRand64(AuUInt64 seed) { WELLRand rand {}; WELL_SeedRand64(&rand, seed); return rand; } WELLRand WELL_SeedRandBig64(const AuArray &seed) { WELLRand rand {}; static_assert(64 == sizeof(rand.state)); AuMemcpy(rand.state, seed.data(), sizeof(rand.state)); return rand; } /** * Generates a pseudo-randomly generated long. */ auline AuUInt32 WELL_NextLong_Unlocked(WELLRand *rand) { AuUInt32 a, b, c, d, ret; a = rand->state[rand->index]; c = rand->state[(rand->index + 13) & 15]; b = a ^ c ^ (a << 16) ^ (c << 15); c = rand->state[(rand->index + 9) & 15]; c ^= (c >> 11); a = rand->state[rand->index] = b ^ c; d = a ^ ((a << 5) & 0xDA442D24UL); rand->index = (rand->index + 15) & 15; a = rand->state[rand->index]; rand->state[rand->index] = a ^ b ^ d ^ (a << 2) ^ (b << 18) ^ (c << 28); ret = rand->state[rand->index]; return ret; } /** * Generates a pseudo-randomly generated long. */ AuUInt32 WELL_NextLong(WELLRand *rand) { AU_LOCK_GUARD(rand->lock); AuUInt32 ret = WELL_NextLong_Unlocked(rand); return ret; } void WELL_NextBytes(WELLRand *rand, void *in, AuUInt32 length) { AuUInt i; AuUInt8 *base; i = 0; base = reinterpret_cast(in); AU_LOCK_GUARD(rand->lock); for (; i < length; i += 4) { AuUInt32 rng = WELL_NextLong_Unlocked(rand); AuMemcpy(base + i, &rng, 4); } if (i > length) { i -= 4; AuUInt32 padRng = WELL_NextLong_Unlocked(rand); AuMemcpy(base + i, &padRng, length - i); } }