#!/usr/bin/env python3
"""La curva de CUBIC según el RFC 9438: W(t) = C (t - K)^3 + Wmax, frente a Reno.

Solo evalúa la fórmula con C = 0,4 y beta = 0,7, sin la región amigable con Reno ni la
convergencia rápida. Compara cuánto tarda cada uno en volver a Wmax después de una pérdida.

    python3 cubic_curve.py --wmax 167 --rtt 40
    python3 cubic_curve.py --recovery
"""
import argparse

C = 0.4
BETA = 0.7


def k_seconds(wmax):
    """Tiempo que tarda W(t) en volver a Wmax, con la ventana recién reducida a BETA * Wmax."""
    return (wmax * (1 - BETA) / C) ** (1 / 3)


def window(t, wmax):
    return C * (t - k_seconds(wmax)) ** 3 + wmax


def reno_recovery_seconds(wmax, rtt_ms):
    """Reno baja a Wmax / 2 y sube un paquete por RTT."""
    return (wmax / 2) * rtt_ms / 1000


def packets_for(rate_mbps, rtt_ms, packet=1500):
    return rate_mbps * 1e6 * (rtt_ms / 1000) / (8 * packet)


def recovery_table():
    print(f"{'enlace':>10} {'RTT':>7} {'BDP (paq.)':>11} {'Reno':>12} {'CUBIC':>10}")
    for rate, rtt in ((50, 40), (50, 200), (1000, 40), (1000, 200), (10000, 100)):
        wmax = packets_for(rate, rtt)
        print(f"{rate:>5} Mbps {rtt:>4} ms {wmax:>11.0f} "
              f"{reno_recovery_seconds(wmax, rtt):>10.1f} s {k_seconds(wmax):>8.1f} s")


def main():
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--wmax", type=float, default=167)
    parser.add_argument("--rtt", type=float, default=40)
    parser.add_argument("--recovery", action="store_true")
    args = parser.parse_args()
    if args.recovery:
        recovery_table()
        return
    k = k_seconds(args.wmax)
    print(f"Wmax = {args.wmax:.0f} paquetes, K = {k:.2f} s")
    print(f"{'t (s)':>6} {'W cubic':>8} {'W reno':>8}")
    reno_slope = 1000 / args.rtt  # paquetes por segundo
    for tenth in range(0, int(k * 2.2 * 2) + 1):
        t = tenth / 2
        reno = args.wmax / 2 + reno_slope * t
        print(f"{t:>6.1f} {window(t, args.wmax):>8.0f} {reno:>8.0f}")


if __name__ == "__main__":
    main()
