Functional Weave
Code in Rust

manufacturing.variance

Standard costing variances: material price and usage, labour rate and efficiency, favourable or adverse.

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

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

What it does

The four basic standard-costing variances for a period, flexed to the output actually produced:

material price = actual material cost - AQ x SP material usage = (AQ - SQ) x SP SQ = standard quantity per unit x actual output labour rate = actual labour cost - AH x SR labour efficiency = (AH - SH) x SR SH = standard hours per unit x actual output

For example

  • cost_variances(standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up) → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £36,000.00, actual cost £36,610.00 the textbook case: 2 kg at £3 and 3 h at £10 a unit, 1000 made with 2200 kg for £6160 and 2900 h for £30450
  • cost_variances(standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, half-up) → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.17, actual cost £342.00 fractional standards: 1/3 kg at £1.25 and 1/4 h at £11.50 a unit, 100 made using 33.5 kg; each flexed cost is rounded once and the variances still add up
  • cost_variances(standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, down) → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.16, actual cost £342.00 the same rounded down: 4187.5p and 4166.67p become 4187p and 4166p

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 cost_variances(material: &MaterialActual, labour: &LabourActual, actual_output: i64, mode: &str) -> CostVariances
materialMaterialActualstandard and actual material for the period
labourLabourActualstandard and actual labour for the period
actual_outputintunits actually produced, which the standard is flexed to
modeRoundingModehow each flexed cost becomes whole minor units
returnsCostVariances

The types it declares, generated into your project

/// Material standard per unit of output, and what was actually used and paid.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaterialActual {
    /// per unit of material (per kg, per metre)
    pub standard_price: Money,
    /// material per unit of output
    pub standard_quantity: Rational,
    /// material actually used
    pub actual_quantity: Rational,
    /// what the material used actually cost
    pub actual_cost: Money,
}

/// Labour standard per unit of output, and what was actually worked and paid.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LabourActual {
    /// per hour
    pub standard_rate: Money,
    /// hours per unit of output
    pub standard_hours: Rational,
    /// hours actually worked
    pub actual_hours: Rational,
    /// what those hours actually cost
    pub actual_cost: Money,
}

/// One variance: how big, and which way.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Variance {
    /// never negative
    pub amount: Money,
    pub effect: String,
}

// VarianceEffect is a string in Rust, one of: "favourable", "adverse", "none".
// Parameters take it as &str and results hold it as String.

/// The four variances, their total, and the costs they reconcile.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CostVariances {
    /// actual cost - actual quantity x standard price
    pub material_price: Variance,
    /// (actual quantity - standard quantity for actual output) x standard price
    pub material_usage: Variance,
    /// actual cost - actual hours x standard rate
    pub labour_rate: Variance,
    /// (actual hours - standard hours for actual output) x standard rate
    pub labour_efficiency: Variance,
    /// actualCost - standardCost; always the sum of the four
    pub total: Variance,
    /// standard material and labour for the actual output
    pub standard_cost: Money,
    /// actual material and labour cost
    pub actual_cost: Money,
}

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

