Commit b6bd65db authored by Taddeus Kroes's avatar Taddeus Kroes

Added some new rules for fractions.

parent 7c142aff
......@@ -10,7 +10,8 @@ from .numerics import match_add_numerics, match_divide_numerics, \
match_multiply_numerics, match_multiply_zero, match_multiply_one, \
match_raise_numerics
from .fractions import match_constant_division, match_add_constant_fractions, \
match_expand_and_add_fractions, match_multiply_fractions
match_expand_and_add_fractions, match_multiply_fractions, \
match_equal_fraction_parts
from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division
from .sort import match_sort_multiplicants
......@@ -25,7 +26,8 @@ RULES = {
match_negated_factor, match_multiply_one,
match_sort_multiplicants, match_multiply_fractions],
OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_negated_division],
match_constant_division, match_negated_division,
match_equal_fraction_parts],
OP_POW: [match_multiply_exponents, match_duplicate_exponent,
match_raised_fraction, match_remove_negative_exponent,
match_exponent_to_root, match_extend_exponent,
......
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
from ..node import ExpressionLeaf as L, Scope, negate, OP_DIV, OP_ADD, \
OP_MUL, nary_node
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
......@@ -226,3 +227,84 @@ def multiply_with_fraction(root, args):
MESSAGES[multiply_with_fraction] = _('Multiply {2} with fraction {3}.')
def match_equal_fraction_parts(node):
"""
Divide nominator and denominator by the same part.
Examples:
ab / (ac) -> b / c
ab / a -> b / 1
a / (ab) -> 1 / b
"""
assert node.is_op(OP_DIV)
nominator, denominator = node
if nominator.is_op(OP_MUL):
n_scope = list(Scope(nominator))
else:
n_scope = [nominator]
if denominator.is_op(OP_MUL):
d_scope = list(Scope(denominator))
else:
d_scope = [denominator]
p = []
# Look for in scope
for i, n in enumerate(n_scope):
for j, d in enumerate(d_scope):
if n == d:
p.append(P(node, divide_fraction_parts,
(n, n_scope, d_scope, i, j)))
return p
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
"""
a, n_scope, d_scope, i, j = args
n, d = root
del n_scope[i]
del d_scope[j]
if not n_scope:
# Last element of nominator scope, replace by 1
nom = L(1)
elif len(n_scope) == 1:
# Only one element left, no multiplication
nom = n_scope[0]
else:
# Still a multiplication
nom = nary_node('*', n_scope)
if not d_scope:
denom = L(1)
elif len(n_scope) == 1:
denom = d_scope[0]
else:
denom = nary_node('*', d_scope)
return nom.negate(n.negated) / denom.negate(d.negated)
MESSAGES[divide_fraction_parts] = \
_('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)
......@@ -64,8 +64,11 @@ class TestLeidenOefenopgaveV12(TestCase):
'aa ^ 6 / b ^ 3 * b ^ 2',
'a ^ (1 + 6) / b ^ 3 * b ^ 2',
'a ^ 7 / b ^ 3 * b ^ 2',
# FIXME: 'b ^ 2 * a ^ 7 / b ^ 3',
'b ^ 2 * a ^ 7 / b ^ 3',
# FIXME: 'b ^ 2 / b ^ 3 * a ^ 7',
# FIXME: 'b ^ (2 - 3) * a ^ 7',
# FIXME: 'b ^ 5 * a ^ 7',
# FIXME: 'a ^ 7 * b ^ 5',
])
def test_2_b(self):
......
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
multiply_fractions, multiply_with_fraction, \
match_equal_fraction_parts, divide_fraction_parts
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -144,3 +145,47 @@ class TestRulesFractions(RulesTestCase):
(ab, e), cd = root = tree('a / b * e * (c / d)')
self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
a * c / (b * d) * e)
def test_match_equal_fraction_parts(self):
(a, b), (c, a) = root = tree('ab / (ca)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [a, b], [c, a], 0, 1))])
(a, b), a = root = tree('ab / a')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [a, b], [a], 0, 0))])
a, (a, b) = root = tree('a / (ab)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [a], [a, b], 0, 0))])
root = tree('abc / (cba)')
((a, b), c) = root[0]
s0, s1 = [a, b, c], [c, b, a]
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, s0, s1, 0, 2)),
P(root, divide_fraction_parts, (b, s0, s1, 1, 1)),
P(root, divide_fraction_parts, (c, s0, s1, 2, 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))
self.assertEqual(result, b / c)
(a, b), a = root = tree('ab / a')
result = divide_fraction_parts(root, (a, [a, b], [a], 0, 0))
self.assertEqual(result, b / 1)
root, l1 = tree('a / (ab), 1')
a, (a, b) = root
result = divide_fraction_parts(root, (a, [a], [a, b], 0, 0))
self.assertEqual(result, l1 / b)
root = tree('abc / (cba)')
((a, b), c) = root[0]
result = divide_fraction_parts(root, (a, [a, b, c], [c, b, a], 0, 2))
self.assertEqual(result, b * c / (c * b))
result = divide_fraction_parts(root, (b, [a, b, c], [c, b, a], 1, 1))
self.assertEqual(result, a * c / (c * a))
result = divide_fraction_parts(root, (c, [a, b, c], [c, b, a], 2, 0))
self.assertEqual(result, a * b / (b * a))
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