from pathlib import Path
from fractions import Fraction as F
from math import gcd
from functools import reduce
import json,hashlib,re

OUT=Path(__file__).resolve().parent;ROOT=OUT.parents[2]
SRC=ROOT/'c932_c1131/evidence/sources/File52c_latest.md'
raw=SRC.read_text();lines=raw.splitlines();sha=hashlib.sha256(SRC.read_bytes()).hexdigest()
assert sha=='a5ea84562101158b60d0cf296765d6eff38e7a2abda4e74ad1b353dfd13b9530'
questions=OUT/'questions_before_calculation.json';assert questions.exists()
keypath=ROOT/'c932_c1131/prep/key_constraints/sourcepacket.json'
key=json.loads(keypath.read_text())
keys={k:F(*v) for k,v in key['literal_inputs']['known_keys'].items()}
measures=key['literal_inputs']['calendar_measures']

def rows_between(start,end):
    section=raw.split(start,1)[1].split(end,1)[0]
    return [[c.strip() for c in s.split('|')[1:-1]] for s in section.splitlines() if re.match(r'^\|\s*\d+\s*\|',s)]
nat=rows_between('### A.2 Nativity-family inverses','### A.3')
con=rows_between('### A.3 Conquest-family inverses','### A.4')
original_endpoint_pairs=[{'number':int(n[0]),'Nativity':int(n[2]),'Conquest':int(c[2])} for n,c in zip(nat,con)]
paths={'regular_Flood':[1400,1050,1650],'regular_Noah':[1400,1650,1050],'regular_Flood_Noah':[1400,1050,600,1050],'cumulative_primary':[1400,3430,9170]}

def I(n):
    # Every call below receives an original source component, never an output.
    scale=1
    while n%10==0:n//=10;scale*=10
    rev=0
    while n:n,digit=divmod(n,10);rev=10*rev+digit
    return scale*rev

packet={'status':'Frozen source-based interface inputs; no numbered action','question_manifest_sha256':hashlib.sha256(questions.read_bytes()).hexdigest(),'primary_source':{'path':str(SRC),'sha256':sha},'key_packet':{'path':str(keypath),'sha256':hashlib.sha256(keypath.read_bytes()).hexdigest()},'literal_paths_from_Nativity':paths,'path_locators':{'section':'File52c3.4','lines':[396,403],'text':'\n'.join(lines[395:403])},'Nativity_BC':6,'Creation_pillar_BC':14726,'Creation_pillar_status':'inherited derived endpoint, used only as a declared next-stage anchor','known_keys':key['literal_inputs']['known_keys'],'calendar_measures':measures,'calibration_excerpt':next(x for x in key['excerpts'] if x['source']=='Strategy'),'original_endpoint_pairs':original_endpoint_pairs,'pair_source_locators':{'Nativity':[2161,2192],'Conquest':[2200,2231]},'inherited_cumulative_branches':{'status':'derived in C940, not independent source primitives','one_pass_component_sums':[10620,11610],'original_total':12600,'register':'two three-digit cores, each exactly one trailing zero'},'nine_member_endpoint_literals':[1731,5526,2421,9321,3226,2076,1616,8516,1846],'nine_member_locator':{'section':'3.7.3','lines':[519,529]},'guards':['No target enumeration or frequency tally','No new literal chronological route','No second decimal inversion','New P/J path images are conditional calibration comparisons','Common-pivot impossibility is a symbolic necessary-condition result, not a pivot search']}
(OUT/'inputs.json').write_text(json.dumps(packet,indent=2)+'\n')

field={}
for name,parts in paths.items():
    evaluated=[I(x) for x in parts]
    prefixes=[];running=0
    for x in evaluated:running+=x;prefixes.append(running)
    scaled=[keys['E']*x for x in evaluated]
    scaled_prefixes=[keys['E']*x for x in prefixes]
    cuts=[i+1 for i,x in enumerate(prefixes) if x%23==0]
    assert sum(evaluated)==14720 and sum(scaled)==16000
    assert cuts==[len(parts)]
    assert all(x.denominator==23 for x in scaled_prefixes[:-1])
    field[name]={'original_source_parts':parts,'single_pass_parts':evaluated,'original_one_pass_prefixes':prefixes,'component_residues_mod23':[x%23 for x in evaluated],'prefix_residues_mod23':[x%23 for x in prefixes],'E_parts':[str(x) for x in scaled],'E_prefix_durations':[str(x) for x in scaled_prefixes],'permitted_integer_cut_indices':cuts,'finest_integral_consecutive_block':'whole numerical path; all source boundaries remain in metadata'}

