"""
Simulare fidela a experimentului propus in:
  Lee Wen Wu, "Superluminal Communication?" (2021)
  Sectiunile 4.1, 4.2 si 4.3

Reproduce distributia spatiala a fotonilor lui Bob (ecuatiile 28-51 din document)
pentru toate cele trei cazuri si arata daca teorema no-signalling este incalcata.
"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import tkinter as tk
from tkinter import messagebox

# =========================================================
# 0. INTERFATA GRAFICA
# =========================================================
def obtine_parametri():
    p = {
        "N":       2000,    # puncte pe ecran
        "sigma":   1.8,     # latime fascie difractata (Gaussian)
        "k_phase": 2.2,     # faza de separatie a fantelor (k * d / L)
        "N_shots": 50000,   # nr. de fotoni simulati (Monte Carlo)
        "delta_D5": 0.15,   # semideschiderea detectorului D5 al Alicei
        "ruleaza":  False,
    }

    def on_start():
        try:
            p["sigma"]    = float(e_sigma.get())
            p["k_phase"]  = float(e_kphase.get())
            p["delta_D5"] = float(e_delta.get())
            p["N_shots"]  = int(e_shots.get())
            if any(v <= 0 for v in [p["sigma"], p["k_phase"], p["delta_D5"], p["N_shots"]]):
                raise ValueError
        except ValueError:
            messagebox.showwarning("Eroare", "Toti parametrii trebuie sa fie numere pozitive!")
            return
        p["ruleaza"] = True
        root.destroy()

    def on_close():
        root.destroy()
        exit()

    root = tk.Tk()
    root.title("DCQE Simulare – Sectiunea 4.3 (Lee Wen Wu 2021)")
    root.geometry("430x370")
    root.resizable(False, False)
    root.protocol("WM_DELETE_WINDOW", on_close)

    tk.Label(root, text="Simulare DCQE – Sectiunea 4.3",
             font=("Arial", 12, "bold"), fg="#0f62fe").pack(pady=(14, 2))
    tk.Label(root,
             text="Reproduce Eq. 28–51 din documentul lui Lee Wen Wu",
             font=("Arial", 9), fg="gray").pack(pady=(0, 10))

    frame = tk.Frame(root)
    frame.pack(padx=30, fill="x")

    def row(label, default):
        f = tk.Frame(frame)
        f.pack(fill="x", pady=4)
        tk.Label(f, text=label, font=("Arial", 10), width=38, anchor="w").pack(side="left")
        e = tk.Entry(f, width=10, font=("Arial", 10), justify="center")
        e.insert(0, str(default))
        e.pack(side="left")
        return e

    e_sigma  = row("Latime fascie difractata σ (u.n.):", 1.8)
    e_kphase = row("Faza de separatie k·d/L (u.n.):", 2.2)
    e_delta  = row("Semideschidere detector D5 al Alicei δ:", 0.15)
    e_shots  = row("Numar de fotoni simulati (shots):", 50000)

    tk.Label(root, text=(
        "δ mic → D5 focalizat (cazul Sec. 4.3)\n"
        "δ mare → D5 acoperă toată ecranul (echivalent Sec. 4.2)"
    ), font=("Arial", 8), fg="gray").pack(pady=4)

    tk.Button(root, text="▶  Rulează Simularea", command=on_start,
              bg="#0f62fe", fg="white", font=("Arial", 11, "bold"),
              padx=10, pady=6).pack(pady=12)

    root.bind("<Return>", lambda e: on_start())
    root.mainloop()
    return p


# =========================================================
# 1. CONSTRUIREA FUNCTIILOR DE UNDA (Aproximatie Fraunhofer)
# =========================================================
def construieste_functii_unda(N, sigma, k_phase):
    """
    Returneaza functiile de unda ale fotonilor A si B pe ecranul lui Bob (idler)
    si pe ecranul Alicei (semnal), conform geometriei din documentul lui Lee Wen Wu.

    Starea SPDC entanglata (Eq. 7 din document):
        |Ψ> = 1/√2 ( |A_s>|A_i> + |B_s>|B_i> )

    In reprezentare de pozitie (Fraunhofer):
        ψ_A(x) = G(x) * exp(+i * k_phase * x)   [faza din fanta A la -d/2]
        ψ_B(x) = G(x) * exp(-i * k_phase * x)   [faza din fanta B la +d/2]
    unde G(x) = exp(-x^2 / 2σ^2)  [anvelopa gaussiana de difractie]
    """
    x = np.linspace(-6, 6, N)

    G = np.exp(-x**2 / (2 * sigma**2))

    # Functii de unda foton idler (Bob's side) - Eq. 7, 31
    psi_A_i = G * np.exp(+1j * k_phase * x)
    psi_B_i = G * np.exp(-1j * k_phase * x)

    # Functii de unda foton semnal (Alice's side) - geometrie oglindita
    psi_A_s = G * np.exp(+1j * k_phase * x)
    psi_B_s = G * np.exp(-1j * k_phase * x)

    # Normalizam
    dx = x[1] - x[0]
    norm_i = np.sqrt(np.sum(np.abs(psi_A_i)**2) * dx)
    psi_A_i /= norm_i
    psi_B_i /= norm_i
    psi_A_s /= norm_i
    psi_B_s /= norm_i

    return x, psi_A_i, psi_B_i, psi_A_s, psi_B_s


# =========================================================
# 2. CALCULUL DISTRIBUTIILOR PENTRU FIECARE CAZ
# =========================================================
def calculeaza_distributii(x, psi_A_i, psi_B_i, psi_A_s, psi_B_s, delta_D5, N_shots):
    dx = x[1] - x[0]
    N  = len(x)

    # --- Densitati de probabilitate de baza ---
    I_A = np.abs(psi_A_i)**2          # distributie Bob | calea A
    I_B = np.abs(psi_B_i)**2          # distributie Bob | calea B

    # -------------------------------------------------------
    # CAZ 0: Alice masoara calea cu D3/D4 (Sectiunea 4.1)
    # Eq. 28-30: Bob vede suma incoerenta
    # P_0(x) = 1/2 |ψ_A_i(x)|^2 + 1/2 |ψ_B_i(x)|^2
    # -------------------------------------------------------
    P_bob_caz0 = 0.5 * I_A + 0.5 * I_B

    # -------------------------------------------------------
    # CAZ 1: Alice sterge cu beam splitter D1/D2 (Sectiunea 4.2)
    # Eq. 37-41:
    #   P_D1(x) = |ψ_A_i + ψ_B_i|^2 / 2
    #   P_D2(x) = |ψ_A_i - ψ_B_i|^2 / 2
    #   P_total(x) = 0.5*P_D1 + 0.5*P_D2 = P_0(x)  [identic!]
    # -------------------------------------------------------
    psi_plus  = (psi_A_i + psi_B_i) / np.sqrt(2)
    psi_minus = (psi_A_i - psi_B_i) / np.sqrt(2)

    P_D1 = np.abs(psi_plus)**2
    P_D2 = np.abs(psi_minus)**2

    P_bob_caz1_total = 0.5 * P_D1 + 0.5 * P_D2          # fara post-selectie
    P_bob_caz1_D1    = P_D1 / (np.sum(P_D1) * dx)        # post-selectat D1
    P_bob_caz1_D2    = P_D2 / (np.sum(P_D2) * dx)        # post-selectat D2

    # -------------------------------------------------------
    # CAZ 2: Alice combina caile si foloseste D5 focalizat (Sectiunea 4.3)
    # Eq. 50-52:
    #   Dupa detectia la D5 (la pozitia u_p):
    #   |ψ_i> = α*|A_i> + β*|B_i>
    #   unde α = ψ_A_s(u_p), β = ψ_B_s(u_p)
    #
    #   Distributia lui Bob (conditionat pe D5):
    #   P(x | D5 la u_p) ∝ |α*ψ_A_i(x) + β*ψ_B_i(x)|^2
    #
    #   Distributia MARGINALA (fara post-selectie) =
    #   ∫_{D5} |ψ_A_s(u)|^2 * |ψ_A_i(x)|^2 du
    #   + ∫_{D5} |ψ_B_s(u)|^2 * |ψ_B_i(x)|^2 du
    #   + 2*Re( ∫_{D5} ψ_A_s*(u)*ψ_B_s(u) du ) * Re(ψ_A_i*(x)*ψ_B_i(x))
    # -------------------------------------------------------

    # Centrul detectorului D5 al Alicei (simetric -> u_p = 0)
    u_p_idx = N // 2

    # Masca D5: detecteaza intr-un interval mic [u_p - delta_D5, u_p + delta_D5]
    masca_D5 = np.abs(x) <= delta_D5

    # Amplitudinile medii la D5 (conform Eq. 44)
    alpha_D5 = psi_A_s[masca_D5]    # amplitudinea caii A pe aria D5
    beta_D5  = psi_B_s[masca_D5]    # amplitudinea caii B pe aria D5

    # --- Distributia POST-SELECTATA (conditionat pe D5) ---
    # Integram starea conditionata a idlerului pe aria D5
    # Aceasta este ceea ce PRETINDE autorul ca vede Bob direct (fara canal clasic)
    P_bob_caz2_postselect = np.zeros(N)
    for alpha_u, beta_u in zip(alpha_D5, beta_D5):
        psi_i_cond = alpha_u * psi_A_i + beta_u * psi_B_i
        P_bob_caz2_postselect += np.abs(psi_i_cond)**2
    P_bob_caz2_postselect *= dx
    norm_ps = np.sum(P_bob_caz2_postselect) * dx
    if norm_ps > 0:
        P_bob_caz2_postselect /= norm_ps

    # --- Distributia MARGINALA (fara post-selectie) ---
    # Teorema no-signalling: traseaza peste TOATE rezultatele lui Alice
    # Chiar daca Alice foloseste D5, Bob nu stie CAND Alice a detectat
    # -> integram peste toata ecranul Alicei
    #
    # P_marginala(x) = Tr_Alice(|Ψ><Ψ|)(x)
    #                = 1/2 * |ψ_A_i(x)|^2 + 1/2 * |ψ_B_i(x)|^2
    # (aceeasi cu Cazul 0! - consecinta teoremei no-signalling)
    P_bob_caz2_marginal = P_bob_caz0.copy()    # IDENTICA cu Cazul 0

    # Termenul de interferenta integrat pe D5 (cuantifica "ce face autorul gresit")
    overlap_D5 = np.sum(np.conj(alpha_D5) * beta_D5) * dx
    termen_interferenta = 2 * np.real(overlap_D5) * np.real(
        np.conj(psi_A_i) * psi_B_i
    )

    # --- Simulare Monte Carlo: esantionare de fotoni ---
    # Simuleaza N_shots perechi entanglate si colecteaza datele lui Bob
    rng = np.random.default_rng(42)

    def esantioneaza(prob_dist, n):
        prob = np.abs(prob_dist)
        prob /= prob.sum()
        return rng.choice(N, size=n, p=prob)

    # Caz 0: Alice masoara A sau B cu prob egala
    idx_alice_AB = rng.integers(0, 2, N_shots)     # 0=calea A, 1=calea B
    idx_bob_caz0 = np.where(
        idx_alice_AB == 0,
        esantioneaza(I_A, N_shots),
        esantioneaza(I_B, N_shots)
    )

    # Caz 2 post-selectat: Alice detecteaza la D5 la u_p (centru)
    # Alpha si beta mediate pe D5
    alpha_med = np.mean(alpha_D5)
    beta_med  = np.mean(beta_D5)
    psi_cond_med = alpha_med * psi_A_i + beta_med * psi_B_i
    P_cond = np.abs(psi_cond_med)**2
    P_cond /= P_cond.sum()
    idx_bob_caz2_ps = rng.choice(N, size=N_shots, p=P_cond)

    return {
        "x": x,
        "P_bob_caz0":             P_bob_caz0,
        "P_bob_caz1_total":       P_bob_caz1_total,
        "P_bob_caz1_D1":          P_bob_caz1_D1,
        "P_bob_caz1_D2":          P_bob_caz1_D2,
        "P_bob_caz2_postselect":  P_bob_caz2_postselect,
        "P_bob_caz2_marginal":    P_bob_caz2_marginal,
        "termen_interferenta":    termen_interferenta,
        "overlap_D5":             overlap_D5,
        "I_A": I_A, "I_B": I_B,
        "psi_A_i": psi_A_i, "psi_B_i": psi_B_i,
        "idx_bob_caz0":     idx_bob_caz0,
        "idx_bob_caz2_ps":  idx_bob_caz2_ps,
        "N_shots": N_shots,
    }


# =========================================================
# 3. VIZUALIZARE
# =========================================================
def genereaza_grafic(d, delta_D5, sigma, k_phase, N_shots):
    x = d["x"]
    dx = x[1] - x[0]
    N  = len(x)

    fig = plt.figure(figsize=(17, 12))
    fig.patch.set_facecolor('#f8f8f8')

    gs = gridspec.GridSpec(3, 3, figure=fig, hspace=0.55, wspace=0.38)

    BLUE   = "#1f77b4"
    ORANGE = "#ff7f0e"
    GREEN  = "#2ca02c"
    RED    = "#d62728"
    GRAY   = "#aaaaaa"

    # ------------------------------------------------------------------
    # RAND 0: Distributia MARGINALA a lui Bob (fara canal clasic)
    # Aceasta este ceea ce Bob poate observa singur
    # ------------------------------------------------------------------
    ax00 = fig.add_subplot(gs[0, 0])
    ax01 = fig.add_subplot(gs[0, 1])
    ax02 = fig.add_subplot(gs[0, 2])

    for ax, P, culoare, titlu in [
        (ax00, d["P_bob_caz0"],          BLUE,
         "Cazul 0 (Sec. 4.1)\nAlice: D3/D4 — pastreaza calea"),
        (ax01, d["P_bob_caz1_total"],    ORANGE,
         "Cazul 1 (Sec. 4.2)\nAlice: beam splitter D1+D2"),
        (ax02, d["P_bob_caz2_marginal"], GREEN,
         f"Cazul 2 (Sec. 4.3)\nAlice: D5 focalizat (δ={delta_D5})"),
    ]:
        ax.fill_between(x, P, alpha=0.25, color=culoare)
        ax.plot(x, P, color=culoare, lw=2)
        ax.set_title(titlu, fontsize=9, fontweight='bold')
        ax.set_xlim([-5, 5])
        ax.set_ylim(bottom=0)
        ax.grid(True, alpha=0.3)
        ax.tick_params(labelsize=8)

    ax00.set_ylabel("Probabilitate P(x)", fontsize=9)

    fig.text(0.5, 0.945,
             "RAND 1 — Ce vede Bob FĂRĂ comunicare clasică de la Alice\n"
             "(distributia marginala, teorema no-signalling)",
             ha='center', fontsize=10, color='darkred',
             fontweight='bold',
             bbox=dict(boxstyle='round', facecolor='#ffe0e0', alpha=0.7))

    # ------------------------------------------------------------------
    # RAND 1: Distributia POST-SELECTATA (necesita canal clasic)
    # ------------------------------------------------------------------
    ax10 = fig.add_subplot(gs[1, 0])
    ax11 = fig.add_subplot(gs[1, 1])
    ax12 = fig.add_subplot(gs[1, 2])

    # Cazul 0 post-selectat: separat pe A sau B
    ax10.plot(x, d["I_A"] / (np.sum(d["I_A"]) * dx), BLUE,   lw=2, label='Calea A (D3)')
    ax10.plot(x, d["I_B"] / (np.sum(d["I_B"]) * dx), RED,    lw=2, label='Calea B (D4)', ls='--')
    ax10.set_title("Cazul 0 post-selectat\n(separat D3 vs D4)", fontsize=9, fontweight='bold')
    ax10.legend(fontsize=7)

    # Cazul 1 post-selectat: doua subpatternuri complementare
    ax11.plot(x, d["P_bob_caz1_D1"], ORANGE, lw=2, label='D1 (franje cos)')
    ax11.plot(x, d["P_bob_caz1_D2"], RED,    lw=2, label='D2 (antifaza)', ls='--')
    ax11.fill_between(x, d["P_bob_caz1_D1"], alpha=0.15, color=ORANGE)
    ax11.fill_between(x, d["P_bob_caz1_D2"], alpha=0.15, color=RED)
    ax11.set_title("Cazul 1 post-selectat\n(D1: franje, D2: antifaza — se anuleaza!)", fontsize=9, fontweight='bold')
    ax11.legend(fontsize=7)

    # Cazul 2 post-selectat: franja UNICA (pretentia autorului)
    ax12.fill_between(x, d["P_bob_caz2_postselect"], alpha=0.2, color=GREEN)
    ax12.plot(x, d["P_bob_caz2_postselect"], GREEN, lw=2, label='D5 click (franja!)')
    ax12.plot(x, d["P_bob_caz2_marginal"],   GRAY,  lw=1.5, ls='--', alpha=0.7,
              label='Marginal (fara post-sel.)')
    ax12.set_title("Cazul 2 post-selectat (Sec. 4.3)\n★ Franja UNICA — dar necesita canal clasic! ★",
                   fontsize=9, fontweight='bold', color='darkgreen')
    ax12.legend(fontsize=7)

    for ax in [ax10, ax11, ax12]:
        ax.set_xlim([-5, 5])
        ax.set_ylim(bottom=0)
        ax.grid(True, alpha=0.3)
        ax.tick_params(labelsize=8)

    ax10.set_ylabel("Probabilitate P(x)", fontsize=9)

    fig.text(0.5, 0.645,
             "RAND 2 — Ce vede Bob CU comunicare clasică de la Alice\n"
             "(post-selectie: Bob filtreaza datele sale dupa rezultatele Alicei)",
             ha='center', fontsize=10, color='darkblue',
             fontweight='bold',
             bbox=dict(boxstyle='round', facecolor='#e0e8ff', alpha=0.7))

    # ------------------------------------------------------------------
    # RAND 2: Termenul de interferenta si concluzia
    # ------------------------------------------------------------------
    ax20 = fig.add_subplot(gs[2, 0])
    ax21 = fig.add_subplot(gs[2, 1])
    ax22 = fig.add_subplot(gs[2, 2])

    # Termenul de interferenta al Cazului 2 (integrat pe D5)
    ax20.plot(x, d["termen_interferenta"], color='purple', lw=2)
    ax20.axhline(0, color='black', lw=0.8, ls='--')
    ax20.fill_between(x, d["termen_interferenta"], alpha=0.2, color='purple')
    ax20.set_title(f"Termenul de interferenta Cazul 2\n"
                   f"(integrat pe D5, δ={delta_D5})\n"
                   f"Overlap D5 = {np.real(d['overlap_D5']):.4f}",
                   fontsize=9, fontweight='bold')
    ax20.set_xlim([-5, 5])
    ax20.grid(True, alpha=0.3)
    ax20.tick_params(labelsize=8)
    ax20.set_ylabel("Amplitudine", fontsize=9)

    # Simulare Monte Carlo: histograma fotonilor lui Bob
    bins = np.linspace(-5, 5, 80)
    ax21.hist(x[d["idx_bob_caz0"]], bins=bins, density=True,
              alpha=0.5, color=BLUE, label='Cazul 0 (D3/D4)')
    ax21.hist(x[d["idx_bob_caz2_ps"]], bins=bins, density=True,
              alpha=0.5, color=GREEN, label='Cazul 2 (D5, post-sel.)')
    ax21.set_title(f"Simulare Monte Carlo ({N_shots:,} fotoni)\nCazul 0 vs Cazul 2 post-selectat",
                   fontsize=9, fontweight='bold')
    ax21.legend(fontsize=7)
    ax21.set_xlim([-5, 5])
    ax21.grid(True, alpha=0.3)
    ax21.tick_params(labelsize=8)
    ax21.set_ylabel("Densitate", fontsize=9)

    # Concluzie
    ax22.axis('off')

    # Verificam daca franja e vizibila fara post-selectie
    diff_marginal = np.max(np.abs(d["P_bob_caz2_marginal"] - d["P_bob_caz0"]))
    teorema_incalcata = diff_marginal > 0.001

    if teorema_incalcata:
        concluzie_text = "⚠ ANOMALIE DETECTATA"
        concluzie_color = "#cc0000"
    else:
        concluzie_text = "✔ Teorema No-Signalling CONFIRMATA"
        concluzie_color = "#006600"

    overlap_real = np.real(d['overlap_D5'])
    vizibilitate = np.max(d["P_bob_caz2_postselect"]) / np.max(d["P_bob_caz2_marginal"])

    ax22.text(0.5, 0.97, "CONCLUZIE", transform=ax22.transAxes,
              ha='center', va='top', fontsize=12, fontweight='bold')

    linii = [
        ("", ""),
        (concluzie_text, concluzie_color),
        ("", ""),
        ("Distributia MARGINALA a lui Bob:", "#333333"),
        ("  Cazul 0 = Cazul 1 = Cazul 2", "#333333"),
        ("  (identice fara canal clasic)", "#333333"),
        ("", ""),
        ("Overlap Alice pe D5:", "#333333"),
        (f"  Re(∫ψ_A*·ψ_B du) = {overlap_real:.5f}", "#555500" if abs(overlap_real) > 0.001 else "#006600"),
        ("", ""),
        ("Franja post-selectata (Sec. 4.3):", "#333333"),
        ("  VIZIBILA — dar necesita ca", "#0000aa"),
        ("  Bob sa stie CAND Alice a tras!", "#0000aa"),
        ("  → canal clasic indispensabil", "#0000aa"),
        ("", ""),
        ("Eroarea autorului (Eq. 51-52):", "#cc0000"),
        ("  φ_s(u) NU e const. pe D5;", "#cc0000"),
        ("  termenul de interferenta se", "#cc0000"),
        ("  anuleaza la integrare globala.", "#cc0000"),
    ]

    y = 0.88
    for txt, col in linii:
        if txt:
            ax22.text(0.05, y, txt, transform=ax22.transAxes,
                      ha='left', va='top', fontsize=8.5, color=col,
                      fontfamily='monospace' if txt.startswith(" ") else 'sans-serif')
        y -= 0.047

    ax22.set_facecolor('#f0f4f0')

    # Titlu general
    fig.suptitle(
        "Simulare Experiment DCQE Modificat — Lee Wen Wu (2021)\n"
        f"σ={sigma}, k·d/L={k_phase}, δ(D5)={delta_D5}, Fotoni={N_shots:,}",
        fontsize=12, fontweight='bold', y=0.99
    )

    for ax in [ax10, ax11, ax12, ax21]:
        ax.set_xlabel("Pozitia pe ecranul lui Bob  x (u.n.)", fontsize=8)
    for ax in [ax00, ax01, ax02]:
        ax.set_xlabel("x (u.n.)", fontsize=8)

    plt.savefig('dcqe_sectiunea43_rezultate.png', dpi=150, bbox_inches='tight',
                facecolor=fig.get_facecolor())
    print("Grafic salvat: dcqe_sectiunea43_rezultate.png")
    return fig


# =========================================================
# 4. RAPORT TEXT
# =========================================================
def genereaza_raport(d, delta_D5, sigma, k_phase):
    x  = d["x"]
    dx = x[1] - x[0]

    diff = np.max(np.abs(d["P_bob_caz2_marginal"] - d["P_bob_caz0"]))
    overlap = np.real(d["overlap_D5"])

    # Vizibilitate franje (post-selectat Cazul 2)
    I_max = np.max(d["P_bob_caz2_postselect"])
    I_min = np.min(d["P_bob_caz2_postselect"])
    vizib = (I_max - I_min) / (I_max + I_min) if (I_max + I_min) > 0 else 0.0

    raport = f"""
=================================================================
RAPORT SIMULARE: Experimentul din Sectiunea 4.3
Lee Wen Wu, "Superluminal Communication?" (2021)
=================================================================

PARAMETRI SIMULARE
------------------
  Latime fascie difractata σ  : {sigma}
  Faza de separatie k·d/L     : {k_phase}
  Semideschidere D5 (δ)       : {delta_D5}
  Numar fotoni simulati        : {d['N_shots']:,}

DISTRIBUTIA MARGINALA A LUI BOB (fara canal clasic)
----------------------------------------------------
  Diferenta maxima intre Cazul 0 si Cazul 2: {diff:.6f}
  Concluzie: {'TEOREMA NO-SIGNALLING INCALCATA!' if diff > 0.001 else 'Teorema no-signalling CONFIRMATA (distributii identice)'}

TERMENUL DE INTERFERENTA (Eq. 51)
-----------------------------------
  Overlap Alice pe aria D5:
    Re( ∫_D5 ψ_A_s*(u) · ψ_B_s(u) du ) = {overlap:.6f}

  Interpretare:
  - Daca overlap ≠ 0 → termenul de interferenta e local nenul (post-selectat)
  - Dar integrat pe TOATA ecranul Alicei → overlap TOTAL = 0 (ortogonalitate)
  - Aceasta inseamna: franja exista CONDITIONAT pe D5, nu marginal.

