/*
 * Instituto Superior de Engenharia do Porto
 *
 * Estruturas de Informação
 *
 * 2000/2001
 *
 * (C) Paulo Sousa
 *
 * ------------------------------------------
 *
 * Trabalho nº 1
 *
 */


#include "teste.h"

#include <fstream>

//
//stream de output para guardar resultados
//
// ofstream s_Resultados;


//definição de um tipo ponteiro para função para ordenação de vectores de inteiros
typedef void (*sort_func)(int v[], int nSize);


sort_func func_ordena(algoritmo SP)
{
	sort_func ordenar = NULL;	//ponteiro para função de ordenação

	switch(SP)
	{
	case qsort1:
		ordenar = QuickSort<int>::quicksort_1;
		break;
	case qsort2:
		ordenar = QuickSort<int>::quicksort_2;
		break;
	case qsort3:
		ordenar = QuickSort<int>::quicksort_3;
		break;
	case qsort4:
		ordenar = QuickSort<int>::quicksort_4;
		break;
	case qsort5:
		ordenar = QuickSort<int>::quicksort_5;
		break;
	case qsort6:
		ordenar = QuickSort<int>::quicksort_6;
		break;
	case qsort7:
		ordenar = QuickSort<int>::quicksort_7;
		break;
	case qsort8:
		ordenar = QuickSort<int>::quicksort_8;
		break;
	case qsort9:
		ordenar = QuickSort<int>::quicksort_9;
		break;
	case qsort10:
		ordenar = QuickSort<int>::quicksort_10;
		break;
	case qsort11:
		ordenar = QuickSort<int>::quicksort_11;
		break;
	case shell1:
		ordenar = ShellSort<int>::shellsort_1;
		break;
	case shell2:
		ordenar = ShellSort<int>::shellsort_2;
		break;
	case shell3:
		ordenar = ShellSort<int>::shellsort_3;
		break;
	case shell4:
		ordenar = ShellSort<int>::shellsort_4;
		break;
	case shell5:
		ordenar = ShellSort<int>::shellsort_5;
		break;
	case shell6:
		ordenar = ShellSort<int>::shellsort_6;
		break;
	case shell7:
		ordenar = ShellSort<int>::shellsort_7;
		break;
	case heap1:
		ordenar = HeapSort<int>::heapsort_1;
		break;
	case shaker1:
		ordenar = ShakerSort<int>::shakersort_1;
		break;
	case shaker2:
		ordenar = ShakerSort<int>::shakersort_2;
		break;
	case shaker3:
		ordenar = ShakerSort<int>::shakersort_3;
		break;
	case merge1:
		ordenar = MergeSort<int>::mergesort_1;
		break;
	case merge2:
		ordenar = MergeSort<int>::mergesort_2;
		break;
	case merge3:
		ordenar = MergeSort<int>::mergesort_3;
		break;
	case merge4:
		ordenar = MergeSort<int>::mergesort_4;
		break;
	case comb11_1:
		ordenar = CombSort11<int>::combsort11_1;
		break;
	case jsort1:
		ordenar = JSort<int>::jsort_1;
		break;
	case shear1:
		ordenar = ShearSort<int>::shearsort_1;
		break;
	case shear2:
		ordenar = ShearSort<int>::shearsort_2;
		break;
	case insertion:
		ordenar = InsertionSort<int>::insertionsort_1;
		break;
	case biins1:
		ordenar = BiInsertionSort<int>::biinsertionsort_1;
		break;
	case selsort1:
		ordenar = SelectionSort<int>::selectionsort_1;
		break;
	case selsort2:
		ordenar = SelectionSort<int>::selectionsort_2;
		break;
	case oddeven1:
		ordenar = OddEvenSort<int>::oddevensort_1;
		break;
	case bubble1:
		ordenar = BubbleSort<int>::bubblesort_1;
		break;
	case bubble2:
		ordenar = BubbleSort<int>::bubblesort_2;
		break;
	case bubble3:
		ordenar = BubbleSort<int>::bubblesort_3;
		break;
	case bubble4:
		ordenar = BubbleSort<int>::bubblesort_4;
		break;
	case bubble5:
		ordenar = BubbleSort<int>::bubblesort_5;
		break;
	case bibubble1:
		ordenar = BiBubbleSort<int>::bibubblesort_1;
		break;
	case stooge1:
		ordenar = StoogeSort<int>::stoogesort_1;
		break;
	case perm1:
		ordenar = PermSort<int>::permsort_1;
		break;
	case bozo1:
		ordenar = BozoSort<int>::bozosort_1;
		break;
	default:
		throw Erro(ERRO_FUNCAO_INVALIDA);
	}
	return ordenar;
}

