#include "BooPHF.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <random>
#include <algorithm>
#include <climits>
#include <iostream>
#include <fstream>
#include <assert.h>
#include <thread>
#include <math.h>



//#include <chrono>


u_int64_t *data;


using namespace std;


//uncomment to check correctness of the func
//#define CHECK_MPHF


#define MAX_RANDOM 2147483648
#define srandomdev() srand((unsigned) time(NULL))


inline double get_time_usecs() {
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return double(tv.tv_sec) * 1000000 + double(tv.tv_usec);
}


uint64_t random64 (){
	uint64_t low, high,res;
	low = random();
	high = random();

	res = (high << 32) + low;
	return res;
}


typedef boomphf::SingleHashFunctor<u_int64_t>  hasher_t;
typedef boomphf::mphf<  u_int64_t, hasher_t  > boophf_t;


// iterator from disk file of u_int64_t with buffered read,   todo template
class bfile_iterator : public std::iterator<std::forward_iterator_tag, u_int64_t>{
	public:

	bfile_iterator()
	: _is(nullptr)
	, _pos(0) ,_inbuff (0), _cptread(0)
	{
		_buffsize = 10000;
		_buffer = (u_int64_t *) malloc(_buffsize*sizeof(u_int64_t));
	}

	bfile_iterator(const bfile_iterator& cr)
	{
		_buffsize = cr._buffsize;
		_pos = cr._pos;
		_is = cr._is;
		_buffer = (u_int64_t *) malloc(_buffsize*sizeof(u_int64_t));
		 memcpy(_buffer,cr._buffer,_buffsize*sizeof(u_int64_t) );
		_inbuff = cr._inbuff;
		_cptread = cr._cptread;
		_elem = cr._elem;
	}

	bfile_iterator(FILE* is): _is(is) , _pos(0) ,_inbuff (0), _cptread(0)
	{
		_buffsize = 10000;
		_buffer = (u_int64_t *) malloc(_buffsize*sizeof(u_int64_t));
		int reso = fseek(_is,0,SEEK_SET);
		advance();
	}

	~bfile_iterator()
	{
		if(_buffer!=NULL)
			free(_buffer);
	}


	u_int64_t const& operator*()  {  return _elem;  }

	bfile_iterator& operator++()
	{
		advance();
		return *this;
	}

	friend bool operator==(bfile_iterator const& lhs, bfile_iterator const& rhs)
	{
		if (!lhs._is || !rhs._is)  {  if (!lhs._is && !rhs._is) {  return true; } else {  return false;  } }
		assert(lhs._is == rhs._is);
		return rhs._pos == lhs._pos;
	}

	friend bool operator!=(bfile_iterator const& lhs, bfile_iterator const& rhs)  {  return !(lhs == rhs);  }
	private:
	void advance()
	{
		_pos++;

		if(_cptread >= _inbuff)
		{
			int res = fread(_buffer,sizeof(u_int64_t),_buffsize,_is);
			_inbuff = res; _cptread = 0;

			if(res == 0)
			{
				_is = nullptr;
				_pos = 0;
				return;
			}
		}

		_elem = _buffer[_cptread];
		_cptread ++;
	}
	u_int64_t _elem;
	FILE * _is;
	unsigned long _pos;

	u_int64_t * _buffer; // for buffered read
	int _inbuff, _cptread;
	int _buffsize;
};


class file_binary{
	public:

	file_binary(const char* filename)
	{
		_is = fopen(filename, "rb");
		if (!_is) {
			throw std::invalid_argument("Error opening " + std::string(filename));
		}
	}

	~file_binary()
	{
		fclose(_is);
	}

	bfile_iterator begin() const
	{
		return bfile_iterator(_is);
	}

	bfile_iterator end() const {return bfile_iterator(); }

	size_t        size () const  {  return 0;  }//todo ?

	private:
	FILE * _is;
};



//simple iterator to generate list of distinct u_int64_t keys (not random, just equally distributed in [0;ULLONG_MAX])
class uint64_iterator : public std::iterator<std::forward_iterator_tag, u_int64_t>{
public:
	
