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


#define FALSE 0
#define TRUE  1


//-------------------------------------------------------------------------------------
//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_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);
		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;
     }
}
//************************************************************************************
// Vygeneruje počáteční řešení(vyhovující hamiltonově kružnici(uzavrena cesta jen pres existujici hrany)),pri neuspechu vraci 0
//************************************************************************************
int VytvorPocatecniReseni(double **V_Hrany, int C_PocetMest,int *V_Cesta)
{
	int i,j,k,
		V_Nalezeno,
		V_Od,
		V_PocatecniMesto,
		V_VelikostZpetnehoKroku,
		V_PocetNavratuNaZacatek,
		*V_PouzitaMesta; 
	
	
	//alokace tabulky použitých měst + inicializace
	V_PouzitaMesta = (int *) malloc(C_PocetMest * sizeof(int));
	for(i = 0; i < C_PocetMest; i++)
	{
		V_PouzitaMesta[i] = FALSE;
	}

	//do cesty přidej náhodné město které není v tab použitých měst(má false) a cesta k němu nevede zakázanou hranou ---Neexhrana by mělo být vždy 0!!! netřeba nastavovat
		//projizdi pole od nahodneho cisla sekvenčne na kone a pak od začátku do toho náhodného čísla
			//když nic nenajdu, tak aplikuju "obejiti záseku" 
	
	V_PocetNavratuNaZacatek = 0;
	i = 0;
	do 
	{		
		if (i == 0)
		{
			//vyber nahodne pocatecni mesto
			V_Cesta[0] = rand() % C_PocetMest;
			V_PocatecniMesto = V_Cesta[0];
			V_PouzitaMesta[V_Cesta[0]] = TRUE;
			V_VelikostZpetnehoKroku = 1;
			V_PocetNavratuNaZacatek++;
			i++;
		}

		j = rand() % C_PocetMest;
		V_Nalezeno = FALSE;
		k = 0;
		do
		{
			if((V_Hrany[V_Cesta[i-1]][j] != 0) && (V_PouzitaMesta[j] == FALSE))
			{
				V_Cesta[i] = j;
				V_PouzitaMesta[j] = TRUE;
				V_Nalezeno = TRUE;
			}
			k++;
			j++;
			if (j == C_PocetMest) j = 1;
		}while((V_Nalezeno == FALSE) && (k < C_PocetMest));

		if((V_Nalezeno == FALSE) || ((i == C_PocetMest - 1) && (V_Hrany[V_Cesta[i]][V_Cesta[0]] == 0)))//nevim proc, ale enchce mi to sezrat fci JeCesta....
		{
			//vynuluje pouziti mest o ktera se vracim zpet(stavaji se neprojitymi)
			
			if (i - V_VelikostZpetnehoKroku > 0)  V_Od = i-V_VelikostZpetnehoKroku;
			else V_Od = 0;

			for(k = V_Od; k < i; k++)
			{
				V_PouzitaMesta[V_Cesta[k]] = FALSE;
			}
			i = i - V_VelikostZpetnehoKroku-1;
			if (i < -1) i = -1;
			V_VelikostZpetnehoKroku++;//cim vickrat zaseknu, tim vic se vracim		
		}
			
		i++;

	}while((i < C_PocetMest)&&(V_PocetNavratuNaZacatek < 1000));//zkusi maximalne 1000krat od zacatku, pak to vzda

	//dealokace tabulky použitých měst
	free(V_PouzitaMesta);

	if (V_PocetNavratuNaZacatek >= 1000) return(0); 

return(1);
}
//************************************************************************************
//inicializace prohazovacího seznamu, vraci ukazatel na pole,//prohazovat se bude vždy všechni se vším
//************************************************************************************
int **InicializujSeznamOperaci(int C_PocetMest, int *P_PocetOperaci, int C_ProhazovaciVzdalenost)
{
	int i,j,k,l,	
		**V_OpSezn;

	k = 0;
		

	if (C_ProhazovaciVzdalenost == 0)
	{
		*P_PocetOperaci = (int)((pow(C_PocetMest,2) - (C_PocetMest)) / 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; i++)
		{
			for(j = i + 1; j < C_PocetMest; j++)
			{
				V_OpSezn[k][0] = i;
				V_OpSezn[k][1] = j;
				k++;
			}
		}

	}else
	{	
		*P_PocetOperaci = (int)((double)C_ProhazovaciVzdalenost * ((double)C_PocetMest - (double)C_ProhazovaciVzdalenost + (((double)C_ProhazovaciVzdalenost -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 ; i++)
		{
			l = 0;
			for(j = i + 1; j < C_PocetMest ; j++)
			{

				V_OpSezn[k][0] = i;
				V_OpSezn[k][1] = j;
				k++;
				l++;			
				if (l == C_ProhazovaciVzdalenost) break;
			}
		}
	} 
	
return(V_OpSezn);
}
//************************************************************************************
//Spocte cenu daneho reseni,
//************************************************************************************
double SpoctiCenu(double **V_Hrany, int *V_Cesta, int C_DelkaCesty)
{
	double V_CenaCesty = 0;
	int i;
	
	for(i = 0; i < C_DelkaCesty - 1; i++)
	{
		V_CenaCesty += V_Hrany[V_Cesta[i]][V_Cesta[i+1]];
	}

return(V_CenaCesty);
}

//************************************************************************************
//Zjisti existenci cesty, pri existenci vraci 1
//************************************************************************************
int JeCesta(double **V_Hrany, int V_Z, int V_Do)
{
	if (V_Hrany[V_Z][V_Do] == 0) return(0);
	else return(1);
}
//************************************************************************************
//Vygeneruje nové řešení
//************************************************************************************
int GenerujNoveReseni(double **V_Hrany, int **V_OpSezn, int V_IndexOperace, int C_DelkaCesty, int *V_Cesta, int *V_NovaCesta, int C_PocetOperaci)
{
	int i,j,
		V_MaxIter,
		V_Pokracuj;

	//prekopiruje starou cestu do nove (pak az neco prehodi)
	for(i = 0; i < C_DelkaCesty; i++)
		V_NovaCesta[i] = V_Cesta[i];
	
	//najdeme vyhovujici operaci(nejblize zadanemu indexu, ale aby vysledek byl hailtonova kruznice)
	V_Pokracuj = 1;
	V_MaxIter = 0;
	i = V_OpSezn[V_IndexOperace][0];
	j = V_OpSezn[V_IndexOperace][1];

	while((V_Pokracuj) && (V_MaxIter < C_PocetOperaci))
	{
		if(
			((i != 0) && (j != C_DelkaCesty - 1) && (JeCesta(V_Hrany, i-1, j)) && (JeCesta(V_Hrany, i, j+1)) && (JeCesta(V_Hrany, j-1, i)) && (JeCesta(V_Hrany, j, i+1)) )//pak mame reseni;	//neprohazujeme ani prvni ani posledni prvek a zaroven existuje cesta (mezi prvkem pred 1.prohazovanym a druhym prohazovanym) a cesta (mezi prvzkem za 2. prohazovanym a 1. prohazovanym)
			||
			((i == 0) && (j != C_DelkaCesty - 1) && (JeCesta(V_Hrany, i, j+1)) && (JeCesta(V_Hrany, j-1, i)) && (JeCesta(V_Hrany, j, i+1)) && (JeCesta(V_Hrany, C_DelkaCesty-1, j)) )//prohazuje se prvni s neposlednim
			||
			((i != 0) && (j == C_DelkaCesty - 1) && (JeCesta(V_Hrany, i-1, j)) && (JeCesta(V_Hrany, j, i+1)) && (JeCesta(V_Hrany, j-1, i)) && (JeCesta(V_Hrany, i, 0)) )//prohazuje se neprvni s poslednim			
		  )
		{
				V_NovaCesta[i] = V_Cesta[j];
				V_NovaCesta[j] = V_Cesta[i];				
				V_Pokracuj = 0;//konec nasli jsme co jsme chteli	
		}else
		{
			if (V_IndexOperace == C_DelkaCesty - 1) V_IndexOperace = 0;
			else V_IndexOperace++;
		}
		V_MaxIter++;
	}

if (V_MaxIter == C_DelkaCesty) return(1);//nezdarilo se najit cestu pres zadnou operaci!!!!
else return(0);
}
//************************************************************************************
//Přijme nové řešení-> prohodi ukazatele na pole V_Cesta a V_NovaCesta
//************************************************************************************
int PrijmiReseni(int **P_Cesta, int **P_NovaCesta)
{
	int *P_Pomocna;

	P_Pomocna = *P_Cesta;
	*P_Cesta = *P_NovaCesta;
	*P_NovaCesta = P_Pomocna;

return 0;
}

//************************************************************************************
// Simulovane žíhání//vraci cestu
//************************************************************************************
int *Annealing(double **V_Hrany, int C_PocetMest, double C_PocatecniTeplota, double C_KoncovaTeplota, double C_Beta, int C_PocetIteraci, int C_ProhazovaciVzdalenost)
{
	int i,j,k,
		V_Err,
		C_PocetOperaci,
		**V_OpSezn,
	    *V_Cesta, *V_NovaCesta, *V_NejlepsiCesta;
	double V_Teplota,
		   V_CenaCesty, V_CenaNoveCesty,V_CenaNejlepsiCesty,	
		   V_Pravd;

	V_Err = 0;

	//naalokuje cestu
	V_Cesta = (int *) malloc(C_PocetMest * sizeof(int));
	V_NovaCesta = (int *) malloc(C_PocetMest * sizeof(int));
	V_NejlepsiCesta = (int *) malloc(C_PocetMest * sizeof(int));
	
	V_OpSezn = InicializujSeznamOperaci(C_PocetMest, &C_PocetOperaci, C_ProhazovaciVzdalenost);
	

	if (!VytvorPocatecniReseni(V_Hrany,C_PocetMest, V_Cesta)) V_Err++;
	V_Teplota = C_PocatecniTeplota;
	for(i = 0; i < C_PocetMest; i++)
		V_NejlepsiCesta[i] = V_Cesta[i]; 

	V_CenaNejlepsiCesty = SpoctiCenu(V_Hrany, V_NejlepsiCesta, C_PocetMest);

	while (V_Teplota > C_KoncovaTeplota)
	{
		
		for(j = 0; j < C_PocetIteraci; j++) 
		{
			printf("*");
			for(k = 0; k < C_PocetOperaci-1; k++)
			{
			//	printf(".");
				GenerujNoveReseni(V_Hrany, V_OpSezn, k, C_PocetMest, V_Cesta, V_NovaCesta, C_PocetOperaci);
				V_CenaCesty = SpoctiCenu(V_Hrany, V_Cesta, C_PocetMest);
				V_CenaNoveCesty = SpoctiCenu(V_Hrany, V_NovaCesta, C_PocetMest);
		
				if (V_CenaNoveCesty < V_CenaCesty) V_Pravd = 1;
				else V_Pravd = exp(-(V_CenaNoveCesty - V_CenaCesty)/V_Teplota);

				if (((double)rand() / RAND_MAX) < V_Pravd)
				{ 
					PrijmiReseni(&V_Cesta, &V_NovaCesta);
					if (V_CenaNoveCesty < V_CenaNejlepsiCesty)
					{
						for(i = 0; i < C_PocetMest; i++)
							V_NejlepsiCesta[i] = V_Cesta[i]; 
						V_CenaNejlepsiCesty = SpoctiCenu(V_Hrany, V_NejlepsiCesta, C_PocetMest);
					} 
				} 
			}
		}

		V_Teplota = V_Teplota * C_Beta;
	}


	printf("\nNejlepsi cesta: ");
	for(i=0;i< C_PocetMest; i++)
	{
		printf("%d ",V_NejlepsiCesta[i]);
	}
	printf("\nCena nejlepsi cesty: %f",V_CenaNejlepsiCesty);		


return(V_NejlepsiCesta);//bude vracet délu cesty a uk na cestu??? 
}



//************************************************************************************
// MAIN-ukázka použití
//************************************************************************************


int main(int argc, char* argv[])
{
	//-------------------------------------------------
	//deklarace promenych
	//-------------------------------------------------
	int i,
		V_VelHrany,
		C_PocetIteraci,
		C_ProhazovaciVzdalenost,
		*V_Cesta,
		V_Err; //error counter	

	double **V_Hrany,
		   C_Beta,
		   C_PocatecniTeplota,
		   C_KoncovaTeplota;

	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_PocatecniTeplota = 3000;
	C_KoncovaTeplota = 1;
	C_PocetIteraci = 10;
	C_Beta = 0.8;
	C_ProhazovaciVzdalenost = 10;
	V_Err = 0;
	//-------------------------------------------------
	if ((Pfw_Vyst = fopen(VYST_SOUBOR, "w")) == NULL) V_Err++;
	
	printf("Chcete spustit program s vychozimi parametry?(y/n):");
 
	if(getchar()=='n')
	{
	 printf("Pocet iteraci: ");
	 scanf("%d",&C_PocetIteraci);
	 printf("Pocatecni teplota: ");
	 scanf("%lf",&C_PocatecniTeplota);
	 printf("Koncova teplota: ");
	 scanf("%lf",&C_KoncovaTeplota);
	 printf("Maximalni vzdalenost prohazovanych prvku: ");
	 scanf("%d",&C_ProhazovaciVzdalenost);
	 printf("Beta: ");
	 scanf("%lf",&C_Beta);
	 getchar();
	}
 
    printf("Spoustim Simulated Annealing s naseldujicimi parametry\n");	
	printf("Pocet iteraci: %d\n",C_PocetIteraci);
	printf("Pocatecni teplota:  %f\n",C_PocatecniTeplota);
	printf("Koncova teplota:  %f\n",C_KoncovaTeplota);
	printf("Maximalni vzdalenost prohazovanych prvku: %d\n",C_ProhazovaciVzdalenost);
	printf("Beta(0-1):  %f\n",C_Beta);

	Loguj(Pfw_Vyst, "Pocet iteraci: ", C_PocetIteraci,'d',1);
	Loguj(Pfw_Vyst, "Pocatecni teplota: ", C_PocatecniTeplota,'f',1);
	Loguj(Pfw_Vyst, "Koncova teplota: ", C_KoncovaTeplota,'f',1);
	Loguj(Pfw_Vyst, "Maximalni vzdalenost prohazovanych prvku: ", C_ProhazovaciVzdalenost,'d',1);
	Loguj(Pfw_Vyst, "Beta: ", C_Beta,'f',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)) == 0) V_Err++;  //vytvori pole vstupu   

	if(V_Err ==0)
	{	



		V_StartTime = clock();

		V_Cesta = Annealing(V_Hrany, V_VelHrany, C_PocatecniTeplota, C_KoncovaTeplota, C_Beta, C_PocetIteraci, C_ProhazovaciVzdalenost);
	
		V_EndTime = clock();
		
		printf("\nCas spracovani[ms]: %d\n",(long int)(V_EndTime - V_StartTime) );

		//vypis do souboru
		Loguj(Pfw_Vyst, "Nalezena nejlepsi cesta je delky: ", SpoctiCenu(V_Hrany, V_Cesta, V_VelHrany),'f',1);	
		Loguj(Pfw_Vyst, "Minimalni cesta: ", NULL,'t',1);
		for (i = 0; i < V_VelHrany;i++)
		{
			Loguj(Pfw_Vyst, " ", V_Cesta[i],'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) );
	
	}
	if (V_Err >0)printf("NASTAL ERROR!!!!!!!\n");
	printf("\nStiskni cokoli\n");
	getchar();

return 0;
}