double run_test(algoritmo SP, preenchimento FP, int nTam, int nRuns = 10, int nIgnoreRuns = 0)
{
	CVector<int> vCopia(nTam);	//vector a ordenar
	CVector<int> vec;			//vector auxiliar para 

	sort_func ordenar = NULL;	//ponteiro para função de ordenação

	// preencher vector de acordo com a politica desejada
	preenche(FP, vCopia, nTam);

	// preparar a ordenação que será efectuada usando um ponteiro para função
	ordenar = func_ordena(SP);

	//executar n ordenações para estabilizar o sistema antes de contabilizar os tempos
	for(int j = 0; j < nIgnoreRuns; j++)
	{
		//preencher o vector com o mesmo conteudo de cada vez que efectua o teste
		vec = vCopia;

		ordenar(vec, nTam);
	}

	//executar a ordenação n vezes e calcular a média
	double durAcc = 0.0;
	CCronometro s(false);
	for(int i = 0; i < nRuns; i++)
	{
		//preencher o vector com o mesmo conteudo de cada vez que efectua o teste
		vec = vCopia;

		//ordenar via ponteiro para função
		s.Start();

		ordenar(vec, nTam);

		durAcc += s.Stop();

		// verificar o correcto funcionamento do algoritmo
		//só é necessário testar da 1ª vez
		if (i == 0 && !verifica_ordenacao<int>(vec, nTam))
			//throw Erro(ERRO_ORDENACAO_INCORRECTA);
			return -1.0;
	}
	
	return durAcc / nRuns;
}

void read_nruns(int& runs, int& ignore)
{
	//cout << endl;
	cout << ":> Quantas execucoes? ";
	cin >> runs;


	cout << ":> Quantas execucoes de ruido (ignora os valores)? ";
	cin >> ignore;
}


void read_dimensao(int& tamanho)
{
	cout << ":> Quantos elementos? (-1 para terminar) ";
	cin >> tamanho;
}

void view_teste_modo_1(algoritmo vec_sp[], int nAlgs, int tamanho, int runs, int ignore)
{
	cout << endl;
	cout << endl;
	cout << setiosflags(ios::left);
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << tamanho << " elementos (media de " << runs << " execucoes)" << endl;
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << setw(20) <<"algoritmo ";
	for (int fp = 0; fp < NUM_PREENCHIMENTOS; fp++)
		cout << setw(10) << tostring( (preenchimento)fp ) << " ";
	cout << endl;
	cout << "-------------------------------------------------------------------------------" << endl;

	try
	{
		double dur;
		for (int sp = 0; sp < nAlgs; sp++)
		{
			cout << setw(20) << tostring(vec_sp[sp]);
			for (int fp = 0; fp < NUM_PREENCHIMENTOS; fp++)
			{
				if (g_configuracao.bUseLimits && (vnLimites[vec_sp[sp]][fp] == -1 || tamanho < vnLimites[vec_sp[sp]][fp]))
				{
					dur = run_test(vec_sp[sp], (preenchimento)fp, tamanho, runs, ignore);
					cout << setw(10) << dur << " ";
				}
				else
					cout << setw(10) << " " << " ";
			}
			cout << endl;
		}
		cout << "-------------------------------------------------------------------------------" << endl;
	}
	catch (Erro e)
	{
		cout << endl;
		cout << endl;
		cout << "ERRO!!!!! " << e.codigo() << endl;
		//cout << "fp: " << fp << "sp: " << sp;
	}
}

