127 lines
3.4 KiB
C++
127 lines
3.4 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 "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<AuUInt8, 64> &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<AuUInt8 *>(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);
|
|
}
|
|
} |