Functional Weave
Code in TypeScript

money.split-even

Split an amount into n near-equal parts that add back up to exactly the whole.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 13 tests, run in TypeScript, Python and Rust.

What it does

Splitting 10.00 three ways gives 3.34, 3.33, 3.33: every part is the whole divided by n, rounded down, and the minor units left over go one each to the first parts. The parts always add back up to the amount, never differ by more than one minor unit, and the larger ones come first, so the first instalment or the first diner absorbs the odd penny.

Negative amounts (refunds, credit notes) are split as the mirror image of the positive split: -10.00 three ways is -3.34, -3.33, -3.33. That is where this differs from `money.allocate` with equal ratios, which rounds toward negative infinity and so puts the odd penny on the last part of a negative amount (-3.33, -3.33, -3.34). Refunding a split bill with this capability undoes the original split part for part.

For example

  • splitEven(£10.00, 3) → £3.34, £3.33, £3.33 ten pounds three ways keeps the penny, larger share first
  • splitEven(-£10.00, 3) → -£3.34, -£3.33, -£3.33 a refund splits as the mirror image, unlike allocate's -333, -333, -334
  • splitEven(£10.00, 4) → £2.50, £2.50, £2.50, £2.50 an exact split has no leftover

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

export function splitEven(amount: Money, parts: number): readonly Money[]
amountMoney
partsinthow many shares, 1 or more
returnsMoney[]parts differ by at most one minor unit; the larger ones come first

Your code names it in one line, in the file that uses it

import { splitEven } from "#fune/money.split-even@^1";
impl/typescript.ts · 29 lines · open · 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 Money, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune

/**
 * Split `amount` into `parts` near-equal shares that sum to exactly `amount`.
 *
 * The leftover minor units go one each to the first shares, and a negative
 * amount splits as the mirror image of the positive one, so a refund undoes
 * the original split share for share.
 */
export function splitEven(amount: Money, parts: number): readonly Money[] {
  if (!Number.isInteger(parts)) {
    throw new TypeError(`parts must be a whole number, received ${parts}`);
  }
  if (parts < 1) {
    throw new RangeError(`parts must be at least 1, received ${parts}`);
  }
  const sign = amount.minor < 0 ? -1 : 1;
  const magnitude = Math.abs(amount.minor);
  const base = Math.floor(magnitude / parts);
  const leftover = magnitude - base * parts;

  const shares: Money[] = [];
  for (let i = 0; i < parts; i++) {
    const share = base + (i < leftover ? 1 : 0);
    // `0 * -1` is -0 in JavaScript; keep zero shares plain zero.
    shares.push(money(share === 0 ? 0 : sign * share, amount.currency));
  }
  return shares;
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add money.split-even
Download for TypeScript money.split-even-1.0.0-typescript.fune · 8,789 bytes sha256 04e951dcb6dde9d3bd4b6162939e69af2ac4374d7d388df0cf785489a3e9499b

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./money.split-even-1.0.0-typescript.fune, or fetch it from a terminal with fune pull money.split-even@1.0.0:typescript.

The whole function, every language, is one file too: money.split-even-1.0.0.fune, 11,382 bytes, sha256 30cfb34e4555102a0abc16f49997744bfa2a6bc56f99ff350fcb758d468ee06e. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before money.split-even

after — your function gets the result and the arguments, and returns the final result.

// fune: after money.split-even

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace money.amount in money.split-even

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show money.split-even --steps.

// fune: step money.split-even after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
ten pounds three ways keeps the penny, larger share first £10.00, 3 → £3.34, £3.33, £3.33
a refund splits as the mirror image, unlike allocate's -333, -333, -334 -£10.00, 3 → -£3.34, -£3.33, -£3.33
an exact split has no leftover £10.00, 4 → £2.50, £2.50, £2.50, £2.50
two leftover pennies go to the first two parts £100.00, 6 → £16.67, £16.67, £16.67, £16.67, £16.66, £16.66
fewer pennies than parts leaves some parts at zero £0.02, 3 → £0.01, £0.01, £0.00
a negative amount smaller than the parts -£0.02, 5 → -£0.01, -£0.01, £0.00, £0.00, £0.00
zero splits into zeros £0.00, 3 → £0.00, £0.00, £0.00
one part is the whole amount £9.99, 1 → £9.99
yen splits in whole yen ¥100, 3 → ¥34, ¥33, ¥33
dinar splits in fils 1.000 KWD, 3 → 0.334 KWD, 0.333 KWD, 0.333 KWD
Show the other 3 tests
CaseArgumentsExpected
zero parts is an error £1.00, 0 → error: parts must be at least 1
negative parts is an error £1.00, -2 → error: parts must be at least 1
fractional parts is an error £1.00, 2.5 → error: parts must be a whole number

More from the author

It does not build on `money.allocate` for that reason, and because an even split needs no ratios: it is one division and one remainder.

The minor unit is whatever the currency's is; 100 yen three ways is 34, 33, 33 yen. `parts` must be a whole number of at least 1.

Files

PathBytes
README.md1,052
impl/python.py984
impl/rust.rs1,510
impl/typescript.ts1,079
vectors.json4,566