Quellcode durchsuchen

Added some more fraction rewrite rules.

Taddeus Kroes vor 14 Jahren
Ursprung
Commit
1659cc67c5
2 geänderte Dateien mit 77 neuen und 21 gelöschten Zeilen
  1. 49 20
      src/rules/fractions.py
  2. 28 1
      tests/test_rules_fractions.py

+ 49 - 20
src/rules/fractions.py

@@ -2,7 +2,7 @@ from itertools import combinations, product
 
 from .utils import least_common_multiple, partition
 from ..node import ExpressionLeaf as L, Scope, negate, OP_DIV, OP_ADD, \
-        OP_MUL, nary_node, negate
+        OP_MUL, OP_POW, nary_node, negate
 from ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 
@@ -235,6 +235,12 @@ def match_equal_fraction_parts(node):
     ab / (ac)  ->  b / c
     ab / a     ->  b / 1
     a / (ab)   ->  1 / b
+
+    If the same root appears in both nominator and denominator, extrct it so
+    that it can be reduced to a single power by power division rules.
+    a ^ p * b / a ^ q  ->  a ^ p / a ^ q * b / 1
+    a ^ p * b / a      ->  a ^ p / a * b / 1
+    a * b / a ^ q      ->  a / a ^ q * b / 1
     """
     assert node.is_op(OP_DIV)
 
@@ -252,28 +258,29 @@ def match_equal_fraction_parts(node):
 
     p = []
 
-    # Look for in scope
+    # Look for matching parts in scopes
     for i, n in enumerate(n_scope):
         for j, d in enumerate(d_scope):
             if n.equals(d, ignore_negation=True):
                 p.append(P(node, divide_fraction_parts,
                            (negate(n, 0), n_scope, d_scope, i, j)))
 
-    return p
+            if n.is_op(OP_POW):
+                a = n[0]
 
+                if d == a or (d.is_op(OP_POW) and d[0] == a):
+                    # a ^ p * b / a  ->  a ^ p / a * b
+                    p.append(P(node, extract_divided_roots,
+                               (a, n_scope, d_scope, i, j)))
+            elif d.is_op(OP_POW) and n == d[0]:
+                # a * b / a ^ q  ->  a / a ^ q * b
+                p.append(P(node, extract_divided_roots,
+                           (d[0], n_scope, d_scope, i, j)))
 
-def divide_fraction_parts(root, args):
-    """
-    Divide nominator and denominator by the same part.
+    return p
 
-    Examples:
-    ab / (ac)  ->  b / c
-    ab / a     ->  b / 1
-    a / (ab)   ->  1 / b
-    -ab / a     ->  -b / 1
-    """
-    a, n_scope, d_scope, i, j = args
-    n, d = root
+
+def remove_from_scopes(n_scope, d_scope, i, j):
     a_n, a_d = n_scope[i], d_scope[j]
 
     del n_scope[i]
@@ -296,17 +303,39 @@ def divide_fraction_parts(root, args):
     else:
         denom = nary_node('*', d_scope)
 
+    return a_n, a_d, nom, denom
+
+
+def divide_fraction_parts(root, args):
+    """
+    Divide nominator and denominator by the same part.
+
+    Examples:
+    ab / (ac)  ->  b / c
+    ab / a     ->  b / 1
+    a / (ab)   ->  1 / b
+    -ab / a     ->  -b / 1
+    """
+    a, n_scope, d_scope, i, j = args
+    n, d = root
+    a_n, a_d, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
+
     # Move negation of removed part to nominator and denominator
     return nom.negate(n.negated + a_n.negated) \
            / denom.negate(d.negated + a_d.negated)
 
 
 MESSAGES[divide_fraction_parts] = \
-        _('Divide nominator and denominator in {0} by {1}')
+        _('Divide nominator and denominator in {0} by {1}.')
 
 
-def match_multiplied_power_division(node):
-    """
-    a ^ p * b / a ^ q  ->  a ^ p / a ^ q * b
-    """
-    assert node.is_op(OP_DIV)
+def extract_divided_roots(root, args):
+    a, n_scope, d_scope, i, j = args
+    n, d = root
+    ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
+
+    return ap / aq * nom.negate(n.negated) / denom.negate(d.negated)
+
+
+MESSAGES[extract_divided_roots] = \
+        _('Extrct the root {1} from nominator and denominator in {0}.')

+ 28 - 1
tests/test_rules_fractions.py

@@ -2,7 +2,8 @@ from src.rules.fractions import match_constant_division, division_by_one, \
         division_of_zero, division_by_self, match_add_constant_fractions, \
         equalize_denominators, add_nominators, match_multiply_fractions, \
         multiply_fractions, multiply_with_fraction, \
-        match_equal_fraction_parts, divide_fraction_parts
+        match_equal_fraction_parts, divide_fraction_parts, \
+        extract_divided_roots
 from src.node import Scope
 from src.possibilities import Possibility as P
 from tests.rulestestcase import RulesTestCase, tree
@@ -171,6 +172,18 @@ class TestRulesFractions(RulesTestCase):
         self.assertEqualPos(match_equal_fraction_parts(root),
                 [P(root, divide_fraction_parts, (a, [-a], [a], 0, 0))])
 
+        (ap, b), aq = root = tree('a ^ p * b / a ^ q')
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, extract_divided_roots, (a, [ap, b], [aq], 0, 0))])
+
+        (a, b), aq = root = tree('a * b / a ^ q')
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, extract_divided_roots, (a, [a, b], [aq], 0, 0))])
+
+        (ap, b), a = root = tree('a ^ p * b / a')
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, extract_divided_roots, (a, [ap, b], [a], 0, 0))])
+
     def test_divide_fraction_parts(self):
         (a, b), (c, a) = root = tree('ab / (ca)')
         result = divide_fraction_parts(root, (a, [a, b], [c, a], 0, 1))
@@ -197,3 +210,17 @@ class TestRulesFractions(RulesTestCase):
         (a, b), a = root = tree('-ab / a')
         result = divide_fraction_parts(root, (a, [-a, b], [a], 0, 0))
         self.assertEqual(result, -b / 1)
+
+    def test_extract_divided_roots(self):
+        r, a = tree('a ^ p * b / a ^ q, a')
+        ((a, p), b), (a, q) = (ap, b), aq = r
+        self.assertEqual(extract_divided_roots(r, (a, [ap, b], [aq], 0, 0)),
+                         a ** p / a ** q * b / 1)
+
+        r = tree('a * b / a ^ q, a')
+        self.assertEqual(extract_divided_roots(r, (a, [a, b], [aq], 0, 0)),
+                         a / a ** q * b / 1)
+
+        r = tree('a ^ p * b / a, a')
+        self.assertEqual(extract_divided_roots(r, (a, [ap, b], [a], 0, 0)),
+                         a ** p / a * b / 1)