A torture test for TEX

by Donald E. Knuth Stanford University

(Version 3, January 1990)

Programs that claim to be implementations of TEX82 are supposed to be able to process the test routine contained in this report, producing the outputs contained in this report.

The preparation of this report was supported in part by the National Science Foundation under grants IST-8201926 and MCS-8300984, and by the System Development Foundation. ‘TEX’ is a trademark of the American Mathematical Society.

Introduction. People often think that their programs are “debugged” when large applications have been run successfully. But system programmers know that a typical large application tends to use at most about 50 per cent of the instructions in a typical compiler. Although the other half of the code—which tends to be the “harder half”—might be riddled with errors, the system seems to be working quite impressively until an unusual case shows up on the next day. And on the following day another error manifests itself, and so on; months or years go by before certain parts of the compiler are even activated, much less tested in combination with other portions of the system, if user applications provide the only tests.

How then shall we go about testing a compiler? Ideally we would like to have a formal proof of correctness, certified by a computer. This would give us a lot of confidence, although of course the formal verification program might itself be incorrect. A more serious drawback of automatic verification is that the formal specifications of the compiler are likely to be wrong, since they aren’t much easier to write than the compiler itself. Alternatively, we can substitute an informal proof of correctness: The programmer writes his or her code in a structured manner and checks that appropriate relations remain invariant, etc. This helps greatly to reduce errors, but it cannot be expected to remove them completely; the task of checking a large system is sufficiently formidable that human beings cannot do it without making at least a few slips here and there.

Thus, we have seen that test programs are unsatisfactory if they are simply large user applications; yet some sort of test program is needed because proofs of correctness aren’t adequate either. People have proposed schemes for constructing test data automatically from a program text, but such approaches run the risk of circularity, since they cannot assume that a given program has the right structure.

I have been having good luck with a somewhat different approach, first used in 1960 to debug an ALGOL compiler. The idea is to construct a test file that is about as different from a typical user application as could be imagined. Instead of testing things that people normally want to do, the file tests complicated things that people would never dare to think of, and it embeds these complexities in still more arcane constructions. Instead of trying to make the compiler do the right thing, the goal is to make it fail (until the bugs have all been found).

To write such a fiendish test routine, one simply gets into a nasty frame of mind and tries to do everything in the unexpected way. Parameters that are normally positive are set negative or zero; borderline cases are pushed to the limit; deliberate errors are made in hopes that the compiler will not be able to recover properly from them.

A user’s application tends to exercise 50% of a compiler’s logic, but my first fiendish tests tend to improve this to about 90%. As the next step I generally make use of frequency-counting software to identify the instructions that have still not been called upon. Then I add ever more fiendishness to the test routine, until more than 99% of the code has been used at least once. (The remaining bits are things that can occur only if the source program is really huge, or if certain fatal errors are detected; or they are cases so similar to other well-tested things that there can be little doubt of their validity.)

Of course, this is not guaranteed to work. But my experience in 1960 was that only two bugs were ever found in that ALGOL compiler after it correctly translated that original fiendish test. And one of those bugs was actually present in the results of the test; I simply had failed to notice that the output was incorrect. Similar experiences occurred later during the 60s and 70s, with respect to a few assemblers, compilers, and simulators that I wrote.

This method of debugging, combined with the methodology of structured programming and informal proofs (otherwise known as careful desk checking), leads to greater reliability of production software than any other method I know. Therefore I have used it in developing TEX82, and the main bulk of this report is simply a presentation of the test program that was used to get the bugs out of TEX.

Such a test file is useful also after a program has been debugged, since it can be used to give some assurance that subsequent modifications don’t mess things up.

The test file is called TRIP.TEX, because of my warped sense of humor: TEX is pronounced “techhh”, so the name reminded me of a triptych (and besides, I wanted to take a trip through the program while tripping it up, etc.).

The contents of this test file are so remote from what people actually do with TEX, I feel apologetic if I have to explain the correct translation of TRIP.TEX; nobody really cares about most of the nitty-gritty rules that are involved. Yet I believe TRIP exemplifies the sort of test program that has outstanding diagnostic ability, as explained above.

If somebody claims to have a correct implementation of TEX, I will not believe it until I see that TRIP.TEX is translated properly. I propose, in fact, that a program must meet two criteria before it can justifiably be called TEX: (1) The person who wrote it must be happy with the way it works at his or her installation; and (2) the program must produce the correct results from TRIP.TEX.

TEX is in the public domain, and its algorithms are published; I’ve done this since I do not want to discourage its use by placing proprietary restrictions on the software. However, I don’t want faulty imitations to masquerade as TEX processors, since users want TEX to produce identical results on different machines. Hence I am planning to do whatever I can to suppress any systems that call themselves TEX without meeting conditions (1) and (2). I have copyrighted the programs so that I have some chance to forbid unauthorized copies; I explicitly authorize copying of correct TEX implementations, and not of incorrect ones!

The remainder of this report consists of appendices, whose contents ought to be described briefly here: Appendix A explains in detail how to carry out a test of TEX, given a tape that contains copies of the

other appendices.

Appendix B is TRIP.TEX, the fiendish test file that has already been mentioned. People who think that they understand TEX are challenged to see if they know what TEX is supposed to do with this file. People who know only a little about TEX might still find it interesting to study Appendix B, just to get some insights into the methodology advocated here.

Appendix C is TRIP.PL, the property-list description of a special font called trip. This is the only font used by TRIP.TEX. There are no graphic characters associated with trip that could possibly be printed; indeed, TRIP.PL describes the properties of a font that is as weird as the “document” described by TRIP.TEX.

