#include <iostream>
#include <stdio.h>
#include <fstream>
#include <stdlib.h>
#include <windows.h>

#include "1MemBF.h"
#include "hash_function.h"
#include "std_bf.h"
#include "ShBFM.h"

bool ifTestSpeed; 
#define CYCLE_NUM 10

using namespace std;

#define KEY_NUM   8000000  //8M elements in total
string *str;			   // storing the element set


bool FPTest_Init(char *fileN)
{
	ifstream fin(fileN);
	if(!fin)
	{
		printf("open file %s failed!\n", fileN);
		return false;
	}

	int j,x=0;
	j=0;

	printf("\nstart to build BFs, please wait...\n");

	str = new string[KEY_NUM];
	if(!str)
	{
		printf("malloc memory failed!");
		return false;
	}

	char str_tmp[13];
	memset(str_tmp,0,sizeof(str_tmp));
	while (!fin.eof() && j++<KEY_NUM)
	{
		for (int i=0;i<13;i++)
		{
			fin>>x;
			str_tmp[i]=(char)x;
		}
		str[j]=str_tmp;

		if (j%1000==0) printf("\r%.2f%%",100*j/(KEY_NUM+0.0));		
	}
	fin.close();

	return true;
}


//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//we only use the following three test functions.
void Test_ShBF_M(uint m,uint n, uint k)
{
	uint ilen=13;
	

	//////construct BF...
	ShBF_M *bf1=new ShBF_M(m,k);
	for (uint i=0;i<n;i++)//一共插入前n个元素
	{
		bf1->insert((const uchar *)str[i].c_str(),ilen);
	}


	///////test query speed and memory accesses
	printf("Starting to test query speed:%d,%d,%d\n",m,n,k);
	double queryTimeUsed = 0.0;
	LARGE_INTEGER frequence, beginTime, endTime;
	if (!QueryPerformanceFrequency(&frequence))return;

	QueryPerformanceCounter(&beginTime);
	for (int j=0; j<CYCLE_NUM; j++)
	{
		for (uint i=0;i<2*n;i++)
		{
			if (1==bf1->query((const uchar *)str[i].c_str(),ilen))
			{
				ilen=13;
			}
		}
	}
	QueryPerformanceCounter(&endTime);
	queryTimeUsed = (double)(endTime.QuadPart-beginTime.QuadPart)/(double)frequence.QuadPart;    // seconds

	FILE * Qfre=fopen("query_speed_shBFm.stat","a");
	fprintf(Qfre,"%d\t%d\t%d\t%.6f\t%.3f\n", m, n, k, queryTimeUsed, CYCLE_NUM*2*n/queryTimeUsed); // query per second, we query 2*n elements to test the speed
	fclose(Qfre);

	FILE * fma=fopen("memory_access_shBFm.stat","a");
	fprintf(fma,"%d\t%d\t%d\t%d\t%d\n",m, n, k, bf1->query_NUM, bf1->memacc_NUM); // m,n,k, query times, memory accesses
	fclose(fma);


	///////test false positive rate....
	printf("Starting to test FP rate:%d,%d,%d\n",m,n,k);
	uint fp=0;

	for (uint i=n;i<KEY_NUM;i++)
	{
		if (1==bf1->query((const uchar *)str[i].c_str(),ilen))
		{
			fp++;
		}
	}
	FILE * fp_all=fopen("fp_shBFm.stat","a");
	fprintf(fp_all,"%d\t%d\t%d\t%.6f\n",m,n,k,fp/(KEY_NUM-n+0.0));
	fclose(fp_all);
}

void Test_OneMem(uint m,uint n, uint k)
{
	uint ilen=13;
	uint fp=0;

	//////construct BF...
	OMABF *bf1=new OMABF(m,k);
	for (uint i=0;i<n;i++)//一共插入前n个元素
	{
		bf1->insert((const uchar *)str[i].c_str(),ilen);
	}

	///////test query speed and memory accesses
	printf("Starting to test query speed:%d,%d,%d\n",m,n,k);
	double queryTimeUsed = 0.0;
	LARGE_INTEGER frequence, beginTime, endTime;
	if (!QueryPerformanceFrequency(&frequence))return;

	QueryPerformanceCounter(&beginTime);
	for (int j=0; j<CYCLE_NUM; j++)
	{
		for (uint i=0;i<2*n;i++)
		{
			if (1==bf1->query((const uchar *)str[i].c_str(),ilen))
			{
				fp++;
			}
		}
	}
	QueryPerformanceCounter(&endTime);
	queryTimeUsed = (double)(endTime.QuadPart-beginTime.QuadPart)/(double)frequence.QuadPart;    // seconds

	FILE * Qfre=fopen("query_speed_onemem.stat","a");
	fprintf(Qfre,"%d\t%d\t%d\t%.6f\t%.3f\n", m, n, k, queryTimeUsed, CYCLE_NUM*2*n/queryTimeUsed); // query per second, we query 2*n elements to test the speed
	fclose(Qfre);


	///////test false positive rate....
	printf("Starting to test FP rate:%d,%d,%d\n",m,n,k);
	fp=0;

	for (uint i=n;i<KEY_NUM;i++)
	{
		if (1==bf1->query((const uchar *)str[i].c_str(),ilen))
		{
			fp++;
		}
	}
	FILE * fp_all=fopen("fp_onemem.stat","a");
	fprintf(fp_all,"%d\t%d\t%d\t%.6f\n",m,n,k,fp/(KEY_NUM-n+0.0));
	fclose(fp_all);
}

