Adapt Fraction for explain and new logic and verify doctest. Need to adapt unittest

This commit is contained in:
Lafrite 2015-02-27 11:43:20 +01:00
parent d7a2df190f
commit 769666892f

View File

@ -4,13 +4,15 @@
from .arithmetic import gcd from .arithmetic import gcd
from .generic import isNumber from .generic import isNumber
from .operator import op from .operator import op
from .expression import Expression from .expression import Expression, Renderable
from .explicable import Explicable
from .render import txt, tex from .render import txt, tex
from copy import copy
__all__ = ['Fraction'] __all__ = ['Fraction']
class Fraction(object): class Fraction(Explicable, Renderable):
"""Fractions!""" """Fractions!"""
def __init__(self, num, denom = 1): def __init__(self, num, denom = 1):
@ -20,6 +22,7 @@ class Fraction(object):
:param denom: the denominator :param denom: the denominator
""" """
super(Fraction, self).__init__()
self._num = num self._num = num
self._denom = denom self._denom = denom
@ -32,44 +35,57 @@ class Fraction(object):
>>> f = Fraction(3, 6) >>> f = Fraction(3, 6)
>>> f.simplify() >>> f.simplify()
[< Expression [1, 3, '*', 2, 3, '*', '/']>, < Fraction 1 / 2>] < Fraction 1 / 2>
>>> for i in f.simplify().explain():
... print(i)
\\frac{ 3 }{ 6 }
\\frac{ 1 \\times 3 }{ 2 \\times 3 }
\\frac{ 1 }{ 2 }
>>> f = Fraction(6,9)
>>> f.simplify()
< Fraction 2 / 3>
>>> for i in f.simplify().explain():
... print(i)
\\frac{ 6 }{ 9 }
\\frac{ 2 \\times 3 }{ 3 \\times 3 }
\\frac{ 2 }{ 3 }
>>> f = Fraction(0,3) >>> f = Fraction(0,3)
>>> f.simplify() >>> f.simplify()
[0] 0
""" """
steps = [] ini_step = [Expression(self.postfix_tokens)]
if self._num == 0: if self._num == 0:
steps.append(0) return Expression([0])
return steps elif self._denom < 0:
if self._denom < 0:
n_frac = Fraction(-self._num, -self._denom) n_frac = Fraction(-self._num, -self._denom)
steps.append(n_frac) ans = n_frac.simplify()
else: ans.steps = ini_step + ans.steps
n_frac = self return ans
gcd_ = gcd(abs(n_frac._num), abs(n_frac._denom)) gcd_ = gcd(abs(self._num), abs(self._denom))
if gcd_ == n_frac._denom: if gcd_ == self._denom:
n_frac = n_frac._num // gcd_ n_frac = self._num // gcd_
steps.append(n_frac) return Expression([n_frac])
elif gcd_ != 1: elif gcd_ != 1:
n_frac = Fraction(n_frac._num // gcd_ , n_frac._denom // gcd_) n_frac = Fraction(self._num // gcd_ , self._denom // gcd_)
steps.append(Expression([n_frac._num, gcd_, op.mul, n_frac._denom, gcd_, op.mul, op.div ])) ini_step += [Expression([n_frac._num, gcd_, op.mul, n_frac._denom, gcd_, op.mul, op.div ])]
steps.append(n_frac) n_frac.steps = ini_step + n_frac.steps
return n_frac
return steps else:
return self
@property @property
def postfix(self): def postfix_tokens(self):
"""Postfix form of the fraction """Postfix form of the fraction
>>> f = Fraction(3, 5) >>> f = Fraction(3, 5)
>>> f.postfix >>> f.postfix_tokens
[3, 5, '/'] [3, 5, '/']
""" """
@ -79,12 +95,13 @@ class Fraction(object):
return [self._num, self._denom, op.div] return [self._num, self._denom, op.div]
def __str__(self): def __str__(self):
return str(Expression(self.postfix)) return str(Expression(self.postfix_tokens))
def __repr__(self): def __repr__(self):
return "< Fraction {num} / {denom}>".format(num=self._num, denom = self._denom) return "< Fraction {num} / {denom}>".format(num=self._num, denom = self._denom)
def __txt__(self): def __txt__(self):
# TODO: À simplifier je ne comprends plus le pourquoi du comment de cette méthode. |ven. févr. 27 09:21:49 CET 2015
old_render = Expression.get_render() old_render = Expression.get_render()
Expression.set_render(txt) Expression.set_render(txt)
_txt = self.__str__() _txt = self.__str__()
@ -119,18 +136,28 @@ class Fraction(object):
>>> f = Fraction(1, 2) >>> f = Fraction(1, 2)
>>> g = Fraction(2, 3) >>> g = Fraction(2, 3)
>>> f + g >>> f + g
[< Expression [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '+']>, < Expression [3, 6, '/', 4, 6, '/', '+']>, < Expression [< Fraction 3 / 6>, < Fraction 4 / 6>, '+']>, < Expression [3, 4, '+', 6, '/']>, < Expression [7, 6, '/']>, < Fraction 7 / 6>] < Fraction 7 / 6>
>>> (f+g).steps
[< <class 'pymath.expression.Expression'> [1, 2, '/', 2, 3, '/', '+'] >, [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '+'], [3, 6, '/', 4, 6, '/', '+'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '+'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '+']]
>>> f + 2 >>> f + 2
[< Expression [1, 1, '*', 2, 1, '*', '/', 2, 2, '*', 1, 2, '*', '/', '+']>, < Expression [1, 2, '/', 4, 2, '/', '+']>, < Expression [< Fraction 1 / 2>, < Fraction 4 / 2>, '+']>, < Expression [1, 4, '+', 2, '/']>, < Expression [5, 2, '/']>, < Fraction 5 / 2>] < Fraction 5 / 2>
>>> (f+2).steps
[< <class 'pymath.expression.Expression'> [1, 2, '/', 2, '+'] >, [1, 1, '*', 2, 1, '*', '/', 2, 2, '*', 1, 2, '*', '/', '+'], [1, 2, '/', 4, 2, '/', '+'], [< Fraction 1 / 2>, < Fraction 4 / 2>, '+'], [< Fraction 1 / 2>, < Fraction 4 / 2>, '+']]
>>> f = Fraction(3, 4) >>> f = Fraction(3, 4)
>>> g = Fraction(5, 4) >>> g = Fraction(5, 4)
>>> f + g >>> f + g
[< Expression [3, 5, '+', 4, '/']>, < Expression [8, 4, '/']>, 2] 2
>>> (f+g).steps
[< <class 'pymath.expression.Expression'> [3, 4, '/', 5, 4, '/', '+'] >, [3, 5, '+', 4, '/'], [8, 4, '/']]
>>> f+0
< Fraction 3 / 4>
>>> (f+0).steps
[]
""" """
if other == 0: if other == 0:
return [self] return copy(self)
number = self.convert2fraction(other) number = self.convert2fraction(other)
@ -148,14 +175,14 @@ class Fraction(object):
exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.add]) exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.add])
with Expression.tmp_render(): ini_step = Expression(self.postfix_tokens) + Expression(number.postfix_tokens)
steps = list(exp.simplify()) ans = exp.simplify()
ans.steps = [ini_step] + ans.steps
return steps return ans
def __radd__(self, other): def __radd__(self, other):
if other == 0: if other == 0:
return [self] return Expression(self.postfix_tokens)
number = self.convert2fraction(other) number = self.convert2fraction(other)
@ -167,11 +194,17 @@ class Fraction(object):
>>> f = Fraction(1, 2) >>> f = Fraction(1, 2)
>>> g = Fraction(2, 3) >>> g = Fraction(2, 3)
>>> f - g >>> f - g
[< Expression [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '-']>, < Expression [3, 6, '/', 4, 6, '/', '-']>, < Expression [< Fraction 3 / 6>, < Fraction 4 / 6>, '-']>, < Expression [3, 4, '-', 6, '/']>, < Expression [-1, 6, '/']>, < Fraction -1 / 6>] < Fraction -1 / 6>
>>> (f-g).steps
[< <class 'pymath.expression.Expression'> [1, 2, '/', 2, 3, '/', '-'] >, [1, 3, '*', 2, 3, '*', '/', 2, 2, '*', 3, 2, '*', '/', '-'], [3, 6, '/', 4, 6, '/', '-'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '-'], [< Fraction 3 / 6>, < Fraction 4 / 6>, '-']]
>>> f - 0
< Fraction 1 / 2>
>>> (f-0).steps
[]
""" """
if other == 0: if other == 0:
return [self] return copy(self)
number = self.convert2fraction(other) number = self.convert2fraction(other)
@ -189,38 +222,40 @@ class Fraction(object):
exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.sub]) exp = Expression([self._num, coef1, op.mul, self._denom, coef1, op.mul, op.div, number._num, coef2, op.mul, number._denom, coef2, op.mul, op.div,op.sub])
with Expression.tmp_render(): ini_step = Expression(self.postfix_tokens) - Expression(number.postfix_tokens)
steps = list(exp.simplify()) ans = exp.simplify()
ans.steps = [ini_step] + ans.steps
return steps return ans
def __rsub__(self, other): def __rsub__(self, other):
if other == 0: if other == 0:
return [self] return copy(self)
number = self.convert2fraction(other) number = self.convert2fraction(other)
return number - self return number - self
def __neg__(self): def __neg__(self):
""" overload - (as arity 1 operator """ overload - (as arity 1 operator)
>>> f = Fraction(1, 2) >>> f = Fraction(1, 2)
>>> -f >>> -f
[< Fraction -1 / 2>] < Fraction -1 / 2>
>>> (-f).steps
[]
>>> f = Fraction(1, -2) >>> f = Fraction(1, -2)
>>> f >>> f
< Fraction 1 / -2> < Fraction 1 / -2>
>>> -f >>> -f
[< Fraction -1 / -2>, < Fraction 1 / 2>] < Fraction 1 / 2>
>>> (-f).steps
[< <class 'pymath.expression.Expression'> [-1, -2, '/'] >]
""" """
f = Fraction(-self._num, self._denom) f = Fraction(-self._num, self._denom)
ans = f.simplify()
with Expression.tmp_render(): return ans
steps = [f] + f.simplify()
return steps
def __mul__(self, other): def __mul__(self, other):
""" overload * """ overload *
@ -228,21 +263,29 @@ class Fraction(object):
>>> f = Fraction(1, 2) >>> f = Fraction(1, 2)
>>> g = Fraction(2, 3) >>> g = Fraction(2, 3)
>>> f*g >>> f*g
[< Expression [1, 1, 2, '*', '*', 1, 2, '*', 3, '*', '/']>, < Expression [1, 2, '*', 2, 3, '*', '/']>, < Expression [2, 6, '/']>, < Expression [1, 2, '*', 3, 2, '*', '/']>, < Fraction 1 / 3>] < Fraction 1 / 3>
>>> (f*g).steps
[< <class 'pymath.expression.Expression'> [1, 2, '/', 2, 3, '/', '*'] >, [1, 1, 2, '*', '*', 1, 2, '*', 3, '*', '/'], [1, 2, '*', 2, 3, '*', '/'], [2, 6, '/'], < <class 'pymath.expression.Expression'> [2, 6, '/'] >, < <class 'pymath.expression.Expression'> [1, 2, '*', 3, 2, '*', '/'] >]
>>> f * 0 >>> f * 0
[0] 0
>>> (f*0).steps
[]
>>> f*1 >>> f*1
[< Fraction 1 / 2>] < Fraction 1 / 2>
>>> (f*1).steps
[]
>>> f*4 >>> f*4
[< Expression [1, 2, '*', 2, '*', 1, 2, '*', '/']>, < Expression [2, 2, '*', 2, '/']>, < Expression [4, 2, '/']>, 2] 2
>>> (f*4).steps
[< <class 'pymath.expression.Expression'> [1, 2, '/', 4, '*'] >, [1, 2, '*', 2, '*', 1, 2, '*', '/'], [2, 2, '*', 2, '/'], [4, 2, '/']]
""" """
steps = [] steps = []
if other == 0: if other == 0:
return [0] return Expression([0])
elif other == 1: elif other == 1:
return [self] return copy(self)
# TODO: Changer dans le cas où il y a trop de 1 |dim. déc. 28 10:44:10 CET 2014 # TODO: Changer dans le cas où il y a trop de 1 |dim. déc. 28 10:44:10 CET 2014
@ -256,6 +299,7 @@ class Fraction(object):
denom = [self._denom] denom = [self._denom]
exp = Expression(num + denom + [op.div]) exp = Expression(num + denom + [op.div])
ini_step = Expression(self.postfix_tokens) * Expression([other])
else: else:
number = self.convert2fraction(other) number = self.convert2fraction(other)
@ -279,28 +323,45 @@ class Fraction(object):
exp = Expression(num1 + num2 + [ op.mul] + denom1 + denom2 + [op.mul, op.div]) exp = Expression(num1 + num2 + [ op.mul] + denom1 + denom2 + [op.mul, op.div])
with Expression.tmp_render(): ini_step = Expression(self.postfix_tokens) * Expression(number.postfix_tokens)
steps = list(exp.simplify()) ans = exp.simplify()
ans.steps = [ini_step] + ans.steps
return steps return ans
def __rmul__(self, other): def __rmul__(self, other):
return self * other return self * other
def __truediv__(self, other): def __truediv__(self, other):
""" overload /
>>> f = Fraction(1,2)
>>> g = Fraction(3,4)
>>> f / 0
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> f / 1
< Fraction 1 / 2>
>>> (f/1).steps
[]
>>> f / g
< Fraction 2 / 3>
"""
if other == 0: if other == 0:
raise ZeroDivisionError("division by zero") raise ZeroDivisionError("division by zero")
elif other == 1: elif other == 1:
return [self] return copy(self)
number = self.convert2fraction(other) number = self.convert2fraction(other)
steps = [] ini_step = Expression(self.postfix_tokens) / Expression(number.postfix_tokens)
number = Fraction(number._denom, number._num)
steps.append(Expression([self, number, op.mul]))
steps += self * number
return steps number = Fraction(number._denom, number._num)
ans = self * number
ans.steps = [ini_step] + ans.steps
return ans
def __rtruediv__(self, other): def __rtruediv__(self, other):
number = self.convert2fraction(other) number = self.convert2fraction(other)
@ -312,44 +373,51 @@ class Fraction(object):
>>> f = Fraction(3, 4) >>> f = Fraction(3, 4)
>>> f**0 >>> f**0
[1] 1
>>> (f**0).steps
[]
>>> f**1 >>> f**1
[< Fraction 3 / 4>] < Fraction 3 / 4>
>>> (f**1).steps
[]
>>> f**3 >>> f**3
[< Expression [3, 3, '^', 4, 3, '^', '/']>, < Expression [27, 64, '/']>, < Fraction 27 / 64>] < Fraction 27 / 64>
>>> (f**3).steps
[< <class 'pymath.expression.Expression'> [3, 4, '/', 3, '^'] >, [3, 3, '^', 4, 3, '^', '/'], [27, 64, '/'], [27, 64, '/']]
>>> f = Fraction(6, 4) >>> f = Fraction(6, 4)
>>> f**3 >>> f**3
[< Expression [6, 3, '^', 4, 3, '^', '/']>, < Expression [216, 64, '/']>, < Expression [27, 8, '*', 8, 8, '*', '/']>, < Fraction 27 / 8>] < Fraction 27 / 8>
>>> (f**3).steps
[< <class 'pymath.expression.Expression'> [6, 4, '/', 3, '^'] >, [6, 3, '^', 4, 3, '^', '/'], [216, 64, '/'], < <class 'pymath.expression.Expression'> [216, 64, '/'] >, < <class 'pymath.expression.Expression'> [27, 8, '*', 8, 8, '*', '/'] >]
""" """
if not type(power) == int: if not type(power) == int:
raise ValueError("Can't raise fraction to power {}".format(str(power))) raise ValueError("Can't raise fraction to power {}".format(str(power)))
if power == 0: if power == 0:
return [1] return Expression([1])
elif power == 1: elif power == 1:
return [self] return copy(self)
else: else:
ini_step = Expression(self.postfix_tokens) ** power
exp = Expression([self._num, power, op.pw, self._denom, power, op.pw, op.div]) exp = Expression([self._num, power, op.pw, self._denom, power, op.pw, op.div])
with Expression.tmp_render():
steps = list(exp.simplify()) ans = exp.simplify()
return steps ans.steps = [ini_step] + ans.steps
return ans
def __xor__(self, power): def __xor__(self, power):
""" overload ^ """ overload ^
Work like **
>>> f = Fraction(3, 4) >>> f = Fraction(3, 4)
>>> f^0 >>> f^0
[1] 1
>>> f^1 >>> f^1
[< Fraction 3 / 4>] < Fraction 3 / 4>
>>> f^3 >>> f^3
[< Expression [3, 3, '^', 4, 3, '^', '/']>, < Expression [27, 64, '/']>, < Fraction 27 / 64>] < Fraction 27 / 64>
>>> f = Fraction(6, 4)
>>> f^3
[< Expression [6, 3, '^', 4, 3, '^', '/']>, < Expression [216, 64, '/']>, < Expression [27, 8, '*', 8, 8, '*', '/']>, < Fraction 27 / 8>]
""" """
return self.__pow__(power) return self.__pow__(power)
@ -382,17 +450,22 @@ class Fraction(object):
""" >= """ """ >= """
return float(self) >= float(other) return float(self) >= float(other)
def __copy__(self):
""" Copying the fraction removing steps where it is from """
return Fraction(self._num, self._denom)
if __name__ == '__main__': if __name__ == '__main__':
#f = Fraction(1, 12) f = Fraction(1, 12)
#g = Fraction(1, 12) g = Fraction(6, 12)
#h = Fraction(1,-5) for i in g.simplify().explain():
#t = Fraction(10,3) print("g = ",i)
#print("---------") h = Fraction(1,-5)
#print("1 + ", str(h)) t = Fraction(10,3)
#for i in (1 + h): print("---------")
# print(i) for i in (0 + h).explain():
print('0 + h = ',i)
#print("---------") #print("---------")
#print(str(f) , "+", str(t)) #print(str(f) , "+", str(t))
#for i in (f + t): #for i in (f + t):