#include <node.h>
#include <v8.h>
#include <climits>

using namespace v8;



/* Period parameters */
const int N = 624;
const int M = 397;
const unsigned int MatrixA = 0x9908b0df; /* constant vector a */
const unsigned int UpperMask = 0x80000000; /* most significant w-r bits */
const unsigned int LowerMask = 0x7fffffff; /* least significant r bits */

/* Tempering parameters */
const unsigned int TemperingMaskB = 0x9d2c5680;
const unsigned int TemperingMaskC = 0xefc60000;

unsigned int _mt[N]; /* the array for the state vector  */
int _mti;

static unsigned int _mag01[] = { 0x0, MatrixA };



// 9007199254740991.0 is the maximum double value which the 53 significand
// can hold when the exponent is 0.
const double FiftyThreeBitsOf1s = 9007199254740991.0;
// Multiply by inverse to (vainly?) try to avoid a division.
const double Inverse53BitsOf1s = 1.0 / FiftyThreeBitsOf1s;
const double OnePlus53BitsOf1s = FiftyThreeBitsOf1s + 1;
const double InverseOnePlus53BitsOf1s = 1.0 / OnePlus53BitsOf1s;


static unsigned int temperingShiftU(unsigned int y) {
	return (y >> 11);
}

static unsigned int temperingShiftS(unsigned int y) {
	return (y << 7);
}

static unsigned int temperingShiftT(unsigned int y) {
	return (y << 15);
}

static unsigned int temperingShiftL(unsigned int y) {
	return (y >> 18);
}


/// <summary>
/// Generates a new pseudo-random <see cref="unsigned int"/>.
/// </summary>
/// <returns>A pseudo-random <see cref="unsigned int"/>.</returns>
unsigned int GenerateUInt32() {
	unsigned int y;

	/* _mag01[x] = x * MatrixA  for x=0,1 */
	if (_mti >= N) /* generate N words at one time */ {
		int kk = 0;

		for (; kk < N - M; ++kk) {
			y = (_mt[kk] & UpperMask) | (_mt[kk + 1] & LowerMask);
			_mt[kk] = _mt[kk + M] ^ (y >> 1) ^ _mag01[y & 0x1];
		}

		for (; kk < N - 1; ++kk) {
			y = (_mt[kk] & UpperMask) | (_mt[kk + 1] & LowerMask);
			_mt[kk] = _mt[kk + (M - N)] ^ (y >> 1) ^ _mag01[y & 0x1];
		}

		y = (_mt[N - 1] & UpperMask) | (_mt[0] & LowerMask);
		_mt[N - 1] = _mt[M - 1] ^ (y >> 1) ^ _mag01[y & 0x1];

		_mti = 0;
	}

	y = _mt[_mti++];
	y ^= temperingShiftU(y);
	y ^= temperingShiftS(y) & TemperingMaskB;
	y ^= temperingShiftT(y) & TemperingMaskC;
	y ^= temperingShiftL(y);

	return y;
}

double compute53BitRandom(double translate, double scale) {
	// get 27 pseudo-random bits
	unsigned long a = (unsigned long)GenerateUInt32() >> 5;
	// get 26 pseudo-random bits
	unsigned long b = (unsigned long)GenerateUInt32() >> 6;

	// shift the 27 pseudo-random bits (a) over by 26 bits (* 67108864.0) and
	// add another pseudo-random 26 bits (+ b).
	return ((a * 67108864.0 + b) + translate) * scale;

	// What about the following instead of the above? Is the multiply better? 
	// Why? (Is it the FMUL instruction? Does this count in .Net? Will the JITter notice?)
	//return BitConverter.Int64BitsToDouble((a << 26) + b));
}

