/*
 * Pseudo Random Number Generator (pRNG)
 * Usando 3 metodos diferentes:
 *	- Cuadrado Medio
 *	- Congruencial Mixto
 *	- Congruencial Multiplicativo
 *
 * 21-Sept-2005
 *
 * nitrous<at>danitrous<dot>org
 */

#include<stdio.h>
#include<stdlib.h>
#include<math.h>

#define A	13	/* impar, no divisible entre 3 y 5; en cong. multip. (8*2-3) */
#define C	5
#define M	(pow((double)2, (double) 12)) /* 4096 */
#define X	7	/* impar, no divisible entre M=4096 y viceversa */

unsigned int pide_n()
{
	unsigned int ntmp;

	printf("\n\nCuantos numeros desea generar?: ");
	scanf("%d", &ntmp);

	return ntmp;
}

void cuad_medio(unsigned int n)
{
	unsigned long seed = 4127, seed_seed, foo;	/* Semilla inicial */
	int t, m, i, med;
	char y[15], z[5];
	foo = n;

	memset(y, 0x00, 15);

	printf("N\tSemilla\t\tSemilla^2\tPseudo-Random\n\n");

	while(foo--){
		seed_seed = seed * seed;
		ltoa(seed_seed,y,10);	/* integer to string */
		t = strlen(y);

		if((t%2) == 1){
			y[t]='0';
			y[t+1]='\0';
		}

		t = strlen(y);
		med = (t/2) - 2;

		for (i = 0; i < 4 ; i++)
			z[i]=y[med++];
		z[4]='\0';

		printf("%d\t%d\t\t%s\t\t%s\n", (n - foo), seed, y, z);

 		seed = atol(z);
	}

	exit(1);
}

void cong_mixto(unsigned int n)
{
	unsigned int Xn = 0, x = X, foo, m;
	float Random;

	foo = n;
	m = M;

	printf("N\tXn\t[(%d * Xn) + %d ]mod(%.f)\tPseudo-Random\n\n", A, C, M);

	while(foo--){
		Xn = ((A * x + C) % m);
		Random = (float) Xn / M;
		printf("%d\t%d\t%d\t\t\t\t%.4f\n", (n - foo), x, Xn, Random);
		x = Xn;
	}

	exit(2);
}

void cong_mult(unsigned int n)
{
	unsigned int Xn = 0, x = X, foo, m;
	float Random;

	foo = n;
	m = M;

	printf("N\tXn\t[(%d * Xn) + %d ]mod(%.f)\tPseudo-Random\n\n", A, C, M);

	while(foo--){
		Xn = ((A * x) % m);
		Random = (float) (Xn % m);
		printf("%d\t%d\t%d\t\t\t\t%.f\n", (n - foo), x, Xn, Random);
		x = Xn;
	}

	exit(3);
}

main()
{
	unsigned int opt, n;

	printf("\nSeleccione un metodo: \n\n\n"
			"\t\t1.- Cuadrado Medio\n\n"
			"\t\t2.- Congruencial Mixto\n\n"
			"\t\t3.- Congruencial Multiplicativo\n\n"
			"\t\tOpcion: ");

	opt = fgetc(stdin);

	switch(opt){
		case '1':
			n = pide_n();
			cuad_medio(n);
		case '2':
			n = pide_n();
			cong_mixto(n);
		case '3':
			n = pide_n();
			cong_mult(n);
		default:
			fprintf(stderr, "\nOpcion invalida... Saliendo\n");
			exit(-1);
	}
}
