/*************************************************************************/
/*************************************************************************/
/* Parallel Modified Additive Lagged Fibonacci Generator                 */
/*                                                                       */ 
/* Modified by: Ashok Srinivasan,                                        */
/*            NCSA, University of Illinois, Urbana-Champaign             */
/* E-Mail: ashoks@ncsa.uiuc.edu                                          */
/*                                                                       */
/* Based on the Implementation by:                                       */
/*  Steven A. Cuccaro and Daniel V. Pryor,                               */
/*            IDA/Center for Computing Sciences (CCS)                    */
/* E-Mail: cuccaro@super.org      pryor@super.org                        */
/*                                                                       */ 
/* Copyright 1996 September 3, United States Government as Represented   */
/* by the Director, National Security Agency. All rights reserved.       */
/*                                                                       */
/* Disclaimer 1: NCSA expressly disclaims any and all warranties, expressed*/
/* or implied, concerning the enclosed software.  The intent in sharing  */
/* this software is to promote the productive interchange of ideas       */
/* throughout the research community. All software is furnished on an    */
/* "as is" basis. No further updates to this software should be          */
/* expected. Although this may occur, no commitment exists. The authors  */
/* certainly invite your comments as well as the reporting of any bugs.  */
/* NCSA cannot commit that any or all bugs will be fixed.                */
/*                                                                       */
/* Disclaimer 2: CCS expressly disclaims any and all warranties, expressed */
/* or implied, concerning the enclosed software. This software was       */
/* developed at CCS for use in internal research. The intent in sharing  */
/* this software is to promote the productive interchange of ideas       */
/* throughout the research community. All software is furnished on an    */
/* "as is" basis. No further updates to this software should be          */
/* expected. Although this may occur, no commitment exists. The authors  */
/* certainly invite your comments as well as the reporting of any bugs.  */
/* CCS cannot commit that any or all bugs will be fixed.                 */
/*************************************************************************/
/*************************************************************************/

/*************************************************************************/
/*      This version has been modified to use two integer-based additive */
/*      lagged-Fibonacci generators to produce integer, float and double */
/*      values. The lagged-Fibonacci generators each have 31 bits of     */
/*      precision (after the bit fixed by the canonical form of the      */
/*      generator is removed), 31-bit values are generated by XORing     */
/*      the values after one has been shifted left one bit. The floating */
/*      point value is formed by dividing the integer by 1.e+32 (the     */
/*      lsb's will be dropped from the mantissa to make room for the     */
/*      exponent), and two of these integer values in sequence are used  */
/*      to get the necessary precision for the double value.             */
/*                                                                       */
/*      This method has the advantage that the generators pass fairly    */
/*      strict randomness tests, including the Birthday Spacings test    */
/*      that additive lagged-Fibonacci generators are well known to      */
/*      fail. The disadvantage is the additional time needed to do the   */
/*      division explicitly, which was avoided in previous versions.     */
/*      (As the division is by powers of 2, the user might well consider */
/*      making machine-specific versions of this code to insert the bits */
/*      into the appropriate places and avoid the problem entirely.)     */
/*************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#define NDEBUG
#include <assert.h>
#include "interface.h"
#include "lfg.h"
#include "memory.h"
#include "store.h"

#define init_rng lfg_init_rng
#define get_rn_int lfg_get_rn_int
#define get_rn_flt lfg_get_rn_flt
#define get_rn_dbl lfg_get_rn_dbl
#define spawn_rng lfg_spawn_rng
#define get_seed_rng lfg_get_seed_rng
#define free_rng lfg_free_rng
#define pack_rng lfg_pack_rng
#define unpack_rng lfg_unpack_rng
#define print_rng lfg_print_rng

#define MAX_STREAMS lfg_MAX_STREAMS
#define NGENS lfg_NGENS
#define valid lfg_valid

/*#define PRINT_GEN*/

/*      BITS_IN_INT_GEN is the log_2 of the modulus of the generator     */
/*           for portability this is set to 32, but can be modified;     */
/*           if modified, make sure INT_MOD_MASK can still be calculated */
#define BITS_IN_INT_GEN 32

