Skip to main content

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

ParameterTypeDefaultDescription
itemsT[]Array of items, each must have a weight: number field
amountToDistributenumberThe total amount to distribute
throwOnErrorbooleanfalseIf 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 item
  • percentage — 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

ParameterTypeDefaultDescription
itemsT[]Array of items, each must have a weight: number field
amountToDistributenumberThe total amount to distribute
precisionnumber12Number of decimal places the allocated amounts are rounded to
throwOnErrorbooleanfalseIf 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 to precision decimal places
  • percentage — 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

ParameterTypeDescription
valuenumberThe number to round
precisionnumberThe 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