AuroraRuntime/Source/RNG/AuWELL.cpp

129 lines
3.5 KiB
C++

/*
* Copyright (C) 2008, Chris Lomont
* 2021-2020, Rocks
* Public Domain
*
* http://lomont.org/papers/2008/Lomont_PRNG_2008.pdf
*/
#include <Source/RuntimeInternal.hpp>
#include "AuWELL.hpp"
#include "AuMTwister.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 AuMove(rand);
}
WELLRand WELL_SeedRand64(AuUInt64 seed)
{
WELLRand rand {};
WELL_SeedRand64(&rand, seed);
return AuMove(rand);
}
WELLRand WELL_SeedRandBig64(const AuArray<AuUInt8, 64> &seed)
{
WELLRand rand {};
static_assert(64 == sizeof(rand.state));
AuMemcpy(rand.state, seed.data(), sizeof(rand.state));
return AuMove(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 AuFnv1a32Runtime<sizeof(ret)>(&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, b;
AuUInt8 *base;
i = 0;
b = length / 4;
base = reinterpret_cast<AuUInt8 *>(in);
AU_LOCK_GUARD(rand->lock);
for (; i < b; i++)
{
AuUInt32 rng = WELL_NextLong_Unlocked(rand);
AuWriteU32(base, i * 4, rng);
}
i *= 4;
if (i != length)
{
AuUInt32 padRng = WELL_NextLong_Unlocked(rand);
AuMemcpy(base + i, &padRng, length - i);
}
}