Pascal's triangle
Rows built from the one above by addition, every entry exact however long, checked by symmetry, the sum 2^n, the hockey stick and the count of odd entries; and the float formula's first mistake, in row 55.
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
Each row built from the one above by addition, so every entry is exact however many digits it has.
main.eml- the menu, the triangle, one row with its checks, one entry beside the float formula, the float formula's first mistake, and the odd entriestriangle.eml- the rows, powers of two, the 1s of a number in binary, the checks, and the float formula
How each part works:
- As in the corpus cases
pascals-triangleandpascal-row-exact, a row is the row above with neighbours added: no division, so nothing to round. C(80, 40) has 24 digits, C(300, 150) has 89. - The checks on a row tie it to things worked out another way: - it is symmetric, C(n, k) = C(n, n - k); - its entries add up to 2^n exactly; - the hockey stick, C(k, k) + ... + C(n, k) = C(n + 1, k + 1), ties it to the next row; - it has 2^(the number of 1s in n in binary) odd entries.
- The multiplicative formula in floats, C(n, k) = C(n, k - 1) * (n - k + 1) / k, is exact up to row 54. In row 55 it gives C(55, 26) as 3560597348629859.5 instead of 3560597348629860. That entry is below 2^53, but the product before the division is not.
- The odd entries of rows 0 to 31 make Sierpinski's triangle.
What is checked: menu choices 1 to 6; a last row from 0 to 15; a row or an n from 0 to 300, and a k from 0 to n. An empty answer cancels.
Sessions: sessions/basic.in gives: - the triangle to row 8; - rows 10 and 80, with their checks; - C(55, 26), where the float formula is wrong, and C(300, 150); - the floats, and the odd entries.
sessions/bad-input.in gives: - menu choices 0 and x; - last rows: a cancel, 16, x and -1; - rows 301 and 1000; - entries: n 5 with k 6 and a cancel, and C(0, 0).
Built on the verified corpus cases pascals-triangle (each row grown from the one before) and pascal-row-exact (a row far past float precision, checked against global properties).
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== Pascal's triangle ==
Each row built from the one above by addition, every entry exact however long.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 0
Pick a number from 1 to 6.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> x
Pick a number from 1 to 6.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 1
last row (0 to 15)>
Cancelled.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 1
last row (0 to 15)> 16
Type a whole number from 0 to 15.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 1
last row (0 to 15)> x
Type a whole number from 0 to 15.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 1
last row (0 to 15)> -1
Type a whole number from 0 to 15.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 2
row (0 to 300)> 301
Type a whole number from 0 to 300.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 2
row (0 to 300)> 1000
Type a whole number from 0 to 300.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 3
n (0 to 300)> 5
k (0 to 5)> 6
Type a whole number from 0 to 5.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 3
n (0 to 300)> 5
k (0 to 5)>
Cancelled.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 3
n (0 to 300)> 0
k (0 to 0)> 0
C(0, 0) = 1
The float formula gets it exactly: 1.0
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 6
Bye.
What was typed (24 lines)
0
x
1
1
16
1
x
1
-1
2
301
2
1000
3
5
6
3
5
3
0
0
6
basic
interpreter: byte-equal== Pascal's triangle ==
Each row built from the one above by addition, every entry exact however long.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 1
last row (0 to 15)> 8
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 2
row (0 to 300)> 10
Row 10: 1 10 45 120 210 252 210 120 45 10 1
symmetric: C(n, k) = C(n, n - k) for every k
the entries add up to 2^10 exactly
hockey stick: C(k, k) + C(k + 1, k) + ... + C(10, k) = C(11, k + 1) for every k
4 odd entries; 10 in binary has 2 ones, and 2^2 = 4
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 2
row (0 to 300)> 80
Row 80 has 81 entries; it starts 1, 80, 3160.
The middle one, C(80, 40), has 24 digits:
107507208733336176461620
symmetric: C(n, k) = C(n, n - k) for every k
the entries add up to 2^80 exactly
hockey stick: C(k, k) + C(k + 1, k) + ... + C(80, k) = C(81, k + 1) for every k
4 odd entries; 80 in binary has 2 ones, and 2^2 = 4
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 3
n (0 to 300)> 55
k (0 to 55)> 26
C(55, 26) = 3560597348629860 (16 digits)
The float formula gives 3560597348629859.5.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 3
n (0 to 300)> 300
k (0 to 300)> 150
C(300, 150) = 93759702772827452793193754439064084879232655700081358920472352712975170021839591675861424 (89 digits)
The float formula gives 9.375970277282747e+88.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 4
The multiplicative formula in floats: C(n, k) = C(n, k - 1) * (n - k + 1) / k.
It is exact up to row 54, and first goes wrong in row 55:
C(55, 26) comes out 3560597348629859.5, and is 3560597348629860.
That entry is below 2^53 = 9007199254740992, but the product before the division,
92575531064376360, is not.
Building each row from the one above by addition needs no division, and is exact.
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 5
Rows 0 to 31, # for an odd entry:
#
# #
# . #
# # # #
# . . . #
# # . . # #
# . # . # . #
# # # # # # # #
# . . . . . . . #
# # . . . . . . # #
# . # . . . . . # . #
# # # # . . . . # # # #
# . . . # . . . # . . . #
# # . . # # . . # # . . # #
# . # . # . # . # . # . # . #
# # # # # # # # # # # # # # # #
# . . . . . . . . . . . . . . . #
# # . . . . . . . . . . . . . . # #
# . # . . . . . . . . . . . . . # . #
# # # # . . . . . . . . . . . . # # # #
# . . . # . . . . . . . . . . . # . . . #
# # . . # # . . . . . . . . . . # # . . # #
# . # . # . # . . . . . . . . . # . # . # . #
# # # # # # # # . . . . . . . . # # # # # # # #
# . . . . . . . # . . . . . . . # . . . . . . . #
# # . . . . . . # # . . . . . . # # . . . . . . # #
# . # . . . . . # . # . . . . . # . # . . . . . # . #
# # # # . . . . # # # # . . . . # # # # . . . . # # # #
# . . . # . . . # . . . # . . . # . . . # . . . # . . . #
# # . . # # . . # # . . # # . . # # . . # # . . # # . . # #
# . # . # . # . # . # . # . # . # . # . # . # . # . # . # . #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit
choice> 6
Bye.
What was typed (15 lines)
1
8
2
10
2
80
3
55
26
3
300
150
4
5
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# P124 Pascal's triangle: the triangle, one row exactly, one entry, the
# checks on a row, the float formula's first mistake, and the odd entries.
import triangle
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 lpad(s, n):
s => out
while len(out) < n:
" " + out => out
return out
def spaces(n):
"" => out
for i in [1:n]:
out + " " => out
return out
def ask_number(prompt, high):
plain(input(prompt)) => t
if t == "":
"Cancelled." ^0
return 0 - 1
if len(t) > 3:
("Type a whole number from 0 to " + str(high) + ".") ^0
return 0 - 1
for c in t:
if c < "0" or c > "9":
("Type a whole number from 0 to " + str(high) + ".") ^0
return 0 - 1
if int(t) > high:
("Type a whole number from 0 to " + str(high) + ".") ^0
return 0 - 1
return int(t)
def row_text(r):
"" => out
for x in r:
if out != "":
out + " " => out
out + str(x) => out
return out
def show_triangle():
ask_number("last row (0 to 15)> ", 15) => n
if n < 0:
return
triangle.rows(n) => rs
len(row_text(rs[n])) => w
for r in rs:
row_text(r) => t
(spaces(int((w - len(t)) / 2)) + t) ^0
def one_row():
ask_number("row (0 to 300)> ", 300) => n
if n < 0:
return
triangle.row(n) => r
if n <= 20:
("Row " + str(n) + ": " + row_text(r)) ^0
else:
int(n / 2) => m
("Row " + str(n) + " has " + str(n + 1) + " entries; it starts " + str(r[0]) + ", " + str(r[1]) + ", " + str(r[2]) + ".") ^0
("The middle one, C(" + str(n) + ", " + str(m) + "), has " + str(triangle.digits(r[m])) + " digits:") ^0
(" " + str(r[m])) ^0
report(n)
def report(n):
triangle.checks(n) => c
if c[0]:
" symmetric: C(n, k) = C(n, n - k) for every k" ^0
else:
" NOT symmetric" ^0
if c[1]:
(" the entries add up to 2^" + str(n) + " exactly") ^0
else:
(" the entries do NOT add up to 2^" + str(n)) ^0
if c[2]:
(" hockey stick: C(k, k) + C(k + 1, k) + ... + C(" + str(n) + ", k) = C(" + str(n + 1) + ", k + 1) for every k") ^0
else:
" the hockey stick does NOT hold" ^0
(" " + str(c[3]) + " odd entries; " + str(n) + " in binary has " + str(triangle.ones(n)) + " ones, and 2^" + str(triangle.ones(n)) + " = " + str(c[4])) ^0
def entry():
ask_number("n (0 to 300)> ", 300) => n
if n < 0:
return
ask_number("k (0 to " + str(n) + ")> ", n) => k
if k < 0:
return
triangle.row(n)[k] => x
if triangle.digits(x) == 1:
("C(" + str(n) + ", " + str(k) + ") = " + str(x)) ^0
else:
("C(" + str(n) + ", " + str(k) + ") = " + str(x) + " (" + str(triangle.digits(x)) + " digits)") ^0
triangle.float_entry(n, k) => f
if f == x:
(" The float formula gets it exactly: " + str(f)) ^0
else:
(" The float formula gives " + str(f) + ".") ^0
def floats():
triangle.first_float_miss(200) => m
"The multiplicative formula in floats: C(n, k) = C(n, k - 1) * (n - k + 1) / k." ^0
("It is exact up to row " + str(m[0] - 1) + ", and first goes wrong in row " + str(m[0]) + ":") ^0
(" C(" + str(m[0]) + ", " + str(m[1]) + ") comes out " + str(m[2]) + ", and is " + str(m[3]) + ".") ^0
(" That entry is below 2^53 = " + str(triangle.power_of_two(53)) + ", but the product before the division,") ^0
(" " + str(m[4]) + ", is not.") ^0
"Building each row from the one above by addition needs no division, and is exact." ^0
def odd_pattern():
"Rows 0 to 31, # for an odd entry:" ^0
triangle.rows(31) => rs
for r in rs:
"" => t
for x in r:
if t != "":
t + " " => t
if x % 2 == 1:
t + "#" => t
else:
t + "." => t
(spaces(31 - len(r) + 1) + t) ^0
"== Pascal's triangle ==" ^0
"Each row built from the one above by addition, every entry exact however long." ^0
True => running
while running:
"" ^0
"1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_triangle()
elif choice == "2":
one_row()
elif choice == "3":
entry()
elif choice == "4":
floats()
elif choice == "5":
odd_pattern()
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import triangle
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 lpad(s, n):
out = s
while len(out) < n:
out = " " + out
return out
def spaces(n):
out = ""
for i in range(1, n+1):
out = out + " "
return out
def ask_number(prompt, high):
t = plain(input(prompt))
if t == "":
print("Cancelled.")
return 0 - 1
if len(t) > 3:
print("Type a whole number from 0 to " + str(high) + ".")
return 0 - 1
for c in t:
if c < "0" or c > "9":
print("Type a whole number from 0 to " + str(high) + ".")
return 0 - 1
if int(t) > high:
print("Type a whole number from 0 to " + str(high) + ".")
return 0 - 1
return int(t)
def row_text(r):
out = ""
for x in r:
if out != "":
out = out + " "
out = out + str(x)
return out
def show_triangle():
n = ask_number("last row (0 to 15)> ", 15)
if n < 0:
return
rs = triangle.rows(n)
w = len(row_text(rs[n]))
for r in rs:
t = row_text(r)
print(spaces(int((w - len(t)) / 2)) + t)
def one_row():
n = ask_number("row (0 to 300)> ", 300)
if n < 0:
return
r = triangle.row(n)
if n <= 20:
print("Row " + str(n) + ": " + row_text(r))
else:
m = int(n / 2)
print("Row " + str(n) + " has " + str(n + 1) + " entries; it starts " + str(r[0]) + ", " + str(r[1]) + ", " + str(r[2]) + ".")
print("The middle one, C(" + str(n) + ", " + str(m) + "), has " + str(triangle.digits(r[m])) + " digits:")
print(" " + str(r[m]))
report(n)
def report(n):
c = triangle.checks(n)
if c[0]:
print(" symmetric: C(n, k) = C(n, n - k) for every k")
else:
print(" NOT symmetric")
if c[1]:
print(" the entries add up to 2^" + str(n) + " exactly")
else:
print(" the entries do NOT add up to 2^" + str(n))
if c[2]:
print(" hockey stick: C(k, k) + C(k + 1, k) + ... + C(" + str(n) + ", k) = C(" + str(n + 1) + ", k + 1) for every k")
else:
print(" the hockey stick does NOT hold")
print(" " + str(c[3]) + " odd entries; " + str(n) + " in binary has " + str(triangle.ones(n)) + " ones, and 2^" + str(triangle.ones(n)) + " = " + str(c[4]))
def entry():
n = ask_number("n (0 to 300)> ", 300)
if n < 0:
return
k = ask_number("k (0 to " + str(n) + ")> ", n)
if k < 0:
return
x = triangle.row(n)[k]
if triangle.digits(x) == 1:
print("C(" + str(n) + ", " + str(k) + ") = " + str(x))
else:
print("C(" + str(n) + ", " + str(k) + ") = " + str(x) + " (" + str(triangle.digits(x)) + " digits)")
f = triangle.float_entry(n, k)
if f == x:
print(" The float formula gets it exactly: " + str(f))
else:
print(" The float formula gives " + str(f) + ".")
def floats():
m = triangle.first_float_miss(200)
print("The multiplicative formula in floats: C(n, k) = C(n, k - 1) * (n - k + 1) / k.")
print("It is exact up to row " + str(m[0] - 1) + ", and first goes wrong in row " + str(m[0]) + ":")
print(" C(" + str(m[0]) + ", " + str(m[1]) + ") comes out " + str(m[2]) + ", and is " + str(m[3]) + ".")
print(" That entry is below 2^53 = " + str(triangle.power_of_two(53)) + ", but the product before the division,")
print(" " + str(m[4]) + ", is not.")
print("Building each row from the one above by addition needs no division, and is exact.")
def odd_pattern():
print("Rows 0 to 31, # for an odd entry:")
rs = triangle.rows(31)
for r in rs:
t = ""
for x in r:
if t != "":
t = t + " "
if x % 2 == 1:
t = t + "#"
else:
t = t + "."
print(spaces(31 - len(r) + 1) + t)
print("== Pascal's triangle ==")
print("Each row built from the one above by addition, every entry exact however long.")
running = True
while running:
print("")
print("1) triangle 2) one row 3) one entry 4) floats 5) odd entries 6) quit")
choice = plain(input("choice> "))
if choice == "1":
show_triangle()
elif choice == "2":
one_row()
elif choice == "3":
entry()
elif choice == "4":
floats()
elif choice == "5":
odd_pattern()
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
triangle.eml
eml# P124 Pascal's triangle - every row built from the one above by addition,
# so every entry is exact however many digits it has (the corpus cases
# pascals-triangle and pascal-row-exact). The checks tie a row to things
# computed another way: its mirror, its sum 2^n, the hockey stick to the
# next row, and the count of its odd entries. The multiplicative formula
# in floats is the comparison that goes wrong.
def next_row(row):
[1] => out
for k in [1:len(row) - 1]:
out + [row[k - 1] + row[k]] => out
return out + [1]
def row(n):
[1] => r
for i in [1:n]:
next_row(r) => r
return r
def rows(n):
# rows 0 to n
[[1]] => out
[1] => r
for i in [1:n]:
next_row(r) => r
out + [r] => out
return out
def power_of_two(n):
1 => p
for i in [1:n]:
p + p => p
return p
def ones(n):
# the 1 bits of n
0 => c
n => m
while m > 0:
if m % 2 == 1:
c + 1 => c
m - 1 => m
int(m / 2) => m
return c
def checks(n):
# [symmetric, sum is 2^n, hockey stick holds for every k, odd entries,
# 2^(1 bits of n)]
rows(n + 1) => rs
rs[n] => r
True => sym
0 => total
0 => odd
for k in [0:n]:
if r[k] != r[n - k]:
False => sym
total + r[k] => total
if r[k] % 2 == 1:
odd + 1 => odd
True => stick
for k in [0:n]:
0 => s
for i in [k:n]:
s + rs[i][k] => s
if s != rs[n + 1][k + 1]:
False => stick
return [sym, total == power_of_two(n), stick, odd, power_of_two(ones(n))]
def float_entry(n, k):
# C(n, k) by the multiplicative formula, in floats
1.0 => c
for i in [1:k]:
c * (n - i + 1) / i => c
return c
def first_float_miss(limit):
# [n, k, the float, the exact entry, the product before the division]
# for the first entry the float formula gets wrong, rows 0 to limit
[1] => r
for n in [0:limit]:
if n > 0:
next_row(r) => r
1.0 => c
for k in [0:n]:
if k > 0:
c * (n - k + 1) / k => c
if c != r[k]:
return [n, k, c, r[k], r[k - 1] * (n - k + 1)]
return []
def digits(x):
return len(str(x))
Python projection (triangle.py)
def next_row(row):
out = [1]
for k in range(1, len(row)):
out = out + [row[k - 1] + row[k]]
return out + [1]
def row(n):
r = [1]
for i in range(1, n+1):
r = next_row(r)
return r
def rows(n):
out = [[1]]
r = [1]
for i in range(1, n+1):
r = next_row(r)
out = out + [r]
return out
def power_of_two(n):
p = 1
for i in range(1, n+1):
p = p + p
return p
def ones(n):
c = 0
m = n
while m > 0:
if m % 2 == 1:
c = c + 1
m = m - 1
m = int(m / 2)
return c
def checks(n):
rs = rows(n + 1)
r = rs[n]
sym = True
total = 0
odd = 0
for k in range(0, n+1):
if r[k] != r[n - k]:
sym = False
total = total + r[k]
if r[k] % 2 == 1:
odd = odd + 1
stick = True
for k in range(0, n+1):
s = 0
for i in range(k, n+1):
s = s + rs[i][k]
if s != rs[n + 1][k + 1]:
stick = False
return [sym, total == power_of_two(n), stick, odd, power_of_two(ones(n))]
def float_entry(n, k):
c = 1.0
for i in range(1, k+1):
c = c * (n - i + 1) / i
return c
def first_float_miss(limit):
r = [1]
for n in range(0, limit+1):
if n > 0:
r = next_row(r)
c = 1.0
for k in range(0, n+1):
if k > 0:
c = c * (n - k + 1) / k
if c != r[k]:
return [n, k, c, r[k], r[k - 1] * (n - k + 1)]
return []
def digits(x):
return len(str(x))