Skip to content
Snippets Groups Projects
Commit d0da0b90 authored by Taddeus Kroes's avatar Taddeus Kroes
Browse files

Added fraction division rules.

parent cf27b263
No related branches found
No related tags found
No related merge requests found
......@@ -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),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment