{
  "id": "P130",
  "slug": "lost-update",
  "key": "P130-lost-update",
  "title": "Lost update",
  "summary": "Users reading, adding to and writing back one shared value by a schedule, with and without a version check: every order of their turns counted, and only the serial ones keep every update without the check.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P130 - Lost update\n\nUsers each read a shared value, add their own amount and write it back,\none step a turn, in the order a schedule gives. A write made from a value\nread before someone else's write throws that write away.\n\n- `main.eml` - the menu, the users, one schedule with and without a\n  version check, every order counted, the amounts, and two or three users\n- `update.eml` - a user's turn, a run of a schedule, every order of the\n  users' turns, and the counts\n\nHow each part works:\n\n- As in the corpus case `lost-update-schedule`, each user takes three\n  turns: read, add, write. The value starts at 100; users add +10, +5\n  and -3. The value carries whose updates it holds, so a lost update is\n  named even when the amounts would cancel out.\n- Without a check, an order loses an update exactly when one user's read\n  and write enclose another user's write. Only the orders that run the\n  users one after another keep every update: 18 of the 20 orders of two\n  users lose one, and 1674 of the 1680 orders of three.\n- With a version check, a write goes through only if nobody has written\n  since its read; otherwise it is refused, and the user reads again and\n  starts over. No order loses an update; two users need 18 writes done\n  again over the 20 orders, three need 2970.\n- Turns left when the schedule ends are taken by the users still going,\n  lowest number first.\n\nWhat is checked: menu choices 1 to 6; a schedule of user numbers, up to\n30 turns; three amounts from -99 to 99, not 0; 2 or 3 users. An empty\nanswer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the users, then 1 1 1 2 2 2 (one after another) and 1 2 1 2 1 2, where\n  user 1's update is lost without a check;\n- every order, for two and three users;\n- three users with 1 2 3 three times: two updates lost, two writes done\n  again with the check.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- schedules: a cancel, 1 3, 1 x and 31 turns;\n- amounts: a cancel, two amounts, 0, x and 100, then 5 -5 3;\n- users: a cancel and 4, then 3, and a schedule that loses user 1's +5.\n\nBuilt on the verified corpus case `lost-update-schedule` (a read, modify\nand write that loses another user's write when the steps interleave).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P130 lost update: two or three users reading, adding and writing back\n# one shared value by a schedule, with and without a version check, and\n# every interleaving counted.\nimport update\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 lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef right_value(start, deltas):\n    start => r\n    for d in deltas:\n        r + d => r\n    return r\n\ndef show(start, deltas):\n    \"\" => line\n    for u in [0:len(deltas) - 1]:\n        if line != \"\":\n            line + \", \" => line\n        line + \"user \" + str(u + 1) + \" adds \" + update.signed(deltas[u]) => line\n    (\"The value starts at \" + str(start) + \"; \" + line + \". Each user reads, adds, then writes: three turns.\") ^0\n    (\"Every update kept, it ends at \" + str(right_value(start, deltas)) + \".\") ^0\n\ndef one(start, deltas):\n    len(deltas) => n\n    (\"A schedule: whose turn it is, users 1 to \" + str(n) + \", such as 1 2 1 2 1 2 (up to 30 turns).\") ^0\n    plain(input(\"schedule> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    [] => sched\n    \"\" => w\n    for c in t + \" \":\n        if c == \" \":\n            if w != \"\":\n                if len(w) != 1 or w[0] < \"1\" or w[0] > str(n):\n                    (\"Type user numbers from 1 to \" + str(n) + \", separated by spaces.\") ^0\n                    return\n                sched + [int(w) - 1] => sched\n            \"\" => w\n        else:\n            w + c => w\n    if len(sched) > 30:\n        \"Up to 30 turns.\" ^0\n        return\n    right_value(start, deltas) => right\n    for checked in [False, True]:\n        update.run(start, deltas, sched, checked) => r\n        if checked:\n            \"With a version check:\" ^0\n        else:\n            \"Without a check:\" ^0\n        for k in [0:len(r[0]) - 1]:\n            r[0][k] => e\n            \"  \" => mark\n            if k >= len(sched):\n                \"  then \" => mark\n            (\"  \" + lpad(str(k + 1), 2) + mark + \"user \" + str(e[0] + 1) + \" \" + e[1]) ^0\n        if len(r[4]) == 0:\n            (\"  ends at \" + str(r[1]) + \": every update kept\") ^0\n        else:\n            \"\" => who\n            for u in r[4]:\n                if who != \"\":\n                    who + \" and \" => who\n                who + \"user \" + str(u + 1) + \"'s\" => who\n            if len(r[4]) == 1:\n                (\"  ends at \" + str(r[1]) + \", where every update kept would make \" + str(right) + \": \" + who + \" update was lost, written over by a value read before it\") ^0\n            else:\n                (\"  ends at \" + str(r[1]) + \", where every update kept would make \" + str(right) + \": \" + who + \" updates were lost, written over by values read before them\") ^0\n        if r[2] == 1:\n            (\"  1 write refused and done again; \" + str(r[3]) + \" turns in all\") ^0\n        elif r[2] > 1:\n            (\"  \" + str(r[2]) + \" writes refused and done again; \" + str(r[3]) + \" turns in all\") ^0\n\ndef every(start, deltas):\n    for n in [2:3]:\n        [] => ds\n        for u in [0:n - 1]:\n            ds + [deltas[u]] => ds\n        update.census(start, ds, False) => a\n        update.census(start, ds, True) => b\n        (str(n) + \" users, \" + str(a[0]) + \" orders of their turns:\") ^0\n        (\"  without a check:      \" + str(a[1]) + \" lose an update\") ^0\n        (\"  with a version check: \" + str(b[1]) + \" lose one; \" + str(b[2]) + \" writes refused and done again in all, at most \" + str(b[3]) + \" turns in one order\") ^0\n    \"An order loses an update when one user's read and write enclose another user's write; run one after another, the users never lose one.\" ^0\n\ndef set_amounts(deltas):\n    (\"Amounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\") ^0\n    plain(input(\"amounts> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return deltas\n    [] => out\n    \"\" => w\n    for c in t + \" \":\n        if c == \" \":\n            if w != \"\":\n                1 => sign\n                w => d\n                if d[0] == \"-\":\n                    0 - 1 => sign\n                    d[1:len(d)] => d\n                True => digits\n                if len(d) == 0 or len(d) > 2:\n                    False => digits\n                for ch in d:\n                    if ch < \"0\" or ch > \"9\":\n                        False => digits\n                if not digits:\n                    \"Each amount is a whole number from -99 to 99, not 0.\" ^0\n                    return deltas\n                if int(d) == 0:\n                    \"Each amount is a whole number from -99 to 99, not 0.\" ^0\n                    return deltas\n                out + [sign * int(d)] => out\n            \"\" => w\n        else:\n            w + c => w\n    if len(out) != 3:\n        \"Type three amounts.\" ^0\n        return deltas\n    (\"Users now add \" + update.signed(out[0]) + \", \" + update.signed(out[1]) + \" and \" + update.signed(out[2]) + \".\") ^0\n    return out\n\n100 => start\n[10, 5, 0 - 3] => deltas\n2 => users\n\n\"== Lost update ==\" ^0\n\"Users each read a shared value, add their own amount and write it back, one step a turn, in the order a schedule gives.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    [] => ds\n    for u in [0:users - 1]:\n        ds + [deltas[u]] => ds\n    if choice == \"1\":\n        show(start, ds)\n    elif choice == \"2\":\n        one(start, ds)\n    elif choice == \"3\":\n        every(start, deltas)\n    elif choice == \"4\":\n        set_amounts(deltas) => deltas\n    elif choice == \"5\":\n        plain(input(\"users (2 or 3)> \")) => t\n        if t == \"2\" or t == \"3\":\n            int(t) => users\n            (\"Now \" + t + \" users.\") ^0\n        elif t == \"\":\n            \"Cancelled.\" ^0\n        else:\n            \"Type 2 or 3.\" ^0\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import update\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 lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef right_value(start, deltas):\n    r = start\n    for d in deltas:\n        r = r + d\n    return r\n\ndef show(start, deltas):\n    line = \"\"\n    for u in range(0, len(deltas)):\n        if line != \"\":\n            line = line + \", \"\n        line = line + \"user \" + str(u + 1) + \" adds \" + update.signed(deltas[u])\n    print(\"The value starts at \" + str(start) + \"; \" + line + \". Each user reads, adds, then writes: three turns.\")\n    print(\"Every update kept, it ends at \" + str(right_value(start, deltas)) + \".\")\n\ndef one(start, deltas):\n    n = len(deltas)\n    print(\"A schedule: whose turn it is, users 1 to \" + str(n) + \", such as 1 2 1 2 1 2 (up to 30 turns).\")\n    t = plain(input(\"schedule> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    sched = []\n    w = \"\"\n    for c in t + \" \":\n        if c == \" \":\n            if w != \"\":\n                if len(w) != 1 or w[0] < \"1\" or w[0] > str(n):\n                    print(\"Type user numbers from 1 to \" + str(n) + \", separated by spaces.\")\n                    return\n                sched = sched + [int(w) - 1]\n            w = \"\"\n        else:\n            w = w + c\n    if len(sched) > 30:\n        print(\"Up to 30 turns.\")\n        return\n    right = right_value(start, deltas)\n    for checked in [False, True]:\n        r = update.run(start, deltas, sched, checked)\n        if checked:\n            print(\"With a version check:\")\n        else:\n            print(\"Without a check:\")\n        for k in range(0, len(r[0])):\n            e = r[0][k]\n            mark = \"  \"\n            if k >= len(sched):\n                mark = \"  then \"\n            print(\"  \" + lpad(str(k + 1), 2) + mark + \"user \" + str(e[0] + 1) + \" \" + e[1])\n        if len(r[4]) == 0:\n            print(\"  ends at \" + str(r[1]) + \": every update kept\")\n        else:\n            who = \"\"\n            for u in r[4]:\n                if who != \"\":\n                    who = who + \" and \"\n                who = who + \"user \" + str(u + 1) + \"'s\"\n            if len(r[4]) == 1:\n                print(\"  ends at \" + str(r[1]) + \", where every update kept would make \" + str(right) + \": \" + who + \" update was lost, written over by a value read before it\")\n            else:\n                print(\"  ends at \" + str(r[1]) + \", where every update kept would make \" + str(right) + \": \" + who + \" updates were lost, written over by values read before them\")\n        if r[2] == 1:\n            print(\"  1 write refused and done again; \" + str(r[3]) + \" turns in all\")\n        elif r[2] > 1:\n            print(\"  \" + str(r[2]) + \" writes refused and done again; \" + str(r[3]) + \" turns in all\")\n\ndef every(start, deltas):\n    for n in range(2, 4):\n        ds = []\n        for u in range(0, n):\n            ds = ds + [deltas[u]]\n        a = update.census(start, ds, False)\n        b = update.census(start, ds, True)\n        print(str(n) + \" users, \" + str(a[0]) + \" orders of their turns:\")\n        print(\"  without a check:      \" + str(a[1]) + \" lose an update\")\n        print(\"  with a version check: \" + str(b[1]) + \" lose one; \" + str(b[2]) + \" writes refused and done again in all, at most \" + str(b[3]) + \" turns in one order\")\n    print(\"An order loses an update when one user's read and write enclose another user's write; run one after another, the users never lose one.\")\n\ndef set_amounts(deltas):\n    print(\"Amounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\")\n    t = plain(input(\"amounts> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return deltas\n    out = []\n    w = \"\"\n    for c in t + \" \":\n        if c == \" \":\n            if w != \"\":\n                sign = 1\n                d = w\n                if d[0] == \"-\":\n                    sign = 0 - 1\n                    d = d[1:len(d)]\n                digits = True\n                if len(d) == 0 or len(d) > 2:\n                    digits = False\n                for ch in d:\n                    if ch < \"0\" or ch > \"9\":\n                        digits = False\n                if not digits:\n                    print(\"Each amount is a whole number from -99 to 99, not 0.\")\n                    return deltas\n                if int(d) == 0:\n                    print(\"Each amount is a whole number from -99 to 99, not 0.\")\n                    return deltas\n                out = out + [sign * int(d)]\n            w = \"\"\n        else:\n            w = w + c\n    if len(out) != 3:\n        print(\"Type three amounts.\")\n        return deltas\n    print(\"Users now add \" + update.signed(out[0]) + \", \" + update.signed(out[1]) + \" and \" + update.signed(out[2]) + \".\")\n    return out\n\nstart = 100\ndeltas = [10, 5, 0 - 3]\nusers = 2\nprint(\"== Lost update ==\")\nprint(\"Users each read a shared value, add their own amount and write it back, one step a turn, in the order a schedule gives.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\")\n    choice = plain(input(\"choice> \"))\n    ds = []\n    for u in range(0, users):\n        ds = ds + [deltas[u]]\n    if choice == \"1\":\n        show(start, ds)\n    elif choice == \"2\":\n        one(start, ds)\n    elif choice == \"3\":\n        every(start, deltas)\n    elif choice == \"4\":\n        deltas = set_amounts(deltas)\n    elif choice == \"5\":\n        t = plain(input(\"users (2 or 3)> \"))\n        if t == \"2\" or t == \"3\":\n            users = int(t)\n            print(\"Now \" + t + \" users.\")\n        elif t == \"\":\n            print(\"Cancelled.\")\n        else:\n            print(\"Type 2 or 3.\")\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "update.eml",
      "eml": "# P130 lost update - users each read a shared value, add their own amount,\n# and write it back, one step a turn as a schedule says. As in the corpus\n# case lost-update-schedule, a write made from a value read before someone\n# else's write throws that write away. With a version check, a write goes\n# through only if nobody has written since its read; otherwise the user\n# reads again and starts over.\n#\n# A user's state is [step, value read, version read, whose updates the value\n# read holds]; step 0 reads, 1 adds, 2 writes, 3 is done. The shared value\n# is [value, version, whose updates it holds].\n\ndef new_state(n):\n    [] => out\n    for i in [1:n]:\n        out + [[0, 0, 0, []]] => out\n    return out\n\ndef turn(users, shared, u, deltas, checked):\n    # one turn for user u: [what happened]; users and shared change in place\n    users[u] => s\n    if s[0] == 0:\n        shared[0] => s[1]\n        shared[1] => s[2]\n        shared[2] => s[3]\n        1 => s[0]\n        return \"reads \" + str(s[1])\n    if s[0] == 1:\n        s[1] + deltas[u] => s[1]\n        2 => s[0]\n        return \"adds \" + signed(deltas[u]) + \", making \" + str(s[1])\n    if s[0] == 2:\n        if checked and shared[1] != s[2]:\n            0 => s[0]\n            return \"finds the version moved from \" + str(s[2]) + \" to \" + str(shared[1]) + \": its write is refused, and it starts over\"\n        shared[0] => old\n        s[1] => shared[0]\n        shared[1] + 1 => shared[1]\n        s[3] + [u] => shared[2]\n        3 => s[0]\n        return \"writes \" + str(s[1]) + \" over \" + str(old)\n    return \"is done\"\n\ndef signed(n):\n    if n < 0:\n        return str(n)\n    return \"+\" + str(n)\n\ndef run(start, deltas, schedule, checked):\n    # [trace of [user, what], final value, refused writes, turns used, whose\n    # updates are lost]: the schedule, then every user still going, lowest\n    # first, until all done\n    len(deltas) => n\n    new_state(n) => users\n    [start, 0, []] => shared\n    [] => trace\n    0 => refused\n    for u in schedule:\n        turn(users, shared, u, deltas, checked) => w\n        trace + [[u, w]] => trace\n        if len(w) > 5 and w[0:5] == \"finds\":\n            refused + 1 => refused\n    True => going\n    0 => extra\n    while going and extra < 200:\n        False => going\n        for u in [0:n - 1]:\n            if not going and users[u][0] < 3:\n                turn(users, shared, u, deltas, checked) => w\n                trace + [[u, w]] => trace\n                if len(w) > 5 and w[0:5] == \"finds\":\n                    refused + 1 => refused\n                True => going\n                extra + 1 => extra\n    [] => lost\n    for u in [0:n - 1]:\n        False => kept\n        for v in shared[2]:\n            if v == u:\n                True => kept\n        if not kept:\n            lost + [u] => lost\n    return [trace, shared[0], refused, len(trace), lost]\n\ndef interleavings(n):\n    # every order of n users' three turns\n    [] => counts\n    for i in [1:n]:\n        counts + [3] => counts\n    [[[], counts]] => todo\n    [] => out\n    while len(todo) > 0:\n        todo[len(todo) - 1] => item\n        todo[0:len(todo) - 1] => todo\n        0 => left\n        for c in item[1]:\n            left + c => left\n        if left == 0:\n            out + [item[0]] => out\n        else:\n            for u in [0:n - 1]:\n                if item[1][u] > 0:\n                    [] => nc\n                    for v in [0:n - 1]:\n                        if v == u:\n                            nc + [item[1][v] - 1] => nc\n                        else:\n                            nc + [item[1][v]] => nc\n                    todo + [[item[0] + [u], nc]] => todo\n    return out\n\ndef census(start, deltas, checked):\n    # over every interleaving: [orders, orders losing an update, refused\n    # writes in all, the most turns any order needs]\n    0 => lost\n    0 => refused\n    0 => most\n    interleavings(len(deltas)) => orders\n    for o in orders:\n        run(start, deltas, o, checked) => r\n        if len(r[4]) > 0:\n            lost + 1 => lost\n        refused + r[2] => refused\n        if r[3] > most:\n            r[3] => most\n    return [len(orders), lost, refused, most]\n",
      "python": "def new_state(n):\n    out = []\n    for i in range(1, n+1):\n        out = out + [[0, 0, 0, []]]\n    return out\n\ndef turn(users, shared, u, deltas, checked):\n    s = users[u]\n    if s[0] == 0:\n        s[1] = shared[0]\n        s[2] = shared[1]\n        s[3] = shared[2]\n        s[0] = 1\n        return \"reads \" + str(s[1])\n    if s[0] == 1:\n        s[1] = s[1] + deltas[u]\n        s[0] = 2\n        return \"adds \" + signed(deltas[u]) + \", making \" + str(s[1])\n    if s[0] == 2:\n        if checked and shared[1] != s[2]:\n            s[0] = 0\n            return \"finds the version moved from \" + str(s[2]) + \" to \" + str(shared[1]) + \": its write is refused, and it starts over\"\n        old = shared[0]\n        shared[0] = s[1]\n        shared[1] = shared[1] + 1\n        shared[2] = s[3] + [u]\n        s[0] = 3\n        return \"writes \" + str(s[1]) + \" over \" + str(old)\n    return \"is done\"\n\ndef signed(n):\n    if n < 0:\n        return str(n)\n    return \"+\" + str(n)\n\ndef run(start, deltas, schedule, checked):\n    n = len(deltas)\n    users = new_state(n)\n    shared = [start, 0, []]\n    trace = []\n    refused = 0\n    for u in schedule:\n        w = turn(users, shared, u, deltas, checked)\n        trace = trace + [[u, w]]\n        if len(w) > 5 and w[0:5] == \"finds\":\n            refused = refused + 1\n    going = True\n    extra = 0\n    while going and extra < 200:\n        going = False\n        for u in range(0, n):\n            if not going and users[u][0] < 3:\n                w = turn(users, shared, u, deltas, checked)\n                trace = trace + [[u, w]]\n                if len(w) > 5 and w[0:5] == \"finds\":\n                    refused = refused + 1\n                going = True\n                extra = extra + 1\n    lost = []\n    for u in range(0, n):\n        kept = False\n        for v in shared[2]:\n            if v == u:\n                kept = True\n        if not kept:\n            lost = lost + [u]\n    return [trace, shared[0], refused, len(trace), lost]\n\ndef interleavings(n):\n    counts = []\n    for i in range(1, n+1):\n        counts = counts + [3]\n    todo = [[[], counts]]\n    out = []\n    while len(todo) > 0:\n        item = todo[len(todo) - 1]\n        todo = todo[0:len(todo) - 1]\n        left = 0\n        for c in item[1]:\n            left = left + c\n        if left == 0:\n            out = out + [item[0]]\n        else:\n            for u in range(0, n):\n                if item[1][u] > 0:\n                    nc = []\n                    for v in range(0, n):\n                        if v == u:\n                            nc = nc + [item[1][v] - 1]\n                        else:\n                            nc = nc + [item[1][v]]\n                    todo = todo + [[item[0] + [u], nc]]\n    return out\n\ndef census(start, deltas, checked):\n    lost = 0\n    refused = 0\n    most = 0\n    orders = interleavings(len(deltas))\n    for o in orders:\n        r = run(start, deltas, o, checked)\n        if len(r[4]) > 0:\n            lost = lost + 1\n        refused = refused + r[2]\n        if r[3] > most:\n            most = r[3]\n    return [len(orders), lost, refused, most]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n2\n\n2\n1 3\n2\n1 x\n2\n1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n4\n\n4\n10 5\n4\n10 0 5\n4\n10 x 5\n4\n100 5 5\n4\n5 -5 3\n5\n\n5\n4\n5\n3\n2\n1 2 1 2 1 2 3 3 3\n6\n",
      "screen": "== Lost update ==\nUsers each read a shared value, add their own amount and write it back, one step a turn, in the order a schedule gives.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 2, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> \nCancelled.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 2, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 3\nType user numbers from 1 to 2, separated by spaces.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 2, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 x\nType user numbers from 1 to 2, separated by spaces.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 2, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1\nUp to 30 turns.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 4\nAmounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\namounts> \nCancelled.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 4\nAmounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\namounts> 10 5\nType three amounts.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 4\nAmounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\namounts> 10 0 5\nEach amount is a whole number from -99 to 99, not 0.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 4\nAmounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\namounts> 10 x 5\nEach amount is a whole number from -99 to 99, not 0.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 4\nAmounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\namounts> 100 5 5\nEach amount is a whole number from -99 to 99, not 0.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 4\nAmounts for users 1, 2 and 3, such as 10 5 -3 (each from -99 to 99, not 0).\namounts> 5 -5 3\nUsers now add +5, -5 and +3.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 5\nusers (2 or 3)> \nCancelled.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 5\nusers (2 or 3)> 4\nType 2 or 3.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 5\nusers (2 or 3)> 3\nNow 3 users.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 3, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 2 1 2 1 2 3 3 3\nWithout a check:\n   1  user 1 reads 100\n   2  user 2 reads 100\n   3  user 1 adds +5, making 105\n   4  user 2 adds -5, making 95\n   5  user 1 writes 105 over 100\n   6  user 2 writes 95 over 105\n   7  user 3 reads 95\n   8  user 3 adds +3, making 98\n   9  user 3 writes 98 over 95\n  ends at 98, where every update kept would make 103: user 1's update was lost, written over by a value read before it\nWith a version check:\n   1  user 1 reads 100\n   2  user 2 reads 100\n   3  user 1 adds +5, making 105\n   4  user 2 adds -5, making 95\n   5  user 1 writes 105 over 100\n   6  user 2 finds the version moved from 0 to 1: its write is refused, and it starts over\n   7  user 3 reads 105\n   8  user 3 adds +3, making 108\n   9  user 3 writes 108 over 105\n  10  then user 2 reads 108\n  11  then user 2 adds -5, making 103\n  12  then user 2 writes 103 over 108\n  ends at 103: every update kept\n  1 write refused and done again; 12 turns in all\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n1 1 1 2 2 2\n2\n1 2 1 2 1 2\n3\n5\n3\n2\n1 2 3 1 2 3 1 2 3\n6\n",
      "screen": "== Lost update ==\nUsers each read a shared value, add their own amount and write it back, one step a turn, in the order a schedule gives.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 1\nThe value starts at 100; user 1 adds +10, user 2 adds +5. Each user reads, adds, then writes: three turns.\nEvery update kept, it ends at 115.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 2, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 1 1 2 2 2\nWithout a check:\n   1  user 1 reads 100\n   2  user 1 adds +10, making 110\n   3  user 1 writes 110 over 100\n   4  user 2 reads 110\n   5  user 2 adds +5, making 115\n   6  user 2 writes 115 over 110\n  ends at 115: every update kept\nWith a version check:\n   1  user 1 reads 100\n   2  user 1 adds +10, making 110\n   3  user 1 writes 110 over 100\n   4  user 2 reads 110\n   5  user 2 adds +5, making 115\n   6  user 2 writes 115 over 110\n  ends at 115: every update kept\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 2, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 2 1 2 1 2\nWithout a check:\n   1  user 1 reads 100\n   2  user 2 reads 100\n   3  user 1 adds +10, making 110\n   4  user 2 adds +5, making 105\n   5  user 1 writes 110 over 100\n   6  user 2 writes 105 over 110\n  ends at 105, where every update kept would make 115: user 1's update was lost, written over by a value read before it\nWith a version check:\n   1  user 1 reads 100\n   2  user 2 reads 100\n   3  user 1 adds +10, making 110\n   4  user 2 adds +5, making 105\n   5  user 1 writes 110 over 100\n   6  user 2 finds the version moved from 0 to 1: its write is refused, and it starts over\n   7  then user 2 reads 110\n   8  then user 2 adds +5, making 115\n   9  then user 2 writes 115 over 110\n  ends at 115: every update kept\n  1 write refused and done again; 9 turns in all\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 3\n2 users, 20 orders of their turns:\n  without a check:      18 lose an update\n  with a version check: 0 lose one; 18 writes refused and done again in all, at most 9 turns in one order\n3 users, 1680 orders of their turns:\n  without a check:      1674 lose an update\n  with a version check: 0 lose one; 2970 writes refused and done again in all, at most 15 turns in one order\nAn order loses an update when one user's read and write enclose another user's write; run one after another, the users never lose one.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 5\nusers (2 or 3)> 3\nNow 3 users.\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 2\nA schedule: whose turn it is, users 1 to 3, such as 1 2 1 2 1 2 (up to 30 turns).\nschedule> 1 2 3 1 2 3 1 2 3\nWithout a check:\n   1  user 1 reads 100\n   2  user 2 reads 100\n   3  user 3 reads 100\n   4  user 1 adds +10, making 110\n   5  user 2 adds +5, making 105\n   6  user 3 adds -3, making 97\n   7  user 1 writes 110 over 100\n   8  user 2 writes 105 over 110\n   9  user 3 writes 97 over 105\n  ends at 97, where every update kept would make 112: user 1's and user 2's updates were lost, written over by values read before them\nWith a version check:\n   1  user 1 reads 100\n   2  user 2 reads 100\n   3  user 3 reads 100\n   4  user 1 adds +10, making 110\n   5  user 2 adds +5, making 105\n   6  user 3 adds -3, making 97\n   7  user 1 writes 110 over 100\n   8  user 2 finds the version moved from 0 to 1: its write is refused, and it starts over\n   9  user 3 finds the version moved from 0 to 1: its write is refused, and it starts over\n  10  then user 2 reads 110\n  11  then user 2 adds +5, making 115\n  12  then user 2 writes 115 over 110\n  13  then user 3 reads 115\n  14  then user 3 adds -3, making 112\n  15  then user 3 writes 112 over 115\n  ends at 112: every update kept\n  2 writes refused and done again; 15 turns in all\n\n1) the users  2) one schedule  3) every order  4) amounts  5) two or three users  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "lost-update-schedule",
      "caseId": "223-lost-update-schedule",
      "title": "Every interleaving, written down"
    }
  ],
  "updated": "2026-10-17"
}
