from research import *
s=begin(1208,'Explain the change from exact to rounded cap reduction','Why does total488 become485 under row rounding?',{},['C1204','C1143','C1131 inherited cap-round result'])
def Q(x):return 5*((x+2)//5)
d=json.loads((ROOT/'model/cap_round_branch_information.json').read_text());out=[]
for i,x in enumerate(d['rows']):
 e=x['L']-x['y'];epsL=Q(x['L'])-x['L'];epsY=Q(x['y'])-x['y'];out.append({'index':i,'exact_excess':e,'baseline_round_error':epsL,'capped_round_error':epsY,'rounded_reduction':Q(x['L'])-Q(x['y']),'identity_rhs':e+epsL-epsY})
a=artifact('model/cap_round_loss_identity.json',json.dumps({'identity':'Q(L)-Q(y)=(L-y)+(Q(L)-L)-(Q(y)-y)','rows':out,'totals':{k:sum(x[k] for x in out) for k in ['exact_excess','baseline_round_error','capped_round_error','rounded_reduction']}},indent=2)+'\n')
finish(s,{'loss_identity':a,'exact_total':sum(x['exact_excess'] for x in out),'rounded_total':sum(x['rounded_reduction'] for x in out)},'Rounded reduction equals exact removed life plus the difference of the two rounding errors. The three SP cap losses115/249/124 become115/250/120, explaining488→485 without a second cap rule.','Check what commutes when both the cap and its input are rounded consistently.',{'identity_all':all(x['rounded_reduction']==x['identity_rhs'] for x in out),'totals':sum(x['exact_excess'] for x in out)==488 and sum(x['rounded_reduction'] for x in out)==485})
