Commit 52dcf02f authored by Taddeus Kroes's avatar Taddeus Kroes

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'.
parent fcfc7cef
graph_drawing @ aade5fc5
Subproject commit ade1a95046e5539a4f11535892061e05b7c23b95 Subproject commit aade5fc51f4b19e84d180fcff9b6c6d89db93667
...@@ -86,12 +86,13 @@ class Parser(BisonParser): ...@@ -86,12 +86,13 @@ class Parser(BisonParser):
# ------------------------------ # ------------------------------
precedences = ( precedences = (
('left', ('COMMA', )), ('left', ('COMMA', )),
('left', ('INTEGRAL', 'DERIVATIVE')),
('left', ('OR', )), ('left', ('OR', )),
('left', ('AND', )), ('left', ('AND', )),
('left', ('EQ', )), ('left', ('EQ', )),
('left', ('MINUS', 'PLUS', 'NEG')), ('left', ('MINUS', 'PLUS', 'NEG')),
('left', ('TIMES', 'DIVIDE')), ('left', ('INTEGRAL', 'DERIVATIVE')),
('left', ('TIMES', )),
('left', ('DIVIDE', )),
('right', ('FUNCTION', )), ('right', ('FUNCTION', )),
('right', ('POW', )), ('right', ('POW', )),
('left', ('SUB', )), ('left', ('SUB', )),
...@@ -179,12 +180,11 @@ class Parser(BisonParser): ...@@ -179,12 +180,11 @@ class Parser(BisonParser):
# a[ -> a * [ # a[ -> a * [
+ '|(\))\s*([\x00-\x09\x0b-\x19a-z0-9])' # )a -> ) * a + '|(\))\s*([\x00-\x09\x0b-\x19a-z0-9])' # )a -> ) * a
+ '|([\x00-\x09\x0b-\x19a-z])\s*' + '|([\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 + '|([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-z])([0-9])' # a4 -> a ^ 4
+ '|([\x00-\x09\x0b-\x19a-z0-9])(\s+[0-9]))' # a 4 -> a * 4 + '|([\x00-\x09\x0b-\x190-9])(\s+[0-9]))' # 4 4 -> 4 * 4
# 4 4 -> 4 * 4
) )
def preprocess_data(match): def preprocess_data(match):
...@@ -199,8 +199,8 @@ class Parser(BisonParser): ...@@ -199,8 +199,8 @@ class Parser(BisonParser):
# If all characters on the right are numbers. e.g. "a4", the # If all characters on the right are numbers. e.g. "a4", the
# expression implies exponentiation. Make sure ")4" is not # expression implies exponentiation. Make sure ")4" is not
# converted into an exponentiation, because that's multiplication. # converted into an exponentiation, because that's multiplication.
if left != ')' and not left.isdigit() and right.isdigit(): #if left != ')' and not left.isdigit() and right.isdigit():
return '%s^%s' % (left, right) # return '%s^%s' % (left, right)
# match: ab | abc | abcd (where left = "a") # match: ab | abc | abcd (where left = "a")
return '*'.join([left] + list(re.sub(r'^ +', '', right))) return '*'.join([left] + list(re.sub(r'^ +', '', right)))
...@@ -219,9 +219,14 @@ class Parser(BisonParser): ...@@ -219,9 +219,14 @@ class Parser(BisonParser):
for i, keyword in enumerate(words): for i, keyword in enumerate(words):
data = data.replace(chr(i), keyword) 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) 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 if self.verbose and data_before != data: # pragma: nocover
print 'hook_read_after() modified the input data:' print 'hook_read_after() modified the input data:'
print 'before:', repr(data_before) print 'before:', repr(data_before)
......
...@@ -19,7 +19,7 @@ class TestB1Ch10(unittest.TestCase): ...@@ -19,7 +19,7 @@ class TestB1Ch10(unittest.TestCase):
('(2+3/7)^4', ('(2+3/7)^4',
N('^', N('+', L(2), N('/', L(3), L(7))), L(4)) N('^', N('+', L(2), N('/', L(3), L(7))), L(4))
), ),
('x3*x2*x', ('x^3*x^2*x',
N('*', N('*',
N('*', N('*',
N('^', L('x'), L(3)), N('^', L('x'), L(3)),
...@@ -27,10 +27,10 @@ class TestB1Ch10(unittest.TestCase): ...@@ -27,10 +27,10 @@ class TestB1Ch10(unittest.TestCase):
L('x') L('x')
) )
), ),
('-x3*-2x5', ('-x^3*-2x^5',
-(L('x') ** L(3) * -(L(2) * L('x') ** L(5))) -(L('x') ** L(3) * -(L(2) * L('x') ** L(5)))
), ),
('(7x2y3)^2/(7x2y3)', ('(7x^2y^3)^2/(7x^2y^3)',
N('/', N('/',
N('^', N('^',
N('*', N('*',
......
...@@ -34,9 +34,8 @@ class TestCalc(unittest.TestCase): ...@@ -34,9 +34,8 @@ class TestCalc(unittest.TestCase):
expressions = [ expressions = [
('xy', N('*', L('x'), L('y'))), ('xy', N('*', L('x'), L('y'))),
('2x', N('*', L(2), L('x'))), ('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))), ('3 4', N('*', L(3), L(4))),
('xy4', N('*', L('x'), N('^', L('y'), L(4)))),
('(x)4', N('*', L('x'), L(4))), ('(x)4', N('*', L('x'), L(4))),
('(3+4)2', N('*', N('+', L(3), L(4)), L(2))), ('(3+4)2', N('*', N('+', L(3), L(4)), L(2))),
] ]
......
...@@ -15,7 +15,7 @@ class TestLeidenOefenopgave(TestCase): ...@@ -15,7 +15,7 @@ class TestLeidenOefenopgave(TestCase):
return return
for exp, solution in [ 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'),
('(x-1)^2', 'x ^ 2 - 2x + 1'), ('(x-1)^2', 'x ^ 2 - 2x + 1'),
('(2x+x)*x', '3x ^ 2'), ('(2x+x)*x', '3x ^ 2'),
...@@ -172,7 +172,7 @@ class TestLeidenOefenopgave(TestCase): ...@@ -172,7 +172,7 @@ class TestLeidenOefenopgave(TestCase):
def test_4_3(self): def test_4_3(self):
self.assertRewrite([ self.assertRewrite([
'(7/3)(3/5)', '(7/3)(3/5)',
'7 * 3 / (3 * 5)', '(7 * 3) / (3 * 5)',
'21 / (3 * 5)', '21 / (3 * 5)',
'21 / 15', '21 / 15',
'7 / 5', '7 / 5',
...@@ -184,7 +184,7 @@ class TestLeidenOefenopgave(TestCase): ...@@ -184,7 +184,7 @@ class TestLeidenOefenopgave(TestCase):
def test_4_5(self): def test_4_5(self):
self.assertRewrite([ self.assertRewrite([
'1/4 * 1/x', '1/4 * 1/x',
'1 / 4 / x', '(1 * 1) / (4x)',
'1 / (4x)', '1 / (4x)',
]) ])
......
...@@ -4,7 +4,7 @@ from tests.rulestestcase import RulesTestCase as TestCase ...@@ -4,7 +4,7 @@ from tests.rulestestcase import RulesTestCase as TestCase
class TestLeidenOefenopgaveV12(TestCase): class TestLeidenOefenopgaveV12(TestCase):
def test_1_a(self): def test_1_a(self):
self.assertRewrite([ 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 - 5 * 3x + 5 * 6)', '-(5x ^ 2 - 5 * 3x + 5 * 6)',
'-(5x ^ 2 - 15x + 5 * 6)', '-(5x ^ 2 - 15x + 5 * 6)',
...@@ -57,44 +57,44 @@ class TestLeidenOefenopgaveV12(TestCase): ...@@ -57,44 +57,44 @@ class TestLeidenOefenopgaveV12(TestCase):
def test_2_a(self): def test_2_a(self):
self.assertRewrite([ self.assertRewrite([
'(a2b^-1)^3(ab2)', '(a^2b^-1)^3(ab^2)',
'(a ^ 2 * (1 / b ^ 1)) ^ 3 * ab ^ 2', '(a ^ 2 * 1 / b ^ 1) ^ 3 * ab ^ 2',
'(a ^ 2 * (1 / b)) ^ 3 * ab ^ 2', '(a ^ 2 * 1 / b) ^ 3 * ab ^ 2',
'(1a ^ 2 / b) ^ 3 * ab ^ 2', '((1a ^ 2) / b) ^ 3 * ab ^ 2',
'(a ^ 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 ^ (2 * 3) / b ^ 3 * ab ^ 2', 'a ^ (2 * 3) / b ^ 3 * ab ^ 2',
'a ^ 6 / 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 ^ (6 + 1) / b ^ 3 * b ^ 2',
'a ^ 7 / b ^ 3 * b ^ 2', 'a ^ 7 / b ^ 3 * b ^ 2',
'a ^ 7 * b ^ 2 / b ^ 3', '(a ^ 7 * b ^ 2) / b ^ 3',
'b ^ 2 / b ^ 3 * (a ^ 7 / 1)', 'b ^ 2 / b ^ 3 * a ^ 7 / 1',
'b ^ (2 - 3)(a ^ 7 / 1)', 'b ^ (2 - 3)a ^ 7 / 1',
'b ^ (-1)(a ^ 7 / 1)', 'b ^ (-1)a ^ 7 / 1',
'1 / b ^ 1 * (a ^ 7 / 1)', '1 / b ^ 1 * a ^ 7 / 1',
'1 / b * (a ^ 7 / 1)', '1 / b * a ^ 7 / 1',
'1 / b * a ^ 7', '1 / b * a ^ 7',
'1a ^ 7 / b', '(1a ^ 7) / b',
'a ^ 7 / b', 'a ^ 7 / b',
]) ])
def test_2_b(self): def test_2_b(self):
self.assertRewrite([ self.assertRewrite([
'a3b2a3', 'a^3b^2a^3',
'a ^ (3 + 3)b ^ 2', 'a ^ (3 + 3)b ^ 2',
'a ^ 6 * b ^ 2', 'a ^ 6 * b ^ 2',
]) ])
#def test_2_c(self): #def test_2_c(self):
# self.assertRewrite([ # self.assertRewrite([
# 'a5+a3', # 'a^5+a^3',
# 'a ^ 5 + a ^ 3', # 'a ^ 5 + a ^ 3',
# ]) # ])
def test_2_d(self): def test_2_d(self):
self.assertRewrite([ self.assertRewrite([
'a2+a2', 'a^2+a^2',
'(1 + 1)a ^ 2', '(1 + 1)a ^ 2',
'2a ^ 2', '2a ^ 2',
]) ])
...@@ -102,8 +102,8 @@ class TestLeidenOefenopgaveV12(TestCase): ...@@ -102,8 +102,8 @@ class TestLeidenOefenopgaveV12(TestCase):
def test_2_e(self): def test_2_e(self):
self.assertRewrite([ self.assertRewrite([
'4b^-2', '4b^-2',
'4(1 / b ^ 2)', '4 * 1 / b ^ 2',
'1 * 4 / b ^ 2', '(1 * 4) / b ^ 2',
'4 / b ^ 2', '4 / b ^ 2',
]) ])
...@@ -113,7 +113,7 @@ class TestLeidenOefenopgaveV12(TestCase): ...@@ -113,7 +113,7 @@ class TestLeidenOefenopgaveV12(TestCase):
'4 ^ (-2)b ^ (-2)', '4 ^ (-2)b ^ (-2)',
'1 / 4 ^ 2 * b ^ (-2)', '1 / 4 ^ 2 * b ^ (-2)',
'1 / 16 * b ^ (-2)', '1 / 16 * b ^ (-2)',
'1 / 16 * (1 / b ^ 2)', '1 / 16 * 1 / b ^ 2',
'1 * 1 / (16b ^ 2)', '(1 * 1) / (16b ^ 2)',
'1 / (16b ^ 2)', '1 / (16b ^ 2)',
]) ])
...@@ -240,7 +240,7 @@ class TestNode(RulesTestCase): ...@@ -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 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 ^ 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 x ^ 2')), 'int_a^b x ^ 2 dx')
self.assertEqual(str(tree('int_(a-b)^(a+b) x ^ 2')), self.assertEqual(str(tree('int_(a-b)^(a+b) x ^ 2')),
...@@ -259,7 +259,7 @@ class TestNode(RulesTestCase): ...@@ -259,7 +259,7 @@ class TestNode(RulesTestCase):
self.assertEqual(infinity(), tree('oo')) self.assertEqual(infinity(), tree('oo'))
def test_absolute(self): def test_absolute(self):
self.assertEqual(absolute(tree('x2')), tree('|x2|')) self.assertEqual(absolute(tree('x^2')), tree('|x^2|'))
def test_sin(self): def test_sin(self):
self.assertEqual(sin(tree('x')), tree('sin(x)')) self.assertEqual(sin(tree('x')), tree('sin(x)'))
...@@ -288,9 +288,9 @@ class TestNode(RulesTestCase): ...@@ -288,9 +288,9 @@ class TestNode(RulesTestCase):
def test_integral(self): def test_integral(self):
x2, x, y, a, b = tree('x ^ 2, x, y, a, b') 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), tree('int x^2 dx'))
self.assertEqual(integral(x2, x, a, b), tree('int_a^b x2 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 x2 dy')) self.assertEqual(integral(x2, y, a, b), tree('int_a^b x^2 dy'))
def test_indef(self): def test_indef(self):
x2, a, b, expect = tree('x ^ 2, a, b, [x ^ 2]_a^b') x2, a, b, expect = tree('x ^ 2, a, b, [x ^ 2]_a^b')
......
...@@ -52,7 +52,7 @@ class TestParser(RulesTestCase): ...@@ -52,7 +52,7 @@ class TestParser(RulesTestCase):
def test_preprocessor(self): def test_preprocessor(self):
self.assertEqual(tree('ab'), tree('a * b')) self.assertEqual(tree('ab'), tree('a * b'))
self.assertEqual(tree('abc'), tree('a * b * c')) 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('a 2'), tree('a * 2'))
self.assertEqual(tree('2a'), tree('2 * a')) self.assertEqual(tree('2a'), tree('2 * a'))
self.assertEqual(tree('2(a + b)'), tree('2 * (a + b)')) self.assertEqual(tree('2(a + b)'), tree('2 * (a + b)'))
...@@ -91,8 +91,8 @@ class TestParser(RulesTestCase): ...@@ -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'), 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) self.assertEqual(tree('d/d'), d / d)
# FIXME: self.assertEqual(tree('d(x ^ 2)/dx'), der(exp, x)) # FIXME: self.assertEqual(tree('d(x ^ 2)/dx'), der(exp, x))
...@@ -120,17 +120,27 @@ class TestParser(RulesTestCase): ...@@ -120,17 +120,27 @@ class TestParser(RulesTestCase):
x, y, dx, a, b, l2 = tree('x, y, dx, a, b, 2') 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 x'), integral(x, x))
self.assertEqual(tree('int x2'), integral(x ** 2, x)) self.assertEqual(tree('int x ^ 2'), integral(x ** 2, x))
self.assertEqual(tree('int x2 dx'), integral(x ** 2, x)) self.assertEqual(tree('int x ^ 2 dx'), integral(x ** 2, x))
self.assertEqual(tree('int x2 dy'), integral(x ** 2, y)) 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 x ^ 2'), 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 x ^ 2 dy'), integral(x ** 2, y, a, b))
self.assertEqual(tree('int_(a-b)^(a+b) x2'), self.assertEqual(tree('int_(a-b)^(a+b) x ^ 2'),
integral(x ** 2, x, a - b, a + b)) 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^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): def test_indefinite_integral(self):
x2, a, b = tree('x ^ 2, a, b') x2, a, b = tree('x ^ 2, a, b')
...@@ -141,7 +151,7 @@ class TestParser(RulesTestCase): ...@@ -141,7 +151,7 @@ class TestParser(RulesTestCase):
x = tree('x') x = tree('x')
self.assertEqual(tree('|x|'), absolute(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): def test_find_possibilities_basic(self):
l1, l2 = root = tree('1 + 2') l1, l2 = root = tree('1 + 2')
...@@ -157,3 +167,6 @@ class TestParser(RulesTestCase): ...@@ -157,3 +167,6 @@ class TestParser(RulesTestCase):
def test_no_expression_error(self): def test_no_expression_error(self):
self.assertRaises(RuntimeError, ParserWrapper(Parser).run, ['', '?']) self.assertRaises(RuntimeError, ParserWrapper(Parser).run, ['', '?'])
def test_precedence(self):
self.assertEqual(tree('ab / cd'), tree('a * (b / c) * d'))
...@@ -109,8 +109,8 @@ class TestRulesDerivatives(RulesTestCase): ...@@ -109,8 +109,8 @@ class TestRulesDerivatives(RulesTestCase):
"x ^ x * ([x]' * ln(x) + x[ln(x)]')", "x ^ x * ([x]' * ln(x) + x[ln(x)]')",
"x ^ x * (1ln(x) + x[ln(x)]')", "x ^ x * (1ln(x) + x[ln(x)]')",
"x ^ x * (ln(x) + x[ln(x)]')", "x ^ x * (ln(x) + x[ln(x)]')",
"x ^ x * (ln(x) + x(1 / x))", "x ^ x * (ln(x) + x * 1 / x)",
"x ^ x * (ln(x) + 1x / x)", "x ^ x * (ln(x) + (1x) / x)",
"x ^ x * (ln(x) + x / x)", "x ^ x * (ln(x) + x / x)",
"x ^ x * (ln(x) + 1)", "x ^ x * (ln(x) + 1)",
"x ^ x * ln(x) + x ^ x * 1", "x ^ x * ln(x) + x ^ x * 1",
......
...@@ -198,14 +198,14 @@ class TestRulesFractions(RulesTestCase): ...@@ -198,14 +198,14 @@ class TestRulesFractions(RulesTestCase):
self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b)) self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
def test_match_extract_fraction_terms(self): 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 n, d = root
self.assertEqualPos(match_extract_fraction_terms(root), 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), a, a))])
lscp = lambda l: Scope(N(OP_MUL, l)) 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), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), a, a))]) [P(root, extract_fraction_terms, (Scope(n), lscp(d), a, a))])
...@@ -213,7 +213,7 @@ class TestRulesFractions(RulesTestCase): ...@@ -213,7 +213,7 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualPos(match_extract_fraction_terms(root), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (lscp(n), Scope(d), a, a))]) [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), 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), a, a)),
P(root, extract_fraction_terms, (Scope(n), Scope(d), b, b)), P(root, extract_fraction_terms, (Scope(n), Scope(d), b, b)),
...@@ -222,19 +222,19 @@ class TestRulesFractions(RulesTestCase): ...@@ -222,19 +222,19 @@ class TestRulesFractions(RulesTestCase):
root = tree('a / a') root = tree('a / a')
self.assertEqualPos(match_extract_fraction_terms(root), []) 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), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, aq))]) [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), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), a, aq))]) [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), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, a))]) [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), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_nominator_term, (2, a))]) [P(root, extract_nominator_term, (2, a))])
...@@ -242,16 +242,16 @@ class TestRulesFractions(RulesTestCase): ...@@ -242,16 +242,16 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualPos(match_extract_fraction_terms(root), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_nominator_term, (1, a))]) [P(root, extract_nominator_term, (1, a))])
root = tree('2*4 / 3') root = tree('(2 * 4) / 3')
self.assertEqualPos(match_extract_fraction_terms(root), []) 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), self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_nominator_term, (2, a)), [P(root, extract_nominator_term, (2, a)),
P(root, extract_fraction_terms, (Scope(n), lscp(d), 2, 2))]) P(root, extract_fraction_terms, (Scope(n), lscp(d), 2, 2))])
def test_extract_nominator_term(self): 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] l2, a = root[0]
self.assertEqual(extract_nominator_term(root, (l2, a)), expect) self.assertEqual(extract_nominator_term(root, (l2, a)), expect)
...@@ -259,28 +259,28 @@ class TestRulesFractions(RulesTestCase): ...@@ -259,28 +259,28 @@ class TestRulesFractions(RulesTestCase):
self.assertEqual(extract_nominator_term(root, (l1, root[0])), expect) self.assertEqual(extract_nominator_term(root, (l1, root[0])), expect)
def test_extract_fraction_terms_basic(self): 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 n, d = root
self.assertEqual(extract_fraction_terms(root, self.assertEqual(extract_fraction_terms(root,
(Scope(n), Scope(d), n[0], d[1])), expect) (Scope(n), Scope(d), n[0], d[1])), expect)
def test_extract_fraction_terms_leaf(self): 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 n, d = root
self.assertEqual(extract_fraction_terms(root, self.assertEqual(extract_fraction_terms(root,
(Scope(n), Scope(N(OP_MUL, d)), n[1], d)), expect) (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 n, d = root
self.assertEqual(extract_fraction_terms(root, self.assertEqual(extract_fraction_terms(root,
(Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect) (Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect)
def test_extract_fraction_terms_chain(self): def test_extract_fraction_terms_chain(self):
self.assertRewrite([ self.assertRewrite([
'a ^ 3 * 4 / (a ^ 2 * 5)', '(a ^ 3 * 4) / (a ^ 2 * 5)',
'a ^ 3 / a ^ 2 * (4 / 5)', 'a ^ 3 / a ^ 2 * 4 / 5',
'a ^ (3 - 2)(4 / 5)', 'a ^ (3 - 2)4 / 5',
'a ^ 1 * (4 / 5)', 'a ^ 1 * 4 / 5',
'a(4 / 5)', 'a * 4 / 5',
# FIXME: '4 / 5 * a', # FIXME: '4 / 5 * a',
]) ])
...@@ -37,7 +37,7 @@ class TestRulesIntegrals(RulesTestCase): ...@@ -37,7 +37,7 @@ class TestRulesIntegrals(RulesTestCase):
self.assertEqual(solve_integral(root, F), Fx(b) - Fx(a)) self.assertEqual(solve_integral(root, F), Fx(b) - Fx(a))
def test_solve_indef(self): 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) self.assertEqual(solve_indef(root, ()), expect)
def test_match_integrate_variable_power(self): def test_match_integrate_variable_power(self):
...@@ -98,11 +98,11 @@ class TestRulesIntegrals(RulesTestCase): ...@@ -98,11 +98,11 @@ class TestRulesIntegrals(RulesTestCase):
[P(root, split_negation_to_constant)]) [P(root, split_negation_to_constant)])
def test_split_negation_to_constant(self): 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) self.assertEqual(split_negation_to_constant(root, ()), expect)
def test_factor_out_constant(self): 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] c, x2 = cx2 = root[0]
self.assertEqual(factor_out_constant(root, (Scope(cx2), c)), expect) self.assertEqual(factor_out_constant(root, (Scope(cx2), c)), expect)
...@@ -124,7 +124,7 @@ class TestRulesIntegrals(RulesTestCase): ...@@ -124,7 +124,7 @@ class TestRulesIntegrals(RulesTestCase):
def test_match_division_integral_chain(self): def test_match_division_integral_chain(self):
self.assertRewrite([ self.assertRewrite([
'int a / x', 'int a / x',
'int a(1 / x) dx', 'int a * 1 / x dx',
# FIXME: 'a int 1 / x dx', # fix with strategy # FIXME: 'a int 1 / x dx', # fix with strategy
# FIXME: 'aln|x| + c', # FIXME: 'aln|x| + c',
]) ])
......
...@@ -59,7 +59,7 @@ class TestRulesLineq(RulesTestCase): ...@@ -59,7 +59,7 @@ class TestRulesLineq(RulesTestCase):
self.assertEqual(subtract_term(root, (a,)), expect) self.assertEqual(subtract_term(root, (a,)), expect)
def test_divide_term(self): 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) self.assertEqual(divide_term(root, (a,)), expect)
def test_multiply_term(self): def test_multiply_term(self):
...@@ -89,7 +89,7 @@ class TestRulesLineq(RulesTestCase): ...@@ -89,7 +89,7 @@ class TestRulesLineq(RulesTestCase):
'5x = 0 * 3x - 5', '5x = 0 * 3x - 5',
'5x = 0 - 5', '5x = 0 - 5',
'5x = -5', '5x = -5',
'5x / 5 = (-5) / 5', '(5x) / 5 = (-5) / 5',
'5 / 5 * x = (-5) / 5', '5 / 5 * x = (-5) / 5',
'1x = (-5) / 5', '1x = (-5) / 5',
'x = (-5) / 5', 'x = (-5) / 5',
......
...@@ -179,7 +179,7 @@ class TestRulesPowers(RulesTestCase): ...@@ -179,7 +179,7 @@ class TestRulesPowers(RulesTestCase):
N('sqrt', a, m)) N('sqrt', a, m))
def test_match_constant_exponent(self): 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), self.assertEqualPos(match_constant_exponent(a0),
[P(a0, remove_power_of_zero, ())]) [P(a0, remove_power_of_zero, ())])
......
...@@ -5,17 +5,17 @@ from src.validation import validate ...@@ -5,17 +5,17 @@ from src.validation import validate
class TestValidation(TestCase): class TestValidation(TestCase):
def test_simple_success(self): def test_simple_success(self):
self.assertTrue(validate('3a+a', '4a')) self.assertTrue(validate('3a + a', '4a'))
def test_simple_failure(self): def test_simple_failure(self):
self.assertFalse(validate('3a+a', '4a+1')) self.assertFalse(validate('3a + a', '4a + 1'))
def test_intermediate_success(self): def test_intermediate_success(self):
self.assertTrue(validate('3a+a+b+2b', '4a+3b')) self.assertTrue(validate('3a + a + b + 2b', '4a + 3b'))
self.assertTrue(validate('a/b/(c/d)', 'ad/(bc)')) self.assertTrue(validate('a / b / (c / d)', '(ad) / (bc)'))
def test_intermediate_failure(self): 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): #def test_advanced_failure(self):
# self.assertFalse(validate('(x-1)^3+(x-1)^3', '4a+4b')) # self.assertFalse(validate('(x-1)^3+(x-1)^3', '4a+4b'))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment