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

Commenced some chanches to fraction rules (unit tests are still failing).

parent 42662f45
No related branches found
No related tags found
No related merge requests found
......@@ -9,9 +9,9 @@ from .powers import match_add_exponents, match_subtract_exponents, \
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_divide_fractions, match_equal_fraction_parts
from .fractions import match_constant_division, match_add_fractions, \
match_multiply_fractions, 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
......@@ -30,12 +30,11 @@ from src.rules.integrals import match_solve_indef, match_constant_integral, \
from src.rules.lineq import match_move_term
RULES = {
OP_ADD: [match_add_numerics, match_add_constant_fractions,
OP_ADD: [match_add_numerics, match_add_fractions,
match_combine_groups, match_add_quadrants,
match_add_logarithms],
OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents,
match_expand_and_add_fractions, match_multiply_zero,
match_negated_factor, match_multiply_one,
match_multiply_zero, match_negated_factor, match_multiply_one,
match_sort_multiplicants, match_multiply_fractions,
match_factor_in_multiplicant],
OP_DIV: [match_subtract_exponents, match_divide_numerics,
......
from itertools import combinations, product
from .utils import least_common_multiple, partition
from ..node import ExpressionLeaf as L, Scope, OP_DIV, OP_ADD, OP_MUL, \
OP_POW, nary_node, negate
from .utils import least_common_multiple, partition, is_numeric_node, \
evals_to_numeric
from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, OP_DIV, \
OP_ADD, OP_MUL, OP_POW, nary_node, negate
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
......@@ -68,51 +69,76 @@ def division_by_self(root, args):
MESSAGES[division_by_self] = _('Division of {1} by itself reduces to 1.')
def match_add_constant_fractions(node):
def match_add_fractions(node):
"""
1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4 # Equalize denominators
2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4
2 / 4 + 3 / 4 -> 5 / 4 # Equal denominators, so nominators can
# be added
2 / 4 - 3 / 4 -> -1 / 4
1 / 2 + 3 / 4 -> 4 / 8 + 6 / 8 # Equalize denominators by multiplying
# them with eachother
a / b + c / b and a, c in Z -> (a + c) / b
a / b + c / d and a, b, c, d in Z -> a' / e + c' / e # e = lcm(b, d)
# | e = b * d
a / b + c and a, b, c in Z -> a / b + b / b * c # =>* (a + bc) / b
"""
assert node.is_op(OP_ADD)
p = []
scope = Scope(node)
fractions, others = partition(lambda n: n.is_op(OP_DIV), scope)
numerics = filter(is_numeric_node, others)
fractions = filter(lambda node: node.is_op(OP_DIV), scope)
for a, b in combinations(fractions, 2):
na, da = a
nb, db = b
for ab, cd in combinations(fractions, 2):
a, b = ab
c, d = cd
if da == db:
if b == d:
# Equal denominators, add nominators to create a single fraction
p.append(P(node, add_nominators, (a, b)))
elif da.is_numeric() and db.is_numeric():
p.append(P(node, add_nominators, (scope, ab, cd)))
elif all(map(is_numeric_node, (a, b, c, d))):
# Denominators are both numeric, rewrite both fractions to the
# least common multiple of their denominators. Later, the
# nominators will be added
denom = least_common_multiple(da.value, db.value)
p.append(P(node, equalize_denominators, (scope, a, b, denom)))
lcm = least_common_multiple(b.value, d.value)
p.append(P(node, equalize_denominators, (scope, ab, cd, lcm)))
# Also, add the (non-recommended) possibility to multiply the
# denominators
p.append(P(node, equalize_denominators, (scope, a, b,
da.value * db.value)))
# denominators. Do this only if the multiplication is not equal to
# the least common multiple, to avoid duplicate possibilities
mult = b.value * d.value
if mult != lcm:
p.append(P(node, equalize_denominators, (scope, ab, cd, mult)))
for ab, c in product(fractions, numerics):
a, b = ab
if a.is_numeric() and b.is_numeric():
# Fraction of constants added to a constant -> create a single
# constant fraction
p.append(P(node, constant_to_fraction, (scope, ab, c)))
return p
def add_nominators(root, args):
"""
a / b + c / b and a, c in Z -> (a + c) / b
"""
scope, ab, cb = args
a, b = ab
c = cb[0]
# Replace the left node with the new expression, transfer fraction
# negations to nominators
scope.replace(ab, (a.negate(ab.negated) + c.negate(cb.negated)) / b)
scope.remove(cb)
return scope.as_nary_node()
MESSAGES[add_nominators] = \
_('Add the nominators of {2} and {3} to create a single fraction.')
def equalize_denominators(root, args):
"""
1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4
1 / 2 - 3 / 4 -> 2 / 4 - 3 / 4
a / 2 + b / 4 -> 2a / 4 + b / 4
a / b + c / d and a, b, c, d in Z -> a' / e + c' / e
"""
scope, denom = args[::3]
......@@ -132,65 +158,46 @@ def equalize_denominators(root, args):
return scope.as_nary_node()
MESSAGES[equalize_denominators] = _('Equalize the denominators of divisions'
' {2} and {3} to {4}.')
MESSAGES[equalize_denominators] = \
_('Equalize the denominators of divisions' ' {2} and {3} to {4}.')
def add_nominators(root, args):
def constant_to_fraction(root, args):
"""
a / b + c / b -> (a + c) / b
a / b - c / b -> (a - c) / b
-(a / b) + c / b -> -((a + c) / b)
-(a / b) - c / b -> (c - a) / -b
a / b + c and a, b, c in Z -> a / b + b / b * c # =>* (a + bc) / b
"""
# TODO: is 'add' Appropriate when rewriting to "(a + (-c)) / b"?
ab, cb = args
a, b = ab
scope = Scope(root)
# Replace the left node with the new expression
scope.replace(ab, (a + cb[0].negate(cb.negated)) / b)
# Remove the right node
scope.remove(cb)
scope, ab, c = args
b = ab[1]
scope.replace(c, b / b * c)
return scope.as_nary_node()
# TODO: convert this to a lambda. Example: 22 / 77 - 28 / 77. the "-" is above
# the "28/77" division.
MESSAGES[add_nominators] = _('Add the nominators of {1} and {2}.')
def match_expand_and_add_fractions(node):
"""
a * b / c + d * b / c -> (a + d) * (b / c)
a * b / c + (- d * b / c) -> (a + (-d)) * (b / c)
"""
# TODO: is 'add' Appropriate when rewriting to "(a + (-d)) / * (b / c)"?
assert node.is_op(OP_MUL)
p = []
return p
MESSAGES[constant_to_fraction] = \
_('Rewrite constant {3} to a fraction to be able to add it to {2}.')
def match_multiply_fractions(node):
"""
a / b * (c / d) -> ac / (bd)
a * (b / c) -> ab / c
a / b * c and a, c in Z or (a = 1 and eval(b) not in Z) -> ac / b
"""
assert node.is_op(OP_MUL)
p = []
scope = Scope(node)
fractions, others = partition(lambda n: n.is_op(OP_DIV), scope)
numerics = filter(is_numeric_node, others)
for ab, cd in combinations(fractions, 2):
p.append(P(node, multiply_fractions, (scope, ab, cd)))
for a, bc in product(others, fractions):
p.append(P(node, multiply_with_fraction, (scope, a, bc)))
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)):
p.append(P(node, multiply_with_fraction, (scope, ab, c)))
return p
......@@ -203,7 +210,7 @@ def multiply_fractions(root, args):
a, b = ab
c, d = cd
scope.replace(ab, a * c / (b * d))
scope.replace(ab, (a * c / (b * d)).negate(ab.negated + cd.negated))
scope.remove(cd)
return scope.as_nary_node()
......@@ -214,18 +221,19 @@ MESSAGES[multiply_fractions] = _('Multiply fractions {2} and {3}.')
def multiply_with_fraction(root, args):
"""
a * (b / c) -> ab / c
a / b * c and a, c in Z or a == 1 -> ac / b
"""
scope, a, bc = args
b, c = bc
scope, ab, c = args
a, b = ab
scope.replace(a, a * b / c)
scope.remove(bc)
scope.replace(ab, (a * c / b).negate(ab.negated))
scope.remove(c)
return scope.as_nary_node()
MESSAGES[multiply_with_fraction] = _('Multiply {2} with fraction {3}.')
MESSAGES[multiply_with_fraction] = \
_('Multiply {3} with the nominator of fraction {2}.')
def match_divide_fractions(node):
......@@ -306,32 +314,48 @@ def fraction_scopes(node):
nominator, denominator = node
if nominator.is_op(OP_MUL):
n_scope = list(Scope(nominator))
n_scope = Scope(nominator)
else:
n_scope = [nominator]
n_scope = Scope(N(OP_MUL, nominator))
if denominator.is_op(OP_MUL):
d_scope = list(Scope(denominator))
d_scope = Scope(denominator)
else:
d_scope = [denominator]
d_scope = Scope(N(OP_MUL, denominator))
return n_scope, d_scope
def is_power_combination(a, b):
"""
Check if two nodes are powers that can be combined in a fraction, for
example:
a and a^2
a^2 and a^2
a^2 and a
"""
if a.is_power():
a = a[0]
if b.is_power():
b = b[0]
return a == b
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
a ^ b * c / (a ^ d * e) -> a ^ b / a ^ d * (c / e)
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
#If the same root appears in both nominator and denominator, extract 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)
......@@ -343,88 +367,111 @@ def match_equal_fraction_parts(node):
return p
# Look for matching parts in scopes
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)))
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)))
return p
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):
def extract_fraction_terms(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 ^ b * c / (a ^ d * e) -> a ^ b / a ^ d * (c / e)
"""
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}.')
n_scope, d_scope, n, d = args
if len(n_scope) == 1:
n_scope.replace(n, L(1))
else:
n_scope.remove(n)
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)
if len(d_scope) == 1:
d_scope.replace(d, L(1))
else:
d_scope.remove(n)
return ap / aq * nom.negate(n.negated) / denom.negate(d.negated)
return n / d * (n_scope.as_nary_node() / d_scope.as_nary_node())
MESSAGES[extract_divided_roots] = \
_('Extract the root {1} from nominator and denominator in {0}.')
#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}.')
from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_MUL, OP_DIV
from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_MUL, OP_DIV, \
OP_ADD, OP_POW, OP_SQRT
def greatest_common_divisor(a, b):
......@@ -132,3 +133,23 @@ def divides(m, n):
Check if m | n (m divides n).
"""
return not divmod(n, m)[1]
def is_numeric_node(node):
"""
Check if a node is numeric.
"""
return node.is_numeric()
def evals_to_numeric(node):
"""
Check if a node will eventually evaluate to a numeric value, by checking if
all leaves are numeric and there are only operators that can be
considerered a constant or will evaluate to one (+, *, /, ^, sqrt).
"""
if node.is_leaf:
return node.is_numeric()
return node.op in (OP_ADD, OP_MUL, OP_DIV, OP_POW, OP_SQRT) \
and all(map(evals_to_numeric, node))
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_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_parts, extract_divided_roots
constant_to_fraction, extract_fraction_terms
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -49,44 +49,51 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualNodes(division_by_self(root, ()), one)
def test_match_add_constant_fractions(self):
def test_match_add_fractions(self):
a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
n0, n1 = root = l1 / l2 + l3 / l4
possibilities = match_add_constant_fractions(root)
possibilities = match_add_fractions(root)
self.assertEqualPos(possibilities,
[P(root, equalize_denominators, (Scope(root), n0, n1, 4)),
P(root, equalize_denominators, (Scope(root), n0, n1, 8))])
(((n0, n1), n2), n3), n4 = root = a + l1 / l2 + b + l3 / l4 + c
possibilities = match_add_constant_fractions(root)
possibilities = match_add_fractions(root)
self.assertEqualPos(possibilities,
[P(root, equalize_denominators, (Scope(root), n1, n3, 4)),
P(root, equalize_denominators, (Scope(root), n1, n3, 8))])
n0, n1 = root = l2 / l4 + l3 / l4
possibilities = match_add_constant_fractions(root)
possibilities = match_add_fractions(root)
self.assertEqualPos(possibilities,
[P(root, add_nominators, (n0, n1))])
[P(root, add_nominators, (Scope(root), n0, n1))])
(((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + l3 / l4 + c
possibilities = match_add_constant_fractions(root)
possibilities = match_add_fractions(root)
self.assertEqualPos(possibilities,
[P(root, add_nominators, (n1, n3))])
[P(root, add_nominators, (Scope(root), n1, n3))])
def test_add_constant_fractions_with_negation(self):
def test_match_add_fractions_constant_to_fraction(self):
l23, l1 = root = tree('2 / 3 + 1')
self.assertEqualPos(match_add_fractions(root),
[P(root, constant_to_fraction, (Scope(root), l23, l1))])
def test_add_fractions_with_negation(self):
a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
(((n0, n1), n2), n3), n4 = root = a + l2 / l2 + b + (-l3 / l4) + c
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
self.assertEqualPos(match_add_fractions(root),
[P(root, equalize_denominators, (Scope(root), n1, n3, 4)),
P(root, equalize_denominators, (Scope(root), n1, n3, 8))])
n0, n1 = root = l1 / l2 + l4 / l3
self.assertEqualPos(match_add_fractions(root),
[P(root, equalize_denominators, (Scope(root), n0, n1, 6))])
(((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + (-l3 / l4) + c
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, add_nominators, (n1, n3))])
self.assertEqualPos(match_add_fractions(root),
[P(root, add_nominators, (Scope(root), n1, n3))])
def test_equalize_denominators(self):
a, b, l1, l2, l3, l4 = tree('a,b,1,2,3,4')
......@@ -113,30 +120,44 @@ class TestRulesFractions(RulesTestCase):
def test_add_nominators(self):
a, b, c = tree('a,b,c')
n0, n1 = root = a / b + c / b
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + c) / b)
self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
(a + c) / b)
n0, n1 = root = a / b + -c / b
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + -c) / b)
self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
(a + -c) / b)
n0, n1 = root = a / b + -(c / b)
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + -c) / b)
self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
(a + -c) / b)
n0, n1 = root = a / -b + c / -b
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + c) / -b)
self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
(a + c) / -b)
n0, n1 = root = a / -b + -c / -b
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + -c) / -b)
self.assertEqualNodes(add_nominators(root, (Scope(root), n0, n1)),
(a + -c) / -b)
def test_constant_to_fraction(self):
root, e = tree('2 / 3 + 1, 2 / 3 + 3 / 3 * 1')
l23, l1 = root
self.assertEqual(constant_to_fraction(root, (Scope(root), l23, l1)), e)
def test_match_multiply_fractions(self):
(a, b), (c, d) = ab, cd = root = tree('a / b * (c / d)')
self.assertEqualPos(match_multiply_fractions(root),
[P(root, multiply_fractions, (Scope(root), ab, cd))])
(ab, e), cd = root = tree('a / b * e * (c / d)')
(ab, e), cd = root = tree('4 / b * 2 * (3 / d)')
self.assertEqualPos(match_multiply_fractions(root),
[P(root, multiply_fractions, (Scope(root), ab, cd)),
P(root, multiply_with_fraction, (Scope(root), e, ab)),
P(root, multiply_with_fraction, (Scope(root), e, cd))])
P(root, multiply_with_fraction, (Scope(root), ab, e)),
P(root, multiply_with_fraction, (Scope(root), cd, e))])
ab, c = root = tree('1 / sqrt(3) * 2')
self.assertEqualPos(match_multiply_fractions(root),
[P(root, multiply_with_fraction, (Scope(root), ab, c))])
def test_multiply_fractions(self):
(a, b), (c, d) = ab, cd = root = tree('a / b * (c / d)')
......@@ -165,78 +186,114 @@ class TestRulesFractions(RulesTestCase):
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)')
root, a, b, c = tree('ab / (ca), a, b, c')
n, d = root
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [a, b], [c, a], 0, 1))])
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
(a, b), a = root = tree('ab / a')
n, d = root = tree('ab / a')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [a, b], [a], 0, 0))])
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
a, (a, b) = root = tree('a / (ab)')
n, d = root = tree('a / (ab)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, divide_fraction_parts, (a, [a], [a, b], 0, 0))])
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
root = tree('abc / (cba)')
((a, b), c) = root[0]
s0, s1 = [a, b, c], [c, b, a]
n, d = root = tree('abc / (cba)')
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))])
[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_divided_roots, (a, [ap, b], [aq], 0, 0))])
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_divided_roots, (a, [a, b], [aq], 0, 0))])
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_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('(-a)b / 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_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)
from src.rules import utils
from src.rules.utils import least_common_multiple, is_fraction, partition, \
find_variables, first_sorted_variable, find_variable, substitute, \
divides
divides, evals_to_numeric
from tests.rulestestcase import tree, RulesTestCase
......@@ -65,3 +65,13 @@ class TestRulesUtils(RulesTestCase):
self.assertTrue(divides(7, 21))
self.assertFalse(divides(4, 2))
self.assertFalse(divides(2, 3))
def test_evals_to_numeric(self):
self.assertTrue(evals_to_numeric(tree('1')))
self.assertFalse(evals_to_numeric(tree('a')))
self.assertTrue(evals_to_numeric(tree('1 + 2')))
self.assertFalse(evals_to_numeric(tree('1 + a')))
self.assertTrue(evals_to_numeric(tree('1 + 2 / 2 * 9')))
self.assertFalse(evals_to_numeric(tree('int 1')))
self.assertFalse(evals_to_numeric(tree('int a')))
self.assertTrue(evals_to_numeric(tree('sqrt 1')))
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