#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <math.h>

typedef struct NMesta
{
	int Mesto;
	struct NMesta *Dalsi;
}T_NMesta;
	
typedef struct 
{ 
	T_NMesta *NMesta;
	double DelkaCesty; //celkova delka ujite cesty
} T_Cesta;

//-------------------------------------------------------------------------------------
//Logovaci fce
//-------------------------------------------------------------------------------------
int Loguj(FILE *fa, char *V_Text, double V_Promena, char C_Double, int C_Odradkuj)
{
	
		switch(C_Double)
		{
			case 'f' : fprintf(fa, "%s%15.8f", V_Text, V_Promena); break;//zapisuji double
			case 'd' : fprintf(fa, "%s%d", V_Text, (int)V_Promena);break;//zapisuji int
			case 't' : fprintf(fa, "%s",V_Text);break;//zapise jen text
			default : fprintf(fa, "ERROR volani fce s chybnym parametrem urceni typu %15.8f", V_Text, V_Promena);break;
		}
		if(C_Odradkuj)fprintf(fa, "\n");//odradkuje 
	
return(0);
}

//************************************************************************************
// //funkce inicializuje pole hran. Pokud jsme na konci vraci 0.
//************************************************************************************
double **VytvorPole2(char* File, int *P_VelikostPole)     
{
  int i;
  char V_Znak;
  double **V_Pole;
  FILE *fr;

  *P_VelikostPole = -1;//prvni radka souboru je hlavicka
  if ((fr = fopen(File, "r")) != NULL)
  {
    while ((V_Znak = getc(fr)) != EOF)
	{
       if (V_Znak == '\n') {(*P_VelikostPole)++;}
	}
    fclose(fr);
    //alokace 
    V_Pole = (double **) malloc(*P_VelikostPole * sizeof(double *));
	for(i = 0; i < *P_VelikostPole; i++)
    {
		V_Pole[i] = (double *) malloc(*P_VelikostPole * sizeof(double));	
	}
	return(V_Pole);

  }else
  {
      printf("NELZE NACIST SOUBOR %c\n", File);
	  return 0;
  } 
} 
//************************************************************************************
// Naplni matici vstupnimi daty. Vraci 0 pri neuspechunacteni souboru
//************************************************************************************
int NactiHrany(char* input, double **V_Pole, int C_VelikostPole, int C_NeexistujiciHrana)
{
	int C_PocetZnakuRadku = 0,	//pocet cisel v radku
	i=0, j=0; //radky a sloupce
	char V_Hlavicka[200];	//jmeno zadani (prvni radek souboru)
	double V_Pomocna;
    FILE *fr;

   if ((fr = fopen(input, "r")) != NULL)
   {
	fscanf(fr, "%s\n", V_Hlavicka);

	i = 0;
	j = 0;
	while(!feof(fr)) 
	{
		fscanf(fr, " %lf", &V_Pomocna);
		if (V_Pomocna == 0) V_Pole[i][j] = C_NeexistujiciHrana;
		else V_Pole[i][j] = V_Pomocna;
	
		j++;	
		if ((j % C_VelikostPole) == 0)
		{
		  j = 0;
		  i++;
		}	
		if (i == C_VelikostPole) break;
	}

	printf("Nactena data '%s', %d radku.\n", V_Hlavicka, C_PocetZnakuRadku);
	fclose(fr);
	return 1;
   }else
     {
      printf("NELZE NACIST SOUBOR %c", input);
	  return 0;
     }
}
//************************************************************************************
//inicializace prohazovacího seznamu, vraci ukazatel na pole
//************************************************************************************
int **InicializujSeznamOperaci(int C_PocetMest, int *P_PocetOperaci, int C_ProhazovaciVzdalenost)
{
	int i,j,k,l,
		V_PomA, V_PomB,
		**V_OpSezn;

	V_PomA = C_PocetMest - 1;
	k = 0;

	if (C_ProhazovaciVzdalenost == 0)
	{
		
		*P_PocetOperaci = (int)(pow(V_PomA,2) - (V_PomA * (V_PomA +1) / 2));

		V_OpSezn = (int **) malloc(*P_PocetOperaci * sizeof(int *));
		for(i = 0; i < *P_PocetOperaci; i++)
		{
			V_OpSezn[i] = (int *) malloc(2 * sizeof(int));	
		}

		for(i = 0; i < C_PocetMest - 1; i++)
		{
			for(j = i + 1; j < C_PocetMest - 1; j++)
			{
				V_OpSezn[k][0] = i;
				V_OpSezn[k][1] = j;
				k++;
			}
		}
	}else
	{
		V_PomB = V_PomA - C_ProhazovaciVzdalenost;
		*P_PocetOperaci = (int)(pow(V_PomA,2) - (V_PomA * (V_PomA + 1) / 2) - ((pow(V_PomB,2) - (V_PomB * (V_PomB + 1) / 2))));
		
		V_OpSezn = (int **) malloc(*P_PocetOperaci * sizeof(int *));
		for(i = 0; i < *P_PocetOperaci; i++)
		{
			V_OpSezn[i] = (int *) malloc(2 * sizeof(int));	
		}

		for(i = 0; i < C_PocetMest - 1; i++)
		{
			l = 0;
			for(j = i + 1; j < C_PocetMest - 1; j++)
			{
				V_OpSezn[k][0] = i;
				V_OpSezn[k][1] = j;
				k++;
				l++;			
				if (l == C_ProhazovaciVzdalenost) break;
			}
		}
		

	}
return(V_OpSezn);
}
//************************************************************************************
//Inicializace cesty, vraci ukazatel na strukturu cesty
//************************************************************************************
T_Cesta *VytvorCestu(int C_PocetMest)
{
	T_Cesta *V_Cesta;
	T_NMesta *P_Akt;
	int i,j,
		V_Pokracuj,
		*V_PouzitaMesta;
	
	V_PouzitaMesta = (int *) malloc(C_PocetMest * sizeof(int));
	for (i = 0; i < C_PocetMest; i++)
		V_PouzitaMesta[i] = 0;//false

	V_Cesta = (T_Cesta *) malloc(sizeof(T_Cesta));
	V_Cesta->NMesta = (T_NMesta *) malloc(sizeof(T_NMesta));
	P_Akt = V_Cesta->NMesta;
	
	P_Akt->Mesto = rand() % C_PocetMest;
	V_PouzitaMesta[P_Akt->Mesto] = 1;//true


	for (i = 1; i < C_PocetMest; i++)
	{
		P_Akt->Dalsi = (T_NMesta *) malloc(sizeof(T_NMesta));
		P_Akt = P_Akt->Dalsi;
	
		j = rand() % C_PocetMest;
		V_Pokracuj = 1;
		while(V_Pokracuj)
		{
			if (V_PouzitaMesta[j] == 0)
			{
				P_Akt->Mesto = j;
				V_PouzitaMesta[j] = 1;
				V_Pokracuj = 0;
			}
			if(j == C_PocetMest-1) j = -1;			
			j++;			
		}
	}
	P_Akt->Dalsi = NULL;

	free(V_PouzitaMesta);

return(V_Cesta);
}
//************************************************************************************
//Dealokuje cestu
//************************************************************************************
int DealokujCestu(T_Cesta *V_Cesta)
{
	T_NMesta *P_Akt, *P_Zrus;

		P_Akt = V_Cesta->NMesta;
		P_Zrus = P_Akt;

	while(P_Akt->Dalsi != NULL)
	{
		P_Akt = P_Akt->Dalsi;
		free(P_Zrus);
		P_Zrus = P_Akt;
	}

return 0;
}
//************************************************************************************
//Prida položku do tabulistu, do tabulistu se uklada cislo operace(index operace v poli operaci)
//************************************************************************************
int VlozDoTabu(int V_Vkladane, int *V_TabuList, int C_VelikostTabu)
{
	int i;
	//posun seznam, zahodi nejstarsi prvek
	for(i = C_VelikostTabu-1; i > 0; i--)
	{
		V_TabuList[i] = V_TabuList[i-1];
	}

	//na zacatek vloz novou tabu operaci
	V_TabuList[0] = V_Vkladane;

return 0;
}

