Functional Weave
Code in Python

math.big-integer@1.0.0

impl/python.py

2,180 bytes · the Python implementation · view raw

import re

from .math_big_integer_types import BigIntegerOp

_INTEGER = re.compile(r"^-?(0|[1-9][0-9]*)$")

#: The largest result, in bits, that power will build: about 19,700 decimal digits.
MAX_POWER_BITS = 65536


def parse_big_integer(text: str) -> int:
    """Parse the registry's decimal integer form. "-0", "+1", "01" and " 1" are refused."""
    if not isinstance(text, str) or not _INTEGER.fullmatch(text) or text == "-0":
        raise ValueError('not a whole number in decimal: "%s"' % (text,))
    return int(text)


def _trunc_div(x: int, y: int) -> int:
    q = abs(x) // abs(y)
    return -q if (x < 0) != (y < 0) else q


def power_big_integer(base: int, exponent: int) -> int:
    """Raise to a power, refusing results beyond MAX_POWER_BITS before building them."""
    if exponent < 0:
        raise ValueError("exponent must not be negative")
    magnitude = abs(base)
    if magnitude >= 2 and magnitude.bit_length() * exponent > MAX_POWER_BITS:
        raise ValueError("result too large: power is limited to %d bits" % (MAX_POWER_BITS,))
    if magnitude <= 1:
        if exponent == 0:
            return 1
        if base == -1:
            return 1 if exponent % 2 == 0 else -1
        return base
    return base ** exponent


def calculate_big_integer(a: str, op: BigIntegerOp, b: str) -> str:
    """Exact integer arithmetic on decimal strings.

    Division truncates towards zero and the remainder takes the sign of the
    dividend, as in TypeScript and Rust. Python's own // floors, so it is not
    used directly on signed values.
    """
    x = parse_big_integer(a)
    y = parse_big_integer(b)
    if op == "add":
        return str(x + y)
    if op == "subtract":
        return str(x - y)
    if op == "multiply":
        return str(x * y)
    if op == "divide":
        if y == 0:
            raise ZeroDivisionError("division by zero")
        return str(_trunc_div(x, y))
    if op == "remainder":
        if y == 0:
            raise ZeroDivisionError("division by zero")
        return str(x - y * _trunc_div(x, y))
    if op == "power":
        return str(power_big_integer(x, y))
    raise ValueError('unknown operation "%s"' % (op,))