/*      INT_MOD_MASK is used to perform modular arithmetic - specifying  */
/*           this value compensates for different sized words on         */
/*           different architectures                                     */
/*      FLT_MULT is used in converting to float and double values; the   */
/*           odd form is due to a compiler glitch on our CM-5, which     */
/*	     caused (0.5/(unsigned)(1<<31)) to be negative.              */
#if (BITS_IN_INT_GEN==32)
#define INT_MOD_MASK 0xffffffff
#define FLT_MULT (0.25/(unsigned)(1<<30))
#else
#define INT_MOD_MASK ((unsigned)(1<<BITS_IN_INT_GEN)-1)
#define FLT_MULT (1.0/(1<<BITS_IN_INT_GEN))
#endif
/*      INT_MASK is used to mask out the part of the generator which     */
/*           is not in the canonical form; it should be                  */
/*           2^{BITS_IN_INT_GEN-1}-1                                     */
#define INT_MASK ((unsigned)INT_MOD_MASK>>1)
/*      MAX_BIT_INT is the largest bit position allowed in the index     */
/*           of the node - it equals BITS_IN_INT_GEN - 2                 */
#define MAX_BIT_INT (BITS_IN_INT_GEN-2)
/*      INTX2_MASK is used in calculation of the node numbers            */
#define INTX2_MASK ((1<<MAX_BIT_INT)-1)
 
/*      RUNUP keeps certain generators from looking too similar in the   */
/*          first few words output                                       */
#define RUNUP (2*BITS_IN_INT_GEN)

/*      GS0 gives a more "random" distribution of generators when the    */
/*      user uses small integers as seeds                                */
#define GS0 0x372f05ac
#define TOOMANY "generator has branched maximum number of times;\nindependence of generators no longer guaranteed"

#define VERSION "00"
/*** Name for Generator ***/
#define GENTYPE VERSION "Additive Lagged Fibonacci Generator"

/*************************************************************************/
/*************************************************************************/
/*                  STRUCTURES AND GLOBAL DATA                           */
/*************************************************************************/
/*************************************************************************/

struct rngen {
	  int rng_type;
      char *gentype;
      unsigned *si;      /* sets next branch seed  */
      unsigned *r0;      /* pointer to the even generator */
      unsigned *r1;      /* pointer to the odd generator */
      int stream_number;
      int hptr;          /* integer pointer into fill */
      int seed;
      int init_seed;
      int lval, kval;
      int param;
};

int MAX_STREAMS=0x7fffffff;

struct vstruct {
      int L;
      int K;
      int LSBS;     /* number of least significant bits that are 1 */
      int first;    /* the first seed whose LSB is 1 */
};

const struct vstruct valid[] = { {1279,861,1,233}, {17,5,1,10}, {31,6,1,2},
{55,24,1,11}, {63,31,1,14}, {127,97,1,21}, {521,353,1,100},
{521,168,1,83}, {607,334,1,166}, {607,273,1,105}, {1279,418,1,208}};

#define NPARAMS 11

int gseed=0,lval=0,kval=0;
int NGENS = 0;

/*************************************************************************/
/*************************************************************************/
/*                    ERROR PRINTING FUNCTION                            */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
static void errprint(char *level, char *routine, char *error)
#else
static void errprint(level, routine, error)
char *level,*routine,*error;
#endif
{
      fprintf(stderr,"%s from %s: %s\n",level,routine,error);
}

/*************************************************************************/
/*************************************************************************/
/*            ROUTINES USED TO CREATE GENERATOR FILLS                    */
/*************************************************************************/
/*************************************************************************/

/**************************/
/* function bitcnt:       */
/**************************/
#ifdef __STDC__
static int bitcnt( int x)
#else
static int bitcnt(x)
int x;
#endif
{
  unsigned i=0,y;

  for (y=(unsigned)x; y; y &= (y-1) ) 
    i++;

  return(i);
}