//************************************************************************************
//Zjisti zda je operace v TabuListu, pokud ano vraci 1
//************************************************************************************
int JeTabu(int V_Overovana , int *V_TabuList, int C_VelikostTabu)
{
	int i;
	
	for(i = 0; i < C_VelikostTabu; i++)
	{
		if (V_Overovana == V_TabuList[i]) return(1);
	}
return 0;
}
//************************************************************************************
//Spocte cenu daneho reseni,
//************************************************************************************
int SpoctiCenu(double **V_Hrany, T_Cesta *V_Cesta)
{
	double V_CenaCesty = 0;
	T_NMesta *P_Z, *P_Do;//z mesta do mesta...

	P_Z = V_Cesta->NMesta;
	P_Do = P_Z->Dalsi;
	do
	{
		V_CenaCesty += V_Hrany[P_Z->Mesto][P_Do->Mesto];		
		P_Z = P_Z->Dalsi;
		P_Do = P_Do->Dalsi;
	}while(P_Do != NULL);
	V_Cesta->DelkaCesty = V_CenaCesty;


return 0;
}
//************************************************************************************
//Provede transformaci reseni dle zadane transformace(ktere mesto za ktere prohodit)
//************************************************************************************
T_Cesta *Transformuj(int **V_OpSezn, int V_IndexOperace, T_Cesta *V_Cesta, T_Cesta *V_NovaCesta)
{
	T_NMesta *P_C, *P_NC, *P_Prohod1, *P_Prohod2;//z mesta do mesta...
	int V_Pomocna;

	//prekopiruju stavajici reseni do noveho
	P_C = V_Cesta->NMesta;
	P_NC = V_NovaCesta->NMesta;

	do
	{
		P_NC->Mesto = P_C->Mesto;
		P_C = P_C->Dalsi;
		P_NC = P_NC->Dalsi;
	}while(P_C != NULL && P_NC != NULL);
	
	//najdu a prohodim dle transformace
	P_Prohod1 = V_NovaCesta->NMesta;
	P_Prohod2 = P_Prohod1;

	while(P_Prohod1->Mesto != V_OpSezn[V_IndexOperace][0])
		P_Prohod1 = P_Prohod1->Dalsi;
	while(P_Prohod2->Mesto != V_OpSezn[V_IndexOperace][1])
		P_Prohod2 = P_Prohod2->Dalsi;

	V_Pomocna = P_Prohod1->Mesto;
	P_Prohod1->Mesto = P_Prohod2->Mesto;
	P_Prohod2->Mesto = V_Pomocna;

return(V_NovaCesta);
}

