"""Print every number the lesson quotes. Run it from this folder: python3 show_numbers.py"""
import statistics

from sim import BASE, CAP, CLIENTS, FIXED, LIMIT, RULES, SEED, TRIES, random_numbers, simulate, wait_after

SEEDS = range(1, 1001)  # how many random runs back the jitter figures


def row(name, run):
    return (f"  {name} | {run.finished} | {run.refused_calls} | {run.gave_up} | {run.busiest_retry_tick} | "
            f"{sum(1 for s, r in zip(run.served, run.refused) if s + r == 0)}")


print(f"== One run: {CLIENTS} clients, the server answers {LIMIT} calls per tick, {TRIES} tries each, seed {SEED}")
print("  rule | last call at tick | refused calls | clients that gave up | busiest retry tick | ticks with no call at all")
for rule, name in RULES:
    print(row(name, simulate(rule)))

print("\n== Calls per tick, seed", SEED, "(answered + refused)")
for rule, name in RULES:
    run = simulate(rule)
    print(f"  {name}: " + " ".join(str(s + r) for s, r in zip(run.served, run.refused)))

print("\n== The waits, in ticks, after the 1st, 2nd, ... refusal")
print("  backoff: " + ", ".join(str(wait_after("backoff", k, None)) for k in range(1, TRIES)))
print("  jitter picks a whole number from 1 up to: " + ", ".join(str(min(CAP, BASE * 2 ** (k - 1))) for k in range(1, TRIES)))
print(f"  fixed: {FIXED} every time; at once: 1 every time")
_gen = random_numbers(99)
print("  jitter average over 20,000 waits: " + ", ".join(
    f"{sum(wait_after('jitter', k, _gen.__next__) for _ in range(20000)) / 20000:.2f}" for k in range(1, TRIES)))

print(f"\n== {len(SEEDS)} seeds, jitter: min / median / max")
runs = [simulate("jitter", seed) for seed in SEEDS]
for label, values in (("last call at tick", [r.finished for r in runs]), ("refused calls", [r.refused_calls for r in runs]),
                      ("clients that gave up", [r.gave_up for r in runs]), ("busiest retry tick", [r.busiest_retry_tick for r in runs])):
    print(f"  {label}: {min(values)} / {statistics.median(values)} / {max(values)}")
once, fixed = simulate("once"), simulate("fixed")
print(f"  runs where jitter finished after retry-at-once: {sum(r.finished > once.finished for r in runs)} of {len(runs)}")
print(f"  runs where jitter finished before the fixed wait: {sum(r.finished < fixed.finished for r in runs)} of {len(runs)}")
print("  the three rules with no random part give the same run for every seed: "
      f"{all(simulate(rule, seed) == simulate(rule) for rule in ('once', 'fixed', 'backoff') for seed in range(1, 51))}")

print("\n== The first five numbers from the generator, seed 1")
gen = random_numbers(1)
print("  " + ", ".join(f"{next(gen):.6f}" for _ in range(5)))

print("\n== A small crowd by hand: 5 clients, 2 calls per tick, 4 tries")
for rule in ("once", "backoff"):
    run = simulate(rule, clients=5, limit=2, tries=4)
    print(f"  {rule}: served {run.served}, refused {run.refused}, gave up {run.gave_up}")
