/* random.c

   written by Frederic Bois
   modifications by Don Robert Maszle
   8 January 1992
   
   Copyright (c) 1993.  Don Maszle, Frederic Bois.  All rights reserved.

   -- Revisions -----
     Logfile:  SCCS/s.random.c
    Revision:  1.3
        Date:  8/7/93
     Modtime:  21:36:34
      Author:  @a
   -- SCCS  ---------

 * Random number generator module:  provides two random number
   generators:

   Random()         yielding uniform random variates between 0 and 1, and
   RandomShuffle()  which shuffles the output of the first generator.

 * Also available are several other types of random variates:

   UniformRandom(a, b)             -- Uniform over [a, b]
   LogUniformRandom(a, b)          -- LogUniform over [a, b]
   BetaRandom(a, b, alpha, beta)   -- Beta(alpha, beta) over [a, b]
   NormalRandom()		   -- Normal(0 mean, 1 variance)
   _NormalRandom (m, s)            -- General Normal
   LogNormalRandom (m, s)          -- exp (Normal)
   TruncNormalRandom (m, s, a, b)  -- Truncated general normal
   TruncLogNormalRandom (m, s, a, b)  -- Truncated log normal
   GammaRandom (a)                 -- gamma(a, 1) over [0,1]
   _GammaRandom (a, b)             -- general gamma variate
   Chi2Random(dof)                 -- Chi-squared w/dof degrees o'freedom
   BinomialRandom(p, n)            -- Binomial of n trials, Pr(each)=p

   multinomial()                   -- several binomial events

 * And utility functions:
 
   lnGamma(x)                            -- Natural log of gamma function

 * The random number generator must be initialized by providing a seed
   to InitRandom().  A non-zero second argument to this function
   instructs the random number generator to "warm up" by filling a
   memory array used by RandomShuffle().  This also initializes a flag
   used by the Normal() routine.

   If the random number generator is not initialized before any of its
   routines are called, it initializes itself with a default seed.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "random.h"

#ifdef TRUE
#undef TRUE
#endif
#define TRUE 1

#ifdef FALSE
#undef FALSE
#endif
#define FALSE 0

typedef struct tagRANDREC {
  double seed, last;
  double mem[50];
  long switchG;
  double memGauss;

} RANDREC, *PRANDREC;


#define SEED_DEFAULT 314159265.3589793
#define SEED_MIN     1.0
#define SEED_MAX     2147483646.0

static vbNoSeed = TRUE;         /* Flag to prevent use without seed */
static vbNotInitd = TRUE;       /* Flag to prevent use without initializing */
static RANDREC vRandRec;        /* Global random informatino shared by */
                                /* all random number functions */

void SetSeed (double dSeed)
{
  int bCorrected = 0;

  if (dSeed == 0.0) {
    dSeed = SEED_DEFAULT;
    bCorrected++;
  }  /* if */
  
  if (dSeed < 0) {
    dSeed = -dSeed;			/*-- Don't announce this correction */
/*.     bCorrected++; */
  }  /* if */

  if (dSeed < SEED_MIN) {
    dSeed = SEED_MIN + (dSeed/SEED_MIN) / (SEED_MAX-SEED_MIN);
    bCorrected++;
  }  /* if */

  if (dSeed > SEED_MAX) {
    dSeed = SEED_MIN + (SEED_MAX/dSeed) / (SEED_MAX-SEED_MIN);
    bCorrected++;
  }  /* if */

  assert ((/* Invalid Seed */ dSeed >= SEED_MIN && dSeed <= SEED_MAX));
  
  /*-- Assign valid seed */

  if (bCorrected)
    fprintf (stderr,
             "* SetSeed():  corrected out of range random number seed\n"
             "  Seed must lie in the range [%g, %g]\n"
             "  New seed --> %g\n", SEED_MIN, SEED_MAX, dSeed);
  
  vRandRec.seed = dSeed;
  vbNoSeed = FALSE;             /*-- Flag that seed has been set */
}  /* SetSeed */


/* InitRandom

   initializes the random generator with the given seed.
   If an invalid seed is given, SetSeed() silently corrects it.

   If the boolean bWarmUp is non-zero, the random number generator is
   "warmed up" by running it a number of times.  After this, a memory
   array is filled from which shuffled random values will be drawn.
   Also, a flag used by the Normal() routine is initialized.
*/

void InitRandom (double dSeed, int bWarmUp)
{
  long i;

  if (vbNoSeed
      || dSeed != SEED_DEFAULT) /*-- Prevent nuking user's seed if not initd */
    SetSeed (dSeed);
  
  if (bWarmUp) {
    for (i = 0; i < 50; i++)
      (void) Random ();         /*-- Warm up generator --NECESSARY?? */

    for (i = 0; i < 50; i++)
      vRandRec.mem[i] = Random (); /*-- Fill the shuffle array */

    vRandRec.last = Random ();  /*-- Draw first number */
    vRandRec.switchG = 0;       /*-- Flag as first Normal */
    vbNotInitd = FALSE;         /*-- Flag as initialized */
  }  /* if */
}  /* InitRandom */


/* Random
   
   An alternative random number generator, so you don't have to use
   the (probably not so good) system supplied standard C version.

   Random() returns random numbers between 0 and 1.  The generator can
   be initialized with InitRandom().

   This generator should be correct on any system for which the
   representattion of reals uses at least a 32-bit mantissa, including
   the sign bit.

   From PARK SK, MILLER KW: Random number generators: good ones are
   hard to find.  Commun. ACM 1988; 31: 1192. (Version Real 2).
*/

double Random (void)
{
#define a  16807.0
#define m  2147483647.0
#define q  127773.0   /* m Div a*/
#define r  2836.0     /* m Mod a*/

  double hi, test;

  if (vbNoSeed)
    SetSeed (SEED_DEFAULT);
  
  hi = (long)(vRandRec.seed / q);
  test = a * (vRandRec.seed - q * hi) - r * hi;

  if (test > 0.0)
    vRandRec.seed = test;
  else
    vRandRec.seed = test + m;

  return (vRandRec.seed / m);

#undef a
#undef m
#undef q
#undef r

}  /* Random */



/* RandomShuffle
   
   assumes that the random number generator is Random ().
   RandomShuffle() shuffles the output of this generator.

   The routine is initialized by calling InitRandom().  A check is
   done to assure that initialization is performed.
   
   Adapted from Press et al., Numerical Recipes, by F.Bois, D.R.Maszle.
*/

double RandomShuffle (void)
{
  long i;

  if (vbNotInitd)
    InitRandom (SEED_DEFAULT, TRUE);

  i = (long) (50.0 * vRandRec.last); /*-- Randomly shuffle output */
  vRandRec.last = vRandRec.mem[i];
  vRandRec.mem[i] = Random ();

  return (vRandRec.last);

}  /* RandomShuffle */


/* NormalRandom
   
   Returns a normally distributed deviate with zero mean and unit variance,
   using a random generator as a source of uniform deviates.
   From Press et al., Numerical Recipes.

   Programs using Normal should initialize the random number
   generator with InitRandom().
*/

double NormalRandom (void)
{
  double factor, racin, tempo1, tempo2;

  if (vbNotInitd)
    InitRandom (SEED_DEFAULT, TRUE);

  if (vRandRec.switchG != 0) {
    vRandRec.switchG = 0;
    return (vRandRec.memGauss);
  }  /* if */

  do {
    tempo1 = 2 * RandomShuffle () - 1;
    tempo2 = 2 * RandomShuffle () - 1;
    racin = tempo1 * tempo1 + tempo2 * tempo2;
  } while (racin >= 1);

  factor = sqrt(-2 * log(racin) / racin);
  vRandRec.memGauss = tempo1 * factor;
  vRandRec.switchG = 1;
  return (tempo2 * factor);

}  /* NormalRandom */



/* _NormalRandom

   returns a Normal random variate based on a unit Normal variate.
*/
#ifndef VARIATE_CALCS_CAN_BE_MACROS
double _NormalRandom (double dMean, double dStdDev)
{
  return (dMean + dStdDev*NormalRandom());
}  /* _NormalRandom */
#endif

/* LogNormalRandom

   returns a variate such that the log of the variate is normally
   distributed.
*/
#ifndef VARIATE_CALCS_CAN_BE_MACROS
double LogNormalRandom (double dMean, double dStdDev)
{
  return exp(dMean + dStdDev*NormalRandom());
}  /* LogNormalRandom */
#endif


/* TruncNormalRandom

   returns a truncated Normal variate in the range [a, b].
*/

double TruncNormalRandom (double dMean, double dStdDev, double a, double b)
{
  double X = 0.0;

  if (a >= b)
    fprintf (stderr, "TruncNormalRandom: min >= max  [%g %g]\n", a, b);

  else do {
    X = _NormalRandom(dMean, dStdDev);
  }
  while (X < a || X > b);

  return X;
}  /* TruncNormalRandom */


/* TruncLogNormalRandom

   returns a truncated LogNormal variate in the range [a, b].
*/

double TruncLogNormalRandom (double dMean, double dStdDev, double a, double b)
{
  double X = 0.0;

  if (a >= b)
    fprintf (stderr, "TruncLogNormalRandom: min >= max  [%g %g]\n", a, b);

  else do {
    X = LogNormalRandom(dMean, dStdDev);
  }
  while (X < a || X > b);

  return X;
}  /* TruncLogNormalRandom */


/* GammaRandom

   returns a gamma distributed random variate with shaping parameter
   a.

   Uses the Cheng (1977) rejection algorithm (GB) from Devroye,
   Non-uniform Random Variate Generation, sec IX.3. p413.
*/

#define log4      1.386294361	/* ln(4.0) */
#define log4_5p1  2.504077397	/* ln(4.5) + 1 */

double GammaRandom (double a)
{
  double b = a - log4;
  double c = a + sqrt(2*a - 1);
  double U, V, X, Y, Z, R;

  do {
    U = Random();
    V = Random();
    Y = a*log (V/(1 - V));
    X = a*exp (V);
    Z = U*V*V;
    R = b + c*Y - X;
  }
  while ((R < 4.5*Z - log4_5p1)
	 && (R < log(Z)));

  return X;
}  /* GammaRandom */
#undef log4
#undef log4_5p1


#ifndef VARIATE_CALCS_CAN_BE_MACROS
double _GammaRandom (double a, double b)
{
  return b*GammaRandom(a);
}  /* GammaRandom */
#endif

/* Chi2Random

   returns a chi-squared random variate, which is a gamma(dof/2, 2).
*/
#ifndef VARIATE_CALCS_CAN_BE_MACROS
double Chi2Random(double dof)
{
  return (_GammaRandom(dof/2.0, 2.0));
}  /* Chi2Random */
#endif

    /*----- Utility functions ---------------*/

/* lnGamma
   
   A function to return the natural log of the Gamma function of x.
   From Press et al., Numerical Recipes.
*/

double lnGamma (double x)
{
  double tmp, ser;

  ser = 1.0 + 76.18009173    / x - 
              86.50532033    / (x + 1) +
              24.01409822    / (x + 2) -
              1.231739516    / (x + 3) +
              0.120858003e-2 / (x + 4) -
              0.536382e-5    / (x + 5);

  tmp = x + 4.5;
  return -tmp + (x - 0.5) * log(tmp) + log(2.50662827465 * ser);

}  /* lnGamma */


/* BinomialRandom
   
   Return as a double floating-point number an integer value that is a random
   deviate drawn from a binomial distribution of n trials each of
   probability p, using Random () as a source of uniform random deviates.
   From Press et al., Numerical Recipes.
*/

double BinomialRandom (double p, long n)
{
  long j;
  static long nOld=(-1);
  double mean, em, g, angle, pTemp, bnl, sq, t, y;
  static double pOld=(-1.0), pc, pLog, pcLog, en, gOld;

  pTemp = ( p <= 0.5 ? p : 1.0 - p);
  mean = n * pTemp;  /* This is the mean of the deviate to be produced. */

	/* Use the direct method while n is not too large.
	   This can require up to 25 calls to random */

  if (n < 25) {
    bnl = 0.0;
    for (j=1; j<=n; j++)
      if (Random () < pTemp )
        bnl += 1.0;
  }  /* if */
  else
    if (mean < 1.0) {

      /* if less than one event is expected out of 25 or more trials,then the
         distribution is quit accurately Poisson. Use direct method. */
      g = exp(-mean);
      t = 1.0;
      for (j=0; j<=n; j++) {
        t *= Random ();
        if (t < g) break;
      }  /* for */

      bnl = (j <= n ? j : n);
    }  /* if */

    else {
	/* Use the rejection method. */

      if (n != nOld) {
        /* if n has changed, compute useful quantities */

        en = n;
        gOld = lnGamma(en + 1.0);
        nOld = n;
      }  /* if */

      if (pTemp != pOld) {
        /* if pTemp has changed, compute useful quantities. */

        pc = 1.0 - pTemp;
        pLog = log(pTemp);
        pcLog = log(pc);
        pOld = pTemp;
      }  /* if */

      sq = sqrt( 2.0 * mean * pc );

      /* Rejection method with a Lorentzian comparison function. */

      do {
        do {
          angle = 3.141592654 * Random ();
          y = tan(angle);
          em = sq * y + mean;
        } while (em < 0.0 || em >= (en + 1.0)); /* Reject */

        em = floor(em); /* Trick for integer-valued distribution */

        t = 1.2 * sq * (1.0 + y * y) *
            exp(gOld - lnGamma(em + 1.0) - lnGamma(en - em + 1.0) +
                em * pLog + (en - em) * pcLog);

      } while (Random () > t);

      /* Reject. This happens about 1.5 time per deviate on average */

      bnl = em;

    }  /* else */  /* end of rejection */

  if ( pTemp != p )
    bnl = n - bnl; /* Remember to undo the symmetry tranf */

  return (bnl);

}  /* BinomialRandom */



/* multinomial
   
   A procedure to compute return multinomial deviates.
   N is the number of trials,
   p the array of probabilities,
   dim the dimension of the array (number of possible events),
   x the array of event occurences which is returned.

   From Devroye "Non-Uniform Random Numbers...".
*/

void multinomial (long n, int dim, double *p, double *x)
{
  int i;
  double sum, ptemp;

  sum = 1;

  for (i=1; i<=dim; i++) {
    if (p[i]) {
      ptemp = p[i] / sum;
      x[i] = BinomialRandom (ptemp, n);
      n = n - (long)x[i];
      sum = sum - p[i];
    }  /* if */
    else
      x[i] = 0.0;
  }  /* for */

}  /* multinomial */


/* UniformRandom

   returns a variate that is uniformly distributed on the interval [a,b].
*/

#ifndef VARIATE_CALCS_CAN_BE_MACROS

double UniformRandom (double a, double b)
{
  return (Random()*(b - a) + a);
}  /* UniformRandom */
#endif


/* LogUniformRandom

   returns a variate that is log-uniformly distributed on the interval
   [a,b]. 
*/

#ifndef VARIATE_CALCS_CAN_BE_MACROS

double LogUniformRandom (double a, double b)
{
  return ( a*pow(b/a, Random()) );
}  /* LogUniformRandom */

#endif


/* BetaRandom

   returns a variate that is Beta distributed on the interval [a,b]
   with shape parameters alpha and beta.  First a Beta variate is
   found over the interval [0, 1] with not the most efficient
   algorithm.  This is then scaled at the end to desired range.

   The beta function has two shaping parameters, alpha and beta.
   Setting these parameters to 1.5 and 1.5 yields a normal-like
   distribution, but without tails.
   
   The beta probability density function over [0,1] is given by:
   
	         /  x^(alpha-1) * (1-x)^(beta-1) * Gamma(alpha+beta)
	         |  ------------------------------------------------
   f_beta(x) =  <              Gamma(alpha) * Gamma(beta)
	         |
	         \   0, x < 0 or x > 1
	
   The algorithm is said to not be the most efficient.   Maindonald
   says to draw two random numbers u1 and u2 on the interval [0,1].
   Then calculate the v's and check that that their sum is less than
   1.  If not, throw away both u's and draw new ones.
   
   It may be tempting to re-use the second number drawn as the first
   random number of the next iteration, and simply draw one more.
   *** Don't do it.  You will produce an incorrect distribution.  You
   must draw two new numbers for the rejection sampling to be correct.

   Reference:
     Statistical Computation, J.H.Maindonald, John Wiley and Sons,
     1984, p 370.
*/

double BetaRandom (double a, double b, double alpha, double beta)
{
  double u1, u2, v1, v2, w;
  
  do {
    u1 = Random();                      /*-- Draw two numbers */
    u2 = Random();

    v1 = pow (u1, 1/alpha);             /*-- alpha and beta are > 0 */
    v2 = pow (u2, 1/beta);

    w = v1 + v2;

  } while (w > 1.0);            /* Cont. until v1/w will be Beta distrib'd */

  return (a + (v1/w)*(b - a));          /*-- Scale to interval [a, b] */
}  /* BetaRandom */


/* double InterpolateX (double rgX[], double rgY[], long lLower, double dY); */
#define InterpolateX(rgX, rgY, l, at_Y) \
  ((rgX)[l]                       \
   + ((at_Y) - (rgY)[l])          \
    *((rgX)[(l) + 1] - (rgX)[l])  \
    /((rgY)[(l) + 1] - (rgY)[l]))

/* PiecewiseConstantRandom

   returns a random variate whose cumulative distribution function is
   defined by the clSamples coordinates (rgdX, rgdSumX).  The two
   coordinates (a, 0) and (b, 1) *MUST* be included in the coordinate
   arrays. 

   A Uniform variate is drawn and transformed to the given
   distribution by interpolating to the rgdSumX array using the two
   points in rgdX which bound the chosen uniform variate.  The
   bounding points are found with a binary search.

   A VARIATE that has this type can be created with the function
   PiecewiseConstantVariateFromData (), given a set of data points
   drawn from a distribution.  The data points do not have to be
   sorted.  The resulting VARIATE's coordinate defining arrays include
   the boundary pairs mentions above.
*/

double PiecewiseConstantRandom (double a, double b,
				long clSamples, double rgdX[], double rgdSumX[])
{
  double dUniform = UniformRandom(a, b);
  long lUpper, lLower, lIndex;

      /*-- Find bounding Xs by a binary search of the sorted rgdX[] array */
  lUpper = clSamples;
  lLower = 0;
  lIndex = 0;

  while (lUpper - lLower > 1) {
    lIndex = (lUpper + lLower)/2;

    if (dUniform > rgdSumX[lIndex])
      lLower = lIndex;          /*-- Move to right half */
    else if (dUniform < rgdSumX[lIndex])
      lUpper = lIndex;          /*-- Move to left half */
    else
      lUpper = lLower = lIndex;
  }  /* while */

  return (lUpper == lLower      /*-- Exactly on a data point */
          ? rgdX[lLower]
          : InterpolateX (rgdX, rgdSumX, lLower, dUniform));
  
}  /* PiecewiseConstantRandom */


/* PiecewiseConstantVariateFromData
   
   creates a VARIATE whose cumulative function is piecewise-linear
   corresponding to the sample date provided.  The resulting variate
   has a distribution that approximates that of the data.  The data
   points do not have to be sorted.  The resulting VARIATE's
   coordinate defining arrays will include the boundary pairs which
   are needed by the PiecewiseConstantRandom() function called by
   CalcVariate(). 
*/

void PiecewiseConstantVariateFromData (VARIATE *pVariate,
                                     double dMin, double dMax,
                                     long clSamples, double *rgdData)
{
  double *rgdX, *rgdSumX;
  double dInterval;
  long i;
  
  /* if pointer exists, we don't know if it is valid!  can't */
  /* re-allocate or nothing.  Oh to be using C++ */

  rgdX = (double *) malloc ((clSamples + 2)*sizeof(double));
  rgdSumX = (double *) malloc ((clSamples + 2)*sizeof(double));
  
/*  Sort (clSamples, rgdData, &rgdX[1]); / *-- Leave space for lower bound */

      /*-- Setup up Cumulative array for faster samples later */

  rgdX[0] = dMin;               /*-- Boundary conditions for cumulative */
  rgdSumX[0] = 0.0;
  dInterval = 1.0/(clSamples + 1);

  for (i = 1; i <= clSamples; i++) 
    rgdSumX[i] = rgdSumX[i - 1] + dInterval;

  rgdX[clSamples+1] = dMax;     /*-- Boundary conditions for cumulative */
  rgdSumX[clSamples+1] = 1.0;

  pVariate->vtType = PiecewiseConstantVariate;
  pVariate->rgdX = rgdX;
  pVariate->rgdSumX = rgdSumX;
  pVariate->rglParms[RGL_CLSAMPLES] = clSamples + 2;

  /*** May want to keep the data points also!  for pdf calcs */
  
}  /* PiecewiseConstantVariateFromData */


/* PdfPWConstant

   returns interpolated value of PiecewiseConstant pdf.
*/
#ifdef ndef
double PdfPWConstant (double a, double b,
		      long clSamples, double rgdX[], double rgdPdf[],
		      double x)
{
  long lUpper, lLower, lIndex;

      /*-- Find bounding Xs by a linear search of the sorted rgdX[] array */
  lUpper = clSamples;
  lLower = 0;

  while (lUpper - lLower > 1) {
    lIndex = (lUpper + lLower)/2;

    if (x > rgdX[lIndex])
      lLower = lIndex;          /*-- Move to right half */
    else if (dUniform < rgdSumX[lIndex])
      lUpper = lIndex;          /*-- Move to left half */
    else
      lUpper = lLower = lIndex;
  }  /* while */

  return (lUpper == lLower      /*-- Exactly on a data point */
          ? rgdX[lLower]
          : InterpolateX (rgdX, rgdSumX, lLower, dUniform));
  
}  /* PdfPWConstant */
#endif

