فهرست منبع

Added new rule for eliminating fractions from fractions.

Taddeus Kroes 13 سال پیش
والد
کامیت
ef8ffd44a8
4فایلهای تغییر یافته به همراه91 افزوده شده و 13 حذف شده
  1. 3 2
      src/rules/__init__.py
  2. 53 0
      src/rules/fractions.py
  3. 10 10
      tests/test_leiden_oefenopgave.py
  4. 25 1
      tests/test_rules_fractions.py

+ 3 - 2
src/rules/__init__.py

@@ -26,7 +26,8 @@ from .numerics import match_add_numerics, match_divide_numerics, \
 from .fractions import match_constant_division, match_add_fractions, \
         match_multiply_fractions, match_divide_fractions, \
         match_extract_fraction_terms, match_division_in_denominator, \
-        match_combine_fractions, match_remove_division_negation
+        match_combine_fractions, match_remove_division_negation, \
+        match_fraction_in_division
 from .negation import match_negated_factor, match_negate_polynome, \
         match_negated_division
 from .sort import match_sort_polynome, match_sort_monomial
@@ -59,7 +60,7 @@ RULES = {
         OP_DIV: [match_subtract_exponents, match_divide_numerics,
                  match_constant_division, match_divide_fractions,
                  match_negated_division, match_extract_fraction_terms,
-                 match_division_in_denominator,
+                 match_division_in_denominator, match_fraction_in_division,
                  match_remove_division_negation],
         OP_POW: [match_multiply_exponents, match_duplicate_exponent,
                  match_raised_fraction, match_remove_negative_child,

+ 53 - 0
src/rules/fractions.py

@@ -534,3 +534,56 @@ def remove_division_negation(root, args):
 
 MESSAGES[remove_division_negation] = \
         _('Move negation from fraction {0} to polynome {2}.')
+
+
+def match_fraction_in_division(node):
+    """
+    (1 / a * b) / c  ->  b / (ac)
+    c / (1 / a * b)  ->  (ac) / b
+    """
+    assert node.is_op(OP_DIV)
+    nom, denom = node
+    p = []
+
+    if nom.is_op(OP_MUL):
+        scope = Scope(nom)
+
+        for n in scope:
+            if n.is_op(OP_DIV) and n[0] == 1:
+                p.append(P(node, fraction_in_division, (True, scope, n)))
+
+    if denom.is_op(OP_MUL):
+        scope = Scope(denom)
+
+        for n in scope:
+            if n.is_op(OP_DIV) and n[0] == 1:
+                p.append(P(node, fraction_in_division, (False, scope, n)))
+
+    return p
+
+
+def fraction_in_division(root, args):
+    """
+    (1 / a * b) / c  ->  b / (ac)
+    c / (1 / a * b)  ->  (ac) / b
+    """
+    is_nominator, scope, fraction = args
+    nom, denom = root
+
+    if fraction.negated:
+        scope.replace(fraction, fraction[0].negate(fraction.negated))
+    else:
+        scope.remove(fraction)
+
+    if is_nominator:
+        nom = scope.as_nary_node()
+        denom = fraction[1] * denom
+    else:
+        nom = fraction[1] * nom
+        denom = scope.as_nary_node()
+
+    return negate(nom / denom, root.negated)
+
+
+MESSAGES[fraction_in_division] = \
+        _('Multiply both sides of fraction {0} with {3[1]}.')

+ 10 - 10
tests/test_leiden_oefenopgave.py

@@ -208,16 +208,16 @@ class TestLeidenOefenopgave(TestCase):
             '1 / (4x)',
         ])
 
-    #def test_4_6(self):
-    #    self.assertRewrite([
-    #        '(3 / x^2) / (x / 7)',
-    #        '3 / x ^ 2 / (1 / 7 * x)',
-    #        '3 / (x ^ 2 * 1 / 7 * x)',
-    #        '3 / (x ^ (2 + 1)1 / 7)',
-    #        '3 / (x ^ 3 * 1 / 7)',
-    #        '3 / (1 / 7 * x ^ 3)',
-    #        '21 / x^3',
-    #    ])
+    def test_4_6(self):
+        self.assertRewrite([
+            '(3 / x^2) / (x / 7)',
+            '3 / x ^ 2 / (1 / 7 * x)',
+            '3 / (x ^ 2 * 1 / 7x)',
+            '3 / (x ^ (2 + 1)1 / 7)',
+            '3 / (x ^ 3 * 1 / 7)',
+            '(7 * 3) / x ^ 3',
+            '21 / x ^ 3',
+        ])
 
     def test_4_7(self):
         self.assertEvaluates('1 / x + 2 / (x + 1)', '(3x + 1) / (x(x + 1))')

+ 25 - 1
tests/test_rules_fractions.py

@@ -20,7 +20,8 @@ from src.rules.fractions import match_constant_division, division_by_one, \
         constant_to_fraction, extract_nominator_term, extract_fraction_terms, \
         match_division_in_denominator, multiply_with_term, \
         divide_fraction_by_term, match_combine_fractions, combine_fractions, \
-        match_remove_division_negation, remove_division_negation
+        match_remove_division_negation, remove_division_negation, \
+        match_fraction_in_division, fraction_in_division
 from src.node import ExpressionNode as N, Scope, OP_MUL
 from src.possibilities import Possibility as P
 from tests.rulestestcase import RulesTestCase, tree
@@ -359,3 +360,26 @@ class TestRulesFractions(RulesTestCase):
         a, (b, c) = root = tree('-a / (-b + c)')
         self.assertEqual(remove_division_negation(root, (False, root[1])),
                          +a / (-b - c))
+
+    def test_match_fraction_in_division(self):
+        (fr, b), c = root = tree('(1 / a * b) / c')
+        self.assertEqualPos(match_fraction_in_division(root),
+                [P(root, fraction_in_division, (True, Scope(root[0]), fr))])
+
+        c, (fr, b) = root = tree('c / (1 / a * b)')
+        self.assertEqualPos(match_fraction_in_division(root),
+                [P(root, fraction_in_division, (False, Scope(root[1]), fr))])
+
+        (fr0, b), (fr1, d) = root = tree('(1 / a * b) / (1 / c * d)')
+        self.assertEqualPos(match_fraction_in_division(root),
+                [P(root, fraction_in_division, (True, Scope(root[0]), fr0)),
+                 P(root, fraction_in_division, (False, Scope(root[1]), fr1))])
+
+    def test_fraction_in_division(self):
+        root, expected = tree('(1 / a * b) / c, b / (ac)')
+        self.assertEqual(fraction_in_division(root,
+            (True, Scope(root[0]), root[0][0])), expected)
+
+        root, expected = tree('c / (1 / a * b), (ac) / b')
+        self.assertEqual(fraction_in_division(root,
+            (False, Scope(root[1]), root[1][0])), expected)