Commit f5a7c1cd authored by Taddeus Kroes's avatar Taddeus Kroes

Added rules to divide/multiply both sides of an equation with a constant.

parent 694854ae
......@@ -27,7 +27,7 @@ from src.rules.logarithmic import match_constant_logarithm, \
from src.rules.integrals import match_solve_indef, match_constant_integral, \
match_integrate_variable_power, match_factor_out_constant, \
match_division_integral, match_function_integral
from src.rules.lineq import match_subtract_term
from src.rules.lineq import match_move_term
RULES = {
OP_ADD: [match_add_numerics, match_add_constant_fractions,
......@@ -61,5 +61,5 @@ RULES = {
match_factor_out_constant, match_division_integral,
match_function_integral],
OP_INT_INDEF: [match_solve_indef],
OP_EQ: [match_subtract_term],
OP_EQ: [match_move_term],
}
from .utils import find_variable
from ..node import Scope, OP_EQ, OP_ADD, OP_MUL, eq
from ..node import Scope, OP_EQ, OP_ADD, OP_MUL, OP_DIV, eq
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
def match_subtract_term(node):
def match_move_term(node):
"""
Perform the same action on both sides of the equation such that variable
terms are moved to the left, and constants (in relation to the variable
that is being solved) are brought to the right side of the equation.
If the variable is only present on the right side of the equation, swap the
sides first.
# Swap
a = b * x -> b * x = a
# Subtraction
x + a = b -> x + a - a = b - a
x = b + cx -> x + - cx = b + cx - cx
a = b + x -> a - x = b + x - x # =>* x = b / a
# Division
x * a = b -> x * a / a = b / a # =>* x = b / a
# Multiplication
x / a = b -> x / a * a = b * a # =>* x = a * b
a / x = b -> a / x * x = b * x # =>* x = a / b
"""
assert node.is_op(OP_EQ)
x = find_variable(node)
p = []
left, right = node
# Swap the left and right side if only the right side contains x
if not left.contains(x):
return [P(node, swap_sides)]
p = []
# Bring terms without x to the right
if left.is_op(OP_ADD):
for n in Scope(left):
# Bring terms without x to the right
if not n.contains(x):
p.append(P(node, subtract_term, (n,)))
# Bring terms with x to the left
if right.is_op(OP_ADD):
for n in Scope(right):
# Bring terms with x to the left
if n.contains(x):
p.append(P(node, subtract_term, (n,)))
# Divide both sides by a constant to 'free' x
if left.is_op(OP_MUL):
for n in Scope(left):
if not n.contains(x):
p.append(P(node, divide_term, (n,)))
# Multiply both sides by the denominator to move x out of the division
if left.is_op(OP_DIV):
p.append(P(node, multiply_term, (left[1],)))
return p
def swap_sides(root, args):
"""
a = bx -> bx = a
"""
left, right = root
return eq(right, left)
MESSAGES[swap_sides] = _('Swap the left and right side of the equation so ' \
'that the variable is on the left side.')
def subtract_term(root, args):
"""
x + a = b -> x + a - a = b - a
x = b + cx -> x + - cx = b + cx - cx
a = b + x -> a - x = b + x - x
"""
left, right = root
term = args[0]
......@@ -42,3 +84,30 @@ def subtract_term(root, args):
MESSAGES[subtract_term] = _('Subtract {1} from both sides of the equation.')
def divide_term(root, args):
"""
x * a = b -> x * a / a = b / a # =>* x = b / a
"""
left, right = root
term = args[0]
return eq(left / term, right / term)
MESSAGES[divide_term] = _('Divide both sides of the equation by {1}.')
def multiply_term(root, args):
"""
x / a = b -> x / a * a = b * a # =>* x = a * b
a / x = b -> a / x * x = b * x # =>* x = a / b
"""
left, right = root
term = args[0]
return eq(left * term, right * term)
MESSAGES[multiply_term] = _('Multiply both sides of the equation with {1}.')
from src.rules.lineq import match_subtract_term, \
subtract_term
from src.rules.lineq import match_move_term, swap_sides, subtract_term, \
divide_term, multiply_term
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -7,15 +7,70 @@ from tests.rulestestcase import RulesTestCase, tree
class TestRulesLineq(RulesTestCase):
def test_match_subtract_term(self):
def test_match_move_term_swap(self):
root = tree('x = b')
self.assertEqualPos(match_move_term(root), [])
root = tree('a = bx')
self.assertEqualPos(match_move_term(root), [P(root, swap_sides)])
def test_match_move_term_subtract(self):
root, a = tree('x + a = b, a')
self.assertEqualPos(match_subtract_term(root),
self.assertEqualPos(match_move_term(root),
[P(root, subtract_term, (a,))])
root, cx = tree('x = b + cx, cx')
self.assertEqualPos(match_subtract_term(root),
self.assertEqualPos(match_move_term(root),
[P(root, subtract_term, (cx,))])
def test_match_move_term_divide(self):
root, a = tree('ax = b, a')
self.assertEqualPos(match_move_term(root),
[P(root, divide_term, (a,))])
def test_match_move_term_multiply(self):
root, a = tree('x / a = b, a')
self.assertEqualPos(match_move_term(root),
[P(root, multiply_term, (a,))])
root, x = tree('a / x = b, x')
self.assertEqualPos(match_move_term(root),
[P(root, multiply_term, (x,))])
def test_swap_sides(self):
root, expect = tree('a = bx, bx = a')
self.assertEqual(swap_sides(root, ()), expect)
def test_subtract_term(self):
root, a, expect = tree('x + a = b, a, x + a - a = b - a')
self.assertEqual(subtract_term(root, (a,)), expect)
def test_divide_term(self):
root, a, expect = tree('x * a = b, a, x * a / a = b / a')
self.assertEqual(divide_term(root, (a,)), expect)
def test_multiply_term(self):
root, a, expect = tree('x / a = b, a, x / a * a = b * a')
self.assertEqual(multiply_term(root, (a,)), expect)
def test_match_move_term_chain(self):
self.assertRewrite([
'2x + 3 = -3x - 2',
'2x + 3 - 3 = -3x - 2 - 3',
'2x + 0 = -3x - 2 - 3',
'2x = -3x - 2 - 3',
'2x = -3x - 5',
'2x - -3x = -3x - 5 - -3x',
'2x + 3x = -3x - 5 - -3x',
'(2 + 3)x = -3x - 5 - -3x',
'5x = -3x - 5 - -3x',
'5x = -3x - 5 + 3x',
'5x = (-3 + 3)x - 5',
'5x = 0x - 5',
'5x = 0 - 5',
'5x = -5',
'5x / 5 = -5 / 5',
'x / 1 = -5 / 5',
'x = -5 / 5',
'x = -1',
])
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