Project P075

Bank queue

A simulated day at a bank, minute by minute: random customers from an EML generator, one first-come-first-served line with a limit, a few tellers; waits, people turned away, how busy the tellers were, Little's law exactly, and the same customers with 1 to 5 tellers.

2 modules · 2 recorded sessionstext-menu UI in the terminalupdated 2026-10-14

Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.

About

A simulated day at a bank, minute by minute. Random customers come, wait in one line in the order they came, and go to the first free teller. The line has a limit, and someone who finds it full leaves. It reports the waits, the people turned away, how busy the tellers were, and the same customers with 1 to 5 tellers side by side. It starts with 2 tellers, 8 hours, a customer in 40% of minutes needing 2 to 7 minutes each, and room for 10 in line.

  • main.eml - the menu, settings, and the reports
  • bank.eml - the random customers and the minute-by-minute simulation

How each part works:

  • Customers: each minute a linear congruential generator (the constants of Numerical Recipes, mod 2^32) decides whether someone comes, and if so how long they need. Only its top 16 bits are used, since the low bits of such a generator repeat quickly. The customers are drawn before anything else, so every number of tellers and every line limit sees exactly the same people.
  • Each minute: first every free teller calls the front of the line - a first-come-first-served queue, as in the corpus case simple-queue. Then the minute's customer, if any, goes straight to a teller who is still free, or joins the line, or leaves when the line already holds its limit. As the corpus case bounded-queue-backpressure says, a full queue has to drop something; here it is the newcomer. The day runs on past closing until the line is empty.
  • Little's law in its exact form: the line's length added up over every minute equals all the customers' waits added up. The program counts both separately and prints both.

What is checked: menu choices 1 to 5; 1 to 5 tellers; 30 to 600 minutes open; a 1 to 100% chance of a customer each minute; a service time from 1 to 30 minutes, the longest no shorter than the shortest; room for 0 to 50 in line; a seed from 0 to 999999. An empty answer keeps a setting as it was.

Sessions: sessions/basic.in gives: - the starting day: 189 customers, all served, an average wait of 4.01 minutes; - the same customers with 1 to 5 tellers - one teller turns 80 away; - the first 20 minutes; - 4 hours at 60%, 2 to 10 minutes, room for 3: 55 turned away with 2 tellers, and the day with 1 to 5.

sessions/bad-input.in gives: - menu choices 0 and x; - every setting out of range once (tellers 6, 0 and abc; 29 and 601 minutes; 0% and 101%; a 31-minute service; a longest below the shortest; room for 51; seed 1000000); - then 1 teller, a customer every minute needing 5 minutes, and nobody able to wait: exactly one in five served.

Built on the verified corpus cases simple-queue (a queue served in the order people came) and bounded-queue-backpressure (what a full queue has to drop).

Recorded sessions

What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.

bad-input

interpreter: byte-equal
== Bank queue ==
A day at a bank, minute by minute: random customers, one line, a few tellers.

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 0
Pick a number from 1 to 5.

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> x
Pick a number from 1 to 5.

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 4
Settings, one at a time; an empty answer keeps the old value.
tellers, now 2 (1 to 5)> 6
Type a whole number from 1 to 5.
tellers, now 2 (1 to 5)> 0
Type a whole number from 1 to 5.
tellers, now 2 (1 to 5)> abc
Type a whole number from 1 to 5.
tellers, now 2 (1 to 5)> 1
minutes open, now 480 (30 to 600)> 29
Type a whole number from 30 to 600.
minutes open, now 480 (30 to 600)> 601
Type a whole number from 30 to 600.
minutes open, now 480 (30 to 600)> 60
chance of a customer each minute, %, now 40 (1 to 100)> 0
Type a whole number from 1 to 100.
chance of a customer each minute, %, now 40 (1 to 100)> 101
Type a whole number from 1 to 100.
chance of a customer each minute, %, now 40 (1 to 100)> 100
shortest service, minutes, now 2 (1 to 30)> 31
Type a whole number from 1 to 30.
shortest service, minutes, now 2 (1 to 30)> 5
longest service, minutes, now 7 (5 to 30)> 4
Type a whole number from 5 to 30.
longest service, minutes, now 7 (5 to 30)> 5
room in the line, now 10 (0 to 50)> 51
Type a whole number from 0 to 50.
room in the line, now 10 (0 to 50)> 0
seed, now 2026 (0 to 999999)> 1000000
Type a whole number from 0 to 999999.
seed, now 2026 (0 to 999999)> 7
Now: 1 teller, open 60 minutes, a customer in 100% of minutes, 5 minutes each, nobody can wait in line, seed 7.

