#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <gd.h>
#include "gdfonts.h"
#include "gdfontmb.h"
#include "gdfontl.h"

#define MEMBERS 26
#define BENCH_VALUES 5

const char *opts[MEMBERS] = {
	"gcc -O4 -march=amdfam10",
	"gcc -m32 -O4 -march=amdfam10",
	"gcc -O4 -march=native",
	"gcc -O0",
	"gcc -O",
	"gcc -Os",
	"gcc -m32 -Os",
	"gcc -O1",
	"gcc -O2",
	"gcc -O3",
	"gcc -O4",
	"gcc -funroll-all-loops",
	"gcc -ffast-math",
	"gcc -minline-all-stringops",
	"gcc -m32 -minline-all-stringops",
	"gcc -m32 -O4 -march=amdfam10 -minline-all-stringops",
	"clang -O3 -march=amdfam10",
	"clang -O0",
	"clang -O",
	"clang -Os",
	"clang -O1",
	"clang -O2",
	"clang -O3",
	"clang -ffast-math",
	"clang -minline-all-stringops",
	"clang -funroll-loops"
};

const double benchmarks[MEMBERS][BENCH_VALUES] = {
	{28108.248225, 11.562123, 46.006018, 2532.145915, 0.102033},
	{18497.398691, 11.553481, 46.148794, 1489.585147, 0.170453},
	{28115.298426, 11.559180, 45.933872, 2525.355105, 0.103841},
	{28624.910852, 11.558817, 46.143087, 672.935947, 0.309791},
	{28142.644872, 11.553871, 46.152263, 1981.970301, 0.118591},
	{18552.890302, 11.561897, 46.142797, 1910.943747, 0.143690},
	{18701.456629, 11.554332, 46.156211, 1044.765022, 0.182566},
	{28579.562472, 11.558696, 45.834958, 1980.709932, 0.118777},
	{28651.564894, 11.558188, 46.144006, 1402.281298, 0.112668},
	{28088.125342, 11.558890, 46.069403, 2304.169597, 0.104111},
	{28088.711065, 11.557657, 46.142990, 2301.087243, 0.105355},
	{28270.522844, 11.558433, 46.039018, 672.378191, 0.312954},
	{28563.812923, 11.558808, 45.929539, 673.060376, 0.310816},
	{28130.116224, 11.559030, 46.155474, 672.793793, 0.309749},
	{18629.647394, 11.556527, 46.081181, 695.764098, 0.342435},
	{18740.214359, 11.558769, 46.105033, 1485.035049, 0.170215},
	{28622.678663, 11.555299, 46.147259, 2276.980271, 0.109194},
	{28097.750243, 11.555401, 35.463995, 773.603540, 0.273650},
	{28606.320673, 11.558835, 46.139134, 2267.343874, 0.112963},
	{28610.672485, 11.558157, 46.137329, 2121.013488, 0.112489},
	{28577.381412, 11.552327, 46.161860, 2328.601418, 0.114134},
	{28593.930020, 11.556146, 46.157884, 2273.850351, 0.112448},
	{28600.481792, 11.554492, 46.123769, 2269.246141, 0.108457},
	{28503.440353, 11.554738, 36.250812, 774.465545, 0.275925},
	{28091.697326, 11.559278, 46.141255, 672.672171, 0.309526},
	{28158.414621, 11.559981, 46.147325, 673.389629, 0.311006}
};

const uint32_t sizes[MEMBERS] = {
	72858, 68079, 72858, 77607, 57511, 52583, 48731, 57511,
	57659, 72858, 72858, 77607, 78670, 77607, 69245, 68363,
	57592, 81731, 57676, 53693, 58415, 57676, 57592, 81731, 81731, 81731
};

