libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

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