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}"`);
}
}