Răsfoiți Sursa

Removed match_negate_group, replaced it with some more generic rules.

Taddeus Kroes 14 ani în urmă
părinte
comite
8d6d4788ac
3 a modificat fișierele cu 121 adăugiri și 50 ștergeri
  1. 5 3
      src/rules/__init__.py
  2. 65 42
      src/rules/negation.py
  3. 51 5
      tests/test_rules_negation.py

+ 5 - 3
src/rules/__init__.py

@@ -10,17 +10,19 @@ from .numerics import match_divide_numerics, match_multiply_numerics, \
         match_multiply_zero
 from .fractions import match_constant_division, match_add_constant_fractions, \
         match_expand_and_add_fractions
-from .negation import match_negate_group, match_negated_division
+from .negation import match_negated_factor, match_negate_polynome, \
+        match_negated_division
 
 RULES = {
         OP_ADD: [match_add_constant_fractions, match_combine_polynomes, \
                  match_combine_groups],
         OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents, \
-                 match_expand_and_add_fractions, match_multiply_zero],
+                 match_expand_and_add_fractions, match_multiply_zero,
+                 match_negated_factor],
         OP_DIV: [match_subtract_exponents, match_divide_numerics, \
                  match_constant_division, match_negated_division],
         OP_POW: [match_multiply_exponents, match_duplicate_exponent, \
                  match_remove_negative_exponent, match_exponent_to_root, \
                  match_extend_exponent],
-        OP_NEG: [match_negate_group],
+        OP_NEG: [match_negate_polynome],
         }

+ 65 - 42
src/rules/negation.py

@@ -1,76 +1,99 @@
-from ..node import get_scope, nary_node, OP_ADD, OP_MUL, OP_DIV
+from ..node import Scope, nary_node, OP_ADD, OP_MUL, OP_DIV
 from ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 
 
-def match_negate_group(node):
+def match_negated_factor(node):
     """
-    --a                 ->  a
-    -(a * ... * -b)     ->  ab
-    -(a + b + ... + z)  ->  -a + -b + ... + -z
+    This rule assures that negations in the scope of a multiplication are
+    brought `outside', to the multiplication itself.
+
+    Example:
+    a * -b  ->  -(ab)
     """
-    assert node.negated
+    assert node.is_op(OP_MUL)
 
-    if node.negated == 2:
-        # --a
-        return [P(node, double_negation, (node,))]
+    p = []
+    scope = Scope(node)
 
-    if not node.is_leaf:
-        scope = get_scope(node)
+    for factor in scope:
+        if factor.negated:
+            p.append(P(node, negated_factor, (scope, factor)))
 
-        if node.is_op(OP_MUL) and any(map(lambda n: n.negated, scope)):
-            # -(-a)b
-            return [P(node, negate_group, (node, scope))]
+    return p
 
-        if node.is_op(OP_ADD):
-            # -(ab + c)   ->  -ab - c
-            # -(-ab + c)  ->  ab - c
-            return [P(node, negate_polynome, (node, scope))]
 
-    return []
+def negated_factor(root, args):
+    """
+    a * -b  ->  -(ab)
+    """
+    scope, factor = args
+    scope.replace(factor, +factor)
+
+    return -scope.as_nary_node()
 
 
-def negate_group(root, args):
+def match_negate_polynome(node):
     """
-    -(a * -3c)       ->  a * 3c
-    -(a * ... * -b)  ->  ab
+    --a       ->  a
+    -(a + b)  ->  -a - b
     """
-    node, scope = args
+    #assert node.negated
 
-    for i, n in enumerate(scope):
-        if n.negated:
-            scope[i] = n.reduce_negation()
+    p = []
+
+    if node.negated == 2:
+        # --a
+        p.append(P(node, double_negation, ()))
+
+    if node.is_op(OP_ADD):
+        # -(a + b)  ->  -a - b
+        p.append(P(node, negate_polynome, ()))
+
+    return p
 
-    return nary_node('*', scope).reduce_negation()
 
+def double_negation(root, args):
+    """
+    --a  ->  a
+    """
+    return root.reduce_negation(2)
 
-MESSAGES[negate_group] = _('Apply negation to the polynome {1[0]}.')
+
+MESSAGES[double_negation] = _('Remove double negation in {1}.')
 
 
 def negate_polynome(root, args):
     """
-    -(-ab + ... + c)  ->  --ab + ... + -c
+    -(a + b)  ->  -a - b
     """
-    node, scope = args
+    scope = Scope(root)
 
     # Negate each group
     for i, n in enumerate(scope):
         scope[i] = -n
 
