// 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-2022 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/>.


#include "LesFonctions_nD.h"
#include "ParaGlob.h"
//#include <extras.h>
#include "CharUtil.h"

// --------------- variables statiques ---------
 MotCle LesFonctions_nD::motCle; // liste des mots clés

LesFonctions_nD::LesFonctions_nD(const LesCourbes1D* lesC1) :  // par defaut
    listeDeFonction_nD(),lesCourbes1D(lesC1)
  #ifdef UTILISATION_MPI
    // pour les i/o
    ,buffer_ioBI_MPI(),tailles_buffer_ioBI_MPI(),offset_LesFonctions_nD()
    ,nb_buffer(1),taille_offset(2)
  #endif
    {
  #ifdef UTILISATION_MPI
      buffer_ioBI_MPI.Change_taille(nb_buffer);
      tailles_buffer_ioBI_MPI.resize(nb_buffer,0);
      offset_LesFonctions_nD.resize(taille_offset);
  #endif
    };
// DESTRUCTEUR :
LesFonctions_nD::~LesFonctions_nD ()
    { // effacement des grandeurs pointées par la liste de référence
      map < string, Fonction_nD * , std::less <string> >::iterator i,ifin=listeDeFonction_nD.end();
      for (i=listeDeFonction_nD.begin() ; i != ifin; i++)
             if ((*i).second != NULL) delete (*i).second;
    };
 
// changement du pointeur sur LesCourbes1D: prévu une seule fois
// sinon cela veut dire que lesCourbes1D n'est pas un singleton !!
// donc n'est possible que si lesCourbes1D n'a pas été attribué
// ok également si c'est le même pointeur qui est déjà enregistré
void LesFonctions_nD::Attribut_pointeur_lesCourbes1D(const LesCourbes1D * lesC1)
 { if (lesCourbes1D == NULL)
     {lesCourbes1D = lesC1;}
   else if (lesCourbes1D != lesC1)
    {cout << "\n *** erreur d'affectation du pointeur interne sur les courbes 1D "
          << " le pointeur est deja affecte ... "
          << "\n LesFonctions_nD::Attribut_pointeur_lesCourbes1D( "
          << endl;
     Sortie(1);
    };
 };


 // lecture des fonctions
 void LesFonctions_nD::Lecture(UtilLecture & entreePrinc)
   { if (strstr(entreePrinc.tablcar,"les_fonctions_nD")!=NULL)
      {try
        { if (ParaGlob::NiveauImpression() >= 4) cout << " debut de la lecture de fonctions " << endl;
          // lecture des fonctions temps qu'il n'y a pas de nouveau mot clé
          string nom_fonction,type_fonction; // variables de travail
          Fonction_nD * fonction=NULL;
          entreePrinc.NouvelleDonnee();
          while (!motCle.SimotCle(entreePrinc.tablcar))
            { // lecture du nom de la fonction et de son type
              *(entreePrinc.entree) >> nom_fonction >> type_fonction;
              if (ParaGlob::NiveauImpression() >= 6) 
                 cout << " lecture de la fonction " << nom_fonction << endl;
              // création de la fonction
              fonction = Fonction_nD::New_Fonction_nD(nom_fonction,Id_Nom_Fonction_nD (type_fonction.c_str()));
              // lecture de la fonction
              fonction->LectDonnParticulieres_Fonction_nD (nom_fonction,&entreePrinc);
              if (ParaGlob::NiveauImpression() >= 6)
                 cout << " fin de la lecture de la fonction " << nom_fonction << endl;
              // verification que l'on n'a pas deux noms de fonctions identiques
              bool res = false;
              map < string, Fonction_nD * , std::less <string> > ::iterator il = listeDeFonction_nD.find(nom_fonction);
              if (il != listeDeFonction_nD.end()) res=true;
              if (res)
                { cout <<" \n Erreur de lecture de fonctions  nD, deux fonctions ont le meme nom \n";
                  cout <<" nom = " << nom_fonction << '\n';
                  cout <<"LesFonctions_nD::LectureLesFonctions_nD(UtilLecture & entreePrinc)"
                       << endl;
                  entreePrinc.MessageBuffer("lecture des  fonctions nD");
                  throw (UtilLecture::ErrNouvelleDonnee(-1));
                  Sortie (1);
                }
              else
                listeDeFonction_nD[nom_fonction]=fonction;
              entreePrinc.NouvelleDonnee(); // nouvelle ligne                    
              };
           if (ParaGlob::NiveauImpression() >= 4) cout << " fin de la lecture de fonctions " << endl;
           }
       catch (ErrSortieFinale)
            // cas d'une direction voulue vers la sortie
            // on relance l'interuption pour le niveau supérieur
          { ErrSortieFinale toto;
            throw (toto);
          }
       catch(...)
        { cout << "\n ** erreur en lecture d'une fonction nD ";
          throw (UtilLecture::ErrNouvelleDonnee(-1));
          Sortie(1);
        };
      };
     // maintenant on établit les liens éventuels entres les fonctions qui peuvent dépendre les unes des autres
     // (exemple de fonction complexe construite à partir de fonction simples)
     //  on boucle tant que les fonctions ne sont pas complète
     //  avec un maxi de boucles arbitraires
     int max_boucle = 100;
     int ib = 1;
     bool complet_fonction = false;
     bool affichage = false;
    
    
     map < string, Fonction_nD * , std::less <string> >::iterator idl,edlfin=listeDeFonction_nD.end();
     while (!complet_fonction && (ib < max_boucle))
       {for (idl = listeDeFonction_nD.begin();idl != edlfin;idl++)
         {Fonction_nD * co = (*idl).second; // pour plus de clarté
          if (co->DependAutreFoncCourbes())
            {// === cas d'une fonction avec dépendance à d'autres fonctions
             // -- récupération des noms de fonctions de dépendance
             list <string> lico;
             co->ListDependanceFonctions(lico);
             // -- création de la liste de pointeur de fonctions correspondantes
             list <string>::iterator ili,ilifin=lico.end();
             list <Fonction_nD *> liptco;
             bool liste_de_fct_complete = true;
             for (ili=lico.begin();ili!=ilifin;ili++)
               {//if (liptco->complet())
                liptco.push_back(LesFonctions_nD::Trouve(*ili));
               };
             
             
             // === cas d'une fonction avec dépendance à des courbes
             // -- récupération des noms de fonctions de dépendance
             list <string> licourbe;
             co->ListDependanceCourbes(licourbe);
             // -- création de la liste de pointeur de courbes correspondantes
             list <Courbe1D *> liptcourbe;
   //          for (string & nom : licourbe)
   //            liptcourbe.push_back(lesCourbes1D->Trouve(nom));
             {list <string>::iterator kk,kkfin=licourbe.end();
              for (kk=licourbe.begin();kk != kkfin;kk++)
                liptcourbe.push_back(lesCourbes1D->Trouve(*kk));
             };
             
             // ---- on renseigne la fonction
             co->Lien_entre_fonc_courbe(liptco,liptcourbe);
            };
         };
        // on fait un passage pour voir si les fonctions sont complètes
        complet_fonction = true; // bon par défaut
        for (idl = listeDeFonction_nD.begin();idl != edlfin;idl++)
           {Fonction_nD * co = (*idl).second; // pour plus de clarté
            if (!(co->Complet_Fonction()))
                {complet_fonction = false;
                 break;
                }
            };
        ib++;
       };
      // si ib > max_boucle c'est cuit
      if (ib >= max_boucle)
        { cout << "\n *** erreur en lectue des fonctions nD "
               << " on n'a pas reussi a associer les courbes dependantes les unes des autres"
               << "\n LesFonctions_nD::Lecture(..."<<endl;
          Sortie(1);
        };
    
  };
    