int read_n_algoritmos(algoritmo*& vec_sp)
{
	algoritmo vec_temp[NUM_ALGORITMOS+NUM_ALGORITMOS_LENTOS];

	memset(vec_temp, 0, sizeof(vec_temp));

	cout << "Algoritmo de ordenacao (-1 para terminar)" << endl;
	cout << " 0..10  - qsort1 ... qsort11" << endl;
	cout << "11..17 - shell1, shell2, shell3, shell4, shell5, shell6, shell7" << endl;
	cout << "18..20 - shaker1, shaker2, shaker3" << endl;
	cout << "21..24 - merge1, merge2, merge3, merge4" << endl;
	cout << "25     - comb11_1" << endl;
	cout << "26     - heap1" << endl;
	cout << "27     - jsort1" << endl;
	cout << "28..29 - shear1, shear2" << endl;
	cout << "30     - insertion" << endl;
	cout << "31     - bi insertion1" << endl;
	cout << "32..33 - selsort1, selsort2" << endl;
	cout << "34     - oddeven1" << endl;
	cout << "35..39 - bubble1, bubble2, bubble3, bubble4, bubble5" << endl;
	cout << "40     - bi bubble1" << endl;
	// ....
	if (g_configuracao.bUseSlow)
	{
		cout << "41     - stooge1" << endl;
		cout << "42     - perm1" << endl;
		cout << "43     - bozo1" << endl;
		// ....
	}
	

	int nAlgs = 0;
	int i_sp;
	do {
		cout << ":> ";
		cin >> i_sp;
		if (i_sp >= 0 && i_sp < NUM_ALGORITMOS + (g_configuracao.bUseSlow?NUM_ALGORITMOS_LENTOS:0))
		{
			vec_temp[nAlgs] = (algoritmo)i_sp;
			nAlgs++;
		}
	} while (nAlgs < NUM_ALGORITMOS + (g_configuracao.bUseSlow?NUM_ALGORITMOS_LENTOS:0) && i_sp != -1);


	vec_sp = new algoritmo[nAlgs];
	memcpy(vec_sp, vec_temp, sizeof(algoritmo)*nAlgs);

	return nAlgs;
}

int gera_vec_sp(algoritmo*& vec_sp)
{
	int nAlgs = NUM_ALGORITMOS + (g_configuracao.bUseSlow?NUM_ALGORITMOS_LENTOS:0);
	vec_sp = new algoritmo[nAlgs];
	for (int i_sp = 0; i_sp < nAlgs; i_sp++)
		vec_sp[i_sp] = (algoritmo)i_sp;

	return nAlgs;
}


void modo_1(bool bUseAllSP)
{
	cout << endl;
	cout << "***  M O D O   1  ***" << endl;
	//cout << "(dimensao fixa; politica de preenchimento variavel; algoritmo variavel)" << endl; 
	cout << endl << endl;


	algoritmo* vec_sp;
	int nAlgs;
	if (bUseAllSP)
		nAlgs = gera_vec_sp(vec_sp);
	else
		nAlgs = read_n_algoritmos(vec_sp);


	int tamanho;
	read_dimensao(tamanho);
	if (tamanho < 0)
		return;


	int runs;
	int ignore;
	read_nruns(runs, ignore);


	view_teste_modo_1(vec_sp, nAlgs, tamanho, runs, ignore);

	delete [] vec_sp;
}


void read_limites(int& tamInf, int& tamSup, int& inc)
{
	cout << ":> Quantos elementos? (limite inferior) ";
	cin >> tamInf;


	cout << ":> Quantos elementos? (limite superior) ";
	cin >> tamSup;

	cout << ":> Incremento? ";
	cin >> inc;
}

void view_teste_modo_2(algoritmo vec_sp[], int nAlgs, preenchimento fp, int tamInf, int tamSup, int inc, int runs, int ignore)
{
	cout << endl;
	cout << endl;
	cout << setiosflags(ios::left);
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << "vector inicialmente " << tostring(fp) << " (media de " << runs << " execucoes)" << endl;
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << setw(20) <<"algoritmo ";
	for (int i = tamInf; i <= tamSup; i += inc)
		cout << setw(10) << i << " ";
	cout << endl;
	//cout << setw(80) << setfill('-') << "-" << setfill(' ') << endl;
	cout << "-------------------------------------------------------------------------------" << endl;

	try
	{
		double dur;
		for (int sp = 0; sp < nAlgs; sp++)
		{
			cout << setw(20) << tostring(vec_sp[sp]);
			for (int tamanho = tamInf; tamanho <= tamSup; tamanho += inc)
			{
				if (g_configuracao.bUseLimits && (vnLimites[vec_sp[sp]][fp] == -1 || tamanho < vnLimites[vec_sp[sp]][fp]))
				{
					dur = run_test(vec_sp[sp], fp, tamanho, runs, ignore);
					cout << setw(10) << dur << " ";
				}
				else
					cout << setw(10) << " " << " ";
			}
			cout << endl;
		}
		cout << "-------------------------------------------------------------------------------" << endl;
	}
	catch (Erro e)
	{
		cout << endl;
		cout << endl;
		cout << "ERRO!!!!! " << e.codigo() << endl;
		//cout << "fp: " << fp << "sp: " << sp;
	}
}

void read_preenchimento(preenchimento& fp)
{
	int i_fp;
	do {
		cout << ":> Politica de preenchimento (0 - ordenado, 1 - invertido, 2 - aleatorio): ";
		cin >> i_fp;
	} while (i_fp >= NUM_PREENCHIMENTOS);
	fp = (preenchimento)i_fp;
}

