#include "RandomTest.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

// 记录有过失败的数据
// 
int fileNo = 1;
int testNo = 1;
int failed[TEST_NUM+1][SAMPLE_NUM+1] = {0};



int main(int argc, char * argv[])
{
    unsigned char *epsilon;
    int pass[TEST_NUM + 1];
    
    if ( (epsilon = (BitSequence *) calloc(SAMPLE_LEN, sizeof(BitSequence))) == NULL ) {
		printf("BITSTREAM DEFINITION:  Insufficient memory available.\n");
		return -1;
	}

	int ret = 0;

	int testVector[30] = {0};
	FILE *fp;
	char FileName[128] = {0};
	double passmin = 0.0;


	int loop = 1, passnum = 0;

	bzero(pass, sizeof(pass));


    printf("\n\n                I N P U T  B A S E D I R\n");
    printf("    Make sure the test datas are ./data/Random_[x].bin\n");
    printf("    Press enter to continue.");
    getchar();



	while (loop)
	{

		ChooseItems(&testVector[0]);
		/*      testVector[0] = 19;*/
		if (testVector[0] == 0)
			break;

		// 一键测试
		if (testVector[0] == 80)
		{
			passnum = 0;

			fprintf(stdout,">> one key test start.");

			for (int k=1; k<=TEST_NUM ; ++k )
			{
				testNo = k;
				printf("\n");
				pass[k] = 0;
				//	for (int i=1; i<=SAMPLE_NUM; i++)
				for (int i = 0; i < SAMPLE_NUM;)
				{
					fileNo = i;

					sprintf(FileName, "./data/Random_%d.txt", i+1);


					if((fp = fopen(FileName,"rb")),!fp)
					{
						printf("\n打开文件失败\n");
						ret = -1;
						goto CLR;
					}

					// read file and test
					readHexDigitsInBinaryFormat(fp,k, epsilon, pass);

					// close file
					if (EOF == fclose(fp))
					{
						printf("\n关闭文件失败\n");
						ret = -1;
						goto CLR;
					}
                    i++;
					if (0 == i%200)
					{
						printf("\n option %d: %d / %d pass.\n",k,pass[k],i);
					}
					if ( SAMPLE_NUM == i )
					{
						passmin = SAMPLE_NUM * (1 - ALPHA - 3 * sqrt( ALPHA*(1-ALPHA)/SAMPLE_NUM));
						if (pass[k] >= passmin)
						{
							passnum++;
							fprintf(stdout,"\n[O] option %d pass the test, result is %d / %d .", k, pass[k], SAMPLE_NUM);

						}
						else
						{
							fprintf(stdout,"\n[X] option %d fail the test, result is %d / %d .", k, pass[k], SAMPLE_NUM);

						}
					}
				}
			}
			if (passnum == sizeof(pass)/sizeof(pass[0]))
			{
				fprintf(stdout,"\n\n>> one key test end, all options passed.");

			}
			else
			{
				fprintf(stdout,"\n\n>> one key test end, some one failed.");

			}

		}
		else
		{
			printf("\n");
			pass[testVector[0]] = 0;
			testNo = testVector[0];

			for (int i=0; i<SAMPLE_NUM;)
			{
				fileNo = i;

				sprintf(FileName, "./data/random%03d.bin", i);

				// open file
				if((fp = fopen(FileName,"rb")),!fp)
				{
					printf("\n打开文件失败\n");
					ret = -1;
					goto CLR;
				}

				// read file and test
				readHexDigitsInBinaryFormat(fp,testVector[0], epsilon, pass);

				// close file
				if (EOF == fclose(fp))
				{
					printf("\n关闭文件失败\n");
					ret = -1;
					goto CLR;
				}
                i++;
				if (0 == i%200)
				{
					printf("\n option %d: %d / %d pass.\n",testVector[0],pass[testVector[0]],i);
				}
				if ( SAMPLE_NUM == i )
				{
					passmin = SAMPLE_NUM * (1 - ALPHA - 3 * sqrt( ALPHA*(1-ALPHA)/SAMPLE_NUM));
					if (pass[testVector[0]] >= passmin)
					{
						fprintf(stdout,"\n[O] option %d pass the test, result is %d / %d .", testVector[0], pass[testVector[0]], SAMPLE_NUM);
					}
					else
					{
						fprintf(stdout,"\n[X] option %d fail the test, result is %d / %d .", testVector[0], pass[testVector[0]], SAMPLE_NUM);
					}
				}
			}

		}

		printf("\n\n                F A I L E D  T E S T  D E T A I L S\n");
		int filePassResult[SAMPLE_NUM + 1] = {0};
		for (int j = 1; j < SAMPLE_NUM+1; j++){
			for (int i = 1; i  <= TEST_NUM; i++){
				if (failed[i][j]){
					printf("    random%d.bin failed test %d\n", j, i);
					filePassResult[j] = 1;
				}
			}
		}
		printf("\n\n                F A I L E D  T E S T  S U M A R R Y\n");
		printf("     The following files are failed in some test:\n");
		for (int j = 1; j < SAMPLE_NUM+1; j++){
			if (filePassResult[j]){
				printf("    random%i.bin\n", j);
			}
		}
		if (testVector[0] == 80){
			break;
		}else{
			printf("\n\nPress any key to go on!\n");
        	getchar();
		}

	}
	CLR:

	free(epsilon);
	return ret;
}

