#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#ifdef IS32BIT
#define ALGS 7
#else
#define ALGS 8
#endif

#define RANGES 4

#ifndef MAX_SIZE
#define MAX_SIZE 1024
#endif

uint8_t ties[MAX_SIZE];

void SET_ONE(const uint16_t pos, const uint8_t bit){
        uint8_t cur = ties[pos];
        uint8_t b0 = (bit == 0) ? 1   : (((cur<<7) & 128) >= 128);
        uint8_t b1 = (bit == 1) ? 2   : (((cur<<6) & 128) >= 128)<<1;
        uint8_t b2 = (bit == 2) ? 4   : (((cur<<5) & 128) >= 128)<<2;
        uint8_t b3 = (bit == 3) ? 8   : (((cur<<4) & 128) >= 128)<<3;
        uint8_t b4 = (bit == 4) ? 16  : (((cur<<3) & 128) >= 128)<<4;
        uint8_t b5 = (bit == 5) ? 32  : (((cur<<2) & 128) >= 128)<<5;
        uint8_t b6 = (bit == 6) ? 64  : (((cur<<1) & 128) >= 128)<<6;
        uint8_t b7 = (bit == 7) ? 128 : (((cur) & 128)  >= 128)<<7;

        ties[pos] = b0+b1+b2+b3+b4+b5+b6+b7;
}

uint8_t IS_SET(const uint16_t pos, const uint8_t bit){
        uint8_t cur = ties[pos];
        cur <<= (7-bit);
        cur &= 128;

        if(cur >= 128)
                return 1;
        else
                return 0;
}

void Increment_Winners(uint16_t start, const uint16_t end, uint16_t *output){
	uint8_t a;
	while(start<end){
		for(a=0; a<ALGS; a++){
			if(IS_SET(start, a))
				output[a]++;
		}
		start++;
	}
}

void Print_with_spaces(const char *alg, const uint16_t length){
	uint16_t a = strlen(alg);
	printf("%s", alg);
	while(a<length){
		printf(" ");
		a++;
	}
}

void Wpp(uint16_t start, uint16_t end, uint16_t *winners){
	float wpp[8] = {0,0,0,0,0,0,0,0};
	float wins[8] = {0,0,0,0,0,0,0,0};
	float b;
	uint8_t a;
	uint16_t pos = 1;
//	const uint16_t min = 8;
	while(start<end){
		
		for(a=0; a<ALGS; a++){
			//if(start<pos)
			//	continue;
			if(IS_SET(start, a)){
				wins[a]++;
				//if(start<min)
				//	continue;
				b = wins[a]/pos;
				if(b > wpp[a]){
					wpp[a] = b;
					winners[a] = start;
				}
			}
		}
		start++;
		pos++;
	}
}