/// <summary>
/// Returns the next pseudo-random <see cref="double"/> value.
/// </summary>
/// <returns>A pseudo-random double floating point value.</returns>
/// <remarks>
/// <para>
/// There are two common ways to create a double floating point using MT19937: 
/// using <see cref="GenerateUInt32"/> and dividing by 0xFFFFFFFF + 1, 
/// or else generating two double words and shifting the first by 26 bits and 
/// adding the second.
/// </para>
/// <para>
/// In a newer measurement of the randomness of MT19937 published in the 
/// journal "Monte Carlo Methods and Applications, Vol. 12, No. 5-6, pp. 385 � 393 (2006)"
/// entitled "A Repetition Test for Pseudo-Random Number Generators",
/// it was found that the 32-bit version of generating a double fails at the 95% 
/// confidence level when measuring for expected repetitions of a particular 
/// number in a sequence of numbers generated by the algorithm.
/// </para>
/// <para>
/// Due to this, the 53-bit method is implemented here and the 32-bit method
/// of generating a double is not. If, for some reason,
/// the 32-bit method is needed, it can be generated by the following:
/// <code>
/// (double)NextUInt32() / ((unsigned long)unsigned int.MaxValue + 1);
/// </code>
/// </para>
/// </remarks>
double NextDouble() {
	return compute53BitRandom(0, InverseOnePlus53BitsOf1s);
}

/// <summary>
/// Returns the next pseudo-random <see cref="unsigned int"/>.
/// </summary>
/// <returns>A pseudo-random <see cref="unsigned int"/> value.</returns>
unsigned int NextUInt32() {
	return GenerateUInt32();
}

/// <summary>
/// Returns the next pseudo-random <see cref="unsigned int"/> 
/// up to <paramref name="maxValue"/>.
/// </summary>
/// <param name="maxValue">
/// The maximum value of the pseudo-random number to create.
/// </param>
/// <returns>
/// A pseudo-random <see cref="unsigned int"/> value which is at most <paramref name="maxValue"/>.
/// </returns>
unsigned int NextUInt32(unsigned int maxValue) {
	return (unsigned int)(GenerateUInt32() / ((double)UINT_MAX / maxValue));
}

/// <summary>
/// Returns the next pseudo-random <see cref="unsigned int"/> at least 
/// <paramref name="minValue"/> and up to <paramref name="maxValue"/>.
/// </summary>
/// <param name="minValue">The minimum value of the pseudo-random number to create.</param>
/// <param name="maxValue">The maximum value of the pseudo-random number to create.</param>
/// <returns>
/// A pseudo-random <see cref="unsigned int"/> value which is at least 
/// <paramref name="minValue"/> and at most <paramref name="maxValue"/>.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// If <c><paramref name="minValue"/> &gt;= <paramref name="maxValue"/></c>.
/// </exception>
unsigned int NextUInt32(unsigned int minValue, unsigned int maxValue) /* throws ArgumentOutOfRangeException */
{
	if (minValue >= maxValue) {
		unsigned int tmpValue = minValue;
		minValue = maxValue;
		maxValue = tmpValue;
	}

	if (maxValue == minValue) {
		return minValue;
	}

	return (unsigned int)(GenerateUInt32() / ((double)UINT_MAX / (maxValue - minValue)) + minValue);
}

/// <summary>
/// Returns the next pseudo-random <see cref="int"/> up to <paramref name="maxValue"/>.
/// </summary>
/// <param name="maxValue">The maximum value of the pseudo-random number to create.</param>
/// <returns>
/// A pseudo-random <see cref="int"/> value which is at most <paramref name="maxValue"/>.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// When <paramref name="maxValue"/> &lt; 0.
/// </exception>
int Next(int maxValue) {
	if (maxValue <= 1) {
		return 0;
	}

	return (int)(NextDouble() * maxValue);
}

/// <summary>
/// Returns the next pseudo-random <see cref="int"/> 
/// at least <paramref name="minValue"/> 
/// and up to <paramref name="maxValue"/>.
/// </summary>
/// <param name="minValue">The minimum value of the pseudo-random number to create.</param>
/// <param name="maxValue">The maximum value of the pseudo-random number to create.</param>
/// <returns>A pseudo-random int value which is at least <paramref name="minValue"/> and at 
/// most <paramref name="maxValue"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// If <c><paramref name="minValue"/> &gt;= <paramref name="maxValue"/></c>.
/// </exception>
int Next(int minValue, int maxValue) {
	if (maxValue < minValue) {
		unsigned int tmpValue = minValue;
		minValue = maxValue;
		maxValue = tmpValue;
	}

	if (maxValue == minValue) {
		return minValue;
	}

	return Next(maxValue - minValue) + minValue;
}