// affichage et definition interactive des commandes 
void LesFonctions_nD::Info_commande_lesFonctions_nD(UtilLecture & entreePrinc)
 { string rep;
   cout << "\n voulez-vous utiliser des fonctions nD ? (rep o ou n (defaut)) ";
   rep = lect_return_defaut(false,"n");
   if (rep == "o")
    { // on récupère la liste des identificateurs de fonctions actuellement disponible
      list <EnumFonction_nD> list_enuFonction_nD = Fonction_nD::Liste_Fonction_disponible();
      // affichage et choix de fonction nD
      cout << "\n Donner le type de fonction que vous souhaitez utiliser: ?  "
           << "\n (NB: l'utilitaire va definir un exemple de fonction qu'il faudra ensuite adapter !)   ";;
      string rep;int num; bool choix_valide = false;
      Tableau <bool> chooi(list_enuFonction_nD.size(),false); // tableau de travail
      list <string> nom_de_fichiers; bool inclusion_fichier=false;
      list <EnumFonction_nD>::iterator it,itfin=list_enuFonction_nD.end();
      while (!choix_valide)
      {
       try 
        { // affichage des éléments possibles
          int undeux=0;
          cout << "\n (0 ou f) fin \n";
          int inu;
          for (it=list_enuFonction_nD.begin(),inu=1;it!=itfin;it++,inu++)
            { cout << "\n";
              cout << " (" << inu << ") " << Nom_Fonction_nD(*it) << "   ";
              //undeux++;
              //if (undeux==2) {cout << "\n";undeux=0;}
             };
          int nb_fonction = list_enuFonction_nD.size();
          cout << "\n (" << nb_fonction+1 << ")   inclusion d'un fichier de def de fonction ";
          cout << "\n un numero ? ";
          rep = lect_return_defaut(false,"f");
          if (rep == "fin_prog") Sortie(1);
          num = ChangeEntier(rep);
          if (Minuscules(rep) == "f")
            num = 0;
          choix_valide=false;  
          if (num == 0) 
           { choix_valide=true;}
          else if (num == nb_fonction+1)
           { string nom_fichier="";
             cout << "\n nom du fichier ? "; nom_fichier=lect_chaine();
             cout << " nom lu: "<<nom_fichier << "\n";
             nom_de_fichiers.push_back(nom_fichier);
             inclusion_fichier=true;
           }
          else // sinon
           { if ((num >= 0)&&(num<=list_enuFonction_nD.size()))
              {if (chooi(num))
                 cout << "\n type d'element deja choisit, recommencer" << endl;
               else    
                chooi(num) = true;
               }
             else { cout << "\n Erreur on attendait un entier entre 0 et "<< list_enuFonction_nD.size()+1 <<" !!, "
                         << "\n redonnez une bonne valeur"
                         << "\n ou taper fin_prog pour arreter le programme"; 
                  };
            };
         }    
       catch (ErrSortieFinale)
            // cas d'une direction voulue vers la sortie
            // on relance l'interuption pour le niveau supérieur
          { ErrSortieFinale toto;
            throw (toto);
          }
       catch (...)//(UtilLecture::ErrNouvelleDonnee erreur)
       {  cout << "\n Erreur on attendait un entier entre 0 et "<< list_enuFonction_nD.size() <<" !!, "
                      << "\n redonnez une bonne valeur"
                      << "\n ou taper fin_prog pour arreter le programme"; 
              choix_valide=false;
        }
       } //-- fin du while 
     // maintenant on définit un exemplaire de chaque fonction choisit
     int cot=1;string nom_fonction; Fonction_nD * fonction=NULL;
     for (it=list_enuFonction_nD.begin();it!=itfin;it++,cot++)
      { if(chooi(cot))
         { nom_fonction = "exemple_fonction";
           nom_fonction += ChangeEntierSTring(cot);
           fonction = Fonction_nD::New_Fonction_nD(nom_fonction,*it);
           listeDeFonction_nD[nom_fonction]=fonction;
          }
       }
     // affichage des infos dans le fichier de commande
     ofstream & sort = *(entreePrinc.Commande_pointInfo()); // pour simplifier
     sort << "\n#----------------------------------------"
          << "\n# definition facultative de fonction  nD|"
          << "\n#----------------------------------------"
          << "\n \n            les_fonctions_nD #------------"
          << endl;
     // puis chaque fonction sort ses infos
     map < string, Fonction_nD * , std::less <string> >::iterator itc,itcfin =  listeDeFonction_nD.end();
     for (itc= listeDeFonction_nD.begin();itc!=itcfin;itc++)
       (*itc).second->Info_commande_Fonctions_nD(entreePrinc);
     // dans le cas où il y a des inclusions de fichiers
     if (inclusion_fichier)     
       { list <string>::iterator ill,illfin = nom_de_fichiers.end();
         for (ill=nom_de_fichiers.begin();ill != illfin;ill++)
           sort << "\n < " << (*ill) ;
       };

     // fin def fonction
     sort << "\n#------------- fin def des fonctions  nD ------------------------"
          << endl;     
     sort << flush;
     };
 };
          
     
 // affichage des fonctions
