/* Coded by Alberto Pelliccione
alberto@bitchx.it

1. Create an object:
- Rand r; // Initializations will be done internally
OR
- Rand r(some_value1, some_value2);
some_value1 and some_value2 are random unsigned _short_ integers,
if you don't have a random value, don't use this method, the
first one will take care of every initialization.

2. Get your random number
- int a = r.rand24(); // Will return a 24bit pseudo-random integer
- int a = r.rand32(); // Will return a 32bit pseudo-random integer
- double a = r.random(2.0); // Will return a double within the range -2.0/2.0
- double a = r.random(0.5); // Will return a double within the range -0.5/0.5
- double a = r.random(1.0); // Will return a double within the range -1.0/1.0
- double a = r.frandom(1.0); // Will return a double within the range -1.0/1.0 (faster)
- unsigned long a = r.rand_mod(6); // Will return an unsigned long between 0 and 6 - 1

3. Have fun :)
*/

#include "Rand.h"

Rand::Rand() :
state_size(97), p(179), pm1(179 - 1), q(179 - 10),
init_c(362436), cd(7654321), cm(16777213), two_to_24(0x01000000),
init_done(0)
{
	FILETIME ft;
	SYSTEMTIME st;
	UINT seed;

	GetSystemTime(&st);
	SystemTimeToFileTime(&st, &ft);

	seed = ft.dwLowDateTime ^ GetTickCount();

	init_rand((USHORT)(seed & 0xFFFF0000 >> 16), (USHORT)(seed & 0x0000FFFF));
}

Rand::Rand(unsigned short a, unsigned short b) :
state_size(97), p(179), pm1(179 - 1), q(179 - 10),
init_c(362436), cd(7654321), cm(16777213), two_to_24(0x01000000),
init_done(0), sa(a), sb(b)
{
	init_rand(sa, sb);
}

unsigned short Rand::col(short anyint, unsigned short size)
{
	short int i = anyint;

	if (i < 0)
		i = -(i >> 2);

	while (i >= size)
		i = i >> 2;

	return i;
}


void Rand::init_rand(unsigned short int seed_a, unsigned short int seed_b)
{
	unsigned long int s, bit;
	unsigned short int ii, jj, kk, mm;
	unsigned short int ll;
	short int sd, ind;

	sd = col(seed_a, pm1 * pm1);
	ii = 1 + sd / pm1;
	jj = 1 + sd % pm1;

	sd = col(seed_b, pm1 * q);
	kk = 1 + sd / pm1;
	ll = sd % q;

	if (ii == 1 && jj == 1 && kk == 1)
		ii = 2;

	for (ind = 0; (unsigned short int)ind < state_size; ind++){
		s = 0;
		bit = 1;

		do{
			mm = (((ii * jj) % p) * kk) % p;

			ii = jj;
			jj = kk;
			kk = mm;

			ll = (53 * ll + 1) % q;

			if ((ll * mm) & 0x0020)
				s += bit;

			bit <<= 1;
		}while (bit < two_to_24);

		u[ind] = s;
	}

	index_i = state_size - 1;
	index_j = state_size / 3;
	c = init_c;

	init_done = 1;
}

// Return uniformly distributed pseudo random numbers in the range
// 0..2^(24)-1 inclusive. There are 2^24 possible return values.
unsigned long Rand::rand24()
{
	unsigned long int temp;

	if (!init_done)
		init_rand(sa, sb);

	c = (c < cd ? c + (cm - cd) : c - cd);

	temp = (u[index_i] -= u[index_j]);

	if (!index_i--)
		index_i = state_size - 1;

	if (!index_j--)
		index_j = state_size - 1;

	return (temp - c) & (two_to_24 - 1);
}

// Return uniformly distributed pseudo random number in the range
// 0..2^(32)-1 inclusive. There are 2^32 possible return values.
unsigned long Rand::rand32()
{
	unsigned short int i, val;

	if (!bno) {
		for (i = 0; i < 12; i += 2) {
			val = (unsigned short int)rand24();
			memcpy(buf + i, &val, 2);
		}
		bno = 12;
	}
	bno -= 4;

	return *(unsigned long *) (buf + 8 - bno);
}

