Add preview of 1984-2019
The 2020 winners are hinted it, but the source code is not there,
nor are the hints. Someone could look into the HTML and
see what was commented out, but that is not a problem because
WHO won is known and the name of their AWARD is known already.
Moreover a number of the winners have talked about what their
entirs do, so the commented out spoiler text is not a bit problem.
One will find HTML commented out with:
<!-- XXX remove when 2020 is ready
2020 links here
XXX remove when 2020 is ready -->
The "XXX remove when 2020 is ready" string is found at the
start and end comment blocks. Again, soneone could read
what was commented out, but that is OK since most of that
information is known or can be known. The following files
have such HTML comment blocks in them.
./winners.html
./years-spoiler.html
./years.html
In addition, ./all/summary.txt was modified to not
include the 2020 entries for the momemt.
The 2020 winner code AND write-ups are NOT part of this set.
p.s. The all/all.tar.bz2 does not contain the 2020 source code (yet?).
This commit is contained in:
1 parent
28efc67f5d
commit
c0663537cb
1683 files changed
+220064
No files matched your search
@@ -0,0 +1,207 @@
|
||||
#!/usr/bin/env make
|
||||
#
|
||||
# 2012 makefile
|
||||
#
|
||||
# This work by Landon Curt Noll, Simon Cooper, and Leonid A. Broukhis
|
||||
# is licensed under:
|
||||
#
|
||||
# Creative Commons Attribution-ShareAlike 3.0 Unported License.
|
||||
#
|
||||
# See: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
|
||||
################
|
||||
# tool locations
|
||||
################
|
||||
#
|
||||
SHELL= /bin/bash
|
||||
CP= cp
|
||||
CPP= cpp
|
||||
GUNZIP= gunzip
|
||||
LD= ld
|
||||
MAKE= make
|
||||
RM= rm
|
||||
SED= sed
|
||||
TAR= tar
|
||||
TRUE= true
|
||||
|
||||
# Set X11_LIBDIR to the directory where the X11 library resides
|
||||
#
|
||||
X11_LIBDIR= /usr/X11R6/lib
|
||||
|
||||
# Set X11_INCLUDEDIR to the directory where the X11 include files reside
|
||||
#
|
||||
X11_INCDIR= /usr/X11R6/include
|
||||
|
||||
# Compiler warnings
|
||||
#
|
||||
#CWARN=
|
||||
CWARN= -Wall -W
|
||||
#CWARN= -Wall -W -pedantic
|
||||
|
||||
# compiler standard
|
||||
#
|
||||
#CSTD=
|
||||
#CSTD= -ansi
|
||||
CSTD= -std=c99
|
||||
|
||||
# compiler bit architecture
|
||||
#
|
||||
# Some entries require 32-bitness:
|
||||
# ARCH= -m32
|
||||
#
|
||||
# Some entries require 64-bitess:
|
||||
# ARCH= -m64
|
||||
#
|
||||
# By default we assume nothing:
|
||||
#
|
||||
ARCH=
|
||||
|
||||
# optimization
|
||||
#
|
||||
# Most compiles will safely use -O2. Some can use only -O1 or -O.
|
||||
# A few compilers have broken optimizers or this entry make break
|
||||
# under those buggy optimizers and thus you may not want anything.
|
||||
#
|
||||
#OPT=
|
||||
#OPT= -O
|
||||
#OPT= -O1
|
||||
OPT= -O2
|
||||
#OPT= -O3
|
||||
|
||||
# Libraries needed to build
|
||||
#
|
||||
LIBS=
|
||||
|
||||
# default flags for ANSI C compilation
|
||||
#
|
||||
CFLAGS= ${CWARN} ${CSTD} ${ARCH} ${OPT} ${LIBS}
|
||||
|
||||
# ANSI compiler
|
||||
#
|
||||
# Set CC to the name of your ANSI compiler.
|
||||
#
|
||||
# Some entries seem to need gcc. If you have gcc, set
|
||||
# both CC and MAY_NEED_GCC to gcc.
|
||||
#
|
||||
# If you do not have gcc, set CC to the name of your ANSI compiler, and
|
||||
# set MAY_NEED_GCC to either ${CC} (and hope for the best) or to just :
|
||||
# to disable such programs.
|
||||
#
|
||||
CC= cc
|
||||
#CC=clang
|
||||
MAY_NEED_GCC= gcc
|
||||
|
||||
|
||||
##############################
|
||||
# Special flags for this entry
|
||||
##############################
|
||||
#
|
||||
ENTRY= tromp
|
||||
DATA= how13 primes.blc uni.blc uni8.Blc bf.Blc hw.bf deflate.Blc inflate.Blc \
|
||||
parse.Blc symbolic.Blc primes.pl
|
||||
ALT_OBJ=
|
||||
ALT_ENTRY=
|
||||
|
||||
|
||||
#################
|
||||
# build the entry
|
||||
#################
|
||||
#
|
||||
all: ${ENTRY} ${ENTRY}64 ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
${ENTRY}: ${ENTRY}.c
|
||||
${CC} ${CFLAGS} -m32 -DInt=int -DX=4 -DA=500000 $< -o $@
|
||||
|
||||
${ENTRY}64: ${ENTRY}.c
|
||||
${CC} ${CFLAGS} -m64 -DInt=long -DX=8 -DA=500000 $< -o $@
|
||||
|
||||
# alternative executable
|
||||
#
|
||||
alt: ${ALT_ENTRY}
|
||||
@${TRUE}
|
||||
|
||||
# data files
|
||||
#
|
||||
data: ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
|
||||
###############
|
||||
# utility rules
|
||||
###############
|
||||
#
|
||||
everything: all alt
|
||||
|
||||
clean:
|
||||
${RM} -f ${ENTRY}.o ${ENTRY}64.o ${ALT_OBJ}
|
||||
|
||||
clobber: clean
|
||||
${RM} -f ${ENTRY} ${ENTRY}64 ${ALT_ENTRY}
|
||||
|
||||
nuke: clobber
|
||||
@${TRUE}
|
||||
|
||||
dist_clean: nuke
|
||||
@${TRUE}
|
||||
|
||||
install:
|
||||
@echo "Surely you're joking Mr. Feynman!"
|
||||
@${TRUE}
|
||||
|
||||
# backwards compatibility
|
||||
#
|
||||
build: all
|
||||
@${TRUE}
|
||||
|
||||
|
||||
##################
|
||||
# 133t hacker rulz
|
||||
##################
|
||||
#
|
||||
love:
|
||||
@echo 'not war?'
|
||||
@${TRUE}
|
||||
|
||||
haste:
|
||||
$(MAKE) waste
|
||||
@${TRUE}
|
||||
|
||||
waste:
|
||||
@echo 'waste'
|
||||
@${TRUE}
|
||||
|
||||
easter_egg:
|
||||
@echo you expected to mis-understand this $${RANDOM} magic
|
||||
@echo chongo '<was here>' "/\\oo/\\"
|
||||
@echo Readers shall be disallowed from not being unable to partly misunderstand this partocular final echo.
|
||||
|
||||
# Understand the history of "I Am the Walrus" and
|
||||
# and in particular John Lennon's remarks on that
|
||||
# song and you might be confused about these next
|
||||
# rules in a different way. :-)
|
||||
#
|
||||
supernova: nuke
|
||||
@-if [ -r .code_anal ]; then \
|
||||
${RM} -f .code_anal_v3; \
|
||||
else \
|
||||
echo "You are not expected to understand this"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
deep_magic:
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --deep_magic 1c2c85c7a02c55d1add91967eca491d53c101dc1 --FNV1a_hash 256-bit "${ENTRY}"; \
|
||||
else \
|
||||
echo "Understand different"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
magic: deep_magic
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --mode 21701 --level 23209 --FNV1a_hash 256-bit "${ENTRY}"; \
|
||||
else \
|
||||
echo "These aren't the droids you're looking for Mr. Spock!"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,494 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta name="GENERATOR" content="mkd2html 2.1.5a DL=DISCOUNT">
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
|
||||
<link rel="stylesheet"
|
||||
type="text/css"
|
||||
href="../hint.css">
|
||||
<title>Most functional</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Most functional</h1>
|
||||
|
||||
<p>John Tromp<br>
|
||||
<a href="mailto:john.tromp@gmail.com">john.tromp@gmail.com</a><br>
|
||||
<a href="http://tromp.github.io/">http://tromp.github.io/</a></p>
|
||||
|
||||
<h2>Judges' comments:</h2>
|
||||
|
||||
<h3>To build:</h3>
|
||||
|
||||
<p>On a 32-bit machine:</p>
|
||||
|
||||
<pre><code>make tromp
|
||||
</code></pre>
|
||||
|
||||
<p>On a 64-bit machine:</p>
|
||||
|
||||
<pre><code>make tromp64
|
||||
# And mentally substitute ./tromp64 for ./tromp everywhere below
|
||||
</code></pre>
|
||||
|
||||
<h3>To run:</h3>
|
||||
|
||||
<pre><code>cat ascii-prog.blc data | ./tromp -b
|
||||
cat binary-prog.Blc data | ./tromp
|
||||
</code></pre>
|
||||
|
||||
<h3>Try:</h3>
|
||||
|
||||
<pre><code>(cat hilbert.Blc; echo -n 1234) | ./tromp
|
||||
(cat oddindices.Blc; echo; cat primes.blc | ./tromp -b) | ./tromp
|
||||
cat primes.blc | ./tromp -b | ./primes.pl
|
||||
</code></pre>
|
||||
|
||||
<h3>Selected Judges Remarks:</h3>
|
||||
|
||||
<p>The judges dare to say that the data files this entry is processing
|
||||
are more obfuscated than the entry itself.</p>
|
||||
|
||||
<h2>Author’s comments:</h2>
|
||||
|
||||
<p>This program celebrates the close connection between obfuscation and conciseness,
|
||||
by implementing the most concise language known, Binary Lambda Calculus (BLC).</p>
|
||||
|
||||
<p>BLC was developed to make Algorithmic Information Theory, the theory of smallest programs,
|
||||
more concrete. It starts with the simplest model of computation, the lambda calculus,
|
||||
and adds the minimum amount of machinery to enable binary input and output.</p>
|
||||
|
||||
<p>More specifically, it defines a universal machine, which, from an input stream of bits,
|
||||
parses the binary encoding of a lambda calculus term, applies that to the remainder
|
||||
of input (translated to a lazy list of booleans,
|
||||
which have a standard representation in lambda calculus),
|
||||
and translates the evaluated result back into a stream of bits to be output.</p>
|
||||
|
||||
<p>Lambda is encoded as 00, application as 01, and the variable bound by the n'th
|
||||
enclosing lambda (denoted n in so-called De Bruijn notation) as 1^{n}0.
|
||||
That’s all there is to BLC!</p>
|
||||
|
||||
<p>For example the encoding of lambda term S = \x \y \z (x z) (y z),
|
||||
with De Bruijn notation \ \ \ (3 1) (2 1),
|
||||
is 00 00 00 01 01 1110 10 01 110 10</p>
|
||||
|
||||
<p>In the closely related BLC8 language, IO is byte oriented,
|
||||
translating between a stream of bytes and a list of length-8 lists of booleans.</p>
|
||||
|
||||
<p>The submission implements the universal machine in the most concise manner conceivable.
|
||||
It lacks #defines and #includes, and compiles to a (stripped) executable of under 6K in size.</p>
|
||||
|
||||
<p>Without arguments, it runs in byte mode, using standard in- and output.
|
||||
With one (arbitrary) argument, it runs in bit mode, using only the least significant
|
||||
bit of input, and using characters ‘0’ and ‘1’ for output.</p>
|
||||
|
||||
<p>The program uses the following exit codes:
|
||||
0: OK; result is a finite list
|
||||
5: Out of term space
|
||||
6: Out of memory
|
||||
1,2,3,4,8,9: result not in list form</p>
|
||||
|
||||
<p>The size of the term space is fixed at compile time with -DA</p>
|
||||
|
||||
<h1>A half byte `cat'</h1>
|
||||
|
||||
<p>The shortest (closed) lambda calculus term is \x x (\ 1 in De Bruijn notation)
|
||||
which is the identity function. When its encoding 0010 is fed
|
||||
into the universal machine, it will simply copy the input to the output.
|
||||
(well, not that simply, since each byte is smashed to bits and rebuilt from scratch)
|
||||
Voila: a half byte cat:</p>
|
||||
|
||||
<pre><code> echo " Hello, world" | ./tromp
|
||||
Hello, world
|
||||
</code></pre>
|
||||
|
||||
<p>Since the least significant 4 bits of the first byte are just arbitrary padding
|
||||
that is ignored by the program,
|
||||
any character from ASCII 32 (space) through 47 (/) will do, e.g.:</p>
|
||||
|
||||
<pre><code> echo "*Hello, world" | ./tromp
|
||||
Hello, world
|
||||
</code></pre>
|
||||
|
||||
<h1>Bad programs</h1>
|
||||
|
||||
<p>If the input doesn’t start with a valid program, that is,
|
||||
if the interpreter reaches end-of-file during program parsing,
|
||||
it will crash in some way:</p>
|
||||
|
||||
<pre><code> echo -n "U" | ./tromp
|
||||
Segmentation fault
|
||||
</code></pre>
|
||||
|
||||
<p>Furthermore, the interpreter requires the initial encoded lambda term to be closed,
|
||||
that is, variable n can only appear within at least n enclosing lambdas.
|
||||
For instance the term \ 5 is not closed,
|
||||
causing the interpreter to crash when looking into a null-pointer environment:</p>
|
||||
|
||||
<pre><code> echo ">Hello, world" | ./tromp
|
||||
Segmentation fault
|
||||
</code></pre>
|
||||
|
||||
<p>Since these properties can be checked when creating BLC programs,
|
||||
the interpreter doesn’t bother checking for it.</p>
|
||||
|
||||
<h1>A Self Interpreter</h1>
|
||||
|
||||
<p>The BLC universal machine may be small at 650 bytes of C
|
||||
(952 bytes including layout), but written
|
||||
as a self interpreter in BLC it is downright minuscule at 232 bits (29 bytes):</p>
|
||||
|
||||
<pre><code> 01010001
|
||||
10100000
|
||||
00010101
|
||||
10000000
|
||||
00011110
|
||||
00010111
|
||||
11100111
|
||||
10000101
|
||||
11001111
|
||||
000000111
|
||||
10000101101
|
||||
1011100111110
|
||||
000111110000101
|
||||
11101001 11010010
|
||||
11001110 00011011
|
||||
00001011 11100001
|
||||
11110000 11100110
|
||||
11110111 11001111
|
||||
01110110 00011001
|
||||
00011010 00011010
|
||||
</code></pre>
|
||||
|
||||
<p>The byte oriented BLC8 version weighs in at 43 bytes (shown in hexadecimal).</p>
|
||||
|
||||
<pre><code> 19468
|
||||
05580
|
||||
05f00
|
||||
bfe5f
|
||||
85f3f
|
||||
03c2d
|
||||
b9fc3f8
|
||||
5e9d65e5f
|
||||
0decb f0fc3
|
||||
9befe 185f7
|
||||
0b7fb 00cf6
|
||||
7bb03 91a1a
|
||||
|
||||
(cat uni8.Blc; echo " Ni hao") | ./tromp
|
||||
Ni hao
|
||||
</code></pre>
|
||||
|
||||
<h1>A prime number sieve</h1>
|
||||
|
||||
<p>Even shorter than the self-interpreter is this prime number sieve in 167 bits (under 21 bytes):</p>
|
||||
|
||||
<pre><code>000100011001100101000110100
|
||||
000000101100000100100010101
|
||||
11110111 101001000
|
||||
11010000 111001101
|
||||
000000000010110111001110011
|
||||
11111011110000000011111001
|
||||
10111000
|
||||
00010110
|
||||
0000110110
|
||||
</code></pre>
|
||||
|
||||
<p>The n'th bit in the output indicates whether n is prime:</p>
|
||||
|
||||
<pre><code> cat primes.blc | ./tromp -b | head -c 70
|
||||
0011010100010100010100010000010100000100010100010000010000010100000100
|
||||
</code></pre>
|
||||
|
||||
<p>For those who prefer to digest their primes in decimal, there is oddindices.Blc,
|
||||
which will print the indices of all odd characters (with lsb = 1)
|
||||
separated by a given character:</p>
|
||||
|
||||
<pre><code> (cat oddindices.Blc; echo -n " "; cat primes.blc | ./tromp -b) | ./tromp | head -c 70
|
||||
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
|
||||
</code></pre>
|
||||
|
||||
<h1>A Space filling program</h1>
|
||||
|
||||
<p>Program hilbert.Blc, at 143 bytes, is a very twisty “one-liner” (shown in hexadecimal):</p>
|
||||
|
||||
<pre><code>1818181 8111154 6806041 55ff041
|
||||
9d f9 de 16 ff fe 5f 3f
|
||||
ef f615ff9 46 84 058117e 05
|
||||
cb fe bc bf
|
||||
ee86cb9 4681600 5c0bfac bfbf71a
|
||||
85 e0 5c f4
|
||||
14d5fe0 8180b048d0800e078 016445f
|
||||
fe 5f
|
||||
f7 ffffe5fff2fc 02f7ad97f5bf ff
|
||||
ff bf ff ca af ff
|
||||
7817ffa df76695 4680601 57f7e16
|
||||
05 c1
|
||||
3fe80b2 2c18581 bfe5c10 42ff805
|
||||
de ec 06 c2 c0 c0
|
||||
60 8191a00167fb cbcfdf65f7c0 a20
|
||||
</code></pre>
|
||||
|
||||
<p>It expects n arbitrary characters of input, and draws a space filling Hilbert curve of order n:</p>
|
||||
|
||||
<pre><code> (cat hilbert.Blc; echo -n "1") | ./tromp
|
||||
_
|
||||
| |
|
||||
|
||||
(cat hilbert.Blc; echo -n "12") | ./tromp
|
||||
_ _
|
||||
| |_| |
|
||||
|_ _|
|
||||
_| |_
|
||||
|
||||
(cat hilbert.Blc; echo -n "123") | ./tromp
|
||||
_ _ _ _
|
||||
| |_| | | |_| |
|
||||
|_ _| |_ _|
|
||||
_| |_____| |_
|
||||
| ___ ___ |
|
||||
|_| _| |_ |_|
|
||||
_ |_ _| _
|
||||
| |___| |___| |
|
||||
|
||||
(cat hilbert.Blc; echo -n "1234") | ./tromp
|
||||
_ _ _ _ _ _ _ _
|
||||
| |_| | | |_| | | |_| | | |_| |
|
||||
|_ _| |_ _| |_ _| |_ _|
|
||||
_| |_____| |_ _| |_____| |_
|
||||
| ___ ___ | | ___ ___ |
|
||||
|_| _| |_ |_| |_| _| |_ |_|
|
||||
_ |_ _| _ _ |_ _| _
|
||||
| |___| |___| |_| |___| |___| |
|
||||
|_ ___ ___ ___ ___ _|
|
||||
_| |_ |_| _| |_ |_| _| |_
|
||||
| _ | _ |_ _| _ | _ |
|
||||
|_| |_| | |___| |___| | |_| |_|
|
||||
_ _ | ___ ___ | _ _
|
||||
| |_| | |_| _| |_ |_| | |_| |
|
||||
|_ _| _ |_ _| _ |_ _|
|
||||
_| |___| |___| |___| |___| |_
|
||||
</code></pre>
|
||||
|
||||
<h1>A BrainFuck interpreter</h1>
|
||||
|
||||
<p>The smallest known BF interpreter is written in… you guessed it, BLC,
|
||||
coming in at 112 bytes (including 3 bits of padding):</p>
|
||||
|
||||
<pre><code> od -t x4 bf.Blc
|
||||
0000000 01a15144 02d55584 223070b7 00f032ff
|
||||
0000020 7f85f9bf 956fe15e c0ee7d7f 006854e5
|
||||
0000040 fbfd5558 fd5745e0 b6f0fbeb 07d62ff0
|
||||
0000060 d7736fe1 c0bc14f1 1f2eff0b 17666fa1
|
||||
0000100 2fef5be8 ff13ffcf 2034cae1 0bd0c80a
|
||||
0000120 e51fee99 6a5a7fff ff0fff1f d0049d87
|
||||
0000140 db0500ab 3bb74023 b0c0cc28 10740e6c
|
||||
0000160
|
||||
</code></pre>
|
||||
|
||||
<p>It expects its input to consist of a Brainfuck program
|
||||
(looking only at bits 0,1,4 to distinguish among ,-.+<>][ )
|
||||
followed by a ], followed by the input for the Brainfuck program.</p>
|
||||
|
||||
<pre><code> more hw.bf
|
||||
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.]
|
||||
cat bf.Blc hw.bf | ./tromp
|
||||
Hello World!
|
||||
</code></pre>
|
||||
|
||||
<p>Curiously, the interpreter bf.Blc is the exact same size as hw.bf.</p>
|
||||
|
||||
<h1>A BLC assembler</h1>
|
||||
|
||||
<p>Writing BLC programs can be made slightly less painful with this parser that
|
||||
translates single-letter-variable lambda calculus into BLC:</p>
|
||||
|
||||
<pre><code> echo "\f\x f (f (f x))" > three
|
||||
cat parse.Blc three | ./tromp
|
||||
000001110011100111010
|
||||
</code></pre>
|
||||
|
||||
<h1>Converting between bits and bytes</h1>
|
||||
|
||||
<p>THe program inflate.Blc and its inverse deflate.Blc allow us to translate between
|
||||
BLC and BLC8.
|
||||
If you assemble a byte oriented program, you’ll need to compact it into BLC8:</p>
|
||||
|
||||
<p>So we could assemble an input reversing program as</p>
|
||||
|
||||
<pre><code> echo "\a a ((\b b b) (\b \c \d \e d (b b) (\f f c e))) (\b \c c)" > reverse
|
||||
cat parse.Blc reverse | ./tromp > reverse.blc
|
||||
</code></pre>
|
||||
|
||||
<p>and change it to BLC8 with</p>
|
||||
|
||||
<pre><code> cat deflate.Blc reverse.blc | ./tromp > rev.Blc
|
||||
wc rev.Blc
|
||||
0 1 9 rev.Blc
|
||||
</code></pre>
|
||||
|
||||
<p>and then try it out with:</p>
|
||||
|
||||
<pre><code> cat rev.Blc - | ./tromp
|
||||
Hello, world!
|
||||
^D
|
||||
!dlrow ,olleH
|
||||
</code></pre>
|
||||
|
||||
<h1>Symbolic Lambda Calculus reduction </h1>
|
||||
|
||||
<p>BLC8 program symbolic.Blc shows individual reduction steps on symbolic lambda terms.
|
||||
Here it is used to show the calculation of 2<sup>3</sup> in Church numerals:</p>
|
||||
|
||||
<pre><code> echo "(\f\x f (f (f x))) (\f\x f (f x))" > threetwo
|
||||
cat parse.Blc threetwo | ./tromp > threetwo.blc
|
||||
cat symbolic.Blc threetwo.blc | ./tromp
|
||||
(\a \b a (a (a b))) (\a \b a (a b))
|
||||
\a (\b \c b (b c)) ((\b \c b (b c)) ((\b \c b (b c)) a))
|
||||
\a \b (\c \d c (c d)) ((\c \d c (c d)) a) ((\c \d c (c d)) ((\c \d c (c d)) a) b)
|
||||
\a \b (\c (\d \e d (d e)) a ((\d \e d (d e)) a c)) ((\c \d c (c d)) ((\c \d c (c d)) a) b)
|
||||
\a \b (\c \d c (c d)) a ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b))
|
||||
\a \b (\c a (a c)) ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b))
|
||||
\a \b a (a ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b)))
|
||||
\a \b a (a ((\c a (a c)) ((\c \d c (c d)) ((\c \d c (c d)) a) b)))
|
||||
\a \b a (a (a (a ((\c \d c (c d)) ((\c \d c (c d)) a) b))))
|
||||
\a \b a (a (a (a ((\c (\d \e d (d e)) a ((\d \e d (d e)) a c)) b))))
|
||||
\a \b a (a (a (a ((\c \d c (c d)) a ((\c \d c (c d)) a b)))))
|
||||
\a \b a (a (a (a ((\c a (a c)) ((\c \d c (c d)) a b)))))
|
||||
\a \b a (a (a (a (a (a ((\c \d c (c d)) a b))))))
|
||||
\a \b a (a (a (a (a (a ((\c a (a c)) b))))))
|
||||
\a \b a (a (a (a (a (a (a (a b)))))))
|
||||
</code></pre>
|
||||
|
||||
<p>As expected, the resulting normal form is Church numeral 8.</p>
|
||||
|
||||
<p>Taking only the first line of output gives us a sort of BLC disassembler,
|
||||
an exact inverse of the above assembler. The prime number sieve disassembles as follows:</p>
|
||||
|
||||
<pre><code> cat symbolic.Blc primes.blc | ./tromp | head -1
|
||||
\a (\b b (b ((\c c c) (\c \d \e e (\f \g g) ((\f c c f ((\g g g) (\g f (g g)))) (\f \g \h \i i g (h (d f))))) (\c \d \e b (e c))))) (\b \c c (\d \e d) b)
|
||||
</code></pre>
|
||||
|
||||
<p>Hardly any less obfuscated…</p>
|
||||
|
||||
<p>The last line of
|
||||
cat symbolic.Blc primes.blc | ./tromp | head -16
|
||||
starts out as
|
||||
\a \b b (\c \d c) (\c c (\d \e d) (\d d (\e \f f) (\e e (\f \g g) ((\f (\g \h \i</p>
|
||||
|
||||
<p>The \a is for ignoring the rest of the input (to which the universal machine
|
||||
applies the decoded lambda term). The \b b (..) (…) is the list constructor,
|
||||
usually called cons, applied to a head (a list element) and a tail (another list).
|
||||
In this case the element is (\c \d c), which represents the boolean true, and
|
||||
which we use to represent a 0 bit. This is the bit that says 0 is not prime.
|
||||
The next list element (following another cons) is (\d \e d). Another 0 bit,
|
||||
this time saying that 1 is not prime. The third list element is (\e \f f),
|
||||
a 1 bit, confirming our suspicion that 2 is prime. As is the next number,
|
||||
according to (\f \g g). We can see that the tail after the first 4 elements
|
||||
is still subject to further reduction. The bit for number 4 will show up
|
||||
for the first time in line 30, as (\g \h g), or 0, as the result of zeroing
|
||||
out all multiples of the first prime, 2.
|
||||
Since my computer reaches swap hell before line 40, we can’t see the next bit arriving,
|
||||
at least not in this symbolic reduction.</p>
|
||||
|
||||
<h1>Performance</h1>
|
||||
|
||||
<p>Performance is quite decent, and amazingly good for such a tiny implementation,
|
||||
being roughly 50% slower than a Haskell implementation of the universal machine
|
||||
using so-called Higher Order Abstract Syntax which relies on the highly optimized
|
||||
Haskell runtime system for evaluation. Of course individual blc programs running
|
||||
under the interpreter perform much worse than that same program written in Haskell.</p>
|
||||
|
||||
<p>Our interpreter copes well with extra levels of interpretation:</p>
|
||||
|
||||
<pre><code> time cat primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m0.043s
|
||||
time cat uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m0.191s
|
||||
time cat uni.blc uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m1.919s
|
||||
time cat uni.blc uni.blc uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m23.514s
|
||||
time cat uni.blc uni.blc uni.blc uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 4m52.700s
|
||||
</code></pre>
|
||||
|
||||
<h1>Obfuscation</h1>
|
||||
|
||||
<p>Obfuscation is due entirely to conciseness. Some questions to ponder:</p>
|
||||
|
||||
<p>Which of the term space codes 0,1,2,3 serves multiple purposes?</p>
|
||||
|
||||
<p>Why is the environment pointer pointing into the term space?</p>
|
||||
|
||||
<p>What does the test u+m&1? do?</p>
|
||||
|
||||
<p>How does the program reach exit code 0?</p>
|
||||
|
||||
<p>And how do any of those blc programs work?</p>
|
||||
|
||||
<h1>Portability</h1>
|
||||
|
||||
<p>The program freely (without casting) converts between int and int*, causing
|
||||
many warnings;
|
||||
note: expected ‘int *’ but argument is of type ‘int’
|
||||
warning: assignment from incompatible pointer type
|
||||
warning: assignment makes integer from pointer without a cast
|
||||
warning: assignment makes pointer from integer without a cast
|
||||
warning: incompatible implicit declaration of built-in function ‘calloc’
|
||||
warning: incompatible implicit declaration of built-in function ‘exit’
|
||||
warning: passing argument 1 of ‘d’ makes pointer from integer without a cast
|
||||
warning: passing argument 1 of ‘p’ makes pointer from integer without a cast
|
||||
warning: pointer/integer type mismatch in conditional expression</p>
|
||||
|
||||
<p>Avoiding these would make the program substantially longer,
|
||||
and detract from its single minded focus on conciseness.</p>
|
||||
|
||||
<p>It implicitly declares functions read, write, exit and calloc, the latter two incompatibly.
|
||||
32 bit and 64 bit executables are separate Makefile targets, involving a change
|
||||
from int to long and from a hardcoded sizeof of 4 to 8.</p>
|
||||
|
||||
<p>The program has been tested to work correctly on Linux/Solaris/MacOSX both in 32 and 64 bits.</p>
|
||||
|
||||
<h1>How the program works</h1>
|
||||
|
||||
<p>See the file how13.</p>
|
||||
|
||||
<h1>Acknowledgements</h1>
|
||||
|
||||
<p>Christopher Hendrie, Bertram Felgenhauer, Alex Stangl, Seong-hoon Kang,
|
||||
and Yusuke Endoh have contributed ideas and suggestions for improvement.</p>
|
||||
|
||||
<h1>References</h1>
|
||||
|
||||
<p>Binary Lambda Calculus
|
||||
<a href="https://tromp.github.io/cl/Binary_lambda_calculus.html">https://tromp.github.io/cl/Binary_lambda_calculus.html</a></p>
|
||||
|
||||
<p>G J Chaitin, Algorithmic information theory, Volume I,
|
||||
Cambridge Tracts in Theoretical Computer Science, Cambridge University Press, October 1987.
|
||||
<a href="http://www.cs.auckland.ac.nz/~chaitin/cup.html">http://www.cs.auckland.ac.nz/~chaitin/cup.html</a></p>
|
||||
|
||||
<p>Jean-Louis Krivine. 2007.
|
||||
A call-by-name lambda-calculus machine
|
||||
Higher Order Symbol. Comput. 20, 3 (September 2007), 199-207.
|
||||
<a href="http://www.pps.univ-paris-diderot.fr/~krivine/articles/lazymach.pdf">http://www.pps.univ-paris-diderot.fr/~krivine/articles/lazymach.pdf</a></p>
|
||||
|
||||
<hr />
|
||||
|
||||
<!--
|
||||
(c) Copyright 1984-2015, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
|
||||
This work is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License][cc].
|
||||
|
||||
[judges]: http://www.ioccc.org/judges.html
|
||||
[cc]: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
-->
|
||||
|
||||
<TABLE><TR>
|
||||
<TD><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/88x31.png" /></a></TD>
|
||||
<TD><P>© Copyright 1984-2015,
|
||||
<A HREF="/judges.html">Leo Broukhis, Simon Cooper, Landon Curt Noll</A>
|
||||
- All rights reserved<BR>
|
||||
This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</P></TD>
|
||||
<TD> <!--<a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-html401" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a>--></TD>
|
||||
</TR></TABLE>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,461 @@
|
||||
# Most functional
|
||||
|
||||
John Tromp
|
||||
<john.tromp@gmail.com>
|
||||
<http://tromp.github.io/>
|
||||
|
||||
|
||||
## Judges' comments:
|
||||
### To build:
|
||||
|
||||
On a 32-bit machine:
|
||||
|
||||
make tromp
|
||||
|
||||
On a 64-bit machine:
|
||||
|
||||
make tromp64
|
||||
# And mentally substitute ./tromp64 for ./tromp everywhere below
|
||||
|
||||
### To run:
|
||||
|
||||
cat ascii-prog.blc data | ./tromp -b
|
||||
cat binary-prog.Blc data | ./tromp
|
||||
|
||||
### Try:
|
||||
|
||||
(cat hilbert.Blc; echo -n 1234) | ./tromp
|
||||
(cat oddindices.Blc; echo; cat primes.blc | ./tromp -b) | ./tromp
|
||||
cat primes.blc | ./tromp -b | ./primes.pl
|
||||
|
||||
### Selected Judges Remarks:
|
||||
|
||||
The judges dare to say that the data files this entry is processing
|
||||
are more obfuscated than the entry itself.
|
||||
|
||||
## Author's comments:
|
||||
This program celebrates the close connection between obfuscation and conciseness,
|
||||
by implementing the most concise language known, Binary Lambda Calculus (BLC).
|
||||
|
||||
BLC was developed to make Algorithmic Information Theory, the theory of smallest programs,
|
||||
more concrete. It starts with the simplest model of computation, the lambda calculus,
|
||||
and adds the minimum amount of machinery to enable binary input and output.
|
||||
|
||||
More specifically, it defines a universal machine, which, from an input stream of bits,
|
||||
parses the binary encoding of a lambda calculus term, applies that to the remainder
|
||||
of input (translated to a lazy list of booleans,
|
||||
which have a standard representation in lambda calculus),
|
||||
and translates the evaluated result back into a stream of bits to be output.
|
||||
|
||||
Lambda is encoded as 00, application as 01, and the variable bound by the n'th
|
||||
enclosing lambda (denoted n in so-called De Bruijn notation) as 1^{n}0.
|
||||
That's all there is to BLC!
|
||||
|
||||
For example the encoding of lambda term S = \x \y \z (x z) (y z),
|
||||
with De Bruijn notation \ \ \ (3 1) (2 1),
|
||||
is 00 00 00 01 01 1110 10 01 110 10
|
||||
|
||||
In the closely related BLC8 language, IO is byte oriented,
|
||||
translating between a stream of bytes and a list of length-8 lists of booleans.
|
||||
|
||||
The submission implements the universal machine in the most concise manner conceivable.
|
||||
It lacks #defines and #includes, and compiles to a (stripped) executable of under 6K in size.
|
||||
|
||||
Without arguments, it runs in byte mode, using standard in- and output.
|
||||
With one (arbitrary) argument, it runs in bit mode, using only the least significant
|
||||
bit of input, and using characters '0' and '1' for output.
|
||||
|
||||
The program uses the following exit codes:
|
||||
0: OK; result is a finite list
|
||||
5: Out of term space
|
||||
6: Out of memory
|
||||
1,2,3,4,8,9: result not in list form
|
||||
|
||||
The size of the term space is fixed at compile time with -DA
|
||||
|
||||
A half byte `cat'
|
||||
=================
|
||||
|
||||
The shortest (closed) lambda calculus term is \x x (\ 1 in De Bruijn notation)
|
||||
which is the identity function. When its encoding 0010 is fed
|
||||
into the universal machine, it will simply copy the input to the output.
|
||||
(well, not that simply, since each byte is smashed to bits and rebuilt from scratch)
|
||||
Voila: a half byte cat:
|
||||
|
||||
echo " Hello, world" | ./tromp
|
||||
Hello, world
|
||||
|
||||
Since the least significant 4 bits of the first byte are just arbitrary padding
|
||||
that is ignored by the program,
|
||||
any character from ASCII 32 (space) through 47 (/) will do, e.g.:
|
||||
|
||||
echo "*Hello, world" | ./tromp
|
||||
Hello, world
|
||||
|
||||
Bad programs
|
||||
============
|
||||
|
||||
If the input doesn't start with a valid program, that is,
|
||||
if the interpreter reaches end-of-file during program parsing,
|
||||
it will crash in some way:
|
||||
|
||||
echo -n "U" | ./tromp
|
||||
Segmentation fault
|
||||
|
||||
Furthermore, the interpreter requires the initial encoded lambda term to be closed,
|
||||
that is, variable n can only appear within at least n enclosing lambdas.
|
||||
For instance the term \ 5 is not closed,
|
||||
causing the interpreter to crash when looking into a null-pointer environment:
|
||||
|
||||
echo ">Hello, world" | ./tromp
|
||||
Segmentation fault
|
||||
|
||||
Since these properties can be checked when creating BLC programs,
|
||||
the interpreter doesn't bother checking for it.
|
||||
|
||||
A Self Interpreter
|
||||
==================
|
||||
|
||||
The BLC universal machine may be small at 650 bytes of C
|
||||
(952 bytes including layout), but written
|
||||
as a self interpreter in BLC it is downright minuscule at 232 bits (29 bytes):
|
||||
|
||||
01010001
|
||||
10100000
|
||||
00010101
|
||||
10000000
|
||||
00011110
|
||||
00010111
|
||||
11100111
|
||||
10000101
|
||||
11001111
|
||||
000000111
|
||||
10000101101
|
||||
1011100111110
|
||||
000111110000101
|
||||
11101001 11010010
|
||||
11001110 00011011
|
||||
00001011 11100001
|
||||
11110000 11100110
|
||||
11110111 11001111
|
||||
01110110 00011001
|
||||
00011010 00011010
|
||||
|
||||
The byte oriented BLC8 version weighs in at 43 bytes (shown in hexadecimal).
|
||||
|
||||
19468
|
||||
05580
|
||||
05f00
|
||||
bfe5f
|
||||
85f3f
|
||||
03c2d
|
||||
b9fc3f8
|
||||
5e9d65e5f
|
||||
0decb f0fc3
|
||||
9befe 185f7
|
||||
0b7fb 00cf6
|
||||
7bb03 91a1a
|
||||
|
||||
(cat uni8.Blc; echo " Ni hao") | ./tromp
|
||||
Ni hao
|
||||
|
||||
A prime number sieve
|
||||
====================
|
||||
|
||||
Even shorter than the self-interpreter is this prime number sieve in 167 bits (under 21 bytes):
|
||||
|
||||
000100011001100101000110100
|
||||
000000101100000100100010101
|
||||
11110111 101001000
|
||||
11010000 111001101
|
||||
000000000010110111001110011
|
||||
11111011110000000011111001
|
||||
10111000
|
||||
00010110
|
||||
0000110110
|
||||
|
||||
The n'th bit in the output indicates whether n is prime:
|
||||
|
||||
cat primes.blc | ./tromp -b | head -c 70
|
||||
0011010100010100010100010000010100000100010100010000010000010100000100
|
||||
|
||||
For those who prefer to digest their primes in decimal, there is oddindices.Blc,
|
||||
which will print the indices of all odd characters (with lsb = 1)
|
||||
separated by a given character:
|
||||
|
||||
(cat oddindices.Blc; echo -n " "; cat primes.blc | ./tromp -b) | ./tromp | head -c 70
|
||||
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
|
||||
|
||||
A Space filling program
|
||||
=======================
|
||||
|
||||
Program hilbert.Blc, at 143 bytes, is a very twisty "one-liner" (shown in hexadecimal):
|
||||
|
||||
1818181 8111154 6806041 55ff041
|
||||
9d f9 de 16 ff fe 5f 3f
|
||||
ef f615ff9 46 84 058117e 05
|
||||
cb fe bc bf
|
||||
ee86cb9 4681600 5c0bfac bfbf71a
|
||||
85 e0 5c f4
|
||||
14d5fe0 8180b048d0800e078 016445f
|
||||
fe 5f
|
||||
f7 ffffe5fff2fc 02f7ad97f5bf ff
|
||||
ff bf ff ca af ff
|
||||
7817ffa df76695 4680601 57f7e16
|
||||
05 c1
|
||||
3fe80b2 2c18581 bfe5c10 42ff805
|
||||
de ec 06 c2 c0 c0
|
||||
60 8191a00167fb cbcfdf65f7c0 a20
|
||||
|
||||
It expects n arbitrary characters of input, and draws a space filling Hilbert curve of order n:
|
||||
|
||||
(cat hilbert.Blc; echo -n "1") | ./tromp
|
||||
_
|
||||
| |
|
||||
|
||||
(cat hilbert.Blc; echo -n "12") | ./tromp
|
||||
_ _
|
||||
| |_| |
|
||||
|_ _|
|
||||
_| |_
|
||||
|
||||
(cat hilbert.Blc; echo -n "123") | ./tromp
|
||||
_ _ _ _
|
||||
| |_| | | |_| |
|
||||
|_ _| |_ _|
|
||||
_| |_____| |_
|
||||
| ___ ___ |
|
||||
|_| _| |_ |_|
|
||||
_ |_ _| _
|
||||
| |___| |___| |
|
||||
|
||||
(cat hilbert.Blc; echo -n "1234") | ./tromp
|
||||
_ _ _ _ _ _ _ _
|
||||
| |_| | | |_| | | |_| | | |_| |
|
||||
|_ _| |_ _| |_ _| |_ _|
|
||||
_| |_____| |_ _| |_____| |_
|
||||
| ___ ___ | | ___ ___ |
|
||||
|_| _| |_ |_| |_| _| |_ |_|
|
||||
_ |_ _| _ _ |_ _| _
|
||||
| |___| |___| |_| |___| |___| |
|
||||
|_ ___ ___ ___ ___ _|
|
||||
_| |_ |_| _| |_ |_| _| |_
|
||||
| _ | _ |_ _| _ | _ |
|
||||
|_| |_| | |___| |___| | |_| |_|
|
||||
_ _ | ___ ___ | _ _
|
||||
| |_| | |_| _| |_ |_| | |_| |
|
||||
|_ _| _ |_ _| _ |_ _|
|
||||
_| |___| |___| |___| |___| |_
|
||||
|
||||
A BrainFuck interpreter
|
||||
=======================
|
||||
|
||||
The smallest known BF interpreter is written in... you guessed it, BLC,
|
||||
coming in at 112 bytes (including 3 bits of padding):
|
||||
|
||||
od -t x4 bf.Blc
|
||||
0000000 01a15144 02d55584 223070b7 00f032ff
|
||||
0000020 7f85f9bf 956fe15e c0ee7d7f 006854e5
|
||||
0000040 fbfd5558 fd5745e0 b6f0fbeb 07d62ff0
|
||||
0000060 d7736fe1 c0bc14f1 1f2eff0b 17666fa1
|
||||
0000100 2fef5be8 ff13ffcf 2034cae1 0bd0c80a
|
||||
0000120 e51fee99 6a5a7fff ff0fff1f d0049d87
|
||||
0000140 db0500ab 3bb74023 b0c0cc28 10740e6c
|
||||
0000160
|
||||
|
||||
It expects its input to consist of a Brainfuck program
|
||||
(looking only at bits 0,1,4 to distinguish among ,-.+<>][ )
|
||||
followed by a ], followed by the input for the Brainfuck program.
|
||||
|
||||
more hw.bf
|
||||
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.]
|
||||
cat bf.Blc hw.bf | ./tromp
|
||||
Hello World!
|
||||
|
||||
Curiously, the interpreter bf.Blc is the exact same size as hw.bf.
|
||||
|
||||
A BLC assembler
|
||||
===============
|
||||
|
||||
Writing BLC programs can be made slightly less painful with this parser that
|
||||
translates single-letter-variable lambda calculus into BLC:
|
||||
|
||||
echo "\f\x f (f (f x))" > three
|
||||
cat parse.Blc three | ./tromp
|
||||
000001110011100111010
|
||||
|
||||
Converting between bits and bytes
|
||||
=================================
|
||||
|
||||
THe program inflate.Blc and its inverse deflate.Blc allow us to translate between
|
||||
BLC and BLC8.
|
||||
If you assemble a byte oriented program, you'll need to compact it into BLC8:
|
||||
|
||||
So we could assemble an input reversing program as
|
||||
|
||||
echo "\a a ((\b b b) (\b \c \d \e d (b b) (\f f c e))) (\b \c c)" > reverse
|
||||
cat parse.Blc reverse | ./tromp > reverse.blc
|
||||
|
||||
and change it to BLC8 with
|
||||
|
||||
cat deflate.Blc reverse.blc | ./tromp > rev.Blc
|
||||
wc rev.Blc
|
||||
0 1 9 rev.Blc
|
||||
|
||||
and then try it out with:
|
||||
|
||||
cat rev.Blc - | ./tromp
|
||||
Hello, world!
|
||||
^D
|
||||
!dlrow ,olleH
|
||||
|
||||
Symbolic Lambda Calculus reduction
|
||||
==================================
|
||||
|
||||
BLC8 program symbolic.Blc shows individual reduction steps on symbolic lambda terms.
|
||||
Here it is used to show the calculation of 2^3 in Church numerals:
|
||||
|
||||
echo "(\f\x f (f (f x))) (\f\x f (f x))" > threetwo
|
||||
cat parse.Blc threetwo | ./tromp > threetwo.blc
|
||||
cat symbolic.Blc threetwo.blc | ./tromp
|
||||
(\a \b a (a (a b))) (\a \b a (a b))
|
||||
\a (\b \c b (b c)) ((\b \c b (b c)) ((\b \c b (b c)) a))
|
||||
\a \b (\c \d c (c d)) ((\c \d c (c d)) a) ((\c \d c (c d)) ((\c \d c (c d)) a) b)
|
||||
\a \b (\c (\d \e d (d e)) a ((\d \e d (d e)) a c)) ((\c \d c (c d)) ((\c \d c (c d)) a) b)
|
||||
\a \b (\c \d c (c d)) a ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b))
|
||||
\a \b (\c a (a c)) ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b))
|
||||
\a \b a (a ((\c \d c (c d)) a ((\c \d c (c d)) ((\c \d c (c d)) a) b)))
|
||||
\a \b a (a ((\c a (a c)) ((\c \d c (c d)) ((\c \d c (c d)) a) b)))
|
||||
\a \b a (a (a (a ((\c \d c (c d)) ((\c \d c (c d)) a) b))))
|
||||
\a \b a (a (a (a ((\c (\d \e d (d e)) a ((\d \e d (d e)) a c)) b))))
|
||||
\a \b a (a (a (a ((\c \d c (c d)) a ((\c \d c (c d)) a b)))))
|
||||
\a \b a (a (a (a ((\c a (a c)) ((\c \d c (c d)) a b)))))
|
||||
\a \b a (a (a (a (a (a ((\c \d c (c d)) a b))))))
|
||||
\a \b a (a (a (a (a (a ((\c a (a c)) b))))))
|
||||
\a \b a (a (a (a (a (a (a (a b)))))))
|
||||
|
||||
As expected, the resulting normal form is Church numeral 8.
|
||||
|
||||
Taking only the first line of output gives us a sort of BLC disassembler,
|
||||
an exact inverse of the above assembler. The prime number sieve disassembles as follows:
|
||||
|
||||
cat symbolic.Blc primes.blc | ./tromp | head -1
|
||||
\a (\b b (b ((\c c c) (\c \d \e e (\f \g g) ((\f c c f ((\g g g) (\g f (g g)))) (\f \g \h \i i g (h (d f))))) (\c \d \e b (e c))))) (\b \c c (\d \e d) b)
|
||||
|
||||
Hardly any less obfuscated...
|
||||
|
||||
The last line of
|
||||
cat symbolic.Blc primes.blc | ./tromp | head -16
|
||||
starts out as
|
||||
\a \b b (\c \d c) (\c c (\d \e d) (\d d (\e \f f) (\e e (\f \g g) ((\f (\g \h \i
|
||||
|
||||
The \a is for ignoring the rest of the input (to which the universal machine
|
||||
applies the decoded lambda term). The \b b (..) (...) is the list constructor,
|
||||
usually called cons, applied to a head (a list element) and a tail (another list).
|
||||
In this case the element is (\c \d c), which represents the boolean true, and
|
||||
which we use to represent a 0 bit. This is the bit that says 0 is not prime.
|
||||
The next list element (following another cons) is (\d \e d). Another 0 bit,
|
||||
this time saying that 1 is not prime. The third list element is (\e \f f),
|
||||
a 1 bit, confirming our suspicion that 2 is prime. As is the next number,
|
||||
according to (\f \g g). We can see that the tail after the first 4 elements
|
||||
is still subject to further reduction. The bit for number 4 will show up
|
||||
for the first time in line 30, as (\g \h g), or 0, as the result of zeroing
|
||||
out all multiples of the first prime, 2.
|
||||
Since my computer reaches swap hell before line 40, we can't see the next bit arriving,
|
||||
at least not in this symbolic reduction.
|
||||
|
||||
Performance
|
||||
===========
|
||||
|
||||
Performance is quite decent, and amazingly good for such a tiny implementation,
|
||||
being roughly 50% slower than a Haskell implementation of the universal machine
|
||||
using so-called Higher Order Abstract Syntax which relies on the highly optimized
|
||||
Haskell runtime system for evaluation. Of course individual blc programs running
|
||||
under the interpreter perform much worse than that same program written in Haskell.
|
||||
|
||||
Our interpreter copes well with extra levels of interpretation:
|
||||
|
||||
time cat primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m0.043s
|
||||
time cat uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m0.191s
|
||||
time cat uni.blc uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m1.919s
|
||||
time cat uni.blc uni.blc uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 0m23.514s
|
||||
time cat uni.blc uni.blc uni.blc uni.blc primes.blc | ./tromp -b | head -c 210 > /dev/null
|
||||
real 4m52.700s
|
||||
|
||||
Obfuscation
|
||||
===========
|
||||
|
||||
Obfuscation is due entirely to conciseness. Some questions to ponder:
|
||||
|
||||
Which of the term space codes 0,1,2,3 serves multiple purposes?
|
||||
|
||||
Why is the environment pointer pointing into the term space?
|
||||
|
||||
What does the test u+m&1? do?
|
||||
|
||||
How does the program reach exit code 0?
|
||||
|
||||
And how do any of those blc programs work?
|
||||
|
||||
Portability
|
||||
===========
|
||||
|
||||
The program freely (without casting) converts between int and int\*, causing
|
||||
many warnings;
|
||||
note: expected ‘int *’ but argument is of type ‘int’
|
||||
warning: assignment from incompatible pointer type
|
||||
warning: assignment makes integer from pointer without a cast
|
||||
warning: assignment makes pointer from integer without a cast
|
||||
warning: incompatible implicit declaration of built-in function ‘calloc’
|
||||
warning: incompatible implicit declaration of built-in function ‘exit’
|
||||
warning: passing argument 1 of ‘d’ makes pointer from integer without a cast
|
||||
warning: passing argument 1 of ‘p’ makes pointer from integer without a cast
|
||||
warning: pointer/integer type mismatch in conditional expression
|
||||
|
||||
Avoiding these would make the program substantially longer,
|
||||
and detract from its single minded focus on conciseness.
|
||||
|
||||
It implicitly declares functions read, write, exit and calloc, the latter two incompatibly.
|
||||
32 bit and 64 bit executables are separate Makefile targets, involving a change
|
||||
from int to long and from a hardcoded sizeof of 4 to 8.
|
||||
|
||||
The program has been tested to work correctly on Linux/Solaris/MacOSX both in 32 and 64 bits.
|
||||
|
||||
How the program works
|
||||
=====================
|
||||
|
||||
See the file how13.
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
Christopher Hendrie, Bertram Felgenhauer, Alex Stangl, Seong-hoon Kang,
|
||||
and Yusuke Endoh have contributed ideas and suggestions for improvement.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
Binary Lambda Calculus
|
||||
<https://tromp.github.io/cl/Binary_lambda_calculus.html>
|
||||
|
||||
G J Chaitin, Algorithmic information theory, Volume I,
|
||||
Cambridge Tracts in Theoretical Computer Science, Cambridge University Press, October 1987.
|
||||
<http://www.cs.auckland.ac.nz/~chaitin/cup.html>
|
||||
|
||||
Jean-Louis Krivine. 2007.
|
||||
A call-by-name lambda-calculus machine
|
||||
Higher Order Symbol. Comput. 20, 3 (September 2007), 199-207.
|
||||
<http://www.pps.univ-paris-diderot.fr/~krivine/articles/lazymach.pdf>
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
<!--
|
||||
(c) Copyright 1984-2015, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
|
||||
This work is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License][cc].
|
||||
|
||||
[judges]: http://www.ioccc.org/judges.html
|
||||
[cc]: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
-->
|
||||
@@ -0,0 +1,45 @@
|
||||
Gur cebtenz vzcyrzragf n fb pnyyrq Xevivar znpuvar,
|
||||
raunaprq jvgu n onfvp vachg/bhgchg flfgrz
|
||||
naq n ersrerapr pbhagvat tneontr pbyyrpgbe.
|
||||
Gur Xevivar znpuvar vf n fgrcjvfr genafsbezre bs n tybony fgngr pbafvfgvat bs
|
||||
gur pheerag grez, gur pheerag raivebazrag (n yvfg bs pybfherf), naq n fgnpx bs pbagvahngvbaf
|
||||
(Frr gur 3eq ersrerapr sbe qrgnvyf.)
|
||||
|
||||
Gur grez fcnpr ubyqf ynzoqn pnyphyhf grezf va gur sbyybjvat sbezng
|
||||
0 qrabgrf fbzr sbez bs VB; gur bayl sbhe bpphenaprf ner ng
|
||||
P=13: erpbtavmrf raq bs n olgr
|
||||
P=20: erpbtavmr bhgchg bs 0 ovg
|
||||
P=21: erpbtavmr bhgchg bs 1 ovg
|
||||
P=raq: vachg yvfg, rkcnaqrq ynmvyl
|
||||
1 qrabgrf n inevnoyr, jvgu arkg ragel vgf qr Oehvwa vaqrk
|
||||
2 qrabgrf nccyvpngvba jvgu arkg ragel gur fvmr bs gur grez orvat nccyvrq
|
||||
3 qrabgrf ynzoqn nofgenpgvba
|
||||
|
||||
N pybfher pbafvfgf bs gur sbyybjvat vgrzf
|
||||
0 cbvagre gb gur arkg pybfher (nf cneg bs na raivebazrag), be arkg erpbeq ba serr yvfg
|
||||
1 ersrerapr pbhag
|
||||
2 cbvagre gb raivebazrag
|
||||
3 ynzoqn grez (vaqrk va grez fcnpr)
|
||||
|
||||
Urer'f jung rnpu inevnoyr qbrf:
|
||||
Y Ynzoqn grez fcnpr
|
||||
z zbqr; 0 sbe ovgf naq 7 sbe olgrf
|
||||
o ovgf erznvavat haernq va V, -1 ba RBS
|
||||
Q Qrfvenoyr grzcbenel
|
||||
p pbagvahngvba pbagrkg
|
||||
n npgvir raivebazrag cbvagre
|
||||
P Pheerag grez vaqrk
|
||||
H Hygvzngr grez cbvagre
|
||||
h hgvyvgl ragel
|
||||
O Obea gb or serr-yvfg bs pybfherf
|
||||
V Vachg punenpgre
|
||||
B Bhgchg punenpgre
|
||||
r ragel bs pheerag grez
|
||||
|
||||
Urer'f jung rnpu bs gur 6 nhkvyvnel shapgvbaf qbrf:
|
||||
f: inevnoyr ybbxhc va npgvir raivebazrag jvgu qr Oehvwa vaqrk h
|
||||
F: trgf bar ovg bs vachg, frggvat o gb -1 ba RBS be gb erznvavat ahzore bs ovgf va pheerag olgr
|
||||
x: xbcl sentzrag sebz fgevat yvgreny ubyqvat ynzoqn grezf kbe 46 vagb pheerag raq bs grez fcnpr
|
||||
k: kcnaq vachg, nqqvat 11 (erfc. 99) ragevrf gb grez fcnpr sbe ovg (erfc. olgr) zbqr
|
||||
c: cnefrf oyp-rapbqrq ynzoqn grez hfvat F(), fgberf erfhygf va grez fcnpr naq ergheaf yratgu
|
||||
q: qrpernfr ersrerapr pbhagre, nqq erpbeq gb serr yvfg ba ernpuvat mreb
|
||||
@@ -0,0 +1 @@
|
||||
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.]
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
F€TUUőUA‚!ţ˙ű/Ď÷öż#B¨Đ
|
||||
¨Đđ"°lą`ż¸W˙ď‡ţěľř�řY˙ü˙ţţţłóůůüĎq4Á`X60
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
00010001100110010100011010000000010110000010010001010111110111101001000110100001110011010000000000101101110011100111111101111000000001111100110111000000101100000110110
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env perl
|
||||
$num = 0;
|
||||
while(read(STDIN, $byte, 1)) {
|
||||
print "$num\n" if $byte;
|
||||
++$num;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
Int L[A],m,b,*D=A,
|
||||
*c,*a=L,C,*U=L,u;s
|
||||
(_){u--&&s(a=*a);}
|
||||
char*B,I,O;S(){b=b
|
||||
--?b:m|read(0,&I,1
|
||||
)-1;return~I>>b&1;
|
||||
}k(l,u){for(;l<=u;
|
||||
U-L<A?*U++=46^l++[
|
||||
"-,&,,/.--/,:-,'/"
|
||||
".-,-,,/.-,*,//..,"
|
||||
]:exit(5));}p(Int*m){
|
||||
return!*U?*m=S()?U++,!S
|
||||
()?m[1]=p(++U),2:3:1,p(U)
|
||||
:S()?U+=2:p(U[1]++),U-m;}x(
|
||||
c){k(7*!b,9);*U++=b&&S();c&&x
|
||||
(b);}d(Int*l){--l[1]||d(l[d(*l),
|
||||
*l=B,B=l,2]);}main(e){for(k(10,33
|
||||
),a[4]-=m=e-2&7,a[23]=p(U),b=0;;e-2
|
||||
?e?e-3?s(D=a),C=a [3],++1[a=a[2]],d(
|
||||
D):c?D=c,c=*D,*D= a,a=D:exit(L[C+1])
|
||||
:C--<23?C=u+m&1?O =O+O|C&1,9:write(m
|
||||
||(O=C+28),&O,1)+ 1:(S(),x(0<b++?k(0,
|
||||
6),U[-5]=96:0)):( D=B?B:calloc(4,X))
|
||||
?B=*D,*D=c,c=D,D[ 2]=a,a[++D[1]]++,D
|
||||
[3]=++C+u:exit(6) )e=L[C++],u=L[C];}
|
||||
@@ -0,0 +1,27 @@
|
||||
Int L[A],m,b,*D=A,
|
||||
*c,*a=L,C,*U=L,u;s
|
||||
(Int*m){return!*U?
|
||||
*m=g()?U++,!g()?m[
|
||||
1]=s(++U),2:3:1,s(
|
||||
U):g()?U+=2:s(U[1]
|
||||
++),U-m;}v(){for(D
|
||||
=a;u--;a=*a);C=a[3
|
||||
];++1[a=a[2]];d(D)
|
||||
;}char *B,I,O,*S =
|
||||
"-,&,,/.--/,:-,'/"
|
||||
".-,-,,/.-,*,//..,"
|
||||
;d(Int*l){--l[1]||d(l
|
||||
[d(*l),*l=B,B=l,2]);}g(
|
||||
){b=b--?b:m|read(0,&I,1)-
|
||||
1;return~I>>b&1;}x(){for(k(
|
||||
0,6),U[-5]=96;b;*U++=g())k(0,
|
||||
9);}k(l,u){for(;l<=u;U-L-A?*U++
|
||||
=S[l++]^46:exit(5));}main(e){for(
|
||||
k(10,33),a[4]-=m =e-2&7,a[23]=s(U),
|
||||
b=0;;e?e-1?e-2?c ?D=c,c=*D,*D=a,a=D
|
||||
:exit(L[C+1]):(D =B?B:calloc(4,X))?
|
||||
B=*D,*D=c,c=D,D[ 3]=++C+u, D[2]=a,a
|
||||
[++D[1] ]++:exit (6):v():C -->22?g(
|
||||
),1<++b &&x(),k( 7*!b,9),* U++=b&&g
|
||||
():(C=u+m&1?O=O+ O|C&1,9:write(m||(
|
||||
O=C+28),&O,1)+1) )e=L[C++],u=L[C];}
|
||||
@@ -0,0 +1 @@
|
||||
0101000110100000000101011000000000011110000101111110011110000101110011110000001111000010110110111001111100001111100001011110100111010010110011100001101100001011111000011111000011100110111101111100111101110110000110010001101000011010
|
||||
@@ -0,0 +1 @@
|
||||
F─U─ПЧ_┘СП<-╧Э?┘Иж^_
Л©ц⌡ОА┘В╟Ж{╟9
|
||||
Reference in new issue
Block a user