Fair share
One worker and one-slot jobs from a heavy tenant and light ones, first come first served or tenants in turn: the light tenants' worst wait changes a lot, the mean per job not at all.
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
One worker, jobs of one slot each, from one heavy tenant and several light ones, run first come first served or taking the tenants in turn.
main.eml- the menu, the tenants, the comparison slot by slot and per tenant, and the settingsfair.eml- the jobs, the two schedules, and the counts per tenant and per job
How each part works:
- Tenant H sends its jobs at slot 0; each light tenant sends one at a later slot. The worker runs one job a slot and is never idle while a job waits. In turn, it takes the next waiting job of the next tenant that has one.
- As in the corpus case
fair-share-vs-fifo, first come is fair to jobs and in turn is fair to tenants. With 20 jobs from H and one each from a to e at slot 1, the light tenants' worst completion is 25 first come and 6 in turn; H's last job moves from 20 to 25. - Per job the two are the same: the worker is busy in the same slots either way, so the mean completion and the mean wait per job do not move at all. Taking turns never makes a light tenant's worst completion later.
What is checked: menu choices 1 to 4; 1 to 30 jobs for H, 1 to 8 light tenants, and a slot from 0 to 30 for their jobs. An empty answer cancels.
Sessions: sessions/basic.in gives: - the tenants and the comparison; - the light jobs at slot 25, after H's are done: the two run alike, the worker idle in between; - 6 jobs from H and 3 light tenants at slot 0.
sessions/bad-input.in gives: - menu choices 0 and x; - settings: a cancel, 0, 31 and x for H, then 5 with a cancel and 9 light tenants, and 5 and 2 with a cancel and slot 31; - the most of everything: 30 jobs from H and 8 light tenants at slot 0.
Built on the verified corpus case fair-share-vs-fifo (the same jobs in two orders, fair to requests or fair to tenants).
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== Fair share ==
One worker, jobs of one slot each, from one heavy tenant and several light ones.
1) the tenants 2) compare 3) settings 4) quit
choice> 0
Pick a number from 1 to 4.
1) the tenants 2) compare 3) settings 4) quit
choice> x
Pick a number from 1 to 4.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)>
Cancelled.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 0
Type a whole number from 1 to 30.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 31
Type a whole number from 1 to 30.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> x
Type a whole number from 1 to 30.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 5
light tenants (1 to 8)>
Cancelled.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 5
light tenants (1 to 8)> 9
Type a whole number from 1 to 8.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 5
light tenants (1 to 8)> 2
slot the light tenants send at (0 to 30)>
Cancelled.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 5
light tenants (1 to 8)> 2
slot the light tenants send at (0 to 30)> 31
Type a whole number from 0 to 30.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 30
light tenants (1 to 8)> 8
slot the light tenants send at (0 to 30)> 0
Tenant H sends 30 jobs at slot 0; tenants a to h send one each at slot 0. Every job takes one slot.
1) the tenants 2) compare 3) settings 4) quit
choice> 2
Slot by slot (H the heavy tenant, a letter each light one):
first come HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHabcdefgh
in turn HabcdefghHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
tenant jobs first come: worst mean in turn: worst mean
H 30 30 15.5 38 23.2
a 1 31 31.0 2 2.0
b 1 32 32.0 3 3.0
c 1 33 33.0 4 4.0
d 1 34 34.0 5 5.0
e 1 35 35.0 6 6.0
f 1 36 36.0 7 7.0
g 1 37 37.0 8 8.0
h 1 38 38.0 9 9.0
Worst completion among the light tenants: 38 first come, 9 in turn; tenant H's: 30 and 38.
Mean completion per job: 19.5 first come, 19.5 in turn; mean wait per job: 18.5 and 18.5.
Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.
1) the tenants 2) compare 3) settings 4) quit
choice> 4
Bye.
What was typed (30 lines)
0
x
3
3
0
3
31
3
x
3
5
3
5
9
3
5
2
3
5
2
31
3
30
8
0
2
4
basic
interpreter: byte-equal== Fair share ==
One worker, jobs of one slot each, from one heavy tenant and several light ones.
1) the tenants 2) compare 3) settings 4) quit
choice> 1
Tenant H sends 20 jobs at slot 0; tenants a to e send one each at slot 1. Every job takes one slot.
1) the tenants 2) compare 3) settings 4) quit
choice> 2
Slot by slot (H the heavy tenant, a letter each light one):
first come HHHHHHHHHHHHHHHHHHHHabcde
in turn HabcdeHHHHHHHHHHHHHHHHHHH
tenant jobs first come: worst mean in turn: worst mean
H 20 20 10.5 25 15.3
a 1 21 21.0 2 2.0
b 1 22 22.0 3 3.0
c 1 23 23.0 4 4.0
d 1 24 24.0 5 5.0
e 1 25 25.0 6 6.0
Worst completion among the light tenants: 25 first come, 6 in turn; tenant H's: 20 and 25.
Mean completion per job: 13.0 first come, 13.0 in turn; mean wait per job: 11.8 and 11.8.
Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 20
light tenants (1 to 8)> 5
slot the light tenants send at (0 to 30)> 25
Tenant H sends 20 jobs at slot 0; tenants a to e send one each at slot 25. Every job takes one slot.
1) the tenants 2) compare 3) settings 4) quit
choice> 2
Slot by slot (H the heavy tenant, a letter each light one):
first come HHHHHHHHHHHHHHHHHHHHabcde
in turn HHHHHHHHHHHHHHHHHHHHabcde
tenant jobs first come: worst mean in turn: worst mean
H 20 20 10.5 20 10.5
a 1 26 26.0 26 26.0
b 1 27 27.0 27 27.0
c 1 28 28.0 28 28.0
d 1 29 29.0 29 29.0
e 1 30 30.0 30 30.0
Worst completion among the light tenants: 30 first come, 30 in turn; tenant H's: 20 and 20.
Mean completion per job: 14.0 first come, 14.0 in turn; mean wait per job: 8.0 and 8.0.
Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.
1) the tenants 2) compare 3) settings 4) quit
choice> 3
tenant H's jobs (1 to 30)> 6
light tenants (1 to 8)> 3
slot the light tenants send at (0 to 30)> 0
Tenant H sends 6 jobs at slot 0; tenants a to c send one each at slot 0. Every job takes one slot.
1) the tenants 2) compare 3) settings 4) quit
choice> 2
Slot by slot (H the heavy tenant, a letter each light one):
first come HHHHHHabc
in turn HabcHHHHH
tenant jobs first come: worst mean in turn: worst mean
H 6 6 3.5 9 6.0
a 1 7 7.0 2 2.0
b 1 8 8.0 3 3.0
c 1 9 9.0 4 4.0
Worst completion among the light tenants: 9 first come, 4 in turn; tenant H's: 6 and 9.
Mean completion per job: 5.0 first come, 5.0 in turn; mean wait per job: 4.0 and 4.0.
Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.
1) the tenants 2) compare 3) settings 4) quit
choice> 4
Bye.
What was typed (13 lines)
1
2
3
20
5
25
2
3
6
3
0
2
4
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# P132 fair share: one heavy tenant and several light ones through first
# come first served and through taking tenants in turn, slot by slot and
# per tenant.
import fair
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 letter(t):
"Habcdefgh" => ls
return ls[t]
def tenths(p, q):
# p / q to one decimal, rounded half up
int((20 * p + q) / (2 * q)) => t
while t * 2 * q > 20 * p + q:
t - 1 => t
while (t + 1) * 2 * q <= 20 * p + q:
t + 1 => t
return str(int(t / 10)) + "." + str(t % 10)
def show(heavy, light, late):
("Tenant H sends " + str(heavy) + " jobs at slot 0; tenants a to " + letter(light) + " send one each at slot " + str(late) + ". Every job takes one slot.") ^0
def compare(heavy, light, late):
fair.jobs(heavy, light, late) => js
light + 1 => tenants
"Slot by slot (H the heavy tenant, a letter each light one):" ^0
[] => orders
for plan in ["fifo", "fair"]:
fair.schedule(js, tenants, plan) => o
orders + [o] => orders
"" => line
for e in o:
line + letter(js[e[0]][0]) => line
"first come" => label
if plan == "fair":
"in turn" => label
(" " + pad(label, 11) + line) ^0
" tenant jobs first come: worst mean in turn: worst mean" ^0
fair.per_tenant(js, tenants, orders[0]) => a
fair.per_tenant(js, tenants, orders[1]) => b
0 => worst_a
0 => worst_b
for t in [0:tenants - 1]:
(" " + pad(letter(t), 7) + lpad(str(a[t][0]), 6) + lpad(str(a[t][1]), 19) + lpad(tenths(a[t][2], a[t][0]), 6) + lpad(str(b[t][1]), 16) + lpad(tenths(b[t][2], b[t][0]), 6)) ^0
if t > 0:
if a[t][1] > worst_a:
a[t][1] => worst_a
if b[t][1] > worst_b:
b[t][1] => worst_b
fair.per_job(js, orders[0]) => ja
fair.per_job(js, orders[1]) => jb
("Worst completion among the light tenants: " + str(worst_a) + " first come, " + str(worst_b) + " in turn; tenant H's: " + str(a[0][1]) + " and " + str(b[0][1]) + ".") ^0
("Mean completion per job: " + tenths(ja[0], len(js)) + " first come, " + tenths(jb[0], len(js)) + " in turn; mean wait per job: " + tenths(ja[1], len(js)) + " and " + tenths(jb[1], len(js)) + ".") ^0
if ja[0] == jb[0]:
"Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes." ^0
def ask_int(prompt, low, high):
plain(input(prompt)) => t
if t == "":
"Cancelled." ^0
return 0 - 1
if len(t) > 2:
("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0
return 0 - 1
for c in t:
if c < "0" or c > "9":
("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0
return 0 - 1
if int(t) < low or int(t) > high:
("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0
return 0 - 1
return int(t)
def settings(heavy, light, late):
ask_int("tenant H's jobs (1 to 30)> ", 1, 30) => h
if h < 0:
return [heavy, light, late]
ask_int("light tenants (1 to 8)> ", 1, 8) => l
if l < 0:
return [heavy, light, late]
ask_int("slot the light tenants send at (0 to 30)> ", 0, 30) => s
if s < 0:
return [heavy, light, late]
show(h, l, s)
return [h, l, s]
20 => heavy
5 => light
1 => late
"== Fair share ==" ^0
"One worker, jobs of one slot each, from one heavy tenant and several light ones." ^0
True => running
while running:
"" ^0
"1) the tenants 2) compare 3) settings 4) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show(heavy, light, late)
elif choice == "2":
compare(heavy, light, late)
elif choice == "3":
settings(heavy, light, late) => s
s[0] => heavy
s[1] => light
s[2] => late
elif choice == "4":
False => running
else:
"Pick a number from 1 to 4." ^0
"Bye." ^0
Python projection (main.py)
import fair
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 letter(t):
ls = "Habcdefgh"
return ls[t]
def tenths(p, q):
t = int((20 * p + q) / (2 * q))
while t * 2 * q > 20 * p + q:
t = t - 1
while (t + 1) * 2 * q <= 20 * p + q:
t = t + 1
return str(int(t / 10)) + "." + str(t % 10)
def show(heavy, light, late):
print("Tenant H sends " + str(heavy) + " jobs at slot 0; tenants a to " + letter(light) + " send one each at slot " + str(late) + ". Every job takes one slot.")
def compare(heavy, light, late):
js = fair.jobs(heavy, light, late)
tenants = light + 1
print("Slot by slot (H the heavy tenant, a letter each light one):")
orders = []
for plan in ["fifo", "fair"]:
o = fair.schedule(js, tenants, plan)
orders = orders + [o]
line = ""
for e in o:
line = line + letter(js[e[0]][0])
label = "first come"
if plan == "fair":
label = "in turn"
print(" " + pad(label, 11) + line)
print(" tenant jobs first come: worst mean in turn: worst mean")
a = fair.per_tenant(js, tenants, orders[0])
b = fair.per_tenant(js, tenants, orders[1])
worst_a = 0
worst_b = 0
for t in range(0, tenants):
print(" " + pad(letter(t), 7) + lpad(str(a[t][0]), 6) + lpad(str(a[t][1]), 19) + lpad(tenths(a[t][2], a[t][0]), 6) + lpad(str(b[t][1]), 16) + lpad(tenths(b[t][2], b[t][0]), 6))
if t > 0:
if a[t][1] > worst_a:
worst_a = a[t][1]
if b[t][1] > worst_b:
worst_b = b[t][1]
ja = fair.per_job(js, orders[0])
jb = fair.per_job(js, orders[1])
print("Worst completion among the light tenants: " + str(worst_a) + " first come, " + str(worst_b) + " in turn; tenant H's: " + str(a[0][1]) + " and " + str(b[0][1]) + ".")
print("Mean completion per job: " + tenths(ja[0], len(js)) + " first come, " + tenths(jb[0], len(js)) + " in turn; mean wait per job: " + tenths(ja[1], len(js)) + " and " + tenths(jb[1], len(js)) + ".")
if ja[0] == jb[0]:
print("Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.")
def ask_int(prompt, low, high):
t = plain(input(prompt))
if t == "":
print("Cancelled.")
return 0 - 1
if len(t) > 2:
print("Type a whole number from " + str(low) + " to " + str(high) + ".")
return 0 - 1
for c in t:
if c < "0" or c > "9":
print("Type a whole number from " + str(low) + " to " + str(high) + ".")
return 0 - 1
if int(t) < low or int(t) > high:
print("Type a whole number from " + str(low) + " to " + str(high) + ".")
return 0 - 1
return int(t)
def settings(heavy, light, late):
h = ask_int("tenant H's jobs (1 to 30)> ", 1, 30)
if h < 0:
return [heavy, light, late]
l = ask_int("light tenants (1 to 8)> ", 1, 8)
if l < 0:
return [heavy, light, late]
s = ask_int("slot the light tenants send at (0 to 30)> ", 0, 30)
if s < 0:
return [heavy, light, late]
show(h, l, s)
return [h, l, s]
heavy = 20
light = 5
late = 1
print("== Fair share ==")
print("One worker, jobs of one slot each, from one heavy tenant and several light ones.")
running = True
while running:
print("")
print("1) the tenants 2) compare 3) settings 4) quit")
choice = plain(input("choice> "))
if choice == "1":
show(heavy, light, late)
elif choice == "2":
compare(heavy, light, late)
elif choice == "3":
s = settings(heavy, light, late)
heavy = s[0]
light = s[1]
late = s[2]
elif choice == "4":
running = False
else:
print("Pick a number from 1 to 4.")
print("Bye.")
fair.eml
eml# P132 fair share - one worker, jobs of one slot each from several tenants.
# As in the corpus case fair-share-vs-fifo, first come first served is fair
# to jobs, and taking the tenants in turn is fair to tenants. Each job is
# [tenant, arrival slot]; the worker runs one job a slot, never idle while a
# job is waiting.
def jobs(heavy, light, late):
# tenant 0 sends heavy jobs at slot 0; tenants 1 to light one each at
# slot late
[] => out
for k in [1:heavy]:
out + [[0, 0]] => out
for t in [1:light]:
out + [[t, late]] => out
return out
def schedule(js, tenants, plan):
# the order the jobs run in, as [job index, start slot]
[] => done
for j in js:
done + [False] => done
[] => out
0 => slot
0 => turn
while len(out) < len(js):
0 - 1 => pick
if plan == "fifo":
for i in [0:len(js) - 1]:
if pick < 0 and not done[i] and js[i][1] <= slot:
i => pick
else:
for step in [0:tenants - 1]:
(turn + step) % tenants => t
if pick < 0:
for i in [0:len(js) - 1]:
if pick < 0 and not done[i] and js[i][0] == t and js[i][1] <= slot:
i => pick
if pick >= 0:
(t + 1) % tenants => turn
if pick >= 0:
True => done[pick]
out + [[pick, slot]] => out
slot + 1 => slot
return out
def per_tenant(js, tenants, order):
# per tenant [jobs, worst completion, total completion]; completion is the
# slot after the job's slot
[] => out
for t in [0:tenants - 1]:
out + [[0, 0, 0]] => out
for e in order:
js[e[0]][0] => t
e[1] + 1 => c
out[t][0] + 1 => out[t][0]
if c > out[t][1]:
c => out[t][1]
out[t][2] + c => out[t][2]
return out
def per_job(js, order):
# [total completion, total wait] over all jobs
0 => c
0 => w
for e in order:
c + e[1] + 1 => c
w + e[1] - js[e[0]][1] => w
return [c, w]
Python projection (fair.py)
def jobs(heavy, light, late):
out = []
for k in range(1, heavy+1):
out = out + [[0, 0]]
for t in range(1, light+1):
out = out + [[t, late]]
return out
def schedule(js, tenants, plan):
done = []
for j in js:
done = done + [False]
out = []
slot = 0
turn = 0
while len(out) < len(js):
pick = 0 - 1
if plan == "fifo":
for i in range(0, len(js)):
if pick < 0 and not done[i] and js[i][1] <= slot:
pick = i
else:
for step in range(0, tenants):
t = (turn + step) % tenants
if pick < 0:
for i in range(0, len(js)):
if pick < 0 and not done[i] and js[i][0] == t and js[i][1] <= slot:
pick = i
if pick >= 0:
turn = (t + 1) % tenants
if pick >= 0:
done[pick] = True
out = out + [[pick, slot]]
slot = slot + 1
return out
def per_tenant(js, tenants, order):
out = []
for t in range(0, tenants):
out = out + [[0, 0, 0]]
for e in order:
t = js[e[0]][0]
c = e[1] + 1
out[t][0] = out[t][0] + 1
if c > out[t][1]:
out[t][1] = c
out[t][2] = out[t][2] + c
return out
def per_job(js, order):
c = 0
w = 0
for e in order:
c = c + e[1] + 1
w = w + e[1] - js[e[0]][1]
return [c, w]