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
split_even(£10.00, 3)→ £3.34, £3.33, £3.33 ten pounds three ways keeps the penny, larger share firstsplit_even(-£10.00, 3)→ -£3.34, -£3.33, -£3.33 a refund splits as the mirror image, unlike allocate's -333, -333, -334split_even(£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.
def split_even(amount: Money, parts: int) -> List[Money]
| amount | Money | |
| parts | int | how many shares, 1 or more |
| returns | Money[] | 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
from fune.money.split_even import split_even # money.split-even@^1
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import List
from .money_amount import Money, money ← from money.amount ^1.0.0 · built alongside by fune
def split_even(amount: Money, parts: int) -> List[Money]:
"""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.
"""
if isinstance(parts, bool) or not isinstance(parts, int):
raise TypeError("parts must be a whole number, received %r" % (parts,))
if parts < 1:
raise ValueError("parts must be at least 1, received %d" % (parts,))
sign = -1 if amount.minor < 0 else 1
# divmod on the magnitude: Python's floor division would round a negative
# amount the other way and put the odd penny last.
base, leftover = divmod(abs(amount.minor), parts)
return [money(sign * (base + (1 if i < leftover else 0)), amount.currency) for i in range(parts)]Install
fune build
With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the Python 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
The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./money.split-even-1.0.0-python.fune, or fetch it from a terminal with fune pull money.split-even@1.0.0:python.
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.
| Case | Arguments | Expected | |
|---|---|---|---|
| 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
| Case | Arguments | Expected | |
|---|---|---|---|
| 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
| Path | Bytes |
|---|---|
| README.md | 1,052 |
| impl/python.py | 984 |
| impl/rust.rs | 1,510 |
| impl/typescript.ts | 1,079 |
| vectors.json | 4,566 |