Commit 6139bd82 authored by Taddeus Kroes's avatar Taddeus Kroes

Merge branch 'master' into gonio

Conflicts:
	tests/test_rules_goniometry.py
parents ab2c06a8 da968378
...@@ -2,47 +2,9 @@ ...@@ -2,47 +2,9 @@
- Sort polynom by its exponents? - Sort polynom by its exponents?
- No possibilities found for:
>>> a2b3 + a2b3
a ^ 2 * b ^ 3 + a ^ 2 * b ^ 3
- 2 + 3 + 4 rewrites to 5 instead of 5 + 4
-> the problem is that the 'root' of the application is actually a subtree
of the entire expression. This means that the parent of each possibility
root (or 'subtree') must me stored to be able to replace the subtree.
- MESSAGES needs to be expanded. - MESSAGES needs to be expanded.
- rewrite match_combine_polynomes to an even more generic form: - Fix division by zero caused by "0/0": Catch exception in front-end
match_combine_factors.
- "--ab + c" has no rewrite possibility. The graph of "--ab + c" is also
not valid:
-
+
╭─┴╮
* c
╭┴╮
- b
a
- The following expression gives a cycle in the possibilities:
>>> ab + ba
possibilities:
Group "ab" is multiplied by 1 and 1, combine them.
>>> (1 + 1) * ab
(1 + 1)ab
possibilities:
Combine the constants 1 and 1.
Group "1" is multiplied by 1 and 1, combine them.
Expand a(1 + 1).
Expand b(1 + 1).
- Fix division by zero caused by "0/0".
smvv@multivac ~/work/trs $ printf "a/0\n??" | ./main.py smvv@multivac ~/work/trs $ printf "a/0\n??" | ./main.py
Traceback (most recent call last): Traceback (most recent call last):
...@@ -84,3 +46,10 @@ smvv@multivac ~/work/trs $ printf "0/1\n??" | ./main.py ...@@ -84,3 +46,10 @@ smvv@multivac ~/work/trs $ printf "0/1\n??" | ./main.py
<Possibility root="0 / 1" handler=divide_numerics args=(0, 1)> <Possibility root="0 / 1" handler=divide_numerics args=(0, 1)>
Division of 0 by 1 reduces to 0. Division of 0 by 1 reduces to 0.
Division of 0 by 1 reduces to 0. Division of 0 by 1 reduces to 0.
- Fractions constant rewrite rules.
- >>> (sin x) ^ 2 + (cos x) ^ 2
sin(x) ^ 2 + cos(x) ^ 2
>>> sin(x) ^ 2 + cos(x) ^ 2
sin(x ^ 2) + cos(x ^ 2)
graph_drawing @ 21f0710c
Subproject commit 821bdb8f8408fb36ee1d92ada162589794a8c5b3 Subproject commit 21f0710c80184b6ea18827808ec40af924f0c8a8
...@@ -61,8 +61,10 @@ class Parser(BisonParser): ...@@ -61,8 +61,10 @@ class Parser(BisonParser):
('left', ('COMMA', )), ('left', ('COMMA', )),
('left', ('MINUS', 'PLUS')), ('left', ('MINUS', 'PLUS')),
('left', ('TIMES', 'DIVIDE')), ('left', ('TIMES', 'DIVIDE')),
('left', ('EQ', )),
('left', ('NEG', )), ('left', ('NEG', )),
('right', ('POW', )), ('right', ('POW', )),
('right', ('SIN', 'COS', 'TAN', 'SOLVE', 'INT', 'SQRT')),
) )
interactive = 0 interactive = 0
...@@ -154,7 +156,7 @@ class Parser(BisonParser): ...@@ -154,7 +156,7 @@ class Parser(BisonParser):
left, right = filter(None, match.groups()) left, right = filter(None, match.groups())
# Filter words (otherwise they will be preprocessed as well) # Filter words (otherwise they will be preprocessed as well)
if left + right in ['graph', 'raise']: if (left + right).upper() in self.tokens:
return left + right return left + right
# If all characters on the right are numbers. e.g. "a4", the # If all characters on the right are numbers. e.g. "a4", the
...@@ -334,6 +336,12 @@ class Parser(BisonParser): ...@@ -334,6 +336,12 @@ class Parser(BisonParser):
def on_unary(self, target, option, names, values): def on_unary(self, target, option, names, values):
""" """
unary : MINUS exp %prec NEG unary : MINUS exp %prec NEG
| SIN exp
| COS exp
| TAN exp
| INT exp
| SOLVE exp
| SQRT exp
""" """
if option == 0: # rule: NEG exp if option == 0: # rule: NEG exp
...@@ -346,6 +354,11 @@ class Parser(BisonParser): ...@@ -346,6 +354,11 @@ class Parser(BisonParser):
return values[1] return values[1]
if option < 7: # rule: SIN exp | COS exp | TAN exp | INT exp
if values[1].type == TYPE_OPERATOR and values[1].op == OP_COMMA:
return Node(values[0], *values[1])
return Node(*values)
raise BisonSyntaxError('Unsupported option %d in target "%s".' raise BisonSyntaxError('Unsupported option %d in target "%s".'
% (option, target)) # pragma: nocover % (option, target)) # pragma: nocover
...@@ -355,13 +368,14 @@ class Parser(BisonParser): ...@@ -355,13 +368,14 @@ class Parser(BisonParser):
| exp TIMES exp | exp TIMES exp
| exp DIVIDE exp | exp DIVIDE exp
| exp POW exp | exp POW exp
| exp EQ exp
| exp MINUS exp | exp MINUS exp
""" """
if 0 <= option < 4: # rule: exp {PLUS,TIMES,DIVIDES,POW} exp if 0 <= option < 5: # rule: exp {PLUS,TIMES,DIVIDES,POW,EQ} exp
return Node(values[1], values[0], values[2]) return Node(values[1], values[0], values[2])
if option == 4: # rule: exp MINUS exp if option == 5: # rule: exp MINUS exp
node = values[2] node = values[2]
# Add negation to the left-most child # Add negation to the left-most child
...@@ -436,7 +450,6 @@ class Parser(BisonParser): ...@@ -436,7 +450,6 @@ class Parser(BisonParser):
[a-zA-Z] { returntoken(IDENTIFIER); } [a-zA-Z] { returntoken(IDENTIFIER); }
"(" { returntoken(LPAREN); } "(" { returntoken(LPAREN); }
")" { returntoken(RPAREN); } ")" { returntoken(RPAREN); }
"," { returntoken(COMMA); }
""" + operators + r""" """ + operators + r"""
"raise" { returntoken(RAISE); } "raise" { returntoken(RAISE); }
"graph" { returntoken(GRAPH); } "graph" { returntoken(GRAPH); }
......
...@@ -11,13 +11,15 @@ from .fractions import match_constant_division, match_add_constant_fractions, \ ...@@ -11,13 +11,15 @@ from .fractions import match_constant_division, match_add_constant_fractions, \
match_expand_and_add_fractions match_expand_and_add_fractions
from .negation import match_negated_factor, match_negate_polynome, \ from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division match_negated_division
from .sort import match_sort_multiplicants
RULES = { RULES = {
OP_ADD: [match_add_numerics, match_add_constant_fractions, OP_ADD: [match_add_numerics, match_add_constant_fractions,
match_combine_groups], match_combine_groups],
OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents, OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents,
match_expand_and_add_fractions, match_multiply_zero, match_expand_and_add_fractions, match_multiply_zero,
match_negated_factor, match_multiply_one], match_negated_factor, match_multiply_one,
match_sort_multiplicants],
OP_DIV: [match_subtract_exponents, match_divide_numerics, OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_negated_division], match_constant_division, match_negated_division],
OP_POW: [match_multiply_exponents, match_duplicate_exponent, OP_POW: [match_multiply_exponents, match_duplicate_exponent,
......
from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, \ from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, OP_ADD, \
OP_ADD, OP_POW, OP_MUL, OP_SIN, OP_COS, OP_TAN OP_POW, OP_MUL, OP_SIN, OP_COS, OP_TAN
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
def sin(*args):
return N('sin', *args)
def cos(*args):
return N('cos', *args)
def tan(*args):
return N('tan', *args)
def match_add_quadrants(node): def match_add_quadrants(node):
""" """
sin(x) ^ 2 + cos(x) ^ 2 -> 1 sin(x) ^ 2 + cos(x) ^ 2 -> 1
......
from itertools import combinations from itertools import combinations
from ..node import ExpressionNode as Node, ExpressionLeaf as Leaf, Scope, \ from ..node import ExpressionLeaf as Leaf, Scope, OP_ADD, OP_MUL, nary_node, \
OP_ADD, OP_MUL, nary_node, negate negate
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
......
...@@ -38,11 +38,6 @@ def add_numerics(root, args): ...@@ -38,11 +38,6 @@ def add_numerics(root, args):
scope, c0, c1 = args scope, c0, c1 = args
value = c0.actual_value() + c1.actual_value() value = c0.actual_value() + c1.actual_value()
if value < 0:
leaf = Leaf(-value).negate()
else:
leaf = Leaf(value)
# Replace the left node with the new expression # Replace the left node with the new expression
scope.replace(c0, Leaf(abs(value)).negate(int(value < 0))) scope.replace(c0, Leaf(abs(value)).negate(int(value < 0)))
......
from itertools import combinations from itertools import combinations
from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, \ from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, \
OP_MUL, OP_DIV, OP_POW, OP_ADD OP_MUL, OP_DIV, OP_POW, OP_ADD, negate
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -12,6 +12,7 @@ def match_add_exponents(node): ...@@ -12,6 +12,7 @@ def match_add_exponents(node):
a * a^q -> a^(1 + q) a * a^q -> a^(1 + q)
a^p * a -> a^(p + 1) a^p * a -> a^(p + 1)
a * a -> a^(1 + 1) a * a -> a^(1 + 1)
-a * a^q -> -a^(1 + q)
""" """
assert node.is_op(OP_MUL) assert node.is_op(OP_MUL)
...@@ -20,12 +21,12 @@ def match_add_exponents(node): ...@@ -20,12 +21,12 @@ def match_add_exponents(node):
scope = Scope(node) scope = Scope(node)
for n in scope: for n in scope:
# Order powers by their roots, e.g. a^p and a^q are put in the same
# list because of the mutual 'a'
if n.is_identifier(): if n.is_identifier():
s = n s = negate(n, 0)
exponent = L(1) exponent = L(1)
elif n.is_op(OP_POW): elif n.is_op(OP_POW):
# Order powers by their roots, e.g. a^p and a^q are put in the same
# list because of the mutual 'a'
s, exponent = n s, exponent = n
else: # pragma: nocover else: # pragma: nocover
continue continue
...@@ -53,8 +54,10 @@ def add_exponents(root, args): ...@@ -53,8 +54,10 @@ def add_exponents(root, args):
""" """
scope, n0, n1, a, p, q = args scope, n0, n1, a, p, q = args
# TODO: combine exponent negations
# Replace the left node with the new expression # Replace the left node with the new expression
scope.replace(n0, a ** (p + q)) scope.replace(n0, (a ** (p + q)).negate(n0.negated + n1.negated))
# Remove the right node # Remove the right node
scope.remove(n1) scope.remove(n1)
......
from itertools import product, combinations
from ..node import Scope, OP_ADD, OP_MUL
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
def match_sort_multiplicants(node):
"""
Sort multiplicant factors by swapping
x * 2 -> 2x
"""
assert node.is_op(OP_MUL)
p = []
scope = Scope(node)
for i, n in enumerate(scope[1:]):
left_nb = scope[i]
if n.is_numeric() and not left_nb.is_numeric():
p.append(P(node, move_constant, (scope, n, left_nb)))
return p
def move_constant(root, args):
scope, constant, destination = args
scope.replace(destination, constant * destination)
scope.remove(constant)
return scope.as_nary_node()
MESSAGES[move_constant] = \
_('Move constant {2} to the left of the multiplication {0}.')
from src.parser import Parser
from tests.parser import ParserWrapper
class ValidationNode(object):
pass
def validate(exp, result):
"""
Validate that exp =>* result.
"""
parser = ParserWrapper(Parser)
exp = parser.run([exp])
result = parser.run([result])
return validate_graph(exp, result)
def iter_preorder(exp, possibility):
"""
Traverse the possibility tree using pre-order traversal.
"""
pass
def validate_graph(exp, result):
"""
Validate that "exp" =>* "result".
"""
# TODO: Traverse the tree of possibility applications
return False
...@@ -4,10 +4,10 @@ from tests.rulestestcase import RulesTestCase as TestCase, rewrite ...@@ -4,10 +4,10 @@ from tests.rulestestcase import RulesTestCase as TestCase, rewrite
class TestLeidenOefenopgave(TestCase): class TestLeidenOefenopgave(TestCase):
def test_1_1(self): def test_1_1(self):
for chain in [['-5(x2 - 3x + 6)', '-5(x ^ 2 - 3x) - 5 * 6', for chain in [['-5(x2 - 3x + 6)', '-5(x ^ 2 - 3x) - 5 * 6',
'-5 * x ^ 2 - 5 * -3x - 5 * 6', '-5x ^ 2 - 5 * -3x - 5 * 6',
'-5 * x ^ 2 - -15x - 5 * 6', '-5x ^ 2 - -15x - 5 * 6',
'-5 * x ^ 2 + 15x - 5 * 6', '-5x ^ 2 + 15x - 5 * 6',
'-5 * x ^ 2 + 15x - 30', '-5x ^ 2 + 15x - 30',
], ],
]: ]:
self.assertRewrite(chain) self.assertRewrite(chain)
...@@ -15,11 +15,11 @@ class TestLeidenOefenopgave(TestCase): ...@@ -15,11 +15,11 @@ class TestLeidenOefenopgave(TestCase):
return return
for exp, solution in [ for exp, solution in [
('-5(x2 - 3x + 6)', '-30 + 15 * x - 5 * x ^ 2'), ('-5(x2 - 3x + 6)', '-30 + 15x - 5x ^ 2'),
('(x+1)^2', 'x ^ 2 + 2 * x + 1'), ('(x+1)^2', 'x ^ 2 + 2x + 1'),
('(x-1)^2', 'x ^ 2 - 2 * x + 1'), ('(x-1)^2', 'x ^ 2 - 2x + 1'),
('(2x+x)*x', '3 * x ^ 2'), ('(2x+x)*x', '3x ^ 2'),
('-2(6x-4)^2*x', '-72 * x^3 + 96 * x ^ 2 + 32 * x'), ('-2(6x-4)^2*x', '-72x ^ 3 + 96x ^ 2 + 32x'),
('(4x + 5) * -(5 - 4x)', '16x^2 - 25'), ('(4x + 5) * -(5 - 4x)', '16x^2 - 25'),
]: ]:
self.assertEqual(str(rewrite(exp)), solution) self.assertEqual(str(rewrite(exp)), solution)
...@@ -36,20 +36,21 @@ class TestLeidenOefenopgave(TestCase): ...@@ -36,20 +36,21 @@ class TestLeidenOefenopgave(TestCase):
'(x ^ 2 + 2x + 1 * 1)(x + 1)', '(x ^ 2 + 2x + 1 * 1)(x + 1)',
'(x ^ 2 + 2x + 1)(x + 1)', '(x ^ 2 + 2x + 1)(x + 1)',
'(x ^ 2 + 2x)x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1', '(x ^ 2 + 2x)x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x * x ^ 2 + x * 2x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1', 'xx ^ 2 + x * 2x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x ^ (1 + 2) + x * 2x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1', 'x ^ (1 + 2) + x * 2x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x ^ 3 + x * 2x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1', 'x ^ 3 + x * 2x + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ (1 + 1) * 2 + (x ^ 2 + 2x) * 1 + 1x + 1 * 1', 'x ^ 3 + 2xx + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 2 + (x ^ 2 + 2x) * 1 + 1x + 1 * 1', 'x ^ 3 + 2x ^ (1 + 1) + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 2 + 1 * x ^ 2 + 1 * 2x + 1x + 1 * 1', 'x ^ 3 + 2x ^ 2 + (x ^ 2 + 2x) * 1 + 1x + 1 * 1',
'x ^ 3 + x ^ 2 * 2 + x ^ 2 + 1 * 2x + 1x + 1 * 1', 'x ^ 3 + 2x ^ 2 + 1x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + (2 + 1) * x ^ 2 + 1 * 2x + 1x + 1 * 1', 'x ^ 3 + 2x ^ 2 + x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + 3 * x ^ 2 + 1 * 2x + 1x + 1 * 1', 'x ^ 3 + (2 + 1)x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + 3 * x ^ 2 + 2x + 1x + 1 * 1', 'x ^ 3 + 3x ^ 2 + 1 * 2x + 1x + 1 * 1',
'x ^ 3 + 3 * x ^ 2 + 2x + x + 1 * 1', 'x ^ 3 + 3x ^ 2 + 2x + 1x + 1 * 1',
'x ^ 3 + 3 * x ^ 2 + (2 + 1)x + 1 * 1', 'x ^ 3 + 3x ^ 2 + 2x + x + 1 * 1',
'x ^ 3 + 3 * x ^ 2 + 3x + 1 * 1', 'x ^ 3 + 3x ^ 2 + (2 + 1)x + 1 * 1',
'x ^ 3 + 3 * x ^ 2 + 3x + 1', 'x ^ 3 + 3x ^ 2 + 3x + 1 * 1',
'x ^ 3 + 3x ^ 2 + 3x + 1',
] ]
]: ]:
self.assertRewrite(chain) self.assertRewrite(chain)
...@@ -115,7 +116,7 @@ class TestLeidenOefenopgave(TestCase): ...@@ -115,7 +116,7 @@ class TestLeidenOefenopgave(TestCase):
def test_1_5(self): def test_1_5(self):
self.assertRewrite(['(2x + x)x', '(2 + 1)xx', '3xx', self.assertRewrite(['(2x + x)x', '(2 + 1)xx', '3xx',
'3 * x ^ (1 + 1)', '3 * x ^ 2']) '3x ^ (1 + 1)', '3x ^ 2'])
def test_1_7(self): def test_1_7(self):
self.assertRewrite(['(4x + 5) * -(5 - 4x)', self.assertRewrite(['(4x + 5) * -(5 - 4x)',
...@@ -124,15 +125,15 @@ class TestLeidenOefenopgave(TestCase): ...@@ -124,15 +125,15 @@ class TestLeidenOefenopgave(TestCase):
'4x * -5 + 4x * 4x + 5 * -5 + 5 * 4x', '4x * -5 + 4x * 4x + 5 * -5 + 5 * 4x',
'-20x + 4x * 4x + 5 * -5 + 5 * 4x', '-20x + 4x * 4x + 5 * -5 + 5 * 4x',
'-20x + 16xx + 5 * -5 + 5 * 4x', '-20x + 16xx + 5 * -5 + 5 * 4x',
'-20x + 16 * x ^ (1 + 1) + 5 * -5 + 5 * 4x', '-20x + 16x ^ (1 + 1) + 5 * -5 + 5 * 4x',
'-20x + 16 * x ^ 2 + 5 * -5 + 5 * 4x', '-20x + 16x ^ 2 + 5 * -5 + 5 * 4x',
'-20x + 16 * x ^ 2 - 25 + 5 * 4x', '-20x + 16x ^ 2 - 25 + 5 * 4x',
'-20x + 16 * x ^ 2 - 25 + 20x', '-20x + 16x ^ 2 - 25 + 20x',
'(-20 + 20)x + 16 * x ^ 2 - 25', '(-20 + 20)x + 16x ^ 2 - 25',
'0x + 16 * x ^ 2 - 25', '0x + 16x ^ 2 - 25',
'0 + 16 * x ^ 2 - 25', '0 + 16x ^ 2 - 25',
'-25 + 16 * x ^ 2']) '-25 + 16x ^ 2'])
# FIXME: '16 * x ^ 2 - 25']) # FIXME: '16x ^ 2 - 25'])
def test_2(self): def test_2(self):
pass pass
......
...@@ -4,7 +4,7 @@ from tests.rulestestcase import RulesTestCase as TestCase, rewrite ...@@ -4,7 +4,7 @@ from tests.rulestestcase import RulesTestCase as TestCase, rewrite
class TestLeidenOefenopgaveV12(TestCase): class TestLeidenOefenopgaveV12(TestCase):
def test_1_e(self): def test_1_e(self):
self.assertRewrite([ self.assertRewrite([
'-2(6x - 4) ^ 2 * x', '-2(6x - 4) ^ 2x',
'-2(6x - 4)(6x - 4)x', '-2(6x - 4)(6x - 4)x',
'(-2 * 6x - 2 * -4)(6x - 4)x', '(-2 * 6x - 2 * -4)(6x - 4)x',
'(-12x - 2 * -4)(6x - 4)x', '(-12x - 2 * -4)(6x - 4)x',
...@@ -12,23 +12,25 @@ class TestLeidenOefenopgaveV12(TestCase): ...@@ -12,23 +12,25 @@ class TestLeidenOefenopgaveV12(TestCase):
'(-12x + 8)(6x - 4)x', '(-12x + 8)(6x - 4)x',
'(-12x * 6x - 12x * -4 + 8 * 6x + 8 * -4)x', '(-12x * 6x - 12x * -4 + 8 * 6x + 8 * -4)x',
'(-72xx - 12x * -4 + 8 * 6x + 8 * -4)x', '(-72xx - 12x * -4 + 8 * 6x + 8 * -4)x',
'(-72 * x ^ (1 + 1) - 12x * -4 + 8 * 6x + 8 * -4)x', '(-72x ^ (1 + 1) - 12x * -4 + 8 * 6x + 8 * -4)x',
'(-72 * x ^ 2 - 12x * -4 + 8 * 6x + 8 * -4)x', '(-72x ^ 2 - 12x * -4 + 8 * 6x + 8 * -4)x',
'(-72 * x ^ 2 - -48x + 8 * 6x + 8 * -4)x', '(-72x ^ 2 - -48x + 8 * 6x + 8 * -4)x',
'(-72 * x ^ 2 + 48x + 8 * 6x + 8 * -4)x', '(-72x ^ 2 + 48x + 8 * 6x + 8 * -4)x',
'(-72 * x ^ 2 + 48x + 48x + 8 * -4)x', '(-72x ^ 2 + 48x + 48x + 8 * -4)x',
'(-72 * x ^ 2 + (1 + 1) * 48x + 8 * -4)x', '(-72x ^ 2 + (1 + 1) * 48x + 8 * -4)x',
'(-72 * x ^ 2 + 2 * 48x + 8 * -4)x', '(-72x ^ 2 + 2 * 48x + 8 * -4)x',
'(-72 * x ^ 2 + 96x + 8 * -4)x', '(-72x ^ 2 + 96x + 8 * -4)x',
'(-72 * x ^ 2 + 96x - 32)x', '(-72x ^ 2 + 96x - 32)x',
'x(-72 * x ^ 2 + 96x) + x * -32', 'x(-72x ^ 2 + 96x) + x * -32',
'x * -72 * x ^ 2 + x * 96x + x * -32', 'x * -72x ^ 2 + x * 96x + x * -32',
'-x * 72 * x ^ 2 + x * 96x + x * -32', '-x * 72x ^ 2 + x * 96x + x * -32',
'-x * 72 * x ^ 2 + x ^ (1 + 1) * 96 + x * -32', '72 * -xx ^ 2 + x * 96x + x * -32',
'-x * 72 * x ^ 2 + x ^ 2 * 96 + x * -32', '-72xx ^ 2 + x * 96x + x * -32',
'-x * 72 * x ^ 2 + x ^ 2 * 96 - x * 32']) '-72x ^ (1 + 2) + x * 96x + x * -32',
# FIXME: '-x ^ (1 + 2) * 72 + x ^ 2 * 96 - x * 32', '-72x ^ 3 + x * 96x + x * -32',
# FIXME: '-x ^ 3 * 72 + x ^ 2 * 96 - x * 32', '-72x ^ 3 + 96xx + x * -32',
# FIXME: '-72x ^ 3 + x ^ 2 * 96 - x * 32', '-72x ^ 3 + 96x ^ (1 + 1) + x * -32',
# FIXME: '-72x ^ 3 + 96x ^ 2 - x * 32', '-72x ^ 3 + 96x ^ 2 + x * -32',
# FIXME: '-72x ^ 3 + 96x ^ 2 - 32x']) '-72x ^ 3 + 96x ^ 2 - x * 32',
'-72x ^ 3 + 96x ^ 2 + 32 * -x',
'-72x ^ 3 + 96x ^ 2 - 32x'])
...@@ -4,6 +4,8 @@ import unittest ...@@ -4,6 +4,8 @@ import unittest
from src.parser import Parser from src.parser import Parser
from src.node import ExpressionNode as Node, ExpressionLeaf as Leaf from src.node import ExpressionNode as Node, ExpressionLeaf as Leaf
from tests.parser import ParserWrapper, run_expressions, line, graph from tests.parser import ParserWrapper, run_expressions, line, graph
from tests.rulestestcase import tree
from src.rules.goniometry import sin, cos
class TestParser(unittest.TestCase): class TestParser(unittest.TestCase):
...@@ -15,11 +17,11 @@ class TestParser(unittest.TestCase): ...@@ -15,11 +17,11 @@ class TestParser(unittest.TestCase):
run_expressions(Parser, [('a', Leaf('a'))]) run_expressions(Parser, [('a', Leaf('a'))])
def test_graph(self): def test_graph(self):
assert graph(Parser, '4a') == (""" self.assertEqual(graph(Parser, '4a'), ("""
* *
╭┴╮ ╭┴╮
4 a 4 a
""").replace('\n ', '\n')[1:-1] """).replace('\n ', '\n')[1:-1])
def test_line(self): def test_line(self):
self.assertEqual(line(Parser, '4-a'), '4 - a') self.assertEqual(line(Parser, '4-a'), '4 - a')
...@@ -35,3 +37,14 @@ class TestParser(unittest.TestCase): ...@@ -35,3 +37,14 @@ class TestParser(unittest.TestCase):
self.assertNotEqual(possibilities2, []) self.assertNotEqual(possibilities2, [])
self.assertNotEqual(possibilities1, possibilities2) self.assertNotEqual(possibilities1, possibilities2)
def test_functions(self):
root, x = tree('sin x, x')
self.assertEqual(root, sin(x))
self.assertEqual(tree('sin x ^ 2'), sin(x) ** 2)
self.assertEqual(tree('sin(x) ^ 2'), sin(x) ** 2)
self.assertEqual(tree('sin (x) ^ 2'), sin(x) ** 2)
self.assertEqual(tree('sin(x ^ 2)'), sin(x ** 2))
self.assertEqual(tree('sin cos x'), sin(cos(x)))
self.assertEqual(tree('sin cos x ^ 2'), sin(cos(x)) ** 2)
...@@ -6,7 +6,7 @@ from tests.rulestestcase import RulesTestCase, tree ...@@ -6,7 +6,7 @@ from tests.rulestestcase import RulesTestCase, tree
class TestRulesGoniometry(RulesTestCase): class TestRulesGoniometry(RulesTestCase):
def test_match_add_quadrants(self): def test_match_add_quadrants(self):
root = tree('sin(x) ^ 2 + cos(x) ^ 2') root = tree('sin x ^ 2 + cos x ^ 2')
possibilities = match_add_quadrants(root) possibilities = match_add_quadrants(root)
self.assertEqualPos(possibilities, [P(root, add_quadrants, ())]) self.assertEqualPos(possibilities, [P(root, add_quadrants, ())])
......
...@@ -47,6 +47,14 @@ class TestRulesPowers(RulesTestCase): ...@@ -47,6 +47,14 @@ class TestRulesPowers(RulesTestCase):
self.assertEqualPos(possibilities, self.assertEqualPos(possibilities,
[P(root, add_exponents, (Scope(root), n0, n1, a, p, q))]) [P(root, add_exponents, (Scope(root), n0, n1, a, p, q))])
def test_match_add_exponents_negated(self):
a, q = tree('a,q')
n0, n1 = root = (-a) * a ** q
possibilities = match_add_exponents(root)
self.assertEqualPos(possibilities,
[P(root, add_exponents, (Scope(root), n0, n1, a, 1, q))])
def test_match_subtract_exponents_powers(self): def test_match_subtract_exponents_powers(self):
a, p, q = tree('a,p,q') a, p, q = tree('a,p,q')
root = a ** p / a ** q root = a ** p / a ** q
......
from src.rules.sort import match_sort_multiplicants, move_constant
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
class TestRulesSort(RulesTestCase):
def test_match_sort_multiplicants(self):
x, l2 = root = tree('x * 2')
possibilities = match_sort_multiplicants(root)
self.assertEqualPos(possibilities,
[P(root, move_constant, (Scope(root), l2, x))])
def test_move_constant(self):
x, l2 = root = tree('x * 2')
self.assertEqualNodes(move_constant(root, (Scope(root), l2, x)),
l2 * x)
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