Charades
clcg4.h
1 #ifndef _CLCG4_H_
2 #define _CLCG4_H_
3 
4 #include "pup.h"
5 
6 #include <cstdint>
7 #include <iostream>
8 
14 class clcg4 {
15 private:
16  enum SeedType {
17  InitialSeed, LastSeed, NewSeed
18  };
19 
20  struct tw_rng {
21  int32_t m[4];
22  int32_t a[4];
23  int32_t aw[4];
24  int32_t avw[4];
25 
26  int64_t b[4]; // Used to make the RNG reversible
27 
28  int32_t seed[4];
29  };
30  static tw_rng rng; // One RNG per process, once intialized, it doesn't change
31 
32  int32_t Ig[4];
33  int32_t Lg[4];
34  int32_t Cg[4];
35 
36  uint64_t _count = 0;
37 
38  static int64_t find_b(int64_t a, int64_t k, int64_t m) {
39  int64_t sqrs[32];
40  sqrs[0] = a;
41  for (int i = 1; i < 32; i++) {
42  sqrs[i] =(sqrs[i - 1] * sqrs[i - 1]) % m;
43  }
44 
45  int64_t power_of_2 = 1;
46  int64_t b = 1;
47  for (int i = 0; i < 32; i++) {
48  if (!(power_of_2 & k)) {
49  sqrs[i] = 1;
50  }
51  b =(b * sqrs[i]) % m;
52  power_of_2 = power_of_2 * 2;
53  }
54 
55  return b;
56  }
57 
58  static constexpr int32_t H = 32768;
59  static int32_t mult_mod_M(int32_t s, int32_t t, int32_t M) {
60  int32_t R, S0, S1, q, qh, rh, k;
61 
62  if (s < 0) { s += M; }
63  if (t < 0) { t += M; }
64 
65  if (s < H) {
66  S0 = s;
67  R = 0;
68  } else {
69  S1 = s / H;
70  S0 = s - H * S1;
71  qh = M / H;
72  rh = M - H * qh;
73 
74  if (S1 >= H) {
75  S1 -= H;
76  k = t / qh;
77  R = H *(t - k * qh) - k * rh;
78  while(R < 0) { R += M; }
79  } else {
80  R = 0;
81  }
82 
83  if (S1 != 0) {
84  q = M / S1;
85  k = t / q;
86  R -= k *(M - S1 * q);
87  if (R > 0) { R -= M; }
88  R += S1 *(t - k * q);
89  while(R < 0) { R += M; }
90  }
91  k = R / qh;
92  R = H *(R - k * qh) - k * rh;
93  while(R < 0) { R += M; }
94  }
95 
96  if (S0 != 0) {
97  q = M / S0;
98  k = t / q;
99  R -= k *(M - S0 * q);
100  if (R > 0) { R -= M; }
101  R += S0 *(t - k * q);
102  while(R < 0) { R += M; }
103  }
104 
105  return R;
106  }
107 
108  void init_generator(SeedType Where) {
109  for (int j = 0; j < 4; j++) {
110  switch(Where) {
111  case InitialSeed:
112  Lg[j] = Ig[j];
113  break;
114  case NewSeed:
115  Lg[j] = mult_mod_M(rng.aw[j], Lg[j], rng.m[j]);
116  break;
117  case LastSeed:
118  break;
119  }
120  Cg[j] = Lg[j];
121  }
122  }
123 
124 public:
125  using result_type = double;
126 
127  static void init(int v, int w) {
128  int32_t default_seed[4] = {11111111, 22222222, 33333333, 44444444};
129  init(v, w, default_seed);
130  }
131 
132  static void init(int v, int w, int32_t init_seed[4]) {
133  // Init m
134  rng.m[0] = 2147483647;
135  rng.m[1] = 2147483543;
136  rng.m[2] = 2147483423;
137  rng.m[3] = 2147483323;
138 
139  // Init a
140  rng.a[0] = 45991;
141  rng.a[1] = 207707;
142  rng.a[2] = 138556;
143  rng.a[3] = 49689;
144 
145  for (int j = 0; j < 4; j++) {
146  // Init aw
147  rng.aw[j] = rng.a[j];
148  for (int i = 0; i < w; i++) {
149  rng.aw[j] = mult_mod_M(rng.aw[j], rng.aw[j], rng.m[j]);
150  }
151 
152  // Init avw
153  rng.avw[j] = rng.aw[j];
154  for (int i = 0; i < v; i++) {
155  rng.avw[j] = mult_mod_M(rng.avw[j], rng.avw[j], rng.m[j]);
156  }
157 
158  // Init b
159  rng.b[j] = find_b(rng.a[j], rng.m[j] - 2, rng.m[j]);
160 
161  // Init seed
162  rng.seed[j] = init_seed[j];
163  }
164  }
165 
166  clcg4() { seed(); }
167  clcg4(int32_t s[4]) { seed(s); }
168  clcg4(uint64_t id) { seed(id); }
169 
170  void seed() {
171  seed(42);
172  }
173 
174  void seed(int32_t s[4]) {
175  for (int j = 0; j < 4; j++)
176  Ig[j] = s[j];
177 
178  init_generator(InitialSeed);
179  }
180 
181  void seed(uint64_t id) {
182  uint64_t mask_bit = 1;
183 
184  int32_t Ig_t[4];
185  int32_t avw_t[4];
186 
187  int positions = ((sizeof(uint64_t)) * 8) - 1;
188 
189  //seed for zero
190  for (int j = 0; j < 4; j++) {
191  Ig_t[j] = rng.seed[j];
192  }
193 
194  mask_bit <<= positions;
195 
196  do {
197  if (id & mask_bit) {
198  for (int j = 0; j < 4; j++) {
199  avw_t[j] = rng.avw[j];
200 
201  // exponentiate modulus
202  for (int i = 0; i < positions; i++) {
203  avw_t[j] = mult_mod_M(avw_t[j], avw_t[j], rng.m[j]);
204  }
205 
206  Ig_t[j] = mult_mod_M(avw_t[j], Ig_t[j], rng.m[j]);
207  }
208  }
209 
210  mask_bit >>= 1;
211  positions--;
212  } while(positions > 0);
213 
214  if (id % 2) {
215  for (int j = 0; j < 4; j++) {
216  Ig_t[j] = mult_mod_M(rng.avw[j], Ig_t[j], rng.m[j]);
217  }
218  }
219 
220  for (int j = 0; j < 4; j++) {
221  Ig[j] = Ig_t[j];
222  }
223 
224  init_generator(InitialSeed);
225  }
226 
227  static constexpr result_type min() noexcept { return 0.0; }
228  static constexpr result_type max() noexcept { return 1.0; }
229 
230  constexpr result_type operator() () noexcept {
231  int32_t s = Cg[0];
232  int32_t k = s / 46693;
233  double u = 0.0;
234 
235  s = 45991 *(s - k * 46693) - k * 25884;
236  if (s < 0)
237  s = s + 2147483647;
238  Cg[0] = s;
239  u = u + 4.65661287524579692e-10 * s;
240 
241  s = Cg[1];
242  k = s / 10339;
243  s = 207707 *(s - k * 10339) - k * 870;
244  if (s < 0)
245  s = s + 2147483543;
246  Cg[1] = s;
247  u = u - 4.65661310075985993e-10 * s;
248  if (u < 0)
249  u = u + 1.0;
250 
251  s = Cg[2];
252  k = s / 15499;
253  s = 138556 *(s - k * 15499) - k * 3979;
254  if (s < 0.0)
255  s = s + 2147483423;
256  Cg[2] = s;
257  u = u + 4.65661336096842131e-10 * s;
258  if (u >= 1.0)
259  u = u - 1.0;
260 
261  s = Cg[3];
262  k = s / 43218;
263  s = 49689 *(s - k * 43218) - k * 24121;
264  if (s < 0)
265  s = s + 2147483323;
266  Cg[3] = s;
267  u = u - 4.65661357780891134e-10 * s;
268  if (u < 0)
269  u = u + 1.0;
270 
271  _count++;
272  return u;
273  }
274 
275  void prev() noexcept {
276  double u = 0.0;
277 
278  int32_t s = Cg[0];
279  s = (rng.b[0] * s) % rng.m[0];
280  Cg[0] = s;
281  u = u + 4.65661287524579692e-10 * s;
282 
283  s = Cg[1];
284  s = (rng.b[1] * s) % rng.m[1];
285  Cg[1] = s;
286  u = u - 4.65661310075985993e-10 * s;
287  if (u < 0) { u = u + 1.0; }
288 
289  s = Cg[2];
290  s = (rng.b[2] * s) % rng.m[2];
291  Cg[2] = s;
292  u = u + 4.65661336096842131e-10 * s;
293  if (u >= 1.0) { u = u - 1.0; }
294 
295  s = Cg[3];
296  s = (rng.b[3] * s) % rng.m[3];
297  Cg[3] = s;
298  u = u - 4.65661357780891134e-10 * s;
299  if (u < 0) { u = u + 1.0; }
300 
301  _count--;
302  }
303 
304  constexpr uint64_t count() noexcept { return _count; }
305 
306  void pup(PUP::er& p) {
307  PUParray(p, Ig, 4);
308  PUParray(p, Lg, 4);
309  PUParray(p, Cg, 4);
310  }
311 
312  constexpr void discard(uint64_t n) noexcept {
313  for (uint64_t i = 0; i < n; ++i) {
314  operator () ();
315  }
316  }
317 
318  constexpr bool operator == (const clcg4& rhs) noexcept {
319  return Ig[0] == rhs.Ig[0] && Ig[1] == rhs.Ig[1] && Ig[2] == rhs.Ig[2] && Ig[3] == rhs.Ig[3] &&
320  Lg[0] == rhs.Lg[0] && Lg[1] == rhs.Lg[1] && Lg[2] == rhs.Lg[2] && Lg[3] == rhs.Lg[3] &&
321  Cg[0] == rhs.Cg[0] && Cg[1] == rhs.Cg[1] && Cg[2] == rhs.Cg[2] && Cg[3] == rhs.Cg[3];
322  }
323 
324  constexpr bool operator != (const clcg4& rhs) noexcept {
325  return !(*this == rhs);
326  }
327 
328  friend std::ostream& operator << (std::ostream& os, const clcg4& rhs) {
329  os << rhs.Ig[0] << ' ' << rhs.Ig[1] << ' ' << rhs.Ig[2] << ' ' << rhs.Ig[3] << ' ';
330  os << rhs.Lg[0] << ' ' << rhs.Lg[1] << ' ' << rhs.Lg[2] << ' ' << rhs.Lg[3] << ' ';
331  os << rhs.Cg[0] << ' ' << rhs.Cg[1] << ' ' << rhs.Cg[2] << ' ' << rhs.Cg[3];
332  return os;
333  }
334 
335  friend auto operator >> (std::istream& is, clcg4& rhs) -> std::istream& {
336  is >> rhs.Ig[0];
337  is >> rhs.Ig[1];
338  is >> rhs.Ig[2];
339  is >> rhs.Ig[3];
340  is >> rhs.Lg[0];
341  is >> rhs.Lg[1];
342  is >> rhs.Lg[2];
343  is >> rhs.Lg[3];
344  is >> rhs.Cg[0];
345  is >> rhs.Cg[1];
346  is >> rhs.Cg[2];
347  is >> rhs.Cg[3];
348  return is;
349  }
350 };
351 
352 #endif
Definition: clcg4.h:20
Implementation of a CLCG4 random number generator, based on the reversible implementation from ROSS (...
Definition: clcg4.h:14