Tax brackets
Income bands written from a sentence and read four ways - closed, half-open, open, left-open - the incomes on a band's end, the slab tax the reading decides and the marginal tax it cannot change.
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
Income bands written from a sentence - 0-10000, 10000-40000, 40000-100000, 100000 and above - read four ways, and the incomes that sit on a band's end.
main.eml- the menu, the bands under each reading, one income under each, the partition check over the incomes on file, and adding onebands.eml- the bands, the readings, the incomes on file, whether a band takes an income, the bands that take it, the slab tax and the marginal tax
How each part works:
- As in the corpus case
boundary-inclusive-or-not, "between" is four questions - closed, half-open, open, left-open - and they part only at the ends: 0, 10000, 40000 and 100000. Every income in the middle lands in the same band under all four, which is why the difference survives every test there. - Consecutive bands read closed take the three inner ends twice; read open they take the four ends not at all; left-open misses only 0. Half-open alone puts every income in exactly one band: the bands are a partition.
- The slab tax charges the whole income at its band's rate, so the reading decides it at an end: 10000 pays 0.00, 1000.00 or 3000.00 - read open no band takes it, and the else branch charges the top rate.
- The marginal tax charges each slice at its own band's rate. It is the same under every reading, a slice's width not depending on which band owns its end, and it does not jump.
What is checked: menu choices 1 to 5; an income from 0 to 9999999. An empty answer cancels.
Sessions: sessions/basic.in gives: - the bands; incomes 10000, 25000, 100000 and 0; - the incomes on file; 40000 added, and the incomes on file again.
sessions/bad-input.in gives: - menu choices 0 and x; - incomes: a cancel, 12345678, 1e5 and -5; - adding: a cancel and x, then 99999; the incomes on file.
Built on the verified corpus case boundary-inclusive-or-not (four readings of a range, consecutive buckets that double-count or drop a boundary, and a partition check by counting).
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== Tax brackets ==
Bands written from a sentence, read four ways, and the incomes that sit on their ends.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 0
Pick a number from 1 to 5.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> x
Pick a number from 1 to 5.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)>
Cancelled.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> 12345678
Type a whole number from 0 to 9999999.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> 1e5
Type a whole number from 0 to 9999999.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> -5
Type a whole number from 0 to 9999999.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 4
income (0 to 9999999)>
Cancelled.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 4
income (0 to 9999999)> x
Type a whole number from 0 to 9999999.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 4
income (0 to 9999999)> 99999
Income 99999 added; 10 on file.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 3
Over the 10 incomes on file:
reading in no band in one in two slab tax in all
closed 0 7 3 113900.00
half-open 0 10 0 128900.00
open 4 6 0 134900.00
left-open 1 9 0 113900.00
The readings part only at 0, 10000, 40000, 100000: incomes on a band's end.
Every income in exactly one band: half-open.
Marginal tax in all: 76900.00, the same under every reading.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 5
Bye.
What was typed (18 lines)
0
x
2
2
12345678
2
1e5
2
-5
4
4
x
4
99999
3
5
basic
interpreter: byte-equal== Tax brackets ==
Bands written from a sentence, read four ways, and the incomes that sit on their ends.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 1
The sentence: 0-10000 at 0%, 10000-40000 at 10%, 40000-100000 at 20%, 100000 and above at 30%.
band closed half-open open left-open
1 0% [0, 10000] [0, 10000) (0, 10000) (0, 10000]
2 10% [10000, 40000] [10000, 40000) (10000, 40000) (10000, 40000]
3 20% [40000, 100000] [40000, 100000) (40000, 100000) (40000, 100000]
4 30% [100000, and up) [100000, and up) (100000, and up) (100000, and up)
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> 10000
Income 10000:
reading bands that take it slab tax: the whole income at the band's rate
closed 1 and 2 0.00 - band 1, the first that takes it
half-open 2 1000.00
open none 3000.00 - no band takes it, and the else branch charges the top rate
left-open 1 0.00
Marginal tax, each slice at its own band's rate: 0.00 under every reading: a slice's width does not depend on which band owns its end.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> 25000
Income 25000:
reading bands that take it slab tax: the whole income at the band's rate
closed 2 2500.00
half-open 2 2500.00
open 2 2500.00
left-open 2 2500.00
Marginal tax, each slice at its own band's rate: 1500.00 under every reading: a slice's width does not depend on which band owns its end.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> 100000
Income 100000:
reading bands that take it slab tax: the whole income at the band's rate
closed 3 and 4 20000.00 - band 3, the first that takes it
half-open 4 30000.00
open none 30000.00 - no band takes it, and the else branch charges the top rate
left-open 3 20000.00
Marginal tax, each slice at its own band's rate: 15000.00 under every reading: a slice's width does not depend on which band owns its end.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 2
income (0 to 9999999)> 0
Income 0:
reading bands that take it slab tax: the whole income at the band's rate
closed 1 0.00
half-open 1 0.00
open none 0.00 - no band takes it, and the else branch charges the top rate
left-open none 0.00 - no band takes it, and the else branch charges the top rate
Marginal tax, each slice at its own band's rate: 0.00 under every reading: a slice's width does not depend on which band owns its end.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 3
Over the 9 incomes on file:
reading in no band in one in two slab tax in all
closed 0 6 3 93900.20
half-open 0 9 0 108900.20
open 4 5 0 114900.20
left-open 1 8 0 93900.20
The readings part only at 0, 10000, 40000, 100000: incomes on a band's end.
Every income in exactly one band: half-open.
Marginal tax in all: 61900.20, the same under every reading.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 4
income (0 to 9999999)> 40000
Income 40000 added; 10 on file.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 3
Over the 10 incomes on file:
reading in no band in one in two slab tax in all
closed 0 6 4 97900.20
half-open 0 10 0 116900.20
open 5 5 0 126900.20
left-open 1 9 0 97900.20
The readings part only at 0, 10000, 40000, 100000: incomes on a band's end.
Every income in exactly one band: half-open.
Marginal tax in all: 64900.20, the same under every reading.
1) bands 2) one income 3) the incomes on file 4) add an income 5) quit
choice> 5
Bye.
What was typed (14 lines)
1
2
10000
2
25000
2
100000
2
0
3
4
40000
3
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# P151 tax brackets: the bands and their four readings, one income under
# each, the partition check over the incomes on file, and adding one.
import bands
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 money(c):
c % 100 => p
if p < 10:
return str(int((c - p) / 100)) + ".0" + str(p)
return str(int((c - p) / 100)) + "." + str(p)
def interval(reading, b):
if reading == "closed" or reading == "half-open":
"[" => left
else:
"(" => left
if b[1] < 0:
return left + str(b[0]) + ", and up)"
if reading == "closed" or reading == "left-open":
"]" => right
else:
")" => right
return left + str(b[0]) + ", " + str(b[1]) + right
def show_bands():
"The sentence: 0-10000 at 0%, 10000-40000 at 10%, 40000-100000 at 20%, 100000 and above at 30%." ^0
(" " + pad("band", 6) + pad("closed", 18) + pad("half-open", 18) + pad("open", 18) + "left-open") ^0
1 => k
for b in bands.bands():
(" " + pad(str(k) + " " + str(b[2]) + "%", 6) + pad(interval("closed", b), 18) + pad(interval("half-open", b), 18) + pad(interval("open", b), 18) + interval("left-open", b)) ^0
k + 1 => k
def names(ks):
if len(ks) == 0:
return "none"
"" => out
for k in ks:
if out == "":
str(k) => out
else:
out + " and " + str(k) => out
return out
def one_income():
plain(input("income (0 to 9999999)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) > 7:
"Type a whole number from 0 to 9999999." ^0
return
for c in t:
if c < "0" or c > "9":
"Type a whole number from 0 to 9999999." ^0
return
int(t) => x
show_income(x)
def show_income(x):
("Income " + str(x) + ":") ^0
(" " + pad("reading", 11) + pad("bands that take it", 20) + "slab tax: the whole income at the band's rate") ^0
for r in bands.readings():
bands.taking(r, x) => ks
money(bands.slab(r, x)) => s
if len(ks) == 0:
s + " - no band takes it, and the else branch charges the top rate" => s
elif len(ks) > 1:
s + " - band " + str(ks[0]) + ", the first that takes it" => s
(" " + pad(r, 11) + pad(names(ks), 20) + s) ^0
("Marginal tax, each slice at its own band's rate: " + money(bands.marginal(x)) + " under every reading: a slice's width does not depend on which band owns its end.") ^0
def partition(xs):
("Over the " + str(len(xs)) + " incomes on file:") ^0
(" " + pad("reading", 11) + "in no band in one in two slab tax in all") ^0
"" => exact
for r in bands.readings():
0 => none
0 => one
0 => two
0 => total
for x in xs:
len(bands.taking(r, x)) => n
if n == 0:
none + 1 => none
elif n == 1:
one + 1 => one
else:
two + 1 => two
total + bands.slab(r, x) => total
(" " + pad(r, 11) + lpad(str(none), 10) + lpad(str(one), 8) + lpad(str(two), 8) + lpad(money(total), 18)) ^0
if one == len(xs):
if exact == "":
r => exact
else:
exact + " and " + r => exact
"" => edge
[] => seen
for x in xs:
bands.taking("closed", x) => a
bands.taking("open", x) => b
False => dup
for y in seen:
if y == x:
True => dup
if (len(a) != 1 or len(b) != 1) and not dup:
seen + [x] => seen
if edge == "":
str(x) => edge
else:
edge + ", " + str(x) => edge
if edge != "":
("The readings part only at " + edge + ": incomes on a band's end.") ^0
("Every income in exactly one band: " + exact + ".") ^0
0 => m
for x in xs:
m + bands.marginal(x) => m
("Marginal tax in all: " + money(m) + ", the same under every reading.") ^0
def add(xs):
plain(input("income (0 to 9999999)> ")) => t
if t == "":
"Cancelled." ^0
return xs
if len(t) > 7:
"Type a whole number from 0 to 9999999." ^0
return xs
for c in t:
if c < "0" or c > "9":
"Type a whole number from 0 to 9999999." ^0
return xs
int(t) => x
("Income " + str(x) + " added; " + str(len(xs) + 1) + " on file.") ^0
return xs + [x]
bands.incomes() => xs
"== Tax brackets ==" ^0
"Bands written from a sentence, read four ways, and the incomes that sit on their ends." ^0
True => running
while running:
"" ^0
"1) bands 2) one income 3) the incomes on file 4) add an income 5) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_bands()
elif choice == "2":
one_income()
elif choice == "3":
partition(xs)
elif choice == "4":
add(xs) => xs
elif choice == "5":
False => running
else:
"Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import bands
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 money(c):
p = c % 100
if p < 10:
return str(int((c - p) / 100)) + ".0" + str(p)
return str(int((c - p) / 100)) + "." + str(p)
def interval(reading, b):
if reading == "closed" or reading == "half-open":
left = "["
else:
left = "("
if b[1] < 0:
return left + str(b[0]) + ", and up)"
if reading == "closed" or reading == "left-open":
right = "]"
else:
right = ")"
return left + str(b[0]) + ", " + str(b[1]) + right
def show_bands():
print("The sentence: 0-10000 at 0%, 10000-40000 at 10%, 40000-100000 at 20%, 100000 and above at 30%.")
print(" " + pad("band", 6) + pad("closed", 18) + pad("half-open", 18) + pad("open", 18) + "left-open")
k = 1
for b in bands.bands():
print(" " + pad(str(k) + " " + str(b[2]) + "%", 6) + pad(interval("closed", b), 18) + pad(interval("half-open", b), 18) + pad(interval("open", b), 18) + interval("left-open", b))
k = k + 1
def names(ks):
if len(ks) == 0:
return "none"
out = ""
for k in ks:
if out == "":
out = str(k)
else:
out = out + " and " + str(k)
return out
def one_income():
t = plain(input("income (0 to 9999999)> "))
if t == "":
print("Cancelled.")
return
if len(t) > 7:
print("Type a whole number from 0 to 9999999.")
return
for c in t:
if c < "0" or c > "9":
print("Type a whole number from 0 to 9999999.")
return
x = int(t)
show_income(x)
def show_income(x):
print("Income " + str(x) + ":")
print(" " + pad("reading", 11) + pad("bands that take it", 20) + "slab tax: the whole income at the band's rate")
for r in bands.readings():
ks = bands.taking(r, x)
s = money(bands.slab(r, x))
if len(ks) == 0:
s = s + " - no band takes it, and the else branch charges the top rate"
elif len(ks) > 1:
s = s + " - band " + str(ks[0]) + ", the first that takes it"
print(" " + pad(r, 11) + pad(names(ks), 20) + s)
print("Marginal tax, each slice at its own band's rate: " + money(bands.marginal(x)) + " under every reading: a slice's width does not depend on which band owns its end.")
def partition(xs):
print("Over the " + str(len(xs)) + " incomes on file:")
print(" " + pad("reading", 11) + "in no band in one in two slab tax in all")
exact = ""
for r in bands.readings():
none = 0
one = 0
two = 0
total = 0
for x in xs:
n = len(bands.taking(r, x))
if n == 0:
none = none + 1
elif n == 1:
one = one + 1
else:
two = two + 1
total = total + bands.slab(r, x)
print(" " + pad(r, 11) + lpad(str(none), 10) + lpad(str(one), 8) + lpad(str(two), 8) + lpad(money(total), 18))
if one == len(xs):
if exact == "":
exact = r
else:
exact = exact + " and " + r
edge = ""
seen = []
for x in xs:
a = bands.taking("closed", x)
b = bands.taking("open", x)
dup = False
for y in seen:
if y == x:
dup = True
if (len(a) != 1 or len(b) != 1) and not dup:
seen = seen + [x]
if edge == "":
edge = str(x)
else:
edge = edge + ", " + str(x)
if edge != "":
print("The readings part only at " + edge + ": incomes on a band's end.")
print("Every income in exactly one band: " + exact + ".")
m = 0
for x in xs:
m = m + bands.marginal(x)
print("Marginal tax in all: " + money(m) + ", the same under every reading.")
def add(xs):
t = plain(input("income (0 to 9999999)> "))
if t == "":
print("Cancelled.")
return xs
if len(t) > 7:
print("Type a whole number from 0 to 9999999.")
return xs
for c in t:
if c < "0" or c > "9":
print("Type a whole number from 0 to 9999999.")
return xs
x = int(t)
print("Income " + str(x) + " added; " + str(len(xs) + 1) + " on file.")
return xs + [x]
xs = bands.incomes()
print("== Tax brackets ==")
print("Bands written from a sentence, read four ways, and the incomes that sit on their ends.")
running = True
while running:
print("")
print("1) bands 2) one income 3) the incomes on file 4) add an income 5) quit")
choice = plain(input("choice> "))
if choice == "1":
show_bands()
elif choice == "2":
one_income()
elif choice == "3":
partition(xs)
elif choice == "4":
xs = add(xs)
elif choice == "5":
running = False
else:
print("Pick a number from 1 to 5.")
print("Bye.")
bands.eml
eml# P151 tax brackets - income bands written from a sentence, "0-10000,
# 10000-40000, 40000-100000, 100000 and above", read four ways. As in the
# corpus case boundary-inclusive-or-not, "between" is four questions -
# closed, half-open, open, left-open - and they differ only at the end
# points, which is why the difference survives every test in the middle.
# Consecutive bands read closed take a boundary twice and read open take it
# not at all; only half-open makes the bands a partition. A band is [low,
# high, rate in percent]; the last has no high.
def bands():
return [[0, 10000, 0], [10000, 40000, 10], [40000, 100000, 20], [100000, 0 - 1, 30]]
def readings():
return ["closed", "half-open", "open", "left-open"]
def incomes():
return [0, 8500, 10000, 25000, 40000, 40001, 72000, 100000, 150000]
def takes(reading, b, x):
# does band b take income x under the reading
if reading == "closed" or reading == "half-open":
x >= b[0] => low_ok
else:
x > b[0] => low_ok
if b[1] < 0:
return low_ok
if reading == "closed" or reading == "left-open":
x <= b[1] => high_ok
else:
x < b[1] => high_ok
return low_ok and high_ok
def taking(reading, x):
# the numbers of the bands that take x, from 1
[] => out
1 => k
for b in bands():
if takes(reading, b, x):
out + [k] => out
k + 1 => k
return out
def slab(reading, x):
# the whole income at the rate of the first band that takes it, in
# cents; with no band the else branch charges the top rate
for b in bands():
if takes(reading, b, x):
return x * b[2]
return x * bands()[len(bands()) - 1][2]
def marginal(x):
# each slice of the income at its own band's rate, in cents
0 => t
for b in bands():
x => top
if b[1] >= 0 and b[1] < top:
b[1] => top
if top > b[0]:
t + (top - b[0]) * b[2] => t
return t
Python projection (bands.py)
def bands():
return [[0, 10000, 0], [10000, 40000, 10], [40000, 100000, 20], [100000, 0 - 1, 30]]
def readings():
return ["closed", "half-open", "open", "left-open"]
def incomes():
return [0, 8500, 10000, 25000, 40000, 40001, 72000, 100000, 150000]
def takes(reading, b, x):
if reading == "closed" or reading == "half-open":
low_ok = x >= b[0]
else:
low_ok = x > b[0]
if b[1] < 0:
return low_ok
if reading == "closed" or reading == "left-open":
high_ok = x <= b[1]
else:
high_ok = x < b[1]
return low_ok and high_ok
def taking(reading, x):
out = []
k = 1
for b in bands():
if takes(reading, b, x):
out = out + [k]
k = k + 1
return out
def slab(reading, x):
for b in bands():
if takes(reading, b, x):
return x * b[2]
return x * bands()[len(bands()) - 1][2]
def marginal(x):
t = 0
for b in bands():
top = x
if b[1] >= 0 and b[1] < top:
top = b[1]
if top > b[0]:
t = t + (top - b[0]) * b[2]
return t