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