Sequence explorer
Collatz paths from starts up to a trillion, worked in exact whole numbers, and the start below a limit with the longest path; exact Fibonacci numbers with Cassini's identity checked and the ratio closing in on the golden ratio; and Pascal's triangle, as numbers and as the pattern its odd entries make.
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 famous sequences, in whole numbers of any size. The Collatz path of a start up to 10^12, and the start below a limit whose path is longest. Fibonacci numbers up to F(2000), each checked with Cassini's identity. And Pascal's triangle, either as numbers or as the pattern its odd numbers make.
main.eml- the menu, reading numbers, and laying out the outputsequences.eml- exact halving, Collatz paths and step counts, Fibonacci numbers, Cassini's identity and Pascal's rows
How each part works:
- Collatz: as in the corpus case
collatz-sequence, halve an even number and take 3n + 1 for an odd one until 1 is reached. The corpus case halves with int(n / 2), which goes through a float and is exact only below 2^53. A path from a start below 10^12 can climb past that: 1,410,123,943 climbs to 7,125,885,122,794,452,160 on its 770 steps. So the halving here is exact long division. - Longest path: the step counts for every start up to the limit, each using the ones before it. A path is followed only until it drops below its start, and the count already known for that number is added. The first start with the most steps is reported: 6171, with 261, up to 10,000.
- Fibonacci: each number the sum of the two before, in whole numbers, so F(1000) has all 209 of its digits. Cassini's identity F(n-1) x F(n+1) - F(n)^2 = (-1)^n is checked every time. As the corpus case
fibonacci-cassini-exactshows, those two products are huge and differ by exactly 1, a check that floats cannot pass. F(n+1) / F(n) is shown to 12 places by long division, closing in on the golden ratio. - Pascal: each number the sum of the two above it, built row by row as in the corpus case
pascals-triangle, and centred. Marking only the odd ones, with 16 or 32 rows, draws Sierpinski's triangle.
What is checked: menu choices 1 to 6; a Collatz start from 1 to 1,000,000,000,000; a limit from 2 to 100,000 for the longest path; n from 1 to 2000 for Fibonacci; 1 to 15 rows of numbers, or 1 to 32 rows of the odd pattern. An empty answer cancels. A path of more than 40 numbers shows its first 20 and last 10; a Fibonacci number over 60 digits shows its first and last 25.
Sessions: sessions/basic.in gives: - the paths of 27 (111 steps, up to 9232) and 1,410,123,943; - the longest path up to 10,000, and the step counts of the starts 1 to 20; - F(10) and F(1000), with Cassini's identity and the ratio to the next; - 7 rows of Pascal's triangle, and 16 rows of its odd numbers.
sessions/bad-input.in gives: - menu choices 0 and x; - Collatz starts 0 and abc, then a cancel; - longest-path limits 1 and 100001, then a cancel; - n = 2001 for Fibonacci, 16 rows of numbers and 33 rows of the pattern, each then cancelled.
Built on the verified corpus cases collatz-sequence (the halve-or-3n+1 step), fibonacci-cassini-exact (Fibonacci numbers checked with an identity floats cannot fake) and pascals-triangle (each row from the one before).
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== Sequence explorer ==
Collatz paths, exact Fibonacci numbers and Pascal's triangle.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 0
Pick a number from 1 to 6.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> x
Pick a number from 1 to 6.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 1
start (1 to 1000000000000)> 0
Type a whole number from 1 to 1000000000000.
start (1 to 1000000000000)> abc
Type a whole number from 1 to 1000000000000.
start (1 to 1000000000000)>
Cancelled.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 2
up to (2 to 100000)> 1
Type a whole number from 2 to 100000.
up to (2 to 100000)> 100001
Type a whole number from 2 to 100000.
up to (2 to 100000)>
Cancelled.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 3
n (1 to 2000)> 2001
Type a whole number from 1 to 2000.
n (1 to 2000)>
Cancelled.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 4
rows (1 to 15)> 16
Type a whole number from 1 to 15.
rows (1 to 15)>
Cancelled.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 5
rows (1 to 32)> 33
Type a whole number from 1 to 32.
rows (1 to 32)>
Cancelled.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 6
Bye.
What was typed (20 lines)
0
x
1
0
abc
2
1
100001
3
2001
4
16
5
33
6
basic
interpreter: byte-equal== Sequence explorer ==
Collatz paths, exact Fibonacci numbers and Pascal's triangle.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 1
start (1 to 1000000000000)> 27
27 82 41 124 62 31 94 47 142 71 214 107 322 161 484 242 121 364 182 91 ... 80 40 20 10 5 16 8 4 2 1
27 reaches 1 in 111 steps, climbing as high as 9232.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 1
start (1 to 1000000000000)> 1410123943
1410123943 4230371830 2115185915 6345557746 3172778873 9518336620 4759168310 2379584155 7138752466 3569376233 10708128700 5354064350 2677032175 8031096526 4015548263 12046644790 6023322395 18069967186 9034983593 27104950780 ... 13 40 20 10 5 16 8 4 2 1
1410123943 reaches 1 in 770 steps, climbing as high as 7125885122794452160.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 2
up to (2 to 100000)> 10000
Up to 10000, the start that takes longest is 6171: 261 steps.
Steps for each start up to 20:
0 1 7 2 5 8 16 3 19 6 14 9 9 17 17 4 12 20 20 7
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 3
n (1 to 2000)> 10
F(10) = 55
Cassini: F(9) x F(11) - F(10)^2 = 1, as (-1)^10 says.
F(11) / F(10) = 1.618181818181... - the golden ratio is 1.618033988749...
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 3
n (1 to 2000)> 1000
F(1000) = 4346655768693745643568852...3704476137795166849228875, 209 digits
Cassini: F(999) x F(1001) - F(1000)^2 = 1, as (-1)^1000 says.
F(1001) / F(1000) = 1.618033988749... - the golden ratio is 1.618033988749...
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 4
rows (1 to 15)> 7
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) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 5
rows (1 to 32)> 16
*
* *
* *
* * * *
* *
* * * *
* * * *
* * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
Each * is an odd number; the even ones are left blank.
1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit
choice> 6
Bye.
What was typed (15 lines)
1
27
1
1410123943
2
10000
3
10
3
1000
4
7
5
16
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# P069 sequence explorer: Collatz paths and the longest one below a limit,
# exact Fibonacci numbers with Cassini's identity and the golden ratio, and
# Pascal's triangle - as numbers and as the pattern its odd entries make.
import sequences
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 steps_text(k):
if k == 1:
return "1 step"
return str(k) + " steps"
def number(s):
if s == "" or len(s) > 13:
return -1
0 => n
for c in s:
if not (c >= "0" and c <= "9"):
return -1
n * 10 + int(c) => n
return n
def ask(prompt, low, high):
while True:
trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> ")) => answer
if answer == "":
return -1
number(answer) => n
if n >= low and n <= high:
return n
("Type a whole number from " + str(low) + " to " + str(high) + ".") ^0
def joined(xs):
"" => s
for x in xs:
if s != "":
s + " " => s
s + str(x) => s
return s
def collatz():
ask("start", 1, 1000000000000) => n
if n == -1:
"Cancelled." ^0
return
sequences.collatz(n) => r
r[0] => path
len(path) - 1 => steps
if len(path) <= 40:
joined(path) ^0
else:
(joined(path[0:20]) + " ... " + joined(path[len(path) - 10:len(path)])) ^0
(str(n) + " reaches 1 in " + steps_text(steps) + ", climbing as high as " + str(r[1]) + ".") ^0
def longest():
ask("up to", 2, 100000) => limit
if limit == -1:
"Cancelled." ^0
return
sequences.steps_up_to(limit) => st
1 => best
for k in [2:limit]:
if st[k] > st[best]:
k => best
("Up to " + str(limit) + ", the start that takes longest is " + str(best) + ": " + steps_text(st[best]) + ".") ^0
"Steps for each start up to 20:" ^0
[] => xs
for k in [1:20]:
if k <= limit:
xs + [st[k]] => xs
(" " + joined(xs)) ^0
def fib():
ask("n", 1, 2000) => n
if n == -1:
"Cancelled." ^0
return
sequences.fibonacci(n) => f
str(f[1]) => s
if len(s) <= 60:
("F(" + str(n) + ") = " + s) ^0
else:
("F(" + str(n) + ") = " + s[0:25] + "..." + s[len(s) - 25:len(s)] + ", " + str(len(s)) + " digits") ^0
sequences.cassini(n) => c
("Cassini: F(" + str(n - 1) + ") x F(" + str(n + 1) + ") - F(" + str(n) + ")^2 = " + str(c) + ", as (-1)^" + str(n) + " says.") ^0
# F(n+1) / F(n) to 12 places, exactly rounded down
f[2] => a
f[1] => b
long_div(a, b, 12) => q
("F(" + str(n + 1) + ") / F(" + str(n) + ") = " + q + "... - the golden ratio is 1.618033988749...") ^0
def long_div(a, b, places):
# a / b to `places` decimals, rounded down, by long division - for a
# quotient below 10, as F(n+1) / F(n) always is.
0 => q
a => r
while r >= b:
r - b => r
q + 1 => q
str(q) + "." => s
for k in [1:places]:
r * 10 => r
0 => d
while r >= b:
r - b => r
d + 1 => d
s + str(d) => s
return s
def pascal():
ask("rows", 1, 15) => n
if n == -1:
"Cancelled." ^0
return
sequences.pascal_rows(n) => rows
len(joined(rows[n - 1])) => width
for row in rows:
joined(row) => s
"" => pad
while len(pad) * 2 + len(s) < width:
pad + " " => pad
(pad + s) ^0
def pascal_odd():
ask("rows", 1, 32) => n
if n == -1:
"Cancelled." ^0
return
sequences.pascal_rows(n) => rows
for r in [0:n - 1]:
"" => line
while len(line) < n - 1 - r:
line + " " => line
for k in [0:r]:
if k > 0:
line + " " => line
if rows[r][k] % 2 == 1:
line + "*" => line
else:
line + " " => line
trim_right(line) ^0
"Each * is an odd number; the even ones are left blank." ^0
def trim_right(s):
len(s) => j
while j > 0 and s[j - 1] == " ":
j - 1 => j
return s[0:j]
"== Sequence explorer ==" ^0
"Collatz paths, exact Fibonacci numbers and Pascal's triangle." ^0
True => running
while running:
"" ^0
"1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit" ^0
trim(input("choice> ")) => choice
if choice == "1":
collatz()
elif choice == "2":
longest()
elif choice == "3":
fib()
elif choice == "4":
pascal()
elif choice == "5":
pascal_odd()
elif choice == "6":
False => running
else:
"Pick a number from 1 to 6." ^0
"Bye." ^0
Python projection (main.py)
import sequences
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 steps_text(k):
if k == 1:
return "1 step"
return str(k) + " steps"
def number(s):
if s == "" or len(s) > 13:
return -1
n = 0
for c in s:
if not (c >= "0" and c <= "9"):
return -1
n = n * 10 + int(c)
return n
def ask(prompt, low, high):
while True:
answer = trim(input(prompt + " (" + str(low) + " to " + str(high) + ")> "))
if answer == "":
return -1
n = number(answer)
if n >= low and n <= high:
return n
print("Type a whole number from " + str(low) + " to " + str(high) + ".")
def joined(xs):
s = ""
for x in xs:
if s != "":
s = s + " "
s = s + str(x)
return s
def collatz():
n = ask("start", 1, 1000000000000)
if n == -1:
print("Cancelled.")
return
r = sequences.collatz(n)
path = r[0]
steps = len(path) - 1
if len(path) <= 40:
print(joined(path))
else:
print(joined(path[0:20]) + " ... " + joined(path[len(path) - 10:len(path)]))
print(str(n) + " reaches 1 in " + steps_text(steps) + ", climbing as high as " + str(r[1]) + ".")
def longest():
limit = ask("up to", 2, 100000)
if limit == -1:
print("Cancelled.")
return
st = sequences.steps_up_to(limit)
best = 1
for k in range(2, limit+1):
if st[k] > st[best]:
best = k
print("Up to " + str(limit) + ", the start that takes longest is " + str(best) + ": " + steps_text(st[best]) + ".")
print("Steps for each start up to 20:")
xs = []
for k in range(1, 21):
if k <= limit:
xs = xs + [st[k]]
print(" " + joined(xs))
def fib():
n = ask("n", 1, 2000)
if n == -1:
print("Cancelled.")
return
f = sequences.fibonacci(n)
s = str(f[1])
if len(s) <= 60:
print("F(" + str(n) + ") = " + s)
else:
print("F(" + str(n) + ") = " + s[0:25] + "..." + s[len(s) - 25:len(s)] + ", " + str(len(s)) + " digits")
c = sequences.cassini(n)
print("Cassini: F(" + str(n - 1) + ") x F(" + str(n + 1) + ") - F(" + str(n) + ")^2 = " + str(c) + ", as (-1)^" + str(n) + " says.")
a = f[2]
b = f[1]
q = long_div(a, b, 12)
print("F(" + str(n + 1) + ") / F(" + str(n) + ") = " + q + "... - the golden ratio is 1.618033988749...")
def long_div(a, b, places):
q = 0
r = a
while r >= b:
r = r - b
q = q + 1
s = str(q) + "."
for k in range(1, places+1):
r = r * 10
d = 0
while r >= b:
r = r - b
d = d + 1
s = s + str(d)
return s
def pascal():
n = ask("rows", 1, 15)
if n == -1:
print("Cancelled.")
return
rows = sequences.pascal_rows(n)
width = len(joined(rows[n - 1]))
for row in rows:
s = joined(row)
pad = ""
while len(pad) * 2 + len(s) < width:
pad = pad + " "
print(pad + s)
def pascal_odd():
n = ask("rows", 1, 32)
if n == -1:
print("Cancelled.")
return
rows = sequences.pascal_rows(n)
for r in range(0, n):
line = ""
while len(line) < n - 1 - r:
line = line + " "
for k in range(0, r+1):
if k > 0:
line = line + " "
if rows[r][k] % 2 == 1:
line = line + "*"
else:
line = line + " "
print(trim_right(line))
print("Each * is an odd number; the even ones are left blank.")
def trim_right(s):
j = len(s)
while j > 0 and s[j - 1] == " ":
j = j - 1
return s[0:j]
print("== Sequence explorer ==")
print("Collatz paths, exact Fibonacci numbers and Pascal's triangle.")
running = True
while running:
print("")
print("1) Collatz path 2) longest Collatz 3) Fibonacci 4) Pascal 5) Pascal's odd numbers 6) quit")
choice = trim(input("choice> "))
if choice == "1":
collatz()
elif choice == "2":
longest()
elif choice == "3":
fib()
elif choice == "4":
pascal()
elif choice == "5":
pascal_odd()
elif choice == "6":
running = False
else:
print("Pick a number from 1 to 6.")
print("Bye.")
sequences.eml
eml# P069 sequence explorer - Collatz, Fibonacci and Pascal, in whole numbers
# of any size.
def half(n):
# n // 2 for an even n >= 0, exactly. The corpus case collatz-sequence
# writes int(n / 2), which goes through a float and is exact only below
# 2^53; a Collatz path from a start below 10^12 can climb past that, so
# the halving is done by long division instead.
0 => q
n => a
while a >= 2:
2 => m
1 => k
while m + m <= a:
m + m => m
k + k => k
a - m => a
q + k => q
return q
def collatz(n):
# The path from n down to 1: halve an even number, and take 3n + 1 for
# an odd one. Returns [path, highest value].
[n] => path
n => top
while n != 1:
if n % 2 == 0:
half(n) => n
else:
3 * n + 1 => n
path + [n] => path
if n > top:
n => top
return [path, top]
def steps_up_to(limit):
# How many steps each start from 1 to limit takes to reach 1, using the
# answers already found: a path is followed only until it drops below
# its start. Returns the list of step counts, index = start.
[0] * (limit + 1) => steps
for start in [2:limit]:
start => n
0 => k
while n >= start:
if n % 2 == 0:
half(n) => n
else:
3 * n + 1 => n
k + 1 => k
k + steps[n] => steps[start]
return steps
def fibonacci(n):
# F(0) = 0, F(1) = 1, each the sum of the two before: [F(n-1), F(n), F(n+1)].
0 => a
1 => b
for i in [1:n]:
a + b => c
b => a
c => b
return [b - a, a, b]
def cassini(n):
# F(n-1) F(n+1) - F(n)^2, which Cassini (1680) showed is (-1)^n: the two
# products are huge and differ by exactly one, which floats cannot see
# (the corpus case fibonacci-cassini-exact).
fibonacci(n) => f
return f[0] * f[2] - f[1] * f[1]
def pascal_rows(count):
# The first `count` rows: each number the sum of the two above it.
[[1]] => rows
for r in [1:count - 1]:
rows[r - 1] => prev
[1] => row
for k in [1:r - 1]:
row + [prev[k - 1] + prev[k]] => row
rows + [row + [1]] => rows
return rows
Python projection (sequences.py)
def half(n):
q = 0
a = n
while a >= 2:
m = 2
k = 1
while m + m <= a:
m = m + m
k = k + k
a = a - m
q = q + k
return q
def collatz(n):
path = [n]
top = n
while n != 1:
if n % 2 == 0:
n = half(n)
else:
n = 3 * n + 1
path = path + [n]
if n > top:
top = n
return [path, top]
def steps_up_to(limit):
steps = [0] * (limit + 1)
for start in range(2, limit+1):
n = start
k = 0
while n >= start:
if n % 2 == 0:
n = half(n)
else:
n = 3 * n + 1
k = k + 1
steps[start] = k + steps[n]
return steps
def fibonacci(n):
a = 0
b = 1
for i in range(1, n+1):
c = a + b
a = b
b = c
return [b - a, a, b]
def cassini(n):
f = fibonacci(n)
return f[0] * f[2] - f[1] * f[1]
def pascal_rows(count):
rows = [[1]]
for r in range(1, count):
prev = rows[r - 1]
row = [1]
for k in range(1, r):
row = row + [prev[k - 1] + prev[k]]
rows = rows + [row + [1]]
return rows