|  | // Copyright Microsoft and CHERIoT Contributors. | 
|  | // SPDX-License-Identifier: MIT | 
|  | // | 
|  | // Imported from snmalloc 4f9d991449380ed7d881a25ba02cc5668c1ff394. | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <cstdlib> | 
|  | #include <debug.hh> | 
|  | #include <initializer_list> | 
|  |  | 
|  | namespace ds::xoroshiro | 
|  | { | 
|  |  | 
|  | namespace detail | 
|  | { | 
|  | using Debug = ConditionalDebug<false, "xoroshiro">; | 
|  |  | 
|  | /** | 
|  | * The xoroshiro+ (not ++) generator(s) of Blackman and Vigna's | 
|  | * Scrambled Linear Pseudorandom Number Generators | 
|  | * (https://arxiv.org/abs/1805.01407, Figure 1). | 
|  | * | 
|  | * This spelling is parameterized on the type of the two state | 
|  | * variables used internally (State), the type of each sample (Result), | 
|  | * and the seed state values (A, B, C). | 
|  | */ | 
|  | template<typename State, | 
|  | typename Result, | 
|  | State A, | 
|  | State B, | 
|  | State C, | 
|  | State Jump0     = 0, | 
|  | State Jump1     = 0, | 
|  | State LongJump0 = 0, | 
|  | State LongJump1 = 0> | 
|  | class XorOshiro | 
|  | { | 
|  | private: | 
|  | static constexpr unsigned StateBits  = 8 * sizeof(State); | 
|  | static constexpr unsigned ResultBits = 8 * sizeof(Result); | 
|  |  | 
|  | static_assert(StateBits >= ResultBits, | 
|  | "State must have at least as many bits as Result"); | 
|  |  | 
|  | /* Parameters must be valid shifts */ | 
|  | static_assert(0 <= A && A <= StateBits); | 
|  | static_assert(0 <= B && B <= StateBits); | 
|  | static_assert(0 <= C && C <= StateBits); | 
|  |  | 
|  | State x; | 
|  | State y; | 
|  |  | 
|  | static inline State rotl(State x, State k) | 
|  | { | 
|  | return (x << k) | (x >> (StateBits - k)); | 
|  | } | 
|  |  | 
|  | void jump(State jump0, State jump1) | 
|  | { | 
|  | const State Jump[] = {jump0, jump1}; | 
|  | uint64_t    s0     = 0; | 
|  | uint64_t    s1     = 0; | 
|  | for (int i : {0, 1}) | 
|  | { | 
|  | for (int b = 0; b < 64; b++) | 
|  | { | 
|  | if (Jump[i] & uint64_t(1) << b) | 
|  | { | 
|  | s0 ^= x; | 
|  | s1 ^= y; | 
|  | } | 
|  | next(); | 
|  | } | 
|  | } | 
|  | x = s0; | 
|  | y = s1; | 
|  | } | 
|  |  | 
|  | public: | 
|  | XorOshiro(State x = 5489, State y = 0) : x(x), y(y) | 
|  | { | 
|  | // If both zero, then this does not work | 
|  | Debug::Invariant((x != 0) || (y != 0), | 
|  | "Invalid state, both x and y are zero"); | 
|  |  | 
|  | next(); | 
|  | } | 
|  |  | 
|  | void set_state(State nx, State ny = 0) | 
|  | { | 
|  | // If both zero, then this does not work | 
|  | Debug::Invariant((nx != 0) || (ny != 0), | 
|  | "Invalid state, both nx and ny are zero"); | 
|  |  | 
|  | x = nx; | 
|  | y = ny; | 
|  | next(); | 
|  | } | 
|  |  | 
|  | Result next() | 
|  | { | 
|  | State oldX = x; | 
|  | State oldY = y; | 
|  | State r    = x + y; | 
|  | y ^= x; | 
|  | x = rotl(x, A) ^ y ^ (y << B); | 
|  | y = rotl(y, C); | 
|  | // If both zero, then this does not work | 
|  | Debug::Invariant((x != 0) || (y != 0), | 
|  | "Invalid state, both x and y are zero after " | 
|  | "next() with x: {}, y: {}", | 
|  | oldX, | 
|  | oldY); | 
|  | return r >> (StateBits - ResultBits); | 
|  | } | 
|  |  | 
|  | Result operator()() | 
|  | { | 
|  | return next(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Jump.  If supported, this is equivalent to 2^64 calls to next(). | 
|  | */ | 
|  | void jump() requires(Jump0 != 0) && (Jump1 != 0) | 
|  | { | 
|  | jump(Jump0, Jump1); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Jump a *really* long way.  If supported, this is equivalent to | 
|  | * 2^96 calls to next(). | 
|  | */ | 
|  | void long_jump() requires(LongJump0 != 0) && (LongJump1 != 0) | 
|  | { | 
|  | jump(LongJump0, LongJump1); | 
|  | } | 
|  | }; | 
|  | } // namespace detail | 
|  |  | 
|  | /** | 
|  | * xoroshiro128+ with 64-bit output using the 2018 parameters. | 
|  | * | 
|  | * Parameters (including jump parameters) from: | 
|  | * https://prng.di.unimi.it/xoroshiro128plus.c | 
|  | */ | 
|  | using P128R64 = detail::XorOshiro<uint64_t, | 
|  | uint64_t, | 
|  | 24, | 
|  | 16, | 
|  | 37, | 
|  | 0xdf900294d8f554a5, | 
|  | 0x170865df4b3201fc, | 
|  | 0xd2a98b26625eee7b, | 
|  | 0xdddf9b1090aa7ac1>; | 
|  |  | 
|  | /** | 
|  | * xoroshiro128+ with 32-bit output using the 2018 parameters. | 
|  | * | 
|  | * Parameters as per P128R64, above. | 
|  | */ | 
|  | using P128R32 = detail::XorOshiro<uint64_t, uint32_t, 24, 16, 37>; | 
|  |  | 
|  | /** | 
|  | * A "xoroshiro64+" with 32-bit outputs. | 
|  | * | 
|  | * As per Sebastino Vigna himself, writing in | 
|  | * https://groups.google.com/g/prng/c/Ll-KDIbpO8k/m/bfHK4FlUCwAJ, these | 
|  | * parameters are one of may full-period triples. | 
|  | */ | 
|  | using P64R32 = detail::XorOshiro<uint32_t, uint32_t, 27, 7, 20>; | 
|  |  | 
|  | /** | 
|  | * A "xoroshiro64+" with 16-bit outputs. | 
|  | * | 
|  | * Parameters as per P64R32, above. | 
|  | */ | 
|  | using P64R16 = detail::XorOshiro<uint32_t, uint16_t, 27, 7, 20>; | 
|  |  | 
|  | /** | 
|  | * A "xoroshiro32+" with 16-bit outputs. | 
|  | * | 
|  | * Parameters as per the Parallax Propeller 2 PRNG (discarding the "++" | 
|  | * variant's fourth parameter "R").  See | 
|  | * https://forums.parallax.com/discussion/comment/1448894 . | 
|  | */ | 
|  | using P32R16 = detail::XorOshiro<uint16_t, uint16_t, 13, 5, 10>; | 
|  |  | 
|  | /** | 
|  | * A "xoroshiro32+" with 16-bit outputs. | 
|  | * | 
|  | * Parameters as per P32R16, above. | 
|  | */ | 
|  | using P32R8 = detail::XorOshiro<uint16_t, uint8_t, 13, 5, 10>; | 
|  |  | 
|  | /** | 
|  | * A "xoroshiro16+" with 8-bit outputs. | 
|  | * | 
|  | * As per Sebastino Vigna himself, writing in | 
|  | * https://groups.google.com/g/prng/c/MWJjq11zRis/m/7nM_cwRzAQAJ , the | 
|  | * parameters are one of may full-period triples, but "There are no good | 
|  | * 16-bit generators". | 
|  | */ | 
|  | using P16R8 = detail::XorOshiro<uint8_t, uint8_t, 4, 7, 3>; | 
|  | } // namespace ds::xoroshiro |