Functional Weave
Code in Rust

inventory.eoq@1.0.0

impl/typescript.ts

2,001 bytes · the TypeScript 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.

import { type RoundingMode } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { integerSqrt } from "./math_integer_sqrt.ts";  ← from math.integer-sqrt ^1.0.0 · built alongside by fune
import { type Money, assertSameCurrency } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune

/**
 * 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.
 */
export function economicOrderQuantity(annualDemand: number, orderCost: Money, holdingCost: Money, mode: RoundingMode): number {
  if (!Number.isInteger(annualDemand) || annualDemand < 0) {
    throw new RangeError(`annualDemand must be a whole number of units, not negative, received ${annualDemand}`);
  }
  assertSameCurrency(orderCost, holdingCost);
  if (orderCost.minor < 0) {
    throw new RangeError(`orderCost must not be negative, received ${orderCost.minor}`);
  }
  if (holdingCost.minor <= 0) {
    throw new RangeError(`holdingCost must be greater than zero, received ${holdingCost.minor}`);
  }
  const d = BigInt(annualDemand);
  const s = BigInt(orderCost.minor);
  const h = BigInt(holdingCost.minor);
  const eightDS = 8n * d * s;
  if (eightDS > BigInt(Number.MAX_SAFE_INTEGER)) {
    throw new RangeError("annualDemand and orderCost are too large: 8 x demand x order cost must stay within 2^53 - 1");
  }
  const twoDS = 2n * d * s;
  const n = BigInt(integerSqrt(Number(twoDS / h)));
  const odd = 2n * n + 1n;
  switch (mode) {
    case "down":
      return Number(n);
    case "up":
      return Number(n * n * h === twoDS ? n : n + 1n);
    case "half-up":
      return Number(odd * odd * h <= eightDS ? n + 1n : n);
    case "half-even": {
      const half = odd * odd * h;
      if (half < eightDS) return Number(n + 1n);
      if (half > eightDS) return Number(n);
      return Number(n % 2n === 0n ? n : n + 1n);
    }
    default:
      throw new RangeError(`unknown rounding mode "${mode}"`);
  }
}