#pragma once #include // CHAR_BIT #include #include #include namespace base { namespace detail { template constexpr size_t BitSizeOf() { return sizeof(T) * CHAR_BIT; } constexpr std::uint64_t splitmix64(std::uint64_t x) { std::uint64_t z = (x += 0x9e3779b97f4a7c15uLL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9uLL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebuLL; return z ^ (z >> 31); } constexpr std::uint64_t rotl(std::uint64_t x, int k) { return (x << k) | (x >> (BitSizeOf() - k)); } struct Xoshiro256ss { using result_type = std::uint64_t; std::uint64_t s[4] {}; constexpr explicit Xoshiro256ss() : Xoshiro256ss(0) {} constexpr explicit Xoshiro256ss(std::uint64_t seed) { s[0] = splitmix64(seed); s[1] = splitmix64(seed); s[2] = splitmix64(seed); s[3] = splitmix64(seed); } constexpr explicit Xoshiro256ss(std::random_device& rd) { // Get 64 bits out of the random device. // // This lambda is quite literal, as it fetches // 2 iterations of the random engine, and // shifts + OR's them into a 64bit value. auto get_u64 = [&rd] { std::uint64_t the_thing = rd(); return (the_thing << 32) | rd(); }; // seed with 256 bits of entropy from the random device + splitmix64 // to ensure we seed it well, as per recommendation. s[0] = splitmix64(get_u64()); s[1] = splitmix64(get_u64()); s[2] = splitmix64(get_u64()); s[3] = splitmix64(get_u64()); } static constexpr result_type min() { return 0; } static constexpr result_type max() { return std::uint64_t(-1); } constexpr result_type operator()() { result_type result = rotl(s[1] * 5, 7) * 9; result_type 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; } }; } // namespace detail using detail::Xoshiro256ss; } // namespace base