#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>

void usage(void){
	printf("./lat <max memory size MB>\n");
}


int main(int argc, char **argv){
	char *memory;
	unsigned int max_memory_bytes = 0;
	unsigned int cur_memory_bytes = 1024;
	struct timeval starttime, endtime;
	double te0, te1, te2;
	//double last_te0, last_te1, last_te2;
	unsigned int increment_offset = 0;
	unsigned int increment_bytes = 4096;
	unsigned char increment_count = 0;
	double CPU_MHZ = 1;

	if(argc != 2){
		usage();
		exit(1);
	}
	max_memory_bytes = atoi(argv[1])*1024*1024;

#ifdef __linux__
	FILE* fp;
	char buf[256];
	char mhz_buf[16];
	int count = 0;
	int found = 0;

        if( ( fp = fopen( "/proc/cpuinfo", "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening /proc/cpuinfo !\n" );
                exit( 1 );
        }
	//cpu MHz		: 3208.243
        while( fgets(buf, sizeof(buf), fp) != NULL)
        {
		if (buf[count] == 'c'){
			if (buf[count+1] == 'p'){
				if (buf[count+4] == 'M'){
					if (buf[count+5] == 'H'){
						for(count=0; count<16; count++){
							mhz_buf[count]='\0';
						}
						found=1;
						//starting at 10 untill end
						for(count = 10; buf[count] != '\n'; count++){
							mhz_buf[count-10]=buf[count];
						}
					}
				}
			}
		}
	}
	if(found){
		CPU_MHZ=atof(mhz_buf);
	}
	fclose( fp );
	//free(buf);
	//free(mhz_buf);
	printf("CPU frequency: %.02lf\n", CPU_MHZ);
	
#endif
	if(CPU_MHZ == 1){
		printf("Warning: your platform is not supported yet, numbers will be inaccurate!\n");
		CPU_MHZ = 3000;
	}

	printf("Write Latencies:\n");
	printf("\tsize\t|\tstride(bytes)\n");	

	printf("\t(KB)");
	printf("\t| 4");
	printf("\t 8");
	printf("\t 16\n");
	//memory = malloc(cur_memory_bytes);
	//memory = malloc(max_memory_bytes);

	//last_te0 = 0;
	//last_te1 = 0;
	//last_te2 = 0;
	//te0 = 1;
	//te1 = 1;
	//te2 = 1;


	while(cur_memory_bytes < max_memory_bytes){

		memory = malloc(cur_memory_bytes);

		
		//prime
		Four_Byte_Walk(memory, cur_memory_bytes, 200);

		gettimeofday(&starttime, NULL);		
		Four_Byte_Walk(memory, cur_memory_bytes, 400);
		gettimeofday(&endtime, NULL);
		//last_te0=te0;
		te0=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;
			

		Eight_Byte_Walk(memory, cur_memory_bytes, 400);
		vgettimeofday(&starttime, NULL);
		Eight_Byte_Walk(memory, cur_memory_bytes, 800);
		vgettimeofday(&endtime, NULL);
		te1=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;

		Sixteen_Byte_Walk(memory, cur_memory_bytes, 800);
		gettimeofday(&starttime, NULL);
		Sixteen_Byte_Walk(memory, cur_memory_bytes, 1600);	
		gettimeofday(&endtime, NULL);
		te2=((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000;


		te0=CPU_MHZ*(1000*1000*te0);
		te1=CPU_MHZ*(1000*1000*te1);
		te2=CPU_MHZ*(1000*1000*te2);

		printf("\t%lu", cur_memory_bytes/1024 );
		printf("\t%.02lf", te0/(double)(cur_memory_bytes*400/4) );
		printf("\t%.02lf", te1/(double)(cur_memory_bytes*800/8) );
		printf("\t%.02lf\n", te2/(double)(cur_memory_bytes*1600/16) );

		//3200 clocks
		//(cur_memory_bytes/8)*2*10000 operations
		//clocks/operation

		//printf("\t%luKB clocks: %lf\n", cur_memory_bytes/1024, te0/(double)(cur_memory_bytes*100/8) );

		//1 4 8 12 16 
		//24 32 40 48 
		//64 80 96 128
		//160 192 224 256
		//320 384 448 512
		//640 768 896 1024
		//1280 1536 1792 2048
		//2560 ...

		increment_count++;
		if(cur_memory_bytes == 1024){
			cur_memory_bytes = 4096;
		}else{
			
			cur_memory_bytes += increment_bytes+increment_offset;

			//must be a multiple of 256
			//while(cur_memory_bytes%256 != 0)
			//	cur_memory_bytes++;
		}
		if(increment_count == 4){
			increment_count = 0;
			//increment_bytes *= 2;
			increment_bytes = floor(increment_bytes*2);
			increment_offset = increment_bytes;

			while(increment_offset%256 != 0)
				increment_offset++;
			increment_offset -= increment_bytes;
		}
		//memory=realloc(memory, cur_memory_bytes);
		free(memory);
		//memory = malloc(cur_memory_bytes);
	}


	//free(memory);

	return 0;
}
