random.hpp (7546B)
1 #ifndef GUARD_UNMALLEABLY_DEFLECTIVE_SMOKED_MEAT_UNDERPOTS_9143 2 #define GUARD_UNMALLEABLY_DEFLECTIVE_SMOKED_MEAT_UNDERPOTS_9143 3 #pragma once 4 5 #include "libshit/assert.hpp" 6 #include "libshit/span.hpp" 7 8 #include <algorithm> 9 #include <array> 10 #include <climits> 11 #include <cstddef> 12 #include <cstdint> 13 #include <limits> 14 #include <type_traits> 15 #include <utility> 16 17 namespace Libshit 18 { 19 void FillRandom(Span<std::byte> buf); 20 21 template <typename T, size_t s> 22 static std::array<T, s> FillRandom() 23 { 24 std::array<T, s> ret; 25 FillRandom({ 26 reinterpret_cast<std::byte*>(ret.data()), sizeof(T) * ret.size()}); 27 return ret; 28 } 29 30 /// Like std::make_unsigned but works on bool (returns bool) 31 template <typename T> struct MakeUnsignedBool : std::make_unsigned<T> {}; 32 template <> struct MakeUnsignedBool<bool> { using type = bool; }; 33 template <typename T> 34 using MakeUnsignedBoolT = typename MakeUnsignedBool<T>::type; 35 36 /// CHAR_BIT * sizeof(T), except 1 for bool 37 template <typename T> struct Bits 38 : std::integral_constant<std::size_t, CHAR_BIT * sizeof(T)> {}; 39 template<> struct Bits<bool> : std::integral_constant<std::size_t, 1> {}; 40 41 template <typename Base> 42 class RandomGenerator : public Base 43 { 44 public: 45 using NativeType = typename Base::NativeType; 46 static_assert(std::is_integral_v<NativeType>); 47 static_assert(std::is_unsigned_v<NativeType>); 48 49 using Base::Base; 50 51 // Same as Gen<U, 1 << Bits<U>::value>, except handles overflow in <<. 52 template <typename U> 53 constexpr std::enable_if_t<std::is_integral_v<U>, U> 54 // TODO: change to noexcept(noexcept(this->GenBlock())) when gcc-8 no longer 55 // needs to be supported. (Search for declval in this file) 56 Gen() noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 57 { 58 static_assert(Bits<U>::value <= Bits<NativeType>::value); 59 NativeType t = this->GenBlock(); 60 return static_cast<U>(t >> (Bits<NativeType>::value - Bits<U>::value)); 61 } 62 63 /** 64 * Generate bool or integer in range [0, mod). 65 * @tparam U integral type 66 * @tparam mod Return a number modulo mod. 67 */ 68 template <typename U, MakeUnsignedBoolT<U> mod> 69 constexpr std::enable_if_t<std::is_integral_v<U>, U> 70 Gen() noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 71 { 72 static_assert(mod >= 2); 73 static_assert(mod <= std::numeric_limits<NativeType>::max()); 74 75 constexpr NativeType div = 76 (std::numeric_limits<NativeType>::max() - (mod-1)) / mod + 1; 77 NativeType g; 78 do 79 g = this->GenBlock(); 80 while (g > div * mod - 1); 81 82 return static_cast<U>(g / div); 83 } 84 85 /** 86 * Generate bool or integer in range [0, mod). 87 * @tparam U integral type 88 * @param mod Return a number modulo mod. 89 */ 90 template <typename U> 91 constexpr std::enable_if_t<std::is_integral_v<U>, U> 92 Gen(MakeUnsignedBoolT<U> mod) noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 93 { 94 if (mod < 2) return 0; 95 LIBSHIT_ASSERT(mod <= std::numeric_limits<NativeType>::max()); 96 97 NativeType div = 98 (std::numeric_limits<NativeType>::max() - (mod-1)) / mod + 1; 99 NativeType g; 100 do 101 g = this->GenBlock(); 102 while (g > div * mod - 1); 103 104 return static_cast<U>(g / div); 105 } 106 107 /** 108 * Generate bool or integer in range [begin, end). 109 * @tparam U integral type 110 */ 111 template <typename U> 112 constexpr std::enable_if_t<std::is_integral_v<U>, U> 113 Gen(U begin, U end) noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 114 { 115 LIBSHIT_ASSERT(end >= begin); 116 return static_cast<U>(Gen<U>(end - begin) + begin); 117 } 118 119 /** 120 * Generate a random enum (class) value. It expects an enum starting from 0 121 * without holes. 122 * @tparam mod Number of elements of the enum (probably U::NumElements or 123 * something similar). 124 */ 125 template <typename U, U mod> 126 constexpr std::enable_if_t<std::is_enum_v<U>, U> 127 Gen() noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 128 { 129 using Ut = std::underlying_type_t<U>; 130 return static_cast<U>(Gen<Ut, static_cast<Ut>(mod)>()); 131 } 132 133 /// Generate a floating point in range [0, mul). 134 template <typename U> 135 constexpr std::enable_if_t<std::is_floating_point_v<U>, U> 136 Gen(U mul = 1) noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 137 { 138 static_assert(sizeof(U) <= sizeof(NativeType)); 139 NativeType g = this->GenBlock(); 140 constexpr auto dig = std::numeric_limits<U>::digits; 141 return (g >> (Bits<NativeType>::value - dig)) * (mul / (NativeType(1)<<dig)); 142 } 143 144 /// Generate a floating point in range [begin, end). 145 template <typename U> 146 constexpr std::enable_if_t<std::is_floating_point_v<U>, U> 147 Gen(U begin, U end) noexcept(noexcept(std::declval<RandomGenerator*>()->GenBlock())) 148 { 149 LIBSHIT_ASSERT(end >= begin); 150 return Gen<U>(end-begin) + begin; 151 } 152 }; 153 154 /** 155 * A ref-like wrapper to turn our own randoms into UniformRandomBitGenerator 156 * used by stl (like std::shuffle, because they had to remove random_shuffle 157 * taking a single function instead of this complicated mess). 158 */ 159 template <typename Rand> 160 class StdRandomRef 161 { 162 public: 163 using result_type = typename Rand::NativeType; 164 165 constexpr StdRandomRef(Rand& rnd) noexcept : rnd{&rnd} {} 166 167 static constexpr result_type min() noexcept 168 { return std::numeric_limits<result_type>::min(); } 169 static constexpr result_type max() noexcept 170 { return std::numeric_limits<result_type>::max(); } 171 172 result_type operator()() noexcept(noexcept(std::declval<Rand*>()->GenBlock())) 173 { return rnd->GenBlock(); } 174 175 private: 176 Rand* rnd; 177 }; 178 179 /** 180 * Random generator based on `xoshiro128+` implementation at 181 * <http://xoshiro.di.unimi.it/xoshiro128plus.c>. 182 * 183 * @par Original license 184 * Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) 185 * To the extent possible under law, the author has dedicated all copyright 186 * and related and neighboring rights to this software to the public domain 187 * worldwide. This software is distributed without any warranty. 188 * See <http://creativecommons.org/publicdomain/zero/1.0/>. 189 */ 190 template <typename T, std::size_t Shift, std::size_t Rot> 191 class XoshiropBase 192 { 193 public: 194 using NativeType = T; 195 XoshiropBase() noexcept : XoshiropBase{FillRandom<T, 4>()} {} 196 197 /// The state must be seeded so that it is not everywhere zero. 198 constexpr XoshiropBase(const std::array<T, 4>& state) noexcept 199 : state{state} 200 { 201 LIBSHIT_RASSERT(std::all_of(state.begin(), state.end(), 202 [](auto x) { return x > 0; })); 203 } 204 205 XoshiropBase(const XoshiropBase&) = delete; 206 void operator=(const XoshiropBase&) = delete; 207 208 constexpr T GenBlock() noexcept 209 { 210 const T result_plus = state[0] + state[3]; 211 212 const T t = state[1] << Shift; 213 214 state[2] ^= state[0]; 215 state[3] ^= state[1]; 216 state[1] ^= state[2]; 217 state[0] ^= state[3]; 218 219 state[2] ^= t; 220 221 state[3] = rotl(state[3], Rot); 222 223 return result_plus; 224 } 225 226 private: 227 static constexpr inline T rotl(T x, std::size_t k) noexcept 228 { return (x << k) | (x >> (Bits<T>::value - k)); } 229 230 std::array<T, 4> state; 231 }; 232 233 using Xoshiro128p = RandomGenerator<XoshiropBase<std::uint32_t, 9, 11>>; 234 using Xoshiro256p = RandomGenerator<XoshiropBase<std::uint64_t, 17, 45>>; 235 } 236 237 #endif