#ifndef SIMULATED_ANNEALING_H
#define	SIMULATED_ANNEALING_H

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include "config.h"

#define SA_INFINITY KD_INFINITY


#define SA_DEBUG

typedef struct SA_PARAM {
    double **x; // jednorozmerne pole, ktere se odkazuje na jednotlive parametry
    double *dmax; // jednorozmerne pole, ktere udava o kolik se muze kazdy parametr zmenit v jednom cyklu
    unsigned int n; // pocet parametru
} sa_param;

typedef struct SA_VECTOR { // Implementace jednoducheho vektoru
    double *x;
    unsigned int n;
} sa_vector;

// Nastaveni algoritmu
typedef struct SA_ALGO {
    double Tmax; // pocatecni teplota
    double Tmin; // minimalni teplota, po ktere se ukoci algoritmus
    unsigned int kmax; // pocet moznych zmen parametru v jedne iteraci
    double dmax; // mozny rozsah pri zmene parametru se vypocita jako soucin teto hodnoty a sa_param->dmax[i]
    double alfa; // rychlost klesani teploty
    double beta; // rychlost klesani rozsahu dmax
    double (*energy)(void *); // chybova funkce
    void *data; // pomocne data, ktere vyuziva chybova funkce ENERGY
    const char *logfile; // pokud neni NULL, tak se budou prubezne vysledky (iterace) ukladat do souboru LOGFILE.
} sa_algo;

typedef struct SA_METROPOLIS_INFO {
    double *dE; // Prubeh zmeny chyby
    unsigned int acceptedStates; // Prijatych stavu
    unsigned int betterStates; // pocet zlepseni
    unsigned int worseStates; // pocet zorseni
    double E; // aktualni chyba
} sa_metropolis_info;

/*
 * Vrati nahodne cislo od 0 do 1.
 */
double sa_rand ();

/*
 * Alokuje mnozinu parametru o poctu N.
 */
sa_param *sa_param_alloc (unsigned int N);

/*
 * Uvolni mnozinu parametru.
 */
void sa_param_free (sa_param *p);

/*
 * Na pozici I v mnozine parametru P ulozi ukazatel na parametr, ktery je typu
 * double.
 */
void sa_param_define (sa_param *p, unsigned int i, double *param);

/*
 * Vrati hodnotu I-teho parametru z mnoziny parametru P.
 */
double sa_param_get (sa_param *p, unsigned int i);

/*
 * Nastavi hodnotu I-teho parametru v mnoziny parametru P.
 */
void sa_param_set (sa_param *p, unsigned int i, double value);

/*
 * Ziska maximalni moznou zmenu parametru P[I].
 */
double sa_param_dmax_get (sa_param *p, unsigned int i);

/*
 * Nastavi maximalni moznou zmenu (D) pro parametr P[I].
 */
void sa_param_dmax_set (sa_param *p, unsigned int i, double d);

/*
 * Vrati pole hodnot parametru z mnoziny parametru P.
 */
double *sa_get_params (sa_param *p);

/*
 * Vytiskne hodnoty vsech parametru.
 */
void sa_param_print (sa_param *p);

/*
 * Alokuje vektor o N prvcich.
 */
sa_vector * sa_vector_alloc (unsigned int n);

/*
 * Uvloni z pameti vektor.
 */
void sa_vector_free (sa_vector *vec);

/*
 * Nastavi I-temu prvku vektrou VEC hodnotu VALUE.
 */
void sa_vector_set (sa_vector *vec, unsigned int i, double value);

/*
 * Vrati hodnotu I-teho prvku vektoru VEC.
 */
double sa_vector_get (sa_vector *vec, unsigned int i);

/*
 * Nastavi hodnoty vektoru VEC hodnotami z mnoziny parametru P.
 */
int sa_vector_from_param (sa_vector *vec, sa_param *p);

/*
 * Nastavi hodnoty mnoziny parametru P z vektoru VEC.
 */
int sa_param_from_vector (sa_param *p, sa_vector *vec);

/*
 * Vypocita a vrati prumer pole X o N prvcich.
 */
double sa_mean (double *x, unsigned int n);

/*
 * Vypocita a vrati smerodatnou odchylky veliciny X o N prvcich.
 */
double sa_stdev (double *x, unsigned int n);

/*
 * Metropolisuv algoritmus.
 */
double sa_metropolis (sa_param *params, unsigned int kmax, double T, double dxmax, double (*energy) (void *), void *data, sa_metropolis_info *info);

/*
 * Upravi parametry PARAMS.
 * Parametry se zmeni o nahodnou hodnotu < - DXMAX ; + DXMAX >
 */
void sa_opert (sa_param *params, double dxmax);

/*
 * Vrati hodnotu pravdepodobnosti od < 0 , 1 >.
 */
double sa_pr_min (double E, double Enew, double T);

/*
 * Algoritmus simulovaneho zihani.
 */
void sa_algorithm (sa_param *params, sa_algo *algo);

/*
 * Ulozi parametry simulovaneho zihani.
 */
int sa_opt_save(const char *filename, sa_algo *opt);

/*
 * Nahraje parametry simulovaneho zihani.
 */
sa_algo *sa_opt_load(const char *filename);


#endif	/* SIMULATED_ANNEALING_H */

