Commit 712dbdbc authored by Taddeus Kroes's avatar Taddeus Kroes

Added rules (and tests) for sorting polynomes and monomials.

parent 464b95ee
......@@ -14,7 +14,7 @@ from .fractions import match_constant_division, match_add_fractions, \
match_extract_fraction_terms, match_division_in_denominator
from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division
from .sort import match_sort_polynome, match_sort_molinome
from .sort import match_sort_polynome, match_sort_monomial
from .goniometry import match_add_quadrants, match_negated_parameter, \
match_half_pi_subtraction, match_standard_radian
from .derivatives import match_zero_derivative, \
......@@ -36,11 +36,11 @@ from .sqrt import match_reduce_sqrt
RULES = {
OP_ADD: [match_add_numerics, match_add_fractions,
match_combine_groups, match_add_quadrants,
match_add_logarithms],
match_add_logarithms, match_sort_polynome],
OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents,
match_multiply_zero, match_negated_factor,
match_multiply_fractions, match_factor_in_multiplicant,
match_sort_molinome],
match_sort_monomial],
OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_divide_fractions,
match_negated_division, match_extract_fraction_terms,
......
......@@ -98,4 +98,5 @@ IMPLICIT_RULES = [
remove_power_of_one,
negated_factor,
add_numerics,
swap_factors,
]
from .utils import iter_pairs
from .utils import iter_pairs, evals_to_numeric
from ..node import ExpressionNode as N, Scope, OP_ADD, OP_MUL
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
def swap_moni((left, right)):
left_num, right_num = left.is_numeric(), right.is_numeric()
def get_power_prop(node):
"""
Get the power properties of a node needed for sorting, being the variable
name and exponent value.
"""
if node.is_variable():
return node.value, 1
if node.is_power():
root, exponent = node
if root.is_variable():
if exponent.is_numeric():
# Numeric exponent: value can be compared to other exponents
exp = exponent.actual_value()
else:
# No numeric exponent: same precedence as the variable itself
exp = 1
return root.value, exp
def swap_mono((left, right)):
"""
Check if a pair of left and right multiplication factors in a monomial
should be swapped to be correctly sorted.
"""
# Numerics should always be on the left of a monomial
if evals_to_numeric(right):
return not evals_to_numeric(left)
left_prop = get_power_prop(left)
right_prop = get_power_prop(right)
if left_prop and right_prop:
left_var, left_exp = left_prop
right_var, right_exp = right_prop
if left_var == right_var:
# Same variable, compare exponents
return left_exp > right_exp
# Compare variable names alphabetically
return left_var > right_var
if left_num and right_num:
return False
left_var, right_var = left.is_variable(), right.is_variable()
if left_var and right_var:
return cmp(left.value, right.value)
def get_poly_prop(node):
"""
Get the polynome properties of an monomial in a polynome, being the leading
variable name and the exponent it is raised to.
"""
if node.is_op(OP_MUL):
scope = Scope(node)
power = None
for n in scope:
new_power = get_power_prop(n)
if new_power:
if not power:
var, exp = power = new_power
continue
if left_var and right_num:
return True
new_var, new_exp = new_power
if (new_exp > exp if new_var == var else new_var < var):
var, exp = power = new_power
return power
return get_power_prop(node)
def swap_poly((left, right)):
pass
"""
Check if a pair of left and right addition factors in a polynomial should
be swapped to be correctly sorted.
"""
left_poly = get_poly_prop(left)
right_poly = get_poly_prop(right)
if not left_poly:
return bool(right_poly)
elif not right_poly:
return False
def match_sort_polynome(node):
left_var, left_exp = left_poly
right_var, right_exp = right_poly
if left_var == right_var:
# Same variable, compare exponents
return left_exp < right_exp
# Compare variable names alphabetically
return left_var > right_var
def match_sort_monomial(node):
"""
Sort a monomial, pursuing the following form:
x^0 * x^1 * ... * x^n
For example: 2xx^2
"""
assert node.is_op(OP_ADD)
assert node.is_op(OP_MUL)
scope = Scope(node)
return [P(node, swap_factors, (scope, l, r))
for l, r in filter(swap_poly, iter_pairs(scope))]
for l, r in filter(swap_mono, iter_pairs(scope))]
def match_sort_molinome(node):
def match_sort_polynome(node):
"""
x * 2 -> 2x # variable > number
x ^ 2 * x ^ 3 -> x ^ 3 * x ^ 2 # exponents
yx -> xy # alphabetically
Sort a polynome, pursuing the following form:
c_n * x^n * ... * c_1 * x^1 * c_0 * x^0
For example: 2x^2 + x - 3
"""
assert node.is_op(OP_MUL)
assert node.is_op(OP_ADD)
scope = Scope(node)
return [P(node, swap_factors, (scope, l, r))
for l, r in filter(swap_moni, iter_pairs(scope))]
for l, r in filter(swap_poly, iter_pairs(scope))]
def swap_factors(root, args):
"""
left * right
f * g -> g * f
"""
scope, left, right = args
......@@ -60,4 +143,4 @@ def swap_factors(root, args):
return scope.as_nary_node()
MESSAGES[swap_factors] = _('Move {2} to the left of {1}.')
MESSAGES[swap_factors] = _('Move {3} to the left of {2}.')
from src.rules.sort import match_sort_polynome, swap_factors, \
match_sort_molinome, iter_pairs
from src.rules.sort import match_sort_polynome, swap_factors, get_poly_prop, \
match_sort_monomial, iter_pairs, swap_mono, swap_poly, get_power_prop
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -7,21 +7,74 @@ from tests.rulestestcase import RulesTestCase, tree
class TestRulesSort(RulesTestCase):
def test_match_sort_molinome_constant(self):
def test_swap_mono(self):
self.assertTrue(swap_mono(tree('x, 1 / 2')))
self.assertFalse(swap_mono(tree('2, 1 / 2')))
self.assertTrue(swap_mono(tree('x, 2')))
self.assertFalse(swap_mono(tree('2, x')))
self.assertTrue(swap_mono(tree('x ^ 2, 2')))
self.assertTrue(swap_mono(tree('y, x')))
self.assertFalse(swap_mono(tree('x, y')))
self.assertFalse(swap_mono(tree('x, x')))
self.assertTrue(swap_mono(tree('x ^ 3, x ^ 2')))
self.assertFalse(swap_mono(tree('x ^ 2, x ^ 3')))
def test_swap_poly(self):
self.assertTrue(swap_poly(tree('2, x')))
self.assertFalse(swap_poly(tree('x, 2')))
self.assertTrue(swap_poly(tree('a, a^2')))
self.assertFalse(swap_poly(tree('a^2, a')))
self.assertTrue(swap_poly(tree('y, x')))
self.assertFalse(swap_poly(tree('x, y')))
self.assertFalse(swap_poly(tree('x, x')))
self.assertFalse(swap_poly(tree('x ^ 3, x ^ 2')))
self.assertTrue(swap_poly(tree('x ^ 2, x ^ 3')))
def test_get_power_prop(self):
self.assertEqual(get_power_prop(tree('a')), ('a', 1))
self.assertEqual(get_power_prop(tree('a ^ b')), ('a', 1))
self.assertEqual(get_power_prop(tree('a ^ 2')), ('a', 2))
self.assertEqual(get_power_prop(tree('a ^ -2')), ('a', -2))
self.assertIsNone(get_power_prop(tree('1')))
def test_get_poly_prop(self):
self.assertEqual(get_poly_prop(tree('a ^ 2')), ('a', 2))
self.assertEqual(get_poly_prop(tree('2a ^ 2')), ('a', 2))
self.assertEqual(get_poly_prop(tree('ca ^ 2 * 2')), ('a', 2))
self.assertEqual(get_poly_prop(tree('ab ^ 2')), ('a', 1))
self.assertEqual(get_poly_prop(tree('a^3 * a^2')), ('a', 3))
self.assertEqual(get_poly_prop(tree('a^2 * a^3')), ('a', 3))
self.assertIsNone(get_poly_prop(tree('1')))
def test_match_sort_monomial_constant(self):
x, l2 = root = tree('x * 2')
self.assertEqualPos(match_sort_molinome(root),
self.assertEqualPos(match_sort_monomial(root),
[P(root, swap_factors, (Scope(root), x, l2))])
root = tree('2x')
self.assertEqualPos(match_sort_molinome(root), [])
self.assertEqualPos(match_sort_monomial(root), [])
def test_match_sort_monomial_variables(self):
y, x = root = tree('yx')
self.assertEqualPos(match_sort_monomial(root),
[P(root, swap_factors, (Scope(root), y, x))])
root = tree('xy')
self.assertEqualPos(match_sort_monomial(root), [])
#def test_match_sort_molinome_variables(self):
# y, x = root = tree('yx')
# self.assertEqualPos(match_sort_molinome(root),
# [P(root, swap_factors, (Scope(root), y, x))])
def test_match_sort_polynome(self):
x, x2 = root = tree('x + x ^ 2')
self.assertEqualPos(match_sort_polynome(root),
[P(root, swap_factors, (Scope(root), x, x2))])
# root = tree('xy')
# self.assertEqualPos(match_sort_molinome(root), [])
root = tree('x + 2')
self.assertEqualPos(match_sort_polynome(root), [])
def test_swap_factors(self):
x, l2 = root = tree('x * 2')
......
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