#!/usr/bin/env python # encoding: utf-8 from .explicable import Explicable from .expression import Expression from .step import Step from .decorators import no_repetition from .polynom import Polynom from .fraction import Fraction from .random_expression import RdExpression __all__ = ['Equation'] class Equation(object): """A calculus expression. Today it can andle only expression with numbers later it will be able to manipulate unknown""" @classmethod def random(self, form="", conditions=[], val_min=-10, val_max=10): """Create a random expression from form and with conditions :param form: the form of the expression (/!\ variables need to be in brackets {}) :param conditions: condition on variables (/!\ variables need to be in brackets {}) :param val_min: min value for generate variables :param val_max: max value for generate variables >>> Equation.random("{a}x + {b} = 0") # doctest:+ELLIPSIS < Equation [..., 0]> >>> Equation.random("{a}x + {b} = _", conditions = ["{a}==2"]) # doctest:+ELLIPSIS < Equation [2 x ...]> """ random_generator = RdExpression(form, conditions) return Equation(random_generator(val_min, val_max)) def __init__(self, exp_str = "", left_poly = Expression([0]), right_poly = Expression([0])): """Create the equation :param exp_str: the equality string which represent the equation :param left_poly: the left polynom of the equation :param right_poly: the right polynom of the equation >>> e = Equation("2x+3 = 4x+5") >>> e < Equation [2 x + 3, 4 x + 5]> >>> Pl = Polynom([1, 2]) >>> Pr = Polynom([3, 4]) >>> e = Equation(left_poly = Pl) >>> e < Equation [2 x + 1, 0]> >>> e = Equation(right_poly = Pr) >>> e < Equation [0, 4 x + 3]> >>> e = Equation(left_poly = Pl, right_poly = Pr) >>> e < Equation [2 x + 1, 4 x + 3]> """ if exp_str: l_part, r_part = exp_str.split("=") self.l_exp = Expression(l_part) self.r_exp = Expression(r_part) else: self.l_exp = left_poly self.r_exp = right_poly self.smpl_each_part() def smpl_each_part(self): """ Simplify left and right part, transform them into polynom and stock them in smpl_*_exp """ self.smpl_l_exp = self.l_exp.simplify() self.smpl_l_exp.steal_history(self.l_exp) self.smpl_r_exp = self.r_exp.simplify() self.smpl_r_exp.steal_history(self.r_exp) try: self.smpl_r_exp = self.smpl_l_exp.conv2poly(self.smpl_r_exp) except AttributeError: pass try: self.smpl_l_exp = self.smpl_r_exp.conv2poly(self.smpl_l_exp) except AttributeError: raise EquationError("None of left and right parts are polynoms. \ Can't use it to make an equation.") # TODO: On pourrait rajouter des tests sur les inconnues |mar. mars 22 10:17:12 EAT 2016 def __str__(self): return str(self.l_exp) + " = " + str(self.r_exp) def __repr__(self): return "< {cls} [{l}, {r}]>".format( cls = str(self.__class__).split('.')[-1][:-2], l = self.l_exp, r = self.r_exp, ) #@no_repetition(lambda x, y: (x[0] == y[0]) & (x[1] == y[1])) @no_repetition() def solve(self): r"""Solve the equation but yielding each steps >>> e = Equation("x + 123 = 0") >>> for i in e.solve(): ... print(" = ".join([str(j) for j in i])) x + 123 = 0 x + 123 - 123 = 0 - 123 x + 123 - 123 = 0 - 123 x + 123 - 123 = -123 x = -123 >>> e = Equation("2x = x + 2") >>> for i in e.solve(): ... print(" = ".join([str(j) for j in i])) 2 x = x + 2 2 x - x = x + 2 - x 2 x - x = x + 2 - x ( 2 - 1 ) x = x - x + 2 x = ( 1 - 1 ) x + 2 x = 2 >>> e = Equation("2x = 1") >>> for i in e.solve(): ... print(" = ".join([str(j) for j in i])) 2 x = 1 2 x \times 2 = 1 \times 2 \frac{ 2 }{ 2 } x = \frac{ 1 }{ 2 } x = \frac{ 1 }{ 2 } >>> e = Equation("2x + 1 = 4x + 2") >>> for i in e.solve(): ... print(" = ".join([str(j) for j in i])) 2 x + 1 = 4 x + 2 2 x + 1 - 1 = 4 x + 2 - 1 2 x + 1 - 1 = 4 x + 2 - 1 2 x + 1 - 1 = 4 x + 2 - 1 2 x = 4 x + 1 2 x - 4 x = 4 x + 1 - 4 x 2 x - 4 x = 4 x + 1 - 4 x ( 2 - 4 ) x = 4 x - 4 x + 1 -2 x = ( 4 - 4 ) x + 1 -2 x = 1 -2 x \times ( -2 ) = 1 \times ( -2 ) \frac{ -2 }{ -2 } x = \frac{ 1 }{ -2 } x = \frac{ -1 }{ 2 } >>> e = Equation("2x + 3x + 1 = 4x + 2") >>> for i in e.solve(): ... print(" = ".join([str(j) for j in i])) 2 x + 3 x + 1 = 4 x + 2 ( 2 + 3 ) x + 1 = 4 x + 2 5 x + 1 = 4 x + 2 5 x + 1 - 1 = 4 x + 2 - 1 5 x + 1 - 1 = 4 x + 2 - 1 5 x + 1 - 1 = 4 x + 2 - 1 5 x = 4 x + 1 5 x - 4 x = 4 x + 1 - 4 x 5 x - 4 x = 4 x + 1 - 4 x ( 5 - 4 ) x = 4 x - 4 x + 1 x = ( 4 - 4 ) x + 1 x = 1 """ yield from self.gene_smpl_steps() if self.smpl_l_exp._coef[0] != 0: eq = Equation( left_poly = self.smpl_l_exp - self.smpl_l_exp._coef[0], right_poly = self.smpl_r_exp - self.smpl_l_exp._coef[0] ) yield from eq.solve() return try: poly_r_part = Polynom([0, self.smpl_r_exp._coef[1]]) except IndexError: pass else: if self.smpl_r_exp._coef[1] != 0: yield from Equation( left_poly = self.smpl_l_exp - poly_r_part, right_poly = self.smpl_r_exp - poly_r_part ).solve() return if self.smpl_l_exp._coef[1] != 1: yield from Equation( left_poly = self.smpl_l_exp / self.smpl_l_exp._coef[1], right_poly = self.smpl_r_exp / self.smpl_l_exp._coef[1] ).solve() return @no_repetition() def gene_smpl_steps(self): r"""Generate simplification steps of the equation >>> e = Equation("2x + 3x + 1 = 4x + 2") >>> e.gene_smpl_steps() # doctest:+ELLIPSIS >>> for i in e.gene_smpl_steps(): ... print(i) [< Step [2, 'x', *, 3, 'x', *, +, 1, +]>, < Step [4, 'x', *, 2, +]>] [< Step [2, 3, +, 'x', *, 1, +]>, < Step [4, 'x', *, 2, +]>] [< Step [5, 'x', *, 1, +]>, < Step [4, 'x', *, 2, +]>] >>> e = Equation("2x + 3x + 1 = 4x + 2") >>> for i in e.gene_smpl_steps(): ... print(" = ".join([str(j) for j in i])) 2 x + 3 x + 1 = 4 x + 2 ( 2 + 3 ) x + 1 = 4 x + 2 5 x + 1 = 4 x + 2 >>> e = Equation("3x / 3 = 5 / 3") >>> for i in e.gene_smpl_steps(): ... print(" = ".join([str(j) for j in i])) 3 \times \frac{ x }{ 3 } = \frac{ 5 }{ 3 } \frac{ x }{ 3 } \times 3 = \frac{ 5 }{ 3 } \frac{ x \times 3 }{ 1 \times 3 } = \frac{ 5 }{ 3 } \frac{ x }{ 1 } = \frac{ 5 }{ 3 } x = \frac{ 5 }{ 3 } """ #yield [Step(self.l_exp), Step(self.r_exp)] for s in Explicable.merge_history( [self.smpl_l_exp, self.smpl_r_exp] ): yield s def solution(self): """Return the solution of the equation :returns: the solution >>> e = Equation("2x + 1 = x + 2") >>> e.solution() 1 >>> e = Equation("2x + 1 = 1") >>> e.solution() 0 >>> e = Equation("1 = 2x + 1") >>> e.solution() 0 >>> e = Equation("3x = 2x") >>> e.solution() 0 >>> e = Equation("3x + 1 = 0") >>> e.solution() < Fraction -1 / 3> >>> e = Equation("6x + 2 = 0") >>> e.solution() < Fraction -1 / 3> """ num = self.smpl_r_exp._coef[0] - self.smpl_l_exp._coef[0] try: denom = self.smpl_l_exp._coef[1] except IndexError: denom = 0 try: denom -= self.smpl_r_exp._coef[1] except IndexError: pass if denom == 0 and num == 0: raise EquationError("All number are solution") elif denom == 0: raise NoSolutionError("This equation has no solution") return Fraction(num, denom).simplify() def is_solution(self, num): """ Tell if a number is a solution. :param num: the number to test >>> e = Equation("2x + 1 = x + 2") >>> e.is_solution(2) False >>> e.is_solution(1) True >>> e = Equation("3x = 2x") >>> e.is_solution(1) False >>> e.is_solution(0) True >>> e = Equation("3x + 1 = 0") >>> e.is_solution(0) False >>> e.is_solution(Fraction(-1, 3)) True >>> e.is_solution(Fraction(-2, 6)) True """ l_part = self.smpl_l_exp.replace_letter(num).simplify() r_part = self.smpl_r_exp.replace_letter(num).simplify() return l_part == r_part class EquationError(Exception): pass class NoSolutionError(EquationError): pass # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del