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

from evalset import QUESTIONS, WOBBLE_CHANCE, WOBBLE_RUNS, WOBBLE_SEED, Z, compare, results, wilson, wobble
from toy import FIELDS, SWITCH_NAMES, Instructions, all_settings, answer

OFF = Instructions()
base = results(OFF)


def count(marks):
    return sum(marks)


def pct(x):
    return f"{round(100 * x)}%"


def interval(passed, n=len(QUESTIONS)):
    low, high = wilson(passed, n)
    return f"{pct(low)} to {pct(high)}"


def name(d):
    on = [f for f in FIELDS if getattr(d, f)]
    return " + ".join(on) if on else "nothing on"


print("== The starting instructions: all three switches off")
print(f"  questions: {len(QUESTIONS)}; passing: {count(base)} ({pct(count(base) / len(QUESTIONS))}); failing: {len(QUESTIONS) - count(base)}")
print("  failing: " + "; ".join(f"Q{i + 1} {q[0]}" for i, q in enumerate(QUESTIONS) if not base[i]))
print(f"  \"Do you deliver bikes?\" answer: {answer('Do you deliver bikes?', OFF)}")

print("\n== Each setting against the starting instructions: fixed, broke, pass rate")
print("  switches | fixed | broke | passing | rate | 95% range")
for d in all_settings():
    after = results(d)
    c = compare(base, after)
    print(f"  {name(d)} | {c.count('fixed')} | {c.count('broke')} | {count(after)} of {len(QUESTIONS)} | {pct(count(after) / len(QUESTIONS))} | {interval(count(after))}")

print("\n== What each single switch fixes and breaks")
for field in FIELDS:
    d = Instructions(**{field: True})
    c = compare(base, results(d))
    fixed = [f"Q{i + 1}" for i, k in enumerate(c) if k == "fixed"]
    broke = [f"Q{i + 1}" for i, k in enumerate(c) if k == "broke"]
    print(f"  {SWITCH_NAMES[field]}: fixed {', '.join(fixed) or 'none'}; broke {', '.join(broke) or 'none'}")

print("\n== Questions that fail under all 8 settings")
always_fail = [i + 1 for i in range(len(QUESTIONS)) if not any(results(d)[i] for d in all_settings())]
always_pass = [i + 1 for i in range(len(QUESTIONS)) if all(results(d)[i] for d in all_settings())]
print(f"  always failing: {always_fail}; always passing: {always_pass}")

print("\n== The Wilson range (95%), worked for 13 of 20")
n, k = 20, 13
p = k / n
centre = p + Z * Z / (2 * n)
spread = Z * math.sqrt(p * (1 - p) / n + Z * Z / (4 * n * n))
shrink = 1 + Z * Z / n
print(f"  p = {p}; z = {Z}; centre = {centre:.4f}; spread = {spread:.4f}; shrink = {shrink:.4f}")
print(f"  low = {(centre - spread) / shrink:.4f}; high = {(centre + spread) / shrink:.4f}")
print("  12 of 20: " + interval(12) + "; 13 of 20: " + interval(13))
print("  range width by size at a 65% rate: " + "; ".join(
    f"{size} questions {pct(wilson(round(0.65 * size), size)[0])} to {pct(wilson(round(0.65 * size), size)[1])}" for size in (20, 100, 400)))

print(f"\n== Run-to-run wobble: the demo table (seed {WOBBLE_SEED}, {WOBBLE_RUNS} runs, each result flips with chance {WOBBLE_CHANCE})")
print("  run | starting instructions | don't-know switch | fixed | broke")
dk = Instructions(dont_know=True)
for r in range(WOBBLE_RUNS):
    b, a = results(OFF, r), results(dk, r)
    c = compare(b, a)
    print(f"  {r + 1} | {count(b)} | {count(a)} | {c.count('fixed')} | {c.count('broke')}")
spreads = []
worse = total = 0
new = results(dk)
for seed in range(1, 2001):
    counts = [(count(m != f for m, f in zip(base, row)), count(m != f for m, f in zip(new, row))) for row in wobble(seed)]
    spreads.append(max(b for b, _ in counts) - min(b for b, _ in counts))
    total += len(counts)
    worse += sum(a < b for b, a in counts)
print(f"  over 2000 seeds: median spread of the starting instructions across 5 runs = {median(spreads)} questions; "
      f"spread of 4 or more in {pct(sum(s >= 4 for s in spreads) / len(spreads))} of seeds")
print(f"  a single run shows the don't-know switch scoring lower than the starting instructions in {pct(worse / total)} of {total} runs")
