
import os

# Paths are resolved relative to this script so the package is self-contained.
_HERE = os.path.dirname(os.path.abspath(__file__))
DATA = os.path.normpath(os.path.join(_HERE, "..", "..", "data"))

"""Appendix A.1 icin: verilen p0 / lb / ub ile kisitli fit yayimlanan sonuca yakinsiyor mu?"""
import csv, numpy as np

lat, bw = {}, {}
for r in csv.DictReader(open(os.path.join(DATA, "latency_210.csv"), encoding="utf-8-sig")):
    lat[(int(r["qIdx"]), int(float(r["B_Mbps"])), int(r["trial"]))] = float(r["latency_ms"])
for r in csv.DictReader(open(os.path.join(DATA, "latency_trials_raw_210.csv"),
                             encoding="utf-8")):
    bw[(int(r["qIdx"]), int(r["target_Mbps"]), int(r["trial"]))] = float(r["achieved_Mbps"])
keys = sorted(lat)
q = np.array([k[0] for k in keys]); L = np.array([lat[k] for k in keys])
b = np.array([bw[k] for k in keys])
Q = np.zeros((len(keys), 6))
for j in range(2, 8):
    Q[:, j - 2] = (q == j)


def model(p):
    L0, be, th, C = p[:4]
    return L0 + be * b + th / (C - b) + Q @ p[4:]


def jac(p):
    L0, be, th, C = p[:4]
    d = C - b
    J = np.zeros((len(b), 10))
    J[:, 0] = 1.0
    J[:, 1] = b
    J[:, 2] = 1.0 / d
    J[:, 3] = -th / d ** 2
    J[:, 4:] = Q
    return J


def project(p, lb, ub):
    return np.minimum(np.maximum(p, lb), ub)


# --- kutu kisitlari (math_model.m mantigina uygun: parametreler negatif olamaz) ---
bmax = b.max()
lb = np.array([0.0, 0.0, 0.0, bmax + 1.0] + [-10.0] * 6)
ub = np.array([80.0, 2.0, 5000.0, 2000.0] + [10.0] * 6)
p0 = np.array([8.0, 0.05, 500.0, 700.0] + [0.0] * 6)

p = project(p0.copy(), lb, ub)
lam = 1e-3
prev = np.inf
hist = []
for it in range(1, 201):
    r = L - model(p)
    s = float(r @ r)
    hist.append(s)
    J = jac(p)
    A = J.T @ J + lam * np.eye(10)
    g = J.T @ r
    try:
        step = np.linalg.solve(A, g)
    except np.linalg.LinAlgError:
        break
    pn = project(p + step, lb, ub)
    rn = L - model(pn)
    sn = float(rn @ rn)
    if sn < s:
        p, lam = pn, max(lam * 0.5, 1e-12)
    else:
        lam *= 4.0
    if abs(prev - s) < 1e-10 * max(1.0, s):
        break
    prev = s

r = L - model(p)
mae = np.mean(np.abs(r)); rmse = np.sqrt(np.mean(r ** 2))
r2 = 1 - np.sum(r ** 2) / np.sum((L - L.mean()) ** 2)

print("Baslangic p0 :", " ".join(f"{v:g}" for v in p0))
print("Alt sinir lb :", " ".join(f"{v:g}" for v in lb))
print("Ust sinir ub :", " ".join(f"{v:g}" for v in ub))
print(f"  (C icin alt sinir = max(b) + 1 = {bmax + 1:.1f} Mbps)")
print()
print(f"Yakinsama: {it} iterasyon")
print()
ref = [8.639, 0.0305, 486.885, 611.395, -0.470, -1.449, -1.306, 0.782, -0.476, -1.681]
nm = ["L0", "beta", "theta", "C", "g2", "g3", "g4", "g5", "g6", "g7"]
print(f"  {'param':6s} {'fit':>12s} {'makale':>12s} {'fark':>10s}")
for n, v, rr in zip(nm, p, ref):
    print(f"  {n:6s} {v:12.4f} {rr:12.4f} {abs(v-rr):10.4f}")
print()
print(f"  MAE={mae:.3f} (makale 2.52)  RMSE={rmse:.3f} (3.13)  R2={r2:.4f} (0.842)")
print()
print("--- math_model.m icindeki GERCEK ayarlar (model e, 21 parametre) ---")
print("  p0 = [ones(1,7)*8, ones(1,7)*1.0, ones(1,7)*0.05]")
print("  lb = zeros(1,21)")
print("  ub = [ones(1,7)*80, ones(1,7)*50, ones(1,7)*2.0]")
print("  MaxFunctionEvaluations = 2e5, FunctionTolerance = 1e-10, StepTolerance = 1e-10")
print("  Bth = 200 Mbit/s (sabit), rng(42)")
