import json, math

d = json.load(open('hdgl_spiral10000_v30.json'))
s = d['statistics']
pts = d['spiral_data']
nodes = d['lattice_nodes']

print("=== V3.0 STATISTICS ===")
print(f"spiral points:  {s['total_points']}")
print(f"energy mean:    {s['energy_stats']['mean']:.6f}")
print(f"energy max:     {s['energy_stats']['max']:.6f}")
print(f"energy min:     {s['energy_stats']['min']:.6f}")
print(f"dn mean:        {s['dn_stats']['mean']:.6f}")
print(f"dn max:         {s['dn_stats']['max']:.6f}")
print(f"lattice nodes:  {s['lattice_stats']['total_nodes']}")
print(f"avg conns:      {s['lattice_stats']['avg_connections']:.2f}")
print(f"dims used:      {s['lattice_stats']['dimensions_used']}")

# Check if prime-indexed points land on spiral resonance nodes
known_mersenne_exp = [2,3,5,7,13,17,19,31,61,89,107,127,521,607,1279,
                      2203,2281,3217,4253,4423,9689,9941]
node_indices = set()
for node in nodes:
    idx = int(node['id'].split('_')[1])
    node_indices.add(idx)

print("\n=== PRIME-INDEXED SPIRAL POINTS ===")
print(f"{'index':>6} {'radius':>10} {'energy':>10} {'dn_amp':>10} {'dimension':>9} {'is_node':>7} {'base_seed':>12}")
for p in pts:
    if p['index'] in known_mersenne_exp:
        is_node = p['index'] in node_indices
        print(f"{p['index']:>6} {p['radius']:>10.4f} {p['energy']:>10.6f} "
              f"{p['dn_amplitude']:>10.6f} {p['dimension_n']:>9} "
              f"{'YES' if is_node else 'no':>7} {p['base_seed']:>12.6f}")

# Distribution of lattice nodes by spiral index — what are the node indices?
print("\n=== FIRST 30 LATTICE NODES (indices) ===")
node_idx_list = sorted([int(n['id'].split('_')[1]) for n in nodes])
print(node_idx_list[:30])

# Gap pattern in node indices
gaps = [node_idx_list[i+1]-node_idx_list[i] for i in range(min(50, len(node_idx_list)-1))]
print("\n=== FIRST 50 NODE-INDEX GAPS ===")
print(gaps)

# Check the r-values (where prism is placed) for spiral points at prime indices
print("\n=== SCALE INVESTIGATION: Dn(r) at prime vs non-prime indices ===")
prime_dn = [p['dn_amplitude'] for p in pts if p['index'] in set(known_mersenne_exp) and p['index'] < 2000]
nonprime_dn = [p['dn_amplitude'] for p in pts if p['index'] not in set(known_mersenne_exp)
               and 2 <= p['index'] < 2000 and p['index'] % 100 == 1]
if prime_dn and nonprime_dn:
    print(f"mean Dn at mersenne-exp indices (<2000): {sum(prime_dn)/len(prime_dn):.6f}")
    print(f"mean Dn at non-mersenne-exp indices:     {sum(nonprime_dn)/len(nonprime_dn):.6f}")

# The key insight: what happens at large indices vs small?
# Spiral radius grows as sqrt(i)*phi — so r is SCALE-DEPENDENT
print("\n=== RADIUS SCALING (where the prism lands) ===")
sample_indices = [2, 3, 5, 7, 13, 31, 61, 127, 521, 1279, 2203, 4423, 9689]
for p in pts:
    if p['index'] in sample_indices:
        # Normalized r for Dn computation was radius/100
        r_norm = min(p['radius'] / 100.0, 1.0)
        # The formula uses r^k where k = (dim+1)/8
        k = (p['dimension_n'] + 1) / 8.0
        r_power = r_norm ** k
        print(f"  p={p['index']:>5}: radius={p['radius']:>8.3f}  r_norm={r_norm:.4f}  k={k:.4f}  r^k={r_power:.6f}  Dn={p['dn_amplitude']:.6f}")
