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