fune!(manufacturing.variance@^1);  // then call cost_variances(…)
impl/rust.rs · 111 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_rational::{multiply_rational, rational, rational_from_value, rational_to_integer, Rational};  ← from math.rational ^1.0.0 · built alongside by fune
use super::money_amount::{assert_same_currency, money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune

fn not_negative(name: &str, value: &Rational) -> Rational {
    let r = rational(value.numerator, value.denominator);
    if r.numerator < 0 {
        panic!("{} must not be negative", name);
    }
    r
}

fn cost_of(name: &str, value: &Money, unit: &Money) -> i64 {
    assert_same_currency(unit, value);
    if value.minor < 0 {
        panic!("{} must not be negative, received {}", name, value.minor);
    }
    value.minor
}

fn variance(signed: i64, currency: &str) -> Variance {
    let effect = if signed > 0 {
        "adverse"
    } else if signed < 0 {
        "favourable"
    } else {
        "none"
    };
    Variance {
        amount: money(signed.abs(), currency),
        effect: effect.to_string(),
    }
}

/// Material price and usage, labour rate and efficiency variances, flexed to
/// actual output. The four flexed costs are each rounded once and every
/// variance is a difference of them, so the variances always sum exactly.
///
/// # Panics
/// Panics on negative output, prices, rates, quantities, hours or costs, mixed
/// currencies, or an unknown rounding mode.
pub fn cost_variances(material: &MaterialActual, labour: &LabourActual, actual_output: i64, mode: &str) -> CostVariances {
    if !(0..=(1i64 << 53) - 1).contains(&actual_output) {
        panic!("actualOutput must be a whole number, not negative, received {}", actual_output);
    }
    let unit = &material.standard_price;
    let currency = unit.currency.as_str();
    let sp = cost_of("material standardPrice", &material.standard_price, unit);
    let mat_actual = cost_of("material actualCost", &material.actual_cost, unit);
    let sr = cost_of("labour standardRate", &labour.standard_rate, unit);
    let lab_actual = cost_of("labour actualCost", &labour.actual_cost, unit);
    let sq = not_negative("material standardQuantity", &material.standard_quantity);
    let aq = not_negative("material actualQuantity", &material.actual_quantity);
    let sh = not_negative("labour standardHours", &labour.standard_hours);
    let ah = not_negative("labour actualHours", &labour.actual_hours);
    let output = rational(actual_output, 1);

    let actual_at_standard = rational_to_integer(&multiply_rational(&aq, &rational(sp, 1)), mode);
    let standard_material = rational_to_integer(&multiply_rational(&multiply_rational(&sq, &output), &rational(sp, 1)), mode);
    let hours_at_standard = rational_to_integer(&multiply_rational(&ah, &rational(sr, 1)), mode);
    let standard_labour = rational_to_integer(&multiply_rational(&multiply_rational(&sh, &output), &rational(sr, 1)), mode);

    let standard_cost = standard_material + standard_labour;
    let actual_cost = mat_actual + lab_actual;
    CostVariances {
        material_price: variance(mat_actual - actual_at_standard, currency),
        material_usage: variance(actual_at_standard - standard_material, currency),
        labour_rate: variance(lab_actual - hours_at_standard, currency),
        labour_efficiency: variance(hours_at_standard - standard_labour, currency),
        total: variance(actual_cost - standard_cost, currency),
        standard_cost: money(standard_cost, currency),
        actual_cost: money(actual_cost, currency),
    }
}

fn variance_to_value(v: &Variance) -> Value {
    Value::obj(vec![("amount", money_to_value(&v.amount)), ("effect", Value::str(&v.effect))])
}

pub fn cost_variances_to_value(c: &CostVariances) -> Value {
    Value::obj(vec![
        ("materialPrice", variance_to_value(&c.material_price)),
        ("materialUsage", variance_to_value(&c.material_usage)),
        ("labourRate", variance_to_value(&c.labour_rate)),
        ("labourEfficiency", variance_to_value(&c.labour_efficiency)),
        ("total", variance_to_value(&c.total)),
        ("standardCost", money_to_value(&c.standard_cost)),
        ("actualCost", money_to_value(&c.actual_cost)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let m = &args[0];
    let l = &args[1];
    if let Value::Float(f) = &args[2] {
        panic!("actualOutput must be a whole number, not negative, received {}", f);
    }
    let material = MaterialActual {
        standard_price: money_from_value(m.get("standardPrice")),
        standard_quantity: rational_from_value(m.get("standardQuantity")),
        actual_quantity: rational_from_value(m.get("actualQuantity")),
        actual_cost: money_from_value(m.get("actualCost")),
    };
    let labour = LabourActual {
        standard_rate: money_from_value(l.get("standardRate")),
        standard_hours: rational_from_value(l.get("standardHours")),
        actual_hours: rational_from_value(l.get("actualHours")),
        actual_cost: money_from_value(l.get("actualCost")),
    };
    cost_variances_to_value(&cost_variances(&material, &labour, args[2].as_i64(), args[3].as_str()))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 3 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 manufacturing.variance
Download for Rust manufacturing.variance-1.0.0-rust.fune · 26,392 bytes sha256 35ab3f623e92c06558818f94e4dee96ba7f5085f65a394029efbb7c9d4758480

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

The whole function, every language, is one file too: manufacturing.variance-1.0.0.fune, 33,167 bytes, sha256 891beb9988a990d3a686a6a4e0993bc51814dd5d685d087f0e5ebdcb2c68455c. 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 manufacturing.variance

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

// fune: after manufacturing.variance

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.rational in manufacturing.variance
// fune: replace math.round-div in manufacturing.variance
// fune: replace money.amount in manufacturing.variance

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 manufacturing.variance --steps.

// fune: step manufacturing.variance 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
the textbook case: 2 kg at £3 and 3 h at £10 a unit, 1000 made with 2200 kg for £6160 and 2900 h for £30450 standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £36,000.00, actual cost £36,610.00
fractional standards: 1/3 kg at £1.25 and 1/4 h at £11.50 a unit, 100 made using 33.5 kg; each flexed cost is rounded once and the variances still add up standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, half-up → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.17, actual cost £342.00
the same rounded down: 4187.5p and 4166.67p become 4187p and 4166p standard price £1.25, standard quantity …, actual quantity …, actual cost £42.00, standard rate £11.50, standard hours …, actual hours …, actual cost £300.00, 100, down → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £329.16, actual cost £342.00
exactly on standard: every variance is none standard price £5.00, standard quantity …, actual quantity …, actual cost £50.00, standard rate £20.00, standard hours …, actual hours …, actual cost £100.00, 10, half-up → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £150.00, actual cost £150.00
nothing made: everything used is usage and efficiency variance standard price £5.00, standard quantity …, actual quantity …, actual cost £20.00, standard rate £20.00, standard hours …, actual hours …, actual cost £40.00, 0, half-up → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £0.00, actual cost £60.00
dear material used sparingly and cheap labour working fast: favourable overall standard price £3.00, standard quantity …, actual quantity …, actual cost £6,045.00, standard rate £10.00, standard hours …, actual hours …, actual cost £27,440.00, 1,000, half-up → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost £36,000.00, actual cost £33,485.00
hours in minutes as a fraction: 90 minutes is 3/2 hours standard price €10.00, standard quantity …, actual quantity …, actual cost €10.00, standard rate €24.00, standard hours …, actual hours …, actual cost €36.00, 1, half-up → material price …, material usage …, labour rate …, labour efficiency …, total …, standard cost €34.00, actual cost €46.00
costs in two currencies are an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate €10.00, standard hours …, actual hours …, actual cost €30,450.00, 1,000, half-up → error: currency mismatch
negative output is an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, -1, half-up → error: actualOutput must be a whole number, not negative
fractional output is an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 2.5, half-up → error: actualOutput must be a whole number, not negative
Show the other 5 tests
CaseArgumentsExpected
a negative actual quantity is an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up → error: material actualQuantity must not be negative
negative standard hours are an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up → error: labour standardHours must not be negative
a negative standard price is an error standard price -£3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half… → error: material standardPrice must not be negative
a negative actual labour cost is an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost -£0.01, 1,000, half-up → error: labour actualCost must not be negative
a zero denominator is an error standard price £3.00, standard quantity …, actual quantity …, actual cost £6,160.00, standard rate £10.00, standard hours …, actual hours …, actual cost £30,450.00, 1,000, half-up → error: denominator must not be zero

More from the author

A variance is **adverse** when actual cost is higher than standard and **favourable** when it is lower; `amount` is always the size and `effect` the direction (`none` for exactly zero). The textbook case: a standard of 2 kg at £3/kg and 3 hours at £10/h, 1,000 units made using 2,200 kg for £6,160 and 2,900 hours for £30,450, gives material price £440 F, usage £600 A, labour rate £1,450 A, efficiency £1,000 F, and £610 A in total.

**The variances always add up.** Quantities and hours are exact fractions (`Rational`: 1/3 kg, 1/4 hour), so the flexed costs (AQ x SP, SQ x SP, AH x SR, SH x SR) are exact fractions of a minor unit. Each of those four is rounded once by `mode`, and every variance is a difference of whole amounts, so price plus usage is exactly actual material cost less standard material cost, and the four together are exactly `actualCost - standardCost`. Rounding each variance on its own would leave a penny or two unexplained.

**Price variance on usage.** The material price variance is computed on the quantity used, not the quantity purchased; where stock is held at standard cost and the price variance is taken on purchase, pass purchases in a separate call with the purchase quantity as `actualQuantity`.

**Rules.** All money must be in the currency of `material.standardPrice`. Prices, rates, quantities, hours and costs may not be negative; output is a whole number, not negative. With zero output, all material and labour used is usage and efficiency variance. Overhead variances are not included.

Source: CIMA Official Terminology (2005), "variance" entries for direct material price, direct material usage, direct labour rate and direct labour efficiency variances; the same definitions appear in ACCA Performance Management (PM) study material on standard costing.

Files

PathBytes
README.md2,242
impl/python.py3,284
impl/rust.rs5,042
impl/typescript.ts3,265
vectors.json12,134