# -*- coding: utf-8 -*-
"""
Visualisation for Appendices E & F of the IT3 paper.
Self-checking: (Z*alpha)^2, the bicone radius and the anchor coordinates
are COMPUTED here (not hard-coded), so a successful render == consistency
with Tables E/F. Panels that carry no exact number (E.3) are explicitly
drawn as schematic (sign/magnitude = ab-initio), matching the text.

Changelog vs previous version:
  * E.2 inset title moved INSIDE the inset (transAxes) so it no longer
    overprints the panel title; inset lowered to clear the block row.
  * E.2 explicit "saturated floor S5 (mirror S6)" label added (left gap).
  * E.4 dead variable `labels` removed.
  * F.3 central plaque nudged to the 3-circle centroid (0.52).
Output: IT3_Appendix_E.png , IT3_Appendix_F.png
"""
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle, Circle

# ---- palette locked to the Topological Matrix (Fig.1) ----
COL = dict(s="#E8A317", f="#17BECF", d="#E6005C", p="#9B59B6")
BLUE, RED = "#4C8CBF", "#C0504D"
ALPHA = 1.0/137.035999084

# ---- floor capacities (mirror-symmetric) and bicone silhouette ----
C = [2, 8, 8, 18, 18, 32, 32, 18, 18, 8, 8, 2]
assert C == C[::-1], "capacity sequence must be a palindrome"
bicone_r = [math.sqrt(c/2.0) for c in C]            # discrete bicone radius
CLASSICAL = list(range(6)); RELATIV = list(range(6, 12))

# ---- Diophantine Vacuum Operator: block partition depends on capacity ----
def part(cap):
    if cap == 32: return [("s", 2), ("f", 16), ("d", 26), ("p", 32)]   # S5,S6
    if cap == 18: return [("s", 2), ("d", 12), ("p", 18)]              # S3,S4,S7,S8
    if cap == 8:  return [("s", 2), ("p", 8)]                          # S1,S2,S9,S10
    return [("s", 2)]                                                  # S0,S11

def block_of(mu, cap):
    prev = 0
    for b, e in part(cap):
        if mu <= e:
            return b, mu - prev
        prev = e
    return ("?", 0)

_STARTS = [0]
for c in C: _STARTS.append(_STARTS[-1] + c)

def coords(Z):
    for n in range(12):
        if _STARTS[n] < Z <= _STARTS[n+1]:
            mu = Z - _STARTS[n]; cap = C[n]; b, p = block_of(mu, cap)
            return n, mu, cap, b, p
    raise ValueError(Z)

def is_closed(b, p):                       # relativistic subshell closure
    return (b == "s" and p == 2) or (b == "d" and p == 10) or \
           (b == "p" and p in (2, 6)) or (b == "f" and p == 14)

# =====================================================================
#  FIGURE E  (2 x 2)
# =====================================================================
figE, ax = plt.subplots(2, 2, figsize=(14, 10))
figE.suptitle("Appendix E  -  Corollaries the standard table lists but cannot derive",
              fontsize=15, fontweight="bold")

# --- E.1 : palindrome + discrete bicone silhouette ---
a = ax[0, 0]
x = np.arange(12)
cols = [BLUE if n in CLASSICAL else RED for n in x]
a.bar(x, C, color=cols, edgecolor="k", lw=0.6, zorder=2)
for lo, hi in [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]:      # pair plateaux
    a.axvspan(lo-0.5, hi+0.5, color="0.90", zorder=0)
a2 = a.twinx()
a2.plot(x, bicone_r, "o-", color=COL["s"], lw=2, ms=7,
        label=r"discrete bicone radius $\sqrt{C/2}$", zorder=3)
a.scatter([0, 11], [C[0], C[11]], marker="*", s=220, color="gold",
          edgecolor="k", zorder=4)
a.text(0,  C[0]  + 1.5, "Core\n$S_0$",   ha="center", va="bottom",
       fontsize=9, fontweight="bold")
a.text(11, C[11] + 1.5, "End\n$S_{11}$", ha="center", va="bottom",
       fontsize=9, fontweight="bold")
a.set_xticks(x); a.set_xticklabels([f"$S_{{{n}}}$" for n in x], fontsize=9)
a.set_ylabel("floor capacity $C(S_n)$")
a2.set_ylabel(r"$\sqrt{C/2}$ (bicone radius)")
a.set_title("E.1  Palindrome of capacities & bicone silhouette", fontweight="bold")
a.set_ylim(0, max(C) + 6); a2.set_ylim(0, max(bicone_r) + 0.5)
a2.legend(loc="upper right", fontsize=8); a.grid(axis="y", ls=":", alpha=.4)

# --- E.2 : f-block is INTERIOR to d (schematic) + contraction profiles ---
a = ax[0, 1]
order = part(32)                            # s,f,d,p on the saturated floor
left = 0; ranges = {}
for b, e in order:
    a.add_patch(Rectangle((left, 0.30), e-left, 0.30, color=COL[b], ec="k", lw=0.8))
    a.text((left+e)/2, 0.45, f"{b}\n$\\mu$ {left+1}..{e}",
           ha="center", va="center", color="white", fontsize=9, fontweight="bold")
    ranges[b] = (left, e); left = e
fc, dc = ranges["f"], ranges["d"]
a.annotate("", xy=((dc[0]+dc[1])/2, 0.22), xytext=((fc[0]+fc[1])/2, 0.22),
           arrowprops=dict(arrowstyle="->", color="k", lw=1.5))
a.text(16, 0.12, "f-cavity is topologically INSIDE d  =>  no transverse growth",
       ha="center", va="top", fontsize=9, style="italic")
# NEW: explicit floor label in the free left-top gap (clears the inset at x>=0.60)
a.text(8, 0.66, "saturated floor $S_5$  (mirror $S_6$)",
       ha="center", va="bottom", fontsize=8.5, color="0.25", fontweight="bold")
# inset: lowered (y 0.62..0.92) and title drawn INSIDE via transAxes (no overflow)
ins = a.inset_axes([0.60, 0.62, 0.38, 0.30])
t = np.arange(1, 15)
lanth = 1.0 - 0.045*t
actin = 1.0 - 0.045*t - 0.18*np.exp(-((t-2.5)**2)/3.0)
ins.plot(t, lanth, "-",  color=BLUE, lw=1.6, label="Ln (monotone)")
ins.plot(t, actin, "--", color=RED,  lw=1.6, label="An (early-enhanced)")
ins.set_xticks([]); ins.set_yticks([])
ins.text(0.5, 0.97, "contraction (schematic)", transform=ins.transAxes,
         ha="center", va="top", fontsize=7.5)
ins.legend(fontsize=6.5, loc="lower left")
a.set_xlim(0, 32); a.set_ylim(0, 1.1); a.axis("off")
a.set_title("E.2  Lanthanide/actinide contraction = geometry (direction only)",
            fontweight="bold", fontsize=10)

# --- E.3 : Og magnetic NON-ADDITIVITY (schematic, sign-free) ---
a = ax[1, 0]
xg = np.array([1, 2, 3, 4, 5]); yg = np.array([1, 3, 6, 10, 15])
a.plot(xg, yg, "o-", color="0.3", lw=1.8,
       label="He..Xe  (monotone $\\propto\\langle r^2\\rangle$)")
a.plot([5, 6], [15, 21], ":", color="0.3", lw=1.8, label="group extrapolation")
a.scatter([6], [21], marker="x", s=80, color="0.3")
a.scatter([6, 6], [9, 30], s=90, color=[COL["d"], COL["p"]], edgecolor="k", zorder=4)
a.annotate("", xy=(6, 30), xytext=(6, 9),
           arrowprops=dict(arrowstyle="<->", color=RED, lw=2))
