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



#include "DdlElement.h"


    // CONSTRUCTEURS :
// par defaut
DdlElement::DdlElement () :
  te(),nbddl(0),tab_enum_famille() {};
// definition du tableau pour n noeuds vide
DdlElement::DdlElement (int n) :
  te(n),nbddl(0),tab_enum_famille() 
    { };
// definition du tableau pour n noeuds et m ddl par noeud
// utile lorsque tous les noeuds ont le meme nombre de ddl 
// par contre ici aucun ddl n'est encore défini   
DdlElement::DdlElement (int n, int m) :
    te(n),nbddl(n*m),tab_enum_famille() 
      { for (int i=1; i<= n;i++) 
        { Tableau <Enum_ddl>& tb=te(i).tb;
          tb.Change_taille (m);
          for (int j=1;j<=m;j++)
              tb(j)=NU_DDL;
        };
       // def du nombre  des types particuliers de ddl, ici il s'agit de NU_DDL   
       Calcul_NbDdl_typer();    
      };
// definition du tableau pour n noeuds et un nombre de  ddl par noeud
// stocke dans le tableau mddl qui doit avoir la dimension n
// par contre ici aucun ddl n'est encore défini   
DdlElement::DdlElement (int n,const Tableau <int>& mddl) :
   te(n),tab_enum_famille()
   { if (mddl.Taille() == n)
       for (int i=1; i<= n;i++) 
        { Tableau <Enum_ddl>& tb=te(i).tb;
          int m = mddl(i);
          tb.Change_taille (m);
          for (int j=1;j<=m;j++)
              tb(j)=NU_DDL;
        }
     else
       { cout << "\n erreur de dimension pour le tableau mddl, il doit avoir la ";
         cout << " dimension : " << n << ", alors qu\'il a la dimension : " 
              << mddl.Taille() << "\nDdlElement (int n, Tableau <int> mddl)" << endl;
         Sortie (1);     
        } 
    // def du nombre total et des types particuliers de ddl    
    Calcul_NbDdl(); Calcul_NbDdl_typer();    
    };    

// definition du tableau pour n noeuds et une meme liste de ddl pour chaque
// noeud identique au second argument
DdlElement::DdlElement (int n, DdlNoeudElement d) :
  te(n,d),nbddl(n * d.tb.Taille()),tab_enum_famille()
     { // def du nombre total et des types particuliers de ddl    
       Calcul_NbDdl(); Calcul_NbDdl_typer();    
      }; 
      
DdlElement::DdlElement (const  DdlElement& a) :
 te(a.te),nbddl(a.nbddl),tab_enum_famille(a.tab_enum_famille) 
   {}; // de copie   
    // DESTRUCTEUR :
DdlElement::~DdlElement () {};

// acces en modification au ddl j du noeud i
void DdlElement::Change_Enum(int i,int j,const Enum_ddl enu) 
  {tab_enum_famille[PremierDdlFamille((te(i)).tb(j))]--;
   (te(i)).tb(j) = enu;
   tab_enum_famille[PremierDdlFamille(enu)]++;
   };

// nombre de ddl du noeudElement i contenu
int DdlElement::NbDdl(int i) const 
    { return (te(i)).tb.Taille();};
     