-    return nary_node('+', scope)
+    return +scope.as_nary_node()
 
 
-MESSAGES[negate_polynome] = _('Apply negation to the subexpression {1[0]}.')
+MESSAGES[negate_polynome] = _('Apply negation to the polynome {1}.')
 
 
-def double_negation(root, args):
-    """
-    --a  ->  a
-    """
-    return args[0].reduce_negation(2)
-
-
-MESSAGES[double_negation] = _('Remove double negation in {1}.')
+#def negate_group(root, args):
+#    """
+#    -(a * -3c)       ->  a * 3c
+#    -(a * ... * -b)  ->  ab
+#    """
+#    node, scope = args
+#
+#    for i, n in enumerate(scope):
+#        if n.negated:
+#            scope[i] = n.reduce_negation()
+#
+#    return nary_node('*', scope).reduce_negation()
+#
+#
+#MESSAGES[negate_polynome] = _('Apply negation to the subexpression {1[0]}.')
 
 
 def match_negated_division(node):
@@ -82,7 +105,7 @@ def match_negated_division(node):
     a, b = node
 
     if a.negated and b.negated:
-        return [P(node, double_negated_division, (node,))]
+        return [P(node, double_negated_division, ())]
     elif a.negated:
         return [P(node, single_negated_division, (+a, b))]
     elif b.negated:
@@ -111,7 +134,7 @@ def double_negated_division(root, args):
     """
     -a / -b  ->  a / b
     """
-    a, b = args[0]
+    a, b = root
 
     return +a / +b
 

+ 51 - 5
tests/test_rules_negation.py

@@ -1,12 +1,58 @@
-
-from src.rules.negation import match_negated_division, \
-        single_negated_division, double_negated_division
+from src.rules.negation import match_negated_factor, negated_factor, \
+        match_negate_polynome, negate_polynome, double_negation, \
+        match_negated_division, single_negated_division, \
+        double_negated_division
+from src.node import Scope
 from src.possibilities import Possibility as P
 from tests.rulestestcase import RulesTestCase, tree
 
 
 class TestRulesNegation(RulesTestCase):
 
+    def test_match_negated_factor(self):
+        a, b = root = tree('a * -b')
+        self.assertEqualPos(match_negated_factor(root),
+                [P(root, negated_factor, (Scope(root), b))])
+
+        (a, b), c = root = tree('a * -b * -c')
+        scope = Scope(root)
+        self.assertEqualPos(match_negated_factor(root),
+                [P(root, negated_factor, (scope, b)),
+                 P(root, negated_factor, (scope, c))])
+
+    def test_negated_factor(self):
+        a, b = root = tree('a * -b')
+        self.assertEqualNodes(negated_factor(root, (Scope(root), b)),
+                              -(a * +b))
+
+        (a, b), c = root = tree('a * -b * -c')
+        scope = Scope(root)
+        self.assertEqualNodes(negated_factor(root, (scope, b)),
+                              -(a * +b * c))
+        self.assertEqualNodes(negated_factor(root, (scope, c)),
+                              -(a * b * +c))
+
+    def test_match_negate_polynome(self):
+        root = tree('--a')
+        self.assertEqualPos(match_negate_polynome(root),
+                [P(root, double_negation, ())])
+
+        root = tree('-(a + b)')
+        self.assertEqualPos(match_negate_polynome(root),
+                [P(root, negate_polynome, ())])
+
+        a, b = root = tree('-(a - -b)')
+        self.assertEqualPos(match_negate_polynome(root),
+                [P(root, double_negation, (b,)),
+                 P(root, negate_polynome, ())])
+
+    def test_negate_polynome(self):
+        a, b = root = tree('-(a + b)')
+        self.assertEqualNodes(negate_polynome(root, ()), -a + -b)
+
+        a, b = root = tree('-(a - b)')
+        self.assertEqualNodes(negate_polynome(root, ()), -a + -b)
+
     def test_match_negated_division_none(self):
         self.assertEqual(match_negated_division(tree('1 / 2')), [])
 
@@ -26,7 +72,7 @@ class TestRulesNegation(RulesTestCase):
 
         possibilities = match_negated_division(root)
         self.assertEqualPos(possibilities,
-                [P(root, double_negated_division, (root,))])
+                [P(root, double_negated_division, ())])
 
     def test_single_negated_division(self):
         l1, l2 = root = tree('-1 / 2')
@@ -40,5 +86,5 @@ class TestRulesNegation(RulesTestCase):
     def test_double_negated_division(self):
         l1, l2 = root = tree('-1 / -2')
 
-        self.assertEqualNodes(double_negated_division(root, (root,)),
+        self.assertEqualNodes(double_negated_division(root, ()),
                               +l1 / +l2)