All checks were successful
Build (Arch Linux) / build (push) Successful in 3m10s
149 lines
4.7 KiB
C++
149 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <random>
|
|
|
|
namespace kuiper::random
|
|
{
|
|
|
|
class xoshiro256pp {
|
|
public:
|
|
xoshiro256pp(const std::array<std::uint64_t, 4>& state)
|
|
: s(state) {}
|
|
|
|
static xoshiro256pp from_random_device() {
|
|
std::random_device rd {};
|
|
|
|
static_assert(sizeof(std::random_device::result_type) == 4);
|
|
|
|
const std::uint64_t a = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
|
const std::uint64_t b = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
|
const std::uint64_t c = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
|
const std::uint64_t d = static_cast<std::uint64_t>(rd()) << 32 | static_cast<std::uint64_t>(rd());
|
|
|
|
return {{{a, b, c, d}}};
|
|
}
|
|
|
|
public:
|
|
std::uint64_t next() noexcept {
|
|
const std::uint64_t result = rotl(s[0] + s[3], 23) + s[0];
|
|
|
|
const std::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] = rotl(s[3], 45);
|
|
|
|
return result;
|
|
}
|
|
|
|
std::uint64_t operator()(void) noexcept {
|
|
return next();
|
|
}
|
|
|
|
private:
|
|
/* This is the jump function for the generator. It is equivalent
|
|
to 2^128 calls to next(); it can be used to generate 2^128
|
|
non-overlapping subsequences for parallel computations. */
|
|
|
|
void jump() noexcept {
|
|
static constexpr std::array<std::uint64_t, 4> JUMP = {
|
|
0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c};
|
|
|
|
std::uint64_t s0 = 0;
|
|
std::uint64_t s1 = 0;
|
|
std::uint64_t s2 = 0;
|
|
std::uint64_t s3 = 0;
|
|
for (int i = 0; i < JUMP.size() / sizeof(std::uint64_t); i++)
|
|
for (int b = 0; b < 64; b++) {
|
|
if (JUMP[i] & UINT64_C(1) << b) {
|
|
s0 ^= s[0];
|
|
s1 ^= s[1];
|
|
s2 ^= s[2];
|
|
s3 ^= s[3];
|
|
}
|
|
next();
|
|
}
|
|
|
|
s[0] = s0;
|
|
s[1] = s1;
|
|
s[2] = s2;
|
|
s[3] = s3;
|
|
}
|
|
|
|
/* This is the long-jump function for the generator. It is equivalent to
|
|
2^192 calls to next(); it can be used to generate 2^64 starting points,
|
|
from each of which jump() will generate 2^64 non-overlapping
|
|
subsequences for parallel distributed computations. */
|
|
|
|
void long_jump() {
|
|
static constexpr std::array<std::uint64_t, 4> LONG_JUMP = {
|
|
0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635};
|
|
|
|
std::uint64_t s0 = 0;
|
|
std::uint64_t s1 = 0;
|
|
std::uint64_t s2 = 0;
|
|
std::uint64_t s3 = 0;
|
|
for (int i = 0; i < LONG_JUMP.size() / sizeof(std::uint64_t); i++)
|
|
for (int b = 0; b < 64; b++) {
|
|
if (LONG_JUMP[i] & UINT64_C(1) << b) {
|
|
s0 ^= s[0];
|
|
s1 ^= s[1];
|
|
s2 ^= s[2];
|
|
s3 ^= s[3];
|
|
}
|
|
next();
|
|
}
|
|
|
|
s[0] = s0;
|
|
s[1] = s1;
|
|
s[2] = s2;
|
|
s[3] = s3;
|
|
}
|
|
|
|
inline std::uint64_t rotl(const std::uint64_t x, int k) {
|
|
return (x << k) | (x >> (64 - k));
|
|
}
|
|
|
|
private:
|
|
std::array<std::uint64_t, 4> s {};
|
|
};
|
|
|
|
// Original license & preface:
|
|
|
|
/* Written in 2019 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.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
/* This is xoshiro256++ 1.0, one of our all-purpose, rock-solid generators.
|
|
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. */
|
|
|
|
} // namespace kuiper::random
|