Commit 7d0df6c9 authored by Taddeus Kroes's avatar Taddeus Kroes

Added absolute value to linear equation rules.

parent 03feca7a
from .utils import find_variable from .utils import find_variable, evals_to_numeric
from ..node import ExpressionLeaf as L, Scope, OP_EQ, OP_ADD, OP_MUL, OP_DIV, \ from ..node import ExpressionLeaf as L, Scope, OP_EQ, OP_ADD, OP_MUL, OP_DIV, \
eq eq, OP_ABS
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -24,17 +24,22 @@ def match_move_term(node): ...@@ -24,17 +24,22 @@ def match_move_term(node):
x / a = b -> x / a * a = b * a # =>* x = a * b x / a = b -> x / a * a = b * a # =>* x = a * b
a / x = b -> a / x * x = b * x # =>* x = a / b a / x = b -> a / x * x = b * x # =>* x = a / b
-x = b -> -x * -1 = b * -1 # =>* x = -b -x = b -> -x * -1 = b * -1 # =>* x = -b
# Absolute value
|f(x)| = c and eval(c) in Z -> f(x) = c vv f(x) = -c
""" """
assert node.is_op(OP_EQ) assert node.is_op(OP_EQ)
x = find_variable(node) x = find_variable(node)
left, right = node left, right = node
p = []
# Swap the left and right side if only the right side contains x
if not left.contains(x): if not left.contains(x):
return [P(node, swap_sides)] # Swap the left and right side if only the right side contains x
if right.contains(x):
p.append(P(node, swap_sides))
p = [] return p
# Bring terms without x to the right # Bring terms without x to the right
if left.is_op(OP_ADD): if left.is_op(OP_ADD):
...@@ -62,6 +67,10 @@ def match_move_term(node): ...@@ -62,6 +67,10 @@ def match_move_term(node):
if left.negated: if left.negated:
p.append(P(node, multiply_term, (-L(1),))) p.append(P(node, multiply_term, (-L(1),)))
# Split absolute equations into two separate, non-absolute equations
if left.is_op(OP_ABS) and evals_to_numeric(right):
p.append(P(node, split_absolute_equation))
return p return p
...@@ -117,3 +126,16 @@ def multiply_term(root, args): ...@@ -117,3 +126,16 @@ def multiply_term(root, args):
MESSAGES[multiply_term] = _('Multiply both sides of the equation with {1}.') MESSAGES[multiply_term] = _('Multiply both sides of the equation with {1}.')
def split_absolute_equation(root, args):
"""
|f(x)| = c and eval(c) in Z -> f(x) = c vv f(x) = -c
"""
(f,), c = root
return eq(f, c) | eq(f, -c)
MESSAGES[split_absolute_equation] = _('Split absolute equation {0} into a ' \
'negative and a positive equation.')
...@@ -3,6 +3,7 @@ import doctest ...@@ -3,6 +3,7 @@ import doctest
from src.node import ExpressionNode from src.node import ExpressionNode
from src.parser import Parser from src.parser import Parser
from src.validation import validate
from tests.parser import ParserWrapper from tests.parser import ParserWrapper
...@@ -73,3 +74,7 @@ class RulesTestCase(unittest.TestCase): ...@@ -73,3 +74,7 @@ class RulesTestCase(unittest.TestCase):
e.args = (e.message,) + e.args[1:] e.args = (e.message,) + e.args[1:]
raise raise
def assertValidate(self, exp, result):
self.assertTrue(validate(exp, result),
'Validation failed: %s !=> %s')
from src.rules.lineq import match_move_term, swap_sides, subtract_term, \ from src.rules.lineq import match_move_term, swap_sides, subtract_term, \
divide_term, multiply_term divide_term, multiply_term, split_absolute_equation
from src.node import Scope from src.node import Scope
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree from tests.rulestestcase import RulesTestCase, tree
...@@ -41,6 +41,15 @@ class TestRulesLineq(RulesTestCase): ...@@ -41,6 +41,15 @@ class TestRulesLineq(RulesTestCase):
self.assertEqualPos(match_move_term(root), self.assertEqualPos(match_move_term(root),
[P(root, multiply_term, (l1,))]) [P(root, multiply_term, (l1,))])
def test_match_move_term_absolute(self):
root = tree('|x| = 2')
self.assertEqualPos(match_move_term(root),
[P(root, split_absolute_equation)])
root = tree('|x - 1| = 2')
self.assertEqualPos(match_move_term(root),
[P(root, split_absolute_equation)])
def test_swap_sides(self): def test_swap_sides(self):
root, expect = tree('a = bx, bx = a') root, expect = tree('a = bx, bx = a')
self.assertEqual(swap_sides(root, ()), expect) self.assertEqual(swap_sides(root, ()), expect)
...@@ -57,6 +66,13 @@ class TestRulesLineq(RulesTestCase): ...@@ -57,6 +66,13 @@ class TestRulesLineq(RulesTestCase):
root, a, expect = tree('x / a = b, a, x / a * a = b * a') root, a, expect = tree('x / a = b, a, x / a * a = b * a')
self.assertEqual(multiply_term(root, (a,)), expect) self.assertEqual(multiply_term(root, (a,)), expect)
def test_split_absolute_equation(self):
root, expect = tree('|x| = 2, x = 2 vv x = -2')
self.assertEqual(split_absolute_equation(root, ()), expect)
# FIXME: following call exeeds recursion limit
# FIXME: self.assertValidate('|x - 1| = 2', 'x = -1 vv x = 3')
def test_match_move_term_chain_negation(self): def test_match_move_term_chain_negation(self):
self.assertRewrite([ self.assertRewrite([
'2x + 3 = -3x - 2', '2x + 3 = -3x - 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