	uint64_iterator() : _nb_elem(0) , _curr(ULLONG_MAX), _step(0),_nb_iterated(0)
	{
	}
	
	uint64_iterator(const uint64_iterator& cr)
	{
		_nb_elem = cr._nb_elem;
		_curr = cr._curr;
		_step = cr._step;
		_stop = cr._stop;
		_nb_iterated = cr._nb_iterated;
	}
	
	uint64_iterator(u_int64_t nb_elem, u_int64_t stop ) : _nb_elem(nb_elem) , _curr(0),_nb_iterated(0), _stop(stop)
	{
		_step =  ULLONG_MAX / _nb_elem;
	}
	
	~uint64_iterator()
	{
	}
	
	u_int64_t const& operator*()  {  return _curr;  }
	
	uint64_iterator& operator++()
	{

		_curr = _curr + _step;
		_nb_iterated++;
		if(_nb_iterated >= _stop) _nb_elem = 0;
		return *this;
	}
	
	friend bool operator==(uint64_iterator const& lhs, uint64_iterator const& rhs)
	{
		if (!lhs._nb_elem || !rhs._nb_elem)  {  if (!lhs._nb_elem && !rhs._nb_elem) {  return true; } else {  return false;  } }
		assert(lhs._nb_elem == rhs._nb_elem);
		return rhs._curr == lhs._curr;
	}
	
	friend bool operator!=(uint64_iterator const& lhs, uint64_iterator const& rhs)  {  return !(lhs == rhs);  }

	u_int64_t _nb_elem;
	u_int64_t _curr;
	u_int64_t _step;
	u_int64_t _stop;
	u_int64_t _nb_iterated;
};

//will generate range over [0;ULLONG_MAX]
//elem equally spaced , separated  by  ULLONG_MAX/nb_elem starting at 0
//will stop after nbiter
class uint64_range{
public:
	uint64_range(u_int64_t nb_elem, u_int64_t nbiter) : _nb_elem(nb_elem), _stop(nbiter)
	{
		
	}
	~uint64_range()
	{
		
	}
	
	uint64_iterator begin() const
	{
		return uint64_iterator(_nb_elem,_stop);
	}
	uint64_iterator end() const
	{
		return uint64_iterator();
	}
	
	
private:
	u_int64_t _nb_elem;
	u_int64_t _stop;

};

//stolen from emphf
struct stats_accumulator {
	stats_accumulator()
	: m_n(0)
	, m_mean(0)
	, m_m2(0)
	{}

	void add(double x)
	{
		m_n += 1;
		auto delta = x - m_mean;
		m_mean += delta / m_n;
		m_m2 += delta * (x - m_mean);
	}

	double mean() const
	{
		return m_mean;
	}

	double variance() const
	{
		return m_m2 / (m_n - 1);
	}

	double relative_stddev() const
	{
		return std::sqrt(variance()) / mean() * 100;
	}

private:
	double m_n;
	double m_mean;
	double m_m2;
};


//PARAMETERS
u_int64_t nelem = 1000*1000;
uint nthreads = 1; //warning must be a divisor of nBuckets
double gammaFactor = 1.0;
bool write_each = false;

u_int64_t nb_in_bench_file;


uint64_t korenXor(uint64_t x){
	x ^= (x << 21);
	x ^= (x >> 35);
	x ^= (x << 4);
	return x;
}


uint nBuckets = 96;
uint nMphfByBucket(96);
vector<FILE*> vFiles(nBuckets);
vector<uint> elinbuckets(nBuckets*nMphfByBucket);
vector<boophf_t> MPHFs(nBuckets*nMphfByBucket);


void multipleMPHF(const vector<vector<u_int64_t>>& datas, uint start, uint n,uint bucketNum){
	for(uint ii(start);ii<start+n;++ii){
		auto data_iterator2 = boomphf::range(static_cast<const u_int64_t*>(&datas[ii][0]), static_cast<const u_int64_t*>(&datas[ii][0]+datas[ii].size()));
		MPHFs[bucketNum*nMphfByBucket+ii]=  boomphf::mphf<u_int64_t,hasher_t>(datas[ii].size(),data_iterator2,1,gammaFactor,write_each,false);
	}
}


