/****************************************************************************/
/* Verifica los tests especificados en FIPS 140.2 (Nov. 1999)               */
/* (FEDERAL INFORMATION PROCESSING STANDARD PUBLICATION)                    */
/* para generadores de números aleatorios.                                  */
/*                                                                          */
/* Jaime Suarez <mcripto@bigfoot.com> 2003                                  */
/* en http://elparaiso.mat.uned.es                                            */
/****************************************************************************/

#define LONGBUF 2500		/* 2500 bytes=20000 bits no modificar */
#define BIT(x,n) ( ((x) >> (n)) & 1)
#define HNIB(x) (((x)>>4)&0xF)
#define LNIB(x) ((x)&0xF)

#include <stdio.h>
typedef unsigned char uchar;

int monobit(uchar *buffer);
int poker(uchar *buffer);
int runs(uchar *buffer);
int longrun(uchar *buffer);

main(int argc, char *argv[])
{
	uchar buffer[LONGBUF];
	FILE *fi;
	
	if (argc<2) {
		printf("Uso: %s <fichero>\n",argv[0]);
		printf("tests estadísticos de aleatoriedad del fichero.\n");
		return 1;
	}
	
	if ((fi=fopen(argv[1],"r"))==NULL) {
		printf("Error: no puedo abrir %s\n",argv[1]);
		return 1;
	}
	
	if (fread((void *)buffer,LONGBUF,1,fi) != 1) {
		printf("Error leyendo %d bytes de %s.\n",LONGBUF,argv[1]);
		fclose(fi);
		return 1;
	}
	fclose(fi);

	printf("Fichero: %s\n",argv[1]);
	printf("Test monobit: %s  ",(monobit(buffer))? "FALLO" : "ok");
	printf("Test poker: %s  ",(poker(buffer))? "FALLO" : "ok");
	printf("Test runs: %s  ",(runs(buffer))? "FALLO" : "ok");
	printf("Test longrun: %s\n",(longrun(buffer))? "FALLO":"ok");

	return 0;
}

/*
 * Comprueba si el número de bits iguales a uno está dentro de unos límites
 */
int monobit(uchar *buffer)
{
	int unos=0,i,j;
	uchar ch;

	for (i=0; i<LONGBUF; i++) 
		for (j=0; j<8; j++) 
			if (BIT(*(buffer+i),j)) unos++;
	
	if (9725<unos && unos<10275) return 0;	/* Ok, error de 0.0001 */
	else return 1;
}

/*
 * Divididos los 20000 bytes en grupos de 4 consecutivos, se analiza si
 * los 16 resultados posibles (0000,0001,...1111) están repartidos
 * "regularmente".
 */
int poker(uchar *buffer)
{
	int f[16],i;
	uchar ch;
	float x;

	for (i=0; i<16; i++) f[i]=0;
	
	for (i=0; i<LONGBUF; i++) {
		ch=*(buffer+i);
		f[HNIB(ch)]++;
		f[LNIB(ch)]++;
	}

	
	x=0.0;
	for (i=0; i<16; i++)
		x += f[i]*f[i];
	x = (x*16.0/5000.0)-5000.0;
	
	if (2.16< x && x<46.17) return 0;	/* Nivel de error 0.0001 */
	else return 1;
}

/*
 * Este test estudia si las secuencias de 1s y 0s repetidos tienen las
 * longitudes esperadas.
 */
int runs(uchar *buffer)
{
	int i,j,cuenta;
	int r[6][2];
	uchar ch;
	
	for(i=0; i<6; i++) { 
		r[i][0]=0;
		r[i][1]=0;
	}

	ch=BIT(*buffer,0);
	cuenta=0;
	for (i=0; i<LONGBUF; i++) {
		for (j=0; j<8; j++) {
			if (BIT(*(buffer+i),j)==ch) cuenta++;
			else {
				if (cuenta>6) cuenta=6;
				r[cuenta-1][ch]++;
				ch = !ch;
				cuenta=1;
			}
		}
	}
	if (cuenta>6) cuenta=6;
	r[cuenta-1][ch]++;

	for (i=0; i<2; i++) {
		if (r[0][i]<2343 || r[0][i]>2657)	return 1;
		if (r[1][i]<1135 || r[1][i]>1365)	return 1;
		if (r[2][i]< 542 || r[2][i]> 708)	return 1;
		if (r[3][i]< 251 || r[3][i]> 373)	return 1;
		if (r[4][i]< 111 || r[4][i]> 201)	return 1;
		if (r[5][i]< 111 || r[5][i]> 201)	return 1;
	}	
	return 0;
}

/*
 * Para pasar este test, en los 20000 bits no debe haber una secuencia de
 * 26 bits o más iguales consecutivos.
 */
int longrun(uchar *buffer)
{
	int i,j,maxc=-1,cuenta;
	uchar ch;
	
	ch=BIT(*buffer,0);
	cuenta=0;
	for (i=0; i<LONGBUF; i++) {
		for (j=0; j<8; j++) {
			if (BIT(*(buffer+i),j)==ch) cuenta++;
			else {
				if (cuenta>maxc) maxc=cuenta;
				ch=!ch;
				cuenta=1;
			}
		}
	}
	if (cuenta>maxc) maxc=cuenta;

	if (maxc>=26) return 1;
	else return 0;
}