Created
April 22, 2017 18:39
-
-
Save jeffreykegler/2507ed1f8db22d1d2c91ddb56b0c405c to your computer and use it in GitHub Desktop.
Sample of CWeb, by Knuth himself
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
\datethis | |
@*Intro. This program generates clauses for the transition relation | |
from time $t$ to time $t+1$ in Conway's Game of Life, assuming that | |
all of the potentially live cells at time $t$ belong to a pattern | |
that's specified in |stdin|. The pattern is defined by one or more | |
lines representing rows of cells, where each line has `\..' in a | |
cell that's guaranteed to be dead at time~$t$, otherwise it has `\.*'. | |
The time is specified separately as a command-line parameter. | |
The Boolean variable for cell $(x,y)$ at time $t$ is named by its | |
so-called ``xty code,'' namely by the decimal value of~$x$, followed | |
by a code letter for~$t$, followed by the decimal value of~$y$. For | |
example, if $x=10$ and $y=11$ and $t=0$, the variable that indicates | |
liveness of the cell is \.{10a11}; and the corresponding variable | |
for $t=1$ is \.{10b11}. | |
Up to 19 auxiliary variables are used together with each xty code, | |
in order to construct clauses that define the successor state. | |
The names of these variables are obtained by appending one of | |
the following two-character combinations to the xty code: | |
\.{A2}, \.{A3}, \.{A4}, | |
\.{B1}, \.{B2}, \.{B3}, \.{B4}, | |
\.{C1}, \.{C2}, \.{C3}, \.{C4}, | |
\.{D1}, \.{D2}, | |
\.{E1}, \.{E2}, | |
\.{F1}, \.{F2}, | |
\.{G1}, \.{G2}. | |
These variables are derived from the Bailleux--Boufkhad method | |
of encoding cardinality constraints: | |
The auxiliary variable \.{A$k$} stands for the condition | |
``at least $k$ of the eight neighbors are alive.'' Similarly, | |
\.{B$k$} stands for ``at least $k$ of the first four neighbors | |
are alive,'' and \.{C$k$} accounts for the other four neighbors. | |
Codes \.D, \.E, \.F, and~\.G refer to pairs of neighbors. | |
Thus, for instance, \.{10a11C2} means that at least two of the | |
last four neighbors of cell $(10,11)$ are alive. | |
Those auxiliary variables receive values by means of up to 77 clauses per cell. | |
For example, if $u$ and~$v$ are the neighbors of cell~$z$ that correspond | |
to a pairing of type~\.D, there are six clauses | |
$$\bar u d_1,\quad | |
\bar v d_1,\quad | |
\bar u\bar v d_2,\quad | |
u v\bar d_1,\quad | |
u\bar d_2,\quad | |
v\bar d_2.$$ | |
The sixteen clauses | |
$$\displaylines{\hfill | |
\bar d_1b_1,\quad | |
\bar e_1b_1,\quad | |
\bar d_2b_2,\quad | |
\bar d_1\bar e_1b_2,\quad | |
\bar e_2b_2,\quad | |
\bar d_2\bar e_1b_3,\quad | |
\bar d_1\bar e_2b_3,\quad | |
\bar d_2\bar e_2b_4, | |
\hfill\cr\hfill | |
d_1e_1\bar b_1,\quad | |
d_1e_2\bar b_2,\quad | |
d_2e_1\bar b_2,\quad | |
d_1\bar b_3,\quad | |
d_2e_2\bar b_3,\quad | |
e_1\bar b_3,\quad | |
d_2\bar b_4,\quad | |
e_2\bar b_4 | |
\hfill}$$ | |
define $b$ variables from $d$'s and $e$'s; and another sixteen | |
define $c$'s from $f$'s and $g$'s in the same fashion. | |
A similar set of 21 clauses will define the $a$'s from the $b$'s and $c$'s. | |
Once the $a$'s are defined, thus essentially counting the | |
live neighbors of cell $z$, the next | |
state~$z'$ is defined by five further clauses | |
$$\bar a_4\bar z',\quad | |
a_2\bar z',\quad | |
a_3z\bar z',\quad | |
\bar a_3a_4z',\quad | |
\bar a_2a_4\bar zz'.$$ | |
For example, the last of these states that $z'$ will be true | |
(i.e., that cell $z$ will be alive at time $t+1$) if | |
$z$ is alive at time~$t$ and has $\ge2$ live neighbors | |
but not $\ge4$. | |
Nearby cells can share auxiliary variables, according to a tricky scheme that | |
is worked out below. In consequence, the actual number of auxiliary variables | |
and clauses per cell is reduced from 19 and $77+5$ to 13 and $57+5$, | |
respectively, except at the boundaries. | |
@ So here's the overall outline of the program. | |
@d maxx 50 /* maximum number of lines in the pattern supplied by |stdin| */ | |
@d maxy 50 /* maximum number of columns per line in |stdin| */ | |
@c | |
#include <stdio.h> | |
#include <stdlib.h> | |
char p[maxx+2][maxy+2]; /* is cell $(x,y)$ potentially alive? */ | |
char have_b[maxx+2][maxy+2]; /* did we already generate $b(x,y)$? */ | |
char have_d[maxx+2][maxy+2]; /* did we already generate $d(x,y)$? */ | |
char have_e[maxx+2][maxy+4]; /* did we already generate $e(x,y)$? */ | |
char have_f[maxx+4][maxy+2]; /* did we already generate $f(x-2,y)$? */ | |
int tt; /* time as given on the command line */ | |
int xmax,ymax; /* the number of rows and columns in the input pattern */ | |
int xmin=maxx,ymin=maxy; /* limits in the other direction */ | |
char timecode[]="abcdefghijklmnopqrstuvwxyz"@| | |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"@| | |
"!\"#$%&'()*+,-./:;<=>?@@[\\]^_`{|}~"; /* codes for $0\le t\le83$ */ | |
@q$@> | |
char buf[maxy+2]; /* input buffer */ | |
unsigned int clause[4]; /* clauses are assembled here */ | |
int clauseptr; /* this many literals are in the current clause */ | |
@<Subroutines@>@; | |
main(int argc,char*argv[]) { | |
register int j,k,x,y; | |
@<Process the command line@>; | |
@<Input the pattern@>; | |
for (x=xmin-1;x<=xmax+1;x++) for (y=ymin-1;y<=ymax+1;y++) { | |
@<If cell $(x,y)$ is obviously dead at time $t+1$, |continue|@>; | |
a(x,y); | |
zprime(x,y); | |
} | |
} | |
@ @<Process the command line@>= | |
if (argc!=2 || sscanf(argv[1],"%d",&tt)!=1) { | |
fprintf(stderr,"Usage: %s t\n",argv[0]); | |
exit(-1); | |
} | |
if (tt<0 || tt>82) { | |
fprintf(stderr,"The time should be between 0 and 82 (not %d)!\n",tt); | |
exit(-2); | |
} | |
@ @<Input the pattern@>= | |
for (x=1;;x++) { | |
if (!fgets(buf,maxy+2,stdin)) break; | |
if (x>maxx) { | |
fprintf(stderr,"Sorry, the pattern should have at most %d rows!\n",maxx); | |
exit(-3); | |
} | |
for (y=1;buf[y-1]!='\n';y++) { | |
if (y>maxy) { | |
fprintf(stderr,"Sorry, the pattern should have at most %d columns!\n", | |
maxy); | |
exit(-4); | |
} | |
if (buf[y-1]=='*') { | |
p[x][y]=1; | |
if (y>ymax) ymax=y; | |
if (y<ymin) ymin=y; | |
if (x>xmax) xmax=x; | |
if (x<xmin) xmin=x; | |
}@+else if (buf[y-1]!='.') { | |
fprintf(stderr,"Unexpected character `%c' found in the pattern!\n", | |
buf[y-1]); | |
exit(-5); | |
} | |
} | |
} | |
@ @d pp(xx,yy) ((xx)>=0 && (yy)>=0? p[xx][yy]: 0) | |
@<If cell $(x,y)$ is obviously dead at time $t+1$, |continue|@>= | |
if (pp(x-1,y-1)+pp(x-1,y)+pp(x-1,y+1)+ | |
pp(x,y-1)+p[x][y]+p[x][y+1]+ | |
pp(x+1,y-1)+p[x+1][y]+p[x+1][y+1]<3) continue; | |
@ Clauses are assembled in the |clause| array (surprise), where we | |
put encoded literals. | |
The code for a literal is an unsigned 32-bit quantity, where the leading | |
bit is 1 if the literal should be complemented. The next three bits | |
specify the type of the literal (0 thru 7 for plain and \.A--\.G); | |
the next three bits specify an integer~$k$; and the next bit is zero. | |
That leaves room for two 12-bit fields, which specify $x$ and $y$. | |
Type 0 literals have $k=0$ for the ordinary xty code. However, the | |
value $k=1$ indicates that the time code should be for $t+1$ instead of~$t$. | |
And $k=2$ denotes a special ``tautology'' literal, which is always true. | |
If the tautology literal is complemented, we omit it from the clause; | |
otherwise we omit the entire clause. | |
Finally, $k=7$ denotes an auxiliary literal, used to avoid | |
clauses of length~4. | |
Here's a subroutine that outputs the current clause and resets | |
the |clause| array. | |
@d taut (2<<25) | |
@d sign (1U<<31) | |
@<Sub...@>= | |
void outclause(void) { | |
register int c,k,x,y,p; | |
for (p=0;p<clauseptr;p++) | |
if (clause[p]==taut) goto done; | |
for (p=0;p<clauseptr;p++) if (clause[p]!=taut+sign) { | |
if (clause[p]>>31) printf(" ~");@+else printf(" "); | |
c=(clause[p]>>28)&0x7; | |
k=(clause[p]>>25)&0x7; | |
x=(clause[p]>>12)&0xfff; | |
y=clause[p]&0xfff; | |
if (c) printf("%d%c%d%c%d", | |
x,timecode[tt],y,c+'@@',k); | |
else if (k==7) printf("%d%c%dx", | |
x,timecode[tt],y); | |
else printf("%d%c%d", | |
x,timecode[tt+k],y); | |
} | |
printf("\n"); | |
done: clauseptr=0; | |
} | |
@ And here's another, which puts a type-0 literal into |clause|. | |
@<Sub...@>= | |
void applit(int x,int y,int bar,int k) { | |
if (k==0 && (x<xmin || x>xmax || y<ymin || y>ymax || p[x][y]==0)) | |
clause[clauseptr++]=(bar? 0: sign)+taut; | |
else clause[clauseptr++]=(bar? sign:0)+(k<<25)+(x<<12)+y; | |
} | |
@ The |d| and |e| subroutines are called for only one-fourth | |
of all cell addresses $(x,y)$. Indeed, one can show that | |
$x$ is always odd, and that $y\bmod4<2$. | |
Therefore we remember if we've seen $(x,y)$ before. | |
Slight trick: If |yy| is not in range, we avoid generating the | |
clause $\bar d_k$ twice. | |
@d newlit(x,y,c,k) clause[clauseptr++]=((c)<<28)+((k)<<25)+((x)<<12)+(y) | |
@d newcomplit(x,y,c,k) | |
clause[clauseptr++]=sign+((c)<<28)+((k)<<25)+((x)<<12)+(y) | |
@<Sub...@>= | |
void d(int x,int y) { | |
register x1=x-1,x2=x,yy=y+1; | |
if (have_d[x][y]!=tt+1) { | |
applit(x1,yy,1,0),newlit(x,y,4,1),outclause(); | |
applit(x2,yy,1,0),newlit(x,y,4,1),outclause(); | |
applit(x1,yy,1,0),applit(x2,yy,1,0),newlit(x,y,4,2),outclause(); | |
applit(x1,yy,0,0),applit(x2,yy,0,0),newcomplit(x,y,4,1),outclause(); | |
applit(x1,yy,0,0),newcomplit(x,y,4,2),outclause(); | |
if (yy>=ymin && yy<=ymax) | |
applit(x2,yy,0,0),newcomplit(x,y,4,2),outclause(); | |
have_d[x][y]=tt+1; | |
} | |
} | |
@# | |
void e(int x,int y) { | |
register x1=x-1,x2=x,yy=y-1; | |
if (have_e[x][y]!=tt+1) { | |
applit(x1,yy,1,0),newlit(x,y,5,1),outclause(); | |
applit(x2,yy,1,0),newlit(x,y,5,1),outclause(); | |
applit(x1,yy,1,0),applit(x2,yy,1,0),newlit(x,y,5,2),outclause(); | |
applit(x1,yy,0,0),applit(x2,yy,0,0),newcomplit(x,y,5,1),outclause(); | |
applit(x1,yy,0,0),newcomplit(x,y,5,2),outclause(); | |
if (yy>=ymin && yy<=ymax) | |
applit(x2,yy,0,0),newcomplit(x,y,5,2),outclause(); | |
have_e[x][y]=tt+1; | |
} | |
} | |
@ The |f| subroutine can't be shared quite so often. But we | |
do save a factor of~2, because $x+y$ is always even. | |
@<Sub...@>= | |
void f(int x,int y) { | |
register xx=x-1,y1=y,y2=y+1; | |
if (have_f[x][y]!=tt+1) { | |
applit(xx,y1,1,0),newlit(x,y,6,1),outclause(); | |
applit(xx,y2,1,0),newlit(x,y,6,1),outclause(); | |
applit(xx,y1,1,0),applit(xx,y2,1,0),newlit(x,y,6,2),outclause(); | |
applit(xx,y1,0,0),applit(xx,y2,0,0),newcomplit(x,y,6,1),outclause(); | |
applit(xx,y1,0,0),newcomplit(x,y,6,2),outclause(); | |
if (xx>=xmin && xx<=xmax) | |
applit(xx,y2,0,0),newcomplit(x,y,6,2),outclause(); | |
have_f[x][y]=tt+1; | |
} | |
} | |
@ The |g| subroutine cleans up the dregs, by somewhat tediously | |
locating the two neighbors that weren't handled by |d|, |e|, or~|f|. | |
No sharing is possible here. | |
@<Sub...@>= | |
void g(int x,int y) { | |
register x1,x2,y1,y2; | |
if (x&1) x1=x-1,y1=y,x2=x+1,y2=y^1; | |
else x1=x+1,y1=y,x2=x-1,y2=y-1+((y&1)<<1); | |
applit(x1,y1,1,0),newlit(x,y,7,1),outclause(); | |
applit(x2,y2,1,0),newlit(x,y,7,1),outclause(); | |
applit(x1,y1,1,0),applit(x2,y2,1,0),newlit(x,y,7,2),outclause(); | |
applit(x1,y1,0,0),applit(x2,y2,0,0),newcomplit(x,y,7,1),outclause(); | |
applit(x1,y1,0,0),newcomplit(x,y,7,2),outclause(); | |
applit(x2,y2,0,0),newcomplit(x,y,7,2),outclause(); | |
} | |
@ Fortunately the |b| subroutine {\it can\/} be shared (since |x| is always | |
odd), thus saving half of the sixteen clauses generated. | |
@<Sub...@>= | |
void b(int x,int y) { | |
register j,k,xx=x,y1=y-(y&2),y2=y+(y&2); | |
if (have_b[x][y]!=tt+1) { | |
d(xx,y1); | |
e(xx,y2); | |
for (j=0;j<3;j++) for (k=0;k<3;k++) if (j+k) { | |
if (j) newcomplit(xx,y1,4,j); /* $\bar d_j$ */ | |
if (k) newcomplit(xx,y2,5,k); /* $\bar e_k$ */ | |
newlit(x,y,2,j+k); /* $b_{j+k}$ */ | |
outclause(); | |
if (j) newlit(xx,y1,4,3-j); /* $d_{3-j}$ */ | |
if (k) newlit(xx,y2,5,3-k); /* $e_{3-k}$ */ | |
newcomplit(x,y,2,5-j-k); /* $\bar b_{5-j-k}$ */ | |
outclause(); | |
} | |
have_b[x][y]=tt+1; | |
} | |
} | |
@ The (unshared) |c| subroutine handles the other four neighbors, | |
by working with |f| and |g| instead of |d| and~|e|. | |
If |y=0|, the overlap rules set |y1=-1|, which can be problematic. | |
I've decided to avoid this case by omitting |f| when it is | |
guaranteed to be zero. | |
@<Sub...@>= | |
void c(int x,int y) { | |
register j,k,x1,y1; | |
if (x&1) x1=x+2,y1=(y-1)|1; | |
else x1=x,y1=y&-2; | |
g(x,y); | |
if (x1-1<xmin || x1-1>xmax || y1+1<ymin || y1>ymax) | |
@<Set |c| equal to |g|@>@; | |
else { | |
f(x1,y1); | |
for (j=0;j<3;j++) for (k=0;k<3;k++) if (j+k) { | |
if (j) newcomplit(x1,y1,6,j); /* $\bar f_j$ */ | |
if (k) newcomplit(x,y,7,k); /* $\bar g_k$ */ | |
newlit(x,y,3,j+k); /* $c_{j+k}$ */ | |
outclause(); | |
if (j) newlit(x1,y1,6,3-j); /* $f_{3-j}$ */ | |
if (k) newlit(x,y,7,3-k); /* $g_{3-k}$ */ | |
newcomplit(x,y,3,5-j-k); /* $\bar c_{5-j-k}$ */ | |
outclause(); | |
} | |
} | |
} | |
@ @<Set |c| equal to |g|@>= | |
{ | |
for (k=1;k<3;k++) { | |
newcomplit(x,y,7,k),newlit(x,y,3,k),outclause(); /* $\bar g_k\lor c_k$ */ | |
newlit(x,y,7,k),newcomplit(x,y,3,k),outclause(); /* $g_k\lor\bar c_k$ */ | |
} | |
newcomplit(x,y,3,3),outclause(); /* $\bar c_3$ */ | |
newcomplit(x,y,3,4),outclause(); /* $\bar c_4$ */ | |
} | |
@ Totals over all eight neighbors are then deduced by the |a| | |
subroutine. | |
@<Sub...@>= | |
void a(int x,int y) { | |
register j,k,xx=x|1; | |
b(xx,y); | |
c(x,y); | |
for (j=0;j<5;j++) for (k=0;k<5;k++) if (j+k>1 && j+k<5) { | |
if (j) newcomplit(xx,y,2,j); /* $\bar b_j$ */ | |
if (k) newcomplit(x,y,3,k); /* $\bar c_k$ */ | |
newlit(x,y,1,j+k); /* $a_{j+k}$ */ | |
outclause(); | |
} | |
for (j=0;j<5;j++) for (k=0;k<5;k++) if (j+k>2 && j+k<6 && j*k) { | |
if (j) newlit(xx,y,2,j); /* $b_j$ */ | |
if (k) newlit(x,y,3,k); /* $c_k$ */ | |
newcomplit(x,y,1,j+k-1); /* $\bar a_{j+k-1}$ */ | |
outclause(); | |
} | |
} | |
@ Finally, as mentioned at the beginning, $z'$ is determined | |
from $z$, $a_2$, $a_3$, and $a_4$. | |
I actually generate six clauses, not five, in order to stick to | |
{\mc 3SAT}. | |
@<Sub...@>= | |
void zprime(int x,int y) { | |
newcomplit(x,y,1,4),applit(x,y,1,1),outclause(); /* $\bar a_4\bar z'$ */ | |
newlit(x,y,1,2),applit(x,y,1,1),outclause(); /* $a_2\bar z'$ */ | |
newlit(x,y,1,3),applit(x,y,0,0),applit(x,y,1,1),outclause(); | |
/* $a_3z\bar z'$ */ | |
newcomplit(x,y,1,3),newlit(x,y,1,4),applit(x,y,0,1),outclause(); | |
/* $\bar a_3a_4z'$ */ | |
applit(x,y,0,7),newcomplit(x,y,1,2),newlit(x,y,1,4),outclause(); | |
/* $x\bar a_2a_4$ */ | |
applit(x,y,1,7),applit(x,y,1,0),applit(x,y,0,1),outclause(); | |
/* $\bar x\bar zz'$ */ | |
} | |
@*Index. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment