//
// new 08-19-11 (jdl).  Mersenne Twister, made into a class with some member functions I want.
//
//
// Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura, All rights reserved.                          
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
//   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the 
//      following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 
//      following disclaimer in the documentation and/or other materials provided with the distribution.
//
//   3. The names of its contributors may not be used to endorse or promote products derived from this software 
//      without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
// PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
//
//
//
//
//
//


#ifndef MT64_H
#define MT64_H

#include <vector>

typedef unsigned char UCHAR;
// typedef   signed char SCHAR;


#define NN 312
#define MM 156
#define MATRIX_A 0xB5026F5AA96619E9ULL
#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */
#define LM 0x7FFFFFFFULL /* Least significant 31 bits */
#define BITS_PER_ULL (64)

#define MAX_ULL  (0xFFFFFFFFFFFFFFFFULL)




class MT64 {
private:
   unsigned long long m_mt[NN]; // The array for the state vector.  unsigned long long is 64-bit (8 byte) unsigned integer.
   int                m_mti;    // mti==NN+1 means mt[NN] is not initialized
   int                m_nCallsSinceInit;  // 11-20-15
   static int         m_nAllCalls;        // for all instances of the class

public:
   MT64(bool bNoInit=false);
   MT64(unsigned long long seed);
   MT64(const unsigned long long init_key[], unsigned long long key_length);
   MT64(const MT64 &rs);
   ~MT64();

   const MT64 &operator=(const MT64 &rs);


   void init_genrand64(unsigned long long seed); // initializes mt[NN] with a seed 
   void init_genrand32(unsigned int seed);      // my addition: initializes from a ULONG.
   void init_by_array64(const unsigned long long init_key[], unsigned long long key_length); // initialize by an array with array-length.  init_key is the array for initializing keys; key_length is its length
   void init_by_otherMT64(const MT64 &otherMT64);
   unsigned long long genrand64_int64(void); // generates a random number on [0, 2^64-1]-interval
   long long genrand64_int63(void);          // generates a random number on [0, 2^63-1]-interval
   unsigned int genrand64_int32(void);      // generates a random number on [0, 2^32-1]-interval
   int genrand64_int31(void);               // generates a random number on [0, 2^31-1]-interval
   double genrand64_real1(void);    // generates a random number on [0,1]-real-interval
   double genrand64_real2(void);    // generates a random number on [0,1)-real-interval
   double genrand64_real3(void);    // generates a random number on (0,1)-real-interval
   double genrand64_real4(void) {return 1.0 - genrand64_real2();} // generates a random number on (0,1]-real-interval
   double realcc() {return genrand64_real1();}   // closed closed
   double realco() {return genrand64_real2();}   // closed open
   double realoc() {return genrand64_real4();}   // open   closed
   double realoo() {return genrand64_real3();}   // open   open
   unsigned int rand_n_quick(unsigned int zeroToMeMinusOne);    // gives slightly uneven results
   unsigned int rand_n_fancy(unsigned int zeroToMeMinusOne);    // gives uniform results
   inline int randSign() {return (int)rand_n_quick(2) * 2  - 1;}  // -1 or +1

   static int  rescrambleVector(std::vector<unsigned long long> &inOut, int nCycle=1);
   static void bytesToVector(const unsigned char *cData, int datLen, std::vector<unsigned long long> &fillMe);
   static void bytesToVector(const unsigned char *cData, int datLen, std::vector<unsigned long long> &fillMe, std::vector<unsigned long long> &fillM2, int nCycle);
// static int getAutoSeed(std::vector<unsigned long long> &fillMe, unsigned int *pTime=NULL, unsigned int *pTGT=NULL);  // will fill 10 of these...
   static int getAutoSeed(std::vector<unsigned long long> &fillMe);  // will fill 10 of these...
   int getSeedFromState(std::vector<unsigned long long> &fillMe) const;   // 10 guys, from inner state.
   int randFromPwd(void);           // combines getSeedFromPwd() and init_by_array64().
   int scramUnscram(UCHAR *cInOut, int nDo, bool bConserv=false);
   int shuffleMe(MT64 *pFromMe=NULL);
   int nCallsSinceInit() const {return m_nCallsSinceInit;}
   static int nAllCalls() {return m_nAllCalls;}
   double NextGaussian();
}; // class MT64 



#endif // define MT64_H

