ultimatepp/uppsrc/Core/Random.cpp
cxl 1cbd73441c Core: Random refactored with xoshiro256**
git-svn-id: svn://ultimatepp.org/upp/trunk@13393 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2019-06-14 13:41:09 +00:00

142 lines
2.8 KiB
C++

#include "Core.h"
namespace Upp {
/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
To the extent possible under law, the author has dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
/* This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has
excellent (sub-ns) speed, a state (256 bits) that is large enough for
any parallel application, and it passes all tests we are aware of.
For generating just floating-point numbers, xoshiro256+ is even faster.
The state must be seeded so that it is not everywhere zero. If you have
a 64-bit seed, we suggest to seed a splitmix64 generator and use its
output to fill s. */
static force_inline uint64_t s_rotl(const uint64_t x, int k) {
return (x << k) | (x >> (64 - k)); // GCC/CLANG/MSC happily optimize this
}
force_inline
static uint64 sNext(uint64 *s)
{
const uint64_t result_starstar = s_rotl(s[1] * 5, 7) * 9;
const uint64_t t = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = s_rotl(s[3], 45);
return result_starstar;
}
never_inline
static void sSeed(uint64 *s)
{
#ifdef PLATFORM_POSIX
int fd = open("/dev/urandom", O_RDONLY);
if(fd != -1) {
IGNORE_RESULT(
read(fd, s, 4 * sizeof(uint64))
);
close(fd);
}
#else
for(int pass = 0; pass < 4; pass++) {
for(int i = 0; i < 4; i++) {
uint64 h = 0;
for(int p = 0; p < 2; p++) {
Uuid uuid;
CoCreateGuid((GUID *)&uuid);
h = (h << 32) | GetHashValue(uuid);
}
s[i] ^= h;
}
}
#endif
}
force_inline
static uint64 *sState()
{
#ifdef COMPILER_MINGW
FastMingwTls<uint64 *> s;
#else
thread_local uint64 *s;
#endif
if(!s) {
thread_local uint64 state[4];
s = state;
sSeed(s);
#ifdef PLATFORM_POSIX
if(Thread::IsMain()) // non-main threads do now work with fork anyway
pthread_atfork(NULL, NULL, [] { sSeed(s); }); // reseed random generator after fork
#endif
}
return s;
}
never_inline
dword Random(void) {
return (dword)sNext(sState());
}
never_inline
uint64 Random64(void) {
return sNext(sState());
}
never_inline
dword Random(dword n) {
ASSERT(n);
uint64 *s = sState();
dword mask = (1 << SignificantBits(n)) - 1;
dword r;
do
r = (dword)sNext(s) & mask;
while(r >= n);
return r;
}
never_inline
uint64 Random64(uint64 n) {
ASSERT(n);
uint64 *s = sState();
uint64 mask = ((uint64)1 << SignificantBits64(n)) - 1;
uint64 r;
do
r = sNext(s) & mask;
while(r >= n);
return r;
}
never_inline
double Randomf()
{
return (sNext(sState()) >> 11) * (1. / (UINT64_C(1) << 53));
}
void SeedRandom()
{
sSeed(sState());
}
void SeedRandom(dword seed) {
uint64 *s = sState();
s[0] = s[1] = s[2] = s[3] = seed + 12345678;
}
}