UNPKG

dinero.js

Version:

Create, calculate, and format money in JavaScript and TypeScript

1,438 lines (1,357 loc) 43.6 kB
//#region src/core/checks/messages.ts const INVALID_AMOUNT_MESSAGE = "Amount is invalid."; const INVALID_SCALE_MESSAGE = "Scale is invalid."; const INVALID_RATIOS_MESSAGE = "Ratios are invalid."; const UNEQUAL_CURRENCIES_MESSAGE = "Objects must have the same currency."; const NON_DECIMAL_CURRENCY_MESSAGE = "Currency is not decimal."; const MISMATCHED_BASES_MESSAGE = "Objects must have the same currency base."; //#endregion //#region src/core/helpers/assert.ts /** * Assert a condition. * * @param condition - The condition to verify. * @param message - The error message to throw. * * @throws If the condition isn't met. */ function assert(condition, message) { if (!condition) throw new Error(`[Dinero.js] ${message}`); } //#endregion //#region src/core/helpers/createDinero.ts function createDinero({ calculator, onCreate, formatter = { toNumber: Number, toString: String } }) { return function dinero({ amount, currency: { code, base, exponent }, scale = exponent }) { const currency = { code, base, exponent }; onCreate?.({ amount, currency, scale }); return { calculator, formatter, create: dinero, toJSON() { return { amount, currency, scale }; } }; }; } //#endregion //#region src/core/types/DineroCalculator.ts let DineroComparisonOperator = /* @__PURE__ */ function(DineroComparisonOperator) { DineroComparisonOperator[DineroComparisonOperator["LT"] = -1] = "LT"; DineroComparisonOperator[DineroComparisonOperator["EQ"] = 0] = "EQ"; DineroComparisonOperator[DineroComparisonOperator["GT"] = 1] = "GT"; return DineroComparisonOperator; }({}); //#endregion //#region src/core/utils/equal.ts /** * Returns an equal function. * * @param calculator - The calculator to use. * * @returns The equal function. */ function equal$2(calculator) { return (subject, comparator) => { return calculator.compare(subject, comparator) === DineroComparisonOperator.EQ; }; } //#endregion //#region src/core/utils/lessThan.ts /** * Returns a lessThan function. * * @param calculator - The calculator to use. * * @returns The lessThan function. */ function lessThan$1(calculator) { return (subject, comparator) => { return calculator.compare(subject, comparator) === DineroComparisonOperator.LT; }; } //#endregion //#region src/core/utils/absolute.ts function absolute(calculator) { const equalFn = equal$2(calculator); const lessThanFn = lessThan$1(calculator); const zero = calculator.zero(); return (input) => { if (equalFn(input, zero)) return zero; if (lessThanFn(input, zero)) { const minusOne = calculator.decrement(zero); return calculator.multiply(minusOne, input); } return input; }; } //#endregion //#region src/core/utils/compare.ts /** * Returns a compare function. * * @param calculator - The calculator to use. * * @returns The compare function. */ function compare$1(calculator) { return (subject, comparator) => { return calculator.compare(subject, comparator); }; } //#endregion //#region src/core/utils/isArray.ts function isArray(maybeArray) { return Array.isArray(maybeArray); } //#endregion //#region src/core/utils/computeBase.ts function computeBase(calculator) { return (base) => { if (isArray(base)) return base.reduce((acc, curr) => calculator.multiply(acc, curr)); return base; }; } //#endregion //#region src/core/utils/countTrailingZeros.ts function countTrailingZeros(calculator) { const equalFn = equal$2(calculator); return (input, base) => { const zero = calculator.zero(); if (equalFn(zero, input)) return calculator.zero(); let i = zero; let temp = input; while (equalFn(calculator.modulo(temp, base), zero)) { temp = calculator.integerDivide(temp, base); i = calculator.increment(i); } return i; }; } //#endregion //#region src/core/utils/greaterThan.ts /** * Returns a greaterThan function. * * @param calculator - The calculator to use. * * @returns The greaterThan function. */ function greaterThan$1(calculator) { return (subject, comparator) => { return calculator.compare(subject, comparator) === DineroComparisonOperator.GT; }; } //#endregion //#region src/core/utils/greaterThanOrEqual.ts /** * Returns a greaterThanOrEqual function. * * @param calculator - The calculator to use. * * @returns The greaterThanOrEqual function. */ function greaterThanOrEqual$1(calculator) { return (subject, comparator) => { return greaterThan$1(calculator)(subject, comparator) || equal$2(calculator)(subject, comparator); }; } //#endregion //#region src/core/utils/distribute.ts /** * Returns a distribute function. * * @param calculator - The calculator to use. * * @returns The distribute function. */ function distribute(calculator) { return (value, ratios) => { const equalFn = equal$2(calculator); const greaterThanFn = greaterThan$1(calculator); const lessThanFn = lessThan$1(calculator); const greaterThanOrEqualFn = greaterThanOrEqual$1(calculator); const zero = calculator.zero(); const one = calculator.increment(zero); const total = ratios.reduce((a, b) => calculator.add(a, b), zero); if (equalFn(total, zero)) return ratios; let remainder = value; const shares = ratios.map((ratio) => { const share = calculator.integerDivide(calculator.multiply(value, ratio), total) || zero; remainder = calculator.subtract(remainder, share); return share; }); const isPositive = greaterThanOrEqualFn(value, zero); const compare = isPositive ? greaterThanFn : lessThanFn; const amount = isPositive ? one : calculator.decrement(zero); const sortedIndices = ratios.map((ratio, index) => ({ ratio, index })).filter(({ ratio }) => !equalFn(ratio, zero)).sort((a, b) => greaterThanFn(a.ratio, b.ratio) ? -1 : 1).map(({ index }) => index); let i = 0; while (compare(remainder, zero)) { const index = sortedIndices[i % sortedIndices.length]; shares[index] = calculator.add(shares[index], amount); const newRemainder = calculator.subtract(remainder, amount); if (equalFn(newRemainder, remainder)) break; remainder = newRemainder; i++; } return shares; }; } //#endregion //#region src/core/utils/isScaledAmount.ts function isScaledAmount(amount) { return amount?.hasOwnProperty("amount"); } //#endregion //#region src/core/utils/getAmountAndScale.ts function getAmountAndScale(value, zero) { if (isScaledAmount(value)) return { amount: value.amount, scale: value?.scale ?? zero }; return { amount: value, scale: zero }; } //#endregion //#region src/core/utils/getDivisors.ts function getDivisors(calculator) { const { multiply } = calculator; return (bases) => { return bases.reduce((divisors, _, i) => { const divisor = bases.slice(i).reduce((acc, curr) => multiply(acc, curr)); return [...divisors, divisor]; }, []); }; } //#endregion //#region src/core/utils/isEven.ts function isEven(calculator) { const equalFn = equal$2(calculator); const zero = calculator.zero(); const two = calculator.increment(calculator.increment(zero)); return (input) => { return equalFn(calculator.modulo(input, two), zero); }; } //#endregion //#region src/core/utils/isHalf.ts function isHalf(calculator) { const equalFn = equal$2(calculator); const absoluteFn = absolute(calculator); return (input, total) => { const remainder = absoluteFn(calculator.modulo(input, total)); return equalFn(calculator.subtract(total, remainder), remainder); }; } //#endregion //#region src/core/utils/lessThanOrEqual.ts /** * Returns a lessThanOrEqual function. * * @param calculator - The calculator to use. * * @returns The lessThanOrEqual function. */ function lessThanOrEqual$1(calculator) { return (subject, comparator) => { return lessThan$1(calculator)(subject, comparator) || equal$2(calculator)(subject, comparator); }; } //#endregion //#region src/core/utils/maximum.ts /** * Returns a maximum function. * * @param calculator - The calculator to use. * * @returns The maximum function. */ function maximum$1(calculator) { const lessThanFn = lessThan$1(calculator); return (values) => { return values.reduce((acc, curr) => { return lessThanFn(acc, curr) ? curr : acc; }); }; } //#endregion //#region src/core/utils/minimum.ts /** * Returns a minimum function. * * @param calculator - The calculator to use. * * @returns The minimum function. */ function minimum$1(calculator) { const greaterThanFn = greaterThan$1(calculator); return (values) => { return values.reduce((acc, curr) => { return greaterThanFn(acc, curr) ? curr : acc; }); }; } //#endregion //#region src/core/utils/sign.ts function sign(calculator) { const equalFn = equal$2(calculator); const lessThanFn = lessThan$1(calculator); const zero = calculator.zero(); return (input) => { if (equalFn(input, zero)) return zero; const one = calculator.increment(zero); const minusOne = calculator.decrement(zero); return lessThanFn(input, zero) ? minusOne : one; }; } //#endregion //#region src/core/api/haveSameCurrency.ts function haveSameCurrency$1(dineroObjects) { const [firstDinero, ...otherDineros] = dineroObjects; const computeBaseFn = computeBase(firstDinero.calculator); const { currency: comparator } = firstDinero.toJSON(); const equalFn = equal$2(firstDinero.calculator); const comparatorBase = computeBaseFn(comparator.base); return otherDineros.every((d) => { const { currency: subject } = d.toJSON(); const subjectBase = computeBaseFn(subject.base); return subject.code === comparator.code && equalFn(subjectBase, comparatorBase) && equalFn(subject.exponent, comparator.exponent); }); } //#endregion //#region src/core/divide/down.ts /** * Divide and round down. * * Rounding down happens whenever the quotient is not an integer. * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const down = (amount, factor, calculator) => { const greaterThanFn = greaterThan$1(calculator); const equalFn = equal$2(calculator); const zero = calculator.zero(); const isPositive = greaterThanFn(amount, zero); const quotient = calculator.integerDivide(amount, factor); const isInteger = equalFn(calculator.modulo(amount, factor), zero); if (isPositive || isInteger) return quotient; return calculator.decrement(quotient); }; //#endregion //#region src/core/divide/halfAwayFromZero.ts /** * Divide and round towards "nearest neighbor" unless both neighbors are * equidistant, in which case round away from zero. * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const halfAwayFromZero = (amount, factor, calculator) => { const signFn = sign(calculator); const isHalfFn = isHalf(calculator); const absoluteFn = absolute(calculator); if (!isHalfFn(amount, factor)) return halfUp(amount, factor, calculator); return calculator.multiply(signFn(amount), up(absoluteFn(amount), factor, calculator)); }; //#endregion //#region src/core/divide/halfDown.ts /** * Divide and round towards "nearest neighbor" unless both neighbors are * equidistant, in which case round down. * * Rounding down happens when: * - The quotient is half (e.g., -1.5, 1.5). * - The quotient is positive and less than half (e.g., 1.4). * - The quotient is negative and greater than half (e.g., -1.6). * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const halfDown = (amount, factor, calculator) => { if (isHalf(calculator)(amount, factor)) return down(amount, factor, calculator); return halfUp(amount, factor, calculator); }; //#endregion //#region src/core/divide/halfEven.ts /** * Divide and round towards "nearest neighbor" unless both neighbors are * equidistant, in which case round to the nearest even integer. * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const halfEven = (amount, factor, calculator) => { const isEvenFn = isEven(calculator); const isHalfFn = isHalf(calculator); const rounded = halfUp(amount, factor, calculator); if (!isHalfFn(amount, factor)) return rounded; return isEvenFn(rounded) ? rounded : calculator.decrement(rounded); }; //#endregion //#region src/core/divide/halfOdd.ts /** * Divide and round towards "nearest neighbor" unless both neighbors are * equidistant, in which case round to the nearest odd integer. * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const halfOdd = (amount, factor, calculator) => { const isEvenFn = isEven(calculator); const isHalfFn = isHalf(calculator); const rounded = halfUp(amount, factor, calculator); if (!isHalfFn(amount, factor)) return rounded; return isEvenFn(rounded) ? calculator.decrement(rounded) : rounded; }; //#endregion //#region src/core/divide/halfTowardsZero.ts /** * Divide and round towards "nearest neighbor" unless both neighbors are * equidistant, in which case round towards zero. * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const halfTowardsZero = (amount, factor, calculator) => { const signFn = sign(calculator); const isHalfFn = isHalf(calculator); const absoluteFn = absolute(calculator); if (!isHalfFn(amount, factor)) return halfUp(amount, factor, calculator); return calculator.multiply(signFn(amount), down(absoluteFn(amount), factor, calculator)); }; //#endregion //#region src/core/divide/halfUp.ts /** * Divide and round towards "nearest neighbor" unless both neighbors are * equidistant, in which case round up. * * Rounding up happens when: * - The quotient is half (e.g., -1.5, 1.5). * - The quotient is positive and greater than half (e.g., 1.6). * - The quotient is negative and less than half (e.g., -1.4). * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const halfUp = (amount, factor, calculator) => { const greaterThanFn = greaterThan$1(calculator); const isHalfFn = isHalf(calculator); const absoluteFn = absolute(calculator); const zero = calculator.zero(); const remainder = absoluteFn(calculator.modulo(amount, factor)); const isLessThanHalf = greaterThanFn(calculator.subtract(factor, remainder), remainder); const isPositive = greaterThanFn(amount, zero); if (isHalfFn(amount, factor) || isPositive && !isLessThanHalf || !isPositive && isLessThanHalf) return up(amount, factor, calculator); return down(amount, factor, calculator); }; //#endregion //#region src/core/divide/up.ts /** * Divide and round up. * * Rounding up happens whenever the quotient is not an integer. * * @param amount - The amount to divide. * @param factor - The factor to divide by. * @param calculator - The calculator to use. * * @returns The rounded amount. */ const up = (amount, factor, calculator) => { const greaterThanFn = greaterThan$1(calculator); const equalFn = equal$2(calculator); const zero = calculator.zero(); const isPositive = greaterThanFn(amount, zero); const quotient = calculator.integerDivide(amount, factor); if (!equalFn(calculator.modulo(amount, factor), zero) && isPositive) return calculator.increment(quotient); return quotient; }; //#endregion //#region src/core/api/transformScale.ts function transformScale$1(calculator) { const greaterThanFn = greaterThan$1(calculator); const computeBaseFn = computeBase(calculator); return function transformScaleFn(...[dineroObject, newScale, divide = down]) { const { amount, currency, scale } = dineroObject.toJSON(); const isLarger = greaterThanFn(newScale, scale); const operation = isLarger ? calculator.multiply : divide; const [a, b] = isLarger ? [newScale, scale] : [scale, newScale]; const base = computeBaseFn(currency.base); const factor = calculator.power(base, calculator.subtract(a, b)); return dineroObject.create({ amount: operation(amount, factor, calculator), currency, scale: newScale }); }; } //#endregion //#region src/core/api/normalizeScale.ts function normalizeScale$1(calculator) { const maximumFn = maximum$1(calculator); const convertScaleFn = transformScale$1(calculator); const equalFn = equal$2(calculator); return function _normalizeScale(...[dineroObjects]) { const highestScale = dineroObjects.reduce((highest, current) => { const { scale } = current.toJSON(); return maximumFn([highest, scale]); }, calculator.zero()); return dineroObjects.map((d) => { const { scale } = d.toJSON(); return !equalFn(scale, highestScale) ? convertScaleFn(d, highestScale) : d; }); }; } //#endregion //#region src/core/api/add.ts function unsafeAdd(calculator) { return function add(...[augend, addend]) { const { amount: augendAmount, currency, scale } = augend.toJSON(); const { amount: addendAmount } = addend.toJSON(); const amount = calculator.add(augendAmount, addendAmount); return augend.create({ amount, currency, scale }); }; } function safeAdd(calculator) { const normalizeFn = normalizeScale$1(calculator); const addFn = unsafeAdd(calculator); return function add(...[augend, addend]) { assert(haveSameCurrency$1([augend, addend]), UNEQUAL_CURRENCIES_MESSAGE); const [newAugend, newAddend] = normalizeFn([augend, addend]); return addFn(newAugend, newAddend); }; } //#endregion //#region src/core/api/allocate.ts function unsafeAllocate(calculator) { return function allocate(...[dineroObject, ratios]) { const { amount, currency, scale } = dineroObject.toJSON(); return distribute(calculator)(amount, ratios.map((ratio) => ratio.amount)).map((share) => { return dineroObject.create({ amount: share, currency, scale }); }); }; } function safeAllocate(calculator) { const allocateFn = unsafeAllocate(calculator); const greaterThanOrEqualFn = greaterThanOrEqual$1(calculator); const greaterThanFn = greaterThan$1(calculator); const convertScaleFn = transformScale$1(calculator); const maximumFn = maximum$1(calculator); const equalFn = equal$2(calculator); const zero = calculator.zero(); const ten = new Array(10).fill(null).reduce(calculator.increment, zero); return function allocate(...[dineroObject, ratios]) { const hasRatios = ratios.length > 0; const scaledRatios = ratios.map((ratio) => getAmountAndScale(ratio, zero)); const highestRatioScale = hasRatios ? maximumFn(scaledRatios.map(({ scale }) => scale)) : zero; const normalizedRatios = scaledRatios.map(({ amount, scale }) => { const factor = equalFn(scale, highestRatioScale) ? zero : calculator.subtract(highestRatioScale, scale); return { amount: calculator.multiply(amount, calculator.power(ten, factor)), scale }; }); const hasOnlyPositiveRatios = normalizedRatios.every(({ amount }) => greaterThanOrEqualFn(amount, zero)); const hasOneNonZeroRatio = normalizedRatios.some(({ amount }) => greaterThanFn(amount, zero)); assert(hasRatios && hasOnlyPositiveRatios && hasOneNonZeroRatio, INVALID_RATIOS_MESSAGE); const { scale } = dineroObject.toJSON(); return allocateFn(convertScaleFn(dineroObject, calculator.add(scale, highestRatioScale)), normalizedRatios); }; } //#endregion //#region src/core/api/compare.ts function unsafeCompare(calculator) { const compareFn = compare$1(calculator); return function compare(...[dineroObject, comparator]) { const [subjectAmount, comparatorAmount] = [dineroObject, comparator].map((d) => { const { amount } = d.toJSON(); return amount; }); return compareFn(subjectAmount, comparatorAmount); }; } function safeCompare(calculator) { const normalizeFn = normalizeScale$1(calculator); const compareFn = unsafeCompare(calculator); return function compare(...[dineroObject, comparator]) { assert(haveSameCurrency$1([dineroObject, comparator]), UNEQUAL_CURRENCIES_MESSAGE); const [subjectAmount, comparatorAmount] = normalizeFn([dineroObject, comparator]); return compareFn(subjectAmount, comparatorAmount); }; } //#endregion //#region src/core/api/convert.ts function convert$1(calculator) { const convertScaleFn = transformScale$1(calculator); const maximumFn = maximum$1(calculator); const computeBaseFn = computeBase(calculator); const equalFn = equal$2(calculator); const zero = calculator.zero(); return function convertFn(...[dineroObject, newCurrency, rates]) { const rate = rates[newCurrency.code]; const { amount, currency, scale } = dineroObject.toJSON(); assert(equalFn(computeBaseFn(currency.base), computeBaseFn(newCurrency.base)), MISMATCHED_BASES_MESSAGE); const { amount: rateAmount, scale: rateScale } = getAmountAndScale(rate, zero); const newScale = calculator.add(scale, rateScale); return convertScaleFn(dineroObject.create({ amount: calculator.multiply(amount, rateAmount), currency: newCurrency, scale: newScale }), maximumFn([newScale, newCurrency.exponent])); }; } //#endregion //#region src/core/api/haveSameAmount.ts function haveSameAmount$1(calculator) { const normalizeFn = normalizeScale$1(calculator); const equalFn = equal$2(calculator); return function _haveSameAmount(...[dineroObjects]) { const [firstDinero, ...otherDineros] = normalizeFn(dineroObjects); const { amount: comparatorAmount } = firstDinero.toJSON(); return otherDineros.every((d) => { const { amount: subjectAmount } = d.toJSON(); return equalFn(subjectAmount, comparatorAmount); }); }; } //#endregion //#region src/core/api/equal.ts function equal$1(calculator) { return function _equal(...[dineroObject, comparator]) { return haveSameAmount$1(calculator)([dineroObject, comparator]) && haveSameCurrency$1([dineroObject, comparator]); }; } //#endregion //#region src/core/api/greaterThan.ts function unsafeGreaterThan(calculator) { const greaterThanFn = greaterThan$1(calculator); return function greaterThan(...[dineroObject, comparator]) { const [subjectAmount, comparatorAmount] = [dineroObject, comparator].map((d) => { const { amount } = d.toJSON(); return amount; }); return greaterThanFn(subjectAmount, comparatorAmount); }; } function safeGreaterThan(calculator) { const normalizeFn = normalizeScale$1(calculator); const greaterThanFn = unsafeGreaterThan(calculator); return function greaterThan(...[dineroObject, comparator]) { assert(haveSameCurrency$1([dineroObject, comparator]), UNEQUAL_CURRENCIES_MESSAGE); const [subjectAmount, comparatorAmount] = normalizeFn([dineroObject, comparator]); return greaterThanFn(subjectAmount, comparatorAmount); }; } //#endregion //#region src/core/api/greaterThanOrEqual.ts function unsafeGreaterThanOrEqual(calculator) { const greaterThanOrEqualFn = greaterThanOrEqual$1(calculator); return function greaterThanOrEqual(...[dineroObject, comparator]) { const [subjectAmount, comparatorAmount] = [dineroObject, comparator].map((d) => { const { amount } = d.toJSON(); return amount; }); return greaterThanOrEqualFn(subjectAmount, comparatorAmount); }; } function safeGreaterThanOrEqual(calculator) { const normalizeFn = normalizeScale$1(calculator); const greaterThanOrEqualFn = unsafeGreaterThanOrEqual(calculator); return function greaterThanOrEqual(...[dineroObject, comparator]) { assert(haveSameCurrency$1([dineroObject, comparator]), UNEQUAL_CURRENCIES_MESSAGE); const [subjectAmount, comparatorAmount] = normalizeFn([dineroObject, comparator]); return greaterThanOrEqualFn(subjectAmount, comparatorAmount); }; } //#endregion //#region src/core/api/hasSubUnits.ts function hasSubUnits$1(calculator) { const equalFn = equal$2(calculator); const computeBaseFn = computeBase(calculator); return function _hasSubUnits(...[dineroObject]) { const { amount, currency, scale } = dineroObject.toJSON(); const base = computeBaseFn(currency.base); return !equalFn(calculator.modulo(amount, calculator.power(base, scale)), calculator.zero()); }; } //#endregion //#region src/core/api/isNegative.ts function isNegative$1(calculator) { const lessThanFn = lessThan$1(calculator); return function _isNegative(...[dineroObject]) { const { amount } = dineroObject.toJSON(); return lessThanFn(amount, calculator.zero()); }; } //#endregion //#region src/core/api/isPositive.ts function isPositive$1(calculator) { const greaterThanFn = greaterThan$1(calculator); return function _isPositive(...[dineroObject]) { const { amount } = dineroObject.toJSON(); return greaterThanFn(amount, calculator.zero()); }; } //#endregion //#region src/core/api/isZero.ts function isZero$1(calculator) { const equalFn = equal$2(calculator); return function _isZero(...[dineroObject]) { const { amount } = dineroObject.toJSON(); return equalFn(amount, calculator.zero()); }; } //#endregion //#region src/core/api/lessThan.ts function unsafeLessThan(calculator) { const lessThanFn = lessThan$1(calculator); return function lessThan(...[dineroObject, comparator]) { const [subjectAmount, comparatorAmount] = [dineroObject, comparator].map((d) => { const { amount } = d.toJSON(); return amount; }); return lessThanFn(subjectAmount, comparatorAmount); }; } function safeLessThan(calculator) { const normalizeFn = normalizeScale$1(calculator); const lessThanFn = unsafeLessThan(calculator); return function lessThan(...[dineroObject, comparator]) { assert(haveSameCurrency$1([dineroObject, comparator]), UNEQUAL_CURRENCIES_MESSAGE); const [subjectAmount, comparatorAmount] = normalizeFn([dineroObject, comparator]); return lessThanFn(subjectAmount, comparatorAmount); }; } //#endregion //#region src/core/api/lessThanOrEqual.ts function unsafeLessThanOrEqual(calculator) { const lessThanOrEqualFn = lessThanOrEqual$1(calculator); return function lessThanOrEqual(...[dineroObject, comparator]) { const [subjectAmount, comparatorAmount] = [dineroObject, comparator].map((d) => { const { amount } = d.toJSON(); return amount; }); return lessThanOrEqualFn(subjectAmount, comparatorAmount); }; } function safeLessThanOrEqual(calculator) { const normalizeFn = normalizeScale$1(calculator); const lessThanOrEqualFn = unsafeLessThanOrEqual(calculator); return function lessThanOrEqual(...[dineroObject, comparator]) { assert(haveSameCurrency$1([dineroObject, comparator]), UNEQUAL_CURRENCIES_MESSAGE); const [subjectAmount, comparatorAmount] = normalizeFn([dineroObject, comparator]); return lessThanOrEqualFn(subjectAmount, comparatorAmount); }; } //#endregion //#region src/core/api/maximum.ts function unsafeMaximum(calculator) { const maxFn = maximum$1(calculator); return function maximum(dineroObjects) { const [firstDinero] = dineroObjects; const { currency, scale } = firstDinero.toJSON(); const amount = maxFn(dineroObjects.map((subject) => { const { amount: subjectAmount } = subject.toJSON(); return subjectAmount; })); return firstDinero.create({ amount, currency, scale }); }; } function safeMaximum(calculator) { const normalizeFn = normalizeScale$1(calculator); const maxFn = unsafeMaximum(calculator); return function maximum(...[dineroObjects]) { assert(haveSameCurrency$1(dineroObjects), UNEQUAL_CURRENCIES_MESSAGE); return maxFn(normalizeFn([...dineroObjects])); }; } //#endregion //#region src/core/api/minimum.ts function unsafeMinimum(calculator) { const minFn = minimum$1(calculator); return function minimum(dineroObjects) { const [firstDinero] = dineroObjects; const { currency, scale } = firstDinero.toJSON(); const amount = minFn(dineroObjects.map((subject) => { const { amount: subjectAmount } = subject.toJSON(); return subjectAmount; })); return firstDinero.create({ amount, currency, scale }); }; } function safeMinimum(calculator) { const normalizeFn = normalizeScale$1(calculator); const minFn = unsafeMinimum(calculator); return function minimum(...[dineroObjects]) { assert(haveSameCurrency$1(dineroObjects), UNEQUAL_CURRENCIES_MESSAGE); return minFn(normalizeFn([...dineroObjects])); }; } //#endregion //#region src/core/api/multiply.ts function multiply$1(calculator) { const convertScaleFn = transformScale$1(calculator); const zero = calculator.zero(); return function multiplyFn(...[multiplicand, multiplier]) { const { amount, currency, scale } = multiplicand.toJSON(); const { amount: multiplierAmount, scale: multiplierScale } = getAmountAndScale(multiplier, zero); const newScale = calculator.add(scale, multiplierScale); return convertScaleFn(multiplicand.create({ amount: calculator.multiply(amount, multiplierAmount), currency, scale: newScale }), newScale); }; } //#endregion //#region src/core/api/subtract.ts function unsafeSubtract(calculator) { return function subtract(...[minuend, subtrahend]) { const { amount: minuendAmount, currency, scale } = minuend.toJSON(); const { amount: subtrahendAmount } = subtrahend.toJSON(); const amount = calculator.subtract(minuendAmount, subtrahendAmount); return minuend.create({ amount, currency, scale }); }; } function safeSubtract(calculator) { const normalizeFn = normalizeScale$1(calculator); const subtractFn = unsafeSubtract(calculator); return function subtract(...[minuend, subtrahend]) { assert(haveSameCurrency$1([minuend, subtrahend]), UNEQUAL_CURRENCIES_MESSAGE); const [newMinuend, newSubtrahend] = normalizeFn([minuend, subtrahend]); return subtractFn(newMinuend, newSubtrahend); }; } //#endregion //#region src/core/api/toUnits.ts function toUnits$1(calculator) { const getDivisorsFn = getDivisors(calculator); return function toUnitsFn(...[dineroObject, transformer]) { const { amount, currency, scale } = dineroObject.toJSON(); const { power, integerDivide, modulo } = calculator; const value = getDivisorsFn((isArray(currency.base) ? currency.base : [currency.base]).map((base) => power(base, scale))).reduce((amounts, divisor, index) => { const amountLeft = amounts[index]; const quotient = integerDivide(amountLeft, divisor); const remainder = modulo(amountLeft, divisor); return [ ...amounts.filter((_, i) => i !== index), quotient, remainder ]; }, [amount]); if (!transformer) return value; return transformer({ value, currency }); }; } //#endregion //#region src/core/api/toDecimal.ts function toDecimal$1(calculator) { const toUnitsFn = toUnits$1(calculator); const computeBaseFn = computeBase(calculator); const equalFn = equal$2(calculator); return function toDecimalFn(...[dineroObject, transformer]) { const { currency, scale } = dineroObject.toJSON(); const base = computeBaseFn(currency.base); const zero = calculator.zero(); const ten = new Array(10).fill(null).reduce(calculator.increment, zero); const isMultiBase = isArray(currency.base); const isBaseTen = equalFn(calculator.modulo(base, ten), zero); assert(!isMultiBase && isBaseTen, NON_DECIMAL_CURRENCY_MESSAGE); const units = toUnitsFn(dineroObject); const value = getDecimal(calculator, dineroObject.formatter)(units, scale); if (!transformer) return value; return transformer({ value, currency }); }; } function getDecimal(calculator, formatter) { const absoluteFn = absolute(calculator); const equalFn = equal$2(calculator); const lessThanFn = lessThan$1(calculator); const zero = calculator.zero(); return (units, scale) => { const whole = formatter.toString(units[0]); const fractional = formatter.toString(absoluteFn(units[1])); const scaleNumber = formatter.toNumber(scale); const decimal = `${whole}${scaleNumber > 0 ? `.${fractional.padStart(scaleNumber, "0")}` : ""}`; const leadsWithZero = equalFn(units[0], zero); const isNegative = lessThanFn(units[1], zero); return leadsWithZero && isNegative ? `-${decimal}` : decimal; }; } //#endregion //#region src/core/api/toSnapshot.ts function toSnapshot$1(dineroObject) { return dineroObject.toJSON(); } //#endregion //#region src/core/api/trimScale.ts function trimScale$1(calculator) { const countTrailingZerosFn = countTrailingZeros(calculator); const equalFn = equal$2(calculator); const maximumFn = maximum$1(calculator); const transformScaleFn = transformScale$1(calculator); const computeBaseFn = computeBase(calculator); return function trimScaleFn(...[dineroObject]) { const { amount, currency, scale } = dineroObject.toJSON(); const trailingZerosLength = countTrailingZerosFn(amount, computeBaseFn(currency.base)); const newScale = maximumFn([calculator.subtract(scale, trailingZerosLength), currency.exponent]); if (equalFn(newScale, scale)) return dineroObject; return transformScaleFn(dineroObject, newScale); }; } //#endregion //#region src/api/add.ts /** * Add up the passed Dinero objects. * * @param augend - The Dinero object to add to. * @param addend - The Dinero object to add. * * @returns A new Dinero object. * * @public */ function add(...[augend, addend]) { const { calculator } = augend; return safeAdd(calculator)(augend, addend); } //#endregion //#region src/api/allocate.ts /** * Distribute the amount of a Dinero object across a list of ratios. * * @param dineroObject - The Dinero object to allocate from. * @param ratios - The ratios to allocate the amount to. * * @returns A new Dinero object. * * @public */ function allocate(...[dineroObject, ratios]) { const { calculator } = dineroObject; return safeAllocate(calculator)(dineroObject, ratios); } //#endregion //#region src/api/compare.ts /** * Compare the value of a Dinero object relative to another. * * @param dineroObject - The Dinero object to compare. * @param comparator - The Dinero object to compare to. * * @returns One of -1, 0, or 1 depending on whether the first Dinero object is less than, equal to, or greater than the other. * * @public */ function compare(...[dineroObject, comparator]) { const { calculator } = dineroObject; return safeCompare(calculator)(dineroObject, comparator); } //#endregion //#region src/api/convert.ts /** * Convert a Dinero object to another currency. * * @param dineroObject - The Dinero object to format. * @param newCurrency - The currency to convert to. * @param rates - The rates to convert with. * * @returns A converted Dinero object. * * @public */ function convert(...[dineroObject, newCurrency, rates]) { const { calculator } = dineroObject; return convert$1(calculator)(dineroObject, newCurrency, rates); } //#endregion //#region src/api/equal.ts /** * Check whether the value of a Dinero object is equal to another. * * @param dineroObject - The first Dinero object to compare. * @param comparator - The second Dinero object to compare. * * @returns Whether the Dinero objects are equal. * * @public */ function equal(...[dineroObject, comparator]) { const { calculator } = dineroObject; return equal$1(calculator)(dineroObject, comparator); } //#endregion //#region src/api/greaterThan.ts /** * Check whether the value of a Dinero object is greater than another. * * @param dineroObject - The Dinero object to compare. * @param comparator - The Dinero object to compare to. * * @returns Whether the Dinero to compare is greater than the other. * * @public */ function greaterThan(...[dineroObject, comparator]) { const { calculator } = dineroObject; return safeGreaterThan(calculator)(dineroObject, comparator); } //#endregion //#region src/api/greaterThanOrEqual.ts /** * Check whether the value of a Dinero object is greater than or equal another. * * @param dineroObject - The Dinero object to compare. * @param comparator - The Dinero object to compare to. * * @returns Whether the Dinero to compare is greater than or equal the other. * * @public */ function greaterThanOrEqual(...[dineroObject, comparator]) { const { calculator } = dineroObject; return safeGreaterThanOrEqual(calculator)(dineroObject, comparator); } //#endregion //#region src/api/hasSubUnits.ts /** * Check whether a Dinero object has minor currency units. * * @param dineroObject - The Dinero object to check. * * @returns Whether the Dinero object has minor currency units. * * @public */ function hasSubUnits(...[dineroObject]) { const { calculator } = dineroObject; return hasSubUnits$1(calculator)(dineroObject); } //#endregion //#region src/api/haveSameAmount.ts /** * Check whether a set of Dinero objects have the same amount. * * @param dineroObjects - The Dinero objects to compare. * * @returns Whether the Dinero objects have the same amount. * * @public */ function haveSameAmount(...[dineroObjects]) { const { calculator } = dineroObjects[0]; return haveSameAmount$1(calculator)(dineroObjects); } //#endregion //#region src/api/haveSameCurrency.ts /** * Check whether a set of Dinero objects have the same currency. * * @param dineroObjects - The Dinero objects to compare. * * @returns Whether the Dinero objects have the same currency. * * @public */ const haveSameCurrency = haveSameCurrency$1; //#endregion //#region src/api/isNegative.ts /** * Check whether a Dinero object is negative. * * @param dineroObject - The Dinero object to check. * * @returns Whether the Dinero object is negative. * * @public */ function isNegative(...[dineroObject]) { const { calculator } = dineroObject; return isNegative$1(calculator)(dineroObject); } //#endregion //#region src/api/isPositive.ts /** * Check whether a Dinero object is positive. * * @param dineroObject - The Dinero object to check. * * @returns Whether the Dinero object is positive. * * @public */ function isPositive(...[dineroObject]) { const { calculator } = dineroObject; return isPositive$1(calculator)(dineroObject); } //#endregion //#region src/api/isZero.ts /** * Check whether the value of a Dinero object is zero. * * @param dineroObject - The Dinero object to check. * * @returns Whether the value of a Dinero object is zero. * * @public */ function isZero(...[dineroObject]) { const { calculator } = dineroObject; return isZero$1(calculator)(dineroObject); } //#endregion //#region src/api/lessThan.ts /** * Check whether the value of a Dinero object is lesser than another. * * @param dineroObject - The Dinero object to compare. * @param comparator - The Dinero object to compare to. * * @returns Whether the Dinero to compare is lesser than the other. * * @public */ function lessThan(...[dineroObject, comparator]) { const { calculator } = dineroObject; return safeLessThan(calculator)(dineroObject, comparator); } //#endregion //#region src/api/lessThanOrEqual.ts /** * Check whether the value of a Dinero object is lesser than or equal to another. * * @param dineroObject - The Dinero object to compare. * @param comparator - The Dinero object to compare to. * * @returns Whether the Dinero to compare is lesser than or equal to the other. * * @public */ function lessThanOrEqual(...[dineroObject, comparator]) { const { calculator } = dineroObject; return safeLessThanOrEqual(calculator)(dineroObject, comparator); } //#endregion //#region src/api/maximum.ts /** * Get the greatest of the passed Dinero objects. * * @param dineroObjects - The Dinero objects to maximum. * * @returns A new Dinero object. * * @public */ function maximum(...[dineroObjects]) { const { calculator } = dineroObjects[0]; return safeMaximum(calculator)(dineroObjects); } //#endregion //#region src/api/minimum.ts /** * Get the lowest of the passed Dinero objects. * * @param dineroObjects - The Dinero objects to minimum. * * @returns A new Dinero object. * * @public */ function minimum(...[dineroObjects]) { const { calculator } = dineroObjects[0]; return safeMinimum(calculator)(dineroObjects); } //#endregion //#region src/api/multiply.ts /** * Multiply the passed Dinero object. * * @param multiplicand - The Dinero object to multiply. * @param multiplier - The number to multiply with. * * @returns A new Dinero object. * * @public */ function multiply(...[multiplicand, multiplier]) { const { calculator } = multiplicand; return multiply$1(calculator)(multiplicand, multiplier); } //#endregion //#region src/api/normalizeScale.ts /** * Normalize a set of Dinero objects to the highest scale of the set. * * @param dineroObjects - The Dinero objects to normalize. * * @returns A new set of Dinero objects. * * @public */ function normalizeScale(...[dineroObjects]) { const { calculator } = dineroObjects[0]; return normalizeScale$1(calculator)(dineroObjects); } //#endregion //#region src/api/subtract.ts /** * Subtract the passed Dinero objects. * * @param minuend - The Dinero object to subtract from. * @param subtrahend - The Dinero object to subtract. * * @returns A new Dinero object. * * @public */ function subtract(...[minuend, subtrahend]) { const { calculator } = minuend; return safeSubtract(calculator)(minuend, subtrahend); } //#endregion //#region src/api/toDecimal.ts /** * Get the amount of a Dinero object in decimal form. * * @param dineroObject - The Dinero object to format. * @param transformer - A transformer function. * * @returns The amount in decimal form. * * @public */ function toDecimal(...[dineroObject, transformer]) { const { calculator } = dineroObject; return toDecimal$1(calculator)(dineroObject, transformer); } //#endregion //#region src/api/toSnapshot.ts /** * Get a snapshot of a Dinero object. * * @param dineroObject - The Dinero object to format. * @param transformer - A transformer function. * * @returns A snapshot of the object. * * @public */ const toSnapshot = toSnapshot$1; //#endregion //#region src/api/toUnits.ts /** * Get the amount of a Dinero object in units. * * @param dineroObject - The Dinero object to format. * @param transformer - A transformer function. * * @returns The amount in units. * * @public */ function toUnits(...[dineroObject, transformer]) { const { calculator } = dineroObject; return toUnits$1(calculator)(dineroObject, transformer); } //#endregion //#region src/api/transformScale.ts /** * Transform a Dinero object to a new scale. * * @param dineroObject - The Dinero object to transform. * @param newScale - The new scale. * @param divide - A custom divide function. * * @returns A new Dinero object. * * @public */ function transformScale(...[dineroObject, newScale, divide]) { const { calculator } = dineroObject; return transformScale$1(calculator)(dineroObject, newScale, divide); } //#endregion //#region src/api/trimScale.ts /** * Trim a Dinero object's scale as much as possible, down to the currency exponent. * * @param dineroObject - The Dinero object which scale to trim. * * @returns A new Dinero object. * * @public */ function trimScale(...[dineroObject]) { const { calculator } = dineroObject; return trimScale$1(calculator)(dineroObject); } //#endregion export { halfEven as A, compare as C, halfUp as D, up as E, createDinero as F, assert as I, INVALID_AMOUNT_MESSAGE as L, halfAwayFromZero as M, down as N, halfTowardsZero as O, DineroComparisonOperator as P, INVALID_SCALE_MESSAGE as R, convert as S, add as T, haveSameAmount as _, toDecimal as a, greaterThan as b, multiply as c, lessThanOrEqual as d, lessThan as f, haveSameCurrency as g, isNegative as h, toSnapshot as i, halfDown as j, halfOdd as k, minimum as l, isPositive as m, transformScale as n, subtract as o, isZero as p, toUnits as r, normalizeScale as s, trimScale as t, maximum as u, hasSubUnits as v, allocate as w, equal as x, greaterThanOrEqual as y };