<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

	<title>John Tromp's Blog</title>
	<link href="http://tromp.github.io/blog/atom.xml" rel="self"/>
	<link href="http://tromp.github.io/blog"/>
	<updated>2026-05-16T17:51:58+00:00</updated>
	<id>http://tromp.github.io/blog</id>
	<author>
		<name>John Tromp</name>
		<email>john.tromp@gmail.com</email>
	</author>

	
		<entry>
			<title>The largest number representable in 64 bits</title>
			<link href="http://tromp.github.io/blog/2026/01/28/largest-number-revised"/>
			<updated>2026-01-28T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2026/01/28/largest-number-revised</id>
			<content type="html">&lt;p&gt;This post is a rewrite of my earlier blog post from
&lt;a href=&quot;https://tromp.github.io/blog/2023/11/24/largest-number&quot;&gt;Nov 2023&lt;/a&gt;
with many new insights and updates.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┬─┬ ┬─┬──────────                                  ┬─┬─┬ ┬─┬────────────
└─┤ │ │ ──┬──────                                  └─┤ │ │ │ ──┬────────
  │ │ │ ┬─┼──────                                    └─┤ │ │ ──┼─┬──────
  │ │ │ └─┤ ┬─┬──                                      │ │ │ ┬─┼─┼──────
  │ │ │   │ ┼─┼─┬                                      │ │ │ └─┤ │ ┬─┬──
  │ │ │   │ │ ├─┘                                      │ │ │   └─┤ ┼─┼─┬
  │ │ │   │ ├─┘                                        │ │ │     │ │ ├─┘
  │ │ │   ├─┘                                          │ │ │     │ ├─┘  
  │ │ ├───┘                                            │ │ │     ├─┘    
  │ ├─┘                                                │ │ ├─────┘      
  └─┘                                                  │ ├─┘            
                                                       └─┘              
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Most people believe 2&lt;sup&gt;64&lt;/sup&gt;-1 = 18446744073709551615, or
0xFFFFFFFFFFFFFFFF in hexadecimal, to be the largest number
representable in 64 bits. In English, it’s quite the mouthful: eighteen
quintillion four hundred forty-six quadrillion seven hundred forty-four
trillion seventy-three billion seven hundred nine million five hundred
fifty-one thousand six hundred fifteen.&lt;/p&gt;

&lt;p&gt;That is indeed the maximum possible value of 64 bit unsigned integers,
available as data type uint64_t in C or u64 in Rust. 
Floating point data types can represent much larger values, courtesy of their base 2 exponent.
The 64-bit double &lt;a href=&quot;https://en.wikipedia.org/wiki/Double-precision_floating-point_format&quot;&gt;floating
point format&lt;/a&gt; has a largest (finite) representable value of 2&lt;sup&gt;1024&lt;/sup&gt;(1-2&lt;sup&gt;-53&lt;/sup&gt;) ~ 1.8*10&lt;sup&gt;308&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;What if we allow representations beyond plain data types?
Since we want representations to remain computable, the most general
kind of representation would be a program in some programming language.
But the program must be small enough to fit in 64 bits.&lt;/p&gt;

&lt;h2 id=&quot;the-largest-number-programmable-in-64-bits&quot;&gt;The largest number programmable in 64 bits&lt;/h2&gt;

&lt;p&gt;The smallest possible valid C program is “main(){}”,
consisting of 8 ASCII characters.
&lt;a href=&quot;https://en.wikipedia.org/wiki/ASCII&quot;&gt;ASCII&lt;/a&gt; is a 7-bit
character encoding standard representing 128 unique characters,
but all modern computers use 8-bit bytes to store either plain ASCII
or &lt;a href=&quot;https://en.wikipedia.org/wiki/UTF-8&quot;&gt;UTF-8&lt;/a&gt;, a Unicode character encoding that’s backward compatible with
ASCII.  So we’ll consider the above all-scaffold do-nothing program to
be the only valid 64-bit C program.&lt;/p&gt;

&lt;p&gt;Plenty other languages require no such scaffolding. For instance,
Linux features the arbitrary precision calculator
&lt;a href=&quot;https://en.wikipedia.org/wiki/Bc_(programming_language)&quot;&gt;bc&lt;/a&gt;. It happily
computes the 954242 digit number 9^999999 = 35908462…48888889, making
it programmable in 8 bytes. So is the much larger 9^9^9^99 =
9^(9^(9^99)) with over 10^10^94 digits, which bc is less happy to
compute. If bc supported the symbol ! for computing factorials, then
9!!!!!!! would represent a much larger number still.&lt;/p&gt;

&lt;p&gt;Allowing such primitives feels a bit like cheating though. Would we allow a
language that has the &lt;a href=&quot;https://en.wikipedia.org/wiki/Ackermann_function&quot;&gt;Ackerman function&lt;/a&gt;
predefined, letting the 8 byte expression ack(9,9) represent a truly huge number?&lt;/p&gt;

&lt;h2 id=&quot;ackerman-considered-unhelpful&quot;&gt;Ackerman considered unhelpful&lt;/h2&gt;

&lt;p&gt;As it turns out, the question is moot.
One can blow way past ack(9,9) in under 64 bits in a language with no built
in primitive whatsoever. A language with no basic arithmetic; not even numbers themselves.
A language in which all those must be defined from scratch.&lt;/p&gt;

&lt;p&gt;But let’s first look at another primitives-lacking language, one that has been particularly
well studied for producing largest possible outputs. That is the language of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Turing_machine&quot;&gt;Turing machines&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;busy-beaver&quot;&gt;Busy Beaver&lt;/h2&gt;

&lt;p&gt;The famous &lt;a href=&quot;https://en.wikipedia.org/wiki/Busy_beaver&quot;&gt;Busy Beaver&lt;/a&gt;
 function, &lt;a href=&quot;https://archive.org/details/bstj41-3-877/mode/2up&quot;&gt;introduced&lt;/a&gt; by
&lt;a href=&quot;https://en.wikipedia.org/wiki/Tibor_Rad%C3%B3&quot;&gt;Tibor Radó&lt;/a&gt; in 1962, which we’ll
denote BB(n), is defined as the maximal number of steps taken by
an n-state Turing Machine (TM) with a binary tape alphabet,
starting from an all 0 tape, before halting.&lt;/p&gt;

&lt;p&gt;Here we have a discrepancy between how the size of a TM is measured, in states,
versus how program size is measured, in bits.
Fortunately there is a straightforward binary encoding of n-state TMs,
which is entirely determined by its transition function.
For each of the n states that the machine’s finite control can be in,
and each of its 2 tape symbols that could be scanned by its tape head,
the transition function specifies what new symbol to write in the scanned tape cell (1 bit),
whether to move the tape head left or right (1 bit),
and what new state (or special halt state) to transition to (⌈log2(n+1)⌉ bits).
This encoding takes 6*2*(2+3) = 60 bits for a a 6-state TM,
and 7*2*(2+3) = 70 bits for a a 7-state TM.&lt;/p&gt;

&lt;p&gt;We’re also stretching the meaning of “representable” a bit,
since BB considers the runtime of the machine instead of its output.
Besides the above BB (that Radó called S), Radó did define another
function called Σ that considers the output of the machine as a number in unary,
namely the number of 1s in the final tape contents. But BB has received
more attention as it allows one to determine from BB(n) all halting n-state machines.
For 6-states and up though, there is no discernable difference in magnitude between the two functions
so we could have just as easily used Σ.&lt;/p&gt;

&lt;p&gt;So the largest number TM programmable in 64 bits is BB(6).&lt;/p&gt;

&lt;h2 id=&quot;how-large-is-bb6&quot;&gt;How large is BB(6)?&lt;/h2&gt;

&lt;p&gt;Unfortunately, we may never know. While all BB(n) have been determined (and even
formally proven) for n≤5, there are some 6-state TMs whose halting behaviour are
closely related to very hard mathematical problems.
Most of these so-called &lt;a href=&quot;https://wiki.bbchallenge.org/wiki/Cryptids&quot;&gt;cryptids&lt;/a&gt;
are likely not to halt, with some,
like &lt;a href=&quot;https://wiki.bbchallenge.org/wiki/Lucy%27s_Moonlight&quot;&gt;Lucy’s Moonlight&lt;/a&gt;,
likely to halt but unlikely to beat the current champion.
The current 6-state champion shows that
&lt;a href=&quot;https://wiki.bbchallenge.org/wiki/BB(6)&quot;&gt;BB(6) &amp;gt; 2↑↑2↑↑2↑↑10 &lt;/a&gt;.
Here, m↑↑n is &lt;a href=&quot;https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation&quot;&gt;Knuth’s up-arrow notation&lt;/a&gt;
for an exponential tower of n m’s, so that for example 2↑↑3 = 2&lt;sup&gt;2&lt;sup&gt;2&lt;/sup&gt;&lt;/sup&gt;.
Large as this number is, it’s still very small compared to
ack(9,9) = 2↑&lt;sup&gt;7&lt;/sup&gt;12 - 3 = 2↑↑↑↑↑↑↑12 - 3.&lt;/p&gt;

&lt;p&gt;It is known however that
&lt;a href=&quot;https://wiki.bbchallenge.org/wiki/BB(7)&quot;&gt;BB(7) &amp;gt; 2↑&lt;sup&gt;11&lt;/sup&gt;2↑&lt;sup&gt;11&lt;/sup&gt;3 &amp;gt; ack(9,9) &lt;/a&gt;.
Several leading BB researchers believe that BB(7) is even larger than the famous
&lt;a href=&quot;https://en.wikipedia.org/wiki/Graham%27s_number&quot;&gt;Graham’s Number&lt;/a&gt;, which iterates
the function mapping n to 3↑&lt;sup&gt;n&lt;/sup&gt;3 64 times starting from n=4.
This appears to me a rather bold belief, considering that the smallest known Graham exceeding TM has
&lt;a href=&quot;https://wiki.bbchallenge.org/wiki/Champions&quot;&gt;14 states&lt;/a&gt;, twice as many.
So I offered a $1000 bet that a proof of BB(7) &amp;gt; Graham’s Number won’t be found within 10 years,
which BB researcher &lt;a href=&quot;https://www.sligocki.com/about/&quot;&gt;Shawn Ligocki&lt;/a&gt; was happy to accept.&lt;/p&gt;

&lt;p&gt;Meanwhile, Graham’s Number is easily surpassed within 64 bits, by moving beyond Turing machines
into the language of&lt;/p&gt;

&lt;h2 id=&quot;lambda-calculus&quot;&gt;Lambda Calculus&lt;/h2&gt;

&lt;p&gt;Alonzo Church conceived the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lambda_calculus&quot;&gt;λ-calculus&lt;/a&gt;
in about 1928 as a formal logic system for expressing
computation based on function abstraction and application using variable binding and substitution.&lt;/p&gt;

&lt;p&gt;The Graham beating lambda term originates in a Code Golf challenge asking for the
“Shortest terminating program whose output size exceeds Graham’s number”,
&lt;a href=&quot;https://codegolf.stackexchange.com/questions/6430/shortest-terminating-program-whose-output-size-exceeds-grahams-number/219734#219734&quot;&gt;answered&lt;/a&gt;
by user &lt;a href=&quot;https://codegolf.stackexchange.com/users/101119/patcail&quot;&gt;Patcail&lt;/a&gt; and
&lt;a href=&quot;https://codegolf.stackexchange.com/questions/6430/shortest-terminating-program-whose-output-size-exceeds-grahams-number/263884#263884&quot;&gt;further optimized&lt;/a&gt; by user
&lt;a href=&quot;https://codegolf.stackexchange.com/users/98257/2014melo03&quot;&gt;2014MELO03&lt;/a&gt;.
The following 49 bit program&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;01 00 01 10 10 00 01 10 01 10 00 00 01 01 10 110 00 00 01 110 01 110 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;is the &lt;a href=&quot;https://tromp.github.io/cl/cl.html&quot;&gt;Binary Lambda Calculus&lt;/a&gt; &lt;a href=&quot;https://gist.github.com/tromp/86b3184f852f65bfb814e3ab0987d861#lambda-encoding&quot;&gt;encoding&lt;/a&gt; of the term&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(λ 1 1) (λ 1 (1 (λ λ 1 2 (λ λ 2 (2 1)))))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;where λ (lambda) denotes an anonymous function, and number i is the variable bound by the i-th nested λ.
This is known as &lt;a href=&quot;https://en.wikipedia.org/wiki/De_Bruijn_notation&quot;&gt;De Bruijn notation&lt;/a&gt;, a
way to avoid naming variables. A more conventional notation using variable names would be&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(λJ.J J) (λy.y (y (λg λm. m g (λf.λx.f (f x)))))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The top left of this post shows a &lt;a href=&quot;https://tromp.github.io/cl/diagrams.html&quot;&gt;graphical representation&lt;/a&gt; of the term.
The last 16 bits of the program, making up almost a third of its size, encodes
the term λf λx. f (f x), which takes arguments f and x in turn, and iterates f twice on x.
In its generalized form, the function λf λx. f&lt;sup&gt;n&lt;/sup&gt; x, 
called Church numeral n, is the most common way of representing numbers in the λ-calculus.
The encoding of Church numeral n is 0000(01110)&lt;sup&gt;n&lt;/sup&gt;10, of size 5n+6 bits.&lt;/p&gt;

&lt;p&gt;The program, which we’ll name after its discoverer, can be expressed more legibly as&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Melo = let { 2 = λf λx. f (f x); H = λg λm. m g 2; J = λy. y (y H) } in J J
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Melo evaluates to a Church numeral, “Melo’s Number”, that comfortably exceeds Graham’s Number.&lt;/p&gt;

&lt;h2 id=&quot;proof-of-exceeding-grahams-number&quot;&gt;Proof of exceeding Graham’s Number&lt;/h2&gt;

&lt;h3 id=&quot;lemma-1-j-j--26-hh-2-where-hh-denotes-h-h&quot;&gt;Lemma 1. J J = 2↑↑6 HH 2, where HH denotes H H&lt;/h3&gt;

&lt;h3 id=&quot;proof&quot;&gt;Proof:&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  J J
= J (J H)
= J (H HH)
= H HH (H HH H)
= H HH H            HH 2
= H HH 2            HH 2
= 2 HH 2            HH 2
= HH (HH 2)         HH 2
= HH 2          H 2 HH 2
= 2 H 2         H 2 HH 2
= H (H 2) H       2 HH 2
= H (H 2) 2       2 HH 2
= 2 (H 2) 2       2 HH 2
= H 2 (H 2 2)     2 HH 2
= H 2 2       2 2 2 HH 2
= 2 2 2       2 2 2 HH 2
= 2↑↑6              HH 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;lemma-2-for-kn--2-k-h-2-n--3k1n&quot;&gt;Lemma 2. For k,n ≥ 2, k H 2 n &amp;gt; 3↑&lt;sup&gt;k&lt;/sup&gt;(1+n)&lt;/h3&gt;

&lt;h3 id=&quot;proof-1&quot;&gt;Proof:&lt;/h3&gt;
&lt;p&gt;By induction on k.  First note that H2 n = H 2 n = n 2 2 = 2^2^n&lt;/p&gt;

&lt;p&gt;Base:   2 H 2 n = H H2 n = n H2 2 = 2↑↑(1+2n) &amp;gt; 3↑&lt;sup&gt;2&lt;/sup&gt;(1+n)
        already at n=2, since 2↑↑5 = 2^2^16 &amp;gt; 3^27 = 3↑↑3
Step: k+1 H 2 n = H (k H 2) n = n (k H 2) 2 &amp;gt; 3↑&lt;sup&gt;k&lt;/sup&gt;(1+ 3↑&lt;sup&gt;k&lt;/sup&gt;(1+ …
3↑&lt;sup&gt;k&lt;/sup&gt;(1+2)…))
                                            &amp;gt; 3↑&lt;sup&gt;k+1&lt;/sup&gt;(1+n)&lt;/p&gt;

&lt;h3 id=&quot;lemma-3-for-n--2-hh-hh-n--3n3&quot;&gt;Lemma 3. For n ≥ 2, HH (HH n) &amp;gt; 3↑&lt;sup&gt;n&lt;/sup&gt;3&lt;/h3&gt;

&lt;h3 id=&quot;proof-2&quot;&gt;Proof&lt;/h3&gt;
&lt;p&gt;By induction on n&lt;/p&gt;

&lt;p&gt;Base: Lemma 1’s proof shows HH (HH 2) = 2↑↑6 &amp;gt; 3↑&lt;sup&gt;2&lt;/sup&gt;3
Step: HH (HH 1+n) = HH 1+n H 2 = 1+n H 2 H 2 = H (n H 2) H 2 =
H (n H 2) 2 2 = 2 (n H 2) 2 2 = n H 2 (n H 2 2) 2 &amp;gt;&lt;sup&gt;Lm2&lt;/sup&gt;
3↑&lt;sup&gt;n&lt;/sup&gt;(1+3↑&lt;sup&gt;n&lt;/sup&gt;(1+2)) 2 &amp;gt; 3↑&lt;sup&gt;n+1&lt;/sup&gt;3.&lt;/p&gt;

&lt;h3 id=&quot;theorem-j-j--grahams-number-g64-where-gn--n-n---3n3-4&quot;&gt;Theorem: J J &amp;gt; Graham’s Number G(64), where G(n) = n (\n -&amp;gt; 3↑&lt;sup&gt;n&lt;/sup&gt;3) 4&lt;/h3&gt;

&lt;h3 id=&quot;proof-3&quot;&gt;Proof:&lt;/h3&gt;
&lt;p&gt;J J =&lt;sup&gt;Lm1&lt;/sup&gt; 2↑↑6 HH 2 &amp;gt;&lt;sup&gt;Lm3&lt;/sup&gt; (2↑↑6 / 2 - 1) (\n -&amp;gt; 3↑&lt;sup&gt;n&lt;/sup&gt;3) 3↑&lt;sup&gt;2&lt;/sup&gt;3&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;(2↑↑6 / 2 - 1) (\n -&amp;gt; 3↑&lt;sup&gt;n&lt;/sup&gt;3) 4 = G(2↑↑6 / 2 - 1) &amp;gt; G(64)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;leaving-melos-number-in-the-dust&quot;&gt;Leaving Melo’s Number in the dust&lt;/h2&gt;

&lt;p&gt;With 15 bits to spare, opportunities for vastly boosting Melo abound.
Discord users 50_ft_lock and Sam found the following term that extends Melo’s H with an extra argument:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;w218 = let { 2 = λf λx. f (f x); A = λa λb λc. c a b 2; T = λy. y (y A) } in T T T
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;which desugars to lambda term&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(λT.T T T) (λy.y (y (λa λb λc. c a b (λf.λx.f (f x)))))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;in conventional notation, or&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(λ 1 1 1) (λ 1 (1 (λ λ λ 1 3 2 (λ λ 2 (2 1)))))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;in de Bruijn notation, with 61-bit encoding&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;01 00 01 01 10 10 10 00 01 10 01 10 00 00 00 01 01 01 10 1110 110 00 00 01 110 01 110 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;lemma-4-t-t-t--218-a-2-2-2-2-2-2-2-2-2-2&quot;&gt;Lemma 4. T T T = 2↑↑18 A 2 2 2 2 2 2 2 2 2 2&lt;/h3&gt;

&lt;h3 id=&quot;proof-let-aa-denote-a-a&quot;&gt;Proof: Let AA denote A A&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  T T                                                            T
= T (T A)                                                        T
= T (A AA)                                                       T
= A AA (A AA A) T
= T AA                                                  (A AA A) 2
= AA (AA A) (A AA A)                                             2
= A AA A A                                              (AA A) 2 2
= A AA A 2                                              (AA A) 2 2
= 2 AA A                                              2 (AA A) 2 2
= AA (AA A) 2                                           (AA A) 2 2
= 2 A (AA A)                                          2 (AA A) 2 2
= A (A (AA A)) 2 (AA A)                                        2 2
= AA A (A (AA A))                                          2 2 2 2
= A (AA A) A A                                           2 2 2 2 2
= A (AA A) A 2                                           2 2 2 2 2
= 2 (AA A) A                                           2 2 2 2 2 2
= AA A (AA A A)                                        2 2 2 2 2 2
= AA A A                                         A A 2 2 2 2 2 2 2
= A A A 2                                        A A 2 2 2 2 2 2 2
= 2 A A                                        2 A A 2 2 2 2 2 2 2
= A AA 2 A                                         A 2 2 2 2 2 2 2
= A AA 2 2                                         A 2 2 2 2 2 2 2
= 2 AA 2                                         2 A 2 2 2 2 2 2 2
= AA (AA 2) 2                                      A 2 2 2 2 2 2 2
= 2 A (AA 2)                                     2 A 2 2 2 2 2 2 2
= A (A (AA 2)) 2 A                                   2 2 2 2 2 2 2
= A (A (AA 2)) 2 2                                   2 2 2 2 2 2 2
= 2 (A (AA 2)) 2                                   2 2 2 2 2 2 2 2
= A (AA 2) (A (AA 2) 2) 2                            2 2 2 2 2 2 2
= 2 (AA 2) (A (AA 2) 2) 2                            2 2 2 2 2 2 2
= AA 2 (AA 2 (A (AA 2) 2))                         2 2 2 2 2 2 2 2
= AA 2 (A (AA 2) 2)                          A 2 2 2 2 2 2 2 2 2 2
= A (AA 2) 2 A                           2 2 A 2 2 2 2 2 2 2 2 2 2
= A (AA 2) 2 2                           2 2 A 2 2 2 2 2 2 2 2 2 2
= 2 (AA 2) 2                           2 2 2 A 2 2 2 2 2 2 2 2 2 2
= AA 2 (AA 2 2)                        2 2 2 A 2 2 2 2 2 2 2 2 2 2
= AA 2 2                         A 2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= 2 A 2                        2 A 2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A (A 2) 2 A                      2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A (A 2) 2 2                      2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= 2 (A 2) 2 2                      2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A 2 (A 2 2) 2                    2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= 2 2 (A 2 2) 2                    2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= 4 (A 2 2) 2                      2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A 2 2 (A 2 2 (A 2 2 (A 2 2 2)))  2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A 2 2 (A 2 2 (A 2 2 2))    2 2 2 2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A 2 2 (A 2 2 2)      2 2 2 2 2 2 2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= A 2 2 2        2 2 2 2 2 2 2 2 2 2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= 2 2 2 2        2 2 2 2 2 2 2 2 2 2 2 2 2 2 A 2 2 2 2 2 2 2 2 2 2
= 2↑↑18 A 2 2 2 2 2 2 2 2 2 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These 2↑↑18 iterations of A also let us relate its magnitude to the so-called
&lt;a href=&quot;https://en.wikipedia.org/wiki/Fast-growing_hierarchy&quot;&gt;Fast-growing hierarchy&lt;/a&gt;,
a family that assigns, to each ordinal α, a function [α] (diverting from the usual f&lt;sub&gt;α&lt;/sub&gt;
notation for improved legibility) from natural numbers to natural numbers.
We’ll treat all numbers as Church Numerals, so we can write n f instead of the
usual f&lt;sup&gt;n&lt;/sup&gt; and write f n instead of f(n) as normally done in λ-calculus.&lt;/p&gt;

&lt;p&gt;Readers unfamiliar with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Ordinal_number&quot;&gt;ordinal&lt;/a&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Ordinal_arithmetic&quot;&gt;arithmetic&lt;/a&gt;,
may want to skip the next section.&lt;/p&gt;

&lt;p&gt;The following FGH definition differs slightly from the standard one,
which has the slightly slower growing [0] n = n+1 and [α+1] n = n [α] n.
This allows Lemma 5 to be exact rather than a mere lower bound.&lt;/p&gt;

&lt;h3 id=&quot;definition-of-fast-growing-hierarchy&quot;&gt;Definition of Fast Growing Hierarchy&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;[0] n = 2 n = n&lt;sup&gt;2&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;[α+1] n = n 2 [α] 2 = A 2 [α] n&lt;/li&gt;
  &lt;li&gt;[ω&lt;sup&gt;i+1&lt;/sup&gt;(α+1)] n = [ω&lt;sup&gt;i+1&lt;/sup&gt;α+ω&lt;sup&gt;i&lt;/sup&gt; n] 2&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;lemma-5-for-k0-n2--k1-a-2-ωk-α-n--ωk-α1-n&quot;&gt;Lemma 5. For k≥0, n&amp;gt;=2, : k+1 A 2 [ω&lt;sup&gt;k&lt;/sup&gt; α] n = [ω&lt;sup&gt;k&lt;/sup&gt; (α+1)] n&lt;/h3&gt;

&lt;h3 id=&quot;proof-4&quot;&gt;Proof:&lt;/h3&gt;
&lt;p&gt;Base k=0:
0+1 A 2 [ω&lt;sup&gt;0&lt;/sup&gt; α] n = A 2 [α] n = n 2 [α] 2 = [α+1] n&lt;/p&gt;

&lt;p&gt;Step k&amp;gt;0:
k+1 A 2 [ω&lt;sup&gt;k&lt;/sup&gt; α] n = A (k A 2) [ω&lt;sup&gt;k&lt;/sup&gt; α] n = n (k A 2) [ω&lt;sup&gt;k&lt;/sup&gt; α] 2 = [ω&lt;sup&gt;k&lt;/sup&gt; α + ω&lt;sup&gt;k-1&lt;/sup&gt; n] 2 = [ω&lt;sup&gt;k&lt;/sup&gt; (α+1)] n&lt;/p&gt;