void LesFonctions_nD::Affiche() const
  {   map < string, Fonction_nD * , std::less <string> >::const_iterator i,ifin=listeDeFonction_nD.end();
      cout << " \n ------- affichage des listes de fonctions ------- \n ";
      cout << "\n nombre= " << listeDeFonction_nD.size() ;
      for (i=listeDeFonction_nD.begin() ; i != ifin; i++)
          { // on écrit le nom de la fonction et de sont type, sous forme énuméré, car ensuite dans chaque fonction le nom en string est écrit
            cout << "\n" << (*i).first << " " << ((*i).second)->Type_Fonction();
             ((*i).second)->Affiche();
          }
      cout << endl;          

   };

// test si la fonction de nom st1 existe reellement
// retourne false si n'existe pas , true sinon
bool LesFonctions_nD::Existe(const string & st1) const
  { // verification 
    bool res = false;
    map < string, Fonction_nD *, std::less <string> > ::const_iterator il = listeDeFonction_nD.find(st1);
    if (il != listeDeFonction_nD.end()) res=true;
    return res;      
   };       

 // retourne la fonction correspondant a une cle
Fonction_nD* LesFonctions_nD::Trouve(const string & st1) const
  { map < string, Fonction_nD *, std::less <string> > ::const_iterator il = listeDeFonction_nD.find(st1);
    if (il != listeDeFonction_nD.end())
        return ((*il).second);
    cout << " \n pb la fonction nD de nom " << st1 << " n\'existe pas !! " << endl;
    Sortie (1);
    Fonction_nD* bidon=NULL ;   // pour ne pas avoir de message de warning
    return  bidon;
   };       

// vérification que tout est ok, pres à l'emploi
// ramène true si ok, false sinon
bool LesFonctions_nD::Complet()
  { // on passe en revue toutes les fonctions
    map < string, Fonction_nD *, std::less <string> > ::const_iterator il, ilfin = listeDeFonction_nD.end();
    bool retour = true;
    for (il=listeDeFonction_nD.begin();il!=ilfin;il++)
     { // on en profite pour mettre à jour les variables globales
       ((*il).second)->Mise_a_jour_variables_globales();
       // puis on vérifie
       bool ret=((*il).second)->Complet_Fonction();
       if (!ret)
        { cout << "\n erreur!!! la fonction n'est pas complete";
          ((*il).second)->Affiche();
          retour = false;
         }
      } 
    return retour;  
   };
   
    // utilitaire pour lire une fonction, soit qui ne sera pas stocké par LesFonctions_nD
    // dans ce cas sont nom est "_", soit son nom correspond à une ref de nom de fonctions existante
    // dans tous les cas on lit un nom et un type de fonction c-a-d un string et un EnumFonction_nD
    //                 -----     différents cas  -------
    // ptfonction: soit == NULL, : on lit un nom, et on lit un type de fonction
    //                           1) si celui-ci est "_", cela signifie que la fonction à lire
    //                           est interne à l'utilisateur, on crée une fonction, on lit la fonction
    //                           avec Lecture_base_info de la fonction et on ramène un pointeur sur la
    //                           nouvelle fonction
    //                           2) si celui-ci est différent de "_", c'est une référence de fonction
    //                           on lit la référence et on ramène un pointeur sur la fonction de
    //                           LesFonctions_nD correspondant
    //                           
    // soit == une fonction existante: on lit un nom, et on regarde le nom actuel de la fonction pointée
    //                               par ptfonction que l'on appelera nom_ref
    //    1) nom = "_" et nom_ref = "_" 
    //                 1-a les deux fonctions sont du même type on relie les données avec Lecture_base_info
    //                     et on ramène ptfonction
    //                 1-b les deux fonctions sont de type différent, on supprime la fonction pointée par
    //                     ptfonction, on en crée une nouvelle adoc, et on ramène un pointeur dessus
    //    2) nom != "_" et nom_ref == "_" 
    //                  la fonctions pointé par ptfonction est supprimé, et on associe le pointeur de retour
    //                  a la fonction correspondant à nom de LesFonctions_nD
    //    3) nom == "_" et nom_ref != "_" 
    //                  on crée une nouvelle fonction adoc, on lie avec Lecture_base_info, et on ramène
    //                  un pointeur sur la fonction ainsi crée
    
Fonction_nD * LesFonctions_nD::Lecture_pour_base_info(istream& ent,const int cas,Fonction_nD * ptfonction)
  {  EnumFonction_nD type_de_fonction;string nom_fonction;
     ent >> nom_fonction >> type_de_fonction;
     if (ptfonction == NULL)
      {if (nom_fonction == "_")
        {// cas d'une fonction interne à l'appelant et pas de fonction défini, on en définit une
         ptfonction = Fonction_nD::New_Fonction_nD(nom_fonction,type_de_fonction);
         // lecture des infos       
         ptfonction->Lecture_base_info(ent,cas);
         }
       else
        {// cas d'une référence de fonction
         if (this->Existe(nom_fonction))
           { ptfonction = this->Trouve(nom_fonction);}
         else
          {cout << "\n erreur 1 en lecture de la fonction  de nom " << nom_fonction
                << " elle n'existe pas dans la liste des fonctions avec reference"
                << "\n LesFonctions_nD::Lecture_pour_base_info(...";
           Sortie(1);     
           } 
          };
       }      
     else 
      { // cas où une fonction est déjà pointé
        if ((ptfonction->NomFonction() == "_") && (nom_fonction == "_"))
         { if (type_de_fonction == ptfonction->Type_Fonction())
            {// même type on se contente de relire
             ptfonction->Lecture_base_info(ent,cas);
             }
           else
            {// pas de même type on supprime, on recrée, on li
             delete ptfonction;
             ptfonction = Fonction_nD::New_Fonction_nD(nom_fonction,type_de_fonction);
             ptfonction->Lecture_base_info(ent,cas);
             }
          }
        else if ((ptfonction->NomFonction() == "_") && (nom_fonction != "_"))
         { // l'ancien est interne et le nouveau est une ref, on supprime, on récup la ref
           delete ptfonction;
           if (this->Existe(nom_fonction))
            { ptfonction = this->Trouve(nom_fonction);}
           else
            {cout << "\n erreur 2 en lecture de la fonction  de nom " << nom_fonction
                  << " elle n'existe pas dans la liste des fonctions avec reference"
                  << "\n LesFonctions_nD::Lecture_pour_base_info(...";
             Sortie(1);     
             } 
          }  
        else if ((ptfonction->NomFonction() != "_") && (nom_fonction == "_"))
         { // l'ancien est une ref et le nouveau est interne, on crée et on li
           ptfonction = Fonction_nD::New_Fonction_nD(nom_fonction,type_de_fonction);
           ptfonction->Lecture_base_info(ent,cas);
          }
       } //-- fin du cas ou ptfonction != NULL
   // retour du pointeur
   return ptfonction;
 };
  
    // écriture pour base info
    // c'est le pendant de  Lecture_pour_base_info, de manière à être cohérent