1 teller, open 60 minutes, a customer in 100% of minutes, 5 minutes each, nobody can wait in line, seed 7.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 1
60 customers came; 12 were served and 48 turned away, finding no room in the line.
Average wait 0.00 minutes; the longest 0. The line was at most 0 long.
The last customer left at 10:00; the tellers were busy 100.00% of that time.
Little's law: the line held 0 customer-minutes in all, and the customers waited 0 minutes in all - the same thing counted two ways.

1 teller, open 60 minutes, a customer in 100% of minutes, 5 minutes each, nobody can wait in line, seed 7.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 3
The first 20 minutes:
  9:00  one arrives and goes to teller 1 - 0 in line
  9:01  one arrives, finds no room in the line and leaves - 0 in line
  9:02  one arrives, finds no room in the line and leaves - 0 in line
  9:03  one arrives, finds no room in the line and leaves - 0 in line
  9:04  one arrives, finds no room in the line and leaves - 0 in line
  9:05  one arrives and goes to teller 1 - 0 in line
  9:06  one arrives, finds no room in the line and leaves - 0 in line
  9:07  one arrives, finds no room in the line and leaves - 0 in line
  9:08  one arrives, finds no room in the line and leaves - 0 in line
  9:09  one arrives, finds no room in the line and leaves - 0 in line
  9:10  one arrives and goes to teller 1 - 0 in line
  9:11  one arrives, finds no room in the line and leaves - 0 in line
  9:12  one arrives, finds no room in the line and leaves - 0 in line
  9:13  one arrives, finds no room in the line and leaves - 0 in line
  9:14  one arrives, finds no room in the line and leaves - 0 in line
  9:15  one arrives and goes to teller 1 - 0 in line
  9:16  one arrives, finds no room in the line and leaves - 0 in line
  9:17  one arrives, finds no room in the line and leaves - 0 in line
  9:18  one arrives, finds no room in the line and leaves - 0 in line
  9:19  one arrives, finds no room in the line and leaves - 0 in line

1 teller, open 60 minutes, a customer in 100% of minutes, 5 minutes each, nobody can wait in line, seed 7.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 5
Bye.
What was typed (24 lines)
0
x
4
6
0
abc
1
29
601
60
0
101
100
31
5
4
5
51
0
1000000
7
1
3
5

basic

interpreter: byte-equal
== Bank queue ==
A day at a bank, minute by minute: random customers, one line, a few tellers.

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 1
189 customers came; 189 were served and 0 turned away, finding no room in the line.
Average wait 4.01 minutes; the longest 14. The line was at most 7 long.
The last customer left at 17:16; the tellers were busy 88.61% of that time.
Little's law: the line held 757 customer-minutes in all, and the customers waited 757 minutes in all - the same thing counted two ways.

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 2
189 customers, the same ones each time:
  tellers  served  away  average wait  longest  busy
        1     109    80         41.51       58  99.81%
        2     189     0          4.01       14  88.61%
        3     189     0          0.32        4  60.54%
        4     189     0          0.06        3  45.50%
        5     189     0          0.01        2  36.40%

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 3
The first 20 minutes:
  9:00  quiet - 0 in line
  9:01  one arrives and goes to teller 1 - 0 in line
  9:02  quiet - 0 in line
  9:03  quiet - 0 in line
  9:04  one arrives and goes to teller 2 - 0 in line
  9:05  quiet - 0 in line
  9:06  quiet - 0 in line
  9:07  one arrives and goes to teller 1 - 0 in line
  9:08  quiet - 0 in line
  9:09  quiet - 0 in line
  9:10  one arrives and goes to teller 1 - 0 in line
  9:11  quiet - 0 in line
  9:12  one arrives and goes to teller 2 - 0 in line
  9:13  quiet - 0 in line
  9:14  quiet - 0 in line
  9:15  one arrives and goes to teller 1 - 0 in line
  9:16  one arrives and goes to teller 2 - 0 in line
  9:17  quiet - 0 in line
  9:18  quiet - 0 in line
  9:19  one arrives and waits - 1 in line