&lt;p&gt;Lemma 5 gives w218 = (2↑↑18 A 2 [0] 2) 2 2 2 2 2 2 2 = 2^2^2^2^2^2^2^([ω&lt;sup&gt;2↑↑18-1&lt;/sup&gt;] 2).
In comparison, Graham’s and Melo’s Numbers are known to be much smaller at around [ω+1] 64 and [ω+1] (2↑↑6)
respectively.&lt;/p&gt;

&lt;h2 id=&quot;a-functional-busy-beaver&quot;&gt;A Functional Busy Beaver&lt;/h2&gt;

&lt;p&gt;The λ-calculus analogue to BB is:&lt;/p&gt;

&lt;p&gt;BBλ(n) = the maximum beta normal form size of any closed lambda term of size n&lt;/p&gt;

&lt;p&gt;which appears in the Online Encyclopedia of Integer Sequences (OEIS) as
&lt;a href=&quot;https://oeis.org/A333479&quot;&gt;functional Busy Beaver function&lt;/a&gt;
Besides being simpler than BB, it has the advantage of using the standard unit of information theory,
bits, rather than states.&lt;/p&gt;

&lt;p&gt;The much more fine-grained use of bits allows the first 36 values of BBλ 
to be currently known, versus only 5 values of BB.&lt;/p&gt;

&lt;p&gt;Since both are Church numerals, term Melo implies that BBλ(49) ≥ 5(Melo’s Number)+6,
while w218 implies that BBλ(61) ≥ 5(2^2^2^2^2^2^2^([ω&lt;sup&gt;2↑↑18-1&lt;/sup&gt;] 2))+6.&lt;/p&gt;

&lt;h2 id=&quot;bb-compared-bit-by-bit-to-bbλ&quot;&gt;BB compared bit-by-bit to BBλ&lt;/h2&gt;

&lt;p&gt;The growth rates of the two BB functions may be compared by how quickly they are known
to exceed certain large number milestones, that correspond to well known ordinals in the
Fast Growing Hierarchy.&lt;/p&gt;

&lt;p&gt;For Graham’s Number, at ordinal ω+1, we saw earlier that Melo’s 49 bits compares with 14 states,
which take 14*2*(2+4) = 168 bits to encode. If I lose my bet, then the comparison
becomes rather closer at 49 vs 70 bits.&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;https://en.wikipedia.org/wiki/Goodstein%27s_theorem&quot;&gt;Goodstein’s function&lt;/a&gt; at ordinal ε&lt;sub&gt;0&lt;/sub&gt;,
&lt;a href=&quot;https://github.com/tromp/AIT/blob/master/fast_growing_and_conjectures/BBE0.lam&quot;&gt;111
bits&lt;/a&gt;
compares with &lt;a href=&quot;https://wiki.bbchallenge.org/wiki/Champions&quot;&gt;51 states&lt;/a&gt; taking 51*2*(2+6) = 816 bits.&lt;/p&gt;

&lt;p&gt;The ε&lt;sub&gt;0&lt;/sub&gt; growth term is actually obsoleted by a &lt;a href=&quot;https://github.com/tromp/AIT/blob/master/fast_growing_and_conjectures/BO.lam&quot;&gt;100-bit term&lt;/a&gt; recently discovered by Patcail that grows at the unfathomably larger Bucholz’ Ordinal,
the catching point between the &lt;a href=&quot;https://en.wikipedia.org/wiki/Slow-growing_hierarchy&quot;&gt;SGH&lt;/a&gt; and the FGH.
As such, that term easily exceeds another famously large number, TREE(3).&lt;/p&gt;

&lt;p&gt;For the limit of Bashicu Matrix System (BMS), at (presumed) ordinal PTO(Z&lt;sub&gt;2&lt;/sub&gt;),
&lt;a href=&quot;https://github.com/tromp/AIT/blob/master/fast_growing_and_conjectures/bms.lam&quot;&gt;331 bits&lt;/a&gt;
compares with &lt;a href=&quot;https://morphett.info/turing/turing.html?c95a199c8e8a3dd56452f8b7e28fabbf&quot;&gt;150 states&lt;/a&gt; taking 150*2*(2+8) = 3000 bits.&lt;/p&gt;

&lt;p&gt;Finally, for Loader’s Number, at (presumed) ordinal PTO(Z&lt;sub&gt;ω&lt;/sub&gt;),
&lt;a href=&quot;https://codegolf.stackexchange.com/questions/176966/golf-a-number-bigger-than-loaders-number/274634#274634&quot;&gt;1850
bits&lt;/a&gt;
compares with &lt;a href=&quot;https://github.com/CatsAreFluffy/metamath-turing-machines/tree/master&quot;&gt;1015 states
&lt;/a&gt; taking 
1015*2*(2+10) = 24360 bits.&lt;/p&gt;

&lt;p&gt;One reason for TMs taking many more bits to achieve comparable growth,
especially at the larger milestones, is the extremely poor programmability of TMs.
The λ-calculus, despite its similar bare bones nature, doesn’t share this drawback.
Modern high level pure functional languages like &lt;a href=&quot;https://www.haskell.org/&quot;&gt;Haskell&lt;/a&gt;
are essentially just syntactically sugared λ-calculus,
with programmer friendly features like &lt;a href=&quot;https://en.wikipedia.org/wiki/Algebraic_data_type&quot;&gt;Algebraic Data Types&lt;/a&gt;
translating directly through &lt;a href=&quot;https://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding&quot;&gt;Scott encodings&lt;/a&gt;.
The &lt;a href=&quot;https://bruijn.marvinborner.de/&quot;&gt;bruijn programming language&lt;/a&gt; is an even
thinner layer of syntactic sugar for the pure untyped lambda calculus, whose
extensive &lt;a href=&quot;https://bruijn.marvinborner.de/std/&quot;&gt;standard library&lt;/a&gt; contains many
datatypes and functions.
It is this excellent programmability of the λ-calculus that facilitated the construction 
of highly optimized programs for BMS and Loader’s.&lt;/p&gt;

&lt;p&gt;Because programming a Turing machine is so impossibly tedious,
that people have resorted to implementing higher level languages like
&lt;a href=&quot;https://github.com/sorear/metamath-turing-machines&quot;&gt;Not-Quite-Laconic&lt;/a&gt;
for writing nontrivial programs such as the TM that halts only upon
finding an inconstency in ZFC. The above 1015 state TM for exceeding Loader’s Number
even includes a λ-calculus interpreter written in NQL!&lt;/p&gt;

&lt;p&gt;In his paper &lt;a href=&quot;https://scottaaronson.com/papers/bb.pdf&quot;&gt;The Busy Beaver Frontier&lt;/a&gt;,
&lt;a href=&quot;https://scottaaronson.com/&quot;&gt;Scott Aaronson&lt;/a&gt; tries to answer the question&lt;/p&gt;
&lt;h2 id=&quot;but-why-turing-machines&quot;&gt;But why Turing machines?&lt;/h2&gt;

&lt;p&gt;“For all their historic importance, haven’t Turing machines been completely superseded
by better alternatives—whether stylized assembly languages or various codegolf languages or Lisp?
As we’ll see, there is a reason why Turing machines were a slightly unfortunate choice
for the Busy Beaver game: namely, the loss incurred when we encode a state transition table
by a string of bits or vice versa.
But Turing machines also turn out to have a massive advantage that compensates for this.”&lt;/p&gt;

&lt;h3 id=&quot;interesting-behaviour-at-small-sizes&quot;&gt;Interesting behaviour at small sizes&lt;/h3&gt;

&lt;p&gt;“Namely, because Turing machines have no “syntax” to speak of, but only graph structure,
we immediately start seeing interesting behavior even with machines of only 3, 4, or 5 states,
which are feasible to enumerate.”&lt;/p&gt;

&lt;p&gt;The number of uniquely behaving TMs with “only” 5 states is
4^10 * &lt;a href=&quot;https://oeis.org/A107668&quot;&gt;632700&lt;/a&gt; = 663434035200 , which is more than
the number of closed lambda terms of size at most 52 bits (513217604750).
The latter certainly exhibit no less interesting behaviour, so TMs hold no advantage here.&lt;/p&gt;

&lt;h3 id=&quot;ancient-and-fixed-computational-model&quot;&gt;Ancient and fixed computational model&lt;/h3&gt;

&lt;p&gt;“And there’s a second advantage. Precisely because the Turing machine model is so ancient and fixed,
whatever emergent behavior we find in the Busy Beaver game, there can be no suspicion that
we “cheated” by changing the model until we got the results we wanted.”&lt;/p&gt;

&lt;p&gt;The λ-calculus is just slightly more ancient and is arguably more fixed.
There is no choice of tape alphabet size, no choice of whether the tape head needs
to move in every transition, no choice of halting and output
convention, and no choice in number of tapes or tape heads.&lt;/p&gt;

&lt;p&gt;The λ-calculus can neither be suspected of being designed toward fast growth,
so again TMs hold no advantage here.&lt;/p&gt;

&lt;p&gt;The only remaining advantage of BB over BBλ is the many decades of research behind
and publications about it.&lt;/p&gt;

&lt;h2 id=&quot;a-universal-busy-beaver&quot;&gt;A Universal Busy Beaver&lt;/h2&gt;

&lt;p&gt;Is BBλ then an ideal Busy Beaver function? Not quite.
It’s still lacking one desirable property, namely universality.&lt;/p&gt;

&lt;p&gt;This property mirrors a notion of optimality for shortest description lengths, where it’s known
as the &lt;a href=&quot;https://en.wikipedia.org/wiki/Kolmogorov_complexity#Invariance_theorem&quot;&gt;Invariance theorem&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Given any description language L, L&lt;sub&gt;opt&lt;/sub&gt;
  is at least as efficient as L, with at most constant additive overhead.&lt;/p&gt;

&lt;p&gt;In the realm of beavers, this means we require of an optimal Busy Beaver BB&lt;sub&gt;opt&lt;/sub&gt; that
it surpass any Busy Beaver function bb (based on self-delimiting programs) with at most constant lag:&lt;/p&gt;

&lt;p&gt;for some constant c depending on bb, and for all n: BB&lt;sub&gt;opt&lt;/sub&gt;(n+c) ≥ bb(n)&lt;/p&gt;

&lt;p&gt;While BBλ is not universal, the closely related&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://oeis.org/A361211&quot;&gt;BBλ2&lt;/a&gt;(n) = the maximum output size of self-delimiting BLC programs of size n&lt;/p&gt;

&lt;p&gt;achieves universality by giving λ-calculus terms access to pure binary data. BLC programs
consist of an encoded lambda term, followed by arbitrary binary data, that the term is applied to.&lt;/p&gt;

&lt;p&gt;Since (λ_. t) applied to any (standard lambda representation of) binary data equals t,
BBλ champions provide lower bounds for BBλ2: for all n, BBλ2(2+n) ≥ BBλ(n).&lt;/p&gt;

&lt;h2 id=&quot;in-conclusion&quot;&gt;In conclusion&lt;/h2&gt;

&lt;p&gt;The largest number (currently known to be) representable in 64 bits is w218,
which lower bounds both BBλ(61) and BBλ2(63).&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>Nesting interpreters</title>
			<link href="http://tromp.github.io/blog/2026/01/16/nesting-interpreters"/>
			<updated>2026-01-16T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2026/01/16/nesting-interpreters</id>
			<content type="html">&lt;h1 id=&quot;nesting-interpreters&quot;&gt;Nesting interpreters&lt;/h1&gt;

&lt;p&gt;A friend of mine enquired about my experience with Lévy-optimal evaluation of lambda calculus,
to which I replied:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;I&apos;ve tried various others and found them horribly slow
on actual benchmarks despite their optimality.
So I prefer simple (and hence non-opimal) runtimes that are pretty
quick in practice, namely combinator graph reduction such as
https://github.com/augustss/MicroHs or my own
https://github.com/tromp/AIT/blob/master/uni.c

There&apos;s one thing that I really would like to get more optimal though.
What I notice with combinatory reducers is that you can run nested
binary combinatory logic interpreters
with only additive overhead; each additional interpreter basically
rewrites itself into the higher level one.
That is not the case with all the binary lambda calculus interpreters
I tried, even the ones based on
Kiselyov&apos;s direct translation
https://theory.stanford.edu/~blynn/lambda/kiselyov.html

I&apos;d like to know if that is fundamentally impossible or not.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My friend understandably asked for clarification on the nesting of interpreters and additive overhead,
and while pondering a reply, I realized this might be of interest to more people, so I decided to
make a blog post about it.&lt;/p&gt;

