<<

. 8
( 13)



>>

| scrollmode | errorstopmode
The four “mode commands” control the amount of interaction during error recovery,
just as in TEX. A job starts in errorstopmode, and you can also resurrect this mode
by interrupting ; scrollmode, nonstopmode, and batchmode are the
220 Chapter 26: Summary of the Language


modes you get into by hitting ˜S™, ˜R™, or ˜Q™, respectively, in response to error messages picture command
addto command
(cf. Chapter 5).
addto
also
picture command ’’ addto command | cull command addto
addto command ’’ addto picture variable also picture expression contour
| addto picture variable contour path expression with list addto
doublepath
| addto picture variable doublepath path expression with list with list
with list ’’ empty | with list with clause with clause
with clause ’’ withpen pen expression | withweight numeric expression withpen
withweight
cull command ’’ cull picture variable keep or drop pair expression cull command
| cull command withweight numeric expression cull
withweight
keep or drop ’’ keeping | dropping keep or drop
keeping
The addto and cull commands are the principal means of making changes to pictures; dropping
they are discussed fully in Chapter 13. display command
display
display command ’’ display picture variable inwindow window inwindow
window
window ’’ numeric expression openwindow command
openwindow command ’’ openwindow window window spec openwindow
window spec ’’ screen place at pair expression window spec
at
screen place ’’ from screen coordinates to screen coordinates screen place
screen coordinates ’’ pair expression from
to
screen coordinates
Chapter 23 explains how to display stu¬ on your screen via display and openwindow.
shipout command
shipout command ’’ shipout picture expression shipout
generic font
proo¬ng
You may have wondered how actually gets pictorial information into a xo¬set
font. Here at last is the answer: ˜shipout v™ puts the pixels of positive weight, as yo¬set
charcode
de¬ned by the picture expression v, into a generic font output ¬le, where they will be
charext
the bitmap image associated with character number charcode mod 256 + charext — 256. charwd
The pixels of v are shifted by (xo¬set , yo¬set ) as they are shipped out. (However, no charht
chardp
output is done if proo¬ng < 0. The values of xo¬set , yo¬set , charcode , and charext are charic
¬rst rounded to integers, if necessary.) This command also saves the values of charwd , chardx
font metric information
charht , chardp , charic , chardx , and chardy ; they will be associated with the current
special command
charcode when font metric information is produced. (See Appendices F and G for the special
basic principles of font metric information and generic font ¬les.) numspecial

special command ’’ special string expression
| numspecial numeric expression
The special and numspecial commands send alphabetic and numeric information
to the generic font output ¬le, if proo¬ng is nonnegative. For example, the labels on
proofsheets are speci¬ed in this way by macros of plain . Appendices G
and H provide further details.
We have now discussed every kind of command but one; and the remaining one
is even more special than the special command , so we had better defer its discussion to
an appendix. Appendix F will complete the syntax by de¬ning font metric command .
For now, we merely need to know that font metric commands specify fussy font facts;
examples are the kerning and ˜font normal space™ statements in the logo
program of Chapter 11.
Chapter 26: Summary of the Language 221


And there™s one more loose end to tie up, going back to the very ¬rst syntax dump
end
rule in this chapter: The token ˜dump™ can be substituted for ˜end™, if a special version
INIMF
of called ˜INIMF™ is being used. This writes a ¬le containing the macros outer
de¬ned so far, together with the current values of variables and the current meanings delimiters
showtoken
of symbolic tokens, so that they can be loaded as a base ¬le. (It is analogous to TEX™s THOREAU
\dump command.) Base ¬les are discussed at the end of Appendix B. truth
ZAPF
EXERCISE 26.1
Run with the input
\newinternal a;
let b=a; outer a,b,c;
let c=b; delimiters a::;
showtoken a,b,c; end
and explain the computer™s responses.




Our life is frittered away by detail.
An honest man has hardly need
to count more than his ten ¬ngers,
or in extreme cases he may add his ten toes,
and lump the rest. Simplicity, simplicity, simplicity!
I say, let your a¬airs be as two or three,
and not a hundred or a thousand . . .
Simplify, simplify.
” HENRY DAVID THOREAU, Walden (1854)

The awesome memory of thy ever attentive computer
accepts all words as truth.
Think, therefore, in analytical, modular steps,
for the truth or untruth spoken through thy ¬ngertips
will be acted upon unerringly.
” HERMANN ZAPF, The Ten Commandments of Photo -Typesetting (1982)
(page 222)




27
Recovery
from
Errors
Chapter 27: Recovery from Errors 223


OK, everything you need to know about has been explained”unless error messages
badio.mf
you happen to be fallible. If you don™t plan to make any errors, don™t bother to !
read this chapter. Otherwise you might ¬nd it helpful to make use of some of to be read again
isolated expression
the ways that tries to pinpoint bugs in your programs. semicolon
In the trial runs you did when reading Chapter 5, you learned the general statement
form of error messages, and you also learned the various ways in which you can
respond to ™s complaints. With practice, you will be able to correct
most errors “online,” as soon as has detected them, by inserting
and deleting a few things. On the other hand, some errors are more devastating
than others; one error might cause some other perfectly valid construction to be
loused up. Furthermore, doesn™t always diagnose your errors cor-
rectly, since the number of ways to misunderstand the rules is vast;
is a rather simple-minded computer program that doesn™t readily comprehend
the human point of view. In fact, there will be times when you and
disagree about something that you feel makes perfectly good sense. This chapter
tries to help avoid a breakdown in communication by explaining how to learn
™s reasons for its actions.
Ideally you™ll be in a mellow mood when you approach , and
you will regard any error messages as amusing puzzles”“Why did the machine
do that?””rather than as personal insults. knows how to issue more
than a hundred di¬erent sorts of error messages, and you probably never will
encounter all of them, because some types of mistakes are very hard to make.
Let™s go back to the ˜badio.mf™ example ¬le of Chapter 5, since it has
more to teach us. If you have a better memory than the author, you™ll recall
that the ¬rst error message was
>> mode.setup
! Isolated expression.
<to be read again>
;
l.1 mode setup;
% an intentional error!
?

In Chapter 5 we just charged ahead at this point, but it would be more normal
for a mature er to think “Shucks, I meant to type ˜mode_setup™, but
I forgot the underscore. Luckily this didn™t cause any harm; just
found an isolated expression, ˜mode.setup ™, which it will ignore. So let me now
insert the correct command, ˜mode setup™.”
Good thinking; so you type ˜I mode_setup™, right? Wrong . . . sorry. Lots
of error messages occur before has read a semicolon in preparation
for another statement; the important clue in this case comes from the two lines
<to be read again>
;
224 Chapter 27: Recovery from Errors


which tell us that the semicolon is still pending. So the correct response would Extra tokens will be ¬‚ushed
¬‚ushing
have been to type ˜I; mode_setup™ instead. Without the semicolon, you get semicolon
what appears at ¬rst to be a horrible mess: Unde¬ned coordinate
misspelling
! Extra tokens will be flushed. typographic errors
???
<to be read again> OK
warningcheck dependent variable
independent variable
mode_setup->warningcheck
:=0;if.unknown.mode:mode=proof;fi...
<insert> mode_setup

<to be read again>
;
l.1 mode setup;
% an intentional error!
?
But relax, there™s a simple way out. The help message says ˜Please insert a
semicolon now in front of anything that you don™t want me to delete™; all you
have to do is type ˜I;™ and the net e¬ect will be the same as if you had correctly
inserted a semicolon before mode_setup in the ¬rst place.
The moral of this story is: When you insert a new statement during
error recovery, you frequently need to put a semicolon just ahead of it. But if
you forget, gives you another chance.
After proceeding through badio with the interactions suggested in Chap-
ter 5, we will come again to the error
>> 0.08682thinn+144
! Undefined x coordinate has been replaced by 0.
(This is where the erroneous ˜thinn™ was detected.) The help message for this
error has some curious advice:
(Chapter 27 of The METAFONTbook explains that
you might want to type ˜I ???™ now.)
Chapter 27? That™s us! What happens if we do type ˜I ???™ now? We get
x4l=0.08682thinn+144
y4=-0.4924thinn+259.0005
x4r=-0.08682thinn+144
y4r=-0.9848thinn+259.0005
! OK.
It is now abundantly clear that ˜thin™ was misspelled. Plain de¬nes
˜???™ to be a macro that shows all of the current dependencies between numeric
variables and stops with ˜OK™; this is useful because a badly typed variable name
might have become a dependent variable instead of an independent variable, in
which case it would be revealed by ˜???™ but not by the error message.
Chapter 27: Recovery from Errors 225