void compactBucket(uint start, uint n){
	//foreach bucket

	for(uint i(start);i<start+n;++i){
		auto data_iterator = file_binary(("bucket"+to_string(i)).c_str());
		vector<vector<u_int64_t>> datas(nMphfByBucket);

		for(uint ireserve(0);ireserve<nMphfByBucket;++ireserve){
			datas[ireserve].reserve(elinbuckets[i*nMphfByBucket+ireserve]);
		}

		// we put element in memory
		for(auto it(data_iterator.begin());it!=data_iterator.end();++it){
			datas[(korenXor(*it)%(nMphfByBucket))].push_back(*it);
		}

		vector<thread> threads;
		for(uint tn(0);tn<1;++tn){
			threads.push_back(thread(multipleMPHF,datas,tn*(nMphfByBucket/1),nMphfByBucket/1,i));
		}
		// threads.push_back(thread(multipleMPHF,datas,(nthreads)*(nMphfByBucket/nthreads),nMphfByBucket-(nthreads)*(nMphfByBucket/nthreads),i));

		for(auto &t : threads){t.join();}
	}
}


template <typename phf_t,typename Range>
int check_mphf_correctness (phf_t * bphf, Range const& input_range){
		u_int64_t nb_collision_detected = 0;
		u_int64_t range_problems = 0;
		u_int64_t mphf_value;
			boomphf::bitVector check_table (nelem);
		//auto data_iterator = file_binary("keyfile");

		for (auto const& val: input_range)
		{
			mphf_value = bphf->lookup(val);
			//printf("%llu  mphf_value %llu\n",val,mphf_value);

			if(mphf_value>=nelem)
			{
				range_problems++; continue;
			}
			if(check_table[mphf_value]==0)
			{
				check_table.set(mphf_value);
			}
			else
			{
				//printf("collision for val %lli : \n",mphf_value);
				printf("collision for %llu  mphf_value %llu\n",(long long unsigned int)val,(long long unsigned int)mphf_value);

				nb_collision_detected++;
			}
		}



		if(nb_collision_detected ==  0 && range_problems ==0)
		{
			printf(" --- boophf working correctly --- \n");
			return 0;

		}
		else
		{
			printf("!!! problem, %llu collisions detected; %llu out of range !!!\n",(long long unsigned int)nb_collision_detected,(long long unsigned int)range_problems);
			return 1;

		}
}


template <typename phf_t,typename Range>
void bench_mphf_lookup (phf_t * bphf, Range const& input_range){


	vector<u_int64_t> sample;
	u_int64_t mphf_value;

	//copy sample in ram
	for (auto const& key: input_range) {
		sample.push_back(key);
	}

	printf("bench lookups  sample size %lu \n",sample.size());
	//bench procedure taken from emphf
	stats_accumulator stats;
	double tick = get_time_usecs();
	size_t lookups = 0;
	static const size_t lookups_per_sample = 1 << 16;
	u_int64_t dumb=0;
	double elapsed;
	size_t runs = 10;

	for (size_t run = 0; run < runs; ++run) {
		for (size_t ii = 0; ii < sample.size(); ++ii) {

			mphf_value = bphf->lookup(sample[ii]);
			//do some silly work
			dumb+= mphf_value;

			if (++lookups == lookups_per_sample) {
				elapsed = get_time_usecs() - tick;
				stats.add(elapsed / (double)lookups);
				tick = get_time_usecs();
				lookups = 0;
			}
		}
	}
	printf("BBhash bench lookups average %.2f ns +- stddev  %.2f %%   (fingerprint %llu)  \n", 1000.0*stats.mean(),stats.relative_stddev(),(long long unsigned int)dumb);

}


//#include "bucketing.h"


