Commit e86e7b9c authored by Sander Mathijs van Veen's avatar Sander Mathijs van Veen

Fixed merge conflict.

parents dae7dd45 c5b2a5c6
# vim: set fileencoding=utf-8 : # vim: set fileencoding=utf-8 :
import os.path import os.path
import sys import sys
import copy
sys.path.insert(0, os.path.realpath('external')) sys.path.insert(0, os.path.realpath('external'))
...@@ -59,6 +60,9 @@ def to_expression(obj): ...@@ -59,6 +60,9 @@ def to_expression(obj):
class ExpressionBase(object): class ExpressionBase(object):
def clone(self):
return copy.deepcopy(self)
def __lt__(self, other): def __lt__(self, other):
""" """
Comparison between this expression{node,leaf} and another Comparison between this expression{node,leaf} and another
...@@ -161,6 +165,9 @@ class ExpressionNode(Node, ExpressionBase): ...@@ -161,6 +165,9 @@ class ExpressionNode(Node, ExpressionBase):
return False return False
def substitute(self, old_child, new_child):
self.nodes[self.nodes.index(old_child)] = new_child
def graph(self): # pragma: nocover def graph(self): # pragma: nocover
return generate_graph(self) return generate_graph(self)
...@@ -182,6 +189,9 @@ class ExpressionNode(Node, ExpressionBase): ...@@ -182,6 +189,9 @@ class ExpressionNode(Node, ExpressionBase):
>>> n2 = N('*', N('^', r, e), c) >>> n2 = N('*', N('^', r, e), c)
>>> n2.extract_polynome() >>> n2.extract_polynome()
(c, r, e) (c, r, e)
>>> n3 = N('-', r)
>>> n3.extract_polynome()
(1, -r, 1)
""" """
# TODO: change "get_polynome" -> "extract_polynome". # TODO: change "get_polynome" -> "extract_polynome".
# TODO: change retval of c * r ^ e to (c, r, e). # TODO: change retval of c * r ^ e to (c, r, e).
...@@ -191,6 +201,10 @@ class ExpressionNode(Node, ExpressionBase): ...@@ -191,6 +201,10 @@ class ExpressionNode(Node, ExpressionBase):
if self.is_power(): if self.is_power():
return (ExpressionLeaf(1), self[0], self[1]) return (ExpressionLeaf(1), self[0], self[1])
# rule: -r -> (1, r, 1)
if self.is_op(OP_NEG):
return (ExpressionLeaf(1), -self[0], ExpressionLeaf(1))
if self.op != OP_MUL: if self.op != OP_MUL:
return return
......
...@@ -79,6 +79,9 @@ class Parser(BisonParser): ...@@ -79,6 +79,9 @@ class Parser(BisonParser):
self.read_buffer = '' self.read_buffer = ''
self.read_queue = Queue.Queue() self.read_queue = Queue.Queue()
self.subtree_map = {}
self.root_node = None
# Override default read method with a version that prompts for input. # Override default read method with a version that prompts for input.
def read(self, nbytes): def read(self, nbytes):
if self.file == sys.stdin and self.file.closed: if self.file == sys.stdin and self.file.closed:
...@@ -181,11 +184,43 @@ class Parser(BisonParser): ...@@ -181,11 +184,43 @@ class Parser(BisonParser):
or retval.type != TYPE_OPERATOR or retval.op not in RULES: or retval.type != TYPE_OPERATOR or retval.op not in RULES:
return retval return retval
# Update the subtree map to let the subtree point to its parent node.
parent_nodes = self.subtree_map.keys()
for child in retval:
if child in parent_nodes:
self.subtree_map[child] = retval
for handler in RULES[retval.op]: for handler in RULES[retval.op]:
self.possibilities.extend(handler(retval)) possibilities = handler(retval)
# Record the subtree root node in order to avoid tree traversal.
# At this moment, the node is the root node since the expression is
# parser using the left-innermost parsing strategy.
for p in possibilities:
self.subtree_map[p.root] = None
self.possibilities.extend(possibilities)
return retval return retval
def display_hint(self):
print pick_suggestion(self.last_possibilities)
def display_possibilities(self):
print '\n'.join(map(str, self.last_possibilities))
def rewrite(self):
suggestion = pick_suggestion(self.last_possibilities)
if not suggestion:
return self.root_node
expression = apply_suggestion(self.root_node, self.subtree_map,
suggestion)
self.read_queue.put_nowait(str(expression))
return expression
#def hook_run(self, filename, retval): #def hook_run(self, filename, retval):
# return retval # return retval
...@@ -226,22 +261,23 @@ class Parser(BisonParser): ...@@ -226,22 +261,23 @@ class Parser(BisonParser):
| REWRITE NEWLINE | REWRITE NEWLINE
| RAISE NEWLINE | RAISE NEWLINE
""" """
if option in [1, 2]: # rule: EXP NEWLINE | DEBUG NEWLINE if option == 1: # rule: EXP NEWLINE
self.root_node = values[0]
return values[0]
if option == 2: # rule: DEBUG NEWLINE
return values[0] return values[0]
if option == 3: # rule: HINT NEWLINE if option == 3: # rule: HINT NEWLINE
print pick_suggestion(self.last_possibilities) self.display_hint()
return return
if option == 4: # rule: POSSIBILITIES NEWLINE if option == 4: # rule: POSSIBILITIES NEWLINE
print '\n'.join(map(str, self.last_possibilities)) self.display_possibilities()
return return
if option == 5: # rule: REWRITE NEWLINE if option == 5: # rule: REWRITE NEWLINE
suggestion = pick_suggestion(self.last_possibilities) return self.rewrite()
expression = apply_suggestion(suggestion)
self.read_queue.put_nowait(str(expression))
return expression
if option == 6: if option == 6:
raise RuntimeError('on_line: exception raised') raise RuntimeError('on_line: exception raised')
......
...@@ -15,8 +15,7 @@ class Possibility(object): ...@@ -15,8 +15,7 @@ class Possibility(object):
if self.handler in MESSAGES: if self.handler in MESSAGES:
return MESSAGES[self.handler].format(self.root, *self.args) return MESSAGES[self.handler].format(self.root, *self.args)
return '<Possibility root="%s" handler=%s args=%s>' \ return self.__repr__()
% (self.root, self.handler.func_name, self.args)
def __repr__(self): def __repr__(self):
return '<Possibility root="%s" handler=%s args=%s>' \ return '<Possibility root="%s" handler=%s args=%s>' \
...@@ -32,11 +31,11 @@ def filter_duplicates(possibilities): ...@@ -32,11 +31,11 @@ def filter_duplicates(possibilities):
""" """
Filter duplicated possibilities. Duplicated possibilities occur in n-ary Filter duplicated possibilities. Duplicated possibilities occur in n-ary
nodes, the root-level node and a lower-level node will both recognize a nodes, the root-level node and a lower-level node will both recognize a
rewrite possibility within their sscope, whereas only the root-level one rewrite possibility within their scope, whereas only the root-level one
matters. matters.
Example: 1 + 2 + 3 Example: 1 + 2 + 3
The addition of 1 and 2 is recognized bij n-ary additions "1 + 2" and The addition of 1 and 2 is recognized by n-ary additions "1 + 2" and
"1 + 2 + 3". The "1 + 2" addition should be removed by this function. "1 + 2 + 3". The "1 + 2" addition should be removed by this function.
""" """
features = [] features = []
...@@ -53,10 +52,37 @@ def filter_duplicates(possibilities): ...@@ -53,10 +52,37 @@ def filter_duplicates(possibilities):
def pick_suggestion(possibilities): def pick_suggestion(possibilities):
if not possibilities:
return
# TODO: pick the best suggestion. # TODO: pick the best suggestion.
suggestion = 0 suggestion = 0
return possibilities[suggestion] return possibilities[suggestion]
def apply_suggestion(suggestion): def apply_suggestion(root, subtree_map, suggestion):
return suggestion.handler(suggestion.root, suggestion.args) # clone the root node before modifying. After deep copying the root node,
# the subtree_map cannot be used since the hash() of each node in the deep
# copied root node has changed.
#root_clone = root.clone()
subtree = suggestion.handler(suggestion.root, suggestion.args)
if suggestion.root in subtree_map:
parent_node = subtree_map[suggestion.root]
else:
parent_node = None
# There is either a parent node or the subtree is the root node.
# FIXME: FAIL: test_diagnostic_test_application in tests/test_b1_ch08.py
#try:
# assert bool(parent_node) != (subtree == root)
#except:
# print 'parent_node: %s' % (str(parent_node))
# print 'subtree: %s == %s' % (str(subtree), str(root))
# raise
if parent_node:
parent_node.substitute(suggestion.root, subtree)
return root
return subtree
...@@ -13,8 +13,6 @@ def match_expand(node): ...@@ -13,8 +13,6 @@ def match_expand(node):
""" """
assert node.is_op(OP_MUL) assert node.is_op(OP_MUL)
scope = node.get_scope()
p = [] p = []
leaves = [] leaves = []
additions = [] additions = []
......
from itertools import combinations from itertools import combinations
from .utils import nary_node, least_common_multiple from .utils import nary_node, least_common_multiple
from ..node import ExpressionLeaf as L, OP_DIV, OP_ADD, OP_MUL from ..node import ExpressionLeaf as L, OP_DIV, OP_ADD, OP_MUL, OP_NEG
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -63,15 +63,23 @@ def match_add_constant_fractions(node): ...@@ -63,15 +63,23 @@ def match_add_constant_fractions(node):
1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4 # Equalize denominators 1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4 # Equalize denominators
2 / 4 + 3 / 4 -> 5 / 4 # Equal denominators, so nominators can 2 / 4 + 3 / 4 -> 5 / 4 # Equal denominators, so nominators can
# be added # be added
2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
2 / 4 - 3 / 4 -> -1 / 4 # Equal denominators, so nominators can
# be subtracted
""" """
assert node.is_op(OP_ADD) assert node.is_op(OP_ADD)
p = [] p = []
fractions = filter(lambda n: n.is_op(OP_DIV), node.get_scope())
def is_division(node):
return node.is_op(OP_DIV) or \
(node.is_op(OP_NEG) and node[0].is_op(OP_DIV))
fractions = filter(is_division, node.get_scope())
for a, b in combinations(fractions, 2): for a, b in combinations(fractions, 2):
na, da = a na, da = a if a.is_op(OP_DIV) else a[0]
nb, db = b nb, db = b if b.is_op(OP_DIV) else b[0]
if da == db: if da == db:
# Equal denominators, add nominators to create a single fraction # Equal denominators, add nominators to create a single fraction
...@@ -96,28 +104,40 @@ def equalize_denominators(root, args): ...@@ -96,28 +104,40 @@ def equalize_denominators(root, args):
scope = root.get_scope() scope = root.get_scope()
for fraction in args[:2]: for fraction in args[:2]:
n, d = fraction n, d = fraction[0] if fraction.is_op(OP_NEG) else fraction
mult = denom / d.value mult = denom / d.value
if mult != 1: if mult != 1:
n = L(n.value * mult) if n.is_numeric() else L(mult) * n n = L(n.value * mult) if n.is_numeric() else L(mult) * n
scope[scope.index(fraction)] = n / L(d.value * mult)
if fraction.is_op(OP_NEG):
scope[scope.index(fraction)] = -(n / L(d.value * mult))
else:
scope[scope.index(fraction)] = n / L(d.value * mult)
return nary_node('+', scope) return nary_node('+', scope)
def add_nominators(root, args): def add_nominators(root, args):
""" """
a / b + c / b -> (a + c) / b a / b + c / b -> (a + c) / b
a / b + (-c / b) -> (a + (-c)) / b
""" """
# TODO: is 'add' Appropriate when rewriting to "(a + (-c)) / b"?
ab, cb = args ab, cb = args
a, b = ab a, b = ab
c = cb[0]
if cb[0].is_op(OP_NEG):
c = cb[0][0]
substitution = (a + (-c)) / b
else:
c = cb[0]
substitution = (a + c) / b
scope = root.get_scope() scope = root.get_scope()
# Replace the left node with the new expression # Replace the left node with the new expression
scope[scope.index(ab)] = (a + c) / b scope[scope.index(ab)] = substitution
# Remove the right node # Remove the right node
scope.remove(cb) scope.remove(cb)
...@@ -127,8 +147,10 @@ def add_nominators(root, args): ...@@ -127,8 +147,10 @@ def add_nominators(root, args):
def match_expand_and_add_fractions(node): def match_expand_and_add_fractions(node):
""" """
a * b / c + d * b / c -> (a + d) * (b / c) a * b / c + d * b / c -> (a + d) * (b / c)
a * b / c + (- d * b / c) -> (a + (-d)) * (b / c)
""" """
# TODO: is 'add' Appropriate when rewriting to "(a + (-d)) / * (b / c)"?
assert node.is_op(OP_MUL) assert node.is_op(OP_MUL)
p = [] p = []
......
...@@ -18,6 +18,7 @@ def match_combine_groups(node): ...@@ -18,6 +18,7 @@ def match_combine_groups(node):
ab + 2ab -> 3ab ab + 2ab -> 3ab
ab + ba -> 2ab ab + ba -> 2ab
""" """
# TODO: handle OP_NEG nodes
assert node.is_op(OP_ADD) assert node.is_op(OP_ADD)
p = [] p = []
......
from itertools import combinations from itertools import combinations
from .utils import nary_node from .utils import nary_node
from ..node import ExpressionLeaf as Leaf, OP_DIV, OP_MUL from ..node import ExpressionLeaf as Leaf, OP_DIV, OP_MUL, OP_NEG
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -11,10 +11,16 @@ def add_numerics(root, args): ...@@ -11,10 +11,16 @@ def add_numerics(root, args):
Combine two constants to a single constant in an n-ary addition. Combine two constants to a single constant in an n-ary addition.
Example: Example:
2 + 3 -> 5 2 + 3 -> 5
2 + -3 -> -1
-2 + 3 -> 1
-2 + -3 -> -5
""" """
n0, n1, c0, c1 = args n0, n1, c0, c1 = args
c0 = (-c0[0].value) if c0.is_op(OP_NEG) else c0.value
c1 = (-c1[0].value) if c1.is_op(OP_NEG) else c1.value
scope = root.get_scope() scope = root.get_scope()
# Replace the left node with the new expression # Replace the left node with the new expression
...@@ -26,8 +32,8 @@ def add_numerics(root, args): ...@@ -26,8 +32,8 @@ def add_numerics(root, args):
return nary_node('+', scope) return nary_node('+', scope)
MESSAGES[add_numerics] = _('Combine the constants {3} and {4}, which' MESSAGES[add_numerics] = _('Combine the constants {1} and {2}, which'
' will reduce to {3} + {4}.') ' will reduce to {1} + {2}.')
#def match_subtract_numerics(node): #def match_subtract_numerics(node):
......
from itertools import combinations from itertools import combinations
from ..node import OP_ADD from ..node import OP_ADD, OP_NEG
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from .utils import nary_node from .utils import nary_node
from .numerics import add_numerics from .numerics import add_numerics
def is_numeric_or_negated_numeric(n):
return n.is_numeric() or (n.is_op(OP_NEG) and n[0].is_numeric())
def match_combine_polynomes(node, verbose=False): def match_combine_polynomes(node, verbose=False):
""" """
n + exp + m -> exp + (n + m) n + exp + m -> exp + (n + m)
...@@ -49,9 +53,12 @@ def match_combine_polynomes(node, verbose=False): ...@@ -49,9 +53,12 @@ def match_combine_polynomes(node, verbose=False):
# roots, or: same root and exponent -> combine coefficients. # roots, or: same root and exponent -> combine coefficients.
# TODO: Addition with zero, e.g. a + 0 -> a # TODO: Addition with zero, e.g. a + 0 -> a
if c0 == 1 and c1 == 1 and e0 == 1 and e1 == 1 \ if c0 == 1 and c1 == 1 and e0 == 1 and e1 == 1 \
and r0.is_numeric() and r1.is_numeric(): and all(map(is_numeric_or_negated_numeric, [r0, r1])):
# 2 + 3 -> 5 # 2 + 3 -> 5
p.append(P(node, add_numerics, (n0, n1, r0.value, r1.value))) # 2 + -3 -> -1
# -2 + 3 -> 1
# -2 + -3 -> -5
p.append(P(node, add_numerics, (n0, n1, r0, r1)))
elif c0.is_numeric() and c1.is_numeric() and r0 == r1 and e0 == e1: elif c0.is_numeric() and c1.is_numeric() and r0 == r1 and e0 == e1:
# 2a + 2a -> 4a # 2a + 2a -> 4a
# a + 2a -> 3a # a + 2a -> 3a
......
...@@ -120,6 +120,8 @@ def apply_expressions(base_class, expressions, fail=True, silent=False, ...@@ -120,6 +120,8 @@ def apply_expressions(base_class, expressions, fail=True, silent=False,
if fail: if fail:
raise raise
def graph(parser, *exp, **kwargs): def graph(parser, *exp, **kwargs):
return generate_graph(ParserWrapper(parser, **kwargs).run(exp)) return generate_graph(ParserWrapper(parser, **kwargs).run(exp))
......
...@@ -4,8 +4,8 @@ from src.parser import Parser ...@@ -4,8 +4,8 @@ from src.parser import Parser
from tests.parser import ParserWrapper from tests.parser import ParserWrapper
def reduce(exp, **kwargs): def rewrite(exp, **kwargs):
return ParserWrapper(Parser, **kwargs).run([exp]).reduce() return ParserWrapper(Parser, **kwargs).run([exp, '@'])
class TestLeidenOefenopgave(TestCase): class TestLeidenOefenopgave(TestCase):
...@@ -19,7 +19,7 @@ class TestLeidenOefenopgave(TestCase): ...@@ -19,7 +19,7 @@ class TestLeidenOefenopgave(TestCase):
('-2(6x-4)^2*x', '-72 * x^3 + 96 * x ^ 2 + 32 * x'), ('-2(6x-4)^2*x', '-72 * x^3 + 96 * x ^ 2 + 32 * x'),
('(4x + 5) * -(5 - 4x)', '16x^2 - 25'), ('(4x + 5) * -(5 - 4x)', '16x^2 - 25'),
]: ]:
self.assertEqual(str(reduce(exp)), solution) self.assertEqual(str(rewrite(exp)), solution)
def test_2(self): def test_2(self):
pass pass
...@@ -28,14 +28,17 @@ class TestLeidenOefenopgave(TestCase): ...@@ -28,14 +28,17 @@ class TestLeidenOefenopgave(TestCase):
pass pass
def test_4(self): def test_4(self):
return
for exp, solution in [ for exp, solution in [
('2/15 + 1/4', '23/60'), ('2/15 + 1/4', '8 / 60 + 15 / 60'),
('2/7 - 4/11', '-6/77'), ('8/60 + 15/60', '(8 + 15) / 60'),
('(7/3) * (3/5)', '7/5'), ('(8 + 15) / 60', '23 / 60'),
('(3/4) / (5/6)', '9/10'), ('2/7 - 4/11', '22 / 77 + -28 / 77'),
('1/4 * 1/x', '1/(4x)'), ('22/77 + -28/77', '(22 + -28) / 77'),
('(3/x^2) / (x/7)', '21/x^3'), ('(22 + -28)/77', '-6 / 77'),
('1/x + 2/(x+1)', '(3x + 1) / (x * (x + 1))'), # FIXME: ('(7/3) * (3/5)', '7 / 5'),
# FIXME: ('(3/4) / (5/6)', '9 / 10'),
# FIXME: ('1/4 * 1/x', '1 / (4x)'),
# FIXME: ('(3/x^2) / (x/7)', '21 / x^3'),
# FIXME: ('1/x + 2/(x+1)', '(3x + 1) / (x * (x + 1))'),
]: ]:
self.assertEqual(str(reduce(exp)), solution) self.assertEqual(str(rewrite(exp)), solution)
from unittest import TestCase
from src.parser import Parser
from tests.parser import ParserWrapper
def rewrite(exp, **kwargs):
return ParserWrapper(Parser, **kwargs).run([exp, '@'])
class TestRewrite(TestCase):
def assertRewrite(self, rewrite_chain):
try:
for i, exp in enumerate(rewrite_chain[:-1]):
self.assertEqual(str(rewrite(exp)), str(rewrite_chain[i+1]))
except AssertionError: # pragma: nocover
print 'rewrite failed:', exp, '->', rewrite_chain[i+1]
print 'rewrite chain:', rewrite_chain
raise
def test_addition_rewrite(self):
self.assertRewrite(['2 + 3 + 4', '5 + 4', '9'])
def test_addition_identifiers_rewrite(self):
self.assertRewrite(['2 + 3a + 4', '6 + 3a'])
def test_division_rewrite(self):
self.assertRewrite(['2/7 - 4/11', '22 / 77 + -28 / 77',
'(22 + -28) / 77', '-6 / 77'])
...@@ -68,6 +68,19 @@ class TestRulesFractions(RulesTestCase): ...@@ -68,6 +68,19 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualPos(possibilities, self.assertEqualPos(possibilities,
[P(root, add_nominators, (n1, n3))]) [P(root, add_nominators, (n1, n3))])
def test_add_constant_fractions_with_negation(self):
a, b, c, l1, l2, l3, l4 = tree('a,b,c,1,2,3,4')
(((n0, n1), n2), n3), n4 = root = a + l2 / l2 + b + (-l3 / l4) + c
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, equalize_denominators, (n1, n3, 4))])
(((n0, n1), n2), n3), n4 = root = a + l2 / l4 + b + (-l3 / l4) + c
possibilities = match_add_constant_fractions(root)
self.assertEqualPos(possibilities,
[P(root, add_nominators, (n1, n3))])
def test_equalize_denominators(self): def test_equalize_denominators(self):
a, b, l1, l2, l3, l4 = tree('a,b,1,2,3,4') a, b, l1, l2, l3, l4 = tree('a,b,1,2,3,4')
...@@ -79,8 +92,22 @@ class TestRulesFractions(RulesTestCase): ...@@ -79,8 +92,22 @@ class TestRulesFractions(RulesTestCase):
self.assertEqualNodes(equalize_denominators(root, (n0, n1, 4)), self.assertEqualNodes(equalize_denominators(root, (n0, n1, 4)),
(l2 * a) / l4 + b / l4) (l2 * a) / l4 + b / l4)
#2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
n0, n1 = root = l1 / l2 + (-l3 / l4)
self.assertEqualNodes(equalize_denominators(root, (n0, n1, 4)),
l2 / l4 + (-l3 / l4))
#2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
n0, n1 = root = a / l2 + (-b / l4)
self.assertEqualNodes(equalize_denominators(root, (n0, n1, 4)),
(l2 * a) / l4 + (-b / l4))
def test_add_nominators(self): def test_add_nominators(self):
a, b, c = tree('a,b,c') a, b, c = tree('a,b,c')
n0, n1 = root = a / b + c / b n0, n1 = root = a / b + c / b
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + c) / b) self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + c) / b)
#2 / 4 + 3 / -4 -> 2 / 4 + -3 / 4
#2 / 4 - 3 / 4 -> -1 / 4 # Equal denominators, so nominators can
n0, n1 = root = a / b + (-c / b)
self.assertEqualNodes(add_nominators(root, (n0, n1)), (a + (-c)) / b)
...@@ -10,8 +10,16 @@ class TestRulesNumerics(RulesTestCase): ...@@ -10,8 +10,16 @@ class TestRulesNumerics(RulesTestCase):
def test_add_numerics(self): def test_add_numerics(self):
l0, a, l1 = tree('1,a,2') l0, a, l1 = tree('1,a,2')
self.assertEqual(add_numerics(l0 + l1, (l0, l1, 1, 2)), 3) self.assertEqual(add_numerics(l0 + l1, (l0, l1, L(1), L(2))), 3)
self.assertEqual(add_numerics(l0 + a + l1, (l0, l1, 1, 2)), L(3) + a) self.assertEqual(add_numerics(l0 + a + l1, (l0, l1, L(1), L(2))),
L(3) + a)
def test_add_numerics_negations(self):
l0, a, l1 = tree('1,a,2')
self.assertEqual(add_numerics(l0 + -l1, (l0, -l1, L(1), -L(2))), -1)
self.assertEqual(add_numerics(l0 + a + -l1, (l0, -l1, L(1), -L(2))),
L(-1) + a)
def test_match_divide_numerics(self): def test_match_divide_numerics(self):
a, b, i2, i3, i6, f1, f2, f3 = tree('a,b,2,3,6,1.0,2.0,3.0') a, b, i2, i3, i6, f1, f2, f3 = tree('a,b,2,3,6,1.0,2.0,3.0')
......
...@@ -36,14 +36,12 @@ class TestRulesPoly(RulesTestCase): ...@@ -36,14 +36,12 @@ class TestRulesPoly(RulesTestCase):
self.assertEqualPos(possibilities, self.assertEqualPos(possibilities,
[P(root, combine_polynomes, (a1, a2, 2, 1, 'a', 3))]) [P(root, combine_polynomes, (a1, a2, 2, 1, 'a', 3))])
def test_identifiers_coeff_exponent_both(self): def test_identifiers_coeff_exponent_both(self):
a1, a2 = root = tree('2a3+2a3') a1, a2 = root = tree('2a3+2a3')
possibilities = match_combine_polynomes(root) possibilities = match_combine_polynomes(root)
self.assertEqualPos(possibilities, self.assertEqualPos(possibilities,
[P(root, combine_polynomes, (a1, a2, 2, 2, 'a', 3))]) [P(root, combine_polynomes, (a1, a2, 2, 2, 'a', 3))])
def test_basic_subexpressions(self): def test_basic_subexpressions(self):
a_b, c, d = tree('a+b,c,d') a_b, c, d = tree('a+b,c,d')
left, right = root = tree('(a+b)^d + (a+b)^d') left, right = root = tree('(a+b)^d + (a+b)^d')
......
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