void modo_2(bool bUseAllSP)
{
	cout << endl;
	cout << "***  M O D O   2  ***" << endl;
	//cout << "(dimensao variavel; politica de preenchimento fixa; algoritmo variavel)" << endl; 
	cout << endl << endl;


	int nAlgs;
	algoritmo* vec_sp;
	if (bUseAllSP)
	{
		nAlgs = NUM_ALGORITMOS + (g_configuracao.bUseSlow?NUM_ALGORITMOS_LENTOS:0);
		vec_sp = new algoritmo[nAlgs];
		for (int i_sp = 0; i_sp < nAlgs; i_sp++)
			vec_sp[i_sp] = (algoritmo)i_sp;
	}
	else
		nAlgs = read_n_algoritmos(vec_sp);


	preenchimento fp;
	read_preenchimento(fp);
	if (fp < 0)
		return;


	int tamInf;	
	int tamSup;	
	int inc;
	read_limites(tamInf, tamSup, inc);


	int runs;
	int ignore;
	read_nruns(runs, ignore);

	
	view_teste_modo_2(vec_sp, nAlgs, (preenchimento)fp, tamInf, tamSup, inc, runs, ignore);

	delete [] vec_sp;
}

void read_algoritmo(algoritmo& sp)
{
	int i_sp;
	do {
		cout << "Algoritmo de ordenacao" << endl;
		cout << " 0..10  - qsort1 ... qsort11" << endl;
		cout << "11..17 - shell1, shell2, shell3, shell4, shell5, shell6, shell7" << endl;
		cout << "18..20 - shaker1, shaker2, shaker3" << endl;
		cout << "21..24 - merge1, merge2, merge3, merge4" << endl;
		cout << "25     - comb11_1" << endl;
		cout << "26     - heap1" << endl;
		cout << "27     - jsort1" << endl;
		cout << "28..29 - shear1, shear2" << endl;
		cout << "30     - insertion" << endl;
		cout << "31     - bi insertion1" << endl;
		cout << "32..33 - selsort1, selsort2" << endl;
		cout << "34     - oddeven1" << endl;
		cout << "35..39 - bubble1, bubble2, bubble3, bubble4, bubble5" << endl;
		cout << "40     - bi bubble1" << endl;
		// ....
		if (g_configuracao.bUseSlow)
		{
			cout << "41     - stooge1" << endl;
			cout << "42     - perm1" << endl;
			cout << "43     - bozo1" << endl;
			// ....
		}
		cout << ":> ";
		cin >> i_sp;
	} while (sp >= NUM_ALGORITMOS);

	sp = (algoritmo)i_sp;
}

void view_teste_modo_3(algoritmo sp, int tamInf, int tamSup, int inc, int runs, int ignore)
{
	cout << endl;
	cout << endl;
	cout << setiosflags(ios::left);
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << tostring(sp) << " (media de " << runs << " execucoes)" << endl;
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << setw(20) <<"dimensao ";
	for (int fp = 0; fp < NUM_PREENCHIMENTOS; fp++)
		cout << setw(10) << tostring( (preenchimento)fp ) << " ";
	cout << endl;
	cout << "-------------------------------------------------------------------------------" << endl;

	try
	{
		double dur;
		for (int tamanho = tamInf; tamanho <= tamSup; tamanho += inc)
		{
			cout << setw(20) << tamanho;
			for (int fp = 0; fp < NUM_PREENCHIMENTOS; fp++)
			{
				if (g_configuracao.bUseLimits && (vnLimites[sp][fp] == -1 || tamanho < vnLimites[sp][fp]))
				{
					dur = run_test(sp, (preenchimento)fp, tamanho, runs, ignore);
					cout << setw(10) << dur << " ";
				}
				else
					cout << setw(10) << " " << " ";
			}
			cout << endl;
		}
		cout << "-------------------------------------------------------------------------------" << endl;
	}
	catch (Erro e)
	{
		cout << endl;
		cout << endl;
		cout << "ERRO!!!!! " << e.codigo() << endl;
		//cout << "fp: " << fp << "sp: " << sp;
	}
}

void modo_3()
{
	cout << endl;
	cout << "***  M O D O   3  ***" << endl;
	//cout << "(dimensao variavel; politica de preenchimento variavel; algoritmo fixo)" << endl; 
	cout << endl << endl;


	algoritmo sp;
	read_algoritmo(sp);
	if (sp < 0)
		return;


	int tamInf;	
	int tamSup;	
	int inc;
	read_limites(tamInf, tamSup, inc);


	int runs;
	int ignore;
	read_nruns(runs, ignore);


	view_teste_modo_3(sp, tamInf, tamSup, inc, runs, ignore);
}

