Commit e20e1936 authored by Taddeus Kroes's avatar Taddeus Kroes

Added some numeric division rules.

parent 1a15deb5
from itertools import combinations
from .utils import greatest_common_divisor
from ..node import ExpressionLeaf as Leaf, Scope, negate, OP_ADD, OP_DIV, \
OP_MUL, OP_POW
from ..possibilities import Possibility as P, MESSAGES
......@@ -86,23 +87,39 @@ def match_divide_numerics(node):
Example:
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 / 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
3 / 1.0 -> 3 # Exceptional case: division of integer by 1.0
# keeps integer precision
2 / 4 -> 1 / 2 # denominator % nominator == 0
4 / 3 -> 1 + 1 / 3 # nominator > denominator
"""
assert node.is_op(OP_DIV)
n, d = node
divide = False
dv = d.value
nv, dv = n.value, d.value
if n.is_int() and d.is_int():
# 6 / 2 -> 3
# 3 / 2 -> 3 / 2
divide = not divmod(n.value, dv)[1]
mod = nv % dv
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():
if d == 1.0:
# 3 / 1.0 -> 3
......@@ -111,14 +128,14 @@ def match_divide_numerics(node):
# 3.0 / 2 -> 1.5
# 3 / 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):
"""
Combine two constants to a single constant in a division.
Combine two divided constants into a single constant.
Examples:
6 / 2 -> 3
......@@ -135,6 +152,39 @@ def divide_numerics(root, args):
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):
"""
a * 0 -> 0
......
from rules.sort import move_constant
from rules.numerics import reduce_fraction_constants
def pick_suggestion(possibilities):
......@@ -7,12 +8,10 @@ def pick_suggestion(possibilities):
# TODO: pick the best suggestion.
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
if not p:
return possibilities[0]
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