from fractions import Fraction as Q def run(load,check): reg=load('C286_DATA.json')['target_register'];rows=[];pivots=[] for f in load('C349_DATA.json')['fields']: z=f['records'];N=z['N'];g=N['gear'] for role in ['Noah birth','Shem birth','Flood start','Noah death','Shem death']: pp=[x for x in reg if x['tradition']=='SP' and x['gear']==g and x['role']==role and x['Cainan']==0 and x['Terah60']==0 and x['full430']==1 and (role=='Flood start' or x['rail']=='primary')] check('one source pivot '+str(f['i'])+role,len(pp)==1);p=pp[0];t=p['center']-N['center'];pivots.append({'i':f['i'],'record':p,'translation':t}) for u,v in f['maps']['E_N'].items(): x=z[u]['center']+t;y=z[v]['center']+t xx=[q for q in reg if q['center']==x];yy=[q for q in reg if q['center']==y] check('affine covariance '+str(f['i'])+role+u,Q(p['center'])+Q(25,23)*(x-p['center'])==y) rows.append({'i':f['i'],'pivot':p,'translation':t,'original_labels':[u,v],'input':x,'output':y,'input_matches':xx,'output_matches':yy,'complete':bool(xx and yy),'nontrivial':u!=v}) matches=[x for x in rows if x['complete'] and x['nontrivial']] expected={(i,t,u) for i in [3,5] for t,u in [(0,'M'),(0,'R'),(0,'H'),(-600,'M'),(-1100,'M')]} check('bounded complete matches', {(x['i'],x['translation'],x['original_labels'][0]) for x in matches}==expected) check('forty controls and ten nontrivial complete',len(rows)==40 and len(matches)==10) last=[x for x in matches if x['translation']==-1100] for r in last: check('Shem death to Noah death source roles '+str(r['i']),any(x['tradition']=='MT' and x['role']=='Shem death' and x['Cainan']==1 and x['Terah60']==1 and x['full430']==1 and x['gear']==r['pivot']['gear'] for x in r['input_matches']) and any(x['tradition']=='MT' and x['role']=='Noah death' and x['Cainan']==0 and x['Terah60']==0 and x['full430']==1 and x['gear']==r['pivot']['gear'] for x in r['output_matches'])) check('final460500 relation '+str(r['i']),r['pivot']['center']-r['input']==460 and r['pivot']['center']-r['output']==500) return {'step':'C356','pivots':pivots,'rows':rows,'complete_nontrivial':matches,'counts':{'pivot_choices':len(pivots),'trials':len(rows),'complete_nontrivial':len(matches),'additional_nontrivial_beyond_untranslated':sum(x['translation']!=0 for x in matches)},'bounded_translation_set':[0,-500,-600,-950,-1100],'new_source_nodes':False,'claim':'Three lifecycle placements support the same460→500 arrow; the other E arrows retain only their original placements in this pivot census.'}