#ifdef ndef
double PdfPWLinear (double a, double b,
		    long clSamples, double rgdX[], double rgdPdf[], double x)
{
  long lUpper, lLower, lIndex;

      /*-- Find bounding Xs by a linear search of the sorted rgdX[] array */
  lUpper = clSamples;
  lLower = 0;

  while (lUpper - lLower > 1) {
    lIndex = (lUpper + lLower)/2;

    if (x > rgdX[lIndex])
      lLower = lIndex;          /*-- Move to right half */
    else if (dUniform < rgdSumX[lIndex])
      lUpper = lIndex;          /*-- Move to left half */
    else
      lUpper = lLower = lIndex;
  }  /* while */

  return (lUpper == lLower      /*-- Exactly on a data point */
          ? rgdX[lLower]
          : InterpolateX (rgdX, rgdSumX, lLower, dUniform));
  
}  /* PdfPWLinear */
#endif


/* CalcVariate

   calculates and returns a new value for the given variate.  The
   value is stored and can be accessed with ValueOfVariate().
*/

double CalcVariate (VARIATE *pVariate)
{
  double dValue = 0.0;

  if (!pVariate)
    return 0.0;
  
  switch (pVariate->vtType) {

    default:
      fprintf (stderr, "CalculateVariate:  Unknown random variate %d\n",
               (int) pVariate->vtType);
      break;
             
    case UniformVariate:                /*----- Uniform distribution */
      dValue = UniformRandom (pVariate->dMin, pVariate->dMax);
      break;
      
    case LogUniformVariate:             /*----- LogUniform distribution */
      dValue = LogUniformRandom (pVariate->dMin, pVariate->dMax);
      break;

    case BetaVariate:                   /*----- Beta distribution */

                                        /*-- Normal-like alpha = beta = 1.5*/
      dValue = BetaRandom (pVariate->dMin, pVariate->dMax, 2.0, 2.0);

/*.    pVariate->rgdParms[RGD_ALPHA], pVariate->rglParms[RGD_BETA]); */
      break;
      
    case Chi2Variate:                   /*----- Chi-squared distribution */
      dValue = Chi2Random (pVariate->rgdParms[RGD_DOF]);
      break;
      
    case _NormalVariate:	/*----- general Normal distribution */
      dValue = _NormalRandom (pVariate->rgdParms[RGD_MEAN],
			       pVariate->rgdParms[RGD_STDDEV]);
      break;

    case LogNormalVariate:	/*----- general Normal distribution */
      dValue = LogNormalRandom (pVariate->rgdParms[RGD_MEAN],
				  pVariate->rgdParms[RGD_STDDEV]);
      break;
      
    case TruncLogNormalVariate:	/*----- general Normal distribution */
      dValue = TruncLogNormalRandom (pVariate->rgdParms[RGD_MEAN],
				     pVariate->rgdParms[RGD_STDDEV],
				     pVariate->dMin, pVariate->dMax);
      break;
      
    case TruncNormalVariate:	/*----- general Normal distribution */
      dValue = TruncNormalRandom (pVariate->rgdParms[RGD_MEAN],
				    pVariate->rgdParms[RGD_STDDEV],
				    pVariate->dMin, pVariate->dMax);
      break;
      
    case NormalVariate:	/*----- Unit Normal distribution */
      dValue = NormalRandom ();
      break;
      
    case BinomialVariate:	/*----- Binomial distribution */
      dValue = BinomialRandom (pVariate->rgdParms[RGD_PR_ONE_TRIAL],
                               pVariate->rglParms[RGL_CLTRIALS]);
      break;

    case PiecewiseConstantVariate:
      dValue = PiecewiseConstantRandom (pVariate->dMin, pVariate->dMax,
					pVariate->rglParms[RGL_CLSAMPLES],
					pVariate->rgdX, pVariate->rgdSumX);
      break;
  }  /* switch */

  return (pVariate->dValue = dValue); /*-- Assign and return new value */
}  /* CalcVariate */


/* ValueOfVariate

   returns the last calculated value of the variate.  No check is done
   to assure that a value has been calculated (could easily be added)
   in which case the variate is truly random.
*/

double ValueOfVariate (VARIATE *pVariate)
{
  if (pVariate)
    return pVariate->dValue;

  return 0.0;
}  /* ValueOfVariate */


/* CalcPdf

   calculates and returns the value of the Pdf of the variate at x.
*/
#ifdef ndef
double CalcPdf (VARIATE *pVariate, double x)
{
  if (!pVariate)
    return 0.0;
  
  switch (pVar)

}  /* CalcPdf */
#endif
/*-- End of random module */
