Functional Weave
Code in Rust

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

  • brick_count(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
  • brick_count(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
  • brick_count(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.

pub fn brick_count(wall_length: f64, wall_height: f64, openings: &[Opening], skins: i64, wastage_basis_points: i64) -> BrickCount
wall_lengthfloatmetres, more than 0 and at most 1000; taken to the nearest millimetre
wall_heightfloatmetres, 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
wastage_basis_pointsintcutting 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.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Opening {
    pub width: f64,
    pub height: f64,
}

/// What to order for the wall.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BrickCount {
    /// the wall face less its openings
    pub net_area_square_metres: f64,
    /// including wastage, rounded up to whole bricks
    pub bricks: i64,
    /// including wastage, rounded up to the whole litre (3 dp)
    pub mortar_cubic_metres: f64,
}

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

fune!(construction.brick-count@^1);  // then call brick_count(…)
impl/rust.rs · 99 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.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_div::round_div;  ← from math.round-div ^1.0.0 · built alongside by fune
use super::math_round_float::round_float;  ← from math.round-float ^1.0.0 · built alongside by fune

// A 215 x 65 mm brick face plus a 10 mm joint is 225 x 75 mm of wall.
const FACE_MM2: i64 = 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: i64 = 2378;
const COLLAR_MORTAR: i64 = 1350;
const MORTAR_DIVISOR: i64 = 135;
const MM3_PER_LITRE: i64 = 1_000_000;

fn millimetres(value: f64, message: &str, max: i64) -> i64 {
    if !value.is_finite() || value <= 0.0 || value > max as f64 {
        panic!(
            "{} must be a finite number greater than 0 and at most {} metres, received {}",
            message, max, value
        );
    }
    (round_float(value, 3) * 1000.0).round() as i64
}

/// 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.
///
/// # Panics
/// Panics on a dimension, skin count or wastage out of range, or openings
/// larger than the wall.
pub fn brick_count(
    wall_length: f64,
    wall_height: f64,
    openings: &[Opening],
    skins: i64,
    wastage_basis_points: i64,
) -> BrickCount {
    let length = millimetres(wall_length, "wallLength", 1000);
    let height = millimetres(wall_height, "wallHeight", 100);
    if !(1..=4).contains(&skins) {
        panic!("skins must be a whole number from 1 to 4, received {}", skins);
    }
    if !(0..=10000).contains(&wastage_basis_points) {
        panic!("wastageBasisPoints must be a whole number from 0 to 10000, received {}", wastage_basis_points);
    }
    let mut net = length * height;
    for (i, opening) in openings.iter().enumerate() {
        let w = millimetres(opening.width, &format!("opening {}: width", i + 1), 1000);
        let h = millimetres(opening.height, &format!("opening {}: height", i + 1), 1000);
        net -= w * h;
    }
    if net < 0 {
        panic!("openings are larger than the wall");
    }

    let bricks = round_div(net * skins * (10000 + wastage_basis_points), FACE_MM2 * 10000, "up");
    let per_mm2 = skins * SKIN_MORTAR + (skins - 1) * COLLAR_MORTAR;
    // Up to the whole litre first, then the wastage, so no product passes 2^53.
    let litres = round_div(net * per_mm2, MORTAR_DIVISOR * MM3_PER_LITRE, "up");
    let mortar = round_div(litres * (10000 + wastage_basis_points), 10000, "up");
    BrickCount {
        net_area_square_metres: net as f64 / 1_000_000.0,
        bricks,
        mortar_cubic_metres: mortar as f64 / 1000.0,
    }
}

pub fn brick_count_to_value(b: &BrickCount) -> Value {
    Value::obj(vec![
        ("netAreaSquareMetres", Value::Float(b.net_area_square_metres)),
        ("bricks", Value::Int(b.bricks)),
        ("mortarCubicMetres", Value::Float(b.mortar_cubic_metres)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let openings: Vec<Opening> = args[2]
        .as_arr()
        .iter()
        .map(|v| Opening { width: v.get("width").as_f64(), height: v.get("height").as_f64() })
        .collect();
    if let Value::Float(f) = &args[3] {
        panic!("skins must be a whole number from 1 to 4, received {}", f);
    }
    if let Value::Float(f) = &args[4] {
        panic!("wastageBasisPoints must be a whole number from 0 to 10000, received {}", f);
    }
    brick_count_to_value(&brick_count(
        args[0].as_f64(),
        args[1].as_f64(),
        &openings,
        args[3].as_i64(),
        args[4].as_i64(),
    ))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Rust 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. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add construction.brick-count
Download for Rust construction.brick-count-1.0.0-rust.fune · 13,805 bytes sha256 4a400693842246a78a7687d7bd239e9bd32cea04dd3c012ff6ee9f42e1f1cf63

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

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