FRANJA POST-SELECTATA (Sec. 4.3)
----------------------------------
  Vizibilitate franje (post-sel.): V = {vizib:.4f}
  (V=1 → franje perfecte, V=0 → fara franje)

  Concluzie: Franja e {('VIZIBILA (V > 0.3)' if vizib > 0.3 else 'SLABA (V < 0.3)')}.
  Dar pentru ca Bob sa o vada, trebuie sa stie CAND a detectat Alice la D5.
  → Necesita canal clasic (Alice → Bob).
  → NU este comunicare supraluminala.

EROAREA DIN DOCUMENTUL LUI LEE WEN WU (Eq. 51-52)
----------------------------------------------------
  Autorul sustine ca φ_s(u_0) ≈ const daca D5 e suficient de focalizat (Eq. 53).
  Aceasta e partial corecta: PENTRU UN SINGUR EVENIMENT la u_p, φ_s e fix.

  Insa autorul omite ca:
  1. Masurarea de catre D5 este tot o masurare cuantica (proiectie pe starea la D5)
  2. Rezultatul "click la D5" este o post-selectie, nu o "stergere fara conditii"
  3. Distributia marginala ρ_Bob = Tr_Alice(|Ψ><Ψ|) e invarianta la orice actiune
     a Alicei — aceasta e teorema no-signalling in forma ei cea mai generala.
  4. Termenul de interferenta integrat pe TOATA ecranul Alicei = 0
     (din ortogonalitatea starii SPDC: <A_s|B_s> = 0)

