mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
149 lines
3.1 KiB
C++
149 lines
3.1 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++) {
|
|
CombineHash h[2];
|
|
for(int j = 0; j < 2; j++) {
|
|
h[j] << GetSysTime().Get() << usecs() << msecs() << j << pass << i;
|
|
for(int p = 0; p < 2; p++) {
|
|
Uuid uuid;
|
|
CoCreateGuid((GUID *)&uuid); // GUID is basically a random number...
|
|
h[j] << uuid.v[0] << uuid.v[1];
|
|
}
|
|
}
|
|
s[i] ^= MAKEQWORD(h[0], h[1]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
force_inline
|
|
static uint64 *sState()
|
|
{
|
|
static bool forked;
|
|
thread_local uint64 *s;
|
|
if(!s || forked && Thread::IsMain()) {
|
|
thread_local uint64 state[4];
|
|
s = state;
|
|
sSeed(s);
|
|
#ifdef PLATFORM_POSIX
|
|
if(Thread::IsMain()) { // non-main threads do not work with fork anyway
|
|
ONCELOCK {
|
|
pthread_atfork(NULL, NULL, [] { forked = true; }); // reseed random generator after fork
|
|
}
|
|
forked = false;
|
|
}
|
|
#endif
|
|
}
|
|
return s;
|
|
}
|
|
|
|
dword Random()
|
|
{
|
|
return (dword)sNext(sState());
|
|
}
|
|
|
|
uint64 Random64()
|
|
{
|
|
return sNext(sState());
|
|
}
|
|
|
|
void Random64(uint64 *t, int n) {
|
|
uint64 *s = sState();
|
|
while(n-- > 0)
|
|
*t++ = sNext(s);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
double Randomf()
|
|
{
|
|
return (sNext(sState()) >> 11) * (1. / (UINT64_C(1) << 53));
|
|
}
|
|
|
|
void SeedRandom()
|
|
{
|
|
sSeed(sState());
|
|
}
|
|
|
|
void SeedRandom(dword seed) {
|
|
uint64 *s = sState();
|
|
for(int i = 0; i < 4; i++)
|
|
s[i] = 12345678 + seed + i; // xoshiro does not work well if all is zero
|
|
}
|
|
|
|
}
|