/**************************/
/* function advance_reg:  */
/**************************/
#ifdef __STDC__
static void advance_reg(int *reg_fill)
#else
static void advance_reg(reg_fill)
int *reg_fill;
#endif
{
/*      the register steps according to the primitive polynomial         */
/*           (64,4,3,1,0); each call steps register 64 times             */
/*      we use two words to represent the register to allow for integer  */
/*           size of 32 bits                                             */

#ifdef __STDC__
  const int mask = 0x1b;
  int adv_64[4][2];
#else
  int mask = 0x1b;
  int adv_64[4][2];
#endif

  int i,new_fill[2];
  unsigned temp;

  adv_64[0][0] = 0xb0000000;
  adv_64[0][1] = 0x1b;
  adv_64[1][0] = 0x60000000;
  adv_64[1][1] = 0x2d;
  adv_64[2][0] = 0xc0000000;
  adv_64[2][1] = 0x5a;
  adv_64[3][0] = 0x80000000;
  adv_64[3][1] = 0xaf;
  new_fill[1] = new_fill[0] = 0;
  temp = mask<<27;

  for (i=27;i>=0;i--) 
  {
    new_fill[0] = (new_fill[0]<<1) | (1&bitcnt(reg_fill[0]&temp));
    new_fill[1] = (new_fill[1]<<1) | (1&bitcnt(reg_fill[1]&temp));
    temp >>= 1;
  }

  for (i=28;i<32;i++) 
  {
    temp = bitcnt(reg_fill[0]&(mask<<i));
    temp ^= bitcnt(reg_fill[1]&(mask>>(32-i)));
    new_fill[0] |= (1&temp)<<i;
    temp = bitcnt(reg_fill[0]&adv_64[i-28][0]);
    temp ^= bitcnt(reg_fill[1]&adv_64[i-28][1]);
    new_fill[1] |= (1&temp)<<i;
  }

  reg_fill[0] = new_fill[0];
  reg_fill[1] = new_fill[1];
}

/**************************/
/*   function get_fill:   */
/**************************/

#ifdef __STDC__
static int get_fill( unsigned *n, unsigned *r, int param, unsigned seed)
#else
static int get_fill(n,r, param, seed)
unsigned *n, *r, seed;
int param;
#endif
{
  int i,j,k,temp[2], length;

  length = valid[param].L;
  
/*      initialize the shift register with the node number XORed with    */
/*           the global seed                                             */
/*      fill the shift register with two copies of this number           */
/*           except when equal to zero                                   */
  temp[1] = temp[0] = n[0]^seed;
  if (!temp[0])
    temp[0] = GS0;

/*      advance the shift register some                                  */
  advance_reg(temp);
  advance_reg(temp);

/*      the first word in the generator is defined by the 31 LSBs of the */
/*           node number                                                 */
  r[0] = (INT_MASK&n[0])<<1;
/*      the generator is filled with the lower 31 bits of the shift      */
/*           register at each time, shifted up to make room for the bits */
/*           defining the canonical form; the node number is XORed into  */
/*           the fill to make the generators unique                      */
  for (i=1;i<length-1;i++) 
  {
    advance_reg(temp);
    r[i] = (INT_MASK&(temp[0]^n[i]))<<1;
  }
  r[length-1] = 0;
/*      the canonical form for the LSB is instituted here                */
  k = valid[param].first + valid[param].LSBS;

  for (j=valid[param].first;j<k;j++)
    r[j] |= 1;

  return(0);
}

/*************************************************************************/
/*************************************************************************/
/*            SI_DOUBLE: updates index for next spawning                 */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
static void si_double(unsigned *a,  unsigned *b, int length)
#else
static void si_double(a,b, length)
unsigned *a,*b;
int length;
#endif
{
  int i;

  if (b[length-2]&(1<<MAX_BIT_INT))
    errprint("WARNING","si_double",TOOMANY);
  a[length-2] = (INTX2_MASK&b[length-2])<<1;

  for (i=length-3;i>=0;i--) 
  {
    if (b[i]&(1<<MAX_BIT_INT)) 
      a[i+1]++;
    a[i] = (INTX2_MASK&b[i])<<1;
  }
}