// change la dimension du tableau de ddl pour n noeuds et m ddl par noeud 
//dans le cas ou tous les noeuds ont le meme nombre de ddl
// si la nouvelle taille est plus petite: suppression 
// si la nouvelle dimension est plus grande on met les ddl suplémentaires à NU_DDL
void DdlElement::Change_taille(int n,int m)
     { int oldTaille = te.Taille();
       te.Change_taille(n);
       int deltataille = n-oldTaille;
       if (deltataille > 0)
       	{ // cas où on augmente la taille
       	  // 1) on traite différemment les anciens ddl et les nouveaux
       	  for (int i=1;i<=oldTaille;i++)
       	  	{ Tableau <Enum_ddl>& tb=te(i).tb;
       	  	  int otaille = tb.Taille();
       	  	  if (m<=otaille)
       	  	  	{// on supprime sans pb
       	  	  	 tb.Change_taille(m);
       	  	  	}
       	  	  else // on augmente avec des ddl NU_DDL
       	  	  	{tb.Change_taille(m);
       	  	     for (int j=otaille+1;j<=m;j++) 
       	  	       tb(j)=NU_DDL;
       	  	  	};	
       	  	};
       	  // 2) les nouveaux DdlNoeudElement sont mis à  NU_DDL
       	  for (int i1=oldTaille+1;i1<= n;i1++)
       	  	{ Tableau <Enum_ddl>& tb=te(i1).tb;
       	   	  for (int j1=1;j1<=m;j1++)
       	   	    tb(j1)=NU_DDL;
       	  	};       		
       	}
       else // si on diminue  	
       	{ for (int i=1;i<=oldTaille;i++)
       	  	{ Tableau <Enum_ddl>& tb=te(i).tb;
       	  	  int otaille = tb.Taille();
       	  	  if (m<=otaille)
       	  	  	{// on supprime sans pb
       	  	  	 tb.Change_taille(m);
       	  	  	}
       	  	  else // on augmente avec des ddl NU_DDL
       	  	  	{tb.Change_taille(m);
       	  	     for (int j=otaille+1;j<=m;j++) 
       	  	       tb(j)=NU_DDL;
       	  	  	};	
       	  	};
       	};
       nbddl = n*m;
       Calcul_NbDdl_typer(); // mise à jour du nombre de ddl de chaque type
     };
     
// change la dimension du tableau de ddl pour n noeuds et un nombre de  ddl par noeud
// stocke dans le tableau mddl qui doit avoir la dimension n
// si la nouvelle taille est plus petite: suppression 
// si la nouvelle dimension est plus grande on met les ddl suplémentaires à NU_DDL
void DdlElement::Change_taille(int n,Tableau <int> mddl) 
   { int oldTaille = te.Taille();
     te.Change_taille(n);
     nbddl = 0;
     if (mddl.Taille() == n)
      {int deltataille = n-oldTaille;
       if (deltataille > 0)
       	{ // cas où on augmente la taille
       	  // 1) on traite différemment les anciens ddl et les nouveaux
       	  for (int i=1;i<=oldTaille;i++)
       	  	{ Tableau <Enum_ddl>& tb=te(i).tb;
       	  	  int otaille = tb.Taille();
       	  	  int m = mddl(i);nbddl += m;
       	  	  if (m<=otaille)
       	  	  	{// on supprime sans pb
       	  	  	 tb.Change_taille(m);
       	  	  	}
       	  	  else // on augmente avec des ddl NU_DDL
       	  	  	{tb.Change_taille(m);
       	  	     for (int j=otaille+1;j<=m;j++) 
       	  	       tb(j)=NU_DDL;
       	  	  	};	
       	  	};
       	  // 2) les nouveaux DdlNoeudElement sont mis à  NU_DDL
       	  for (int i1=oldTaille+1;i1<= n;i1++)
       	  	{ Tableau <Enum_ddl>& tb=te(i1).tb;
       	  	  int m = mddl(i1);nbddl += m;
       	   	  for (int j1=1;j1<=m;j1++)
       	   	    tb(j1)=NU_DDL;
       	  	};       		
       	}
       else // si on diminue  	
       	{ for (int i=1;i<=oldTaille;i++)
       	  	{ Tableau <Enum_ddl>& tb=te(i).tb;
       	  	  int otaille = tb.Taille();
       	  	  int m = mddl(i); nbddl += m;
       	  	  if (m<=otaille)
       	  	  	{// on supprime sans pb
       	  	  	 tb.Change_taille(m);
       	  	  	}
       	  	  else // on augmente avec des ddl NU_DDL
       	  	  	{tb.Change_taille(m);
       	  	     for (int j=otaille+1;j<=m;j++) 
       	  	       tb(j)=NU_DDL;
       	  	  	};	
       	  	};
       	};
      }
     else
       { cout << "\n erreur de dimension pour le tableau mddl, il doit avoir la ";
         cout << " dimension : " << n << ", alors qu\'il a la dimension : " 
              << mddl.Taille() << "\nChange_taille(int n,Tableau <int> mddl)" << endl;
         Sortie (1);     
        }
     Calcul_NbDdl_typer(); // mise à jour du nombre de ddl de chaque type
    };    
      
    // met le tableau de ddl a zero
void DdlElement::TailleZero()
    {  int maxi_elem = te.Taille();
       for (int i=1; i<=maxi_elem;i++)
        (te(i)).tb.Change_taille (0);
       te.Change_taille(0);
       nbddl = 0;
       tab_enum_famille.erase(tab_enum_famille.begin(),tab_enum_famille.end());  
     };      
// change un des ddlNoeudElements
void DdlElement::Change_un_ddlNoeudElement(int i,const DdlNoeudElement& add)
  { int nbdold = te(i).tb.Taille();
    nbddl -= nbdold;
    int nbdnew = add.tb.Taille();
    nbddl += nbdnew;
    // mise en place de add
    te(i)=add;    
    // mise à jour des ddl par famille
    Calcul_NbDdl_typer(); // mise à jour du nombre de ddl de chaque type
    /*Tableau <Enum_ddl>& tb=te(i).tb;
    for (int j=1;j<=nbdold;j++)
       (tab_enum_famille[PremierDdlFamille(tb(j))])--;
    for (int i=1;i<= nbdnew;i++)
       (tab_enum_famille[PremierDdlFamille(add.tb(i))])++;*/
    };  
 
// surcharge de l'operateur d'affectation
DdlElement& DdlElement::operator = (const DdlElement & a)
  { int nb = a.NbNoeud();
    this->te.Change_taille(nb);
    for (int i=1;i<= nb;i++)
      this->te(i) = a(i); 
    // mise à jour du nombre de ddl
    nbddl = a.nbddl; tab_enum_famille = a.tab_enum_famille;  
    return *this;   
   };
// surcharge des operator de test
bool DdlElement::operator == (const DdlElement & a) const 
  { int nb = a.NbNoeud();
    for (int i=1;i<=nb;i++)
      if (this->te(i) != a(i)) return false;  
    return true;
   };
bool DdlElement::operator != (const DdlElement & a) const 
  { if (*this == a) return false; else return true;
   };

  // surcharge de l'operator de lecture
istream & operator >> (istream & entree, DdlElement& a)
  { // vérification du type
    string type;
    entree >> type;
    if (type != "DdlElement")
      {Sortie (1);
       return entree;
       }
    // entrée du tableau   
    entree >> a.te;
    // puis def du nombre total de ddl
    a.Calcul_NbDdl();
    a.Calcul_NbDdl_typer(); // mise à jour du nombre de ddl de chaque type
    return entree;      
  };
   // surcharge de l'operator d'ecriture
ostream & operator << (ostream & sort, const DdlElement& a)
     { // tout d'abord un indicateur donnant le type
       sort << "DdlElement "  ; 
       // puis le tableau 
       sort << a.te;
       return sort;      
     }; 
     
// retour du nombre de ddl d'une famille donnée
int DdlElement::NbDdl_famille(Enum_ddl enu) const
    { // pour l'instant on sort la totalité des ddl ce qui est faux mais permet de fonctionner comme avant
//        return nbddl;
      map < Enum_ddl, Int_initer , std::less <Enum_ddl> >::const_iterator il = tab_enum_famille.find(enu);
        if (il == tab_enum_famille.end()) return 0; else return (*il).second.vali;
        };
   
//================================= methodes proteges ==================

    // calcul du nb de ddl
void DdlElement::Calcul_NbDdl()  
     { nbddl = 0;
       int tetaille = te.Taille();
       for (int i=1; i<= tetaille;i++)
          nbddl += (te(i)).tb.Taille();
     };

    // calcul du nb de ddl de chaque type contenu
void DdlElement::Calcul_NbDdl_typer()       
     { // méthode en utilisant "tab_enum_famille" 
       // qui est un tableau associatif 
       // on commence par initialiser le tableau
       tab_enum_famille.erase(tab_enum_famille.begin(),tab_enum_famille.end());  
       // on passe en revue tous les ddl
       int tetaille = te.Taille();
       for (int i=1; i<= tetaille;i++)
         { Tableau <Enum_ddl>& tb=te(i).tb;
           int nbd= tb.Taille();           
           for (int j=1;j<=nbd;j++)
              (tab_enum_famille[PremierDdlFamille(tb(j))])++;
          }
  /*    // pour vérif
       map < Enum_ddl, Int_initer , std::less <Enum_ddl> >::iterator il,ilfin=tab_enum_famille.end();
       for (il=tab_enum_famille.begin();il != ilfin;il++)
         cout << "\n ddl= " << (*il).first << " nombre " << (*il).second.vali << endl;  
         
       cout << "toto ?";
       int toto; cin >> toto; */      
     };

   