int main(int argc, char **argv){
	FILE *in;
	uint16_t a,b,c=0;
//	float fa, fb;
//	uint16_t cur_wins = 0;
	uint16_t max_wins = 0;
	uint16_t winning_ends[RANGES] = {0,0,0,0};
	uint16_t **data;
	uint16_t read_buffer_size = 16;
	char *read_buffer;
	//uint16_t start_b = 0;
	//uint16_t start_c = 0;
	//uint16_t start_d = 0;
//	const uint16_t forced_a_switch = MAX_SIZE/4;
	const uint16_t min = 8;
	const uint16_t min_start_a = 0;
//	const uint16_t min_start_b = 1;
//	const uint16_t min_start_c = 2;
//	const uint16_t min_start_d = 3;
//	const uint16_t min_starts[] = {0,1,2,3};

	const uint16_t max_end_d = MAX_SIZE;
	uint16_t winners[RANGES] = {0,0,0,0};
//	uint16_t wins[RANGES-1][ALGS];
//	uint16_t wins_T[RANGES][ALGS];

	uint16_t wins_A[ALGS];
//	uint16_t wins_B[ALGS];
//	uint16_t wins_C[ALGS];
//	float wpp[ALGS];


#ifdef IS32BIT
	const char *algs[] = {"byte_loop", "loop", "unrolled_loop", "rep_byte", "rep_4byte", "vector_loop", "libcall"};
#else
	const char *algs[] = {"byte_loop", "loop", "unrolled_loop", "rep_byte", "rep_4byte", "rep_8byte", "vector_loop", "libcall"};
#endif

	//if(argc != 1)
	//	max_end_d = atoi(argv[1]);
	printf("Testing to %u\n", max_end_d);

	data = (uint16_t **)malloc(sizeof(uint16_t *)*max_end_d);
	for(a=min_start_a; a<max_end_d; a++){
		data[a] = (uint16_t *)malloc(sizeof(uint16_t)*ALGS);
	}

	read_buffer = (char *)malloc(read_buffer_size);

//	(void)memset((void *)wins, '\0', sizeof(uint16_t)*(RANGES-1)*ALGS);
//	(void)memset((void *)wins_T, '\0', sizeof(uint16_t)*RANGES*ALGS);

	for(a=0; a<8; a++){
#ifdef IS32BIT
		if(a == 5)
			continue;
#endif
		(void)sprintf(read_buffer, "results/r%u.txt", a+1);
		in = fopen(read_buffer, "r");
		b = 0;
		while( fgets(read_buffer, read_buffer_size, in) != NULL){
			if(b == max_end_d)
				break;
			if(atoi(read_buffer) < UINT16_MAX)
				data[b][c] = atoi(read_buffer);
			else
				data[b][c] = UINT16_MAX;
			b++;
		}
		fclose(in);
		c++;
	}
	free(read_buffer);
	(void)memset((void *)ties, '\0', sizeof(uint8_t)*MAX_SIZE);
	for(a = min_start_a; a<max_end_d; a++){
		c = UINT16_MAX;
		for(b=0; b<ALGS; b++){
			if(data[a][b] < c)
				c = data[a][b];
		}
		for(b=0; b<ALGS; b++){
			if(data[a][b] == c)
				SET_ONE(a, b);
		}
		free(data[a]);
	}
	//let's assume min_starts might be changed to be safe
	//for(a = min_start_a; a<max_end_d; a++)
	//	free(data[a]);
	free(data);
//	(void)memset((void *)wins_A, '\0', sizeof(uint16_t)*ALGS);
//	(void)memset((void *)ties, '\0', sizeof(uint8_t)*MAX_SIZE);
//	(void)memset((void *)wins_B, '\0', sizeof(uint16_t)*ALGS);
//	(void)memset((void *)wins_C, '\0', sizeof(uint16_t)*ALGS);
//	(void)memset((void *)wpp, '\0', sizeof(float)*ALGS);

/*
	//find highest wins/points ratio from min_start_a closest to min_start_a
	for(start_a = min_start_a; start_a<max_end_d; start_a++){
		for(b=0; b<ALGS; b++){
			if(IS_SET(start_a, b)){
				wins_B[b]++;
				fa = (float)wins_B[b]/(start_a+1);
				if(fa > wpp[b]){
					//save position and wpp
					wins_A[b] = start_a;
					wpp[b] = fa;
					//wins_C[b] = b;
				}
			}
		}
	}
*/


//start_a = min_starts[0];
//winning_starts[0] = 0;

	for(a=0; a<RANGES; a++){
		if(a != 0)
			c = winning_ends[a-1];
		else
			c = a;
		(void)memset((void *)wins_A, '\0', sizeof(uint16_t)*ALGS);
		Wpp(c, max_end_d, wins_A);

		winning_ends[a] = UINT16_MAX;
		//c = wins_A[1];//UINT16_MAX;
		//winners[a] = 0;
		//grab alg with lowest wpp peak
		for(b=0; b<ALGS; b++){
printf("%u\n", wins_A[b]);

			if(wins_A[b] < c+min)
				continue;
			if(wins_A[b] < winning_ends[a]){
				winning_ends[a] = wins_A[b];
				winners[a] = b;
				//winning_starts[] = star
			}
		}
		winning_ends[a]++;
printf("\n");
	}


	(void)memset((void *)wins_A, '\0', sizeof(uint16_t)*ALGS);
	printf("\nTotal wins for all data (ties count as win)\n");
	Increment_Winners(min_start_a, max_end_d, wins_A);

	for(a=b=0; a<ALGS; a++){
		printf("\t");
		Print_with_spaces(algs[a], 15);
		printf("%u\n", wins_A[a]);
		b += wins_A[a];
	}
	printf("\ntotal: %u\n\n", b);
	printf("Config with most wins/ties:\n");
	printf("\tstart-end\twins\talg/s\n");
	max_wins = 0;

	for(a=0; a<RANGES; a++){
		if(a != 0)
			c = winning_ends[a-1];
		else
			c = a;

		(void)memset((void *)wins_A, '\0', sizeof(uint16_t)*ALGS);

		Increment_Winners(c, winning_ends[a], wins_A);
		b = winners[a];
		printf("\t%u-%u\t%u\t", c, winning_ends[a], wins_A[b]);
		max_wins += wins_A[b];
		printf("%s(%u) ", algs[b], b);
/*
		cur_wins = 0;
		for(b=0; b<ALGS; b++){
			if(wins_A[b] > cur_wins)
				cur_wins = wins_A[b];
		}

		printf("\t%u-%u\t%u\t", c, winning_ends[a], cur_wins);
		max_wins += cur_wins;

		for(b=0; b<ALGS; b++){
			if(winners[b] == b)
				printf("%s(%u) ", algs[b], b);
		}
*/

		printf("\n");
	}
	printf("\ntotal: %u\n", max_wins);

	return 0;
}