/*************************************************************************/
/*************************************************************************/
/*            GET_RN: returns generated random number                    */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
int get_rn_int(int *genptr)
#else
int get_rn_int(genptr)
int *genptr;
#endif
/*      returns value put into new position                              */
{
        unsigned new_val,*r0,*r1;
        int hptr,lptr,*hp = &((struct rngen *)genptr)->hptr;
	int lval, kval;

	lval = ((struct rngen *)genptr)->lval;
	kval = ((struct rngen *)genptr)->kval;
        r0 = ((struct rngen *)genptr)->r0;
        r1 = ((struct rngen *)genptr)->r1;
        hptr = *hp;
        lptr = hptr + kval;
        if (lptr>=lval) lptr -= lval;
/*    INT_MOD_MASK causes arithmetic to be modular when integer size is  */
/*         different from generator modulus                              */
        r0[hptr] = INT_MOD_MASK&(r0[hptr] + r0[lptr]);
        r1[hptr] = INT_MOD_MASK&(r1[hptr] + r1[lptr]);
        new_val = (r1[hptr]&(~1)) ^ (r0[hptr]>>1);
        if (--hptr < 0) hptr = lval - 1; /* skip an element in the sequence */
        if (--lptr < 0) lptr = lval - 1;
        r0[hptr] = INT_MOD_MASK&(r0[hptr] + r0[lptr]);
        r1[hptr] = INT_MOD_MASK&(r1[hptr] + r1[lptr]);
        *hp = (--hptr < 0) ? lval-1 : hptr;

  
        return (new_val>>1);
}

#ifdef __STDC__
float get_rn_flt(int *genptr)
#else
float get_rn_flt(genptr)
int *genptr;
#endif
/*      returns value put into new position                              */
{
  unsigned long new_val; /* this cannot be unsigned int due to a bug in the SGI compiler */
  unsigned  *r0,*r1;	
  int hptr,lptr,*hp = &((struct rngen *)genptr)->hptr;
  int lval, kval;
	
  lval = ((struct rngen *)genptr)->lval;
  kval = ((struct rngen *)genptr)->kval;
  r0 = ((struct rngen *)genptr)->r0;
  r1 = ((struct rngen *)genptr)->r1;
  hptr = *hp;
  lptr = hptr + kval;
  if (lptr>=lval) lptr -= lval;
/*    INT_MOD_MASK causes arithmetic to be modular when integer size is  */
/*         different from generator modulus                              */
  r0[hptr] = INT_MOD_MASK&(r0[hptr] + r0[lptr]);
  r1[hptr] = INT_MOD_MASK&(r1[hptr] + r1[lptr]);
  new_val = (r1[hptr]&(~1)) ^ (r0[hptr]>>1);
  if (--hptr < 0) hptr = lval - 1; /* skip an element in the sequence */
  if (--lptr < 0) lptr = lval - 1;
  r0[hptr] = INT_MOD_MASK&(r0[hptr] + r0[lptr]);
  r1[hptr] = INT_MOD_MASK&(r1[hptr] + r1[lptr]);
  *hp = (--hptr<0) ? lval-1 : hptr;

        return (new_val*FLT_MULT);
} 

#ifdef __STDC__
double get_rn_dbl(int *genptr)
#else
double get_rn_dbl(genptr)
int *genptr;
#endif
/*      returns value put into new position                              */
{
  unsigned *r0,*r1;
  unsigned long temp1,temp2; /* Due to a bug in the SGI compiler, this should not be unsigned int */
  int hptr,lptr,*hp = &((struct rngen *)genptr)->hptr;
  double new_val;
  int lval, kval;
	
  lval = ((struct rngen *)genptr)->lval;
  kval = ((struct rngen *)genptr)->kval;
  r0 = ((struct rngen *)genptr)->r0;
  r1 = ((struct rngen *)genptr)->r1;
  hptr = *hp;
  lptr = hptr + kval;
  if (lptr>=lval) lptr -= lval;
  /*    INT_MOD_MASK causes arithmetic to be modular when integer size is  */
  /*         different from generator modulus                              */
  r0[hptr] = INT_MOD_MASK&(r0[hptr] + r0[lptr]);
  r1[hptr] = INT_MOD_MASK&(r1[hptr] + r1[lptr]);
  temp1 = (r1[hptr]&(~1)) ^ (r0[hptr]>>1);
  if (--hptr < 0) hptr = lval - 1;
  if (--lptr < 0) lptr = lval - 1;
  r0[hptr] = INT_MOD_MASK&(r0[hptr] + r0[lptr]);
  r1[hptr] = INT_MOD_MASK&(r1[hptr] + r1[lptr]);
  temp2 = (r1[hptr]&(~1)) ^ (r0[hptr]>>1);
  *hp = (--hptr < 0) ? lval-1 : hptr;

        new_val = ((unsigned int) temp2*(double)FLT_MULT + (unsigned int) temp1)*FLT_MULT;
        return (new_val);
}

