فهرست منبع

Added rule that removed fractions from denominators.

Taddeus Kroes 13 سال پیش
والد
کامیت
f45d27511f
3فایلهای تغییر یافته به همراه72 افزوده شده و 8 حذف شده
  1. 3 2
      src/rules/__init__.py
  2. 35 5
      src/rules/fractions.py
  3. 34 1
      tests/test_rules_fractions.py

+ 3 - 2
src/rules/__init__.py

@@ -11,7 +11,7 @@ from .numerics import match_add_numerics, match_divide_numerics, \
         match_multiply_numerics, match_multiply_zero, match_raise_numerics
 from .fractions import match_constant_division, match_add_fractions, \
         match_multiply_fractions, match_divide_fractions, \
-        match_extract_fraction_terms
+        match_extract_fraction_terms, match_division_in_denominator
 from .negation import match_negated_factor, match_negate_polynome, \
         match_negated_division
 from .sort import match_sort_multiplicants
@@ -43,7 +43,8 @@ RULES = {
                  match_factor_in_multiplicant],
         OP_DIV: [match_subtract_exponents, match_divide_numerics,
                  match_constant_division, match_divide_fractions,
-                 match_negated_division, match_extract_fraction_terms],
+                 match_negated_division, match_extract_fraction_terms,
+                 match_division_in_denominator],
         OP_POW: [match_multiply_exponents, match_duplicate_exponent,
                  match_raised_fraction, match_remove_negative_exponent,
                  match_exponent_to_root, match_extend_exponent,

+ 35 - 5
src/rules/fractions.py

@@ -268,7 +268,7 @@ def divide_fraction(root, args):
     """
     (a, b), c = root
 
-    return (a / (b * c)).negate(root.negated)
+    return negate(a / (b * c), root.negated)
 
 
 MESSAGES[divide_fraction] = _('Move {3} to denominator of fraction {1} / {2}.')
@@ -281,7 +281,7 @@ def divide_by_fraction(root, args):
     a, bc = root
     b, c = bc
 
-    return (a * c / b).negate(root.negated + bc.negated)
+    return negate(a * c / b, root.negated + bc.negated)
 
 
 MESSAGES[divide_by_fraction] = \
@@ -370,7 +370,7 @@ def extract_nominator_term(root, args):
     """
     a, c = args
 
-    return a / root[1] * c
+    return negate(a / root[1] * c, root.negated)
 
 
 MESSAGES[extract_nominator_term] = \
@@ -385,9 +385,39 @@ def extract_fraction_terms(root, args):
     a ^ b * c / (a ^ d * e)  ->  a ^ b / a ^ d * (c / e)
     """
     n_scope, d_scope, n, d = args
+    div = n / d * (remove_from_mult_scope(n_scope, n) \
+                   / remove_from_mult_scope(d_scope, d))
 
-    return n / d * (remove_from_mult_scope(n_scope, n) \
-                    / remove_from_mult_scope(d_scope, d))
+    return negate(div, root.negated)
 
 
 MESSAGES[extract_fraction_terms] = _('Extract {3} / {4} from fraction {0}.')
+
+
+def match_division_in_denominator(node):
+    """
+    a / (b / c + d)  ->  (ca) / (c(b / c + d))
+    """
+    assert node.is_op(OP_DIV)
+
+    denom = node[1]
+
+    if not denom.is_op(OP_ADD):
+        return []
+
+    return [P(node, multiply_with_term, (n[1],))
+            for n in Scope(denom) if n.is_op(OP_DIV)]
+
+
+def multiply_with_term(root, args):
+    """
+    a / (b / c + d)  ->  (ca) / (c(b / c + d))
+    """
+    c = args[0]
+    nom, denom = root
+
+    return negate(c * nom / (c * denom), root.negated)
+
+
+MESSAGES[multiply_with_term] = \
+        _('Multiply nominator and denominator of {0} with {1}.')

+ 34 - 1
tests/test_rules_fractions.py

@@ -3,7 +3,8 @@ from src.rules.fractions import match_constant_division, division_by_one, \
         equalize_denominators, add_nominators, match_multiply_fractions, \
         multiply_fractions, multiply_with_fraction, match_divide_fractions, \
         divide_fraction, divide_by_fraction, match_extract_fraction_terms, \
-        constant_to_fraction, extract_nominator_term, extract_fraction_terms
+        constant_to_fraction, extract_nominator_term, extract_fraction_terms, \
+        match_division_in_denominator, multiply_with_term
 from src.node import ExpressionNode as N, Scope, OP_MUL
 from src.possibilities import Possibility as P
 from tests.rulestestcase import RulesTestCase, tree
@@ -284,3 +285,35 @@ class TestRulesFractions(RulesTestCase):
             'a * 4 / 5',
             # FIXME: '4 / 5 * a',
         ])
+
+    def test_match_division_in_denominator(self):
+        a, ((b, c), d) = root = tree('a / (b / c + d)')
+        self.assertEqualPos(match_division_in_denominator(root),
+                [P(root, multiply_with_term, (c,))])
+
+        a, ((d, (b, c)), e) = root = tree('a / (d + b / c + e)')
+        self.assertEqualPos(match_division_in_denominator(root),
+                [P(root, multiply_with_term, (c,))])
+
+    def test_multiply_with_term_chain(self):
+        self.assertRewrite([
+            '1 / (1 / b - 1 / a)',
+            '(b * 1) / (b(1 / b - 1 / a))',
+            'b / (b(1 / b - 1 / a))',
+            'b / (b * 1 / b + b(-1 / a))',
+            'b / ((b * 1) / b + b(-1 / a))',
+            'b / (b / b + b(-1 / a))',
+            'b / (1 + b(-1 / a))',
+            'b / (1 - b * 1 / a)',
+            'b / (1 - (b * 1) / a)',
+            'b / (1 - b / a)',
+            '(ab) / (a(1 - b / a))',
+            '(ab) / (a * 1 + a(-b / a))',
+            '(ab) / (a + a(-b / a))',
+            '(ab) / (a - ab / a)',
+            '(ab) / (a - (ab) / a)',
+            '(ab) / (a - a / a * b / 1)',
+            '(ab) / (a - 1b / 1)',
+            '(ab) / (a - 1b)',
+            '(ab) / (a - b)',
+        ])