<!-- canonical: https://efficientnewlanguage.org/eml-p/projects/P131-event-log/ | updated: 2026-10-17 -->

# P131 Event log

An account replayed from its events beside a stored balance column one write path forgets: a check that compares the column with itself, a replay that finds where they part, and how many orders of the events end where the log does.

EML-P project `projects/event-log` in the EML language repo: 2 module(s), entry `main.eml`, terminal UI. There, `eml project run projects/event-log` runs it and `eml project verify projects/event-log` replays every session under CPython (two hash seeds) and in the interpreter; the site build replays every session in the interpreter again and publishes a session only if its screen matches.

Built on verified corpus cases: denormalized-total-drifts (https://efficientnewlanguage.org/cases/295-denormalized-total-drifts/), state-from-events-vs-stored (https://efficientnewlanguage.org/cases/289-state-from-events-vs-stored/).

## Sessions

### bad-input - interpreter: byte-equal to the golden

Input:

```text
0
x
5
4

4
pay 10
4
deposit
4
deposit x
4
deposit 0
4
deposit 10000
4
fee 101
4
withdraw -5
4
adjust -15
4
5
5
5
5
5
3
5
1
2
3
5
6
```

Screen:

```text
== Event log ==
An account kept two ways: the balance replayed from its events, and a stored balance column that each write path updates.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 0
Pick a number from 1 to 6.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> x
Pick a number from 1 to 6.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 5 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> 
Cancelled.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> pay 10
Type deposit, withdraw, fee or adjust, then a number.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> deposit
Type a whole number up to 9999 (a fee from 1 to 100).

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> deposit x
Type a whole number up to 9999 (a fee from 1 to 100).

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> deposit 0
Type a whole number up to 9999 (a fee from 1 to 100).

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> deposit 10000
Type a whole number up to 9999 (a fee from 1 to 100).

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> fee 101
Type a whole number up to 9999 (a fee from 1 to 100).

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> withdraw -5
Type a whole number up to 9999 (a fee from 1 to 100).

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> adjust -15
Added: adjust -15.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
6 events is the most.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 5 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 4 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 3 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 2 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 1 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 3
Two events or more are needed.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 0 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 1
The log is empty.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 2
The log is empty.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 3
Two events or more are needed.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
The log is empty.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 6
Bye.
```

### basic - interpreter: byte-equal to the golden

Input:

```text
1
2
3
5
1
2
3
4
fee 5
3
6
```

Screen:

```text
== Event log ==
An account kept two ways: the balance replayed from its events, and a stored balance column that each write path updates.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 1
  #  event                              replayed  stored
  1  deposit 100                             100     100
  2  withdraw 30                              70      70
  3  fee 10% = 7                              63      63
  4  deposit 50                              113     113
  5  adjust +20                              133     113
  6  withdraw 120                             13      -7

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 2
The check the system has: the stored column, -7, against the copy the same write paths keep, -7: no difference.
Replaying the events: 13, and stored says -7: off by -20.
They part at event 5, adjust +20: that write path never touches the stored column.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 3
Every order of these 6 events, replayed: 720 orders, 40 different final balances; 8 end as the log does.
Of the 15 pairs of events, 3 give the same balance in either order from every starting balance from 0 to 300.
Replaying gives the truth only in the order the events happened: the log's order is part of what it records.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 5
Removed the last event; 5 left.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 1
  #  event                              replayed  stored
  1  deposit 100                             100     100
  2  withdraw 30                              70      70
  3  fee 10% = 7                              63      63
  4  deposit 50                              113     113
  5  adjust +20                              133     113

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 2
The check the system has: the stored column, 113, against the copy the same write paths keep, 113: no difference.
Replaying the events: 133, and stored says 113: off by -20.
They part at event 5, adjust +20: that write path never touches the stored column.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 3
Every order of these 5 events, replayed: 120 orders, 19 different final balances; 6 end as the log does.
Of the 10 pairs of events, 3 give the same balance in either order from every starting balance from 0 to 300.
Replaying gives the truth only in the order the events happened: the log's order is part of what it records.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 4
An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.
event> fee 5
Added: fee 5%.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 3
Every order of these 6 events, replayed: 720 orders, 47 different final balances; 17 end as the log does.
Of the 15 pairs of events, 3 give the same balance in either order from every starting balance from 0 to 300.
Replaying gives the truth only in the order the events happened: the log's order is part of what it records.

1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit
choice> 6
Bye.
```

## Modules

### main.eml

```eml
# P131 event log: the log with both balances, two reconciliations, every
# order of the events, and adding or removing events.
import events

def plain(s):
    0 => i
    len(s) => j
    while i < j and s[i] == " ":
        i + 1 => i
    while j > i and s[j - 1] == " ":
        j - 1 => j
    return s[i:j]

def pad(s, n):
    s => out
    while len(out) < n:
        out + " " => out
    return out

def lpad(s, n):
    s => out
    while len(out) < n:
        " " + out => out
    return out

def show(log):
    if len(log) == 0:
        "The log is empty." ^0
        return
    events.ledger(log) => rows
    "  #  event                              replayed  stored" ^0
    for k in [0:len(log) - 1]:
        rows[k] => r
        ("  " + str(k + 1) + "  " + pad(r[0], 33) + lpad(str(r[1]), 10) + lpad(str(r[2]), 8)) ^0

def reconcile(log):
    if len(log) == 0:
        "The log is empty." ^0
        return
    events.ledger(log) => rows
    rows[len(rows) - 1] => last
    if last[2] == last[3]:
        ("The check the system has: the stored column, " + str(last[2]) + ", against the copy the same write paths keep, " + str(last[3]) + ": no difference.") ^0
    else:
        ("The check the system has finds a difference: " + str(last[2]) + " against " + str(last[3]) + ".") ^0
    if last[1] == last[2]:
        ("Replaying the events: " + str(last[1]) + ", the same as stored.") ^0
    else:
        ("Replaying the events: " + str(last[1]) + ", and stored says " + str(last[2]) + ": off by " + events.signed(last[2] - last[1]) + ".") ^0
        0 => first
        for k in [0:len(rows) - 1]:
            if first == 0 and rows[k][1] != rows[k][2]:
                k + 1 => first
        ("They part at event " + str(first) + ", " + rows[first - 1][0] + ": that write path never touches the stored column.") ^0

def orders(log):
    if len(log) < 2:
        "Two events or more are needed." ^0
        return
    events.orders(log) => o
    events.pairs(log, 300) => p
    ("Every order of these " + str(len(log)) + " events, replayed: " + str(o[0]) + " orders, " + str(o[1]) + " different final balances; " + str(o[2]) + " end as the log does.") ^0
    ("Of the " + str(p[0]) + " pairs of events, " + str(p[1]) + " give the same balance in either order from every starting balance from 0 to 300.") ^0
    if o[1] > 1:
        "Replaying gives the truth only in the order the events happened: the log's order is part of what it records." ^0

def add(log):
    if len(log) >= 6:
        "6 events is the most." ^0
        return log
    "An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25." ^0
    plain(input("event> ")) => t
    if t == "":
        "Cancelled." ^0
        return log
    "" => kind
    "" => num
    False => space
    for c in t:
        if c == " " and not space:
            True => space
        elif space:
            num + c => num
        else:
            kind + c => kind
    if kind != "deposit" and kind != "withdraw" and kind != "fee" and kind != "adjust":
        "Type deposit, withdraw, fee or adjust, then a number." ^0
        return log
    plain(num) => num
    1 => sign
    if len(num) > 0 and num[0] == "-" and kind == "adjust":
        0 - 1 => sign
        num[1:len(num)] => num
    if len(num) == 0 or len(num) > 4:
        "Type a whole number up to 9999 (a fee from 1 to 100)." ^0
        return log
    for c in num:
        if c < "0" or c > "9":
            "Type a whole number up to 9999 (a fee from 1 to 100)." ^0
            return log
    int(num) => n
    if n == 0 or (kind == "fee" and n > 100):
        "Type a whole number up to 9999 (a fee from 1 to 100)." ^0
        return log
    log + [[kind, sign * n]] => out
    if kind == "fee":
        ("Added: fee " + str(n) + "%.") ^0
    elif kind == "adjust":
        ("Added: adjust " + events.signed(sign * n) + ".") ^0
    else:
        ("Added: " + kind + " " + str(n) + ".") ^0
    return out

events.default_log() => log

"== Event log ==" ^0
"An account kept two ways: the balance replayed from its events, and a stored balance column that each write path updates." ^0
True => running
while running:
    "" ^0
    "1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit" ^0
    plain(input("choice> ")) => choice
    if choice == "1":
        show(log)
    elif choice == "2":
        reconcile(log)
    elif choice == "3":
        orders(log)
    elif choice == "4":
        add(log) => log
    elif choice == "5":
        if len(log) == 0:
            "The log is empty." ^0
        else:
            log[0:len(log) - 1] => log
            ("Removed the last event; " + str(len(log)) + " left.") ^0
    elif choice == "6":
        False => running
    else:
        "Pick a number from 1 to 6." ^0
"Bye." ^0
```

Python projection of main.eml:

```python
import events

def plain(s):
    i = 0
    j = len(s)
    while i < j and s[i] == " ":
        i = i + 1
    while j > i and s[j - 1] == " ":
        j = j - 1
    return s[i:j]

def pad(s, n):
    out = s
    while len(out) < n:
        out = out + " "
    return out

def lpad(s, n):
    out = s
    while len(out) < n:
        out = " " + out
    return out

def show(log):
    if len(log) == 0:
        print("The log is empty.")
        return
    rows = events.ledger(log)
    print("  #  event                              replayed  stored")
    for k in range(0, len(log)):
        r = rows[k]
        print("  " + str(k + 1) + "  " + pad(r[0], 33) + lpad(str(r[1]), 10) + lpad(str(r[2]), 8))

def reconcile(log):
    if len(log) == 0:
        print("The log is empty.")
        return
    rows = events.ledger(log)
    last = rows[len(rows) - 1]
    if last[2] == last[3]:
        print("The check the system has: the stored column, " + str(last[2]) + ", against the copy the same write paths keep, " + str(last[3]) + ": no difference.")
    else:
        print("The check the system has finds a difference: " + str(last[2]) + " against " + str(last[3]) + ".")
    if last[1] == last[2]:
        print("Replaying the events: " + str(last[1]) + ", the same as stored.")
    else:
        print("Replaying the events: " + str(last[1]) + ", and stored says " + str(last[2]) + ": off by " + events.signed(last[2] - last[1]) + ".")
        first = 0
        for k in range(0, len(rows)):
            if first == 0 and rows[k][1] != rows[k][2]:
                first = k + 1
        print("They part at event " + str(first) + ", " + rows[first - 1][0] + ": that write path never touches the stored column.")

def orders(log):
    if len(log) < 2:
        print("Two events or more are needed.")
        return
    o = events.orders(log)
    p = events.pairs(log, 300)
    print("Every order of these " + str(len(log)) + " events, replayed: " + str(o[0]) + " orders, " + str(o[1]) + " different final balances; " + str(o[2]) + " end as the log does.")
    print("Of the " + str(p[0]) + " pairs of events, " + str(p[1]) + " give the same balance in either order from every starting balance from 0 to 300.")
    if o[1] > 1:
        print("Replaying gives the truth only in the order the events happened: the log's order is part of what it records.")

def add(log):
    if len(log) >= 6:
        print("6 events is the most.")
        return log
    print("An event: deposit n, withdraw n, fee p (percent), or adjust n (either sign), such as withdraw 25.")
    t = plain(input("event> "))
    if t == "":
        print("Cancelled.")
        return log
    kind = ""
    num = ""
    space = False
    for c in t:
        if c == " " and not space:
            space = True
        elif space:
            num = num + c
        else:
            kind = kind + c
    if kind != "deposit" and kind != "withdraw" and kind != "fee" and kind != "adjust":
        print("Type deposit, withdraw, fee or adjust, then a number.")
        return log
    num = plain(num)
    sign = 1
    if len(num) > 0 and num[0] == "-" and kind == "adjust":
        sign = 0 - 1
        num = num[1:len(num)]
    if len(num) == 0 or len(num) > 4:
        print("Type a whole number up to 9999 (a fee from 1 to 100).")
        return log
    for c in num:
        if c < "0" or c > "9":
            print("Type a whole number up to 9999 (a fee from 1 to 100).")
            return log
    n = int(num)
    if n == 0 or kind == "fee" and n > 100:
        print("Type a whole number up to 9999 (a fee from 1 to 100).")
        return log
    out = log + [[kind, sign * n]]
    if kind == "fee":
        print("Added: fee " + str(n) + "%.")
    elif kind == "adjust":
        print("Added: adjust " + events.signed(sign * n) + ".")
    else:
        print("Added: " + kind + " " + str(n) + ".")
    return out

log = events.default_log()
print("== Event log ==")
print("An account kept two ways: the balance replayed from its events, and a stored balance column that each write path updates.")
running = True
while running:
    print("")
    print("1) the log  2) reconcile  3) every order  4) add an event  5) remove the last  6) quit")
    choice = plain(input("choice> "))
    if choice == "1":
        show(log)
    elif choice == "2":
        reconcile(log)
    elif choice == "3":
        orders(log)
    elif choice == "4":
        log = add(log)
    elif choice == "5":
        if len(log) == 0:
            print("The log is empty.")
        else:
            log = log[0:len(log) - 1]
            print("Removed the last event; " + str(len(log)) + " left.")
    elif choice == "6":
        running = False
    else:
        print("Pick a number from 1 to 6.")
print("Bye.")
```

### events.eml

```eml
# P131 event log - an account kept two ways: the balance replayed from its
# events, and a stored balance column that each write path updates. As in
# the corpus case denormalized-total-drifts, one path (an adjustment)
# forgets the stored column, and a check that compares the column with a
# copy kept by the same paths compares a number with itself. As in
# state-from-events-vs-stored, replaying is right only for the order the
# events happened in: a fee and a refused withdrawal do not commute with
# the rest.
#
# An event is [kind, amount]: deposit n, withdraw n (refused if more than
# the balance), fee p (p percent of the balance, rounded down), adjust n
# (a correction, either sign).

def default_log():
    return [["deposit", 100], ["withdraw", 30], ["fee", 10], ["deposit", 50], ["adjust", 20], ["withdraw", 120]]

def div(p, q):
    int(p / q) => f
    while f * q > p:
        f - 1 => f
    while (f + 1) * q <= p:
        f + 1 => f
    return f

def apply(balance, ev):
    # [balance after, what happened]
    if ev[0] == "deposit":
        return [balance + ev[1], "deposit " + str(ev[1])]
    if ev[0] == "withdraw":
        if ev[1] > balance:
            return [balance, "withdraw " + str(ev[1]) + " refused: only " + str(balance)]
        return [balance - ev[1], "withdraw " + str(ev[1])]
    if ev[0] == "fee":
        div(balance * ev[1], 100) => f
        return [balance - f, "fee " + str(ev[1]) + "% = " + str(f)]
    return [balance + ev[1], "adjust " + signed(ev[1])]

def signed(n):
    if n < 0:
        return str(n)
    return "+" + str(n)

def replay(log):
    0 => b
    for ev in log:
        apply(b, ev)[0] => b
    return b

def ledger(log):
    # per event [what happened, replayed balance, stored column, the copy
    #  the check reads]; every path but adjust writes the column and the copy
    0 => b
    0 => stored
    0 => copy
    [] => out
    for ev in log:
        b => before
        apply(b, ev) => r
        r[0] => b
        if ev[0] != "adjust":
            stored + b - before => stored
            stored => copy
        out + [[r[1], b, stored, copy]] => out
    return out

def permutations(xs):
    if len(xs) <= 1:
        return [xs]
    [] => out
    for i in [0:len(xs) - 1]:
        [] => rest
        for j in [0:len(xs) - 1]:
            if j != i:
                rest + [xs[j]] => rest
        for p in permutations(rest):
            out + [[xs[i]] + p] => out
    return out

def orders(log):
    # [orders, distinct final balances, orders ending as the log does]
    replay(log) => right
    {} => seen
    0 => same
    permutations(log) => ps
    for p in ps:
        replay(p) => b
        1 => seen[b]
        if b == right:
            same + 1 => same
    return [len(ps), len(seen), same]

def commutes(a, b, top):
    # a then b equals b then a from every balance 0 to top
    for x in [0:top]:
        if apply(apply(x, a)[0], b)[0] != apply(apply(x, b)[0], a)[0]:
            return False
    return True

def pairs(log, top):
    # [pairs, pairs that commute]
    0 => n
    0 => c
    for i in [0:len(log) - 2]:
        for j in [i + 1:len(log) - 1]:
            n + 1 => n
            if commutes(log[i], log[j], top):
                c + 1 => c
    return [n, c]
```

Python projection of events.eml:

```python
def default_log():
    return [["deposit", 100], ["withdraw", 30], ["fee", 10], ["deposit", 50], ["adjust", 20], ["withdraw", 120]]

def div(p, q):
    f = int(p / q)
    while f * q > p:
        f = f - 1
    while (f + 1) * q <= p:
        f = f + 1
    return f

def apply(balance, ev):
    if ev[0] == "deposit":
        return [balance + ev[1], "deposit " + str(ev[1])]
    if ev[0] == "withdraw":
        if ev[1] > balance:
            return [balance, "withdraw " + str(ev[1]) + " refused: only " + str(balance)]
        return [balance - ev[1], "withdraw " + str(ev[1])]
    if ev[0] == "fee":
        f = div(balance * ev[1], 100)
        return [balance - f, "fee " + str(ev[1]) + "% = " + str(f)]
    return [balance + ev[1], "adjust " + signed(ev[1])]

def signed(n):
    if n < 0:
        return str(n)
    return "+" + str(n)

def replay(log):
    b = 0
    for ev in log:
        b = apply(b, ev)[0]
    return b

def ledger(log):
    b = 0
    stored = 0
    copy = 0
    out = []
    for ev in log:
        before = b
        r = apply(b, ev)
        b = r[0]
        if ev[0] != "adjust":
            stored = stored + b - before
            copy = stored
        out = out + [[r[1], b, stored, copy]]
    return out

def permutations(xs):
    if len(xs) <= 1:
        return [xs]
    out = []
    for i in range(0, len(xs)):
        rest = []
        for j in range(0, len(xs)):
            if j != i:
                rest = rest + [xs[j]]
        for p in permutations(rest):
            out = out + [[xs[i]] + p]
    return out

def orders(log):
    right = replay(log)
    seen = {}
    same = 0
    ps = permutations(log)
    for p in ps:
        b = replay(p)
        seen[b] = 1
        if b == right:
            same = same + 1
    return [len(ps), len(seen), same]

def commutes(a, b, top):
    for x in range(0, top+1):
        if apply(apply(x, a)[0], b)[0] != apply(apply(x, b)[0], a)[0]:
            return False
    return True

def pairs(log, top):
    n = 0
    c = 0
    for i in range(0, len(log) - 2+1):
        for j in range(i + 1, len(log)):
            n = n + 1
            if commutes(log[i], log[j], top):
                c = c + 1
    return [n, c]
```

## README

# P131 - Event log

An account kept two ways: the balance replayed from its events, and a
stored balance column that each write path updates.

- `main.eml` - the menu, the log with both balances, the two
  reconciliations, every order of the events, and adding or removing one
- `events.eml` - an event's effect, a replay, the ledger with the stored
  column, every order, and which pairs of events commute

How each part works:

- An event is a deposit, a withdrawal (refused if more than the balance),
  a fee of a percentage of the balance (rounded down), or an adjustment of
  either sign.
- As in the corpus case `denormalized-total-drifts`, the adjustment's
  write path forgets the stored column. The column is always the replayed
  balance less the adjustments: after adjust +20 it is 20 short, and a
  later withdrawal of 120 takes it to -7, while the replayed balance is
  13.
- The check the system has compares the column with a copy kept by the
  same write paths, so it compares a number with itself and never finds a
  difference. Replaying the events finds it, and where they part.
- As in the corpus case `state-from-events-vs-stored`, replaying is right
  only in the order the events happened: the 720 orders of the 6 events
  end at 40 different balances, and only 3 of the 15 pairs give the same
  balance in either order from every balance from 0 to 300.

What is checked: menu choices 1 to 6; an event as deposit, withdraw, fee
or adjust and a whole number up to 9999 (a fee from 1 to 100%, only an
adjustment negative), at most 6 events. An empty answer cancels.

Sessions: `sessions/basic.in` gives:
- the log, the reconciliations and every order;
- the last event removed, and the same again;
- fee 5% added at the end, and every order again.

`sessions/bad-input.in` gives:
- menu choices 0 and x;
- one event removed; then events: a cancel, pay 10, deposit alone, x, 0
  and 10000, fee 101 and withdraw -5, then adjust -15, and a seventh;
- the log emptied one event at a time, with every order, the log and the
  reconciliations on the way and at the end.

Built on the verified corpus cases `denormalized-total-drifts` (a stored
total kept by every write path, and a reconciliation that compares it
with itself) and `state-from-events-vs-stored` (replaying events is right
only for some orders).