Appendix D is TRIPIN.LOG, a correct transcript file TRIP.LOG that results if INITEX is applied to TRIP.TEX. (INITEX is the name of a version of TEX that does certain initializations; this run of INITEX also creates a binary format file called TRIP.FMT.)

Appendix E is a correct transcript file TRIP.LOG that results if INITEX or any other version of TEX is applied to TRIP.TEX with format TRIP.FMT.

Appendix F is TRIP.TYP, the symbolic version of a correct output file TRIP.DVI that was produced at the same time as the TRIP.LOG file of Appendix E.

Appendix G is TRIPOS.TEX, a short file written out and read in by TEX when it processes TRIP.TEX. Appendix H is TRIP.FOT, an abbreviated version of Appendix E that appears on the user’s terminal

during the run that produces TRIP.LOG and TRIP.DVI.

The debugging of TEX and the testing of the adequacy of TRIP.TEX could not have been done nearly as well as reported here except for the magnificent software support provided by my colleague David R. Fuchs. In particular, he extended our local Pascal compiler so that frequency counting and a number of other important features were added to its online debugging abilities.

The method of testing advocated here has one chief difficulty that deserves comment: I had to verify by hand that TEX did the right things to TRIP.TEX. This took many hours, and perhaps I have missed something (as I did in 1960); I must confess that I have not checked every single number in Appendices E and F. However, I’m willing to pay $327.68 to the first finder of any remaining bug in TEX, and I will be surprised if that bug doesn’t show up also in Appendix E. (I plan to write a technical report about all of the errors ultimately found in TEX; that report will tell whether any bugs are discovered between now and then!)

Appendix A: How to test TEX.

0. Let’s assume that you have a tape containing TRIP.TEX, TRIP.PL, TRIPIN.LOG, TRIP.LOG, TRIP.TYP, and TRIP.FOT, as in Appendices B, C, D, E, F, and G. Furthermore, let’s suppose that you have a working WEB system, and that you have working programs TFtoPL, PLtoTF, DVItype, as described in the TEXware report.

1. Use PLtoTF to convert TRIP.PL into TRIP.TFM. Then use TFtoPL to convert TRIP.TFM into TMP.PL.

Check that TMP.PL is identical to TRIP.PL (this is a partial test of PLtoTF and TFtoPL). Install TRIP.TFM in the standard file area for TEX font metric files.

2. Prepare a special version of INITEX. (This means that your WEB change file should have init and tini defined to be null.) The stat and tats macros should also be null, so that statistics are kept and other special features are enabled. Set mem min and mem bot equal to 1, and set mem top and mem max equal to 3000, for purposes of this test version. Also set error line = 64, half error line = 32, and max print line = 72; these parameters affect many of the lines of the test output, so your job will be much easier if you use the same settings that were used to produce Appendix E. You probably should also use the “normal” settings of other parameters found in TEX.WEB (e.g., stack size = 200, font max = 75, etc.), since these show up in a few lines of the test output. Your test version should not change the default definition of unprintable characters (§49 of the program).

3. Run the INITEX prepared in step 2. In response to the first ‘**’ prompt, type carriage return (thus getting another ‘**’). Then type ‘\input trip’. You should get an output that matches the file TRIPIN.LOG (Appendix D). Don’t be alarmed by the error messages that you see, unless they are different from those in Appendix D.

4. Run INITEX again. This time type ‘␣&trip␣␣trip␣’. (The spaces in this input help to check certain parts of TEX that aren’t otherwise used.) You should get outputs TRIP.LOG, TRIP.DVI, and TRIPOS.TEX; there will also be an empty file 8TERMINAL.TEX. Furthermore, your terminal should receive output that matches TRIP.FOT (Appendix H). During the middle part of this test, however, the terminal will not be getting output, because \batchmode is being tested; don’t worry if nothing seems to be happening for a while—nothing is supposed to.

5. Compare the TRIP.LOG file from step 4 with the “master” TRIP.LOG file of step 0. (Let’s hope you put that master file in a safe place so that it wouldn’t be clobbered.) There should be perfect agreement between these files except in the following respects:

a) The dates and possibly the file names will naturally be different.

b) Glue settings in the displays of TEX boxes are subject to system-dependent rounding, so slight deviations are permissible. However, such deviations apply only to the ‘glue set’ values that appear at the end of an \hbox or \vbox line; all other numbers should agree exactly, since they are computed with integer arithmetic in a prescribed system-independent manner.

c) The amount of space in kerns that are marked “for accent” are, similarly, subject to systemdependent rounding.

d) If you had different values for stack size, buf size, etc., the corresponding capacity values will be different when they are printed out at the end.

e) Help messages may be different; indeed, the author encourages non-English help messages in versions of TEX for people who don’t understand English as well as some other language.

f) The total number and length of strings at the end may well be different.

g) If your TEX uses a different memory allocation or packing scheme or DVI output logic, the memory

usage statistics may change.

6. Use DVItype to convert your file TRIP.DVI to a file TRIP.TYP. The following options should be set when

using DVItype:

Output level = 2

                 Starting page = *.*.*.*.*.*.*.*.*.*

Number of pages = 1000000  (this is the default)
Resolution = 7227/100  (this is one point per pixel)
New magnification = 0  (this is the default)

The resulting file should agree with the master TRIP.TYP file of step 0, except that some of the values might be a little off due to floating-point rounding discrepancies. Furthermore there may be differences between ‘right ’ and ‘w ’ and ‘x ’ commands, and between ‘down’ and ‘y ’ and ‘z ’; the key thing is that all characters and rules and xxx ’s should be in almost the same positions as specified in Appendix F. (If your DVI-writing routines differ substantially from those in TEX.WEB, you may want to write a DVIcompare program that detects any substantive differences between two given DVI files. Such a routine would be of general use besides. On the other hand, if you have set dvi buf size to 800, then your DVI file should be virtually identical to the one supplied.)

