import csv, importlib.util, json
from pathlib import Path
root=Path(__file__).resolve().parent
spec=importlib.util.spec_from_file_location('frontier', 'scripts/exchange-frontier.py')
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
profile=json.loads((root/'current-profile-exchanges.json').read_text())
epochs={int(g['dimensions']['phase'])&0x7fffffff:int(g['dimensions']['epoch']) for g in profile['groups']}
rows=json.loads((root/'plots/results.json').read_text())
for r in rows:r['widths']='selected'
paired=json.loads((root/'paired-plots/results.json').read_text())
for r in paired:r['widths']='paired';r['configuration']+='-paired'
rows+=paired
for r in rows:
 r['executions']=27 if epochs[r['phase']]==1 else 1
 r['scope']='layer ×27' if r['executions']==27 else 'once'
out=root/'combined';out.mkdir(exist_ok=True)
manifest={'ordinary':json.loads((root/'plots/manifest.json').read_text()),'paired':json.loads((root/'paired-plots/manifest.json').read_text())}
(out/'manifest.json').write_text(json.dumps(manifest,indent=2)+'\n')
m.render(rows,out)
index=out/'index.html'
index.write_text(index.read_text().replace('<h1>Exchange scheduling frontiers</h1>', '<h1>Materialized ViT exchange frontiers</h1><p>Resident 27-layer ViT · batch 1 · FP8 weights · 80 phases · 1,539 measurements. Phases 7–40 execute in each layer; the others run once.</p>'))
summary=[]
for phase in sorted({r['phase'] for r in rows}):
 points=[r for r in rows if r['phase']==phase];base=m.compact_reference(points)
 caps=['maximum_row_bytes','total_row_bytes']
 free=min((r for r in points if all(r[k]<=base[k] for k in caps)),key=lambda r:(r['cycles'],r['maximum_row_bytes'],r['total_row_bytes']))
 small=min((r for r in points if r['maximum_row_bytes']<=base['maximum_row_bytes']+256),key=lambda r:r['cycles'])
 for kind,best in [('no_growth',free),('max_row_plus_256',small)]:
  summary.append({'phase':phase,'scope':base['scope'],'comparison':kind,'baseline':base['configuration'],'candidate':best['configuration'],
    'before_cycles':base['cycles'],'after_cycles':best['cycles'],'saved_cycles':base['cycles']-best['cycles'],
    'weighted_saved_cycles':(base['cycles']-best['cycles'])*base['executions'],
    'before_max_bytes':base['maximum_row_bytes'],'after_max_bytes':best['maximum_row_bytes'],
    'before_total_bytes':base['total_row_bytes'],'after_total_bytes':best['total_row_bytes']})
summary.sort(key=lambda r:(r['comparison'],-r['weighted_saved_cycles']))
(root/'comparisons.json').write_text(json.dumps(summary,indent=2)+'\n')
with (root/'comparisons.csv').open('w') as f:
 w=csv.DictWriter(f,fieldnames=list(summary[0]));w.writeheader();w.writerows(summary)
for kind in ['no_growth','max_row_plus_256']:
 print(kind)
 for r in [r for r in summary if r['comparison']==kind][:12]:print(r)
 print('Weighted scheduled cycles saved:',sum(r['weighted_saved_cycles'] for r in summary if r['comparison']==kind))
