PASS one colour: 1500 g body / 1650 g with allowance / 17 balls
PASS three colours rounded separately: 9 + 5 + 5 = 19, not 17
PASS 2:1 and 200:100 give equivalent normalised proportions
PASS inch fixture 6×6 / 12g and 50×60 = 1000g +10%, exactly 11 balls
PASS exact metric/inch equivalence
PASS 0% allowance and zero-share colour
PASS any positive colour demand needs at least one ball
PASS meaningfully over whole-ball boundary rounds up
PASS demo: 1120g body / 1232g estimate / 7+4+3=14 balls
PASS reject width=0
PASS reject width=-1
PASS reject width=NaN
PASS reject width=Infinity
PASS reject width=-Infinity
PASS reject height=0
PASS reject height=-1
PASS reject height=NaN
PASS reject height=Infinity
PASS reject height=-Infinity
PASS reject swatchWidth=0
PASS reject swatchWidth=-1
PASS reject swatchWidth=NaN
PASS reject swatchWidth=Infinity
PASS reject swatchWidth=-Infinity
PASS reject swatchHeight=0
PASS reject swatchHeight=-1
PASS reject swatchHeight=NaN
PASS reject swatchHeight=Infinity
PASS reject swatchHeight=-Infinity
PASS reject swatchGrams=0
PASS reject swatchGrams=-1
PASS reject swatchGrams=NaN
PASS reject swatchGrams=Infinity
PASS reject swatchGrams=-Infinity
PASS reject ballGrams=0
PASS reject ballGrams=-1
PASS reject ballGrams=NaN
PASS reject ballGrams=Infinity
PASS reject ballGrams=-Infinity
PASS reject allowance=-1
PASS reject allowance=NaN
PASS reject allowance=Infinity
PASS reject allowance=1001
PASS reject invalid proportions []
PASS reject invalid proportions [{"parts":0}]
PASS reject invalid proportions [{"parts":-1}]
PASS reject invalid proportions [{"parts":null}]
PASS reject invalid proportions [{"parts":null}]
PASS reject numeric overflow rather than Infinity
PASS reject unsafe whole-ball count
50 calculation checks passed.
