Math
The math module provides two sub-modules: distribution for proportional value allocation, and rounding for precision-aware number rounding.
Distribution
Import
import { prorate } from '@open-kerno/commons/math/distribution';
prorate
Distributes an amount across a list of items proportionally, based on each item's weight property. The last item always receives the remaining amount to avoid floating-point drift.
Types
interface Allocable {
weight: number;
}
type Prorated<T> = T & {
proratedValue: number;
percentage: number;
};
interface ProrateOptions<T extends Allocable> {
items: T[];
amountToDistribute: number;
throwOnError?: boolean;
}
Signature
function prorate<T extends Allocable>(options: ProrateOptions<T>): Prorated<T>[]
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
items | T[] | — | Array of items, each must have a weight: number field |
amountToDistribute | number | — | The total amount to distribute |
throwOnError | boolean | false | If true, throws when items are empty, total is zero, or any weight is negative |
Returns
An array of the original items, each extended with:
proratedValue— the allocated amount for that itempercentage— the proportion used (between 0 and 1)
Returns an empty array when the input is invalid and throwOnError is false.
Example
import { prorate } from '@open-kerno/commons/math/distribution';
const items = [
{ id: 'A', weight: 30 },
{ id: 'B', weight: 20 },
{ id: 'C', weight: 50 },
];
const result = prorate({ items, amountToDistribute: 1000 });
// result:
// [
// { id: 'A', weight: 30, percentage: 0.3, proratedValue: 300 },
// { id: 'B', weight: 20, percentage: 0.2, proratedValue: 200 },
// { id: 'C', weight: 50, percentage: 0.5, proratedValue: 500 },
// ]
Edge Cases
// Empty array → returns []
prorate({ items: [], amountToDistribute: 100 }); // []
// Total weight is zero → returns [] (or throws if throwOnError is true)
prorate({ items: [{ weight: 0 }], amountToDistribute: 100 }); // []
// Negative weights → returns [] (or throws if throwOnError is true)
prorate({ items: [{ weight: -10 }], amountToDistribute: 100 }); // []
proratePennies
Distributes an amount across a list of items proportionally, based on each item's weight property, guaranteeing the results sum exactly to amountToDistribute at a given decimal precision. Unlike prorate, which lets the last item absorb any floating-point drift outright, proratePennies rounds every item to whole units first and, if that rounding would push the last item negative, reclaims the deficit one unit at a time from the items with the largest rounding error — so no item ever goes negative.
Types
interface Weighted {
weight: number;
}
type PennyAllocated<T> = T & {
proratedValue: number;
percentage: number;
};
interface AllocatePenniesOptions<T extends Weighted> {
amountToDistribute: number;
items: T[];
precision?: number;
throwOnError?: boolean;
}
Signature
function proratePennies<T extends Weighted>(options: AllocatePenniesOptions<T>): PennyAllocated<T>[]
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
items | T[] | — | Array of items, each must have a weight: number field |
amountToDistribute | number | — | The total amount to distribute |
precision | number | 12 | Number of decimal places the allocated amounts are rounded to |
throwOnError | boolean | false | If true, throws when items are empty, total weight is zero, or any weight is negative |
Returns
An array of the original items, each extended with:
proratedValue— the allocated amount for that item, rounded toprecisiondecimal placespercentage— the proportion used (between 0 and 1)
Returns an empty array when the input is invalid and throwOnError is false.
Example
import { proratePennies } from '@open-kerno/commons/math/distribution';
const items = [
{ id: 'A', weight: 1 },
{ id: 'B', weight: 1 },
{ id: 'C', weight: 0 },
];
const result = proratePennies({ items, amountToDistribute: 0.05, precision: 2 });
// result:
// [
// { id: 'A', weight: 1, percentage: 0.5, proratedValue: 0.02 },
// { id: 'B', weight: 1, percentage: 0.5, proratedValue: 0.03 },
// { id: 'C', weight: 0, percentage: 0, proratedValue: 0 },
// ]
Edge Cases
// Empty array → returns []
proratePennies({ items: [], amountToDistribute: 100 }); // []
// Total weight is zero → returns [] (or throws if throwOnError is true)
proratePennies({ items: [{ weight: 0 }], amountToDistribute: 100 }); // []
// Negative weights → returns [] (or throws if throwOnError is true)
proratePennies({ items: [{ weight: -10 }], amountToDistribute: 100 }); // []
Rounding
Import
import { roundWithPrecision } from '@open-kerno/commons/math/rounding';
roundWithPrecision
Rounds a number to a specific number of decimal places, using Number.EPSILON to avoid floating-point inaccuracies.
Signature
function roundWithPrecision(options: RoundingOptions): number
Type
interface RoundingOptions {
value: number;
precision: number;
}
Parameters
| Parameter | Type | Description |
|---|---|---|
value | number | The number to round |
precision | number | The number of decimal places |
Example
import { roundWithPrecision } from '@open-kerno/commons/math/rounding';
roundWithPrecision({ value: 1.005, precision: 2 }); // 1.01
roundWithPrecision({ value: 3.14159, precision: 3 }); // 3.142
roundWithPrecision({ value: 100, precision: 0 }); // 100