Functional Weave
Code in TypeScript

construction.brick-count

Bricks and mortar for a wall, openings subtracted, for UK standard 215 x 102.5 x 65 mm bricks with 10 mm joints.

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

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

What it does

Bricks and mortar for a wall of UK standard bricks, with doors and windows subtracted.

## The brick

For example

  • brickCount(1, 1, , 1, 0%) → net area square metres 1, bricks 60, mortar cubic metres 0.018 one square metre of half-brick wall is the trade's 60 bricks
  • brickCount(10, 2.4, openings ×2, 1, 5%) → net area square metres 20.85, bricks 1,298, mortar cubic metres 0.387 a wall with a door and a window, 5 percent wastage
  • brickCount(1, 1, , 2, 0%) → net area square metres 1, bricks 119, mortar cubic metres 0.046 a one-brick wall is 119 bricks a square metre, not 120, and adds a collar joint

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 brickCount(wallLength: number, wallHeight: number, openings: readonly Opening[], skins: number, wastageBasisPoints: number): BrickCount
wallLengthfloatmetres, more than 0 and at most 1000; taken to the nearest millimetre
wallHeightfloatmetres, more than 0 and at most 100; taken to the nearest millimetre
openingsOpening[]doors and windows to leave out; may be empty
skinsint1 for a half-brick wall, 2 for a one-brick wall; 1 to 4
wastageBasisPointsintcutting and breakage, applied to bricks and mortar, 500 = 5%; 0 to 10000
returnsBrickCount

The types it declares, generated into your project

/** A door, window or other hole in the wall, in metres. */
export interface Opening {
  readonly width: number;
  readonly height: number;
}

/** What to order for the wall. */
export interface BrickCount {
  /** the wall face less its openings */
  readonly netAreaSquareMetres: number;
  /** including wastage, rounded up to whole bricks */
  readonly bricks: number;
  /** including wastage, rounded up to the whole litre (3 dp) */
  readonly mortarCubicMetres: number;
}

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

import { brickCount } from "#fune/construction.brick-count@^1";
impl/typescript.ts · 65 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 { roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { roundFloat } from "./math_round_float.ts";  ← from math.round-float ^1.0.0 · built alongside by fune
import { type BrickCount, type Opening } from "./construction_brick_count_types.ts";

// A 215 x 65 mm brick face plus a 10 mm joint is 225 x 75 mm of wall.
const FACE_MM2 = 225 * 75;
// Mortar per mm2 of wall face, in mm3: each skin fills the joints around a
// brick, (16875 - 215 x 65) / 16875 of the face, 102.5 mm deep, which is
// 2378/135 mm; each collar joint between skins is 10 mm = 1350/135 mm.
const SKIN_MORTAR = 2378;
const COLLAR_MORTAR = 1350;
const MORTAR_DIVISOR = 135;
const MM3_PER_LITRE = 1000000;

function millimetres(value: number, message: string, max: number): number {
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > max) {
    throw new RangeError(`${message} must be a finite number greater than 0 and at most ${max} metres, received ${value}`);
  }
  return Math.round(roundFloat(value, 3) * 1000);
}

/**
 * Bricks and mortar for a wall of UK standard bricks, openings subtracted.
 *
 * The count is the net face area over one brick's 225 x 75 mm share of it,
 * 59.26 per m2 per skin (the trade's "60 a metre"), times the skins, plus
 * wastage, rounded up. Mortar is the joint volume that geometry implies.
 * Everything is whole millimetres and integer arithmetic.
 */
export function brickCount(
  wallLength: number,
  wallHeight: number,
  openings: readonly Opening[],
  skins: number,
  wastageBasisPoints: number
): BrickCount {
  const length = millimetres(wallLength, "wallLength", 1000);
  const height = millimetres(wallHeight, "wallHeight", 100);
  if (!Number.isInteger(skins) || skins < 1 || skins > 4) {
    throw new RangeError(`skins must be a whole number from 1 to 4, received ${skins}`);
  }
  if (!Number.isInteger(wastageBasisPoints) || wastageBasisPoints < 0 || wastageBasisPoints > 10000) {
    throw new RangeError(`wastageBasisPoints must be a whole number from 0 to 10000, received ${wastageBasisPoints}`);
  }
  let net = length * height;
  openings.forEach((opening, i) => {
    const w = millimetres(opening.width, `opening ${i + 1}: width`, 1000);
    const h = millimetres(opening.height, `opening ${i + 1}: height`, 1000);
    net -= w * h;
  });
  if (net < 0) {
    throw new RangeError("openings are larger than the wall");
  }

  const bricks = roundDiv(net * skins * (10000 + wastageBasisPoints), FACE_MM2 * 10000, "up");
  const perMm2 = skins * SKIN_MORTAR + (skins - 1) * COLLAR_MORTAR;
  // Up to the whole litre first, then the wastage, so no product passes 2^53.
  const litres = roundDiv(net * perMm2, MORTAR_DIVISOR * MM3_PER_LITRE, "up");
  const mortar = roundDiv(litres * (10000 + wastageBasisPoints), 10000, "up");
  return {
    netAreaSquareMetres: net / 1000000,
    bricks,
    mortarCubicMetres: mortar / 1000,
  };
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 2 dependencies, 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 construction.brick-count
Download for TypeScript construction.brick-count-1.0.0-typescript.fune · 12,767 bytes sha256 a74df82c0a7f44c6fb3ec5247abfbd93c9c30057e13bad485a711e7becf9be48

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

The whole function, every language, is one file too: construction.brick-count-1.0.0.fune, 19,874 bytes, sha256 4c520d53927a6f63aa67ea0907846e0323782667d63ef704067c362ffc799a8f. 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 construction.brick-count

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

// fune: after construction.brick-count

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 math.round-div in construction.brick-count
// fune: replace math.round-float in construction.brick-count

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 construction.brick-count --steps.

// fune: step construction.brick-count 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
one square metre of half-brick wall is the trade's 60 bricks 1, 1, , 1, 0% → net area square metres 1, bricks 60, mortar cubic metres 0.018
a wall with a door and a window, 5 percent wastage 10, 2.4, openings ×2, 1, 5% → net area square metres 20.85, bricks 1,298, mortar cubic metres 0.387
a one-brick wall is 119 bricks a square metre, not 120, and adds a collar joint 1, 1, , 2, 0% → net area square metres 1, bricks 119, mortar cubic metres 0.046
exactly 100 bricks plus 10 percent is exactly 110 2.25, 0.75, , 1, 10% → net area square metres 1.688, bricks 110, mortar cubic metres 0.033
a garden wall that comes out to exactly 336 bricks with wastage 6, 0.9, , 1, 5% → net area square metres 5.4, bricks 336, mortar cubic metres 0.101
three skins 1, 1, , 3, 0% → net area square metres 1, bricks 178, mortar cubic metres 0.073
dimensions are taken to the nearest millimetre 3, 1, , 1, 0% → net area square metres 3, bricks 178, mortar cubic metres 0.053
an opening the size of the wall leaves nothing to build 2, 1, openings ×1, 1, 5% → net area square metres 0, bricks 0, mortar cubic metres 0
openings larger than the wall are an error 2, 1, openings ×1, 1, 0% → error: openings are larger than the wall
no skins is an error 1, 1, , 0, 0% → error: skins must be a whole number from 1 to 4
Show the other 7 tests
CaseArgumentsExpected
five skins is an error 1, 1, , 5, 0% → error: skins must be a whole number from 1 to 4
a fractional skin count is an error 1, 1, , 1.5, 0% → error: skins must be a whole number from 1 to 4
a zero-length wall is an error 0, 1, , 1, 0% → error: wallLength must be a finite number greater than 0 and at most 1000 metres
a wall over 100 metres high is an error 5, 101, , 1, 0% → error: wallHeight must be a finite number greater than 0 and at most 100 metres
a negative opening is an error 5, 2, openings ×1, 1, 0% → error: opening 1: width must be a finite number greater than 0 and at most 1000 metres
negative wastage is an error 1, 1, , 1, -0.01% → error: wastageBasisPoints must be a whole number from 0 to 10000
fractional wastage is an error 1, 1, , 1, 0.025% → error: wastageBasisPoints must be a whole number from 0 to 10000

More from the author

A UK metric brick is 215 × 102.5 × 65 mm (work size). Laid with 10 mm joints it occupies 225 × 75 mm of wall face (the coordinating size), so one square metre of a half-brick (single-skin) wall takes 1,000,000 / 16,875 = 59.26 bricks, which the trade calls "60 a metre". A one-brick wall (two skins) is twice that, 118.5, so 119 for a square metre rather than the 120 that doubling the rounded figure gives.

## How it is worked out

1. Wall and openings, given in metres, are taken to the nearest millimetre; the net face area is the wall less the openings, in whole mm². 2. **Bricks** = net area × skins × (1 + wastage) ÷ 16,875 mm², rounded up. 3. **Mortar** is the joint volume the geometry implies, not a rule of thumb: per skin, the joints are (16,875 − 215 × 65) / 16,875 of the face, 102.5 mm deep, which is 17.6 litres per m²; each collar joint between skins adds a 10 mm layer, 10 litres per m². Rounded up to a whole litre, then the wastage added and rounded up to a litre again.

All arithmetic is in integers after step 1, so it does not suffer from floating-point ceilings.

## What it does not count

Frogs and perforations (a frog-up brick takes more mortar; add it to the wastage), cavity walls built with a block inner leaf (count the brick skin here, blocks separately), ties, lintels, piers and returns, and the bond: English and Flemish bond one-brick walls use the same number of bricks per m² as two stretcher skins, but headers change how many facing bricks you need.

Limits: length up to 1000 m, height up to 100 m, 1 to 4 skins, wastage 0 to 10,000 basis points. Openings larger than the wall are an error; openings equal to it leave nothing to build.

## Sources

- BS EN 771-1 (clay masonry units) and the UK standard format of 215 × 102.5 × 65 mm with a 225 × 112.5 × 75 mm coordinating size; see "Brick sizes", Designing Buildings Wiki, https://www.designingbuildings.co.uk/wiki/Brick_sizes - The 60 bricks per m² single-skin rule as used in the UK trade, e.g. https://www.imperialbricks.co.uk/guidance/bricks-per-m2-guide/

Files

PathBytes
README.md2,227
impl/python.py3,006
impl/rust.rs3,842
impl/typescript.ts2,842
vectors.json4,191