int main (int argc, char* argv[]){
	//if we want a random seed from timer
//	typedef std::chrono::high_resolution_clock myclock;
//	myclock::time_point beginning = myclock::now();

	bool check_correctness = false;
	bool bench_lookup = false;
	bool save_mphf = false;
	bool load_mphf = false;
	bool buckets = false;
	bool from_disk = true;
	bool bench_lookup_out = false;
	bool on_the_fly= false;
	 write_each = true;
	if(argc <4 ){
		printf("Usage :\n");
		printf("%s <nelem> <nthreads> <gamma>  [options]\n",argv[0]);
		printf("Options:\n");
		printf("\t-check  (check correctness of mphf)\n");
		printf("\t-bench  (bench query time of mphf)\n");
		printf("\t-save\n");
		printf("\t-load\n");
		printf("\t-inram\n");
		printf("\t-nodisk  (do not write each intermediate level on disk)\n");
		printf("\t-buckets\n");
		printf("\t-outquery (bench the fp rate of the mphf)\n");  // bench fp rate
		printf("\t-onthefly (generates key on the fly without storing them on disk or in ram)\n");
	


		return EXIT_FAILURE;
	}

	if(argc >=4 ){
		nelem = strtoul(argv[1], NULL,0);
		nthreads = atoi(argv[2]);
		gammaFactor = atoi(argv[3]);
	}


	
	for (int ii=4; ii<argc; ii++){
		if(!strcmp("-check",argv[ii])) check_correctness= true;
		if(!strcmp("-bench",argv[ii])) bench_lookup= true;
		if(!strcmp("-save",argv[ii])) save_mphf= true;
		if(!strcmp("-load",argv[ii])) load_mphf= true;
		if(!strcmp("-inram",argv[ii])) from_disk= false;
		if(!strcmp("-buckets",argv[ii])) buckets= true;
		if(!strcmp("-outquery",argv[ii])) bench_lookup_out= true;
		if(!strcmp("-onthefly",argv[ii])) on_the_fly= true;
		if(!strcmp("-nodisk",argv[ii])) write_each= false;

	}

	
	if(gammaFactor == 0) {
		fprintf(stderr,"gamma value error\n");
		fprintf(stderr,"Usage should be \n");
		fprintf(stderr,"%s <nelem> <nthreads> <gamma>  [options]\n",argv[0]);
		exit(1);
	}

	
//	///testing terator
//	printf("testing terator :\n");
//	uint64_range terator_in =  uint64_range(10,10);
//	uint64_range terator = uint64_range(terator_in);
//	
//	
//	for (auto const& val: terator) {
//		printf("%llu \n",val);
//
//	}
//	exit(0);
	
	
	FILE * key_file = NULL;
	FILE * bench_file = NULL;

	if(from_disk){
		key_file = fopen("keyfile","w+");
	}

	uint64_t ii, jj;

	/////  generation of keys
	if(!from_disk && !buckets){
		uint64_t rab = 100;

		static std::mt19937_64 rng;
		rng.seed(std::mt19937_64::default_seed); //default seed

		//rng.seed(seed2); //random seed from timer
		data = (u_int64_t * ) calloc(nelem+rab,sizeof(u_int64_t));

		for (u_int64_t i = 1; i < nelem+rab; i++){
			data[i] = rng();
		}
		printf("de-duplicating items \n");

		std::sort(data,data+nelem+rab);

		for (ii = 1, jj = 0; ii < nelem+rab; ii++) {
			if (data[ii] != data[jj])
				data[++jj] = data[ii];
		}
		printf("found %lli duplicated items  \n",nelem+(long long int)rab-(jj + 1) );
	}
	else{
		if(!on_the_fly)
		{
			//methode simple pas besoin de  de-dupliquer, mais pas "random"
			u_int64_t step = ULLONG_MAX / nelem;
			// u_int64_t step = 100 / nelem;
			u_int64_t current = 0;
			for (u_int64_t i = 0; i < nelem; i++)
			{
				fwrite(&current, sizeof(u_int64_t), 1, key_file);
				//printf("%llu \n",current);
				current = current + step;
			}
			fclose(key_file);
			printf("key file generated \n");
			
			if(bench_lookup)
			{
				bench_file = fopen("benchfile","w+");
				//create a test file
				//if n < 10 M take all elements, otherwise regular sample to have 10 M elements
				u_int64_t stepb =  nelem  / 10000000;
				if(stepb==0) stepb=1;
				auto data_iterator = file_binary("keyfile");
				u_int64_t cpt = 0;
				nb_in_bench_file=0;
				for (auto const& key: data_iterator) {
					if( (cpt % stepb) == 0)
					{
						fwrite(&key, sizeof(u_int64_t), 1, bench_file);
						nb_in_bench_file++;
					}
					cpt++;
				}
				fclose(bench_file);
			}
		}
	}


	vector<uint> nb_elem_in_previous_buckets (nBuckets*nMphfByBucket);

	
	if(buckets){
		clock_t begin, end;
		double t_begin,t_end; struct timeval timet;
		gettimeofday(&timet, NULL); t_begin = timet.tv_sec +(timet.tv_usec/1000000.0);

		
		for(uint i(0);i<nBuckets;++i){
			vFiles[i]=fopen(("bucket"+to_string(i)).c_str(),"w+");
		}

		printf("splitting keys ..\n");
		
		double tick_split = get_time_usecs();

		
		int buffsize = 10000;
		vector < vector<u_int64_t> > buffers (nBuckets);
		for(int ii=0; ii<nBuckets;ii++)
			buffers[ii].reserve(buffsize);
		
		auto data_iterator = file_binary("keyfile");
		for (auto const& key: data_iterator) {
			u_int64_t hash=(korenXor(key)%(nBuckets*nMphfByBucket)/nMphfByBucket);
			
			if(buffers[hash].size()==buffsize)
			{
				fwrite(buffers[hash].data(), sizeof(u_int64_t), buffers[hash].size(), vFiles[hash]);
				buffers[hash].clear();//hope it keeps capacity intact
			}
			buffers[hash].push_back(key);
			
			++elinbuckets[korenXor(key)%(nBuckets*nMphfByBucket)];
		}
		//flush buffers
		for(int ii=0; ii<nBuckets;ii++)
		{
			fwrite(buffers[ii].data(), sizeof(u_int64_t), buffers[ii].size(), vFiles[ii]);
		}

		for (int ii=0; ii<vFiles.size(); ii++) {
			fclose(vFiles[ii]);
		}

		nb_elem_in_previous_buckets[0] = 0 ;
		for(int ii=1; ii<nBuckets*nMphfByBucket; ii++ ){
			nb_elem_in_previous_buckets[ii] = nb_elem_in_previous_buckets[ii-1] + elinbuckets[ii-1];
		}

		double elapsed_split = get_time_usecs() - tick_split;
		printf("time key split  %.2f s \n", elapsed_split/1000000.0);

		printf("Go compactions !!!\n");

		double integ;

		assert(  modf((double)nBuckets/nthreads ,&integ) == 0  );
		vector<thread> threads;
		for(uint n(0);n<nthreads;++n){
			threads.push_back(thread(compactBucket,n*nBuckets/nthreads,nBuckets/nthreads));
		}
		for(auto &t : threads){t.join();}
		//~ compactBucket(0, nBuckets);

		for(uint i(0);i<nBuckets;++i){
			remove(("bucket"+to_string(i)).c_str());
		}
		gettimeofday(&timet, NULL); t_end = timet.tv_sec +(timet.tv_usec/1000000.0);

		double elapsed = t_end - t_begin;

		printf("BooPHF constructed perfect hash for %llu keys in %.2fs\n", (long long unsigned int)nelem,elapsed);
		// cin.get();

		if(check_correctness){
			u_int64_t step2 = ULLONG_MAX / nelem;
			u_int64_t current2 = 0;
			u_int64_t range_problems(0);
			u_int64_t nb_collision_detected(0);
			begin = clock();
			boomphf::bitVector check_table (nelem);
			for (u_int64_t i = 0; i < nelem; i++){

				uint64_t hash=korenXor(current2)%(nBuckets*nMphfByBucket);
				u_int64_t mphf_value = MPHFs[hash].lookup(current2)+  nb_elem_in_previous_buckets [hash];
				if(mphf_value>=nelem){
					range_problems++;
					printf("there is %llu problems \n", (long long unsigned int)range_problems);
				}
				if(check_table[mphf_value]==0)
				{
					check_table.set(mphf_value);
				}
				else
				{
					//printf("collision for val %lli \n",mphf_value);
					nb_collision_detected++;
				}
				current2 += step2;
			}
			printf("there is %llu problems\n", (long long unsigned int)range_problems);
			printf("there is %llu coll\n", (long long unsigned int)nb_collision_detected);

			end = clock();
			//printf("BooPHF %llu lookups in  %.2fs,  approx  %.2f ns per lookup \n", nelem, (double)(end - begin) / CLOCKS_PER_SEC,  ((double)(end - begin) / CLOCKS_PER_SEC)*1000000000/nelem);
		}

		if(bench_lookup)
		{

			auto input_range = file_binary("benchfile");

			vector<u_int64_t> sample;
			u_int64_t mphf_value;
			
			//copy sample in ram
			for (auto const& key: input_range) {
				sample.push_back(key);
			}
			
			printf("bench lookups  sample size %lu \n",sample.size());
			//bench procedure taken from emphf
			stats_accumulator stats;
			double tick = get_time_usecs();
			size_t lookups = 0;
			static const size_t lookups_per_sample = 1 << 16;
			u_int64_t dumb=0;
			double elapsed;
			size_t runs = 10;
			
			for (size_t run = 0; run < runs; ++run) {
				for (size_t ii = 0; ii < sample.size(); ++ii) {
					
					uint64_t hash=korenXor(sample[ii])%(nBuckets*nMphfByBucket);
					mphf_value = MPHFs[hash].lookup(sample[ii]) +  nb_elem_in_previous_buckets [hash];
					dumb+= mphf_value;
					
					//do some silly work
					
					if (++lookups == lookups_per_sample) {
						elapsed = get_time_usecs() - tick;
						stats.add(elapsed / (double)lookups);
						tick = get_time_usecs();
						lookups = 0;
					}
				}
			}
			printf("BBhash buckets bench lookups average %.2f ns +- stddev  %.2f %%   (fingerprint %llu)  \n", 1000.0*stats.mean(),stats.relative_stddev(),(long long unsigned int)dumb);
			
			///
		}



		return EXIT_SUCCESS;
	}



	std::string output_filename;
	output_filename = "saved_mphf";
	boophf_t * bphf = NULL;;


	clock_t begin, end;
	double t_begin,t_end; struct timeval timet;

	if(!load_mphf){
		printf("Construct a BooPHF with  %lli elements  \n",(long long int)nelem);
		///create the boophf

		gettimeofday(&timet, NULL); t_begin = timet.tv_sec +(timet.tv_usec/1000000.0);

		//MPHF CREATION
		if (on_the_fly)
		{
			auto data_iterator =  uint64_range(nelem,nelem) ;
			bphf = new boomphf::mphf<u_int64_t,hasher_t>(nelem,data_iterator,nthreads,gammaFactor,write_each);
		}
		else if(from_disk)
		{
			auto data_iterator = file_binary("keyfile");
			bphf = new boomphf::mphf<u_int64_t,hasher_t>(nelem,data_iterator,nthreads,gammaFactor,write_each);
		}
		else
		{
			auto data_iterator = boomphf::range(static_cast<const u_int64_t*>(data), static_cast<const u_int64_t*>(data+nelem));
			bphf = new boomphf::mphf<u_int64_t,hasher_t>(nelem,data_iterator,nthreads,gammaFactor,write_each);
		}

		gettimeofday(&timet, NULL); t_end = timet.tv_sec +(timet.tv_usec/1000000.0);

		double elapsed = t_end - t_begin;


		printf("BooPHF constructed perfect hash for %llu keys in %.2fs\n", (long long unsigned int)nelem,elapsed);
		printf("boophf  bits/elem : %f\n",(float) (bphf->totalBitSize())/nelem);

	}
	else{
		//assumes the mphf was saved before, reload it
		bphf = new boomphf::mphf<u_int64_t,hasher_t>();

		printf("Loading a BooPHF with  %lli elements  \n",(long long unsigned int)nelem);

		gettimeofday(&timet, NULL); t_begin = timet.tv_sec +(timet.tv_usec/1000000.0);

		std::ifstream is(output_filename, std::ios::binary);
		bphf->load(is);

		gettimeofday(&timet, NULL); t_end = timet.tv_sec +(timet.tv_usec/1000000.0);
		double elapsed = t_end - t_begin;

		printf("BooPHF re-loaded perfect hash for %llu keys in %.2fs\n", (long long unsigned int)nelem,elapsed);
		printf("boophf  bits/elem : %f\n",(float) (bphf->totalBitSize())/nelem);
	}


	if(save_mphf)
	{
		std::ofstream os(output_filename, std::ios::binary);
		bphf->save(os);
	}


	if(check_correctness && on_the_fly )
	{
		auto data_iterator =  uint64_range(nelem,nelem) ;
		check_mphf_correctness(bphf ,data_iterator);
	}
	else if(check_correctness && from_disk )
	{
		auto data_iterator = file_binary("keyfile");
		check_mphf_correctness(bphf ,data_iterator);
	}
	else if(check_correctness &&  !from_disk )
	{
		auto data_iterator = boomphf::range(static_cast<const u_int64_t*>(data), static_cast<const u_int64_t*>(data+nelem));
		check_mphf_correctness(bphf ,data_iterator);
	}

	
	if(bench_lookup &&  on_the_fly)
	{
		auto data_iterator = uint64_range(nelem,1000000) ;
		
		bench_mphf_lookup(bphf,data_iterator);
		
	}
	else if(bench_lookup &&  from_disk)
	{
		auto data_iterator = file_binary("benchfile");

		bench_mphf_lookup(bphf,data_iterator);

	}
	else if(bench_lookup &&  !from_disk)
	{
		auto data_iterator = boomphf::range(static_cast<const u_int64_t*>(data), static_cast<const u_int64_t*>(data+nelem));

		bench_mphf_lookup(bphf,data_iterator);
	}
	
	if(bench_lookup_out)
	{
		int nrandom = 100000000; //10000000
		static std::mt19937_64 rng;
		rng.seed(std::mt19937_64::default_seed); //default seed
		u_int64_t *  data_random = (u_int64_t * ) calloc(nrandom,sizeof(u_int64_t));
		for (u_int64_t i = 0; i < nrandom; i++){
			data_random[i] = rng();
		//	printf("%llu \n",data_random[i]);
		}
		u_int64_t mphf_value;
		u_int64_t dumb=0;
		u_int64_t nb_fp =0;
		u_int64_t nb_out_of_range =0;

		
		for (size_t ii = 0; ii < nrandom; ++ii) {
			
			mphf_value = bphf->lookup(data_random[ii]);

			//printf("m %llu \n",mphf_value);

			if(mphf_value != ULLONG_MAX)
			{
				nb_fp++;
			}
			
			if((mphf_value != ULLONG_MAX) &&  (mphf_value >= nelem))
			{
				nb_out_of_range++;
			}
			
		}
		
		
		double tick = get_time_usecs();
		
		for (size_t ii = 0; ii < nrandom; ++ii) {
			mphf_value = bphf->lookup(data_random[ii]);
			//do some silly work
			dumb+= mphf_value;

		}
		double elapsed = get_time_usecs() - tick;

		printf("query %i elem  out of set  FP rate %.2f   nb issues %llu    lookup %.2f  ns \n",
               nrandom,
               nb_fp/(float)nrandom,
               (long long unsigned int)nb_out_of_range,
               1000.0*elapsed/(double)nrandom);
	}
	

	if(!from_disk){
		free(data);
	}

		delete bphf;

	return EXIT_SUCCESS;
}
