{
  "id": "P120",
  "slug": "leader-lease",
  "key": "P120-leader-lease",
  "title": "Leader lease",
  "summary": "A lease from a store that A trusts by its own clock while B takes over by the store's: two leaders over every pair of clock drifts, counting from asking or hearing, and the margin that removes them and what it costs.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P120 - Leader lease\n\nA holds a lease from a store, then goes quiet. B takes over when the\nstore's clock says the lease has run out, while A trusts its own clock.\nWhen A's lease outlasts the store's in real time, there are two leaders,\nand neither can tell.\n\n- `main.eml` - the menu, the settings, one lease in real time, two\n  leaders over every pair of drifts, and the margin that removes them\n- `lease.eml` - when A stops, when the store frees the lease and B\n  starts, the time with two leaders or none, and the smallest margin\n\nHow each part works:\n\n- Times are whole milliseconds of real time. A clock that drifts d\n  percent counts 100 + d ms for every 100 real ones. A's request takes A's\n  delay to reach the store, and the answer as long again; B keeps asking\n  and hears after B's delay.\n- A counts its lease from when it asks, or from when it hears, and stops\n  when its clock has counted the lease less a margin. As in the corpus\n  case `leader-lease-expiry`, neither side is wrong about its own clock.\n- With a lease of 1000 ms, A 30 ms from the store and B 10 ms:\n  - no drift, A counting from when it asks: no leader for 40 ms;\n  - counting from when it hears: two leaders for 20 ms with no drift at\n    all, since A starts late by its delay back;\n  - A's clock 5% slow: two leaders for 13 ms.\n- Every pair of drifts within 5%: two leaders in 23 of the 121 pairs, for\n  up to 60 ms. A slow clock keeps A leading longer, and a fast store frees\n  the lease sooner.\n- The margin: each pair alone would need from 0 to 57 ms, and a margin\n  safe for every pair has to cover the worst, A slow and the store fast.\n  57 ms leaves no leader for up to 194 ms at the other pairs; counting from\n  when A hears needs 114 ms. Within 10%, 145 ms and 199 ms.\n\nWhat is checked: menu choices 1 to 8; a lease from 100 to 5000 ms, longer\nthan the margin; delays from 0 to 100 ms; drifts from -10 to 10 percent\nand a bound from 0 to 10; send or receive; a margin below the lease. An\nempty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the settings, one lease, every drift and the margin;\n- A counting from when it hears: one lease and the margin;\n- A's clock 5% slow: one lease and every drift, then a margin of 57 ms:\n  every drift and one lease again;\n- a bound of 10%, and the margin for it.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- the lease: a cancel, 50, 6000 and x, then 1000 with a cancel and 101\n  for A's delay, then 30 with a cancel and -1 for B's;\n- clocks: a cancel, 11, -11 and x, then 0 with a cancel, 0 0 with a\n  cancel and 11, and 0 0 5 with a cancel and both;\n- the margin: a cancel, 1000, -1 and x, then 500, a lease of 400 (not\n  longer than the margin), and a margin of 999, where A's lease is over\n  before it hears.\n\nBuilt on the verified corpus case `leader-lease-expiry` (two leaders from\na lease both sides believe, and a guard that removes them).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P120 leader lease: one lease from A to B in real time, two leaders over\n# every pair of clock drifts, and the margin that removes them.\nimport lease\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef is_digits(s):\n    if len(s) == 0:\n        return False\n    for c in s:\n        if c < \"0\" or c > \"9\":\n            return False\n    return True\n\ndef timer_text(timer):\n    if timer == \"send\":\n        return \"A timing from when it asks\"\n    return \"A timing from when it hears\"\n\ndef drift_text(d):\n    if d > 0:\n        return \", running \" + str(d) + \"% fast,\"\n    if d < 0:\n        return \", running \" + str(0 - d) + \"% slow,\"\n    return \"\"\n\ndef show(st):\n    # st: [lease, A's delay, B's delay, A's drift, store's drift, bound, timer, margin]\n    (\"Lease \" + str(st[0]) + \" ms. A's delay to the store \" + str(st[1]) + \" ms each way, B's \" + str(st[2]) + \" ms.\") ^0\n    (\"A's clock drifts \" + str(st[3]) + \"%, the store's \" + str(st[4]) + \"%; the tables cover drifts within \" + str(st[5]) + \"%.\") ^0\n    (timer_text(st[6]) + \"; a margin of \" + str(st[7]) + \" ms, so A stops when its clock has counted \" + str(st[0] - st[7]) + \" ms.\") ^0\n\ndef one_lease(st):\n    lease.outcome(st[0], st[7], st[3], st[4], st[1], st[2], st[6]) => o\n    2 * st[1] => heard\n    lease.store_frees(st[0], st[4], st[1]) => frees\n    (\"One lease, \" + timer_text(st[6]) + \":\") ^0\n    [] => ev\n    if st[6] == \"send\":\n        ev + [[0, \"A asks the store for the lease, and starts its timer\"]] => ev\n    else:\n        ev + [[0, \"A asks the store for the lease\"]] => ev\n    ev + [[st[1], \"the store grants it to A, for \" + str(st[0]) + \" ms on the store's clock\"]] => ev\n    if o[2] <= heard:\n        ev + [[o[2], \"A's clock\" + drift_text(st[3]) + \" has counted \" + str(st[0] - st[7]) + \" ms, the lease less the margin, before A has heard\"]] => ev\n        ev + [[heard, \"A hears, but by its clock the lease is already over, so it never leads\"]] => ev\n    else:\n        if st[6] == \"send\":\n            ev + [[heard, \"A hears, and acts as leader\"]] => ev\n        else:\n            ev + [[heard, \"A hears, starts its timer, and acts as leader\"]] => ev\n        ev + [[o[2], stop_text(st)]] => ev\n    ev + [[frees, \"the store's clock\" + drift_text(st[4]) + \" has counted the lease: B's request is granted\"]] => ev\n    ev + [[o[3], \"B hears, and acts as leader\"]] => ev\n    # in order of time; events at the same ms keep their order\n    [] => ordered\n    for e in ev:\n        [] => out\n        False => placed\n        for x in ordered:\n            if not placed and e[0] < x[0]:\n                out + [e] => out\n                True => placed\n            out + [x] => out\n        if not placed:\n            out + [e] => out\n        out => ordered\n    for e in ordered:\n        (\"  \" + lpad(str(e[0]), 6) + \" ms  \" + e[1]) ^0\n    if o[0] > 0:\n        (\"Two leaders for \" + str(o[0]) + \" ms, from \" + str(lease.max2(o[3], heard)) + \" to \" + str(o[2]) + \" ms: A still trusts its lease, and B holds the new one.\") ^0\n    elif o[1] > 0:\n        (\"No leader for \" + str(o[1]) + \" ms, from \" + str(lease.max2(o[2], heard)) + \" to \" + str(o[3]) + \" ms.\") ^0\n    else:\n        \"One leader at every moment: A stops just as B starts.\" ^0\n\ndef stop_text(st):\n    if st[7] == 0:\n        return \"A's clock\" + drift_text(st[3]) + \" has counted \" + str(st[0]) + \" ms: A stops\"\n    return \"A's clock\" + drift_text(st[3]) + \" has counted \" + str(st[0] - st[7]) + \" ms, the lease less the margin: A stops\"\n\ndef every(st):\n    lease.grid(st[0], st[7], st[5], st[1], st[2], st[6]) => g\n    (\"Two leaders, in ms: A's clock drift down the side, the store's across; a margin of \" + str(st[7]) + \" ms, \" + timer_text(st[6]) + \".\") ^0\n    \"  \" + pad(\"\", 8) => head\n    for s in [0 - st[5]:st[5]]:\n        head + lpad(str(s) + \"%\", 6) => head\n    head ^0\n    for row in g[0]:\n        \"  \" + pad(\"A \" + str(row[0]) + \"%\", 8) => line\n        for x in row[1]:\n            line + lpad(str(x), 6) => line\n        line ^0\n    (2 * st[5] + 1) * (2 * st[5] + 1) => pairs\n    if g[1][0] == 0:\n        (\"No two leaders in any of the \" + str(pairs) + \" pairs; no leader for up to \" + str(g[1][2]) + \" ms.\") ^0\n    else:\n        (\"Two leaders in \" + str(g[1][0]) + \" of the \" + str(pairs) + \" pairs, for up to \" + str(g[1][1]) + \" ms; no leader for up to \" + str(g[1][2]) + \" ms.\") ^0\n    if st[5] > 0:\n        \"A slow clock keeps A leading longer in real time, and a fast store frees the lease sooner.\" ^0\n\ndef find(st):\n    lease.safe_margin(st[0], st[5], st[1], st[2], st[6]) => m\n    (\"For drifts anywhere within \" + str(st[5]) + \"%, \" + timer_text(st[6]) + \":\") ^0\n    if m[0] < 0:\n        \"  No margin shorter than the lease removes two leaders here.\" ^0\n    else:\n        (\"  The smallest margin with no two leaders for any of them is \" + str(m[0]) + \" ms; at that margin, no leader for up to \" + str(m[3]) + \" ms.\") ^0\n        if m[1] < m[2]:\n            (\"  Each pair alone would need from \" + str(m[1]) + \" to \" + str(m[2]) + \" ms. The margin has to cover the worst pair, A's clock slow and the store's fast, and the others pay for it in time without a leader.\") ^0\n    \"receive\" => other\n    if st[6] == \"receive\":\n        \"send\" => other\n    lease.safe_margin(st[0], st[5], st[1], st[2], other) => m2\n    if m2[0] < 0:\n        (\"  \" + timer_text(other) + \" instead, no margin shorter than the lease would do.\") ^0\n    else:\n        (\"  \" + timer_text(other) + \" instead, the smallest such margin is \" + str(m2[0]) + \" ms.\") ^0\n\ndef ask_int(prompt, low, high):\n    # [ok, value]; a cancel or a bad number says so and is not ok\n    plain(input(prompt)) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return [False, 0]\n    1 => sign\n    t => digits\n    if t[0] == \"-\":\n        0 - 1 => sign\n        t[1:len(t)] => digits\n    if not is_digits(digits) or len(digits) > 4 or sign * int(digits) < low or sign * int(digits) > high:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return [False, 0]\n    return [True, sign * int(digits)]\n\ndef lease_and_delays(st):\n    ask_int(\"lease (100 to 5000 ms)> \", 100, 5000) => l\n    if not l[0]:\n        return st\n    if l[1] <= st[7]:\n        (\"The lease must be longer than the margin, \" + str(st[7]) + \" ms.\") ^0\n        return st\n    ask_int(\"A's delay to the store (0 to 100 ms)> \", 0, 100) => da\n    if not da[0]:\n        return st\n    ask_int(\"B's delay to the store (0 to 100 ms)> \", 0, 100) => db\n    if not db[0]:\n        return st\n    [l[1], da[1], db[1], st[3], st[4], st[5], st[6], st[7]] => out\n    show(out)\n    return out\n\ndef clocks(st):\n    ask_int(\"A's clock drift (-10 to 10 percent)> \", 0 - 10, 10) => a\n    if not a[0]:\n        return st\n    ask_int(\"the store's clock drift (-10 to 10 percent)> \", 0 - 10, 10) => s\n    if not s[0]:\n        return st\n    ask_int(\"drift bound for the tables (0 to 10 percent)> \", 0, 10) => b\n    if not b[0]:\n        return st\n    plain(input(\"A times its lease from (send or receive)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return st\n    if t != \"send\" and t != \"receive\":\n        \"Type send or receive.\" ^0\n        return st\n    [st[0], st[1], st[2], a[1], s[1], b[1], t, st[7]] => out\n    show(out)\n    return out\n\ndef margin(st):\n    ask_int(\"margin (0 to \" + str(st[0] - 1) + \" ms)> \", 0, st[0] - 1) => m\n    if not m[0]:\n        return st\n    [st[0], st[1], st[2], st[3], st[4], st[5], st[6], m[1]] => out\n    show(out)\n    return out\n\n[1000, 30, 10, 0, 0, 5, \"send\", 0] => st\n\n\"== Leader lease ==\" ^0\n\"A holds a lease from a store and then goes quiet; B takes over when the store's clock says the lease has run out, while A trusts its own clock.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show(st)\n    elif choice == \"2\":\n        one_lease(st)\n    elif choice == \"3\":\n        every(st)\n    elif choice == \"4\":\n        find(st)\n    elif choice == \"5\":\n        lease_and_delays(st) => st\n    elif choice == \"6\":\n        clocks(st) => st\n    elif choice == \"7\":\n        margin(st) => st\n    elif choice == \"8\":\n        False => running\n    else:\n        \"Pick a number from 1 to 8.\" ^0\n\"Bye.\" ^0\n",
      "python": "import lease\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef is_digits(s):\n    if len(s) == 0:\n        return False\n    for c in s:\n        if c < \"0\" or c > \"9\":\n            return False\n    return True\n\ndef timer_text(timer):\n    if timer == \"send\":\n        return \"A timing from when it asks\"\n    return \"A timing from when it hears\"\n\ndef drift_text(d):\n    if d > 0:\n        return \", running \" + str(d) + \"% fast,\"\n    if d < 0:\n        return \", running \" + str(0 - d) + \"% slow,\"\n    return \"\"\n\ndef show(st):\n    print(\"Lease \" + str(st[0]) + \" ms. A's delay to the store \" + str(st[1]) + \" ms each way, B's \" + str(st[2]) + \" ms.\")\n    print(\"A's clock drifts \" + str(st[3]) + \"%, the store's \" + str(st[4]) + \"%; the tables cover drifts within \" + str(st[5]) + \"%.\")\n    print(timer_text(st[6]) + \"; a margin of \" + str(st[7]) + \" ms, so A stops when its clock has counted \" + str(st[0] - st[7]) + \" ms.\")\n\ndef one_lease(st):\n    o = lease.outcome(st[0], st[7], st[3], st[4], st[1], st[2], st[6])\n    heard = 2 * st[1]\n    frees = lease.store_frees(st[0], st[4], st[1])\n    print(\"One lease, \" + timer_text(st[6]) + \":\")\n    ev = []\n    if st[6] == \"send\":\n        ev = ev + [[0, \"A asks the store for the lease, and starts its timer\"]]\n    else:\n        ev = ev + [[0, \"A asks the store for the lease\"]]\n    ev = ev + [[st[1], \"the store grants it to A, for \" + str(st[0]) + \" ms on the store's clock\"]]\n    if o[2] <= heard:\n        ev = ev + [[o[2], \"A's clock\" + drift_text(st[3]) + \" has counted \" + str(st[0] - st[7]) + \" ms, the lease less the margin, before A has heard\"]]\n        ev = ev + [[heard, \"A hears, but by its clock the lease is already over, so it never leads\"]]\n    else:\n        if st[6] == \"send\":\n            ev = ev + [[heard, \"A hears, and acts as leader\"]]\n        else:\n            ev = ev + [[heard, \"A hears, starts its timer, and acts as leader\"]]\n        ev = ev + [[o[2], stop_text(st)]]\n    ev = ev + [[frees, \"the store's clock\" + drift_text(st[4]) + \" has counted the lease: B's request is granted\"]]\n    ev = ev + [[o[3], \"B hears, and acts as leader\"]]\n    ordered = []\n    for e in ev:\n        out = []\n        placed = False\n        for x in ordered:\n            if not placed and e[0] < x[0]:\n                out = out + [e]\n                placed = True\n            out = out + [x]\n        if not placed:\n            out = out + [e]\n        ordered = out\n    for e in ordered:\n        print(\"  \" + lpad(str(e[0]), 6) + \" ms  \" + e[1])\n    if o[0] > 0:\n        print(\"Two leaders for \" + str(o[0]) + \" ms, from \" + str(lease.max2(o[3], heard)) + \" to \" + str(o[2]) + \" ms: A still trusts its lease, and B holds the new one.\")\n    elif o[1] > 0:\n        print(\"No leader for \" + str(o[1]) + \" ms, from \" + str(lease.max2(o[2], heard)) + \" to \" + str(o[3]) + \" ms.\")\n    else:\n        print(\"One leader at every moment: A stops just as B starts.\")\n\ndef stop_text(st):\n    if st[7] == 0:\n        return \"A's clock\" + drift_text(st[3]) + \" has counted \" + str(st[0]) + \" ms: A stops\"\n    return \"A's clock\" + drift_text(st[3]) + \" has counted \" + str(st[0] - st[7]) + \" ms, the lease less the margin: A stops\"\n\ndef every(st):\n    g = lease.grid(st[0], st[7], st[5], st[1], st[2], st[6])\n    print(\"Two leaders, in ms: A's clock drift down the side, the store's across; a margin of \" + str(st[7]) + \" ms, \" + timer_text(st[6]) + \".\")\n    head = \"  \" + pad(\"\", 8)\n    for s in range(0 - st[5], st[5]+1):\n        head = head + lpad(str(s) + \"%\", 6)\n    print(head)\n    for row in g[0]:\n        line = \"  \" + pad(\"A \" + str(row[0]) + \"%\", 8)\n        for x in row[1]:\n            line = line + lpad(str(x), 6)\n        print(line)\n    pairs = (2 * st[5] + 1) * (2 * st[5] + 1)\n    if g[1][0] == 0:\n        print(\"No two leaders in any of the \" + str(pairs) + \" pairs; no leader for up to \" + str(g[1][2]) + \" ms.\")\n    else:\n        print(\"Two leaders in \" + str(g[1][0]) + \" of the \" + str(pairs) + \" pairs, for up to \" + str(g[1][1]) + \" ms; no leader for up to \" + str(g[1][2]) + \" ms.\")\n    if st[5] > 0:\n        print(\"A slow clock keeps A leading longer in real time, and a fast store frees the lease sooner.\")\n\ndef find(st):\n    m = lease.safe_margin(st[0], st[5], st[1], st[2], st[6])\n    print(\"For drifts anywhere within \" + str(st[5]) + \"%, \" + timer_text(st[6]) + \":\")\n    if m[0] < 0:\n        print(\"  No margin shorter than the lease removes two leaders here.\")\n    else:\n        print(\"  The smallest margin with no two leaders for any of them is \" + str(m[0]) + \" ms; at that margin, no leader for up to \" + str(m[3]) + \" ms.\")\n        if m[1] < m[2]:\n            print(\"  Each pair alone would need from \" + str(m[1]) + \" to \" + str(m[2]) + \" ms. The margin has to cover the worst pair, A's clock slow and the store's fast, and the others pay for it in time without a leader.\")\n    other = \"receive\"\n    if st[6] == \"receive\":\n        other = \"send\"\n    m2 = lease.safe_margin(st[0], st[5], st[1], st[2], other)\n    if m2[0] < 0:\n        print(\"  \" + timer_text(other) + \" instead, no margin shorter than the lease would do.\")\n    else:\n        print(\"  \" + timer_text(other) + \" instead, the smallest such margin is \" + str(m2[0]) + \" ms.\")\n\ndef ask_int(prompt, low, high):\n    t = plain(input(prompt))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return [False, 0]\n    sign = 1\n    digits = t\n    if t[0] == \"-\":\n        sign = 0 - 1\n        digits = t[1:len(t)]\n    if not is_digits(digits) or len(digits) > 4 or sign * int(digits) < low or sign * int(digits) > high:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return [False, 0]\n    return [True, sign * int(digits)]\n\ndef lease_and_delays(st):\n    l = ask_int(\"lease (100 to 5000 ms)> \", 100, 5000)\n    if not l[0]:\n        return st\n    if l[1] <= st[7]:\n        print(\"The lease must be longer than the margin, \" + str(st[7]) + \" ms.\")\n        return st\n    da = ask_int(\"A's delay to the store (0 to 100 ms)> \", 0, 100)\n    if not da[0]:\n        return st\n    db = ask_int(\"B's delay to the store (0 to 100 ms)> \", 0, 100)\n    if not db[0]:\n        return st\n    out = [l[1], da[1], db[1], st[3], st[4], st[5], st[6], st[7]]\n    show(out)\n    return out\n\ndef clocks(st):\n    a = ask_int(\"A's clock drift (-10 to 10 percent)> \", 0 - 10, 10)\n    if not a[0]:\n        return st\n    s = ask_int(\"the store's clock drift (-10 to 10 percent)> \", 0 - 10, 10)\n    if not s[0]:\n        return st\n    b = ask_int(\"drift bound for the tables (0 to 10 percent)> \", 0, 10)\n    if not b[0]:\n        return st\n    t = plain(input(\"A times its lease from (send or receive)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return st\n    if t != \"send\" and t != \"receive\":\n        print(\"Type send or receive.\")\n        return st\n    out = [st[0], st[1], st[2], a[1], s[1], b[1], t, st[7]]\n    show(out)\n    return out\n\ndef margin(st):\n    m = ask_int(\"margin (0 to \" + str(st[0] - 1) + \" ms)> \", 0, st[0] - 1)\n    if not m[0]:\n        return st\n    out = [st[0], st[1], st[2], st[3], st[4], st[5], st[6], m[1]]\n    show(out)\n    return out\n\nst = [1000, 30, 10, 0, 0, 5, \"send\", 0]\nprint(\"== Leader lease ==\")\nprint(\"A holds a lease from a store and then goes quiet; B takes over when the store's clock says the lease has run out, while A trusts its own clock.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show(st)\n    elif choice == \"2\":\n        one_lease(st)\n    elif choice == \"3\":\n        every(st)\n    elif choice == \"4\":\n        find(st)\n    elif choice == \"5\":\n        st = lease_and_delays(st)\n    elif choice == \"6\":\n        st = clocks(st)\n    elif choice == \"7\":\n        st = margin(st)\n    elif choice == \"8\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 8.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "lease.eml",
      "eml": "# P120 leader lease - A holds a lease from a store, then goes quiet; B\n# takes over when the store's clock says the lease has run out. A believes\n# it leads until its own clock says so. As in the corpus case\n# leader-lease-expiry, nobody is wrong about their own clock, and two\n# leaders overlap when A's lease outlasts the store's in real time. A\n# margin makes A stop early.\n#\n# Times are whole milliseconds of real time. A clock that drifts d percent\n# counts (100 + d) ms for every 100 real ones. A's request reaches the\n# store after A's delay, and the answer takes as long again; B is always\n# asking, and hears after B's delay.\n\ndef ceil_div(p, q):\n    # the smallest whole number at least p / q, for q > 0\n    int(p / q) => f\n    while f * q < p:\n        f + 1 => f\n    while (f - 1) * q >= p:\n        f - 1 => f\n    return f\n\ndef floor_div(p, q):\n    ceil_div(p, q) => f\n    if f * q > p:\n        f - 1 => f\n    return f\n\ndef timer_start(da, timer):\n    # A counts its lease from when it asks, or from when it hears\n    if timer == \"send\":\n        return 0\n    return 2 * da\n\ndef a_stops(lease, margin, a, da, timer):\n    # the first real ms at which A's clock has counted the lease less the\n    # margin\n    return timer_start(da, timer) + ceil_div((lease - margin) * 100, 100 + a)\n\ndef store_frees(lease, s, da):\n    # the first real ms at which the store's clock has counted the lease\n    # since it granted it to A\n    return ceil_div(da * (100 + s) + lease * 100, 100 + s)\n\ndef b_starts(lease, s, da, db):\n    return store_frees(lease, s, da) + db\n\ndef outcome(lease, margin, a, s, da, db, timer):\n    # [ms with two leaders, ms with none, A stops, B starts]; A leads from\n    # when it hears, 2 * da\n    a_stops(lease, margin, a, da, timer) => ea\n    b_starts(lease, s, da, db) => sb\n    2 * da => heard\n    ea - max2(sb, heard) => two\n    sb - max2(ea, heard) => none\n    return [max2(two, 0), max2(none, 0), ea, sb]\n\ndef max2(x, y):\n    if x > y:\n        return x\n    return y\n\ndef smallest_margin(lease, a, s, da, db, timer):\n    # the smallest margin with no two leaders for these drifts, or -1 if\n    # none below the lease\n    max2(b_starts(lease, s, da, db), 2 * da) - timer_start(da, timer) => room\n    if room <= 0:\n        return 0 - 1\n    max2(0, lease - floor_div(room * (100 + a), 100)) => m\n    if m >= lease:\n        return 0 - 1\n    return m\n\ndef grid(lease, margin, bound, da, db, timer):\n    # rows of [A's drift, overlaps for the store's drifts -bound..bound],\n    # and [pairs with two leaders, most two, most none]\n    [] => rows\n    0 => pairs\n    0 => most_two\n    0 => most_none\n    for a in [0 - bound:bound]:\n        [] => cells\n        for s in [0 - bound:bound]:\n            outcome(lease, margin, a, s, da, db, timer) => o\n            cells + [o[0]] => cells\n            if o[0] > 0:\n                pairs + 1 => pairs\n            if o[0] > most_two:\n                o[0] => most_two\n            if o[1] > most_none:\n                o[1] => most_none\n        rows + [[a, cells]] => rows\n    return [rows, [pairs, most_two, most_none]]\n\ndef safe_margin(lease, bound, da, db, timer):\n    # [the smallest margin with no two leaders for any drifts within the\n    #  bound (-1 if none), the smallest and largest margin a single pair\n    #  needs, the most ms with no leader at the safe margin]\n    0 => worst\n    lease => least\n    for a in [0 - bound:bound]:\n        for s in [0 - bound:bound]:\n            smallest_margin(lease, a, s, da, db, timer) => m\n            if m < 0:\n                return [0 - 1, 0, 0, 0]\n            if m > worst:\n                m => worst\n            if m < least:\n                m => least\n    0 => gap\n    for a in [0 - bound:bound]:\n        for s in [0 - bound:bound]:\n            outcome(lease, worst, a, s, da, db, timer)[1] => g\n            if g > gap:\n                g => gap\n    return [worst, least, worst, gap]\n",
      "python": "def ceil_div(p, q):\n    f = int(p / q)\n    while f * q < p:\n        f = f + 1\n    while (f - 1) * q >= p:\n        f = f - 1\n    return f\n\ndef floor_div(p, q):\n    f = ceil_div(p, q)\n    if f * q > p:\n        f = f - 1\n    return f\n\ndef timer_start(da, timer):\n    if timer == \"send\":\n        return 0\n    return 2 * da\n\ndef a_stops(lease, margin, a, da, timer):\n    return timer_start(da, timer) + ceil_div((lease - margin) * 100, 100 + a)\n\ndef store_frees(lease, s, da):\n    return ceil_div(da * (100 + s) + lease * 100, 100 + s)\n\ndef b_starts(lease, s, da, db):\n    return store_frees(lease, s, da) + db\n\ndef outcome(lease, margin, a, s, da, db, timer):\n    ea = a_stops(lease, margin, a, da, timer)\n    sb = b_starts(lease, s, da, db)\n    heard = 2 * da\n    two = ea - max2(sb, heard)\n    none = sb - max2(ea, heard)\n    return [max2(two, 0), max2(none, 0), ea, sb]\n\ndef max2(x, y):\n    if x > y:\n        return x\n    return y\n\ndef smallest_margin(lease, a, s, da, db, timer):\n    room = max2(b_starts(lease, s, da, db), 2 * da) - timer_start(da, timer)\n    if room <= 0:\n        return 0 - 1\n    m = max2(0, lease - floor_div(room * (100 + a), 100))\n    if m >= lease:\n        return 0 - 1\n    return m\n\ndef grid(lease, margin, bound, da, db, timer):\n    rows = []\n    pairs = 0\n    most_two = 0\n    most_none = 0\n    for a in range(0 - bound, bound+1):\n        cells = []\n        for s in range(0 - bound, bound+1):\n            o = outcome(lease, margin, a, s, da, db, timer)\n            cells = cells + [o[0]]\n            if o[0] > 0:\n                pairs = pairs + 1\n            if o[0] > most_two:\n                most_two = o[0]\n            if o[1] > most_none:\n                most_none = o[1]\n        rows = rows + [[a, cells]]\n    return [rows, [pairs, most_two, most_none]]\n\ndef safe_margin(lease, bound, da, db, timer):\n    worst = 0\n    least = lease\n    for a in range(0 - bound, bound+1):\n        for s in range(0 - bound, bound+1):\n            m = smallest_margin(lease, a, s, da, db, timer)\n            if m < 0:\n                return [0 - 1, 0, 0, 0]\n            if m > worst:\n                worst = m\n            if m < least:\n                least = m\n    gap = 0\n    for a in range(0 - bound, bound+1):\n        for s in range(0 - bound, bound+1):\n            g = outcome(lease, worst, a, s, da, db, timer)[1]\n            if g > gap:\n                gap = g\n    return [worst, least, worst, gap]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n5\n\n5\n50\n5\n6000\n5\nx\n5\n1000\n\n5\n1000\n101\n5\n1000\n30\n\n5\n1000\n30\n-1\n6\n\n6\n11\n6\n-11\n6\nx\n6\n0\n\n6\n0\n0\n\n6\n0\n0\n11\n6\n0\n0\n5\n\n6\n0\n0\n5\nboth\n7\n\n7\n1000\n7\n-1\n7\nx\n7\n500\n5\n400\n7\n999\n2\n8\n",
      "screen": "== Leader lease ==\nA holds a lease from a store and then goes quiet; B takes over when the store's clock says the lease has run out, while A trusts its own clock.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 0\nPick a number from 1 to 8.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> x\nPick a number from 1 to 8.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 50\nType a whole number from 100 to 5000.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 6000\nType a whole number from 100 to 5000.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> x\nType a whole number from 100 to 5000.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 1000\nA's delay to the store (0 to 100 ms)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 1000\nA's delay to the store (0 to 100 ms)> 101\nType a whole number from 0 to 100.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 1000\nA's delay to the store (0 to 100 ms)> 30\nB's delay to the store (0 to 100 ms)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 1000\nA's delay to the store (0 to 100 ms)> 30\nB's delay to the store (0 to 100 ms)> -1\nType a whole number from 0 to 100.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 11\nType a whole number from -10 to 10.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> -11\nType a whole number from -10 to 10.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> x\nType a whole number from -10 to 10.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> 11\nType a whole number from 0 to 10.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> 5\nA times its lease from (send or receive)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> 5\nA times its lease from (send or receive)> both\nType send or receive.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> \nCancelled.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> 1000\nType a whole number from 0 to 999.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> -1\nType a whole number from 0 to 999.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> x\nType a whole number from 0 to 999.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> 500\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts 0%, the store's 0%; the tables cover drifts within 5%.\nA timing from when it asks; a margin of 500 ms, so A stops when its clock has counted 500 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 5\nlease (100 to 5000 ms)> 400\nThe lease must be longer than the margin, 500 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> 999\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts 0%, the store's 0%; the tables cover drifts within 5%.\nA timing from when it asks; a margin of 999 ms, so A stops when its clock has counted 1 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 2\nOne lease, A timing from when it asks:\n       0 ms  A asks the store for the lease, and starts its timer\n       1 ms  A's clock has counted 1 ms, the lease less the margin, before A has heard\n      30 ms  the store grants it to A, for 1000 ms on the store's clock\n      60 ms  A hears, but by its clock the lease is already over, so it never leads\n    1030 ms  the store's clock has counted the lease: B's request is granted\n    1040 ms  B hears, and acts as leader\nNo leader for 980 ms, from 60 to 1040 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n4\n6\n0\n0\n5\nreceive\n2\n4\n6\n-5\n0\n5\nsend\n2\n3\n7\n57\n3\n2\n6\n0\n0\n10\nsend\n4\n8\n",
      "screen": "== Leader lease ==\nA holds a lease from a store and then goes quiet; B takes over when the store's clock says the lease has run out, while A trusts its own clock.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 1\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts 0%, the store's 0%; the tables cover drifts within 5%.\nA timing from when it asks; a margin of 0 ms, so A stops when its clock has counted 1000 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 2\nOne lease, A timing from when it asks:\n       0 ms  A asks the store for the lease, and starts its timer\n      30 ms  the store grants it to A, for 1000 ms on the store's clock\n      60 ms  A hears, and acts as leader\n    1000 ms  A's clock has counted 1000 ms: A stops\n    1030 ms  the store's clock has counted the lease: B's request is granted\n    1040 ms  B hears, and acts as leader\nNo leader for 40 ms, from 1000 to 1040 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 3\nTwo leaders, in ms: A's clock drift down the side, the store's across; a margin of 0 ms, A timing from when it asks.\n             -5%   -4%   -3%   -2%   -1%    0%    1%    2%    3%    4%    5%\n  A -5%        0     0     0     0     2    13    22    32    42    51    60\n  A -4%        0     0     0     0     0     2    11    21    31    40    49\n  A -3%        0     0     0     0     0     0     0    10    20    29    38\n  A -2%        0     0     0     0     0     0     0     0    10    19    28\n  A -1%        0     0     0     0     0     0     0     0     0     9    18\n  A 0%         0     0     0     0     0     0     0     0     0     0     7\n  A 1%         0     0     0     0     0     0     0     0     0     0     0\n  A 2%         0     0     0     0     0     0     0     0     0     0     0\n  A 3%         0     0     0     0     0     0     0     0     0     0     0\n  A 4%         0     0     0     0     0     0     0     0     0     0     0\n  A 5%         0     0     0     0     0     0     0     0     0     0     0\nTwo leaders in 23 of the 121 pairs, for up to 60 ms; no leader for up to 140 ms.\nA slow clock keeps A leading longer in real time, and a fast store frees the lease sooner.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 4\nFor drifts anywhere within 5%, A timing from when it asks:\n  The smallest margin with no two leaders for any of them is 57 ms; at that margin, no leader for up to 194 ms.\n  Each pair alone would need from 0 to 57 ms. The margin has to cover the worst pair, A's clock slow and the store's fast, and the others pay for it in time without a leader.\n  A timing from when it hears instead, the smallest such margin is 114 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> 5\nA times its lease from (send or receive)> receive\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts 0%, the store's 0%; the tables cover drifts within 5%.\nA timing from when it hears; a margin of 0 ms, so A stops when its clock has counted 1000 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 2\nOne lease, A timing from when it hears:\n       0 ms  A asks the store for the lease\n      30 ms  the store grants it to A, for 1000 ms on the store's clock\n      60 ms  A hears, starts its timer, and acts as leader\n    1030 ms  the store's clock has counted the lease: B's request is granted\n    1040 ms  B hears, and acts as leader\n    1060 ms  A's clock has counted 1000 ms: A stops\nTwo leaders for 20 ms, from 1040 to 1060 ms: A still trusts its lease, and B holds the new one.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 4\nFor drifts anywhere within 5%, A timing from when it hears:\n  The smallest margin with no two leaders for any of them is 114 ms; at that margin, no leader for up to 189 ms.\n  Each pair alone would need from 0 to 114 ms. The margin has to cover the worst pair, A's clock slow and the store's fast, and the others pay for it in time without a leader.\n  A timing from when it asks instead, the smallest such margin is 57 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> -5\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> 5\nA times its lease from (send or receive)> send\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts -5%, the store's 0%; the tables cover drifts within 5%.\nA timing from when it asks; a margin of 0 ms, so A stops when its clock has counted 1000 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 2\nOne lease, A timing from when it asks:\n       0 ms  A asks the store for the lease, and starts its timer\n      30 ms  the store grants it to A, for 1000 ms on the store's clock\n      60 ms  A hears, and acts as leader\n    1030 ms  the store's clock has counted the lease: B's request is granted\n    1040 ms  B hears, and acts as leader\n    1053 ms  A's clock, running 5% slow, has counted 1000 ms: A stops\nTwo leaders for 13 ms, from 1040 to 1053 ms: A still trusts its lease, and B holds the new one.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 3\nTwo leaders, in ms: A's clock drift down the side, the store's across; a margin of 0 ms, A timing from when it asks.\n             -5%   -4%   -3%   -2%   -1%    0%    1%    2%    3%    4%    5%\n  A -5%        0     0     0     0     2    13    22    32    42    51    60\n  A -4%        0     0     0     0     0     2    11    21    31    40    49\n  A -3%        0     0     0     0     0     0     0    10    20    29    38\n  A -2%        0     0     0     0     0     0     0     0    10    19    28\n  A -1%        0     0     0     0     0     0     0     0     0     9    18\n  A 0%         0     0     0     0     0     0     0     0     0     0     7\n  A 1%         0     0     0     0     0     0     0     0     0     0     0\n  A 2%         0     0     0     0     0     0     0     0     0     0     0\n  A 3%         0     0     0     0     0     0     0     0     0     0     0\n  A 4%         0     0     0     0     0     0     0     0     0     0     0\n  A 5%         0     0     0     0     0     0     0     0     0     0     0\nTwo leaders in 23 of the 121 pairs, for up to 60 ms; no leader for up to 140 ms.\nA slow clock keeps A leading longer in real time, and a fast store frees the lease sooner.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 7\nmargin (0 to 999 ms)> 57\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts -5%, the store's 0%; the tables cover drifts within 5%.\nA timing from when it asks; a margin of 57 ms, so A stops when its clock has counted 943 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 3\nTwo leaders, in ms: A's clock drift down the side, the store's across; a margin of 57 ms, A timing from when it asks.\n             -5%   -4%   -3%   -2%   -1%    0%    1%    2%    3%    4%    5%\n  A -5%        0     0     0     0     0     0     0     0     0     0     0\n  A -4%        0     0     0     0     0     0     0     0     0     0     0\n  A -3%        0     0     0     0     0     0     0     0     0     0     0\n  A -2%        0     0     0     0     0     0     0     0     0     0     0\n  A -1%        0     0     0     0     0     0     0     0     0     0     0\n  A 0%         0     0     0     0     0     0     0     0     0     0     0\n  A 1%         0     0     0     0     0     0     0     0     0     0     0\n  A 2%         0     0     0     0     0     0     0     0     0     0     0\n  A 3%         0     0     0     0     0     0     0     0     0     0     0\n  A 4%         0     0     0     0     0     0     0     0     0     0     0\n  A 5%         0     0     0     0     0     0     0     0     0     0     0\nNo two leaders in any of the 121 pairs; no leader for up to 194 ms.\nA slow clock keeps A leading longer in real time, and a fast store frees the lease sooner.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 2\nOne lease, A timing from when it asks:\n       0 ms  A asks the store for the lease, and starts its timer\n      30 ms  the store grants it to A, for 1000 ms on the store's clock\n      60 ms  A hears, and acts as leader\n     993 ms  A's clock, running 5% slow, has counted 943 ms, the lease less the margin: A stops\n    1030 ms  the store's clock has counted the lease: B's request is granted\n    1040 ms  B hears, and acts as leader\nNo leader for 47 ms, from 993 to 1040 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 6\nA's clock drift (-10 to 10 percent)> 0\nthe store's clock drift (-10 to 10 percent)> 0\ndrift bound for the tables (0 to 10 percent)> 10\nA times its lease from (send or receive)> send\nLease 1000 ms. A's delay to the store 30 ms each way, B's 10 ms.\nA's clock drifts 0%, the store's 0%; the tables cover drifts within 10%.\nA timing from when it asks; a margin of 57 ms, so A stops when its clock has counted 943 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 4\nFor drifts anywhere within 10%, A timing from when it asks:\n  The smallest margin with no two leaders for any of them is 145 ms; at that margin, no leader for up to 374 ms.\n  Each pair alone would need from 0 to 145 ms. The margin has to cover the worst pair, A's clock slow and the store's fast, and the others pay for it in time without a leader.\n  A timing from when it hears instead, the smallest such margin is 199 ms.\n\n1) settings  2) one lease  3) every drift  4) find a margin  5) lease and delays  6) clocks  7) margin  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "leader-lease-expiry",
      "caseId": "252-leader-lease-expiry",
      "title": "Leader lease expiry — two leaders, no bug"
    }
  ],
  "updated": "2026-10-16"
}