void view_teste_modo_4(algoritmo sp, preenchimento fp, int tamanho, int runs, int ignore)
{
	cout << endl;
	cout << endl;
	cout << setiosflags(ios::left);
	cout << "-------------------------------------------------------------------------------" << endl;
	cout << tostring(sp) << " vector " << tostring(fp) << " com " << tamanho << " elementos (" <<  runs << " execucoes): ";
	try
	{
		double dur;
		if (g_configuracao.bUseLimits && (vnLimites[sp][fp] == -1 || tamanho < vnLimites[sp][fp]))
		{
			dur = run_test(sp, fp, tamanho, runs, ignore);
			cout << setw(10) << dur << " ";
		}
		else
			cout << setw(10) << " " << " ";
		cout << endl;
		cout << "-------------------------------------------------------------------------------" << endl;
	}
	catch (Erro e)
	{
		cout << endl;
		cout << endl;
		cout << "ERRO!!!!! " << e.codigo() << endl;
		//cout << "fp: " << fp << "sp: " << sp;
	}
}

void modo_4()
{
	cout << endl;
	cout << "***  M O D O   4  ***" << endl;
	cout << endl << endl;


	algoritmo sp;
	read_algoritmo(sp);
	if (sp < 0)
		return;


	preenchimento fp;
	read_preenchimento(fp);


	int tamanho;
	read_dimensao(tamanho);
	if (tamanho < 0)
		return;


	view_teste_modo_4(sp, fp, tamanho, 1, 0);
}

string tostring(algoritmo SP)
{
	switch(SP)
	{
	case qsort1:
		return "quicksort_1";
		break;
	case qsort2:
		return "quicksort_2";
		break;
	case qsort3:
		return "quicksort_3";
		break;
	case qsort4:
		return "quicksort_4";
		break;
	case qsort5:
		return "quicksort_5";
		break;
	case qsort6:
		return "quicksort_6";
		break;
	case qsort7:
		return "quicksort_7";
		break;
	case qsort8:
		return "quicksort_8";
		break;
	case qsort9:
		return "quicksort_9";
		break;
	case qsort10:
		return "quicksort_10";
		break;
	case qsort11:
		return "quicksort_11";
		break;
	case shell1:
		return "shellsort_1";
		break;
	case shell2:
		return "shellsort_2";
		break;
	case shell3:
		return "shellsort_3";
		break;
	case shell4:
		return "shellsort_4";
		break;
	case shell5:
		return "shellsort_5";
		break;
	case shell6:
		return "shellsort_6";
		break;
	case shell7:
		return "shellsort_7";
		break;
	case heap1:
		return "heapsort_1";
		break;
	case shaker1:
		return "shakersort_1";
		break;
	case shaker2:
		return "shakersort_2";
		break;
	case shaker3:
		return "shakersort_3";
		break;
	case merge1:
		return "mergesort_1";
		break;
	case merge2:
		return "mergesort_2";
		break;
	case merge3:
		return "mergesort_3";
		break;
	case merge4:
		return "mergesort_4";
		break;
	case comb11_1:
		return "combsort11_1";
		break;
	case jsort1:
		return "Jsort_1";
		break;
	case shear1:
		return "shearsort_1";
		break;
	case shear2:
		return "shearsort_2";
		break;
	case insertion:
		return "insertionsort_1";
		break;
	case biins1:
		return "biinsertsort_1";
		break;
	case selsort1:
		return "selectionsort_1";
		break;
	case selsort2:
		return "selectionsort_2";
		break;
	case oddeven1:
		return "oddevensort_1";
		break;
	case bubble1:
		return "bubblesort_1";
		break;
	case bubble2:
		return "bubblesort_2";
		break;
	case bubble3:
		return "bubblesort_3";
		break;
	case bubble4:
		return "bubblesort_4";
		break;
	case bubble5:
		return "bubblesort_5";
		break;
	case bibubble1:
		return "bibubblesort_1";
		break;
	case stooge1:
		return "stoogesort_1";
		break;
	case perm1:
		return "permsort_1";
		break;
	case bozo1:
		return "bozosort_1";
		break;
	default:
		return "?";
	}
}

string tostring(preenchimento FP)
{
	switch (FP)
	{
	case ordenado:
		return "ordenado";
		break;
	case invertido:
		return "invertido";
		break;
	case aleatorio:
		return "aleatorio";
		break;
	default:
		return "?";
	}
}

