Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarRandom.cpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarRandom.cpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/core/StarRandom.cpp')
-rw-r--r--source/core/StarRandom.cpp346
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);
+ }
+}
+
+}