Commit 1659cc67 authored by Taddeus Kroes's avatar Taddeus Kroes

Added some more fraction rewrite rules.

parent f46cf729
...@@ -2,7 +2,7 @@ from itertools import combinations, product ...@@ -2,7 +2,7 @@ from itertools import combinations, product
from .utils import least_common_multiple, partition from .utils import least_common_multiple, partition
from ..node import ExpressionLeaf as L, Scope, negate, OP_DIV, OP_ADD, \ from ..node import ExpressionLeaf as L, Scope, negate, OP_DIV, OP_ADD, \
OP_MUL, nary_node, negate OP_MUL, OP_POW, nary_node, negate
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -235,6 +235,12 @@ def match_equal_fraction_parts(node): ...@@ -235,6 +235,12 @@ def match_equal_fraction_parts(node):
ab / (ac) -> b / c ab / (ac) -> b / c
ab / a -> b / 1 ab / a -> b / 1
a / (ab) -> 1 / b a / (ab) -> 1 / b
If the same root appears in both nominator and denominator, extrct it so
that it can be reduced to a single power by power division rules.
a ^ p * b / a ^ q -> a ^ p / a ^ q * b / 1
a ^ p * b / a -> a ^ p / a * b / 1
a * b / a ^ q -> a / a ^ q * b / 1
""" """
assert node.is_op(OP_DIV) assert node.is_op(OP_DIV)
...@@ -252,28 +258,29 @@ def match_equal_fraction_parts(node): ...@@ -252,28 +258,29 @@ def match_equal_fraction_parts(node):
p = [] p = []
# Look for in scope # Look for matching parts in scopes
for i, n in enumerate(n_scope): for i, n in enumerate(n_scope):
for j, d in enumerate(d_scope): for j, d in enumerate(d_scope):
if n.equals(d, ignore_negation=True): if n.equals(d, ignore_negation=True):
p.append(P(node, divide_fraction_parts, p.append(P(node, divide_fraction_parts,
(negate(n, 0), n_scope, d_scope, i, j))) (negate(n, 0), n_scope, d_scope, i, j)))
return p if n.is_op(OP_POW):
a = n[0]
if d == a or (d.is_op(OP_POW) and d[0] == a):
# a ^ p * b / a -> a ^ p / a * b
p.append(P(node, extract_divided_roots,
(a, n_scope, d_scope, i, j)))
elif d.is_op(OP_POW) and n == d[0]:
# a * b / a ^ q -> a / a ^ q * b
p.append(P(node, extract_divided_roots,
(d[0], n_scope, d_scope, i, j)))
def divide_fraction_parts(root, args): return p
"""
Divide nominator and denominator by the same part.
Examples:
ab / (ac) -> b / c def remove_from_scopes(n_scope, d_scope, i, j):
ab / a -> b / 1
a / (ab) -> 1 / b
-ab / a -> -b / 1
"""
a, n_scope, d_scope, i, j = args
n, d = root
a_n, a_d = n_scope[i], d_scope[j] a_n, a_d = n_scope[i], d_scope[j]
del n_scope[i] del n_scope[i]
...@@ -296,17 +303,39 @@ def divide_fraction_parts(root, args): ...@@ -296,17 +303,39 @@ def divide_fraction_parts(root, args):
else: else:
denom = nary_node('*', d_scope) denom = nary_node('*', d_scope)
return a_n, a_d, nom, denom
def divide_fraction_parts(root, args):
"""
Divide nominator and denominator by the same part.
Examples:
ab / (ac) -> b / c
ab / a -> b / 1
a / (ab) -> 1 / b
-ab / a -> -b / 1
"""
a, n_scope, d_scope, i, j = args
n, d = root
a_n, a_d, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
# Move negation of removed part to nominator and denominator # Move negation of removed part to nominator and denominator
return nom.negate(n.negated + a_n.negated) \ return nom.negate(n.negated + a_n.negated) \
/ denom.negate(d.negated + a_d.negated) / denom.negate(d.negated + a_d.negated)
MESSAGES[divide_fraction_parts] = \ MESSAGES[divide_fraction_parts] = \
_('Divide nominator and denominator in {0} by {1}') _('Divide nominator and denominator in {0} by {1}.')
def match_multiplied_power_division(node): def extract_divided_roots(root, args):
""" a, n_scope, d_scope, i, j = args
a ^ p * b / a ^ q -> a ^ p / a ^ q * b n, d = root
""" ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
assert node.is_op(OP_DIV)
return ap / aq * nom.negate(n.negated) / denom.negate(d.negated)
MESSAGES[extract_divided_roots] = \
_('Extrct the root {1} from nominator and denominator in {0}.')
...@@ -2,7 +2,8 @@ from src.rules.fractions import match_constant_division, division_by_one, \ ...@@ -2,7 +2,8 @@ from src.rules.fractions import match_constant_division, division_by_one, \
division_of_zero, division_by_self, match_add_constant_fractions, \ division_of_zero, division_by_self, match_add_constant_fractions, \
equalize_denominators, add_nominators, match_multiply_fractions, \ equalize_denominators, add_nominators, match_multiply_fractions, \
multiply_fractions, multiply_with_fraction, \ multiply_fractions, multiply_with_fraction, \
match_equal_fraction_parts, divide_fraction_parts match_equal_fraction_parts, divide_fraction_parts, \
extract_divided_roots
from src.node import Scope from src.node import Scope
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree from tests.rulestestcase import RulesTestCase, tree
...@@ -171,6 +172,18 @@ class TestRulesFractions(RulesTestCase): ...@@ -171,6 +172,18 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualPos(match_equal_fraction_parts(root), self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [-a], [a], 0, 0))]) [P(root, divide_fraction_parts, (a, [-a], [a], 0, 0))])
(ap, b), aq = root = tree('a ^ p * b / a ^ q')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_divided_roots, (a, [ap, b], [aq], 0, 0))])
(a, b), aq = root = tree('a * b / a ^ q')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_divided_roots, (a, [a, b], [aq], 0, 0))])
(ap, b), a = root = tree('a ^ p * b / a')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_divided_roots, (a, [ap, b], [a], 0, 0))])
def test_divide_fraction_parts(self): def test_divide_fraction_parts(self):
(a, b), (c, a) = root = tree('ab / (ca)') (a, b), (c, a) = root = tree('ab / (ca)')
result = divide_fraction_parts(root, (a, [a, b], [c, a], 0, 1)) result = divide_fraction_parts(root, (a, [a, b], [c, a], 0, 1))
...@@ -197,3 +210,17 @@ class TestRulesFractions(RulesTestCase): ...@@ -197,3 +210,17 @@ class TestRulesFractions(RulesTestCase):
(a, b), a = root = tree('-ab / a') (a, b), a = root = tree('-ab / a')
result = divide_fraction_parts(root, (a, [-a, b], [a], 0, 0)) result = divide_fraction_parts(root, (a, [-a, b], [a], 0, 0))
self.assertEqual(result, -b / 1) self.assertEqual(result, -b / 1)
def test_extract_divided_roots(self):
r, a = tree('a ^ p * b / a ^ q, a')
((a, p), b), (a, q) = (ap, b), aq = r
self.assertEqual(extract_divided_roots(r, (a, [ap, b], [aq], 0, 0)),
a ** p / a ** q * b / 1)
r = tree('a * b / a ^ q, a')
self.assertEqual(extract_divided_roots(r, (a, [a, b], [aq], 0, 0)),
a / a ** q * b / 1)
r = tree('a ^ p * b / a, a')
self.assertEqual(extract_divided_roots(r, (a, [ap, b], [a], 0, 0)),
a ** p / a * b / 1)
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