Eligibility rules
300 made-up applicants and three rules written two ways: a negation distributed by hand keeps 56 it should skip, one clause of member-or-referred never decides, and a guard moved behind its test divides by zero.
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
300 made-up applicants for a benefit and three rules, each written two ways: who the difference reaches.
main.eml- the menu, the applicants, the skip rule, which clause decides an approval, the means test, and an applicant of your ownrules.eml- the applicants, drawn from a fixed seed, and each rule in its two forms
How each part works:
- An applicant is skipped unless a resident adult, approved as a member or a referred applicant, and paid only if the average income on record is under 2,000.00 a month.
- As in the corpus case
the-negation-was-distributed-by-hand, not (resident and adult) rewritten as (not resident) and (not adult) agrees where both clauses hold or both fail, and keeps the applicants who fail exactly one: 56 of the 300, 11 residents under 18 and 45 adults from elsewhere. - As in
which-clause-actually-decided, "member or referred" is right for every applicant, and of the 101 approvals 76 are carried by membership alone, 25 by both and none by the referral alone: every referred applicant here is a member. That is a fact about who applies. - As in
the-guard-was-moved-for-readability, the means test with the months checked first answers for all 101; with the average first it gives the same answer on the 94 with months on record and divides by zero on the 7 without.
What is checked: menu choices 1 to 6; y or n (or yes or no) for resident, member and referred; an age from 0 to 120; 0 to 24 months on record; a whole income from 0 to 999999. An empty answer cancels.
Sessions: sessions/basic.in gives: - the applicants, the skip rule, the clauses and the means test; - your own: a 17-year-old resident member (the hand-written rule keeps them), a referred adult from elsewhere with no months on record, and a resident member referred as well, over the income limit.
sessions/bad-input.in gives: - menu choices 0 and x; - a cancel and "maybe" for resident; ages: a cancel, 121 and x; member: a cancel and k; referred: a cancel and q; months: a cancel, 25 and x; income: a cancel, 1000000 and 1.5; - a 16-year-old from elsewhere, neither member nor referred, with 12 months and no income.
Built on the verified corpus cases the-negation-was-distributed-by-hand (a skip condition inverted with the negation distributed as an and), which-clause-actually-decided (administrator or owner, scored by which clause carried each request) and the-guard-was-moved-for-readability (two clauses swapped so the meaningful one reads first).
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== Eligibility rules ==
Three rules for a benefit, each written two ways, and who the difference reaches.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 0
Pick a number from 1 to 6.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> x
Pick a number from 1 to 6.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)>
Cancelled.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> maybe
Type y or n.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)>
Cancelled.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 121
Type a whole number from 0 to 120.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> x
Type a whole number from 0 to 120.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)>
Cancelled.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> k
Type y or n.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)>
Cancelled.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> q
Type y or n.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)>
Cancelled.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)> 25
Type a whole number from 0 to 24.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)> x
Type a whole number from 0 to 24.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)> 4
income over those months, whole units (0 to 999999)>
Cancelled.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)> 4
income over those months, whole units (0 to 999999)> 1000000
Type a whole number from 0 to 999999.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 30
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)> 4
income over those months, whole units (0 to 999999)> 1.5
Type a whole number from 0 to 999999.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> no
age (0 to 120)> 16
member (y/n)> no
referred (y/n)> no
months of income on record (0 to 24)> 12
income over those months, whole units (0 to 999999)> 0
The skip rule: as meant it skips them; written by hand it skips them.
Member or referred: not approved.
The means test: 0.00 a month on average, under 2,000.00, so it passes either way.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 6
Bye.
What was typed (80 lines)
0
x
5
5
maybe
5
y
5
y
121
5
y
x
5
y
30
5
y
30
k
5
y
30
y
5
y
30
y
q
5
y
30
y
n
5
y
30
y
n
25
5
y
30
y
n
x
5
y
30
y
n
4
5
y
30
y
n
4
1000000
5
y
30
y
n
4
1.5
5
no
16
no
no
12
0
6
basic
interpreter: byte-equal== Eligibility rules ==
Three rules for a benefit, each written two ways, and who the difference reaches.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 1
300 applicants for a benefit; the first eight:
resident age member referred months income on record
1 yes 34 yes yes 9 27,900.00
2 yes 43 no no 9 16,650.00
3 no 46 no no 0 0.00
4 yes 35 yes yes 6 14,700.00
5 yes 60 no no 10 30,000.00
6 yes 43 yes yes 2 6,800.00
7 yes 61 yes yes 7 22,400.00
8 yes 27 yes no 8 15,200.00
The rules: skip anyone who is not a resident adult; approve a member or a referred applicant; pay only if the average income on record is under 2,000.00 a month.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 2
Skip unless resident and adult, written two ways:
resident adult not (r and a) not r and not a applicants
yes yes no no 242
yes no yes no 11
no yes yes no 45
no no yes yes 2
The two agree where both clauses hold or both fail. Where exactly one fails, the hand-written form keeps the applicant: 56 of 300 here, the residents under 18 and the adults from elsewhere.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 3
Of the 242 resident adults, 101 are approved, as member or referred. Which clause carried each:
membership alone 76
referral alone 0
both 25
The referral clause decided 0 approvals: every referred applicant here is a member. The rule is right every time, and half of it has never been the reason - a fact about who applies, not about the code.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 4
The means test on the 101 approved applicants: income on record / months under 2,000.00, and months on record above 0.
Guard first, months checked before the division: an answer for all 101.
Meaning first, the average before the months: the same answer on 94, and a division by zero on the 7 with no months on record.
Moving the guard behind the test it protects reads better and is no longer a guard.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 17
member (y/n)> y
referred (y/n)> n
months of income on record (0 to 24)> 3
income over those months, whole units (0 to 999999)> 4500
The skip rule: as meant it skips them; written by hand it keeps them.
Member or referred: approved, by membership alone.
The means test: 1,500.00 a month on average, under 2,000.00, so it passes either way.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> n
age (0 to 120)> 30
member (y/n)> n
referred (y/n)> y
months of income on record (0 to 24)> 0
The skip rule: as meant it skips them; written by hand it keeps them.
Member or referred: approved, by referral alone.
The means test: guard first, no months on record, so no; meaning first, a division by zero before the guard is read.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 5
resident (y/n)> y
age (0 to 120)> 40
member (y/n)> y
referred (y/n)> y
months of income on record (0 to 24)> 6
income over those months, whole units (0 to 999999)> 15000
The skip rule: as meant it keeps them; written by hand it keeps them.
Member or referred: approved, by both.
The means test: 2,500.00 a month on average, not under 2,000.00, so it fails either way.
1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit
choice> 6
Bye.
What was typed (25 lines)
1
2
3
4
5
y
17
y
n
3
4500
5
n
30
n
y
0
5
y
40
y
y
6
15000
6
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# P170 eligibility rules: 300 applicants and three rules written two ways -
# the applicants, the skip rule, which clause decides an approval, the
# means test with its guard moved, and an applicant of your own.
import rules
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 digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def commas(n):
str(n) => s
"" => out
0 => k
len(s) => i
while i > 0:
if k == 3:
"," + out => out
0 => k
s[i - 1] + out => out
k + 1 => k
i - 1 => i
return out
def money(c):
c % 100 => r
if r < 10:
return commas(int((c - r) / 100)) + ".0" + str(r)
return commas(int((c - r) / 100)) + "." + str(r)
def yn(b):
if b:
return "yes"
return "no"
def show_applicants():
rules.applicants() => aps
("300 applicants for a benefit; the first eight:") ^0
(" " + pad("", 4) + pad("resident", 10) + lpad("age", 4) + " " + pad("member", 8) + pad("referred", 10) + lpad("months", 7) + lpad("income on record", 18)) ^0
0 => i
while i < 8:
aps[i] => a
(" " + pad(str(i + 1), 4) + pad(yn(a[0]), 10) + lpad(str(a[1]), 4) + " " + pad(yn(a[2]), 8) + pad(yn(a[3]), 10) + lpad(str(a[4]), 7) + lpad(money(a[5]), 18)) ^0
i + 1 => i
"The rules: skip anyone who is not a resident adult; approve a member or a referred applicant; pay only if the average income on record is under 2,000.00 a month." ^0
def show_skip():
rules.applicants() => aps
"Skip unless resident and adult, written two ways:" ^0
(" " + pad("resident", 10) + pad("adult", 7) + pad("not (r and a)", 15) + pad("not r and not a", 17) + "applicants") ^0
[[True, True], [True, False], [False, True], [False, False]] => combos
0 => differ
for c in combos:
0 => n
for a in aps:
if a[0] == c[0] and rules.adult(a) == c[1]:
n + 1 => n
[c[0], 18] => probe
if not c[1]:
[c[0], 17] => probe
rules.skip_meant(probe) => s1
rules.skip_by_hand(probe) => s2
if s1 != s2:
differ + n => differ
(" " + pad(yn(c[0]), 10) + pad(yn(c[1]), 7) + pad(yn(s1), 15) + pad(yn(s2), 17) + lpad(str(n), 10)) ^0
("The two agree where both clauses hold or both fail. Where exactly one fails, the hand-written form keeps the applicant: " + str(differ) + " of 300 here, the residents under 18 and the adults from elsewhere.") ^0
def show_clause():
rules.applicants() => aps
0 => kept
0 => yes
0 => m
0 => r
0 => b
for a in aps:
if not rules.skip_meant(a):
kept + 1 => kept
if rules.approved(a):
yes + 1 => yes
rules.decided(a) => d
if d == "member":
m + 1 => m
if d == "referral":
r + 1 => r
if d == "both":
b + 1 => b
("Of the " + str(kept) + " resident adults, " + str(yes) + " are approved, as member or referred. Which clause carried each:") ^0
(" membership alone " + lpad(str(m), 4)) ^0
(" referral alone " + lpad(str(r), 4)) ^0
(" both " + lpad(str(b), 4)) ^0
("The referral clause decided " + str(r) + " approvals: every referred applicant here is a member. The rule is right every time, and half of it has never been the reason - a fact about who applies, not about the code.") ^0
def show_income():
rules.applicants() => aps
0 => zero
0 => agree
0 => n
for a in aps:
if not rules.skip_meant(a) and rules.approved(a):
n + 1 => n
rules.income_guard_first(a) => g
rules.income_meaning_first(a) => f
if f == "divides by zero":
zero + 1 => zero
elif f == g:
agree + 1 => agree
("The means test on the " + str(n) + " approved applicants: income on record / months under 2,000.00, and months on record above 0.") ^0
("Guard first, months checked before the division: an answer for all " + str(n) + ".") ^0
("Meaning first, the average before the months: the same answer on " + str(agree) + ", and a division by zero on the " + str(zero) + " with no months on record.") ^0
"Moving the guard behind the test it protects reads better and is no longer a guard." ^0
def ask_yes(prompt):
# True, False, or "" when cancelled, "bad" when neither
plain(input(prompt + " (y/n)> ")) => t
if t == "":
return ""
if t == "y" or t == "yes":
return True
if t == "n" or t == "no":
return False
return "bad"
def own():
ask_yes("resident") => res
if res == "":
"Cancelled." ^0
return
if res == "bad":
"Type y or n." ^0
return
plain(input("age (0 to 120)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) > 3 or not digits(t) or int(t) > 120:
"Type a whole number from 0 to 120." ^0
return
int(t) => age
ask_yes("member") => mem
if mem == "":
"Cancelled." ^0
return
if mem == "bad":
"Type y or n." ^0
return
ask_yes("referred") => ref
if ref == "":
"Cancelled." ^0
return
if ref == "bad":
"Type y or n." ^0
return
plain(input("months of income on record (0 to 24)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) > 2 or not digits(t) or int(t) > 24:
"Type a whole number from 0 to 24." ^0
return
int(t) => months
0 => total
if months > 0:
plain(input("income over those months, whole units (0 to 999999)> ")) => t
if t == "":
"Cancelled." ^0
return
if len(t) > 6 or not digits(t):
"Type a whole number from 0 to 999999." ^0
return
int(t) * 100 => total
[res, age, mem, ref, months, total] => a
("The skip rule: as meant it " + skipped(rules.skip_meant(a)) + " them; written by hand it " + skipped(rules.skip_by_hand(a)) + " them.") ^0
if rules.approved(a):
("Member or referred: approved, by " + ["membership alone", "referral alone", "both"][index(rules.decided(a))] + ".") ^0
else:
"Member or referred: not approved." ^0
rules.income_guard_first(a) => g
rules.income_meaning_first(a) => f
if months == 0:
"The means test: guard first, no months on record, so no; meaning first, a division by zero before the guard is read." ^0
else:
("The means test: " + money(int(total / months)) + " a month on average, " + verdict(g) + " either way.") ^0
def skipped(b):
if b:
return "skips"
return "keeps"
def index(d):
if d == "member":
return 0
if d == "referral":
return 1
return 2
def verdict(g):
if g:
return "under 2,000.00, so it passes"
return "not under 2,000.00, so it fails"
"== Eligibility rules ==" ^0
"Three rules for a benefit, each written two ways, and who the difference reaches." ^0
True => running
while running:
"" ^0
"1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_applicants()
elif choice == "2":
show_skip()
elif choice == "3":
show_clause()
elif choice == "4":
show_income()
elif choice == "5":
own()
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import rules
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 digits(t):
for c in t:
if c < "0" or c > "9":
return False
return len(t) > 0
def commas(n):
s = str(n)
out = ""
k = 0
i = len(s)
while i > 0:
if k == 3:
out = "," + out
k = 0
out = s[i - 1] + out
k = k + 1
i = i - 1
return out
def money(c):
r = c % 100
if r < 10:
return commas(int((c - r) / 100)) + ".0" + str(r)
return commas(int((c - r) / 100)) + "." + str(r)
def yn(b):
if b:
return "yes"
return "no"
def show_applicants():
aps = rules.applicants()
print("300 applicants for a benefit; the first eight:")
print(" " + pad("", 4) + pad("resident", 10) + lpad("age", 4) + " " + pad("member", 8) + pad("referred", 10) + lpad("months", 7) + lpad("income on record", 18))
i = 0
while i < 8:
a = aps[i]
print(" " + pad(str(i + 1), 4) + pad(yn(a[0]), 10) + lpad(str(a[1]), 4) + " " + pad(yn(a[2]), 8) + pad(yn(a[3]), 10) + lpad(str(a[4]), 7) + lpad(money(a[5]), 18))
i = i + 1
print("The rules: skip anyone who is not a resident adult; approve a member or a referred applicant; pay only if the average income on record is under 2,000.00 a month.")
def show_skip():
aps = rules.applicants()
print("Skip unless resident and adult, written two ways:")
print(" " + pad("resident", 10) + pad("adult", 7) + pad("not (r and a)", 15) + pad("not r and not a", 17) + "applicants")
combos = [[True, True], [True, False], [False, True], [False, False]]
differ = 0
for c in combos:
n = 0
for a in aps:
if a[0] == c[0] and rules.adult(a) == c[1]:
n = n + 1
probe = [c[0], 18]
if not c[1]:
probe = [c[0], 17]
s1 = rules.skip_meant(probe)
s2 = rules.skip_by_hand(probe)
if s1 != s2:
differ = differ + n
print(" " + pad(yn(c[0]), 10) + pad(yn(c[1]), 7) + pad(yn(s1), 15) + pad(yn(s2), 17) + lpad(str(n), 10))
print("The two agree where both clauses hold or both fail. Where exactly one fails, the hand-written form keeps the applicant: " + str(differ) + " of 300 here, the residents under 18 and the adults from elsewhere.")
def show_clause():
aps = rules.applicants()
kept = 0
yes = 0
m = 0
r = 0
b = 0
for a in aps:
if not rules.skip_meant(a):
kept = kept + 1
if rules.approved(a):
yes = yes + 1
d = rules.decided(a)
if d == "member":
m = m + 1
if d == "referral":
r = r + 1
if d == "both":
b = b + 1
print("Of the " + str(kept) + " resident adults, " + str(yes) + " are approved, as member or referred. Which clause carried each:")
print(" membership alone " + lpad(str(m), 4))
print(" referral alone " + lpad(str(r), 4))
print(" both " + lpad(str(b), 4))
print("The referral clause decided " + str(r) + " approvals: every referred applicant here is a member. The rule is right every time, and half of it has never been the reason - a fact about who applies, not about the code.")
def show_income():
aps = rules.applicants()
zero = 0
agree = 0
n = 0
for a in aps:
if not rules.skip_meant(a) and rules.approved(a):
n = n + 1
g = rules.income_guard_first(a)
f = rules.income_meaning_first(a)
if f == "divides by zero":
zero = zero + 1
elif f == g:
agree = agree + 1
print("The means test on the " + str(n) + " approved applicants: income on record / months under 2,000.00, and months on record above 0.")
print("Guard first, months checked before the division: an answer for all " + str(n) + ".")
print("Meaning first, the average before the months: the same answer on " + str(agree) + ", and a division by zero on the " + str(zero) + " with no months on record.")
print("Moving the guard behind the test it protects reads better and is no longer a guard.")
def ask_yes(prompt):
t = plain(input(prompt + " (y/n)> "))
if t == "":
return ""
if t == "y" or t == "yes":
return True
if t == "n" or t == "no":
return False
return "bad"
def own():
res = ask_yes("resident")
if res == "":
print("Cancelled.")
return
if res == "bad":
print("Type y or n.")
return
t = plain(input("age (0 to 120)> "))
if t == "":
print("Cancelled.")
return
if len(t) > 3 or not digits(t) or int(t) > 120:
print("Type a whole number from 0 to 120.")
return
age = int(t)
mem = ask_yes("member")
if mem == "":
print("Cancelled.")
return
if mem == "bad":
print("Type y or n.")
return
ref = ask_yes("referred")
if ref == "":
print("Cancelled.")
return
if ref == "bad":
print("Type y or n.")
return
t = plain(input("months of income on record (0 to 24)> "))
if t == "":
print("Cancelled.")
return
if len(t) > 2 or not digits(t) or int(t) > 24:
print("Type a whole number from 0 to 24.")
return
months = int(t)
total = 0
if months > 0:
t = plain(input("income over those months, whole units (0 to 999999)> "))
if t == "":
print("Cancelled.")
return
if len(t) > 6 or not digits(t):
print("Type a whole number from 0 to 999999.")
return
total = int(t) * 100
a = [res, age, mem, ref, months, total]
print("The skip rule: as meant it " + skipped(rules.skip_meant(a)) + " them; written by hand it " + skipped(rules.skip_by_hand(a)) + " them.")
if rules.approved(a):
print("Member or referred: approved, by " + ["membership alone", "referral alone", "both"][index(rules.decided(a))] + ".")
else:
print("Member or referred: not approved.")
g = rules.income_guard_first(a)
f = rules.income_meaning_first(a)
if months == 0:
print("The means test: guard first, no months on record, so no; meaning first, a division by zero before the guard is read.")
else:
print("The means test: " + money(int(total / months)) + " a month on average, " + verdict(g) + " either way.")
def skipped(b):
if b:
return "skips"
return "keeps"
def index(d):
if d == "member":
return 0
if d == "referral":
return 1
return 2
def verdict(g):
if g:
return "under 2,000.00, so it passes"
return "not under 2,000.00, so it fails"
print("== Eligibility rules ==")
print("Three rules for a benefit, each written two ways, and who the difference reaches.")
running = True
while running:
print("")
print("1) the applicants 2) the skip rule 3) which clause decides 4) the means test 5) an applicant of your own 6) quit")
choice = plain(input("choice> "))
if choice == "1":
show_applicants()
elif choice == "2":
show_skip()
elif choice == "3":
show_clause()
elif choice == "4":
show_income()
elif choice == "5":
own()
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
rules.eml
eml# P170 eligibility rules - 300 made-up applicants for a benefit, and three
# rules each written two ways. As in the corpus case
# the-negation-was-distributed-by-hand, "skip unless resident and adult"
# rewritten as "skip if not resident and not adult" skips only those who
# fail both. As in which-clause-actually-decided, "member or referred" is
# right every time, and in this population the referral clause never
# decides. And as in the-guard-was-moved-for-readability, an average
# income test reads better with the average first, and then divides by
# zero for anyone with no months on record.
def limit():
# average monthly income below which the benefit is paid, in cents
return 200000
def step(x):
return (x * 48271) % 2147483647
def applicants():
# [resident, age, member, referred, months on record, total income in cents]
[0] * 300 => out
424242 => x
0 => k
while k < 300:
step(x) => x
x % 100 < 85 => resident
step(x) => x
14 + x % 67 => age
step(x) => x
x % 100 < 40 => member
step(x) => x
member and x % 100 < 30 => referred
step(x) => x
x % 13 => months
step(x) => x
months * (50000 + 5000 * (x % 71)) => total
[resident, age, member, referred, months, total] => out[k]
k + 1 => k
return out
def adult(a):
return a[1] >= 18
def skip_meant(a):
# skip unless a resident adult
return not (a[0] and adult(a))
def skip_by_hand(a):
# the negation distributed by hand
return not a[0] and not adult(a)
def approved(a):
return a[2] or a[3]
def decided(a):
# which clause carried an approval: "member", "referral" or "both"
if a[2] and a[3]:
return "both"
if a[2]:
return "member"
if a[3]:
return "referral"
return "neither"
def income_guard_first(a):
# True, False: the applicant passes the means test, guard read first
if a[4] > 0:
if a[5] < limit() * a[4]:
return True
return False
def income_meaning_first(a):
# the average first: "divides by zero" when there are no months
if a[4] == 0:
return "divides by zero"
if a[5] / a[4] < limit():
if a[4] > 0:
return True
return False
Python projection (rules.py)
def limit():
return 200000
def step(x):
return x * 48271 % 2147483647
def applicants():
out = [0] * 300
x = 424242
k = 0
while k < 300:
x = step(x)
resident = x % 100 < 85
x = step(x)
age = 14 + x % 67
x = step(x)
member = x % 100 < 40
x = step(x)
referred = member and x % 100 < 30
x = step(x)
months = x % 13
x = step(x)
total = months * (50000 + 5000 * (x % 71))
out[k] = [resident, age, member, referred, months, total]
k = k + 1
return out
def adult(a):
return a[1] >= 18
def skip_meant(a):
return not (a[0] and adult(a))
def skip_by_hand(a):
return not a[0] and not adult(a)
def approved(a):
return a[2] or a[3]
def decided(a):
if a[2] and a[3]:
return "both"
if a[2]:
return "member"
if a[3]:
return "referral"
return "neither"
def income_guard_first(a):
if a[4] > 0:
if a[5] < limit() * a[4]:
return True
return False
def income_meaning_first(a):
if a[4] == 0:
return "divides by zero"
if a[5] / a[4] < limit():
if a[4] > 0:
return True
return False