void ChooseItems(int *pChoice)
{
	printf("\n\n                R A N D O M N E S S   T E S T S\n");
	printf("                _________________________________\n\n");
	printf("    [01] monobit frequency test          (单比特频数检测)\n");
	printf("    [02] frequency test with a block     (块内频数检测)\n");
	printf("    [03] poker test                      (扑克检测, m=4)\n");

	printf("    [04] serial test                     (重叠子序列检测, m=2)\n");

	printf("    [05] runs test                       (游程总数检测)\n");
	printf("    [06] runs distribution test          (游程分布检测)\n");
	printf("    [07] test for the longest run of ones in a block\n");
	printf("                                         (块内最大“1”游程检测)\n");
	printf("    [08] binary derivative test          (二元推导检测, k=3)\n");

	printf("    [09] autocorrelation test            (自相关检测, d=1)\n");

	printf("    [10] binary matrix rank test         (矩阵秩检测)\n");
	printf("    [11] cumulative test                 (累加和检测)\n");
	printf("    [12] approximate entropy test        (近似熵检测)\n");
	printf("    [13] linear complexity test          (线性复杂度检测)\n");
	printf("    [14] Maurer's \"Universal Test\"       (Maurer通用统计检测)\n");
	printf("    [15] discrete fourier transform test (离散傅立叶检测)\n\n");

	printf("    [16] poker test                      (扑克检测, m=8)\n");
	printf("    [17] serial test                     (重叠子序列检测, m=5)\n");
	printf("    [18] binary derivative test          (二元推导检测, k=7)\n");
	printf("    [19] autocorrelation test            (自相关检测, d=2)\n");
	printf("    [20] autocorrelation test            (自相关检测, d=8)\n");
	printf("    [21] autocorrelation test            (自相关检测, d=16)\n\n");

	printf("    [80] One Key Test All\n");
	printf("    [00] Quit\n\n");
	printf("> Enter Choice: ");
	scanf("%d", pChoice);
	printf("\n");
}

