Functional Weave
Code in Python

inventory.eoq@1.0.0

impl/python.py

1,931 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from .math_integer_sqrt import integer_sqrt  ← from math.integer-sqrt ^1.0.0 · built alongside by fune
from .math_round_div import RoundingMode  ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import Money, assert_same_currency  ← from money.amount ^1.0.0 · built alongside by fune

MAX_SAFE = 2**53 - 1


def economic_order_quantity(annual_demand: int, order_cost: Money, holding_cost: Money, mode: RoundingMode) -> int:
    """Economic order quantity, sqrt(2DS / H), in whole units.

    The root is never taken in floating point: the floor is an exact integer
    square root and the rounding decision compares whole numbers, so an exact
    half (sqrt(6.25) = 2.5) rounds by ``mode`` and not by the language.
    """
    if isinstance(annual_demand, bool) or not isinstance(annual_demand, int) or annual_demand < 0:
        raise ValueError(
            "annualDemand must be a whole number of units, not negative, received %r" % (annual_demand,)
        )
    assert_same_currency(order_cost, holding_cost)
    if order_cost.minor < 0:
        raise ValueError("orderCost must not be negative, received %d" % order_cost.minor)
    if holding_cost.minor <= 0:
        raise ValueError("holdingCost must be greater than zero, received %d" % holding_cost.minor)
    eight_ds = 8 * annual_demand * order_cost.minor
    if eight_ds > MAX_SAFE:
        raise ValueError(
            "annualDemand and orderCost are too large: 8 x demand x order cost must stay within 2^53 - 1"
        )
    two_ds = 2 * annual_demand * order_cost.minor
    h = holding_cost.minor
    n = integer_sqrt(two_ds // h)
    odd = 2 * n + 1
    if mode == "down":
        return n
    if mode == "up":
        return n if n * n * h == two_ds else n + 1
    if mode == "half-up":
        return n + 1 if odd * odd * h <= eight_ds else n
    if mode == "half-even":
        half = odd * odd * h
        if half < eight_ds:
            return n + 1
        if half > eight_ds:
            return n
        return n if n % 2 == 0 else n + 1
    raise ValueError('unknown rounding mode "%s"' % (mode,))