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
from .utils import least_common_multiple, partition
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 ..translate import _
......@@ -235,6 +235,12 @@ def match_equal_fraction_parts(node):
ab / (ac) -> b / c
ab / a -> b / 1
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)
......@@ -252,28 +258,29 @@ def match_equal_fraction_parts(node):
p = []
# Look for in scope
# Look for matching parts in scopes
for i, n in enumerate(n_scope):
for j, d in enumerate(d_scope):
if n.equals(d, ignore_negation=True):
p.append(P(node, divide_fraction_parts,
(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):
"""
Divide nominator and denominator by the same part.
return p
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
def remove_from_scopes(n_scope, d_scope, i, j):
a_n, a_d = n_scope[i], d_scope[j]
del n_scope[i]
......@@ -296,17 +303,39 @@ def divide_fraction_parts(root, args):
else:
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
return nom.negate(n.negated + a_n.negated) \
/ denom.negate(d.negated + a_d.negated)
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):
"""
a ^ p * b / a ^ q -> a ^ p / a ^ q * b
"""
assert node.is_op(OP_DIV)
def extract_divided_roots(root, args):
a, n_scope, d_scope, i, j = args
n, d = root
ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
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, \
division_of_zero, division_by_self, match_add_constant_fractions, \
equalize_denominators, add_nominators, match_multiply_fractions, \
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.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -171,6 +172,18 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualPos(match_equal_fraction_parts(root),
[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):
(a, b), (c, a) = root = tree('ab / (ca)')
result = divide_fraction_parts(root, (a, [a, b], [c, a], 0, 1))
......@@ -197,3 +210,17 @@ class TestRulesFractions(RulesTestCase):
(a, b), a = root = tree('-ab / a')
result = divide_fraction_parts(root, (a, [-a, b], [a], 0, 0))
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