#!/usr/bin/env python3
# Pyromaths
# Un programme en Python qui permet de créer des fiches d'exercices types de
# mathématiques niveau collège ainsi que leur corrigé en LaTeX.
# Copyright (C) 2006 -- Jérôme Ortais (jerome.ortais@pyromaths.org)
#
# 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
#
#----------------------------------------------------------------------
# Pyromaths : GESTION DES PRIORITES
#----------------------------------------------------------------------
# from Affichage import decimaux
from pyromaths.outils import Arithmetique
[docs]def cherche_classe(calcul, index):
"""**cherche_classe**\ (*calcul*\ , *index*)
Recherche le premier objet polynôme ou fraction dans la chaîne calcul à une
position supérieure ou égale à index, sans expression régulière
:param calcul: le calcul à tester
:type calcul: string
:param index: position à partir de laquelle effectuer le test
:type index: integer
>>> from pyromaths.classes.PolynomesCollege import Polynome
>>> from pyromaths.classes.Fractions import Fraction
>>> from pyromaths.outils import Priorites3
>>> Priorites3.cherche_classe('Polynome([[-4, 2]], "x")-Polynome([[-5, 1], [4, 2], [1, 0]], "x")+Polynome([[6, 0], [5, 1]], "x")', 5)
Polynome([[-5, 1], [4, 2], [1, 0]], "x")
>>> Priorites3.cherche_classe('Polynome([[-4, 2]], "x")+Fraction(3,2)+Polynome([[6, 0], [5, 1]], "x")', 5)
Fraction(3,2)
:rtype: string
"""
classes = ["Polynome(", "Fraction(", "SquareRoot("]
indexes = [calcul.find(classe, index) for classe in classes]
position, i = 0, 1
while i < len(classes):
if 0 <= indexes[i] and (indexes[position] < 0 or indexes[i] < indexes[position]):
position = i
i += 1
if indexes[position] < 0: return None
classe = classes[position]
index = indexes[position]
par, i = 1, index + len(classe) # nombre de parenthèses ouvertes, début de recherche
while par:
if calcul.find("(", i) > 0:
i = min(calcul.find("(", i), calcul.find(")", i))
else:
i = calcul.find(")", i)
if calcul[i] == ")": par += -1
else: par += 1
i += 1
return calcul[index:i]
[docs]def cherche_decimal(calcul, index):
"""**cherche_decimal**\ (*calcul*\ , *index*)
Recherche le premier nombre décimal dans la chaîne calcul à une position
supérieure ou égale à index, sans expression régulière.
Un nombre décimal peut être entouré de parenthèses s'il a un signe, ou avoir
un signe s'il est en début de chaîne.
Cette fonction est 130 fois plus rapide qu'avec une expression régulière.
:param calcul: le calcul à tester
:type calcul: string
:param index: position à partir de laquelle effectuer le test
:type index: integer
>>> from pyromaths.outils import Priorites3
>>> p = '-Polynome([[-4, 2]], "x")*6**2+3'
>>> Priorites3.cherche_decimal(p, 1)
4
>>> p = '-6*(-11)*(-5)'
>>> Priorites3.cherche_decimal(p, 1)
6
>>> Priorites3.cherche_decimal(p, 0)
6
**TODO :** vérifier ces deux derniers exemples. Je pense que je devrais récupérer -6
:rtype: string
"""
end = len(calcul)
while index <= end:
try:
int(calcul[index])
except:
index += 1
else:
break
if index > end: return None
while end > index:
try:
float(calcul[index:end])
except ValueError:
end -= 1
else:
break
if index > 1 and (calcul[index - 2:index] == "(+" or calcul[index - 2:index] ==
"(-") :
if end < len(calcul) and calcul[end] == ")":
index -= 2
end += 1
else:
index -= 1
return calcul[index:end]
def cherche_string(calcul, index):
apostrophes = ["'", "\""]
indexes = [calcul.find(apostrophe, index) for apostrophe in apostrophes]
position, i = 0, 1
while i < len(apostrophes):
if 0 <= indexes[i] and (indexes[position] < 0 or indexes[i] < indexes[position]):
position = i
i += 1
if indexes[position] < 0: return None
apostrophe = apostrophes[position]
index = indexes[position] + 1
i = calcul.find(apostrophe, index)
return calcul[index:i]
[docs]def cherche_operateur(calcul, index):
"""**cherche_operateur**\ (*calcul*\ , *min_i*)
Recherche le premier opérateur +, -, \*, /, ^ dans la chaîne calcul à une position
supérieure ou égale à min_i, sans expression régulière.
:param calcul: le calcul à tester
:type calcul: string
:param min_i: position à partir de laquelle effectuer le test
:type min_i: integer
>>> from pyromaths.outils import Priorites3
>>> Priorites3.cherche_operateur('-Polynome([[-4, 2]], "x")*6**2+3', 1)
-
>>> p = '-6*(-11)*(-5)'
>>> Priorites3.cherche_operateur(p, 1)
*
>>> Priorites3.cherche_operateur(p, 0)
-
:rtype: string
"""
operateur = ['**', '+', '-', '*', '/', '^']
l = []
for op in operateur:
try:
l.append(calcul[index:].index(op) + index)
except:
l.append(None)
min_i = 0
for i in range (1, len(l)):
if l[i] is not None and (l[min_i] is None or l[i] < l[min_i]):
min_i = i
if l[min_i] != None:
return operateur[min_i]
else:
return None
[docs]def split_calcul(calcul):
"""**split_calcul**\ (*calcul*)
Partitionne la chaîne de caractères pour obtenir une liste d'opérateurs,
de parenthèses, de polynômes et de nombres
:param calcul: le calcul à traiter
:type calcul: string
>>> from pyromaths.outils import Priorites3
>>> Priorites3.split_calcul('-Polynome([[-4, 2]], "x")*6**2+3')
['-', 'Polynome([[-4, 2]], "x")', '*', '6', '**', '2', '+', '3']
>>> Priorites3.split_calcul('-6*(-11)*(-5)')
['-', '6', '*', '(-11)', '*', '(-5)']
>>> Priorites3.split_calcul("Polynome('Fraction(1,3)x')+Polynome('x')")
["Polynome('Fraction(1,3)x')", '+', "Polynome('x')"]
**TODO :** vérifier ce dernier exemple. Je pense que je devrais récupérer -6
:rtype: list
"""
l = []
findings = (cherche_classe, cherche_string, cherche_decimal) # , cherche_operateur)
for finding in findings:
nb = finding(calcul, 0)
while nb:
l.extend(calcul.partition(nb))
l[-3] = l[-3].strip()
l[-2] = l[-2].strip()
l[-1] = l[-1].strip()
if l[-3] and l[-2]:
l0 = split_calcul(l[-3])
if l0:
l0.extend(l[-2:])
l = l[:-3]
l.extend(l0)
elif not l[-3]:
l.pop(-3)
calcul = l[-1]
nb = finding(calcul, 0)
l = l[:-1]
if calcul: l.append(calcul)
while l and not l[0]:
l.pop(0)
return l
[docs]def EstNombre(value):
"""**EstNombre**\ (*value*)
Teste si `value` est une valeur, c'est à dire un entier, un réel, un polynôme, une fraction, une racine carrée.
Attention : (-11) est considéré comme un nombre
:param value: la valeur à traiter
:type value: string
>>> from pyromaths.outils import Priorites3
>>> Priorites3.EstNombre('-15')
True
>>> Priorites3.EstNombre('Polynome([[-4, 2]], "x")')
True
:rtype: list
"""
from pyromaths.classes.PolynomesCollege import Polynome
from pyromaths.classes.Fractions import Fraction
from pyromaths.classes.SquareRoot import SquareRoot
classe = cherche_classe(value, 0)
if classe:
if classe != value: return False
else: return True
#===========================================================================
# if cherche_operateur(value, 1):
# return False
#===========================================================================
try:
return isinstance(eval(value), (float, int, Polynome, Fraction, SquareRoot))
except:
return False
[docs]def splitting(calcul):
"""**splitting**\ (*calcul*)
Partitionne la chaîne de caractères pour obtenir une liste d'opérateurs,
de parenthèses, de polynômes et de nombres puis arrange la liste des
opérandes et opérateurs
:param calcul: le calcul à traiter
:type calcul: string
>>> from pyromaths.outils import Priorites3
>>> Priorites3.splitting('-Polynome([[-4, 2]], "x")*6**2+3')
['-', 'Polynome([[-4, 2]], "x")', '*', '6', '**', '2', '+', '3']
>>> Priorites3.splitting('-6*(-11)*(-5)')
['-6', '*', '(-11)', '*', '(-5)']
>>> Priorites3.splitting("Fraction(1,7)x^2-Fraction(3,8)x-1")
['Fraction(1,7)', 'x', '^', '2', '-', 'Fraction(3,8)', 'x', '-', '1']
>>> Priorites3.splitting('-7**2')
['-', '7', '**', '2']
:rtype: list
"""
if calcul == "": return []
l = split_calcul(calcul)
if l[0] == "+" and len(l) > 1: l[0] += l.pop(1)
elif l[0] == "-" and len(l) > 1 and l[1][0] in "0123456789":
if len(l)==2 or (len(l)>2 and l[2]!="**" and l[2]!="^"): l[0] += l.pop(1)
j = 0
while j < len(l):
op, i, t = l[j], 0, []
if op == "**": j += 1
else:
if not EstNombre(op):
while i < len(op):
if op[i:i + 2] == "**":
t.append("**")
i += 2
else:
t.append(op[i])
i += 1
l[j:j + 1] = t
j += len(t)
else: j += 1
# Recherche les erreurs d'écriture du style 3+-5 à la place de 3+(-5)
index = 0
while l[index:].count('-'):
i = l.index('-', index)
if i and l[i - 1][-1] in '+-*/^':
del l[i]
if l[i][0] == '-': l[i] == l[i][1:]
else:l[i] = '(-%s)' % l[i]
index = i + 1
for dummy in range(l.count('\'')):
l.remove('\'')
for dummy in range(l.count('"')):
l.remove('"')
return l
[docs]def recherche_parentheses(calcul):
"""**recherche_parentheses**\ (*calcul*)
Recherche les premières parenthèses (éventuellement intérieures) dans une expression
:param calcul: le calcul à traiter
:type calcul: list
>>> from pyromaths.outils import Priorites3
>>> Priorites3.recherche_parentheses(['-6', '*', '(-11)', '*', '(-5)'])
None
>>> Priorites3.recherche_parentheses(['-9', '-', '6', '*', '(', '(-2)', '-', '4', ')'])
(4, 9)
:rtype: tuple
"""
if calcul.count("("):
debut = calcul.index("(")
if calcul[debut + 1:].count(")"): fin = calcul[debut:].index(")") + debut
else: return None
for dummy in range(calcul[debut + 1:fin].count("(")):
debut += calcul[debut + 1:].index("(") + 1
return (debut, fin + 1)
else:
return None
[docs]def recherche_puissance(calcul):
"""**recherche_puissance**\ (*calcul*)
Recherche la première occurrence d'une puissance dans une liste de
calculs. Elle doit être entourée de deux nombres.
:param calcul: le calcul à traiter
:type calcul: list
>>> from pyromaths.outils import Priorites3
>>> c = ['-6', '**', '(-11)', '*', '(-5)']
>>> Priorites3.recherche_puissance(c)
(0, 3)
>>> p = ['-', 'Polynome([[-4, 2]], "x")', '**', '2', '+', '3']
>>> Priorites3.recherche_puissance(p)
(1, 4)
:rtype: tuple
"""
if calcul.count("**"):
i = calcul.index("**")
# Si le calcul commence par '**', il n'y a rien à faire
if i: return (i - 1, i + 2)
return None
[docs]def recherche_operation(calcul, op, l_interdit, debut=0):
"""**recherche_operation**\ (*calcul*\ , *op*\ , *l_interdit*\ [, *debut*])
Recherche la première occurrence de l'opération op dans la liste de
calculs list. L'opérateur doit être entouré de deux nombres, le premier ne
doit pas être précédé d'un opérateur inclus dans la liste l_interdit.
L'opérateur à trouver doit être avant la position debut (si debut est non
nul).
:param calcul: le calcul à traiter
:type calcul: list
:param op: l'opérateur à chercher
:type op: string
:param l_interdit: liste des opérateurs à ignorer, pour respecter les priorités
:type l_interdit: list
:param debut: À partir d'où chercher l'opérateur dans l'expression
:type debut: integer
>>> from pyromaths.outils import Priorites3
>>> c = ['-6', '**', '(-11)', '*', '(-5)']
>>> Priorites3.recherche_operation(c, "*", ("**", "/"))
None
>>> c = ['-9', '-', '6', '*', '2', '-', '4']
>>> Priorites3.recherche_operation(c, "*", ("**", "/"))
3
:rtype: integer
"""
ind, fin = 0, len(calcul)
for dummy in range(calcul.count(op)):
ind = calcul.index(op, ind)
if ind == 1 and ind < fin - 1 and EstNombre(calcul[ind - 1]) and\
EstNombre(calcul[ind + 1]):
if debut: debut = min(debut, ind)
else: debut = ind
break
elif ind > 1 and ind < fin - 1 and EstNombre(calcul[ind - 1]) and\
EstNombre(calcul[ind + 1]) and calcul[ind - 2] not in l_interdit:
if debut: debut = min(debut, ind)
else: debut = ind
break
else:
ind += 1
if debut: return debut
else: return None
[docs]def recherche_fin_operation(calcul, op, l_interdit, debut):
"""**recherche_fin_operation**\ (*calcul*\ , *op*\ , *l_interdit*\ , *debut*)
Recherche la fin de la suite d'opération @op situé à la position @debut
de la liste de calculs @calcul, en vérifiant que cet enchaînement n'est pas
suivi par un opérateur compris dans @l_interdit.
:param calcul: le calcul à traiter
:type calcul: string
:param op: l'opérateur à chercher
:type op: string
:param l_interdit: liste des opérateurs à ignorer, pour respecter les priorités
:type l_interdit: list
:param debut: À partir d'où chercher l'opérateur dans l'expression
:type debut: integer
>>> from pyromaths.outils import Priorites3
>>> c = ['-9', '-', '6', '*', '2', '*', '5', '-', '4']
>>> Priorites3.recherche_fin_operation(c, "*", ("**", "/"),0)
2
>>> Priorites3.recherche_fin_operation(c, "*", ("**", "/"),4)
6
:rtype: integer
"""
index, fin = debut + 2, len(calcul)
while index < fin - 1 and calcul[index] == op and EstNombre(calcul[index + 1]):
index += 2
if index < fin and calcul[index] in l_interdit:
index -= 2
if index == debut: return None
return index
[docs]def recherche_produit(calcul):
"""**recherche_produit**\ (*calcul*)
Recherche la première occurrence d'une multiplication ou divisions dans
une liste de calculs. Elle doit être précédée d'un nombre qui ne doit pas
être précédé d'une puissance ou d'une division, ni suivie d'un exposant.
:param calcul: le calcul à traiter
:type calcul: list
>>> from pyromaths.outils import Priorites3
>>> c = ['-9', '-', '6', '*', '2', '*', '5', '-', '4']
>>> Priorites3.recherche_produit(c)
(2, 7)
:rtype: tuple
"""
debut = recherche_operation(calcul, "*", ("**", "/"))
debut = recherche_operation(calcul, "/", ("**", "/"), debut)
if debut is not None:
fin = recherche_fin_operation(calcul, calcul[debut], ("**"), debut)
if fin is not None: return (debut - 1, fin)
return None
[docs]def recherche_somme(calcul):
"""**recherche_somme**\ (*calcul*)
Recherche la première occurrence d'une addition ou soustraction dans une
liste de calculs. Elle doit être précédée d'un nombre qui ne doit pas être
précédé d'une puissance, d'une multiplication, d'une division ou d'une
soustraction, ni suivie d'un exposant, d'une multiplaication ou d'une
division.
>>> from pyromaths.outils import Priorites3
>>> Priorites3.recherche_somme(['1', '-', '2', '-', '3'])
(0, 5)
>>> Priorites3.recherche_somme(['Polynome("x+1")', '-', 'Polynome("x+2")', '-', 'Polynome("x+3")'])
(0, 5)
:param calcul: le calcul à traiter
:type calcul: list
:rtype: tuple
"""
debut = recherche_operation(calcul, "+", ("**", "*", "/", "-"))
debut = recherche_operation(calcul, "-", ("**", "*", "/", "-"), debut)
if debut is not None:
fin = recherche_fin_operation(calcul, calcul[debut], ("**", "*", "/"),
debut)
if fin is not None: return (debut - 1, fin)
return None
[docs]def recherche_neg(calcul):
"""**recherche_neg**\ (*calcul*)
Recherche la première occurence d'un opposé `-(-5)` ou `-Polynome` ou
d'une écriture du style `+(-5)` dans une liste de calculs qui ne doit pas être
suivie d'un exposant.
:param calcul: le calcul à traiter
:type calcul: list
:rtype: tuple
"""
ind, debut, fin = 0, None, len(calcul)
for dummy in range(calcul.count("-")):
ind = calcul.index("-", ind)
if ind < fin - 1 and EstNombre(calcul[ind + 1]):
if calcul[ind + 1][0] == "(" or ind == 0: # or isinstance(n, Polynome):
# or (isinstance(n, Polynome) and (len(n)>1 or n.monomes[0][0]<0)):
debut = ind
break
else:
ind += 1
for dummy in range(calcul[ind + 1:].count("+")):
ind = calcul.index("+", ind)
if ind < fin - 1 and EstNombre(calcul[ind + 1]) and calcul[ind + 1][0] == "(":
if debut is not None: debut = min(debut, ind)
else: debut = ind
break
else:
ind += 1
if debut is not None and (debut + 2 == fin or (debut + 2 < fin and calcul[debut + 2] != "**")):
return (debut, debut + 2)
else:
return None
[docs]def effectue_calcul(calcul):
"""**effectue_calcul**\ (*calcul*)
Effectue une étape du calcul en respectant les priorités
:param calcul: le calcul à traiter
:type calcul: list
>>> from pyromaths.outils import Priorites3
>>> Priorites3.effectue_calcul(['-5', '-', '(', '(-6)', '-', '1', '+', '(-3)', ')', '*', '(-1)'])
['-5', '-', '(', '-7', '-', '3', ')', '*', '(-1)']
>>> Priorites3.effectue_calcul(['-5', '-', '(', '-7', '-', '3', ')', '*', '(-1)'])
['-5', '-', '-10', '*', '(-1)']
>>> Priorites3.effectue_calcul(['-5', '-', '-10', '*', '(-1)'])
['-5', '-', '10']
>>> Priorites3.effectue_calcul(['-5', '-', '10'])
['-15']
>>> Priorites3.effectue_calcul(['-', 'Polynome("x-1")', '*', '2'])
['-', '(', 'Polynome([[2, 1]], "x", 0)', '+', 'Polynome([[-2, 0]], "x", 0)', ')']
>>> Priorites3.effectue_calcul(['4', '*', 'Polynome("x+1")', '**', '2'])
['4', '*', '(', 'Polynome([[1, 1]], "x", 0)', '**', '2', '+', '2', '*', 'Polynome([[1, 1]], "x", 0)', '*', 'Polynome([[1, 0]], "x", 0)', '+', 'Polynome([[1, 0]], "x", 0)', '**', '2', ')']
:rtype: list
"""
from pyromaths.classes.PolynomesCollege import Polynome
from pyromaths.classes.Fractions import Fraction
from pyromaths.classes.SquareRoot import SquareRoot
serie = (recherche_parentheses, recherche_puissance, recherche_produit,
recherche_neg, recherche_somme)
result, post, break_somme = [], "", False
for recherche in serie:
if break_somme and recherche == recherche_somme: break
if calcul: test = recherche(calcul)
else: test = None
while test:
pre = calcul[:test[0]]
calc = calcul[test[0]:test[1]]
post = calcul[test[1]:]
# On essaie d'utiliser les priorités sur la première partie du
# calcul pour raccourcir la résolution de l'expression
if test != recherche_neg and pre:
# Si on simplifie les écritures, on n'essaie pas les sommes
tmp = effectue_calcul(pre)
if tmp and result and result[-1] not in "+-**/(" and tmp[0][0]\
not in "+-*/)":
# un signe + si l'expression en a besoin
result.append("+")
# On ajoute le résultat ou l'expression
if tmp: result.extend(tmp)
else: result.extend(pre)
else:
result.extend(pre)
if recherche == recherche_parentheses:
# On supprime les parenthèses autour de l'expression
calc = calc[1:-1]
# On réduit directement les calculs numériques dans une
# expression littérale
"Effectue les calculs entre parenthèses et remet les parenthèses si l'expression est de longueur supérieure à 1"
sol = effectue_calcul(calc)
if len(sol) > 1:
sol.insert(0, "(")
sol.append(")")
elif sol and isinstance(eval(sol[0]), (int, float, Fraction)) and post and post[0] == "*" and \
_('Polynome(') in post[1] and len(eval(post[1])) == 1 and eval(post[1])[0][0] == 1:
# Sans doute une factorisation de sommes de polynômes
sol = [repr(eval(sol[0]) * eval(post[1]))]
post = post[2:]
# Suppression des parenthèses autour de ((9.0-80.0)*Polynome("x")) devenu (Polynome("-71.0x"))
if post and result and post[0] == ")" and result[-1] == "(" :
result, post = result[:-1], post[1:]
else:
if recherche == recherche_somme:
# Permet les cas 1 + Fraction(1, 2) + 1
# ou 3 + Polynome("5x") + 4
frac, poly, nombres = False, False, []
for i in range(0, len(calc), 2):
nombres.append(eval(calc[i]))
if isinstance(nombres[-1], Fraction): frac = True
elif isinstance(nombres[-1], Polynome): poly, var, details = True, nombres[-1].var, nombres[-1].details
if poly: nombres = [(Polynome([[i, 0]], var, details) , i)[isinstance(i, Polynome)] for i in nombres]
elif frac: nombres = [(Fraction(i, 1), i)[isinstance(i, Fraction)] for i in nombres]
if poly: classe = Polynome
elif frac: classe = Fraction
if poly or frac:
if calc[1] == '+': operation = classe.__add__
else: operation = classe.__sub__
if isinstance(nombres[0], (int, float)):
sol = operation(classe(nombres[0]), *nombres[1:])
else:
sol = operation(nombres[0], *nombres[1:])
else: sol = eval("".join(calc))
elif recherche == recherche_produit and calc[1] == "*":
frac, poly, nombres = False, False, []
for i in range(0, len(calc), 2):
nombres.append(eval(calc[i]))
if isinstance(nombres[-1], Fraction): frac = True
elif isinstance(nombres[-1], Polynome): poly, var, details = True, nombres[-1].var, nombres[-1].details
if poly: nombres = [(Polynome([[i, 0]], var, details) , i)[isinstance(i, Polynome)] for i in nombres]
elif frac: nombres = [(Fraction(i, 1), i)[isinstance(i, Fraction)] for i in nombres]
if poly: classe = Polynome
elif frac: classe = Fraction
if poly or frac:
if isinstance(nombres[0], (int, float)):
sol = classe.__mul__(classe(nombres[0]), *nombres[1:])
else:
sol = classe.__mul__(nombres[0], *nombres[1:])
else: sol = eval("".join(calc))
elif recherche == recherche_puissance:
sol = eval('(' + calc[0] + ')**' + calc[2])
else:
sol = eval("".join(calc))
if isinstance(sol, str): sol = splitting(sol)
elif isinstance(sol, (int, float)): sol = [str(sol)]
elif isinstance(sol, (Polynome, Fraction, SquareRoot)): sol = [repr(sol)]
else :
raise ValueError(_(u"Le résultat %s a un format inattendu") % sol)
if recherche == recherche_neg and (pre or result):
# Ajoute le "+" ou sépare le "-":
# "1-(-9)" => "1 + 9" et "1+(-9)" => "1 - 9"
if sol[0][0] == "-": sol = ["-", sol[0][1:]]
else: sol = ["+", sol[0]]
#===================================================================
# if recherche == recherche_produit and len(sol) > 1:
# # Ajoute les parenthèses si produit précédé d'un "-" ou "*"
# # ou suivi d'un "*"
#===================================================================
if len(sol) > 1 and sol[0] != "(" and sol[-1] != ")":
# Ajoute les parenthèses si le résultat est précédé d'un "-" ou "*"
# ou suivi d'un "*"
if (result and result[-1] in "*-") or (pre and pre[-1] in "*-") or (post and post[0] == "*"):
sol.insert(0, "(")
sol.append(")")
# Si @sol est négatif et @result se termine par un "+", on l'enlève
if result and result[-1] == "+" and sol and sol[0][0] == "-":
result[-1] = "-"
sol[0] = sol[0].lstrip("-")
result.extend(sol)
calcul = post
if calcul: test = recherche(calcul)
else: test = None
if post and recherche == recherche_neg: break_somme = True
result.extend(post)
if not result: result = calcul
return result
[docs]def priorites(calcul):
r"""**priorites**\ (*calcul*)
Effectue un enchaînement d'opérations contenues dans calcul en
respectant les priorités et en détaillant les étapes.
:param calcul: le calcul à traiter
:type calcul: string
>>> from pyromaths.outils import Priorites3
>>> Priorites3.priorites('-1+5-(-5)+(-6)*1')
[['4', '+', '5', '-', '6'], ['9', '-', '6'], ['3']]
>>> Priorites3.priorites('-5**2+6')
[['-', '25', '+', '6'], ['-25', '+', '6'], ['-19']]
>>> Priorites3.priorites('Polynome([[Fraction(6, 7), 0]], "x")*Polynome([[Fraction(1,3), 1], [1,0]], "x")')
[['Polynome([[Fraction(6, 21, "s"), 1]], "x", 0)', '+', 'Polynome([[Fraction(6, 7), 0]], "x", 0)'], ['Polynome([[Fraction(2, 7), 1], [Fraction(6, 7), 0]], "x", 0)']]
>>> Priorites3.priorites('-Fraction(-6,1)/Fraction(-4,1)')
[['-', '(', 'Fraction(-6, 1)', '*', 'Fraction(1, -4)', ')'], ['-', 'Fraction("2*-3", "2*-2", "s")'], ['Fraction(-3, 2)']]
:rtype: list
"""
calcul = splitting(calcul)
#calcul = split_calcul(calcul)
solution = []
while len(calcul) > 1:
s = effectue_calcul(calcul)
if s:
if s == calcul: s, solution[0]
solution.append(s)
calcul = list(s) # dissocie calcul et s
if 'Polynome(' in calcul[0][:9]:
from pyromaths.classes.PolynomesCollege import Polynome
from pyromaths.classes.Fractions import Fraction
p = eval(calcul[0])
p = p.nreduction()
while (solution and repr(p) != solution[-1][0]) or (not solution and repr(p) != calcul[0]):
solution.append([repr(p)])
p = p.nreduction()
if 'Fraction(' in calcul[0][:9]:
from pyromaths.classes.Fractions import Fraction
from pyromaths.classes.SquareRoot import SquareRoot
f = eval(calcul[0])
f = f.traitement(True)
while (solution and repr(f) != solution[-1][0]) or (not solution and repr(f) != calcul[0]):
solution.append([repr(f)])
# si la fraction est écrite sous la forme d'un entier, c'est fini
if isinstance(f, str): f = eval(f)
if isinstance(f, int): break
f = f.traitement(True)
return solution
[docs]def texifystring(calcul):
r"""**texify**\ (*calcul*)
convertit la chaîne de caractères en chaîne au format TeX.
"""
return texify([splitting(calcul)])[0]
[docs]def texify(liste_calculs):
r"""**texify**\ (*liste_calculs*)
Convertit la liste de chaînes de caractères `liste_calculs` contenant
des polynômes en liste de chaînes de caractères au format TeX
**TODO :** intégrer cela dans :mod:`outils.Affichage` et gérer l'ensemble des
classes de Pyromaths.
:param calcul: le calcul à traiter
:type calcul: string
>>> from pyromaths.outils import Priorites3
>>> from pyromaths.classes.PolynomesCollege import Polynome
>>> l = [['4', '+', '5', '-', '6'], ['9', '-', '6'], ['3']]
>>> Priorites3.texify(l)
['4+5-6', '9-6', '3']
>>> Priorites3.texify(Priorites3.priorites('(-7)+8-Polynome([[-4, 1], [-9, 2], [-5, 0]], "x")'))
['1-\\left( -4\\,x-9\\,x^{2}-5\\right) ', '1+4\\,x+9\\,x^{2}+5', '9\\,x^{2}+4\\,x+6']
>>> Priorites3.texify([['Fraction(5,6)', '**', '2']])
['\\left( \\dfrac{5}{6} \\right) ^{2}']
:rtype: list
"""
from pyromaths.classes.PolynomesCollege import Polynome
from pyromaths.classes.Fractions import Fraction
from pyromaths.classes.SquareRoot import SquareRoot
from .Affichage import decimaux
ls = []
enluminures = {'indice': r'_{', 'cancel':r'\cancel{'}
isEnlumine = {'indice': False, 'cancel':False}
for calcul in liste_calculs:
if isinstance(calcul, str): calcul = splitting(calcul)
s = ""
puiss = 0
for index in range(len(calcul)):
el = calcul[index]
for cle in list(isEnlumine.keys()):
if isEnlumine[cle] and (not isinstance(el, list) or el[1] != cle):
s += '}'
isEnlumine[cle] = False
if isinstance(el, list):
if not isEnlumine[el[1]]:
s += enluminures[el[1]]
isEnlumine[el[1]] = True
el = el[0]
if el[:9] == "Polynome(":
# Doit-on entourer ce polynôme de parenthèses ?
p = eval(el)
if index + 1 < len(calcul): q = calcul[index + 1]
else: q = ""
""" 3 cas :
* {*,-}(2x+3) ou {*,-}(-2x)
* (2x+3)*...
* (2x+1)**2"""
if type(p.monomes[0][0])==str:
a=eval(priorites(p.monomes[0][0])[0][-1])
else:
a=p.monomes[0][0]
if (s and s[-1] in "*-" and (len(p) > 1 or a < 0)) \
or (q and q == "*" and len(p) > 1) \
or ((len(p) > 1 or (a != 1 and p.monomes[0][1] > 0) or \
a < 0 or \
(a != 1 and isinstance(a, Fraction) and a.d != 1)) and q and q == "**"):
s += "(" + str(p) + ")"
elif s and s[-1] == "+" and type(a) == int and a < 0:
s = s[:-1]
s += str(p)
else:
s += str(p)
elif el[:9] == "Fraction(":
# Doit-on entourer cette fraction de parenthèses ?
p = splitting(el[9:-1])
# Gère le cas ou la fraction comprend des objets SquareRoot
t = [""]
for i in range(len(p)):
if p[i] == ',': t.append("")
else: t[-1] += p[i]
p = t
if len(p) == 2:
# texfrac = str(Fraction(eval(p[0]), eval(p[1])))
texfrac = str(Fraction(p[0], p[1]))
else:
texfrac = str(Fraction(p[0], p[1], p[2]))
if index + 1 < len(calcul): q = calcul[index + 1]
else: q = ""
if (eval(p[0]) < 0 or p[1] != "1") and q == "**":
s += "( " + texfrac + " )"
else:
s += texfrac
elif el[:11] == "SquareRoot(":
p = eval(el)
if index + 1 < len(calcul): q = calcul[index + 1]
else: q = ""
""" 3 cas :
* {*,-}(2x+3) ou {*,-}(-2x)
* (2x+3)*...
* (2x+1)**2"""
if (s and s[-1] in "*-" and (len(p) > 1 or p[0][0] < 0)) \
or (q and q == "*" and len(p) > 1) \
or ((len(p) > 1 or (p[0][0] != 1 and p[0][1] > 0) or \
p[0][0] < 0 or \
(p[0][0] != 1 and isinstance(p[0][0], Fraction) and p[0][0].d != 1)) and q and q == "**"):
s += "(" + str(p) + ")"
elif s and s[-1] == "+" and p[0][0] < 0:
s = s[:-1]
s += str(p)
else:
s += str(p)
elif EstNombre(el):
if index + 1 < len(calcul): q = calcul[index + 1]
else: q = ""
if el[0] == "(": s += "(" + decimaux(el[1:-1]) + ")"
elif el[0] == '-' and ((s and s[-1] in "+/*-") \
or (q and q == "**")):
s += "(" + decimaux(el) + ")"
else: s += decimaux(el)
elif el == "**":
s += "**{"
puiss = 1
elif el == "(":
if puiss: puiss += 1
s += "("
elif el == ")":
if puiss: puiss -= 1
s += ")"
else :
# "+", "-", "*", "/"
s += el
if puiss == 1 and s[-1] != r"{":
puiss = 0
s += "}"
for cle in list(isEnlumine.keys()):
if isEnlumine[cle] and (not isinstance(el, list) or el[1] != cle):
s += '}'
s = s.replace("**{", "^{")
s = s.replace("(", "\\left( ")
s = s.replace(")", "\\right) ")
s = s.replace("\\left\\left", "\\left ")
s = s.replace("\\right\\right", "\\right ")
s = s.replace("*", "\\times ")
s = s.replace("/", "\\div ")
if not ls or s != ls[-1]:
ls.append(s)
return ls
[docs]def plotify(calcul):
r"""**plotify**\ (*calcul*)
Convertit la chaîne de caractères `calcul` contenant
des polynômes une chaîne de caractères au format psplot
**TODO :** intégrer cela dans :mod:`outils.Affichage` et gérer l'ensemble des
classes de Pyromaths.
:param calcul: le calcul à traiter
:type calcul: string
>>> from pyromaths.outils import Priorites3
>>> Priorites3.plotify('Polynome([[Fraction(-5, 192), 4], [Fraction(2, 96), 3], [Fraction(41, 48), 2], [Fraction(-7, 12), 1], [-4, 0]], "x", False)')
-5/192*x^4+2/96*x^3+41/48*x^2-7/12*x^1-4
:rtype: str
"""
from pyromaths.classes.PolynomesCollege import Polynome
from pyromaths.classes.Fractions import Fraction
from .Affichage import decimaux
if isinstance(calcul, str): calcul = splitting(calcul)
s = ""
puiss = 0
for i in range(len(calcul)):
el = calcul[i]
if el[:9] == "Polynome(":
# Doit-on entourer ce polynôme de parenthèses ?
p = eval(el)
if i + 1 < len(calcul): q = calcul[i + 1]
else: q = ""
""" 3 cas :
* {*,-}(2x+3) ou {*,-}(-2x)
* (2x+3)*...
* (2x+1)**2"""
res = []
for m in p.monomes:
texte = ''
if isinstance(m[0], Fraction): texte = str(m[0].n) + "/" + str(m[0].d)
elif isinstance(m[0], (float, int)): texte = str(m[0])
if m[1]: texte += "*x^%s" % m[1]
res.append(texte)
t = "+".join(res)
t = t.replace("+-", "-")
if (s and s[-1] in "*-" and (len(p) > 1 or p.monomes[0][0] < 0)) \
or (q and q == "*" and len(p) > 1) \
or ((len(p) > 1 or p.monomes[0][0] != 1) and q and q == "**"):
s += "(" + t + ")"
elif s and s[-1] == "+" and p.monomes[0][0] < 0:
s = s[:-1]
s += t
else:
s += t
elif el[:9] == "Fraction(":
# Doit-on entourer cette fraction de parenthèses ?
p = el[9:-1].split(",")
if len(p) == 2:
texfrac = p[0] + "/" + p[1]
else:
raise ValueError(_(u'On ne devrait pas rencontrer de fraction non simplifiée ici'))
if i + 1 < len(calcul): q = calcul[i + 1]
else: q = ""
if (eval(p[0]) < 0 or p[1] != "1") and q and q == "**":
s += "( " + texfrac + " )"
else:
s += texfrac
elif EstNombre(el):
if el[0] == "(": s += "(" + decimaux(el[1:-1]) + ")"
else: s += decimaux(el)
elif el == "**":
s += "^("
puiss += 1
elif el == "(":
if puiss: puiss += 1
s += "("
elif el == ")":
if puiss: puiss -= 1
s += ")"
else :
# "+", "-", "*", "/"
s += el
if puiss == 1 and s[-1] != "":
puiss = 0
s += ")"
s = s.replace("**{", "^(")
s = s.replace(r"\,", "*")
return s