/*************************************************************************/
/*************************************************************************/
/*            INITIALIZE: starts the whole thing going                   */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
static int **initialize(int rng_type, int ngen, int param, unsigned seed, unsigned *nstart, unsigned initseed)
#else
static int **initialize(rng_type, ngen,param, seed,nstart, initseed)
int rng_type,  ngen, param;
unsigned *nstart, seed, initseed;
#endif
{
  int i,j,k,l,*order, length;
  struct rngen **q;
  unsigned *nindex;

  length = valid[param].L;
  
/*      allocate memory for node number and fill of each generator       */
  order = (int *) mymalloc(ngen*sizeof(int));
  q = (struct rngen **) mymalloc(ngen*sizeof(struct rngen *));
  if (q == NULL || order == NULL) 
    return NULL;

  for (i=0;i<ngen;i++) 
  {
    q[i] = (struct rngen *) mymalloc(sizeof(struct rngen));
    if (q[i] == NULL) 
      return NULL;

    q[i]->rng_type = rng_type;
    q[i]->hptr = length - 1;
    q[i]->si = (unsigned *) mymalloc((length-1)*sizeof(unsigned));
    q[i]->r0 = (unsigned *) mymalloc(length*sizeof(unsigned));
    q[i]->r1 = (unsigned *) mymalloc(length*sizeof(unsigned));
    q[i]->lval = length;
    q[i]->kval = valid[param].K;
    q[i]->param = param;
    q[i]->seed = seed;
    q[i]->init_seed = initseed;
    q[i]->gentype = GENTYPE;
    
    if (q[i]->r1 == NULL || q[i]->r0 == NULL || q[i]->si == NULL) 
      return NULL;
  }
/*      specify register fills and node number arrays                    */
/*      do fills in tree fashion so that all fills branch from index     */
/*           contained in nstart array                                   */
  q[0]->stream_number = nstart[0];
  si_double(q[0]->si,nstart,length);
  get_fill(q[0]->si,q[0]->r0,param,seed);
  q[0]->si[0]++;
  get_fill(q[0]->si,q[0]->r1,param,seed);

  i = 1;
  order[0] = 0;
  if (ngen>1) 
    while (1) 
    {
      l = i;
      for (k=0;k<l;k++) 
      {
	nindex = q[order[k]]->si;
	q[i]->stream_number = nindex[0];
	si_double(nindex,nindex, length);
	for (j=0;j<length-1;j++) 
	  q[i]->si[j] = nindex[j];
	get_fill(q[i]->si,q[i]->r0,param,seed);
	q[i]->si[0]++;
	get_fill(q[i]->si,q[i]->r1,param,seed);
	if (ngen == ++i) 
	  break;
      }
      
      if (ngen == i) 
	break;
                
      for (k=l-1;k>0;k--) 
      {
	order[2*k+1] = l+k;
	order[2*k] = order[k];
      }
      order[1] = l;
    }

  free(order);

  for (i=ngen-1;i>=0;i--) 
  {
    k = 0;
    for (j=1;j<lval-1;j++)
      if (q[i]->si[j]) 
	k = 1;
    if (!k) 
      break;
    for (j=0;j<length*RUNUP;j++)
      get_rn_int((int *)(q[i]));
  }

  while (i>=0)
  {
    for (j=0;j<4*length;j++)
      get_rn_int((int *)(q[i]));
    i--;
  }   

  return((int **)q);
}

