Преглед изворни кода

Modified parser to fit MathJax syntax.

- Division now has a higher precedence than multiplication.
- Integrals and derivatives now have a precedence higher than addition, but
  lower than multiplication.
- 'a2' is now translated to 'a * 2' instead of 'a ^ 2'.
Taddeus Kroes пре 14 година
родитељ
комит
52dcf02f95

+ 1 - 1
external/graph_drawing

@@ -1 +1 @@
-Subproject commit ade1a95046e5539a4f11535892061e05b7c23b95
+Subproject commit aade5fc51f4b19e84d180fcff9b6c6d89db93667

+ 13 - 8
src/parser.py

@@ -86,12 +86,13 @@ class Parser(BisonParser):
     # ------------------------------
     precedences = (
         ('left', ('COMMA', )),
-        ('left', ('INTEGRAL', 'DERIVATIVE')),
         ('left', ('OR', )),
         ('left', ('AND', )),
         ('left', ('EQ', )),
         ('left', ('MINUS', 'PLUS', 'NEG')),
-        ('left', ('TIMES', 'DIVIDE')),
+        ('left', ('INTEGRAL', 'DERIVATIVE')),
+        ('left', ('TIMES', )),
+        ('left', ('DIVIDE', )),
         ('right', ('FUNCTION', )),
         ('right', ('POW', )),
         ('left', ('SUB', )),
@@ -179,12 +180,11 @@ class Parser(BisonParser):
                                                               # a[  -> a * [
                 + '|(\))\s*([\x00-\x09\x0b-\x19a-z0-9])'      # )a  -> ) * a
                 + '|([\x00-\x09\x0b-\x19a-z])\s*'
-                  + '([\x00-\x09\x0b-\x19a-z])'               # ab  -> a * b
+                  + '([\x00-\x09\x0b-\x19a-z0-9])'            # ab  -> a * b
                 + '|(\|)(\|)'                                 # ||  -> | * |
                 + '|([0-9])\s*([\x00-\x09\x0b-\x19a-z])'      # 4a  -> 4 * a
                 + '|([\x00-\x09\x0b-\x19a-z])([0-9])'         # a4  -> a ^ 4
-                + '|([\x00-\x09\x0b-\x19a-z0-9])(\s+[0-9]))'  # a 4 -> a * 4
-                                                              # 4 4 -> 4 * 4
+                + '|([\x00-\x09\x0b-\x190-9])(\s+[0-9]))'     # 4 4 -> 4 * 4
                 )
 
         def preprocess_data(match):
@@ -199,8 +199,8 @@ class Parser(BisonParser):
             # If all characters on the right are numbers. e.g. "a4", the
             # expression implies exponentiation. Make sure ")4" is not
             # converted into an exponentiation, because that's multiplication.
-            if left != ')' and not left.isdigit() and right.isdigit():
-                return '%s^%s' % (left, right)
+            #if left != ')' and not left.isdigit() and right.isdigit():
+            #    return '%s^%s' % (left, right)
 
             # match: ab | abc | abcd (where left = "a")
             return '*'.join([left] + list(re.sub(r'^ +', '', right)))
@@ -219,9 +219,14 @@ class Parser(BisonParser):
         for i, keyword in enumerate(words):
             data = data.replace(chr(i), keyword)
 
-        # Fix TIMES operator next to OR
+        # Remove TIMES operators around OR that the preprocessor put there
         data = re.sub(r'\*?vv\*?', 'vv', data)
 
+        # Add parentheses to integrals with matching 'dx' so that the 'dx' acts
+        # as a right parenthesis for the integral function
+        data = re.sub(r'(int(?:_.+\^.+\*)?)(.+?)(\*d\*[a-z])',
+                      '\\1(\\2)\\3', data)
+
         if self.verbose and data_before != data:  # pragma: nocover
             print 'hook_read_after() modified the input data:'
             print 'before:', repr(data_before)

+ 3 - 3
tests/test_b1_ch10.py

