FAILURE MAP
← Case archive

FA-97431 / Knitting and sewing pattern grading / Open access

Yarn yardage estimator: leftover basis · case 01

An exact two-skein need reports a full skein left over.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

Leftover is computed from the remainder in the last skein, which is wrong when the need is exact.

VERIFIED REPAIR

Leftover is purchased metres minus needed metres.

Unsuccessful approach: Using pre-margin metres counts the safety margin as leftover.

Case contract

grams = garment_area / swatch_area * swatch_g; meters = grams * skein_m/skein_g; add margin_pct as a multiplier (meters*(1+margin/100)). skeins = ceiling(meters/skein_m); leftover = skeins*skein_m - meters. Return [meters, skeins, leftover] with meters and leftover half-up integers. Non-positive swatch area or skein weight -> "error: swatch".

Why this case matters

Designers and knitters estimate how many skeins to buy from a weighed swatch.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(swatch_g, swatch_w_cm, swatch_h_cm, garment_area_cm2, skein_g, skein_m, margin_pct):
    sw_area = Fraction(str(swatch_w_cm)) * Fraction(str(swatch_h_cm))
    if sw_area <= 0 or skein_g <= 0:
        return 'error: swatch'
    grams = Fraction(str(garment_area_cm2)) / sw_area * Fraction(str(swatch_g))
    meters = grams * Fraction(skein_m, skein_g)
    meters = meters * (1 + Fraction(margin_pct, 100))
    skeins = math.ceil(meters / skein_m)
    leftover = skein_m - meters % skein_m
    return [math.floor(meters + Fraction(1, 2)), skeins, math.floor(leftover + Fraction(1, 2))]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['regression: leftover basis', [20, 15, 12, 9000, 50, 400, 10], [8800, 22, 0]],
  ['repair check: leftover basis', [8, 10, 15, 3000, 100, 100, 10], [176, 2, 24]],
  ['generated control 1', [8, 15, 10, 2000, 100, 200, 0], [213, 2, 187]],
  ['generated control 2', [12, 12, 15, 6000, 50, 150, 20], [1440, 10, 60]],
  ['generated control 3', [8, 15, 12, 1500, 50, 125, 0], [167, 2, 83]]],
 [['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['regression: leftover basis', [15, 15, 12, 2000, 50, 150, 20], [600, 4, 0]],
  ['repair check: leftover basis', [20, 10, 10, 2400, 50, 125, 15], [1380, 12, 120]],
  ['generated control 1', [15, 12, 15, 2000, 100, 400, 10], [733, 2, 67]],
  ['generated control 2', [15, 15, 10, 3000, 100, 125, 15], [431, 4, 69]],
  ['generated control 3', [8, 15, 15, 6000, 100, 400, 0], [853, 3, 347]]],
 [['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['regression: leftover basis', [20, 10, 12, 3000, 50, 100, 0], [1000, 10, 0]],
  ['repair check: leftover basis', [15, 15, 10, 2400, 50, 125, 10], [660, 6, 90]],
  ['generated control 1', [8, 10, 15, 2000, 50, 100, 0], [213, 3, 87]],
  ['generated control 2', [15, 10, 12, 4800, 100, 200, 0], [1200, 6, 0]],
  ['generated control 3', [12, 15, 12, 9000, 50, 400, 15], [5520, 14, 80]]],
 [['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['regression: leftover basis', [10, 12, 15, 9000, 100, 100, 20], [600, 6, 0]],
  ['repair check: leftover basis', [10, 12, 12, 4800, 50, 125, 15], [958, 8, 42]],
  ['generated control 1', [15, 10, 15, 9000, 100, 125, 10], [1238, 10, 13]],
  ['generated control 2', [12, 12, 10, 1500, 100, 400, 10], [660, 2, 140]],
  ['generated control 3', [15, 10, 10, 6000, 100, 400, 15], [4140, 11, 260]]],
 [['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['regression: leftover basis', [12, 12, 12, 3000, 100, 150, 20], [450, 3, 0]],
  ['repair check: leftover basis', [20, 15, 10, 3000, 100, 100, 10], [440, 5, 60]],
  ['generated control 1', [8, 10, 12, 2400, 50, 125, 10], [440, 4, 60]],
  ['generated control 2', [15, 12, 12, 9000, 50, 125, 15], [2695, 22, 55]],
  ['generated control 3', [20, 12, 15, 2000, 100, 125, 10], [306, 3, 69]]]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
exact two skeins[400, 2, 200][400, 2, 0]Failed
with margin[1320, 11, 55][1320, 11, 55]Passed
zero swatcherror: swatcherror: swatchPassed
regression: leftover basis[8800, 22, 400][8800, 22, 0]Failed
repair check: leftover basis[176, 2, 24][176, 2, 24]Passed
generated control 1[213, 2, 187][213, 2, 187]Passed
generated control 2[1440, 10, 60][1440, 10, 60]Passed
generated control 3[167, 2, 83][167, 2, 83]Passed

SHA-256 / f34df92576450a20141df2413815e9033191ef67f933c13bee3aa20e77da5aeb

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(swatch_g, swatch_w_cm, swatch_h_cm, garment_area_cm2, skein_g, skein_m, margin_pct):
    sw_area = Fraction(str(swatch_w_cm)) * Fraction(str(swatch_h_cm))
    if sw_area <= 0 or skein_g <= 0:
        return 'error: swatch'
    grams = Fraction(str(garment_area_cm2)) / sw_area * Fraction(str(swatch_g))
    meters = grams * Fraction(skein_m, skein_g)
    meters = meters * (1 + Fraction(margin_pct, 100))
    skeins = math.ceil(meters / skein_m)
    leftover = skeins * skein_m - grams * Fraction(skein_m, skein_g)
    return [math.floor(meters + Fraction(1, 2)), skeins, math.floor(leftover + Fraction(1, 2))]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['regression: leftover basis', [20, 15, 12, 9000, 50, 400, 10], [8800, 22, 0]],
  ['repair check: leftover basis', [8, 10, 15, 3000, 100, 100, 10], [176, 2, 24]],
  ['generated control 1', [8, 15, 10, 2000, 100, 200, 0], [213, 2, 187]],
  ['generated control 2', [12, 12, 15, 6000, 50, 150, 20], [1440, 10, 60]],
  ['generated control 3', [8, 15, 12, 1500, 50, 125, 0], [167, 2, 83]]],
 [['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['regression: leftover basis', [15, 15, 12, 2000, 50, 150, 20], [600, 4, 0]],
  ['repair check: leftover basis', [20, 10, 10, 2400, 50, 125, 15], [1380, 12, 120]],
  ['generated control 1', [15, 12, 15, 2000, 100, 400, 10], [733, 2, 67]],
  ['generated control 2', [15, 15, 10, 3000, 100, 125, 15], [431, 4, 69]],
  ['generated control 3', [8, 15, 15, 6000, 100, 400, 0], [853, 3, 347]]],
 [['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['regression: leftover basis', [20, 10, 12, 3000, 50, 100, 0], [1000, 10, 0]],
  ['repair check: leftover basis', [15, 15, 10, 2400, 50, 125, 10], [660, 6, 90]],
  ['generated control 1', [8, 10, 15, 2000, 50, 100, 0], [213, 3, 87]],
  ['generated control 2', [15, 10, 12, 4800, 100, 200, 0], [1200, 6, 0]],
  ['generated control 3', [12, 15, 12, 9000, 50, 400, 15], [5520, 14, 80]]],
 [['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['regression: leftover basis', [10, 12, 15, 9000, 100, 100, 20], [600, 6, 0]],
  ['repair check: leftover basis', [10, 12, 12, 4800, 50, 125, 15], [958, 8, 42]],
  ['generated control 1', [15, 10, 15, 9000, 100, 125, 10], [1238, 10, 13]],
  ['generated control 2', [12, 12, 10, 1500, 100, 400, 10], [660, 2, 140]],
  ['generated control 3', [15, 10, 10, 6000, 100, 400, 15], [4140, 11, 260]]],
 [['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['regression: leftover basis', [12, 12, 12, 3000, 100, 150, 20], [450, 3, 0]],
  ['repair check: leftover basis', [20, 15, 10, 3000, 100, 100, 10], [440, 5, 60]],
  ['generated control 1', [8, 10, 12, 2400, 50, 125, 10], [440, 4, 60]],
  ['generated control 2', [15, 12, 12, 9000, 50, 125, 15], [2695, 22, 55]],
  ['generated control 3', [20, 12, 15, 2000, 100, 125, 10], [306, 3, 69]]]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
exact two skeins[400, 2, 0][400, 2, 0]Passed
with margin[1320, 11, 175][1320, 11, 55]Failed
zero swatcherror: swatcherror: swatchPassed
regression: leftover basis[8800, 22, 800][8800, 22, 0]Failed
repair check: leftover basis[176, 2, 40][176, 2, 24]Failed
generated control 1[213, 2, 187][213, 2, 187]Passed
generated control 2[1440, 10, 300][1440, 10, 60]Failed
generated control 3[167, 2, 83][167, 2, 83]Passed

SHA-256 / a33e057592205daf522a3551258390361602d3b48382808aaf4e3028d91afed3

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(swatch_g, swatch_w_cm, swatch_h_cm, garment_area_cm2, skein_g, skein_m, margin_pct):
    sw_area = Fraction(str(swatch_w_cm)) * Fraction(str(swatch_h_cm))
    if sw_area <= 0 or skein_g <= 0:
        return 'error: swatch'
    grams = Fraction(str(garment_area_cm2)) / sw_area * Fraction(str(swatch_g))
    meters = grams * Fraction(skein_m, skein_g)
    meters = meters * (1 + Fraction(margin_pct, 100))
    skeins = math.ceil(meters / skein_m)
    leftover = skeins * skein_m - meters
    return [math.floor(meters + Fraction(1, 2)), skeins, math.floor(leftover + Fraction(1, 2))]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['regression: leftover basis', [20, 15, 12, 9000, 50, 400, 10], [8800, 22, 0]],
  ['repair check: leftover basis', [8, 10, 15, 3000, 100, 100, 10], [176, 2, 24]],
  ['generated control 1', [8, 15, 10, 2000, 100, 200, 0], [213, 2, 187]],
  ['generated control 2', [12, 12, 15, 6000, 50, 150, 20], [1440, 10, 60]],
  ['generated control 3', [8, 15, 12, 1500, 50, 125, 0], [167, 2, 83]]],
 [['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['regression: leftover basis', [15, 15, 12, 2000, 50, 150, 20], [600, 4, 0]],
  ['repair check: leftover basis', [20, 10, 10, 2400, 50, 125, 15], [1380, 12, 120]],
  ['generated control 1', [15, 12, 15, 2000, 100, 400, 10], [733, 2, 67]],
  ['generated control 2', [15, 15, 10, 3000, 100, 125, 15], [431, 4, 69]],
  ['generated control 3', [8, 15, 15, 6000, 100, 400, 0], [853, 3, 347]]],
 [['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['regression: leftover basis', [20, 10, 12, 3000, 50, 100, 0], [1000, 10, 0]],
  ['repair check: leftover basis', [15, 15, 10, 2400, 50, 125, 10], [660, 6, 90]],
  ['generated control 1', [8, 10, 15, 2000, 50, 100, 0], [213, 3, 87]],
  ['generated control 2', [15, 10, 12, 4800, 100, 200, 0], [1200, 6, 0]],
  ['generated control 3', [12, 15, 12, 9000, 50, 400, 15], [5520, 14, 80]]],
 [['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['regression: leftover basis', [10, 12, 15, 9000, 100, 100, 20], [600, 6, 0]],
  ['repair check: leftover basis', [10, 12, 12, 4800, 50, 125, 15], [958, 8, 42]],
  ['generated control 1', [15, 10, 15, 9000, 100, 125, 10], [1238, 10, 13]],
  ['generated control 2', [12, 12, 10, 1500, 100, 400, 10], [660, 2, 140]],
  ['generated control 3', [15, 10, 10, 6000, 100, 400, 15], [4140, 11, 260]]],
 [['with margin', [12, 10, 12, 4800, 50, 125, 10], [1320, 11, 55]],
  ['zero swatch', [10, 0, 10, 2000, 100, 200, 0], 'error: swatch'],
  ['exact two skeins', [10, 10, 10, 2000, 100, 200, 0], [400, 2, 0]],
  ['regression: leftover basis', [12, 12, 12, 3000, 100, 150, 20], [450, 3, 0]],
  ['repair check: leftover basis', [20, 15, 10, 3000, 100, 100, 10], [440, 5, 60]],
  ['generated control 1', [8, 10, 12, 2400, 50, 125, 10], [440, 4, 60]],
  ['generated control 2', [15, 12, 12, 9000, 50, 125, 15], [2695, 22, 55]],
  ['generated control 3', [20, 12, 15, 2000, 100, 125, 10], [306, 3, 69]]]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
exact two skeins[400, 2, 0][400, 2, 0]Passed
with margin[1320, 11, 55][1320, 11, 55]Passed
zero swatcherror: swatcherror: swatchPassed
regression: leftover basis[8800, 22, 0][8800, 22, 0]Passed
repair check: leftover basis[176, 2, 24][176, 2, 24]Passed
generated control 1[213, 2, 187][213, 2, 187]Passed
generated control 2[1440, 10, 60][1440, 10, 60]Passed
generated control 3[167, 2, 83][167, 2, 83]Passed

SHA-256 / b348949b766dade239aa2e9334c211d94849b081d8000cfbc34a7c30e02d5ade

Verification & scope

A deterministic toy contract stated in full here; it is a bounded teaching model, not an authoritative reference or standards implementation. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:52:32.148881+00:00.

Case digest / 253a7e42c2add4f5eaabf5bfaab39d81f1f20200cf26be1ae5df480e5dd758a3