"""Print every number the lesson quotes. Run it from this folder: python3 show_numbers.py"""
from leak_sim import (EXPERIMENTS, GUESSES, INSTRUCTION, NOTE, QUEUE_MS, QUESTIONS, RIGHT, SEQUENCE, TAIL, TRIES, ideal_waits, simulate,
                      timing, totals, wait_ms)

shared, per_user = simulate(False), simulate(True)

print("== The requests (the lesson's table)")
print("  # | user | question | shared: result | per user: result")
for a, b in zip(shared, per_user):
    def result(s):
        return "LEAK" if s.leak else f"hit (request {s.written_by})" if s.hit else "miss"
    print(f"  {a.n} | {a.user} | {a.question} | {result(a)} | {result(b)}")

print("\n== Counts")
for name, steps in (("shared key (question only)", shared), ("per-user key (user + question)", per_user)):
    t = totals(steps)
    print(f"  {name}: {t['hits']} hits of {t['requests']}, {t['leaks']} leaks, hit rate {t['rate']}%")
harmless = [s.n for s, p in zip(shared, per_user) if s.hit and not s.leak and not p.hit]
print(f"  hits the shared key got right that the per-user key gives up: requests {harmless}")
good_shared = sum(s.hit and not s.leak for s in shared)
good_user = sum(s.hit and not s.leak for s in per_user)
print(f"  hits that served the right answer: {good_shared} shared, {good_user} per user")
print(f"  leaked answers by who got them: " + ", ".join(f"{s.n} (User {s.user} got an answer written for request {s.written_by})" for s in shared if s.leak))
public = [i for i, (_, a) in enumerate(QUESTIONS) if "{" not in a]
print(f"  questions: {len(QUESTIONS)}; asked in the sequence: {len({q for _, q in SEQUENCE})}; answers with no private fact: question numbers {public}")

print("\n== The prompt cache and the wait before the first word")
tokens = INSTRUCTION + NOTE + TAIL
print(f"  prompt: {INSTRUCTION} shared instruction + {NOTE} note + {TAIL} question = {tokens} tokens; the stored start is {INSTRUCTION + NOTE}")
print(f"  best-case wait if the start is reused: {wait_ms(tokens, INSTRUCTION + NOTE):.1f} ms; read again from the top: {wait_ms(tokens, 0):.1f} ms")
blocks = wait_ms(tokens, INSTRUCTION)
print(f"  with storage in blocks a wrong guess still reuses the {INSTRUCTION}-token instruction and reads {NOTE + TAIL} tokens: {blocks:.1f} ms, a gap of {blocks - wait_ms(tokens, INSTRUCTION + NOTE):.1f} ms to the right guess")
print(f"  random queueing delay added to each wait: mean {QUEUE_MS:g} ms; {TRIES} waits per guess; {EXPERIMENTS} experiments; guesses {GUESSES}, right one is number {RIGHT + 1}")
for salted in (False, True):
    t = timing(salted)
    label = "user in the key (salted)" if salted else "shared key"
    print(f"  {label}:")
    print(f"    waits with no delay (ms): " + ", ".join(f"{g} {w:.1f}" for g, w in zip(GUESSES, ideal_waits(salted))))
    print(f"    average of {TRIES} waits, averaged over {EXPERIMENTS} experiments (ms): " + ", ".join(f"{g} {w:.0f}" for g, w in zip(GUESSES, t["average"])))
    print(f"    the fastest guess is the right one: {t['one']}% with one try per guess, {t['many']}% with the average of {TRIES} (chance is 33%)")
