FA-97351 / Knitting and sewing pattern grading / Open access
Gauge-based cast-on calculator: repeat rounding · case 01
A 22-stitch need in a 4-stitch rib casts on 20 instead of 24 whenever the raw count is past half a repeat.
ROOT CAUSE
Stitches are floored to a whole repeat instead of rounded to the nearest repeat.
VERIFIED REPAIR
Round raw stitches to the nearest multiple of the repeat.
Unsuccessful approach: Rounding to the nearest stitch ignores the repeat, so the rib does not close.
Case contract
Target width = width + ease (ease may be negative). Raw stitches = target * gauge / 10 (gauge per 10 cm). Body stitches = nearest multiple of repeat, half-up in repeat units, at least one repeat. Cast on = body + 2*edge (selvedge each side). Actual body width = body*10/gauge, half-up to 0.1 cm. Non-positive target, gauge or repeat returns an error string.
Why this case matters
Knitting patterns and calculators convert a finished measurement and gauge into a stitch count that fits the stitch pattern repeat.
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(width_cm, sts_per_10cm, repeat, edge, ease_cm):
w = Fraction(str(width_cm)) + Fraction(str(ease_cm))
g = Fraction(str(sts_per_10cm))
if w <= 0 or g <= 0 or repeat < 1:
return 'error: bad measurements'
raw = w * g / 10
body = repeat * math.floor(raw / repeat)
body = max(repeat, body)
total = body + 2 * edge
actual = body * 10 / g
return [total, math.floor(actual * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['regression: repeat rounding', [50, '24', 8, 0, 5], [136, 56.7]],
['repair check: repeat rounding', [10, '22', 12, 2, 7.5], [40, 16.4]],
['generated control 1', [10, '18', 8, 2, -5], [12, 4.4]],
['generated control 2', [40, '18', 2, 3, 10], [96, 50.0]],
['generated control 3', [40, '20', 4, 2, 5], [96, 46.0]]],
[['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['regression: repeat rounding', [52.5, '18', 1, 0, 5], [104, 57.8]],
['repair check: repeat rounding', [10, '22', 12, 2, 5], [40, 16.4]],
['generated control 1', [45, '24', 4, 2, 10], [136, 55.0]],
['generated control 2', [50, '27.5', 2, 1, 7.5], [160, 57.5]],
['generated control 3', [3, '27.5', 1, 2, 5], [26, 8.0]]],
[['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['regression: repeat rounding', [3, '22', 6, 0, 10], [30, 13.6]],
['repair check: repeat rounding', [50, '18', 4, 0, 0], [92, 51.1]],
['generated control 1', [40, '20', 2, 3, -5], [76, 35.0]],
['generated control 2', [40, '24', 6, 0, 7.5], [114, 47.5]],
['generated control 3', [50, '20', 8, 2, -2], [100, 48.0]]],
[['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['regression: repeat rounding', [3, '18', 1, 1, -2], [4, 1.1]],
['repair check: repeat rounding', [52.5, '27.5', 6, 2, 10], [178, 63.3]],
['generated control 1', [45, '27.5', 4, 0, -2], [120, 43.6]],
['generated control 2', [45, '20', 12, 0, 10], [108, 54.0]],
['generated control 3', [3, '18', 6, 0, -2], [6, 3.3]]],
[['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['regression: repeat rounding', [30, '20', 4, 1, -5], [54, 26.0]],
['repair check: repeat rounding', [3, '27.5', 8, 2, 10], [36, 11.6]],
['generated control 1', [50, '16', 2, 3, 0], [86, 50.0]],
['generated control 2', [50, '20', 2, 1, 5], [112, 55.0]],
['generated control 3', [45, '24', 12, 2, 0], [112, 45.0]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| plain stockinette | [100, 50.0] | [100, 50.0] | Passed |
| rib with selvedges | [110, 49.1] | [114, 50.9] | Failed |
| narrow strap one repeat | [12, 5.0] | [12, 5.0] | Passed |
| regression: repeat rounding | [128, 53.3] | [136, 56.7] | Failed |
| repair check: repeat rounding | [40, 16.4] | [40, 16.4] | Passed |
| generated control 1 | [12, 4.4] | [12, 4.4] | Passed |
| generated control 2 | [96, 50.0] | [96, 50.0] | Passed |
| generated control 3 | [92, 44.0] | [96, 46.0] | Failed |
SHA-256 / f4f1fc999fbe9600fbcfafe2672a43b2dc1722fbf11e30989fe94c5ad97a8953
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(width_cm, sts_per_10cm, repeat, edge, ease_cm):
w = Fraction(str(width_cm)) + Fraction(str(ease_cm))
g = Fraction(str(sts_per_10cm))
if w <= 0 or g <= 0 or repeat < 1:
return 'error: bad measurements'
raw = w * g / 10
body = math.floor(raw + Fraction(1, 2))
body = max(repeat, body)
total = body + 2 * edge
actual = body * 10 / g
return [total, math.floor(actual * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['regression: repeat rounding', [50, '24', 8, 0, 5], [136, 56.7]],
['repair check: repeat rounding', [10, '22', 12, 2, 7.5], [40, 16.4]],
['generated control 1', [10, '18', 8, 2, -5], [12, 4.4]],
['generated control 2', [40, '18', 2, 3, 10], [96, 50.0]],
['generated control 3', [40, '20', 4, 2, 5], [96, 46.0]]],
[['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['regression: repeat rounding', [52.5, '18', 1, 0, 5], [104, 57.8]],
['repair check: repeat rounding', [10, '22', 12, 2, 5], [40, 16.4]],
['generated control 1', [45, '24', 4, 2, 10], [136, 55.0]],
['generated control 2', [50, '27.5', 2, 1, 7.5], [160, 57.5]],
['generated control 3', [3, '27.5', 1, 2, 5], [26, 8.0]]],
[['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['regression: repeat rounding', [3, '22', 6, 0, 10], [30, 13.6]],
['repair check: repeat rounding', [50, '18', 4, 0, 0], [92, 51.1]],
['generated control 1', [40, '20', 2, 3, -5], [76, 35.0]],
['generated control 2', [40, '24', 6, 0, 7.5], [114, 47.5]],
['generated control 3', [50, '20', 8, 2, -2], [100, 48.0]]],
[['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['regression: repeat rounding', [3, '18', 1, 1, -2], [4, 1.1]],
['repair check: repeat rounding', [52.5, '27.5', 6, 2, 10], [178, 63.3]],
['generated control 1', [45, '27.5', 4, 0, -2], [120, 43.6]],
['generated control 2', [45, '20', 12, 0, 10], [108, 54.0]],
['generated control 3', [3, '18', 6, 0, -2], [6, 3.3]]],
[['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['regression: repeat rounding', [30, '20', 4, 1, -5], [54, 26.0]],
['repair check: repeat rounding', [3, '27.5', 8, 2, 10], [36, 11.6]],
['generated control 1', [50, '16', 2, 3, 0], [86, 50.0]],
['generated control 2', [50, '20', 2, 1, 5], [112, 55.0]],
['generated control 3', [45, '24', 12, 2, 0], [112, 45.0]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| plain stockinette | [100, 50.0] | [100, 50.0] | Passed |
| rib with selvedges | [112, 50.0] | [114, 50.9] | Failed |
| narrow strap one repeat | [12, 5.0] | [12, 5.0] | Passed |
| regression: repeat rounding | [132, 55.0] | [136, 56.7] | Failed |
| repair check: repeat rounding | [43, 17.7] | [40, 16.4] | Failed |
| generated control 1 | [13, 5.0] | [12, 4.4] | Failed |
| generated control 2 | [96, 50.0] | [96, 50.0] | Passed |
| generated control 3 | [94, 45.0] | [96, 46.0] | Failed |
SHA-256 / 946b6f2ca2834c2d714d49c52666c222a8be4a729f6d187cc2e71577c79e5502
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(width_cm, sts_per_10cm, repeat, edge, ease_cm):
w = Fraction(str(width_cm)) + Fraction(str(ease_cm))
g = Fraction(str(sts_per_10cm))
if w <= 0 or g <= 0 or repeat < 1:
return 'error: bad measurements'
raw = w * g / 10
body = repeat * math.floor(raw / repeat + Fraction(1, 2))
body = max(repeat, body)
total = body + 2 * edge
actual = body * 10 / g
return [total, math.floor(actual * 10 + Fraction(1, 2)) / 10]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['regression: repeat rounding', [50, '24', 8, 0, 5], [136, 56.7]],
['repair check: repeat rounding', [10, '22', 12, 2, 7.5], [40, 16.4]],
['generated control 1', [10, '18', 8, 2, -5], [12, 4.4]],
['generated control 2', [40, '18', 2, 3, 10], [96, 50.0]],
['generated control 3', [40, '20', 4, 2, 5], [96, 46.0]]],
[['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['regression: repeat rounding', [52.5, '18', 1, 0, 5], [104, 57.8]],
['repair check: repeat rounding', [10, '22', 12, 2, 5], [40, 16.4]],
['generated control 1', [45, '24', 4, 2, 10], [136, 55.0]],
['generated control 2', [50, '27.5', 2, 1, 7.5], [160, 57.5]],
['generated control 3', [3, '27.5', 1, 2, 5], [26, 8.0]]],
[['narrow strap one repeat', [3, '16', 8, 2, 0], [12, 5.0]],
['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['regression: repeat rounding', [3, '22', 6, 0, 10], [30, 13.6]],
['repair check: repeat rounding', [50, '18', 4, 0, 0], [92, 51.1]],
['generated control 1', [40, '20', 2, 3, -5], [76, 35.0]],
['generated control 2', [40, '24', 6, 0, 7.5], [114, 47.5]],
['generated control 3', [50, '20', 8, 2, -2], [100, 48.0]]],
[['negative ease', [40, '24', 2, 1, -5], [86, 35.0]],
['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['regression: repeat rounding', [3, '18', 1, 1, -2], [4, 1.1]],
['repair check: repeat rounding', [52.5, '27.5', 6, 2, 10], [178, 63.3]],
['generated control 1', [45, '27.5', 4, 0, -2], [120, 43.6]],
['generated control 2', [45, '20', 12, 0, 10], [108, 54.0]],
['generated control 3', [3, '18', 6, 0, -2], [6, 3.3]]],
[['invalid width', [3, '20', 2, 0, -5], 'error: bad measurements'],
['plain stockinette', [50, '20', 1, 0, 0], [100, 50.0]],
['rib with selvedges', [45, '22', 4, 1, 5], [114, 50.9]],
['regression: repeat rounding', [30, '20', 4, 1, -5], [54, 26.0]],
['repair check: repeat rounding', [3, '27.5', 8, 2, 10], [36, 11.6]],
['generated control 1', [50, '16', 2, 3, 0], [86, 50.0]],
['generated control 2', [50, '20', 2, 1, 5], [112, 55.0]],
['generated control 3', [45, '24', 12, 2, 0], [112, 45.0]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| plain stockinette | [100, 50.0] | [100, 50.0] | Passed |
| rib with selvedges | [114, 50.9] | [114, 50.9] | Passed |
| narrow strap one repeat | [12, 5.0] | [12, 5.0] | Passed |
| regression: repeat rounding | [136, 56.7] | [136, 56.7] | Passed |
| repair check: repeat rounding | [40, 16.4] | [40, 16.4] | Passed |
| generated control 1 | [12, 4.4] | [12, 4.4] | Passed |
| generated control 2 | [96, 50.0] | [96, 50.0] | Passed |
| generated control 3 | [96, 46.0] | [96, 46.0] | Passed |
SHA-256 / bdff7c2f3972582c72bd35ed8fa24512ce6008586da24c0776345e688b4b02e4
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:31.369694+00:00.
Case digest / f6fe607b42787ef15bfbaf1797a336dff2204b434723b90b9d0700200578cb3c