/*------------------------------------------------------------------*/
/* calcul.cpp :   Fichier contenant le constructeur de la classe    */
/*                type_formule, c'est-à-dire les fonctions qui      */
/*                serviront à batir l'arbre de calcul d'équation    */
/*------------------------------------------------------------------*/


#include <cmath>
#include <string>
#include <vector>

#include "globale.h"
#include "calcul.h"

/*---------------------------------------------------------------------*/
/* Cette fonction vérifie si l'opérateur (+, -, * ou /) se trouve à    */
/* l'intérieur d'une parenthèse.  Elle compte le nombre de parenthèses */
/* ouvrantes '(' situées avant l'opérateur, ainsi que le nombre de     */
/* parenthèses fermantes ')'.  Si la différence des deux est nulle,    */
/* l'opérateur n'est pas entre deux parenthèses.                       */
/*---------------------------------------------------------------------*/  

inline bool In(const std::string& Equation, int min, int max)
{
	int Compteur = 0;
	for (int i = min; i < max; i++)
	{
		if (Equation[i] == '(')
			Compteur++;
		if (Equation[i] == ')')
			Compteur--;
	}
	return (Compteur != 0);
}


/*-----------------------------------------------------------------------*/
/* Cette fonction construit l'expression de la façon suivante:           */
/* (par plusieurs appels récursifs)                                      */
/*                                                                       */
/* - Elle cherche, à partir de la droite, un opérateur + ou -.  si elle  */
/*   en trouve un, elle vérifie qu'il n'est pas à l'intérieur de         */
/*   parenthèses et retourne la construction de la partie droite évaluée */
/*   par ConstrucFormule() et de la partie gauche évaluée par            */
/*   ConstrucFormule().  Si l'opérateur est à l'intérieur de parenthèse, */
/*   il est ignoré.  Si l'opérateur - est trouvé, mais qu'il s'agit du   */
/*   premier caractère, l'opposé du reste de la chaîne est retourné.     */
/*                                                                       */
/* - Elle procède à la même opération pour les opérateurs * et /, qui    */
/*   sont également ignorés s'ils sont situés entre deux parenthèses.    */
/*                                                                       */
/* - Elle recherche ensuite les fonctions particulières, soit sqrt, sin, */
/*   cos, tan, exp ou l'opérateur ^.  Si cette fonction n'est pas située */
/*   à l'intérieur de parenthèses, elle retourne la construction de la   */
/*   fonction de l'expression à la droite de la fonction.                */
/*                                                                       */
/* - Elle vérifie ensuite s'il y a une parenthèse ouvrante.  Si tel est  */
/*   le  cas, elle part de la fin et recherche une parenthèse fermante   */
/*   ')'.  Elle retourne ensuite la construction de l'intérieur des      */
/*   parenthèses évalué par ConstrucFormule().                           */
/*                                                                       */
/* - Elle vérifie par la suite s'il y a une variable x, y ou z et        */
/*   retourne la construction de la variable.                            */
/*                                                                       */
/* - Elle recherche après s'il y a un chiffre.  Si tel est le cas, elle  */
/*   se rend jusqu'au prochain caractère qui n'est pas un chiffre ou un  */
/*   point et copie la partie de la chaîne contenant le nombre dans une  */
/*   autre chaîne créée dynamiquement.  Cette chaîne est convertie en    */
/*   nombre réelle à l'aide de atof() et cette valeur est construite.    */
/*                                                                       */
/* - Elle vérifie ensuite s'il y a un espace, caractère qu'elle ignore   */
/*   en retournant la construction de l'expression au complet sauf       */
/*   l'espace évaluée par ConstrucFormule().                             */
/*-----------------------------------------------------------------------*/