/// <summary>
/// Returns the next pseudo-random <see cref="int"/>.
/// </summary>
/// <returns>A pseudo-random <see cref="int"/> value.</returns>
int Next() {
	return Next(INT_MAX);
}

/// <summary>
/// Fills a buffer with pseudo-random bytes.
/// </summary>
/// <param name="buffer">The buffer to fill.</param>
/// <exception cref="ArgumentNullException">
/// If <c><paramref name="buffer"/> == <see langword="null"/></c>.
/// </exception>
void NextBytes(unsigned char buffer[]) {
	// [codekaizen: corrected this to check null before checking length.]
	if (buffer == NULL) {
		return;
	}

	int bufLen = sizeof(buffer) / sizeof(unsigned char);

	for (int idx = 0; idx < bufLen; ++idx) {
		buffer[idx] = (unsigned char)Next(256);
	}
}

/// <summary>
/// Returns a pseudo-random number greater than or equal to zero, and 
/// either strictly less than one, or less than or equal to one, 
/// depending on the value of the given parameter.
/// </summary>
/// <param name="includeOne">
/// If <see langword="true"/>, the pseudo-random number returned will be 
/// less than or equal to one; otherwise, the pseudo-random number returned will
/// be strictly less than one.
/// </param>
/// <returns>
/// If <paramref name="includeOne"/> is <see langword="true"/>, 
/// this method returns a double-precision pseudo-random number greater than 
/// or equal to zero, and less than or equal to one. 
/// If <paramref name="includeOne"/> is <see langword="false"/>, this method
/// returns a double-precision pseudo-random number greater than or equal to zero and
/// strictly less than one.
/// </returns>
double NextDouble(bool includeOne) {
	return includeOne ? compute53BitRandom(0, Inverse53BitsOf1s) : NextDouble();
}

/// <summary>
/// Returns a pseudo-random number greater than 0.0 and less than 1.0.
/// </summary>
/// <returns>A pseudo-random number greater than 0.0 and less than 1.0.</returns>
double NextDoublePositive() {
	return compute53BitRandom(0.5, Inverse53BitsOf1s);
}

/// <summary>
/// Returns a pseudo-random number between 0.0 and 1.0.
/// </summary>
/// <returns>
/// A single-precision floating point number greater than or equal to 0.0, 
/// and less than 1.0.
/// </returns>
float NextSingle() {
	return (float)NextDouble();
}

/// <summary>
/// Returns a pseudo-random number greater than or equal to zero, and either strictly
/// less than one, or less than or equal to one, depending on the value of the
/// given boolean parameter.
/// </summary>
/// <param name="includeOne">
/// If <see langword="true"/>, the pseudo-random number returned will be 
/// less than or equal to one; otherwise, the pseudo-random number returned will
/// be strictly less than one.
/// </param>
/// <returns>
/// If <paramref name="includeOne"/> is <see langword="true"/>, this method returns a
/// single-precision pseudo-random number greater than or equal to zero, and less
/// than or equal to one. If <paramref name="includeOne"/> is <see langword="false"/>, 
/// this method returns a single-precision pseudo-random number greater than or equal to zero and
/// strictly less than one.
/// </returns>
float NextSingle(bool includeOne) {
	return (float)NextDouble(includeOne);
}

/// <summary>
/// Returns a pseudo-random number greater than 0.0 and less than 1.0.
/// </summary>
/// <returns>A pseudo-random number greater than 0.0 and less than 1.0.</returns>
float NextSinglePositive() {
	return (float)NextDoublePositive();
}

void InitSeed(unsigned int seed) {
	_mt[0] = seed & 0xffffffffU;

	for (_mti = 1; _mti < N; _mti++) {
		_mt[_mti] = (unsigned int)(1812433253U * (_mt[_mti - 1] ^ (_mt[_mti - 1] >> 30)) + _mti);
		// See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. 
		// In the previous versions, MSBs of the seed affect   
		// only MSBs of the array _mt[].                        
		// 2002/01/09 modified by Makoto Matsumoto             
		_mt[_mti] &= 0xffffffffU;
		// for >32 bit machines
	}
}

