Probability lab
The birthday problem, the Monty Hall game with a host who knows and a host who guesses, and the sum of two dice - each worked out exactly as a fraction and then tried thousands of times on EML's own random numbers, side by side.
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
Three classic experiments, each worked out exactly and then tried many times with random numbers, side by side: the birthday problem, the Monty Hall game with two kinds of host, and the sum of two dice.
main.eml- the menu, the experiments' questions and resultslab.eml- the exact answers and the simulationsfrac.eml- exact fractions, from P007 by way of P038, P044 and P056rng.eml- random numbers written in EML, the generator of P008
How each part works:
- Birthdays: the chance that n people all have different birthdays is 365/365 x 364/365 x ... x (366 - n)/365. The chance that two share one is 1 minus that. It is kept as one exact fraction, not reduced: its numbers reach 60 digits, and only showing the percentage needs a division. The smallest group where a shared birthday is more likely than not, 23, is found by comparing 2 x shared with all, without dividing.
- Monty Hall: the car is behind one of three doors, and you pick one. - The usual host knows where the car is and opens another door with a goat. Staying wins 1/3 of the time, switching 2/3. - A host who opens one of the other two doors at random sometimes shows the car. Counting only the games where he showed a goat, staying and switching each win 1/2. The doors and the goat you see are the same; what the host knew is what makes switching pay.
- Two dice: of the 36 equal throws, 6 make 7, and 1 each make 2 and 12. The throws are tallied by their sum in a dict, as in the corpus case
dice-roll-tally: read the count, write it back plus one. - The random numbers come from EML code, so a seed gives the same experiment on every machine. One generator runs through the session, so the next experiment continues where the last one stopped.
What is checked: menu choices 1 to 5; 2 to 100 people; 100 to 5000 groups, games or throws; a seed from 0 to 999999999. An empty answer cancels.
Sessions: sessions/basic.in runs from the default seed 2026. - Birthdays for 23 people: exactly 50.73%, and 1,030 of 2,000 random groups. For 50 people: 97.04%, and 478 of 500. - Monty Hall, 3,000 games. With the host who knows, switching won 66.23%. With the random host, 2,068 games showed a goat, and switching won 52.76% of those. That is 2.5 standard errors above the exact 1/2, which a run of 2,068 games does about once in 80. Longer runs come closer. - Two dice, 3,600 throws, each sum next to its exact share out of 36.
sessions/bad-input.in gives: - menu choices 0 and x; - groups of 1, 101 and x people, and an empty answer; - 99 and 5001 groups, and an empty answer; - cancelled Monty Hall and dice runs; - seeds -1 and abc, and an empty seed; - seed 7, then 5 people in 100 groups: exactly 2.71%, and 4 of 100 tried.
Built on the verified corpus case dice-roll-tally (dice rolls tallied by face in a dict, and the most frequent face found by hand).
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== Probability lab ==
Each experiment is worked out exactly, then tried many times with random numbers.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 0
Pick a number from 1 to 5.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> x
Pick a number from 1 to 5.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 1
people in the group (2 to 100)> 1
Type a whole number from 2 to 100.
people in the group (2 to 100)> 101
Type a whole number from 2 to 100.
people in the group (2 to 100)> x
Type a whole number from 2 to 100.
people in the group (2 to 100)>
Cancelled.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 1
people in the group (2 to 100)> 10
groups to try (100 to 5000)> 99
Type a whole number from 100 to 5000.
groups to try (100 to 5000)> 5001
Type a whole number from 100 to 5000.
groups to try (100 to 5000)>
Cancelled.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 2
games (100 to 5000)>
Cancelled.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 3
throws (100 to 5000)>
Cancelled.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 4
seed (0 to 999999999)> -1
Type a whole number from 0 to 999999999.
seed (0 to 999999999)> abc
Type a whole number from 0 to 999999999.
seed (0 to 999999999)>
Cancelled.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 4
seed (0 to 999999999)> 7
The experiments now draw from seed 7.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 1
people in the group (2 to 100)> 5
groups to try (100 to 5000)> 100
Exactly: 2.71% of groups of 5 have two people sharing a birthday.
Tried: 4 of 100 random groups did, 4.00%.
From 23 people on, a shared birthday is more likely than not.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 5
Bye.
What was typed (26 lines)
0
x
1
1
101
x
1
10
99
5001
2
3
4
-1
abc
4
7
1
5
100
5
basic
interpreter: byte-equal== Probability lab ==
Each experiment is worked out exactly, then tried many times with random numbers.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 1
people in the group (2 to 100)> 23
groups to try (100 to 5000)> 2000
Exactly: 50.73% of groups of 23 have two people sharing a birthday.
Tried: 1030 of 2000 random groups did, 51.50%.
From 23 people on, a shared birthday is more likely than not.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 1
people in the group (2 to 100)> 50
groups to try (100 to 5000)> 500
Exactly: 97.04% of groups of 50 have two people sharing a birthday.
Tried: 478 of 500 random groups did, 95.60%.
From 23 people on, a shared birthday is more likely than not.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 2
games (100 to 5000)> 3000
A host who knows where the car is always opens a door with a goat:
staying won 1013 of 3000 (33.77%), switching 1987 (66.23%); exactly 1/3 and 2/3.
A host who opens one of the other doors at random, counting only the games where that door had a goat:
2068 of 3000 games counted; staying won 977 (47.24%), switching 1091 (52.76%); exactly 1/2 each.
Same doors, same goat shown - what the host knew is what makes switching pay.
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 3
throws (100 to 5000)> 3600
sum exactly thrown
2 1/36 2.78% 90 2.50% #
3 2/36 5.56% 194 5.39% ###
4 3/36 8.33% 307 8.53% ####
5 4/36 11.11% 402 11.17% ######
6 5/36 13.89% 521 14.47% #######
7 6/36 16.67% 586 16.28% ########
8 5/36 13.89% 500 13.89% #######
9 4/36 11.11% 387 10.75% #####
10 3/36 8.33% 313 8.69% ####
11 2/36 5.56% 200 5.56% ###
12 1/36 2.78% 100 2.78% #
1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit
choice> 5
Bye.
What was typed (11 lines)
1
23
2000
1
50
500
2
3000
3
3600
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# P060 probability lab: three classic experiments, each worked out exactly
# and then run many times on EML's own random numbers - the birthday
# problem, the Monty Hall game under two kinds of host, and the sum of two
# dice.
import frac
import lab
import rng
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 ask_number(prompt, low, high):
while True:
trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> ")) => answer
if answer == "":
return -1
True => ok
if len(answer) > 9:
False => ok
for c in answer:
if not (c >= "0" and c <= "9"):
False => ok
if ok and int(answer) >= low and int(answer) <= high:
return int(answer)
("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0
def share(count, trials):
return lab.percent(frac.make(count, trials))
def birthday(state):
ask_number("people in the group", 2, 100) => n
if n == -1:
"Cancelled." ^0
return
ask_number("groups to try", 100, 5000) => trials
if trials == -1:
"Cancelled." ^0
return
lab.birthday_exact(n) => p
lab.birthday_trials(n, trials, state[0]) => hits
("Exactly: " + lab.percent(p) + " of groups of " + str(n) + " have two people sharing a birthday.") ^0
("Tried: " + str(hits) + " of " + str(trials) + " random groups did, " + share(hits, trials) + ".") ^0
("From " + str(lab.birthday_threshold()) + " people on, a shared birthday is more likely than not.") ^0
def monty(state):
ask_number("games", 100, 5000) => trials
if trials == -1:
"Cancelled." ^0
return
lab.monty_trials(trials, state[0], False) => a
"A host who knows where the car is always opens a door with a goat:" ^0
(" staying won " + str(a[1]) + " of " + str(a[0]) + " (" + share(a[1], a[0]) + "), switching " + str(a[2]) + " (" + share(a[2], a[0]) + "); exactly 1/3 and 2/3.") ^0
lab.monty_trials(trials, state[0], True) => b
"A host who opens one of the other doors at random, counting only the games where that door had a goat:" ^0
(" " + str(b[0]) + " of " + str(trials) + " games counted; staying won " + str(b[1]) + " (" + share(b[1], b[0]) + "), switching " + str(b[2]) + " (" + share(b[2], b[0]) + "); exactly 1/2 each.") ^0
"Same doors, same goat shown - what the host knew is what makes switching pay." ^0
def dice(state):
ask_number("throws", 100, 5000) => trials
if trials == -1:
"Cancelled." ^0
return
lab.dice_exact() => ways
lab.dice_trials(trials, state[0]) => tally
"sum exactly thrown" ^0
for s in [2:12]:
str(s) => a
if len(a) == 1:
" " + a => a
lab.percent(frac.make(ways[s], 36)) => e
while len(e) < 7:
" " + e => e
str(tally[s]) => c
while len(c) < 5:
" " + c => c
share(tally[s], trials) => f
while len(f) < 7:
" " + f => f
int((tally[s] * 100 + trials) / (2 * trials)) => marks
(" " + a + " " + str(ways[s]) + "/36 " + e + " " + c + " " + f + " " + "#" * marks) ^0
def set_seed(state):
ask_number("seed", 0, 999999999) => seed
if seed == -1:
"Cancelled." ^0
return
rng.Rng(seed) => state[0]
("The experiments now draw from seed " + str(seed) + ".") ^0
"== Probability lab ==" ^0
"Each experiment is worked out exactly, then tried many times with random numbers." ^0
# [the generator]
[rng.Rng(2026)] => state
True => running
while running:
"" ^0
"1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit" ^0
trim(input("choice> ")) => choice
if choice == "1":
birthday(state)
elif choice == "2":
monty(state)
elif choice == "3":
dice(state)
elif choice == "4":
set_seed(state)
elif choice == "5":
False => running
else:
"Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import frac
import lab
import rng
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 ask_number(prompt, low, high):
while True:
answer = trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> "))
if answer == "":
return -1
ok = True
if len(answer) > 9:
ok = False
for c in answer:
if not (c >= "0" and c <= "9"):
ok = False
if ok and int(answer) >= low and int(answer) <= high:
return int(answer)
print("Type a whole number from " + str(low) + " to " + str(high) + ".")
def share(count, trials):
return lab.percent(frac.make(count, trials))
def birthday(state):
n = ask_number("people in the group", 2, 100)
if n == -1:
print("Cancelled.")
return
trials = ask_number("groups to try", 100, 5000)
if trials == -1:
print("Cancelled.")
return
p = lab.birthday_exact(n)
hits = lab.birthday_trials(n, trials, state[0])
print("Exactly: " + lab.percent(p) + " of groups of " + str(n) + " have two people sharing a birthday.")
print("Tried: " + str(hits) + " of " + str(trials) + " random groups did, " + share(hits, trials) + ".")
print("From " + str(lab.birthday_threshold()) + " people on, a shared birthday is more likely than not.")
def monty(state):
trials = ask_number("games", 100, 5000)
if trials == -1:
print("Cancelled.")
return
a = lab.monty_trials(trials, state[0], False)
print("A host who knows where the car is always opens a door with a goat:")
print(" staying won " + str(a[1]) + " of " + str(a[0]) + " (" + share(a[1], a[0]) + "), switching " + str(a[2]) + " (" + share(a[2], a[0]) + "); exactly 1/3 and 2/3.")
b = lab.monty_trials(trials, state[0], True)
print("A host who opens one of the other doors at random, counting only the games where that door had a goat:")
print(" " + str(b[0]) + " of " + str(trials) + " games counted; staying won " + str(b[1]) + " (" + share(b[1], b[0]) + "), switching " + str(b[2]) + " (" + share(b[2], b[0]) + "); exactly 1/2 each.")
print("Same doors, same goat shown - what the host knew is what makes switching pay.")
def dice(state):
trials = ask_number("throws", 100, 5000)
if trials == -1:
print("Cancelled.")
return
ways = lab.dice_exact()
tally = lab.dice_trials(trials, state[0])
print("sum exactly thrown")
for s in range(2, 13):
a = str(s)
if len(a) == 1:
a = " " + a
e = lab.percent(frac.make(ways[s], 36))
while len(e) < 7:
e = " " + e
c = str(tally[s])
while len(c) < 5:
c = " " + c
f = share(tally[s], trials)
while len(f) < 7:
f = " " + f
marks = int((tally[s] * 100 + trials) / (2 * trials))
print(" " + a + " " + str(ways[s]) + "/36 " + e + " " + c + " " + f + " " + "#" * marks)
def set_seed(state):
seed = ask_number("seed", 0, 999999999)
if seed == -1:
print("Cancelled.")
return
state[0] = rng.Rng(seed)
print("The experiments now draw from seed " + str(seed) + ".")
print("== Probability lab ==")
print("Each experiment is worked out exactly, then tried many times with random numbers.")
state = [rng.Rng(2026)]
running = True
while running:
print("")
print("1) birthdays 2) Monty Hall 3) two dice 4) seed 5) quit")
choice = trim(input("choice> "))
if choice == "1":
birthday(state)
elif choice == "2":
monty(state)
elif choice == "3":
dice(state)
elif choice == "4":
set_seed(state)
elif choice == "5":
running = False
else:
print("Pick a number from 1 to 5.")
print("Bye.")
lab.eml
eml# P060 probability lab - each experiment worked out exactly, as a fraction,
# and run many times on EML's own random numbers.
import frac
def percent(x):
# A fraction [numerator, denominator] from 0 to 1 as a percentage, two
# decimal places. The fraction need not be in lowest terms.
frac.decimal([x[0] * 100, x[1]], 2) => s
if len(s) > 6 and s[0:6] == "about ":
s[6:len(s)] => s
# always two places, so the columns line up: 2.5 -> 2.50, 50 -> 50.00
0 => k
while k < len(s) and s[k] != ".":
k + 1 => k
if k == len(s):
s + ".00" => s
elif len(s) - k == 2:
s + "0" => s
return s + "%"
def birthday_exact(n):
# The chance that among n people (365 equally likely birthdays, no 29
# February) at least two share one: 1 minus the chance that all differ,
# 365/365 x 364/365 x ... x (366 - n)/365. Kept as one exact fraction,
# not reduced: the numbers reach 60 digits, and reducing them takes long
# division, which only showing the result needs, once.
1 => differ
1 => all
for k in [0:n - 1]:
differ * (365 - k) => differ
all * 365 => all
return [all - differ, all]
def birthday_threshold():
# The smallest group where a shared birthday is more likely than not:
# shared / all >= 1/2, compared as 2 x shared >= all - no division.
1 => n
birthday_exact(n) => p
while 2 * p[0] < p[1]:
n + 1 => n
birthday_exact(n) => p
return n
def birthday_trials(n, trials, g):
# How many of `trials` random groups of n have a shared birthday.
0 => shared
for t in [1:trials]:
[False] * 365 => seen
False => hit
for k in [1:n]:
g.below(365) => day
if seen[day]:
True => hit
True => seen[day]
if hit:
shared + 1 => shared
return shared
def monty_trials(trials, g, host_random):
# [games counted, wins by staying, wins by switching]. The car is behind
# one of three doors and you pick one. The usual host knows where the car
# is and opens another door with a goat (choosing at random when both
# are goats). A host who opens one of the other two doors at random
# sometimes shows the car; those games are not counted, so the rest are
# the games where he happened to show a goat.
0 => counted
0 => stay
0 => switch
for t in [1:trials]:
g.below(3) => car
g.below(3) => pick
# one more draw, used by either host to choose between two doors
g.below(2) => r
# the two doors you did not pick, in order
[] => others
for d in [0:2]:
if d != pick:
others + [d] => others
if host_random:
others[r] => opened
elif others[0] == car:
others[1] => opened
elif others[1] == car:
others[0] => opened
else:
others[r] => opened
if opened != car:
counted + 1 => counted
if pick == car:
stay + 1 => stay
else:
switch + 1 => switch
return [counted, stay, switch]
def dice_exact():
# How many of the 36 equally likely throws of two dice give each sum,
# sums 2 to 12 at indexes 2 to 12.
[0] * 13 => ways
for a in [1:6]:
for b in [1:6]:
ways[a + b] + 1 => ways[a + b]
return ways
def dice_trials(trials, g):
# Throws of two dice tallied by their sum in a dict, as the corpus case
# dice-roll-tally tallies faces: read the count, write it back plus one.
{} => tally
for s in [2:12]:
0 => tally[s]
for t in [1:trials]:
g.below(6) + 1 + g.below(6) + 1 => s
tally[s] + 1 => tally[s]
return tally
Python projection (lab.py)
import frac
def percent(x):
s = frac.decimal([x[0] * 100, x[1]], 2)
if len(s) > 6 and s[0:6] == "about ":
s = s[6:len(s)]
k = 0
while k < len(s) and s[k] != ".":
k = k + 1
if k == len(s):
s = s + ".00"
elif len(s) - k == 2:
s = s + "0"
return s + "%"
def birthday_exact(n):
differ = 1
all = 1
for k in range(0, n):
differ = differ * (365 - k)
all = all * 365
return [all - differ, all]
def birthday_threshold():
n = 1
p = birthday_exact(n)
while 2 * p[0] < p[1]:
n = n + 1
p = birthday_exact(n)
return n
def birthday_trials(n, trials, g):
shared = 0
for t in range(1, trials+1):
seen = [False] * 365
hit = False
for k in range(1, n+1):
day = g.below(365)
if seen[day]:
hit = True
seen[day] = True
if hit:
shared = shared + 1
return shared
def monty_trials(trials, g, host_random):
counted = 0
stay = 0
switch = 0
for t in range(1, trials+1):
car = g.below(3)
pick = g.below(3)
r = g.below(2)
others = []
for d in range(0, 3):
if d != pick:
others = others + [d]
if host_random:
opened = others[r]
elif others[0] == car:
opened = others[1]
elif others[1] == car:
opened = others[0]
else:
opened = others[r]
if opened != car:
counted = counted + 1
if pick == car:
stay = stay + 1
else:
switch = switch + 1
return [counted, stay, switch]
def dice_exact():
ways = [0] * 13
for a in range(1, 7):
for b in range(1, 7):
ways[a + b] = ways[a + b] + 1
return ways
def dice_trials(trials, g):
tally = {}
for s in range(2, 13):
tally[s] = 0
for t in range(1, trials+1):
s = g.below(6) + 1 + g.below(6) + 1
tally[s] = tally[s] + 1
return tally
frac.eml
eml# P060 probability lab - exact fractions, from P007 (calculator) by way of
# P038, P044 and P056.
# A number is a list [n, d]: n / d in lowest terms with d > 0. Integers have
# no size limit, but EML has no //, and a / b goes through a float that cannot
# hold a large quotient exactly, so whole-number division is written out.
def quotient(a, b):
# a // b for whole numbers a >= 0 and b > 0, exact at any size. Long
# division by doubling: take away the largest b * 2^k that still fits.
0 => q
while a >= b:
b => m
1 => k
while m + m <= a:
m + m => m
k + k => k
a - m => a
q + k => q
return q
def gcd(a, b):
while b != 0:
a % b => r
b => a
r => b
return a
def make(n, d):
# n / d in lowest terms with a positive denominator (d != 0).
if d < 0:
0 - n => n
0 - d => d
abs(n) => a
gcd(a, d) => g
if n < 0:
return [0 - quotient(a, g), quotient(d, g)]
return [quotient(a, g), quotient(d, g)]
def plus(x, y):
return make(x[0] * y[1] + y[0] * x[1], x[1] * y[1])
def minus(x, y):
return make(x[0] * y[1] - y[0] * x[1], x[1] * y[1])
def times(x, y):
return make(x[0] * y[0], x[1] * y[1])
def over(x, y):
# x / y; the caller has checked that y is not zero.
return make(x[0] * y[1], x[1] * y[0])
def is_zero(x):
return x[0] == 0
def digits_value(s):
# The value of a string of 1 to 9 digits, otherwise -1.
if s == "" or len(s) > 9:
return -1
0 => n
for c in s:
if not (c in "0123456789"):
return -1
n * 10 + int(c) => n
return n
def parse(s):
# "3", "-2", "3/4", "-0.25" as a fraction; [] if it is not a number.
1 => sign
if len(s) > 0 and s[0] == "-":
-1 => sign
s[1:len(s)] => s
0 => k
while k < len(s) and s[k] != "/" and s[k] != ".":
k + 1 => k
if k == len(s):
digits_value(s) => n
if n < 0:
return []
return make(sign * n, 1)
digits_value(s[0:k]) => whole
s[k + 1:len(s)] => rest
digits_value(rest) => part
if whole < 0 or part < 0:
return []
if s[k] == "/":
if part == 0:
return []
return make(sign * whole, part)
# a decimal point: 0.25 is 25 / 100
1 => scale
for c in rest:
scale * 10 => scale
return make(sign * (whole * scale + part), scale)
def less(x, y):
return x[0] * y[1] < y[0] * x[1]
def decimal(x, places):
# x to the given number of decimal places, halves rounded away from zero,
# with "about " in front when the value does not end there exactly.
"" => sign
abs(x[0]) => n
if x[0] < 0:
"-" => sign
1 => scale
for k in [1:places]:
scale * 10 => scale
quotient(2 * n * scale + x[1], 2 * x[1]) => t
"" => about
if (n * scale) % x[1] != 0:
"about " => about
str(quotient(t, scale)) => whole
str(t % scale) => part
while len(part) < places:
"0" + part => part
# drop trailing zeros of an exact value
if about == "":
while len(part) > 0 and part[len(part) - 1] == "0":
part[0:len(part) - 1] => part
if t == 0:
"" => sign
if part == "":
return about + sign + whole
return about + sign + whole + "." + part
def text(x):
# "3", "-3", "3/4", "-3/4".
if x[1] == 1:
return str(x[0])
return str(x[0]) + "/" + str(x[1])
Python projection (frac.py)
def quotient(a, b):
q = 0
while a >= b:
m = b
k = 1
while m + m <= a:
m = m + m
k = k + k
a = a - m
q = q + k
return q
def gcd(a, b):
while b != 0:
r = a % b
a = b
b = r
return a
def make(n, d):
if d < 0:
n = 0 - n
d = 0 - d
a = abs(n)
g = gcd(a, d)
if n < 0:
return [0 - quotient(a, g), quotient(d, g)]
return [quotient(a, g), quotient(d, g)]
def plus(x, y):
return make(x[0] * y[1] + y[0] * x[1], x[1] * y[1])
def minus(x, y):
return make(x[0] * y[1] - y[0] * x[1], x[1] * y[1])
def times(x, y):
return make(x[0] * y[0], x[1] * y[1])
def over(x, y):
return make(x[0] * y[1], x[1] * y[0])
def is_zero(x):
return x[0] == 0
def digits_value(s):
if s == "" or len(s) > 9:
return -1
n = 0
for c in s:
if not c in "0123456789":
return -1
n = n * 10 + int(c)
return n
def parse(s):
sign = 1
if len(s) > 0 and s[0] == "-":
sign = -1
s = s[1:len(s)]
k = 0
while k < len(s) and s[k] != "/" and s[k] != ".":
k = k + 1
if k == len(s):
n = digits_value(s)
if n < 0:
return []
return make(sign * n, 1)
whole = digits_value(s[0:k])
rest = s[k + 1:len(s)]
part = digits_value(rest)
if whole < 0 or part < 0:
return []
if s[k] == "/":
if part == 0:
return []
return make(sign * whole, part)
scale = 1
for c in rest:
scale = scale * 10
return make(sign * (whole * scale + part), scale)
def less(x, y):
return x[0] * y[1] < y[0] * x[1]
def decimal(x, places):
sign = ""
n = abs(x[0])
if x[0] < 0:
sign = "-"
scale = 1
for k in range(1, places+1):
scale = scale * 10
t = quotient(2 * n * scale + x[1], 2 * x[1])
about = ""
if n * scale % x[1] != 0:
about = "about "
whole = str(quotient(t, scale))
part = str(t % scale)
while len(part) < places:
part = "0" + part
if about == "":
while len(part) > 0 and part[len(part) - 1] == "0":
part = part[0:len(part) - 1]
if t == 0:
sign = ""
if part == "":
return about + sign + whole
return about + sign + whole + "." + part
def text(x):
if x[1] == 1:
return str(x[0])
return str(x[0]) + "/" + str(x[1])
rng.eml
eml# P060 probability lab - random numbers written in EML: the linear congruential
# generator of P008 (number guessing), with the constants of the C
# standard's example rand(). Python's random module is not used, so a seed
# gives the same experiment on every machine, and the interpreter can check a
# whole session byte for byte.
class Rng:
def __init__(self, seed):
seed % 2147483648 => self.state
def step(self):
(1103515245 * self.state + 12345) % 2147483648 => self.state
return self.state
def below(self, n):
# A number from 0 to n - 1, taken from the high bits of the state: the
# low bits of this generator repeat with short periods. Dividing by
# 65536 is exact in a float for a state below 2^31.
return int(self.step() / 65536) % n
Python projection (rng.py)
class Rng:
def __init__(self, seed):
self.state = seed % 2147483648
def step(self):
self.state = (1103515245 * self.state + 12345) % 2147483648
return self.state
def below(self, n):
return int(self.step() / 65536) % n