void Test_StandardBF(uint m,uint n, uint k)
{
	uint ilen=13;
	uint fp=0;

	//////construct BF...
	std_bf *bf1=new std_bf(m,k);
	for (uint i=0;i<n;i++)//一共插入前n个元素
	{
		bf1->insert((const uchar *)str[i].c_str(),ilen);
	}


	///////test query speed and memory accesses
	printf("Starting to test query speed:%d,%d,%d\n",m,n,k);
	double queryTimeUsed = 0.0;
	LARGE_INTEGER frequence, beginTime, endTime;
	if (!QueryPerformanceFrequency(&frequence))return;

	QueryPerformanceCounter(&beginTime);
	for (int j=0; j<CYCLE_NUM; j++)
	{
		for (uint i=0;i<2*n;i++)
		{
			if (1==bf1->query((const uchar *)str[i].c_str(),ilen))
			{
				fp++;
			}
		}
	}
	QueryPerformanceCounter(&endTime);
	queryTimeUsed = (double)(endTime.QuadPart-beginTime.QuadPart)/(double)frequence.QuadPart;    // seconds

	FILE * Qfre=fopen("query_speed_BF.stat","a");
	fprintf(Qfre,"%d\t%d\t%d\t%.6f\t%.3f\n", m, n, k, queryTimeUsed, CYCLE_NUM*2*n/queryTimeUsed); // query per second, we query 2*n elements to test the speed
	fclose(Qfre);

	FILE * fma=fopen("memory_access_BF.stat","a");
	fprintf(fma,"%d\t%d\t%d\t%d\t%d\n",m, n, k, bf1->query_NUM, bf1->memacc_NUM); // m,n,k, query times, memory accesses
	fclose(fma);


	///////test false positive rate....
	printf("Starting to test FP rate:%d,%d,%d\n",m,n,k);
	fp=0;

	for (uint i=n;i<KEY_NUM;i++)
	{
		if (1==bf1->query((const uchar *)str[i].c_str(),ilen))
		{
			fp++;
		}
	}
	FILE * fp_all=fopen("fp_BF.stat","a");
	fprintf(fp_all,"%d\t%d\t%d\t%.6f\n",m,n,k,fp/(KEY_NUM-n+0.0));
	fclose(fp_all);
}

void testChange_M(int argc, char ** argv)
{
	uint m,n,k;
	k=6;
	n=4000;
	//m=20000;m*=kn/ln2=22857=357*64;

	for (m=500*64+3;m<=700*64+3;m+=10*64)
	{
		if (strcmp("onemem", argv[1]) == 0)	Test_OneMem(m,n,k);
		else if (strcmp("shbfm",argv[1]) ==0) Test_ShBF_M(m,n,k/2);
		else if (strcmp("bf",argv[1]) ==0) Test_StandardBF(m,n,k);
	}
}

void testChange_N(int argc, char ** argv)
{
	uint m,n,k;
	m=22003;
	k=8;
	//m*=kn/ln2=22857=357*64;

	for (n=1000;n<=2000;n=n+20)
	{
		if (strcmp("onemem", argv[1]) == 0)	Test_OneMem(m,n,k);
		else if (strcmp("shbfm",argv[1]) ==0) Test_ShBF_M(m,n,k/2);
		else if (strcmp("bf",argv[1]) ==0) Test_StandardBF(m,n,k);
	}
}

void testChange_K(int argc, char ** argv)
{
	uint m,n,k;
	m=23003;
	n=2000;
	//m*=kn/ln2=22857=357*64;

	for (k=4;k<=16;k+=2)
	{
		if (strcmp("onemem", argv[1]) == 0)	Test_OneMem(m,n,k);
		else if (strcmp("shbfm",argv[1]) ==0) Test_ShBF_M(m,n,k/2);
		else if (strcmp("bf",argv[1]) ==0) Test_StandardBF(m,n,k);
	}
}

void main(int argc, char ** argv)
{
	if (argc < 3)return;
	if(true==FPTest_Init("..\\all.bytes"));
	else if(false==FPTest_Init("all.bytes"))
	{
		printf("set reads failed! Please have a check!\n");
		return;
	}

	     if(strcmp("m", argv[2]) == 0)testChange_M(argc, argv);
	else if(strcmp("n", argv[2]) == 0)testChange_N(argc, argv);
	else if(strcmp("k", argv[2]) == 0)testChange_K(argc, argv);

	printf("Congrats! Mission -%s  changing %s Complete! Press any key to continue...", argv[1], argv[2]);
	//system("pause");
}