type_valeur* ConstrucFormule(const std::string& Equation, int min, int max)
{
	int i, j;

	// Recherche d'opérateurs + ou -
	for (i = max-1; i > min-1; i--)
		if ((Equation[i] == '+') || (Equation[i] == '-'))
			if ( !(In(Equation, min, i)) )
				if (Equation[i] == '+')
					return(new type_add(ConstrucFormule(Equation, min, i), 
										ConstrucFormule(Equation, i+1, max)));
				else
					if (i == min)
						return(new type_prod(new type_nombre(-1), 
							   ConstrucFormule(Equation, min+1, max)));
					else
						return(new type_sub(ConstrucFormule(Equation, min, i), 
											ConstrucFormule(Equation, i+1, max)));
						
	// Recherche d'opérateur * ou /
	for (i = max-1; i > min; i--)
		if ((Equation[i] == '*') || (Equation[i] == '/'))
			if ( !(In(Equation, min, i)) )
				if (Equation[i] == '*')
					return(new type_prod(ConstrucFormule(Equation, min, i), 
										 ConstrucFormule(Equation, i+1, max)));
				else
					return(new type_div(ConstrucFormule(Equation, min, i),
										ConstrucFormule(Equation, i+1, max)));

	// Recherche de fonction sqrt, sin, cos, tan, exp ou l'opérateur ^
	for (i = min; i < max; i++)
		if ((Equation[i] == 's') || (Equation[i] == 'c') || (Equation[i] == 't') || (Equation[i] == 'e') || (Equation[i] == '^'))
				if ( !(In(Equation, min, i)) )
					switch (Equation[i])
					{
					case('s'): if ((Equation[i+1] == 'q') && (Equation[i+2] == 'r') && (Equation[i+3] == 't'))
								   return(new type_sqrt(ConstrucFormule(Equation, i+4, max)));
								
							   if ((Equation[i+1] == 'i') && (Equation[i+2] == 'n'))
								   return(new type_sin(ConstrucFormule(Equation, i+3, max)));
								break;

					case('c'): if ((Equation[i+1] == 'o') && (Equation[i+2] == 's'))
								   return(new type_cos(ConstrucFormule(Equation, i+3, max)));
								break;

					case('t'): if ((Equation[i+1] == 'a') && (Equation[i+2] == 'n'))
								   return(new type_cos(ConstrucFormule(Equation, i+3, max)));
								break;

					case('e'): if ((Equation[i+1] == 'x') && (Equation[i+2] == 'p'))
								   return(new type_exp(ConstrucFormule(Equation, i+3, max)));
								break;

					case('^') : return(new type_pow(ConstrucFormule(Equation, min, i),
													ConstrucFormule(Equation, i+1, max)));
								break;
					}

	// Recherche une parenthèse ouvrante '('
	for (i = min; i < max; i++)
		if (Equation[i] == '(')
			for (j = max; j > i; j--)
				if (Equation[j-1] == ')')
					return(ConstrucFormule(Equation, i+1, j));

	// Recherche d'une variable x, y ou z
	for (i = min; i < max; i++)
		if ((Equation[i] == 'x') || (Equation[i] == 'y'))
			if (Equation[i] == 'x')
				return(new type_variable(0));
			else
				if ((max - min == 1) || (Equation[i+1] != '\''))
					return(new type_variable(1));
				else
					return(new type_variable(2));

	// Recherche d'un chiffre
	for (i = min; i < max; i++)
		if ((Equation[i] <= '9') && (Equation[i] >= '0'))
		{
			j = i;
			while ((((Equation[j] <= '9') && (Equation[j] >= '0')) || (Equation[j] == '.')) && (j < max))
				{j++;}
			char* Temp = new char[j-i];
			for (int k = i; k < j; k++)
				Temp[k-i] = Equation[k];
			double Valeur = atof(Temp);
			delete []Temp;
			return(new type_nombre(Valeur));
		}
	
	// Recherche des espaces
	for (i = min; i < max; i++)
		if (Equation[i] == ' ')
			return(ConstrucFormule(Equation, min+1, max));
	
	// Si rien n'est reconnu
	throw;
}


// Contructeur de la formule qui fait appel à ConstrucFormule()
type_formule::type_formule(const std::string& Equation)
{
	unsigned int i = 0;
	while (Equation[i] != '=')
	{
		if (i == Equation.size())
			throw;
		i++;
	}
	
	Formule = ConstrucFormule(Equation, i+1, Equation.size());
}
