
static char rcsid[] = "generator.c\n";


/* see chap 7 of Numerical Recipes in C for algorithms used here */


#include "generator.h"
#include "misc.h"

#include <sys/types.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <limits.h>


#define IM1    2147483563
#define IM2    2147483399
#define AM     (1.0 / IM1)
#define IMM1   (IM1 - 1)
#define IA1    40014
#define IA2    40692
#define IQ1    53668
#define IQ2    52774
#define IR1    12211
#define IR2    3791
#define NTAB   32
#define NDIV   (1 + IMM1/NTAB)
#define EPS    2.2204e-16
#define RNMX   (1.0 - EPS)


typedef struct {
  long idum1;
  long idum2;
  long iy;
  long iv[NTAB];
} SimpleGen;

typedef struct {
  struct _int_generator_struct_ outer;
  SimpleGen sg;
  double a, wid;
} UniformIntInternal;

typedef struct {
  struct _float_generator_struct_ outer;
  SimpleGen sg;
  double a, wid;
} UniformFloatInternal;

typedef struct {
  struct _float_generator_struct_ outer;
  SimpleGen sg;
  double mean, sigma;
  double gset;
  boolean iset;
} GaussianInternal;

typedef struct {
  struct _float_generator_struct_ outer;
  SimpleGen sg;
  double nilambda;
} ExponentialInternal;



static double update_sg(SimpleGen *sg)
{
  long k, j;
  double value;
  
  k = sg->idum1 / IQ1;
  sg->idum1 = IA1*(sg->idum1 - k*IQ1) - k*IR1;
  if (sg->idum1 < 0)      sg->idum1 += IM1;
  k = sg->idum2 / IQ2;
  sg->idum2 = IA2*(sg->idum2 - k*IQ2) - k*IR2;
  if (sg->idum2 < 0)      sg->idum2 += IM2;
  j = sg->iy/NDIV;
  sg->iy = sg->iv[j] - sg->idum2;
  sg->iv[j] = sg->idum1;
  if (sg->iy < 1)         sg->iy += IMM1;

  if ((value = AM*sg->iy) > RNMX)
    return RNMX;
  else
    return value;
}

static void reset_sg(SimpleGen *sg, GenSeed seed)
{
  long j, k;
  static boolean did_gen_init = FALSE;
  static SimpleGen seed_gen;
  
  
  while (seed <= GEN_RAND_SEED) {
    if (!did_gen_init) {
      long seed_gen_seed;
      struct timeb t;
      extern int ftime(struct timeb *t);

      if (ftime(&t) == -1) {
	seed_gen_seed = 1;		     /* error -- well have to use seed = 1 */
      }
      else {
	seed_gen_seed = (unsigned long) t.time ^ (unsigned long) t.millitm;
      }

      reset_sg(&seed_gen, (GenSeed) seed_gen_seed);
      did_gen_init = TRUE;
    }

    /* get new seed from the seed generator */
    seed = (long) (1.0 + LONG_MAX*update_sg(&seed_gen));
  }

  if (seed <= 0) {			     /* prevent seed == 0 */
    seed = 1;
  }

  sg->idum1 = sg->idum2 = (long) seed;
  for (j = NTAB + 7; j >= 0; j--) {	     /* load shuffle table after 8 warmups */
    k = sg->idum1 / IQ1;
    sg->idum1 = IA1*(sg->idum1 - k*IQ1) - k*IR1;
    if (sg->idum1 < 0)      sg->idum1 += IM1;
    if (j < NTAB)           sg->iv[j] = sg->idum1;
  }
  sg->iy = sg->iv[0];  
}

static long uniform_int_generator(IntGen g)
{
  UniformIntInternal *gen = (UniformIntInternal *) g;

  return gen->outer.current = (long) (gen->a + update_sg(&(gen->sg)) * gen->wid);
}

static double uniform_float_generator(FloatGen g)
{
  UniformFloatInternal *gen = (UniformFloatInternal *) g;

  return gen->outer.current = gen->a + update_sg(&(gen->sg)) * gen->wid;
}

