/*
								+----------------------------------+
								|                                  |
								| ***  Random generator model  *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2007   |
								|                                  |
								|            RandGen.h             |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __RANDOM_GENERATOR_INCLUDED
#define __RANDOM_GENERATOR_INCLUDED

/**
 *	@file RandGen.h
 *	@brief generic random generator models
 *	@date 2007
 *	@author -tHE SWINe-
 *
 *	@date 2007-10-17
 *
 *	added CMTGenerator::CMTGenerator(unsigned int n_seed = 0) constructor for greater convenience
 *
 */

#include "Integer.h"
#include "Mersene.h" // Mersene Twister
#include <stdlib.h> // srand(), rand()

/**
 *	@brief generic minimal random number generator class (intended
 *		for experiments with random number generation methods)
 */
class CRandomGeneratorModel {
public:
	/**
	 *	@brief sets a new seed and restarts the generator with this seed
	 *	@param[in] n_seed is random generator seed
	 */
	virtual void Seed(unsigned int n_seed) = 0;

	/**
	 *	@brief generates a random number on [0, 1] real interval
	 *	@return Returns floating-point random number in range 0 to 1 inclusive.
	 */
	virtual float f_Rand0() const = 0;

	/**
	 *	@brief generates a random number on [0, 1) real interval
	 *	@return Returns floating-point random number in range 0 to 1 inclusive.
	 */
	virtual float f_Rand1() const = 0;

	/**
	 *	@brief generates a random number on (0, 1) real interval
	 *	@return Returns floating-point random number in range 0 to 1 inclusive.
	 */
	virtual float f_Rand2() const = 0;
};

/**
 *	@brief Mersene Twister-based random number generator
 */
class CMTGenerator : public CRandomGeneratorModel {
protected:
	CMerseneTwister m_twister; /**< @brief Mersene Twister instance */

public:
	/**
	 *	@brief default constructor; has no effect
	 */
	inline CMTGenerator()
	{}

