"""The four panels, in the order the commentary argues them.

  A  an uneven bag of tasks: stealing works, exactly as advertised
  B  steal from the *wrong* end: nothing happens -- and it happens 500 times
  C  spawning, with no size gradient: the two ends are the same integer
  D  a divide-and-conquer tree: the same flip now costs 1.62x
  E  the steal_cost sweep, so the page is not arguing from one constant
"""

import statistics
import wsq

SC, LEAF, HEAD = 8, 8, 7          # steal cost, leaf granularity, headline seed


def bar(title):
    print()
    print(title)
    print("-" * len(title))


def flat(loads, end, stealing=True, nworkers=4):
    s = wsq.Sim(nworkers, leaf=LEAF, steal_end=end, steal_cost=SC)
    wsq.preload(s, loads)
    mk = s.run(stealing=stealing)
    return mk, s


def spawned(builder, end, nworkers=8, steal_cost=SC):
    s = wsq.Sim(nworkers, leaf=LEAF, steal_end=end, steal_cost=steal_cost)
    builder(s)
    mk = s.run()
    return mk, s


ROW = "  {:<26} {:>9} {:>7} {:>7} {:>7} {:>9}"
HDR = ROW.format("", "makespan", "steals", "failed", "moved", "per steal")


# ---------------------------------------------------------------- panel A
bar("A. An uneven bag: 24 tasks on worker 0, one each on workers 1-3 (P=4)")
loads = wsq.uneven_bag(HEAD)
W = sum(sum(c) for c in loads)
lb = -(-W // 4)
print(f"  total work W = {W} ticks, {len(loads[0])} tasks on worker 0, "
      f"steal_cost = {SC}")
print(f"  perfect-split floor ceil(W/P) = ceil({W}/4) = {lb}")
print()
print(HDR)
for label, kw in (("no stealing at all", dict(stealing=False)),
                  ("steal from the far end", dict())):
    mk, s = flat(loads, wsq.FAR, **kw)
    per = f"{s.moved / s.steals:.1f}" if s.steals else "-"
    print(ROW.format(label, mk, s.steals, s.failed, s.moved, per),
          f" = {mk / lb:.2f}x floor")

# ---------------------------------------------------------------- panel B
bar("B. Same bag, same everything -- steal from the OWNER'S end instead")
print(HDR)
res = {}
for label, end in (("far end (the rule)", wsq.FAR), ("own end (wrong)", wsq.OWN)):
    mk, s = flat(loads, end)
    res[end] = mk
    print(ROW.format(label, mk, s.steals, s.failed, s.moved,
                     f"{s.moved / s.steals:.1f}"))
print(f"\n  breaking the famous rule changed the makespan by "
      f"{res[wsq.OWN] - res[wsq.FAR]:+d} ticks out of {res[wsq.FAR]}. "
      f"It got faster.")

diffs = []
for seed in range(1, 501):
    L = wsq.uneven_bag(seed)
    diffs.append(flat(L, wsq.OWN)[0] - flat(L, wsq.FAR)[0])
worse = sum(1 for d in diffs if d > 0)
better = sum(1 for d in diffs if d < 0)
print(f"\n  over 500 generated orderings of the same workload shape:")
print(f"    mean(own - far) = {statistics.mean(diffs):+.3f} ticks   "
      f"stdev = {statistics.stdev(diffs):.1f}   "
      f"range {min(diffs):+d} .. {max(diffs):+d}")
print(f"    own end worse: {worse}    own end BETTER: {better}    "
      f"identical: {diffs.count(0)}")

# ---------------------------------------------------------------- panel C
bar("C. Control: one root spawning 128 equal tasks of 8 (P=8, no gradient)")
print(HDR)
for label, end in (("far end", wsq.FAR), ("own end", wsq.OWN)):
    mk, s = spawned(lambda x: wsq.spawn_flat(x, 128, 8), end)
    print(ROW.format(label, mk, s.steals, s.failed, s.moved,
                     f"{s.moved / s.steals:.1f}"))
print("\n  every counter identical. With no size gradient the two ends are")
print("  not merely similar, they are interchangeable.")

# ---------------------------------------------------------------- panel D
bar("D. Same P, same task size, same steal cost -- a 1024 divide-and-conquer"
    " tree")
mk0, s0 = spawned(lambda x: wsq.spawn_tree(x, 1024), wsq.FAR)
Wt = wsq.total_work(s0)
print(f"  total work W = {Wt} ticks (1024 in leaves + 127 splits), "
      f"floor ceil(W/8) = {-(-Wt // 8)}")
print()
print(HDR)
tree = {}
for label, end in (("far end (the rule)", wsq.FAR), ("own end (wrong)", wsq.OWN)):
    mk, s = spawned(lambda x: wsq.spawn_tree(x, 1024), end)
    tree[end] = mk
    print(ROW.format(label, mk, s.steals, s.failed, s.moved,
                     f"{s.moved / s.steals:.1f}"))
print(f"\n  same flip, same scheduler: {tree[wsq.OWN] / tree[wsq.FAR]:.2f}x "
      f"the makespan.")

# ---------------------------------------------------------------- panel E
bar("E. The one tuned constant: steal_cost, swept on panel D's tree")
SWEEP = "  {:>10} {:>7} {:>7} {:>7} {:>8} {:>8} {:>8}"
print(SWEEP.format("steal_cost", "far mk", "own mk", "ratio",
                   "far st", "own st", "own/far"))
for sc in (1, 4, 8, 16):
    row = {}
    for end in (wsq.FAR, wsq.OWN):
        mk, s = spawned(lambda x: wsq.spawn_tree(x, 1024), end, steal_cost=sc)
        row[end] = (mk, s.steals)
    f, o = row[wsq.FAR], row[wsq.OWN]
    print(SWEEP.format(sc, f[0], o[0], f"{o[0] / f[0]:.2f}x", f[1], o[1],
                       f"{o[1] / f[1]:.2f}x"))
print("\n  at steal_cost=1 a wasted steal is free: the own end needs 35 attempts")
print("  where the far end needs 32, pays nothing for them, and the rule looks")
print("  like superstition. Raise the price and the count explodes with it.")
