# -*- coding: iso-8859-1 -*-
from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0)

##--------------------------------------##
#              WxGeometrie               #
#                tabvar                  #
##--------------------------------------##
#    WxGeometrie
#    Dynamic geometry, graph plotter, and more for french mathematic teachers.
#    Copyright (C) 2005-2007  Nicolas Pourcelot
#
#    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 2 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, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import mathlib
import mathlib.universal_functions as maths
import math
import param
import sympy
maths.oo = sympy.oo
import pylib
import re

#~ try:
    #~ maths.num_oo = float(mathlib.array(1.)/0.)
#~ except ZeroDivisionError:
    #~ if param.debug:
        #~ print_error()
    #~ maths.num_oo = maths.oo

import numpy
maths.num_oo = numpy.inf
maths.pi = math.pi
maths.e = math.e

import LIB
import param


def traduire_latex(expression):
    return LIB.parsers.traduire_formule(  expression,
                                            fonctions = [key for key, obj in maths.__dict__.items() if hasattr(obj, "__call__")],
##                                            variables = '[_A-Za-z][_A-Za-z0-9]*',
                                            OOo = False,
                                            LaTeX = True,
                                            changer_separateurs = True,
                                            separateurs_personnels = (',', ';'))


def convertir_en_latex(chaine):
    chaine = pylib.regsub("[ ]+", chaine, " ") # inutile en LaTeX, mais ça peut simplifier certaines expressions regulieres

    # On traite maintenant le (délicat) cas des fractions, ie. 2/3, mais aussi (pi+3)/(5-e)
    securite = 10000
    while "/" in chaine:
        securite -= 1
        if securite < 0:
            raise RuntimeError, "Boucle probablement infinie."
        i = chaine.find("/")

        # analyse des caractères précédants, pour localiser le numérateur
        k = i
        parentheses = 0
        debut_numerateur = fin_numerateur = None # indices correspondants au début et à la fin du numérateur
        while k>0:
            k -= 1
            if chaine[k].isalnum():
                if fin_numerateur is None:
                    fin_numerateur = k
            elif chaine[k] == ")":
                if parentheses == 0:
                    fin_numerateur = k
                parentheses += 1
            elif chaine[k] == "(":
                parentheses -= 1
                if parentheses == 0:
                    debut_numerateur = k
                    break
            elif parentheses == 0 and fin_numerateur is not None:
                debut_numerateur = k+1
                break
        if debut_numerateur is None:
            debut_numerateur = 0
        assert fin_numerateur is not None, "Numerateur introuvable"

        numerateur = chaine[debut_numerateur : fin_numerateur+1].strip()
        if numerateur[0] == "(":
            numerateur = numerateur[1:-1]


        # analyse des caractères suivants, pour localiser le dénominateur
        k = i
        parentheses = 0
        debut_denominateur = fin_denominateur = None # indices correspondants au début et à la fin du numérateur

        while k < len(chaine) - 1:
            k += 1
            if chaine[k].isalnum():
                if debut_denominateur is None:
                    debut_denominateur = k
            elif chaine[k] == "(":
                if parentheses == 0:
                    debut_denominateur = k
                parentheses += 1
            elif chaine[k] == ")":
                parentheses -= 1
                if parentheses == 0:
                    fin_denominateur = k
                    break
            elif parentheses == 0 and debut_denominateur is not None:
                fin_denominateur = k-1
                break
        if fin_denominateur is None:
           fin_denominateur = len(chaine)  - 1

        assert debut_denominateur is not None, "Denominateur introuvable"

        denominateur = chaine[debut_denominateur : fin_denominateur+1].strip()
        if denominateur[0] == "(":
            denominateur = denominateur[1:-1]

        # remplacement de la fraction python par une fraction LaTeX
        chaine = chaine[:debut_numerateur] + "\\frac{" + numerateur + "}{" + denominateur + "}" + chaine[fin_denominateur+1:]

    # Autres remplacements :
    chaine = pylib.regsub("(?<!\\w)pi(?!\\w)", chaine, "\\\\pi")
    chaine = pylib.regsub("(?<!\\w)oo(?!\\w)", chaine, "\\\\infty")
    chaine = pylib.regsub("(?<!\\w)e(?!\\w)", chaine, "\\\\mathrm{e}")
    chaine = pylib.regsub("(?<!\\w)sin(?!\\w)", chaine, "\\\\sin")
    chaine = pylib.regsub("(?<!\\w)cos(?!\\w)", chaine, "\\\\cos")
    chaine = pylib.regsub("(?<!\\w)tan(?!\\w)", chaine, "\\\\tan")
    chaine = pylib.regsub("(?<!\\w)ln(?!\\w)", chaine, "\\\\ln")
    chaine = pylib.regsub("(?<!\\w)exp(?!\\w)", chaine, "\\\\exp")

    chaine = chaine.replace("**", "^").replace("*", " ")

    return "$" + chaine + "$" # premiere ligne : la variable


def test_parentheses(chaine):
    u"Retourne True si le parenthésage est correct, False sinon."
    count = 0
    n = 0
    k = 0
    while count >= 0:
        k += 1
        i = chaine.find('(', n)
        j = chaine.find(')', n)
        if i == -1:
            n = j
        elif j == -1:
            n = i
        else:
            n = min(i, j)
        if n == -1:
            break
        parenthese = chaine[n]
        assert parenthese in '()'
        count += (1 if parenthese == '(' else -1)
        n += 1
    return not count




def tabvar(chaine = "", derivee = True):
    u"""Indiquer les variations de la fonction.

Exemples :
f: (-oo;3) << (1;2;0) << (3;+oo|-oo) << (5;2) >> (+oo;-oo)
\\sqrt{x};\\sqrt'{x}: 0;0;| << +oo;+oo"""

    chaine_originale = chaine = chaine.strip()

    chaine = chaine.replace("-oo", "-\\infty").replace("+oo", "+\\infty")

    #ligne_variable = ligne_derivee = ligne_fonction = ""

    liste = chaine.split(":", 1)
    if len(liste) == 1 or len(liste[0].strip()) == 0:
        ligne_variable = "x"
        ligne_derivee = "f'(x)"
        ligne_fonction = "f"

    else:
        legende, chaine = liste
        legende = legende.split(";")

        if len(legende) > 1:
            ligne_variable = legende[0].strip()
            ligne_fonction = legende[1].strip()

            if len(legende) > 2:
                ligne_derivee = legende[2].strip()
            else:
                deb = ligne_fonction.find("(")
                if deb == -1:
                    ligne_derivee = ligne_fonction[:].strip() + "'(" + ligne_variable + ")"
                else:
                    ligne_derivee = ligne_fonction[:deb].strip() + "'(" + ligne_variable + ")"

        else: # un seul argument pour la legende: c'est alors la fonction
            ligne_fonction = legende[0].strip()
            deb = ligne_fonction.find("(")
            fin = ligne_fonction.find(")")
            if deb == -1:
                ligne_variable = "x"
                ligne_derivee = ligne_fonction + "'(x)"
            else:
                ligne_variable = ligne_fonction[deb+1:fin].strip()
                ligne_derivee = ligne_fonction[:deb].strip() + "'(" + ligne_variable + ")"




    # on découpe la chaîne, en une suite contenant soit les valeurs de x, f(x) (et éventuellement f'(x)), 
    # soit le sens de variation entre ces valeurs.
    # ex: "-oo;3 << 1;2 >> 3;-oo|+oo << 5;2 << +oo;+oo" devient 
    # ["-oo;3", "<<", "1;2", ">>", "3;-oo|+oo", "<<", "5;2", "<<", "+oo;+oo"]

    sequence = re.split("(>>|<<|==)", chaine.strip())
    
    if not sequence[0]:     
        # en l'absence d'indication, x varie de -oo...
        sequence[0] = "-\\infty;"
    if not sequence[-1]:    
        # ... à +oo
        sequence[-1] = "+\\infty;"

    def formater(chaine):
        chaine = chaine.strip()
        if chaine not in ("<<", ">>", "==") and ";" not in chaine:
            if chaine[0] == '(' and chaine[-1] == ')' and test_parentheses(chaine[1:-1]):
                chaine = chaine[1:-1]
            chaine += ";" # il faut au minimum un ";" pour indiquer l'absence de valeur
        return chaine

    sequence = [formater(elt) for elt in sequence]

    # On effectue un premier balayage uniquement pour detecter les niveaux.
    niveaux = [] # chaque element de la liste correspond a une portion de tableau de variation comprise entre deux valeurs interdites.
    # A chacune de ces portions va correspondre un doublon (niveau minimal atteint, niveau maximal atteint) qu'on ajoute a la liste.
    niveau = niveau_minimum = niveau_maximum = 0
    for elt in sequence:
        if ";" in elt and "|" in elt.split(";")[1]: # presence d'une valeur interdite
            niveaux.append((niveau_minimum, niveau_maximum))
            niveau = niveau_minimum = niveau_maximum = 0
        else:
            if elt == "<<":
                niveau += 1
            elif elt == ">>":
                niveau -= 1
            if niveau < niveau_minimum:
                niveau_minimum = niveau
            if niveau > niveau_maximum:
                niveau_maximum = niveau
    niveaux.append((niveau_minimum, niveau_maximum))
    ecart_maximal = max(val[1] - val[0] for val in niveaux)

    # L'environnement tabvar ne permet pas de positionner un texte entre deux lignes.
    # Si, dans la 3e partie du tableau (la fonction elle-même),
    # le nombre de lignes (c-à-d. ecart_maximal+1) est impair,
    # on décale le texte légèrement vers le haut pour le centrer verticalement (via raisebox).
    if ecart_maximal%2:
        ligne_fonction = "\\niveau{" + str((ecart_maximal+2)//2) +"}{" + str(ecart_maximal+1) + "}\\raisebox{0.5em}{$" + ligne_fonction + "$}"
    else:
        ligne_fonction = "\\niveau{" + str((ecart_maximal+2)//2) +"}{" + str(ecart_maximal+1) + "}" + ligne_fonction

    #print "niveaux: ", niveaux
    nombre_colonnes = 0
    portion = 0 # indique la derniere portion traitee (les portions sont delimitees par les bornes de l'ensemble de definition et les valeurs interdites)
    debut = True

    # Deuxieme et dernier balayage :
    # on parcourt maintenant la liste pour construire colonne par colonne le tableau de variations.
    for i in xrange(len(sequence)):
        # on justifie apres chaque etape, ce qui rend une eventuelle relecture du tableau plus agreable
        n = max(len(ligne_variable), len(ligne_derivee), len(ligne_fonction))
        ligne_variable = ligne_variable.ljust(n)
        ligne_derivee = ligne_derivee.ljust(n)
        ligne_fonction = ligne_fonction.ljust(n)

        ligne_variable += "&"
        if debut:
            debut = False
            ligne_fonction += "&\\niveau{" + str(1-niveaux[portion][0]) +"}{" + str(ecart_maximal+1) + "}"
        else:
            ligne_fonction += "&"
        ligne_derivee += "&"

        elt = sequence[i]
        if elt in (">>", "<<", "=="):  # il s'agit d'une variation
            nombre_colonnes += 1
            #ligne_variable += " "
            if elt == "<<":
                ligne_derivee += "+"
                ligne_fonction += "\\croit"
            elif elt == ">>":
                ligne_derivee += "-"
                ligne_fonction += "\\decroit"
            else:
                ligne_derivee += "0"
                ligne_fonction += "\\constante"

        else: # il s'agit des valeurs entre deux variations
            valeurs = elt.split(";") 
            # valeurs = x, f(x), et eventuellement f'(x).
            # Si f(x) n'est pas defini (valeur interdite), il y a (en general) une valeur-double pour f(x) :
            # les limites a gauche et a droite, separees par un "|". (Idem pour f'(x)).
            
            if (valeurs[0].count('(') - valeurs[0].count(')') == 1 
                    and valeurs[-1].count(')') - valeurs[-1].count('(') == 1
                    and valeurs[0][0] == '(' 
                    and valeurs[-1][-1] == ')'
                    ):
                valeurs[0] = valeurs[0][1:-1]
                
            
            largeur = max(("|" in val and 3 or 1) for val in valeurs) 
            # 3 si x est une valeur interdite pour f ou f' (ie. une valeur contient un "|"), 1 sinon.

            if largeur == 3: # x est une valeur interdite pour f(x) ou f'(x)
                ligne_variable += " &" + convertir_en_latex(valeurs[0]) + "& "

                vals_fonc = valeurs[1].split("|")
                if len(vals_fonc) == 2: # x est une valeur interdite pour f(x)
                    portion += 1 # on change de portion
                    ligne_fonction += convertir_en_latex(vals_fonc[0]) + "&\\dbarre&" \
                                    + "\\niveau{" + str(1-niveaux[portion][0]) +"}{" + str(ecart_maximal+1) + "}"  \
                                    + convertir_en_latex(vals_fonc[1])
                else:
                    ligne_fonction += " &" + convertir_en_latex(vals_fonc[0]) + "&"

                if len(valeurs) < 3: # le nombre derive n'est pas specifie
                    valeurs.append("|") # si la fonction n'est pas definie en x, sa derivee non plus

                vals_deriv = valeurs[2].split("|")
                if len(vals_deriv) == 2:
                    ligne_derivee += convertir_en_latex(vals_deriv[0]) + "&\\dbarre&" + convertir_en_latex(vals_deriv[1])
                else:
                    ligne_derivee += " &" + convertir_en_latex(vals_deriv[0]) + "&"

            else: # x n'est pas une valeur interdite
                ligne_variable += convertir_en_latex(valeurs[0])
                ligne_fonction += convertir_en_latex(valeurs[1])

                if len(valeurs) < 3: # le nombre derive n'est pas specifie
                    if 0 < i < len(sequence)-1 and sequence[i-1] != sequence[i+1]: # changement de sens de variation en x
                        valeurs.append("0") # la derivee s'annule donc
                    else:
                        valeurs.append(" ")
                ligne_derivee += convertir_en_latex(valeurs[2])


            nombre_colonnes += largeur



    code = "\\[\\begin{tabvar}{|C|" + nombre_colonnes*"C" + "|}\n\\hline\n"
    code += ligne_variable + "\\\\\n"

    if derivee:
        code += "\\hline\n" + ligne_derivee + "\\\\\n"

    code += "\\hline\n" + ligne_fonction + "\\\\\n"

    code += "\\hline\n\\end{tabvar}\\]\n%" + chaine_originale + "\n"

    return code




# exemple: x;sin(x)::-5;5;0.1:0.01 (variable;expression:valeur initiale;valeur finale;pas:precision d'arrondi).
# exemple: x;h(x):sin(x)+1:-5;0;0.5|0;3;1:0.01 (variable;expression:valeur initiale;valeur finale;pas:precision d'arrondi).

def tabval(chaine = ""):
    u"""Syntaxe:
variable;fonction:expression:valeur initiale;valeur finale;pas:precision d'arrondi

Exemples:
x;\\sin(x)::-5;5;0.1:0.01
x;h(x):sin(x)+1:-5;0;0.5|0;3;1:0.01"""

    chaine_originale = chaine = chaine.strip()
    sequence = chaine.split(":")



    legende = sequence[0].split(";")
    fonction = legende[-1].strip()
    if fonction == "":
        fonction = "f(x)"
    if len(legende) == 1: # on essaie (sommairement) de detecter la variable (en principe, elle est entre parentheses)
        deb = fonction.find("(")
        fin = fonction.find(")")
        if deb == -1:
            variable = "x"
        else:
            variable = fonction[deb+1:fin].strip()
    else:
        variable = legende[0].strip()

    expression = sequence[1].strip()
    if expression == "":
        expression = fonction

    intervalles = sequence[2].split("|")
    valeurs_variable = []
    for intervalle in intervalles:
        intervalle = intervalle.split(";")
        if len(intervalle) == 1:
            valeur_initiale = 0
            valeur_finale = eval(intervalle[0], maths.__dict__)
            pas = 1
        else:
            valeur_initiale = float(eval(intervalle[0], maths.__dict__))
            valeur_finale = float(eval(intervalle[1], maths.__dict__))
            if len(intervalle) == 2:
                pas = 1
            else:
                pas = float(eval(intervalle[2], maths.__dict__))
        nouvelles_valeurs = [x for x in mathlib.pylab.arange(valeur_initiale, valeur_finale, pas)] + [valeur_finale]
        if valeurs_variable and valeurs_variable[-1] == nouvelles_valeurs[0]:
            valeurs_variable.pop()
        valeurs_variable += nouvelles_valeurs

    if len(sequence) == 4:
        precision = float(eval(sequence[3], maths.__dict__))
    else:
        precision = 0.01
    nbr_colonnes = len(valeurs_variable) + 1

    code_variable = "$" + variable + "$"
    code_expression = "$" + fonction + "$"

    expression = traduire_latex(expression)

    for val in valeurs_variable: # on construit le tableau colonne par colonne
        n = max(len(code_variable), len(code_expression))
        code_variable = code_variable.ljust(n) # on justifie avant chaque nouvelle colonne (le code LaTeX sera plus agreable a lire !)
        code_expression = code_expression.ljust(n)

        code_variable += "&$\\nombre{" + str(val).replace(".", ",") + "}$"
        try:
            dict = maths.__dict__.copy()
            dict.update({variable: val})
            evaluation = eval(expression, dict)
            if evaluation >= maths.num_oo:
                code_expression += "&$\\times$"
            elif evaluation <= -maths.num_oo:
                code_expression += "&$\\times$"
            else:
                code_expression += "&$\\nombre{" + str(precision*round(evaluation/precision)).replace(".", ",") + "}$"
        except:
            LIB.print_error()
            code_expression += "&$\\times$"

    code = "\\begin{center}\n\\begin{tabular}{|" + nbr_colonnes*"c|" + "}\n\\hline\n"
    code += code_variable + "\\\\\n"
    code += "\\hline\n"
    code += code_expression + "\\\\\n"
    code += "\\hline\n\\end{tabular}\n\\end{center}\n%" + chaine_originale + "\n"
    return code






#~ # Exemples:
#~ # \frac{x+1}{x-1}:x:-2;2: x+1:-;-1;+: &x-1:-;1;+
#~ # \tan(x):  x:-\pi;\pi:  \sin(x):-\pi;-;0;+;\pi:  &\cos(x):-;-\frac{\pi}{2};+;\frac{\pi}{2};-


#~ def tabsign(chaine = "", cellspace = False):
    #~ chaine_originale = chaine
    #~ chaine = chaine.strip()
    #~ chaine = chaine.replace("-oo", "-\\infty").replace("+oo", "+\\infty")
    #~ if chaine[-1] == ":": # il peut arriver que l'utilisateur mette un delimiteur à la fin (il n'en faut pas en principe)
        #~ chaine = chaine[:-1]

    #~ sequence = chaine.split(":")

    #~ resultat = sequence[0].strip()

    #~ variable = sequence[1].strip()
    #~ donnees_variable = sequence[2].strip()

    #~ # Par defaut, la variable varie entre -oo et +oo
    #~ if donnees_variable == "":
        #~ donnees_variable = ";"
    #~ if donnees_variable[0] == ";":
        #~ donnees_variable = "-\\infty" + donnees_variable
    #~ if donnees_variable[-1] == ";":
        #~ donnees_variable += "+\\infty"

    #~ # Separation de la legende et des autres donnees pour chaque ligne
    #~ expressions = []
    #~ donnees_expressions = []
    #~ for n in xrange(3, len(sequence)):
        #~ if n%2:
            #~ expressions.append(sequence[n].strip())
        #~ else:
            #~ donnees_expressions.append(sequence[n].strip())

    #~ # Legende pour le resultat (produit ou quotient)
    #~ if resultat == "":
        #~ numerateur = []
        #~ denominateur = []
        #~ for expression in expressions:
            #~ print "deb", expression[1]
            #~ if expression[0] == "&":
                #~ liste = denominateur
                #~ expression = expression[1:]
            #~ else:
                #~ liste = numerateur
            #~ if "+" in expression or "-" in expression[1:]: # il s'agit sans doute d'une somme ou d'une difference, il faut donc des parentheses
                #~ liste.append("(" + expression + ")")
            #~ else:
                #~ liste.append(expression)
        #~ if denominateur:
            #~ if len(denominateur) == 1:
                #~ denominateur[0] = denominateur[0][1:-1]
            #~ if len(numerateur) == 1:
                #~ numerateur[0] = numerateur[0][1:-1]
            #~ if not numerateur:
                #~ numerateur = ["1"]
            #~ resultat = "\\frac{%s}{%s}" %("".join(numerateur), "".join(denominateur))
        #~ else:
            #~ resultat = "".join(numerateur)

    #~ # Legende pour la variable
    #~ if variable == "":
        #~ if "x" in resultat:
            #~ variable = "x"
        #~ elif "X" in resultat:
            #~ variable = "X"
        #~ elif "t" in resultat:
            #~ variable = "t"
        #~ elif "y" in resultat:
            #~ variable = "y"
        #~ elif "n" in resultat:
            #~ variable = "n"
        #~ else:
            #~ variable = "x"

    #~ # On recupere la liste de toutes les valeurs de x
    #~ valeurs = set([]) #va contenir toutes les valeurs numeriques
    #~ correspondances = {} # servira a retrouver le code (LaTeX notamment) a partir de la valeur numerique.
    #~ for donnees in donnees_expressions + [donnees_variable]:
        #~ donnees = donnees.split(";")
        #~ for valeur in donnees:
            #~ if not (valeur == "-" or valeur == "+"):
                #print "valeur", valeur
                #~ valeur_num = eval(traduire_latex(valeur), maths.__dict__)

                #~ if valeur_num == -maths.oo: # on ne peut pas laisser l'objet -oo, car il n'est pas hashable
                    #~ valeur_num = None # en python, None se comporte comme -oo pour la relation d'ordre <
                #~ elif valeur_num == maths.oo: # on ne peut pas laisser l'objet +oo, car il n'est pas hashable
                    #~ valeur_num = () # en python, () se comporte comme +oo pour la relation d'ordre <

                #~ valeurs.add(valeur_num)
                #print "val num", valeur_num
                #~ correspondances[valeur_num] = valeur.strip()
    #~ valeurs = list(valeurs)
    #~ valeurs.sort()
    #~ print valeurs


    #~ # On preformate les donnees en vue d'un traitement ulterieur. Ex: "-5;+;&1;-;4" devient [(-5, "0"), "+", (1, "||"), "-", (4, "0")].
    #~ for i in xrange(len(donnees_expressions)):
        #~ donnees = donnees_expressions[i].split(";")
        #~ def traiter_chaine(chaine): # interprete les donnees s'il s'agit de valeurs
            #~ chaine = chaine.strip()
            #~ if chaine == "-" or chaine == "+":
                #~ return chaine
            #~ valeur = "0"
            #~ if chaine[0] == "&":
                #~ chaine = chaine[1:]
                #~ valeur = "||"

            #~ return eval(traduire_latex(chaine), maths.__dict__), valeur


        #~ donnees_expressions[i] = [traiter_chaine(chaine) for chaine in donnees]

    #print  donnees_expressions


    #~ indices_denominateurs = [] # indique que les lignes correspondantes se trouvent au denominateur



    #~ # On initialise le code LaTeX de chaque ligne avec la legende
    #~ n = max(len(chaine) for chaine in expressions + [variable, resultat])
    #~ lignes = ["$" + variable + "$"] # contiendra le code LaTeX pour chaque ligne
    #~ i = 1
    #~ for expression in expressions:
        #~ if expression[0] == "&":
            #~ expression = expression[1:]
            #~ indices_denominateurs.append(i)
        #~ lignes.append("$" + expression + "$")
        #~ i += 1
    #~ lignes.append("$" + resultat + "$")



    #~ # On genere maintenant le code LaTeX correspondant a chaque ligne
    #print valeurs
    #~ nbr_colonnes = 2*len(valeurs)
    #~ for i in xrange(1, nbr_colonnes):
        #~ #print i
        #~ n = max(len(ligne) for ligne in lignes)
        #~ for j in xrange(len(lignes)):
            #~ lignes[j] = lignes[j].ljust(n) + "&"

        #~ if i%2: # on est au niveau d'une valeur
            #~ valeur_num = valeurs[(i-1)//2]
            #~ if valeur_num == maths.oo:
                #~ valeur = "+\\infty"
            #~ elif valeur_num == -maths.oo:
                #~ valeur = "-\\infty"
            #~ else:
                #~ valeur = correspondances[valeur_num]
            #~ lignes[0] += "$" + valeur + "$" # premiere ligne : la variable

        #~ if i%2:
            #~ signe = ""
        #~ else:
            #~ signe = 1 # 1 pour positif, -1 pour negatif


        #~ for j in xrange(1, len(expressions) + 1):
            #~ donnees = donnees_expressions[j - 1]
            #~ valeurs_precedentes = [k for k in xrange(len(donnees)) if isinstance(donnees[k], tuple) and donnees[k][0]<=valeur_num]
            #~ if valeurs_precedentes:
                #~ k = valeurs_precedentes[-1] # cas contient la derniere valeur de la ligne
                #~ if i%2: # on est au niveau d'une valeur
                    #~ if donnees[k][0] == valeur_num:
                        #~ if donnees[k][1] == "||":
                            #~ lignes[j] += "||"
                            #~ signe = "||"
                        #~ else:
                            #~ lignes[j] += "$0$"
                            #~ if not signe:
                                #~ signe = "$0$"
                        #~ if j in indices_denominateurs:
                            #~ signe = "||"
                #~ else: # on est entre deux valeurs
                    #~ lignes[j] += "$" + donnees[k+1] + "$" # le signe qui est juste apres la derniere valeur
                    #~ signe *= (donnees[k+1] == "+" and 1 or -1)
            #~ else: # on est au debut
                #~ if i%2 == 0: # on est entre deux valeurs
                    #~ lignes[j] += "$" + donnees[0] + "$"
                    #~ signe *= (donnees[0] == "+" and 1 or -1)

        #~ if i%2: # derniere ligne : signe du produit ou du quotient
            #~ lignes[-1] += signe
        #~ else:
            #~ lignes[-1] += (signe == 1 and "$+$" or "$-$")


    #~ # Un dernier alignement des colonnes
    #~ n = max(len(ligne) for ligne in lignes)
    #~ for j in xrange(len(lignes)):
        #~ lignes[j] = lignes[j].ljust(n)


    #~ # Et on rassemble les lignes
    #~ if cellspace:
        #~ code = "\\begin{center}\n\\begin{tabular}{|Sc|" + (nbr_colonnes - 1)*"Sc" + "|}\n\\hline\n"
    #~ else:
        #~ code = "\\begin{center}\n\\begin{tabular}{|c|" + (nbr_colonnes - 1)*"c" + "|}\n\\hline\n"
    #~ for ligne in lignes:
        #~ code += ligne + "\\\\\n\\hline\n"
    #~ code += "\\end{tabular}\n\\end{center}\n%" + chaine_originale + "\n"
    #~ return code





def tabsign(chaine = "", cellspace = False):
    u"""Indiquer ligne par ligne le signe de chaque facteur.
La dernière ligne (produit ou quotient) est générée automatiquement.

Exemples:
x:-pi;pi #  sin(x):-pi;-;0;+;pi #  &cos(x):-;-pi/2;+;pi/2;- # tan(x)
x:-2;2 # x+1:-;-1;+ # &x-1:-;1;+

Le symbole '&' en début de ligne indique qu'elle correspond à un dénominateur."""

    chaine_originale = chaine = chaine.strip()
    chaine = chaine.replace("-oo", "-\\infty").replace("+oo", "+\\infty")

    if chaine[-1] == "#": # il peut arriver que l'utilisateur mette un delimiteur à la fin (il n'en faut pas en principe)
        chaine = chaine[:-1]

    sequence = chaine.split("#")

    resultat = sequence[-1].strip()
    if ":" in resultat: #sequence[-1] ne contient pas le résultat
        resultat = ""
    else:
        sequence.pop()

    ligne_variable = sequence.pop(0)
    if ":" in ligne_variable:
        variable, donnees_variable = ligne_variable.split(":")
    elif ";" in ligne_variable:
        variable = ""
        donnees_variable = ligne_variable
    else:
        variable = ligne_variable
        donnees_variable = ""

    variable = variable.strip()
    donnees_variable = donnees_variable.strip()

    # Par defaut, la variable varie entre -oo et +oo
    if donnees_variable == "":
        donnees_variable = ";"
    if donnees_variable[0] == ";":
        donnees_variable = "-\\infty" + donnees_variable
    if donnees_variable[-1] == ";":
        donnees_variable += "+\\infty"

    # Separation de la legende et des autres donnees pour chaque ligne
    expressions = []
    donnees_expressions = []
    for ligne in sequence:
        expression, signe_expression = ligne.split(":")
        expressions.append(expression.strip())
        donnees_expressions.append(signe_expression.strip())

    # Legende pour le resultat (produit ou quotient)
    if resultat == "":
        numerateur = []
        denominateur = []
        for expression in expressions:
#            print "deb", expression[1]
            if expression[0] == "&":
                liste = denominateur
                expression = expression[1:]
            else:
                liste = numerateur
            if "+" in expression or "-" in expression[1:]: # il s'agit sans doute d'une somme ou d'une difference, il faut donc des parentheses
                liste.append("(" + expression + ")")
            else:
                liste.append(expression)
        if denominateur:
            if len(denominateur) == 1 and denominateur[0][0] == "(" and denominateur[0][-1] == ")":
                denominateur[0] = denominateur[0][1:-1]
            if len(numerateur) == 1 and numerateur[0][0] == "(" and numerateur[0][-1] == ")":
                numerateur[0] = numerateur[0][1:-1]
            if not numerateur:
                numerateur = ["1"]
            resultat = "\\frac{%s}{%s}" %("".join(numerateur), "".join(denominateur))
        else:
            resultat = "".join(numerateur)

    # Cas particulier : 'produit' de 1 seul élément -> inutile d'afficher une ligne résultat
    if len(expressions) == 1 and not expressions[0].startswith("&"):
        afficher_resultat = False
    else:
        afficher_resultat = True

    # Legende pour la variable
    if variable == "":
        if "x" in resultat:
            variable = "x"
        elif "X" in resultat:
            variable = "X"
        elif "t" in resultat:
            variable = "t"
        elif "y" in resultat:
            variable = "y"
        elif "n" in resultat:
            variable = "n"
        else:
            variable = "x"

    # On recupere la liste de toutes les valeurs de x
    valeurs = set([]) #va contenir toutes les valeurs numeriques
    correspondances = {} # servira a retrouver le code (LaTeX notamment) a partir de la valeur numerique.
    for donnees in donnees_expressions + [donnees_variable]:
        donnees = donnees.split(";")
        for valeur in donnees:
            valeur = valeur.strip()
            if not (valeur == "-" or valeur == "+"):
                #~ print "valeur", valeur
                valeur_num = eval(traduire_latex(valeur), maths.__dict__)

                if valeur_num == -maths.oo: # on ne peut pas laisser l'objet -oo, car il n'est pas hashable
                    valeur_num = None # en python, None se comporte comme -oo pour la relation d'ordre <
                elif valeur_num == maths.oo: # on ne peut pas laisser l'objet +oo, car il n'est pas hashable
                    valeur_num = () # en python, () se comporte comme +oo pour la relation d'ordre <

                valeurs.add(valeur_num)
                #~ print "val num", valeur_num
                correspondances[valeur_num] = valeur.strip()
    valeurs = list(valeurs)
    valeurs.sort()
    print valeurs


    # On preformate les donnees en vue d'un traitement ulterieur. Ex: "-5;+;&1;-;4" devient [(-5, "0"), "+", (1, "||"), "-", (4, "0")].
    for i in xrange(len(donnees_expressions)):
        donnees = donnees_expressions[i].split(";")
        def traiter_chaine(chaine): # interprete les donnees s'il s'agit de valeurs
            chaine = chaine.strip()
            if chaine == "-" or chaine == "+":
                return chaine
            valeur = "0"
            if chaine[0] == "&":
                chaine = chaine[1:]
                valeur = "||"

            return eval(traduire_latex(chaine), maths.__dict__), valeur


        donnees_expressions[i] = [traiter_chaine(chaine) for chaine in donnees]

    #~ print  donnees_expressions


    indices_denominateurs = [] # indique que les lignes correspondantes se trouvent au denominateur



    # On initialise le code LaTeX de chaque ligne avec la legende
    n = max(len(chaine) for chaine in expressions + [variable, resultat])
    lignes = [convertir_en_latex(variable)] # contiendra le code LaTeX pour chaque ligne
    i = 1
    for expression in expressions:
        if expression[0] == "&":
            expression = expression[1:]
            indices_denominateurs.append(i)
        lignes.append(convertir_en_latex(expression))
        i += 1
    lignes.append(convertir_en_latex(resultat))



    # On genere maintenant le code LaTeX correspondant a chaque ligne
    #~ print valeurs
    nbr_colonnes = 2*len(valeurs)
    for i in xrange(1, nbr_colonnes):
        #print i
        n = max(len(ligne) for ligne in lignes)
        for j in xrange(len(lignes)):
            lignes[j] = lignes[j].ljust(n) + "&"

        if i%2: # on est au niveau d'une valeur
            valeur_num = valeurs[(i-1)//2]
            if valeur_num == maths.oo:
                valeur = "+\\infty"
            elif valeur_num == -maths.oo:
                valeur = "-\\infty"
            else:
                valeur = correspondances[valeur_num]
            valeur = correspondances[valeur_num]

            # On applique un formatage LaTeX à certaines expression :

            lignes[0] += convertir_en_latex(valeur)

        if i%2:
            signe = ""
        else:
            signe = 1 # 1 pour positif, -1 pour negatif


        for j in xrange(1, len(expressions) + 1):
            donnees = donnees_expressions[j - 1]
            valeurs_precedentes = [k for k in xrange(len(donnees)) if isinstance(donnees[k], tuple) and donnees[k][0]<=valeur_num]
            if valeurs_precedentes:
                k = valeurs_precedentes[-1] # cas contient la derniere valeur de la ligne
                if i%2: # on est au niveau d'une valeur
                    if donnees[k][0] == valeur_num:
                        if donnees[k][1] == "||":
                            lignes[j] += "||"
                            signe = "||"
                        else:
                            lignes[j] += "$0$"
                            if not signe:
                                signe = "$0$"
                        if j in indices_denominateurs:
                            signe = "||"
                else: # on est entre deux valeurs
                    lignes[j] += "$" + donnees[k+1] + "$" # le signe qui est juste apres la derniere valeur
                    signe *= (donnees[k+1] == "+" and 1 or -1)
            else: # on est au debut
                if i%2 == 0: # on est entre deux valeurs
                    lignes[j] += "$" + donnees[0] + "$"
                    signe *= (donnees[0] == "+" and 1 or -1)

        if i%2: # derniere ligne : signe du produit ou du quotient
            lignes[-1] += signe
        else:
            lignes[-1] += (signe == 1 and "$+$" or "$-$")


    # Un dernier alignement des colonnes
    n = max(len(ligne) for ligne in lignes)
    for j in xrange(len(lignes)):
        lignes[j] = lignes[j].ljust(n)


    # Cas particulier : 'produit' de 1 seul élément
    if not afficher_resultat:
        lignes.pop()

    # Et on rassemble les lignes
    if cellspace:
        code = "\\begin{center}\n\\begin{tabular}{|Sc|" + (nbr_colonnes - 1)*"Sc" + "|}\n\\hline\n"
    else:
        code = "\\begin{center}\n\\begin{tabular}{|c|" + (nbr_colonnes - 1)*"c" + "|}\n\\hline\n"
    for ligne in lignes:
        code += ligne + "\\\\\n\\hline\n"
    code += "\\end{tabular}\n\\end{center}\n%" + chaine_originale + "\n"
    return code




#2x+1:-;-1/2;+#-3x+2:-;2/3;+
