"""Print every number the lesson quotes. Run: python3 show_numbers.py"""
import statistics

import settings_calc as sc


def main() -> None:
    chats = sc.CHATS
    print(f"card {sc.GPU_BYTES / 1e9:.0f} GB; weights {sc.WEIGHT_BYTES / 1e9:.2f} GB; cache {sc.KV_BYTES:,} bytes a token; block {sc.BLOCK_TOKENS} tokens = {sc.BLOCK_BYTES:,} bytes; reserve {sc.RESERVE_BYTES / 1e9:.1f} GB (invented)")
    print(f"chats: seed {sc.SEED}, {sc.N_CHATS}; prompts {min(chats['prompts'])} to {max(chats['prompts'])} (mean {statistics.fmean(chats['prompts']):.0f}); "
          f"replies {min(chats['replies'])} to {max(chats['replies'])} (mean {statistics.fmean(chats['replies']):.0f}, total {sum(chats['replies']):,}); "
          f"longest whole chat {max(p + r for p, r in zip(chats['prompts'], chats['replies'])):,}; mean whole chat {statistics.fmean(p + r for p, r in zip(chats['prompts'], chats['replies'])):.0f}")
    print()
    print("memory parts by share (GB): weights, reserve, pool, unused; pool blocks and tokens; chats of 2,000 / of the longest (8,000 and 32,000)")
    for share in (20, 23, 30, 40, 50, 60, 70, 80, 90, 92, 95, 98):
        r8, r32 = sc.room(share, 8000), sc.room(share, 32000)
        print(f"  {share:3}%: allowed {sc.gb(r8['allowed']):>5}  pool {sc.gb(r8['pool']):>5}  unused {sc.gb(r8['unused']):>5}  blocks {r8['blocks']:6,}  tokens {r8['pool_tokens']:8,}  "
              f"typical {r8['typical']:4}  at 8,000 {r8['at_longest']:3}  at 32,000 {r32['at_longest']:3}")
    print("  lowest share that leaves any pool:", next(s for s in range(1, 100) if sc.room(s, 16)["blocks"] > 0))
    gb80, gib80 = sc.memory_parts(90), sc.memory_parts(90, sc.GPU_BYTES_GIB)
    per_typical = sc.blocks_for(sc.TYPICAL_CHAT)
    print(f"  the card read as 80 GiB, at 90%: card {sc.GPU_BYTES_GIB / sc.GPU_BYTES - 1:+.1%}; pool {sc.gb(gib80['pool'])} GB against {sc.gb(gb80['pool'])} GB ({gib80['pool'] / gb80['pool'] - 1:+.1%}); "
          f"blocks {gib80['blocks']:,} against {gb80['blocks']:,}; chats of {sc.TYPICAL_CHAT:,} {gib80['blocks'] // per_typical} against {gb80['blocks'] // per_typical}")
    print()
    print("opening state, and the room for each longest chat at 90%")
    for longest in sc.LONGEST:
        r, rr = sc.room(90, longest), sc.room(90, longest, True)
        print(f"  longest {longest:7,}: starts {r['starts']}; {sc.room_line(r, longest, False)}  | reserved: {sc.room_line(rr, longest, True)}")
    print("  lowest share that starts, by longest chat:", ", ".join(f"{longest:,}: {next(s for s in range(1, 100) if sc.room(s, longest)['starts'])}%" for longest in sc.LONGEST))
    for share in (40, 44):
        r = sc.room(share, 128000)
        print(f"  share {share}% with a 128,000-token longest chat: allowed {sc.gb(r['allowed'])} GB, pool {sc.gb(r['pool'])} GB = {r['blocks']:,} blocks, needs {r['per_longest']:,}; starts {r['starts']}")
    print()
    prompt_tokens = sum(chats["prompts"])
    prefill_seconds = prompt_tokens * 2 * sc.L24.PARAMS / sc.L24.A100_PEAK_FLOPS
    print(f"reading every prompt once: {prompt_tokens:,} tokens, at least {prefill_seconds:.1f} s of arithmetic, so no cap can pass {sum(chats['replies']) / prefill_seconds:,.0f} tokens a second")
    print()
    print("cap sweep at the opening share and longest chat (90%, 8,000)")
    rows = sc.sweep()
    print("cap | tokens/s | ms between words | chats running | pauses | cache rebuilt")
    for row in rows:
        print(f"{row['cap']:4} | {row['tokens_per_second']:8,.0f} | {row['seconds_per_word'] * 1000:6.1f} | {row['running']:6.1f} | {row['paused']:3} | {row['rebuilt_tokens']:,}")
    print("  stops paying at cap", sc.stops_paying(rows))
    for a, b in zip(rows, rows[1:]):
        print(f"  cap {a['cap']:3} to {b['cap']:3}: tokens/s x{b['tokens_per_second'] / a['tokens_per_second']:.3f}, wait x{b['seconds_per_word'] / a['seconds_per_word']:.2f}")
    print()
    print("the same sweep at other shares (tokens/s, ms per word, pauses)")
    for share in (35, 50, 90):
        out = sc.sweep(share)
        print(f"  share {share}%: pool {sc.room(share, 8000)['pool_tokens']:,} tokens;", "; ".join(f"cap {x['cap']}: {x['tokens_per_second']:,.0f}/{x['seconds_per_word'] * 1000:.0f}/{x['paused']}" for x in out if x['cap'] >= 32))
    print()
    print("blocks as chats grow, against reserving the full length (90%, cap 512)")
    for longest in (8000, 32000):
        r = sc.room(90, longest)
        for reserved in (False, True):
            run = sc.serve(sc.backlog(), 512, r["blocks"], longest, reserved)
            print(f"  longest {longest:6,} reserved={reserved!s:5}: {run['tokens_per_second']:,.0f} tokens/s, {run['seconds_per_word'] * 1000:.1f} ms, running {run['running']:.1f}, pauses {run['paused']}, turned away {run['refused']}")
    print()
    print("longest chat cuts off chats (90%, cap 64)")
    for longest in sc.LONGEST:
        run = sc.serve(sc.backlog(), 64, sc.room(90, longest)["blocks"], longest)
        print(f"  longest {longest:7,}: turned away {run['refused']}, tokens/s {run['tokens_per_second']:,.0f}")


if __name__ == "__main__":
    main()