branches=[]
for total in [10620,11610]:
    original_tail=1400;single_pass_tail=I(original_tail)
    S=total+single_pass_tail;out=keys['E']*S
    branches.append({'inherited_one_pass_component_sum':total,'single_pass_tail':single_pass_tail,'completed_duration':S,'residue_mod23':S%23,'E_duration':str(out),'E_integral':out.denominator==1,'matches_regular_primary':total==10620})
assert all(x['E_integral']==x['matches_regular_primary'] for x in branches)

S=14720;K=F(8400,23)
calibration={name:{'duration':str(k*S),'calendar_days':measures[name],'volume':str(k*S*measures[name]),'integral_year_count':(k*S).denominator==1} for name,k in keys.items()}
assert {x['volume'] for x in calibration.values()}=={'5376000'}
assert K*S==5376000
allocations={}
for target in ['P','J']:
    f=(keys[target]-1)/(keys['E']-1);selected=f*S
    allocations[target]={'required_fraction':str(f),'required_selected_duration':str(selected),'integer_selected_span_possible':selected.denominator==1,'whole_source_component_subset_possible':selected.denominator==1,'status':'No source cut supplied; a fractional split would add a premise'}
    assert selected.denominator!=1

long_branch_compatibility={}
for delta in [990,1980]:
    rem={name:delta%k.denominator for name,k in keys.items()}
    assert all(x!=0 for x in rem.values())
    long_branch_compatibility[str(delta)]={'gap_residues_by_Key_denominator':rem,'any_common_integer_pivot_can_make_both_integral':False}

all_endpoints=[p[role] for p in original_endpoint_pairs for role in ['Nativity','Conquest']]
g=reduce(gcd,(x-all_endpoints[0] for x in all_endpoints))
nine=packet['nine_member_endpoint_literals'];g9=reduce(gcd,(x-nine[0] for x in nine))
assert g==5 and g9==115
reference=1731;anchor=14726;image=anchor+keys['E']*(reference-anchor)
assert image==601
meshes={'source_full_difference_gcd':g,'full_E_difference_mesh':str(keys['E']*g),'source_nine_difference_gcd':g9,'nine_E_difference_mesh':str(keys['E']*g9),'shared_existing_reference_source':reference,'shared_existing_E_image':str(image),'full_affine_lattice':'601+(125/23)Z','nine_affine_lattice':'601+125Z','status':'Difference-lattice description only; no new target outcomes computed'}

rawrow=[F(100),F(10),F(1)];revrow=[F(1),F(10),F(100)]
postrows={k:[str(f*v) for v in revrow] for k,f in keys.items()}
minor=rawrow[0]*revrow[1]-rawrow[1]*revrow[0]
assert minor==990
result={'status':'PASS','completed_path_prefix_field':field,'branch_equivalence':branches,'calibration_transfer':calibration,'allocation_transfer':allocations,'long_core_gap_obstruction':long_branch_compatibility,'short_core_symbolic_gap_conditions':{'two_digit_Conquest_core':'delta=90(14+a−b−10c); shared E/P integer-pivot compatibility requires23|(14+a−b−10c), while J requires299|(14+a−b−10c)','one_digit_Conquest_core':'delta=990(1−c); E/P require23|(1−c), while J requires299|(1−c)','retained_zero':'n200 delta0 imposes no difference obstruction; n410 delta7200 fails all three difference divisibility tests','placement_caution':'Difference divisibility permits a common residue class; a source-appointed pivot still needs the correct residue.'},'mesh_comparison':meshes,'measurement_dependence':{'raw_row':[str(v) for v in rawrow],'reverse_row':[str(v) for v in revrow],'uniform_Key_after_reverse_rows':postrows,'rank_with_all_rows':2,'nonzero_minor':str(minor),'reason':'Every added Key row is a scalar multiple of the existing reverse row, so none restores component allocation or order.'},'checks':{'all_four_existing_path_totals':True,'no_proper_integral_E_prefix':True,'branch_selectors_equivalent_in_fixed_domain':True,'same_exact_day_volume':True,'whole_component_allocation_unavailable':True,'long_core_common_pivot_obstruction':True,'exact_difference_meshes':True,'uniform_postmeasurement_rank_unchanged':True}}
(OUT/'calculations.json').write_text(json.dumps(result,indent=2)+'\n')
print(json.dumps({'status':'PASS','input_sha256':hashlib.sha256((OUT/'inputs.json').read_bytes()).hexdigest(),'path_prefix_residues':{k:v['prefix_residues_mod23'] for k,v in field.items()},'calibrated_durations':{k:v['duration'] for k,v in calibration.items()},'branch_equivalence':branches}))