@@ -19,7 +19,7 @@ class TestB1Ch10(unittest.TestCase):
             ('(2+3/7)^4',
                 N('^', N('+', L(2), N('/', L(3), L(7))), L(4))
             ),
-            ('x3*x2*x',
+            ('x^3*x^2*x',
                 N('*',
                   N('*',
                     N('^', L('x'), L(3)),
@@ -27,10 +27,10 @@ class TestB1Ch10(unittest.TestCase):
                   L('x')
                 )
             ),
-            ('-x3*-2x5',
+            ('-x^3*-2x^5',
                 -(L('x') ** L(3) * -(L(2) * L('x') ** L(5)))
             ),
-            ('(7x2y3)^2/(7x2y3)',
+            ('(7x^2y^3)^2/(7x^2y^3)',
                 N('/',
                   N('^',
                     N('*',

+ 1 - 2
tests/test_calc.py

@@ -34,9 +34,8 @@ class TestCalc(unittest.TestCase):
         expressions = [
                        ('xy',     N('*', L('x'), L('y'))),
                        ('2x',     N('*', L(2), L('x'))),
-                       ('x4',     N('^', L('x'), L(4))),
+                       ('x4',     N('*', L('x'), L(4))),
                        ('3 4',    N('*', L(3), L(4))),
-                       ('xy4',    N('*', L('x'), N('^', L('y'), L(4)))),
                        ('(x)4',   N('*', L('x'), L(4))),
                        ('(3+4)2', N('*', N('+', L(3), L(4)), L(2))),
                       ]

+ 3 - 3
tests/test_leiden_oefenopgave.py

@@ -15,7 +15,7 @@ class TestLeidenOefenopgave(TestCase):
         return
 
         for exp, solution in [
-                ('-5(x2 - 3x + 6)',       '-30 + 15x - 5x ^ 2'),
+                ('-5(x^2 - 3x + 6)',     '-30 + 15x - 5x ^ 2'),
                 ('(x+1)^2',              'x ^ 2 + 2x + 1'),
                 ('(x-1)^2',              'x ^ 2 - 2x + 1'),
                 ('(2x+x)*x',             '3x ^ 2'),
@@ -172,7 +172,7 @@ class TestLeidenOefenopgave(TestCase):
     def test_4_3(self):
         self.assertRewrite([
             '(7/3)(3/5)',
-            '7 * 3 / (3 * 5)',
+            '(7 * 3) / (3 * 5)',
             '21 / (3 * 5)',
             '21 / 15',
             '7 / 5',
@@ -184,7 +184,7 @@ class TestLeidenOefenopgave(TestCase):
     def test_4_5(self):
         self.assertRewrite([
             '1/4 * 1/x',
-            '1 / 4 / x',
+            '(1 * 1) / (4x)',
             '1 / (4x)',
         ])
 

+ 20 - 20
tests/test_leiden_oefenopgave_v12.py

@@ -4,7 +4,7 @@ from tests.rulestestcase import RulesTestCase as TestCase
 class TestLeidenOefenopgaveV12(TestCase):
     def test_1_a(self):
         self.assertRewrite([
-            '-5(x2 - 3x + 6)',
+            '-5(x^2 - 3x + 6)',
             '-(5x ^ 2 + 5(-3x) + 5 * 6)',
             '-(5x ^ 2 - 5 * 3x + 5 * 6)',
             '-(5x ^ 2 - 15x + 5 * 6)',
@@ -57,44 +57,44 @@ class TestLeidenOefenopgaveV12(TestCase):
 
     def test_2_a(self):
         self.assertRewrite([
-            '(a2b^-1)^3(ab2)',
-            '(a ^ 2 * (1 / b ^ 1)) ^ 3 * ab ^ 2',
-            '(a ^ 2 * (1 / b)) ^ 3 * ab ^ 2',
-            '(1a ^ 2 / b) ^ 3 * ab ^ 2',
+            '(a^2b^-1)^3(ab^2)',
+            '(a ^ 2 * 1 / b ^ 1) ^ 3 * ab ^ 2',
+            '(a ^ 2 * 1 / b) ^ 3 * ab ^ 2',
+            '((1a ^ 2) / b) ^ 3 * ab ^ 2',
             '(a ^ 2 / b) ^ 3 * ab ^ 2',
             '(a ^ 2) ^ 3 / b ^ 3 * ab ^ 2',
             'a ^ (2 * 3) / b ^ 3 * ab ^ 2',
             'a ^ 6 / b ^ 3 * ab ^ 2',
-            'a ^ 6 * a / b ^ 3 * b ^ 2',
+            '(a ^ 6 * a) / b ^ 3 * b ^ 2',
             'a ^ (6 + 1) / b ^ 3 * b ^ 2',
             'a ^ 7 / b ^ 3 * b ^ 2',
-            'a ^ 7 * b ^ 2 / b ^ 3',
-            'b ^ 2 / b ^ 3 * (a ^ 7 / 1)',
-            'b ^ (2 - 3)(a ^ 7 / 1)',
-            'b ^ (-1)(a ^ 7 / 1)',
-            '1 / b ^ 1 * (a ^ 7 / 1)',
-            '1 / b * (a ^ 7 / 1)',
+            '(a ^ 7 * b ^ 2) / b ^ 3',
+            'b ^ 2 / b ^ 3 * a ^ 7 / 1',
+            'b ^ (2 - 3)a ^ 7 / 1',
+            'b ^ (-1)a ^ 7 / 1',
+            '1 / b ^ 1 * a ^ 7 / 1',
+            '1 / b * a ^ 7 / 1',
             '1 / b * a ^ 7',
-            '1a ^ 7 / b',
+            '(1a ^ 7) / b',
             'a ^ 7 / b',
         ])
 
     def test_2_b(self):
         self.assertRewrite([
-            'a3b2a3',
+            'a^3b^2a^3',
             'a ^ (3 + 3)b ^ 2',
             'a ^ 6 * b ^ 2',
         ])
 
     #def test_2_c(self):
     #    self.assertRewrite([
-    #        'a5+a3',
+    #        'a^5+a^3',
     #        'a ^ 5 + a ^ 3',
     #    ])
 
     def test_2_d(self):
         self.assertRewrite([
-            'a2+a2',
+            'a^2+a^2',
             '(1 + 1)a ^ 2',
             '2a ^ 2',
         ])
@@ -102,8 +102,8 @@ class TestLeidenOefenopgaveV12(TestCase):
     def test_2_e(self):
         self.assertRewrite([
             '4b^-2',
-            '4(1 / b ^ 2)',
-            '1 * 4 / b ^ 2',
+            '4 * 1 / b ^ 2',
+            '(1 * 4) / b ^ 2',
             '4 / b ^ 2',
         ])
 
@@ -113,7 +113,7 @@ class TestLeidenOefenopgaveV12(TestCase):
             '4 ^ (-2)b ^ (-2)',
             '1 / 4 ^ 2 * b ^ (-2)',
             '1 / 16 * b ^ (-2)',
-            '1 / 16 * (1 / b ^ 2)',
-            '1 * 1 / (16b ^ 2)',
+            '1 / 16 * 1 / b ^ 2',
+            '(1 * 1) / (16b ^ 2)',
             '1 / (16b ^ 2)',
         ])

+ 5 - 5
tests/test_node.py

@@ -240,7 +240,7 @@ class TestNode(RulesTestCase):
         self.assertEqual(str(tree('int x ^ 2 dx')), 'int x ^ 2 dx')
         self.assertEqual(str(tree('int x ^ 2 dy')), 'int x ^ 2 dy')
         self.assertEqual(str(tree('int x ^ 2 dy')), 'int x ^ 2 dy')
-        self.assertEqual(str(tree('int x + 1')), 'int (x + 1) dx')
+        self.assertEqual(str(tree('int x + 1')), 'int x dx + 1')
 
         self.assertEqual(str(tree('int_a^b x ^ 2')), 'int_a^b x ^ 2 dx')
         self.assertEqual(str(tree('int_(a-b)^(a+b) x ^ 2')),
@@ -259,7 +259,7 @@ class TestNode(RulesTestCase):
         self.assertEqual(infinity(), tree('oo'))
 
     def test_absolute(self):
-        self.assertEqual(absolute(tree('x2')), tree('|x2|'))
+        self.assertEqual(absolute(tree('x^2')), tree('|x^2|'))
 
     def test_sin(self):
         self.assertEqual(sin(tree('x')), tree('sin(x)'))
@@ -288,9 +288,9 @@ class TestNode(RulesTestCase):
 
     def test_integral(self):
         x2, x, y, a, b = tree('x ^ 2, x, y, a, b')
-        self.assertEqual(integral(x2, x), tree('int x2 dx'))
-        self.assertEqual(integral(x2, x, a, b), tree('int_a^b x2 dx'))
-        self.assertEqual(integral(x2, y, a, b), tree('int_a^b x2 dy'))
+        self.assertEqual(integral(x2, x), tree('int x^2 dx'))
+        self.assertEqual(integral(x2, x, a, b), tree('int_a^b x^2 dx'))
+        self.assertEqual(integral(x2, y, a, b), tree('int_a^b x^2 dy'))
 
     def test_indef(self):
         x2, a, b, expect = tree('x ^ 2, a, b, [x ^ 2]_a^b')

+ 24 - 11
tests/test_parser.py

@@ -52,7 +52,7 @@ class TestParser(RulesTestCase):
     def test_preprocessor(self):
         self.assertEqual(tree('ab'), tree('a * b'))
         self.assertEqual(tree('abc'), tree('a * b * c'))
-        self.assertEqual(tree('a2'), tree('a ^ 2'))
+        self.assertEqual(tree('a2'), tree('a * 2'))
         self.assertEqual(tree('a 2'), tree('a * 2'))
         self.assertEqual(tree('2a'), tree('2 * a'))
         self.assertEqual(tree('2(a + b)'), tree('2 * (a + b)'))
@@ -91,8 +91,8 @@ class TestParser(RulesTestCase):
 
         self.assertEqual(tree('d/dx x ^ 2'), der(exp, x))
         self.assertEqual(tree('d / dx x ^ 2'), der(exp, x))
-        self.assertEqual(tree('d/dx x ^ 2 + x'), der(exp + x, x))
-        self.assertEqual(tree('(d/dx x ^ 2) + x'), der(exp, x) + x)
+        self.assertEqual(tree('d/dx x ^ 2 + x'), der(exp, x) + x)
+        self.assertEqual(tree('d/dx (x ^ 2 + x)'), der(exp + x, x))
         self.assertEqual(tree('d/d'), d / d)
         # FIXME: self.assertEqual(tree('d(x ^ 2)/dx'), der(exp, x))
 
@@ -120,17 +120,27 @@ class TestParser(RulesTestCase):
         x, y, dx, a, b, l2 = tree('x, y, dx, a, b, 2')
 
         self.assertEqual(tree('int x'), integral(x, x))
-        self.assertEqual(tree('int x2'), integral(x ** 2, x))
-        self.assertEqual(tree('int x2 dx'), integral(x ** 2, x))
-        self.assertEqual(tree('int x2 dy'), integral(x ** 2, y))
+        self.assertEqual(tree('int x ^ 2'), integral(x ** 2, x))
+        self.assertEqual(tree('int x ^ 2 dx'), integral(x ** 2, x))
+        self.assertEqual(tree('int x ^ 2 dy'), integral(x ** 2, y))
 
-        self.assertEqual(tree('int_a^b x2'), integral(x ** 2, x, a, b))
-        self.assertEqual(tree('int_a^b x2 dy'), integral(x ** 2, y, a, b))
-        self.assertEqual(tree('int_(a-b)^(a+b) x2'),
+        self.assertEqual(tree('int_a^b x ^ 2'), integral(x ** 2, x, a, b))
+        self.assertEqual(tree('int_a^b x ^ 2 dy'), integral(x ** 2, y, a, b))
+        self.assertEqual(tree('int_(a-b)^(a+b) x ^ 2'),
                          integral(x ** 2, x, a - b, a + b))
 
         self.assertEqual(tree('int_a^b 2x'), integral(l2 * x, x, a, b))
-        self.assertEqual(tree('int_a^b2 x'), integral(x, x, a, b ** 2))
+        self.assertEqual(tree('int_a^b^2 x'), integral(x, x, a, b ** 2))
+        self.assertEqual(tree('int_a^(b2) x'), integral(x, x, a, b * 2))
+
+        self.assertEqual(tree('int x ^ 2 + 1'), integral(x ** 2, x) + 1)
+        self.assertEqual(tree('int x ^ 2 + 1 dx'), integral(x ** 2 + 1, x))
+
+        self.assertEqual(tree('int_a^b x ^ 2 dx'), integral(x ** 2, x, a, b))
+        self.assertEqual(tree('int_a^(b2) x ^ 2 + 1 dx'),
+                         integral(x ** 2 + 1, x, a, b * 2))
+        self.assertEqual(tree('int_(a^2)^b x ^ 2 + 1 dx'),
+                         integral(x ** 2 + 1, x, a ** 2, b))
 
     def test_indefinite_integral(self):
         x2, a, b = tree('x ^ 2, a, b')
@@ -141,7 +151,7 @@ class TestParser(RulesTestCase):
         x = tree('x')
 
         self.assertEqual(tree('|x|'), absolute(x))
-        self.assertEqual(tree('|x2|'), absolute(x ** 2))
+        self.assertEqual(tree('|x2|'), absolute(x * 2))
 
     def test_find_possibilities_basic(self):
         l1, l2 = root = tree('1 + 2')
@@ -157,3 +167,6 @@ class TestParser(RulesTestCase):
 
     def test_no_expression_error(self):
         self.assertRaises(RuntimeError, ParserWrapper(Parser).run, ['', '?'])
+
+    def test_precedence(self):
+        self.assertEqual(tree('ab / cd'), tree('a * (b / c) * d'))

+ 2 - 2
tests/test_rules_derivatives.py

@@ -109,8 +109,8 @@ class TestRulesDerivatives(RulesTestCase):
             "x ^ x * ([x]' * ln(x) + x[ln(x)]')",
             "x ^ x * (1ln(x) + x[ln(x)]')",
             "x ^ x * (ln(x) + x[ln(x)]')",
-            "x ^ x * (ln(x) + x(1 / x))",
-            "x ^ x * (ln(x) + 1x / x)",
+            "x ^ x * (ln(x) + x * 1 / x)",
+            "x ^ x * (ln(x) + (1x) / x)",
             "x ^ x * (ln(x) + x / x)",
             "x ^ x * (ln(x) + 1)",
             "x ^ x * ln(x) + x ^ x * 1",

+ 18 - 18
tests/test_rules_fractions.py

@@ -198,14 +198,14 @@ class TestRulesFractions(RulesTestCase):
         self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
 
     def test_match_extract_fraction_terms(self):
-        root, a, b, c = tree('ab / (ca), a, b, c')
+        root, a, b, c = tree('(ab) / (ca), a, b, c')
         n, d = root
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
 
         lscp = lambda l: Scope(N(OP_MUL, l))
 
-        n, d = root = tree('ab / a')
+        n, d = root = tree('(ab) / a')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (Scope(n), lscp(d), a, a))])
 
@@ -213,7 +213,7 @@ class TestRulesFractions(RulesTestCase):
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (lscp(n), Scope(d), a, a))])
 
-        n, d = root = tree('abc / (cba)')
+        n, d = root = tree('(abc) / (cba)')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a)),
                  P(root, extract_fraction_terms, (Scope(n), Scope(d), b, b)),
@@ -222,19 +222,19 @@ class TestRulesFractions(RulesTestCase):
         root = tree('a / a')
         self.assertEqualPos(match_extract_fraction_terms(root), [])
 
-        (ap, b), aq = n, d = root = tree('a ^ p * b / a ^ q')
+        (ap, b), aq = n, d = root = tree('(a ^ p * b) / a ^ q')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, aq))])
 
-        (a, b), aq = n, d = root = tree('a * b / a ^ q')
+        (a, b), aq = n, d = root = tree('(ab) / a ^ q')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (Scope(n), lscp(d), a, aq))])
 
-        (ap, b), a = n, d = root = tree('a ^ p * b / a')
+        (ap, b), a = n, d = root = tree('(a ^ p * b) / a')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, a))])
 
-        (l2, a), l3 = n, d = root = tree('2a / 3')
+        (l2, a), l3 = n, d = root = tree('(2a) / 3')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_nominator_term, (2, a))])
 
@@ -242,16 +242,16 @@ class TestRulesFractions(RulesTestCase):
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_nominator_term, (1, a))])
 
-        root = tree('2*4 / 3')
+        root = tree('(2 * 4) / 3')
         self.assertEqualPos(match_extract_fraction_terms(root), [])
 
-        n, d = root = tree('2a / 2')
+        n, d = root = tree('(2a) / 2')
         self.assertEqualPos(match_extract_fraction_terms(root),
                 [P(root, extract_nominator_term, (2, a)),
                  P(root, extract_fraction_terms, (Scope(n), lscp(d), 2, 2))])
 
     def test_extract_nominator_term(self):
-        root, expect = tree('2a / 3, 2 / 3 * a')
+        root, expect = tree('(2a) / 3, 2 / 3 * a')
         l2, a = root[0]
         self.assertEqual(extract_nominator_term(root, (l2, a)), expect)
 
@@ -259,28 +259,28 @@ class TestRulesFractions(RulesTestCase):
         self.assertEqual(extract_nominator_term(root, (l1, root[0])), expect)
 
     def test_extract_fraction_terms_basic(self):
-        root, expect = tree('ab / (ca), a / a * (b / c)')
+        root, expect = tree('(ab) / (ca), a / a * b / c')
         n, d = root
         self.assertEqual(extract_fraction_terms(root,
                 (Scope(n), Scope(d), n[0], d[1])), expect)
 
     def test_extract_fraction_terms_leaf(self):
-        root, expect = tree('ba / a, a / a * (b / 1)')
+        root, expect = tree('(ba) / a, a / a * b / 1')
         n, d = root
         self.assertEqual(extract_fraction_terms(root,
                 (Scope(n), Scope(N(OP_MUL, d)), n[1], d)), expect)
 
-        root, expect = tree('a / (ab), a / a * (1 / b)')
+        root, expect = tree('a / (ab), a / a * 1 / b')
         n, d = root
         self.assertEqual(extract_fraction_terms(root,
                 (Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect)
 
     def test_extract_fraction_terms_chain(self):
         self.assertRewrite([
-            'a ^ 3 * 4 / (a ^ 2 * 5)',
-            'a ^ 3 / a ^ 2 * (4 / 5)',
-            'a ^ (3 - 2)(4 / 5)',
-            'a ^ 1 * (4 / 5)',
-            'a(4 / 5)',
+            '(a ^ 3 * 4) / (a ^ 2 * 5)',
+            'a ^ 3 / a ^ 2 * 4 / 5',
+            'a ^ (3 - 2)4 / 5',
+            'a ^ 1 * 4 / 5',
+            'a * 4 / 5',
             # FIXME: '4 / 5 * a',
         ])

+ 4 - 4
tests/test_rules_integrals.py

@@ -37,7 +37,7 @@ class TestRulesIntegrals(RulesTestCase):
         self.assertEqual(solve_integral(root, F), Fx(b) - Fx(a))
 
     def test_solve_indef(self):
-        root, expect = tree('[x ^ 2]_a^b, b2 - a2')
+        root, expect = tree('[x ^ 2]_a^b, b ^ 2 - a ^ 2')
         self.assertEqual(solve_indef(root, ()), expect)
 
     def test_match_integrate_variable_power(self):
@@ -98,11 +98,11 @@ class TestRulesIntegrals(RulesTestCase):
                 [P(root, split_negation_to_constant)])
 
     def test_split_negation_to_constant(self):
-        root, expect = tree('int -x2 dx, int (-1)x2 dx')
+        root, expect = tree('int -x ^ 2 dx, int (-1)x ^ 2 dx')
         self.assertEqual(split_negation_to_constant(root, ()), expect)
 
     def test_factor_out_constant(self):
-        root, expect = tree('int cx2 dx, c int x2 dx')
+        root, expect = tree('int cx dx, c int x dx')
         c, x2 = cx2 = root[0]
         self.assertEqual(factor_out_constant(root, (Scope(cx2), c)), expect)
 
@@ -124,7 +124,7 @@ class TestRulesIntegrals(RulesTestCase):
     def test_match_division_integral_chain(self):
         self.assertRewrite([
             'int a / x',
-            'int a(1 / x) dx',
+            'int a * 1 / x dx',
             # FIXME: 'a int 1 / x dx',  # fix with strategy
             # FIXME: 'aln|x| + c',
         ])

+ 2 - 2
tests/test_rules_lineq.py

@@ -59,7 +59,7 @@ class TestRulesLineq(RulesTestCase):
         self.assertEqual(subtract_term(root, (a,)), expect)
 
     def test_divide_term(self):
-        root, a, expect = tree('x * a = b, a, x * a / a = b / a')
+        root, a, expect = tree('x * a = b, a, (xa) / a = b / a')
         self.assertEqual(divide_term(root, (a,)), expect)
 
     def test_multiply_term(self):
@@ -89,7 +89,7 @@ class TestRulesLineq(RulesTestCase):
             '5x = 0 * 3x - 5',
             '5x = 0 - 5',
             '5x = -5',
-            '5x / 5 = (-5) / 5',
+            '(5x) / 5 = (-5) / 5',
             '5 / 5 * x = (-5) / 5',
             '1x = (-5) / 5',
             'x = (-5) / 5',

+ 1 - 1
tests/test_rules_powers.py

@@ -179,7 +179,7 @@ class TestRulesPowers(RulesTestCase):
                               N('sqrt', a, m))
 
     def test_match_constant_exponent(self):
-        a0, a1, a2 = tree('a0,a1,a2')
+        a0, a1, a2 = tree('a ^ 0, a ^ 1, a ^ 2')
 
         self.assertEqualPos(match_constant_exponent(a0),
                             [P(a0, remove_power_of_zero, ())])

+ 5 - 5
tests/test_validation.py

@@ -5,17 +5,17 @@ from src.validation import validate
 class TestValidation(TestCase):
 
     def test_simple_success(self):
-        self.assertTrue(validate('3a+a', '4a'))
+        self.assertTrue(validate('3a + a', '4a'))
 
     def test_simple_failure(self):
-        self.assertFalse(validate('3a+a', '4a+1'))
+        self.assertFalse(validate('3a + a', '4a + 1'))
 
     def test_intermediate_success(self):
-        self.assertTrue(validate('3a+a+b+2b', '4a+3b'))
-        self.assertTrue(validate('a/b/(c/d)', 'ad/(bc)'))
+        self.assertTrue(validate('3a + a + b + 2b', '4a + 3b'))
+        self.assertTrue(validate('a / b / (c / d)', '(ad) / (bc)'))
 
     def test_intermediate_failure(self):
-        self.assertFalse(validate('3a+a+b+2b', '4a+4b'))
+        self.assertFalse(validate('3a + a + b + 2b', '4a + 4b'))
 
     #def test_advanced_failure(self):
     #    self.assertFalse(validate('(x-1)^3+(x-1)^3', '4a+4b'))