void LesFonctions_nD::Ecriture_pour_base_info(ostream& sort,const int cas,Fonction_nD * ptfonction)
 { // la fonction
   sort << ptfonction->NomFonction() << " " << ptfonction->Type_Fonction() << " ";
   // écriture le la fonction elle-même que si le nom est "_"
   if (ptfonction->NomFonction() == "_")
     {ptfonction->Ecriture_base_info(sort,cas);};
  };
	 
     
//----- lecture écriture de base info -----
//   lecture base info
// = 1 : on récupère tout
// = 2 : on récupère uniquement les données variables (supposées comme telles)
void LesFonctions_nD::Lecture_base_info(istream& entr,const int cas)
 {bool mono_proc_on_lit_un_BI_multiProc = false;
 
#ifdef UTILISATION_MPI
  int proc_en_cours = ParaGlob::Monde()->rank();
  // === première partie commune à tous les proc
  // normalement tous les buffers ont été rempli, on se contente de rediriger les flux
   
  if (tailles_buffer_ioBI_MPI[0] > 0)
   {// on crée le flux sur le premier buffer:
    istrstream * entrons = new istrstream(buffer_ioBI_MPI(1).c_str(),tailles_buffer_ioBI_MPI[0]) ;
    istream& entr = *entrons; // on redéfinit ent
#endif

    string toto;int taille;
      
    entr >> toto ; // entête
    // dans le cas où on lit un fichier créé avec MPI io, et que l'on n'est pas
    // en MPI, on passe les offsets et tailles
  #ifndef UTILISATION_MPI
    if (toto == "buf0_LesFonctions_nD")
     {for (int i=0;i<3;i++) //
       {toto.clear();
        std::getline(entr, toto);
       };
      mono_proc_on_lit_un_BI_multiProc = true;
     };
  #endif
    if (mono_proc_on_lit_un_BI_multiProc) // cas de la lecture en mono
     {entr >> toto >> taille;}   // d'un fichier créé en multi proc
    else // cas d'une lecture en mono d'un fichier mono
         // ou cas d'une lecture multi d'un fichier créé en multi proc
     {entr >> taille;}; //  on a déjà lue toto

    if (cas == 1)
     {if (ParaGlob::NiveauImpression() >= 6)
        cout << " == lecture des fonctions \n";
      // on boucle sur le nombre de fonction à lire
      EnumFonction_nD type_de_fonction; //   "
      Fonction_nD* fonction; string nom_fonction; string type_fonction; //               "
      
      for (int i=1;i<= taille; i++)
       { // lecture du nom de la fonction et de son type
         entr >> nom_fonction >> type_de_fonction;
         // création de la fonction
         fonction = Fonction_nD::New_Fonction_nD(nom_fonction,type_de_fonction);
         // lecture de la fonction
         fonction->Lecture_base_info(entr,cas);
         // verification que l'on n'a pas deux noms de fonctions identiques
         bool res = false;
         map < string, Fonction_nD* , std::less <string> > ::iterator il = listeDeFonction_nD.find(nom_fonction);
         if (il != listeDeFonction_nD.end()) res=true;

         if (res)
          { cout <<" \n Erreur de lecture de fonctions  nD, deux fonctions ont le meme nom \n";
            cout <<" nom = " << nom_fonction << '\n';
            cout <<"LesFonctions_nD::Lecture_base_info(..." << endl;
            Sortie (1);
           }
         else
           // intégration dans la liste
           listeDeFonction_nD[nom_fonction]=fonction;
       };
      // maintenant on établit les liens éventuels entres les fonctions qui peuvent dépendre les unes des autres
      // (exemple de fonction complexe construite à partir de fonction simples)
      // --- on suit la même procédure que dans Lecture()
      map < string, Fonction_nD * , std::less <string> >::iterator idl,edlfin=listeDeFonction_nD.end();
      for (idl = listeDeFonction_nD.begin();idl != edlfin;idl++)
        {Fonction_nD * co = (*idl).second; // pour plus de clarté
         if (co->DependAutreFoncCourbes())
           {// === cas d'une fonction avec dépendance à d'autres fonctions
            // -- récupération des noms de fonctions de dépendance
            list <string> lico;
            co->ListDependanceFonctions(lico);
            // -- création de la liste de pointeur de fonctions correspondantes
            list <string>::iterator ili,ilifin=lico.end();
            list <Fonction_nD *> liptco;
            for (ili=lico.begin();ili!=ilifin;ili++)
              liptco.push_back(LesFonctions_nD::Trouve(*ili));
            
            
            // === cas d'une fonction avec dépendance à des courbes
            // -- récupération des noms de fonctions de dépendance
            list <string> licourbe;
            co->ListDependanceCourbes(licourbe);
            // -- création de la liste de pointeur de courbes correspondantes
            list <Courbe1D *> liptcourbe;
  //          for (string & nom : licourbe)
  //            liptcourbe.push_back(lesCourbes1D->Trouve(nom));
            {list <string>::iterator kk,kkfin=licourbe.end();
             for (kk=licourbe.begin();kk != kkfin;kk++)
               liptcourbe.push_back(lesCourbes1D->Trouve(*kk));
            };
            
            // ---- on renseigne la fonction
            co->Lien_entre_fonc_courbe(liptco,liptcourbe);
           };
        };
     };
#ifdef UTILISATION_MPI
   }; // fin lecture  buffer
#endif

 };
        
