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

Added some new rules for fractions.

Taddeus Kroes 14 лет назад
Родитель
Сommit
b6bd65dbcb

+ 4 - 2
src/rules/__init__.py

@@ -10,7 +10,8 @@ from .numerics import match_add_numerics, match_divide_numerics, \
         match_multiply_numerics, match_multiply_zero, match_multiply_one, \
         match_raise_numerics
 from .fractions import match_constant_division, match_add_constant_fractions, \
-        match_expand_and_add_fractions, match_multiply_fractions
+        match_expand_and_add_fractions, match_multiply_fractions, \
+        match_equal_fraction_parts
 from .negation import match_negated_factor, match_negate_polynome, \
         match_negated_division
 from .sort import match_sort_multiplicants
@@ -25,7 +26,8 @@ RULES = {
                  match_negated_factor, match_multiply_one,
                  match_sort_multiplicants, match_multiply_fractions],
         OP_DIV: [match_subtract_exponents, match_divide_numerics,
-                 match_constant_division, match_negated_division],
+                 match_constant_division, match_negated_division,
+                 match_equal_fraction_parts],
         OP_POW: [match_multiply_exponents, match_duplicate_exponent,
                  match_raised_fraction, match_remove_negative_exponent,
                  match_exponent_to_root, match_extend_exponent,

+ 83 - 1
src/rules/fractions.py

@@ -1,7 +1,8 @@
 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
+from ..node import ExpressionLeaf as L, Scope, negate, OP_DIV, OP_ADD, \
+        OP_MUL, nary_node
 from ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 
@@ -226,3 +227,84 @@ def multiply_with_fraction(root, args):
 
 
 MESSAGES[multiply_with_fraction] = _('Multiply {2} with fraction {3}.')
+
+
+def match_equal_fraction_parts(node):
+    """
+    Divide nominator and denominator by the same part.
+
+    Examples:
+    ab / (ac)  ->  b / c
+    ab / a     ->  b / 1
+    a / (ab)   ->  1 / b
+    """
+    assert node.is_op(OP_DIV)
+
+    nominator, denominator = node
+
+    if nominator.is_op(OP_MUL):
+        n_scope = list(Scope(nominator))
+    else:
+        n_scope = [nominator]
+
+    if denominator.is_op(OP_MUL):
+        d_scope = list(Scope(denominator))
+    else:
+        d_scope = [denominator]
+
+    p = []
+
+    # Look for in scope
+    for i, n in enumerate(n_scope):
+        for j, d in enumerate(d_scope):
+            if n == d:
+                p.append(P(node, divide_fraction_parts,
+                           (n, n_scope, d_scope, i, j)))
+
+    return p
+
+
+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
+    """
+    a, n_scope, d_scope, i, j = args
+    n, d = root
+
+    del n_scope[i]
+    del d_scope[j]
+
+    if not n_scope:
+        # Last element of nominator scope, replace by 1
+        nom = L(1)
+    elif len(n_scope) == 1:
+        # Only one element left, no multiplication
+        nom = n_scope[0]
+    else:
+        # Still a multiplication
+        nom = nary_node('*', n_scope)
+
+    if not d_scope:
+        denom = L(1)
+    elif len(n_scope) == 1:
+        denom = d_scope[0]
+    else:
+        denom = nary_node('*', d_scope)
+
+    return nom.negate(n.negated) / denom.negate(d.negated)
+
+
+MESSAGES[divide_fraction_parts] = \
+        _('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)

+ 4 - 1
tests/test_leiden_oefenopgave_v12.py

@@ -64,8 +64,11 @@ class TestLeidenOefenopgaveV12(TestCase):
             'aa ^ 6 / b ^ 3 * b ^ 2',
             'a ^ (1 + 6) / b ^ 3 * b ^ 2',
             'a ^ 7 / b ^ 3 * b ^ 2',
-            # FIXME: 'b ^ 2 * a ^ 7 / b ^ 3',
+            'b ^ 2 * a ^ 7 / b ^ 3',
+            # FIXME: 'b ^ 2 / b ^ 3 * a ^ 7',
             # FIXME: 'b ^ (2 - 3) * a ^ 7',
+            # FIXME: 'b ^ 5 * a ^ 7',
+            # FIXME: 'a ^ 7 * b ^ 5',
         ])
 
     def test_2_b(self):

+ 46 - 1
tests/test_rules_fractions.py

@@ -1,7 +1,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
+        multiply_fractions, multiply_with_fraction, \
+        match_equal_fraction_parts, divide_fraction_parts
 from src.node import Scope
 from src.possibilities import Possibility as P
 from tests.rulestestcase import RulesTestCase, tree
@@ -144,3 +145,47 @@ class TestRulesFractions(RulesTestCase):
         (ab, e), cd = root = tree('a / b * e * (c / d)')
         self.assertEqual(multiply_fractions(root, (Scope(root), ab, cd)),
                          a * c / (b * d) * e)
+
+    def test_match_equal_fraction_parts(self):
+        (a, b), (c, a) = root = tree('ab / (ca)')
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, divide_fraction_parts, (a, [a, b], [c, a], 0, 1))])
+
+        (a, b), a = root = tree('ab / a')
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, divide_fraction_parts, (a, [a, b], [a], 0, 0))])
+
+        a, (a, b) = root = tree('a / (ab)')
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, divide_fraction_parts, (a, [a], [a, b], 0, 0))])
+
+        root = tree('abc / (cba)')
+        ((a, b), c) = root[0]
+        s0, s1 = [a, b, c], [c, b, a]
+        self.assertEqualPos(match_equal_fraction_parts(root),
+                [P(root, divide_fraction_parts, (a, s0, s1, 0, 2)),
+                 P(root, divide_fraction_parts, (b, s0, s1, 1, 1)),
+                 P(root, divide_fraction_parts, (c, s0, s1, 2, 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))
+        self.assertEqual(result, b / c)
+
+        (a, b), a = root = tree('ab / a')
+        result = divide_fraction_parts(root, (a, [a, b], [a], 0, 0))
+        self.assertEqual(result, b / 1)
+
+        root, l1 = tree('a / (ab), 1')
+        a, (a, b) = root
+        result = divide_fraction_parts(root, (a, [a], [a, b], 0, 0))
+        self.assertEqual(result, l1 / b)
+
+        root = tree('abc / (cba)')
+        ((a, b), c) = root[0]
+        result = divide_fraction_parts(root, (a, [a, b, c], [c, b, a], 0, 2))
+        self.assertEqual(result, b * c / (c * b))
+        result = divide_fraction_parts(root, (b, [a, b, c], [c, b, a], 1, 1))
+        self.assertEqual(result, a * c / (c * a))
+        result = divide_fraction_parts(root, (c, [a, b, c], [c, b, a], 2, 0))
+        self.assertEqual(result, a * b / (b * a))