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, \
match_raise_numerics
from .fractions import match_constant_division, match_add_constant_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, \
match_negated_division
from .sort import match_sort_multiplicants
......@@ -26,8 +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_equal_fraction_parts],
match_constant_division, match_divide_fractions, \
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,
......
......@@ -227,20 +227,79 @@ def multiply_with_fraction(root, args):
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:
ab / (ac) -> b / c
ab / a -> b / 1
a / (ab) -> 1 / b
a / b / c -> a / (bc)
a / (b / c) -> ac / 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
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
nom, denom = node
p = []
if nom.is_op(OP_DIV):
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)
......@@ -256,6 +315,28 @@ def match_equal_fraction_parts(node):
else:
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 = []
# Look for matching parts in scopes
......@@ -330,6 +411,11 @@ MESSAGES[divide_fraction_parts] = \
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
n, d = root
ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
......
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, \
extract_divided_roots
multiply_fractions, multiply_with_fraction, match_divide_fractions, \
divide_fraction, divide_by_fraction, 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
......@@ -147,6 +147,23 @@ class TestRulesFractions(RulesTestCase):
self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
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):
(a, b), (c, a) = root = tree('ab / (ca)')
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