begin process at 2008 08 29 22:51:10
1 233 930 membres
450 nouveaux aujourd'hui
14 294 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

CALCUL : ANALYSE D'EXPRESSIONS ALGÉBRIQUES GRÂCE À LA RÉCURSIVITÉ (UTILISATION DES TEMPLATE ÉGALEMENT).


Information sur la source

Catégorie :Maths & Algorithmes Classé sous : calcul, analyse, expression, algèbre, math Niveau : Initié Date de création : 15/09/2007 Date de mise à jour : 12/10/2007 19:32:44 Vu / téléchargé: 3 286 / 110

Note :
Aucune note

Commentaire sur cette source (6)
Ajouter un commentaire et/ou une note

Description

Voici un petit analyseur d'expressions algébriques. Il gère les opérateurs +-*/, la priorité, et les parenthèses imbriquées.
Il y a déjà pas mal de code de ce genre, et sûrement des meilleurs que le mien, mais j'ai pensé qu'il était intéressant à cause de sa méthode particulière de résolution des expressions. Je me suis fortement inspiré d'un petit prog Java écrit comme exemple pour un livre (Introduction à Java, ou Learning Java, par Pat Niemeyer & Jonathan Knudsen, Oreilly, exemple: Calculator.java) qui utilise un code particulièrement élégant, utilisant toute la puissance de la récursivité pour éviter les codes avec 15 000 if à la suite, ce qui est le cas de la plupart des codes d'analyse d'expression postés ici.

(ACTUELLEMENT)-A ce jour, la gestion des opérateurs : */+-%^ est implémentée. Les parenthèses imbriquées sont supportées. On peut définir ses propres variables et fonctions pour les insérer dans des expressions (attention : précéder les noms de fonctions de _ dans les expressions analysées). Syntaxe de calculatrice (supporte les multiplications du genre (2ab+5)c + 3(2x^3) ).

A noter que les racine (carrée, cubique, etc...) sont supportées : il suffit de mettre un nombre à la puissance 1/2 pour la racine carrée, 1/3 pour la cubique, etc...
Par exemple, racine carrée de deux : 2^(1/2) ; racine cubique de deux : 2^(1/3)


J'ai commenté au maximum les fonctions intéressantes, mais si vous avez des questions, n'hésitez pas.

Note: j'utilise ici boost mais seulement pour la fonction erase_all. Si vous voulez compiler le code sans boost, il serait facile d'écrire votre propre fonction qui retire tout les espaces de l'expression.