//   écriture base info   
// = 1 : on sauvegarde tout
// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
void LesFonctions_nD::Ecriture_base_info(ostream& sort,const int cas)
 {
#ifdef UTILISATION_MPI
  {int proc_en_cours = ParaGlob::Monde()->rank();
   // 1) dans le cas d'un proc 0 ou i, on redéfinit le flux de sortie, pour bufferiser les infos en sortie
   // qui sont ensuite sortie via MPI i-o à l'aide de la méthode Ecriture_base_info_MPI_IO
   // les récupérations et les sorties sont effectuées de manière asynchrone par rapport aux autres proc
   // 2) seul le proc 0 sort pour le cas 1, rien pour cas 2
   std::ostringstream sort; // redéfinition de sort
   if (proc_en_cours == 0) // partie dédiée proc 0
    {
#endif

     sort << "\n \n****les_fonctions_nD_:_nombre= " << listeDeFonction_nD.size() ;
     if (cas == 1)
      {// on balaie la liste
       map < string, Fonction_nD* , std::less <string> >::iterator i,ifin=listeDeFonction_nD.end();
       for (i=listeDeFonction_nD.begin() ; i != ifin; i++)
        { // on écrit le nom de la fonction et de sont type, sous forme énuméré, car ensuite dans chaque fonction le nom
          // en string est écrit
          sort << "\n" << (*i).first << " " << ((*i).second)->Type_Fonction();
          // puis écriture de la fonction
          ((*i).second)->Ecriture_base_info(sort,cas);
         }
        }
       sort << "\n";
#ifdef UTILISATION_MPI
    };
   buffer_ioBI_MPI(1) = sort.str(); // on sauvegarde
   tailles_buffer_ioBI_MPI[0] = buffer_ioBI_MPI(1).size();
  };
#endif

 };


#ifdef UTILISATION_MPI
// retour de la taille globale des sorties retardées
// concernant les sorties sur .BI
MPI_Offset LesFonctions_nD::Taille_buffer_sortie_BI()
  {MPI_Offset taille_totale = 0; // init
   int proc_en_cours = ParaGlob::Monde()->rank();
   if (proc_en_cours == 0) // partie dédiée proc 0
     {// tout d'abord on prend en compte les buffers propres
      for (MPI_Offset i=1;i<= nb_buffer;i++) // normalement 1
        taille_totale += buffer_ioBI_MPI(i).size();
      // puis on intègre la taille de l'ensemble des offsets:
      MPI_Offset taille_entete = 44 + (taille_offset)*21+27; // cf. LesFonctions_nD::Ecriture_base_info_MPI_IO
      taille_totale += taille_entete;
     };
   // retour
   return taille_totale; // seule la valeur retournée par proc 0 est licite
  };

// récupération de l'offset global sur .BI pour la classe
// et mise à jour des offsets de classes dérivées si besoin
void LesFonctions_nD::Mise_a_jour_Offset_BI_ecriture(MPI_Offset offset,const int cas)
 {// seul le proc 0 calcule les offsets qui sont ensuites transmis à tous les proc
  // mais il faut que tous les proc passent par toutes les méthodes
  int proc_en_cours = ParaGlob::Monde()->rank();
  if (proc_en_cours == 0)
   {
    // on commence par prendre en compte l'offset général puis ceux propres à proc 0
    // ce sont des données qui se situent avant ceux des proc i
    offset_LesFonctions_nD[0] = offset;
    // puis la première partie propre à proc 0
    MPI_Offset a_ajouter = offset+tailles_buffer_ioBI_MPI[0];
    // puis on intègre la taille de l'ensemble des offsets:
    MPI_Offset taille_entete = 44 + (taille_offset)*21+27; // cf. LesFonctions_nD::Ecriture_base_info_MPI_IO
    offset_LesFonctions_nD[1] = taille_entete+a_ajouter; // position final +1
   };
     
  // on transmets à tous les proc i les offsets
  broadcast(*ParaGlob::Monde(), offset_LesFonctions_nD, 0);
 };
// idem en lecture, mais là il s'agit uniquement de la mise à jour
// de l'offset seul puis c'est la classe qui s'en débrouille
void LesFonctions_nD::Mise_a_jour_Offset_BI_lecture(MPI_Offset offset,const int cas)
  { offset_LesFonctions_nD[0] = offset;
  };