7. You might also wish to test TRIP with other versions of TEX (i.e., VIRTEX or a production version with other fonts and macros preloaded). It should work unless TEX’s primitives have been redefined. However, this step isn’t essential, since all the code of VIRTEX appears in INITEX; you probably won’t catch any more errors this way, unless they would already become obvious from normal use of the system.

Appendix B: The TRIP.TEX file. The contents of the test routine are prefixed here with line numbers, for ease in comparing this file with the error messages printed later; the line numbers aren’t actually present.

 1 % This is a diabolical test file for TeX82. Watch your step.
 2 \immediate\catcode `{ = 1 \endlinechar=13
 3 \catcode `} = 2
 4 \catcode `$ = 3 {\catcode`$13\gdef\dol{$}}
 5 \catcode `& = 4
 6 \let\paR=\par
 7 \let\%=\relax
 8 \outer\xdef\par{\catcode `\% 14}
 9 % this line should change % from type 5 to type 14
10 \let\par=\paR \defaulthyphenchar=`- \defaultskewchar=256
11 \ifx\initex\undefined  \def\initex{} % next lines are skipped if format loaded
12 \catcode `# = 6 \catcode `U=\catcode`# % # for parameters
13 \catcode `^= 7 \catcode `| = 8 % ^for superscripts and | for subscripts
14 \catcode `~ = 9 % ~ will be ignored
15 \catcode `* = 10 % * will be like a space
16 \catcode `E = 12 % E is not a letter
17 \catcode`\@ = 15 % @ will be invalid
18 \catcode `^^A = 0008 % this is another way to get a subscript
19 \catcode `\^^@ = 11 % a strange letter will be allowed
20 \catcode `\^^? = \badness % and so will a strange escape delimiter
21 \fontdimen12\nullfont=13pt % give the null font more parameters
22 \font\trip = trip\relax % see TRIP.PL for details of this font
23 ^^?trip  \font\smalltrip=trip scaled 500 % this will be our symbols font
24 \global\fontdimen22\smalltrip 7pt % the axis height
25 \textfont2=\smalltrip \scriptfont2 \smalltrip \scriptscriptfont2 \smalltrip
26 \nonstopmode\lccode256-0\mathchardef\a="8000\def\a{ SCALED 3~2769}
27 \font\rip=trip\a % font \rip will be the same as \trip
28 \skewchar\rip=`B \countdef\countz % \countz will be \count0
29 \def\on{1} \toksdef\tokens=256 \show\errorstopmode
30 \showthe\font \showthe\pageshrink \showthe\pagegoal
31 \font\bigtr^^@p=trip at20pt\textfont3=\bigtr^^@p % this will be extension font
32 \skip200 = 10pt plUs5fil\ifdim\hsize<\hsize\fi lllminus 0 fill
33 \setbox200=\vbox{\hrule\vskip\skip200} \wd200-2pt \setbox100=\hbox{A}
34 \skipdef\shkip100\shkip -18pt plus\catcode`\}fil minus 10fil
35 \advance\shkip by \skip200 \dimen33=3pt \count33=-\dimen33
36 \divide\shkip by \count33
37 \multiply\shkip by \count33 % so \skip100=-6pt plus 3filll minus 9fil
38 \count200 -5 \multiply\count200 by -100 % \count200 is 500
39 \count100=1000000 \divide\count100 by \count200 % \count100 is 2000
40 \dimen100=,00152587890625in % (100/65536)in = 7227sp
41 \multiply\dimen100 by 65536 \divide\dimen100 by 9 % \dimen100 is 803pt
42 \lineskip 0pt plus 40pt
43 \baselineskip=10pt plus 41pt
44 \parskip -0pt plus 42pt minus 8pt
45 \splittopskip 1pt plus 43pt
46 \splitmaxdepth -2pt \boxmaxdepth 1000pt
47 \belowdisplayskip 3pt plus 44pt minus\baselineskip \abovedisplayskip3pt
48 \abovedisplayshortskip 1pt plus 45pt minus\dimen100
49 \belowdisplayshortskip -\count33sp plus 46pt
50 \global\mathchardef\minus"232D % mathbin, family 3, character "2D (-)

51 \thinmuskip 1mu plus 2fill minus 3mu
52 \medmuskip 2mu minus 3mu
53 \thickmuskip -4mu
54 \def\gobble#1{} \floatingpenalty 100 \holdinginserts1
55 \everypar{A\insert200{\baselineskip400pt\splittopskip\count15pt\hbox{\vadjust
56  {\penalty999}}\hbox to -10pt{}}\showthe\pagetotal\showthe\pagegoal
57  \advance\count15by1\mark{\the\count15}\splitmaxdepth-1pt
58  \paR\gobble} % this aborts every paragraph abruptly
59 \def\weird#1{\csname\expandafter\gobble\string#1 \string\csname\endcsname}
60 \message{\the\output\weird\one on line \the\inputlineno}
61 \hyphenpenalty 88 \exhyphenpenalty 89 \badness
62 \clubpenalty 125 \widowpenalty 125 \displaywidowpenalty -125
63 \brokenpenalty 37
64 \interlinepenalty -125
65 \doublehyphendemerits 1000
66 \finalhyphendemerits 100000
67 \mag 2000 \righthyphenmin=1000000000
68 \delimiterfactor 10 \delimitershortfall 190pt
69 \showboxbreadth 55 \showboxdepth 9999 \chardef\nul0\def\0{\nul}
70 \tracingstats=4 \tracinglostchars=2 \tracingparagraphs\day \tracingpages\year
71 \chardef\?=`b \lccode`A=1 \let\^^bbb \hyphenchar\trip=1
72 \language-1\hyphenation\relax{b-\?-\char`b -\^^bb-^^62-^^" -t- }\lccode`149
73 {\everypar{\parindent\\\looseness-1}\skipdef\\8\language\?\\.01014pt\patterns
74 {0111}\emergencystretch9pt\language255\patterns{\the\\} % \patterns{.01015pt}
75 {\language256\patterns{0111 \?50AA1b3 *1AcA. bb bb1 0B2B0 b1c}} % *==space
76 \pretolerance-1\setbox0=\hbox{11}\setbox0=\hbox{\hbadness100\valign{#\cr
77 \hskip-9pt7A\righthyphenmin0\setlanguage\?\unhbox0{*\language`b11\noboundary}
78 1Z1pts\patterns{q9q} -\0qq \showlists{\language\?\noboundary111}%
79 \hyphenchar\rip=`-\cr}}\patterns{toolate}\showbox0}
80 \showboxbreadth 9999\lefthyphenmin=2\righthyphenmin=3
81 \nulldelimiterspace --+.1pt \mathcode`q="3171
82 \scriptspace\if00-0.\fi\ifnum'\ifnum10=10 12="\fi
83  A 01p\ifdim1,0pt<`^^Abpt\fi\fi % this boils down to -0.01pt
84 \overfullrule 5pt \voffset-2pt
85 \def\sh{\ifnum\count4>10\else\dimen5=\count4pt
86  \advance\dimen5 by 10pt
87  \xdef\a{\a\the\count4pt \the\dimen5}
88  \advance\count4 by 1 \sh\fi}
89 \count4=1 \def\a{} \sh % \def\a{1pt 11pt 2pt 12pt ... 10pt 20pt}
90 \let\next=\dump \everyjob{\message{#}}
91 \else\let\next=\relax\fi
92 \next % if no format was preloaded, this will dump the trip.fmt file and halt
93 \tracingcommands2\tracingrestores+2\write-1{log file only\the\prevgraf}
94 \openout-'78terminal \openout10=tr\romannumeral1 \gobble\newcs pos
95 \write10{} % writing three lines on tripos.tex (the first line is empty)
96 \write10{\uppercase{\number{\outputpenalty}}} % 0{\outputpenalty} + error
97 \write10{[\uppercase{\romannumeral-\the\outputpenalty}[} % "mmmmmmmmmm" (-10000)
98 \vsize 2000pt
99 \vbadness=1
100 \topskip 20pt plus 1fil
101 \penalty -12345 % this will be ignored since the page is still empty
102 \maxdepth=2pt
103 \tracingoutput\on

104 \moveleft20pt\copy200
105 \moveright20pt\hbox{\vrule depth20pt height-19pt width1pt}
106 \penalty-10000 % now we'll compute silently for awhile, after default output
107 \batchmode\output={\tracingcommands0\showthe\outputpenalty
108 \showboxbreadth 9999 \showboxdepth 9999 \hoffset1sp
109 {\setbox 254=\box255\shipout\ifvbox2\ifhbox254 \error\fi54\copy25\fi4}
110 \ifvoid 254\relax\else\error\fi

111 }

112 \setbox255\vbox{}
113 \dimen200=10000pt
114 {\output{\dimen 9=\ht200\count5=\dimen9\global\countz=\outputpenalty
115  \ifnum\holdinginserts>0\global\holdinginserts0\unvbox255\penalty\countz
116  \else\setbox255\copy255 % at end of group, \box255 reverts to former value
117  \shipout\hbox{\box100\box200\vsplit 255 to 55pt}
118  \unvcopy255\showlists\showthe\insertpenalties\showthe\pageshrink
119  \globaldefs1\halign{#\tabskip\lineskip\cr}
120  \showboxdepth1\showboxbreadth2\fi
121  \message{\topmark:\firstmark:\botmark:\splitfirstmark:\splitbotmark}}
122 \insert100{\def\box{\vbox to 267.7pt{}} \vskip0pt plus 1fil
123  \baselineskip 0pt \lineskip 0pt minus .4pt
124  \box \penalty-101 \box \penalty-100 \box \penalty-1000
125  } % since \dimen100=803pt<3*267.7pt, the insertion splits;
126 % and the natural height+depth of the split-off part is 267.7pt;
127 % now since \count100=2000,
128 % this insertion adds about 535.4pt to the current page
129 \topskip1pt plus 44pt
130 \vbox spread 1000pt{} % beginning of new page
131 \insertpenalties=-50\penalty12345
132 \cleaders\hbox{\lower2pt\vbox to 17pt{}
133  \leaders\hrule\hskip10pt
134  \cleaders\hbox{A}\hskip 9pt % the A is 2pt wide
135  \leaders\hbox{A}\hskip 9pt
136  \xleaders\hbox{A}\hskip 9pt
137  \write111{\help} % \write will be ignored in leaders
138  }\vskip50pt minus 10pt
139 \mark{alpha}
140 AAA\everypar=\errhelp % because of previous \everypar, this makes 3 paragraphs
141 % and each paragraph consists of A\insert 200{400pt of stuff}\mark{n}
142 % but \count200=500 so the inserts are rated 200pt each
143 % so the third insertion will be split
144 \kern-50pt
145 A\hfill\vadjust{\newlinechar128\special{^^80\the\prevdepth}\penalty-5000}%
146 \penalty-1000000000 % forces line break in paragraph
147 % this is not the end of paragraph
148 A\par\insert200{\vskip10000pt\floatingpenalty3}% this insert will be held over
149 \pagefilstretch-1pt\showthe\insertpenalties\penalty99999999\showlists
150 \showthe\pagefilllstretch\vskip 1000pt\penalty-333\hbox to 23pt{} % output now
151 \vsize.pt\global\vsize=16383.99999237060546875pt % page size \approx infinity
152 } % now we revert to the former output routine
153 {\tracingoutput-2\tracingstats1\shipout\hbox{\closeout10\closeout-10}}
154 \showthe\everypar
155 \everypar{}\showthe\everypar
156 \def\showlonglists{{\tracingcommands0\pagefillstretch-1\dimen100

157  \showboxbreadth 9999 \showboxdepth 9999 \showlists \pagegoal=10000pt}}
158 \tracingmacros=1
159 \def\t12#101001#{-.#1pt} \let\T=\t
160 \dimendef\varunit=222\varunit=+1,001\ifdim.5\mag>0cc0\fi1pt
161 \ifdim -0.01001\varunit=\t120100101001001{\relax}\else\error\fi
162 \countz=-1
163 \ifodd\count0\advance\countz by -1\fi
164 \penalty -12345 % output the remaining stuff
165 \tracingmacros\tracingstats % the next part tests line-break computations
166 % the two competing ways to set the paragraph have respective demerits
167 % (30+l)^2+(30+l)^2+a and (51+l)^2+l^2, where a=adjdemerits, l=linepenalty
168 \adjdemerits=782
169 \linepenalty=1
170 \def\1#1{\hbox to#1pt{}}
171 \valign{\baselineskip20ptplus1pt\global\parfillskip0pt
172 \global\global\leftskip4pt
173 \rightskip-1pt
174 \global\hsize13pt
175 \setbox2\12
176 \noindent\copy2\hskip2pt plus5pt minus1pt
177 \copy2\hskip5pt minus2pt
178 \lower2pt\11\hskip3pt % this affects depth of the second line
179 \copy2 \hskip2pt plus.5pc
180 \box2#\cr
181 \noalign{\spacefactor=2000\global\xspaceskip=-1pt}
182 \noalign{ \vrule width0pt{ }}
183 \cr % set that paragraph with a=782, l=1 (demerits 2704 vs 2705)
184 \adjdemerits=784 \cr % increase a, so the second alternative is better
185 \linepenalty=2\hbadness=51\cr % increase l by 1, suppress diagnostic typeout
186 \noalign{ \spacefactor=1}}\message{\the\spacefactor}
187 {\hsize1000pt\par\parindent1pt\indent}\leftskip3pt\def\?{\vrule width-2pt
188 \hbox spread2pt{}}\noindent\indent\hbox spread2pt{\hskip0pt plus-1bp}%
189 \discretionary{\?AAAB}{\?B-}{\?/A\kern2pt}\unkern % the widths are 7pt, 4pt, 6pt
190 \showthe\lastkern\vbox{\hrule width 6pt} \par % should set with nothing overfull
191 \penalty-22222 % end of demerits test, hyphenation is next
192 \looseness-10
193 \uchyph=1
194 \hsize 100pt
195 A /A\char`A BBBBCACAC//% that becomes /k[AA]k[BB]k[BB][CA][CA][C/]/,
196 % where [] means a ligature and k means a kern.
197 % the word "aabbbbcaca" should be hyphenated to "aa1b3b2b2b1c1aca",
198 % which becomes {[AA]k-||[AA]k}{B-|[BB]kBk|[BB][BB]}{-||}{C-|A|[CA]}[CA]
199 % if I use the notation {x|y|z} for \discretionary{x}{y}{z}.
200 \vadjust{\uchyph=0\ BBBBBB}% underfull box will show no hyphens
201 \vadjust{\ \closeout1BBBBBB}{\hyphenchar\trip`C}% this time we get hyphens
202 \hyphenation{BbB-BbB}\vadjust{\ BBBBBB\kern0ptB}% different hyphens
203 \hyphenchar\rip`-\vadjust{\def\B{B}\ \pretolerance10000 B\B BBBB} % no hyphens

204

205 \hbox{\sfcode`B=1234AB aB }\noindent \scriptscriptfont3 \smalltrip
206 $$\eqno^{}$\scriptfont3=\rip\fontdimen2\smalltrip=0pt
207 {\rightskip0pt plus 104pt minus 100fil
208 \looseness 5 \spaceskip 4pt plus 2pt minus 1fil
209 A\spacefactor32767\discretionary{}{\kern2pt-}{B\kern2pt} C$ \scriptfont2=\trip

210 \mathsurround143pt$ C $\mathsurround40pt$$\mathsurround60pt\hbox{$$}$\par}
211 \uccode`m=`A\font\mumble=mumble\input tripos % "AAAAAAAAAA"+errors
212 \par\penalty-33333 % end hyphenation, math is next
213 {\catcode`?=13 \font?xyzzy at0pt\font ? xyzzy scaled1?} % nonexistent
214 \font\enorm=trip at 2047.999992370605468749999 pt
215 \font\ip trip at -10pt % through the looking glass
216 \showthe$
217 \showthe\font
218 \message{\fontname\ip}
219 \rip
220 \textfont1=\font \scriptfont1=\smalltrip \scriptscriptfont1=\bigtr^^@p % [sick]
221 \def\symbolpar #1*#2*#3*{\global\fontdimen#1\smalltrip = #3 pt}
222 {\tracingmacros-1
223 \symbolpar8 num1 9.1
224 \symbolpar9 num2 9.2
225 \symbolpar10 num3  9.3
226 \symbolpar11 denom1 3.1
227 \symbolpar12 denom2 3.2
228 \symbolpar13 sup1 8.1
229 \symbolpar"E sup2 8.2
230 \symbolpar15 sup3 8.3
231 \symbolpar16 sub1 4.1
232 \symbolpar17 sub2 4.2
233 \symbolpar18 supdrop 0.3
234 \symbolpar19 subdrop 0.4
235 \symbolpar20 delim1 10
236 \symbolpar21 delim2 20

237 }

238 \mathcode`+='20457 % mathbin, family 1, character '57 (/)
239 \mathcode`=="322D % mathrel, family 2, character "2D (-)
240 \delcode`["161361 % small (family 1, character "61 (a)), large (3,"61)
241 \catcode`(=13 \catcode`(=13 \mathcode`y"7320\mathcode`z"8000
242 \def({\delimiter"4162362 }{\catcode`z=13\global\let z=(}
243 \parshape 10 \a \chardef\x200
244 \hangindent- \parshape pt\hangafter-12% \parshape will take precedence
245 \begingroup
246 \looseness 2
247 \rightskip 0pt plus 10fil minus 1sp
248 \--\--\char-0-A\- % this makes lines 1 to 3
249 $$\number\the\delcode`\relax\over{{{}}}}\pagestretch=-1\pagetotal\showlists
250 \begingroup\halign to\the\displaywidth{#&#\crcr\crcr\cr} % makes lines 4--6
251 \global\count6=\displayindent
252 \predisplaypenalty=101
253 \global\postdisplaypenalty-\predisplaysize* \global\setbox=
254 \eqno % another error (actually causes two error messages and inserts $$)
255 \looseness-2
256 $\right\relax\mathchardef\minus="322D % locally \minus is the same as =
257 \left.A\over A\abovewithdelims.?\right(+\mskip1A\minus=A+\penalty+1000A
258 \relpenalty-2222
259 \binoppenalty-3333
260 \mathsurround.11em$\x % this formula goes on line 7
261 $$ % here we begin a hairy display that covers lines 8 to 10
262 \vadjust{\penalty7}\mkern-9mu\the\prevgraf \prevgraf=8 \insert255{\penalty999}

263 \x\vcenter spread-2pt{} {\mathaccent"32D {A}}|-
264 ^{\raise 2pt\hbox{a}\displaystyle\char`+\textstyle}
265 \overline{^A A|\minus\mathinner{}^
266 {A \mathchar"141 \char`B^^A{\mathaccent"7161
267  {\mathop A \mathbin A \mathopen A \mathpunct A\mathclose A \mathrel A
268  \global\scriptscriptfont0=\trip
269  \mathaccent"161 {\fam13A9\the\scriptscriptfont-1}}}}}
270 \mathop\char`B^\mathchar"143
271 \mathop b\nolimits\limits|C
272 \mathord \radical"161 % missing { will be inserted
273 {\textstyle\radical"282382{\left(\scriptscriptstyle\mathop{\underline{
274  A\atop\displaystyle A|{A\hfil\over B\nonscript\kern1pt}^=}}
275  \nolimits|{\mathop y\nonscript\textstyle\nonscript\mskip9mu minus1fil
276  \showthe\lastskip B\abovewithdelims(.2pt\displaylimits}^z
277  \discretionary{\showthe\spacefactor-}{\smalltrip A\hss}{\smalltrip A}
278  \right[A}}}
279 \let\penalty=\minus \aftergroup\expandafter
280 \eqno\aftergroup\relax\scriptstyle\penalty % reader, be alert
281 (\mathpunct{AA}
282 |{B\fam1-}^{\hbox{A}}{\above9pt{v\overwithdelims..
283  \displaystyle{pq\atopwithdelims((\vrule height 9pt}}
284 \show\penalty \showlonglists
285 $\expandafter$\csname!\endcsname % end of hairy display, missing } inserted
286 \parshape=-1 % now the hanging indentation is relevant
287 \leftskip \parshape pt plus -10fil
288 \spacefactor1\raise1pt\hbox{\special{\the\hangafter} } \penalty-10000
289 \showbox0\spacefactor=0
290 \write10{\the\spacefactor}\par % it's illegal to \write the space factor
291 } % this fails to match \begingroup
292 \aftergroup\lccode\aftergroup`\endgroup A`a % this restores \parshape
293 \mark{\the\spacefactor} % \spacefactor: not in vertical mode
294 $$\global\count7=\predisplaysize
295 \mskip18mu minus 18mu \catcode`J=13 \catcode`j=\the\catcode`J \def j{\relax}
296 \vtop to\displaywidth{\everydisplay{\global}\vbox to -1sp{}\noindent$$
297 \count9=\predisplaysize\lowercase{AaJ}$\ifvmode$\fi}\hss
298 \leqno\mathchardef A\/\left(\over\left(\global\errorcontextlines5$$

299

300 \hangindent1pt\par\showthe\hangindent\hangindent 254cm
301 \parfillskip 0pt plus 100pt \fontdimen6\the\scriptfont2=-19sp
302 \the\fam % begins a paragraph, but there's no 0 in the font
303 A \char'202$$\global\count8=\predisplaysize\leqno\kern1009pt$\par
304 \showlists {\catcode`!13\global\everyhbox{\def!{}}}
305 \count5=\lastskip % \lastskip=3pt (\belowdisplayskip)
306 \baselineskip 10pt
307 {\sfcode`A=500\vfuzz18pt\everyvbox{ }% overfull \vbox won't be shown: 37-8=11+18
308 \vbox to 11pt{\hsize 10pt\tolerance 1 A A A A A\clubpenalty10000\par
309  \hbadness100\hfuzz 3pt A A A A A\leaders\vrule\hskip5pt\par}
310 \message{\the\badness}}
311 \vbox to 10pt{\hbadness 99\hfuzz1pt\hbox to 0pt{\hskip 10pt minus 9pt}
312 \hbadness100\hbox to 10bp{\hskip 0pt plus 10pt}\tracingcommands1
313 \if\the\badness\fi\message{\the\badness}}\lineskiplimit-1pt\everyhbox{}
314 \def\space{ } \dimendef\df=188 \dimen188=1pt
315 \vbox to 11pt{\tracinglostchars-9 A\/\space\space\ignorespaces\space\space J

316 \vskip2pt\moveleft1pt\vbox to10pt{\boxmaxdepth=-1pt\mark{vii}}\vskip3pt
317 \unskip\setbox22=\lastbox\showthe\lastskip % \lastskip=-1pt (\baselineskip)
318 \unskip\vskip-\lastskip\kern\lastkern\penalty\lastkern\showbox22}
319 \showbox22\kern3pt\message{\the\lastkern}\unkern
320 \show\botmark \catcode`;13\def;{\setbox`; }
321 \lineskiplimit-0.9999 \space\df\space\count9 0
322 \vbox\space to 11pt{\accent\x\space\accent\space"42 \def\^^M{\  } ; \char'101
323 A\     \fontdimen   4 \trip    =    88    pt\     \spaceskip    2    pt      \
324 \vskip 10pt minus 10pt}
325 \penalty-2147483647 % that's the largest value TeX will scan
326 \penalty-2147483648 % see?
327 \tabskip 1009.9sp minus .25cc % and now for alignment tests
328 \let\A=\relax\count1=2{\errhelp{all is lost}\errmessage{}}
329 \def\d#1\d{#1#1} \looseness-1
330 \setbox3=\vtop{\vskip-3mm} % this box has a depth of -3mm
331 \halign spread-12.truedd{&#\span\iftrue\A\span\else\span\fi\span&
332 \vbox{\halign to 0pt{\t2\dp3\A\crcr}#A}
333 &\hss\tabskip1ex plus7200bp minus 4\wd4\d#\d\cr % \d#\d becomes (erroneous) ##
334 \global\let\t=\tabskip \spaceskip=4pt minus 1sp
335 \def\A{B}\def\xx{\global\gdef\A{\global\count\count1=####\cr
336  \omit\cr\tabskip}}\expandafter\xx\span % please don't ask what this does
337 A&\omit\valign to -5pt{#&#\cr A\char`}\span\cr{ }\span\cr}\cr
338 \global\edef\A{\uppercase{
339  \message{\fontname\smalltrip\the\font\romannumeral1009}\lowercase{vq}} }
340 \lccode`Q=`b \span\omit$$\span\A&\show\cr\omit\cr
341 \noalign{\global\prevdepth20pt\errmessage{\count2=\the\count2}}
342 \omit\mark{a}&\omit\mark{b}\cr} % \count2 was set to -6mm=-1118806sp
343 \errmessage{\prevdepth=\the\prevdepth}
344 \penalty-88888 % end alignment test, now miscellaneous error messages
345 \newlinechar`Y\global\unskip\show^^Y\newlinechar\lastpenalty\unpenalty\unkern
346 \lastbox\penalty5\message{\the\lastpenalty\the\newlinechar}\textfont16=\relax
347 \outer\def{}?
348 \dimen5=-'7777777777sp\showthe\dimen5 % this should be OK
349 \dimen6=-'40000pt\showthe\dimen6 % this should overflow
350 \dimen7=.51\dimen5\showthe\dimen7 \multiply\dimen7 2\showthe\dimen7
351 \a^^@^^@a@ % an undefined control sequence followed by invalid character
352 {\aftergroup\gobble\aftergroup\c\gdef\b{\c} \def\c{} \b} % \c undefined
353 \def\b#1\par{}
354 \outer\gdef\a^^@^^@a#1\par#2{}\tokens{\a^^@^^@a\par!
355 \long\gdef\l#1{}
356 \outer\global\long\edef\lo#1#2U3#4#5#6#7#8#8#9#{\relax}
357 \ifcase 1 \undefined\or\l\par\b{\par % occurrence of \par aborts \b
358 \b{\l\undefined}\par\else\b{\par}\fi % but not there!
359 \ifcase\iftrue-1a\else\fi \ifcase0\fi\else\ifcase5\fi\fi
360 \catcode`^^C = 6 % another parameter symbol
361 \let\^^C=\halign
362 \def\^^@^^C{}
363 \^^C{{\span\ifcase3 \lo#\cr............89{}\cr} % runaway preamble?
364 \def\a^^C1{\d#1\d\l{#2}\l#1\par\a^^@^^@a#1\par# % runaway in definition; #2 bad
365 \xdef\a^^C1{\d#1\d\l{#2}\l#1\par\a^^@^^@a#1\par# % runaway in definition; #2 OK!
366 \T^^?a^^@^^@a\par{\lo\par % runaway in use
367 \lo\par\par\par  P  \par\par\par\par\par\par89{} \muskip3=-\thickmuskip
368 \muskipdef\shmip=3 \shmip=5mu plus \muskip3minus.5\shmip \showthe\shmip

369 {\advance\shmip by \shkip\endlinechar-1
370 \divide\shmip by \shkip\endlinechar`}
371 \global\multiply\shmip by 2
372 \showthe\shmip
373 \div^^)de\count88
374 By ^^p \toks1={\a\test}
375 ^\leaders\vrule\mskip\shmip M\leaders\hrule\nonscript\hskip\thinmuskip

376

377 {\setbox3\hbox{\vfill\vsplit 3 0pt}
378 \def\a#2{}
379 \show A
380 \show\a^^@^^@a
381 \show (
382 \message{\meaning\lo\noexpand\lo}
383 \show\^^C
384 \show\batchmode
385 \show\error
386 \showthe\output
387 \showthe\thinmuskip
388 \showthe\fontdimen1\enorm
389 \ifx T\span\else\par\if\span\else\else\else\fi\fi
390 \ifdim72p\iftrue t1i\fi n\fi\fi \message{\jobname\ifx\lo\lo OK}\fi
391 \hangindent 2pt
392 {\if 11 \prevgraf=-1\if 0123\error\else\relax\fi\else\error\fi
393 \prevgraf1\global\hangafter=2}\showthe\hangafter\showthe\prevgraf
394 \char'203\showthe\prevgraf$\indent\mark{twain}
395 \setbox3\hbox{\vrule}&\moveleft\lastbox % can't do that in math mode
396 \unhbox234\unhcopy3\accent\x\vfill\vfil\vfilneg\vss % \vfill exits, \vss bad
397 \def\a}{\let\a\xyzzy\csname a\endcsname}
398 \def\a{ab

399

400 \c}\def\b{ab*\par\c}\let\c\b \def\b{\a\c} \ifx\a\ifx \.
401 \else\expandafter\ifx\b \ifinner\error\else\relax\fi\else\error\fi\fi
402 \ifvmode$\ifmmode\hbox tt\ifhmode\hfilneg\else\error\fi}$\fi\fi % missing {
403 \noalign\omit\endcsname % these are extra
404 \fontdimen 1000=20\varunit\showthe\fontdimen1000\trip\let\PAR=\par
405 \gdef\par{\relax\PAR}\expandafter\ifx\csname xyzzy\endcsname\relax \mag=1999

406

407 \fi\noindent{\halign to 1truemm\expandafter{\csname#\endcsname#&#&\l{#}\cr
408 \global\let\endt=&\endt&$&&&.}

409

410 \hbox{\/\hrule\textfont3=\enorm\prevdepth\advance\xspaceskip by-\xspaceskip
411 \spacefactor2000{ }\everymath{\radical"3}\fontdimen2\rip=0pt
412 $62{}\delimiterfactor1600\left(Aa\right\delimiter"300$AA\/}
413 \openin 15 tripos\closein 15\iftrue{\ifeof 15\openin 100 tripos
414 \def\loop{\ifeof 0\let\loop=\relax\else{\global\read0to \a}\show\a\fi\loop}
415 \catcode`015\catcode`[1\outer\def\uppercase{}\loop}\else\fi
416 \endt\def\test#1{\let\test= }\test. \show\test
417 \def\a#1{\ifcat#1 \message\ifx#1 {\iffalse\fi\the\tokens\fi\fi}}
418 \pretolerance-1\tokens\toks1\unhbox16\par\everycr{\noalign{\penalty97}}
419 \the\tokens\ifcase1\or\ifeof\fi\def\stopinput{\error\let\input\die}
420 \let\lb={\let\rb=}\halign\relax{\span\iffalse}\fi\cr#&\ifnum0=`{\fi\cr\cr}
421 \def\trap#1{\def#1{danger}\global\let\trap#1\show#1\trap}\def\unbalanced

422 {\halign\lb}\unbalanced#\cr\relax\expandafter\trap\cr\show\cr\trap}

423

424 \expandafter\stopinput\input tripos\endinput\input % one line of tripos
425 \setbox10=\vbox to8192pt{\hbox{\hbox{\vadjust{A}}}}\vrule\unhbox10\hrule
426 \output{\showthe\deadcycles\global\advance\countz by1\global\globaldefs-1
427 \gdef\local{}\unvbox255\end\rb}\futurelet\dump\maxdeadcycles=3\show\dump
428 \catcode`q=7 \catcode`\qqM=0 \expandafter\let\csname^^Mendcsname=\^^@\relax
429 \relax\catcode`\qq1qM=13 \defqqM{\relax}#\begingroup{\showboxdepth=4\showbox10}

430

431 \long\def\l#1\l{#1}\immediate\write10{\string\caution \l} % living dangerously
432 \escapechar`|\tracingoutput0\shipout\vbox{\copy10qq5e^5cbox10}
433 \setbox9\hbox{\fontdimen8\rip 0pt % \over becomes \atop in \scriptstyle
434 \afterassignment\relax\advance\prevdepth\afterassignment\relax\futurelet\x
435 \message{\noexpand\l\meaning\l\the\skewchar\ip}\vbox{\hyphenchar\ip-1%
436 \-\ BBBBBB\par\hyphenchar\ip`?\-\ BBBBBB}\if$\expandafter\noexpand\dol\fi%
437 \expandafter\expandafter\noexpand\undefined\noexpand\expandafter%
438 $\begingroup\mathop{\vbox{\vss}}\limits^\mathchoice{}a}{A|{}}{\mathchoice}
439 {}{\relax{}{B\over}\endgroup\showlonglists$}\showboxbreadth9\showboxdepth9
440 \showbox9\PAR{\output{}\penalty-10001\deadcycles=2}\scrollmode%
441 \hbox{\write-100000{\if01{\else unbal}\fi}\showlists\tracingonline1%
442 \escapechar127\global\tracingoutput1\global\escapechar256\end
443 % things not tested:
444 % interaction (error insertion/deletion, interrupts, \pausing, files not there)
445 % system-dependent parsing of file names, areas, extensions
446 % certain error messages, especially fatal ones
447 % things that can't happen in INITEX
448 % unusual cases of fixed-point arithmetic