Commit e20e1936 authored by Taddeus Kroes's avatar Taddeus Kroes

Added some numeric division rules.

parent 1a15deb5
from itertools import combinations from itertools import combinations
from .utils import greatest_common_divisor
from ..node import ExpressionLeaf as Leaf, Scope, negate, OP_ADD, OP_DIV, \ from ..node import ExpressionLeaf as Leaf, Scope, negate, OP_ADD, OP_DIV, \
OP_MUL, OP_POW OP_MUL, OP_POW
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
...@@ -86,23 +87,39 @@ def match_divide_numerics(node): ...@@ -86,23 +87,39 @@ def match_divide_numerics(node):
Example: Example:
6 / 2 -> 3 6 / 2 -> 3
3 / 2 -> 3 / 2 # 1.5 would mean a decrease in precision 3 / 2 -> 3 / 2 # 1.5 would mean a decrease in precision
3.0 / 2 -> 1.5 3.0 / 2 -> 1.5
3 / 2.0 -> 1.5 3 / 2.0 -> 1.5
3.0 / 2.0 -> 1.5 3.0 / 2.0 -> 1.5
3 / 1.0 -> 3 # Exceptional case: division of integer by 1.0 keeps 3 / 1.0 -> 3 # Exceptional case: division of integer by 1.0
# integer precision # keeps integer precision
2 / 4 -> 1 / 2 # denominator % nominator == 0
4 / 3 -> 1 + 1 / 3 # nominator > denominator
""" """
assert node.is_op(OP_DIV) assert node.is_op(OP_DIV)
n, d = node n, d = node
divide = False divide = False
dv = d.value nv, dv = n.value, d.value
if n.is_int() and d.is_int(): if n.is_int() and d.is_int():
# 6 / 2 -> 3 mod = nv % dv
# 3 / 2 -> 3 / 2
divide = not divmod(n.value, dv)[1] if not mod:
# 6 / 2 -> 3
# 3 / 2 -> 3 / 2
return [P(node, divide_numerics, (nv, dv))]
gcd = greatest_common_divisor(nv, dv)
if 1 < gcd <= nv:
# 2 / 4 -> 1 / 2
return [P(node, reduce_fraction_constants, (gcd,))]
if nv > dv:
# 4 / 3 -> 1 + 1 / 3
return [P(node, fraction_to_int_fraction,
((nv - mod) / dv, mod, dv))]
elif n.is_numeric() and d.is_numeric(): elif n.is_numeric() and d.is_numeric():
if d == 1.0: if d == 1.0:
# 3 / 1.0 -> 3 # 3 / 1.0 -> 3
...@@ -111,14 +128,14 @@ def match_divide_numerics(node): ...@@ -111,14 +128,14 @@ def match_divide_numerics(node):
# 3.0 / 2 -> 1.5 # 3.0 / 2 -> 1.5
# 3 / 2.0 -> 1.5 # 3 / 2.0 -> 1.5
# 3.0 / 2.0 -> 1.5 # 3.0 / 2.0 -> 1.5
divide = True return [P(node, divide_numerics, (nv, dv))]
return [P(node, divide_numerics, (n.value, dv))] if divide else [] return []
def divide_numerics(root, args): def divide_numerics(root, args):
""" """
Combine two constants to a single constant in a division. Combine two divided constants into a single constant.
Examples: Examples:
6 / 2 -> 3 6 / 2 -> 3
...@@ -135,6 +152,39 @@ def divide_numerics(root, args): ...@@ -135,6 +152,39 @@ def divide_numerics(root, args):
MESSAGES[divide_numerics] = _('Divide constant {1} by constant {2}.') MESSAGES[divide_numerics] = _('Divide constant {1} by constant {2}.')
def reduce_fraction_constants(root, args):
"""
Reduce the nominator and denominator of a fraction with a given greatest
common divisor.
Example:
2 / 4 -> 1 / 2
"""
gcd = args[0]
a, b = root
return Leaf(a.value / gcd).negate(a.negated) \
/ Leaf(b.value / gcd).negate(b.negated)
MESSAGES[reduce_fraction_constants] = _('Simplify fraction {0}.')
def fraction_to_int_fraction(root, args):
"""
Combine two divided integer into an integer with a fraction.
Examples:
4 / 3 -> 1 + 1 / 3
"""
integer, nominator, denominator = map(Leaf, args)
return integer + nominator / denominator
MESSAGES[divide_numerics] = _('Divide constant {1} by constant {2}.')
def match_multiply_zero(node): def match_multiply_zero(node):
""" """
a * 0 -> 0 a * 0 -> 0
......
from rules.sort import move_constant from rules.sort import move_constant
from rules.numerics import reduce_fraction_constants
def pick_suggestion(possibilities): def pick_suggestion(possibilities):
...@@ -7,12 +8,10 @@ def pick_suggestion(possibilities): ...@@ -7,12 +8,10 @@ def pick_suggestion(possibilities):
# TODO: pick the best suggestion. # TODO: pick the best suggestion.
for suggestion, p in enumerate(possibilities + [None]): for suggestion, p in enumerate(possibilities + [None]):
if p and p.handler not in [move_constant]: if p and p.handler not in [move_constant, reduce_fraction_constants]:
break break
if not p: if not p:
return possibilities[0] return possibilities[0]
return possibilities[suggestion] return possibilities[suggestion]
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