from typing import Tuple from .math_gcd_lcm import gcd_wide from .math_rational_types import Rational _MAX_SAFE = 9007199254740991 def rational_from_wide(numerator: int, denominator: int) -> Rational: """Reduce a wide fraction and bring it back into the exact number range. The arithmetic works on products that may be past 2^53, and only the reduced result has to fit. """ 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 rational_from_wide(numerator, denominator) def rational_parts(r: Rational) -> Tuple[int, int]: """A checked, reduced operand as (numerator, denominator).""" n = rational(r.numerator, r.denominator) return n.numerator, n.denominator