/**
 * @file approximation.c
 * @brief Contients les fonctions pour l'approximation via la méthode de regression.
 * @author Valentin PORTAIL et Jérémie VILLEPREUX
 * @version 1.0
 * @date 08/11/2022
 */

#include "interpolation.h"
#include "gestionFichier.h"
#include "approximation.h"

/** 
 * @brief Permet de calculer un floattant au carre.
 *
 * @param[in] x              Nombre a élever au carrée.
 *
 * @return Renvoie la *x**2.
 */
float carre(float x){
    return x*x;
}


/** 
 * @brief Procédure permettant de liberer tableau 1D.
 *
 * @param[in] tab    Pointeur sur la première case du tabeau.
 *
 */
void libere_tab_1d(float *tab){
    if (!tab) free(tab);
}


/** 
 * @brief Procédure permettant de remplacer chaque case du tableau par sa composé avec ln.
 *
 * @param[in] tab    Pointeur sur la première case du tabeau.
 * @param[in] n      Taille du tabeau.
 *
 */
void tab_ln(float *tab, int n){
    for(int i = 0; i < n; i++)
        tab[i] = log(tab[i]);
}


/** 
 * @brief Permet de calculer la moyenne de chaque case du tableau.
 *
 * @param[in] tab    Pointeur sur la première case du tabeau.
 * @param[in] n      Taille du tabeau.
 *
 * @return Renvoie la *x**2.
 */
float moyenne(float *points, int n){

    float somme = 0.0;
    float moyenne = 0.0;
    int i = 0;
    
    if (!n){
	perror("Impossible de faire la moyenne de zéros termes.\n");
	exit(EXIT_FAILURE);
    }


    for(i=0; i<n; i++){
	 somme +=points[i];
    }

    moyenne = ((float) (somme/n));
    
    return moyenne;
}


/** 
 * @brief Procédure permettant de remplacer chaque case du tableau par son produit avec elle meme.
 *
 * @param[in] tab    Pointeur sur la première case du tabeau.
 * @param[in] n      Taille du tabeau.
 *
 */
void produit_tab(float *points0, float *points1, int n){

    int i = 0;

    for(i=0; i<n; ++i){
	points0[i] *= points1[i];
    }
}



/** 
 * @brief Permet de faire la copie d'un tableau.
 *
 * @param[in] tab    Pointeur sur la première case du tabeau.
 * @param[in] taille Taille du tabeau.
 *
 * @return Renvoie un poitneur sur ce nouveau tableau.
 */
float *copy_tab(float*tab, int taille){

    float* tabb = malloc(taille*sizeof(float));

    if (!tabb){
	perror("Echec d'allcoation.\n");
	exit(EXIT_FAILURE);
    }

    for (int i=0; i<taille; ++i){
	tabb[i]=tab[i];
    }

    return tabb;
}


/** 
 * @brief Permet de faire l'approximation par la methode de regression linéaire.
 *
 * @param[in] pointsx    Pointeur sur la première case du tabeau des abscisse.
 * @param[in] pointsx    Pointeur sur la première case du tabeau des ordonnées.
 * @param[in] nb_points  Nombredepoint du tablea.
 *
 * @return Renvoie un pointeur sur ce nouveau tableau qui contientdeux cases,
 *une pour **a** et l'autre pour **b** dans l'equation *ax + b*.
 */
float *approximation(float *pointsx, float *pointsy, int nb_points){
    

    float x_moy = 0;
    float y_moy = 0;
    float xy_moy = 0;
    float x2_moy = 0;
    float x_moy2 = 0;

    float a = 0;
    float b = 0;

    float *tabInter0 = NULL;
    float *tabInter1 = NULL;
    float *tabFinal = NULL;

    tabFinal = (float *)malloc(2*sizeof(float));

    if (!tabFinal){
	perror("Allocation failure.\n");
	exit(EXIT_FAILURE);
    }
	
    x_moy = moyenne(pointsx, nb_points);
    x_moy2 = x_moy * x_moy;
    
    y_moy = moyenne(pointsy, nb_points);

    tabInter0 = copy_tab(pointsx, nb_points);
    tabInter1 = copy_tab(pointsy, nb_points);
    produit_tab(tabInter0, tabInter1, nb_points);
    xy_moy = moyenne(tabInter0, nb_points);
    libere_tab_1d(tabInter0);
    libere_tab_1d(tabInter1);
    

    tabInter0 = copy_tab(pointsx, nb_points);
    produit_tab(tabInter0, tabInter0, nb_points);
    x2_moy = moyenne(tabInter0, nb_points);
    libere_tab_1d(tabInter0);

    a = (1.0*(xy_moy - x_moy*y_moy))/(1.0*(x2_moy - x_moy2));
    b = (float) y_moy - a*x_moy;

    tabFinal[0] = a;
    tabFinal[1] = b;
    
    return tabFinal;
}


/** 
 * @brief Permet de faire l'approximation par la methode de regression pour une equation **ax^b**.
 *
 * @param[in] pointsx    Pointeur sur la première case du tabeau des abscisse.
 * @param[in] pointsx    Pointeur sur la première case du tabeau des ordonnées.
 * @param[in] nb_points  Nombredepoint du tablea.
 *
 * @return Renvoie un pointeur sur ce nouveau tableau qui contientdeux cases,
 *une pour **a** et l'autre pour **b** dans l'equation *ax^b*.
 */
float *approximation_exp(float *pointsx, float *pointsy, int nb_points){
    

    float x_moy = 0;
    float y_moy = 0;
    float xy_moy = 0;
    float x2_moy = 0;
    float x_moy2 = 0;

    float a = 0;
    float b = 0;

    float *tabInter0 = NULL;
    float *tabInter1 = NULL;
    float *tabFinal = NULL;

    tabFinal = (float *)malloc(2*sizeof(float));

    if (!tabFinal){
	perror("Allocation failure.\n");
	exit(EXIT_FAILURE);
    }

    tab_ln(pointsx, nb_points);
    tab_ln(pointsy, nb_points);
	
    x_moy = moyenne(pointsx, nb_points);
    x_moy2 = x_moy * x_moy;
    
    y_moy = moyenne(pointsy, nb_points);

    tabInter0 = copy_tab(pointsx, nb_points);
    tabInter1 = copy_tab(pointsy, nb_points);
    produit_tab(tabInter0, tabInter1, nb_points);
    xy_moy = moyenne(tabInter0, nb_points);
    libere_tab_1d(tabInter0);
    libere_tab_1d(tabInter1);
    

    tabInter0 = copy_tab(pointsx, nb_points);
    produit_tab(tabInter0, tabInter0, nb_points);
    x2_moy = moyenne(tabInter0, nb_points);
    libere_tab_1d(tabInter0);

    a = (1.0*(xy_moy - x_moy*y_moy))/(1.0*(x2_moy - x_moy2));
    b = expf((float) y_moy - a*x_moy);

    tabFinal[0] = a;
    tabFinal[1] = b;
    
    return tabFinal;
}