void InitSeed(unsigned int key[]) {
	int i, j, k;
	InitSeed(19650218U);

	int keyLength = sizeof(key) / sizeof(unsigned int);
	i = 1; j = 0;
	k = (N > keyLength ? N : keyLength);

	for (; k > 0; k--) {
		_mt[i] = (unsigned int)((_mt[i] ^ ((_mt[i - 1] ^ (_mt[i - 1] >> 30)) * 1664525U)) + key[j] + j); /* non linear */
		_mt[i] &= 0xffffffffU; // for WORDSIZE > 32 machines
		i++; j++;
		if (i >= N) { _mt[0] = _mt[N - 1]; i = 1; }
		if (j >= keyLength) j = 0;
	}

	for (k = N - 1; k > 0; k--) {
		_mt[i] = (unsigned int)((_mt[i] ^ ((_mt[i - 1] ^ (_mt[i - 1] >> 30)) * 1566083941U)) - i); /* non linear */
		_mt[i] &= 0xffffffffU; // for WORDSIZE > 32 machines
		i++;

		if (i < N) {
			continue;
		}

		_mt[0] = _mt[N - 1]; i = 1;
	}

	_mt[0] = 0x80000000U; // MSB is 1; assuring non-zero initial array
}


Handle<Value> Seed(const Arguments& args) {
	HandleScope scope;

	if (args.Length() < 1) {
		ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
		return scope.Close(Undefined());
	}

	if (!args[0]->IsNumber()) {
		ThrowException(Exception::TypeError(String::New("Wrong arguments")));
		return scope.Close(Undefined());
	}

	InitSeed((unsigned long)args[0]->NumberValue());

	return scope.Close(args[0]);
}

Handle<Value> Next(const Arguments& args) {
	HandleScope scope;
	return scope.Close(Number::New(Next()));
}

Handle<Value> NextMax(const Arguments& args) {
	HandleScope scope;

	if (args.Length() < 1) {
		ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
		return scope.Close(Undefined());
	}

	if (!args[0]->IsNumber()) {
		ThrowException(Exception::TypeError(String::New("Wrong arguments")));
		return scope.Close(Undefined());
	}

	return scope.Close(Number::New(Next((unsigned long)args[0]->NumberValue())));
}

Handle<Value> NextMinMax(const Arguments& args) {
	HandleScope scope;

	if (args.Length() < 2) {
		ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
		return scope.Close(Undefined());
	}

	if (!args[0]->IsNumber()) {
		ThrowException(Exception::TypeError(String::New("Wrong arguments")));
		return scope.Close(Undefined());
	}

	if (!args[1]->IsNumber()) {
		ThrowException(Exception::TypeError(String::New("Wrong arguments")));
		return scope.Close(Undefined());
	}

	return scope.Close(Number::New(Next((unsigned long)args[0]->NumberValue(), (unsigned long)args[1]->NumberValue())));
}

Handle<Value> NextDouble(const Arguments& args) {
	HandleScope scope;
	return scope.Close(Number::New(NextDouble()));
}

Handle<Value> NextDoubleOne(const Arguments& args) {
	HandleScope scope;

	return scope.Close(Number::New(NextDouble(true)));
}

Handle<Value> NextDoublePositive(const Arguments& args) {
	HandleScope scope;
	return scope.Close(Number::New(NextDoublePositive()));
}

void init(Handle<Object> exports) {
	exports->Set(String::NewSymbol("Seed"),
		FunctionTemplate::New(Seed)->GetFunction());

	exports->Set(String::NewSymbol("Next"),
		FunctionTemplate::New(Next)->GetFunction());

	exports->Set(String::NewSymbol("NextMax"),
		FunctionTemplate::New(NextMax)->GetFunction());

	exports->Set(String::NewSymbol("NextMinMax"),
		FunctionTemplate::New(NextMinMax)->GetFunction());

	exports->Set(String::NewSymbol("NextDouble"),
		FunctionTemplate::New(NextDouble)->GetFunction());

	exports->Set(String::NewSymbol("NextDoubleOne"),
		FunctionTemplate::New(NextDoubleOne)->GetFunction());

	exports->Set(String::NewSymbol("NextDoublePositive"),
		FunctionTemplate::New(NextDoublePositive)->GetFunction());
}

NODE_MODULE(mt19937, init)