One more example of online error correction should su¬ce to make the
general strategy clear. Suppose you accidentally type square brackets instead of
parentheses; the computer will scream:
! A primary expression can™t begin with ˜[™.
<inserted text>
0
<to be read again>
[
<*> show round[
1 + sqrt43];
?
(By coincidence, the help message for this particular error also refers to Chap-
ter 27.) When needs to see an expression, because of the tokens it
has already digested, it will try to insert ˜0™ in order to keep going. In this case
we can see that zero isn™t what we intended; so we type ˜7™ to delete the next
seven tokens, and the computer comes back with
<*> show round[1 + sqrt43]
;
?
Now ˜I (1 + sqrt43)™ will insert the correct formula, and the program will be
able to continue happily as if there were no mistake.
EXERCISE 27.1
Why was ˜7™ the right number of tokens to delete?
EXERCISE 27.2
If the user hadn™t deleted or inserted anything, but had just plunged ahead,
would have come up with another error:
>> 0
! Extra tokens will be flushed.
<to be read again>
[
<to be read again>
(7.55743)
<to be read again>
]
<*> show round[1 + sqrt43]
;
?
Explain what happened. What should be done next?

It™s wise to remember that the ¬rst error in your program may well
spawn spurious “errors” later on, because anomalous commands can in¬‚ict se-
rious injury on ™s ability to cope with the subsequent material. But
226 Chapter 27: Recovery from Errors


most of the time you will ¬nd that a single run through the machine will locate batchmode
nonstopmode
all of the places in which your input con¬‚icts with ™s rules. emergency stop
end
Sometimes an error is so bad that is forced to quit prematurely. Fatal base ¬le error
For example, if you are running in batchmode or nonstopmode, - loop
in¬nite loop
makes an “emergency stop” if it needs input from the terminal; this happens
I can™t go on
when a necessary ¬le can™t be opened, or when no end was found in the input. Here This can™t happen
are some of the messages you might get just before gives up the ghost: METAFONT capacity exceeded

Fatal base file error; I™m stymied.
This means that the preloaded base you have speci¬ed cannot be used, because it was
prepared for a di¬erent version of .
That makes 100 errors; please try again.
has scrolled past 100 errors since the last statement ended, so it™s probably
in an endless loop.
I can™t go on meeting you like this.
A previous error has gotten out of whack. Fix it and try again.
This can™t happen.
Something is wrong with the you are using. Complain ¬ercely.
There™s also a dreadful message that issues only with great reluc-
tance. But it can happen:
METAFONT capacity exceeded, sorry.
This, alas, means that you have tried to stretch too far. The message
will tell you what part of ™s memory has become overloaded; one of the
following eighteen things will be mentioned:
(strings and names of symbolic tokens and ¬les)
number of strings
(the characters in such strings)
pool size
(pairs, paths, pens, pictures, token lists, transforms, etc.)
main memory size
(symbolic token names)
hash size
(simultaneous input sources)
input stack size
(internal quantities)
number of internals
(transitions between octants in cycles)
rounding table size
(macro parameters)
parameter stack size
(characters in lines being read from ¬les)
buffer size
(input ¬les and error insertions)
text input levels
(key points per path)
path size
(rows of picture being simultaneously accessed)
move table size
(pen o¬sets per octant)
pen polygon size
(accumulated ligtable instructions)
ligtable size
(distinct kern amounts)
kern
(built-up characters)
extensible
(largest headerbyte address)
headerbyte
(largest fontdimen address)
fontdimen
The current amount of memory available will also be shown.
Chapter 27: Recovery from Errors 227


If you have a job that doesn™t over¬‚ow ™s capacity, yet you want tracingstats
stack positions
to see just how closely you have approached the limits, just set tracingstats
memory usage
to a positive value before the end of your job. The log ¬le will then conclude with a recursion
report on your actual usage of the ¬rst nine things named above (i.e., the number of interrupt
in¬nite loops
strings, . . . , the bu¬er size), in that order. Furthermore, the showstats command can errorstopmode
be used to discover the current string memory and main memory usage at any time show
hide
during a run. The main memory statistics are broken into two parts; ˜490&5950™ means,
for example, that 490 words are being used for “large” things like pens, capsules, and
transforms, while 5950 words are being used for “small” things like tokens and edges.
What can be done if ™s capacity is exceeded? All of the above-listed
components of the capacity can be increased, except the memory for kerns and
extensible characters, provided that your computer is large enough; in fact, the space
necessary to increase one component can usually be obtained by decreasing some other
component, without increasing the total size of . If you have an especially
important application, you may be able to convince your local system people to provide
you with a special whose capacities have been hand-tailored to your needs.
But before taking such a drastic step, be sure that you are using properly.
If you have speci¬ed a gigantic picture that has lots of transitions between black and
white pixels, you should change your approach, because has to remember
every change between adjacent pixel values in every currently accessible picture. If you
keep saving di¬erent pens, you might be wasting memory as discussed in Chapter 16. If
you have built up an enormous macro library, you should realize that has
to remember all of the replacement texts that you de¬ne; therefore if memory space is
in short supply, you should load only the macros that you need.
Some erroneous programs will over¬‚ow any ¬nite memory capac-
ity. For example, after ˜def recurse=(recurse)enddef™, the use of recurse
will immediately bomb out:
! METAFONT capacity exceeded, sorry [input stack size=30].
recurse->(recurse
)
recurse->(recurse
)
recurse->(recurse
)
...
The same sort of error will obviously occur no matter how much you increase -
™s input stack size.
Most implementations of allow you to interrupt the program in
some way. This makes it possible to diagnose the causes of in¬nite loops,
if the machine doesn™t stop because of memory limitations. switches to
errorstopmode when interrupted; hence you have a chance to insert commands into
the input: You can abort the run, or you can show or change the current contents
of variables, etc. In such cases you will probably want to “hide” your diagnostic com-
mands, for example by typing
I hide(showstopping:=1; alpha:=2; show x)
228 Chapter 27: Recovery from Errors


so that you don™t mess up the expression is currently evaluating. Inter- strange
El Palo Alto
ruption can also give you a feeling for where is spending most of its time,
¬‚ex
if you happen to be using an ine¬cient macro, since random interrupts will tend to for
occur in whatever place visits most often. octant
SSW
compass directions
™s second most frustrating error messages are its occasional claims
SSE
that you have “strange” paths. Sometimes a glance at your output will make ESE
it clear that you did indeed specify a path that crossed over itself, something like a WSW
WNW
¬gure-8; but sometimes a path that looks ¬ne to you will be rejected by the computer. NNE
In such cases you need to decipher ™s octant codes, which look scary at ¬rst NNW
ENE
although they turn out to be helpful when you get used to them. For example, let™s
reconsider branch4 of El Palo Alto, from the program in Chapter 14:
branch4=
flex((0,509),(-14,492),(-32,481))
&flex((-32,481),(-42,455),(-62,430))
&flex((-62,430),(-20,450),(42,448))
&flex((42,448),(38,465),(4,493),(0,509))
&cycle;
If the number 450 in the third ¬‚ex had been 452 instead, would have
stopped and told you this:
> 0 SSW WSW 1 2 SSW 3 WSW 4 (WNW NNW) NNE ENE 5 ESE 6 (ENE)
NNE NNW 7 WNW NNW 8 NNE 0 (NNW WNW WSW)
! Strange path (turning number is zero).
<to be read again>
;
<for(4)> ...]shifted(150,50)scaled(w/300);
ENDFOR
p.4,l.94 endfor
endchar;
?
The ˜for(4)™ in the ¬fth-last line implies that branch4 is at fault, because it says that
the for loop index is 4; but the octant codes like ˜SSW™ are your only clues about why
branch4 is considered strange. (A simpler example appeared in Chapter 13, which
you might want to review now.) You probably also have a proofmode diagram:




(Figure 27a will be inserted here; too bad you can™t see it now.)




Starting at time 0, and at the point (0, 509), the path goes South by Southwest, then
West by Southwest until time 2 (the end of the ¬rst ¬‚ex). Then it goes SSW again,
Chapter 27: Recovery from Errors 229


and WSW again (that™s the second ¬‚ex). But at time 4, the path makes a sharp turn counterclockwise
eps
through the directions WNW and NNW, without moving (because these octant codes are
turningcheck
in parentheses). Aha! That™s where the path was supposed to turn counterclockwise, tracingspecs
through SSW and SSE and ESE; turned clockwise because it was the shortest tracingtitles
tracingmacros
way to go. The path actually makes a little loop at time 4, between the end of the tracingrestores
second ¬‚ex and the beginning of the third. Therefore its turning number is indeed zero, tracingequations
tracingchoices
and the path is strange by de¬nition.
control points
tracingpens
EXERCISE 27.3
tracingoutput
At what point do the second and third ¬‚exes cross, in this example?
There are three main ways to avoid problems with strange paths. One is to
stay away from paths that turn so abruptly. Or you can displace the paths by
epsilon , as in the serif example at the end of Chapter 16. (Displacing by eps would be
even safer.) Or you can discipline yourself to ¬ll all cycles counterclockwise, so that
you can set turningcheck := 0; this means that won™t check for strange
paths, but that™s OK because tiny little loops won™t hurt anything if you are ¬lling
cycles in the correct direction.
Sometimes the octant codes of a strange path are shown backwards, because
the system may have tried to reverse the path to get rid of its strangeness.
Sooner or later”hopefully sooner”you™ll get to process
your whole ¬le without stopping once to complain. But maybe the output still
won™t be right; the mere fact that didn™t stop doesn™t mean that
you can avoid looking at proofsheets. At this stage it™s usually easy to see how
to ¬x typographic errors by correcting the input; hardcopy proofs such as those
discussed in Appendix H usually clear up obvious mistakes, especially if you have
remembered to label the key points in your constructions.
But your output may contain seemingly inexplicable errors. If you can™t
¬nd out what went wrong, try the old trick of simplifying your program: Remove
all the things that do work, until you obtain the shortest possible input ¬le that
fails in the same way as the original. The shorter the ¬le, the easier it will be
for you or somebody else to pinpoint the problem.
One of the important tricks for shortening a buggy program is to assign a
positive value to tracingspecs , because this will put all the key points and
control points of a problematic path into your log ¬le. (See the example at the end
of Chapter 24, “before subdivision.”) If something is wrong with the treatment of
some path, you can copy the path™s description from the log ¬le and use it directly in
input, thereby avoiding all the complexity of equations that might have
been involved in that path™s original creation.
We™ve just talked about tracingstats and tracingspecs ; is able
to produce lots of other kinds of tracing. For example, Chapter 22 discusses
tracingtitles , Chapter 18 discusses tracingmacros , Chapter 17 discusses tracingrestores ,
and Chapter 9 discusses tracingequations . You can also invoke tracingchoices , which
shows all paths before and after their control points are chosen according to the rules in
Chapter 14; or tracingpens , which shows the pen polygons that arise when a future pen
becomes a full-¬‚edged pen; or tracingoutput , which shows every picture that™s shipped
230 Chapter 27: Recovery from Errors


out, using edge-transitions to represent the pixel values as illustrated in Chapter 13. interim
tracingcommands
Each of these types of tracing is enabled by assigning a positive value to the correspond-
command
ing internal quantity; for example, you can simply set tracingpens := 1 (or interim expandable token
tracingpens := 1) if you want the data about pens. algebraic
operation
Digitized output
If tracingcommands = 1, shows every command just before it is tracingedges
carried out. If tracingcommands = 2, also shows every expand- lowres
mag
able token just before it is expanded (except that macros are separate, they™re traced
draw
only when tracingmacros > 0). And if tracingcommands = 3, also shows ¬lldraw
every algebraic operation just before it is evaluated. Thus you can get “stream of octant
transition
consciousness” information about everything is doing.
peno¬set
tracingonline
Digitized output can be monitored by setting tracingedges = 1. For example, tracingall
if we ask to draw the Ionian ˜ ™ of Chapter 5 at a resolution of showstopping
show
100 pixels per inch (lowres mode with mag = .5), tracingedges will report as follows:
loggingall
interact
Tracing edges at line 15: (weight 1) tracingnone
tracingstats
(1,5)(1,2)(2,2)(2,1)(3,1)(3,0)(8,0)(8,1)(9,1)(9,2)(10,2)(10,8)(9,8)
(9,9)(8,9)(8,10)(3,10)(3,9)(2,9)(2,8)(1,8)(1,5).
Tracing edges at line 15: (weight -1)
(3,5)(3,2)(4,2)(4,1)(7,1)(7,2)(8,2)(8,8)(7,8)(7,9)(4,9)(4,8)(3,8)(3,5).

By following these edges (and negating their weights on the inner boundary) we ¬nd
that the character at this low resolution is symmetric:




Further information about digitization comes out when tracingedges > 1, if
¬xed pens are used to draw or ¬lldraw a shape. In this case detailed informa-
tion is presented about the activity in each octant direction; straight line “transition”
edges are also reported whenever changes from one peno¬set to another.

The tracing . . . commands put all of their output into your log ¬le, unless the
tracingonline parameter is positive; in the latter case, all diagnostic informa-
tion goes to the terminal as well as to the log ¬le. Plain has a tracingall
macro that turns on the maximum amount of tracing of all kinds. It not only sets up
tracingcommands , tracingedges , tracingspecs , and so on, it also sets tracingonline := 1,
and it sets showstopping := 1 so that you can do interactive debugging via show com-
mands. This is the works. There™s also loggingall, which is like tracingall except
that it doesn™t touch tracingonline or showstopping . You can say interact if you want
just tracingonline := showstopping := 1. Finally, there™s tracingnone, which shuts o¬
every form of tracing after you™ve had enough.

Some production versions of have been streamlined for speed.
These implementations don™t look at the value of tracingstats , nor do you
get extra information when tracingedges > 1, because runs faster when it
doesn™t have to maintain statistics or keep tabs on whether tracing is required. If you
want all of ™s diagnostic tools, you should be sure to use the right version.
Chapter 27: Recovery from Errors 231


If you set pausing := 1, will give you a chance to edit each line of pausing
lies
input as it is read from the ¬le. In this way you can make temporary patches
jokes
(e.g., you can insert show. . . commands) while troubleshooting, without changing the FONT
actual contents of the ¬le, and you can keep running at human speed. HEIN

Final hint: When working on a large font, it™s best to prepare only a
few characters at a time. Set up a “test” ¬le and a “master” ¬le, and do your
work in the test ¬le. (Appendix E suggests a convenient way to prepare control
¬les that supply parameters to individual test characters as well as to the whole
font.) After the characters come out looking right, you can append them to the
master ¬le; and you can run the master ¬le through occasionally, in
order to see how the font is shaping up. Characters can always be moved back
to the test ¬le if you have to ¬x some unexpected problems.
EXERCISE 27.4
Final exercise: Find all of the lies in this manual, and all of the jokes.

Final exhortation: GO FORTH now and create masterpieces of digital typography!




With respect to the directions of the route
I may have made some errors.
” FRAY PEDRO FONT, Diary (1776)

The road to wisdom? Well, it™s plain
and simple to express:
Err
and err
and err again
but less
and less
and less.
” PIET HEIN, Grooks (1966)
(page 232)




A
Answers to
All the
Exercises
Appendix A: Answers to All the Exercises 233


The preface to this manual points out the wisdom of trying to ¬gure out each whatever
dir
exercise before you look up the answer here. But these answers are intended to
be read, since they occasionally provide additional information that you are best
equipped to understand when you have just worked on a problem.
2.1. Point 5 = (100, 0) is closer than any of the others. (See the diagram below.)
2.2. False. But they all do have the
same y coordinate.
(Figure A2a will be inserted here; too bad you
2.3. 5 units to the left of the reference can™t see it now.)

point, and 15 units up.
2.4. (200, ’60).
2.5. top lft z1 = (0, b); top z2 = (a, b); top rt z3 = (2a ’ 1, b); bot lft z4 = (0, 0);
bot z5 = (a, 0); bot rt z6 = (2a ’ 1, 0). Adjacent characters will be separated by exactly
one column of white pixels, if the character is 2a pixels wide, because the right edge of
black pixels is speci¬ed here to have the x coordinate 2a ’ 1.
2.6. right = (1, 0); left = (’1, 0); down = (0, ’1); up = (0, 1).
2.7. True; this is (2, 3) ’ (5, ’2).
2.8. 0[z1 , z2 ] = z1 , because we move none of the way towards z2 ; similarly 1[z1 , z2 ]
simpli¬es to z2 , because we move all of the way. If we keep going in the same direction
until we™ve gone twice as far as the distance from z1 to z2 , we get to 2[z1 , z2 ]. But if
we start at point z1 and face z2 , then back up exactly half the distance between them,
we wind up at (’.5)[z1 , z2 ].
2.9. (a) True; both are equal to z1 + 1 (z2 ’ z1 ). (b) False, but close; the right-hand
2
side should be 2 z1 + 1 z2 . (c) True; both are equal to (1 ’ t)z1 + tz2 .
3 3

2.10. There are several reasons. (1) The equations in a program should
represent the programmer™s intentions as directly as possible; it™s hard to understand
those intentions if you are shown only their ultimate consequences, since it™s not easy
to reconstruct algebraic manipulations that have gone on behind the scenes. (2) It™s
easier and safer to let the computer do algebraic calculations, rather than to do them
by hand. (3) If the speci¬cations for z1 and z5 change, the formula ( 1 [x1 , x5 ], b) still
2
gives a reasonable value for z3 . It™s almost always good to anticipate the need for
subsequent modi¬cations.
However, the stated formula for z3 isn™t the only reasonable way to proceed.
We could, for example, give two equations
x3 ’ x1 = x5 ’ x3 ; y3 = b;
the ¬rst of these states that the horizontal distance from 1 to 3 is the same as the
horizontal distance from 3 to 5. We™ll see later that is able to solve a wide
variety of equations.
2.11. The following four equations su¬ce to de¬ne the four unknown quantities x2 ,
y2 , x4 , and y4 : z4 ’ z2 = whatever — dir 20; 1 [y2 , y4 ] = 2 [y3 , y1 ]; z2 = whatever [z1 , z3 ];
2 3
z4 = whatever [z3 , z5 ].
234 Appendix A: Answers to All the Exercises


3.1. The direction at z2 is parallel to the line z4 . . z3 , but the vector z4 ’ z3 drawdot
speci¬es a direction towards z4 , which is 180—¦ di¬erent from the direction z3 ’ z4 that one point

was discussed in the text. Thus, we have a di¬cult speci¬cation to meet, and - curl
draws a pretzel-shaped curve that loops around in a way that™s too ugly to
show here. The ¬rst part of the path, from z4 to z2 , is mirror symmetric about the
line z1 . . z5 that bisects z4 . . z2 , so it starts out in a south-by-southwesterly direction;
the second part is mirror symmetric about the vertical line that bisects z2 . . z3 , so
when the curve ends at z3 it™s traveling roughly northwest. The moral is: Don™t specify
a direction that runs opposite to (i.e., is the negative of) the one you really want.

3.2. draw z5 . . z4 {z4 ’ z2 } . . z1 . . z3 . . z6 {z2 ’ z6 } . . cycle.

4.1. (a) An ellipse 0.8 pt tall and 0.2 pt wide (˜ ™); (b) a circle of diameter 0.8 pt
(rotation doesn™t change a circle!); (c) same as (a).

4.2. Six individual points will be drawn, instead of lines or curves. These points will
be drawn with the current pen. However, for technical reasons explained in Chapter 24,
the draw command does its best work when it is moving the pen; the pixels you get
at the endpoints of curves are not always what you would expect, especially at low
resolutions. It is usually best to say ˜drawdot™ instead of ˜draw™ when you are drawing
only one point.

4.3. True, for all of the pens discussed so far. But false in general, since we will see
later that pens might extend further upward than downward; i.e., t might be unequal
to b in the equations for top and bot .

4.4. x2 = x1 ; x3 = 1 [x2 , x4 ]; x4 = x5 ; bot y1 = ’o; top y2 = h + o; y4 = y2 ;
2
y5 = y1 ; draw z1 . . z2 ; draw z2 . . z3 ; draw z3 . . z4 ; draw z4 . . z5 . We will learn
later that the four draw commands can be replaced by

draw z1 - - z2 - - z3 - - z4 - - z5 ;

in fact, this will make run slightly faster.

4.5. Either say ˜¬ll z5 . . z4 . . z1 . . z3 . . z6 . . z5 . . cycle™, which doubles point z5
and abandons smoothness there, or ˜¬ll z5 {curl 1} . . z4 . . z1 . . z3 . . z6 . . {curl 1}cycle™.
In the latter case you can omit either one of the curl speci¬cations, but not both.

4.6. After the six original points have been de¬ned, say

¬ll z5 . . z4 . . z1 . . z3 . . z6 . . cycle;
z0 = (.8[x1 , x2 ], .5[y1 , y4 ]);
for k = 1 upto 6: zk = .2[zk , z0 ]; endfor
un¬ll z5 . . z4 . . z1 . . z3 . . z6 . . cycle.
1
[North , 1 [North , West ]] = 1 [90, 1 [90, 180]] = 1 [90, 135] = 112.5.
4.7. 2 2 2 2 2

4.8. 30—¦ , 60—¦ , 210—¦ , and 240—¦ . Since it™s possible to add or subtract 360—¦ without
changing the meaning, the answers ’330—¦ , ’300—¦ , ’150—¦ , and ’120—¦ are also correct.

4.9. z1l = (25, 30), z1r = (25, 20).

4.10. He said ˜penstroke z1e {up } . . z2e {left } . . z3e {down } . . z4e {right } . . cycle™.
Appendix A: Answers to All the Exercises 235


4.11. We use angles perpendicular to (w, h) and (w, ’h) at the diagonal endpoints: perpendicular
S
x1l = x4l = 0; direction
trial path
x2 = x5 = .5w; perpendicular
x3r = x6r = w;
y1r = y2 = y3l = h;
y4r = y5 = y6l = 0;
z1 = .25[z1 , z6 ]; z6 = .75[z1 , z6 ];
theta1 := angle(w, ’h) + 90;
penpos1 (b, theta1 ); penpos6 (b, theta1 );
z7 = .5[z1 , z6 ]; penpos7 (.6b, theta1 );
penpos1 (b, theta1 ); penpos6 (b, theta1 );
penstroke z1e . . z1 e {z6 ’ z1 } . . z7e . . {z6 ’ z1 }z6 . . z6e ;
e
z3 = .25[z3 , z4 ]; z4 = .75[z3 , z4 ];
theta3 := angle(’w, ’h) + 90;
penpos3 (b, theta3 ); penpos4 (b, theta3 );
z8 = .5[z1 , z6 ]; penpos8 (.6b, theta3 );
penpos3 (b, theta3 ); penpos4 (b, theta3 );
penstroke z3e . . z3 e {z4 ’ z3 } . . z8e . . {z4 ’ z3 }z4 . . z4e ;
e
penpos2 (b, 0); penpos5 (b, 0); penstroke z2e . . z5e .
5.1. The width is 0.8em#, and an em# is 10 true points, so the box will be exactly
8 pt wide in device-independent units. The height will be 7 pt. (And the depth below
the baseline will be 0 pt.)
5.2. 8 — 3.6 = 28.8 rounds to the value w = 29; similarly, h = 25. (And d = 0.)
5.3. Here™s one way, using a variable slab to control the pen breadth at the ends
of the stroke:
slab#:=.8pt#; define_blacker_pixels(slab);
beginchar("S",5/9em#,cap#,0); "The letter S";
penpos1(slab,70); penpos2(.5slab,80);
penpos3(.5[slab,thick],200); penpos5(.5[slab,thick],210);
penpos6(.7slab,80);
penpos7(.25[slab,thick],72);
x1=x5; y1r=.94h+o;
x2=x4=x6=.5w; y2r=h+o; y4=.54h; y6l=-o;
x3r=.04em; y3=.5[y4,y2];
x5l=w-.03em; y5=.5[y4,y6];
(Figure A5a will be inserted
.5[x7l,x7]=.04em; y7l=.12h-o; here; too bad you can™t see it
path trial; trial=z3{down}..z4..{down}z5; now.)
pair dz; dz=direction 1 of trial;
penpos4(thick,angle dz-90);
penstroke z1e..z2e{left}..z3e{down}
..z4e{dz}..z5e{down}..z6e{left}..z7e;
penlabels(1,2,3,4,5,6,7); endchar;
Notice that the pen angle at point 4 has been found by letting construct
a trial path through the center points, then using the perpendicular direction. The
letters work reasonably well at their true size: ˜ .™
236 Appendix A: Answers to All the Exercises


5.4. After an “isolated expression,” thinks it is at the end of a state- zero
enormous number
ment or command, so it expects to see a semicolon next. You should type, e.g.,
backslash
˜I; mode_setup™ to keep happy. begingroup
save
5.5. Yes. endgroup
1
6.1. (a) No, the second token represents 65536 . (A token has the same meaning
as ˜0™ if and only if its decimal value is strictly less than 2’17 = .00000 76293 94531 25.)
1
(b) Yes; both tokens represent 65536 , because 1 is the nearest integer to both .00001 —
2
65536 = .65536 and 0.00002 — 65536 = 1.31072. (c) No, 0.00003 represents 65536 .
(d) Yes, they both mean “enormous number that needs to be reduced”;
complains in both cases and substitutes the largest legal numeric token. (Rounding
1
4095.999999 to the nearest multiple of 65536 yields 4096, which is too big.)
6.2. xx , 3.1 (a numeric token), .6 (another numeric token), .. , [[ , a ,
+- , bc d , e , ] , ] , "a %" (a string token), <|> , ( (see rule 5), ( , $ , 1 (a
numeric token), 5 (likewise numeric), "+-" (a string token), and "" (a string token
that denotes an empty sequence of characters). All of these tokens are symbolic unless
otherwise mentioned. (Notice that four of the spaces and two of the periods were
deleted by rule 1. One way to verify that ¬nds precisely these tokens is to
prepare a test ¬le that says ˜isolated expression;™ on its ¬rst line and that contains
the stated text on its second line. Then respond to ™s error message by
repeatedly typing ˜1™, so that one token is deleted at a time.)
6.3. The statement is basically true but potentially misleading. You can insert any
number of spaces between tokens without changing the meaning of a program, but you
cannot insert a space in the middle of any token without changing something. You can
delete spaces between tokens unless that would “glue” two adjacent tokens together.
6.4. False. It may seem that this new sort of numeric token would be recognized
only in cases where the period is not followed by a digit, hence the period would be
dropped anyway by rule 1. However, the new rule would have disastrous consequences
in a line like ˜draw z1..z2™ !
7.1. You can put a space between the subscripts, as in ˜a1 5™. (We™ll see later that
a backslash acts as a null symbol, hence ˜a1\5™ is another solution.)
7.2. No; a[-1] can™t be accessed without using [ and ]. The only other form of
subscript is numeric token , which can™t be negative. (Well, strictly speaking, you
could say ˜let ?=[; let ??=]™ and then refer to ˜a?-1??™; but that™s cheating.)
7.3. Assuming that ˜+™ was still a spark when he said ˜let plus=+™, he can™t refer
to the variable ˜a.plus1™ unless he changes the meaning of plus again to make it a tag.
(We will eventually learn a way to do this without permanently clobbering plus, as
follows: ˜begingroup save plus; a.plus1 endgroup™.)
7.4. True. (But a su¬x is not always a variable .)
7.5. Yes, because it removes any existing value that x may have had, of whatever
type; otherwise you couldn™t safely use x in a numeric equation. It™s wise to declare
numeric variables when you™re not sure about their former status, and when you™re
sure that you don™t care what their previous value was. A numeric declaration together
with a comment also provides useful documentation. (Incidentally, ˜numeric x™ doesn™t
a¬ect other variables like ˜x2™ or ˜x.x™ that might be present.)
Appendix A: Answers to All the Exercises 237


7.6. (a) The ˜42™ is illegal because subscripts must be collective. (b) The ˜24™ is **
FORTRAN
illegal because a declared variable must start with a symbolic token , not a numeric
or
token. (c) There™s nothing wrong with the consecutive commas; the second comma ¡
begins a declared variable , so it loses its former meaning and becomes a tag. Thus ¿
accuracy
tries to declare the variable ˜,t,path™. However, ˜path™ cannot appear in
a su¬x, since it™s a spark. (Yes, this is admittedly tricky. Computers follow rules.)

8.1. ((z1+z2)..((z3/4)*5))..(z6-(7*(8z9))).
8.2. The fraction 100/3 is evaluated ¬rst (because such divisions take precedence);
the rounding error in this fraction is then magni¬ed by 100.

8.3. A sqrt takes precedence over any operation with two operands, hence the
machine computes ˜(sqrt 2)**2™; was somewhat lucky that the answer
turned out to be exactly 2. (The sqrt operation computes the nearest multiple of
1
, and the rounding error in this quantity is magni¬ed when it is squared. If you try
65536
sqrt 3**2, you™ll get 3.00002; also sqrt 2**4 turns out to be 4.00002.) Incidentally,
the ** operation of plain has the same precedence as * and /; hence
˜x*y**2™ means the same as ˜(x*y)**2™, and ˜-x**2™ means ˜(-x)**2™, contrary to the
conventions of FORTRAN.

8.4. Since ˜or™ has stronger precedence than ˜<™ or ˜>™, tries to eval-
uate this expression by putting things in parentheses as follows: ˜(0 > (1 or a)) < a™.
Now ˜1 or a™ makes no sense, because ˜or™ operates only on booleans; in such cases
uses the right operand ˜a™ as the result. Then ˜0 > a™ is indeterminate
because a is unknown; treats this as false. Finally ˜false < a™ is another
illegal combination of types.
8.5. The token ˜++-™ is unde¬ned, so it is a tag; therefore ++-7 is a subscripted
variable, which was multiplied by zero.

8.6. The associative law is valid for exact computations, but not for rounded com-
putations. For example, it fails even in the case of multiplication, since (.1 — .1) — 10 =
0.09995 while .1 — (.1 — 10) = .1 when products are rounded to the nearest multiples
1
of 65536 . However, this observation doesn™t quite explain the stated example, which
would have yielded 7 in all cases if √ had computed 2 ++ 4 with full accu-
racy! The closest approximation to 20 is 4 65536 , but 2 ++ 4 turns out to be 4 30941
30942
65536
instead. computes the absolutely best possible approximations to the true
answers when it does multiplications, divisions, and square roots, but not when it does
Pythagorean operations.
8.7. It™s impossible to make an expression from ˜ numeric token numeric token ™,
because the rule for scalar multiplication operator speci¬cally prohibits this. -
will recognize the ¬rst ˜2™ as a numeric primary , which is ultimately regarded
as a numeric expression ; the other ˜2™ will probably be an extra token that is ¬‚ushed
away after an error message has been given.

8.8. If a numeric token is followed by ˜/ numeric token ™ but not preceded by
˜ numeric token /™, the syntax allows it to become part of an expression only by us-
ing the ¬rst case of numeric token primary . Therefore ˜1/2/3/4™ must be treated as
˜(1/2)/(3/4)™, and ˜a/2/3/4™ must be treated as ˜a/(2/3)/4™.
238 Appendix A: Answers to All the Exercises


8.9. string primary ’’ string variable rotated
| string token dotprod
abs
| ( string expression ) ypart
| substring pair expression of string primary pythagorean subtraction
unitvector
string secondary ’’ string primary length
string tertiary ’’ string secondary
string expression ’’ string tertiary
| string expression & string tertiary
(The full syntax in Chapter 25 includes several more varieties of string primary that
haven™t been hinted at yet.)
9.1. (a) Point 1 should lie nine pixels to the left of point 7, considering horizontal
positions only; no information is given about the vertical positions y1 or y7 . (b) Point 7
should sit directly above or below point 4, and its distance up from the baseline should
be halfway between that of points 4 and 5. (c) The left edge of the currently-picked-up
pen, when that pen is centered at point 21, should be one pixel to the right of its right
edge when at point 20. (Thus there should be one clear pixel of white space between
the images of the pen at points 20 and 21.)
9.2. (a) y13 = ’y11 (or ’y13 = y11 , or y13 + y11 = 0). (b) z10 = z12 + (mm , ’1).
(c) z43 = 1 [(0, h), (w, ’d)].
3

9.3. (a) z1 = z2 = z3 = (w, h); z4 = z5 = z6 = (0, 0). (b) z1 = z6 = (.5w, .5h);
z2 = (.75w, .75h); z3 = (w, h); z4 = (0, 0); z5 = (.25w, .25h).
9.4. z = whatever [z1 , z2 ]; z = whatever [z3 , z4 ]. (Incidentally, it™s interesting
to watch this computation in action. Run with \tracingequations:=
tracingonline:=1 and say, for example,
z=whatever[(1,5),(8,19)]; z=whatever[(0,17),(6,1)];
the solution appears as if by magic. If you use alpha and beta in place of the whatevers,
the machine will also calculate values for alpha and beta .)
9.5. z = whatever [z1 , z2 ]; z ’ z3 = whatever — (z5 ’ z4 ).
9.6. z11 ’ z12 = whatever — (z13 ’ z14 ) rotated 90, assuming that z13 ’ z14 is known.
(It™s also possible to say ˜(z11 ’z12 )dotprod(z13 ’z14 ) = 0™, although this risks over¬‚ow
if the coordinates are large.)
9.7. One solution constructs the point z4 on z2 . . z3 such that z4 . . z1 is per-
pendicular to z2 . . z3 , using ideas like those in the previous two exercises: ˜z4 =
whatever [z2 , z3 ]; z4 ’ z1 = whatever — (z3 ’ z2 ) rotated 90™. Then the requested dis-
tance is length(z4 ’ z1 ). But there™s a slicker solution: Just calculate
abs ypart((z1 ’ z2 ) rotated ’angle(z3 ’ z2 )).
9.8. It would be nice to say simply ˜z = whatever [z2 , z3 ]™ and then to be able to
say either ˜length(z ’ z1 ) = l™ or ˜z ’ z1 = (l, 0) rotated whatever ™; but neither of the
second equations is legal. (Indeed, there couldn™t possibly be a legal solution that has
this general ¬‚avor, because any such solution would determine a unique z, while there
are two points to be determined.) The best way seems to be to compute z4 as in the
previous exercise, and then to let v = (l +’+ length(z4 ’ z1 )) — unitvector(z3 ’ z2 ); the
desired points are then z4 + v and z4 ’ v.
Appendix A: Answers to All the Exercises 239


9.9. Such an equation tells us nothing new about a or b. Indeed, each use of whatever
CAPSULE
whatever introduces a new independent variable, and each new independent variable
capsule
“uses up” one equation, since we need n equations to determine the values of n un- hash hash
knowns. On the other hand an equation between pairs counts as two equations; so tracingcapsules
there™s a net gain of one, when whatever appears in an equation between pairs.
10.1. Yes, but it must be done in two steps: ˜numeric newcode ; newcode = code +1;
numeric code ; code = newcode ™.
10.2. The assignment ˜x3 := whatever ™ does exactly what you want.
10.3. The result shows that s1 = s3 = s4 and s2 = s5 = s6 now:
s[]=unknown string
s1=unknown string s3
s2=unknown string s6
s3=unknown string s4
s4=unknown string s1
s5=unknown string s2
s6=unknown string s5
(The assignment s2 := s5 broke s2 ™s former relationship with s1 , s3 , and s4 .)
10.4. The results are
## a=1
(after the ¬rst assignment)
## a=b+1
(after the second assignment)
## b=0.5a-0.5
(after the third assignment)
### -1.5a=-%CAPSULEnnnn-0.5
(after the third, see below)
## a=%CAPSULEnnnn
(after ˜show™; variable a is independent)
>> a
(this is the ¬nal value of b)
>> 0.33333a-0.33333
Let ak denote the value of a after k assignments were made. Thus, a0 = 1, and a1
was dependent on the independent variable b. Then a1 was discarded and b became
dependent on the independent variable a2 . The right-hand side of the third assignment
was therefore a2 + b. At the time a2 was about to be discarded, had two
dependencies b = 0.5a2 ’ 0.5 and κ = 1.5a2 ’ 0.5, where κ was a nameless “capsule”
inside of the computer, representing the new value to be assigned. Since κ had a
higher coe¬cient of dependency than b, chose to make κ an independent
variable, after which ’1.5a2 was replaced by ’κ ’ 0.5 in all dependencies; hence b was
equal to 0.33333κ ’ 0.33333. After the third assignment was ¬nished, κ disappeared
and a3 became independent in its place. (The line ˜## a=%CAPSULEnnnn™ means that
a was temporarily dependent on κ, before κ was discarded. If the equation a = κ had
happened to make κ dependent on a, rather than vice versa, no ˜##™ line would have
been printed; such lines are omitted when a capsule or part of a capsule has been made
dependent, unless you have made tracingcapsules > 0.)
11.1. Almost, but not quite. The values of standard dimension variables like pt
and mm will be identical in both setups, as will the values of ad hoc dimension
variables like em and x height . But pen-oriented dimensions that are de¬ned via
de¬ne blacker pixels will be slightly di¬erent, because cheapo mode has blacker =
240 Appendix A: Answers to All the Exercises


0.65 while luxo mode has blacker = 0.1 (since the luxo printer has di¬erent physi- Tobin
cal characteristics). Similarly, de¬ne corrected pixels (which we are just about to
discuss) will produce slightly di¬erent results in the two given modes.

11.2. Increasing ht # would make the letter shape and the bounding box taller;
increasing xgap # would move point 5 to the left, thereby making the middle bar shorter;
increasing u# would make the shape and its bounding box wider; increasing s# would
widen the bounding box at both sides without changing the letter shape; increasing o#
would move points 4, 5, and 6 to the right; increasing px # would make the pen thicker
(preserving the top edge of the upper bar, the bottom edge of the lower bar, and the
center of the middle bar and the stem).

11.3. The only possible surprise is the position of y1 , which should match similar
details in the ˜ ™ and the ˜ ™ of Chapter 4:
beginchar("F",14*u#+2s#,ht#,0); pickup logo_pen;
x1=x2=x3=leftstemloc; x4=w-x1+o; x5=x4-xgap;
y2=y5; y3=y4; bot y1=-o; top y3=h; y2=barheight;
draw z1--z3--z4; draw z2--z5;
labels(1,2,3,4,5); endchar;

11.4. The quantity called ss in Chapter 4 is now leftstemloc .
beginchar("M",18*u#+2s#,ht#,0); pickup logo_pen;
x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;
y1=y5; y2=y4; bot y1=-o; top y2=h+o; y3=y1+ygap;
draw z1--z2--z3--z4--z5;
labels(1,2,3,4,5); endchar;
beginchar("T",13*u#+2s#,ht#,0); pickup logo_pen;
lft x1=0; x2=w-x1; x3=x4=.5w;
y1=y2=y3; top y1=h; bot y4=-o;
draw z1--z2; draw z3--z4;
labels(1,2,3,4); endchar;

11.5. ˜ ™; possibly also ˜ ™; and Georgia Tobin sug-
gests that ˜ ™ might be a legal term.

11.6. Delete the line of logo.mf that de¬nes barheight#, and insert that line into
each of the parameter ¬les logo10.mf, logo9.mf, logo8.mf. Then other bar-line heights
are possible by providing new parameter ¬les; another degree of “meta-ness” has there-
fore been added to the meta-font.

11.7. (This is tricky.) Insert the lines
if known pixmag: begingroup interim hppp:=pixmag*hppp;
special "title cheapo simulation" endgroup;
extra_endchar:="currentpicture:=currentpicture scaled pixmag;"
& "w:=w*pixmag;" & extra_endchar; fi

right after ˜mode_setup™ in logo.mf, and also include the line

if known pixmag: hppp:=pixmag*hppp; vppp:=pixmag*vppp; fi
Appendix A: Answers to All the Exercises 241


at the very end of that ¬le. Then run with special
gf
\mode="cheapo"; input cheaplogo10
where the ¬le ˜cheaplogo10.mf™ says simply ˜pixmag=10; input logo10™. (The interim
hppp setting and the special command are used to fool into giving the
appropriate extension to the gf ¬le name. Incidentally, you could print with this font on
cheapo at ten-fold magni¬cation if you told TEX to use the font ˜cheaplogo10 scaled
10000™; but on luxo you would simply call this font ˜cheaplogo10™.)
12.1. The changes are straightforward, except for the italic correction (for which a
rough estimate like the one shown here is good enough):
"Right parenthesis";
numeric ht #, dp #; ht # = body height #; .5[ht #, ’dp #] = axis #;
beginchar (")", 7u#, ht #, dp #); italcorr axis # — slant ’ .5u#;
pickup ¬ne.nib ; penpos1 (hair ’ ¬ne , 0);
penpos2 (.75[thin , thick ] ’ ¬ne , 0); penpos3 (hair ’ ¬ne , 0);
lft x1l = lft x3l = u; rt x2r = x1 + 4u; top y1 = h; y2 = .5[y1 , y3 ] = axis ;
¬lldraw z1l {(z2l ’ z1l ) xscaled 3} . . . z2l . . . {(z3l ’ z2l ) xscaled 3}z3l
- - z3r {(z2r ’ z3r ) xscaled 3} . . . z2r . . . {(z1r ’ z2r ) xscaled 3}z1r - - cycle;
penlabels(1, 2, 3); endchar;
We will see in Chapter 15 that it™s possible to guarantee perfect symmetry between left
and right parentheses by using picture transformations.
12.2. When horizontal lines are being typeset, TEX keeps track of the maximum
height and maximum depth of all boxes on the line; this determines whether or not
extra space is needed between baselines. The height and depth are also used to position
an accent above or below a character, and to place symbols in mathematical formulas.
Sometimes boxes are also stacked up vertically, in which case their heights and depths
are just as important as their widths are for horizontal setting.
13.1. (4, 4), (4, 5), (5, 5), (5, 4). (Therefore the command
un¬ll (4, 4) - - (4, 5) - - (5, 5) - - (5, 4) - - cycle
will decrease the value of this pixel by 1.)
13.2. The result would be exactly the same; ¬ll and un¬ll commands can be given
in any order. (After an initial un¬ll command, some pixel values will be ’1, the
others will be zero.)
13.3. un¬ll (4, 1) - - (4, 8) - - (5, 8) - - (5, 1) - - cycle.
13.4. Here are two of the many solutions:
¬ll (0, 3) - - (9, 3) - - (9, 6) - - (6, 6) - - (6, 9) - -
(3, 9) - - (3, 0) - - (6, 0) - - (6, 6) - - (0, 6) - - cycle;
¬ll (0, 3) - - (9, 3) - - (9, 6) - - (0, 6) - - (0, 3) - -
(3, 3) - - (3, 0) - - (6, 0) - - (6, 9) - - (3, 9) - - (3, 3) - - cycle.
(It turns out that any pixel pattern can be obtained by a single, su¬ciently hairy ¬ll
command. But unnatural commands are usually also ine¬cient and unreadable.)
242 Appendix A: Answers to All the Exercises


13.5. The value of the enclosed pixel is increased by 2. (We™ll see later that there™s cullit
undraw
a simpler way to do this.)
withpen
clearit
13.6. True; j ’ k = l ’ m, since k + l = j + m. (What comes up must go down.) withpen
addto
13.7. The tricky part is to remember that ˜erase draw zi - - zj ™ will erase pixels withweight
undraw
near zi and zj . Therefore if z3 - - z4 is drawn before z4 - - z2 , we can™t erase z4 - - z2
cullit
without losing some of z3 - - z4 ; it™s necessary to erase only part of one line. One way undraw
Leban
to solve the problem is to do the following, after de¬ning the points and picking up the
pen as before:

draw z3 - - z4 ; draw z5 - - z6 ;
cullit; pickup pencircle scaled 1.6pt ;
undraw z7 - - 1 [z7 , z5 ]; undraw z2 - - 1 [z2 , z4 ];
2 2
cullit; pickup pencircle scaled .4pt ;
draw z3 - - z1 - - z2 - - z4 ; draw z5 - - z7 - - z8 - - z6 ;
for k = 1 upto 4: draw zk - - zk+4 ; endfor.

(Note that it would not be quite enough to erase only from z7 to 1 [z7 , z5 ]!)
3
It™s also possible to solve this problem without partial erasing, if we use addi-
tional features of that haven™t been explained yet. Let™s consider only the
job of drawing z7 - - z5 - - z6 and z3 - - z4 - - z2 , since the other eight lines can easily be
added later. Alternative Solution 1 uses picture operations:

pen eraser ; eraser = pencircle scaled 1.6pt ;
draw z3 - - z4 ; erase draw z7 - - z5 withpen eraser ; draw z7 - - z5 ;
picture savedpicture ; savedpicture = currentpicture ; clearit;
draw z6 - - z5 ; erase draw z2 - - z4 withpen eraser ; draw z2 - - z4 ;
addto currentpicture also savedpicture .

Alternative Solution 2 is trickier, but still instructive; it uses ˜withweight™ options and
the fact that draw does not increase any pixel values by more than the stated weight
when the path is a straight line:

- - z4 ; undraw z7 - - z5 withpen eraser ;
draw z3
draw z7 - - z5 withweight 2; cullit withweight 2;
- - z5 ; undraw z2 - - z4 withpen eraser ;
draw z6
draw z2 - - z4 withweight 2;

(These alternative solutions were suggested by Bruce Leban.)

13.8. Here™s an analog of the ¬rst solution to the previous exercise:

beginchar ("*", 10pt #, 7pt #, 2pt #);
pair center ; . . . as in the hint
pickup pencircle scaled .4pt ; draw star ;
cullit; pickup pencircle scaled 1.6pt ;
for k = 0 upto 4: undraw subpath(k + .55, k + .7) of star ; endfor
cullit; pickup pencircle scaled .4pt ;
for k = 0 upto 4: draw subpath(k + .47, k + .8) of star ; endfor
labels(0,1,2,3,4); endchar.
Appendix A: Answers to All the Exercises 243


However, as in the previous case, there™s an Alternate Solution 1 by Bruce Leban that Leban
overdraw
is preferable because it doesn™t depend on magic constants like .55 and .47:
begingroup
interim
beginchar . . . as above . . . scaled .4pt ; addto
picture savedpicture ; savedpicture = nullpicture; cull
cullit
pen eraser ; eraser := pencircle scaled 1.6pt ; also
for k = 0 upto 4: endgroup
origin
draw subpath(k, k + 1) of star ; cullit;
undraw subpath(k + 3, k + 4) of star withpen eraser ; cullit;
addto savedpicture also currentpicture ; clearit; endfor
currentpicture := savedpicture ; labels(0,1,2,3,4); endchar.

13.9. It increases pixel values by 1 in the ¬ve lobes of the star, and by 2 in the
central pentagon-like region.

13.10. def overdraw expr c = erase ¬ll c; draw c enddef .

13.11. First we need to generalize the overdraw macro of the previous exercise so
that it applies to arbitrary cycles c, even those that are self-intersecting:

def overdraw expr c = begingroup
picture region ; region := nullpicture;
interim turningcheck := 0; addto region contour c;
cull region dropping (0, 0);
cullit; addto currentpicture also ’region ; cullit;
draw c endgroup enddef ;

(This code uses operations de¬ned later in this chapter; it erases the region of pixels
that would be made nonzero by the command ˜¬ll c™.) The watchband is now formed
by overdrawing its links, one at a time, doing ¬rst the ones that are underneath:

beginchar(M , 1.25in #, .5in #, 0); pickup pencircle scaled .4pt ;
z1 = (20, ’13); z2 = (30, ’6); z3 = (20, 1); z4 = (4, ’7);
z5 = (’12, ’13); z6 = (’24, ’4); z7 = (’15, 6);
path M ; M = (origin . . z1 . . z2 . . z3 . . z4 . . z5 . . z6 . . z7 . .
origin . . ’z7 . . ’z6 . . ’z5 . . ’z4 . . ’z3 . . ’z2 . . ’z1 . . cycle)
scaled (h/26) shifted (.5w, .5h);
def link(expr n) =
overdraw subpath 1 (n, n + 1) of M - -
3
subpath 1 (n + 25, n + 24) of M - - cycle enddef ;
3
for k = 1 upto 12: link(k + 11); link(12 ’ k); endfor endchar;

13.12. The pixel pattern 1 1 is culled to 11, and needs to sort the edges
21 11
as it does this; so the result is simply
row 1: | 0+ 2-
row 0: | 0+ 2-

13.13. The pixel pattern is 1 1 + 1 1 + 1 1 ’ 2 1 = 1 2 before the ¬nal rotation, with
21 21 12 11 43
the reference point at the lower left corner of the 4; after rotation it is 2 3 , with the
14
reference point at the lower right corner of the 4. Rotation causes to sort
244 Appendix A: Answers to All the Exercises


the edges, but the transition values per edge are never more than ±3. You weren™t xscaled-1
expected to know about this limit of ±3, but it accounts for what is actually reported: yscaled-1
save
interim
row 1: | -2++ -1+ 0---
turningcheck
row 0: | -2+ -1+++ 0--- 0-

13.14. ˜V scaled-1™ should be the same as ˜V rotated 180™, because transformations
apply to coordinates rather than to pixel values. (Note, incidentally, that the re¬‚ections
˜V xscaled-1™ and ˜V yscaled-1™ both work, and that ˜V scaled-1™ is the same as
˜V xscaled-1 yscaled-1™.)
13.15. The result is the same as ˜V shifted (2,3)™; the coordinates of a shift are
rounded to the nearest integers when a picture is being shifted.
13.16. row 3: 0+ 4- |
row 2: 0+ 4- |
row 1: 0+ 4- 0+ 2- |
row 0: 0+ 4- 0+ 2- |
(Scaling of pictures must be by an integer.)
13.17. is currently executing instructions after having read as far as line 5
of the ¬le expr.mf.
13.18. The pixel values of currentpicture become 1 if they were ±1, otherwise they
become 0.
13.19. (a) addto V1 also V2 ; cull V1 keeping (2, 2). (b) Same, but cull keeping
(1, 2). (c) Same, but cull keeping (1, 1).
13.20. Subtract one from the other, and cull the result dropping (0, 0); then test to
see if the total weight is zero.
13.21. (a) Same as ˜draw p™, but using q instead of the currently-picked-up pen.
(b) Same e¬ect as ˜draw p; draw p; draw p™ (but faster). (c) Same as ˜draw p
withweight w™, because undraw™s ˜withweight ’1™ is overridden. (d) Same as
˜un¬lldraw c; un¬lldraw c™, but using q instead of currentpen . (e) Same as ˜erase
¬lldraw c™, because the ˜withweight 2™ is overridden. [Since erase has culled all
weights to 0 or 1, there™s no need to “doubly erase.”] (f) Same e¬ect as ˜cullit; addto
currentpicture also currentpicture ™ (but faster).
13.22. vardef safe¬ll expr c = save region ;
picture region ; region =nullpicture;
interim turningcheck := 0;
addto region contour c; cull region dropping (0, 0);
addto currentpicture also region enddef .
13.23. cull currentpicture keeping (1, in¬nity );
picture v; v := currentpicture ;
cull currentpicture keeping (1, 1) withweight 3;
addto currentpicture also v ’ v shifted right
’ v shifted left ’ v shifted up ’ v shifted down ;
cull currentpicture keeping (1, 4).
Appendix A: Answers to All the Exercises 245


13.24. (We assume that currentpicture initially has some con¬guration in which all directiontime
pixel values are zero or one; one means “alive.”)
picture v; def c = currentpicture enddef ;
forever: v := c; showit;
addto c also c shifted left + c shifted right ;
addto c also c shifted up + c shifted down ;
addto c also c ’ v; cull c keeping (5, 7); endfor.
(It is wise not to waste too much computer time watching this program.)
14.1. beginchar ("b", 5pt #, 5pt #, 0);
¬ll ((0, 0) - - quartercircle - - cycle) scaled 10pt ; endchar.
1
14.2. A quartercircle corresponds to a circle whose diameter is 1; the radius is .
2

14.3. beginchar ("c", 5pt #, 5pt #, 0);
pickup pencircle scaled (.4pt + blacker );
draw quartercircle rotated 90 scaled 10pt shifted (5pt , 0); endchar.
14.4. beginchar ("d", 5pt # — sqrt 2, 5pt #, 0);
pickup pencircle scaled (.4pt + blacker );
draw ((0, 0) - - quartercircle - - cycle) rotated 45 scaled 10pt shifted (.5w, 0);
endchar.
14.5. beginchar ("e", 10pt #, 7.5pt #, 2.5pt #);
pickup pencircle scaled (.4pt + blacker );
for D = .2w, .6w, w: draw fullcircle scaled D shifted (.5w, .5[’d, h]);
endfor endchar.
The program for ˜ ™ is similar, but ˜fullcircle scaled D™ is replaced by
unitsquare shifted ’(.5, .5) rotated 45 scaled (D/ sqrt 2).

14.6. There are in¬‚ection points, because there are no bounding triangles for the
˜. . .™ operations in the superellipse macro of Appendix B, unless .5 ¤ s ¤ 1.
14.7. (0, 0) . . (1, 0) & (1, 0) . . (1, 1) & (1, 1) . . (0, 1) & (0, 1) . . (0, 0) & cycle.
Incidentally, if each ˜&™ in this path is changed to ˜. .™, we get a path that goes through
the same points; but it is a path of length 8 that comes to a complete stop at each
corner. In other words, the path remains motionless between times 1 ¤ t ¤ 2, 3 ¤ t ¤ 4,
etc. This length-8 path therefore behaves somewhat strangely with respect to the
˜directiontime™ operation. It™s better to use ˜&™ than to repeat points of a path.
14.8. Let δ1 = z1 ’ z0 , δ2 = z2 ’ z1 , δ3 = z0 ’ z2 ; l1 = |δ1 |, l2 = |δ2 |, l3 = |δ3 |;
ψ1 = arg(δ2 /δ1 ), ψ2 = arg(δ3 /δ2 ), ψ3 = arg(δ1 /δ3 ). The equations to be solved are (—)
and (——) for 1 ¤ k ¤ 3, where ±3 = ±0 and β4 = β1 . These six equations determine
θ1 , θ2 , θ3 and φ1 , φ2 , φ3 .
14.9. The path is of length 9, and it is equivalent to ˜(0, 1) - - (1, 1) - - (1, 2) - -
(0, 2) - - (0, 1){down } . . {right }(1, 0) - - (2, 0) - - (2, 1) - - (1, 1) - - (1, 0)™. Although
unitsquare is a cycle, the cycle is broken when it is used inside a larger path; the
resulting non-cyclic square path goes down when it ends and right when it begins.
246 Appendix A: Answers to All the Exercises


14.10. Yes; for example, ˜z0 . . z1 . . z2 - - z3 ™ would be equivalent to ˜z0 . . z1 . . Bernshte˜
±n
{curl 2}z2 {curl 2} . . {curl 2}z3 ™. But a path like z0 - - z1 - - z2 - - z3 would not be
a¬ected, because all directions would turn out to be the same as before. (The path
˜z0 {curl a} . . {curl b}z1 ™ is a straight line regardless of the values of a and b, because
equations (———) and (——— ) always have the solution θ0 = φ1 = 0 when n = 1.)
14.11. It treats this as ˜((0, 0) . . (3, 3) . . cycle){curl 1}™; i.e., the part up to and
including ˜cycle™ is treated as a subpath (cf. ˜p2™ in Chapter 8). The cycle is broken, after
which we have ˜(0, 0) . . controls (2, ’2) and (5, 1) . . (3, 3) . . controls (1, 5) and (’2, 2) . .
(0, 0){curl 1}™. Finally the ˜{curl 1}™ is dropped, because all control points are known.
(The syntax by itself isn™t really enough to answer this question, as you probably
realize. You also need to be told that the computation of directions and control points
is performed whenever uses the second or third alternative in the de¬nition
of path expression .)
14.12. True. The length of a path is the number of ˜zk . . controls uk and vk+1 . . zk+1 ™
segments that it contains, after all control points have been chosen.
14.13. True if 0 ¤ t ¤ 1, except perhaps for rounding errors; otherwise false. The
path z0 - - z1 expands into ˜z0 . . controls 1/3[z0 , z1 ] and 2/3[z0 , z1 ] . . z1 ™, and the
Bernshte˜ polynomial simpli¬es because t[w, w + δ, w + 2δ, w + 3δ] = w + 3tδ. In-
±n
cidentally, ˜point t of (z0 - - - z1 )™ is usually quite di¬erent from t[z0 , z1 ].
14.14. If p is a cycle, or if p is a path of length ≥ 4, the stated subpath has length 2.
Otherwise the length is max(0, length p ’ 2).
14.15. vardef posttension expr t of p =
save q; path q;
q = point t of p {direction t of p} . . {direction t + 1 of p} point t + 1 of p;
length(postcontrol 0 of q ’ point 0 of q)
/length(postcontrol t of p ’ point t of p) enddef ;
vardef pretension expr t of p =
save q; path q;
q = point t ’ 1 of p {direction t ’ 1 of p} . . {direction t of p} point t of p;
length(precontrol 1 of q ’ point 1 of q)
/length(precontrol t of p ’ point t of p) enddef ;
The stated posttension turns out to be 4.54019.
14.16. The ˜&™ had to be changed to ˜. .™, because point t of p might not be exactly
equal to point u of q.
14.17. Since p intersects itself in¬nitely often at times (t, t), the task may seem im-
possible; but ™s shu¬„ed-binary search procedure provides a way. Namely,
p intersectiontimes reverse p = (0.17227, 0.28339), from which we can deduce that
t = 0.17227 and 1 ’ u = 0.28339.
15.1. (x,-y).
15.2. (x, y) rotated 180, or (x, y) scaled ’1.
15.3. True if and only if xpart t = ypart t = 0. If the stated equation holds for
at least one pair (x, y), it holds for all (x, y). According to the syntax of Chapter 8,
Appendix A: Answers to All the Exercises 247


interprets ˜’(x, y) transformed t™ as (’(x, y)) transformed t. (Inciden- a¬ne transformations
homogeneous
tally, mathematicians call ™s transformers “a¬ne transformations,” and the
parentheses
special case in which the xpart and ypart are zero is called “homogeneous.”) makepath
¬llin
15.4. z1 + z2 . commutativity
capsule
15.5. beginchar(126, 25u#, hheight # + border #, 0); "Dangerous left bend";
currentpicture := dbend re¬‚ectedabout ((.5w, 0), (.5w, h)); endchar;

The same idea can be used to create right parentheses as perfect mirror images of left
parentheses, etc., if the parentheses aren™t slanted.

15.6. Change line 9 to

draw (z1 . . . p) rotatedaround((.5w, .5h), ’45)
withpen pencircle scaled 3/4pt yscaled 1/3 rotated ’15;

15.7. Replace line 10 by

pickup pencircle scaled 3/4pt yscaled 1/3 rotated ’60;
draw (z1 . . . p) transformed t;

16.1. If there are two points zk and zk+1 with maximum y coordinate, the value of
˜peno¬set (’in¬nity , epsilon ) of p™ will be zk and ˜peno¬set (’in¬nity , ’epsilon ) of p™
will be zk+1 ; ˜peno¬set left of p™ will be one or the other. If there™s only one top point,
all three of these formulas will produce it. (Actually also allows pens to
be made with three or more vertices in a straight line. If there are more than two top
vertices, you can use peno¬set to discover the ¬rst and the last, as above; furthermore,
if you really want to ¬nd them all, makepath will produce a path from which they
can be deduced in a straightforward manner.)

16.2. ˜pencircle scaled 1.06060™ is the diamond but ˜pencircle scaled 1.06061™
is the square. (This assumes that ¬llin = 0. If, for example, ¬llin = .1, the change
doesn™t occur until the diameter is 1.20204.) The next change is at diameter 1.5, which
gives a diamond twice the size of the ¬rst.

17.1. (a + 1) + (2a + 2) = 3a + 3 and (2a) + (2a + 1) = 4a + 1, respectively. The
¬nal value of x in the ¬rst case is 2a + 2, hence a = .5x ’ 1; expr will report the answer
as 1.5x (in terms of x™s new value), since it has not been told about ˜a™. In the second
case expr will, similarly, say 2x-1.
This example shows that ± + β is not necessarily equal to β + ±, when ± and β
involve group expressions. evaluates expressions strictly from left to right,
performing the statements within groups as they appear.

17.2. The save instruction gives ˜?™ a fresh meaning, hence ˜?™ is a numeric variable
unconnected to any other variables. When the group ends and ˜?™ is restored to its
old meaning, the value of the group expression no longer has a name. (It™s called a
“capsule” if you try to show it.) Therefore the value of the group expression is a new,
nameless variable, as desired.

17.3. It™s a nameless pair whose xpart and ypart are equal; thus it is essentially
equivalent to ˜whatever — (1, 1)™.
248 Appendix A: Answers to All the Exercises


17.4. ˜v3 := begingroup save ?; picture ?; ? endgroup™ refreshes the picture special-purpose
...
variable v3 without changing other variables like v2 . This construction works also for
pairs, pens, strings, etc.
18.1. Yes; the direction at z.j will be either left or right .
18.2. beginlogochar("A",15);
x1=.5w;
x2=x4=leftstemloc;
x3=x5=w-x2;
top y1=h+o;
(Figure A18a will be inserted here;
y2=y3=barheight; too bad you can™t see it now.)
bot y4=bot y5=-o;
draw z4--z2--z3--z5;
super_half(2,1,3);
labels(1,2,3,4,5);
endchar;
Notice that all three calls of super_half in logo.mf are of the form ˜super half (2, j, 3)™.
But it would not be good style to eliminate parameters i and k, even though super_half
is a special-purpose subroutine; that would make it too too special.
18.3. If bracket = 0 or serif darkness = 0. (It™s probably not a good idea to make
serif darkness = 0, because this would lead to an extreme case of the ˜. . .™ triangle,
which might not be numerically stable in the presence of rounding errors.) Another
case, not really desirable, is left jut = right jut = 0.
18.4. That™s a strange question. The serif routine includes a penpos that de¬nes
z$l , z$ , and z$r relative to each other, and it de¬nes the other six points relative to
them. Outside the routine the user ought to specify just one x coordinate and one
y coordinate, in order to position all of the points. This can be done either before or
after serif is called, but has an easier job if it™s done beforehand.
18.5. Yes; see the previous exercise. (But in the program for "A" it™s necessary to
de¬ne y4l and y5r , so that theta 4 and theta 5 can be calculated.)
18.6. beginchar ("H", 13u#, ht #, 0);
x1 = x2 = x5 = 3u;
x3 = x4 = x6 = w ’ x1 ;
y1c = y3c = h; y2c = y4c = 0;
serif (1, thick , ’90, jut , jut );
serif (2, thick , 90, jut , jut ); (Figure A18b will be inserted here; too bad
you can™t see it now.)
serif (3, thick , ’90, jut , jut );
serif (4, thick , 90, jut , jut );
¬ll serif edge 2
- - reverse serif edge 1 - - cycle;
¬ll serif edge 4
- - reverse serif edge 3 - - cycle;
penpos5 (thin , 90); penpos6 (thin , 90);
y5 = y6 = .52h; penstroke z5e - - z6e ;
penlabels(1, 2, 3, 4, 5, 6); endchar.
Appendix A: Answers to All the Exercises 249


18.7. def top serif (su¬x $)(expr xx , theta , left jut , right jut ) = future pen
penpos$ (xx , 0); z$a ’ z$l = z$f ’ z$r = (bracket /abs sind theta ) — dir theta ;
y$c = y$d = y$ ; x$c = x$l ’ left jut ; x$d = x$r + right jut ;
z$b = z$l + whatever — dir theta = z$c + whatever — dir ’phi ;
z$e = z$r + whatever — dir theta = z$d + whatever — dir phi ;
labels($a, $b, $c, $d, $e, $f ) enddef ;
def top serif edge su¬x $ =
(z$a . . controls z$b . . z$c
- - (¬‚ex (z$c , .5[z$c , z$d ] ’ dishing , z$d )) shifted (0, +epsilon )
- - z$d . . controls z$e . . z$f ) enddef ;

18.8. Assuming that py = 0, the e¬ective right stroke weight would be px ·sin(θ5 ’φ)
if it were drawn with one stroke of broad pen , and xxx · sin θ5 is the additional weight
corresponding to separate strokes xxx apart. The right-hand side of the equation is
the same calculation in the case of vertical strokes (θ = 90—¦ ), when the stroke weight
of "I" is considered. (Since a similar calculation needs to be done for the letters K,
V, W, X, Y, and Z, it would be a good idea to embed these details in another macro.)

18.9. beginchar ("H", 13u#, ht #, 0);
x1 = x2 = x5 = 3u;
x3 = x4 = x6 = w ’ x1 ;
y1 = y3 = h; y2 = y4 = 0;
top serif (1, xx , ’90, jut , jut );
bot serif (2, xx , 90, jut , jut ); (Figure A18c will be inserted here; too bad

top serif (3, xx , ’90, jut , jut );
you can™t see it now.)


bot serif (4, xx , 90, jut , jut );
¬lldraw bot serif edge 2
- - reverse top serif edge 1 - - cycle;
¬ll bot serif edge 4
- - reverse top serif edge 3 - - cycle;
y5 = y6 = .52h; draw z5 - - z6 ;
penlabels(1, 2, 3, 4, 5, 6); endchar.

18.10. The replacement text contains ten tokens,

t e p
def = enddef ; def t =

where t , e , and p are placeholders for argument insertion. When this macro is
expanded with tracingmacros > 0, will type

foo(TEXT0)<expr>of<primary>->def(TEXT0)=(EXPR1)enddef;def.t=(EXPR2)

followed by the arguments (TEXT0), (EXPR1), and (EXPR2).

<<

. 8
( 13)



>>