Commit f0701c3a authored by Taddeus Kroes's avatar Taddeus Kroes

Added some more fraction rewrite rules.

parent 9f8b2e9c
...@@ -4,12 +4,14 @@ from .powers import match_add_exponents, match_subtract_exponents, \ ...@@ -4,12 +4,14 @@ from .powers import match_add_exponents, match_subtract_exponents, \
match_multiply_exponents, match_duplicate_exponent, \ match_multiply_exponents, match_duplicate_exponent, \
match_remove_negative_exponent, match_exponent_to_root match_remove_negative_exponent, match_exponent_to_root
from .numerics import match_divide_numerics from .numerics import match_divide_numerics
from .fractions import match_constant_division from .fractions import match_constant_division, match_add_constant_fractions, \
match_expand_and_add_fractions
RULES = { RULES = {
OP_ADD: [match_combine_polynomes], OP_ADD: [match_add_constant_fractions, match_combine_polynomes],
OP_MUL: [match_expand, match_add_exponents], OP_MUL: [match_expand, match_add_exponents, \
match_expand_and_add_fractions],
OP_DIV: [match_subtract_exponents, match_divide_numerics, \ OP_DIV: [match_subtract_exponents, match_divide_numerics, \
match_constant_division], match_constant_division],
OP_POW: [match_multiply_exponents, match_duplicate_exponent, \ OP_POW: [match_multiply_exponents, match_duplicate_exponent, \
......
from ..node import ExpressionLeaf as L, OP_DIV from itertools import combinations
from .utils import nary_node, least_common_multiple
from ..node import ExpressionLeaf as L, OP_DIV, OP_ADD, OP_MUL
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -8,6 +11,7 @@ def match_constant_division(node): ...@@ -8,6 +11,7 @@ def match_constant_division(node):
a / 0 -> Division by zero a / 0 -> Division by zero
a / 1 -> a a / 1 -> a
0 / a -> 0 0 / a -> 0
a / a -> 1
""" """
assert node.is_op(OP_DIV) assert node.is_op(OP_DIV)
...@@ -15,6 +19,7 @@ def match_constant_division(node): ...@@ -15,6 +19,7 @@ def match_constant_division(node):
nominator, denominator = node nominator, denominator = node
# a / 0 # a / 0
# TODO: move to parser
if denominator == 0: if denominator == 0:
raise ZeroDivisionError() raise ZeroDivisionError()
...@@ -26,6 +31,10 @@ def match_constant_division(node): ...@@ -26,6 +31,10 @@ def match_constant_division(node):
if nominator == 0: if nominator == 0:
p.append(P(node, division_of_zero)) p.append(P(node, division_of_zero))
# a / a
if nominator == denominator:
p.append(P(node, division_by_self))
return p return p
...@@ -41,3 +50,88 @@ def division_of_zero(root, args): ...@@ -41,3 +50,88 @@ def division_of_zero(root, args):
0 / a -> 0 0 / a -> 0
""" """
return L(0) return L(0)
def division_by_self(root, args):
"""
a / a -> 1
"""
return L(1)
def match_add_constant_fractions(node):
"""
1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4 # Equalize denominators
2 / 4 + 3 / 4 -> 5 / 4 # Equal denominators, so nominators can
# be added
"""
assert node.is_op(OP_ADD)
p = []
fractions = filter(lambda n: n.is_op(OP_DIV), node.get_scope())
for a, b in combinations(fractions, 2):
na, da = a
nb, db = b
if da == db:
# Equal denominators, add nominators to create a single fraction
p.append(P(node, add_nominators, (a, b)))
elif da.is_numeric() and db.is_numeric():
# Denominators are both numeric, rewrite both fractions to the
# least common multiple of their denominators. Later, the
# nominators will be added
denom = least_common_multiple(da.value, db.value)
p.append(P(node, equalize_denominators, (a, b, denom)))
return p
def equalize_denominators(root, args):
"""
1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4
a / 2 + b / 4 -> 2a / 4 + b / 4
"""
denom = args[2]
scope = root.get_scope()
for fraction in args[:2]:
n, d = fraction
mult = denom / d.value
if mult != 1:
n = L(n.value * mult) if n.is_numeric() else L(mult) * n
scope[scope.index(fraction)] = n / L(d.value * mult)
return nary_node('+', scope)
def add_nominators(root, args):
"""
a / b + c / b -> (a + c) / b
"""
ab, cb = args
a, b = ab
c = cb[0]
scope = root.get_scope()
# Replace the left node with the new expression
scope[scope.index(ab)] = (a + c) / b
# Remove the right node
scope.remove(cb)
return nary_node('+', scope)
def match_expand_and_add_fractions(node):
"""
a * b / c + d * b / c -> (a + d) * (b / c)
"""
assert node.is_op(OP_MUL)
p = []
return p
from src.rules.fractions import match_constant_division, division_by_one, \ from src.rules.fractions import match_constant_division, division_by_one, \
division_of_zero division_of_zero, division_by_self, match_add_constant_fractions, \
equalize_denominators, add_nominators
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from tests.test_rules_poly import tree from tests.test_rules_poly import tree
from tests.rulestestcase import RulesTestCase from tests.rulestestcase import RulesTestCase
...@@ -21,6 +22,10 @@ class TestRulesFractions(RulesTestCase): ...@@ -21,6 +22,10 @@ class TestRulesFractions(RulesTestCase):
possibilities = match_constant_division(root) possibilities = match_constant_division(root)
self.assertEqualPos(possibilities, [P(root, division_of_zero)]) self.assertEqualPos(possibilities, [P(root, division_of_zero)])
root = a / a
possibilities = match_constant_division(root)
self.assertEqualPos(possibilities, [P(root, division_by_self)])
def test_division_by_one(self): def test_division_by_one(self):
a = tree('a') a = tree('a')
root = a / 1 root = a / 1
...@@ -32,3 +37,49 @@ class TestRulesFractions(RulesTestCase): ...@@ -32,3 +37,49 @@ class TestRulesFractions(RulesTestCase):
root = zero / a root = zero / a
self.assertEqualNodes(division_of_zero(root, ()), zero) self.assertEqualNodes(division_of_zero(root, ()), zero)
def test_division_by_self(self):
a, one = tree('a,1')
root = a / a
self.assertEqualNodes(division_by_self(root, ()), one)
def test_match_add_constant_fractions(self):
a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
n0, n1 = root = l1 / l2 + l3 / l4
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, equalize_denominators, (n0, n1, 4))])
(((n0, n1), n2), n3), n4 = root = a + l1 / l2 + b + l3 / l4 + c
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, equalize_denominators, (n1, n3, 4))])
n0, n1 = root = l2 / l4 + l3 / l4
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, add_nominators, (n0, n1))])
(((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + l3 / l4 + c
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, add_nominators, (n1, n3))])
def test_equalize_denominators(self):
a, b, l1, l2, l3, l4 = tree('a,b,1,2,3,4')
n0, n1 = root = l1 / l2 + l3 / l4
self.assertEqualNodes(equalize_denominators(root, (n0, n1, 4)),
l2 / l4 + l3 / l4)
n0, n1 = root = a / l2 + b / l4
self.assertEqualNodes(equalize_denominators(root, (n0, n1, 4)),
(l2 * a) / l4 + b / l4)
def test_add_nominators(self):
a, b, c = tree('a,b,c')
n0, n1 = root = a / b + c / b
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + c) / b)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment