#include <math.h>
#include "util.h"
#include <time.h>

// random number seeds:
static long seeds[NUM_RANDOM_GENERATORS];

// a hack to initialize all random number seeds to 1:
class init_generators {
   public: init_generators();
};
init_generators::init_generators() {
  for (int i=0; i<NUM_RANDOM_GENERATORS; i++)  seeds[i] = 1;
}
static init_generators InitGenerators;

void opp_randomize()
{
       randseed( time(NULL) );
}

void genk_opp_randomize(int gen_nr)
{
       genk_randseed( gen_nr, time(NULL) );
}

long randseed()
{
       return seeds[0];
}

long genk_randseed(int gen_nr)
{
//       if (gen_nr<0 || gen_nr>=NUM_RANDOM_GENERATORS)
//          {opp_error("Invalid random number generator %d",gen_nr);return 0;}
       return seeds[gen_nr];
}

long randseed(long seed)
{
//       if (seed<=0 || seed>INTRAND_MAX)
//          {opp_error("Invalid random number seed %ld",seed);return 0;}

       long res = seeds[0];
       seeds[0] = seed;
       return res;
}

long genk_randseed(int gen_nr, long seed)
{
//       if (gen_nr<0 || gen_nr>=NUM_RANDOM_GENERATORS)
//          {opp_error("Invalid random number generator %d",gen_nr);return 0;}
//       if (seed<=0 || seed>INTRAND_MAX)
//          {opp_error("Invalid random number seed %ld",seed);return 0;}

       long res = seeds[gen_nr];
       seeds[gen_nr] = seed;
       return res;
}

/*------- long int pseudorandom number generator -------------
*    Range:          1 ... 2**31-2  (INTRAND_MAX=2**31-2)
*    Period length:  2**31-2
*    Global variable used:   long int theSeed : seed
*    Method:  x=(x * 7**5) mod (2**31-1)
*    To check:  if  x[0]=1  then  x[10000]=1,043,618,065
*    Required hardware:  exactly 32-bit integer aritmetics
*    Source:  Raj Jain: The Art of Computer Systems Performance Analysis
*                (John Wiley & Sons, 1991)   Pages 441-444, 455
*---------------------------------------------------------*/
long intrand()
{
     const long int a=16807, q=127773, r=2836;
     seeds[0]=a*(seeds[0]%q) - r*(seeds[0]/q);
     if (seeds[0]<=0) seeds[0]+=INTRAND_MAX+1;
     return seeds[0];
}

long genk_intrand(int gen_nr)
{
//     if (gen_nr<0 || gen_nr>=NUM_RANDOM_GENERATORS)
//        {opp_error("Invalid random number generator %d",gen_nr);return 0;}

     const long int a=16807, q=127773, r=2836;
     long& seed = seeds[gen_nr];
     seed=a*(seed%q) - r*(seed/q);
     if (seed<=0) seed+=INTRAND_MAX+1;
     return seed;
}

int testrand()
{
     long oldseed = seeds[0];
     seeds[0] = 1;
     for(int i=0; i<10000; i++) intrand();
     int good = (seeds[0]==1043618065L);
     seeds[0] = oldseed;
     return good;
}

long intrand(long r)
{
     if (r>0)
         return intrand()%r;   // good if r<<MAX_LONG
//     else if (r==0)
//         opp_error("intrand(r) called with r=0 (cannot generate 0<=x<0 integer)");
//     else
//         opp_error("intrand(r) called with negative r argument");
     return 0L;
}

long genk_intrand(int gen_nr, long r)
{
     if (r>0)
         return genk_intrand(gen_nr)%r;   // good if r<<MAX_LONG
//     else if (r==0)
//         opp_error("genk_intrand(g,r) called with r=0 (cannot generate 0<=x<0 integer)");
//     else
//         opp_error("genk_intrand(g,r) called with negative r argument");
     return 0L;
}


//==========================================================================
// Distributions:

double uniform(double a, double b)
{
    return a + dblrand() * (b-a);
}

double intuniform(double a, double b)
{
    long a1 = (long)a,
         b1 = (long)b;
    return a1 + intrand() % (b1-a1+1);
}

double exponential(double p)
{
    return -p * log(dblrand());
}

double normal(double m, double d)
{
    return m + d *  sqrt(-2.0*log(dblrand()))*cos(PI*2*dblrand());
}

double truncnormal(double m, double d)
{
    double res;
    do {
         res = m + d *  sqrt(-2.0*log(dblrand()))*cos(PI*2*dblrand());
    } while(res<0);

    return res;
}

// Now their genk_ versions:

double genk_uniform(double gen_nr, double a, double b)
{
    return a + genk_dblrand((int)gen_nr) * (b-a);
}

double genk_intuniform(double gen_nr, double a, double b)
{
    long a1 = (long)a,
         b1 = (long)b;
    return a1 + genk_intrand((int)gen_nr) % (b1-a1+1);
}

double genk_exponential(double gen_nr, double p)
{
    return -p * log(genk_dblrand((int)gen_nr));
}

double genk_normal(double gen_nr, double m, double d)
{
    return m + d * sqrt(-2.0*log(genk_dblrand((int)gen_nr))) *
                   cos(PI*2*genk_dblrand((int)gen_nr));
}

double genk_truncnormal(double gen_nr, double m, double d)
{
    double res;
    do {
         res = m + d * sqrt(-2.0*log(genk_dblrand((int)gen_nr))) *
                       cos(PI*2*genk_dblrand((int)gen_nr));
    } while(res<0);

    return res;
}

