from pathlib import Path
import csv,json,sys,hashlib,math
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
P=Path(__file__).resolve().parent;b=json.loads((P/'stored-baseline.json').read_text());rows=b['network'];prefs=b['preferences']
assert len(rows)==24 and len({(r['topology'],r['removed_edge_fraction'],r['probability_per_one_time_attempt'])for r in rows})==24
for r in rows:
 assert 1/120<=r['mean_coverage']<=1 and 0<=r['fraction_reaching_90pct']<=1
 assert r['coverage_block_bootstrap_low']<=r['mean_coverage']<=r['coverage_block_bootstrap_high']
 if r['fraction_reaching_90pct']==0:assert r['mean_of_successful_block_mean_rounds']is None
 else:assert 1<=r['mean_of_successful_block_mean_rounds']<=20
assert math.isclose(sum(r['pooled_match']for r in prefs)/5,b['pooled_mean'],abs_tol=1e-15)
assert math.isclose(sum(r['regional_match']for r in prefs)/5,b['regional_mean'],abs_tol=1e-15)
for name,data in [('network-cells.csv',rows),('preference-regions.csv',prefs)]:
 with(P/name).open('w',newline='',encoding='utf8')as f:w=csv.DictWriter(f,fieldnames=list(data[0]));w.writeheader();w.writerows(data)
plt.rcParams.update({'font.size':10,'axes.spines.top':False,'axes.spines.right':False,'savefig.dpi':180})
fig,axes=plt.subplots(1,3,figsize=(12,4.8),layout='constrained')
for ax,drop in zip(axes,(0,.1,.3)):
 for top,label,style in [('local_ring','Local ring','-'),('degree_preserving_randomized','Degree-preserving swaps','--')]:
  rr=[r for r in rows if r['topology']==top and r['removed_edge_fraction']==drop];p=[r['probability_per_one_time_attempt']for r in rr];y=np.array([r['mean_coverage']for r in rr]);lo=np.array([r['coverage_block_bootstrap_low']for r in rr]);hi=np.array([r['coverage_block_bootstrap_high']for r in rr]);ax.errorbar(p,y,yerr=[y-lo,hi-y],fmt='o',ls=style,capsize=3,label=label)
 ax.set(xlabel='Probability per eligible one-time attempt',ylabel='Mean informed fraction',title=f'{drop:.0%} of edges removed',ylim=(0,1.03));ax.grid(alpha=.2);ax.legend(fontsize=8)
fig.suptitle('Stored one-chance cascades: 120 fictional nodes, at most 20 rounds\nIntervals resample 20 block means; they do not describe a real population')
for e in ('png','svg','pdf'):fig.savefig(P/f'network-reach.{e}')
plt.close(fig)
fig,ax=plt.subplots(figsize=(9,5),layout='constrained');x=np.arange(5)
ax.bar(x-.18,[r['pooled_match']for r in prefs],.35,label='Pooled recommendation',color='#2865a4');ax.bar(x+.18,[r['regional_match']for r in prefs],.35,label='Regional recommendations',color='#ac799e',hatch='//');ax.axhline(1,color='gray',ls=':',label='No change: match = 1 by definition');ax.set(xticks=x,xticklabels=[f'Region {i+1}'for i in x],ylabel='Fraction still matching initial preference',ylim=(0,1.15),title='Stored one-shot recommendation example\nInvented preferences; agreement is not welfare, fairness or improvement');ax.legend(fontsize=9);ax.grid(axis='y',alpha=.2)
for e in ('png','svg','pdf'):fig.savefig(P/f'preference-agreement.{e}')
plt.close(fig)
(P/'validation.json').write_text(json.dumps(dict(all_pass=True,network_cells=24,preference_regions=5,stored_only=True,raw_simulations_rerun=False,unique_cells=True,bounds_nulls_and_equal_region_mean_checks=True),indent=2)+'\n')
(P/'study.json').write_text(json.dumps(dict(runtime=dict(python=sys.version,numpy=np.__version__,matplotlib=matplotlib.__version__),generator_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),baseline_sha256=hashlib.sha256((P/'stored-baseline.json').read_bytes()).hexdigest(),source_sha256=b['source_sha256'],metadata_correction='Probability per eligible one-time attempt; one-chance cascade, not repeated transmission',bootstrap='Stored 5000 percentile resamples of 20 block means',replot_only=True),indent=2)+'\n')
print('PASS24network+5region rows')