&lt;h2 id=&quot;universal-lambda-machine&quot;&gt;Universal Lambda Machine&lt;/h2&gt;

&lt;p&gt;To dive straight in, I’ve proposed a ULM [1] [2] that reads from a binary input stream
an encoded lambda term, and applies it to the remainder of input.&lt;/p&gt;

&lt;p&gt;When the encoded term is itself a lambda calculus interpreter operating as a universal lambda machine,
then the ULM’s behaviour on the remaining input is identical to its original behaviour.&lt;/p&gt;

&lt;p&gt;Except when behaviour includes performance on actual hardware.
Nesting interpreters normally incur a constant factor slowdown or worse.&lt;/p&gt;

&lt;p&gt;This was already noted in my 2012 IOCCC submission where a “Performance” section [4]
compares runtimes with 0,1,2,3, and 4 nested interpreters running a prime number sieve,
incurring slowdowns of respectively 4.4x, 10.0x, 12.2x, and 12.4x.&lt;/p&gt;

&lt;p&gt;Let’s rerun that experiment with the much more performant (and compilable) uni.c runtime [4],
and with a simpler end-task resulting in a normal form:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ (cat delimit.blc; echo -n 1111000111001) | ../uni -b
11010steps 9746 time 0ms steps/s 666M #GC 0 HP 20492
$ (cat uni.blc delimit.blc; echo -n 1111000111001) | ../uni -b
11010steps 143176 time 3ms steps/s 47M #GC 0 HP 159806
$ (cat uni.blc uni.blc delimit.blc; echo -n 1111000111001) | ../uni -b
11010steps 1554114 time 36ms steps/s 43M #GC 1 HP 539112
$ (cat uni.blc uni.blc uni.blc delimit.blc; echo -n 1111000111001) | ../uni -b
11010steps 17928565 time 407ms steps/s 44M #GC 18 HP 282330
$ (cat uni.blc uni.blc uni.blc uni.blc delimit.blc; echo -n 1111000111001) | ../uni -b
11010steps 209071006 time 5047ms steps/s 41M #GC 233 HP 518110
$ (cat uni.blc uni.blc uni.blc uni.blc uni.blc delimit.blc; echo -n 1111000111001) | ../uni -b
11010steps 2439995720 time 69617ms steps/s 35M #GC 3407 HP 940096
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;File uni.blc contains the 196 bit encoding of the universal lambda term
(λ11)(λ(λλλ1(λλ2(1(λ6(λ2(6(λλ3(λλ23(14))))(7(λ7(λ31(21)))))))(41(111))))(11))(λ1((λ11)(λ11))).&lt;/p&gt;

&lt;p&gt;The delimit program decodes the Levenshtein encoding [5] 1111000111001 of the binary string 11010.
Now we see step slowdowns of 14.7x, 10.9x, 11.5x, 11.7x, and 11.7x.&lt;/p&gt;