2 tellers, open 480 minutes, a customer in 40% of minutes, 2 to 7 minutes each, room for 10 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 4
Settings, one at a time; an empty answer keeps the old value.
tellers, now 2 (1 to 5)> 
minutes open, now 480 (30 to 600)> 240
chance of a customer each minute, %, now 40 (1 to 100)> 60
shortest service, minutes, now 2 (1 to 30)> 
longest service, minutes, now 7 (2 to 30)> 10
room in the line, now 10 (0 to 50)> 3
seed, now 2026 (0 to 999999)> 
Now: 2 tellers, open 240 minutes, a customer in 60% of minutes, 2 to 10 minutes each, room for 3 in line, seed 2026.

2 tellers, open 240 minutes, a customer in 60% of minutes, 2 to 10 minutes each, room for 3 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 1
139 customers came; 84 were served and 55 turned away, finding no room in the line.
Average wait 7.20 minutes; the longest 14. The line was at most 3 long.
The last customer left at 13:14; the tellers were busy 99.21% of that time.
Little's law: the line held 605 customer-minutes in all, and the customers waited 605 minutes in all - the same thing counted two ways.

2 tellers, open 240 minutes, a customer in 60% of minutes, 2 to 10 minutes each, room for 3 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 2
139 customers, the same ones each time:
  tellers  served  away  average wait  longest  busy
        1      41    98         17.24       29  99.63%
        2      84    55          7.20       14  99.21%
        3     122    17          3.11       10  96.18%
        4     136     3          1.07        7  83.20%
        5     139     0          0.17        3  68.26%

2 tellers, open 240 minutes, a customer in 60% of minutes, 2 to 10 minutes each, room for 3 in line, seed 2026.
1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit
choice> 5
Bye.
What was typed (14 lines)
1
2
3
4

240
60

10
3

1
2
5

Modules

The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.

main.eml(entry)

eml
# P075 bank queue: a simulated day at a bank with random customers, one
# line and a few tellers - the waits, the people turned away when the line
# is full, how busy the tellers were, and the same customers with 1 to 5
# tellers side by side.
import bank