a.text(6.15, 19.5, "non-additivity\ngap (split $7p$ shell)", color=RED,
       fontsize=9, fontweight="bold", va="center")
a.text(6.15, 9,  "$7p_{1/2}$ contracted",  fontsize=8, color=COL["d"], va="center")
a.text(6.15, 30, "$7p_{3/2}$ + Van Vleck", fontsize=8, color=COL["p"], va="center")
a.set_xticks([1, 2, 3, 4, 5, 6])
a.set_xticklabels(["He", "Ne", "Ar", "Kr", "Xe", "Og"])
a.set_xlim(0.5, 7.8)
a.set_ylabel(r"relative $|\chi|$  (schematic a.u.)")
a.set_title("E.3  Magnetic non-additivity of Og (sign = ab-initio)", fontweight="bold")
a.legend(fontsize=8, loc="upper left"); a.grid(ls=":", alpha=.4)

# --- E.4 : group-12 (Z*alpha)^2 and the phase sequence ---
a = ax[1, 1]
Z12 = [30, 48, 80, 112]; nm12 = ["Zn", "Cd", "Hg", "Cn"]
phase = ["(solid)", "(solid)", "(liquid)", "(gas)"]
za2 = [(Z*ALPHA)**2 for Z in Z12]
barc = [BLUE, BLUE, RED, RED]
bars = a.bar(nm12, za2, color=barc, edgecolor="k", lw=0.7)
a.axhline(0.20, ls="--", color="k", lw=1.3)
a.text(3.45, 0.215, "bond-quenching threshold\n(phenomenological)",
       ha="right", va="bottom", fontsize=8)
for b, v in zip(bars, za2):
    a.text(b.get_x()+b.get_width()/2, v+0.015, f"{v:.3f}", ha="center", fontsize=9)
a.set_xticks(range(4))
a.set_xticklabels([f"{n}\n{p}" for n, p in zip(nm12, phase)],
                  fontweight="bold", fontsize=10)        # phase fused into tick
a.set_ylabel(r"$(Z\alpha)^2$  (computed)")
a.set_title(r"E.4  Why Hg is the unique liquid metal  ->  Zn,Cd(s)->Hg(l)->Cn(g)",
            fontweight="bold", fontsize=10)
a.set_ylim(0, 0.85); a.grid(axis="y", ls=":", alpha=.4)

figE.tight_layout(rect=[0, 0, 1, 0.96])
figE.savefig("IT3_Appendix_E.png", dpi=200, bbox_inches="tight")

# =====================================================================
#  FIGURE F  (1 x 3)
# =====================================================================
figF, ax = plt.subplots(1, 3, figsize=(18, 5.5),
                        gridspec_kw={"width_ratios": [2.3, 1.0, 1.2]})
figF.suptitle("Appendix F  -  Island of stability: topological proton anchors & survival",
              fontsize=16, fontweight="bold")

# --- F.1 : node belts S6, S7 with anchors and the 126 'hole' ---
a = ax[0]
ANCHORS = {112: "d10", 114: "p1/2^2", 118: "p6", 120: "8s2", 130: "d10", 136: "p6"}
HOLE = {126}
def draw_belt(axx, n, y):
    cap = C[n]
    for mu in range(1, cap+1):
        Z = _STARTS[n] + mu
        b, p = block_of(mu, cap)
        axx.add_patch(Rectangle((mu-1, y), 1, 0.8, color=COL[b], ec="white", lw=1.0))
        if Z in ANCHORS:
            axx.add_patch(Rectangle((mu-1, y), 1, 0.8, fill=False, ec="white", lw=2.4))
            axx.plot(mu-0.5, y+0.4, marker="*", ms=14, color="white", markeredgecolor="k")
            axx.text(mu-0.5, y-0.15, f"{Z}", ha="center", va="top",
                     fontsize=8, fontweight="bold", color=COL[b])
        if Z in HOLE:
            axx.plot(mu-0.5, y+0.4, marker="x", ms=12, mew=2.4, color="white")
            axx.text(mu-0.5, y-0.15, f"{Z}\nno e-closure", ha="center", va="top",
                     fontsize=8, fontweight="bold", color="red")
    axx.text(-0.8, y+0.4, f"$S_{{{n}}}$", ha="right", va="center",
             fontsize=12, fontweight="bold")
    axx.text(cap/2, y+0.9,
             "fill order: " + r" $\rightarrow$ ".join(b for b, _ in part(cap)),
             ha="center", va="bottom", color="black", fontsize=9, fontweight="bold")

