Commit d0da0b90 authored by Taddeus Kroes's avatar Taddeus Kroes

Added fraction division rules.

parent cf27b263
...@@ -11,7 +11,7 @@ from .numerics import match_add_numerics, match_divide_numerics, \ ...@@ -11,7 +11,7 @@ from .numerics import match_add_numerics, match_divide_numerics, \
match_raise_numerics match_raise_numerics
from .fractions import match_constant_division, match_add_constant_fractions, \ 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 match_divide_fractions, match_equal_fraction_parts
from .negation import match_negated_factor, match_negate_polynome, \ from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division match_negated_division
from .sort import match_sort_multiplicants from .sort import match_sort_multiplicants
...@@ -26,8 +26,8 @@ RULES = { ...@@ -26,8 +26,8 @@ RULES = {
match_negated_factor, match_multiply_one, match_negated_factor, match_multiply_one,
match_sort_multiplicants, match_multiply_fractions], match_sort_multiplicants, match_multiply_fractions],
OP_DIV: [match_subtract_exponents, match_divide_numerics, OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_negated_division, match_constant_division, match_divide_fractions, \
match_equal_fraction_parts], match_negated_division, match_equal_fraction_parts],
OP_POW: [match_multiply_exponents, match_duplicate_exponent, OP_POW: [match_multiply_exponents, match_duplicate_exponent,
match_raised_fraction, match_remove_negative_exponent, match_raised_fraction, match_remove_negative_exponent,
match_exponent_to_root, match_extend_exponent, match_exponent_to_root, match_extend_exponent,
......
...@@ -227,20 +227,79 @@ def multiply_with_fraction(root, args): ...@@ -227,20 +227,79 @@ def multiply_with_fraction(root, args):
MESSAGES[multiply_with_fraction] = _('Multiply {2} with fraction {3}.') MESSAGES[multiply_with_fraction] = _('Multiply {2} with fraction {3}.')
def match_equal_fraction_parts(node): def match_divide_fractions(node):
""" """
Divide nominator and denominator by the same part. Reduce divisions of fractions to a single fraction.
Examples: Examples:
ab / (ac) -> b / c a / b / c -> a / (bc)
ab / a -> b / 1 a / (b / c) -> ac / b
a / (ab) -> 1 / b """
# TODO: IMPLICIT: a / b / (c / d) ->* ad / bd -> validation test!
assert node.is_op(OP_DIV)
If the same root appears in both nominator and denominator, extrct it so nom, denom = node
that it can be reduced to a single power by power division rules. p = []
a ^ p * b / a ^ q -> a ^ p / a ^ q * b / 1
a ^ p * b / a -> a ^ p / a * b / 1 if nom.is_op(OP_DIV):
a * b / a ^ q -> a / a ^ q * b / 1 p.append(P(node, divide_fraction, tuple(nom) + (denom,)))
if denom.is_op(OP_DIV):
p.append(P(node, divide_by_fraction, (nom,) + tuple(denom)))
return p
def divide_fraction(root, args):
"""
a / b / c -> a / (bc)
"""
a, b, c = args
return a / (b * c)
MESSAGES[divide_fraction] = _('Move {2} to denominator of fraction {0[0]}.')
def divide_by_fraction(root, args):
"""
a / (b / c) -> ac / b
"""
a, b, c = args
return a * c / b
MESSAGES[divide_by_fraction] = \
_('Move {3} to nominator of fraction {1} / {2}.')
#def match_extract_divided_fractions(node):
# """
# Reduce divisions of fractions to a single fraction.
#
# Examples:
# a / b / c -> a / bc
# a / (b / c) -> ac / b
# # IMPLICIT: a / b / (c / d) ->* ad / bd -> validation test!
# """
# assert node.is_op(OP_DIV)
#
# nom, denom = node
# n_scope, d_scope = fraction_scopes(node)
# is_division = lambda n: n.is_op(OP_DIV)
# n_fractions, n_others = partition(is_division, n_scope)
# d_fractions, d_others = partition(is_division, d_scope)
#
#
# return []
def fraction_scopes(node):
"""
Get the multiplication scopes of the nominator and denominator of a
fraction.
""" """
assert node.is_op(OP_DIV) assert node.is_op(OP_DIV)
...@@ -256,6 +315,28 @@ def match_equal_fraction_parts(node): ...@@ -256,6 +315,28 @@ def match_equal_fraction_parts(node):
else: else:
d_scope = [denominator] d_scope = [denominator]
return n_scope, d_scope
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
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)
nominator, denominator = node
n_scope, d_scope = fraction_scopes(node)
p = [] p = []
# Look for matching parts in scopes # Look for matching parts in scopes
...@@ -330,6 +411,11 @@ MESSAGES[divide_fraction_parts] = \ ...@@ -330,6 +411,11 @@ MESSAGES[divide_fraction_parts] = \
def extract_divided_roots(root, args): def extract_divided_roots(root, args):
"""
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
"""
a, n_scope, d_scope, i, j = args a, n_scope, d_scope, i, j = args
n, d = root n, d = root
ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j) ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
......
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_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_divide_fractions, \
match_equal_fraction_parts, divide_fraction_parts, \ divide_fraction, divide_by_fraction, match_equal_fraction_parts, \
extract_divided_roots 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
...@@ -147,6 +147,23 @@ class TestRulesFractions(RulesTestCase): ...@@ -147,6 +147,23 @@ class TestRulesFractions(RulesTestCase):
self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)), self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
a * c / (b * d) * e) a * c / (b * d) * e)
def test_match_divide_fractions(self):
(a, b), c = root = tree('a / b / c')
self.assertEqualPos(match_divide_fractions(root),
[P(root, divide_fraction, (a, b, c))])
root = tree('a / (b / c)')
self.assertEqualPos(match_divide_fractions(root),
[P(root, divide_by_fraction, (a, b, c))])
def test_divide_fraction(self):
(a, b), c = root = tree('a / b / c')
self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * c))
def test_divide_by_fraction(self):
a, (b, c) = root = tree('a / (b / c)')
self.assertEqual(divide_by_fraction(root, (a, b, c)), a * c / b)
def test_match_equal_fraction_parts(self): def test_match_equal_fraction_parts(self):
(a, b), (c, a) = root = tree('ab / (ca)') (a, b), (c, a) = root = tree('ab / (ca)')
self.assertEqualPos(match_equal_fraction_parts(root), self.assertEqualPos(match_equal_fraction_parts(root),
......
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