Просмотр исходного кода

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

Taddeus Kroes 14 лет назад
Родитель
Сommit
f5a7c1cda5
3 измененных файлов с 140 добавлено и 16 удалено
  1. 2 2
      src/rules/__init__.py
  2. 78 9
      src/rules/lineq.py
  3. 60 5
      tests/test_rules_lineq.py

+ 2 - 2
src/rules/__init__.py

@@ -27,7 +27,7 @@ from src.rules.logarithmic import match_constant_logarithm, \
 from src.rules.integrals import match_solve_indef, match_constant_integral, \
 from src.rules.integrals import match_solve_indef, match_constant_integral, \
         match_integrate_variable_power, match_factor_out_constant, \
         match_integrate_variable_power, match_factor_out_constant, \
         match_division_integral, match_function_integral
         match_division_integral, match_function_integral
-from src.rules.lineq import match_subtract_term
+from src.rules.lineq import match_move_term
 
 
 RULES = {
 RULES = {
         OP_ADD: [match_add_numerics, match_add_constant_fractions,
         OP_ADD: [match_add_numerics, match_add_constant_fractions,
@@ -61,5 +61,5 @@ RULES = {
                  match_factor_out_constant, match_division_integral,
                  match_factor_out_constant, match_division_integral,
                  match_function_integral],
                  match_function_integral],
         OP_INT_INDEF: [match_solve_indef],
         OP_INT_INDEF: [match_solve_indef],
-        OP_EQ: [match_subtract_term],
+        OP_EQ: [match_move_term],
         }
         }

+ 78 - 9
src/rules/lineq.py

@@ -1,39 +1,81 @@
 from .utils import find_variable
 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 ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 from ..translate import _
 
 
 
 
-def match_subtract_term(node):
+def match_move_term(node):
     """
     """
-    x + a = b   ->  x + a - a = b - a
-    x = b + cx  ->  x + - cx = b + cx - cx
+    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
+    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)
     assert node.is_op(OP_EQ)
 
 
     x = find_variable(node)
     x = find_variable(node)
-    p = []
     left, right = node
     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):
     if left.is_op(OP_ADD):
         for n in Scope(left):
         for n in Scope(left):
-            # Bring terms without x to the right
             if not n.contains(x):
             if not n.contains(x):
                 p.append(P(node, subtract_term, (n,)))
                 p.append(P(node, subtract_term, (n,)))
 
 
+    # Bring terms with x to the left
     if right.is_op(OP_ADD):
     if right.is_op(OP_ADD):
         for n in Scope(right):
         for n in Scope(right):
-            # Bring terms with x to the left
             if n.contains(x):
             if n.contains(x):
                 p.append(P(node, subtract_term, (n,)))
                 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
     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):
 def subtract_term(root, args):
     """
     """
-    x + a = b   ->  x + a - a = b - a
-    x = b + cx  ->  x + - cx = b + cx - cx
+    x + a = b  ->  x + a - a = b - a
+    a = b + x  ->  a - x = b + x - x
     """
     """
     left, right = root
     left, right = root
     term = args[0]
     term = args[0]
@@ -42,3 +84,30 @@ def subtract_term(root, args):
 
 
 
 
 MESSAGES[subtract_term] = _('Subtract {1} from both sides of the equation.')
 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}.')

+ 60 - 5
tests/test_rules_lineq.py

@@ -1,5 +1,5 @@
-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.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
@@ -7,15 +7,70 @@ from tests.rulestestcase import RulesTestCase, tree
 
 
 class TestRulesLineq(RulesTestCase):
 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')
         root, a = tree('x + a = b, a')
-        self.assertEqualPos(match_subtract_term(root),
+        self.assertEqualPos(match_move_term(root),
                 [P(root, subtract_term, (a,))])
                 [P(root, subtract_term, (a,))])
 
 
         root, cx = tree('x = b + cx, cx')
         root, cx = tree('x = b + cx, cx')
-        self.assertEqualPos(match_subtract_term(root),
+        self.assertEqualPos(match_move_term(root),
                 [P(root, subtract_term, (cx,))])
                 [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):
     def test_subtract_term(self):
         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(subtract_term(root, (a,)), expect)
         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',
+        ])