draw_belt(a, 6, 1.8)
draw_belt(a, 7, 0.3)
a.set_xlim(-2.5, 33); a.set_ylim(-0.5, 3.2); a.axis("off")
a.set_title("F.1  Closed relativistic subshells (anchors) vs Z=126 (mid-d, no closure)",
            fontweight="bold", fontsize=11)

# --- F.2 : nuclear vs IT3 overlap matrix ---
a = ax[1]
rows = ["Z=114", "Z=120", "Z=126", "N=184"]
M = np.array([[1, 1], [1, 1], [1, 0], [1, 2]])        # 1=yes 0=no 2=n/a
cmap = plt.cm.colors.ListedColormap(["#C0504D", "#4CAF50", "#999999"])
a.imshow(M, cmap=cmap, vmin=0, vmax=2, aspect="auto")
lab = {1: "YES", 0: "NO", 2: "n/a"}
for i in range(4):
    for j in range(2):
        a.text(j, i, lab[M[i, j]], ha="center", va="center",
               color="white", fontweight="bold", fontsize=12)
a.set_xticks([0, 1]); a.set_xticklabels(["nuclear\nshell model", "IT$^3$\ne-topology"],
                                        fontsize=10)
a.set_yticks(range(4)); a.set_yticklabels(rows, fontsize=10)
a.set_title("F.2  Overlap (126 & N=184 not mirrored)", fontweight="bold", fontsize=11)

# --- F.3 : Venn of the three factors ---
a = ax[2]
a.set_xlim(0, 1); a.set_ylim(0, 1); a.axis("off")
circ = [((0.35, 0.60), "#999999"),
        ((0.65, 0.60), COL["d"]),
        ((0.50, 0.35), COL["f"])]
for (cx, cy), col in circ:
    a.add_patch(Circle((cx, cy), 0.26, color=col, alpha=0.30, ec=col, lw=1.6))
a.text(0.25, 0.90, "(i) nuclear shell min\n[not derived]",
       ha="center", va="center", fontsize=9, fontweight="bold")
a.text(0.75, 0.90, "(ii) closed-subshell belt\n[derived]",
       ha="center", va="center", fontsize=9, fontweight="bold")
a.text(0.50, 0.05, "(iii) accelerated $\\alpha$-channel\n[derived]",
       ha="center", va="center", fontsize=9, fontweight="bold")
a.text(0.50, 0.52, "observed\nisland\n(114,120)", ha="center", va="center",  # 3-way centroid
       fontsize=10, fontweight="bold", color="black",
       bbox=dict(boxstyle="round,pad=0.4", fc="white", ec="k", lw=1.5))
a.set_title("F.3  Island = intersection of three factors", fontweight="bold", fontsize=11)

figF.tight_layout(rect=[0, 0, 1, 0.93])
figF.savefig("IT3_Appendix_F.png", dpi=200, bbox_inches="tight")
plt.show()

# ---- self-check (must match Tables E/F) ----
print("OK  anchors:", {Z: coords(Z) for Z in [112,114,118,120,126,130,136]})
print("OK  (Za)^2 group12:", [round((Z*ALPHA)**2,4) for Z in Z12])
print("OK  palindrome:", C == C[::-1], " bicone_r:", [round(r,3) for r in bicone_r])