	/**
	 *	@brief constructor; initializes the random generator
	 */
	inline CMTGenerator(unsigned int n_seed)
	{
		Seed(n_seed);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::Seed()
	 */
	virtual void Seed(unsigned int n_seed)
	{
		m_twister.init_genrand(n_seed);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand0()
	 */
	virtual float f_Rand0() const
	{
		return (float)m_twister.genrand_real1();
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand1()
	 */
	virtual float f_Rand1() const
	{
		return (float)m_twister.genrand_real2();
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand2()
	 */
	virtual float f_Rand2() const
	{
		return (float)m_twister.genrand_real3();
	}
};

/**
 *	@brief random number generator, based on standard library
 */
class CCLibGenerator : public CRandomGeneratorModel {
public:
	/**
	 *	@brief default constructor; has no effect
	 */
	inline CCLibGenerator()
	{}

	/**
	 *	@brief constructor; initializes the random generator
	 */
	inline CCLibGenerator(unsigned int n_seed)
	{
		Seed(n_seed);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::Seed()
	 */
	virtual void Seed(unsigned int n_seed)
	{
		srand(n_seed);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand0()
	 */
	virtual float f_Rand0() const
	{
		return rand() / float(RAND_MAX);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand1()
	 */
	virtual float f_Rand1() const
	{
		_ASSERTE(RAND_MAX < INT_MAX);
		return rand() / float(RAND_MAX + 1);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand2()
	 */
	virtual float f_Rand2() const
	{
		_ASSERTE(RAND_MAX < INT_MAX);
		return (rand() + 1) / float(RAND_MAX + 1);
	}
};

/**
 *	@brief a simple and fast congruential random nunber generator
 */
class CCongruentialGenerator : public CRandomGeneratorModel {
protected:
	mutable uint32_t m_n_x; /**< @brief random generator state */

public:
	/**
	 *	@brief default constructor; has no effect
	 */
	inline CCongruentialGenerator()
	{}

	/**
	 *	@brief constructor; initializes the random generator
	 */
	inline CCongruentialGenerator(unsigned int n_seed)
	{
		Seed(n_seed);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::Seed()
	 */
	virtual void Seed(unsigned int n_seed)
	{
		m_n_x = n_seed;
		n_Rand32(); // do not return seed
	}

	/**
	 *	@brief generates a 32-bit unsigned random number
	 *	@return Returns the generated random number.
	 */
	uint32_t n_Rand32() const
	{
		m_n_x = m_n_x * 1664525U + 1013904223U;
		return m_n_x;
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand0()
	 */
	virtual float f_Rand0() const
	{
		return n_Rand32() / float(UINT32_MAX);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand1()
	 */
	virtual float f_Rand1() const
	{
		return float(n_Rand32() / (double(UINT32_MAX) + 1));
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand2()
	 */
	virtual float f_Rand2() const
	{
		_ASSERTE(RAND_MAX < INT_MAX);
		return float((double(n_Rand32()) + 1) / (double(UINT32_MAX) + 1));
	}
};

/**
 *	@brief Numerical Recipes' long generator (period 3.138 * 10^57)
 */
class CNRLongGenerator : public CRandomGeneratorModel {
protected:
	mutable uint64_t m_n_u; /**< @brief random generator state */
	mutable uint64_t m_n_v; /**< @brief random generator state */
	mutable uint64_t m_n_w; /**< @brief random generator state */

public:
	/**
	 *	@brief constructor; initializes the random generator
	 */
	CNRLongGenerator(uint64_t n_seed = 0)
		:m_n_v((uint64_t(0x38ECAC5F) << 32) |
		0xB3251641U/*4101842887655102017LL*/), m_n_w(1)
	{
		Seed64(n_seed);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::Seed()
	 */
	void Seed(unsigned int n_seed)
	{
		_ASSERTE(n_seed != m_n_v);
		m_n_u = n_seed ^ m_n_v;
		n_Rand64();
		m_n_v = m_n_u;
		n_Rand64();
		m_n_w = m_n_v;
		n_Rand64();
	}

	/**
	 *	@copydoc CRandomGeneratorModel::Seed()
	 */
	void Seed64(uint64_t n_seed)
	{
		_ASSERTE(n_seed != m_n_v);
		m_n_u = n_seed ^ m_n_v;
		n_Rand64();
		m_n_v = m_n_u;
		n_Rand64();
		m_n_w = m_n_v;
		n_Rand64();
	}

	/**
	 *	@brief generates a 64-bit unsigned random number
	 *	@return Returns the generated random number.
	 */
	uint64_t n_Rand64() const
	{
		m_n_u = m_n_u * ((uint64_t(0x27BB2EE6U) << 32) | 0x87B0B0FDU)/*2862933555777941757LL*/ +
			((uint64_t(0x61C88646U) << 32) | 0x80B583BFU)/*7046029254386353087LL*/;
		m_n_v ^= m_n_v >> 17;
		m_n_v ^= m_n_v << 31;
		m_n_v ^= m_n_v >> 8;
		m_n_w = 4294957665U * (m_n_w & 0xffffffff) + (m_n_w >> 32);
		uint64_t x = m_n_u ^ (m_n_u << 21);
		x ^= x >> 35;
		x ^= x << 4;
		return (x + m_n_v) ^ m_n_w;
	}

	/**
	 *	@brief generates a 32-bit unsigned random number
	 *	@return Returns the generated random number.
	 */
	uint32_t n_Rand32() const
	{
		return uint32_t(n_Rand64());
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand0()
	 */
	virtual float f_Rand0() const
	{
		return float(n_Rand64() * 5.42101086242752217e-20);
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand1()
	 */
	virtual float f_Rand1() const
	{
		return f_Rand0() * (1 - 1e-5f); // this is not good
	}

	/**
	 *	@copydoc CRandomGeneratorModel::f_Rand2()
	 */
	virtual float f_Rand2() const
	{
		return f_Rand0() * (1 - 2e-5f) + 1e-5f; // this is not good
	}
};

#endif // !__RANDOM_GENERATOR_INCLUDED