&lt;p&gt;Now to make the experiment vastly more interesting, let’s try nesting binary combinatory logic interpreters instead.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ (cat uniSK.blc delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 62490 time 1ms steps/s 62M #GC 0 HP 78802
$ (cat uniSK.blc uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 119378 time 2ms steps/s 59M #GC 0 HP 127812
$ (cat uniSK.blc uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 158725 time 3ms steps/s 52M #GC 0 HP 145464
$ (cat uniSK.blc uniSK.bcl uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 201786 time 4ms steps/s 50M #GC 0 HP 163116
$ (cat uniSK.blc uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 248561 time 4ms steps/s 62M #GC 0 HP 180768
$ (cat uniSK.blc uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 299050 time 5ms steps/s 59M #GC 0 HP 198420
$ (cat uniSK.blc uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 353253 time 6ms steps/s 58M #GC 0 HP 216072
$ (cat uniSK.blc uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 411170 time 7ms steps/s 58M #GC 0 HP 233724
$ (cat uniSK.blc uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl uniSK.bcl delimit.bcl; echo -n 1111000111001) | ../uni -b
11010steps 472801 time 8ms steps/s 59M #GC 0 HP 251376
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shockingly, the constant factor slowdown is gone!
The slowdown is now more like an additive one, of 56888, 39347, 43061, 46775, 50489, 54203, 57917, and 61631 steps.&lt;/p&gt;

&lt;p&gt;Two contributing factors make this possible.
First, uni.c is a combinatory graph reducer, bulding an acyclic graph of binary application nodes
and combinator leaves, and repeatedly rewriting the graph for combinators applied to sufficiently many arguments.
For a combinator that duplicates arguments, such as S x y z = x z (y z), the z node will be shared as the right
child of both the (newly created) application nodes for x z and for y z.&lt;/p&gt;

&lt;p&gt;Second, the combinatory logic binary encoding is exceedingly simple, using 1 for (prefix [6]) application,
10 for K, and 11 for S, allowing for a very simple interpreter that immediately passes an encoded combinator to a given
continuation after reading its code.&lt;/p&gt;

&lt;p&gt;Combined, this means that the graph of the interpreter, applied to the graph representing the input stream of bits,
ends up rewriting itself back to the graph of the interpreter. Or mostly, considering the step difference still increases.&lt;/p&gt;

&lt;p&gt;The lambda calculus interpreters are not quite so straightforward. After reading the code of a term, what they pass 
to the given continuation is a function that maps an environment of free variables to the encoded term.
This extra mapping prevents the combinator graph from just rewriting itself.&lt;/p&gt;

&lt;p&gt;I had hoped that a more direct translation from encoded lambda terms to combinators, based on Oleg Kiselyov’s work [7],
could also avoid the constant factor slowdown, but sadly that was not the case.&lt;/p&gt;

&lt;h1 id=&quot;future-work&quot;&gt;Future work&lt;/h1&gt;

&lt;p&gt;If we replaced the uni.c runtime with a Lévy-optimal lambda evaluator, then supposedly we
should see additive slowdowns, at least in terms of number of beta reductions performed.
But we may also see constant factor slowdowns in the number of beta reductions performed per second,
negating the former. Hopefully we will see more performant optimal evaluators in future,
perhaps based on interaction nets [8],
to continue these experiments with.&lt;/p&gt;

&lt;p&gt;I’ve started work on a binary language that is a hybrid of lambda calculus and combinatory logic,
tentatively called Binary Combinatory Lambda Calculus, or BCLC.
It encodes application as 001, K as 010, S as 011, abstraction as 000, and a De Bruijn index as 1^i0.
This allows one to slowly morph a combinatory logic interpreter into a lambda calculus interpreter,
and see where the “phase transition” from additive slowdown to constant factor slowdown occurs.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;p&gt;[1] &lt;a href=&quot;https://www.ioccc.org/2012/tromp/&quot;&gt;Most functional&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] &lt;a href=&quot;https://rosettacode.org/wiki/Universal_Lambda_Machine&quot;&gt;Universal Lambda Machine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] &lt;a href=&quot;https://www.ioccc.org/2012/tromp/index.html#performance&quot;&gt;Performance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] &lt;a href=&quot;https://github.com/tromp/AIT/blob/master/uni.c&quot;&gt;uni.c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[5] &lt;a href=&quot;https://en.wikipedia.org/wiki/Levenshtein_coding&quot;&gt;Levenshtein code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[6] &lt;a href=&quot;https://en.wikipedia.org/wiki/Polish_notation&quot;&gt;Polish notation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[7] &lt;a href=&quot;https://theory.stanford.edu/~blynn/lambda/kiselyov.html&quot;&gt;Kiselyov Combinator Translation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[8] &lt;a href=&quot;https://en.wikipedia.org/wiki/Interaction_nets&quot;&gt;Interaction Nets&lt;/a&gt;&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>Gaming the coordinates</title>
			<link href="http://tromp.github.io/blog/2024/04/15/gaming-the-coordinates"/>
			<updated>2024-04-15T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2024/04/15/gaming-the-coordinates</id>
			<content type="html">&lt;svg width=&quot;700&quot; height=&quot;700&quot; viewbox=&quot;0 0 2800 2800&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
&lt;line x1=&quot;256&quot; y1=&quot;256&quot; x2=&quot;2560&quot; y2=&quot;256&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;384&quot; x2=&quot;2560&quot; y2=&quot;384&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;512&quot; x2=&quot;2560&quot; y2=&quot;512&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;640&quot; x2=&quot;2560&quot; y2=&quot;640&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;768&quot; x2=&quot;2560&quot; y2=&quot;768&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;896&quot; x2=&quot;2560&quot; y2=&quot;896&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1024&quot; x2=&quot;2560&quot; y2=&quot;1024&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1152&quot; x2=&quot;2560&quot; y2=&quot;1152&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1280&quot; x2=&quot;2560&quot; y2=&quot;1280&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1408&quot; x2=&quot;2560&quot; y2=&quot;1408&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1536&quot; x2=&quot;2560&quot; y2=&quot;1536&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1664&quot; x2=&quot;2560&quot; y2=&quot;1664&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1792&quot; x2=&quot;2560&quot; y2=&quot;1792&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;1920&quot; x2=&quot;2560&quot; y2=&quot;1920&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;2048&quot; x2=&quot;2560&quot; y2=&quot;2048&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;2176&quot; x2=&quot;2560&quot; y2=&quot;2176&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;2304&quot; x2=&quot;2560&quot; y2=&quot;2304&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;2432&quot; x2=&quot;2560&quot; y2=&quot;2432&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;2560&quot; x2=&quot;2560&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;256&quot; y1=&quot;256&quot; x2=&quot;256&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;384&quot; y1=&quot;256&quot; x2=&quot;384&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;512&quot; y1=&quot;256&quot; x2=&quot;512&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;640&quot; y1=&quot;256&quot; x2=&quot;640&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;768&quot; y1=&quot;256&quot; x2=&quot;768&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;896&quot; y1=&quot;256&quot; x2=&quot;896&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1024&quot; y1=&quot;256&quot; x2=&quot;1024&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1152&quot; y1=&quot;256&quot; x2=&quot;1152&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1280&quot; y1=&quot;256&quot; x2=&quot;1280&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1408&quot; y1=&quot;256&quot; x2=&quot;1408&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1536&quot; y1=&quot;256&quot; x2=&quot;1536&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1664&quot; y1=&quot;256&quot; x2=&quot;1664&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1792&quot; y1=&quot;256&quot; x2=&quot;1792&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;1920&quot; y1=&quot;256&quot; x2=&quot;1920&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;2048&quot; y1=&quot;256&quot; x2=&quot;2048&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;2176&quot; y1=&quot;256&quot; x2=&quot;2176&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;2304&quot; y1=&quot;256&quot; x2=&quot;2304&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;2432&quot; y1=&quot;256&quot; x2=&quot;2432&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;line x1=&quot;2560&quot; y1=&quot;256&quot; x2=&quot;2560&quot; y2=&quot;2560&quot; style=&quot;stroke:black;&quot; /&gt;
&lt;circle cx=&quot;640&quot; cy=&quot;640&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;1408&quot; cy=&quot;640&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2176&quot; cy=&quot;640&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;640&quot; cy=&quot;1408&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;1408&quot; cy=&quot;1408&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2176&quot; cy=&quot;1408&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;640&quot; cy=&quot;2176&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;1408&quot; cy=&quot;2176&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2176&quot; cy=&quot;2176&quot; r=&quot;7&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;234,109 278,109 256,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;polygon points=&quot;234,2669 278,2669 256,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;polygon points=&quot;362,109 406,109 384,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;362&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;362,2669 406,2669 384,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;362&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;490,109 534,109 512,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;490&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;534&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;490,2669 534,2669 512,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;490&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;534&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;618,109 662,109 640,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;618&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;640&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;618,2669 662,2669 640,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;618&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;640&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;746,109 790,109 768,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;790&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;746,2669 790,2669 768,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;790&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;874,109 918,109 896,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;918&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;896&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;874,2669 918,2669 896,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;918&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;896&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1002,109 1046,109 1024,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1002&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1046&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;1002,2669 1046,2669 1024,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1002&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1046&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;1130,109 1174,109 1152,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1152&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1130,2669 1174,2669 1152,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1152&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1258,109 1302,109 1280,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1258&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1280&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1258,2669 1302,2669 1280,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1258&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1280&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1386,109 1430,109 1408,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1430&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;1408&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1386,2669 1430,2669 1408,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1430&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;1408&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1514,109 1558,109 1536,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1514&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;1514,2669 1558,2669 1536,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1514&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;1642,109 1686,109 1664,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1642&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1686&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1642,2669 1686,2669 1664,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1642&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1686&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1770,109 1814,109 1792,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1770&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1792&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;1770,2669 1814,2669 1792,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1770&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;1792&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;1898,109 1942,109 1920,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1942&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;1898,2669 1942,2669 1920,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;1942&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2026,109 2070,109 2048,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2070&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2048&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2026,2669 2070,2669 2048,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2070&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2048&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2154,109 2198,109 2176,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2154&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2198&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2154,2669 2198,2669 2176,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2154&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2198&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2282,109 2326,109 2304,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2304&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2282,2669 2326,2669 2304,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2304&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2410,109 2454,109 2432,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2410&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2432&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2410,2669 2454,2669 2432,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2410&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2432&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2538,109 2582,109 2560,147&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2582&quot; cy=&quot;109&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2560&quot; cy=&quot;147&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2538,2669 2582,2669 2560,2707&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2582&quot; cy=&quot;2669&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2560&quot; cy=&quot;2707&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,2579 150,2579 128,2541&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;polygon points=&quot;2666,2579 2710,2579 2688,2541&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;polygon points=&quot;106,2451 150,2451 128,2413&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;2451&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,2451 2710,2451 2688,2413&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;2451&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,2323 150,2323 128,2285&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;2323&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;2323&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,2323 2710,2323 2688,2285&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;2323&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;2323&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,2195 150,2195 128,2157&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;2195&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;2157&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,2195 2710,2195 2688,2157&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;2195&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;2157&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,2067 150,2067 128,2029&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;2067&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,2067 2710,2067 2688,2029&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;2067&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,1939 150,1939 128,1901&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;1939&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;1901&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,1939 2710,1939 2688,1901&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;1939&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;1901&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,1811 150,1811 128,1773&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;1811&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;1811&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,1811 2710,1811 2688,1773&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;1811&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;1811&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,1683 150,1683 128,1645&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;1645&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,1683 2710,1683 2688,1645&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;1645&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,1555 150,1555 128,1517&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;1555&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;1517&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,1555 2710,1555 2688,1517&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;1555&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;1517&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,1427 150,1427 128,1389&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;1427&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;1389&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,1427 2710,1427 2688,1389&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;1427&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;1389&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,1299 150,1299 128,1261&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;1299&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,1299 2710,1299 2688,1261&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;1299&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,1171 150,1171 128,1133&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;1171&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;1171&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,1171 2710,1171 2688,1133&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;1171&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;1171&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,1043 150,1043 128,1005&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;1043&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;1005&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,1043 2710,1043 2688,1005&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;1043&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;1005&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,915 150,915 128,877&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;915&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,915 2710,915 2688,877&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;915&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,787 150,787 128,749&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;787&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;749&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,787 2710,787 2688,749&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;787&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;749&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,659 150,659 128,621&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;659&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;659&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;2666,659 2710,659 2688,621&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;659&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;659&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;106,531 150,531 128,493&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;493&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,531 2710,531 2688,493&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;493&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,403 150,403 128,365&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;106&quot; cy=&quot;403&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;365&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,403 2710,403 2688,365&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2666&quot; cy=&quot;403&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;365&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;106,275 150,275 128,237&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;150&quot; cy=&quot;275&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;128&quot; cy=&quot;237&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;polygon points=&quot;2666,275 2710,275 2688,237&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;2710&quot; cy=&quot;275&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;circle cx=&quot;2688&quot; cy=&quot;237&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;white&quot; /&gt;
&lt;/svg&gt;

&lt;p&gt;Sensei’s library [1] lists several coordinate systems for the standard 19x19 size Go board.
The system shown above must rank as one of the more unusual ones.&lt;/p&gt;

&lt;p&gt;It was inspired by a recent email from fellow games researcher Ryan Hayward [2], in which he noticed
that a triangle shaped board on just 3 points, has precisely 19 legal positions, as well as 3439
legal games (under the Logical rules [3]).&lt;/p&gt;

&lt;p&gt;Having studied Hamiltonian games in our Combinatorics of Go paper [4], I was naturally curious to see
if triangular Go was also Hamiltonian. That is, whether one can play a game that visits all legal positions.&lt;/p&gt;

&lt;p&gt;As it turns out, not only can one do so, but (up to symmetry) the game is unique and has no intermediate passes!&lt;/p&gt;

&lt;p&gt;That makes it quite natural to use the 19 successive positions in that game as
coordinates to mark the successive vertical lines of the Go board.
We similarly mark successive horizontal lines of the Go board, except for 
flipping the triangles upside-down, so that x and y coordinates nicely align into
a slanted 2x3 grid, as shown for example with tengen coordinates&lt;/p&gt;

&lt;svg width=&quot;154&quot; height=&quot;80&quot; viewbox=&quot;0
0 154 80&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
&lt;polygon points=&quot;0,21 44,21 22,59&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;44&quot; cy=&quot;21&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;22&quot; cy=&quot;59&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;polygon points=&quot;68,59 112,59 90,21&quot; style=&quot;fill:white;stroke:black&quot; /&gt;
&lt;circle cx=&quot;112&quot; cy=&quot;59&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;circle cx=&quot;90&quot; cy=&quot;21&quot; r=&quot;21&quot; stroke=&quot;black&quot; fill=&quot;black&quot; /&gt;
&lt;/svg&gt;

&lt;p&gt;Curiously, the star points are exactly those, both of whose coordinates have 2 black stones.&lt;/p&gt;

&lt;p&gt;Obviously this is not the most practical of coordinate systems, but it does have a certain aesthetic appeal…&lt;/p&gt;

&lt;p&gt;[1] &lt;a href=&quot;https://senseis.xmp.net/?Coordinates&quot;&gt;Sensei’s Library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] &lt;a href=&quot;https://webdocs.cs.ualberta.ca/~hayward/&quot;&gt;Ryan Hayward&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] &lt;a href=&quot;https://tromp.github.io/go.html&quot;&gt;The Logical rules of Go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] &lt;a href=&quot;https://tromp.github.io/go/gostate.pdf&quot;&gt;Combinatorics of Go&lt;/a&gt;&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>The largest number representable in 64 bits</title>
			<link href="http://tromp.github.io/blog/2023/11/24/largest-number"/>
			<updated>2023-11-24T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2023/11/24/largest-number</id>
			<content type="html">&lt;p&gt;A rewrite of this blog post with many new insights and updates appeared in
&lt;a href=&quot;https://tromp.github.io/blog/2026/01/28/largest-number-revised&quot;&gt;Jan 2026&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┬─┬─────────┬─┬─┬ ┬─┬──
└─┤ ──┬──── │ │ │ ┼─┼─┬
  │ ──┼─┬── │ │ │ │ ├─┘
  │ ┬─┼─┼─┬ │ │ │ ├─┘  
  │ └─┤ │ │ │ │ │ │    
  │   └─┤ │ │ │ │ │    
  │     ├─┘ │ │ │ │    
  └─────┤   │ │ │ │    
        └───┤ │ │ │    
            └─┤ │ │    
              └─┤ │    
                └─┘    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Most people believe 2&lt;sup&gt;64&lt;/sup&gt;-1 = 18446744073709551615, or 0xFFFFFFFFFFFFFFFF in hexadecimal,
to be the largest number representable in 64 bits. In English, it’s quite the mouthful:
eighteen quintillion four hundred forty-six quadrillion seven hundred forty-four
trillion seventy-three billion seven hundred nine million five hundred fifty-one
thousand six hundred fifteen.&lt;/p&gt;

&lt;p&gt;That is indeed the maximum possible value of 64 bit unsigned integers,
available as datatype uint64_t in C or u64 in Rust.
We can easily surpass this with floating point numbers. The 64-bit
&lt;a href=&quot;https://en.wikipedia.org/wiki/Double-precision_floating-point_format&quot;&gt;double floating point format&lt;/a&gt;
has a largest (finite) representable value of
2&lt;sup&gt;1024&lt;/sup&gt;(1-2&lt;sup&gt;-53&lt;/sup&gt;) ~ 1.8*10&lt;sup&gt;308&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;What if we allow representations beyond plain datatypes?
Such as a program small enough to fit in 64 bits.
For most programming languages, there is very little you can do in a mere  8 bytes.
In C that only leaves you with the nothingness of “main(){}”.&lt;/p&gt;

&lt;p&gt;But there are plenty languages that require no such scaffolding. For instance,
on Linux there is arbitrary precision calculator “bc”. It happily
computes the 954242 digit number 9^999999 = 35908462…48888889, which can thus
be said to be representable in 64 bits. Had it supported the symbol ! for computing factorials,
then 9!!!!!!! would make a much larger number representable in 64 bits.&lt;/p&gt;

&lt;p&gt;Allowing such primitives feels a bit like cheating though. Would we allow a
language that has the &lt;a href=&quot;https://en.wikipedia.org/wiki/Ackermann_function&quot;&gt;Ackerman function&lt;/a&gt;
predefined, which sports the 8 byte expression ack(9,9) representing a truly huge number?&lt;/p&gt;

&lt;h2 id=&quot;no-primitives-needed&quot;&gt;No primitives needed&lt;/h2&gt;

&lt;p&gt;As it turns out, the question is moot. There are simple languages with no built
in primitives. Not even basic arithmetic. Not even numbers themselves.
Languages in which all those must be defined from scratch. One such
language allows us to blow way past ack(9,9) in under 64 bits.&lt;/p&gt;

&lt;p&gt;But let’s first look at another such language, one that has been particularly well studied for
producing largest possible outputs. That is the language of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Turing_machine&quot;&gt;Turing machines&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;busy-beaver&quot;&gt;Busy Beaver&lt;/h2&gt;

&lt;p&gt;The famous &lt;a href=&quot;https://en.wikipedia.org/wiki/Busy_beaver&quot;&gt;Busy Beaver&lt;/a&gt;
 function, &lt;a href=&quot;https://archive.org/details/bstj41-3-877/mode/2up&quot;&gt;introduced&lt;/a&gt; by
&lt;a href=&quot;https://en.wikipedia.org/wiki/Tibor_Rad%C3%B3&quot;&gt;Tibor Radó&lt;/a&gt; in 1962, which we’ll
denote BB&lt;sub&gt;TM&lt;/sub&gt;(n), is defined as the maximum number of 1s that can be written with
an n state Turing machine starting from an all 0 tape before halting. Note that if we
consider this output as a number M written in binary, then it only gets
credited for its length, which is log&lt;sub&gt;2&lt;/sub&gt;(M+1).&lt;/p&gt;

&lt;p&gt;In 64 bits, one can fully specify a 6 state binary Turing machine, or TM for
short. For each of its internal states and each of its 2 tape symbols, one can
specify what new tape symbol it should write in the currently scanned tape
cell, whether to move the tape head left or right, and what new internal state,
or special halt state, to transition to. That takes 6*2*(2+⌈log2(6+1)⌉) = 60 bits.
Just how big an output can a 6 state TM produce?&lt;/p&gt;

&lt;p&gt;The best known result for 6 states is
&lt;a href=&quot;https://www.sligocki.com/2022/06/21/bb-6-2-t15.html&quot;&gt;BB&lt;sub&gt;TM&lt;/sub&gt;(6) &amp;gt; 10↑↑15&lt;/a&gt;,
which denotes an exponential tower of fifteen 10s. Clearly, in this notation there’s not that
much difference between a number and its size in bits.
Large as this number is, it’s still pathetically small compared to even
ack(5,5), which no known TM of less than 10 states—amounting to 110 bits of
description—can surpass.&lt;/p&gt;

&lt;p&gt;For that, we need to move beyond Turing machines, into the language of&lt;/p&gt;

&lt;h2 id=&quot;lambda-calculus&quot;&gt;Lambda Calculus&lt;/h2&gt;

&lt;p&gt;Alonzo Church conceived the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lambda_calculus&quot;&gt;λ-calculus&lt;/a&gt;
in about 1928 as a formal logic system for expressing
computation based on function abstraction and application using variable binding and substitution.&lt;/p&gt;

&lt;p&gt;A tiny 63 bit program in this language represents a number unfathomably larger than not only ack(9,9),
but the far larger &lt;a href=&quot;https://en.wikipedia.org/wiki/Graham%27s_number&quot;&gt;Graham’s Number&lt;/a&gt; as well.
It originates in a Code Golf challenge asking for the
“Shortest terminating program whose output size exceeds Graham’s number”,
&lt;a href=&quot;https://codegolf.stackexchange.com/questions/6430/shortest-terminating-program-whose-output-size-exceeds-grahams-number/219734#219734&quot;&gt;answered&lt;/a&gt;
by user &lt;a href=&quot;https://codegolf.stackexchange.com/users/101119/patcail&quot;&gt;Patcail&lt;/a&gt; and
&lt;a href=&quot;https://codegolf.stackexchange.com/questions/6430/shortest-terminating-program-whose-output-size-exceeds-grahams-number/219734#comment533337_219734&quot;&gt;further optimized&lt;/a&gt; by user
&lt;a href=&quot;https://codegolf.stackexchange.com/users/98257/2014melo03&quot;&gt;2014MELO03&lt;/a&gt;.
With one final optimization applied, the following 63 bit program&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;01 00 01 01 01 01 01 10 10 00 00 00 01 01 01 10 1110 110 10 10 10 10 00 00 01 110 01 110 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;is the &lt;a href=&quot;https://tromp.github.io/cl/cl.html&quot;&gt;Binary Lambda Calculus&lt;/a&gt; &lt;a href=&quot;https://gist.github.com/tromp/86b3184f852f65bfb814e3ab0987d861#lambda-encoding&quot;&gt;encoding&lt;/a&gt; of the term&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(λ 1 1 (λ λ λ 1 3 2 1) 1 1 1) (λ λ 2 (2 1))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;where λ (lambda) denotes an anonymous function, and number i is the variable bound by the i-th nested λ.
This is known as &lt;a href=&quot;https://en.wikipedia.org/wiki/De_Bruijn_notation&quot;&gt;De Bruijn notation&lt;/a&gt;, a
way to avoid naming variables. A more conventional notation using variable names would be&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(λt. t t (λh λf λn. n h f n) t t t) (λf λx. f (f x))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The top of this post shows a &lt;a href=&quot;https://tromp.github.io/cl/diagrams.html&quot;&gt;graphical representation&lt;/a&gt; of the term.
The last 16 bits of the program—making up more than a quarter of its size—encodes
the term λf λx. f (f x), which takes arguments f and x in turn, and iterates f twice on x.
In general, the function that iterates a given function n times on a given argument
is called Church numeral n, and is the standard way of representing numbers in the λ-calculus.
The program, which we’ll name after its underlying growth rate, can be expressed more legibly as&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wCubed = let { 2 = λf λx. f (f x); H = λh λf λn. n h f n } in 2 2 H 2 2 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The next section is mostly for the benefit of readers familiar with
&lt;a href=&quot;https://en.wikipedia.org/wiki/Ordinal_number&quot;&gt;ordinal&lt;/a&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Ordinal_arithmetic&quot;&gt;arithmetic&lt;/a&gt;,
and is probably better skipped by others.&lt;/p&gt;

&lt;h2 id=&quot;proof-of-exceeding-grahams-number&quot;&gt;Proof of exceeding Graham’s Number&lt;/h2&gt;

&lt;p&gt;Following the great suggestion of Googology user “BMS is not well-founded”, let
us start by defining a wCubed-customized
&lt;a href=&quot;https://en.wikipedia.org/wiki/Fast-growing_hierarchy&quot;&gt;Fast-growing hierarchy&lt;/a&gt;, a family that
assigns, to each ordinal α, a function [α] (diverting from the usual f&lt;sub&gt;α&lt;/sub&gt;
notation for improved legibility) from natural numbers to natural numbers.
We’ll treat all numbers as Church Numerals, so we can write n f instead of the
usual f&lt;sup&gt;n&lt;/sup&gt; and write f n instead of f(n) as normally done in λ-calculus.&lt;/p&gt;

&lt;h3 id=&quot;definitions&quot;&gt;Definitions:&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;H h f n = n h f n&lt;/li&gt;
  &lt;li&gt;H2 = H 2&lt;/li&gt;
  &lt;li&gt;[0] n = 2 n = n&lt;sup&gt;2&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;[α+1] n = n 2 [α] n = H 2 [α] n&lt;/li&gt;
  &lt;li&gt;[ωα+ω] n = [ωα+n] n&lt;/li&gt;
  &lt;li&gt;[ω&lt;sup&gt;i+1&lt;/sup&gt;(α+1)] n = [ω&lt;sup&gt;i+1&lt;/sup&gt;α+ω&lt;sup&gt;i&lt;/sup&gt; n] n&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;lemmas&quot;&gt;Lemmas:&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;2 H 2 [ω i] n = H H2 [ω i] n =&lt;sup&gt;(Def. 1)&lt;/sup&gt; n H2 [ω i] n =&lt;sup&gt;(n x Def. 4)&lt;/sup&gt; [ω i+n] n =&lt;sup&gt;(Def. 5)&lt;/sup&gt; [ω(i+1)] n&lt;/li&gt;
  &lt;li&gt;3 H 2 [ω&lt;sup&gt;2&lt;/sup&gt;i] n = H (2 H 2) [ω&lt;sup&gt;2&lt;/sup&gt;i] n =&lt;sup&gt;(Def. 1)&lt;/sup&gt; n (2 H 2) [ω&lt;sup&gt;2&lt;/sup&gt;i] n =&lt;sup&gt;(n x Lemma 1)&lt;/sup&gt; [ω&lt;sup&gt;2&lt;/sup&gt;i+ω n] n =&lt;sup&gt;(Def. 6)&lt;/sup&gt; [ω&lt;sup&gt;2&lt;/sup&gt;(i+1)] n&lt;/li&gt;
  &lt;li&gt;4 H 2 [0] n = H (3 H 2) [0] n =&lt;sup&gt;(Def. 1)&lt;/sup&gt; n (3 H 2) [0] n =&lt;sup&gt;(n x Lemma 2)&lt;/sup&gt; [ω&lt;sup&gt;2&lt;/sup&gt;n] n =&lt;sup&gt;(Def 6)&lt;/sup&gt; [ω&lt;sup&gt;3&lt;/sup&gt;] n&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lemma 3 gives wCubed = 2 2 H 2 2 2 = 4 H 2 [0] 2 = [ω&lt;sup&gt;3&lt;/sup&gt;] 2. In comparison,
Graham’s number is known to be less than the much much smaller [ω+1] 64. As it
turns out, this proof becomes almost trivial in our custom hierarchy. We start
with defining Graham’s number as a Church numeral, exploiting the fact that in
&lt;a href=&quot;https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation&quot;&gt;Knuth’s up-arrow notation&lt;/a&gt;,
3 ↑ n = 3&lt;sup&gt;n&lt;/sup&gt; = upify (mult 3) n, and 3 ↑&lt;sup&gt;k+1&lt;/sup&gt; n = (3 ↑&lt;sup&gt;k&lt;/sup&gt;
)&lt;sup&gt;n-1&lt;/sup&gt; 3 = (3 ↑&lt;sup&gt;k&lt;/sup&gt; )&lt;sup&gt;n-1&lt;/sup&gt; (3 ↑&lt;sup&gt;k&lt;/sup&gt; 1) = (3 ↑&lt;sup&gt;k&lt;/sup&gt; )&lt;sup&gt;n&lt;/sup&gt; 1:&lt;/p&gt;

&lt;h3 id=&quot;definitions-1&quot;&gt;Definitions:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;mult a b f = a (b f)&lt;/li&gt;
  &lt;li&gt;upify f n = n f 1&lt;/li&gt;
  &lt;li&gt;g n = n upify (mult 3) 3&lt;/li&gt;
  &lt;li&gt;Graham = 64 g 4&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;lemmas-assuming-n--3&quot;&gt;Lemmas (assuming n ≥ 3):&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;times 3 n ≤ n&lt;sup&gt;2&lt;/sup&gt; = [0] n&lt;/li&gt;
  &lt;li&gt;upify [α] n = n [α] 1 &amp;lt; 2 n [α] 1 = [α+1] n&lt;/li&gt;
  &lt;li&gt;g n = n upify (times 3) 3 ≤&lt;sup&gt;(Lemma 1)&lt;/sup&gt; n upify [0] 3 &amp;lt;&lt;sup&gt;(Lemma 2)&lt;/sup&gt; f&lt;sub&gt;n&lt;/sub&gt; n = [ω] n&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By Lemma 3, Graham = 64 g 4 &amp;lt; 64 [ω] 64 = [ω+1] 64&lt;/p&gt;

&lt;h2 id=&quot;a-functional-busy-beaver&quot;&gt;A Functional Busy Beaver&lt;/h2&gt;

&lt;p&gt;Based on the λ-calculus, I recently added to OEIS a
&lt;a href=&quot;https://oeis.org/A333479&quot;&gt;functional Busy Beaver function&lt;/a&gt; BB&lt;sub&gt;λ&lt;/sub&gt; that,
besides greater simplicity, has the advantage of
measuring program size in bits rather than states. Note how, similar to BB&lt;sub&gt;TM&lt;/sub&gt;(),
the value of BB&lt;sub&gt;λ&lt;/sub&gt;() is not the program output considered as a number itself, but
rather the output size. And in case of binary λ-calculus, the size of a Church numeral n is 5n+6.
The first unknown BB&lt;sub&gt;TM&lt;/sub&gt; is at 5 states, while the first unknown BB&lt;sub&gt;λ&lt;/sub&gt; is at 37 bits.&lt;/p&gt;

&lt;p&gt;The growth rates of the two BB functions may be compared by how quickly they exceed
that most famous of large numbers: Graham’s number.
The current best effort for BB&lt;sub&gt;TM&lt;/sub&gt;, after many rounds of optimization,
is &lt;a href=&quot;https://googology.fandom.com/wiki/Busy_beaver_function#Small_values&quot;&gt;stuck at 16 states&lt;/a&gt;,
weighing in at over 16*2*(2+4) = 192 bits. That compares rather unfavorably with our 63 bits.&lt;/p&gt;

&lt;p&gt;The existence of a &lt;a href=&quot;https://mathoverflow.net/questions/353514/whats-the-smallest-lambda-calculus-term-not-known-to-have-a-normal-form&quot;&gt;29 bit Ackermann-like function&lt;/a&gt;
and a &lt;a href=&quot;https://github.com/tromp/AIT/blob/master/fast_growing_and_conjectures/E0.lam&quot;&gt;79 bit function&lt;/a&gt;
growing too fast to be provably total in Peano Arithmetic,
also have no parallels in the realm of Turing machines, suggesting that the λ-calculus exhibits faster growth.&lt;/p&gt;

&lt;p&gt;It further enjoys massive advantages in programmability.
Modern high level pure functional languages like &lt;a href=&quot;https://www.haskell.org/&quot;&gt;Haskell&lt;/a&gt;
are essentially just syntactically sugared λ-calculus,
with programmer friendly features like &lt;a href=&quot;https://en.wikipedia.org/wiki/Algebraic_data_type&quot;&gt;Algebraic Data Types&lt;/a&gt;
translating directly through &lt;a href=&quot;https://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding&quot;&gt;Scott encodings&lt;/a&gt;.
The &lt;a href=&quot;https://bruijn.marvinborner.de/&quot;&gt;bruijn programming language&lt;/a&gt; is an even
thinner layer of syntactic sugar for the pure untyped lambda calculus, whose
extensive &lt;a href=&quot;https://bruijn.marvinborner.de/std/&quot;&gt;standard library&lt;/a&gt; contains many
datatypes and functions.
It is this excellent programmability of the λ-calculus that facilitated the creation of wCubed.&lt;/p&gt;

&lt;p&gt;In contrast, programming a Turing machine has been called impossibly tedious,
which explains why people have resorted to implementing higher level languages like
&lt;a href=&quot;https://github.com/sorear/metamath-turing-machines&quot;&gt;Not-Quite-Laconic&lt;/a&gt;
for writing nontrivial programs that don’t waste too many states.&lt;/p&gt;

&lt;p&gt;In his paper &lt;a href=&quot;https://scottaaronson.com/papers/bb.pdf&quot;&gt;The Busy Beaver Frontier&lt;/a&gt;, &lt;a href=&quot;https://scottaaronson.com/&quot;&gt;Scott Aaronson&lt;/a&gt; tries to answer the question&lt;/p&gt;

&lt;h2 id=&quot;but-why-turing-machines&quot;&gt;But why Turing machines?&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;For all their historic importance, haven’t Turing machines been completely superseded
by better alternatives—whether stylized assembly languages or various codegolf languages or Lisp?
As we’ll see, there is a reason why Turing machines were a slightly unfortunate choice
for the Busy Beaver game: namely, the loss incurred when we encode a state transition table
by a string of bits or vice versa.
But Turing machines also turn out to have a massive advantage that compensates for this.
Namely, because Turing machines have no “syntax” to speak of, but only graph structure,
we immediately start seeing interesting behavior even with machines of only 3, 4, or 5 states,
which are feasible to enumerate.
And there’s a second advantage. Precisely because the Turing machine model is so ancient and fixed,
whatever emergent behavior we find in the Busy Beaver game, there can be no suspicion that
we “cheated” by changing the model until we got the results we wanted.
In short, the Busy Beaver game seems like about as good a yardstick as any for gauging humanity’s
progress against the uncomputable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The claimed advantages for the “slightly unfortunate choice” do not hold over that even more
ancient model of the λ-calculus, while the latter’s relatively straightforward binary encoding make
it a preferable yardstick for exploring the limits of computation. The real question then is
“Why not λ-calculus?”, the answer to which appears to be rooted in historical accident more than anything.&lt;/p&gt;

&lt;h2 id=&quot;a-universal-busy-beaver&quot;&gt;A Universal Busy Beaver&lt;/h2&gt;

&lt;p&gt;Is BB&lt;sub&gt;λ&lt;/sub&gt; then an ideal Busy Beaver function (apart from a historical lack of study)?
Not quite. It’s still lacking one desirable property, namely universality.&lt;/p&gt;

&lt;p&gt;This property mirrors a notion of optimality for shortest description lengths, where it’s known
as the &lt;a href=&quot;https://en.wikipedia.org/wiki/Kolmogorov_complexity#Invariance_theorem&quot;&gt;Invariance theorem&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Given any description language L, the optimal description language is at least as efficient as L, with some constant overhead.&lt;/p&gt;

&lt;p&gt;In the realm of beavers, this means that given any Busy Beaver function BB
(based on self-delimiting programs), an optimal Busy Beaver surpasses it with
at most constant lag:&lt;/p&gt;

&lt;p&gt;for some constant c depending on BB, and for all n: BB&lt;sub&gt;opt&lt;/sub&gt;(n+c) ≥ BB(n)&lt;/p&gt;

&lt;p&gt;While BB&lt;sub&gt;λ&lt;/sub&gt; is not universal, it’s not far from one either.
By giving λ-calculus terms access to pure binary data, as in the Binary Lambda Calculus,
function &lt;a href=&quot;https://oeis.org/A361211&quot;&gt;BB&lt;sub&gt;BLC&lt;/sub&gt;&lt;/a&gt; achieves universality
while lagging only 2 bits behind BB&lt;sub&gt;λ&lt;/sub&gt;.
It’s known to eventually outgrow the latter, but that could take thousands of bits.&lt;/p&gt;

&lt;p&gt;Besides having a somewhat more complicated definition, and being somewhat harder to analyze,
BB&lt;sub&gt;BLC&lt;/sub&gt; has one other downside: it doesn’t represent the ginormous wCubed in 64 bits…&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>John adds blog</title>
			<link href="http://tromp.github.io/blog/2023/11/20/blog-launched"/>
			<updated>2023-11-20T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2023/11/20/blog-launched</id>
			<content type="html">&lt;p&gt;Well. Finally got around to putting this blog site together. Using these &lt;a href=&quot;https://easyperf.net/guides/github-pages&quot;&gt;helpful instructions&lt;/a&gt; by &lt;a href=&quot;https://easyperf.net/&quot;&gt;Denis Bakhvalov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;View &lt;a href=&quot;http://tromp.github.io/blog/atom.xml&quot;&gt;my feed&lt;/a&gt;.&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>Flip Puzzles and Linear Algebra</title>
			<link href="http://tromp.github.io/blog/2021/12/18/flip-puzzles"/>
			<updated>2021-12-18T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2021/12/18/flip-puzzles</id>
			<content type="html">&lt;p&gt;This post discusses how to solve flip puzzles like Simon Tatham’s “Flip” [1] or David Johnson-Davies’s 16 LEDs puzzle [2], where each cell in a rectangular grid represents a light that can be either on or off, and the goal is to have all lights turned on.&lt;/p&gt;

&lt;p&gt;The challenge is that, while each light can be flipped by clicking on its cell (or pressing a button directly below the LED), this action also flips several other lights in a certain pattern.&lt;/p&gt;

&lt;p&gt;Because the LED puzzle uses diagonals through a cell as patterns, this decomposes the 16 LEDs puzzle into 2 disjoint 8 LEDs puzzles:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_ A _ B                 H _ I _
C _ D _                 _ J _ K
_ E _ F                 L _ M _
B _ G _                 _ N _ O
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;according to the light and dark squares in a checkerboard pattern. Let’s focus on the 8 LEDs puzzle on the left, with the 8 lights marked as A..G.
If we denote the corresponding switches in lowercase a..g, then the behavior of light A can be described as A = a + c + d + f, since pressing any of these 4 switches will flip light A, and the other switches have no effect on A. A switch takes on value 0 if not pressed, and 1 if pressed. Furthermore, since flipping twice is the same as not flipping, we add modulo 2, where 1 + 1 = 0. Mathematicians call this number system GF(2). The behavior of all 8 light can now be expressed in a single matrix equation&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; A   ( 1 0 1 1 0 1 0 )   a
 B   ( 0 1 0 1 1 0 0 )   b
 C   ( 1 0 1 0 1 0 1 )   c
 D = ( 1 1 0 1 1 1 0 )   d
 E   ( 0 1 1 1 1 0 1 )   e
 F   ( 1 0 0 1 0 1 1 )   f
 G   ( 0 0 1 0 1 1 1 )   g
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This matrix over GF(2) is not invertible; its rank is only 5, so the lights cannot be changed arbitrarily. By Gaussian elimination [3], one finds that switch f does the same as switches a+b+e, and button g the same as b+c+d. This means there’s no need to use these switches (f=g=0) and the matrix equation simplifies to&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;A   ( 1 0 1 1 0 )   a
B   ( 0 1 0 1 1 )   b
C = ( 1 0 1 0 1 )   c
D   ( 1 1 0 1 1 )   d
E   ( 0 1 1 1 1 )   e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Using Linear Algebra, we can invert the matrix [4] and express the switch values in terms of the light values:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;a   ( 0 1 0 1 0 )   A
b   ( 1 1 1 0 0 )   B
c = ( 0 1 0 0 1 )   C
d   ( 1 0 0 1 1 )   D
e   ( 0 0 1 1 1 )   E
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So if the puzzle has only lights A and E off, and we want to know what switches to press to flip these two lights and no others, then we compute the sum of the 1st and 5th column of the matrix:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0   ( 0 1 0 1 0 )   1
1   ( 1 1 1 0 0 )   0
1 = ( 0 1 0 0 1 )   0
0   ( 1 0 0 1 1 )   0
1   ( 0 0 1 1 1 )   1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;to find that switches b, c, and e must be pressed. Indeed:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;_ 0 _ 1       _ 1 _ 0       _ 0 _ 1         _ 1 _ 0
0 _ 1_        1 _ 0 _       1 _ 1 _         0 _ 0 _
_ 1 _ 0   +   _ 1 _ 0   +   _ 1 _ 0    =    _ 1 _ 0
1 _ 0 _       0 _ 1 _       1 _ 1 _         0 _ 0 _
   b             c             e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Happy flipping!&lt;/p&gt;

&lt;p&gt;[1] &lt;a href=&quot;https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/flip.html&quot;&gt;https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/flip.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] &lt;a href=&quot;http://www.technoblogy.com/show?3PO0&quot;&gt;http://www.technoblogy.com/show?3PO0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] &lt;a href=&quot;https://en.wikipedia.org/wiki/Gaussian_elimination&quot;&gt;https://en.wikipedia.org/wiki/Gaussian_elimination&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] &lt;a href=&quot;https://en.wikipedia.org/wiki/Gaussian_elimination#Finding_the_inverse_of_a_matrix&quot;&gt;https://en.wikipedia.org/wiki/Gaussian_elimination#Finding_the_inverse_of_a_matrix&lt;/a&gt;&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>SK numerals</title>
			<link href="http://tromp.github.io/blog/2021/11/05/sk-numerals"/>
			<updated>2021-11-05T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2021/11/05/sk-numerals</id>
			<content type="html">&lt;h2 id=&quot;church-numerals&quot;&gt;Church Numerals&lt;/h2&gt;

&lt;p&gt;Church numerals are the standard way of representing natural numbers in the lambda calculus. Cn, the Church numeral for n, iterates a given function n times on a given argument. So we have&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C0 f x = f⁰ x = x
C1 f x = f¹ x = f x
C2 f x = f² x = f (f x)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;etcetera. We can define a successor function “Csucc” which iterates a given function one more time than a given numeral does:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Csucc = λn.λf.λx. f (n f x)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;(λn.λf.λx. n f (f x) works equally well). Each Church numeral Cn is thus itself the n’th iterate of Csucc on C0:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Cn = Cn Csucc C0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The best thing about Church numerals is the ease of defining arithmetic operators. It is straightforward to verify that&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;add = λm.λn.λf.λx. m f (n f x)
mul = λm.λn.λf. m (n f)
pow = λm.λn. n m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;work as advertised.&lt;/p&gt;

&lt;p&gt;E.g. pow C2 C3 = C3 C2 = λf. C2 (C2 (C2 f)) = λf.((f²)²)² = λf.f⁸ = C8&lt;/p&gt;

&lt;p&gt;Much less straightforward is the predecessor operator, that takes C(n+1) to Cn and leaves C0 unchanged:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Cpred = λn.λf.λx. n(λg.λh. h(g f))(λu. x)(λu. u)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Wikipedia tries to explain its operation in detail.&lt;/p&gt;

&lt;p&gt;Even less straightforward is the following division operator that springs from the creative mind of Bertram Felgenhauer:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;div = λn.λm.λf.λx.n(λx.λf.f x)(λd.x)(n (λt.m(λx.λf.f x)(λc.f(c t))(λx.x))x)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;whose operation is illustrated in this Algorithmic Information Theory github repository.&lt;/p&gt;

&lt;h2 id=&quot;sk-combinatory-logic&quot;&gt;SK Combinatory Logic&lt;/h2&gt;

&lt;p&gt;Combinatory Logic is concerned with terms consisting of the 2 basic combinators&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;S = λx.λy.λz. x z (y z)
K = λx.λy. x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;combined through application. It turns out that any closed lambda term is equivalent to a combinator. For example, the identity combinator can be obtained as&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;I = S K K
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;since I x = S K K x = K x (K x) = x.&lt;/p&gt;

&lt;p&gt;While reading Stephen Wolfram’s latest book, I came across an interesting alternative to Church numerals for Combinatory Logic:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F0 = K
F1 = S K
F2 = S (S K)
F3 = S ( S (S K)))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;etcetera, so S iterated n times on K, or Fn = Cn S K.&lt;/p&gt;

&lt;p&gt;This takes economy to an extreme, by using the 2 primitive combinators S and K as the very building blocks of numbers:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F0 = K
Fsucc = S
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We have&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F0 x y = x
F(n+1) x y = S Fn x y = Fn y (x y)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;so that&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F1 x y = y
F2 x y = x y
F3 x y = y (x y)
F4 x y = x y (y (x y))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;etcetera, which can be shown by induction so satisfy the Fibonacci recurrence&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F(n+2) x y = (Fn x y) (F(n+1) x y)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;with sum replaced by application! Consequently, there are exactly fib(n) occurrences of variable y in Fn x y.&lt;/p&gt;

&lt;p&gt;In honor of this close connection we denote these SK numerals with the letter F . Curiously, F0 and F1 coincide with the standard representations of booleans&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;True = K
False = S K
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The big question, though, is whether they work as numerals. The ultimate test of that is the ability to convert them back into Church numerals. Wolfram’s book suggests a way to do that by applying an SK numeral to x and y that are both Church numerals, perhaps C3 and C2, which leads to all applications working as powers. For example, F3 C3 C2 = C2 (C3 C2) = C2 C8 = C256. And from that we could work our way back to the 3, resulting in a size 181 conversion combinator.&lt;/p&gt;

&lt;p&gt;A more elegant conversion can be obtained by defining predecessor and iszero operators, and using these to count down to zero&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Fpred = λn.λx.λy. n (K y) x
Fiszero = λn. n False I S K
F2C = λn. Fiszero n C0 (Csucc (F2C (pred n)))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We may check that&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Fpred (Fsucc n) = \x\y. S n (K y) x = \x\y. n x y = n
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;so Fpred F(n+1) = Fn for all n.&lt;/p&gt;

&lt;p&gt;Fiszero works as follows:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Fiszero F0 = F0 False I S K = False S K = K = True
Fiszero F(n+1) False I S K = S Fn False I S K = Fn I I S K = I S K = S K = False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While Fpred lets us define negative numbers as well:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;F-1 x y = Fpred F0 x y = F0 (K y) x = K y
F-2 x y = Fpred F-1 x y = F-1 (K y) x = K x
F-3 x y = Fpred F-2 x y = F-2 (K y) x = K (K y)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Fsucc doesn’t work on these, so they’re mostly useless.&lt;/p&gt;

&lt;p&gt;Using the optimal Y combinator S S K (S (K (S S (S (S S K)))) K), F2C is combinator of size 69, a huge improvement.&lt;/p&gt;

&lt;p&gt;But it turns out we can do better still, courtesy of good old Bertram:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pair = λx.λy. λp. p x y
F2C = λn.(λp. n p(p p) False) (pair (λf.λy.λx. pair f (Csucc y)) C0)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This size 58 combinator&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;S (S (K (S S (K (K (S K))))) (S S (K (S I I)))) (K (S (S I (K (S (K (S (K (S S (K (S (S (K S) K))))) K)) (S (K (S (K (S (K (S (K (S (K (S S (K K))) K)) (S (K K)))) S)) (S I))) K)))) (K (S K))))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;applies the SK numeral to x and y that are both pairs of some function and a church numeral. Applying one such pair to another results in the function applied to itself and the two numerals:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(pair f Cm) (pair f Cn) = (pair f Cn) f Cm = f f Cn Cm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;so all function f has to do is take the successor of the right Church numeral and wrap it back up into a pair. In the F2C definition above, p is a wrapped C0 and (p p) becomes a wrapped C1.&lt;/p&gt;

&lt;p&gt;And that wraps us our brief exploration of SK numerals.&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>A case for using soft total supply.</title>
			<link href="http://tromp.github.io/blog/2020/12/20/soft-supply"/>
			<updated>2020-12-20T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2020/12/20/soft-supply</id>
			<content type="html">&lt;p&gt;The cryptocurrency space features endless debates about the pros and cons of various possible emission curves. Prime among them is question of whether supply should be capped (finite) or uncapped (infinite). But is this really an essential difference?&lt;/p&gt;

&lt;p&gt;It doesn’t take much to change a finite supply into an infinite one. Bitcoin’s block reward drops from 1 satoshi to 0 satoshi somewhere in the year 2140, completing a supply of approximately 21 million bitcoin [1]. What if that final drop never happened?&lt;/p&gt;

&lt;p&gt;Bitcoin would continue emitting a 1 satoshi block reward in perpetuity, yielding an infinite supply. It would eventually reach 21 million + 1 bitcoin. Let’s see when that happens. We need to wait an additional 100 million blocks, which takes approximately 10⁸/6/24/365 = 1902 years.&lt;/p&gt;

&lt;p&gt;Would that make make Bitcoin any less hard a currency? I don’t think anyone would be willing to argue that. So what makes a currency hard, if not a capped supply? I think the answer is obvious. Eventual negligible inflation is what makes for hard currency. It is what distinguishes cryptocurrencies from fiat.&lt;/p&gt;

&lt;p&gt;How low should inflation be to be considered negligible? There are two related
measures for quantifying this. One is the yearly supply inflation rate,
commonly used for fiat currencies. The other is its inverse, stock-to-flow
ratio, the ratio between existing supply (stock) and the supply to be added in
the next year (flow). This measure is popular for commodities. As discussed
more extensively in [2],
bitcoin’s inflation rate fell below 2% in 2020, and will fall below that of
Gold in the next few years.&lt;/p&gt;

&lt;p&gt;As long as the block reward (flow) never increases, then just by having supply increase every year, the inflation rate will naturally fall towards zero. The technical term for this is “disinflationary”. The inflation rate never reaches 0, but it gets arbitrarily close.&lt;/p&gt;

&lt;p&gt;In cryptocurrency practice though, it doesn’t matter whether inflation has reached 0.1% or 0.01%. And that is because unlike Gold, it’s rather easy to lose cryptocurrency. Estimates for the fraction of all Bitcoin that is forever lost range from 15% to as much as 25%. People forget or lose passwords or take them to the grave. People accidentally erase wallet files. People accidentally send bitcoin to obsolete addresses, or straight into the void. People explicitly burn bitcoin. With newer coins, standards for recovery phrases, more mature software and hardware wallets, we can expect much lower loss percentages, but getting the yearly loss rate significantly below 1% will remain a challenge.&lt;/p&gt;

&lt;p&gt;This motivates a definition of the “soft” end of emission as the time when the inflation rate drops below 1%, and the soft (total) supply as the supply at the time. At this time, new supply more or less balances the inevitable ongoing loss of coins. After its 4th halving in 2024, bitcoin will reach the soft end of its emission with a soft supply of (15/16) * 21M = 19.6875M BTC. At the extreme end of slow emissions is Grin. Its pure linear emission of 1 Grin per second forever, will reach its soft supply of a century (100*365*24*60*60s) of Grin, only in 2119.&lt;/p&gt;

&lt;p&gt;This allows for a sensible supply comparison of nearly all coins. The only coins with uncapped soft supply are those with some explicit positive percentage growth rate, such as what fiat currencies aim for. I think there are only a handful of proof-of-stake coins that fall into that category. For all others, it gives a sensible measure of how far along a coin is in its emission. Using soft supply, a daily dollar issuance ranking like [3] would no longer need to show infinities for remaining supply of coins with a tail emission.&lt;/p&gt;

&lt;p&gt;[1] &lt;a href=&quot;https://medium.com/amberdata/why-the-bitcoin-supply-will-never-reach-21-million-7263e322de1&quot;&gt;https://medium.com/amberdata/why-the-bitcoin-supply-will-never-reach-21-million-7263e322de1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] &lt;a href=&quot;https://medium.com/the-capital/stock-to-flow-ratio-why-bitcoins-value-increases-after-each-halving-d08c23d46a08&quot;&gt;https://medium.com/the-capital/stock-to-flow-ratio-why-bitcoins-value-increases-after-each-halving-d08c23d46a08&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] &lt;a href=&quot;https://www.f2pool.com/coins&quot;&gt;https://www.f2pool.com/coins&lt;/a&gt;&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>Beyond the Hashcash Proof-of-Work (there’s more to mining than hashing)</title>
			<link href="http://tromp.github.io/blog/2015/09/07/beyond-hashcash"/>
			<updated>2015-09-07T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2015/09/07/beyond-hashcash</id>
			<content type="html">&lt;p&gt;Many people equate Proof of Work (PoW) with one particular instance of it.  It’s not hard to understand why. The Hashcash PoW is used not only in Bitcoin but in the vast majority of altcoins as well.&lt;/p&gt;

&lt;p&gt;In Hashcash, miners all compete to look for a so called `nonce’ which, if provided as input (together with other parts of a block header) to a hash function, yields an output that’s numerically small enough to claim the next block reward.&lt;/p&gt;

&lt;p&gt;Where most crypto currencies differ is in the choice of hash function; the Hashcash flavor as it were. Besides Bitcoin’s `vanilla’ flavor of SHA256, there is Litecoin’s scrypt, Cryptonote’s CryptoNight, Darkcoin’s X11, and many more. Most alternative flavors have the explicitly stated goal of reducing the performance gap between custom and commodity hardware, either by use of memory, or by sheer complexity.&lt;/p&gt;

&lt;p&gt;But miners are only part of the picture. Proofs of work must not only be found, but verified as well, by every single client, including smartphones and other devices with limited resources. In Hashcash, verification amounts to evaluating the hash function on the given nonce and comparing the output with the difficulty threshold. Which is exactly the same effort as a single proof attempt.&lt;/p&gt;

&lt;p&gt;Thus, in order to keep verification cheap, hash functions in Hashcash must restrict their resource usage as well. That’s why scrypt is configured to use only 128KB of memory.&lt;/p&gt;

&lt;p&gt;Non-Hashcash PoWs do not suffer this limitation; they are asymmetric, with verification much cheaper than proof attempt. The first such PoW is Primecoin, which finds chains of nearly doubled prime numbers. The most recent example is my Cuckoo Cycle PoW, which was presented at the BITCOIN’2015 workshop in January. The whitepaper can be found at github.com/tromp/cuckoo, which also hosts various implementations, as well as bounties for improving on them.&lt;/p&gt;

&lt;p&gt;In Cuckoo Cycle, proofs take the form of a length 42 cycle (loop) in a large random graph defined by some nonce. Imagine two countries, each with a billion cities, and imagine picking a billion border crossing roads that connect a random city in one country to a random city in the other country (the PoW actually uses a cheaply computed hash function to map the nonce, road number, and country to a city). We are asked if there is cycle of 42 roads visiting 42 different cities. If someone hands you a nonce and 42 road numbers, it is indeed easy to verify, requiring negligible time and memory.&lt;/p&gt;

&lt;p&gt;But finding such a cycle is no easy task. Note however, that a city that connects to one road only cannot be part of the solution, nor can that road. David Andersen pointed out that such dead-end roads can be repeatedly eliminated, using one bit of memory per road to remember if that road is useful, and two bits per city to count if there are zero, one, or multiple useful roads to that city.&lt;/p&gt;

&lt;p&gt;This process of computing counts for cities, and marking roads that lead to a city with count one as not useful, is the essence of Cuckoo Cycle mining and accounts for about 98% of the effort. It results in billions of random global memory accesses for reading and writing the counters. Consequently, about 2/3 of the runtime is memory latency, making this a low-power algorithm that keeps computers running cool.&lt;/p&gt;

&lt;p&gt;After a sufficient number of counting and marking rounds, so few useful roads remain that another algorithm, inspired by Cuckoo Hashing, can quickly identify cycles (re-using the memory for the no longer needed counters).&lt;/p&gt;

&lt;p&gt;Cuckoo Cycle has some downsides as well. First of all, proofs are large and will roughly triple the size of block headers. Secondly, it is very slow, taking the better part of a minute on a high end CPU (or GPU, which offer roughly the same speed) to look for a cycle among a billion roads.&lt;/p&gt;

&lt;p&gt;In order to give slower CPUs a (somewhat) fair chance to win, the block interval should be much longer than a single proof attempt, so the amount of memory Cuckoo Cycle can use is constrained by the choice of block interval length.&lt;/p&gt;

&lt;p&gt;These seem like reasonable compromises for an instantly verifiable memory bound PoW that is unique in being dominated by latency rather than computation. In that sense, mining Cuckoo Cycle is a form of ASIC mining where DRAM chips serve the application of randomly reading and writing billions of bits.&lt;/p&gt;

&lt;p&gt;When even phones charging overnight can mine without orders of magnitude loss in efficiency, not with a mindset of profitability but of playing the lottery, the mining hardware landscape will see vast expansion, benefiting adoption as well as decentralization.&lt;/p&gt;
</content>
		</entry>
	
		<entry>
			<title>Useless Darts Trivia</title>
			<link href="http://tromp.github.io/blog/2014/12/22/darts-trivia"/>
			<updated>2014-12-22T00:00:00+00:00</updated>
			<id>http://tromp.github.io/blog/2014/12/22/darts-trivia</id>
			<content type="html">&lt;h2 id=&quot;which-scores-are-finishes&quot;&gt;Which scores are finishes?&lt;/h2&gt;

&lt;p&gt;One can throw 36, 40, or 50 plus any triple up to 120, and these numbers are 0, 1, and 2 modulo 3 respectively. In a picture:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;           ... 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
triple+36       *           *           *
triple+40           *           *           *           *
triple+50               *           *           *           *           *           *           *

finishes:  ... 150 151 152 153 154 155 156 157 158     160 161         164         167         170
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;how-many-possible-9-darter-games-are-there&quot;&gt;How many possible 9-darter games are there?&lt;/h2&gt;

&lt;p&gt;This seems rather dificult to count until we start to distinguish cases based on final double and minimum of the first 8 darts. The 50,50 entry for instance is calculated as partitions of 501 - 50 - 8 * 50 = 51 into 0s, 1s, 4s, 7s, and 10s, with at least one 0:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(    8    )   (    8    )   (    8    )
(2 1 0 0 5) + (2 0 1 1 4) + (2 0 0 3 3) = 168 + 840 + 560 = 1568.


  \dbl     24    30    34    36    40    50
min\       --------------------------------
34                                       56
40                                      672
45                            8
48                           56
50                     56         672  1568
51                8         224
54               56         448
57          8    56          56
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Giving a grand total of 3944 possible 9-darters, as confirmed in this report, which also states the number of essentially different solutions (as a multi-set of first 8 dart scores) as 22.&lt;/p&gt;

&lt;p&gt;Note that a finishing double of e.g. 38 is not possible, as there is no way to partition 501-8*60-38 = -17 into the numbers -3, -6, -9, -10, -12, and -15.&lt;/p&gt;

&lt;p&gt;In practice you’ll only see 9-darters starting with two 180s, leaving a 141 finish. The number of ways to finish 141 is calculated similarly:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  \dbl     24    30    34    36    40    50
min\       --------------------------------
34                                        2
40                                        2
45                            2
48                            2
50                      2           2
51                2           2
54                2
57          2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s 20 ways.&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-most-impressive-9-darter&quot;&gt;What is the most impressive 9-darter?&lt;/h2&gt;

&lt;p&gt;That would be throwing three 167 “finishes” in a row, repeating triple 20, triple 19, and bulls eye. The bulls eye is harder to hit than any triple, and a 9-darter can include at most three of them. I will likely never witness that in my lifetime, but then who could have imagined all of these incredible rarities?&lt;/p&gt;

</content>
		</entry>
	

</feed>