static double gaussian_generator(FloatGen g)
{
  GaussianInternal *gen = (GaussianInternal *) g;
  double v1, v2, rsq, fac;
  extern double sqrt(double x);
  extern double log(double x);
  
  if (gen->iset) {
    gen->iset = FALSE;
    return gen->outer.current = gen->gset;
  }
  else {
    do {
      v1 = 2.0 * update_sg(&(gen->sg)) - 1.0;
      v2 = 2.0 * update_sg(&(gen->sg)) - 1.0;
      rsq = v1*v1 + v2*v2;
    } while (rsq >= 1.0 || rsq == 0.0);
    fac = sqrt(-2.0*log(rsq)/rsq);
    gen->gset = gen->mean + gen->sigma*v1*fac;
    gen->iset = TRUE;
    return gen->outer.current = gen->mean + gen->sigma*v2*fac;
  }  
}

static double exponential_generator(FloatGen g)
{
  ExponentialInternal *gen = (ExponentialInternal *) g;
  double val;
  extern double log(double x);

  do {
    val = update_sg(&(gen->sg));
  } while (val == 0.0);
  
  return gen->nilambda * log(val);	     /* mult by neg inv of lambda */
}


/************************* Exported Functions ******************************/


void *genNew_(GenType type, GenSeed seed, void *param)
{
  switch (type) {
  case UNIFORM_INT_GEN: {
    UniformIntInternal *gen;
    UniformIntParams *params = (UniformIntParams *) param;

    if ((gen = (UniformIntInternal *) malloc(sizeof(UniformIntInternal))) == NULL) {
      return NULL;
    }

    gen->outer.type = type;
    gen->outer.next = uniform_int_generator;
    gen->a = params->a;
    gen->wid = 1.0 + params->b - params->a;
    reset_sg(&(gen->sg), seed);
    genNext(&(gen->outer));

    return (void *) gen;
  }
    
  case UNIFORM_FLOAT_GEN: {
    UniformFloatInternal *gen;
    UniformFloatParams *params = (UniformFloatParams *) param;

    if ((gen = (UniformFloatInternal *) malloc(sizeof(UniformFloatInternal))) == NULL) {
      return NULL;
    }

    gen->outer.type = type;
    gen->outer.next = uniform_float_generator;
    gen->a = params->a;
    gen->wid = params->b - params->a;
    reset_sg(&(gen->sg), seed);
    genNext(&(gen->outer));

    return (void *) gen;
  }
    
  case GAUSSIAN_GEN: {
    GaussianInternal *gen;
    GaussianParams *params = (GaussianParams *) param;
   

    if ((gen = (GaussianInternal *) malloc(sizeof(GaussianInternal))) == NULL) {
      return NULL;
    }

    gen->outer.type = type;
    gen->outer.next = gaussian_generator;
    gen->mean  = params->mean;
    gen->sigma = params->sigma;
    gen->gset  = 0.0;
    gen->iset  = FALSE;
    reset_sg(&(gen->sg), seed);
    genNext(&(gen->outer));

    return (void *) gen;
  }
    
  case EXPONENTIAL_GEN: {
    ExponentialInternal *gen;
    ExponentialParams *params = (ExponentialParams *) param;

    if ((gen = (ExponentialInternal *) malloc(sizeof(ExponentialInternal))) == NULL) {
      return NULL;
    }

    gen->outer.type = type;
    gen->outer.next = exponential_generator;
    gen->nilambda   = -1.0 / params->lambda;
    reset_sg(&(gen->sg), seed);
    genNext(&(gen->outer));

    return (void *) gen;
  }
    
  default:
    panic("bad generator tag");
    return NULL;
  }
}


void genReset_(GenType type, void *gen, GenSeed seed)
{
  switch (type) {
  case UNIFORM_INT_GEN:     
    reset_sg(&(((UniformIntInternal *) gen)->sg), seed);
    genNext((IntGen) gen);
    break;
    
  case UNIFORM_FLOAT_GEN:     
    reset_sg(&(((UniformFloatInternal *) gen)->sg), seed);
    genNext((FloatGen) gen);
    break;
    
  case GAUSSIAN_GEN:    
    reset_sg(&(((GaussianInternal *) gen)->sg), seed);
    genNext((FloatGen) gen);
    break;
    
  case EXPONENTIAL_GEN:    
    reset_sg(&(((ExponentialInternal *) gen)->sg), seed);
    genNext((FloatGen) gen);
    break;

  default:
    panic("bad generator tag");
    break;
  }

  return;
}


void genFree_(GenType type, void *gen)
{
  if (type != (GenType) 0  &&  gen != (void *) NULL) {
    free(gen);
  }
}
