1 /***************************************************************************** 2 * Copyright (c) 2014-2020 OpenRCT2 developers 3 * 4 * For a complete list of all authors, please refer to contributors.md 5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 6 * 7 * OpenRCT2 is licensed under the GNU General Public License version 3. 8 *****************************************************************************/ 9 10 #pragma once 11 12 #include "Meta.hpp" 13 #include "Numerics.hpp" 14 15 #include <algorithm> 16 #include <array> 17 #include <cstdint> 18 #include <iostream> 19 #include <limits> 20 #include <random> 21 #include <type_traits> 22 23 namespace Random 24 { 25 using namespace Numerics; 26 27 /** 28 * FixedSeedSequence adheres to the _Named Requirement_ `SeedSequence`. 29 */ 30 template<size_t TNum = 0> class FixedSeedSequence 31 { 32 public: 33 using result_type = uint32_t; 34 35 static constexpr size_t N = TNum; 36 static constexpr result_type default_seed = 0x1234567F; 37 FixedSeedSequence()38 explicit FixedSeedSequence() 39 { 40 std::fill(v.begin(), v.end(), default_seed); 41 } 42 43 template< 44 typename... TTypes, typename std::enable_if<sizeof...(TTypes) == N, int>::type = 0, 45 typename std::enable_if<Meta::all_convertible<result_type, TTypes...>::value, int>::type = 0> FixedSeedSequence(TTypes...s)46 explicit FixedSeedSequence(TTypes... s) 47 : v{ static_cast<result_type>(s)... } 48 { 49 } 50 51 template<typename TIt, typename = decltype(*std::declval<TIt&>(), ++std::declval<TIt&>(), void())> FixedSeedSequence(TIt begin,TIt end)52 explicit FixedSeedSequence(TIt begin, TIt end) 53 { 54 std::copy(begin, end, v.begin()); 55 } 56 57 template<typename TType> FixedSeedSequence(std::initializer_list<TType> il)58 explicit FixedSeedSequence(std::initializer_list<TType> il) 59 : FixedSeedSequence(il.begin(), il.end()) 60 { 61 } 62 generate(TIt begin,TIt end) const63 template<typename TIt> void generate(TIt begin, TIt end) const 64 { 65 std::copy_n(v.begin(), std::min(static_cast<size_t>(end - begin), N), begin); 66 } 67 size() const68 constexpr size_t size() const 69 { 70 return N; 71 } 72 param(TIt ob) const73 template<typename TIt> constexpr void param(TIt ob) const 74 { 75 std::copy(v.begin(), v.end(), ob); 76 } 77 78 protected: 79 std::array<result_type, N> v; 80 }; 81 82 template<typename TUIntType> struct RotateEngineState 83 { 84 using value_type = TUIntType; 85 86 value_type s0; 87 value_type s1; 88 }; 89 90 /** 91 * RotateEngine adheres to the _Named Requirement_ `RandomNumberEngine` 92 * https://en.cppreference.com/w/cpp/named_req/RandomNumberEngine 93 */ 94 template<typename TUIntType, TUIntType TX, size_t TR1, size_t TR2> 95 class RotateEngine : protected RotateEngineState<TUIntType> 96 { 97 static_assert(std::is_unsigned<TUIntType>::value, "Type must be unsigned integral."); 98 99 using RotateEngineState<TUIntType>::s0; 100 using RotateEngineState<TUIntType>::s1; 101 102 public: 103 using result_type = TUIntType; 104 using state_type = RotateEngineState<TUIntType>; 105 106 static constexpr result_type x = TX; 107 static constexpr size_t r1 = TR1; 108 static constexpr size_t r2 = TR2; 109 static constexpr result_type default_seed = 1; 110 min()111 static constexpr result_type min() 112 { 113 return std::numeric_limits<result_type>::min(); 114 } 115 max()116 static constexpr result_type max() 117 { 118 return std::numeric_limits<result_type>::max(); 119 } 120 RotateEngine(result_type seed_value=default_seed)121 explicit RotateEngine(result_type seed_value = default_seed) 122 { 123 seed(seed_value); 124 } 125 RotateEngine(RotateEngine & r)126 RotateEngine(RotateEngine& r) 127 { 128 s0 = r.s0; 129 s1 = r.s1; 130 } 131 132 template<typename TSseq, typename = typename std::enable_if<!std::is_same<TSseq, RotateEngine>::value>::type> RotateEngine(TSseq & seed_seq)133 explicit RotateEngine(TSseq& seed_seq) 134 { 135 seed(seed_seq); 136 } 137 seed(result_type s=default_seed)138 void seed(result_type s = default_seed) 139 { 140 s0 = s; 141 s1 = s; 142 } 143 seed(TSseq & seed_seq)144 template<typename TSseq> typename std::enable_if<std::is_class<TSseq>::value, void>::type seed(TSseq& seed_seq) 145 { 146 std::array<result_type, 2> s; 147 seed_seq.generate(s.begin(), s.end()); 148 s0 = s[0]; 149 s1 = s[1]; 150 } 151 discard(size_t n)152 void discard(size_t n) 153 { 154 for (; n > 0; n--) 155 (*this)(); 156 } operator ()()157 result_type operator()() 158 { 159 auto s0z = s0; 160 s0 += ror(s1 ^ x, r1); 161 s1 = ror(s0z, r2); 162 return s1; 163 } 164 state() const165 const state_type& state() const 166 { 167 return *this; 168 } 169 operator ==(const RotateEngine & lhs,const RotateEngine & rhs)170 friend bool operator==(const RotateEngine& lhs, const RotateEngine& rhs) 171 { 172 return lhs.s0 == rhs.s0 && lhs.s1 == rhs.s1; 173 } 174 operator <<(std::ostream & os,const RotateEngine & e)175 friend std::ostream& operator<<(std::ostream& os, const RotateEngine& e) 176 { 177 os << e.s0 << ' ' << e.s1; 178 return os; 179 } 180 operator >>(std::istream & is,RotateEngine & e)181 friend std::istream& operator>>(std::istream& is, RotateEngine& e) 182 { 183 is >> e.s0; 184 is >> e.s1; 185 return is; 186 } 187 }; 188 189 namespace Rct2 190 { 191 using Engine = RotateEngine<uint32_t, 0x1234567F, 7, 3>; 192 using Seed = FixedSeedSequence<2>; 193 using State = Engine::state_type; 194 } // namespace Rct2 195 } // namespace Random 196