int main(int argc, char **argv){
	uint32_t num1 = 0;
	uint32_t num2 = 0;
	//uint32_t num3 = 0;
	gdImagePtr im;
	FILE *out;
	int width  = 1200;
	int height = 1080;
	im = gdImageCreate(width, height);
	int white = gdImageColorAllocate(im, 255, 255, 255);
	int black = gdImageColorAllocate(im, 0, 0, 0);
	int red = gdImageColorAllocate(im, 255, 0, 0);
	int blue = gdImageColorAllocate(im, 0, 0, 255);
	int green = gdImageColorAllocate(im, 0, 255, 0);
	int color_tmp = white;
	uint32_t label_width = (width-100)/MEMBERS;
	uint32_t label_height = (width-150)/(MEMBERS+6);
	int highest_b[MEMBERS][BENCH_VALUES];
	int lowest_b[MEMBERS][BENCH_VALUES];
	double tmp_h[5] = {0, 0, 0, 0, 0};
	double tmp_l[5] = {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
	uint32_t lowest_size = UINT32_MAX;
	uint32_t highest_size = 0;
	uint32_t size_l[MEMBERS];
	uint32_t size_h[MEMBERS];
	char chart_l[200];
	double perf_rank[MEMBERS];
	uint32_t perf_order[MEMBERS];
	double perf_tmp = 0;
	double perf_lowest = 1000000;
	uint32_t perf_indx = 0;
	uint32_t perf_blacklist[MEMBERS];


	while(num1 < MEMBERS){
		for(num2=0; num2<5; num2++){
			if(benchmarks[num1][num2] >= tmp_h[num2])
				tmp_h[num2] = benchmarks[num1][num2];
			if(benchmarks[num1][num2] <= tmp_l[num2])
				tmp_l[num2] = benchmarks[num1][num2];
		}
		if(sizes[num1] <= lowest_size)
			lowest_size = sizes[num1];
		if(sizes[num1] >= highest_size)
			highest_size = sizes[num1];
		num1++;
	}
	num1 = 0;
	while(num1 < MEMBERS){
		for(num2=0; num2<5; num2++){
			if(benchmarks[num1][num2] == tmp_h[num2])
				highest_b[num1][num2] = 1;
			else
				highest_b[num1][num2] = 0;
			if(benchmarks[num1][num2] == tmp_l[num2])
				lowest_b[num1][num2] = 1;
			else
				lowest_b[num1][num2] = 0;
		}
		if(sizes[num1] == lowest_size)
			size_l[num1] = 1;
		else
			size_l[num1] = 0;
		if(sizes[num1] == highest_size)
			size_h[num1] = 1;
		else
			size_h[num1] = 0;
		num1++;
	}
	num1 = 0;
	//calculate performance rank
	while(num1 < MEMBERS){
		perf_rank[num1] = 0;
		for(num2=0; num2<4; num2++){
			if(num2) //don't count test 1
				continue;
			perf_rank[num1] += benchmarks[num1][num2]/tmp_h[num2]*100;
		}
		//last test lower is better
		perf_rank[num1] += 100-(benchmarks[num1][num2]/tmp_l[num2]*100);
		if(perf_rank[num1] < perf_lowest)
			perf_lowest = perf_rank[num1];
		num1++;
	}
	//grab highest
	for(num1=0; num1<MEMBERS; num1++){
		perf_blacklist[num1] = 0;
		//if(perf_rank[num1] > perf_tmp)
		//	perf_tmp = perf_rank[num1];
	}
	num1 = 0;
	//sort by performance rank
	while(num1 < MEMBERS){
		//grab highest that's not blacklisted(already recorded) and record to perf_order
		perf_tmp = perf_lowest;
		//num3 = 0;
		//perf_indx = 
		for(num2=0; num2<MEMBERS; num2++){
			//if(num2 == num1)
			//	continue;
			if(perf_blacklist[num2])
				continue;
			if(perf_rank[num2] >= perf_tmp){
				perf_indx = num2;
				//num3++;
				perf_tmp = perf_rank[num2];
			}
		}
		perf_blacklist[perf_indx] = 1;
		perf_order[num1] = perf_indx;
		num1++;
	}
	num1 = 0;

	//chart description
	gdImageString(im, gdFontMediumBold, 10,   20,   (unsigned char *)"gcc/clang CFLAG comparison", black);
	gdImageString(im, gdFontMediumBold, 10,   35,   (unsigned char *)"using:", black);
	gdImageString(im, gdFontMediumBold, 25,   50,   (unsigned char *)"glibc 2.14.1", black);
	gdImageString(im, gdFontMediumBold, 25,   65,   (unsigned char *)"gcc 4.5.3", black);
	gdImageString(im, gdFontMediumBold, 25,   80,   (unsigned char *)"clang/llvm svn 11/23/11", black);
	gdImageString(im, gdFontMediumBold, 25,   95,   (unsigned char *)"lsbench2 0.0.6", black);
	gdImageString(im, gdFontMediumBold, 10,   110,   (unsigned char *)"invocation:", black);
	gdImageString(im, gdFontMediumBold, 25,   125,   (unsigned char *)"lsbench2 -t 4 -m 2048", black);





	//chart boundary
	gdImageLine(im, 400, 10, 400, height-50, black);
	gdImageLine(im, 400, height-50, width-50, height-50, black);

	gdImageString(im, gdFontMediumBold, 1*(label_width*3)+290,   label_height/2,   (unsigned char *)"memcpy/set/mov", black);
	gdImageString(im, gdFontMediumBold, 2*(label_width*3)+290, 2*label_height/2, (unsigned char *)"pthread+asm", black);
	gdImageString(im, gdFontMediumBold, 3*(label_width*3)+290, 3*label_height/2, (unsigned char *)"pattern scan", black);
	gdImageString(im, gdFontMediumBold, 4*(label_width*3)+290, 4*label_height/2, (unsigned char *)"simulator", black);
	gdImageString(im, gdFontMediumBold, 5*(label_width*3)+290, 5*label_height/2, (unsigned char *)"binary size", black);
	gdImageString(im, gdFontMediumBold, 6*(label_width*3)+290, 6*label_height/2, (unsigned char *)"perf rating", black);
	gdImageString(im, gdFontMediumBold, 6*(label_width*3)+290, 7*label_height/2, (unsigned char *)"(exclude size)", black);

	gdImageLine(im, 1*(label_width*3)+400, height-50, 1*(label_width*3)+400, label_height/2, black);
	gdImageLine(im, 2*(label_width*3)+400, height-50, 2*(label_width*3)+400, label_height/2, black);
	gdImageLine(im, 3*(label_width*3)+400, height-50, 3*(label_width*3)+400, label_height/2, black);
	gdImageLine(im, 4*(label_width*3)+400, height-50, 4*(label_width*3)+400, label_height/2, black);
	gdImageLine(im, 5*(label_width*3)+400, height-50, 5*(label_width*3)+400, label_height/2, black);
	gdImageLine(im, 6*(label_width*3)+400, height-50, 6*(label_width*3)+400, label_height/2, black);

	num2 = 0;
	while(num2 < MEMBERS){
		num1 = perf_order[num2];
		//printf("%u\t%s\n\t%lf\t%lf\t%lf\t%lf\t%u\n", num1, opts[num1], benchmarks[num1][0], benchmarks[num1][2], benchmarks[num1][3], benchmarks[num1][4], sizes[num1]);

		gdImageLine(im, 1, (num2+4)*label_height+25, width-50, (num2+4)*label_height+25, black);
		(void)sprintf(chart_l, "%s", opts[num1]);
		gdImageString(im, gdFontMediumBold, 5, (num2+5)*label_height, (unsigned char *)chart_l, black);
		//(void)sprintf(chart_l, "%s", opts[num1]);
		//gdImageLine(im, 1, (num1+5)*label_height+25, width-50, (num1+5)*label_height+25, black);

		(void)sprintf(chart_l, "%.04lf%%", benchmarks[num1][0]/tmp_h[0]*100);
		//(void)sprintf(chart_l, "%.04lf", benchmarks[num1][0]);
		if(highest_b[num1][0])
			color_tmp = green;
		else if(lowest_b[num1][0])
			color_tmp = red;
		else
			color_tmp = blue;
		gdImageString(im, gdFontLarge, (label_width*3)+300, (num2+5)*label_height, (unsigned char *)chart_l, color_tmp);

		(void)sprintf(chart_l, "%.04lf%%", benchmarks[num1][2]/tmp_h[2]*100);
		//(void)sprintf(chart_l, "%.04lf", benchmarks[num1][2]);
		if(highest_b[num1][2])
			color_tmp = green;
		else if(lowest_b[num1][2])
			color_tmp = red;
		else
			color_tmp = blue;
		gdImageString(im, gdFontLarge, 2*(label_width*3)+300, (num2+5)*label_height, (unsigned char *)chart_l, color_tmp);

		(void)sprintf(chart_l, "%.04lf%%", benchmarks[num1][3]/tmp_h[3]*100);
		//(void)sprintf(chart_l, "%.04lf", benchmarks[num1][3]);
		if(highest_b[num1][3])
			color_tmp = green;
		else if(lowest_b[num1][3])
			color_tmp = red;
		else
			color_tmp = blue;
		gdImageString(im, gdFontLarge, 3*(label_width*3)+300, (num2+5)*label_height, (unsigned char *)chart_l, color_tmp);

		(void)sprintf(chart_l, "%.04lf%%", benchmarks[num1][4]/tmp_l[4]*100);
		//(void)sprintf(chart_l, "%.04lf", benchmarks[num1][4]);
		if(highest_b[num1][4])
			color_tmp = red;
		else if(lowest_b[num1][4])
			color_tmp = green;
		else
			color_tmp = blue;
		gdImageString(im, gdFontLarge, 4*(label_width*3)+300, (num2+5)*label_height, (unsigned char *)chart_l, color_tmp);


		(void)sprintf(chart_l, "%.04lf%%", (double)sizes[num1]/lowest_size*100);
		//(void)sprintf(chart_l, "%u", sizes[num1]);
		if(size_h[num1])
			color_tmp = red;
		else if(size_l[num1])
			color_tmp = green;
		else
			color_tmp = blue;
		gdImageString(im, gdFontLarge, 5*(label_width*3)+300, (num2+5)*label_height, (unsigned char *)chart_l, color_tmp);

		(void)sprintf(chart_l, "%.04lf%%", perf_rank[num1]);
		gdImageString(im, gdFontLarge, 6*(label_width*3)+300, (num2+5)*label_height, (unsigned char *)chart_l, black);


		num2++;
	}
	gdImageLine(im, 1, (num2+4)*label_height+25, width-50, (num2+4)*label_height+25, black);


	//border around image
	gdImageLine(im, 0, 0, width, 0, black);
	gdImageLine(im, 0, 0, 0, height, black);
	gdImageLine(im, width-1, 0, width-1, height, black);
	gdImageLine(im, 0, height-1, width, height-1, black);
	//watermark
	gdImageString(im, gdFontMediumBold, width-400,   height-25,   (unsigned char *)"http:/""/linuxsociety.org 2011", black);

	out = fopen("image2.png", "w");

	gdImageInterlace(im, 1);
	gdImagePng(im, out);
	fclose(out);
	gdImageDestroy(im);

	return 0;
}