//-------------------------------------------------------------------------
//
// File:		RandomMT.cpp
// Desc:		Marsenne Twister Random generator.
// Author:	memon <memon@inside.org>
//					based on Marsenne Twister implmentation by Takuji Nishimura
//					http://www.math.keio.ac.jp/~matumoto/emt.html
//
//-------------------------------------------------------------------------
//	This source code is part of the game Fibble.
//  Made by Kylpyamme Design for Crytek.
//-------------------------------------------------------------------------

#include "PajaTypes.h"
#include "RandomMT.h"

using namespace PajaTypes;

// Period parameters
#define N 624
#define M 397
#define MATRIX_A		0x9908b0df		// constant vector a
#define UPPER_MASK	0x80000000		// most significant w-r bits
#define LOWER_MASK	0x7fffffff		// least significant r bits

// Tempering parameters
#define TEMPERING_MASK_B				0x9d2c5680
#define TEMPERING_MASK_C				0xefc60000
#define TEMPERING_SHIFT_U( y )	(y >> 11)
#define TEMPERING_SHIFT_S( y )	(y << 7)
#define TEMPERING_SHIFT_T( y )	(y << 15)
#define TEMPERING_SHIFT_L( y )	(y >> 18)

static uint32	g_dwMT[N];					// the array for the state vector
static int		g_iMTI = N + 1;		// g_iMTI==N+1 means g_dwMT[N] is not initialized

// Initializing the array with a seed
void
seed_gen_rand( uint32 dwSeed )
{
	int i;

	for( i = 0; i < N; i++ )
	{
		g_dwMT[i] = dwSeed & 0xffff0000;
		dwSeed = 69069 * dwSeed + 1;
		g_dwMT[i] |= (dwSeed & 0xffff0000) >> 16;
		dwSeed = 69069 * dwSeed + 1;
	}
	g_iMTI = N;
}

uint32
gen_rand()
{
	uint32 y;
	static uint32 dwMag01[2] = { 0x0, MATRIX_A };

	if(g_iMTI >= N )
	{
		// generate N words at one time
		int kk;

		// if sgenrand() has not been called, a default initial seed is used.
		if( g_iMTI == N + 1 )   
			seed_gen_rand( 4357 );

		for( kk = 0; kk < N - M; kk++ )
		{
			y = (g_dwMT[kk] & UPPER_MASK) | (g_dwMT[kk + 1] & LOWER_MASK);
			g_dwMT[kk] = g_dwMT[kk+M] ^ (y >> 1) ^ dwMag01[y & 0x1];
		}
		for( ; kk < N - 1; kk++ )
		{
			y = (g_dwMT[kk] & UPPER_MASK) | (g_dwMT[kk + 1] & LOWER_MASK);
			g_dwMT[kk] = g_dwMT[kk + (M - N)] ^ (y >> 1) ^ dwMag01[y & 0x1];
		}
		y = (g_dwMT[N - 1] & UPPER_MASK) | (g_dwMT[0] & LOWER_MASK);
		g_dwMT[N - 1] = g_dwMT[M - 1] ^ (y >> 1) ^ dwMag01[y & 0x1];

		g_iMTI = 0;
	}

	y = g_dwMT[g_iMTI++];
	y ^= TEMPERING_SHIFT_U( y );
	y ^= TEMPERING_SHIFT_S( y ) & TEMPERING_MASK_B;
	y ^= TEMPERING_SHIFT_T( y ) & TEMPERING_MASK_C;
	y ^= TEMPERING_SHIFT_L( y );

	return y; 
}

float64
float_gen_rand()
{
	return (double)gen_rand() * 2.3283064370807974e-10;
}