//************************************************************************************
// TABU SEARCH
//************************************************************************************
T_Cesta *TabuSearch(double **V_Hrany, int V_VelHrany, int C_NeexistujiciHrana, int C_VelikostTabu, int C_ProhazovaciVzdalenost, int C_PocetIteraci)// je tu vubec nekde treba neexhrana?????
{
	//deklarace promenych----------------------------------------------------------
	int **V_OpSezn,//pole obsahujici sezxnam operaci co se budou provadet
		*V_TabuList,
		V_PocetOperaci, //delka seznamu operaci
		V_DoTabu,//pomocna promena, co budeme vkladat do tabu za operaci....
		i,j;
	T_Cesta *V_Cesta, *V_NovaCesta, *V_VyslCesta, *V_LokOptCesta;
	T_NMesta *P_Akt, *P_AktN;



	//Ruzne inicialziace------------------------------------------------------------------
	V_OpSezn = InicializujSeznamOperaci(V_VelHrany, &V_PocetOperaci, C_ProhazovaciVzdalenost);
		
	//inicialiace tabulistu
	V_TabuList = (int *) malloc(C_VelikostTabu * sizeof(int));
	for(i = 0; i < C_VelikostTabu; i++)
	{
		V_TabuList[i] = 0;
	}
	
	//inicializace + vygenerovani prvniho reseni(Mesta v poradi 0,1,2,3,....=====================================================================================
	V_Cesta = VytvorCestu(V_VelHrany);
	SpoctiCenu(V_Hrany, V_Cesta);//spocti cenu prvniho reseni

	//inic pomocne cesty	
	V_NovaCesta = VytvorCestu(V_VelHrany);

	//inic lok optimalnic cesty
	V_LokOptCesta = VytvorCestu(V_VelHrany);
	
	//inic vysledne cesty	
	V_VyslCesta = VytvorCestu(V_VelHrany);

	V_VyslCesta->DelkaCesty = V_VelHrany * C_NeexistujiciHrana + 1;//nastavi lokalni minimum na max co to jde


	
	//Samotny Algoritmus======================================================================================================================================================
	for(i = 0; i < C_PocetIteraci; i++)
	{
		printf("*");
		V_LokOptCesta->DelkaCesty = V_VelHrany * C_NeexistujiciHrana + 1;//nastavi lokalni minimum na max co to jdeV_NovaCesta->DelkaCesty = V_VelHrany * C_NeexistujiciHrana + 1;//nastavi lokalni minimum na max co to jde
		for(j = 0; j < V_PocetOperaci; j++)
		{
		//	printf(".");
			V_NovaCesta = Transformuj(V_OpSezn, j, V_Cesta, V_NovaCesta); //transformuj dle operace pokud neni v tabu
			SpoctiCenu(V_Hrany, V_NovaCesta);//prepocitej delku noveho reseni
	
			if ((V_NovaCesta->DelkaCesty < V_LokOptCesta->DelkaCesty) && (!JeTabu(j , V_TabuList, C_VelikostTabu)))
			{
				V_LokOptCesta->DelkaCesty = V_NovaCesta->DelkaCesty;
				P_Akt = V_LokOptCesta->NMesta; //prohodi se oba seznamy mest, druhej se v tranformuj prekopiruje,t ak by to melo byt ok!!!!!!!!!!!!!!!!!!!!!1
				V_LokOptCesta->NMesta = V_NovaCesta->NMesta;
				V_NovaCesta->NMesta = P_Akt; 
				V_DoTabu = j;				
			}
		
		}
		if (V_LokOptCesta->DelkaCesty < V_VyslCesta->DelkaCesty) 
		{
			V_VyslCesta->DelkaCesty = V_LokOptCesta->DelkaCesty;
			P_Akt = V_VyslCesta->NMesta;
			P_AktN = V_LokOptCesta->NMesta;
			do
			{
				P_Akt->Mesto = P_AktN->Mesto;
				P_Akt = P_Akt->Dalsi;
				P_AktN = P_AktN->Dalsi;
			}while((P_Akt != NULL) && (P_AktN != NULL));

		}
	
		VlozDoTabu(V_DoTabu, V_TabuList, C_VelikostTabu);//vlozi provedenou operaci do tabu
		
		V_Cesta->DelkaCesty = V_LokOptCesta->DelkaCesty;
		P_Akt = V_Cesta->NMesta;
		P_AktN = V_LokOptCesta->NMesta;
		do
		{
			P_Akt->Mesto = P_AktN->Mesto;
			P_Akt = P_Akt->Dalsi;
			P_AktN = P_AktN->Dalsi;
		}while((P_Akt != NULL) && (P_AktN != NULL));

	}
	//=============================================================================================================================================================================

	//dealokuj tabu, vsechny cesty krom vysledne, seznam operaci
	
	DealokujCestu(V_NovaCesta);
	DealokujCestu(V_LokOptCesta);
	DealokujCestu(V_Cesta);
	free(V_TabuList);
	free(V_OpSezn[0]);
	free(V_OpSezn[1]);
	free(V_OpSezn);


return(V_VyslCesta);
}


