Format fix + unrot13 text in 1992/gson/README.md

This commit is contained in:
Cody Boone Ferguson committed 2023-10-30 06:37:01 -07:00
1 parent 102675d4dd
commit 6ac877dba9
1 file changed
+136 -135
+136 -135
View File
@@ -1,9 +1,6 @@
# Most Humorous Output
Andreas Gustafsson\
Helsinki University of Technology\
Arentikuja 1 D 305\
00410 Helsinki\
Finland
@@ -69,16 +66,16 @@ The name of the game:
AG is short for either Anagram Generator or simply AnaGram. It might also be
construed to mean Alphabet Game, and by pure coincidence it happens to be the
author's initials.
\
\
### What it does
\
AG takes one or more words as arguments, and tries to find anagrams of those
words, i.e. words or sentences containing exactly the same letters.
### How to use it
\
To run AG, you need a dictionary file consisting of distinct words in the
natural language of your choice, one word on each line. If your machine doesn't
have one already, you can make your own dictionary by concatenating a few
@@ -102,7 +99,7 @@ limit can be changed using a numeric command line option, as in
`./ag -4 international obfuscated c code contest </usr/dict/words`.
### Bugs
\
- There is no error checking.
- Standard input must be seekable, so you can't pipe the dictionary into AG.
- The input sentence and each line in the dictionary may contain at most 32
@@ -115,8 +112,8 @@ less.
### Obfuscatory notes
\
As you can see, AG takes advantage of the new '92 whitespace rules to
As you can see, AG takes advantage of the new '92 whitespace rules' to
achieve a clear, readable, self-documenting layout. The identifiers
have been chosen in a way appropriate for an alphabet game, and common
sources of bugs such as goto statements and malloc/free have been
@@ -124,135 +121,139 @@ eliminated. As AG also refrains from abusing the preprocessor, it
doesn't really have much to offer in terms of "surface obfuscation".
Instead, it tries to achieve both its speed and its obscurity through a
careful choice of algorithms. Some of the finer points of those
algorithms are outlined in the rot-13 encoded spoiler below.
algorithms are outlined in the spoiler below.
### How it works: (ROT13 to read)
Here follows a description of some of the data structures and
algorithms used by AG. It is by no means complete, but it may help
you get an idea about the general principles.
--
Internally, AG represents words and sentences as arrays of 32
4-bit integer elements. Each element represents the number of
times a letter occurs in the word/sentence. There are 32 elements
because 32 is a convenient power of two larger than the number of
letters in most western alphabets, and the elements are 4 bits
each because the same letter is unlikely to occur more than 15
times in a practical anagram generation problem.
These `32*4`-bit arrays are actually stored in memory in a
"bit-transposed" format, as arrays of four "long" values. It is
assumed that a "long" is at least 32 bits. The first 4-bit letter
count is formed by the least significant (2^0) bit in each of the
four longs, the next one is formed by the 2^1 bits, etc.
This storage format makes it possible to add or subtract two such
vectors of 32 4-bit values in parallel by simulating a set of 32
binary full adders in software using bitwise logical operations.
E.g., all the LSB:s of the result are formed in parallel by taking
the exclusive OR of the LSB:s in each summand, and 32 carry bits
are formed in parallel in a similar way using a logical AND.
Thus, 32 independent 4-bit additions can be performed by just four
iterations of a loop containing some 32-bit bitwise logical
operations, but no arithmetic operations other than those implied
by array indexing.
Subtraction works similarly, and in fact AG only implements
subtraction directly, handling addition by means of the identity
`a+b = a-(0-b)`.
In addition to this `32*4`-bit representation, AG also forms a so-called
"signature" that is the bitwise OR of the four longs, which is
equivalent to saying that the signature of a word contains a logical 1
in the bit positions corresponding to letters occurring at least once
in that word.
The first thing AG does is to construct a lookup table of 256
longs, one for each 8-bit character value. The entry for a
character will be zero if that character doesn't appear in the
sentence given on the command line, or it will have a single bit
set if the character does appear in the sentence. By adding
together the bit masks for all the letters in the input sentence
using the transpose addition method described above, AG forms the
`32*4` bit array representation of the input sentence.
The next action performed is reading the dictionary. Those words that
contain letters not in the input sentence are immediately discarded.
Words containing the right letters but in excessive numbers are
eliminated in a separate check involving the `32*4` bit array.
The remaining words, which will be referred to as "candidate words",
are stored in `32*4`-bit representation, together with their signatures\
and offsets into the dictionary file so that the plain-text version of
a word can later be found for printing. This information is kept in a
local "struct" in the dictionary-reading function, and memory is
allocated for each candidate word simply by making another recursive
call to that function.
Each struct so allocated is linked into a fixed-size hash table of
4096 entries indexed by the 12 low bits of the word's signature.\
When the dictionary-reading function encounters end-of-file, all the
candidate words have been stored in nested activation records on the
stack, accessible through the hash table.
Generating the anagrams is then done by traversing the hash table and
subtracting the letters of each word in the hash table from the
"current sentence", which initially is the sentence given on the
command line.
The subtraction is performed in parallel on the 4-bit letter counts
as described above, and if all 32 results are zero, an anagram has
been found. If the result is negative for one or more of the letters
(as indicated by one or more "1" in a vector of 32 borrow bits
returned by the subtraction routine), the word did not match the
current sentence and is ignored. Finally, if the result contained
only non-negative letter counts, we have found a partial anagram:\
a word containing some, but not all, of the letters in the current
sentence. In this case we recursively try to find an anagram of the
remaining letters. The depth of the recursion is limited to the
maximum number of words in the anagram, as specified by the user.
When the deepest recursion level has been reached, an optimization can
be applied: because no further recursion will be done, there is no
need to look for partial anagrams, and therefore AG only needs to
check for words that contain exactly the same letters as the current
sentence. Those words can be found simply by indexing the hash table
with the signature of the current sentence.
Even when not on the deepest recursion level, AG generally avoids
examining all the entries of the hash table. The idea is that we are
not interested in hash buckets whose words contain any letters not
in the current sentence; these buckets are exactly those whose index
has a logical one in a bit position where the signature of the current
sentence has a zero. Put another way, we want to loop through only
those hash bucket indices "i" that contain zeroes in all the bit
positions where the signature "s" of the current sentence contains
a zero; this can be expressed in C as (i & ~s == 0).
It is possible to loop through all such numbers in an efficient way by
taking advantage of certain properties of binary arithmetic: by
forcing the bits corresponding to zeroes in "s" to ones, we can make
the carries generated in incrementing "i" propagate straight across
those bits that should remain zero. For example, the following
program prints all those 16-bit integers that contain zeroes in all
even bit positions:
```c
main(){int i=0,s=0xAAAA;do{printf("%04x\t",i);}while(i=((i|~s)+1)&s);}
```
AG uses a similar method but works in the opposite direction, finding
the next lower value with zeroes in given bit positions by propagating
borrows across those bits. Some additional adjustments are made
to the hash table index when initiating a recursive search, using
similar bit-twiddling techniques.
Whenever an anagram has been found, it is printed by traversing a
linked list formed by structs in the activation records of the
recursive invocations of the search function, seeking to the beginning
of the word matched by that invocation, and copying the characters of
the word directly from standard input to standard output.
Urer sbyybjf n qrfpevcgvba bs fbzr bs gur qngn fgehpgherf naq
nytbevguzf hfrq ol NT. Vg vf ol ab zrnaf pbzcyrgr, ohg vg znl uryc
lbh trg na vqrn nobhg gur trareny cevapvcyrf.
\
--
\
Vagreanyyl, NT ercerfragf jbeqf naq fragraprf nf neenlf bs 32
4-ovg vagrtre ryrzragf. Rnpu ryrzrag ercerfragf gur ahzore bs
gvzrf n yrggre bpphef va gur jbeq/fragrapr. Gurer ner 32 ryrzragf
orpnhfr 32 vf n pbairavrag cbjre bs gjb ynetre guna gur ahzore bs
yrggref va zbfg jrfgrea nycunorgf, naq gur ryrzragf ner 4 ovgf
rnpu orpnhfr gur fnzr yrggre vf hayvxryl gb bpphe zber guna 15
gvzrf va n cenpgvpny nantenz trarengvba ceboyrz.
\
Gurfr 32*4-ovg neenlf ner npghnyyl fgberq va zrzbel va n
"ovg-genafcbfrq" sbezng, nf neenlf bs sbhe "ybat" inyhrf. Vg vf
nffhzrq gung n "ybat" vf ng yrnfg 32 ovgf. Gur svefg 4-ovg yrggre
pbhag vf sbezrq ol gur yrnfg fvtavsvpnag (2^0) ovg va rnpu bs gur
sbhe ybatf, gur arkg bar vf sbezrq ol gur 2^1 ovgf, rgp.
\
Guvf fgbentr sbezng znxrf vg cbffvoyr gb nqq be fhogenpg gjb fhpu
irpgbef bs 32 4-ovg inyhrf va cnenyyry ol fvzhyngvat n frg bs 32
ovanel shyy nqqref va fbsgjner hfvat ovgjvfr ybtvpny bcrengvbaf.
R.t., nyy gur YFO:f bs gur erfhyg ner sbezrq va cnenyyry ol gnxvat
gur rkpyhfvir BE bs gur YFO:f va rnpu fhzznaq, naq 32 pneel ovgf
ner sbezrq va cnenyyry va n fvzvyne jnl hfvat n ybtvpny NAQ.
Guhf, 32 vaqrcraqrag 4-ovg nqqvgvbaf pna or cresbezrq ol whfg sbhe
vgrengvbaf bs n ybbc pbagnvavat fbzr 32-ovg ovgjvfr ybtvpny
bcrengvbaf, ohg ab nevguzrgvp bcrengvbaf bgure guna gubfr vzcyvrq
ol neenl vaqrkvat.
\
Fhogenpgvba jbexf fvzvyneyl, naq va snpg NT bayl vzcyrzragf
fhogenpgvba qverpgyl, unaqyvat nqqvgvba ol zrnaf bs gur vqragvgl
n+o = n-(0-o).
\
Va nqqvgvba gb guvf 32*4-ovg ercerfragngvba, NT nyfb sbezf n fb-pnyyrq
"fvtangher" gung vf gur ovgjvfr BE bs gur sbhe ybatf, juvpu vf
rdhvinyrag gb fnlvat gung gur fvtangher bs n jbeq pbagnvaf n ybtvpny 1
va gur ovg cbfvgvbaf pbeerfcbaqvat gb yrggref bppheevat ng yrnfg bapr
va gung jbeq.
\
Gur svefg guvat NT qbrf vf gb pbafgehpg n ybbxhc gnoyr bs 256
ybatf, bar sbe rnpu 8-ovg punenpgre inyhr. Gur ragel sbe n
punenpgre jvyy or mreb vs gung punenpgre qbrfa'g nccrne va gur
fragrapr tvira ba gur pbzznaq yvar, be vg jvyy unir n fvatyr ovg
frg vs gur punenpgre qbrf nccrne va gur fragrapr. Ol nqqvat
gbtrgure gur ovg znfxf sbe nyy gur yrggref va gur vachg fragrapr
hfvat gur genafcbfr nqqvgvba zrgubq qrfpevorq nobir, NT sbezf gur
32*4 ovg neenl ercerfragngvba bs gur vachg fragrapr.
\
Gur arkg npgvba cresbezrq vf ernqvat gur qvpgvbanel. Gubfr jbeqf gung
pbagnva yrggref abg va gur vachg fragrapr ner vzzrqvngryl qvfpneqrq.
Jbeqf pbagnvavat gur evtug yrggref ohg va rkprffvir ahzoref ner
ryvzvangrq va n frcnengr purpx vaibyivat gur 32*4 ovg neenl.
\
Gur erznvavat jbeqf, juvpu jvyy or ersreerq gb nf "pnaqvqngr jbeqf",
ner fgberq va 32*4-ovg ercerfragngvba, gbtrgure jvgu gurve fvtangherf\
naq bssfrgf vagb gur qvpgvbanel svyr fb gung gur cynva-grkg irefvba bs
n jbeq pna yngre or sbhaq sbe cevagvat. Guvf vasbezngvba vf xrcg va n
ybpny "fgehpg" va gur qvpgvbanel-ernqvat shapgvba, naq zrzbel vf
nyybpngrq sbe rnpu pnaqvqngr jbeq fvzcyl ol znxvat nabgure erphefvir
pnyy gb gung shapgvba.
\
Rnpu fgehpg fb nyybpngrq vf yvaxrq vagb n svkrq-fvmr unfu gnoyr bs
4096 ragevrf vaqrkrq ol gur 12 ybj ovgf bs gur jbeq'f fvtangher.\
Jura gur qvpgvbanel-ernqvat shapgvba rapbhagref raq-bs-svyr, nyy gur
pnaqvqngr jbeqf unir orra fgberq va arfgrq npgvingvba erpbeqf ba gur
fgnpx, npprffvoyr guebhtu gur unfu gnoyr.
\
Trarengvat gur nantenzf vf gura qbar ol genirefvat gur unfu gnoyr naq
fhogenpgvat gur yrggref bs rnpu jbeq va gur unfu gnoyr sebz gur
"pheerag fragrapr", juvpu vavgvnyyl vf gur fragrapr tvira ba gur
pbzznaq yvar.
\
Gur fhogenpgvba vf cresbezrq va cnenyyry ba gur 4-ovg yrggre pbhagf
nf qrfpevorq nobir, naq vs nyy 32 erfhygf ner mreb, na nantenz unf
orra sbhaq. Vs gur erfhyg vf artngvir sbe bar be zber bs gur yrggref
(nf vaqvpngrq ol bar be zber "1" va n irpgbe bs 32 obeebj ovgf
erghearq ol gur fhogenpgvba ebhgvar), gur jbeq qvq abg zngpu gur
pheerag fragrapr naq vf vtaberq. Svanyyl, vs gur erfhyg pbagnvarq
bayl abaartngvir yrggre pbhagf, jr unir sbhaq n cnegvny nantenz:\
n jbeq pbagnvavat fbzr, ohg abg nyy, bs gur yrggref va gur pheerag
fragrapr. Va guvf pnfr jr erphefviryl gel gb svaq na nantenz bs gur
erznvavat yrggref. Gur qrcgu bs gur erphefvba vf yvzvgrq gb gur
znkvzhz ahzore bs jbeqf va gur nantenz, nf fcrpvsvrq ol gur hfre.
\
Jura gur qrrcrfg erphefvba yriry unf orra ernpurq, na bcgvzvmngvba pna
or nccyvrq: orpnhfr ab shegure erphefvba jvyy or qbar, gurer vf ab
arrq gb ybbx sbe cnegvny nantenzf, naq gurersber NT bayl arrqf gb
purpx sbe jbeqf gung pbagnva rknpgyl gur fnzr yrggref nf gur pheerag
fragrapr. Gubfr jbeqf pna or sbhaq fvzcyl ol vaqrkvat gur unfu gnoyr
jvgu gur fvtangher bs gur pheerag fragrapr.
\
Rira jura abg ba gur qrrcrfg erphefvba yriry, NT trarenyyl nibvqf
rknzvavat nyy gur ragevrf bs gur unfu gnoyr. Gur vqrn vf gung jr ner
abg vagrerfgrq va unfu ohpxrgf jubfr jbeqf pbagnva nal yrggref abg
va gur pheerag fragrapr; gurfr ohpxrgf ner rknpgyl gubfr jubfr vaqrk
unf n ybtvpny bar va n ovg cbfvgvba jurer gur fvtangher bs gur pheerag
fragrapr unf n mreb. Chg nabgure jnl, jr jnag gb ybbc guebhtu bayl
gubfr unfu ohpxrg vaqvprf "v" gung pbagnva mrebrf va nyy gur ovg
cbfvgvbaf jurer gur fvtangher "f" bs gur pheerag fragrapr pbagnvaf
n mreb; guvf pna or rkcerffrq va P nf (v & ~f == 0).
\
Vg vf cbffvoyr gb ybbc guebhtu nyy fhpu ahzoref va na rssvpvrag jnl ol
gnxvat nqinagntr bs pregnva cebcregvrf bs ovanel nevguzrgvp: ol
sbepvat gur ovgf pbeerfcbaqvat gb mrebrf va "f" gb barf, jr pna znxr
gur pneevrf trarengrq va vaperzragvat "v" cebcntngr fgenvtug npebff
gubfr ovgf gung fubhyq erznva mreb. Sbe rknzcyr, gur sbyybjvat
cebtenz cevagf nyy gubfr 16-ovg vagrtref gung pbagnva mrebrf va nyy
rira ovg cbfvgvbaf:
\
znva(){vag v=0,f=0kNNNN;qb{cevags("%04k\g",v);}juvyr(v=((v|~f)+1)&f);}
\
NT hfrf n fvzvyne zrgubq ohg jbexf va gur bccbfvgr qverpgvba, svaqvat
gur arkg ybjre inyhr jvgu mrebrf va tvira ovg cbfvgvbaf ol cebcntngvat
obeebjf npebff gubfr ovgf. Fbzr nqqvgvbany nqwhfgzragf ner znqr
gb gur unfu gnoyr vaqrk jura vavgvngvat n erphefvir frnepu, hfvat
fvzvyne ovg-gjvqqyvat grpuavdhrf.
\
Jurarire na nantenz unf orra sbhaq, vg vf cevagrq ol genirefvat n
yvaxrq yvfg sbezrq ol fgehpgf va gur npgvingvba erpbeqf bs gur
erphefvir vaibpngvbaf bs gur frnepu shapgvba, frrxvat gb gur ortvaavat
bs gur jbeq zngpurq ol gung vaibpngvba, naq pbclvat gur punenpgref bs
gur jbeq qverpgyl sebz fgnaqneq vachg gb fgnaqneq bhgchg.
## Copyright and CC BY-SA 4.0 License: