/* r250.c	the r250 uniform random number algorithm

		Kirkpatrick, S., and E. Stoll, 1981; "A Very Fast
		Shift-Register Sequence Random Number Generator",
		Journal of Computational Physics, V.40

		also:

		see W.L. Maier, DDJ May 1991



*/

#include <limits.h>
#include "r250.h"

// static functions
static unsigned int randlcg();


#define MSB          0x80000000L
#define ALL_BITS     0xffffffffL
#define HALF_RANGE   0x40000000L
#define STEP         7
#define BITS         32

static unsigned int r250_buffer[ 250 ];
static int r250_index;

static unsigned int randlcg(int sd)       /* returns a random unsigned integer */
{
		static int quotient1  = LONG_MAX / 16807L;
		static int remainder1 = LONG_MAX % 16807L;

        if ( sd <= quotient1 )
                sd = (sd * 16807L) % LONG_MAX;
        else
        {
                int high_part = sd / quotient1;
                int low_part  = sd % quotient1;

                int test = 16807L * low_part - remainder1 * high_part;

                if ( test > 0 )
                        sd = test;
                else
                        sd = test + LONG_MAX;

        }

        return sd;
}

void r250_init(int sd)
{
	int j, k;
	unsigned int mask, msb;

	r250_index = 0;
	for (j = 0; j < 250; j++)      /* fill r250 buffer with BITS-1 bit values */
		sd = r250_buffer[j] = randlcg(sd);


	for (j = 0; j < 250; j++)	/* set some MSBs to 1 */
		if ( (sd=randlcg(sd)) > HALF_RANGE )
			r250_buffer[j] |= MSB;


	msb = MSB;	        /* turn on diagonal bit */
	mask = ALL_BITS;	/* turn off the leftmost bits */

	for (j = 0; j < BITS; j++)
	{
		k = STEP * j + 3;	/* select a word to operate on */
		r250_buffer[k] &= mask; /* turn off bits left of the diagonal */
		r250_buffer[k] |= msb;	/* turn on the diagonal bit */
		mask >>= 1;
		msb  >>= 1;
	}

}

unsigned int r250()		/* returns a random unsigned integer */
{
	register int	j;
	register unsigned int new_rand;

	if ( r250_index >= 147 )
		j = r250_index - 147;	/* wrap pointer around */
	else
		j = r250_index + 103;

	new_rand = r250_buffer[ r250_index ] ^ r250_buffer[ j ];
	r250_buffer[ r250_index ] = new_rand;

	if ( r250_index >= 249 )	/* increment pointer for next time */
		r250_index = 0;
	else
		r250_index++;

	return new_rand;

}


double dr250()		/* returns a random double in range 0..1 */
{
	register int	j;
	register unsigned int new_rand;

	if ( r250_index >= 147 )
		j = r250_index - 147;	/* wrap pointer around */
	else
		j = r250_index + 103;

	new_rand = r250_buffer[ r250_index ] ^ r250_buffer[ j ];
	r250_buffer[ r250_index ] = new_rand;

	if ( r250_index >= 249 )	/* increment pointer for next time */
		r250_index = 0;
	else
		r250_index++;

	return (double)new_rand / ALL_BITS;

}