Source

  • //////////////////////////////////////////////////////////////
  • //author lovejava (alias lovecpp, real name : Yann Hamdaoui)//
  • //////////////////////////////////////////////////////////////
  • //This source code is under the GNU/GPL license. //
  • //////////////////////////////////////////////////////////////
  • #ifndef _CALCUL_HPP_
  • #define _CALCUL_HPP_
  • #include <iostream>
  • #include <map>
  • #include <boost/algorithm/string.hpp>
  • #include <string>
  • #include <sstream>
  • #include <cmath>
  • using namespace std;
  • /**********************************************
  • *définit une fonctions (et ses sous fonctions)
  • *capable d'évaluer une expression mathématique sous forme de chaîne
  • *et de renvoyer le résultat
  • */
  • namespace calcul
  • {
  • /**
  • *exception signalant que la syntaxe de l'expression passée en paramètre
  • *n'est pas valide
  • */
  • class syntax_exception : public exception
  • {
  • protected:
  • string message;
  • int position; //position du caractère ayant provoqué l'erreur
  • //expression mathématique complète
  • string chaine;
  • public:
  • syntax_exception() throw() { };
  • syntax_exception(const string& message, const string& chaine = "", int position = string::npos) throw();
  • syntax_exception(const syntax_exception& source) throw();
  • const char* what() const throw();
  • syntax_exception& operator=(const syntax_exception& source) throw();
  • virtual ~syntax_exception() throw() { };
  • };
  • /**
  • *exception levée lors d'une division par zéro
  • */
  • class division_by_zero_exception : public exception
  • {
  • public:
  • division_by_zero_exception() throw() { };
  • division_by_zero_exception(const division_by_zero_exception& source) throw() { };
  • const char* what() const throw();
  • division_by_zero_exception& operator=(const division_by_zero_exception& source) throw() { };
  • virtual ~division_by_zero_exception() throw() { };
  • };
  • /**
  • *exception levée lorsqu'une variable ou une fonction utilisée dans un calcul n'est pas définie
  • */
  • class undefined_symbol_exception : public exception
  • {
  • protected:
  • string symbole; //le nom de la variable/fonction indéfinie
  • public:
  • undefined_symbol_exception(const string& symbole = "?") throw();
  • undefined_symbol_exception(const undefined_symbol_exception& source) throw();
  • const char* what() const throw();
  • undefined_symbol_exception& operator=(const undefined_symbol_exception& source) throw();
  • virtual ~undefined_symbol_exception() throw() { };
  • };
  • /**
  • *Classe qui permet d'envelopper les exceptions qui ont sont levées lorsque l'évaluateur
  • *parse une expression de fonction. Par exemple, si l'utilisateur définit la fonction f : (x^2 + 2x + 5)/(x-1)
  • *et qu'il tape 2*_f(1)/2, il peut ne pas comprendre d'où vient l'erreur si le seul message qu'il obtient est "division by zero".
  • *C'est encore plus un problème lorsqu'une erreur de syntaxe est présente dans la déclaration de la fonction.
  • *Cette exception permet donc de palier à ce problèmes en encapsulant l'erreur de base et en rajoutant dans le message d'erreur final
  • *la provenance de l'erreur : par exemple [in function f]division by zero
  • */
  • class fonction_error_wrapper_exception : public exception
  • {
  • protected:
  • string nom; //le nom de la fonction d'où provient l'erreur
  • string message_origine; //le message d'erreur d'origine
  • public:
  • fonction_error_wrapper_exception(const exception& erreur, const string& nom = "?") throw();
  • fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw();
  • const char* what() const throw();
  • fonction_error_wrapper_exception& operator=(const fonction_error_wrapper_exception& source) throw();
  • virtual ~fonction_error_wrapper_exception() throw() { };
  • };
  • #define ALGORITHMIC_FUNCTION number_type (*)(number_type)
  • /**
  • *Classe qui évalue une expression pour la parser sous forme de nombre.
  • *le paramètre template number_type permet de l'utiliser aussi bien avec
  • *des int, des doubles, des float, etc.
  • *le constructeur seul ne fait qu'initialiser l'objet, le parsage de l'expression
  • *se fait lors de la conversion en objet de type number_type (voir operator number_type())
  • *ou de l'appel explicite à eval()
  • */
  • template<typename number_type, typename algorithmic_function = number_type (*)(number_type)>
  • class expression_evaluator
  • {
  • /*public:
  • typedef (number_type (*)(number_type)) algorithmic_function;*/
  • protected:
  • string expression; //l'expression à parser
  • unsigned int index; //index utilisé lors du parsage
  • map<char, number_type> variables; //variables pouvant être utilisées pour des expressions
  • map<string, expression_evaluator<number_type> > fonctions; //fonctions définies par l'utilisateur
  • map<string, algorithmic_function> fonctions_algo; //fonctions algorithmiques et prédéfinies
  • //méthodes d'analyse et de parcours de la chaîne (voir le corps des méthodes
  • //pour des commentaires plus détaillés)
  • number_type somme(); //+ et -
  • number_type prior(); //*, / et %
  • number_type puissance(); // ^
  • number_type terme(); //analyse les nombres à proprement dit
  • bool more(); //si l'on est arrivé à la fin de la chaîne
  • /*Fonction qui renvoie true s'il y a un signe multiplié implicite à la position courante,
  • *comme par exemple dans 2x + 3ab(2 + 3z)d, c'est à dire lorsque des lettres, des nombres et/ou des parenthèses
  • *se suivent sans opérateur*/
  • bool implicit_mult();
  • public:
  • expression_evaluator() { };
  • expression_evaluator(const string& expression);
  • expression_evaluator(const expression_evaluator& source);
  • string get_expression() const;
  • /*cette fonction renvoie la valeur d'une variable, ou 0 si elle n'existe pas
  • *pour vérifier qu'une variable existe, utiliser is_var_defined() au préalable, car une variable
  • *existante peut très bien avoir pour valeur 0
  • */
  • number_type get_var(char variable) const;
  • /*
  • *renvoie l'expression d'une fonction définie par l'utilisateur
  • */
  • string get_function(const string& nom) const;
  • /*
  • *renvoie un pointeur de fonction vers une fonction "algorithmique" définie par l'utilisateur
  • *ou prédéfinie (sinus, cosinus, etc...). Par algorithmique, j'entend une fonction qui ne peut être
  • *définie seulement par une expression mathématique comme '2x+3', par exemple la fonction cosinus, partie entière
  • *ou encore racine carrée.
  • */
  • algorithmic_function get_algorithmic_function(const string& nom) const;
  • void set_expression(const string& expression);
  • //permet d'assigner une valeur à une variable
  • void set_var(char variable, number_type valeur);
  • //permet d'assigner à une fonction son expression
  • void set_function(const string& nom, const string& expression);
  • //permet d'assigner à une fonction son algorithme
  • void set_algorithmic_function(const string& nom, algorithmic_function fonction);
  • bool is_var_defined(char variable) const;
  • bool is_function_defined(const string& nom) const;
  • //évalue l'expression et renvoie un nombre
  • number_type eval();
  • //vérifie la syntaxe. Lève une syntax_exception si celle-ci n'est pas valide
  • void check_syntax() const throw(syntax_exception);
  • //méthode de classification des caractères
  • static bool is_digit(char caractere);
  • static bool is_alpha(char caractere);
  • static bool is_alphanum(char caractere);
  • static bool is_operator(char caractere);
  • static bool is_forbidden(char caractere);
  • expression_evaluator& operator=(const expression_evaluator& source);
  • operator number_type();
  • ~expression_evaluator();
  • };
  • //fonctions d'adaptation permettant d'utiliser les fonctions usuelles comme cosinus, log, etc...
  • //avec n'importe quel type générique number_type en effectuant les conversions nécessaires :
  • template<typename number_type>
  • number_type _abs_adapter(number_type parametre)
  • {
  • return (number_type)fabs((double)parametre);
  • }
  • template<typename number_type>
  • number_type _acos_adapter(number_type parametre)
  • {
  • return (number_type)acos((double)parametre);
  • }
  • template<typename number_type>
  • number_type _asin_adapter(number_type parametre)
  • {
  • return (number_type)asin((double)parametre);
  • }
  • template<typename number_type>
  • number_type _atan_adapter(number_type parametre)
  • {
  • return (number_type)atan((double)parametre);
  • }
  • template<typename number_type>
  • number_type _ceil_adapter(number_type parametre)
  • {
  • return (number_type)ceil((double)parametre);
  • }
  • template<typename number_type>
  • number_type _exp_adapter(number_type parametre)
  • {
  • return (number_type)exp((double)parametre);
  • }
  • template<typename number_type>
  • number_type _floor_adapter(number_type parametre)
  • {
  • return (number_type)floor((double)parametre);
  • }
  • template<typename number_type>
  • number_type _log_adapter(number_type parametre)
  • {
  • return (number_type)log((double)parametre);
  • }
  • template<typename number_type>
  • number_type _log10_adapter(number_type parametre)
  • {
  • return (number_type)log10((double)parametre);
  • }
  • template<typename number_type>
  • number_type _cos_adapter(number_type parametre)
  • {
  • return (number_type)cos((double)parametre);
  • }
  • template<typename number_type>
  • number_type _cosh_adapter(number_type parametre)
  • {
  • return (number_type)cosh((double)parametre);
  • }
  • template<typename number_type>
  • number_type _sin_adapter(number_type parametre)
  • {
  • return (number_type)sin((double)parametre);
  • }
  • template<typename number_type>
  • number_type _sinh_adapter(number_type parametre)
  • {
  • return (number_type)sinh((double)parametre);
  • }
  • template<typename number_type>
  • number_type _tan_adapter(number_type parametre)
  • {
  • return (number_type)tan((double)parametre);
  • }
  • template<typename number_type>
  • number_type _tanh_adapter(number_type parametre)
  • {
  • return (number_type)tanh((double)parametre);
  • }
  • ///////////////////////////////////////////////////////
  • syntax_exception::syntax_exception(const string& message, const string& chaine, int position) throw() : exception()
  • {
  • this->message = message;
  • this->chaine = chaine;
  • this->position = position;
  • }
  • syntax_exception::syntax_exception(const syntax_exception& source) throw() : exception()
  • {
  • *this = source;
  • }
  • const char* syntax_exception::what() const throw()
  • {
  • string temp(message);
  • //si un numéro de caractère est spécifié pour l'erreur syntaxique, on affiche le caractère en question
  • //et sa position dans l'expression après le message d'erreur
  • if(position != string::npos)
  • {
  • ostringstream sortie;
  • sortie << position+1;
  • temp.append("(caractère \'").append(chaine, position, 1).append("\', en position ").append(sortie.str()).append(")");
  • }
  • return temp.c_str();
  • }
  • syntax_exception& syntax_exception::operator=(const syntax_exception& source) throw()
  • {
  • message = source.message;
  • chaine = source.chaine;
  • position = source.position;
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • const char* division_by_zero_exception::what() const throw()
  • {
  • return "division par zéro";
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • undefined_symbol_exception::undefined_symbol_exception(const string& symbole) throw()
  • {
  • this->symbole = symbole;
  • }
  • undefined_symbol_exception::undefined_symbol_exception(const undefined_symbol_exception& source) throw()
  • {
  • *this = source;
  • }
  • const char* undefined_symbol_exception::what() const throw()
  • {
  • return string("\'").append(symbole).append("\' n'est pas défini.").c_str();
  • }
  • undefined_symbol_exception& undefined_symbol_exception::operator=(const undefined_symbol_exception& source) throw()
  • {
  • symbole = source.symbole;
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • fonction_error_wrapper_exception::fonction_error_wrapper_exception(const exception& erreur, const string& nom) throw()
  • {
  • this->message_origine = erreur.what();
  • this->nom = nom;
  • }
  • fonction_error_wrapper_exception::fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw()
  • {
  • *this = source;
  • }
  • const char* fonction_error_wrapper_exception::what() const throw()
  • {
  • return string("[in function ").append(nom).append("]").append(message_origine).c_str();
  • }
  • fonction_error_wrapper_exception& fonction_error_wrapper_exception::operator=(const fonction_error_wrapper_exception& source) throw()
  • {
  • nom = source.nom;
  • message_origine = source.message_origine;
  • }
  • ///////////////////////////////////////////////////////////////////////////
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const string& expression)
  • {
  • this->expression = expression;
  • //ajout des fonctions mathématiques de base
  • fonctions_algo["abs"] = &_abs_adapter<number_type>;
  • fonctions_algo["acos"] = &_acos_adapter<number_type>;
  • fonctions_algo["asin"] = &_asin_adapter<number_type>;
  • fonctions_algo["atan"] = &_atan_adapter<number_type>;
  • fonctions_algo["ceil"] = &_ceil_adapter<number_type>;
  • fonctions_algo["exp"] = &_exp_adapter<number_type>;
  • fonctions_algo["floor"] = &_floor_adapter<number_type>;
  • fonctions_algo["log"] = &_log_adapter<number_type>;
  • fonctions_algo["log10"] = &_log10_adapter<number_type>;
  • fonctions_algo["cos"] = &_cos_adapter<number_type>;
  • fonctions_algo["cosh"] = &_cosh_adapter<number_type>;
  • fonctions_algo["sin"] = &_sin_adapter<number_type>;
  • fonctions_algo["sinh"] = &_sinh_adapter<number_type>;
  • fonctions_algo["tan"] = &_tan_adapter<number_type>;
  • fonctions_algo["tanh"] = &_tanh_adapter<number_type>;
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const expression_evaluator<number_type, algorithmic_function>& source)
  • {
  • *this = source;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::somme()
  • {
  • /*on commence par séparer les termes via les signes + et -,
  • *ceux qui ont la plus faible priorité. On calculera d'abord
  • *ces termes indépendamment pour ensuite leur appliquer, en dernier lieu,
  • *l'opérateur + ou -.
  • */
  • //à l'appel de la fonction, on considère que le premier élément est forcément
  • //soit un nombre, soit une parenthèse : on appelle donc la méthode terme()
  • //via la méthode prior() pour récupérer ce premier nombre.
  • number_type retour = prior();
  • //une fois obtenu un nombre, on cherche un signe + ou -
  • //si on trouve, on appel les méthodes sous-jacente pour calculer
  • //l'ensemble de la valeur qui sera ajoutée ou retranchée au final
  • while(more() && (expression[index] == '+' || expression[index] == '-'))
  • {
  • ++index; //on avance d'un caractère
  • if(expression[index-1] == '+')
  • retour += prior();
  • else
  • retour -= prior();
  • }
  • /*si on arrive à la fin d'une parenthèse, on sort d'une sous-expression.
  • *On avance donc la position courante pour que, lorsque l'on rend la main à
  • *la fonction terme(), l'analyse de l'expression ne s'arrête pas prématurément
  • *à cause du fait que comme la parenthèse n'est ni un opérateur, ni un nombre,
  • *les fonctions rendraient la main une à une sans effectuer aucun traitement supplémentaire
  • *Lorsqu'on enlève cette ligne par exemple, (5+(5+5)*2)*2 font 15 (en fait, seulement les
  • *trois premiers 5 on été analysés et ajoutés) et non pas 50.
  • */
  • if(more() && expression[index] == ')')
  • ++index;
  • return retour;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::prior()
  • {
  • number_type retour = puissance(); //on obtient le premier nombre de l'expression via puissance()
  • /*une fois obtenu un nombre, on cherche un signe *, / ou %
  • *on continue jusqu'à que le signe suivant ne soit ni un nombre ni un * ni un / ni un %
  • *pour être sûr d'avoir effectué toutes les divisions/multiplications de l'expression
  • *ou sous-expression. Ainsi la priorité est respectée et on rend la main à la fonction somme
  • */
  • bool implicite = false; //on doit savoir si la multiplication était implicite ou non pour avancer ou pas index
  • while( more() && (expression[index] == '*' || expression[index] == '/' || expression[index] == '%' || (implicite = implicit_mult())) )
  • {
  • if(!implicite)
  • ++index;
  • if(expression[index-1] == '/')
  • {
  • number_type temp = puissance();
  • if(temp == 0)
  • throw division_by_zero_exception();
  • retour /= temp;
  • }
  • else if(expression[index-1] == '%')
  • retour = (number_type) ((long long int)retour % (long long int)puissance());
  • else
  • retour *= puissance();
  • /*on réinitialise implicite pour les prochains passages, car si expression[index] vaut '*' par exemple, l'instruction
  • *implicite = implicit_mult() ne sera pas exécutée ; donc si au précédent passage implicite valait true, il garderait cette valeur
  • *si on ne le remettait pas à false
  • */
  • implicite = false;
  • }
  • return retour;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::puissance()
  • {
  • /*fonction qui regarde si dans l'expression ou sous-expression en cours
  • *il n'y a pas de signe '^'. Elle est après la fonction prior() dans le chaînage
  • *des appels car l'opérateur d'élévation à la puissance est le plus prioritaire
  • */
  • number_type retour = terme();
  • while(more() && (expression[index] == '^'))
  • {
  • ++index;
  • retour = (number_type)pow((double)retour, (double)terme());
  • }
  • return retour;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::terme()
  • {
  • //c'est la fonction qui lit réellement les nombres
  • //si on tombe sur une parenthèse ouvrante, on ré-analyse
  • //cette sous-expression comme une nouvelle expression apparentière
  • if(expression[index] == '(')
  • {
  • ++index;
  • return somme();
  • }
  • else
  • {
  • number_type nombre;
  • unsigned int index_depart = index;
  • bool moins_unaire = false; //s'il y avait un moins unaire au début de l'expression
  • //gestion du moins unaire : si l'on rencontre un moins lorsque les fonctions
  • //appellantes veulent un nombre, on considère que c'est un moins unaire
  • //on met un booléen à true puis on avance l'index courant
  • if(expression[index] == '-')
  • {
  • moins_unaire = true;
  • ++index;
  • ++index_depart;
  • }
  • if(expression_evaluator<number_type, algorithmic_function>::is_operator(expression[index]))
  • throw syntax_exception("plusieurs opérateurs se suivent", expression, index);
  • if(expression[index] == '_')
  • {
  • //on avance la position courante jusqu'à la parenthèse ouvrante
  • ++index;
  • ++index_depart;
  • for(; more() && expression[index] != '('; ++index) ;
  • //on doit récupérer la valeur d'une fonction
  • if(!more())
  • throw syntax_exception("Un nom de fonction doit être suivi d'une parenthèse ouvrante", expression, index_depart);
  • string nom_fonction = expression.substr(index_depart, index-index_depart);
  • typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::iterator it1 = fonctions_algo.find(nom_fonction);
  • typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it2 = fonctions.find(nom_fonction);
  • ++index; //on passe après la parenthèse
  • number_type x_value = somme(); //on évalue indépendamment l'expression qui suit la fonction (c'est à dire la valeur de x)
  • if(it1 != fonctions_algo.end())
  • {
  • try {
  • nombre = (*(*it1).second)(x_value);
  • }
  • //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
  • //et non pas dans la chaîne analysée en ce moment
  • catch(const exception& e)
  • {
  • throw fonction_error_wrapper_exception(e, nom_fonction);
  • }
  • }
  • else if(it2 != fonctions.end())
  • {
  • //on met à jour les définitions de variables et de fonctions pour que la suite tout ça etc.
  • fonctions[nom_fonction].variables = variables;
  • fonctions[nom_fonction].fonctions = fonctions;
  • fonctions[nom_fonction].set_var('x', x_value);
  • try {
  • nombre = fonctions[nom_fonction].eval();
  • }
  • //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
  • //et non pas dans la chaîne analysée en ce moment
  • catch(const exception& e)
  • {
  • throw fonction_error_wrapper_exception(e, nom_fonction);
  • }
  • }
  • else
  • throw undefined_symbol_exception(nom_fonction);
  • }
  • else if(expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index]))
  • {
  • char variable = expression[index];
  • if(!is_var_defined(variable))
  • throw undefined_symbol_exception(string(&variable, 1));
  • number_type valeur = get_var(variable);
  • nombre = valeur;
  • ++index;
  • }
  • else
  • {
  • //sinon, on lit le nombre à la position courante
  • for(; more() && is_digit(expression[index]); ++index) ;
  • string nombre_str(expression, index_depart, index-index_depart);
  • istringstream entree(nombre_str);
  • entree >> nombre;
  • }
  • if(moins_unaire)
  • return -nombre;
  • else
  • return nombre;
  • }
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::more()
  • {
  • return index < expression.length();
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::implicit_mult()
  • {
  • /*Vérifie s'il n'y a pas de signe multiplier implicite
  • *avec des variables (comme par exemple 2a(3+2) ou encore (x+2)(x-3)bc
  • *vérifications : si un chiffre et une lettre se suivent (ou l'inverse)
  • * si deux lettres se suivent
  • * si un chiffre/une lettre suivent une parenthèse fermante (ou précèdent une parenthèse ouvrante)
  • * si une parenthèse fermante et une parenthèse ouvrante se suivent
  • */
  • return ( (expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
  • (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index])) ||
  • (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
  • (expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index-1]) && expression[index] == '(') ||
  • (expression[index-1] == ')' && expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index])) ||
  • (expression[index-1] == ')' && expression[index] == '(') );
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_digit(char caractere)
  • {
  • return (caractere == '.' ||
  • (caractere >= '0' && caractere <= '9'));
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_alpha(char caractere)
  • {
  • return (caractere >= 'a' && caractere <= 'z') || (caractere >= 'A' && caractere <= 'Z') || caractere == '_';
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_alphanum(char caractere)
  • {
  • return is_digit(caractere) || is_alpha(caractere);
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_operator(char caractere)
  • {
  • return (caractere == '+' || caractere == '-' || caractere == '%'
  • || caractere == '*' || caractere == '/' || caractere == '^');
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_forbidden(char caractere)
  • {
  • return (!is_digit(caractere) && !is_operator(caractere) && !is_alpha(caractere)
  • && caractere != '(' && caractere != ')' && caractere != ' ');
  • }
  • template<typename number_type, typename algorithmic_function>
  • string expression_evaluator<number_type, algorithmic_function>::get_expression() const
  • {
  • return expression;
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::get_var(char variable) const
  • {
  • typename map<char, number_type>::const_iterator it = variables.find(variable);
  • if(it == variables.end())
  • return 0;
  • else
  • return (*it).second;
  • }
  • template<typename number_type, typename algorithmic_function>
  • string expression_evaluator<number_type, algorithmic_function>::get_function(const string& nom) const
  • {
  • typename map<string, expression_evaluator<number_type, algorithmic_function> >::const_iterator it = fonctions.find(nom);
  • if(it == fonctions.end())
  • return "";
  • else
  • return (*it).second.get_expression();
  • }
  • template<typename number_type, typename algorithmic_function>
  • algorithmic_function expression_evaluator<number_type, algorithmic_function>::get_algorithmic_function(const string& nom) const
  • {
  • typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::const_iterator it = fonctions.find(nom);
  • if(it == fonctions.end())
  • return NULL;
  • else
  • return (*it).second;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_expression(const string& expression)
  • {
  • this->expression = expression;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_var(char variable, number_type valeur)
  • {
  • variables[variable] = valeur;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_function(const string& nom, const string& expression)
  • {
  • //on créer une nouvelle fonction seulement si elle n'est pas déjà définie parmis les fonctions algorithmiques ou prédéfinies
  • //pour éviter les conflits
  • if(fonctions_algo.find(nom) == fonctions_algo.end())
  • fonctions[nom] = expression;
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::set_algorithmic_function(const string& nom, algorithmic_function fonction)
  • {
  • //si la fonction existe déjà parmis les fonctions définies par l'utilisateur, on la supprime de la liste avant de l'insérer
  • //parmis les fonctions algorithmiques
  • typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it = fonctions.find(nom);
  • if(it != fonctions.end())
  • fonctions.erase(it);
  • fonctions_algo[nom] = fonction;
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_var_defined(char variable) const
  • {
  • return variables.find(variable) != variables.end();
  • }
  • template<typename number_type, typename algorithmic_function>
  • bool expression_evaluator<number_type, algorithmic_function>::is_function_defined(const string& nom) const
  • {
  • return fonctions.find(nom) != fonctions.end() || fonctions_algo.find(nom) != fonctions_algo.end();
  • }
  • template<typename number_type, typename algorithmic_function>
  • number_type expression_evaluator<number_type, algorithmic_function>::eval()
  • {
  • if(expression.find(' ') != string::npos)
  • boost::algorithm::erase_all(expression, " "); //suppression des espaces
  • check_syntax();
  • index = 0;
  • return somme();
  • }
  • template<typename number_type, typename algorithmic_function>
  • void expression_evaluator<number_type, algorithmic_function>::check_syntax() const throw(syntax_exception)
  • {
  • if((expression_evaluator<number_type, algorithmic_function>::is_operator(expression[0]) && expression[0] != '-')
  • || expression_evaluator<number_type, algorithmic_function>::is_operator(expression[expression.length()-1]))
  • throw syntax_exception("l'expression commence ou se termine par un opérateur");
  • register int i;
  • register int compteur = 0;
  • for(i = 0; i < expression.length(); ++i)
  • {
  • if(expression_evaluator<number_type, algorithmic_function>::is_forbidden(expression[i]))
  • throw syntax_exception("caractère interdit", expression, i);
  • if(expression[i] == '(')
  • ++compteur;
  • if(expression[i] == ')')
  • --compteur;
  • }
  • if(compteur != 0)
  • throw syntax_exception("le nombre de parenthèses ouvrantes ne correspond pas au nombres de parenthèses fermantes.");
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>& expression_evaluator<number_type, algorithmic_function>::operator=(const expression_evaluator<number_type, algorithmic_function>& source)
  • {
  • expression = source.expression;
  • variables = source.variables;
  • fonctions = source.fonctions;
  • fonctions_algo = source.fonctions_algo;
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::operator number_type()
  • {
  • return eval();
  • }
  • template<typename number_type, typename algorithmic_function>
  • expression_evaluator<number_type, algorithmic_function>::~expression_evaluator()
  • {
  • }
  • }
  • #endif /* ndef _CALCUL_HPP_ */
//////////////////////////////////////////////////////////////
//author lovejava (alias lovecpp, real name : Yann Hamdaoui)//
//////////////////////////////////////////////////////////////
//This source code is under the GNU/GPL license.            //
//////////////////////////////////////////////////////////////

#ifndef _CALCUL_HPP_
#define _CALCUL_HPP_

#include <iostream>
#include <map>
#include <boost/algorithm/string.hpp>
#include <string>
#include <sstream>
#include <cmath>

using namespace std;

/**********************************************
 *définit une fonctions (et ses sous fonctions)
 *capable d'évaluer une expression mathématique sous forme de chaîne
 *et de renvoyer le résultat
 */

namespace calcul
{
    /**
     *exception signalant que la syntaxe de l'expression passée en paramètre
     *n'est pas valide
     */
    class syntax_exception : public exception
    {
        protected:
            string message;
            int position; //position du caractère ayant provoqué l'erreur
            //expression mathématique complète
            string chaine;

        public:
            syntax_exception() throw() { };
            syntax_exception(const string& message, const string& chaine = "", int position = string::npos) throw();
            syntax_exception(const syntax_exception& source) throw();

            const char* what() const throw();

            syntax_exception& operator=(const syntax_exception& source) throw();

            virtual ~syntax_exception() throw() { };
    };

    /**
     *exception levée lors d'une division par zéro
     */
    class division_by_zero_exception : public exception
    {
        public:
            division_by_zero_exception() throw() { };
            division_by_zero_exception(const division_by_zero_exception& source) throw() { };

            const char* what() const throw();

            division_by_zero_exception& operator=(const division_by_zero_exception& source) throw() { };

            virtual ~division_by_zero_exception() throw() { };
    };

    /**
     *exception levée lorsqu'une variable ou une fonction utilisée dans un calcul n'est pas définie
     */
    class undefined_symbol_exception : public exception
    {
        protected:
            string symbole; //le nom de la variable/fonction indéfinie

        public:
            undefined_symbol_exception(const string& symbole = "?") throw();
            undefined_symbol_exception(const undefined_symbol_exception& source) throw();

            const char* what() const throw();

            undefined_symbol_exception& operator=(const undefined_symbol_exception& source) throw();

            virtual ~undefined_symbol_exception() throw() { };
    };

    /**
     *Classe qui permet d'envelopper les exceptions qui ont sont levées lorsque l'évaluateur
     *parse une expression de fonction. Par exemple, si l'utilisateur définit la fonction f : (x^2 + 2x + 5)/(x-1)
     *et qu'il tape 2*_f(1)/2, il peut ne pas comprendre d'où vient l'erreur si le seul message qu'il obtient est "division by zero".
     *C'est encore plus un problème lorsqu'une erreur de syntaxe est présente dans la déclaration de la fonction.
     *Cette exception permet donc de palier à ce problèmes en encapsulant l'erreur de base et en rajoutant dans le message d'erreur final
     *la provenance de l'erreur : par exemple [in function f]division by zero
     */
    class fonction_error_wrapper_exception : public exception
    {
        protected:
            string nom; //le nom de la fonction d'où provient l'erreur
            string message_origine; //le message d'erreur d'origine

        public:
            fonction_error_wrapper_exception(const exception& erreur, const string& nom = "?") throw();
            fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw();

            const char* what() const throw();

            fonction_error_wrapper_exception& operator=(const fonction_error_wrapper_exception& source) throw();

            virtual ~fonction_error_wrapper_exception() throw() { };
    };

    #define ALGORITHMIC_FUNCTION number_type (*)(number_type)

    /**
     *Classe qui évalue une expression pour la parser sous forme de nombre.
     *le paramètre template number_type permet de l'utiliser aussi bien avec
     *des int, des doubles, des float, etc.
     *le constructeur seul ne fait qu'initialiser l'objet, le parsage de l'expression
     *se fait lors de la conversion en objet de type number_type (voir operator number_type())
     *ou de l'appel explicite à eval()
     */
    template<typename number_type, typename algorithmic_function = number_type (*)(number_type)>
    class expression_evaluator
    {
        /*public:
            typedef (number_type (*)(number_type)) algorithmic_function;*/

        protected:
            string expression; //l'expression à parser
            unsigned int index; //index utilisé lors du parsage
            map<char, number_type> variables; //variables pouvant être utilisées pour des expressions
            map<string, expression_evaluator<number_type> > fonctions; //fonctions définies par l'utilisateur
            map<string, algorithmic_function> fonctions_algo; //fonctions algorithmiques et prédéfinies

            //méthodes d'analyse et de parcours de la chaîne (voir le corps des méthodes
            //pour des commentaires plus détaillés)
            number_type somme(); //+ et -
            number_type prior(); //*, / et %
            number_type puissance(); // ^
            number_type terme(); //analyse les nombres à proprement dit

            bool more(); //si l'on est arrivé à la fin de la chaîne
            /*Fonction qui renvoie true s'il y a un signe multiplié implicite à la position courante,
             *comme par exemple dans 2x + 3ab(2 + 3z)d, c'est à dire lorsque des lettres, des nombres et/ou des parenthèses
             *se suivent sans opérateur*/
            bool implicit_mult();

        public:
            expression_evaluator() { };
            expression_evaluator(const string& expression);
            expression_evaluator(const expression_evaluator& source);

            string get_expression() const;
            /*cette fonction renvoie la valeur d'une variable, ou 0 si elle n'existe pas
             *pour vérifier qu'une variable existe, utiliser is_var_defined() au préalable, car une variable
             *existante peut très bien avoir pour valeur 0
             */
            number_type get_var(char variable) const;
            /*
             *renvoie l'expression d'une fonction définie par l'utilisateur
             */
            string get_function(const string& nom) const;
            /*
             *renvoie un pointeur de fonction vers une fonction "algorithmique" définie par l'utilisateur
             *ou prédéfinie (sinus, cosinus, etc...). Par algorithmique, j'entend une fonction qui ne peut être
             *définie seulement par une expression mathématique comme '2x+3', par exemple la fonction cosinus, partie entière
             *ou encore racine carrée.
             */
            algorithmic_function get_algorithmic_function(const string& nom) const;

            void set_expression(const string& expression);
            //permet d'assigner une valeur à une variable
            void set_var(char variable, number_type valeur);
            //permet d'assigner à une fonction son expression
            void set_function(const string& nom, const string& expression);
            //permet d'assigner à une fonction son algorithme
            void set_algorithmic_function(const string& nom, algorithmic_function fonction);

            bool is_var_defined(char variable) const;
            bool is_function_defined(const string& nom) const;

            //évalue l'expression et renvoie un nombre
            number_type eval();
            //vérifie la syntaxe. Lève une syntax_exception si celle-ci n'est pas valide
            void check_syntax() const throw(syntax_exception);

            //méthode de classification des caractères
            static bool is_digit(char caractere);
            static bool is_alpha(char caractere);
            static bool is_alphanum(char caractere);
            static bool is_operator(char caractere);
            static bool is_forbidden(char caractere);

            expression_evaluator& operator=(const expression_evaluator& source);
            operator number_type();

            ~expression_evaluator();
    };

    //fonctions d'adaptation permettant d'utiliser les fonctions usuelles comme cosinus, log, etc...
    //avec n'importe quel type générique number_type en effectuant les conversions nécessaires :
    template<typename number_type>
    number_type _abs_adapter(number_type parametre)
    {
        return (number_type)fabs((double)parametre);
    }


    template<typename number_type>
    number_type _acos_adapter(number_type parametre)
    {
        return (number_type)acos((double)parametre);
    }

    template<typename number_type>
    number_type _asin_adapter(number_type parametre)
    {
        return (number_type)asin((double)parametre);
    }

    template<typename number_type>
    number_type _atan_adapter(number_type parametre)
    {
        return (number_type)atan((double)parametre);
    }


    template<typename number_type>
    number_type _ceil_adapter(number_type parametre)
    {
        return (number_type)ceil((double)parametre);
    }


    template<typename number_type>
    number_type _exp_adapter(number_type parametre)
    {
        return (number_type)exp((double)parametre);
    }


    template<typename number_type>
    number_type _floor_adapter(number_type parametre)
    {
        return (number_type)floor((double)parametre);
    }


    template<typename number_type>
    number_type _log_adapter(number_type parametre)
    {
        return (number_type)log((double)parametre);
    }

    template<typename number_type>
    number_type _log10_adapter(number_type parametre)
    {
        return (number_type)log10((double)parametre);
    }


    template<typename number_type>
    number_type _cos_adapter(number_type parametre)
    {
        return (number_type)cos((double)parametre);
    }

    template<typename number_type>
    number_type _cosh_adapter(number_type parametre)
    {
        return (number_type)cosh((double)parametre);
    }

    template<typename number_type>
    number_type _sin_adapter(number_type parametre)
    {
        return (number_type)sin((double)parametre);
    }

    template<typename number_type>
    number_type _sinh_adapter(number_type parametre)
    {
        return (number_type)sinh((double)parametre);
    }

    template<typename number_type>
    number_type _tan_adapter(number_type parametre)
    {
        return (number_type)tan((double)parametre);
    }

    template<typename number_type>
    number_type _tanh_adapter(number_type parametre)
    {
        return (number_type)tanh((double)parametre);
    }

    ///////////////////////////////////////////////////////

    syntax_exception::syntax_exception(const string& message, const string& chaine, int position) throw() : exception()
    {
        this->message = message;
        this->chaine = chaine;
        this->position = position;
    }

    syntax_exception::syntax_exception(const syntax_exception& source) throw() : exception()
    {
        *this = source;
    }

    const char* syntax_exception::what() const throw()
    {
        string temp(message);
        //si un numéro de caractère est spécifié pour l'erreur syntaxique, on affiche le caractère en question
        //et sa position dans l'expression après le message d'erreur
        if(position != string::npos)
        {
            ostringstream sortie;
            sortie << position+1;
            temp.append("(caractère \'").append(chaine, position, 1).append("\', en position ").append(sortie.str()).append(")");
        }

        return temp.c_str();
    }

    syntax_exception& syntax_exception::operator=(const syntax_exception& source) throw()
    {
        message = source.message;
        chaine = source.chaine;
        position = source.position;
    }

    ///////////////////////////////////////////////////////////////////////////

    const char* division_by_zero_exception::what() const throw()
    {
        return "division par zéro";
    }

    ///////////////////////////////////////////////////////////////////////////

    undefined_symbol_exception::undefined_symbol_exception(const string& symbole) throw()
    {
        this->symbole = symbole;
    }

    undefined_symbol_exception::undefined_symbol_exception(const undefined_symbol_exception& source) throw()
    {
        *this = source;
    }

    const char* undefined_symbol_exception::what() const throw()
    {
        return string("\'").append(symbole).append("\' n'est pas défini.").c_str();
    }

    undefined_symbol_exception& undefined_symbol_exception::operator=(const undefined_symbol_exception& source) throw()
    {
        symbole = source.symbole;
    }

    ///////////////////////////////////////////////////////////////////////////

    fonction_error_wrapper_exception::fonction_error_wrapper_exception(const exception& erreur, const string& nom) throw()
    {
        this->message_origine = erreur.what();
        this->nom = nom;
    }

    fonction_error_wrapper_exception::fonction_error_wrapper_exception(const fonction_error_wrapper_exception& source) throw()
    {
        *this = source;
    }

    const char* fonction_error_wrapper_exception::what() const throw()
    {
        return string("[in function ").append(nom).append("]").append(message_origine).c_str();
    }

    fonction_error_wrapper_exception& fonction_error_wrapper_exception::operator=(const fonction_error_wrapper_exception& source) throw()
    {
        nom = source.nom;
        message_origine = source.message_origine;
    }

    ///////////////////////////////////////////////////////////////////////////

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const string& expression)
    {
        this->expression = expression;

        //ajout des fonctions mathématiques de base
        fonctions_algo["abs"] = &_abs_adapter<number_type>;

        fonctions_algo["acos"] = &_acos_adapter<number_type>;
        fonctions_algo["asin"] = &_asin_adapter<number_type>;
        fonctions_algo["atan"] = &_atan_adapter<number_type>;

        fonctions_algo["ceil"] = &_ceil_adapter<number_type>;

        fonctions_algo["exp"] = &_exp_adapter<number_type>;

        fonctions_algo["floor"] = &_floor_adapter<number_type>;

        fonctions_algo["log"] = &_log_adapter<number_type>;
        fonctions_algo["log10"] = &_log10_adapter<number_type>;

        fonctions_algo["cos"] = &_cos_adapter<number_type>;
        fonctions_algo["cosh"] = &_cosh_adapter<number_type>;
        fonctions_algo["sin"] = &_sin_adapter<number_type>;
        fonctions_algo["sinh"] = &_sinh_adapter<number_type>;
        fonctions_algo["tan"] = &_tan_adapter<number_type>;
        fonctions_algo["tanh"] = &_tanh_adapter<number_type>;
    }

    template<typename number_type, typename algorithmic_function>
    expression_evaluator<number_type, algorithmic_function>::expression_evaluator(const expression_evaluator<number_type, algorithmic_function>& source)
    {
        *this = source;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::somme()
    {
        /*on commence par séparer les termes via les signes + et -,
         *ceux qui ont la plus faible priorité. On calculera d'abord
         *ces termes indépendamment pour ensuite leur appliquer, en dernier lieu,
         *l'opérateur + ou -.
         */


        //à l'appel de la fonction, on considère que le premier élément est forcément
        //soit un nombre, soit une parenthèse : on appelle donc la méthode terme()
        //via la méthode prior() pour récupérer ce premier nombre.
        number_type retour = prior();

        //une fois obtenu un nombre, on cherche un signe + ou -
        //si on trouve, on appel les méthodes sous-jacente pour calculer
        //l'ensemble de la valeur qui sera ajoutée ou retranchée au final
        while(more() && (expression[index] == '+' || expression[index] == '-'))
        {
            ++index; //on avance d'un caractère
            if(expression[index-1] == '+')
                retour += prior();
            else
                retour -= prior();
        }

        /*si on arrive à la fin d'une parenthèse, on sort d'une sous-expression.
         *On avance donc la position courante pour que, lorsque l'on rend la main à
         *la fonction terme(), l'analyse de l'expression ne s'arrête pas prématurément
         *à cause du fait que comme la parenthèse n'est ni un opérateur, ni un nombre,
         *les fonctions rendraient la main une à une sans effectuer aucun traitement supplémentaire
         *Lorsqu'on enlève cette ligne par exemple, (5+(5+5)*2)*2 font 15 (en fait, seulement les
         *trois premiers 5 on été analysés et ajoutés) et non pas 50.
         */
        if(more() && expression[index] == ')')
            ++index;

        return retour;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::prior()
    {
        number_type retour = puissance(); //on obtient le premier nombre de l'expression via puissance()

        /*une fois obtenu un nombre, on cherche un signe *, / ou %
         *on continue jusqu'à que le signe suivant ne soit ni un nombre ni un * ni un / ni un %
         *pour être sûr d'avoir effectué toutes les divisions/multiplications de l'expression
         *ou sous-expression. Ainsi la priorité est respectée et on rend la main à la fonction somme
         */
        bool implicite = false; //on doit savoir si la multiplication était implicite ou non pour avancer ou pas index
        while( more() && (expression[index] == '*' || expression[index] == '/' || expression[index] == '%' || (implicite = implicit_mult())) )
        {
            if(!implicite)
                ++index;
            if(expression[index-1] == '/')
            {
                number_type temp = puissance();
                if(temp == 0)
                    throw division_by_zero_exception();

                retour /= temp;
            }
            else if(expression[index-1] == '%')
                retour = (number_type) ((long long int)retour % (long long int)puissance());
            else
                retour *= puissance();

            /*on réinitialise implicite pour les prochains passages, car si expression[index] vaut '*' par exemple, l'instruction
             *implicite = implicit_mult() ne sera pas exécutée ; donc si au précédent passage implicite valait true, il garderait cette valeur
             *si on ne le remettait pas à false
             */
            implicite = false;
        }

        return retour;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::puissance()
    {
        /*fonction qui regarde si dans l'expression ou sous-expression en cours
         *il n'y a pas de signe '^'. Elle est après la fonction prior() dans le chaînage
         *des appels car l'opérateur d'élévation à la puissance est le plus prioritaire
         */

        number_type retour = terme();

        while(more() && (expression[index] == '^'))
        {
            ++index;
            retour = (number_type)pow((double)retour, (double)terme());
        }

        return retour;
    }

    template<typename number_type, typename algorithmic_function>
    number_type expression_evaluator<number_type, algorithmic_function>::terme()
    {
        //c'est la fonction qui lit réellement les nombres
        //si on tombe sur une parenthèse ouvrante, on ré-analyse
        //cette sous-expression comme une nouvelle expression apparentière
        if(expression[index] == '(')
        {
            ++index;
            return somme();
        }
        else
        {
            number_type nombre;
            unsigned int index_depart = index;
            bool moins_unaire = false; //s'il y avait un moins unaire au début de l'expression

            //gestion du moins unaire : si l'on rencontre un moins lorsque les fonctions
            //appellantes veulent un nombre, on considère que c'est un moins unaire
            //on met un booléen à true puis on avance l'index courant
            if(expression[index] == '-')
            {
                moins_unaire = true;
                ++index;
                ++index_depart;
            }

            if(expression_evaluator<number_type, algorithmic_function>::is_operator(expression[index]))
                throw syntax_exception("plusieurs opérateurs se suivent", expression, index);

            if(expression[index] == '_')
            {
                //on avance la position courante jusqu'à la parenthèse ouvrante
                ++index;
                ++index_depart;

                for(; more() && expression[index] != '('; ++index) ;
                //on doit récupérer la valeur d'une fonction
                if(!more())
                    throw syntax_exception("Un nom de fonction doit être suivi d'une parenthèse ouvrante", expression, index_depart);

                string nom_fonction = expression.substr(index_depart, index-index_depart);
                typename map<string, /*expression_evaluator<number_type, algorithmic_function>::algorithmic_function*/ALGORITHMIC_FUNCTION>::iterator it1 = fonctions_algo.find(nom_fonction);
                typename map<string, expression_evaluator<number_type, algorithmic_function> >::iterator it2 = fonctions.find(nom_fonction);

                ++index; //on passe après la parenthèse
                number_type x_value = somme(); //on évalue indépendamment l'expression qui suit la fonction (c'est à dire la valeur de x)

                if(it1 != fonctions_algo.end())
                {
                    try {
                        nombre = (*(*it1).second)(x_value);
                    }
                    //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
                    //et non pas dans la chaîne analysée en ce moment
                    catch(const exception& e)
                    {
                        throw fonction_error_wrapper_exception(e, nom_fonction);
                    }
                }
                else if(it2 != fonctions.end())
                {

                    //on met à jour les définitions de variables et de fonctions pour que la suite tout ça etc.
                    fonctions[nom_fonction].variables = variables;
                    fonctions[nom_fonction].fonctions = fonctions;
                    fonctions[nom_fonction].set_var('x', x_value);

                    try {
                        nombre = fonctions[nom_fonction].eval();
                    }
                    //il faut convertir les messages d'erreurs pour bien spécifier que l'erreur se trouve dans la définition de la fonction,
                    //et non pas dans la chaîne analysée en ce moment
                    catch(const exception& e)
                    {
                        throw fonction_error_wrapper_exception(e, nom_fonction);
                    }
                }
                else
                    throw undefined_symbol_exception(nom_fonction);
            }
            else if(expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index]))
            {
                char variable = expression[index];
                if(!is_var_defined(variable))
                    throw undefined_symbol_exception(string(&variable, 1));

                number_type valeur = get_var(variable);
                nombre = valeur;
                ++index;
            }
            else
            {
                //sinon, on lit le nombre à la position courante
                for(; more() && is_digit(expression[index]); ++index) ;

                string nombre_str(expression, index_depart, index-index_depart);
                istringstream entree(nombre_str);
                entree >> nombre;
            }

            if(moins_unaire)
                return -nombre;
            else
                return nombre;
        }
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::more()
    {
        return index < expression.length();
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::implicit_mult()
    {
        /*Vérifie s'il n'y a pas de signe multiplier implicite
         *avec des variables (comme par exemple 2a(3+2) ou encore (x+2)(x-3)bc
         *vérifications : si un chiffre et une lettre se suivent (ou l'inverse)
         *                si deux lettres se suivent
         *                si un chiffre/une lettre suivent une parenthèse fermante (ou précèdent une parenthèse ouvrante)
         *                si une parenthèse fermante et une parenthèse ouvrante se suivent
         */
        return ( (expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
                 (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_digit(expression[index])) ||
                 (expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index-1]) && expression_evaluator<number_type, algorithmic_function>::is_alpha(expression[index])) ||
                 (expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index-1]) && expression[index] == '(') ||
                 (expression[index-1] == ')' && expression_evaluator<number_type, algorithmic_function>::is_alphanum(expression[index])) ||
                 (expression[index-1] == ')' && expression[index] == '(') );
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_digit(char caractere)
    {
        return (caractere == '.' ||
                (caractere >= '0' && caractere <= '9'));
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_alpha(char caractere)
    {
        return (caractere >= 'a' && caractere <= 'z') || (caractere >= 'A' && caractere <= 'Z') || caractere == '_';
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_alphanum(char caractere)
    {
        return is_digit(caractere) || is_alpha(caractere);
    }

    template<typename number_type, typename algorithmic_function>
    bool expression_evaluator<number_type, algorithmic_function>::is_operator(char caractere)
    {
        return (caractere == '+' || caractere == '-' || caractere == '%'