1 //
2 // -*- C++ -*-
3 //
4 // -----------------------------------------------------------------------
5 //                          HEP Random
6 //                       --- MixMaxRng ---
7 //                     class implementation file
8 // -----------------------------------------------------------------------
9 //
10 // This file interfaces the MixMax PseudoRandom Number Generator
11 // proposed by:
12 //
13 // G.K.Savvidy and N.G.Ter-Arutyunian,
14 //   On the Monte Carlo simulation of physical systems,
15 //   J.Comput.Phys. 97, 566 (1991);
16 //   Preprint EPI-865-16-86, Yerevan, Jan. 1986
17 //   http://dx.doi.org/10.1016/0021-9991(91)90015-D
18 //
19 // K.Savvidy
20 //   "The MIXMAX random number generator"
21 //   Comp. Phys. Commun. (2015)
22 //   http://dx.doi.org/10.1016/j.cpc.2015.06.003
23 //
24 // K.Savvidy and G.Savvidy
25 //   "Spectrum and Entropy of C-systems. MIXMAX random number generator"
26 //   Chaos, Solitons & Fractals, Volume 91, (2016) pp. 33-38
27 //   http://dx.doi.org/10.1016/j.chaos.2016.05.003
28 //
29 // =======================================================================
30 // Implementation by Konstantin Savvidy - Copyright 2004-2017
31 // =======================================================================
32 
33 #include "CLHEP/Random/defs.h"
34 #include "CLHEP/Random/Random.h"
35 #include "CLHEP/Random/MixMaxRng.h"
36 #include "CLHEP/Random/engineIDulong.h"
37 #include "CLHEP/Utility/atomic_int.h"
38 
39 #include <string.h>        // for strcmp
40 #include <cmath>
41 
42 const unsigned long MASK32=0xffffffff;
43 
44 namespace CLHEP {
45 
46 namespace {
47   // Number of instances with automatic seed selection
48   CLHEP_ATOMIC_INT_TYPE numberOfEngines(0);
49 }
50 
51 static const int MarkerLen = 64; // Enough room to hold a begin or end marker.
52 
MixMaxRng()53 MixMaxRng::MixMaxRng()
54 : HepRandomEngine()
55 {
56    int numEngines = ++numberOfEngines;
57    setSeed(static_cast<long>(numEngines));
58 }
59 
MixMaxRng(long seed)60 MixMaxRng::MixMaxRng(long seed)
61 : HepRandomEngine()
62 {
63    theSeed=seed;
64    setSeed(seed);
65 }
66 
MixMaxRng(std::istream & is)67 MixMaxRng::MixMaxRng(std::istream& is)
68 : HepRandomEngine()
69 {
70    get(is);
71 }
72 
~MixMaxRng()73 MixMaxRng::~MixMaxRng()
74 {
75 }
76 
MixMaxRng(const MixMaxRng & rng)77 MixMaxRng::MixMaxRng(const MixMaxRng& rng)
78   : HepRandomEngine(rng)
79 {
80    S.V = rng.S.V;
81    S.sumtot= rng.S.sumtot;
82    S.counter= rng.S.counter;
83 }
84 
operator =(const MixMaxRng & rng)85 MixMaxRng& MixMaxRng::operator=(const MixMaxRng& rng)
86 {
87    // Check assignment to self
88    //
89    if (this == &rng)  { return *this; }
90 
91    // Copy base class data
92    //
93    HepRandomEngine::operator=(rng);
94 
95    S.V = rng.S.V;
96    S.sumtot= rng.S.sumtot;
97    S.counter= rng.S.counter;
98 
99    return *this;
100 }
101 
saveStatus(const char filename[]) const102 void MixMaxRng::saveStatus( const char filename[] ) const
103 {
104    // Create a C file-handle or an object that can accept the same output
105    FILE *fh= fopen(filename, "w");
106    if( fh )
107    {
108      int j;
109      fprintf(fh, "mixmax state, file version 1.0\n" );
110      fprintf(fh, "N=%u; V[N]={", rng_get_N() );
111      for (j=0; (j< (rng_get_N()-1) ); j++) {
112          fprintf(fh, "%llu, ", S.V[j] );
113      }
114      fprintf(fh, "%llu", S.V[rng_get_N()-1] );
115      fprintf(fh, "}; " );
116      fprintf(fh, "counter=%u; ", S.counter );
117      fprintf(fh, "sumtot=%llu;\n", S.sumtot );
118      fclose(fh);
119    }
120 }
121 
122 #define MIXMAX_ARRAY_INDEX_OUT_OF_BOUNDS   0xFF01
123 #define MIXMAX_SEED_WAS_ZERO               0xFF02
124 #define MIXMAX_ERROR_READING_STATE_FILE    0xFF03
125 #define MIXMAX_ERROR_READING_STATE_COUNTER       0xFF04
126 #define MIXMAX_ERROR_READING_STATE_CHECKSUM      0xFF05
127 
restoreStatus(const char filename[])128 void MixMaxRng::restoreStatus( const char filename[] )
129 {
130    // a function for reading the state from a file
131    FILE* fin;
132    if(  ( fin = fopen(filename, "r") ) )
133    {
134       char l=0;
135       while ( l != '{' ) { // 0x7B = "{"
136         l=fgetc(fin); // proceed until hitting opening bracket
137       }
138       ungetc(' ', fin);
139    }
140    else
141    {
142       fprintf(stderr, "mixmax -> read_state: error reading file %s\n", filename);
143       exit(MIXMAX_ERROR_READING_STATE_FILE);
144    }
145 
146    myuint_t vecVal;
147    //printf("mixmax -> read_state: starting to read state from file\n");
148    if (!fscanf(fin, "%llu", &S.V[0]) )
149    {
150      fprintf(stderr, "mixmax -> read_state: error reading file %s\n", filename);
151      exit(MIXMAX_ERROR_READING_STATE_FILE);
152    }
153 
154    int i;
155    for( i = 1; i < rng_get_N(); i++)
156    {
157      if (!fscanf(fin, ", %llu", &vecVal) )
158      {
159        fprintf(stderr, "mixmax -> read_state: error reading vector component i=%d from file %s\n", i, filename);
160        exit(MIXMAX_ERROR_READING_STATE_FILE);
161      }
162      if(  vecVal <= MixMaxRng::M61 )
163      {
164        S.V[i] = vecVal;
165      }
166      else
167      {
168        fprintf(stderr, "mixmax -> read_state: Invalid state vector value= %llu"
169                " ( must be less than %llu ) "
170                " obtained from reading file %s\n"
171                , vecVal, MixMaxRng::M61, filename);
172      }
173    }
174 
175    int counter;
176    if (!fscanf( fin, "}; counter=%i; ", &counter))
177    {
178      fprintf(stderr, "mixmax -> read_state: error reading counter from file %s\n", filename);
179      exit(MIXMAX_ERROR_READING_STATE_FILE);
180    }
181    if( counter <= rng_get_N() )
182    {
183      S.counter= counter;
184    }
185    else
186    {
187      fprintf(stderr, "mixmax -> read_state: Invalid counter = %d"
188              "  Must be 0 <= counter < %u\n" , counter, rng_get_N());
189      print_state();
190      exit(MIXMAX_ERROR_READING_STATE_COUNTER);
191    }
192    precalc();
193    myuint_t sumtot;
194    if (!fscanf( fin, "sumtot=%llu\n", &sumtot))
195    {
196      fprintf(stderr, "mixmax -> read_state: error reading checksum from file %s\n", filename);
197      exit(MIXMAX_ERROR_READING_STATE_FILE);
198    }
199 
200    if (S.sumtot != sumtot)
201    {
202      fprintf(stderr, "mixmax -> checksum error while reading state from file %s - corrupted?\n", filename);
203      exit(MIXMAX_ERROR_READING_STATE_CHECKSUM);
204    }
205    fclose(fin);
206 }
207 
208 #undef MIXMAX_ARRAY_INDEX_OUT_OF_BOUNDS
209 #undef MIXMAX_SEED_WAS_ZERO
210 #undef MIXMAX_ERROR_READING_STATE_FILE
211 #undef MIXMAX_ERROR_READING_STATE_COUNTER
212 #undef MIXMAX_ERROR_READING_STATE_CHECKSUM
213 
showStatus() const214 void MixMaxRng::showStatus() const
215 {
216    std::cout << std::endl;
217    std::cout << "------- MixMaxRng engine status -------" << std::endl;
218 
219    std::cout << " Current state vector is:" << std::endl;
220    print_state();
221    std::cout << "---------------------------------------" << std::endl;
222 }
223 
setSeed(long longSeed,int)224 void MixMaxRng::setSeed(long longSeed, int /* extraSeed */)
225 {
226    //seed_uniquestream(0,0,0,longSeed);
227    theSeed = longSeed;
228    seed_spbox(longSeed);
229 }
230 
231 //  Preferred Seeding method
232 //  the values of 'Seeds' must be valid 32-bit integers
233 //  Higher order bits will be ignored!!
234 
setSeeds(const long * Seeds,int seedNum)235 void MixMaxRng::setSeeds(const long* Seeds, int seedNum)
236 {
237    unsigned long seed0, seed1= 0, seed2= 0, seed3= 0;
238 
239    if( seedNum < 1 ) {  // Assuming at least 2 seeds in vector...
240        seed0= static_cast<unsigned long>(Seeds[0]) & MASK32;
241        seed1= static_cast<unsigned long>(Seeds[1]) & MASK32;
242    }
243    else
244    {
245      if( seedNum < 4 ) {
246        seed0= static_cast<unsigned long>(Seeds[0]) & MASK32;
247        if( seedNum > 1){ seed1= static_cast<unsigned long>(Seeds[1]) & MASK32; }
248        if( seedNum > 2){ seed2= static_cast<unsigned long>(Seeds[2]) & MASK32; }
249      }
250      if( seedNum >= 4 ) {
251        seed0= static_cast<unsigned long>(Seeds[0]) & MASK32;
252        seed1= static_cast<unsigned long>(Seeds[1]) & MASK32;
253        seed2= static_cast<unsigned long>(Seeds[2]) & MASK32;
254        seed3= static_cast<unsigned long>(Seeds[3]) & MASK32;
255      }
256    }
257    theSeed = Seeds[0];
258    theSeeds = Seeds;
259    seed_uniquestream(seed3, seed2, seed1, seed0);
260 }
261 
engineName()262 std::string MixMaxRng::engineName()
263 {
264    return "MixMaxRng";
265 }
266 
rng_get_N()267 constexpr int MixMaxRng::rng_get_N()
268 {
269    return N;
270 }
271 
rng_get_SPECIAL()272 constexpr long long int MixMaxRng::rng_get_SPECIAL()
273 {
274    return SPECIAL;
275 }
276 
rng_get_SPECIALMUL()277 constexpr int MixMaxRng::rng_get_SPECIALMUL()
278 {
279    return SPECIALMUL;
280 }
281 
generate(int i)282 double MixMaxRng::generate(int i)
283 {
284    S.counter++;
285 #if defined(__clang__) || defined(__llvm__)
286    return INV_M61*static_cast<double>(S.V[i]);
287 #elif defined(__GNUC__) && (__GNUC__ < 7) && (!defined(__ICC)) && defined(__x86_64__) && defined(__SSE2_MATH__)
288    int64_t Z=S.V[i];
289    double F=0.0;
290    //#warning Using the inline assembler
291     /* using SSE inline assemly to zero the xmm register, just before int64 -> double conversion,
292        not necessary in GCC-5 or better, but huge penalty on earlier compilers
293     */
294    __asm__ __volatile__(  "pxor %0, %0;"
295                           "cvtsi2sdq %1, %0;"
296                           :"=x"(F)
297                           :"r"(Z)
298                        );
299    return F*INV_M61;
300 #else
301   //#warning other method
302    return convert1double(S.V[i]); //get_next_float_packbits();
303 #endif
304 }
305 
iterate()306 double MixMaxRng::iterate()
307 {
308    myuint_t* Y=S.V.data();
309    myuint_t  tempP, tempV;
310    Y[0] = ( tempV = S.sumtot);
311    myuint_t sumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
312    tempP = 0;              // will keep a partial sum of all old elements
313    myuint_t tempPO;
314    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[1] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[1]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
315    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[2] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[2]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
316    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[3] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[3]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
317    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[4] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[4]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
318    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[5] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[5]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
319    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[6] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[6]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
320    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[7] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[7]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
321    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[8] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[8]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
322    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[9] ); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[9]  = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
323    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[10]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[10] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
324    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[11]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[11] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
325    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[12]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[12] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
326    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[13]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[13] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
327    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[14]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[14] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
328    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[15]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[15] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
329    tempPO = MULWU(tempP); tempP = modadd(tempP, Y[16]); tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); Y[16] = tempV; sumtot += tempV; if (sumtot < tempV) {ovflow++;};
330    S.sumtot = MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(sumtot) + (ovflow <<3 ));
331 
332    S.counter=2;
333    return double(S.V[1])*INV_M61;
334 }
335 
flatArray(const int size,double * vect)336 void MixMaxRng::flatArray(const int size, double* vect )
337 {
338    // fill_array( S, size, arrayDbl );
339    for (int i=0; i<size; ++i) { vect[i] = flat(); }
340 }
341 
operator double()342 MixMaxRng::operator double()
343 {
344   return flat();
345 }
346 
operator float()347 MixMaxRng::operator float()
348 {
349   return float( flat() );
350 }
351 
operator unsigned int()352 MixMaxRng::operator unsigned int()
353 {
354    return static_cast<unsigned int>(get_next());
355    // clhep_get_next returns a 64-bit integer, of which the lower 61 bits
356    // are random and upper 3 bits are zero
357 }
358 
put(std::ostream & os) const359 std::ostream & MixMaxRng::put ( std::ostream& os ) const
360 {
361    char beginMarker[] = "MixMaxRng-begin";
362    char endMarker[]   = "MixMaxRng-end";
363 
364    int pr = os.precision(24);
365    os << beginMarker << " ";
366    os << theSeed << "\n";
367    for (int i=0; i<rng_get_N(); ++i) {
368       os <<  S.V[i] << "\n";
369    }
370    os << S.counter << "\n";
371    os << S.sumtot << "\n";
372    os << endMarker << "\n";
373    os.precision(pr);
374    return os;
375 }
376 
put() const377 std::vector<unsigned long> MixMaxRng::put () const
378 {
379    std::vector<unsigned long> v;
380    v.push_back (engineIDulong<MixMaxRng>());
381    for (int i=0; i<rng_get_N(); ++i)
382    {
383      v.push_back(static_cast<unsigned long>(S.V[i] & MASK32));
384        // little-ended order on all platforms
385      v.push_back(static_cast<unsigned long>(S.V[i] >> 32  ));
386        // pack uint64 into a data structure which is 32-bit on some platforms
387    }
388    v.push_back(static_cast<unsigned long>(S.counter));
389    v.push_back(static_cast<unsigned long>(S.sumtot & MASK32));
390    v.push_back(static_cast<unsigned long>(S.sumtot >> 32));
391    return v;
392 }
393 
get(std::istream & is)394 std::istream & MixMaxRng::get  ( std::istream& is)
395 {
396    char beginMarker [MarkerLen];
397    is >> std::ws;
398    is.width(MarkerLen);  // causes the next read to the char* to be <=
399                          // that many bytes, INCLUDING A TERMINATION \0
400                          // (Stroustrup, section 21.3.2)
401    is >> beginMarker;
402    if (strcmp(beginMarker,"MixMaxRng-begin")) {
403       is.clear(std::ios::badbit | is.rdstate());
404       std::cerr << "\nInput stream mispositioned or"
405                 << "\nMixMaxRng state description missing or"
406                 << "\nwrong engine type found." << std::endl;
407       return is;
408    }
409    return getState(is);
410 }
411 
beginTag()412 std::string MixMaxRng::beginTag ()
413 {
414    return "MixMaxRng-begin";
415 }
416 
getState(std::istream & is)417 std::istream &  MixMaxRng::getState ( std::istream& is )
418 {
419    char endMarker[MarkerLen];
420    is >> theSeed;
421    for (int i=0; i<rng_get_N(); ++i)  is >> S.V[i];
422    is >> S.counter;
423    myuint_t checksum;
424    is >> checksum;
425    is >> std::ws;
426    is.width(MarkerLen);
427    is >> endMarker;
428    if (strcmp(endMarker,"MixMaxRng-end")) {
429        is.clear(std::ios::badbit | is.rdstate());
430        std::cerr << "\nMixMaxRng state description incomplete."
431                  << "\nInput stream is probably mispositioned now.\n";
432        return is;
433    }
434    if ( S.counter < 0 || S.counter > rng_get_N() ) {
435        std::cerr << "\nMixMaxRng::getState(): "
436                  << "vector read wrong value of counter from file!"
437                  << "\nInput stream is probably mispositioned now.\n";
438        return is;
439    }
440    precalc();
441    if ( checksum != S.sumtot) {
442        std::cerr << "\nMixMaxRng::getState(): "
443                  << "checksum disagrees with value stored in file!"
444                  << "\nInput stream is probably mispositioned now.\n";
445        return is;
446    }
447    return is;
448 }
449 
get(const std::vector<unsigned long> & v)450 bool MixMaxRng::get (const std::vector<unsigned long> & v)
451 {
452    if ((v[0] & 0xffffffffUL) != engineIDulong<MixMaxRng>()) {
453      std::cerr <<
454         "\nMixMaxRng::get(): vector has wrong ID word - state unchanged\n";
455      return false;
456    }
457    return getState(v);
458 }
459 
getState(const std::vector<unsigned long> & v)460 bool MixMaxRng::getState (const std::vector<unsigned long> & v)
461 {
462    if (v.size() != VECTOR_STATE_SIZE ) {
463      std::cerr <<
464         "\nMixMaxRng::getState(): vector has wrong length - state unchanged\n";
465      return false;
466    }
467    for (int i=1; i<2*rng_get_N() ; i=i+2) {
468      S.V[i/2]= ( (v[i] & MASK32) | ( (myuint_t)(v[i+1]) << 32 ) );
469      // unpack from a data structure which is 32-bit on some platforms
470    }
471    S.counter = v[2*rng_get_N()+1];
472    precalc();
473    if ( ( (v[2*rng_get_N()+2] & MASK32)
474         | ( (myuint_t)(v[2*rng_get_N()+3]) << 32 ) ) != S.sumtot) {
475      std::cerr << "\nMixMaxRng::getState(): vector has wrong checksum!"
476                << "\nInput vector is probably mispositioned now.\n";
477      return false;
478    }
479    return true;
480 }
481 
MOD_MULSPEC(myuint_t k)482 myuint_t MixMaxRng ::MOD_MULSPEC(myuint_t k)
483 {
484    switch (N)
485    {
486       case 17:
487           return 0;
488           break;
489       case 8:
490           return 0;
491           break;
492       case 240:
493           return fmodmulM61( 0, SPECIAL , (k) );
494           break;
495       default:
496           std::cerr << "MIXMAX ERROR: " << "Disallowed value of parameter N\n";
497           std::terminate();
498           break;
499    }
500 }
501 
MULWU(myuint_t k)502 myuint_t MixMaxRng::MULWU (myuint_t k)
503 {
504    return (( (k)<<(SPECIALMUL) & M61) ^ ( (k) >> (BITS-SPECIALMUL))  );
505 }
506 
iterate_raw_vec(myuint_t * Y,myuint_t sumtotOld)507 myuint_t MixMaxRng::iterate_raw_vec(myuint_t* Y, myuint_t sumtotOld)
508 {
509    // operates with a raw vector, uses known sum of elements of Y
510    int i;
511 
512    myuint_t  tempP, tempV;
513    Y[0] = ( tempV = sumtotOld);
514    myuint_t sumtot = Y[0], ovflow = 0; // will keep a running sum of all new elements
515    tempP = 0;              // will keep a partial sum of all old elements
516    for (i=1; (i<N); i++)
517    {
518      myuint_t tempPO = MULWU(tempP);
519      tempP = modadd(tempP, Y[i]);
520      tempV = MIXMAX_MOD_MERSENNE(tempV+tempP+tempPO); // new Y[i] = old Y[i] + old partial * m
521      Y[i] = tempV;
522      sumtot += tempV; if (sumtot < tempV) {ovflow++;}
523    }
524    return MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(sumtot) + (ovflow <<3 ));
525 }
526 
get_next()527 myuint_t MixMaxRng::get_next()
528 {
529    int i;
530    i=S.counter;
531 
532    if ((i<=(N-1)) )
533    {
534      S.counter++;
535      return S.V[i];
536    }
537    else
538    {
539      S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot);
540      S.counter=2;
541      return S.V[1];
542    }
543 }
544 
precalc()545 myuint_t MixMaxRng::precalc()
546 {
547    int i;
548    myuint_t temp;
549    temp = 0;
550    for (i=0; i < N; i++){
551      temp = MIXMAX_MOD_MERSENNE(temp + S.V[i]);
552    }
553    S.sumtot = temp;
554    return temp;
555 }
556 
get_next_float_packbits()557 double MixMaxRng::get_next_float_packbits()
558 {
559    myuint_t Z=get_next();
560    return convert1double(Z);
561 }
562 
seed_vielbein(unsigned int index)563 void MixMaxRng::seed_vielbein(unsigned int index)
564 {
565    int i;
566    if (index<N)
567    {
568      for (i=0; i < N; i++){
569        S.V[i] = 0;
570      }
571      S.V[index] = 1;
572    }
573    else
574    {
575      std::terminate();
576    }
577    S.counter = N;  // set the counter to N if iteration should happen right away
578    S.sumtot = 1;
579 }
580 
581 #define MIXMAX_SEED_WAS_ZERO 0xFF02
582 
seed_spbox(myuint_t seed)583 void MixMaxRng::seed_spbox(myuint_t seed)
584 {
585    // a 64-bit LCG from Knuth line 26, in combination with a bit swap is used to seed
586 
587    const myuint_t MULT64=6364136223846793005ULL;
588    int i;
589 
590    myuint_t sumtot=0,ovflow=0;
591    if (seed == 0)
592    {
593      fprintf(stderr, " try seeding with nonzero seed next time!\n");
594      exit(MIXMAX_SEED_WAS_ZERO);
595    }
596 
597    myuint_t l = seed;
598 
599    for (i=0; i < N; i++){
600      l*=MULT64; l = (l << 32) ^ (l>>32);
601      S.V[i] = l & M61;
602      sumtot += S.V[(i)]; if (sumtot < S.V[(i)]) {ovflow++;}
603    }
604    S.counter = N;  // set the counter to N if iteration should happen right away
605    S.sumtot = MIXMAX_MOD_MERSENNE(MIXMAX_MOD_MERSENNE(sumtot) + (ovflow <<3 ));
606 }
607 
608 #undef MIXMAX_SEED_WAS_ZERO
609 
seed_uniquestream(myID_t clusterID,myID_t machineID,myID_t runID,myID_t streamID)610 void MixMaxRng::seed_uniquestream( myID_t clusterID, myID_t machineID, myID_t runID, myID_t  streamID )
611 {
612    seed_vielbein(0);
613    S.sumtot = apply_bigskip(S.V.data(), S.V.data(),  clusterID,  machineID,  runID,   streamID );
614    S.counter = 1;
615 }
616 
apply_bigskip(myuint_t * Vout,myuint_t * Vin,myID_t clusterID,myID_t machineID,myID_t runID,myID_t streamID)617 myuint_t MixMaxRng::apply_bigskip( myuint_t* Vout, myuint_t* Vin, myID_t clusterID, myID_t machineID, myID_t runID, myID_t  streamID )
618 {
619   /*
620     makes a derived state vector, Vout, from the mother state vector Vin
621     by skipping a large number of steps, determined by the given seeding ID's
622 
623     it is mathematically guaranteed that the substreams derived in this way from the SAME (!!!) Vin will not collide provided
624     1) at least one bit of ID is different
625     2) less than 10^100 numbers are drawn from the stream
626     (this is good enough : a single CPU will not exceed this in the lifetime of the universe, 10^19 sec,
627     even if it had a clock cycle of Planch time, 10^44 Hz )
628 
629     Caution: never apply this to a derived vector, just choose some mother vector Vin, for example the unit vector by seed_vielbein(X,0),
630     and use it in all your runs, just change runID to get completely nonoverlapping streams of random numbers on a different day.
631 
632     clusterID and machineID are provided for the benefit of large organizations who wish to ensure that a simulation
633     which is running in parallel on a large number of  clusters and machines will have non-colliding source of random numbers.
634 
635     did i repeat it enough times? the non-collision guarantee is absolute, not probabilistic
636 
637    */
638 
639    const myuint_t skipMat17[128][17] =
640    #include "CLHEP/Random/mixmax_skip_N17.icc"
641    ;
642 
643    const myuint_t* skipMat[128];
644    for (int i=0; i<128; i++) { skipMat[i] = skipMat17[i];}
645 
646    myID_t IDvec[4] = {streamID, runID, machineID, clusterID};
647    int r,i,j,  IDindex;
648    myID_t id;
649    myuint_t Y[N], cum[N];
650    myuint_t coeff;
651    myuint_t* rowPtr;
652    myuint_t sumtot=0;
653 
654    for (i=0; i<N; i++) { Y[i] = Vin[i]; sumtot = modadd( sumtot, Vin[i]); } ;
655    for (IDindex=0; IDindex<4; IDindex++)
656    { // go from lower order to higher order ID
657      id=IDvec[IDindex];
658      //printf("now doing ID at level %d, with ID = %d\n", IDindex, id);
659      r = 0;
660      while (id)
661      {
662        if (id & 1)
663        {
664          rowPtr = (myuint_t*)skipMat[r + IDindex*8*sizeof(myID_t)];
665          for (i=0; i<N; i++){ cum[i] = 0; }
666          for (j=0; j<N; j++)
667          { // j is lag, enumerates terms of the poly
668            // for zero lag Y is already given
669            coeff = rowPtr[j]; // same coeff for all i
670            for (i =0; i<N; i++){
671              cum[i] =  fmodmulM61( cum[i], coeff ,  Y[i] ) ;
672            }
673            sumtot = iterate_raw_vec(Y, sumtot);
674          }
675          sumtot=0;
676          for (i=0; i<N; i++){ Y[i] = cum[i]; sumtot = modadd( sumtot, cum[i]); } ;
677        }
678        id = (id >> 1); r++; // bring up the r-th bit in the ID
679      }
680    }
681    sumtot=0;
682    for (i=0; i<N; i++){ Vout[i] = Y[i]; sumtot = modadd( sumtot, Y[i]); }
683    // returns sumtot, and copy the vector over to Vout
684    return (sumtot) ;
685 }
686 
687 #if defined(__x86_64__)
mod128(__uint128_t s)688   myuint_t MixMaxRng::mod128(__uint128_t s)
689   {
690     myuint_t s1;
691     s1 = ( (  ((myuint_t)s)&M61 ) + (  ((myuint_t)(s>>64)) * 8 ) + ( ((myuint_t)s) >>BITS) );
692     return	MIXMAX_MOD_MERSENNE(s1);
693   }
fmodmulM61(myuint_t cum,myuint_t a,myuint_t b)694   myuint_t MixMaxRng::fmodmulM61(myuint_t cum, myuint_t a, myuint_t b)
695   {
696     __uint128_t temp;
697     temp = (__uint128_t)a*(__uint128_t)b + cum;
698     return mod128(temp);
699   }
700 #else // on all other platforms, including 32-bit linux, PPC and PPC64, ARM and all Windows
fmodmulM61(myuint_t cum,myuint_t s,myuint_t a)701   myuint_t MixMaxRng::fmodmulM61(myuint_t cum, myuint_t s, myuint_t a)
702   {
703     const myuint_t MASK32=0xFFFFFFFFULL;
704     myuint_t o,ph,pl,ah,al;
705     o=(s)*a;
706     ph = ((s)>>32);
707     pl = (s) & MASK32;
708     ah = a>>32;
709     al = a & MASK32;
710     o = (o & M61) + ((ph*ah)<<3) + ((ah*pl+al*ph + ((al*pl)>>32))>>29) ;
711     o += cum;
712     o = (o & M61) + ((o>>61));
713     return o;
714   }
715 #endif
716 
modadd(myuint_t foo,myuint_t bar)717 myuint_t MixMaxRng::modadd(myuint_t foo, myuint_t bar)
718 {
719 #if defined(__x86_64__) && defined(__GNUC__) && (!defined(__ICC))
720    //#warning Using assembler routine in modadd
721    myuint_t out;
722    /* Assembler trick suggested by Andrzej Görlich     */
723    __asm__ ("addq %2, %0; "
724             "btrq $61, %0; "
725             "adcq $0, %0; "
726             :"=r"(out)
727             :"0"(foo), "r"(bar)
728            );
729    return out;
730 #else
731    return MIXMAX_MOD_MERSENNE(foo+bar);
732 #endif
733 }
734 
print_state() const735 void MixMaxRng::print_state() const
736 {
737    int j;
738    std::cout << "mixmax state, file version 1.0\n";
739    std::cout << "N=" << rng_get_N() << "; V[N]={";
740    for (j=0; (j< (rng_get_N()-1) ); j++) {
741      std::cout << S.V[j] << ", ";
742    }
743    std::cout << S.V[rng_get_N()-1];
744    std::cout << "}; ";
745    std::cout << "counter= " << S.counter;
746    std::cout << "sumtot= " << S.sumtot << "\n";
747 }
748 
Branch()749 MixMaxRng MixMaxRng::Branch()
750 {
751    S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot); S.counter = 1;
752    MixMaxRng tmp=*this;
753    tmp.BranchInplace(0); // daughter id
754    return tmp;
755 }
756 
BranchInplace(int id)757 void MixMaxRng::BranchInplace(int id)
758 {
759    // Dont forget to iterate the mother, when branching the daughter, or else will have collisions!
760    // a 64-bit LCG from Knuth line 26, is used to mangle a vector component
761    constexpr myuint_t MULT64=6364136223846793005ULL;
762    myuint_t tmp=S.V[id];
763    S.V[1] *= MULT64; S.V[id] &= M61;
764    S.sumtot = MIXMAX_MOD_MERSENNE( S.sumtot + S.V[id] - tmp + M61);
765    S.sumtot = iterate_raw_vec(S.V.data(), S.sumtot);// printf("iterating!\n");
766    S.counter = 1;
767 }
768 
769 }  // namespace CLHEP
770