CORSIKA  @c8_version@
The framework to simulate particle cascades for astroparticle physics
Squares4_128.hpp
1 /*
2  * (c) Copyright 2021 CORSIKA Project, corsika-project@lists.kit.edu
3  *
4  * This software is distributed under the terms of the GNU General Public
5  * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
6  * the license.
7  */
8 
9 /*
10  * Squares4_128.hpp
11  *
12  * Created on: 25/02/2021
13  * Author: Antonio Augusto Alves Junior
14  */
15 
16 #pragma once
17 
18 #include <stdint.h>
19 #include "SquaresKeys.hpp"
20 #include "uint128.hpp"
21 
22 namespace random_iterator {
23 
24  namespace detail {
25 
26  /*
27  * Three round counter-based middle square
28  *
29  * squares3 - returns a 32-bit unsigned int [0,0xffffffff]
30  *
31  *
32  * Three rounds of squaring are performed and the result is returned.
33  * For the first two rounds, the result is rotated right 32 bits.
34  * This places the random data in the best position for the next round.
35  * y = ctr*key or z = (ctr+1)*key is added on each round. For keys
36  * generated by the key utility, either ctr*key or (ctr+1)*key will
37  * have non-zero digits. This improves randomization and also provides
38  * for a uniform output.
39  *
40  * Note: The squares RNG was based on ideas derived from Middle Square
41  * Weyl Sequence RNG. One of the ideas was to obtain uniformity by adding
42  * the Weyl sequence after squaring. Richard P. Brent (creator of the
43  * xorgens RNG) suggested this method. It turns out that adding ctr*key
44  * is equivalent. Brent's idea provides the basis for uniformity in this
45  * generator.
46  *
47  * Implementation of the algorithm authored by Bernard Widynski and
48  * described in https://arxiv.org/pdf/2004.06278v2.pdf
49  */
50 
51  class Squares4_128 {
52 
53  public:
54  typedef uint128_t init_type;
55  typedef uint128_t state_type;
56  typedef uint128_t seed_type;
57  typedef uint64_t advance_type;
58  typedef uint64_t result_type;
59 
60  Squares4_128() = delete;
61 
62  Squares4_128(size_t s, uint32_t stream = 0)
63  : state_(uint64_t(stream) << 32, 0)
64  , seed_(seed_type{splitmix<size_t>(s)}) {}
65 
66  Squares4_128(Squares4_128 const& other)
67  : state_(other.getState())
68  , seed_(other.getSeed()) {}
69 
70  inline Squares4_128& operator=(Squares4_128 const& other) {
71  if (this == &other) return *this;
72 
73  state_ = other.getState();
74  seed_ = other.getSeed();
75 
76  return *this;
77  }
78 
79  inline result_type operator()(void) {
80  uint128_t x, y, z;
81 
82  y = x = seed_ * state_;
83  z = y + seed_;
84 
85  x = x * x + y;
86  x = x.rotate_right(); /* round 1 */
87 
88  x = x * x + z;
89  x = x.rotate_right(); /* round 2 */
90 
91  x = x * x + y;
92  x = x.rotate_right(); /* round 1 */
93 
94  ++state_; /* advance state */
95 
96  return (x * x + z).upper(); /* round 3 */
97  }
98 
99  inline void discard(advance_type n) { state_ += n; }
100 
101  inline seed_type getSeed() const { return seed_; }
102 
103  inline void setSeed(seed_type seed) { seed_ = seed; }
104 
105  inline state_type getState() const { return state_; }
106 
107  inline void setState(state_type state) { state_ = state; }
108 
109  inline static uint64_t generateSeed(size_t i) { return keys[i]; }
110 
111  friend inline std::ostream& operator<<(std::ostream& os, const Squares4_128& be) {
112  return os << "state: " << be.getState() << " seed: " << be.getSeed();
113  }
114 
115  static constexpr result_type min() { return 0; }
116 
117  static constexpr result_type max() {
118  return std::numeric_limits<result_type>::max();
119  }
120 
121  private:
122  state_type state_;
123  seed_type seed_;
124  };
125 
126  } // namespace detail
127 
128 } // namespace random_iterator