Add alt code to 1989/paul + format README.md

The alt code is based on the author's remarks: a trace routine had been
provided but it was commented out. It offers for some rather fun output.
The Makefile was updated accordingly.

The README.md file has been typo and format fixed.

The .gitignore file has been updated - added paul.alt.
This commit is contained in:
Cody Boone Ferguson committed 2023-10-20 08:02:58 -07:00
1 parent d1cf700126
commit 887417c6ab
5 files changed
+113 -49

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
paul
paul.alt
paul.orig
prog.orig
+2 -2
View File
@@ -114,8 +114,8 @@ OBJ= ${PROG}.o
DATA=
TARGET= ${PROG}
#
ALT_OBJ=
ALT_TARGET=
ALT_OBJ= ${PROG}.alt.o
ALT_TARGET= ${PROG}.alt
#################
+71 -46
View File
@@ -1,10 +1,6 @@
# Most complex algorithm
Paul E. Black\
CIRRUS LOGIC, Inc.\
1463 Centre Pointe Dr.\
Milpitas, CA\
95035\
US
## To build:
@@ -13,6 +9,8 @@ US
make all
```
There is an alternate version with a trace routine that the author had commented
out. See [alternate code](#alternate-code) below for more details.
## Try:
@@ -20,17 +18,43 @@ make all
./paul
```
### Alternate code:
This version was described by the author like:
> I could have squeezed the program under 1024 bytes without the
trace subroutine, but I felt it was important for understanding
the program. Besides it is fun to watch the tape zooming back
and forth as the program runs. A much better debugger or trace
could easily be added.
#### To build:
```sh
make alt
```
#### To use:
```sh
./paul.alt
```
## Judges' remarks:
The original source contained a long line which caused many
mailers to barf. The original file may be re-constructed by
removing the trailing "\" on line 12 and joining lines 12 and
13 together without a space.
The author's comments is a detailed program explanation and spoiler. If you
want a real challenge, don't read any further and try to understand the program
via the source.
WHAT FOLLOWS IS A DETAILED PROGRAM EXPLANATION AND SPOILER.
IF YOU WANT A REAL CHALLENGE, DON'T READ ANY FURTHER AND TRY
TO UNDERSTAND THE PROGRAM VIA THE SOURCE.
### Historical note:
The original source contained a long line which caused many mailers to barf.
The original file may be re-constructed by removing the trailing `\` on line 12
and joining lines 12 and 13 together without a space.
However as this is no longer a problem the trailing `\` has been restored in all
versions, both the original and the fixed for modern systems version.
## Author's remarks:
@@ -43,69 +67,70 @@ natural to write incredibly obscure Turing programs.)
If the program is invoked with an operand, the operand is used
as the Turing program. It includes a "trace" facility,
subroutine r (commented out for obscurity), to help write and
subroutine `r` (commented out for obscurity), to help write and
debug Turing programs. Just the thing for some fun in a Theory
of Computation class.
The Turing machine tape is represented as a doubly linked list
of pointers. The forward and backward links are XOR'd together
of pointers. The forward and backward links are `XOR`ed together
and stored in one pointer. If we always keep one of the links
on hand, we can recover the other link at any time. The
variable q is the scan head (and a pointer at some tape cell),
and p is a "previous" link. The state of the tape is stored in
variable `q` is the scan head (and a pointer at some tape cell),
and `p` is a "previous" link. The state of the tape is stored in
the low order bit of the pointer. Since we always allocate an
even number of bytes, the low order bit carries no information
(see portability below.) Memory representing a tape cell is
(see [portability](#portability) below.) Memory representing a tape cell is
allocated when the cell is first scanned. Thus the simulation
begins with a tape effectively the size of virtual memory set
to all zeros. Since a header can be added to any Turing
program to write initial data and position the scan head, this
is little loss of generality.
The simulated Turing machine has a single tape with either an 1
or a 0 in each cell. The Turing machine language format is a
The simulated Turing machine has a single tape with either a `1`
or a `0` in each cell. The Turing machine language format is a
string of three bytes. The first byte is the current state.
The second byte is the next state. (The last bit of states is
ignored, e.g., B and C are the same state, in an attempt to be
ignored, i.e., `B` and `C` are the same state, in an attempt to be
able to have interesting words in the program.) The third byte
is composed of bits. Bit 1 (2&byte) is the symbol scanned,
is composed of bits. Bit `1` (`2&byte`) is the symbol scanned,
i.e. an instruction is selected for state and for a match with
the symbol under the scan head. Bit 2 (4&byte) is the new
symbol to be written to the cell. Bit 3 (8&byte) is the
direction to move the scan head: 0 for left and 1 for right.
If bit 4 (16&byte) is true, the next character is sent to
stdout. (I added this feature so programs could print
the symbol under the scan head. Bit `2` (`4&byte`) is the new
symbol to be written to the cell. Bit `3` (`8&byte`) is the
direction to move the scan head: `0` for left and `1` for right.
If bit `4` (`16&byte`) is true, the next character is sent to
`stdout`. (I added this feature so programs could print
results.)
The Turing machine has next state `j` when it begins. The
cycle is 1) exit if the state is `x`, 2) find the next
cycle is (1) exit if the state is `x`, (2) find the next
instruction (given the state and the character under the scan
head). [The program string is searched forward for the next
head). (The program string is searched forward for the next
matching instruction. If the end of the string is reached, the
search begins again at the first of the string. Thus states
can be used as local labels in different places.] 3) change to
the next state, 4) print a character if indicated, 5) write the
tape symbol, and 6) move the scan head. The cycle then repeats
can be used as local labels in different places.) (3) change to
the next state, (4) print a character if indicated, (5) write the
tape symbol, and (6) move the scan head. The cycle then repeats
with step 1. A call to the trace routine is just before step
2, but is commented out.
The following ROT13'ed text is a quick outline of the actual
Turing program:
### Quick outline of the Turing program:
Urer vf n dhvpx bhgyvar bs gur Ghevat cebtenz: gur
cerivbhf naq pheerag Svobanppv ahzoref ner xrcg va onfr
1 sbez jvgu gur pheerag ba gur evtug. Gur svefg guerr
fgrcf frg hc gur svefg gjb ahzoref, 1 naq 1. Gura
[ortvaavat jvgu "@ ("] n znexre bs VVV vf perngrq naq
gur pheerag ahzore vf pbcvrq gb gur evtug bs gur
znexre. Gura [ortvaavat jvgu "BI "] gur ahzore vf
pbairegrq gb ovanel ol ercrngrq qvivqvat ol 2 yrnivat V
sbe erznvaqre 1, naq VV sbe erznvaqre 0. Arkg
[ortvaavat jvgu "JI "] gur ovanel ercerfragngvba vf
cevagrq naq vgf flzobyf naq gur znexre ner renfrq.
Svanyyl [ortvaavat jvgu "RRa"] gur gjb ahzoref ner
nqqrq naq gur pheerag ahzore pbcvrq gb gur yrsg gb
orpbzr gur cerivbhf. Gura gur plpyr ercrngf.
The previous and current Fibonacci numbers are kept in base
1 form with the current on the right. The first three
steps set up the first two numbers, `1` and `1`. Then
[beginning with `@ (`] a marker of `III` is created and
the current number is copied to the right of the
marker. Then (beginning with `OV `) the number is
converted to binary by repeatedly dividing by 2 leaving I
for remainder `1`, and II for remainder `0`. Next
(beginning with `WV `) the binary representation is
printed and its symbols and the marker are erased.
Finally (beginning with `EEn`) the two numbers are
added and the current number copied to the left to
become the previous. Then the cycle repeats.
### Portability
The program requires that the lowest bit of a pointer to be 0.
+35
View File
@@ -0,0 +1,35 @@
#include <stdio.h>
#define f long
#define v (void)printf(
#define x ),exit(1);
#define y ){if(n)c=z(n,u),u=n,n=c;o[i]=n?'0'+(1&*n):'0';}
#define z(a,b) (f*)(~1&*a^(f)b)
#define k(l) if(!(l=(f*)malloc(sizeof(l))))v 23+m x if(1&(f)l)v 39+m x*l=
r(p,q,d)f*p,*q;{char o[81];f*n=p,i=39,*c,*u=d?q:z(p,q);o[40]='0'+(1&*p);
for(;i>=0;i--y u=d?z(p,q):q;n=p;for(i=41;i<79;i++y o[i++]='\r';o[i++]=0;
v o);(void)fflush(stdout);sleep(1);}
main(a,c)char**c;{char*u,*malloc(),*m=
"Usage: black [string]\n\0No more memory\n\0Unusable memory alignment\n\0jt,s@m@ (beleY%XX&Yz {z&z}i|R(|)*((.)i)hiniFiGJ%FG.JJgJ: ;;&;z {z&z}-RS/ROiOV OP+PsaPh+ijainnjmamfmfAlnnnnphppopv%vvgv.aABiB1/BVP11/1.%..&.OhrR-WV V1#1VP1CcC0R\
\n\n'CVP0\n!\n\n'\nEaEEnEamat!akckk'kwaww'wz,zzozEit +",
*n=m;f*q,*p=0,*g,b=3,d;
if(a>2)v m x n=a>1?c[1]:n;
/*v"\t\t\t\t\tV\n");*/
k(q)0;u=n;a=~1&'j';
while(a!='x'){
r(q,p,b);
for(;;u+=3){
u= *u?u:n;
if((~1&*u)==a&&(1&*q)<<1==(2&u[2]))break;
}
a=~1&u[1];
d=(8&u[2])>>3;
if(16&u[2])putchar(u[3]);
if(4&u[2])*q|=1;else*q&=~1;
if(b==d)g=p;else{
g=z(q,p);
if(!g){k(g)(f)q;*q^=(f)g;}
}
p=q;q=g;b=1-d;
}
r(q,p,b);v"\n");exit(0);
}
+4 -1
View File
@@ -565,9 +565,12 @@ Cody fixed a segfault under macOS that prevented it from working. The problem
was that the int (from `#define f`) should be a long. This became apparent when
he was using lldb and saw that the type of a pointer was too `long` :-)
Cody also provided the [alternate version](1989/paul/paul.alt.c)
which has the trace function that the author included but commented out. See the
README.md for details.
## [1989/robison](1989/robison/robison.c) ([README.md](1989/robison/README.md]))
cd 1989/robison ; make orig_prog_diff
Yusuke Endoh fixed this to compile under modern systems. To see the changes
made, try: