Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import Tuple
from .math_gcd_lcm import gcd_wide ← from math.gcd-lcm ^1.0.0 · built alongside by fune
from .math_rational_types import Rational, RationalOp
from .math_round_div import RoundingMode, round_div ← from math.round-div ^1.0.0 · built alongside by fune
MAX_SAFE = 9007199254740991
def _reduce(numerator: int, denominator: int) -> Rational:
"""Reduce a wide fraction and bring it back into the exact number range."""
if denominator == 0:
raise ValueError("denominator must not be zero")
g = gcd_wide(numerator, denominator)
n = numerator // g
d = denominator // g
if d < 0:
n, d = -n, -d
# Python ints never overflow, but TypeScript numbers stop being exact here,
# and the three languages must agree.
if n > MAX_SAFE or n < -MAX_SAFE or d > MAX_SAFE:
raise ValueError("rational overflow: the reduced result exceeds 2^53 - 1")
return Rational(numerator=n, denominator=d)
def rational(numerator: int, denominator: int) -> Rational:
"""Build a reduced fraction with a positive denominator."""
for v in (numerator, denominator):
if isinstance(v, bool) or not isinstance(v, int):
raise TypeError("numerator and denominator must be integers")
if v > MAX_SAFE or v < -MAX_SAFE:
raise ValueError("rational overflow: numerator and denominator must be within 2^53 - 1")
return _reduce(numerator, denominator)
def _parts(r: Rational) -> Tuple[int, int]:
n = rational(r.numerator, r.denominator)
return n.numerator, n.denominator
def add_rational(a: Rational, b: Rational) -> Rational:
an, ad = _parts(a)
bn, bd = _parts(b)
return _reduce(an * bd + bn * ad, ad * bd)
def subtract_rational(a: Rational, b: Rational) -> Rational:
an, ad = _parts(a)
bn, bd = _parts(b)
return _reduce(an * bd - bn * ad, ad * bd)
def multiply_rational(a: Rational, b: Rational) -> Rational:
an, ad = _parts(a)
bn, bd = _parts(b)
return _reduce(an * bn, ad * bd)
def divide_rational(a: Rational, b: Rational) -> Rational:
an, ad = _parts(a)
bn, bd = _parts(b)
if bn == 0:
raise ValueError("division by zero")
return _reduce(an * bd, ad * bn)
def compare_rational(a: Rational, b: Rational) -> int:
"""-1, 0 or 1. Exact: cross-multiplied, never through a float."""
an, ad = _parts(a)
bn, bd = _parts(b)
left = an * bd
right = bn * ad
return -1 if left < right else 1 if left > right else 0
def rational_to_integer(r: Rational, mode: RoundingMode) -> int:
"""The nearest integer under an explicit rounding mode."""
n, d = _parts(r)
return round_div(n, d, mode)
def calculate_rational(a: Rational, op: RationalOp, b: Rational) -> Rational:
"""Add, subtract, multiply or divide two fractions exactly.
The result is always reduced with a positive denominator, so equal values
have equal fields.
"""
if op == "add":
return add_rational(a, b)
if op == "subtract":
return subtract_rational(a, b)
if op == "multiply":
return multiply_rational(a, b)
if op == "divide":
return divide_rational(a, b)
raise ValueError('unknown operation "%s"' % (op,))