diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarRandom.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarRandom.cpp')
-rw-r--r-- | source/core/StarRandom.cpp | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/source/core/StarRandom.cpp b/source/core/StarRandom.cpp new file mode 100644 index 0000000..abd3984 --- /dev/null +++ b/source/core/StarRandom.cpp @@ -0,0 +1,346 @@ +#include "StarRandom.hpp" +#include "StarThread.hpp" +#include "StarTime.hpp" +#include "StarMathCommon.hpp" + +namespace Star { + +RandomSource::RandomSource() { + init(Random::randu64()); +} + +void RandomSource::init() { + init(Random::randu64()); +} + +RandomSource::RandomSource(uint64_t seed) { + init(seed); +} + +void RandomSource::init(uint64_t seed) { + /* choose random initial m_carry < 809430660 and */ + /* 256 random 32-bit integers for m_data[] */ + m_carry = seed % 809430660; + + m_data[0] = seed; + m_data[1] = seed >> 32; + + for (size_t i = 2; i < 256; ++i) + m_data[i] = 69069 * m_data[i - 2] + 362437; + + m_index = 255; + + // Hard-coded initial skip of random values, to get the random generator + // going. + const unsigned RandomInitialSkip = 32; + for (unsigned i = 0; i < RandomInitialSkip; ++i) + gen32(); +} + +void RandomSource::addEntropy() { + addEntropy(Random::randu64()); +} + +void RandomSource::addEntropy(uint64_t seed) { + // to avoid seed aliasing + seed ^= randu64(); + + // Same algo as init, but bitwise xor with existing data + + m_carry = (m_carry ^ seed) % 809430660; + + m_data[0] ^= seed; + m_data[1] ^= (seed >> 32) ^ seed; + + for (size_t i = 2; i < 256; ++i) + m_data[i] ^= 69069 * m_data[i - 2] + 362437; +} + +uint32_t RandomSource::randu32() { + return gen32(); +} + +uint64_t RandomSource::randu64() { + uint64_t r = randu32(); + r = r << 32; + r = r | randu32(); + return r; +} + +int32_t RandomSource::randi32() { + return (int32_t)(randu32()); +} + +int64_t RandomSource::randi64() { + return (int64_t)(randu64()); +} + +float RandomSource::randf() { + return (randu32() & 0x7fffffff) / 2147483648.0f; +} + +double RandomSource::randd() { + return (randu64() & 0x7fffffffffffffff) / 9223372036854775808.0; +} + +int64_t RandomSource::randInt(int64_t max) { + return randUInt(max); +} + +uint64_t RandomSource::randUInt(uint64_t max) { + uint64_t denom = (uint64_t)(-1) / ((uint64_t)max + 1); + return randu64() / denom; +} + +int64_t RandomSource::randInt(int64_t min, int64_t max) { + if (max < min) + throw StarException("Maximum bound in randInt must be >= minimum bound!"); + return randInt(max - min) + min; +} + +uint64_t RandomSource::randUInt(uint64_t min, uint64_t max) { + if (max < min) + throw StarException("Maximum bound in randUInt must be >= minimum bound!"); + return randUInt(max - min) + min; +} + +float RandomSource::randf(float min, float max) { + if (max < min) + throw StarException("Maximum bound in randf must be >= minimum bound!"); + return randf() * (max - min) + min; +} + +double RandomSource::randd(double min, double max) { + if (max < min) + throw StarException("Maximum bound in randd must be >= minimum bound!"); + return randd() * (max - min) + min; +} + +bool RandomSource::randb() { + uint32_t v = gen32(); + bool parity = false; + while (v) { + parity = !parity; + v = v & (v - 1); + } + return parity; +} + +void RandomSource::randBytes(char* buf, size_t len) { + while (len) { + uint32_t ui = gen32(); + for (size_t i = 0; i < 4; ++i) { + if (len) { + *buf = (char)(ui >> (i * 8)); + --len; + ++buf; + } + } + } +} + +ByteArray RandomSource::randBytes(size_t len) { + ByteArray array(len, 0); + randBytes(array.ptr(), len); + return array; +} + +// normal distribution via Box-Muller +float RandomSource::nrandf(float stddev, float mean) { + float rand1, rand2, distSqr; + do { + rand1 = 2 * randf() - 1; + rand2 = 2 * randf() - 1; + distSqr = rand1 * rand1 + rand2 * rand2; + } while (distSqr >= 1); + + float mapping = std::sqrt(-2 * std::log(distSqr) / distSqr); + return (rand1 * mapping * stddev + mean); +} + +double RandomSource::nrandd(double stddev, double mean) { + double rand1, rand2, distSqr; + do { + rand1 = 2 * randd() - 1; + rand2 = 2 * randd() - 1; + distSqr = rand1 * rand1 + rand2 * rand2; + } while (distSqr >= 1); + + double mapping = std::sqrt(-2 * std::log(distSqr) / distSqr); + return (rand1 * mapping * stddev + mean); +} + +int64_t RandomSource::stochasticRound(double val) { + double fpart = val - floor(val); + if (randd() < fpart) + return ceil(val); + else + return floor(val); +} + +uint32_t RandomSource::gen32() { + uint64_t a = 809430660; + uint64_t t = a * m_data[++m_index] + m_carry; + + m_carry = (t >> 32); + m_data[m_index] = t; + + return t; +} + +namespace Random { + static Maybe<RandomSource> g_randSource; + static Mutex g_randMutex; + + static uint64_t produceRandomSeed() { + int64_t seed = Time::monotonicTicks(); + seed *= 1099511628211; + seed ^= (((int64_t)rand()) << 32) | ((int64_t)rand()); + return seed; + } + + void doInit(uint64_t seed) { + g_randSource = RandomSource(seed); + // Also set the C stdlib random seed + srand(seed); + } + + void checkInit() { + // Mutex must already be held + if (!g_randSource) { + doInit(produceRandomSeed()); + } + } + + void init() { + MutexLocker locker(g_randMutex); + doInit(produceRandomSeed()); + } + + void init(uint64_t seed) { + MutexLocker locker(g_randMutex); + doInit(seed); + } + + void addEntropy() { + MutexLocker locker(g_randMutex); + checkInit(); + g_randSource->addEntropy(produceRandomSeed()); + } + + void addEntropy(uint64_t seed) { + MutexLocker locker(g_randMutex); + checkInit(); + g_randSource->addEntropy(seed); + } + + uint32_t randu32() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randu32(); + } + + uint64_t randu64() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randu64(); + } + + int32_t randi32() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randi32(); + } + + int64_t randi64() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randi64(); + } + + float randf() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randf(); + } + + double randd() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randd(); + } + + float randf(float min, float max) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randf(min, max); + } + + double randd(double min, double max) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randd(min, max); + } + + bool randb() { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randb(); + } + + long long randInt(long long max) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randInt(max); + } + + unsigned long long randUInt(unsigned long long max) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randUInt(max); + } + + long long randInt(long long min, long long max) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randInt(min, max); + } + + unsigned long long randUInt(unsigned long long min, unsigned long long max) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randUInt(min, max); + } + + float nrandf(float stddev, float mean) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->nrandf(stddev, mean); + } + + double nrandd(double stddev, double mean) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->nrandd(stddev, mean); + } + + int64_t stochasticRound(double val) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->stochasticRound(val); + } + + void randBytes(char* buf, size_t len) { + MutexLocker locker(g_randMutex); + checkInit(); + g_randSource->randBytes(buf, len); + } + + ByteArray randBytes(size_t len) { + MutexLocker locker(g_randMutex); + checkInit(); + return g_randSource->randBytes(len); + } +} + +} |