#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>

#define prng_IMPORT
#include "prng.h"

// To use the system random function, define this macro.
//#define prng_USE_SYSTEM

// To use the this module's built-in random function, define this macro:
#define prng_USE_INTERNAL


#ifdef prng_USE_SYSTEM
#define prng_RAND_MAX RAND_MAX
#define prng_SRAND(seed) srand(seed)
#define prng_RAND() (rand())
#endif


#ifdef prng_USE_INTERNAL

/*
 * Uses the C99, C11 suggestion in the ISO/IEC 9899:
 * https://en.wikipedia.org/wiki/Linear_congruential_generator
 */

#define prng_RAND_MAX 0x7fff
static unsigned int prng_seed;

static void prng_SRAND(unsigned int seed)
{
	prng_seed = seed;
}

static int prng_RAND()
{
	prng_seed = 1103515245 * prng_seed + 12345;
	return (prng_seed >> 16) & prng_RAND_MAX;
}

#endif


static int inited;

/** Mask of bits covered by numbers returned by rand(). */
static unsigned int rand_max_mask;

/** How many random bits per rand() value returned. */
static int bits_per_rand;


static void prng_init()
{
	inited = 1;
	
	/* Determines how many random bits are returned by rand(): */
	bits_per_rand = 8*sizeof(unsigned int);
	rand_max_mask = (unsigned int) -1;
	while(rand_max_mask > prng_RAND_MAX){
		rand_max_mask >>= 1;
		bits_per_rand--;
	}
	
	/*
	 * To get different sequences each time the process is started,
	 * set a "random" seed based on: current wall time; current number of
	 * tick since computer start; current process ID.
	 */
	struct timeval tv; 
	gettimeofday(&tv,NULL);
	prng_setSeed( tv.tv_sec + tv.tv_usec + clock() + getpid() );
}


void prng_setSeed(unsigned int seed)
{
	if( ! inited )
		prng_init();
	prng_SRAND(seed);
}


/*
 * By generating, say, 1 million of random numbers on a very wide range of the
 * int spectrum, about half of them should fall before the middle, right?
 * The typical naive implementation rand() % n to get a value in the range
 * [0,n-1] does not work and retrieves something like 666666 rather than
 * the expected 500000.
 * 
 * Gets rid of this nasty "modulo bias" as suggested by:
 * 
 * Mark Amery
 * http://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator
 * 
 * The penalty is that the random bits sequence must be generated twice in about
 * the 50% of the cases.
 * 
 * The difference here is that the generated random number can be larger than
 * RAND_MAX, so we must keep the algorithm more general and replace RAND_MAX
 * with a variable bits_covered.
 */
int prng_getIntInRange(int min, int max)
{
	assert(min <= max);
	if( ! inited )
		prng_init();
	unsigned int range_max = (unsigned int) (max - min);
	unsigned int bits; // generated random bits
	unsigned int bits_covered; // mask of covered generated random bits
	do {
		
		/*
		 * Generate random bits enough to cover the requested range [0,range_max].
		 * Keep generating random numbers and shift left their bits accounting
		 * in bits_covered the mask of the covered bits. Continue until
		 * bits_covered >= range_max.
		 */
		bits = prng_RAND();
		bits_covered = rand_max_mask;
		while(bits_covered < range_max){
			bits_covered = (bits_covered << bits_per_rand) | rand_max_mask;
			bits = (bits << bits_per_rand) | (prng_RAND() & rand_max_mask);
		}
		
		/*
		 * Now 0 <= bits <= bits_covered. If range_max equals bits_covered,
		 * there is not the modulus bias problem and we can safely returns these
		 * bits. This also prevents several possible overflows below.
		 */
		if( range_max == bits_covered )
			return min + (int) bits;
		
		/*
		 * Now 0 <= bits < bits_covered but we must cast bits to the requested
		 * range [0,range_max] preventing the modulo bias problem, so a simple
		 * bits % (range_max+1) does not work. We may think at this interval as
		 * a sequence of sections, each section containing (bits_covered+1) /
		 * (range_max+1) numbers plus the last, possibly shorter, section
		 * containing the remaining numbers:
		 * 
		 *    rem = (bits_covered + 1) % (range_max + 1)
		 * 
		 * If rem==0 there is no bias problem and the modulo operator
		 * will return each number with the same probability. Otherwise, if
		 * "bits" falls in this section, we must reject it and try another one.
		 * But there is a catch: (bits_covered + 1) may overflow, so we must
		 * evaluate the reminder on (bits_covered + 1 - (range_max + 1)) instead
		 * getting the same result without overflow:
		 */
		unsigned int rem = (bits_covered - range_max) % (range_max + 1);
		if( rem == 0 || bits <= bits_covered - rem )
			break;
		
	} while( 1 );
	
	/*
	 * Maps 0 <= bits <= range_max into [min,max].
	 */
	return min + (int) (bits % (range_max + 1));
}


double prng_getDouble()
{
	if( ! inited )
		prng_init();
	unsigned int bits = (unsigned int) prng_RAND();
	int bits_no = bits_per_rand;
	/*
	 * Typical implementation of rand() returns 31 bits (Linux, Cygwin);
	 * 31 bits are enough, do not waste one more call to rand() just to fill 1 bit:
	 */
	while( bits_no < 8*sizeof(unsigned int) - 1 ){
		bits = (bits << bits_per_rand) | prng_RAND();
		bits_no += bits_per_rand;
	}
	/* ...but now fill the highest bit if needed: */
	if( bits_no < 8*sizeof(unsigned int) )
		bits <<= 1;
	return (double) bits / ((double) UINT_MAX + 1.0);
}


double prng_getDouble2()
{
	return 2.0 * prng_getDouble() - 1.0;
}


void prng_fill(void *buffer, int buffer_len)
{
	if( ! inited )
		prng_init();
	unsigned int bits = 0;
	int bits_ready = 0;
	while(buffer_len > 0){
		while(bits_ready < 8){
			bits = (bits << bits_per_rand) | prng_RAND();
			bits_ready += bits_per_rand;
			if( bits_ready > 8 * sizeof(unsigned int) )
				bits_ready = 8 * sizeof(unsigned int);
		}
		*((char *) buffer) = bits;
		bits >>= 8;
		bits_ready -= 8;
		buffer++;
		buffer_len--;
	}
}