CONCLUZIE FINALA
----------------
  Propunerea din Sectiunea 4.3 NU incalca teorema no-signalling.
  Franjele de interferenta sunt reale, dar sunt vizibile NUMAI cu post-selectie
  (adica dupa ce Bob primeste de la Alice informatie clasica: "am detectat la D5").

  Experimentul reproduce fidel Delayed Choice Quantum Eraser (Kim et al., 2000),
  unde franjele apar in coincidata (post-selectate), nu in distributia bruta.
=================================================================
"""
    with open("raport_sectiunea43.txt", "w", encoding="utf-8") as f:
        f.write(raport)
    print("Raport salvat: raport_sectiunea43.txt")
    print(raport)


# =========================================================
# MAIN
# =========================================================
if __name__ == "__main__":
    p = obtine_parametri()

    print(f"\nCalculam functiile de unda (N={p['N']} puncte)...")
    x, psi_A_i, psi_B_i, psi_A_s, psi_B_s = construieste_functii_unda(
        p["N"], p["sigma"], p["k_phase"]
    )

    print("Calculam distributiile pentru toate cazurile...")
    d = calculeaza_distributii(
        x, psi_A_i, psi_B_i, psi_A_s, psi_B_s,
        p["delta_D5"], p["N_shots"]
    )

    print("Generam graficul...")
    fig = genereaza_grafic(d, p["delta_D5"], p["sigma"], p["k_phase"], p["N_shots"])

    genereaza_raport(d, p["delta_D5"], p["sigma"], p["k_phase"])

    plt.show()
    print("\nSimulare incheiata.")
