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