Commit 08555af7 authored by Taddeus Kroes's avatar Taddeus Kroes

Applied most of the new fraction rules.

parent 7a9605a7
......@@ -11,7 +11,7 @@ from .numerics import match_add_numerics, match_divide_numerics, \
match_raise_numerics
from .fractions import match_constant_division, match_add_fractions, \
match_multiply_fractions, match_divide_fractions, \
match_equal_fraction_parts
match_extract_fraction_terms
from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division
from .sort import match_sort_multiplicants
......@@ -39,7 +39,7 @@ RULES = {
match_factor_in_multiplicant],
OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_divide_fractions,
match_negated_division, match_equal_fraction_parts],
match_negated_division, match_extract_fraction_terms],
OP_POW: [match_multiply_exponents, match_duplicate_exponent,
match_raised_fraction, match_remove_negative_exponent,
match_exponent_to_root, match_extend_exponent,
......
......@@ -180,7 +180,7 @@ MESSAGES[constant_to_fraction] = \
def match_multiply_fractions(node):
"""
a / b * (c / d) -> ac / (bd)
a / b * c and a, c in Z or (a = 1 and eval(b) not in Z) -> ac / b
a / b * c and (c in Z or eval(a / b) not in Z) -> ac / b
"""
assert node.is_op(OP_MUL)
......@@ -195,8 +195,9 @@ def match_multiply_fractions(node):
for ab, c in product(fractions, others):
a, b = ab
if (a.is_numeric() and c.is_numeric()) or \
(a == 1 and evals_to_numeric(b)):
#if (a.is_numeric() and c.is_numeric()) or \
# (a == 1 and evals_to_numeric(b)):
if c.is_numeric() or not evals_to_numeric(ab):
p.append(P(node, multiply_with_fraction, (scope, ab, c)))
return p
......@@ -243,6 +244,9 @@ def match_divide_fractions(node):
Examples:
a / b / c -> a / (bc)
a / (b / c) -> ac / b
Note that:
a / b / (c / d) ->* ad / bd # chain test!
"""
assert node.is_op(OP_DIV)
......@@ -260,11 +264,11 @@ def match_divide_fractions(node):
def divide_fraction(root, args):
"""
a / b / c -> a / (bc)
a / b / c -> a / (bc)
"""
a, b, c = args
(a, b), c = root
return a / (b * c)
return (a / (b * c)).negate(root.negated)
MESSAGES[divide_fraction] = _('Move {3} to denominator of fraction {1} / {2}.')
......@@ -272,38 +276,18 @@ MESSAGES[divide_fraction] = _('Move {3} to denominator of fraction {1} / {2}.')
def divide_by_fraction(root, args):
"""
a / (b / c) -> ac / b
a / (b / c) -> ac / b
"""
a, b, c = args
a, bc = root
b, c = bc
return a * c / b
return (a * c / b).negate(root.negated + bc.negated)
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
......@@ -344,7 +328,7 @@ def is_power_combination(a, b):
return a == b
def match_equal_fraction_parts(node):
def match_extract_fraction_terms(node):
"""
Divide nominator and denominator by the same part.
......@@ -357,6 +341,7 @@ def match_equal_fraction_parts(node):
#a ^ p * b / a -> a ^ p / a * b / 1
#a * b / a ^ q -> a / a ^ q * b / 1
"""
# TODO: ac / b -> a / b * c
assert node.is_op(OP_DIV)
nominator, denominator = node
......@@ -369,31 +354,16 @@ def match_equal_fraction_parts(node):
# Look for matching parts in scopes
for n, d in product(n_scope, d_scope):
if is_power_combination(n, d):
p.append(P(N, extract_fraction_terms, (n_scope, d_scope, n, d)))
#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)))
# 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)))
p.append(P(node, extract_fraction_terms, (n_scope, d_scope, n, d)))
return p
def extract_fraction_terms(root, args):
"""
ab / a -> a / a * (b / 1)
a / (ba) -> a / a * (1 / b)
a * c / (ae) -> a / a * (c / e)
a ^ b * c / (a ^ d * e) -> a ^ b / a ^ d * (c / e)
"""
n_scope, d_scope, n, d = args
......@@ -406,72 +376,9 @@ def extract_fraction_terms(root, args):
if len(d_scope) == 1:
d_scope.replace(d, L(1))
else:
d_scope.remove(n)
d_scope.remove(d)
return n / d * (n_scope.as_nary_node() / d_scope.as_nary_node())
#def remove_from_scopes(n_scope, d_scope, i, j):
# a_n, a_d = n_scope[i], d_scope[j]
#
# 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 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}.')
#
#
#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)
#
# return ap / aq * nom.negate(n.negated) / denom.negate(d.negated)
#
#
#MESSAGES[extract_divided_roots] = \
# _('Extract the root {1} from nominator and denominator in {0}.')
MESSAGES[extract_fraction_terms] = _('Extract {3} / {4} from fraction {0}.')
from itertools import combinations
from .utils import evals_to_numeric
from ..node import ExpressionLeaf as Leaf, Scope, OP_ADD, OP_MUL, nary_node, \
negate
from ..possibilities import Possibility as P, MESSAGES
......@@ -35,9 +36,7 @@ def match_combine_groups(node):
l = len(n_scope)
for i, sub_node in enumerate(n_scope):
# TODO: use utitlity function evals_to_numeric
#if evals_to_numeric(sub_node):
if sub_node.is_numeric():
if evals_to_numeric(sub_node):
others = [n_scope[j] for j in range(i) + range(i + 1, l)]
if len(others) == 1:
......
......@@ -2,9 +2,9 @@ from src.rules.fractions import match_constant_division, division_by_one, \
division_of_zero, division_by_self, match_add_fractions, \
equalize_denominators, add_nominators, match_multiply_fractions, \
multiply_fractions, multiply_with_fraction, match_divide_fractions, \
divide_fraction, divide_by_fraction, match_equal_fraction_parts, \
divide_fraction, divide_by_fraction, match_extract_fraction_terms, \
constant_to_fraction, extract_fraction_terms
from src.node import Scope
from src.node import ExpressionNode as N, Scope, OP_MUL
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -181,119 +181,82 @@ class TestRulesFractions(RulesTestCase):
(a, b), c = root = tree('a / b / c')
self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * c))
(a, b), c = root = tree('-a / b / c')
self.assertEqual(divide_fraction(root, (a, b, c)), -(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) = root = tree('-a / (b / c)')
self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
root = tree('a / -(b / c)')
self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
def test_match_extract_fraction_terms(self):
root, a, b, c = tree('ab / (ca), a, b, c')
n, d = root
self.assertEqualPos(match_equal_fraction_parts(root),
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
lscp = lambda l: Scope(N(OP_MUL, l))
n, d = root = tree('ab / a')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), a, a))])
n, d = root = tree('a / (ab)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (lscp(n), Scope(d), a, a))])
n, d = root = tree('abc / (cba)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_fraction_terms, (Scope(n), scope(d), a, a)),
P(root, extract_fraction_terms, (Scope(n), scope(d), b, b)),
P(root, extract_fraction_terms, (Scope(n), scope(d), c, c))])
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a)),
P(root, extract_fraction_terms, (Scope(n), Scope(d), b, b)),
P(root, extract_fraction_terms, (Scope(n), Scope(d), c, c))])
root = tree('a / a')
self.assertEqualPos(match_equal_fraction_parts(root), [])
(ap, b), aq = root = tree('a ^ p * b / a ^ q')
self.assertequalpos(match_equal_fraction_parts(root),
[p(root, extract_fraction_terms, (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_fraction_terms, (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_fraction_terms, (a, [ap, b], [a], 0, 0))])
#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))])
# root = tree('-a / a')
# 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))
# 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))
# (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)
self.assertEqualPos(match_extract_fraction_terms(root), [])
(ap, b), aq = n, d = root = tree('a ^ p * b / a ^ q')
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, aq))])
(a, b), aq = n, d = root = tree('a * b / a ^ q')
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), a, aq))])
(ap, b), a = n, d = root = tree('a ^ p * b / a')
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, a))])
def test_extract_fraction_terms_basic(self):
root, expect = tree('ab / (ca), a / a * (b / c)')
n, d = root
self.assertEqual(extract_fraction_terms(root,
(Scope(n), Scope(d), n[0], d[1])), expect)
def test_extract_fraction_terms_leaf(self):
root, expect = tree('ba / a, a / a * (b / 1)')
n, d = root
self.assertEqual(extract_fraction_terms(root,
(Scope(n), Scope(N(OP_MUL, d)), n[1], d)), expect)
root, expect = tree('a / (ab), a / a * (1 / b)')
n, d = root
self.assertEqual(extract_fraction_terms(root,
(Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect)
def test_extract_fraction_terms_chain(self):
self.assertRewrite([
'a ^ 3 * 4 / (a ^ 2 * 5)',
'a ^ 3 / a ^ 2 * (4 / 5)',
'a ^ (3 - 2)(4 / 5)',
'a ^ 1 * (4 / 5)',
'a(4 / 5)',
# FIXME: '4 / 5 * 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