/*************************************************************************/
/*************************************************************************/
/*            INIT_RNG's: user interface to start things off             */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
int *init_rng(int rng_type, int gennum,  int total_gen,  int seed, int param)
#else
int *init_rng(int rng_type,gennum,total_gen,seed,param)
int rng_type,gennum,param,seed,total_gen;
#endif
{
  int doexit=0,i,k, length;
  int **p=NULL, *rng;
  unsigned *nstart=NULL,*si;

  
/*      gives back one generator (node gennum) with updated spawning     */
/*      info; should be called total_gen times, with different value     */
/*      of gennum in [0,total_gen) each call                             */

/*      check values of gennum and total_gen                             */

  if (total_gen <= 0) /* check if total_gen is valid */
  {
    total_gen = 1;
    errprint("WARNING","init_rng","Total_gen <= 0. Default value of 1 used for total_gen");
  }

  if (gennum >= MAX_STREAMS) /* check if gen_num is valid    */
    fprintf(stderr,"WARNING - init_rng: gennum: %d > maximum number of independent streams: %d\n\tIndependence of streams cannot be guranteed.\n",
	    gennum, MAX_STREAMS); 

  if (gennum < 0 || gennum >= total_gen) /* check if gen_num is valid */
  {
    errprint("ERROR","init_rng","gennum out of range. "); 
    return (int *) NULL;
  }

  seed &= 0x7fffffff;		/* Only 31 LSB of seed considered */
  
  if (param < 0 || param >= NPARAMS) 
  {
    errprint("WARNING","init_rng","Parameter not valid. Using Default param");
    param = 0;
  }

/*      check whether generators have previously been defined            */
/*      guard against access while defining generator parameters for     */
/*            the 1st time                                               */
  length = valid[param].L; /* determine parameters   */
  k = valid[param].K;
  if (!lval) 
  {
    lval = length; /* determine parameters   */
    kval = k;
    gseed = seed^GS0;
  }
  else 
  {
    if (lval != length) 
      doexit++;
    if( seed != (gseed^GS0) ) 
      doexit += 2;

    if (doexit) 
    {
      if (doexit&1) 
	errprint("WARNING","init_rng","changing global L value! Independence of streams is not guaranteed");
      if (doexit&2) 
	errprint("WARNING","init_rng","changing global seed value! Independence of streams is not guaranteed");
    }
  }
  
/*      define the starting vector for the initial node                  */
  nstart = (unsigned *) mymalloc((length-1)*sizeof(unsigned));
  if (nstart == NULL)
    return NULL;

  nstart[0] = gennum;
  for (i=1;i<length-1;i++) 
    nstart[i] = 0;

  p = initialize(rng_type,1,param,seed^GS0,nstart,seed);  /* create a generator  */
  if (p==NULL) 
    return NULL;
  
  ((struct rngen *)(p[0]))->stream_number = gennum;
/*      update si array to allow for future spawning of generators       */
  si = ((struct rngen *)(p[0]))->si;
  while (si[0] < total_gen && !si[1]) 
    si_double(si,si,length);

  NGENS++;
      
  free(nstart);

  rng = p[0];
  ((struct rngen *)rng)->rng_type = rng_type;
  free(p);
  
  return rng;
}


/*************************************************************************/
/*************************************************************************/
/*                  SPAWN_RNG: spawns new generators                     */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
int spawn_rng(int *genptr,  int nspawned, int ***newgens, int checkid)
#else
int spawn_rng(genptr,nspawned,newgens, checkid)
int *genptr,nspawned, ***newgens, checkid;
#endif
{
  int **q=NULL, i;
  unsigned *p;
  struct rngen *temp;
  
  if (nspawned <= 0) /* check if nspawned is valid */
  {
    nspawned = 1;
    errprint("WARNING","spawn_rng","Nspawned <= 0. Default value of 1 used for nspawned");
  }
  
  temp = (struct rngen *) genptr;
  
  p = temp->si;
  
  q = initialize(temp->rng_type,nspawned,temp->param,temp->seed,p,temp->init_seed);
  
  if (q == NULL) 
    {
      *newgens = NULL;
      return 0;
    }
  
  si_double(p,p,temp->lval);

  NGENS += nspawned;
      
  *newgens = (int **) q;

  if(checkid != 0)
    for(i=0; i<nspawned; i++)
      if(addID((*newgens)[i]) == NULL)
	return i;

  return nspawned;
}

/*************************************************************************/
/*************************************************************************/
/*                  UTILITY ROUTINES                                     */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
static int get_llag_rng(void)
#else
static int get_llag_rng()
#endif
{
        return(lval);
}

#ifdef __STDC__
static int get_klag_rng(void)
#else
static int get_klag_rng()
#endif
{
        return(kval);
}

#ifdef __STDC__
int get_seed_rng(int *gen)
#else
int get_seed_rng(gen)
int *gen;
#endif
{
        return(GS0^gseed);
}

#ifdef __STDC__
static int get_hptr_rng( int *genptr)
#else
static int get_hptr_rng(genptr)
int *genptr;
#endif
{
        return (((struct rngen *)genptr)->hptr);
}

#ifdef __STDC__
static int *get_fill_rng( int *genptr)
#else
static int *get_fill_rng(genptr)
int *genptr;
#endif
{
  int i,*p;
  unsigned *pp;
  struct rngen *temp;
  
  temp = (struct rngen *) genptr;
  
  p = (int *) mymalloc(2*(temp->lval)*sizeof(int));
  if(p == NULL)
    return NULL;
  
  pp = ((struct rngen *)genptr)->r0;
  for (i=0;i<lval;i++)
    p[i] = pp[i];
  pp = ((struct rngen *)genptr)->r1;
  for (i=0;i<temp->lval;i++)
    p[temp->lval+i] = pp[i];

  return(p);
}

#ifdef __STDC__
static int *get_next_index_rng( int *genptr)
#else
static int *get_next_index_rng(genptr)
int *genptr;
#endif
{
  int i,*p, lval;
  unsigned *pp;
      
  lval = ((struct rngen *) genptr)->lval;
      
  pp = ((struct rngen *)genptr)->si;
  p = (int *) mymalloc((lval-1)*sizeof(int));
  if(p == NULL)
    return NULL;
  
  for (i=0;i<lval-1;i++) 
    p[i] = pp[i];

  return(p);
}

#ifdef __STDC__
static void si_halve(int *a, int length)
#else
static void si_halve(a, length)
int *a, length;
#endif
{
  int i;
  
  for (i=0;i<length-2;i++) 
  {
    a[i] >>= 1;
    if (a[i+1]&1)
      a[i] ^= (1<<MAX_BIT_INT);
  }

  a[length-2] >>= 1;
}

#ifdef __STDC__
static int *get_node_index_rng( int *genptr)
#else
static int *get_node_index_rng(genptr)
int *genptr;
#endif
{
  int *p, length;

  length = ( (struct rngen *) genptr)->lval;
  
  p = get_next_index_rng(genptr);
  if(p == NULL)
    return NULL;
  
  while (!(p[0]&1)) 
    si_halve(p,length);
  si_halve(p, length);

  return(p);
}

 
/*************************************************************************/
/*************************************************************************/
/*                  MESSAGE PASSING ROUTINES                             */
/*************************************************************************/
/*************************************************************************/


#ifdef __STDC__
int pack_rng( int *genptr, char **buffer)
#else
int pack_rng(genptr,buffer)
int *genptr;
char **buffer;
#endif
{
  int i, size;
  struct rngen *q;
  unsigned char *p, *initp;
  
  q = (struct rngen *)genptr;
  size = 4 + (3*(q->lval)+5)*4 + strlen(q->gentype)+1;
  /* The new load/store routines make using sizeof unnecessary. Infact, */
  /* using sizeof could be erroneous. */
  initp = p = (unsigned char *) mymalloc(size);
  if(p == NULL)
  {
    *buffer = NULL;
    return 0;
  }
  
  p += store_int(q->rng_type,4,p);
  strcpy((char *)p,q->gentype);
  p += strlen(q->gentype)+1;
  p += store_int(q->lval,4,p);
  p += store_int(q->kval,4,p);
  p += store_int(q->seed,4,p);
  p += store_int(q->init_seed,4,p);
  p += store_int(q->stream_number,4,p);
  p += store_intarray(q->si,q->lval-1,4,p);
  p += store_intarray(q->r0,q->lval,4,p);
  p += store_intarray(q->r1,q->lval,4,p);
  p += store_int(q->hptr,4,p);

  *buffer = (char *) initp;
  assert(p-initp == size);
  
  return p-initp;
}


#ifdef __STDC__
int *unpack_rng( char *p)
#else
int *unpack_rng(p)
char *p;
#endif
{
  int doexit=0,i, found, length, k, param;
  struct rngen *q;
  unsigned seed, lag1, lag2;
  unsigned char *packed;
  int rng_type;
  
  packed = (unsigned char *) p;
  
  packed += load_int(packed,4,&rng_type);
  if(strcmp((char *)packed,GENTYPE) != 0)
  {
    fprintf(stderr,"ERROR: Unpacked ' %.24s ' instead of ' %s '\n",  
	    packed, GENTYPE); 
    return NULL; 
  }
  packed += strlen(GENTYPE)+1;
    
  packed += load_int(packed,4,&lag1);
  packed += load_int(packed,4,&lag2);
  packed += load_int(packed,4,&seed);
  
/*      check values of parameters for consistency                       */
  for(i=found=0; i<NPARAMS; i++)
    if(lag1==valid[i].L && lag2==valid[i].K)
    {
      found = 1;
      break;
    }
  
  if(found == 0)
  {
    fprintf(stderr,"ERROR: Unpacked parameters are not acceptable.\n");
    return NULL;
  }

  param = i;
  length = valid[param].L;
  k = valid[param].K;

  if (!lval) 
  {
    lval = length;
    kval = k;
    gseed = seed;
  }
  else 
  {
    if (seed!=gseed)
    {
      errprint("WARNING","unpack_rng","different global seed value!");
      fprintf(stderr,"\t Independence of streams is not guaranteed\n");
    }
  }

  q = (struct rngen *) mymalloc(sizeof(struct rngen));
  if(q == NULL)
    return NULL;
  
  q->rng_type = rng_type;
  q->gentype = GENTYPE;
  q->si = (unsigned *) mymalloc((length-1)*sizeof(unsigned));
  q->r0 = (unsigned *) mymalloc(length*sizeof(unsigned));
  q->r1 = (unsigned *) mymalloc(length*sizeof(unsigned));
  if (q->r1 == NULL || q->si == NULL || q->r0 == NULL) 
    return NULL;

  q->lval = length;
  q->kval = k;
  q->seed = seed;
  q->param = param;

  packed += load_int(packed,4,(unsigned int *)&q->init_seed);
  packed += load_int(packed,4,(unsigned int *)&q->stream_number);
  packed += load_intarray(packed,length-1,4,q->si);
  packed += load_intarray(packed,length,4,q->r0);
  packed += load_intarray(packed,length,4,q->r1);
  packed += load_int(packed,4,(unsigned int *)&q->hptr);

  NGENS++;
      
  return (int *) q;
}

/*************************************************************************/
/*************************************************************************/
/*      FREE_RNG: remove memory for a generator                          */
/*************************************************************************/
/*************************************************************************/

#ifdef __STDC__
int free_rng(int *genptr)
#else
int free_rng(genptr)
int *genptr;
#endif
{
  struct rngen *q;

  q = (struct rngen *)genptr;
  free(q->si);
  free(q->r0);
  free(q->r1);
  free(q);

  NGENS--;
      
  return NGENS;
}



#ifdef __STDC__
int print_rng( int *igen)
#else
int print_rng(igen)
int *igen;
#endif
{
  struct rngen *gen;
  
  gen = (struct rngen *) igen;
  
  printf("\n%s\n", GENTYPE+2);
  
  printf("\n \tseed = %d, stream_number = %d\tparameter = %d\n\n", gen->init_seed, gen->stream_number, gen->param);

  return 1;
}

