SUNphi  1.0
Sitmo.hpp
Go to the documentation of this file.
1 #ifndef SITMO_HPP
2 #define SITMO_HPP
3 
4 /// \file Sitmo.hpp
5 ///
6 /// \brief Implements the SITMO random number generator
7 ///
8 /// Copyright (c) 2012-2016 M.A. (Thijs) van den Berg, http://sitmo.com/
9 ///
10 /// Use, modification and distribution are subject to the MIT Software License.
11 ///
12 /// The MIT License (MIT)
13 /// Permission is hereby granted, free of charge, to any person obtaining a copy
14 /// of this software and associated documentation files (the "Software"), to deal
15 /// in the Software without restriction, including without limitation the rights
16 /// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 /// copies of the Software, and to permit persons to whom the Software is
18 /// furnished to do so, subject to the following conditions:
19 ///
20 /// The above copyright notice and this permission notice shall be included in
21 /// all copies or substantial portions of the Software.
22 ///
23 /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 /// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 /// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 /// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 /// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 /// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 /// THE SOFTWARE.
30 
31 #include <array>
32 #include <cstdint>
33 #include <iostream>
34 
35 #include <metaprogramming/LoopUnroll.hpp>
36 
37 /// Maximal value of the prng output
38 #define SITMO_RAND_MAX sitmo::prng_engine::max()
39 
40 namespace SUNphi
41 {
42  /// Class encapsulating the Sitmo random generator
43  class Sitmo
44  {
45  /// Key
47 
48  /// State (counter)
50 
51  /// Cipher output: 4*64 bit=256 bit output
52  union
53  {
54  std::array<uint64_t,4> state;
55  std::array<uint32_t,8> output;
56  } ciphered;
57 
58  /// Output chunk counter: determines wihch 32 of the 256 random bits in cipheredState is returned
59  unsigned short iChunk;
60 
61  /// Encrypts the counter, to set the state
62  void encryptCounter(std::array<uint64_t,4>& outputState) ///< Output state
63  const
64  {
65  /// Copy of the state, to be modififed
66  uint64_t work[4];
67  for(int i=0;i<4;i++)
68  work[i]=
69  state[i];
70 
71  loopUnroll<0,5>([&work,this](const int j)
72  {
73  loopUnroll<0,2>([j,&work,this](const int i)
74  {
75  constexpr uint64_t mk[2][2]=
76  {{14,16},{25,33}};
77 
78  uint64_t& x0=
79  work[2*i];
80  uint64_t& x1=
81  work[(2*i+1)%4];
82  const uint64_t& rx=
83  mk[j%2][i];
84 
85  x1+=
86  key[(2*i+1+j)%5]+((i==1)?j:0);
87  x0+=
88  x1+key[(2*i+j)%5];
89  x1=
90  (x1<<rx)|(x1>>(64-rx));
91  x1^=
92  x0;
93  });
94  loopUnroll<0,3>([j,&work](const int l)
95  {
96  loopUnroll<0,2>([l,j,&work](const int i)
97  {
98  constexpr uint64_t m[2][3][2]=
99  {{{52,57},{23,40},{5,37}},
100  {{46,12},{58,22},{32,32}}};
101 
102  uint64_t& x0=
103  work[2*i];
104  uint64_t& x1=
105  work[(2*i+((l%2==0)?3:1))%4];
106  const uint64_t& rx=
107  m[j%2][l][i];
108 
109  x0+=
110  x1;
111  x1=
112  (x1<<rx)|(x1>>(64-rx));
113  x1^=
114  x0;
115  });
116  });
117  });
118 
119  // Copy the result to the output
120  for(int i=0;i<4;i++)
121  outputState[i]=
122  work[i]+key[i];
123 
124  // Increment entry 3
125  outputState[3]+=
126  5;
127  }
128 
129  public:
130 
131  /// Maximal output
132  static constexpr uint32_t max=
133  0xFFFFFFFF;
134 
135  /// Copy constructor
136  Sitmo(const Sitmo& oth) =///< Other engine
137  default;
138 
139  /// Construct from a seed
140  Sitmo(const uint32_t& s=0)
141  {
142  seed(s);
143  }
144 
145  /// Sets the key
146  void setKey(const uint32_t& k0=0,
147  const uint32_t& k1=0,
148  const uint32_t& k2=0,
149  const uint32_t& k3=0)
150  {
151  key[0]=
152  k0;
153  key[1]=
154  k1;
155  key[2]=
156  k2;
157  key[3]=
158  k3;
159  key[4]=
160  0x1BD11BDAA9FC1A22^key[0]^key[1]^key[2]^key[3];
161  }
162 
163  /// Init using the passed seed
164  void seed(const uint32_t& s=0)
165  {
166  for(int i=0;i<4;i++)
167  {
168  key[i]=0;
169  state[i]=0;
170  }
171 
172  setKey(s);
173  iChunk=0;
174 
175  encryptCounter(ciphered.state);
176  }
177 
178  // // req: 26.5.1.4 Random number engine requirements, p.908 table 117, row 8
179  // // Advances e’s state ei to ei+1 = TA(ei) and returns GA(ei).
180  // uint32_t operator()()
181  // {
182  // // can we return a value from the current block?
183  // if (iChunk < 8) {
184  // unsigned short cipheredState_index = iChunk >> 1;
185  // iChunk++;
186  // if (iChunk&1)
187  // return cipheredState[cipheredState_index] & 0xFFFFFFFF;
188  // else
189  // return cipheredState[cipheredState_index] >> 32;
190  // }
191 
192  // // generate a new block and return the first 32 bits
193  // (*this)++;
194  // encryptCounter(cipheredState);
195  // iChunk = 1; // the next call
196  // return cipheredState[0] & 0xFFFFFFFF; // this call
197  // }
198 
199  /// Returns one of the 8 chunks
201  {
202  if(iChunk>=8)
203  {
204  // Generate a new block and return the first 32 bits
205  (*this)++;
206  encryptCounter(ciphered.state);
207 
208  // Reset the pointer to the beginnning of the chunk
209  iChunk=
210  0;
211  }
212 
213  return
214  ciphered.output[iChunk++];
215  }
216 
217  // -------------------------------------------------
218  // misc
219  // -------------------------------------------------
220 
221  /// Advances e’s state ei to ei+z
222  void discard(uint64_t z)
223  {
224  // check if we stay in the current block
225  // cast to unsigned int to prevent comparison warning
226  if (z < (uint64_t)(8 - iChunk)) {
227  iChunk += static_cast<unsigned short>(z);
228  return;
229  }
230 
231  // we will have to generate a new block...
232  z -= (8 - iChunk); // discard the remainder of the current blok
233  iChunk = z % 8; // set the pointer in the correct element in the new block
234  z -= iChunk; // update z
235  z >>= 3; // the number of buffers is elements/8
236  ++z; // and one more because we crossed the buffer line
237  (*this)+=z;
238  encryptCounter(ciphered.state);
239  }
240 
241  /// Output
242  template<class CharT, class Traits>
243  friend std::basic_ostream<CharT,Traits>&
245  for (unsigned short i=0; i<4; ++i)
246  os << s.key[i] << ' ' << s.state[i] << ' ' << s.ciphered.state[i] << ' ';
247  os << s.iChunk;
248  return os;
249  }
250 
251  /// Input
252  template<class CharT, class Traits>
253  friend std::basic_istream<CharT,Traits>&
255  for (unsigned short i=0; i<4; ++i)
256  is >> s.key[i] >> s.state[i] >> s.ciphered.state[i];
257  is >> s.iChunk;
258  return is;
259  }
260 
261  /// Check that the two generators are the same
262  bool operator==(const Sitmo& y)
263  {
264  if (iChunk != y.iChunk) return false;
265  for (unsigned short i=0; i<4; ++i) {
266  if (state[i] != y.state[i]) return false;
267  if (key[i] != y.key[i]) return false;
268  if (ciphered.state[i] != y.ciphered.state[i]) return false;
269  }
270  return true;
271  }
272 
273  /// Check that the two generators are different
274  bool operator!=(const Sitmo& y)
275  {
276  return !(*this == y);
277  }
278 
279  /// Set the counter
280  void set_counter(uint64_t s0=0, uint64_t s1=0, uint64_t s2=0, uint64_t s3=0, unsigned short o_counter=0)
281  {
282  state[0] = s0;
283  state[1] = s1;
284  state[2] = s2;
285  state[3] = s3;
286  iChunk = o_counter % 8;
287  encryptCounter(ciphered.state);
288  }
289 
290  /// Increments the counter by a given amount
291  Sitmo& operator+=(const uint64_t z) ///< Amount to increment
292  {
293  /// Take note of old value, to check for overflow
294  const uint64_t old0=
295  state[0];
296 
297  // Increment
298  state[0]+=
299  z;
300 
301  // Overflow check
302  if(state[0]<=old0)
303  {
304  /// Digit to increment
305  int iDigit=
306  1;
307 
308  // Carry over
309  do state[iDigit]++;
310  while(state[iDigit++]==0 and iDigit<4);
311  }
312 
313  return
314  *this;
315  }
316 
317  /// Unitary increment
318  Sitmo& operator++(int)
319  {
320  return
321  *this+=
322  1;
323  }
324  };
325 }
326 
327 #endif
Sitmo & operator++(int)
Unitary increment.
Definition: Sitmo.hpp:318
Sitmo & operator+=(const uint64_t z)
Increments the counter by a given amount.
Definition: Sitmo.hpp:291
void setKey(const uint32_t &k0=0, const uint32_t &k1=0, const uint32_t &k2=0, const uint32_t &k3=0)
Sets the key.
Definition: Sitmo.hpp:146
bool operator==(const Sitmo &y)
Check that the two generators are the same.
Definition: Sitmo.hpp:262
void seed(const uint32_t &s=0)
Init using the passed seed.
Definition: Sitmo.hpp:164
Sitmo(const Sitmo &oth)
Copy constructor.
friend std::basic_istream< CharT, Traits > & operator>>(std::basic_istream< CharT, Traits > &is, Sitmo &s)
Input.
Definition: Sitmo.hpp:254
void set_counter(uint64_t s0=0, uint64_t s1=0, uint64_t s2=0, uint64_t s3=0, unsigned short o_counter=0)
Set the counter.
Definition: Sitmo.hpp:280
void discard(uint64_t z)
Advances e’s state ei to ei+z.
Definition: Sitmo.hpp:222
void encryptCounter(std::array< uint64_t, 4 > &outputState) const
Encrypts the counter, to set the state.
Definition: Sitmo.hpp:62
Sitmo(const uint32_t &s=0)
Construct from a seed.
Definition: Sitmo.hpp:140
uint32_t operator()()
Returns one of the 8 chunks.
Definition: Sitmo.hpp:200
bool operator!=(const Sitmo &y)
Check that the two generators are different.
Definition: Sitmo.hpp:274
unsigned short iChunk
Output chunk counter: determines wihch 32 of the 256 random bits in cipheredState is returned...
Definition: Sitmo.hpp:59