//  RAND.C

#include <stdlib.h>

#include "me5.h"

//  Variables and constants for rnd().

unsigned long mh_random_number;
unsigned long b0 = 0x5D81B5L;       //  6128053, prime  
unsigned long c0 = 0xF4D23L;        //  1002787, prime  

//  Variables and constants for Park-Miller PRNG, pm().
//  Stephen K. Park and Keith W. Miller, "Random Number Generators:
//  Good ones are hard to find", Communications of the ACM, October 1988,
//  Vol. 31, No. 10, pp.1192-1201.

unsigned long pm_rn;                //  must be unsigned long

const long m = 2147483647L;         //  2^31 - 1
const long q0 = 127773L;            //  m/a
const long a0 = 16807L;
const long r0 = 2836L;              //  m%a

const unsigned char *pm_rn_ptr = (unsigned char *)&pm_rn;
const unsigned char *rnd_ptr = (unsigned char *)&mh_random_number;

int pm_i;
int rnd_i;

//  key[] must be defined and should consist of MAX_KEY_LENGTH bytes.
/*----------------*/
void seed_rngs(void)
{
unsigned int i, k;
unsigned long pm_seed;

MD5Init2(&mh_mdContext);
MD5Update2(&mh_mdContext,key,MAX_KEY_LENGTH);
MD5Final2(v,&mh_mdContext);

mh_random_number = *((unsigned long *)&v[0]) +  *((unsigned long *)&v[8]);

rnd_i = 4;

pm_seed = *((unsigned long *)&v[4]) + *((unsigned long *)&v[12]);

//  pm_seed must be in the range 1 - 2147483646.

if ( !pm_seed )
    pm_rn = (unsigned long)key[0] * key[1] * key[2];
else if ( pm_seed > 2147483646L )
    pm_rn = pm_seed/(3+(key[0]&3));   //  divide by 3 through 6
else
    pm_rn = pm_seed;

pm_i = 3;

//  Run the RNGs a bit.

k = 16 + rnd()%32 + pm()%32;

for ( i=0; i<k; i++ )
    {
    rnd();
    pm();
    }

pm_seed = 0;

//  num_rnd = num_pm = 0;
}

//  This generates a new unsigned long only once every three calls.
/*-------------------*/
unsigned char rnd(void)
{
if ( rnd_i == 4 )
    {
    mh_random_number *= b0;
    mh_random_number += c0;
    rnd_i = 1;       //  not 0, so as to skip least significant byte
    }

//  num_rnd++;
return ( rnd_ptr[rnd_i++] );
}

//  This generates a new long only once every three calls.
/*------------------*/
unsigned char pm(void)
{
if ( pm_i == 3 )      //  Don't use most significant byte
    {                 //  since it's always < 0x80.
    pm_rn = a0*(pm_rn%q0) - r0*(pm_rn/q0);
    if ( pm_rn <= 0 )
        pm_rn += m;
    pm_i = 0;
    }

//  num_pm++;
return ( pm_rn_ptr[pm_i++] );
}