// This file is part of the Herezh++ application.
//
// The finite element software Herezh++ is dedicated to the field
// of mechanics for large transformations of solid structures.
// It is developed by Gérard Rio (APP: IDDN.FR.010.0106078.000.R.P.2006.035.20600)
// INSTITUT DE RECHERCHE DUPUY DE LÔME (IRDL) <https://www.irdl.fr/>.
//
// Herezh++ is distributed under GPL 3 license ou ultérieure.
//
// Copyright (C) 1997-2021 Université Bretagne Sud (France)
// AUTHOR : Gérard Rio
// E-MAIL  : gerardrio56@free.fr
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// For more information, please consult: <https://herezh.irdl.fr/>.

/************************************************************************
 *     DATE:        11/10/2003                                          *
 *                                                                $     *
 *     AUTEUR:      G RIO   (mailto:gerardrio56@free.fr)                *
 *                                                                $     *
 *     PROJET:      Herezh++                                            *
 *                                                                $     *
 ************************************************************************
 *     BUT:   Algorithmes de base pour la recherche de zéro d'une       *
 *            fonction ou d'un ensemble de fonctions.                   *
 *                                                                $     *
 *     ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''     *
 *                                                                $     *
 ************************************************************************/
#ifndef ALGO_ZERO_H
#define ALGO_ZERO_H

#include "Vecteur.h"
#include "Mat_abstraite.h"
#include "Racine.h"
#include <math.h>

/// @addtogroup Les_classes_algo
///  @{
///


/**
*
*     BUT: pour la gestion d'exception pour non convergence
*
*
* \author    Gérard Rio
* \version   1.0
* \date      11/10/2003
* \brief       pour la gestion d'exception pour non convergence
*
*/

class ErrNonConvergence_Newton
  // =0 cas courant, pas d'information particulière
  // =1 cas où l'erreur est sévère et ne pourra pas être corrigé en refaisant un calcul avec un
  //    pas de temps plus petit. Il faut refaire le calcul en se positionnant plusieurs pas de temps
  //    auparavant (utilisé par l'hystérésis par exemple) 
  { public :
     int cas;   
     ErrNonConvergence_Newton () : cas(0) {} ;  // par défaut
     ErrNonConvergence_Newton (int ca) : cas(ca) {} ;  // pb
  };
/// @}  // end of group

/// @addtogroup Les_classes_algo
///  @{
///


/**
*
*     BUT:   Algorithmes de base pour la recherche de zéro d'une
*            fonction ou d'un ensemble de fonctions.
*
*
* \author    Gérard Rio
* \version   1.0
* \date      11/10/2003
* \brief      Algorithmes de base pour la recherche de zéro d'une fonction ou d'un ensemble de fonctions.
*
*/

class Algo_zero
{
  public :
    // CONSTRUCTEURS :
    /// constructeur par défaut
    Algo_zero();
    /// constructeur de copie
    Algo_zero(const Algo_zero& a);    
    /// DESTRUCTEUR :
    ~Algo_zero();
    
    // METHODES PUBLIQUES :
    
    /// recherche du zéro d'une équation du second degré dans l'espace des réels: ax^2+bx+c=0.
    /// cas indique les différents cas:
    ///  = 1   : il y a deux racines , racine2 > racine1
    ///  = 2   : il y a une racine double
    ///  = 0   : pas de racine
    ///  = -1  : pas de racine car a,b,c sont tous inférieur à la précision de traitement
    ///  = -2  : pas de racine car a,b sont inférieur à la précision de traitement tandis que c est non nul
    ///  = -3  : une racine simple car a est inférieur à la précision de traitement, donc considéré comme nul
    ///          b et c sont non nul -> equa du premier degré, solution: racine1
    void SecondDegre(double a, double b, double c, double& racine1, double& racine2, int& cas) const ;
    
    /// recherche du zéro d'une équation du troisième degré dans l'espace des réels: ax^3+bx^2+cx+d=0.
    /// cas indique les différents cas:
    ///  = 1   : il y a deux racines , racine2 > racine1
    ///  = 2   : il y a une racine double
    ///  = 3   : il y a une seule racine réelle racine1 (et deux complexes non calculées)
    ///  = 4   : il y a une racine simple: racine1, et une racine double: racine 2
    ///  = 5   : il y a 3 racines réelles: racine1, racine2, racine3
    ///  = 0   : pas de racine
    ///  = -1  : pas de racine car a,b,c sont tous inférieur à la précision de traitement
    ///  = -2  : pas de racine car a,b sont inférieur à la précision de traitement tandis que c est non nul
    ///  = -3  : une racine simple car a est inférieur à la précision de traitement, donc considéré comme nul
    ///          b et c sont non nul -> equa du premier degré, solution: racine1
    void TroisiemeDegre(double a, double b, double c, double d, double& racine1
                       , double& racine2, double& racine3, int& cas) ;

    ///recherche du zéro d'une fonction en utilisant la méthode de Newton-Raphson incrémentale
    /// ici il s'agit d'une fonction à une variable
    ///  *Ptfonc : le pointeur de la fonction dont il faut chercher le zéro
    ///  *Ptder_fonc : pointeur de la dérivée de la fonction
    ///               pour ces deux fonctions, la variable alpha est un facteur de charge qui varie de
    ///               0 à 1. Lorsque alpha=0, la racine vaut val_initiale, et ce que l'on
    ///               cherche c'est la racine pour alpha = 1. Lorsque alpha varie progressivement
    ///               de 0 à 1, la racine est sensée varier progressivement de val_initiale à résidu
    ///               l'utilisation d'alpha permet de faire du Newton incrémentale.
    ///   pour ces deux fonctions, l'argument test ramène
    ///     . 1 si le calcul a été ok, -1 s'il y a eu un pb, mais on peut continuer, 0 s'il y a eu un pb
    ///       fatal, qui invalide le calcul de la fonction et/ou de la dérivée
    ///  val_initiale : une valeur initiale de x pour la recherche de zéro
    ///  max_delta_x : si > 0 donne le norme maxi permise sur delta_x au cours d'une itération
    ///               si ||delta_x|| est plus grand on fait delta_x = max_delta_x * delta_x / || delta_x||
    ///  ramène en sortie:
    ///     un booléen qui indique si la résolution est correcte ou non
    ///     racine : la racine trouvée
    ///     der_at_racine : contient en retour la valeur de la dérivée pour la racine trouvée
    ///     nb_incr_total : le nombre total d'incrément qui a été nécessaire
    ///     nb_iter_total : le nombre total d'itération, qui cumule les iter de tous les incréments
    template <class T> bool  Newton_raphson
                 (T& instance,double (T::*Ptfonc) (double & alpha,double & x,int& test)
                  ,double (T::*Ptder_fonc) (double & alpha,double & x,int& test)
                  ,double val_initiale,double & racine,double & der_at_racine
                  ,int& nb_incr_total, int& nb_iter_total
                  ,double max_delta_x) const ;
    /// idem précédemment, mais pour une fonction à valeur vectorielle
    /// les matrices sont telles que: der_at_racine(i,j) = df(i)/d(x(j), soit: df = der_at_racine * dx
    template <class T> bool  Newton_raphson
                 (T& instance,Vecteur& (T::*Ptfonc) (const double & alpha,const Vecteur & x,int& test)
                  ,Mat_abstraite& (T::*Ptder_fonc) (const double & alpha,const Vecteur & x,int& test)
                  ,const Vecteur& val_initiale,Vecteur & racine,Mat_abstraite & der_at_racine
                  ,int& nb_incr_total, int& nb_iter_total
                  ,double max_delta_x) const;
    /// idem précédemment, mais pour une fonction à valeur vectorielle et ..
    ///  la méthode externe calcul la valeur de la fonction et de la dérivée en même temps
    /// mais on utilise également la fonction résidu toute seule
    /// les matrices sont telles que: der_at_racine(i,j) = df(i)/d(x(j), soit: df = der_at_racine * dx
    template <class T> bool  Newton_raphson
                (T& instance,Vecteur& (T::*Ptfonc) (const double & alpha,const Vecteur & x,int& test)
                 ,Mat_abstraite& (T::*Ptder_fonc) (const double & alpha,const Vecteur & x,Vecteur& res,int& test)
                 ,const Vecteur& val_initiale,Vecteur & racine,Mat_abstraite & der_at_racine
                 ,int& nb_incr_total, int& nb_iter_total
                 ,double max_delta_x) const ;
                         
                         
    /// méthodes pour modifier les différents paramètres
        /// précision sur le résidu à convergence
    void Modif_prec_res_abs (double eps) {prec_res_abs = eps;}; ///< précision absolue
    void Modif_prec_res_rel (double eps) {prec_res_rel = eps;}; ///< précision relative
        /// nombre d'itération maxi pour un incrément
    void Modif_iter_max (double eps) { iter_max = eps;};    
        /// précision relative sur le mini de l'incrément de x
    void Modif_coef_mini_delta_x (double eps) {coef_mini_delta_x = eps;};                       
        /// minimum de delta x permis
    void Modif_mini_delta_x (double eps) {mini_delta_x = eps;};    
        /// maximum de delta x permis
    void Modif_maxi_delta_x (double eps) {maxi_delta_x = eps;};    
        /// nombre maxi d'incrément permis
    void Modif_nbMaxiIncre (int  nb) {nbMaxiIncre = nb;};
        /// nombre maxi de test -1 permis
    void Modif_nbMaxi_test_moins1 (int  nb) {maxi_test_moins1 = nb;};
        /// initialisation de tous les paramètres à leurs valeurs par défaut
    void Init_param_val_defaut();
        /// le niveau d'affichage
    void Modif_affichage(int niveau) {permet_affichage = niveau;};
 
