| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- from itertools import combinations
- from ..node import ExpressionLeaf as Leaf, Scope, negate, OP_ADD, OP_DIV, \
- OP_MUL
- from ..possibilities import Possibility as P, MESSAGES
- from ..translate import _
- def match_add_numerics(node):
- """
- Combine two constants to a single constant in an n-ary addition.
- Example:
- 2 + 3 -> 5
- 2 + -3 -> -1
- -2 + 3 -> 1
- -2 + -3 -> -5
- """
- assert node.is_op(OP_ADD)
- p = []
- scope = Scope(node)
- numerics = filter(lambda n: n.is_numeric(), scope)
- for c0, c1 in combinations(numerics, 2):
- p.append(P(node, add_numerics, (scope, c0, c1)))
- return p
- def add_numerics(root, args):
- """
- 2 + 3 -> 5
- 2 + -3 -> -1
- -2 + 3 -> 1
- -2 + -3 -> -5
- """
- scope, c0, c1 = args
- 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
- scope.replace(c0, Leaf(abs(value)).negate(int(value < 0)))
- # Remove the right node
- scope.remove(c1)
- return scope.as_nary_node()
- MESSAGES[add_numerics] = _('Add the constants {2} and {3}.')
- #def match_subtract_numerics(node):
- # """
- # 3 - 2 -> 2.0
- # 3.0 - 2 -> 1.0
- # 3 - 2.0 -> 1.0
- # 3.0 - 2.0 -> 1.0
- # """
- # # TODO: This should be handled by match_combine_polynomes
- # assert node.is_op(OP_MUL)
- def match_divide_numerics(node):
- """
- Combine two constants to a single constant in a division, if it does not
- lead to a decrease in precision.
- Example:
- 6 / 2 -> 3
- 3 / 2 -> 3 / 2 # 1.5 would mean a decrease in precision
- 3.0 / 2 -> 1.5
- 3 / 2.0 -> 1.5
- 3.0 / 2.0 -> 1.5
- 3 / 1.0 -> 3 # Exceptional case: division of integer by 1.0 keeps
- # integer precision
- """
- assert node.is_op(OP_DIV)
- n, d = node
- divide = False
- dv = d.value
- if n.is_int() and d.is_int():
- # 6 / 2 -> 3
- # 3 / 2 -> 3 / 2
- divide = not divmod(n.value, dv)[1]
- elif n.is_numeric() and d.is_numeric():
- if d == 1.0:
- # 3 / 1.0 -> 3
- dv = 1
- # 3.0 / 2 -> 1.5
- # 3 / 2.0 -> 1.5
- # 3.0 / 2.0 -> 1.5
- divide = True
- return [P(node, divide_numerics, (n.value, dv))] if divide else []
- def divide_numerics(root, args):
- """
- Combine two constants to a single constant in a division.
- Examples:
- 6 / 2 -> 3
- 3.0 / 2 -> 1.5
- 3 / 2.0 -> 1.5
- 3.0 / 2.0 -> 1.5
- 3 / 1.0 -> 3
- """
- n, d = args
- return Leaf(n / d)
- MESSAGES[divide_numerics] = _('Divide constant {1} by constant {2}.')
- def match_multiply_zero(node):
- """
- a * 0 -> 0
- 0 * a -> 0
- -0 * a -> -0
- 0 * -a -> -0
- -0 * -a -> 0
- """
- assert node.is_op(OP_MUL)
- left, right = node
- if (left.is_leaf and left.value == 0) \
- or (right.is_leaf and right.value == 0):
- return [P(node, multiply_zero, (left.negated + right.negated,))]
- return []
- def multiply_zero(root, args):
- """
- a * 0 -> 0
- 0 * a -> 0
- -0 * a -> -0
- 0 * -a -> -0
- -0 * -a -> 0
- """
- return negate(Leaf(0), args[0])
- MESSAGES[multiply_zero] = _('Multiplication with zero yields zero.')
- def match_multiply_one(node):
- """
- a * 1 -> a
- 1 * a -> a
- -1 * a -> -a
- 1 * -a -> -a
- -1 * -a -> a
- """
- assert node.is_op(OP_MUL)
- left, right = node
- if left.value == 1:
- return [P(node, multiply_one, (right, left))]
- if right.value == 1:
- return [P(node, multiply_one, (left, right))]
- return []
- def multiply_one(root, args):
- """
- a * 1 -> a
- 1 * a -> a
- -1 * a -> -a
- 1 * -a -> -a
- -1 * -a -> a
- """
- a, one = args
- return a.negate(one.negated + root.negated)
- MESSAGES[multiply_one] = _('Multiplication with one yields the multiplicant.')
- def match_multiply_numerics(node):
- """
- 3 * 2 -> 6
- 3.0 * 2 -> 6.0
- 3 * 2.0 -> 6.0
- 3.0 * 2.0 -> 6.0
- """
- assert node.is_op(OP_MUL)
- p = []
- scope = Scope(node)
- numerics = filter(lambda n: n.is_numeric(), scope)
- for c0, c1 in combinations(numerics, 2):
- p.append(P(node, multiply_numerics, (scope, c0, c1)))
- return p
- def multiply_numerics(root, args):
- """
- Combine two constants to a single constant in an n-ary multiplication.
- Example:
- 2 * 3 -> 6
- """
- scope, c0, c1 = args
- # Replace the left node with the new expression
- substitution = Leaf(c0.value * c1.value).negate(c0.negated + c1.negated)
- scope.replace(c0, substitution)
- # Remove the right node
- scope.remove(c1)
- return scope.as_nary_node()
- MESSAGES[multiply_numerics] = _('Multiply constant {2} with {3}.')
|