// lecture MPI des infos bufferisées
void LesFonctions_nD::Lecture_base_info_MPI_IO(MPI_File * ent_MPI,const int cas)
 { int proc_en_cours = ParaGlob::Monde()->rank();

  // tous les proc lisent
  // 1) ==== la taille de l'ensemble des offsets
  MPI_Offset taille_buf = 0; // init
  // on encapsule pour ré-utiliser la même trame
  { char * buffer_car = new char [44] ; // def du tableau de travail
    MPI_Status status;
    int ierr = MPI_File_read_all(*ent_MPI, buffer_car, 44, MPI_CHAR, &status);
    // on crée un flux sur le tableau de caractères:
    istrstream * ent_inter = new istrstream(buffer_car,44) ;
    // lecture de l'entête
    string toto;
    (*ent_inter) >> toto;
    if (toto != "buf0_LesFonctions_nD" )
      {cout << "\n **** erreur on devait lire : 'buf0_LesFonctions_nD' "
            << " et on lue "<< toto << " la suite n'est pas possible -> arret !! "
            << "\n LesFonctions_nD::Lecture_base_info_MPI_IO(... "<<endl;
       Sortie(1);
      };
    // on lit la taille de buf2
    (*ent_inter)  >> taille_buf;
  };
  // 2) ==== lecture de la taille de l'ensemble des offsets
  { char * buffer_car = new char [taille_buf] ; // def du tableau de travail
    MPI_Status status;
    int ierr = MPI_File_read_all(*ent_MPI, buffer_car, taille_buf, MPI_CHAR, &status);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc "<<proc_en_cours << ", lecture  de la taille de l'ensemble des offsets "
            << "\n LesFonctions_nD::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    // on crée un flux sur le tableau de caractères:
    istrstream * ent_inter = new istrstream(buffer_car,taille_buf) ;
    // lecture de l'entête
    string toto;
    (*ent_inter) >> toto;
    if (toto != "offset_base_info_MPI_IO" )
      {cout << "\n **** erreur on devait lire : 'offset_base_info_MPI_IO' "
            << " et on lue "<< toto << " la suite n'est pas possible -> arret !! "
            << "\n LesFonctions_nD::Lecture_base_info_MPI_IO(... "<<endl;
       Sortie(1);
      };
    // on lit les offsets
    MPI_Offset taille_offset = 2;
    for (MPI_Offset i=0;i<taille_offset;i++)
      (*ent_inter) >>  offset_LesFonctions_nD[i];

   };

  // 3) ==== on dimensionne  les_taille_buffer_ioBI_MPI
  { MPI_Offset taille_entete = 44 + (taille_offset)*21+27; // cf. Ecriture_base_info_MPI_IO

    tailles_buffer_ioBI_MPI[0] = offset_LesFonctions_nD[1] - (offset_LesFonctions_nD[0]+taille_entete);
   };
  
  // 3) ==== lecture du  buffer
  if (tailles_buffer_ioBI_MPI[0] > 0)
  { char * buffer_car = new char [tailles_buffer_ioBI_MPI[0]] ; // def du tableau de travail
    MPI_Status status;
    int ierr = MPI_File_read_all(*ent_MPI, buffer_car, tailles_buffer_ioBI_MPI[0], MPI_CHAR, &status);
    if (ierr)
      {cout << "\n *** erreur d'acces a MPI_File_read_all: code " << ierr << " , stop Herezh "
            << "\n proc "<<proc_en_cours << ", lecture  buffer1 "
            << "\n LesFonctions_nD::Lecture_base_info_MPI_IO(..." << endl;
       Sortie(1);
      };
    buffer_ioBI_MPI(1).resize(tailles_buffer_ioBI_MPI[0]);
    buffer_ioBI_MPI(1).replace(0,tailles_buffer_ioBI_MPI[0],buffer_car);
  };
 };

  
// écriture MPI des infos bufferisées
void LesFonctions_nD::Ecriture_base_info_MPI_IO(MPI_File * sort_MPI, const int cas)
  { int proc_en_cours = ParaGlob::Monde()->rank();
    // si proc 0, on sort les ref
    if (proc_en_cours == 0)
     {//  === écriture dans flot string de la taille des buffers propres
      // pour avoir une sortie lisible, on crée tout d'abord un buffer string avec les infos
      std::ostringstream buf2(std::ios_base::out);
      buf2 << "\n offset_base_info_MPI_IO \n"; // 27 char
      for (MPI_Offset i=0;i<taille_offset;i++)
        // on sort donc (1)*21 char
        buf2 << std::right << setw(20) << offset_LesFonctions_nD[i] << " ";
       
      // le buffer du stream n'est pas pérenne il faut le récupérer
      string buf_inter = buf2.str(); // on récupère le buffer
      MPI_Offset taille_buf = buf_inter.size();
      #ifdef MISE_AU_POINT
       if (taille_buf != (taille_offset)*21+27)
         {cout << "\n *** sans doute erreur, taille_buf "<<taille_buf
               << " est  different de (taille_offset)*21+27 = " << (taille_offset)*21+27
               << "\n  LesFonctions_nD::Ecriture_base_info_MPI_IO(..." << endl;
          Sortie(1);
         };
      #endif

      // 1) ==== écriture de la taille de l'ensemble des offsets propres
      // on encapsule pour ré-utiliser la même trame
      {
        std::ostringstream bufdeb(std::ios_base::out);
        bufdeb << "\n buf0_LesFonctions_nD "; // 23 char
        bufdeb << std::right << setw(20) << taille_buf; // on justifie à droite et on utilise 20 char
        bufdeb << " "; // 1 char
        string buf_inter = bufdeb.str(); // normalement 44 char
        // sortie sur fichier de la taille de buf2
        MPI_Offset offset = offset_LesFonctions_nD[0]; // récup de l'offset
        int ierr = MPI_File_seek(*sort_MPI, offset, MPI_SEEK_SET); // positionnement
        if (ierr) // traitement erreur éventuelle
            {cout << "\n *** erreur de positionnement MPI_File_seek: code " << ierr << " , stop Herezh "
                  << "\n proc 0, ecriture buf0_LesFonctions_nD "
                  << "\n  LesFonctions_nD::Ecriture_base_info_MPI_IO(..." << endl;
             Sortie(1);
            };
        MPI_Status status; // pour le retour
        ierr = MPI_File_write(*sort_MPI,buf_inter.c_str(),44,MPI_CHAR,&status);
        if (ierr)
          {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                << "\n proc 0, ecriture entete buffer "
                << "\n  LesFonctions_nD::Ecriture_base_info_MPI_IO(..." << endl;
           Sortie(1);
          };

      };
      // 2) ==== écriture des offsets c-a-d sortie sur fichier de buf2
      { MPI_Status status; // pour le retour
        int ierr = MPI_File_write(*sort_MPI,buf_inter.c_str(),taille_buf,MPI_CHAR,&status);
        if (ierr)
          {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                << "\n proc 0, ecriture des offsets "
                     << "\n  LesFonctions_nD::Ecriture_base_info_MPI_IO(..." << endl;
                Sortie(1);
          };
      };


      // 3) ==== écriture du  buffer propre
      // dans le cas cas != 1 le buffer est vide
      if (buffer_ioBI_MPI(1).size() > 0)
      { MPI_Status status; // pour le retour
        int ierr = MPI_File_write(*sort_MPI,buffer_ioBI_MPI(1).c_str()
                                     ,buffer_ioBI_MPI(1).size(),MPI_CHAR,&status);
        if (ierr)
            {cout << "\n *** erreur d'acces a MPI_File_write: code " << ierr << " , stop Herezh "
                  << "\n proc 0, ecriture premier buffer propre "
                  << "\n  LesFonctions_nD::Ecriture_base_info_MPI_IO(..." << endl;
             Sortie(1);
         };
      };
     };
       
  };
       
#endif

// sortie du schemaXML: en fonction de enu  
void LesFonctions_nD::SchemaXML_lesFonctions_nD(ostream& sort,const Enum_IO_XML enu)
 { switch (enu)
    {case XML_TYPE_GLOBAUX: // cas du premier passage
 	  {// cas des classes de base, on commence par créé la liste globale des fonctions possible
       // on récupère la liste des identificateurs de fonctions actuellement disponible
       list <EnumFonction_nD> list_enuFonction_nD = Fonction_nD::Liste_Fonction_disponible();
       // maintenant on définit un exemplaire de chaque fonction possible
       list <EnumFonction_nD>::iterator it,itfin=list_enuFonction_nD.end();
       int cot=1;string nom_fonction; Fonction_nD * fonction=NULL;
       for (it=list_enuFonction_nD.begin();it!=itfin;it++,cot++)
         { nom_fonction = "exemple_fonction";
           nom_fonction += ChangeEntierSTring(cot);
           fonction = Fonction_nD::New_Fonction_nD(nom_fonction,*it);
           listeDeFonction_nD[nom_fonction]=fonction;
          };           
       break;		
      }
     case XML_IO_POINT_INFO:
      {// cas de def de  LesMaillages
   /*    sort  << "\n<xsd:element name=\"LesMaillages\" >"
  	          << "\n  <xsd:complexType>"
  	          << "\n    <xsd:element  name=\"****les_maillages:_nombre=\" type=\"xs:positiveInteger\" "
  	          << "\n              use=\"required\" minOccurs=\"1\" maxOccurs=\"unbounded\" />"
  	          << "\n    <xsd:element  name=\"defMaillages\" type=\"Maillage\" use=\"required\" />"
  	          << "\n  </xsd:complexType>"
              << "\n</xsd:element>";*/
     // def de Maillage
//     SchemaXML_Maillage(sort,niveau);
         break;		
      }
    };
  // on balaie l'ensemble des fonctions
  map < string, Fonction_nD * , std::less <string> >::iterator itc,itcfin =  listeDeFonction_nD.end();
  for (itc= listeDeFonction_nD.begin();itc!=itcfin;itc++)
       (*itc).second->SchemaXML_Fonctions_nD(sort,enu);
    
 };


