Răsfoiți Sursa

Added absolute value to linear equation rules.

Taddeus Kroes 14 ani în urmă
părinte
comite
7d0df6c923
3 a modificat fișierele cu 50 adăugiri și 7 ștergeri
  1. 28 6
      src/rules/lineq.py
  2. 5 0
      tests/rulestestcase.py
  3. 17 1
      tests/test_rules_lineq.py

+ 28 - 6
src/rules/lineq.py

@@ -1,6 +1,6 @@
-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, \
-        eq
+        eq, OP_ABS
 from ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 
@@ -23,18 +23,23 @@ def match_move_term(node):
     # Multiplication
     x / a = b  ->  x / a * a = b * a  # =>*  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)
 
     x = find_variable(node)
     left, right = node
+    p = []
 
-    # Swap the left and right side if only the right side 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
     if left.is_op(OP_ADD):
@@ -62,6 +67,10 @@ def match_move_term(node):
     if left.negated:
         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
 
 
@@ -117,3 +126,16 @@ def multiply_term(root, args):
 
 
 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.')

+ 5 - 0
tests/rulestestcase.py

@@ -3,6 +3,7 @@ import doctest
 
 from src.node import ExpressionNode
 from src.parser import Parser
+from src.validation import validate
 from tests.parser import ParserWrapper
 
 
@@ -73,3 +74,7 @@ class RulesTestCase(unittest.TestCase):
             e.args = (e.message,) + e.args[1:]
 
             raise
+
+    def assertValidate(self, exp, result):
+        self.assertTrue(validate(exp, result),
+                        'Validation failed: %s  !=>  %s')

+ 17 - 1
tests/test_rules_lineq.py

@@ -1,5 +1,5 @@
 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.possibilities import Possibility as P
 from tests.rulestestcase import RulesTestCase, tree
@@ -41,6 +41,15 @@ class TestRulesLineq(RulesTestCase):
         self.assertEqualPos(match_move_term(root),
                 [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):
         root, expect = tree('a = bx, bx = a')
         self.assertEqual(swap_sides(root, ()), expect)
@@ -57,6 +66,13 @@ class TestRulesLineq(RulesTestCase):
         root, a, expect = tree('x / a = b, a, x / a * a = b * a')
         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):
         self.assertRewrite([
             '2x + 3 = -3x - 2',