    /// récup en lecture des différents paramètres
    const double& Prec_res_abs() const {return prec_res_abs;}; ///< précision absolue sur le résidu à convergence
    const double& Prec_res_rel() const {return prec_res_rel;};///< précision relative sur le résidu à convergence
    const int& Iter_max() const {return iter_max;};///< nombre d'itération maxi pour un incrément
    const double& Coef_mini_delta_x() const {return coef_mini_delta_x;};///< précision relative sur le mini de l'incrément de x
    const double& Mini_delta_x() const {return mini_delta_x;};///< minimum de delta x permis
    const double& Maxi_delta_x() const {return maxi_delta_x;};///< maximum de delta x permis
    const int& NbMaxiIncre() const {return nbMaxiIncre;};///< nombre maxi d'incrément permis
    const int& Maxi_test_moins1() const {return maxi_test_moins1;};///< nombre maxi de test -1 permis
    const int& Permet_affichage() const {return permet_affichage;}; ///< le niveau d'affichage

 
			 /// affichage à l'écran des infos
			 void Affiche() const;
 
    ///----- lecture écriture de restart -----
    /// cas donne le niveau de la récupération
    /// = 1 : on récupère tout
    /// = 2 : on récupère uniquement les données variables (supposées comme telles)
	   void Lecture_base_info(ifstream& ent,const int cas);
    /// cas donne le niveau de sauvegarde
    /// = 1 : on sauvegarde tout
    /// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
	   void Ecriture_base_info(ofstream& sort,const int cas);
 
 
  private :  
    // VARIABLES PROTEGEES :
    double prec_res_abs; // précision absolue sur le résidu à convergence
    double prec_res_rel; // précision relative sur le résidu à convergence
    int iter_max; // nombre d'itération maxi pour un incrément
    double coef_mini_delta_x; // précision relative sur le mini de l'incrément de x
    double mini_delta_x; // minimum de delta x permis
    double maxi_delta_x; // maximum de delta x permis
    int nbMaxiIncre; // nombre maxi d'incrément permis
    int maxi_test_moins1; // nombe maxi de test = -1 permi
    // ----- controle de la sortie des informations
    int permet_affichage; // pour permettre un affichage spécifique dans les méthodes,
                          // pour les erreurs et des wernings
 
    Quartic  algo_quartic; // une suite d'algo pour résoudre une quartic et des cubic
    
    
    // METHODES PROTEGEES :

 };
 
  // pour faire de l'inline: nécessaire avec les templates
  // on n'inclut que les méthodes templates
  #include "Algo_zero_2.cc"
  #define  Algo_zero_deja_inclus
 /// @}  // end of group

#endif  