//************************************************************************************
// MAIN-úříklad použití
//************************************************************************************
int main(int argc, char* argv[]) {
	//-------------------------------------------------
	//deklarace promenych
	//-------------------------------------------------
int		V_VelHrany,
		C_NeexistujiciHrana,
		C_ProhazovaciVzdalenost,
		C_PocetIteraci,//pocet iteraci 
		C_VelikostTabu,// udava maximalni pocet polozek v tabu listu
		V_Err; //error counter	

	double **V_Hrany;
	T_Cesta *V_Vysledek;
	T_NMesta *P_Akt;
	char *VST_SOUBOR, *VYST_SOUBOR;
	FILE *Pfw_Vyst;

	clock_t V_StartTime, V_EndTime; 
	
	//-------------------------------------------------
	//inicializace promenych
	//-------------------------------------------------	
	VST_SOUBOR = "input.txt";
	VYST_SOUBOR = "vystup.txt";
	C_NeexistujiciHrana = 10000;//hodnota pro neexistujici hrany
	C_VelikostTabu = 5;
	C_ProhazovaciVzdalenost = 10;
	C_PocetIteraci = 1000;
	V_Err = 0;
	//-------------------------------------------------
	
	
	printf("Chcete spustit program s vychozimi parametry?(y/n):");
 
	if(getchar()=='n')
	{
	 printf("Pocet iteraci: ");
	 scanf("%d",&C_PocetIteraci);
	 printf("Cena Neexistujici hrany: ");
	 scanf("%d",&C_NeexistujiciHrana);
	 printf("Maximalni vzdalenost prohazovanych prvku: ");
	 scanf("%d",&C_ProhazovaciVzdalenost);
	 printf("Velikost tabu listu: ");
	 scanf("%d",&C_VelikostTabu);
	 getchar();
	}
 
    printf("Spoustim TabuSearch s naseldujicimi parametry\n");	
	printf("Pocet iteraci: %d\n",C_PocetIteraci);
	printf("Cena Neexistujici hrany: %d\n",C_NeexistujiciHrana);
	printf("Maximalni vzdalenost prohazovanych prvku: %d\n",C_ProhazovaciVzdalenost);
	printf("Velikost tabulistu: %d\n",C_VelikostTabu);

	
	
	if ((Pfw_Vyst = fopen(VYST_SOUBOR, "w")) == NULL) V_Err++;
	
    Loguj(Pfw_Vyst, "Pocet iteraci: ", C_PocetIteraci,'d',1);
	Loguj(Pfw_Vyst, "Cena Neexistujici hrany: ", C_NeexistujiciHrana,'d',1);
	Loguj(Pfw_Vyst, "Maximalni vzdalenost prohazovanych prvku: ", C_ProhazovaciVzdalenost,'d',1);
	Loguj(Pfw_Vyst, "Velikost tabulistu: ", C_VelikostTabu,'d',1);
	
		
	srand((int)time(NULL)); //incializace pro rand()

	if ((V_Hrany = VytvorPole2(VST_SOUBOR, &V_VelHrany)) == 0) V_Err++;  //vytvori pole vstupu
	if ((NactiHrany(VST_SOUBOR, V_Hrany, V_VelHrany, C_NeexistujiciHrana)) == 0) V_Err++;  //vytvori pole vstupu   

	if(V_Err ==0)
	{	
		
		V_StartTime = clock();

		V_Vysledek = TabuSearch(V_Hrany, V_VelHrany, C_NeexistujiciHrana, C_VelikostTabu, C_ProhazovaciVzdalenost, C_PocetIteraci);
		
		V_EndTime = clock();
			printf("\nCas spracovani[ms]: %d\n",(long int)(V_EndTime - V_StartTime) ); 
			printf("Nalezena nejlepsi cesta je delky: %f", V_Vysledek->DelkaCesty);
		
		//vypis
		printf("=================================================================== \n", C_PocetIteraci);
		printf("Nalezena nejlepsi cesta je delky: %f \n", V_Vysledek->DelkaCesty);
		printf("Cesta:\n");
		for (P_Akt = V_Vysledek->NMesta; P_Akt != NULL; P_Akt = P_Akt->Dalsi) printf("%d ",P_Akt->Mesto);
		printf("\n");

		//vypis do souboru
		Loguj(Pfw_Vyst, "Nalezena nejlepsi cesta je delky: ", V_Vysledek->DelkaCesty,'f',1);	
		Loguj(Pfw_Vyst, "Minimalni cesta: ", NULL,'t',1);
		for (P_Akt = V_Vysledek->NMesta; P_Akt != NULL; P_Akt = P_Akt->Dalsi)
		{
			Loguj(Pfw_Vyst, " ", P_Akt->Mesto,'d',1);
		} 
		Loguj(Pfw_Vyst, "", NULL,'t',1);
		Loguj(Pfw_Vyst, "Cas vypoctu[ms]: ", (long int)(V_EndTime - V_StartTime),'d',1);
		printf("\nCas spracovani[ms]: %d\n",(long int)(V_EndTime - V_StartTime) ); 

	}
	printf("V programu nastalo %d erroru\n", V_Err);
	printf("Stisknete libovolnou klavesu");
	fclose(Pfw_Vyst);
	

	getchar();

return 1;
}