def trim(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 number(s):
    if len(s) == 0 or len(s) > 6:
        return -1
    0 => n
    for c in s:
        if not (c >= "0" and c <= "9"):
            return -1
        n * 10 + int(c) => n
    return n

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

def decimal(a, b):
    # a / b to two places, rounded half up, for whole a >= 0 and b > 0.
    200 * a + b => n
    2 * b => d
    0 => q
    while n >= d:
        d => m
        1 => k
        while m + m <= n:
            m + m => m
            k + k => k
        n - m => n
        q + k => q
    0 => whole
    q => r
    while r >= 100:
        r - 100 => r
        whole + 1 => whole
    if r < 10:
        return str(whole) + ".0" + str(r)
    return str(whole) + "." + str(r)

def clock(m):
    # Minutes after opening at 9:00 as a time of day.
    9 => h
    m => r
    while r >= 60:
        r - 60 => r
        h + 1 => h
    "" => z
    if r < 10:
        "0" => z
    return str(h) + ":" + z + str(r)

def ask(prompt, low, high):
    while True:
        trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> ")) => a
        if a == "":
            return -1
        number(a) => n
        if n >= low and n <= high:
            return n
        ("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0

def settings_text(s):
    "1 teller" => who
    if s[0] > 1:
        str(s[0]) + " tellers" => who
    "nobody can wait in line" => room
    if s[5] > 0:
        "room for " + str(s[5]) + " in line" => room
    str(s[3]) + " to " + str(s[4]) => service
    if s[3] == s[4]:
        str(s[3]) => service
    return who + ", open " + str(s[1]) + " minutes, a customer in " + str(s[2]) + "% of minutes, " + service + " minutes each, " + room + ", seed " + str(s[6])

def people_of(s):
    return bank.customers(s[1], s[2], s[3], s[4], s[6])

def day(s):
    people_of(s) => people
    if len(people) == 0:
        "Nobody came all day." ^0
        return
    bank.simulate(people, s[0], s[5], 0) => r
    (str(len(people)) + " customers came; " + str(r[0]) + " were served and " + str(r[1]) + " turned away, finding no room in the line.") ^0
    if r[0] > 0:
        ("Average wait " + decimal(r[2], r[0]) + " minutes; the longest " + str(r[3]) + ". The line was at most " + str(r[4]) + " long.") ^0
    ("The last customer left at " + clock(r[6]) + "; the tellers were busy " + decimal(100 * r[5], s[0] * r[6]) + "% of that time.") ^0
    ("Little's law: the line held " + str(r[7]) + " customer-minutes in all, and the customers waited " + str(r[2]) + " minutes in all - the same thing counted two ways.") ^0

def compare(s):
    people_of(s) => people
    if len(people) == 0:
        "Nobody came all day." ^0
        return
    (str(len(people)) + " customers, the same ones each time:") ^0
    "  tellers  served  away  average wait  longest  busy" ^0
    for k in [1:5]:
        bank.simulate(people, k, s[5], 0) => r
        "-" => avg
        if r[0] > 0:
            decimal(r[2], r[0]) => avg
        ("  " + lpad(str(k), 7) + lpad(str(r[0]), 8) + lpad(str(r[1]), 6) + lpad(avg, 14) + lpad(str(r[3]), 9) + lpad(decimal(100 * r[5], k * r[6]) + "%", 8)) ^0

def opening(s):
    people_of(s) => people
    bank.simulate(people, s[0], s[5], 20) => r
    "The first 20 minutes:" ^0
    for e in r[8]:
        "quiet" => what
        if e[1] != "":
            e[1][0:len(e[1]) - 2] => what
        ("  " + clock(e[0]) + "  " + what + " - " + str(e[2]) + " in line") ^0

def change(s):
    "Settings, one at a time; an empty answer keeps the old value." ^0
    [["tellers", 1, 5], ["minutes open", 30, 600], ["chance of a customer each minute, %", 1, 100], ["shortest service, minutes", 1, 30], ["longest service, minutes", 1, 30], ["room in the line", 0, 50], ["seed", 0, 999999]] => qs
    [0] * 7 => out
    for k in [0:6]:
        qs[k] => q
        q[1] => low
        if k == 4:
            out[3] => low
        ask(q[0] + ", now " + str(s[k]), low, q[2]) => v
        if v == -1:
            s[k] => v
            if k == 4 and v < low:
                low => v
        v => out[k]
    ("Now: " + settings_text(out) + ".") ^0
    return out

[2, 480, 40, 2, 7, 10, 2026] => settings

"== Bank queue ==" ^0
"A day at a bank, minute by minute: random customers, one line, a few tellers." ^0
True => running
while running:
    "" ^0
    (settings_text(settings) + ".") ^0
    "1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit" ^0
    trim(input("choice> ")) => choice
    if choice == "1":
        day(settings)
    elif choice == "2":
        compare(settings)
    elif choice == "3":
        opening(settings)
    elif choice == "4":
        change(settings) => settings
    elif choice == "5":
        False => running
    else:
        "Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import bank

def trim(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 number(s):
    if len(s) == 0 or len(s) > 6:
        return -1
    n = 0
    for c in s:
        if not (c >= "0" and c <= "9"):
            return -1
        n = n * 10 + int(c)
    return n

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

def decimal(a, b):
    n = 200 * a + b
    d = 2 * b
    q = 0
    while n >= d:
        m = d
        k = 1
        while m + m <= n:
            m = m + m
            k = k + k
        n = n - m
        q = q + k
    whole = 0
    r = q
    while r >= 100:
        r = r - 100
        whole = whole + 1
    if r < 10:
        return str(whole) + ".0" + str(r)
    return str(whole) + "." + str(r)

def clock(m):
    h = 9
    r = m
    while r >= 60:
        r = r - 60
        h = h + 1
    z = ""
    if r < 10:
        z = "0"
    return str(h) + ":" + z + str(r)

def ask(prompt, low, high):
    while True:
        a = trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> "))
        if a == "":
            return -1
        n = number(a)
        if n >= low and n <= high:
            return n
        print("Type a whole number from " + str(low) + " to " + str(high) + ".")

def settings_text(s):
    who = "1 teller"
    if s[0] > 1:
        who = str(s[0]) + " tellers"
    room = "nobody can wait in line"
    if s[5] > 0:
        room = "room for " + str(s[5]) + " in line"
    service = str(s[3]) + " to " + str(s[4])
    if s[3] == s[4]:
        service = str(s[3])
    return who + ", open " + str(s[1]) + " minutes, a customer in " + str(s[2]) + "% of minutes, " + service + " minutes each, " + room + ", seed " + str(s[6])

def people_of(s):
    return bank.customers(s[1], s[2], s[3], s[4], s[6])

def day(s):
    people = people_of(s)
    if len(people) == 0:
        print("Nobody came all day.")
        return
    r = bank.simulate(people, s[0], s[5], 0)
    print(str(len(people)) + " customers came; " + str(r[0]) + " were served and " + str(r[1]) + " turned away, finding no room in the line.")
    if r[0] > 0:
        print("Average wait " + decimal(r[2], r[0]) + " minutes; the longest " + str(r[3]) + ". The line was at most " + str(r[4]) + " long.")
    print("The last customer left at " + clock(r[6]) + "; the tellers were busy " + decimal(100 * r[5], s[0] * r[6]) + "% of that time.")
    print("Little's law: the line held " + str(r[7]) + " customer-minutes in all, and the customers waited " + str(r[2]) + " minutes in all - the same thing counted two ways.")

def compare(s):
    people = people_of(s)
    if len(people) == 0:
        print("Nobody came all day.")
        return
    print(str(len(people)) + " customers, the same ones each time:")
    print("  tellers  served  away  average wait  longest  busy")
    for k in range(1, 6):
        r = bank.simulate(people, k, s[5], 0)
        avg = "-"
        if r[0] > 0:
            avg = decimal(r[2], r[0])
        print("  " + lpad(str(k), 7) + lpad(str(r[0]), 8) + lpad(str(r[1]), 6) + lpad(avg, 14) + lpad(str(r[3]), 9) + lpad(decimal(100 * r[5], k * r[6]) + "%", 8))

def opening(s):
    people = people_of(s)
    r = bank.simulate(people, s[0], s[5], 20)
    print("The first 20 minutes:")
    for e in r[8]:
        what = "quiet"
        if e[1] != "":
            what = e[1][0:len(e[1]) - 2]
        print("  " + clock(e[0]) + "  " + what + " - " + str(e[2]) + " in line")

def change(s):
    print("Settings, one at a time; an empty answer keeps the old value.")
    qs = [["tellers", 1, 5], ["minutes open", 30, 600], ["chance of a customer each minute, %", 1, 100], ["shortest service, minutes", 1, 30], ["longest service, minutes", 1, 30], ["room in the line", 0, 50], ["seed", 0, 999999]]
    out = [0] * 7
    for k in range(0, 7):
        q = qs[k]
        low = q[1]
        if k == 4:
            low = out[3]
        v = ask(q[0] + ", now " + str(s[k]), low, q[2])
        if v == -1:
            v = s[k]
            if k == 4 and v < low:
                v = low
        out[k] = v
    print("Now: " + settings_text(out) + ".")
    return out

settings = [2, 480, 40, 2, 7, 10, 2026]
print("== Bank queue ==")
print("A day at a bank, minute by minute: random customers, one line, a few tellers.")
running = True
while running:
    print("")
    print(settings_text(settings) + ".")
    print("1) the day  2) 1 to 5 tellers  3) the first 20 minutes  4) change settings  5) quit")
    choice = trim(input("choice> "))
    if choice == "1":
        day(settings)
    elif choice == "2":
        compare(settings)
    elif choice == "3":
        opening(settings)
    elif choice == "4":
        settings = change(settings)
    elif choice == "5":
        running = False
    else:
        print("Pick a number from 1 to 5.")
print("Bye.")

bank.eml

eml
# P075 bank queue - a day at a bank, minute by minute. Customers arrive at
# random, wait in one line in the order they came (the corpus case
# simple-queue), and go to the first free teller. The line has a limit;
# a customer who finds it full is turned away (the corpus case
# bounded-queue-backpressure: a full queue has to drop something, and here
# it is the newcomer).

def draw(x):
    # One step of a linear congruential generator (the constants of
    # Numerical Recipes, mod 2^32): [next state, its top 16 bits]. The low
    # bits of such a generator repeat quickly, so only the top ones are
    # used; x / 65536 divides by a power of two below 2^53, which is exact.
    (1664525 * x + 1013904223) % 4294967296 => x
    return [x, int(x / 65536)]

def customers(open_for, chance, low, high, seed):
    # Who comes, decided before anything else so that every number of
    # tellers and every line limit sees the same customers: each minute
    # someone arrives with `chance` percent probability, and needs a
    # service time from low to high minutes. Returns [[arrival, service]].
    [] => out
    seed => x
    for t in [0:open_for - 1]:
        draw(x) => d
        d[0] => x
        if d[1] % 100 < chance:
            draw(x) => e
            e[0] => x
            out + [[t, low + e[1] % (high - low + 1)]] => out
    return out

def simulate(people, tellers, limit, show):
    # Minute by minute until everyone is served. First each teller who is
    # free calls the front of the line. Then the minute's customer, if any,
    # goes straight to a teller who is still free, or joins the line, or -
    # when the line already holds `limit` - is turned away. Returns
    # [served, turned away, total wait, longest wait, longest line, busy
    # teller-minutes, minutes until the last leaves, line-minutes (the
    # line's length summed over the minutes), log of the first `show`
    # minutes as [minute, what happened, line length]].
    [0] * tellers => free_at
    [] => line
    0 => i
    0 => served
    0 => away
    0 => total
    0 => longest
    0 => most
    0 => busy
    0 => line_minutes
    [] => log
    0 => t
    while i < len(people) or len(line) > 0:
        "" => note
        for k in [0:tellers - 1]:
            if free_at[k] <= t and len(line) > 0:
                line[0] => c
                line[1:len(line)] => line
                t + c[1] => free_at[k]
                busy + c[1] => busy
                served + 1 => served
                total + t - c[0] => total
                if t - c[0] > longest:
                    t - c[0] => longest
                note + "teller " + str(k + 1) + " calls the one from minute " + str(c[0]) + "; " => note
        if i < len(people) and people[i][0] == t:
            people[i] => c
            i + 1 => i
            -1 => k
            for j in [0:tellers - 1]:
                if k == -1 and free_at[j] <= t:
                    j => k
            if k >= 0:
                t + c[1] => free_at[k]
                busy + c[1] => busy
                served + 1 => served
                note + "one arrives and goes to teller " + str(k + 1) + "; " => note
            elif len(line) < limit:
                line + [c] => line
                note + "one arrives and waits; " => note
            else:
                away + 1 => away
                note + "one arrives, finds no room in the line and leaves; " => note
        if len(line) > most:
            len(line) => most
        line_minutes + len(line) => line_minutes
        if t < show:
            log + [[t, note, len(line)]] => log
        t + 1 => t
    t => last
    for k in [0:tellers - 1]:
        if free_at[k] > last:
            free_at[k] => last
    return [served, away, total, longest, most, busy, last, line_minutes, log]
Python projection (bank.py)
def draw(x):
    x = (1664525 * x + 1013904223) % 4294967296
    return [x, int(x / 65536)]

def customers(open_for, chance, low, high, seed):
    out = []
    x = seed
    for t in range(0, open_for):
        d = draw(x)
        x = d[0]
        if d[1] % 100 < chance:
            e = draw(x)
            x = e[0]
            out = out + [[t, low + e[1] % (high - low + 1)]]
    return out

def simulate(people, tellers, limit, show):
    free_at = [0] * tellers
    line = []
    i = 0
    served = 0
    away = 0
    total = 0
    longest = 0
    most = 0
    busy = 0
    line_minutes = 0
    log = []
    t = 0
    while i < len(people) or len(line) > 0:
        note = ""
        for k in range(0, tellers):
            if free_at[k] <= t and len(line) > 0:
                c = line[0]
                line = line[1:len(line)]
                free_at[k] = t + c[1]
                busy = busy + c[1]
                served = served + 1
                total = total + t - c[0]
                if t - c[0] > longest:
                    longest = t - c[0]
                note = note + "teller " + str(k + 1) + " calls the one from minute " + str(c[0]) + "; "
        if i < len(people) and people[i][0] == t:
            c = people[i]
            i = i + 1
            k = -1
            for j in range(0, tellers):
                if k == -1 and free_at[j] <= t:
                    k = j
            if k >= 0:
                free_at[k] = t + c[1]
                busy = busy + c[1]
                served = served + 1
                note = note + "one arrives and goes to teller " + str(k + 1) + "; "
            elif len(line) < limit:
                line = line + [c]
                note = note + "one arrives and waits; "
            else:
                away = away + 1
                note = note + "one arrives, finds no room in the line and leaves; "
        if len(line) > most:
            most = len(line)
        line_minutes = line_minutes + len(line)
        if t < show:
            log = log + [[t, note, len(line)]]
        t = t + 1
    last = t
    for k in range(0, tellers):
        if free_at[k] > last:
            last = free_at[k]
    return [served, away, total, longest, most, busy, last, line_minutes, log]

Built on these corpus cases

ΣEML 2026

Efficient New Language — a high-density semantic overlay for humans and AI agents.

This site’s demos run on the real EML toolchain, in your browser.

© 2026 EveMissLab (一言諾科技有限公司) / Neo.K. All rights reserved.