void Test_Suite(unsigned char *epsilon, int n, int Choice, int *pass)
{
	if (TEST_FREQUENCY == Choice)
        pass[TEST_FREQUENCY] += Frequency(epsilon, n);

    if (TEST_BLOCK_FREQUENCY == Choice)
        pass[TEST_BLOCK_FREQUENCY] += BlockFrequency(epsilon, n);

    if ( TEST_CUSUM == Choice )
        pass[TEST_CUSUM] += CumulativeSums(epsilon, n);

    if ( TEST_RUNS == Choice )
        pass[TEST_RUNS] += Runs(epsilon, n);

    if ( TEST_LONGEST_RUN == Choice)
        pass[TEST_LONGEST_RUN] += LongestRunOfOnes(epsilon, n);

    if ( TEST_RANK == Choice)
        pass[TEST_RANK] += Rank(epsilon, n);

    if ( TEST_FFT == Choice)
        pass[TEST_FFT] += DiscreteFourierTransform(epsilon, n);

    if ( TEST_UNIVERSAL == Choice)
        pass[TEST_UNIVERSAL] += Universal(epsilon, n);

    if ( TEST_APEN == Choice)
        pass[TEST_APEN] += ApproximateEntropy(epsilon, n);

    if ( TEST_SERIAL2 == Choice)
        pass[TEST_SERIAL2] += Serial(epsilon, n, 2);

    if ( TEST_LINEARCOMPLEXITY == Choice)
        pass[TEST_LINEARCOMPLEXITY] += LinearComplexity(epsilon, n);

    if (TEST_POKER4 == Choice)
        pass[TEST_POKER4] += Poker(epsilon, n, 4);

    if (TEST_AUTOCORRELATION1 == Choice)
        pass[TEST_AUTOCORRELATION1] += Autocorrelation(epsilon, n, 1);

    if (TEST_BINARYDERIVATION3 == Choice)
        pass[TEST_BINARYDERIVATION3] += BinaryDerivative(epsilon, n, 3);

    if (TEST_RUNSDISTRIBUTION == Choice)
        pass[TEST_RUNSDISTRIBUTION] += RunsDistribution(epsilon, n);

    if (TEST_POKER8 == Choice)
        pass[TEST_POKER8] += Poker(epsilon, n, 8);

    if (TEST_SERIAL5 == Choice)
        pass[TEST_SERIAL5] += Serial(epsilon, n, 5);

    if (TEST_BINARYDERIVATION7 == Choice)
        pass[TEST_BINARYDERIVATION7] += BinaryDerivative(epsilon, n, 7);

    if (TEST_AUTOCORRELATION2 == Choice)
        pass[TEST_AUTOCORRELATION2] += Autocorrelation(epsilon, n, 2);

    if (TEST_AUTOCORRELATION8 == Choice)
        pass[TEST_AUTOCORRELATION8] += Autocorrelation(epsilon, n, 8);

    if (TEST_AUTOCORRELATION16 == Choice)
        pass[TEST_AUTOCORRELATION16] += Autocorrelation(epsilon, n, 16);
}

void readHexDigitsInBinaryFormat(FILE *fp,int choose, unsigned char *epsilon, int *pass)
{
	int		i, done, num_0s, num_1s, bitsRead;
	BYTE	buffer[4];



//	printf("     Statistical Testing In Progress.........\n\n");
	for ( i=0; i<1; i++ ) {
		num_0s = 0;
		num_1s = 0;
		bitsRead = 0;
		done = 0;
		do {
			if ( fread(buffer, sizeof(unsigned char), 4, fp) != 4 ) {
				printf("READ ERROR:  Insufficient data in file.\n");
				free(epsilon);
				return;
			}
			done = convertToBits(epsilon,buffer, 32,SAMPLE_LEN, &num_0s, &num_1s, &bitsRead);
		} while ( !done );
//		fprintf(freqfp, "\t\tBITSREAD = %d 0s = %d 1s = %d\n", bitsRead, num_0s, num_1s);

		Test_Suite(epsilon, SAMPLE_LEN, choose, pass);

	}

}


int convertToBits(unsigned char *epsilon, BYTE *x, int xBitLength, int bitsNeeded, int *num_0s, int *num_1s, int *bitsRead)
{
	int		i, j, count, bit;
	BYTE	mask;
	int		zeros, ones;

	count = 0;
	zeros = ones = 0;
	for ( i=0; i<(xBitLength+7)/8; i++ ) {
		mask = 0x80;
		for ( j=0; j<8; j++ ) {
			if ( *(x+i) & mask ) {
				bit = 1;
				(*num_1s)++;
				ones++;
			}
			else {
				bit = 0;
				(*num_0s)++;
				zeros++;
			}
			mask >>= 1;
			epsilon[*bitsRead] = bit;
			(*bitsRead)++;
			if ( *bitsRead == bitsNeeded )
				return 1;
			if ( ++count == xBitLength )
				return 0;
		}
	}

	return 0;
}