Commit efc3c91b authored by Taddeus Kroes's avatar Taddeus Kroes

Added non-strict equivalence match.

parent b47f8fee
...@@ -220,7 +220,9 @@ class ExpressionNode(Node, ExpressionBase): ...@@ -220,7 +220,9 @@ class ExpressionNode(Node, ExpressionBase):
return (self[1], self[0], ExpressionLeaf(1)) return (self[1], self[0], ExpressionLeaf(1))
def get_scope(self): def get_scope(self):
"""""" """
Find all n nodes within the n-ary scope of this operator.
"""
scope = [] scope = []
#op = OP_ADD | OP_SUB if self.op & (OP_ADD | OP_SUB) else self.op #op = OP_ADD | OP_SUB if self.op & (OP_ADD | OP_SUB) else self.op
...@@ -234,6 +236,41 @@ class ExpressionNode(Node, ExpressionBase): ...@@ -234,6 +236,41 @@ class ExpressionNode(Node, ExpressionBase):
return scope return scope
def equals(self, other):
"""
Perform a non-strict equivalence check between two nodes:
- If the other node is a leaf, it cannot be equal to this node.
- If their operators differ, the nodes are not equal.
- If both nodes are additions or both are multiplications, match each
node in one scope to one in the other (an injective relationship).
Any difference in order of the scopes is irrelevant.
- If both nodes are divisions, the nominator and denominator have to be
non-strictly equal.
"""
if not other.is_op(self.op):
return False
if self.op in (OP_ADD, OP_MUL):
s0 = self.get_scope()
s1 = set(other.get_scope())
matched = set()
for n0 in s0:
found = False
for n1 in s1 - matched:
if n0.equals(n1):
found = True
matched.add(n1)
break
if not found:
return False
elif self.op == OP_DIV:
return self[0].equals(other[0]) and self[1].equals(other[1])
return True
class ExpressionLeaf(Leaf, ExpressionBase): class ExpressionLeaf(Leaf, ExpressionBase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
...@@ -242,6 +279,9 @@ class ExpressionLeaf(Leaf, ExpressionBase): ...@@ -242,6 +279,9 @@ class ExpressionLeaf(Leaf, ExpressionBase):
self.type = TYPE_MAP[type(args[0])] self.type = TYPE_MAP[type(args[0])]
def __eq__(self, other): def __eq__(self, other):
"""
Check strict equivalence.
"""
other_type = type(other) other_type = type(other)
if other_type in TYPE_MAP: if other_type in TYPE_MAP:
...@@ -249,6 +289,13 @@ class ExpressionLeaf(Leaf, ExpressionBase): ...@@ -249,6 +289,13 @@ class ExpressionLeaf(Leaf, ExpressionBase):
return other.type == self.type and self.value == other.value return other.type == self.type and self.value == other.value
def equals(self, other):
"""
Check non-strict equivalence.
Between leaves, this is the same as strict equivalence.
"""
return self == other
def extract_polynome_properties(self): def extract_polynome_properties(self):
""" """
An expression leaf will return the polynome tuple (1, r, 1), where r is An expression leaf will return the polynome tuple (1, r, 1), where r is
......
import unittest import unittest
from src.node import ExpressionNode from src.node import ExpressionNode
from src.parser import Parser
from tests.parser import ParserWrapper
def tree(exp, **kwargs):
return ParserWrapper(Parser, **kwargs).run([exp])
class RulesTestCase(unittest.TestCase): class RulesTestCase(unittest.TestCase):
......
import unittest import unittest
from src.node import ExpressionNode as N, ExpressionLeaf as L from src.node import ExpressionNode as N, ExpressionLeaf as L
from tests.rulestestcase import tree
class TestNode(unittest.TestCase): class TestNode(unittest.TestCase):
...@@ -88,3 +89,48 @@ class TestNode(unittest.TestCase): ...@@ -88,3 +89,48 @@ class TestNode(unittest.TestCase):
def test_get_scope_nested_deep(self): def test_get_scope_nested_deep(self):
plus = N('+', N('+', N('+', *self.l[:2]), self.l[2]), self.l[3]) plus = N('+', N('+', N('+', *self.l[:2]), self.l[2]), self.l[3])
self.assertEqual(plus.get_scope(), self.l) self.assertEqual(plus.get_scope(), self.l)
def test_equals_node_leaf(self):
a, b = plus = tree('a + b')
self.assertFalse(a.equals(plus))
self.assertFalse(plus.equals(a))
def test_equals_other_op(self):
plus, mul = tree('a + b, a * b')
self.assertFalse(plus.equals(mul))
def test_equals_add(self):
p0, p1, p2, p3 = tree('a + b,a + b,b + a, a + c')
self.assertTrue(p0.equals(p1))
self.assertTrue(p0.equals(p2))
self.assertFalse(p0.equals(p3))
self.assertFalse(p2.equals(p3))
def test_equals_mul(self):
m0, m1, m2, m3 = tree('a * b,a * b,b * a, a * c')
self.assertTrue(m0.equals(m1))
self.assertTrue(m0.equals(m2))
self.assertFalse(m0.equals(m3))
self.assertFalse(m2.equals(m3))
def test_equals_nary(self):
p0, p1, p2, p3, p4 = \
tree('a + b + c,a + c + b,b + a + c,b + c + a, a + b + d')
self.assertTrue(p0.equals(p1))
self.assertTrue(p0.equals(p2))
self.assertTrue(p0.equals(p3))
self.assertTrue(p1.equals(p2))
self.assertTrue(p1.equals(p3))
self.assertTrue(p2.equals(p3))
self.assertFalse(p2.equals(p4))
def test_equals_div(self):
d0, d1, d2 = tree('a / b,a / b,b / a')
self.assertTrue(d0.equals(d1))
self.assertFalse(d0.equals(d2))
...@@ -2,7 +2,7 @@ import unittest ...@@ -2,7 +2,7 @@ import unittest
from src.possibilities import MESSAGES, Possibility as P, filter_duplicates from src.possibilities import MESSAGES, Possibility as P, filter_duplicates
from src.rules.numerics import add_numerics from src.rules.numerics import add_numerics
from tests.test_rules_poly import tree from tests.rulestestcase import tree
from src.parser import Parser from src.parser import Parser
from tests.parser import ParserWrapper from tests.parser import ParserWrapper
......
from src.rules.factors import match_expand, expand_single, expand_double from src.rules.factors import match_expand, expand_single, expand_double
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase from tests.rulestestcase import RulesTestCase, tree
from tests.test_rules_poly import tree
class TestRulesFactors(RulesTestCase): class TestRulesFactors(RulesTestCase):
......
...@@ -2,8 +2,7 @@ from src.rules.fractions import match_constant_division, division_by_one, \ ...@@ -2,8 +2,7 @@ from src.rules.fractions import match_constant_division, division_by_one, \
division_of_zero, division_by_self, match_add_constant_fractions, \ division_of_zero, division_by_self, match_add_constant_fractions, \
equalize_denominators, add_nominators equalize_denominators, add_nominators
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from tests.test_rules_poly import tree from tests.rulestestcase import RulesTestCase, tree
from tests.rulestestcase import RulesTestCase
class TestRulesFractions(RulesTestCase): class TestRulesFractions(RulesTestCase):
......
from src.rules.groups import match_combine_groups
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
class TestRulesGroups(RulesTestCase):
def test_(self):
pass
...@@ -2,8 +2,7 @@ from src.rules.numerics import add_numerics, match_divide_numerics, \ ...@@ -2,8 +2,7 @@ from src.rules.numerics import add_numerics, match_divide_numerics, \
divide_numerics, match_multiply_numerics, multiply_numerics divide_numerics, match_multiply_numerics, multiply_numerics
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from src.node import ExpressionLeaf as L from src.node import ExpressionLeaf as L
from tests.rulestestcase import RulesTestCase from tests.rulestestcase import RulesTestCase, tree
from tests.test_rules_poly import tree
class TestRulesNumerics(RulesTestCase): class TestRulesNumerics(RulesTestCase):
......
from src.rules.poly import match_combine_polynomes, combine_polynomes from src.rules.poly import match_combine_polynomes, combine_polynomes
from src.rules.numerics import add_numerics from src.rules.numerics import add_numerics
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from src.parser import Parser from tests.rulestestcase import RulesTestCase, tree
from tests.parser import ParserWrapper
from tests.rulestestcase import RulesTestCase
def tree(exp, **kwargs):
return ParserWrapper(Parser, **kwargs).run([exp])
class TestRulesPoly(RulesTestCase): class TestRulesPoly(RulesTestCase):
......
...@@ -6,8 +6,7 @@ from src.rules.powers import match_add_exponents, add_exponents, \ ...@@ -6,8 +6,7 @@ from src.rules.powers import match_add_exponents, add_exponents, \
match_exponent_to_root, exponent_to_root match_exponent_to_root, exponent_to_root
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from src.node import ExpressionNode as N from src.node import ExpressionNode as N
from tests.test_rules_poly import tree from tests.rulestestcase import RulesTestCase, tree
from tests.rulestestcase import RulesTestCase
class TestRulesPowers(RulesTestCase): class TestRulesPowers(RulesTestCase):
......
from src.node import ExpressionNode as N from src.node import ExpressionNode as N
from src.rules.utils import nary_node, is_prime, least_common_multiple from src.rules.utils import nary_node, is_prime, least_common_multiple
from tests.test_rules_poly import tree from tests.rulestestcase import RulesTestCase, tree
from tests.rulestestcase import RulesTestCase
class TestRulesUtils(RulesTestCase): class TestRulesUtils(RulesTestCase):
......
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