summaryrefslogtreecommitdiff
path: root/systems/knuth/dist/mf
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /systems/knuth/dist/mf
Initial commit
Diffstat (limited to 'systems/knuth/dist/mf')
-rw-r--r--systems/knuth/dist/mf/mf.web23135
-rw-r--r--systems/knuth/dist/mf/mfbook.tex20160
-rw-r--r--systems/knuth/dist/mf/trap.fot59
-rw-r--r--systems/knuth/dist/mf/trap.log4266
-rw-r--r--systems/knuth/dist/mf/trap.mf170
-rw-r--r--systems/knuth/dist/mf/trap.pl490
-rw-r--r--systems/knuth/dist/mf/trap.typ210
-rw-r--r--systems/knuth/dist/mf/trapin.log179
-rw-r--r--systems/knuth/dist/mf/trapman.tex400
9 files changed, 49069 insertions, 0 deletions
diff --git a/systems/knuth/dist/mf/mf.web b/systems/knuth/dist/mf/mf.web
new file mode 100644
index 0000000000..ca3cb2518d
--- /dev/null
+++ b/systems/knuth/dist/mf/mf.web
@@ -0,0 +1,23135 @@
+% This program is copyright (C) 1984 by D. E. Knuth; all rights are reserved.
+% Copying of this file is authorized only if (1) you are D. E. Knuth, or if
+% (2) you make absolutely no changes to your copy. (The WEB system provides
+% for alterations via an auxiliary file; the master file should stay intact.)
+% In other words, METAFONT is under essentially the same ground rules as TeX.
+
+% TeX is a trademark of the American Mathematical Society.
+% METAFONT is a trademark of Addison-Wesley Publishing Company.
+
+% Version 0 was completed on July 28, 1984.
+% Version 1 was completed on January 4, 1986; it corresponds to "Volume D".
+% Version 1.1 trivially corrected the punctuation in one message (June 1986).
+% Version 1.2 corrected an arithmetic overflow problem (July 1986).
+% Version 1.3 improved rounding when elliptical pens are made (November 1986).
+% Version 1.4 corrected scan_declared_variable timing (May 1988).
+% Version 1.5 fixed negative halving in allocator when mem_min<0 (June 1988).
+% Version 1.6 kept open_log_file from calling fatal_error (November 1988).
+% Version 1.7 solved that problem a better way (December 1988).
+% Version 1.8 introduced major changes for 8-bit extensions (September 1989).
+% Version 1.9 improved skimping and was edited for style (December 1989).
+% Version 2.0 fixed bug in addto; released with TeX version 3.0 (March 1990).
+% Version 2.7 made consistent with TeX version 3.1 (September 1990).
+% Version 2.71 fixed bug in draw, allowed unprintable filenames (March 1992).
+% Version 2.718 fixed bug in <Choose a dependent...> (March 1995).
+% Version 2.7182 fixed bugs related to "<unprintable char>" (August 1996).
+% Version 2.71828 suppressed autorounding in dangerous cases (June 2003).
+% Version 2.718281 was a general cleanup with minor fixes (February 2008).
+% Version 2.7182818 was similar (January 2014).
+
+% A reward of $327.68 will be paid to the first finder of any remaining bug.
+
+% Although considerable effort has been expended to make the METAFONT program
+% correct and reliable, no warranty is implied; the author disclaims any
+% obligation or liability for damages, including but not limited to
+% special, indirect, or consequential damages arising out of or in
+% connection with the use or performance of this software. This work has
+% been a ``labor of love'' and the author hopes that users enjoy it.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\def\ph{\hbox{Pascal-H}}
+\def\psqrt#1{\sqrt{\mathstrut#1}}
+\def\k{_{k+1}}
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+\font\tenlogo=logo10 % font used for the METAFONT logo
+\font\logos=logosl10
+\font\eightlogo=logo8
+\def\MF{{\tenlogo META}\-{\tenlogo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+\let\swap=\leftrightarrow
+\def\round{\mathop{\rm round}\nolimits}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\outer\def\N#1. \[#2]#3.{\MN#1.\vfil\eject % begin starred section
+ \def\rhead{PART #2:\uppercase{#3}} % define running headline
+ \message{*\modno} % progress report
+ \edef\next{\write\cont{\Z{\?#2]#3}{\modno}{\the\pageno}}}\next
+ \ifon\startsection{\bf\ignorespaces#3.\quad}\ignorespaces}
+\let\?=\relax % we want to be able to \write a \?
+
+\def\title{{\eightlogo METAFONT}}
+\def\topofcontents{\hsize 5.5in
+ \vglue -30pt plus 1fil minus 1.5in
+ \def\?##1]{\hbox to 1in{\hfil##1.\ }}
+ }
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\pageno=3
+\def\glob{13} % this should be the section number of "<Global...>"
+\def\gglob{20, 26} % this should be the next two sections of "<Global...>"
+
+@* \[1] Introduction.
+This is \MF, a font compiler intended to produce typefaces of high quality.
+The \PASCAL\ program that follows is the definition of \MF84, a standard
+@:PASCAL}{\PASCAL@>
+@!@:METAFONT84}{\MF84@>
+version of \MF\ that is designed to be highly portable so that identical output
+will be obtainable on a great variety of computers. The conventions
+of \MF84 are the same as those of \TeX82.
+
+The main purpose of the following program is to explain the algorithms of \MF\
+as clearly as possible. As a result, the program will not necessarily be very
+efficient when a particular \PASCAL\ compiler has translated it into a
+particular machine language. However, the program has been written so that it
+can be tuned to run efficiently in a wide variety of operating environments
+by making comparatively few changes. Such flexibility is possible because
+the documentation that follows is written in the \.{WEB} language, which is
+at a higher level than \PASCAL; the preprocessing step that converts \.{WEB}
+to \PASCAL\ is able to introduce most of the necessary refinements.
+Semi-automatic translation to other languages is also feasible, because the
+program below does not make extensive use of features that are peculiar to
+\PASCAL.
+
+A large piece of software like \MF\ has inherent complexity that cannot
+be reduced below a certain level of difficulty, although each individual
+part is fairly simple by itself. The \.{WEB} language is intended to make
+the algorithms as readable as possible, by reflecting the way the
+individual program pieces fit together and by providing the
+cross-references that connect different parts. Detailed comments about
+what is going on, and about why things were done in certain ways, have
+been liberally sprinkled throughout the program. These comments explain
+features of the implementation, but they rarely attempt to explain the
+\MF\ language itself, since the reader is supposed to be familiar with
+{\sl The {\logos METAFONT\/}book}.
+@.WEB@>
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+
+@ The present implementation has a long ancestry, beginning in the spring
+of~1977, when its author wrote a prototype set of subroutines and macros
+@^Knuth, Donald Ervin@>
+that were used to develop the first Computer Modern fonts.
+This original proto-\MF\ required the user to recompile a {\mc SAIL} program
+whenever any character was changed, because it was not a ``language'' for
+font design; the language was {\mc SAIL}. After several hundred characters
+had been designed in that way, the author developed an interpretable language
+called \MF, in which it was possible to express the Computer Modern programs
+less cryptically. A complete \MF\ processor was designed and coded by the
+author in 1979. This program, written in {\mc SAIL}, was adapted for use
+with a variety of typesetting equipment and display terminals by Leo Guibas,
+Lyle Ramshaw, and David Fuchs.
+@^Guibas, Leonidas Ioannis@>
+@^Ramshaw, Lyle Harold@>
+@^Fuchs, David Raymond@>
+Major improvements to the design of Computer Modern fonts were made in the
+spring of 1982, after which it became clear that a new language would
+better express the needs of letterform designers. Therefore an entirely
+new \MF\ language and system were developed in 1984; the present system
+retains the name and some of the spirit of \MF79, but all of the details
+have changed.
+
+No doubt there still is plenty of room for improvement, but the author
+is firmly committed to keeping \MF84 ``frozen'' from now on; stability
+and reliability are to be its main virtues.
+
+On the other hand, the \.{WEB} description can be extended without changing
+the core of \MF84 itself, and the program has been designed so that such
+extensions are not extremely difficult to make.
+The |banner| string defined here should be changed whenever \MF\
+undergoes any modifications, so that it will be clear which version of
+\MF\ might be the guilty party when a problem arises.
+@^extensions to \MF@>
+@^system dependencies@>
+
+If this program is changed, the resulting system should not be called
+`\MF\kern.5pt'; the official name `\MF\kern.5pt' by itself is reserved
+for software systems that are fully compatible with each other.
+A special test suite called the ``\.{TRAP} test'' is available for
+helping to determine whether an implementation deserves to be
+known as `\MF\kern.5pt' [cf.~Stanford Computer Science report CS1095,
+January 1986].
+
+@d banner=='This is METAFONT, Version 2.7182818' {printed when \MF\ starts}
+
+@ Different \PASCAL s have slightly different conventions, and the present
+@!@:PASCAL H}{\ph@>
+program expresses \MF\ in terms of the \PASCAL\ that was
+available to the author in 1984. Constructions that apply to
+this particular compiler, which we shall call \ph, should help the
+reader see how to make an appropriate interface for other systems
+if necessary. (\ph\ is Charles Hedrick's modification of a compiler
+@^Hedrick, Charles Locke@>
+for the DECsystem-10 that was originally developed at the University of
+Hamburg; cf.\ {\sl SOFTWARE---Practice \AM\ Experience \bf6} (1976),
+29--42. The \MF\ program below is intended to be adaptable, without
+extensive changes, to most other versions of \PASCAL, so it does not fully
+use the admirable features of \ph. Indeed, a conscious effort has been
+made here to avoid using several idiosyncratic features of standard
+\PASCAL\ itself, so that most of the code can be translated mechanically
+into other high-level languages. For example, the `\&{with}' and `\\{new}'
+features are not used, nor are pointer types, set types, or enumerated
+scalar types; there are no `\&{var}' parameters, except in the case of files
+or in the system-dependent |paint_row| procedure;
+there are no tag fields on variant records; there are no |real| variables;
+no procedures are declared local to other procedures.)
+
+The portions of this program that involve system-dependent code, where
+changes might be necessary because of differences between \PASCAL\ compilers
+and/or differences between
+operating systems, can be identified by looking at the sections whose
+numbers are listed under `system dependencies' in the index. Furthermore,
+the index entries for `dirty \PASCAL' list all places where the restrictions
+of \PASCAL\ have not been followed perfectly, for one reason or another.
+@!@^system dependencies@>
+@!@^dirty \PASCAL@>
+
+@ The program begins with a normal \PASCAL\ program heading, whose
+components will be filled in later, using the conventions of \.{WEB}.
+@.WEB@>
+For example, the portion of the program called `\X\glob:Global
+variables\X' below will be replaced by a sequence of variable declarations
+that starts in $\section\glob$ of this documentation. In this way, we are able
+to define each individual global variable when we are prepared to
+understand what it means; we do not have to define all of the globals at
+once. Cross references in $\section\glob$, where it says ``See also
+sections \gglob, \dots,'' also make it possible to look at the set of
+all global variables, if desired. Similar remarks apply to the other
+portions of the program heading.
+
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \MF\ user
+to specify a file name if |output| were specified here.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@f mtype==type {`\&{mtype}' will be equivalent to `\&{type}'}
+@f type==true {but `|type|' will not be treated as a reserved word}
+
+@p @t\4@>@<Compiler directives@>@/
+program MF; {all file names are defined dynamically}
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+mtype @<Types in the outer block@>@/
+var @<Global variables@>@/
+@#
+procedure initialize; {this procedure gets things started properly}
+ var @<Local variables for initialization@>@/
+ begin @<Set initial values of key variables@>@/
+ end;@#
+@t\4@>@<Basic printing procedures@>@/
+@t\4@>@<Error handling procedures@>@/
+
+@ The overall \MF\ program begins with the heading just shown, after which
+comes a bunch of procedure declarations and function declarations.
+Finally we will get to the main program, which begins with the
+comment `|start_here|'. If you want to skip down to the
+main program now, you can look up `|start_here|' in the index.
+But the author suggests that the best way to understand this program
+is to follow pretty much the order of \MF's components as they appear in the
+\.{WEB} description you are now reading, since the present ordering is
+intended to combine the advantages of the ``bottom up'' and ``top down''
+approaches to the problem of understanding a somewhat complicated system.
+
+@ Three labels must be declared in the main program, so we give them
+symbolic names.
+
+@d start_of_MF=1 {go here when \MF's variables are initialized}
+@d end_of_MF=9998 {go here to close files and terminate gracefully}
+@d final_end=9999 {this label marks the ending of the program}
+
+@<Labels in the out...@>=
+start_of_MF@t\hskip-2pt@>, end_of_MF@t\hskip-2pt@>,@,final_end;
+ {key control points}
+
+@ Some of the code below is intended to be used only when diagnosing the
+strange behavior that sometimes occurs when \MF\ is being installed or
+when system wizards are fooling around with \MF\ without quite knowing
+what they are doing. Such code will not normally be compiled; it is
+delimited by the codewords `$|debug|\ldots|gubed|$', with apologies
+to people who wish to preserve the purity of English.
+
+Similarly, there is some conditional code delimited by
+`$|stat|\ldots|tats|$' that is intended for use when statistics are to be
+kept about \MF's memory usage. The |stat| $\ldots$ |tats| code also
+implements special diagnostic information that is printed when
+$\\{tracingedges}>1$.
+@^debugging@>
+
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@f debug==begin
+@f gubed==end
+@#
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@f stat==begin
+@f tats==end
+
+@ This program has two important variations: (1) There is a long and slow
+version called \.{INIMF}, which does the extra calculations needed to
+@.INIMF@>
+initialize \MF's internal tables; and (2)~there is a shorter and faster
+production version, which cuts the initialization to a bare minimum.
+Parts of the program that are needed in (1) but not in (2) are delimited by
+the codewords `$|init|\ldots|tini|$'.
+
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@f init==begin
+@f tini==end
+
+@ If the first character of a \PASCAL\ comment is a dollar sign,
+\ph\ treats the comment as a list of ``compiler directives'' that will
+affect the translation of this program into machine language. The
+directives shown below specify full checking and inclusion of the \PASCAL\
+debugger when \MF\ is being debugged, but they cause range checking and other
+redundant code to be eliminated when the production system is being generated.
+Arithmetic overflow will be detected in all cases.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+@^overflow in arithmetic@>
+
+@<Compiler directives@>=
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+
+@ This \MF\ implementation conforms to the rules of the {\sl Pascal User
+@:PASCAL}{\PASCAL@>
+@^system dependencies@>
+Manual} published by Jensen and Wirth in 1975, except where system-dependent
+@^Wirth, Niklaus@>
+@^Jensen, Kathleen@>
+code is necessary to make a useful system program, and except in another
+respect where such conformity would unnecessarily obscure the meaning
+and clutter up the code: We assume that |case| statements may include a
+default case that applies if no matching label is found. Thus, we shall use
+constructions like
+$$\vbox{\halign{\ignorespaces#\hfil\cr
+|case x of|\cr
+1: $\langle\,$code for $x=1\,\rangle$;\cr
+3: $\langle\,$code for $x=3\,\rangle$;\cr
+|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
+|endcases|\cr}}$$
+since most \PASCAL\ compilers have plugged this hole in the language by
+incorporating some sort of default mechanism. For example, the \ph\
+compiler allows `|others|:' as a default label, and other \PASCAL s allow
+syntaxes like `\&{else}' or `\&{otherwise}' or `\\{otherwise}:', etc. The
+definitions of |othercases| and |endcases| should be changed to agree with
+local conventions. Note that no semicolon appears before |endcases| in
+this program, so the definition of |endcases| should include a semicolon
+if the compiler wants one. (Of course, if no default mechanism is
+available, the |case| statements of \MF\ will have to be laboriously
+extended by listing all remaining cases. People who are stuck with such
+\PASCAL s have, in fact, done this, successfully but not happily!)
+@:PASCAL H}{\ph@>
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The following parameters can be changed at compile time to extend or
+reduce \MF's capacity. They may have different values in \.{INIMF} and
+in production versions of \MF.
+@.INIMF@>
+@^system dependencies@>
+
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MF's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|}
+@!max_internal=100; {maximum number of internal quantities}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!screen_width=768; {number of pixels in each row of screen display}
+@!screen_depth=1024; {number of pixels in each column of screen display}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_strings=2000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MF's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all identifiers;
+ must exceed |string_vacancies| by the total
+ length of \MF's own strings, which is currently about 22000}
+@!move_size=5000; {space for storing moves in a single octant}
+@!max_wiggle=300; {number of autorounded points per cycle}
+@!gf_buf_size=800; {size of the output buffer, must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='MFbases:MF.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@.MFbases@>
+@!path_size=300; {maximum number of knots between breakpoints of a path}
+@!bistack_size=785; {size of stack for bisection algorithms;
+ should probably be left at this value}
+@!header_size=100; {maximum number of \.{TFM} header words, times~4}
+@!lig_table_size=5000; {maximum number of ligature/kern steps, must be
+ at least 255 and at most 32510}
+@!max_kerns=500; {maximum number of distinct kern amounts}
+@!max_font_dimen=50; {maximum number of \&{fontdimen} parameters}
+
+@ Like the preceding parameters, the following quantities can be changed
+at compile time to extend or reduce \MF's capacity. But if they are changed,
+it is necessary to rerun the initialization program \.{INIMF}
+@.INIMF@>
+to generate new tables for the production \MF\ program.
+One can't simply make helter-skelter changes to the following constants,
+since certain rather complex initialization
+numbers are computed from them. They are defined here using
+\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to
+emphasize this distinction.
+
+@d mem_min=0 {smallest index in the |mem| array, must not be less
+ than |min_halfword|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@d hash_size=2100 {maximum number of symbolic tokens,
+ must be less than |max_halfword-3*param_size|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d max_in_open=6 {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@d param_size=150 {maximum number of simultaneous macro parameters}
+@^system dependencies@>
+
+@ In case somebody has inadvertently made bad settings of the ``constants,''
+\MF\ checks them using a global variable called |bad|.
+
+This is the first of many sections of \MF\ where global variables are
+defined.
+
+@<Glob...@>=
+@!bad:integer; {is some ``constant'' wrong?}
+
+@ Later on we will say `\ignorespaces|if mem_max>=max_halfword then bad:=10|',
+or something similar. (We can't do that until |max_halfword| has been defined.)
+
+@<Check the ``constant'' values for consistency@>=
+bad:=0;
+if (half_error_line<30)or(half_error_line>error_line-15) then bad:=1;
+if max_print_line<60 then bad:=2;
+if gf_buf_size mod 8<>0 then bad:=3;
+if mem_min+1100>mem_top then bad:=4;
+if hash_prime>hash_size then bad:=5;
+if header_size mod 4 <> 0 then bad:=6;
+if(lig_table_size<255)or(lig_table_size>32510)then bad:=7;
+
+@ Labels are given symbolic names by the following definitions, so that
+occasional |goto| statements will be meaningful. We insert the label
+`|exit|' just before the `\ignorespaces|end|\unskip' of a procedure in
+which we have used the `|return|' statement defined below; the label
+`|restart|' is occasionally used at the very beginning of a procedure; and
+the label `|reswitch|' is occasionally used just prior to a |case|
+statement in which some cases change the conditions and we wish to branch
+to the newly applicable case. Loops that are set up with the |loop|
+construction defined below are commonly exited by going to `|done|' or to
+`|found|' or to `|not_found|', and they are sometimes repeated by going to
+`|continue|'. If two or more parts of a subroutine start differently but
+end up the same, the shared code may be gathered together at
+`|common_ending|'.
+
+Incidentally, this program never declares a label that isn't actually used,
+because some fussy \PASCAL\ compilers will complain about redundant labels.
+
+@d exit=10 {go here to leave a procedure}
+@d restart=20 {go here to start a procedure again}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d done1=31 {like |done|, when there is more than one loop}
+@d done2=32 {for exiting the second loop in a long block}
+@d done3=33 {for exiting the third loop in a very long block}
+@d done4=34 {for exiting the fourth loop in an extremely long block}
+@d done5=35 {for exiting the fifth loop in an immense block}
+@d done6=36 {for exiting the sixth loop in a block}
+@d found=40 {go here when you've found it}
+@d found1=41 {like |found|, when there's more than one per routine}
+@d found2=42 {like |found|, when there's more than two per routine}
+@d not_found=45 {go here when you've found nothing}
+@d common_ending=50 {go here when you want to merge with another branch}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d negate(#) == #:=-# {change the sign of a variable}
+@d double(#) == #:=#+# {multiply a variable by two}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@f loop == xclause
+ {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil {\.{WEB} will henceforth say |return| instead of \\{return}}
+
+@* \[2] The character set.
+In order to make \MF\ readily portable to a wide variety of
+computers, all of its input text is converted to an internal eight-bit
+code that includes standard ASCII, the ``American Standard Code for
+Information Interchange.'' This conversion is done immediately when each
+character is read in. Conversely, characters are converted from ASCII to
+the user's external representation just before they are output to a
+text file.
+@^ASCII code@>
+
+Such an internal code is relevant to users of \MF\ only with respect to
+the \&{char} and \&{ASCII} operations, and the comparison of strings.
+
+@ Characters of text that have been converted to \MF's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lowercase
+letters. Nowadays, of course, we need to deal with both capital and small
+letters in a convenient way, especially in a program for font design;
+so the present specification of \MF\ has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes @'40 through @'176; all of these characters are now
+available on most computer terminals.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+
+@ The \MF\ processor converts between ASCII code and
+the user's external character set by means of arrays |xord| and |xchr|
+that are analogous to \PASCAL's |ord| and |chr| functions.
+
+@<Glob...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ Since we are assuming that our \PASCAL\ system is able to read and
+write the visible characters of standard ASCII (although not
+necessarily using the ASCII codes to represent them), the following
+assignment statements initialize the standard part of the |xchr| array
+properly, without needing any system-dependent changes. On the other
+hand, it is possible to implement \MF\ with less complete character
+sets, and in such cases it will be necessary to change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';@/
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. If \MF\ is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0..@'37]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \MF\ more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{<>}'.
+People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \MF\ are allowed to have in their input files.
+Appropriate changes to \MF's |char_class| table should then be made.
+(Unlike \TeX, each installation of \MF\ has a fixed assignment of category
+codes, called the |char_class|.) Such changes make portability of programs
+more difficult, so they should be introduced cautiously if at all.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
+where |i<j<@'177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below @'40 in case there is a coincidence.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'177;
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+
+@* \[3] Input and output.
+The bane of portability is the fact that different operating systems treat
+input and output quite differently, perhaps because computer scientists
+have not given sufficient attention to this problem. People have felt somehow
+that input and output are not part of ``real'' programming. Well, it is true
+that some kinds of programming are more fun than others. With existing
+input/output conventions being so diverse and so messy, the only sources of
+joy in such parts of the code are the rare occasions when one can find a
+way to make the program a little less bad than it might have been. We have
+two choices, either to attack I/O now and get it over with, or to postpone
+I/O until near the end. Neither prospect is very attractive, so let's
+get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of
+text, to or from a file or the user's terminal; (2)~inputting and
+outputting of eight-bit bytes, to or from a file; (3)~instructing the
+operating system to initiate (``open'') or to terminate (``close'') input or
+output from a specified file; (4)~testing whether the end of an input
+file has been reached; (5)~display of bits on the user's screen.
+The bit-display operation will be discussed in a later section; we shall
+deal here only with more traditional kinds of I/O.
+
+\MF\ needs to deal with two kinds of files.
+We shall use the term |alpha_file| for a file that contains textual data,
+and the term |byte_file| for a file that contains eight-bit binary information.
+These two types turn out to be the same on many computers, but
+sometimes there is a significant distinction, so we shall be careful to
+distinguish between them. Standard protocols for transferring
+such files from computer to computer, via high-speed networks, are
+now becoming available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization. We shall define a word file later; but it will be possible
+for us to specify simple operations on word files before they are defined.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ Most of what we need to do with respect to input and output can be handled
+by the I/O facilities that are standard in \PASCAL, i.e., the routines
+called |get|, |put|, |eof|, and so on. But
+standard \PASCAL\ does not allow file variables to be associated with file
+names that are determined at run time, so it cannot be used to implement
+\MF; some sort of extension to \PASCAL's ordinary |reset| and |rewrite|
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \MF\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+
+@ The \ph\ compiler with which the present version of \MF\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `\ignorespaces|packed
+array[@t\<\\{any}>@>] of text_char|', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\MF\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\MF's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var @!f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var @!f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_out(var @!f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var @!f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var @!f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+
+@ Files can be closed with the \ph\ routine `|close(f)|', which
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+should be used when all input or output with respect to |f| has been completed.
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+@p procedure a_close(var @!f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var @!f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var @!f:word_file); {close a word file}
+begin close(f);
+end;
+
+@ Binary input and output are done with \PASCAL's ordinary |get| and |put|
+procedures, so we don't have to make any other special arrangements for
+binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
+The treatment of text input is more difficult, however, because
+of the necessary translation to |ASCII_code| values.
+\MF's conventions should be efficient, and they should
+blend nicely with the user's operating environment.
+
+@ Input from text files is read one line at a time, using a routine called
+|input_ln|. This function is defined in terms of global variables called
+|buffer|, |first|, and |last| that will be described in detail later; for
+now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+values, and that |first| and |last| are indices into this array
+representing the beginning and ending of a line of text.
+
+@<Glob...@>=
+@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
+@!first:0..buf_size; {the first unused position in |buffer|}
+@!last:0..buf_size; {end of the line just input to |buffer|}
+@!max_buf_stack:0..buf_size; {largest index used in |buffer|}
+
+@ The |input_ln| function brings the next line of input from the specified
+field into available positions of the buffer array and returns the value
+|true|, unless the file has already been entirely read, in which case it
+returns |false| and sets |last:=first|. In general, the |ASCII_code|
+numbers that represent the next line of the file are input into
+|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
+global variable |last| is set equal to |first| plus the length of the
+line. Trailing blanks are removed from the line; thus, either |last=first|
+(in which case the line was entirely blank) or |buffer[last-1]<>" "|.
+@^inner loop@>
+
+An overflow error is given, however, if the normal actions of |input_ln|
+would make |last>=buf_size|; this is done so that other parts of \MF\
+can safely look at the contents of |buffer[last+1]| without overstepping
+the bounds of the |buffer| array. Upon entry to |input_ln|, the condition
+|first<buf_size| will always hold, so that there is always room for an
+``empty'' line.
+
+The variable |max_buf_stack|, which is used to keep track of how large
+the |buf_size| parameter must be to accommodate the present job, is
+also kept up to date by |input_ln|.
+
+If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
+before looking at the first character of the line; this skips over
+an |eoln| that was in |f^|. The procedure does not do a |get| when it
+reaches the end of the line; therefore it can be used to acquire input
+from the user's terminal as well as from ordinary text files.
+
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \MF\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+@p function input_ln(var @!f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+var @!last_nonblank:0..buf_size; {|last| with trailing blanks removed}
+begin if bypass_eoln then if not eof(f) then get(f);
+ {input the first character of the line into |f^|}
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if eof(f) then input_ln:=false
+else begin last_nonblank:=first;
+ while not eoln(f) do
+ begin if last>=max_buf_stack then
+ begin max_buf_stack:=last+1;
+ if max_buf_stack=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ buffer[last]:=xord[f^]; get(f); incr(last);
+ if buffer[last-1]<>" " then last_nonblank:=last;
+ end;
+ last:=last_nonblank; input_ln:=true;
+ end;
+end;
+
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O')
+ {open the terminal for text output}
+
+@ Sometimes it is necessary to synchronize the input/output mixture that
+happens on the user's terminal, and three system-dependent
+procedures are used for this
+purpose. The first of these, |update_terminal|, is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+The second, |clear_terminal|, is called when we wish to cancel any
+input that the user may have typed ahead (since we are about to
+issue an unexpected error message). The third, |wake_up_terminal|,
+is supposed to revive the terminal if the user has disabled it by
+some instruction to the operating system. The following macros show how
+these operations can be specified in \ph:
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+
+@ We need a special routine to read the first line of \MF\ input from
+the user's terminal. This line is different because it is read before we
+have opened the transcript file; there is sort of a ``chicken and
+egg'' problem here. If the user types `\.{input cmr10}' on the first
+line, or if some macro invoked by that line does such an \.{input},
+the transcript file will be named `\.{cmr10.log}'; but if no \.{input}
+commands are performed during the first line of terminal input, the transcript
+file will acquire its default name `\.{mfput.log}'. (The transcript file
+will not contain error messages generated by the first line before the
+first \.{input} command.)
+@.mfput@>
+
+The first line is even more special if we are lucky enough to have an operating
+system that treats \MF\ differently from a run-of-the-mill \PASCAL\ object
+program. It's nice to let the user start running a \MF\ job by typing
+a command line like `\.{MF cmr10}'; in such a case, \MF\ will operate
+as if the first line of input were `\.{cmr10}', i.e., the first line will
+consist of the remainder of the command line, after the part that invoked \MF.
+
+The first line is special also because it may be read before \MF\ has
+input a base file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the
+@^system dependencies@>
+statement `|goto final_end|' should be replaced by something that
+quietly terminates the program.)
+
+@<Report overflow of the input buffer, and abort@>=
+if base_ident=0 then
+ begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
+@.Buffer size exceeded@>
+ end
+else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
+ overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+ end
+
+@ Different systems have different ways to get started. But regardless of
+what conventions are adopted, the routine that initializes the terminal
+should satisfy the following specifications:
+
+\yskip\textindent{1)}It should open file |term_in| for input from the
+ terminal. (The file |term_out| will already be open for output to the
+ terminal.)
+
+\textindent{2)}If the user has given a command line, this line should be
+ considered the first line of terminal input. Otherwise the
+ user should be prompted with `\.{**}', and the first line of input
+ should be whatever is typed in response.
+
+\textindent{3)}The first line of input, which might or might not be a
+ command line, should appear in locations |first| to |last-1| of the
+ |buffer| array.
+
+\textindent{4)}The global variable |loc| should be set so that the
+ character to be read next by \MF\ is in |buffer[loc]|. This
+ character should not be blank, and we should have |loc<last|.
+
+\yskip\noindent(It may be necessary to prompt the user several times
+before a non-blank line comes in. The prompt is `\.{**}' instead of the
+later `\.*' because the meaning is slightly different: `\.{input}' need
+not be typed immediately after~`\.{**}'.)
+
+@d loc==cur_input.loc_field {location of first unread character in |buffer|}
+
+@ The following program does the required initialization
+without retrieving a possible command line.
+It should be clear how to modify this routine to deal with command lines,
+if the system permits them.
+@^system dependencies@>
+
+@p function init_terminal:boolean; {gets the terminal input started}
+label exit;
+begin t_open_in;
+loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then {this shouldn't happen}
+ begin write_ln(term_out);
+ write(term_out,'! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false; return;
+ end;
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do incr(loc);
+ if loc<last then
+ begin init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out,'Please type the name of your input file.');
+ end;
+exit:end;
+
+@* \[4] String handling.
+Symbolic token names and diagnostic messages are variable-length strings
+of eight-bit characters. Since \PASCAL\ does not have a well-developed string
+mechanism, \MF\ does all of its string processing by homegrown methods.
+
+Elaborate facilities for dynamic strings are not needed, so all of the
+necessary operations can be handled with a simple data structure.
+The array |str_pool| contains all of the (eight-bit) ASCII codes in all
+of the strings, and the array |str_start| contains indices of the starting
+points of each string. Strings are referred to by integer numbers, so that
+string number |s| comprises the characters |str_pool[j]| for
+|str_start[s]<=j<str_start[s+1]|. Additional integer variables
+|pool_ptr| and |str_ptr| indicate the number of entries used so far
+in |str_pool| and |str_start|, respectively; locations
+|str_pool[pool_ptr]| and |str_start[str_ptr]| are
+ready for the next string to be allocated.
+
+String numbers 0 to 255 are reserved for strings that correspond to single
+ASCII characters. This is in accordance with the conventions of \.{WEB},
+@.WEB@>
+which converts single-character strings into the ASCII code number of the
+single character involved, while it converts other strings into integers
+and builds a string pool file. Thus, when the string constant \.{"."} appears
+in the program below, \.{WEB} converts it into the integer 46, which is the
+ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
+into some integer greater than~255. String number 46 will presumably be the
+single character `\..'\thinspace; but some ASCII codes have no standard visible
+representation, and \MF\ may need to be able to print an arbitrary
+ASCII character, so the first 256 strings are used to specify exactly what
+should be printed for each of the 256 possibilities.
+
+Elements of the |str_pool| array must be ASCII codes that can actually be
+printed; i.e., they must have an |xchr| equivalent in the local
+character set. (This restriction applies only to preloaded strings,
+not to those generated dynamically by the user.)
+
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128..127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+@^system dependencies@>
+
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
+@!str_start : array[str_number] of pool_pointer; {the starting pointers}
+@!pool_ptr : pool_pointer; {first unused position in |str_pool|}
+@!str_ptr : str_number; {number of the current string being created}
+@!init_pool_ptr : pool_pointer; {the starting value of |pool_ptr|}
+@!init_str_ptr : str_number; {the starting value of |str_ptr|}
+@!max_pool_ptr : pool_pointer; {the maximum so far of |pool_ptr|}
+@!max_str_ptr : str_number; {the maximum so far of |str_ptr|}
+
+@ Several of the elementary string operations are performed using \.{WEB}
+macros instead of \PASCAL\ procedures, because many of the
+operations are done quite frequently and we want to avoid the
+overhead of procedure calls. For example, here is
+a simple macro that computes the length of a string.
+@.WEB@>
+
+@d length(#)==(str_start[#+1]-str_start[#]) {the number of characters
+ in string number \#}
+
+@ The length of the current string is called |cur_length|:
+
+@d cur_length == (pool_ptr - str_start[str_ptr])
+
+@ Strings are created by appending character codes to |str_pool|.
+The |append_char| macro, defined here, does not check to see if the
+value of |pool_ptr| has gotten too high; this test is supposed to be
+made before |append_char| is used.
+
+To test if there is room to append |l| more characters to |str_pool|,
+we shall write |str_room(l)|, which aborts \MF\ and gives an
+apologetic error message if there isn't enough room.
+
+@d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|}
+begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
+end
+@d str_room(#) == {make sure that the pool hasn't overflowed}
+ begin if pool_ptr+# > max_pool_ptr then
+ begin if pool_ptr+# > pool_size then
+ overflow("pool size",pool_size-init_pool_ptr);
+@:METAFONT capacity exceeded pool size}{\quad pool size@>
+ max_pool_ptr:=pool_ptr+#;
+ end;
+ end
+
+@ \MF's string expressions are implemented in a brute-force way: Every
+new string or substring that is needed is simply copied into the string pool.
+
+Such a scheme can be justified because string expressions aren't a big
+deal in \MF\ applications; strings rarely need to be saved from one
+statement to the next. But it would waste space needlessly if we didn't
+try to reclaim the space of strings that are going to be used only once.
+
+Therefore a simple reference count mechanism is provided: If there are
+@^reference counts@>
+no references to a certain string from elsewhere in the program, and
+if there are no references to any strings created subsequent to it,
+then the string space will be reclaimed.
+
+The number of references to string number |s| will be |str_ref[s]|. The
+special value |str_ref[s]=max_str_ref=127| is used to denote an unknown
+positive number of references; such strings will never be recycled. If
+a string is ever referred to more than 126 times, simultaneously, we
+put it in this category. Hence a single byte suffices to store each |str_ref|.
+
+@d max_str_ref=127 {``infinite'' number of references}
+@d add_str_ref(#)==begin if str_ref[#]<max_str_ref then incr(str_ref[#]);
+ end
+
+@<Glob...@>=
+@!str_ref:array[str_number] of 0..max_str_ref;
+
+@ Here's what we do when a string reference disappears:
+
+@d delete_str_ref(#)== begin if str_ref[#]<max_str_ref then
+ if str_ref[#]>1 then decr(str_ref[#])@+else flush_string(#);
+ end
+
+@<Declare the procedure called |flush_string|@>=
+procedure flush_string(@!s:str_number);
+begin if s<str_ptr-1 then str_ref[s]:=0
+else repeat decr(str_ptr);
+ until str_ref[str_ptr-1]<>0;
+pool_ptr:=str_start[str_ptr];
+end;
+
+@ Once a sequence of characters has been appended to |str_pool|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@p function make_string : str_number; {current string enters the pool}
+begin if str_ptr=max_str_ptr then
+ begin if str_ptr=max_strings then
+ overflow("number of strings",max_strings-init_str_ptr);
+@:METAFONT capacity exceeded number of strings}{\quad number of strings@>
+ incr(max_str_ptr);
+ end;
+str_ref[str_ptr]:=1; incr(str_ptr); str_start[str_ptr]:=pool_ptr;
+make_string:=str_ptr-1;
+end;
+
+@ The following subroutine compares string |s| with another string of the
+same length that appears in |buffer| starting at position |k|;
+the result is |true| if and only if the strings are equal.
+
+@p function str_eq_buf(@!s:str_number;@!k:integer):boolean;
+ {test equality of strings}
+label not_found; {loop exit}
+var @!j: pool_pointer; {running index}
+@!result: boolean; {result of comparison}
+begin j:=str_start[s];
+while j<str_start[s+1] do
+ begin if so(str_pool[j])<>buffer[k] then
+ begin result:=false; goto not_found;
+ end;
+ incr(j); incr(k);
+ end;
+result:=true;
+not_found: str_eq_buf:=result;
+end;
+
+@ Here is a similar routine, but it compares two strings in the string pool,
+and it does not assume that they have the same length. If the first string
+is lexicographically greater than, less than, or equal to the second,
+the result is respectively positive, negative, or zero.
+
+@p function str_vs_str(@!s,@!t:str_number):integer;
+ {test equality of strings}
+label exit;
+var @!j,@!k: pool_pointer; {running indices}
+@!ls,@!lt:integer; {lengths}
+@!l:integer; {length remaining to test}
+begin ls:=length(s); lt:=length(t);
+if ls<=lt then l:=ls@+else l:=lt;
+j:=str_start[s]; k:=str_start[t];
+while l>0 do
+ begin if str_pool[j]<>str_pool[k] then
+ begin str_vs_str:=str_pool[j]-str_pool[k]; return;
+ end;
+ incr(j); incr(k); decr(l);
+ end;
+str_vs_str:=ls-lt;
+exit:end;
+
+@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
+and |str_ptr| are computed by the \.{INIMF} program, based in part
+on the information that \.{WEB} has output while processing \MF.
+@.INIMF@>
+@^string pool@>
+
+@p @!init function get_strings_started:boolean; {initializes the string pool,
+ but returns |false| if something goes wrong}
+label done,exit;
+var @!k,@!l:0..255; {small indices or counters}
+@!m,@!n:text_char; {characters input from |pool_file|}
+@!g:str_number; {garbage}
+@!a:integer; {accumulator for check sum}
+@!c:boolean; {check sum has been checked}
+begin pool_ptr:=0; str_ptr:=0; max_pool_ptr:=0; max_str_ptr:=0; str_start[0]:=0;
+@<Make the first 256 strings@>;
+@<Read the other strings from the \.{MF.POOL} file and return |true|,
+ or give an error message and return |false|@>;
+exit:end;
+tini
+
+@ @d app_lc_hex(#)==l:=#;
+ if l<10 then append_char(l+"0")@+else append_char(l-10+"a")
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else if k<@'200 then append_char(k-@'100)
+ else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
+ end;
+ end
+ else append_char(k);
+ g:=make_string; str_ref[g]:=max_str_ref;
+ end
+
+@ The first 128 strings will contain 95 standard ASCII characters, and the
+other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
+unless a system-dependent change is made here. Installations that have
+an extended character set, where for example |xchr[@'32]=@t\.{\'^^Z\'}@>|,
+would like string @'32 to be the single character @'32 instead of the
+three characters @'136, @'136, @'132 (\.{\^\^Z}). On the other hand,
+even people with an extended character set will want to represent string
+@'15 by \.{\^\^M}, since @'15 is ASCII's ``carriage return'' code; the idea is
+to produce visible strings instead of tabs or line-feeds or carriage-returns
+or bell-rings or characters that are treated anomalously in text files.
+
+Unprintable characters of codes 128--255 are, similarly, rendered
+\.{\^\^80}--\.{\^\^ff}.
+
+The boolean expression defined here should be |true| unless \MF\ internal
+code number~|k| corresponds to a non-troublesome visible symbol in the
+local character set.
+If character |k| cannot be printed, and |k<@'200|, then character |k+@'100| or
+|k-@'100| must be printable; moreover, ASCII codes
+|[@'60..@'71, @'136, @'141..@'146]|
+must be printable.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Character |k| cannot be printed@>=
+ (k<" ")or(k>"~")
+
+@ When the \.{WEB} system program called \.{TANGLE} processes the \.{MF.WEB}
+description that you are now reading, it outputs the \PASCAL\ program
+\.{MF.PAS} and also a string pool file called \.{MF.POOL}. The \.{INIMF}
+@.WEB@>@.INIMF@>
+program reads the latter file, where each string appears as a two-digit decimal
+length followed by the string itself, and the information is recorded in
+\MF's string memory.
+
+@<Glob...@>=
+@!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}}
+tini
+
+@ @d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#);
+ a_close(pool_file); get_strings_started:=false; return;
+ end
+@<Read the other strings...@>=
+name_of_file:=pool_name; {we needn't set |name_length|}
+if a_open_in(pool_file) then
+ begin c:=false;
+ repeat @<Read one string, but return |false| if the
+ string memory space is getting too tight for comfort@>;
+ until c;
+ a_close(pool_file); get_strings_started:=true;
+ end
+else bad_pool('! I can''t read MF.POOL.')
+@.I can't read MF.POOL@>
+
+@ @<Read one string...@>=
+begin if eof(pool_file) then bad_pool('! MF.POOL has no check sum.');
+@.MF.POOL has no check sum@>
+read(pool_file,m,n); {read two digits of string length}
+if m='*' then @<Check the pool check sum@>
+else begin if (xord[m]<"0")or(xord[m]>"9")or@|
+ (xord[n]<"0")or(xord[n]>"9") then
+ bad_pool('! MF.POOL line doesn''t begin with two digits.');
+@.MF.POOL line doesn't...@>
+ l:=xord[m]*10+xord[n]-"0"*11; {compute the length}
+ if pool_ptr+l+string_vacancies>pool_size then
+ bad_pool('! You have to increase POOLSIZE.');
+@.You have to increase POOLSIZE@>
+ for k:=1 to l do
+ begin if eoln(pool_file) then m:=' '@+else read(pool_file,m);
+ append_char(xord[m]);
+ end;
+ read_ln(pool_file); g:=make_string; str_ref[g]:=max_str_ref;
+ end;
+end
+
+@ The \.{WEB} operation \.{@@\$} denotes the value that should be at the
+end of this \.{MF.POOL} file; any other value means that the wrong pool
+file has been loaded.
+@^check sum@>
+
+@<Check the pool check sum@>=
+begin a:=0; k:=1;
+loop@+ begin if (xord[n]<"0")or(xord[n]>"9") then
+ bad_pool('! MF.POOL check sum doesn''t have nine digits.');
+@.MF.POOL check sum...@>
+ a:=10*a+xord[n]-"0";
+ if k=9 then goto done;
+ incr(k); read(pool_file,n);
+ end;
+done: if a<>@$ then bad_pool('! MF.POOL doesn''t match; TANGLE me again.');
+@.MF.POOL doesn't match@>
+c:=true;
+end
+
+@* \[5] On-line and off-line printing.
+Messages that are sent to a user's terminal and to the transcript-log file
+are produced by several `|print|' procedures. These procedures will
+direct their output to a variety of places, based on the setting of
+the global variable |selector|, which has the following possible
+values:
+
+\yskip
+\hang |term_and_log|, the normal setting, prints on the terminal and on the
+ transcript file.
+
+\hang |log_only|, prints only on the transcript file.
+
+\hang |term_only|, prints only on the terminal.
+
+\hang |no_print|, doesn't print at all. This is used only in rare cases
+ before the transcript file is open.
+
+\hang |pseudo|, puts output into a cyclic buffer that is used
+ by the |show_context| routine; when we get to that routine we shall discuss
+ the reasoning behind this curious mode.
+
+\hang |new_string|, appends the output to the current string in the
+ string pool.
+
+\yskip
+\noindent The symbolic names `|term_and_log|', etc., have been assigned
+numeric codes that satisfy the convenient relations |no_print+1=term_only|,
+|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
+
+Three additional global variables, |tally| and |term_offset| and
+|file_offset|, record the number of characters that have been printed
+since they were most recently cleared to zero. We use |tally| to record
+the length of (possibly very long) stretches of printing; |term_offset|
+and |file_offset|, on the other hand, keep track of how many characters
+have appeared so far on the current line that has been output to the
+terminal or to the transcript file, respectively.
+
+@d no_print=0 {|selector| setting that makes data disappear}
+@d term_only=1 {printing is destined for the terminal only}
+@d log_only=2 {printing is destined for the transcript file only}
+@d term_and_log=3 {normal |selector| setting}
+@d pseudo=4 {special |selector| setting for |show_context|}
+@d new_string=5 {printing is deflected to the string pool}
+@d max_selector=5 {highest selector setting}
+
+@<Glob...@>=
+@!log_file : alpha_file; {transcript of \MF\ session}
+@!selector : 0..max_selector; {where to print a message}
+@!dig : array[0..22] of 0..15; {digits in a number being output}
+@!tally : integer; {the number of characters recently printed}
+@!term_offset : 0..max_print_line;
+ {the number of characters on the current terminal line}
+@!file_offset : 0..max_print_line;
+ {the number of characters on the current file line}
+@!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for
+ pseudoprinting}
+@!trick_count: integer; {threshold for pseudoprinting, explained later}
+@!first_count: integer; {another variable for pseudoprinting}
+
+@ @<Initialize the output routines@>=
+selector:=term_only; tally:=0; term_offset:=0; file_offset:=0;
+
+@ Macro abbreviations for output to the terminal and to the log file are
+defined here for convenience. Some systems need special conventions
+for terminal output, and it is possible to adhere to those conventions
+by changing |wterm|, |wterm_ln|, and |wterm_cr| here.
+@^system dependencies@>
+
+@d wterm(#)==write(term_out,#)
+@d wterm_ln(#)==write_ln(term_out,#)
+@d wterm_cr==write_ln(term_out)
+@d wlog(#)==write(log_file,#)
+@d wlog_ln(#)==write_ln(log_file,#)
+@d wlog_cr==write_ln(log_file)
+
+@ To end a line of text output, we call |print_ln|.
+
+@<Basic print...@>=
+procedure print_ln; {prints an end-of-line}
+begin case selector of
+term_and_log: begin wterm_cr; wlog_cr;
+ term_offset:=0; file_offset:=0;
+ end;
+log_only: begin wlog_cr; file_offset:=0;
+ end;
+term_only: begin wterm_cr; term_offset:=0;
+ end;
+no_print,pseudo,new_string: do_nothing;
+end; {there are no other cases}
+end; {note that |tally| is not affected}
+
+@ The |print_char| procedure sends one character to the desired destination,
+using the |xchr| array to map it into an external character compatible with
+|input_ln|. All printing comes through |print_ln| or |print_char|.
+
+@<Basic printing...@>=
+procedure print_char(@!s:ASCII_code); {prints a single character}
+begin case selector of
+term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
+ incr(term_offset); incr(file_offset);
+ if term_offset=max_print_line then
+ begin wterm_cr; term_offset:=0;
+ end;
+ if file_offset=max_print_line then
+ begin wlog_cr; file_offset:=0;
+ end;
+ end;
+log_only: begin wlog(xchr[s]); incr(file_offset);
+ if file_offset=max_print_line then print_ln;
+ end;
+term_only: begin wterm(xchr[s]); incr(term_offset);
+ if term_offset=max_print_line then print_ln;
+ end;
+no_print: do_nothing;
+pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
+new_string: begin if pool_ptr<pool_size then append_char(s);
+ end; {we drop characters if the string space is full}
+end; {there are no other cases}
+incr(tally);
+end;
+
+@ An entire string is output by calling |print|. Note that if we are outputting
+the single standard ASCII character \.c, we could call |print("c")|, since
+|"c"=99| is the number of a single-character string, as explained above. But
+|print_char("c")| is quicker, so \MF\ goes directly to the |print_char|
+routine when it knows that this is safe. (The present implementation
+assumes that it is always safe to print a visible ASCII character.)
+@^system dependencies@>
+
+@<Basic print...@>=
+procedure print(@!s:integer); {prints string |s|}
+var @!j:pool_pointer; {current character code position}
+begin if (s<0)or(s>=str_ptr) then s:="???"; {this can't happen}
+@.???@>
+if (s<256)and(selector>pseudo) then print_char(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+ end;
+end;
+
+@ Sometimes it's necessary to print a string whose characters
+may not be visible ASCII codes. In that case |slow_print| is used.
+
+@<Basic print...@>=
+procedure slow_print(@!s:integer); {prints string |s|}
+var @!j:pool_pointer; {current character code position}
+begin if (s<0)or(s>=str_ptr) then s:="???"; {this can't happen}
+@.???@>
+if (s<256)and(selector>pseudo) then print_char(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+ end;
+end;
+
+@ Here is the very first thing that \MF\ prints: a headline that identifies
+the version number and base name. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that the banner
+and base identifier together will occupy at most |max_print_line|
+character positions.
+
+@<Initialize the output...@>=
+wterm(banner);
+if base_ident=0 then wterm_ln(' (no base preloaded)')
+else begin slow_print(base_ident); print_ln;
+ end;
+update_terminal;
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@<Basic print...@>=
+procedure print_nl(@!s:str_number); {prints string |s| at beginning of line}
+begin if ((term_offset>0)and(odd(selector)))or@|
+ ((file_offset>0)and(selector>=log_only)) then print_ln;
+print(s);
+end;
+
+@ An array of digits in the range |0..9| is printed by |print_the_digs|.
+
+@<Basic print...@>=
+procedure print_the_digs(@!k:eight_bits);
+ {prints |dig[k-1]|$\,\ldots\,$|dig[0]|}
+begin while k>0 do
+ begin decr(k); print_char("0"+dig[k]);
+ end;
+end;
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
+to negative arguments, since such operations are not implemented consistently
+by all \PASCAL\ compilers.
+
+@<Basic print...@>=
+procedure print_int(@!n:integer); {prints an integer in decimal form}
+var k:0..23; {index to current digit; we assume that $|n|<10^{23}$}
+@!m:integer; {used to negate |n| in possibly dangerous cases}
+begin k:=0;
+if n<0 then
+ begin print_char("-");
+ if n>-100000000 then negate(n)
+ else begin m:=-1-n; n:=m div 10; m:=(m mod 10)+1; k:=1;
+ if m<10 then dig[0]:=m
+ else begin dig[0]:=0; incr(n);
+ end;
+ end;
+ end;
+repeat dig[k]:=n mod 10; n:=n div 10; incr(k);
+until n=0;
+print_the_digs(k);
+end;
+
+@ \MF\ also makes use of a trivial procedure to print two digits. The
+following subroutine is usually called with a parameter in the range |0<=n<=99|.
+
+@p procedure print_dd(@!n:integer); {prints two least significant digits}
+begin n:=abs(n) mod 100; print_char("0"+(n div 10));
+print_char("0"+(n mod 10));
+end;
+
+@ Here is a procedure that asks the user to type a line of input,
+assuming that the |selector| setting is either |term_only| or |term_and_log|.
+The input is placed into locations |first| through |last-1| of the
+|buffer| array, and echoed on the transcript file if appropriate.
+
+This procedure is never called when |interaction<scroll_mode|.
+
+@d prompt_input(#)==begin wake_up_terminal; print(#); term_input;
+ end {prints a string and gets a line of input}
+
+@p procedure term_input; {gets a line from the terminal}
+var @!k:0..buf_size; {index into |buffer|}
+begin update_terminal; {now the user sees the prompt for sure}
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@.End of file on the terminal@>
+term_offset:=0; {the user's line ended with \<\rm return>}
+decr(selector); {prepare to echo the input}
+if last<>first then for k:=first to last-1 do print(buffer[k]);
+print_ln; buffer[last]:="%"; incr(selector); {restore previous status}
+end;
+
+@* \[6] Reporting errors.
+When something anomalous is detected, \MF\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr
+|print_err("Something anomalous has been detected");|\cr
+|help3("This is the first line of my offer to help.")|\cr
+|("This is the second line. I'm trying to")|\cr
+|("explain the best way for you to proceed.");|\cr
+|error;|\cr}}$$
+A two-line help message would be given using |help2|, etc.; these informal
+helps should use simple vocabulary that complements the words used in the
+official error message that was printed. (Outside the U.S.A., the help
+messages should preferably be translated into the local vernacular. Each
+line of help is at most 60 characters long, in the present implementation,
+so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message,
+and makes sure that the terminal is awake if a stop is going to occur.
+The |error| procedure supplies a `\..' after the official message, then it
+shows the location of the error; and if |interaction=error_stop_mode|,
+it also enters into a dialog with the user, during which time the help
+message may be printed.
+@^system dependencies@>
+
+@ The global variable |interaction| has four settings, representing increasing
+amounts of user interaction:
+
+@d batch_mode=0 {omits all stops and omits terminal output}
+@d nonstop_mode=1 {omits all stops}
+@d scroll_mode=2 {omits error stops}
+@d error_stop_mode=3 {stops at every opportunity to interact}
+@d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal;
+ print_nl("! "); print(#);
+@.!\relax@>
+ end
+
+@<Glob...@>=
+@!interaction:batch_mode..error_stop_mode; {current level of interaction}
+
+@ @<Set init...@>=interaction:=error_stop_mode;
+
+@ \MF\ is careful not to call |error| when the print |selector| setting
+might be unusual. The only possible values of |selector| at the time of
+error messages are
+
+\yskip\hang|no_print| (when |interaction=batch_mode|
+ and |log_file| not yet open);
+
+\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
+
+\hang|log_only| (when |interaction=batch_mode| and |log_file| is open);
+
+\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
+
+@<Initialize the print |selector| based on |interaction|@>=
+if interaction=batch_mode then selector:=no_print@+else selector:=term_only
+
+@ A global variable |deletions_allowed| is set |false| if the |get_next|
+routine is active when |error| is called; this ensures that |get_next|
+will never be called recursively.
+@^recursion@>
+
+The global variable |history| records the worst level of error that
+has been detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an
+|error| occurs without an interactive dialog, and it is reset to zero at
+the end of every statement. If |error_count| reaches 100, \MF\ decides
+that there is no point in continuing further.
+
+@d spotless=0 {|history| value when nothing has been amiss yet}
+@d warning_issued=1 {|history| value when |begin_diagnostic| has been called}
+@d error_message_issued=2 {|history| value when |error| has been called}
+@d fatal_error_stop=3 {|history| value when termination was premature}
+
+@<Glob...@>=
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_next|?}
+@!history:spotless..fatal_error_stop; {has the source input been clean so far?}
+@!error_count:-1..100; {the number of scrolled errors since the
+ last statement ended}
+
+@ The value of |history| is initially |fatal_error_stop|, but it will
+be changed to |spotless| if \MF\ survives the initialization process.
+
+@<Set init...@>=
+deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
+
+@ Since errors can be detected almost anywhere in \MF, we want to declare the
+error procedures near the beginning of the program. But the error procedures
+in turn use some other procedures, which need to be declared |forward|
+before we get to |error| itself.
+
+It is possible for |error| to be called recursively if some error arises
+when |get_next| is being used to delete a token, and/or if some fatal error
+occurs while \MF\ is trying to fix a non-fatal one. But such recursion
+@^recursion@>
+is never more than two levels deep.
+
+@<Error handling...@>=
+procedure@?normalize_selector; forward;@t\2@>@/
+procedure@?get_next; forward;@t\2@>@/
+procedure@?term_input; forward;@t\2@>@/
+procedure@?show_context; forward;@t\2@>@/
+procedure@?begin_file_reading; forward;@t\2@>@/
+procedure@?open_log_file; forward;@t\2@>@/
+procedure@?close_files_and_terminate; forward;@t\2@>@/
+procedure@?clear_for_error_prompt; forward;@t\2@>@/
+@t\4\hskip-\fontdimen2\font@>@;@+@!debug@+procedure@?debug_help;
+ forward;@;@+gubed@;@/
+@t\4@>@<Declare the procedure called |flush_string|@>
+
+@ Individual lines of help are recorded in the array |help_line|, which
+contains entries in positions |0..(help_ptr-1)|. They should be printed
+in reverse order, i.e., with |help_line[0]| appearing last.
+
+@d hlp1(#)==help_line[0]:=#;@+end
+@d hlp2(#)==help_line[1]:=#; hlp1
+@d hlp3(#)==help_line[2]:=#; hlp2
+@d hlp4(#)==help_line[3]:=#; hlp3
+@d hlp5(#)==help_line[4]:=#; hlp4
+@d hlp6(#)==help_line[5]:=#; hlp5
+@d help0==help_ptr:=0 {sometimes there might be no help}
+@d help1==@+begin help_ptr:=1; hlp1 {use this with one help line}
+@d help2==@+begin help_ptr:=2; hlp2 {use this with two help lines}
+@d help3==@+begin help_ptr:=3; hlp3 {use this with three help lines}
+@d help4==@+begin help_ptr:=4; hlp4 {use this with four help lines}
+@d help5==@+begin help_ptr:=5; hlp5 {use this with five help lines}
+@d help6==@+begin help_ptr:=6; hlp6 {use this with six help lines}
+
+@<Glob...@>=
+@!help_line:array[0..5] of str_number; {helps for the next |error|}
+@!help_ptr:0..6; {the number of help lines present}
+@!use_err_help:boolean; {should the |err_help| string be shown?}
+@!err_help:str_number; {a string set up by \&{errhelp}}
+
+@ @<Set init...@>=
+help_ptr:=0; use_err_help:=false; err_help:=0;
+
+@ The |jump_out| procedure just cuts across all active procedure levels and
+goes to |end_of_MF|. This is the only nontrivial |@!goto| statement in the
+whole program. It is used when there is no recovery from a particular error.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the body of |jump_out| should simply be
+`|close_files_and_terminate|;\thinspace' followed by a call on some system
+procedure that quietly terminates the program.
+
+@<Error hand...@>=
+procedure jump_out;
+begin goto end_of_MF;
+end;
+
+@ Here now is the general |error| routine.
+
+@<Error hand...@>=
+procedure error; {completes the job of error reporting}
+label continue,exit;
+var @!c:ASCII_code; {what the user types}
+@!s1,@!s2,@!s3:integer; {used to save global variables when deleting tokens}
+@!j:pool_pointer; {character position being printed}
+begin if history<error_message_issued then history:=error_message_issued;
+print_char("."); show_context;
+if interaction=error_stop_mode then @<Get user's advice and |return|@>;
+incr(error_count);
+if error_count=100 then
+ begin print_nl("(That makes 100 errors; please try again.)");
+@.That makes 100 errors...@>
+ history:=fatal_error_stop; jump_out;
+ end;
+@<Put help message on the transcript file@>;
+exit:end;
+
+@ @<Get user's advice...@>=
+loop@+begin continue: clear_for_error_prompt; prompt_input("? ");
+@.?\relax@>
+ if last=first then return;
+ c:=buffer[first];
+ if c>="a" then c:=c+"A"-"a"; {convert to uppercase}
+ @<Interpret code |c| and |return| if done@>;
+ end
+
+@ It is desirable to provide an `\.E' option here that gives the user
+an easy way to return from \MF\ to the system editor, with the offending
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+
+@<Interpret code |c| and |return| if done@>=
+case c of
+"0","1","2","3","4","5","6","7","8","9": if deletions_allowed then
+ @<Delete |c-"0"| tokens and |goto continue|@>;
+@t\4\4@>@;@+@!debug "D":begin debug_help;goto continue;@+end;@+gubed@/
+"E": if file_ptr>0 then
+ begin print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[file_ptr].name_field);
+ print(" at line "); print_int(line);@/
+ interaction:=scroll_mode; jump_out;
+ end;
+"H": @<Print the help information and |goto continue|@>;
+"I":@<Introduce new material from the terminal and |return|@>;
+"Q","R","S":@<Change the interaction level and |return|@>;
+"X":begin interaction:=scroll_mode; jump_out;
+ end;
+othercases do_nothing
+endcases;@/
+@<Print the menu of available options@>
+
+@ @<Print the menu...@>=
+begin print("Type <return> to proceed, S to scroll future error messages,");@/
+@.Type <return> to proceed...@>
+print_nl("R to run without stopping, Q to run quietly,");@/
+print_nl("I to insert something, ");
+if file_ptr>0 then print("E to edit your file,");
+if deletions_allowed then
+ print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+print_nl("H for help, X to quit.");
+end
+
+@ Here the author of \MF\ apologizes for making use of the numerical
+relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings
+|batch_mode|, |nonstop_mode|, |scroll_mode|.
+@^Knuth, Donald Ervin@>
+
+@<Change the interaction...@>=
+begin error_count:=0; interaction:=batch_mode+c-"Q";
+print("OK, entering ");
+case c of
+"Q":begin print("batchmode"); decr(selector);
+ end;
+"R":print("nonstopmode");
+"S":print("scrollmode");
+end; {there are no other cases}
+print("..."); print_ln; update_terminal; return;
+end
+
+@ When the following code is executed, |buffer[(first+1)..(last-1)]| may
+contain the material inserted by the user; otherwise another prompt will
+be given. In order to understand this part of the program fully, you need
+to be familiar with \MF's input stacks.
+
+@<Introduce new material...@>=
+begin begin_file_reading; {enter a new syntactic level for terminal input}
+if last>first+1 then
+ begin loc:=first+1; buffer[first]:=" ";
+ end
+else begin prompt_input("insert>"); loc:=first;
+@.insert>@>
+ end;
+first:=last+1; cur_input.limit_field:=last; return;
+end
+
+@ We allow deletion of up to 99 tokens at a time.
+
+@<Delete |c-"0"| tokens...@>=
+begin s1:=cur_cmd; s2:=cur_mod; s3:=cur_sym; OK_to_interrupt:=false;
+if (last>first+1) and (buffer[first+1]>="0")and(buffer[first+1]<="9") then
+ c:=c*10+buffer[first+1]-"0"*11
+else c:=c-"0";
+while c>0 do
+ begin get_next; {one-level recursive call of |error| is possible}
+ @<Decrease the string reference count, if the current token is a string@>;
+ decr(c);
+ end;
+cur_cmd:=s1; cur_mod:=s2; cur_sym:=s3; OK_to_interrupt:=true;
+help2("I have just deleted some text, as you asked.")@/
+("You can now delete more, or insert, or whatever.");
+show_context; goto continue;
+end
+
+@ @<Print the help info...@>=
+begin if use_err_help then
+ begin @<Print the string |err_help|, possibly on several lines@>;
+ use_err_help:=false;
+ end
+else begin if help_ptr=0 then
+ help2("Sorry, I don't know how to help in this situation.")@/
+ @t\kern1em@>("Maybe you should try asking a human?");
+ repeat decr(help_ptr); print(help_line[help_ptr]); print_ln;
+ until help_ptr=0;
+ end;
+help4("Sorry, I already gave what help I could...")@/
+ ("Maybe you should try asking a human?")@/
+ ("An error might have occurred before I noticed any problems.")@/
+ ("``If all else fails, read the instructions.''");@/
+goto continue;
+end
+
+@ @<Print the string |err_help|, possibly on several lines@>=
+j:=str_start[err_help];
+while j<str_start[err_help+1] do
+ begin if str_pool[j]<>si("%") then print(so(str_pool[j]))
+ else if j+1=str_start[err_help+1] then print_ln
+ else if str_pool[j+1]<>si("%") then print_ln
+ else begin incr(j); print_char("%");
+ end;
+ incr(j);
+ end
+
+@ @<Put help message on the transcript file@>=
+if interaction>batch_mode then decr(selector); {avoid terminal output}
+if use_err_help then
+ begin print_nl("");
+ @<Print the string |err_help|, possibly on several lines@>;
+ end
+else while help_ptr>0 do
+ begin decr(help_ptr); print_nl(help_line[help_ptr]);
+ end;
+print_ln;
+if interaction>batch_mode then incr(selector); {re-enable terminal output}
+print_ln
+
+@ In anomalous cases, the print selector might be in an unknown state;
+the following subroutine is called to fix things just enough to keep
+running a bit longer.
+
+@p procedure normalize_selector;
+begin if log_opened then selector:=term_and_log
+else selector:=term_only;
+if job_name=0 then open_log_file;
+if interaction=batch_mode then decr(selector);
+end;
+
+@ The following procedure prints \MF's last words before dying.
+
+@d succumb==begin if interaction=error_stop_mode then
+ interaction:=scroll_mode; {no more interaction}
+ if log_opened then error;
+ @!debug if interaction>batch_mode then debug_help;@;@+gubed@;@/
+ history:=fatal_error_stop; jump_out; {irrecoverable error}
+ end
+
+@<Error hand...@>=
+procedure fatal_error(@!s:str_number); {prints |s|, and that's it}
+begin normalize_selector;@/
+print_err("Emergency stop"); help1(s); succumb;
+@.Emergency stop@>
+end;
+
+@ Here is the most dreaded error message.
+
+@<Error hand...@>=
+procedure overflow(@!s:str_number;@!n:integer); {stop due to finiteness}
+begin normalize_selector;
+print_err("METAFONT capacity exceeded, sorry [");
+@.METAFONT capacity exceeded ...@>
+print(s); print_char("="); print_int(n); print_char("]");
+help2("If you really absolutely need more capacity,")@/
+ ("you can ask a wizard to enlarge me.");
+succumb;
+end;
+
+@ The program might sometime run completely amok, at which point there is
+no choice but to stop. If no previous error has been detected, that's bad
+news; a message is printed that is really intended for the \MF\
+maintenance person instead of the user (unless the user has been
+particularly diabolical). The index entries for `this can't happen' may
+help to pinpoint the problem.
+@^dry rot@>
+
+@<Error hand...@>=
+procedure confusion(@!s:str_number);
+ {consistency check violated; |s| tells where}
+begin normalize_selector;
+if history<error_message_issued then
+ begin print_err("This can't happen ("); print(s); print_char(")");
+@.This can't happen@>
+ help1("I'm broken. Please show this to someone who can fix can fix");
+ end
+else begin print_err("I can't go on meeting you like this");
+@.I can't go on...@>
+ help2("One of your faux pas seems to have wounded me deeply...")@/
+ ("in fact, I'm barely conscious. Please fix it and try again.");
+ end;
+succumb;
+end;
+
+@ Users occasionally want to interrupt \MF\ while it's running.
+If the \PASCAL\ runtime system allows this, one can implement
+a routine that sets the global variable |interrupt| to some nonzero value
+when such an interrupt is signalled. Otherwise there is probably at least
+a way to make |interrupt| nonzero using the \PASCAL\ debugger.
+@^system dependencies@>
+@^debugging@>
+
+@d check_interrupt==begin if interrupt<>0 then pause_for_instructions;
+ end
+
+@<Global...@>=
+@!interrupt:integer; {should \MF\ pause for instructions?}
+@!OK_to_interrupt:boolean; {should interrupts be observed?}
+
+@ @<Set init...@>=
+interrupt:=0; OK_to_interrupt:=true;
+
+@ When an interrupt has been detected, the program goes into its
+highest interaction level and lets the user have the full flexibility of
+the |error| routine. \MF\ checks for interrupts only at times when it is
+safe to do this.
+
+@p procedure pause_for_instructions;
+begin if OK_to_interrupt then
+ begin interaction:=error_stop_mode;
+ if (selector=log_only)or(selector=no_print) then
+ incr(selector);
+ print_err("Interruption");
+@.Interruption@>
+ help3("You rang?")@/
+ ("Try to insert some instructions for me (e.g.,`I show x'),")@/
+ ("unless you just want to quit by typing `X'.");
+ deletions_allowed:=false; error; deletions_allowed:=true;
+ interrupt:=0;
+ end;
+end;
+
+@ Many of \MF's error messages state that a missing token has been
+inserted behind the scenes. We can save string space and program space
+by putting this common code into a subroutine.
+
+@p procedure missing_err(@!s:str_number);
+begin print_err("Missing `"); print(s); print("' has been inserted");
+@.Missing...inserted@>
+end;
+
+@* \[7] Arithmetic with scaled numbers.
+The principal computations performed by \MF\ are done entirely in terms of
+integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this
+program can be carried out in exactly the same way on a wide variety of
+computers, including some small ones.
+@^small computers@>
+
+But \PASCAL\ does not define the @!|div|
+operation in the case of negative dividends; for example, the result of
+|(-2*n-1) div 2| is |-(n+1)| on some computers and |-n| on others.
+There are two principal types of arithmetic: ``translation-preserving,''
+in which the identity |(a+q*b)div b=(a div b)+q| is valid; and
+``negation-preserving,'' in which |(-a)div b=-(a div b)|. This leads to
+two \MF s, which can produce different results, although the differences
+should be negligible when the language is being used properly.
+The \TeX\ processor has been defined carefully so that both varieties
+of arithmetic will produce identical output, but it would be too
+inefficient to constrain \MF\ in a similar way.
+
+@d el_gordo == @'17777777777 {$2^{31}-1$, the largest value that \MF\ likes}
+
+@ One of \MF's most common operations is the calculation of
+$\lfloor{a+b\over2}\rfloor$,
+the midpoint of two given integers |a| and~|b|. The only decent way to do
+this in \PASCAL\ is to write `|(a+b) div 2|'; but on most machines it is
+far more efficient to calculate `|(a+b)| right shifted one bit'.
+
+Therefore the midpoint operation will always be denoted by `|half(a+b)|'
+in this program. If \MF\ is being implemented with languages that permit
+binary shifting, the |half| macro should be changed to make this operation
+as efficient as possible.
+
+@d half(#)==(#) div 2
+
+@ A single computation might use several subroutine calls, and it is
+desirable to avoid producing multiple error messages in case of arithmetic
+overflow. So the routines below set the global variable |arith_error| to |true|
+instead of reporting errors directly to the user.
+@^overflow in arithmetic@>
+
+@<Glob...@>=
+@!arith_error:boolean; {has arithmetic overflow occurred recently?}
+
+@ @<Set init...@>=
+arith_error:=false;
+
+@ At crucial points the program will say |check_arith|, to test if
+an arithmetic error has been detected.
+
+@d check_arith==begin if arith_error then clear_arith;@+end
+
+@p procedure clear_arith;
+begin print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+help4("Uh, oh. A little while ago one of the quantities that I was")@/
+ ("computing got too large, so I'm afraid your answers will be")@/
+ ("somewhat askew. You'll probably have to adopt different")@/
+ ("tactics next time. But I shall try to carry on anyway.");
+error; arith_error:=false;
+end;
+
+@ Addition is not always checked to make sure that it doesn't overflow,
+but in places where overflow isn't too unlikely the |slow_add| routine
+is used.
+
+@p function slow_add(@!x,@!y:integer):integer;
+begin if x>=0 then
+ if y<=el_gordo-x then slow_add:=x+y
+ else begin arith_error:=true; slow_add:=el_gordo;
+ end
+else if -y<=el_gordo+x then slow_add:=x+y
+ else begin arith_error:=true; slow_add:=-el_gordo;
+ end;
+end;
+
+@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
+of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
+positions from the right end of a binary computer word.
+
+@d quarter_unit == @'40000 {$2^{14}$, represents 0.250000}
+@d half_unit == @'100000 {$2^{15}$, represents 0.50000}
+@d three_quarter_unit == @'140000 {$3\cdot2^{14}$, represents 0.75000}
+@d unity == @'200000 {$2^{16}$, represents 1.00000}
+@d two == @'400000 {$2^{17}$, represents 2.00000}
+@d three == @'600000 {$2^{17}+2^{16}$, represents 3.00000}
+
+@<Types...@>=
+@!scaled = integer; {this type is used for scaled integers}
+@!small_number=0..63; {this type is self-explanatory}
+
+@ The following function is used to create a scaled integer from a given decimal
+fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
+given in |dig[i]|, and the calculation produces a correctly rounded result.
+
+@p function round_decimals(@!k:small_number) : scaled;
+ {converts a decimal fraction}
+var @!a:integer; {the accumulator}
+begin a:=0;
+while k>0 do
+ begin decr(k); a:=(a+dig[k]*two) div 10;
+ end;
+round_decimals:=half(a+1);
+end;
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \MF\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly. A decimal point is printed only if the value is
+not an integer. If there is more than one way to print the result with
+the optimum number of digits following the decimal point, the closest
+possible value is given.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@<Basic printing...@>=
+procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+var @!delta:scaled; {amount of allowable inaccuracy}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+s:=10*(s mod unity)+5;
+if s<>5 then
+ begin delta:=10; print_char(".");
+ repeat if delta>unity then
+ s:=s+@'100000-(delta div 2); {round the final digit}
+ print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10;
+ until s<=delta;
+ end;
+end;
+
+@ We often want to print two scaled quantities in parentheses,
+separated by a comma.
+
+@<Basic printing...@>=
+procedure print_two(@!x,@!y:scaled); {prints `|(x,y)|'}
+begin print_char("("); print_scaled(x); print_char(","); print_scaled(y);
+print_char(")");
+end;
+
+@ The |scaled| quantities in \MF\ programs are generally supposed to be
+less than $2^{12}$ in absolute value, so \MF\ does much of its internal
+arithmetic with 28~significant bits of precision. A |fraction| denotes
+a scaled integer whose binary point is assumed to be 28 bit positions
+from the right.
+
+@d fraction_half==@'1000000000 {$2^{27}$, represents 0.50000000}
+@d fraction_one==@'2000000000 {$2^{28}$, represents 1.00000000}
+@d fraction_two==@'4000000000 {$2^{29}$, represents 2.00000000}
+@d fraction_three==@'6000000000 {$3\cdot2^{28}$, represents 3.00000000}
+@d fraction_four==@'10000000000 {$2^{30}$, represents 4.00000000}
+
+@<Types...@>=
+@!fraction=integer; {this type is used for scaled fractions}
+
+@ In fact, the two sorts of scaling discussed above aren't quite
+sufficient; \MF\ has yet another, used internally to keep track of angles
+in units of $2^{-20}$ degrees.
+
+@d forty_five_deg==@'264000000 {$45\cdot2^{20}$, represents $45^\circ$}
+@d ninety_deg==@'550000000 {$90\cdot2^{20}$, represents $90^\circ$}
+@d one_eighty_deg==@'1320000000 {$180\cdot2^{20}$, represents $180^\circ$}
+@d three_sixty_deg==@'2640000000 {$360\cdot2^{20}$, represents $360^\circ$}
+
+@<Types...@>=
+@!angle=integer; {this type is used for scaled angles}
+
+@ The |make_fraction| routine produces the |fraction| equivalent of
+|p/q|, given integers |p| and~|q|; it computes the integer
+$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
+positive. If |p| and |q| are both of the same scaled type |t|,
+the ``type relation'' |make_fraction(t,t)=fraction| is valid;
+and it's also possible to use the subroutine ``backwards,'' using
+the relation |make_fraction(t,fraction)=t| between scaled types.
+
+If the result would have magnitude $2^{31}$ or more, |make_fraction|
+sets |arith_error:=true|. Most of \MF's internal computations have
+been designed to avoid this sort of error.
+
+Notice that if 64-bit integer arithmetic were available,
+we could simply compute |(@t$(2^{29}$@>*p+q)div (2*q)|.
+But when we are restricted to \PASCAL's 32-bit arithmetic we
+must either resort to multiple-precision maneuvering
+or use a simple but slow iteration. The multiple-precision technique
+would be about three times faster than the code adopted here, but it
+would be comparatively long and tricky, involving about sixteen
+additional multiplications and divisions.
+
+This operation is part of \MF's ``inner loop''; indeed, it will
+consume nearly 10\pct! of the running time (exclusive of input and output)
+if the code below is left unchanged. A machine-dependent recoding
+will therefore make \MF\ run faster. The present implementation
+is highly portable, but slow; it avoids multiplication and division
+except in the initial stage. System wizards should be careful to
+replace it with a routine that is guaranteed to produce identical
+results in all cases.
+@^system dependencies@>
+
+As noted below, a few more routines should also be replaced by machine-dependent
+code, for efficiency. But when a procedure is not part of the ``inner loop,''
+such changes aren't advisable; simplicity and robustness are
+preferable to trickery, unless the cost is too high.
+@^inner loop@>
+
+@p function make_fraction(@!p,@!q:integer):fraction;
+var @!f:integer; {the fraction bits, with a leading 1 bit}
+@!n:integer; {the integer part of $\vert p/q\vert$}
+@!negative:boolean; {should the result be negated?}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin if p>=0 then negative:=false
+else begin negate(p); negative:=true;
+ end;
+if q<=0 then
+ begin debug if q=0 then confusion("/");@;@+gubed@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q); negative:=not negative;
+ end;
+n:=p div q; p:=p mod q;
+if n>=8 then
+ begin arith_error:=true;
+ if negative then make_fraction:=-el_gordo@+else make_fraction:=el_gordo;
+ end
+else begin n:=(n-1)*fraction_one;
+ @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
+ if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n;
+ end;
+end;
+
+@ The |repeat| loop here preserves the following invariant relations
+between |f|, |p|, and~|q|:
+(i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+$p_0$ is the original value of~$p$.
+
+Notice that the computation specifies
+|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+Let us hope that optimizing compilers do not miss this point; a
+special variable |be_careful| is used to emphasize the necessary
+order of computation. Optimizing compilers should keep |be_careful|
+in a register, not store it in memory.
+@^inner loop@>
+
+@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
+f:=1;
+repeat be_careful:=p-q; p:=be_careful+p;
+if p>=0 then f:=f+f+1
+else begin double(f); p:=p+q;
+ end;
+until f>=fraction_one;
+be_careful:=p-q;
+if be_careful+p>=0 then incr(f)
+
+@ The dual of |make_fraction| is |take_fraction|, which multiplies a
+given integer~|q| by a fraction~|f|. When the operands are positive, it
+computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function
+of |q| and~|f|.
+
+This routine is even more ``inner loopy'' than |make_fraction|;
+the present implementation consumes almost 20\pct! of \MF's computation
+time during typical jobs, so a machine-language or 64-bit
+substitute is advisable.
+@^inner loop@> @^system dependencies@>
+
+@p function take_fraction(@!q:integer;@!f:fraction):integer;
+var @!p:integer; {the fraction so far}
+@!negative:boolean; {should the result be negated?}
+@!n:integer; {additional multiple of $q$}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin @<Reduce to the case that |f>=0| and |q>=0|@>;
+if f<fraction_one then n:=0
+else begin n:=f div fraction_one; f:=f mod fraction_one;
+ if q<=el_gordo div n then n:=n*q
+ else begin arith_error:=true; n:=el_gordo;
+ end;
+ end;
+f:=f+fraction_one;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
+be_careful:=n-el_gordo;
+if be_careful+p>0 then
+ begin arith_error:=true; n:=el_gordo-p;
+ end;
+if negative then take_fraction:=-(n+p)
+else take_fraction:=n+p;
+end;
+
+@ @<Reduce to the case that |f>=0| and |q>=0|@>=
+if f>=0 then negative:=false
+else begin negate(f); negative:=true;
+ end;
+if q<0 then
+ begin negate(q); negative:=not negative;
+ end;
+
+@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
+@^inner loop@>
+
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
+p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$}
+if q<fraction_four then
+ repeat if odd(f) then p:=half(p+q)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+
+
+@ When we want to multiply something by a |scaled| quantity, we use a scheme
+analogous to |take_fraction| but with a different scaling.
+Given positive operands, |take_scaled|
+computes the quantity $p=\lfloor qf/2^{16}+{1\over2}\rfloor$.
+
+Once again it is a good idea to use 64-bit arithmetic if
+possible; otherwise |take_scaled| will use more than 2\pct! of the running time
+when the Computer Modern fonts are being generated.
+@^inner loop@>
+
+@p function take_scaled(@!q:integer;@!f:scaled):integer;
+var @!p:integer; {the fraction so far}
+@!negative:boolean; {should the result be negated?}
+@!n:integer; {additional multiple of $q$}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin @<Reduce to the case that |f>=0| and |q>=0|@>;
+if f<unity then n:=0
+else begin n:=f div unity; f:=f mod unity;
+ if q<=el_gordo div n then n:=n*q
+ else begin arith_error:=true; n:=el_gordo;
+ end;
+ end;
+f:=f+unity;
+@<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>;
+be_careful:=n-el_gordo;
+if be_careful+p>0 then
+ begin arith_error:=true; n:=el_gordo-p;
+ end;
+if negative then take_scaled:=-(n+p)
+else take_scaled:=n+p;
+end;
+
+@ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>=
+p:=half_unit; {that's $2^{15}$; the invariants hold now with $k=16$}
+@^inner loop@>
+if q<fraction_four then
+ repeat if odd(f) then p:=half(p+q)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+
+@ For completeness, there's also |make_scaled|, which computes a
+quotient as a |scaled| number instead of as a |fraction|.
+In other words, the result is $\lfloor2^{16}p/q+{1\over2}\rfloor$, if the
+operands are positive. \ (This procedure is not used especially often,
+so it is not part of \MF's inner loop.)
+
+@p function make_scaled(@!p,@!q:integer):scaled;
+var @!f:integer; {the fraction bits, with a leading 1 bit}
+@!n:integer; {the integer part of $\vert p/q\vert$}
+@!negative:boolean; {should the result be negated?}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin if p>=0 then negative:=false
+else begin negate(p); negative:=true;
+ end;
+if q<=0 then
+ begin debug if q=0 then confusion("/");@+gubed@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q); negative:=not negative;
+ end;
+n:=p div q; p:=p mod q;
+if n>=@'100000 then
+ begin arith_error:=true;
+ if negative then make_scaled:=-el_gordo@+else make_scaled:=el_gordo;
+ end
+else begin n:=(n-1)*unity;
+ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>;
+ if negative then make_scaled:=-(f+n)@+else make_scaled:=f+n;
+ end;
+end;
+
+@ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>=
+f:=1;
+repeat be_careful:=p-q; p:=be_careful+p;
+if p>=0 then f:=f+f+1
+else begin double(f); p:=p+q;
+ end;
+until f>=unity;
+be_careful:=p-q;
+if be_careful+p>=0 then incr(f)
+
+@ Here is a typical example of how the routines above can be used.
+It computes the function
+$${1\over3\tau}f(\theta,\phi)=
+{\tau^{-1}\bigl(2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi)
+ (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\bigr)\over
+3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)},$$
+where $\tau$ is a |scaled| ``tension'' parameter. This is \MF's magic
+fudge factor for placing the first control point of a curve that starts
+at an angle $\theta$ and ends at an angle $\phi$ from the straight path.
+(Actually, if the stated quantity exceeds 4, \MF\ reduces it to~4.)
+
+The trigonometric quantity to be multiplied by $\sqrt2$ is less than $\sqrt2$.
+(It's a sum of eight terms whose absolute values can be bounded using
+relations such as $\sin\theta\cos\theta\L{1\over2}$.) Thus the numerator
+is positive; and since the tension $\tau$ is constrained to be at least
+$3\over4$, the numerator is less than $16\over3$. The denominator is
+nonnegative and at most~6. Hence the fixed-point calculations below
+are guaranteed to stay within the bounds of a 32-bit computer word.
+
+The angles $\theta$ and $\phi$ are given implicitly in terms of |fraction|
+arguments |st|, |ct|, |sf|, and |cf|, representing $\sin\theta$, $\cos\theta$,
+$\sin\phi$, and $\cos\phi$, respectively.
+
+@p function velocity(@!st,@!ct,@!sf,@!cf:fraction;@!t:scaled):fraction;
+var @!acc,@!num,@!denom:integer; {registers for intermediate calculations}
+begin acc:=take_fraction(st-(sf div 16), sf-(st div 16));
+acc:=take_fraction(acc,ct-cf);
+num:=fraction_two+take_fraction(acc,379625062);
+ {$2^{28}\sqrt2\approx379625062.497$}
+denom:=fraction_three+take_fraction(ct,497706707)+take_fraction(cf,307599661);
+ {$3\cdot2^{27}\cdot(\sqrt5-1)\approx497706706.78$ and
+ $3\cdot2^{27}\cdot(3-\sqrt5\,)\approx307599661.22$}
+if t<>unity then num:=make_scaled(num,t);
+ {|make_scaled(fraction,scaled)=fraction|}
+if num div 4>=denom then velocity:=fraction_four
+else velocity:=make_fraction(num,denom);
+end;
+
+@ The following somewhat different subroutine tests rigorously if $ab$ is
+greater than, equal to, or less than~$cd$,
+given integers $(a,b,c,d)$. In most cases a quick decision is reached.
+The result is $+1$, 0, or~$-1$ in the three respective cases.
+
+@d return_sign(#)==begin ab_vs_cd:=#; return;
+ end
+
+@p function ab_vs_cd(@!a,b,c,d:integer):integer;
+label exit;
+var @!q,@!r:integer; {temporary registers}
+begin @<Reduce to the case that |a,c>=0|, |b,d>0|@>;
+loop@+ begin q := a div d; r := c div b;
+ if q<>r then
+ if q>r then return_sign(1)@+else return_sign(-1);
+ q := a mod d; r := c mod b;
+ if r=0 then
+ if q=0 then return_sign(0)@+else return_sign(1);
+ if q=0 then return_sign(-1);
+ a:=b; b:=q; c:=d; d:=r;
+ end; {now |a>d>0| and |c>b>0|}
+exit:end;
+
+@ @<Reduce to the case that |a...@>=
+if a<0 then
+ begin negate(a); negate(b);
+ end;
+if c<0 then
+ begin negate(c); negate(d);
+ end;
+if d<=0 then
+ begin if b>=0 then
+ if ((a=0)or(b=0))and((c=0)or(d=0)) then return_sign(0)
+ else return_sign(1);
+ if d=0 then
+ if a=0 then return_sign(0)@+else return_sign(-1);
+ q:=a; a:=c; c:=q; q:=-b; b:=-d; d:=q;
+ end
+else if b<=0 then
+ begin if b<0 then if a>0 then return_sign(-1);
+ if c=0 then return_sign(0) else return_sign(-1);
+ end
+
+@ We conclude this set of elementary routines with some simple rounding
+and truncation operations that are coded in a machine-independent fashion.
+The routines are slightly complicated because we want them to work
+without overflow whenever $-2^{31}\L x<2^{31}$.
+
+@p function floor_scaled(@!x:scaled):scaled;
+ {$2^{16}\lfloor x/2^{16}\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=0 then floor_scaled:=x-(x mod unity)
+else begin be_careful:=x+1;
+ floor_scaled:=x+((-be_careful) mod unity)+1-unity;
+ end;
+end;
+@#
+function floor_unscaled(@!x:scaled):integer;
+ {$\lfloor x/2^{16}\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=0 then floor_unscaled:=x div unity
+else begin be_careful:=x+1; floor_unscaled:=-(1+((-be_careful) div unity));
+ end;
+end;
+@#
+function round_unscaled(@!x:scaled):integer;
+ {$\lfloor x/2^{16}+.5\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=half_unit then round_unscaled:=1+((x-half_unit) div unity)
+else if x>=-half_unit then round_unscaled:=0
+else begin be_careful:=x+1;
+ round_unscaled:=-(1+((-be_careful-half_unit) div unity));
+ end;
+end;
+@#
+function round_fraction(@!x:fraction):scaled;
+ {$\lfloor x/2^{12}+.5\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=2048 then round_fraction:=1+((x-2048) div 4096)
+else if x>=-2048 then round_fraction:=0
+else begin be_careful:=x+1;
+ round_fraction:=-(1+((-be_careful-2048) div 4096));
+ end;
+end;
+
+@* \[8] Algebraic and transcendental functions.
+\MF\ computes all of the necessary special functions from scratch, without
+relying on |real| arithmetic or system subroutines for sines, cosines, etc.
+
+@ To get the square root of a |scaled| number |x|, we want to calculate
+$s=\lfloor 2^8\!\sqrt x +{1\over2}\rfloor$. If $x>0$, this is the unique
+integer such that $2^{16}x-s\L s^2<2^{16}x+s$. The following subroutine
+determines $s$ by an iterative method that maintains the invariant
+relations $x=2^{46-2k}x_0\bmod 2^{30}$, $0<y=\lfloor 2^{16-2k}x_0\rfloor
+-s^2+s\L q=2s$, where $x_0$ is the initial value of $x$. The value of~$y$
+might, however, be zero at the start of the first iteration.
+
+@p function square_rt(@!x:scaled):scaled;
+var @!k:small_number; {iteration control counter}
+@!y,@!q:integer; {registers for intermediate calculations}
+begin if x<=0 then @<Handle square root of zero or negative argument@>
+else begin k:=23; q:=2;
+ while x<fraction_two do {i.e., |while x<@t$2^{29}$@>|\unskip}
+ begin decr(k); x:=x+x+x+x;
+ end;
+ if x<fraction_four then y:=0
+ else begin x:=x-fraction_four; y:=1;
+ end;
+ repeat @<Decrease |k| by 1, maintaining the invariant
+ relations between |x|, |y|, and~|q|@>;
+ until k=0;
+ square_rt:=half(q);
+ end;
+end;
+
+@ @<Handle square root of zero...@>=
+begin if x<0 then
+ begin print_err("Square root of ");
+@.Square root...replaced by 0@>
+ print_scaled(x); print(" has been replaced by 0");
+ help2("Since I don't take square roots of negative numbers,")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+ error;
+ end;
+square_rt:=0;
+end
+
+@ @<Decrease |k| by 1, maintaining...@>=
+double(x); double(y);
+if x>=fraction_four then {note that |fraction_four=@t$2^{30}$@>|}
+ begin x:=x-fraction_four; incr(y);
+ end;
+double(x); y:=y+y-q; double(q);
+if x>=fraction_four then
+ begin x:=x-fraction_four; incr(y);
+ end;
+if y>q then
+ begin y:=y-q; q:=q+2;
+ end
+else if y<=0 then
+ begin q:=q-2; y:=y+q;
+ end;
+decr(k)
+
+@ Pythagorean addition $\psqrt{a^2+b^2}$ is implemented by an elegant
+iterative scheme due to Cleve Moler and Donald Morrison [{\sl IBM Journal
+@^Moler, Cleve Barry@>
+@^Morrison, Donald Ross@>
+of Research and Development\/ \bf27} (1983), 577--581]. It modifies |a| and~|b|
+in such a way that their Pythagorean sum remains invariant, while the
+smaller argument decreases.
+
+@p function pyth_add(@!a,@!b:integer):integer;
+label done;
+var @!r:fraction; {register used to transform |a| and |b|}
+@!big:boolean; {is the result dangerously near $2^{31}$?}
+begin a:=abs(a); b:=abs(b);
+if a<b then
+ begin r:=b; b:=a; a:=r;
+ end; {now |0<=b<=a|}
+if b>0 then
+ begin if a<fraction_two then big:=false
+ else begin a:=a div 4; b:=b div 4; big:=true;
+ end; {we reduced the precision to avoid arithmetic overflow}
+ @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>;
+ if big then
+ if a<fraction_two then a:=a+a+a+a
+ else begin arith_error:=true; a:=el_gordo;
+ end;
+ end;
+pyth_add:=a;
+end;
+
+@ The key idea here is to reflect the vector $(a,b)$ about the
+line through $(a,b/2)$.
+
+@<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>=
+loop@+ begin r:=make_fraction(b,a);
+ r:=take_fraction(r,r); {now $r\approx b^2/a^2$}
+ if r=0 then goto done;
+ r:=make_fraction(r,fraction_four+r);
+ a:=a+take_fraction(a+a,r); b:=take_fraction(b,r);
+ end;
+done:
+
+@ Here is a similar algorithm for $\psqrt{a^2-b^2}$.
+It converges slowly when $b$ is near $a$, but otherwise it works fine.
+
+@p function pyth_sub(@!a,@!b:integer):integer;
+label done;
+var @!r:fraction; {register used to transform |a| and |b|}
+@!big:boolean; {is the input dangerously near $2^{31}$?}
+begin a:=abs(a); b:=abs(b);
+if a<=b then @<Handle erroneous |pyth_sub| and set |a:=0|@>
+else begin if a<fraction_four then big:=false
+ else begin a:=half(a); b:=half(b); big:=true;
+ end;
+ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>;
+ if big then a:=a+a;
+ end;
+pyth_sub:=a;
+end;
+
+@ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>=
+loop@+ begin r:=make_fraction(b,a);
+ r:=take_fraction(r,r); {now $r\approx b^2/a^2$}
+ if r=0 then goto done;
+ r:=make_fraction(r,fraction_four-r);
+ a:=a-take_fraction(a+a,r); b:=take_fraction(b,r);
+ end;
+done:
+
+@ @<Handle erroneous |pyth_sub| and set |a:=0|@>=
+begin if a<b then
+ begin print_err("Pythagorean subtraction "); print_scaled(a);
+ print("+-+"); print_scaled(b); print(" has been replaced by 0");
+@.Pythagorean...@>
+ help2("Since I don't take square roots of negative numbers,")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+ error;
+ end;
+a:=0;
+end
+
+@ The subroutines for logarithm and exponential involve two tables.
+The first is simple: |two_to_the[k]| equals $2^k$. The second involves
+a bit more calculation, which the author claims to have done correctly:
+|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
+2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
+nearest integer.
+
+@<Glob...@>=
+@!two_to_the:array[0..30] of integer; {powers of two}
+@!spec_log:array[1..28] of integer; {special logarithms}
+
+@ @<Local variables for initialization@>=
+@!k:integer; {all-purpose loop index}
+
+@ @<Set init...@>=
+two_to_the[0]:=1;
+for k:=1 to 30 do two_to_the[k]:=2*two_to_the[k-1];
+spec_log[1]:=93032640;
+spec_log[2]:=38612034;
+spec_log[3]:=17922280;
+spec_log[4]:=8662214;
+spec_log[5]:=4261238;
+spec_log[6]:=2113709;
+spec_log[7]:=1052693;
+spec_log[8]:=525315;
+spec_log[9]:=262400;
+spec_log[10]:=131136;
+spec_log[11]:=65552;
+spec_log[12]:=32772;
+spec_log[13]:=16385;
+for k:=14 to 27 do spec_log[k]:=two_to_the[27-k];
+spec_log[28]:=1;
+
+@ Here is the routine that calculates $2^8$ times the natural logarithm
+of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$,
+when |x| is a given positive integer.
+
+The method is based on exercise 1.2.2--25 in {\sl The Art of Computer
+Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$,
+and the logarithm of $2^{30}x$ remains to be added to an accumulator
+register called~$y$. Three auxiliary bits of accuracy are retained in~$y$
+during the calculation, and sixteen auxiliary bits to extend |y| are
+kept in~|z| during the initial argument reduction. (We add
+$100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will
+not become negative; also, the actual amount subtracted from~|y| is~96,
+not~100, because we want to add~4 for rounding before the final division by~8.)
+
+@p function m_log(@!x:scaled):scaled;
+var @!y,@!z:integer; {auxiliary registers}
+@!k:integer; {iteration counter}
+begin if x<=0 then @<Handle non-positive logarithm@>
+else begin y:=1302456956+4-100; {$14\times2^{27}\ln2\approx1302456956.421063$}
+ z:=27595+6553600; {and $2^{16}\times .421063\approx 27595$}
+ while x<fraction_four do
+ begin double(x); y:=y-93032639; z:=z-48782;
+ end; {$2^{27}\ln2\approx 93032639.74436163$
+ and $2^{16}\times.74436163\approx 48782$}
+ y:=y+(z div unity); k:=2;
+ while x>fraction_four+4 do
+ @<Increase |k| until |x| can be multiplied by a
+ factor of $2^{-k}$, and adjust $y$ accordingly@>;
+ m_log:=y div 8;
+ end;
+end;
+
+@ @<Increase |k| until |x| can...@>=
+begin z:=((x-1) div two_to_the[k])+1; {$z=\lceil x/2^k\rceil$}
+while x<fraction_four+z do
+ begin z:=half(z+1); k:=k+1;
+ end;
+y:=y+spec_log[k]; x:=x-z;
+end
+
+@ @<Handle non-positive logarithm@>=
+begin print_err("Logarithm of ");
+@.Logarithm...replaced by 0@>
+print_scaled(x); print(" has been replaced by 0");
+help2("Since I don't take logs of non-positive numbers,")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+error; m_log:=0;
+end
+
+@ Conversely, the exponential routine calculates $\exp(x/2^8)$,
+when |x| is |scaled|. The result is an integer approximation to
+$2^{16}\exp(x/2^{24})$, when |x| is regarded as an integer.
+
+@p function m_exp(@!x:scaled):scaled;
+var @!k:small_number; {loop control index}
+@!y,@!z:integer; {auxiliary registers}
+begin if x>174436200 then
+ {$2^{24}\ln((2^{31}-1)/2^{16})\approx 174436199.51$}
+ begin arith_error:=true; m_exp:=el_gordo;
+ end
+else if x<-197694359 then m_exp:=0
+ {$2^{24}\ln(2^{-1}/2^{16})\approx-197694359.45$}
+else begin if x<=0 then
+ begin z:=-8*x; y:=@'4000000; {$y=2^{20}$}
+ end
+ else begin if x<=127919879 then z:=1023359037-8*x
+ {$2^{27}\ln((2^{31}-1)/2^{20})\approx 1023359037.125$}
+ else z:=8*(174436200-x); {|z| is always nonnegative}
+ y:=el_gordo;
+ end;
+ @<Multiply |y| by $\exp(-z/2^{27})$@>;
+ if x<=127919879 then m_exp:=(y+8) div 16@+else m_exp:=y;
+ end;
+end;
+
+@ The idea here is that subtracting |spec_log[k]| from |z| corresponds
+to multiplying |y| by $1-2^{-k}$.
+
+A subtle point (which had to be checked) was that if $x=127919879$, the
+value of~|y| will decrease so that |y+8| doesn't overflow. In fact,
+$z$ will be 5 in this case, and |y| will decrease by~64 when |k=25|
+and by~16 when |k=27|.
+
+@<Multiply |y| by...@>=
+k:=1;
+while z>0 do
+ begin while z>=spec_log[k] do
+ begin z:=z-spec_log[k];
+ y:=y-1-((y-two_to_the[k-1]) div two_to_the[k]);
+ end;
+ incr(k);
+ end
+
+@ The trigonometric subroutines use an auxiliary table such that
+|spec_atan[k]| contains an approximation to the |angle| whose tangent
+is~$1/2^k$.
+
+@<Glob...@>=
+@!spec_atan:array[1..26] of angle; {$\arctan2^{-k}$ times $2^{20}\cdot180/\pi$}
+
+@ @<Set init...@>=
+spec_atan[1]:=27855475;
+spec_atan[2]:=14718068;
+spec_atan[3]:=7471121;
+spec_atan[4]:=3750058;
+spec_atan[5]:=1876857;
+spec_atan[6]:=938658;
+spec_atan[7]:=469357;
+spec_atan[8]:=234682;
+spec_atan[9]:=117342;
+spec_atan[10]:=58671;
+spec_atan[11]:=29335;
+spec_atan[12]:=14668;
+spec_atan[13]:=7334;
+spec_atan[14]:=3667;
+spec_atan[15]:=1833;
+spec_atan[16]:=917;
+spec_atan[17]:=458;
+spec_atan[18]:=229;
+spec_atan[19]:=115;
+spec_atan[20]:=57;
+spec_atan[21]:=29;
+spec_atan[22]:=14;
+spec_atan[23]:=7;
+spec_atan[24]:=4;
+spec_atan[25]:=2;
+spec_atan[26]:=1;
+
+@ Given integers |x| and |y|, not both zero, the |n_arg| function
+returns the |angle| whose tangent points in the direction $(x,y)$.
+This subroutine first determines the correct octant, then solves the
+problem for |0<=y<=x|, then converts the result appropriately to
+return an answer in the range |-one_eighty_deg<=@t$\theta$@><=one_eighty_deg|.
+(The answer is |+one_eighty_deg| if |y=0| and |x<0|, but an answer of
+|-one_eighty_deg| is possible if, for example, |y=-1| and $x=-2^{30}$.)
+
+The octants are represented in a ``Gray code,'' since that turns out
+to be computationally simplest.
+
+@d negate_x=1
+@d negate_y=2
+@d switch_x_and_y=4
+@d first_octant=1
+@d second_octant=first_octant+switch_x_and_y
+@d third_octant=first_octant+switch_x_and_y+negate_x
+@d fourth_octant=first_octant+negate_x
+@d fifth_octant=first_octant+negate_x+negate_y
+@d sixth_octant=first_octant+switch_x_and_y+negate_x+negate_y
+@d seventh_octant=first_octant+switch_x_and_y+negate_y
+@d eighth_octant=first_octant+negate_y
+
+@p function n_arg(@!x,@!y:integer):angle;
+var @!z:angle; {auxiliary register}
+@!t:integer; {temporary storage}
+@!k:small_number; {loop counter}
+@!octant:first_octant..sixth_octant; {octant code}
+begin if x>=0 then octant:=first_octant
+else begin negate(x); octant:=first_octant+negate_x;
+ end;
+if y<0 then
+ begin negate(y); octant:=octant+negate_y;
+ end;
+if x<y then
+ begin t:=y; y:=x; x:=t; octant:=octant+switch_x_and_y;
+ end;
+if x=0 then @<Handle undefined arg@>
+else begin @<Set variable |z| to the arg of $(x,y)$@>;
+ @<Return an appropriate answer based on |z| and |octant|@>;
+ end;
+end;
+
+@ @<Handle undefined arg@>=
+begin print_err("angle(0,0) is taken as zero");
+@.angle(0,0)...zero@>
+help2("The `angle' between two identical points is undefined.")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+error; n_arg:=0;
+end
+
+@ @<Return an appropriate answer...@>=
+case octant of
+first_octant:n_arg:=z;
+second_octant:n_arg:=ninety_deg-z;
+third_octant:n_arg:=ninety_deg+z;
+fourth_octant:n_arg:=one_eighty_deg-z;
+fifth_octant:n_arg:=z-one_eighty_deg;
+sixth_octant:n_arg:=-z-ninety_deg;
+seventh_octant:n_arg:=z-ninety_deg;
+eighth_octant:n_arg:=-z;
+end {there are no other cases}
+
+@ At this point we have |x>=y>=0|, and |x>0|. The numbers are scaled up
+or down until $2^{28}\L x<2^{29}$, so that accurate fixed-point calculations
+will be made.
+
+@<Set variable |z| to the arg...@>=
+while x>=fraction_two do
+ begin x:=half(x); y:=half(y);
+ end;
+z:=0;
+if y>0 then
+ begin while x<fraction_one do
+ begin double(x); double(y);
+ end;
+ @<Increase |z| to the arg of $(x,y)$@>;
+ end
+
+@ During the calculations of this section, variables |x| and~|y|
+represent actual coordinates $(x,2^{-k}y)$. We will maintain the
+condition |x>=y|, so that the tangent will be at most $2^{-k}$.
+If $x<2y$, the tangent is greater than $2^{-k-1}$. The transformation
+$(a,b)\mapsto(a+b\tan\phi,b-a\tan\phi)$ replaces $(a,b)$ by
+coordinates whose angle has decreased by~$\phi$; in the special case
+$a=x$, $b=2^{-k}y$, and $\tan\phi=2^{-k-1}$, this operation reduces
+to the particularly simple iteration shown here. [Cf.~John E. Meggitt,
+@^Meggitt, John E.@>
+{\sl IBM Journal of Research and Development\/ \bf6} (1962), 210--226.]
+
+The initial value of |x| will be multiplied by at most
+$(1+{1\over2})(1+{1\over8})(1+{1\over32})\cdots\approx 1.7584$; hence
+there is no chance of integer overflow.
+
+@<Increase |z|...@>=
+k:=0;
+repeat double(y); incr(k);
+if y>x then
+ begin z:=z+spec_atan[k]; t:=x; x:=x+(y div two_to_the[k+k]); y:=y-t;
+ end;
+until k=15;
+repeat double(y); incr(k);
+if y>x then
+ begin z:=z+spec_atan[k]; y:=y-x;
+ end;
+until k=26
+
+@ Conversely, the |n_sin_cos| routine takes an |angle| and produces the sine
+and cosine of that angle. The results of this routine are
+stored in global integer variables |n_sin| and |n_cos|.
+
+@<Glob...@>=
+@!n_sin,@!n_cos:fraction; {results computed by |n_sin_cos|}
+
+@ Given an integer |z| that is $2^{20}$ times an angle $\theta$ in degrees,
+the purpose of |n_sin_cos(z)| is to set
+|x=@t$r\cos\theta$@>| and |y=@t$r\sin\theta$@>| (approximately),
+for some rather large number~|r|. The maximum of |x| and |y|
+will be between $2^{28}$ and $2^{30}$, so that there will be hardly
+any loss of accuracy. Then |x| and~|y| are divided by~|r|.
+
+@p procedure n_sin_cos(@!z:angle); {computes a multiple of the sine and cosine}
+var @!k:small_number; {loop control variable}
+@!q:0..7; {specifies the quadrant}
+@!r:fraction; {magnitude of |(x,y)|}
+@!x,@!y,@!t:integer; {temporary registers}
+begin while z<0 do z:=z+three_sixty_deg;
+z:=z mod three_sixty_deg; {now |0<=z<three_sixty_deg|}
+q:=z div forty_five_deg; z:=z mod forty_five_deg;
+x:=fraction_one; y:=x;
+if not odd(q) then z:=forty_five_deg-z;
+@<Subtract angle |z| from |(x,y)|@>;
+@<Convert |(x,y)| to the octant determined by~|q|@>;
+r:=pyth_add(x,y); n_cos:=make_fraction(x,r); n_sin:=make_fraction(y,r);
+end;
+
+@ In this case the octants are numbered sequentially.
+
+@<Convert |(x,...@>=
+case q of
+0:do_nothing;
+1:begin t:=x; x:=y; y:=t;
+ end;
+2:begin t:=x; x:=-y; y:=t;
+ end;
+3:negate(x);
+4:begin negate(x); negate(y);
+ end;
+5:begin t:=x; x:=-y; y:=-t;
+ end;
+6:begin t:=x; x:=y; y:=-t;
+ end;
+7:negate(y);
+end {there are no other cases}
+
+@ The main iteration of |n_sin_cos| is similar to that of |n_arg| but
+applied in reverse. The values of |spec_atan[k]| decrease slowly enough
+that this loop is guaranteed to terminate before the (nonexistent) value
+|spec_atan[27]| would be required.
+
+@<Subtract angle |z|...@>=
+k:=1;
+while z>0 do
+ begin if z>=spec_atan[k] then
+ begin z:=z-spec_atan[k]; t:=x;@/
+ x:=t+y div two_to_the[k];
+ y:=y-t div two_to_the[k];
+ end;
+ incr(k);
+ end;
+if y<0 then y:=0 {this precaution may never be needed}
+
+@ And now let's complete our collection of numeric utility routines
+by considering random number generation.
+\MF\ generates pseudo-random numbers with the additive scheme recommended
+in Section 3.6 of {\sl The Art of Computer Programming}; however, the
+results are random fractions between 0 and |fraction_one-1|, inclusive.
+
+There's an auxiliary array |randoms| that contains 55 pseudo-random
+fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-24})\bmod 2^{28}$,
+we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
+The global variable |j_random| tells which element has most recently
+been consumed.
+
+@<Glob...@>=
+@!randoms:array[0..54] of fraction; {the last 55 random values generated}
+@!j_random:0..54; {the number of unused |randoms|}
+
+@ To consume a random fraction, the program below will say `|next_random|'
+and then it will fetch |randoms[j_random]|. The |next_random| macro
+actually accesses the numbers backwards; blocks of 55~$x$'s are
+essentially being ``flipped.'' But that doesn't make them less random.
+
+@d next_random==if j_random=0 then new_randoms
+ else decr(j_random)
+
+@p procedure new_randoms;
+var @!k:0..54; {index into |randoms|}
+@!x:fraction; {accumulator}
+begin for k:=0 to 23 do
+ begin x:=randoms[k]-randoms[k+31];
+ if x<0 then x:=x+fraction_one;
+ randoms[k]:=x;
+ end;
+for k:=24 to 54 do
+ begin x:=randoms[k]-randoms[k-24];
+ if x<0 then x:=x+fraction_one;
+ randoms[k]:=x;
+ end;
+j_random:=54;
+end;
+
+@ To initialize the |randoms| table, we call the following routine.
+
+@p procedure init_randoms(@!seed:scaled);
+var @!j,@!jj,@!k:fraction; {more or less random integers}
+@!i:0..54; {index into |randoms|}
+begin j:=abs(seed);
+while j>=fraction_one do j:=half(j);
+k:=1;
+for i:=0 to 54 do
+ begin jj:=k; k:=j-k; j:=jj;
+ if k<0 then k:=k+fraction_one;
+ randoms[(i*21)mod 55]:=j;
+ end;
+new_randoms; new_randoms; new_randoms; {``warm up'' the array}
+end;
+
+@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
+or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
+
+Note that the call of |take_fraction| will produce the values 0 and~|x|
+with about half the probability that it will produce any other particular
+values between 0 and~|x|, because it rounds its answers.
+
+@p function unif_rand(@!x:scaled):scaled;
+var @!y:scaled; {trial value}
+begin next_random; y:=take_fraction(abs(x),randoms[j_random]);
+if y=abs(x) then unif_rand:=0
+else if x>0 then unif_rand:=y
+else unif_rand:=-y;
+end;
+
+@ Finally, a normal deviate with mean zero and unit standard deviation
+can readily be obtained with the ratio method (Algorithm 3.4.1R in
+{\sl The Art of Computer Programming\/}).
+
+@p function norm_rand:scaled;
+var @!x,@!u,@!l:integer; {what the book would call $2^{16}X$, $2^{28}U$,
+ and $-2^{24}\ln U$}
+begin repeat
+ repeat next_random;
+ x:=take_fraction(112429,randoms[j_random]-fraction_half);
+ {$2^{16}\sqrt{8/e}\approx 112428.82793$}
+ next_random; u:=randoms[j_random];
+ until abs(x)<u;
+x:=make_fraction(x,u);
+l:=139548960-m_log(u); {$2^{24}\cdot12\ln2\approx139548959.6165$}
+until ab_vs_cd(1024,l,x,x)>=0;
+norm_rand:=x;
+end;
+
+@* \[9] Packed data.
+In order to make efficient use of storage space, \MF\ bases its major data
+structures on a |memory_word|, which contains either a (signed) integer,
+possibly scaled, or a small number of fields that are one half or one
+quarter of the size used for storing integers.
+
+If |x| is a variable of type |memory_word|, it contains up to four
+fields that can be referred to as follows:
+$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
+|x|&.|int|&(an |integer|)\cr
+|x|&.|sc|\qquad&(a |scaled| integer)\cr
+|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
+|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
+ field)\cr
+|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
+ &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
+This is somewhat cumbersome to write, and not very readable either, but
+macros will be used to make the notation shorter and more transparent.
+The \PASCAL\ code below gives a formal definition of |memory_word| and
+its subsidiary types, using packed variant records. \MF\ makes no
+assumptions about the relative positions of the fields within a word.
+
+Since we are assuming 32-bit integers, a halfword must contain at least
+16 bits, and a quarterword must contain at least 8 bits.
+@^system dependencies@>
+But it doesn't hurt to have more bits; for example, with enough 36-bit
+words you might be able to have |mem_max| as large as 262142.
+
+N.B.: Valuable memory space will be dreadfully wasted unless \MF\ is compiled
+by a \PASCAL\ that packs all of the |memory_word| variants into
+the space of a single integer. Some \PASCAL\ compilers will pack an
+integer whose subrange is `|0..255|' into an eight-bit field, but others
+insist on allocating space for an additional sign bit; on such systems you
+can get 256 values into a quarterword only if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+
+@ Here are the inequalities that the quarterword and halfword values
+must satisfy (or rather, the inequalities that they mustn't satisfy):
+
+@<Check the ``constant''...@>=
+init if mem_max<>mem_top then bad:=10;@+tini@;@/
+if mem_max<mem_top then bad:=10;
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+if (min_halfword>0)or(max_halfword<32767) then bad:=12;
+if (min_quarterword<min_halfword)or@|
+ (max_quarterword>max_halfword) then bad:=13;
+if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+if max_strings>max_halfword then bad:=15;
+if buf_size>max_halfword then bad:=16;
+if (max_quarterword-min_quarterword<255)or@|
+ (max_halfword-min_halfword<65535) then bad:=17;
+
+@ The operation of subtracting |min_halfword| occurs rather frequently in
+\MF, so it is convenient to abbreviate this operation by using the macro
+|ho| defined here. \MF\ will run faster with respect to compilers that
+don't optimize the expression `|x-0|', if this macro is simplified in the
+obvious way when |min_halfword=0|. Similarly, |qi| and |qo| are used for
+input to and output from quarterwords.
+@^system dependencies@>
+
+@d ho(#)==#-min_halfword
+ {to take a sixteen-bit item from a halfword}
+@d qo(#)==#-min_quarterword {to read eight bits from a quarterword}
+@d qi(#)==#+min_quarterword {to store eight bits in a quarterword}
+
+@ The reader should study the following definitions closely:
+@^system dependencies@>
+
+@d sc==int {|scaled| data is equivalent to |integer|}
+
+@<Types...@>=
+@!quarterword = min_quarterword..max_quarterword; {1/4 of a word}
+@!halfword=min_halfword..max_halfword; {1/2 of a word}
+@!two_choices = 1..2; {used when there are two variants in a record}
+@!three_choices = 1..3; {used when there are three variants in a record}
+@!two_halves = packed record@;@/
+ @!rh:halfword;
+ case two_choices of
+ 1: (@!lh:halfword);
+ 2: (@!b0:quarterword; @!b1:quarterword);
+ end;
+@!four_quarters = packed record@;@/
+ @!b0:quarterword;
+ @!b1:quarterword;
+ @!b2:quarterword;
+ @!b3:quarterword;
+ end;
+@!memory_word = record@;@/
+ case three_choices of
+ 1: (@!int:integer);
+ 2: (@!hh:two_halves);
+ 3: (@!qqqq:four_quarters);
+ end;
+@!word_file = file of memory_word;
+
+@ When debugging, we may want to print a |memory_word| without knowing
+what type it is; so we print it in all modes.
+@^dirty \PASCAL@>@^debugging@>
+
+@p @!debug procedure print_word(@!w:memory_word);
+ {prints |w| in all ways}
+begin print_int(w.int); print_char(" ");@/
+print_scaled(w.sc); print_char(" "); print_scaled(w.sc div @'10000); print_ln;@/
+print_int(w.hh.lh); print_char("="); print_int(w.hh.b0); print_char(":");
+print_int(w.hh.b1); print_char(";"); print_int(w.hh.rh); print_char(" ");@/
+print_int(w.qqqq.b0); print_char(":"); print_int(w.qqqq.b1); print_char(":");
+print_int(w.qqqq.b2); print_char(":"); print_int(w.qqqq.b3);
+end;
+gubed
+
+@* \[10] Dynamic memory allocation.
+The \MF\ system does nearly all of its own memory allocation, so that it
+can readily be transported into environments that do not have automatic
+facilities for strings, garbage collection, etc., and so that it can be in
+control of what error messages the user receives. The dynamic storage
+requirements of \MF\ are handled by providing a large array |mem| in
+which consecutive blocks of words are used as nodes by the \MF\ routines.
+
+Pointer variables are indices into this array, or into another array
+called |eqtb| that will be explained later. A pointer variable might
+also be a special flag that lies outside the bounds of |mem|, so we
+allow pointers to assume any |halfword| value. The minimum memory
+index represents a null pointer.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==mem_min {the null pointer}
+
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+variable-length records consisting of two or more words each. This region
+is maintained using an algorithm similar to the one described in exercise
+2.5--19 of {\sl The Art of Computer Programming}. However, no size field
+appears in the allocated nodes; the program is responsible for knowing the
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_min| and |mem_top| may be dumped as part
+of preloaded base files, by the \.{INIMF} preprocessor.
+@.INIMF@>
+Production versions of \MF\ may extend the memory at the top end in order to
+provide more space; these locations, between |mem_top| and |mem_max|,
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\hbox{|null=mem_min<lo_mem_max<hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
+
+@<Glob...@>=
+@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
+@!lo_mem_max : pointer; {the largest location of variable-size memory in use}
+@!hi_mem_min : pointer; {the smallest location of one-word memory in use}
+
+@ Users who wish to study the memory requirements of specific applications can
+use optional special features that keep track of current and
+maximum memory usage. When code between the delimiters |@!stat| $\ldots$
+|tats| is not ``commented out,'' \MF\ will run a bit slower but it will
+report these statistics when |tracing_stats| is positive.
+
+@<Glob...@>=
+@!var_used, @!dyn_used : integer; {how much memory is in use}
+
+@ Let's consider the one-word memory region first, since it's the
+simplest. The pointer variable |mem_end| holds the highest-numbered location
+of |mem| that has ever been used. The free locations of |mem| that
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+|two_halves|, and we write |info(p)| and |link(p)| for the |lh|
+and |rh| fields of |mem[p]| when it is of this type. The single-word
+free locations form a linked list
+$$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
+terminated by |null|.
+
+@d link(#) == mem[#].hh.rh {the |link| field of a memory word}
+@d info(#) == mem[#].hh.lh {the |info| field of a memory word}
+
+@<Glob...@>=
+@!avail : pointer; {head of the list of available one-word nodes}
+@!mem_end : pointer; {the last one-word node used in |mem|}
+
+@ If one-word memory is exhausted, it might mean that the user has forgotten
+a token like `\&{enddef}' or `\&{endfor}'. We will define some procedures
+later that try to help pinpoint the trouble.
+
+@p @t\4@>@<Declare the procedure called |show_token_list|@>@;
+@t\4@>@<Declare the procedure called |runaway|@>
+
+@ The function |get_avail| returns a pointer to a new one-word node whose
+|link| field is null. However, \MF\ will halt if there is no more room left.
+@^inner loop@>
+
+@p function get_avail : pointer; {single-word node allocation}
+var @!p:pointer; {the new node being got}
+begin p:=avail; {get top location in the |avail| stack}
+if p<>null then avail:=link(avail) {and pop it off}
+else if mem_end<mem_max then {or go into virgin territory}
+ begin incr(mem_end); p:=mem_end;
+ end
+else begin decr(hi_mem_min); p:=hi_mem_min;
+ if hi_mem_min<=lo_mem_max then
+ begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("main memory size",mem_max+1-mem_min);
+ {quit; all one-word nodes are busy}
+@:METAFONT capacity exceeded main memory size}{\quad main memory size@>
+ end;
+ end;
+link(p):=null; {provide an oft-desired initialization of the new node}
+@!stat incr(dyn_used);@+tats@;{maintain statistics}
+get_avail:=p;
+end;
+
+@ Conversely, a one-word node is recycled by calling |free_avail|.
+
+@d free_avail(#)== {single-word node liberation}
+ begin link(#):=avail; avail:=#;
+ @!stat decr(dyn_used);@+tats@/
+ end
+
+@ There's also a |fast_get_avail| routine, which saves the procedure-call
+overhead at the expense of extra programming. This macro is used in
+the places that would otherwise account for the most calls of |get_avail|.
+@^inner loop@>
+
+@d fast_get_avail(#)==@t@>@;@/
+ begin #:=avail; {avoid |get_avail| if possible, to save time}
+ if #=null then #:=get_avail
+ else begin avail:=link(#); link(#):=null;
+ @!stat incr(dyn_used);@+tats@/
+ end;
+ end
+
+@ The available-space list that keeps track of the variable-size portion
+of |mem| is a nonempty, doubly-linked circular list of empty nodes,
+pointed to by the roving pointer |rover|.
+
+Each empty node has size 2 or more; the first word contains the special
+value |max_halfword| in its |link| field and the size in its |info| field;
+the second word contains the two pointers for double linking.
+
+Each nonempty node also has size 2 or more. Its first word is of type
+|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
+Otherwise there is complete flexibility with respect to the contents
+of its other fields and its other words.
+
+(We require |mem_max<max_halfword| because terrible things can happen
+when |max_halfword| appears in the |link| field of a nonempty node.)
+
+@d empty_flag == max_halfword {the |link| of an empty variable-size node}
+@d is_empty(#) == (link(#)=empty_flag) {tests for empty node}
+@d node_size == info {the size field in empty variable-size nodes}
+@d llink(#) == info(#+1) {left link in doubly-linked list of empty nodes}
+@d rlink(#) == link(#+1) {right link in doubly-linked list of empty nodes}
+
+@<Glob...@>=
+@!rover : pointer; {points to some node in the list of empties}
+
+@ A call to |get_node| with argument |s| returns a pointer to a new node
+of size~|s|, which must be 2~or more. The |link| field of the first word
+of this new node is set to null. An overflow stop occurs if no suitable
+space exists.
+
+If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
+areas and returns the value |max_halfword|.
+
+@p function get_node(@!s:integer):pointer; {variable-size node allocation}
+label found,exit,restart;
+var @!p:pointer; {the node currently under inspection}
+@!q:pointer; {the node physically after node |p|}
+@!r:integer; {the newly allocated node, or a candidate for this honor}
+@!t,@!tt:integer; {temporary registers}
+@^inner loop@>
+begin restart: p:=rover; {start at some free node in the ring}
+repeat @<Try to allocate within node |p| and its physical successors,
+ and |goto found| if allocation was possible@>;
+p:=rlink(p); {move to the next node in the ring}
+until p=rover; {repeat until the whole list has been traversed}
+if s=@'10000000000 then
+ begin get_node:=max_halfword; return;
+ end;
+if lo_mem_max+2<hi_mem_min then if lo_mem_max+2<=mem_min+max_halfword then
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size",mem_max+1-mem_min);
+ {sorry, nothing satisfactory is left}
+@:METAFONT capacity exceeded main memory size}{\quad main memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+tats@;@/
+get_node:=r;
+exit:end;
+
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \MF\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
+else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max) div 2;
+ {|lo_mem_max+2<=t<hi_mem_min|}
+if t>mem_min+max_halfword then t:=mem_min+max_halfword;
+p:=llink(rover); q:=lo_mem_max; rlink(p):=q; llink(rover):=q;@/
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null;
+rover:=q; goto restart;
+end
+
+@ @<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+while is_empty(q) do {merge node |p| with node |q|}
+ begin t:=rlink(q); tt:=llink(q);
+@^inner loop@>
+ if q=rover then rover:=t;
+ llink(t):=tt; rlink(tt):=t;@/
+ q:=q+node_size(q);
+ end;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if rlink(p)<>p then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+
+@ @<Allocate from the top...@>=
+begin node_size(p):=r-p; {store the remaining size}
+rover:=p; {start searching here next time}
+goto found;
+end
+
+@ Here we delete node |p| from the ring, and let |rover| rove around.
+
+@<Allocate entire...@>=
+begin rover:=rlink(p); t:=llink(p);
+llink(rover):=t; rlink(t):=rover;
+goto found;
+end
+
+@ Conversely, when some variable-size node |p| of size |s| is no longer needed,
+the operation |free_node(p,s)| will make its words available, by inserting
+|p| as a new empty node just before where |rover| now points.
+
+@p procedure free_node(@!p:pointer; @!s:halfword); {variable-size node
+ liberation}
+var @!q:pointer; {|llink(rover)|}
+begin node_size(p):=s; link(p):=empty_flag;
+@^inner loop@>
+q:=llink(rover); llink(p):=q; rlink(p):=rover; {set both links}
+llink(rover):=p; rlink(q):=p; {insert |p| into the ring}
+@!stat var_used:=var_used-s;@+tats@;{maintain statistics}
+end;
+
+@ Just before \.{INIMF} writes out the memory, it sorts the doubly linked
+available space list. The list is probably very short at such times, so a
+simple insertion sort is used. The smallest available location will be
+pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
+
+@p @!init procedure sort_avail; {sorts the available variable-size nodes
+ by location}
+var @!p,@!q,@!r: pointer; {indices into |mem|}
+@!old_rover:pointer; {initial |rover| setting}
+begin p:=get_node(@'10000000000); {merge adjacent free areas}
+p:=rlink(rover); rlink(rover):=max_halfword; old_rover:=rover;
+while p<>old_rover do @<Sort |p| into the list starting at |rover|
+ and advance |p| to |rlink(p)|@>;
+p:=rover;
+while rlink(p)<>max_halfword do
+ begin llink(rlink(p)):=p; p:=rlink(p);
+ end;
+rlink(p):=rover; llink(rover):=p;
+end;
+tini
+
+@ The following |while| loop is guaranteed to
+terminate, since the list that starts at
+|rover| ends with |max_halfword| during the sorting procedure.
+
+@<Sort |p|...@>=
+if p<rover then
+ begin q:=p; p:=rlink(q); rlink(q):=rover; rover:=q;
+ end
+else begin q:=rover;
+ while rlink(q)<p do q:=rlink(q);
+ r:=rlink(p); rlink(p):=rlink(q); rlink(q):=p; p:=r;
+ end
+
+@* \[11] Memory layout.
+Some areas of |mem| are dedicated to fixed usage, since static allocation is
+more efficient than dynamic allocation when we can get away with it. For
+example, locations |mem_min| to |mem_min+2| are always used to store the
+specification for null pen coordinates that are `$(0,0)$'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_min| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive.
+
+@d null_coords==mem_min {specification for pen offsets of $(0,0)$}
+@d null_pen==null_coords+3 {we will define |coord_node_size=3|}
+@d dep_head==null_pen+10 {and |pen_node_size=10|}
+@d zero_val==dep_head+2 {two words for a permanently zero value}
+@d temp_val==zero_val+2 {two words for a temporary value node}
+@d end_attr==temp_val {we use |end_attr+2| only}
+@d inf_val==end_attr+2 {and |inf_val+1| only}
+@d bad_vardef==inf_val+2 {two words for \&{vardef} error recovery}
+@d lo_mem_stat_max==bad_vardef+1 {largest statically
+ allocated word in the variable-size |mem|}
+@#
+@d sentinel==mem_top {end of sorted lists}
+@d temp_head==mem_top-1 {head of a temporary list of some kind}
+@d hold_head==mem_top-2 {head of a temporary list of another kind}
+@d hi_mem_stat_min==mem_top-2 {smallest statically allocated word in
+ the one-word |mem|}
+
+@ The following code gets the dynamic part of |mem| off to a good start,
+when \MF\ is initializing itself the slow way.
+
+@<Initialize table entries (done by \.{INIMF} only)@>=
+rover:=lo_mem_stat_max+1; {initialize the dynamic memory}
+link(rover):=empty_flag;
+node_size(rover):=1000; {which is a 1000-word available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/
+for k:=hi_mem_stat_min to mem_top do
+ mem[k]:=mem[lo_mem_max]; {clear list heads}
+avail:=null; mem_end:=mem_top;
+hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory}
+var_used:=lo_mem_stat_max+1-mem_min; dyn_used:=mem_top+1-hi_mem_min;
+ {initialize statistics}
+
+@ The procedure |flush_list(p)| frees an entire linked list of one-word
+nodes that starts at a given position, until coming to |sentinel| or a
+pointer that is not in the one-word region. Another procedure,
+|flush_node_list|, frees an entire linked list of one-word and two-word
+nodes, until coming to a |null| pointer.
+@^inner loop@>
+
+@p procedure flush_list(@!p:pointer); {makes list of single-word nodes
+ available}
+label done;
+var @!q,@!r:pointer; {list traversers}
+begin if p>=hi_mem_min then if p<>sentinel then
+ begin r:=p;
+ repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/
+ if r<hi_mem_min then goto done;
+ until r=sentinel;
+ done: {now |q| is the last node on the list}
+ link(q):=avail; avail:=p;
+ end;
+end;
+@#
+procedure flush_node_list(@!p:pointer);
+var @!q:pointer; {the node being recycled}
+begin while p<>null do
+ begin q:=p; p:=link(p);
+ if q<hi_mem_min then free_node(q,2)@+else free_avail(q);
+ end;
+end;
+
+@ If \MF\ is extended improperly, the |mem| array might get screwed up.
+For example, some pointers might be wrong, or some ``dead'' nodes might not
+have been freed when the last reference to them disappeared. Procedures
+|check_mem| and |search_mem| are available to help diagnose such
+problems. These procedures make use of two arrays called |free| and
+|was_free| that are present only if \MF's debugging routines have
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@<Glob...@>=
+@!debug @!free: packed array [mem_min..mem_max] of boolean; {free cells}
+@t\hskip1em@>@!was_free: packed array [mem_min..mem_max] of boolean;
+ {previously free cells}
+@t\hskip1em@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer;
+ {previous |mem_end|, |lo_mem_max|, and |hi_mem_min|}
+@t\hskip1em@>@!panicking:boolean; {do we want to check memory constantly?}
+gubed
+
+@ @<Set initial...@>=
+@!debug was_mem_end:=mem_min; {indicate that everything was previously free}
+was_lo_max:=mem_min; was_hi_min:=mem_max;
+panicking:=false;
+gubed
+
+@ Procedure |check_mem| makes sure that the available space lists of
+|mem| are well formed, and it optionally prints out all locations
+that are reserved now but were free the last time this procedure was called.
+
+@p @!debug procedure check_mem(@!print_locs : boolean);
+label done1,done2; {loop exits}
+var @!p,@!q,@!r:pointer; {current locations of interest in |mem|}
+@!clobbered:boolean; {is something amiss?}
+begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably
+ do this faster}
+for p:=hi_mem_min to mem_end do free[p]:=false; {ditto}
+@<Check single-word |avail| list@>;
+@<Check variable-size |avail| list@>;
+@<Check flags of unavailable nodes@>;
+@<Check the list of linear dependencies@>;
+if print_locs then @<Print newly busy locations@>;
+for p:=mem_min to lo_mem_max do was_free[p]:=free[p];
+for p:=hi_mem_min to mem_end do was_free[p]:=free[p];
+ {|was_free:=free| might be faster}
+was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min;
+end;
+gubed
+
+@ @<Check single-word...@>=
+p:=avail; q:=null; clobbered:=false;
+while p<>null do
+ begin if (p>mem_end)or(p<hi_mem_min) then clobbered:=true
+ else if free[p] then clobbered:=true;
+ if clobbered then
+ begin print_nl("AVAIL list clobbered at ");
+@.AVAIL list clobbered...@>
+ print_int(q); goto done1;
+ end;
+ free[p]:=true; q:=p; p:=link(q);
+ end;
+done1:
+
+@ @<Check variable-size...@>=
+p:=rover; q:=null; clobbered:=false;
+repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true
+ else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true;
+ if clobbered then
+ begin print_nl("Double-AVAIL list clobbered at ");
+@.Double-AVAIL list clobbered...@>
+ print_int(q); goto done2;
+ end;
+for q:=p to p+node_size(p)-1 do {mark all locations free}
+ begin if free[q] then
+ begin print_nl("Doubly free location at ");
+@.Doubly free location...@>
+ print_int(q); goto done2;
+ end;
+ free[q]:=true;
+ end;
+q:=p; p:=rlink(p);
+until p=rover;
+done2:
+
+@ @<Check flags...@>=
+p:=mem_min;
+while p<=lo_mem_max do {node |p| should not be empty}
+ begin if is_empty(p) then
+ begin print_nl("Bad flag at "); print_int(p);
+@.Bad flag...@>
+ end;
+ while (p<=lo_mem_max) and not free[p] do incr(p);
+ while (p<=lo_mem_max) and free[p] do incr(p);
+ end
+
+@ @<Print newly busy...@>=
+begin print_nl("New busy locs:");
+@.New busy locs@>
+for p:=mem_min to lo_mem_max do
+ if not free[p] and ((p>was_lo_max) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+for p:=hi_mem_min to mem_end do
+ if not free[p] and
+ ((p<was_hi_min) or (p>was_mem_end) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+end
+
+@ The |search_mem| procedure attempts to answer the question ``Who points
+to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
+that might not be of type |two_halves|. Strictly speaking, this is
+@^dirty \PASCAL@>
+undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to
+point to |p| purely by coincidence). But for debugging purposes, we want
+to rule out the places that do {\sl not\/} point to |p|, so a few false
+drops are tolerable.
+
+@p @!debug procedure search_mem(@!p:pointer); {look for pointers to |p|}
+var @!q:integer; {current position being searched}
+begin for q:=mem_min to lo_mem_max do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+for q:=hi_mem_min to mem_end do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+@<Search |eqtb| for equivalents equal to |p|@>;
+end;
+gubed
+
+@* \[12] The command codes.
+Before we can go much further, we need to define symbolic names for the internal
+code numbers that represent the various commands obeyed by \MF. These codes
+are somewhat arbitrary, but not completely so. For example,
+some codes have been made adjacent so that |case| statements in the
+program need not consider cases that are widely spaced, or so that |case|
+statements can be replaced by |if| statements. A command can begin an
+expression if and only if its code lies between |min_primary_command| and
+|max_primary_command|, inclusive. The first token of a statement that doesn't
+begin with an expression has a command code between |min_command| and
+|max_statement_command|, inclusive. The ordering of the highest-numbered
+commands (|comma<semicolon<end_group<stop|) is crucial for the parsing
+and error-recovery methods of this program.
+
+At any rate, here is the list, for future reference.
+
+@d if_test=1 {conditional text (\&{if})}
+@d fi_or_else=2 {delimiters for conditionals (\&{elseif}, \&{else}, \&{fi})}
+@d input=3 {input a source file (\&{input}, \&{endinput})}
+@d iteration=4 {iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor})}
+@d repeat_loop=5 {special command substituted for \&{endfor}}
+@d exit_test=6 {premature exit from a loop (\&{exitif})}
+@d relax=7 {do nothing (\.{\char`\\})}
+@d scan_tokens=8 {put a string into the input buffer}
+@d expand_after=9 {look ahead one token}
+@d defined_macro=10 {a macro defined by the user}
+@d min_command=defined_macro+1
+@d display_command=11 {online graphic output (\&{display})}
+@d save_command=12 {save a list of tokens (\&{save})}
+@d interim_command=13 {save an internal quantity (\&{interim})}
+@d let_command=14 {redefine a symbolic token (\&{let})}
+@d new_internal=15 {define a new internal quantity (\&{newinternal})}
+@d macro_def=16 {define a macro (\&{def}, \&{vardef}, etc.)}
+@d ship_out_command=17 {output a character (\&{shipout})}
+@d add_to_command=18 {add to edges (\&{addto})}
+@d cull_command=19 {cull and normalize edges (\&{cull})}
+@d tfm_command=20 {command for font metric info (\&{ligtable}, etc.)}
+@d protection_command=21 {set protection flag (\&{outer}, \&{inner})}
+@d show_command=22 {diagnostic output (\&{show}, \&{showvariable}, etc.)}
+@d mode_command=23 {set interaction level (\&{batchmode}, etc.)}
+@d random_seed=24 {initialize random number generator (\&{randomseed})}
+@d message_command=25 {communicate to user (\&{message}, \&{errmessage})}
+@d every_job_command=26 {designate a starting token (\&{everyjob})}
+@d delimiters=27 {define a pair of delimiters (\&{delimiters})}
+@d open_window=28 {define a window on the screen (\&{openwindow})}
+@d special_command=29 {output special info (\&{special}, \&{numspecial})}
+@d type_name=30 {declare a type (\&{numeric}, \&{pair}, etc.)}
+@d max_statement_command=type_name
+@d min_primary_command=type_name
+@d left_delimiter=31 {the left delimiter of a matching pair}
+@d begin_group=32 {beginning of a group (\&{begingroup})}
+@d nullary=33 {an operator without arguments (e.g., \&{normaldeviate})}
+@d unary=34 {an operator with one argument (e.g., \&{sqrt})}
+@d str_op=35 {convert a suffix to a string (\&{str})}
+@d cycle=36 {close a cyclic path (\&{cycle})}
+@d primary_binary=37 {binary operation taking `\&{of}' (e.g., \&{point})}
+@d capsule_token=38 {a value that has been put into a token list}
+@d string_token=39 {a string constant (e.g., |"hello"|)}
+@d internal_quantity=40 {internal numeric parameter (e.g., \&{pausing})}
+@d min_suffix_token=internal_quantity
+@d tag_token=41 {a symbolic token without a primitive meaning}
+@d numeric_token=42 {a numeric constant (e.g., \.{3.14159})}
+@d max_suffix_token=numeric_token
+@d plus_or_minus=43 {either `\.+' or `\.-'}
+@d max_primary_command=plus_or_minus {should also be |numeric_token+1|}
+@d min_tertiary_command=plus_or_minus
+@d tertiary_secondary_macro=44 {a macro defined by \&{secondarydef}}
+@d tertiary_binary=45 {an operator at the tertiary level (e.g., `\.{++}')}
+@d max_tertiary_command=tertiary_binary
+@d left_brace=46 {the operator `\.{\char`\{}'}
+@d min_expression_command=left_brace
+@d path_join=47 {the operator `\.{..}'}
+@d ampersand=48 {the operator `\.\&'}
+@d expression_tertiary_macro=49 {a macro defined by \&{tertiarydef}}
+@d expression_binary=50 {an operator at the expression level (e.g., `\.<')}
+@d equals=51 {the operator `\.='}
+@d max_expression_command=equals
+@d and_command=52 {the operator `\&{and}'}
+@d min_secondary_command=and_command
+@d secondary_primary_macro=53 {a macro defined by \&{primarydef}}
+@d slash=54 {the operator `\./'}
+@d secondary_binary=55 {an operator at the binary level (e.g., \&{shifted})}
+@d max_secondary_command=secondary_binary
+@d param_type=56 {type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.)}
+@d controls=57 {specify control points explicitly (\&{controls})}
+@d tension=58 {specify tension between knots (\&{tension})}
+@d at_least=59 {bounded tension value (\&{atleast})}
+@d curl_command=60 {specify curl at an end knot (\&{curl})}
+@d macro_special=61 {special macro operators (\&{quote}, \.{\#\AT!}, etc.)}
+@d right_delimiter=62 {the right delimiter of a matching pair}
+@d left_bracket=63 {the operator `\.['}
+@d right_bracket=64 {the operator `\.]'}
+@d right_brace=65 {the operator `\.{\char`\}}'}
+@d with_option=66 {option for filling (\&{withpen}, \&{withweight})}
+@d cull_op=67 {the operator `\&{keeping}' or `\&{dropping}'}
+@d thing_to_add=68
+ {variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also})}
+@d of_token=69 {the operator `\&{of}'}
+@d from_token=70 {the operator `\&{from}'}
+@d to_token=71 {the operator `\&{to}'}
+@d at_token=72 {the operator `\&{at}'}
+@d in_window=73 {the operator `\&{inwindow}'}
+@d step_token=74 {the operator `\&{step}'}
+@d until_token=75 {the operator `\&{until}'}
+@d lig_kern_token=76
+ {the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc.}
+@d assignment=77 {the operator `\.{:=}'}
+@d skip_to=78 {the operation `\&{skipto}'}
+@d bchar_label=79 {the operator `\.{\char'174\char'174:}'}
+@d double_colon=80 {the operator `\.{::}'}
+@d colon=81 {the operator `\.:'}
+@#
+@d comma=82 {the operator `\.,', must be |colon+1|}
+@d end_of_statement==cur_cmd>comma
+@d semicolon=83 {the operator `\.;', must be |comma+1|}
+@d end_group=84 {end a group (\&{endgroup}), must be |semicolon+1|}
+@d stop=85 {end a job (\&{end}, \&{dump}), must be |end_group+1|}
+@d max_command_code=stop
+@d outer_tag=max_command_code+1 {protection code added to command code}
+
+@<Types...@>=
+@!command_code=1..max_command_code;
+
+@ Variables and capsules in \MF\ have a variety of ``types,''
+distinguished by the following code numbers:
+
+@d undefined=0 {no type has been declared}
+@d unknown_tag=1 {this constant is added to certain type codes below}
+@d vacuous=1 {no expression was present}
+@d boolean_type=2 {\&{boolean} with a known value}
+@d unknown_boolean=boolean_type+unknown_tag
+@d string_type=4 {\&{string} with a known value}
+@d unknown_string=string_type+unknown_tag
+@d pen_type=6 {\&{pen} with a known value}
+@d unknown_pen=pen_type+unknown_tag
+@d future_pen=8 {subexpression that will become a \&{pen} at a higher level}
+@d path_type=9 {\&{path} with a known value}
+@d unknown_path=path_type+unknown_tag
+@d picture_type=11 {\&{picture} with a known value}
+@d unknown_picture=picture_type+unknown_tag
+@d transform_type=13 {\&{transform} variable or capsule}
+@d pair_type=14 {\&{pair} variable or capsule}
+@d numeric_type=15 {variable that has been declared \&{numeric} but not used}
+@d known=16 {\&{numeric} with a known value}
+@d dependent=17 {a linear combination with |fraction| coefficients}
+@d proto_dependent=18 {a linear combination with |scaled| coefficients}
+@d independent=19 {\&{numeric} with unknown value}
+@d token_list=20 {variable name or suffix argument or text argument}
+@d structured=21 {variable with subscripts and attributes}
+@d unsuffixed_macro=22 {variable defined with \&{vardef} but no \.{\AT!\#}}
+@d suffixed_macro=23 {variable defined with \&{vardef} and \.{\AT!\#}}
+@#
+@d unknown_types==unknown_boolean,unknown_string,
+ unknown_pen,unknown_picture,unknown_path
+
+@<Basic printing procedures@>=
+procedure print_type(@!t:small_number);
+begin case t of
+vacuous:print("vacuous");
+boolean_type:print("boolean");
+unknown_boolean:print("unknown boolean");
+string_type:print("string");
+unknown_string:print("unknown string");
+pen_type:print("pen");
+unknown_pen:print("unknown pen");
+future_pen:print("future pen");
+path_type:print("path");
+unknown_path:print("unknown path");
+picture_type:print("picture");
+unknown_picture:print("unknown picture");
+transform_type:print("transform");
+pair_type:print("pair");
+known:print("known numeric");
+dependent:print("dependent");
+proto_dependent:print("proto-dependent");
+numeric_type:print("numeric");
+independent:print("independent");
+token_list:print("token list");
+structured:print("structured");
+unsuffixed_macro:print("unsuffixed macro");
+suffixed_macro:print("suffixed macro");
+othercases print("undefined")
+endcases;
+end;
+
+@ Values inside \MF\ are stored in two-word nodes that have a |name_type|
+as well as a |type|. The possibilities for |name_type| are defined
+here; they will be explained in more detail later.
+
+@d root=0 {|name_type| at the top level of a variable}
+@d saved_root=1 {same, when the variable has been saved}
+@d structured_root=2 {|name_type| where a |structured| branch occurs}
+@d subscr=3 {|name_type| in a subscript node}
+@d attr=4 {|name_type| in an attribute node}
+@d x_part_sector=5 {|name_type| in the \&{xpart} of a node}
+@d y_part_sector=6 {|name_type| in the \&{ypart} of a node}
+@d xx_part_sector=7 {|name_type| in the \&{xxpart} of a node}
+@d xy_part_sector=8 {|name_type| in the \&{xypart} of a node}
+@d yx_part_sector=9 {|name_type| in the \&{yxpart} of a node}
+@d yy_part_sector=10 {|name_type| in the \&{yypart} of a node}
+@d capsule=11 {|name_type| in stashed-away subexpressions}
+@d token=12 {|name_type| in a numeric token or string token}
+
+@ Primitive operations that produce values have a secondary identification
+code in addition to their command code; it's something like genera and species.
+For example, `\.*' has the command code |primary_binary|, and its
+secondary identification is |times|. The secondary codes start at 30 so that
+they don't overlap with the type codes; some type codes (e.g., |string_type|)
+are used as operators as well as type identifications.
+
+@d true_code=30 {operation code for \.{true}}
+@d false_code=31 {operation code for \.{false}}
+@d null_picture_code=32 {operation code for \.{nullpicture}}
+@d null_pen_code=33 {operation code for \.{nullpen}}
+@d job_name_op=34 {operation code for \.{jobname}}
+@d read_string_op=35 {operation code for \.{readstring}}
+@d pen_circle=36 {operation code for \.{pencircle}}
+@d normal_deviate=37 {operation code for \.{normaldeviate}}
+@d odd_op=38 {operation code for \.{odd}}
+@d known_op=39 {operation code for \.{known}}
+@d unknown_op=40 {operation code for \.{unknown}}
+@d not_op=41 {operation code for \.{not}}
+@d decimal=42 {operation code for \.{decimal}}
+@d reverse=43 {operation code for \.{reverse}}
+@d make_path_op=44 {operation code for \.{makepath}}
+@d make_pen_op=45 {operation code for \.{makepen}}
+@d total_weight_op=46 {operation code for \.{totalweight}}
+@d oct_op=47 {operation code for \.{oct}}
+@d hex_op=48 {operation code for \.{hex}}
+@d ASCII_op=49 {operation code for \.{ASCII}}
+@d char_op=50 {operation code for \.{char}}
+@d length_op=51 {operation code for \.{length}}
+@d turning_op=52 {operation code for \.{turningnumber}}
+@d x_part=53 {operation code for \.{xpart}}
+@d y_part=54 {operation code for \.{ypart}}
+@d xx_part=55 {operation code for \.{xxpart}}
+@d xy_part=56 {operation code for \.{xypart}}
+@d yx_part=57 {operation code for \.{yxpart}}
+@d yy_part=58 {operation code for \.{yypart}}
+@d sqrt_op=59 {operation code for \.{sqrt}}
+@d m_exp_op=60 {operation code for \.{mexp}}
+@d m_log_op=61 {operation code for \.{mlog}}
+@d sin_d_op=62 {operation code for \.{sind}}
+@d cos_d_op=63 {operation code for \.{cosd}}
+@d floor_op=64 {operation code for \.{floor}}
+@d uniform_deviate=65 {operation code for \.{uniformdeviate}}
+@d char_exists_op=66 {operation code for \.{charexists}}
+@d angle_op=67 {operation code for \.{angle}}
+@d cycle_op=68 {operation code for \.{cycle}}
+@d plus=69 {operation code for \.+}
+@d minus=70 {operation code for \.-}
+@d times=71 {operation code for \.*}
+@d over=72 {operation code for \./}
+@d pythag_add=73 {operation code for \.{++}}
+@d pythag_sub=74 {operation code for \.{+-+}}
+@d or_op=75 {operation code for \.{or}}
+@d and_op=76 {operation code for \.{and}}
+@d less_than=77 {operation code for \.<}
+@d less_or_equal=78 {operation code for \.{<=}}
+@d greater_than=79 {operation code for \.>}
+@d greater_or_equal=80 {operation code for \.{>=}}
+@d equal_to=81 {operation code for \.=}
+@d unequal_to=82 {operation code for \.{<>}}
+@d concatenate=83 {operation code for \.\&}
+@d rotated_by=84 {operation code for \.{rotated}}
+@d slanted_by=85 {operation code for \.{slanted}}
+@d scaled_by=86 {operation code for \.{scaled}}
+@d shifted_by=87 {operation code for \.{shifted}}
+@d transformed_by=88 {operation code for \.{transformed}}
+@d x_scaled=89 {operation code for \.{xscaled}}
+@d y_scaled=90 {operation code for \.{yscaled}}
+@d z_scaled=91 {operation code for \.{zscaled}}
+@d intersect=92 {operation code for \.{intersectiontimes}}
+@d double_dot=93 {operation code for improper \.{..}}
+@d substring_of=94 {operation code for \.{substring}}
+@d min_of=substring_of
+@d subpath_of=95 {operation code for \.{subpath}}
+@d direction_time_of=96 {operation code for \.{directiontime}}
+@d point_of=97 {operation code for \.{point}}
+@d precontrol_of=98 {operation code for \.{precontrol}}
+@d postcontrol_of=99 {operation code for \.{postcontrol}}
+@d pen_offset_of=100 {operation code for \.{penoffset}}
+
+@p procedure print_op(@!c:quarterword);
+begin if c<=numeric_type then print_type(c)
+else case c of
+true_code:print("true");
+false_code:print("false");
+null_picture_code:print("nullpicture");
+null_pen_code:print("nullpen");
+job_name_op:print("jobname");
+read_string_op:print("readstring");
+pen_circle:print("pencircle");
+normal_deviate:print("normaldeviate");
+odd_op:print("odd");
+known_op:print("known");
+unknown_op:print("unknown");
+not_op:print("not");
+decimal:print("decimal");
+reverse:print("reverse");
+make_path_op:print("makepath");
+make_pen_op:print("makepen");
+total_weight_op:print("totalweight");
+oct_op:print("oct");
+hex_op:print("hex");
+ASCII_op:print("ASCII");
+char_op:print("char");
+length_op:print("length");
+turning_op:print("turningnumber");
+x_part:print("xpart");
+y_part:print("ypart");
+xx_part:print("xxpart");
+xy_part:print("xypart");
+yx_part:print("yxpart");
+yy_part:print("yypart");
+sqrt_op:print("sqrt");
+m_exp_op:print("mexp");
+m_log_op:print("mlog");
+sin_d_op:print("sind");
+cos_d_op:print("cosd");
+floor_op:print("floor");
+uniform_deviate:print("uniformdeviate");
+char_exists_op:print("charexists");
+angle_op:print("angle");
+cycle_op:print("cycle");
+plus:print_char("+");
+minus:print_char("-");
+times:print_char("*");
+over:print_char("/");
+pythag_add:print("++");
+pythag_sub:print("+-+");
+or_op:print("or");
+and_op:print("and");
+less_than:print_char("<");
+less_or_equal:print("<=");
+greater_than:print_char(">");
+greater_or_equal:print(">=");
+equal_to:print_char("=");
+unequal_to:print("<>");
+concatenate:print("&");
+rotated_by:print("rotated");
+slanted_by:print("slanted");
+scaled_by:print("scaled");
+shifted_by:print("shifted");
+transformed_by:print("transformed");
+x_scaled:print("xscaled");
+y_scaled:print("yscaled");
+z_scaled:print("zscaled");
+intersect:print("intersectiontimes");
+substring_of:print("substring");
+subpath_of:print("subpath");
+direction_time_of:print("directiontime");
+point_of:print("point");
+precontrol_of:print("precontrol");
+postcontrol_of:print("postcontrol");
+pen_offset_of:print("penoffset");
+othercases print("..")
+endcases;
+end;
+
+@ \MF\ also has a bunch of internal parameters that a user might want to
+fuss with. Every such parameter has an identifying code number, defined here.
+
+@d tracing_titles=1 {show titles online when they appear}
+@d tracing_equations=2 {show each variable when it becomes known}
+@d tracing_capsules=3 {show capsules too}
+@d tracing_choices=4 {show the control points chosen for paths}
+@d tracing_specs=5 {show subdivision of paths into octants before digitizing}
+@d tracing_pens=6 {show details of pens that are made}
+@d tracing_commands=7 {show commands and operations before they are performed}
+@d tracing_restores=8 {show when a variable or internal is restored}
+@d tracing_macros=9 {show macros before they are expanded}
+@d tracing_edges=10 {show digitized edges as they are computed}
+@d tracing_output=11 {show digitized edges as they are output}
+@d tracing_stats=12 {show memory usage at end of job}
+@d tracing_online=13 {show long diagnostics on terminal and in the log file}
+@d year=14 {the current year (e.g., 1984)}
+@d month=15 {the current month (e.g., 3 $\equiv$ March)}
+@d day=16 {the current day of the month}
+@d time=17 {the number of minutes past midnight when this job started}
+@d char_code=18 {the number of the next character to be output}
+@d char_ext=19 {the extension code of the next character to be output}
+@d char_wd=20 {the width of the next character to be output}
+@d char_ht=21 {the height of the next character to be output}
+@d char_dp=22 {the depth of the next character to be output}
+@d char_ic=23 {the italic correction of the next character to be output}
+@d char_dx=24 {the device's $x$ movement for the next character, in pixels}
+@d char_dy=25 {the device's $y$ movement for the next character, in pixels}
+@d design_size=26 {the unit of measure used for |char_wd..char_ic|, in points}
+@d hppp=27 {the number of horizontal pixels per point}
+@d vppp=28 {the number of vertical pixels per point}
+@d x_offset=29 {horizontal displacement of shipped-out characters}
+@d y_offset=30 {vertical displacement of shipped-out characters}
+@d pausing=31 {positive to display lines on the terminal before they are read}
+@d showstopping=32 {positive to stop after each \&{show} command}
+@d fontmaking=33 {positive if font metric output is to be produced}
+@d proofing=34 {positive for proof mode, negative to suppress output}
+@d smoothing=35 {positive if moves are to be ``smoothed''}
+@d autorounding=36 {controls path modification to ``good'' points}
+@d granularity=37 {autorounding uses this pixel size}
+@d fillin=38 {extra darkness of diagonal lines}
+@d turning_check=39 {controls reorientation of clockwise paths}
+@d warning_check=40 {controls error message when variable value is large}
+@d boundary_char=41 {the right boundary character for ligatures}
+@d max_given_internal=41
+
+@<Glob...@>=
+@!internal:array[1..max_internal] of scaled;
+ {the values of internal quantities}
+@!int_name:array[1..max_internal] of str_number;
+ {their names}
+@!int_ptr:max_given_internal..max_internal;
+ {the maximum internal quantity defined so far}
+
+@ @<Set init...@>=
+for k:=1 to max_given_internal do internal[k]:=0;
+int_ptr:=max_given_internal;
+
+@ The symbolic names for internal quantities are put into \MF's hash table
+by using a routine called |primitive|, which will be defined later. Let us
+enter them now, so that we don't have to list all those names again
+anywhere else.
+
+@<Put each of \MF's primitives into the hash table@>=
+primitive("tracingtitles",internal_quantity,tracing_titles);@/
+@!@:tracingtitles_}{\&{tracingtitles} primitive@>
+primitive("tracingequations",internal_quantity,tracing_equations);@/
+@!@:tracing_equations_}{\&{tracingequations} primitive@>
+primitive("tracingcapsules",internal_quantity,tracing_capsules);@/
+@!@:tracing_capsules_}{\&{tracingcapsules} primitive@>
+primitive("tracingchoices",internal_quantity,tracing_choices);@/
+@!@:tracing_choices_}{\&{tracingchoices} primitive@>
+primitive("tracingspecs",internal_quantity,tracing_specs);@/
+@!@:tracing_specs_}{\&{tracingspecs} primitive@>
+primitive("tracingpens",internal_quantity,tracing_pens);@/
+@!@:tracing_pens_}{\&{tracingpens} primitive@>
+primitive("tracingcommands",internal_quantity,tracing_commands);@/
+@!@:tracing_commands_}{\&{tracingcommands} primitive@>
+primitive("tracingrestores",internal_quantity,tracing_restores);@/
+@!@:tracing_restores_}{\&{tracingrestores} primitive@>
+primitive("tracingmacros",internal_quantity,tracing_macros);@/
+@!@:tracing_macros_}{\&{tracingmacros} primitive@>
+primitive("tracingedges",internal_quantity,tracing_edges);@/
+@!@:tracing_edges_}{\&{tracingedges} primitive@>
+primitive("tracingoutput",internal_quantity,tracing_output);@/
+@!@:tracing_output_}{\&{tracingoutput} primitive@>
+primitive("tracingstats",internal_quantity,tracing_stats);@/
+@!@:tracing_stats_}{\&{tracingstats} primitive@>
+primitive("tracingonline",internal_quantity,tracing_online);@/
+@!@:tracing_online_}{\&{tracingonline} primitive@>
+primitive("year",internal_quantity,year);@/
+@!@:year_}{\&{year} primitive@>
+primitive("month",internal_quantity,month);@/
+@!@:month_}{\&{month} primitive@>
+primitive("day",internal_quantity,day);@/
+@!@:day_}{\&{day} primitive@>
+primitive("time",internal_quantity,time);@/
+@!@:time_}{\&{time} primitive@>
+primitive("charcode",internal_quantity,char_code);@/
+@!@:char_code_}{\&{charcode} primitive@>
+primitive("charext",internal_quantity,char_ext);@/
+@!@:char_ext_}{\&{charext} primitive@>
+primitive("charwd",internal_quantity,char_wd);@/
+@!@:char_wd_}{\&{charwd} primitive@>
+primitive("charht",internal_quantity,char_ht);@/
+@!@:char_ht_}{\&{charht} primitive@>
+primitive("chardp",internal_quantity,char_dp);@/
+@!@:char_dp_}{\&{chardp} primitive@>
+primitive("charic",internal_quantity,char_ic);@/
+@!@:char_ic_}{\&{charic} primitive@>
+primitive("chardx",internal_quantity,char_dx);@/
+@!@:char_dx_}{\&{chardx} primitive@>
+primitive("chardy",internal_quantity,char_dy);@/
+@!@:char_dy_}{\&{chardy} primitive@>
+primitive("designsize",internal_quantity,design_size);@/
+@!@:design_size_}{\&{designsize} primitive@>
+primitive("hppp",internal_quantity,hppp);@/
+@!@:hppp_}{\&{hppp} primitive@>
+primitive("vppp",internal_quantity,vppp);@/
+@!@:vppp_}{\&{vppp} primitive@>
+primitive("xoffset",internal_quantity,x_offset);@/
+@!@:x_offset_}{\&{xoffset} primitive@>
+primitive("yoffset",internal_quantity,y_offset);@/
+@!@:y_offset_}{\&{yoffset} primitive@>
+primitive("pausing",internal_quantity,pausing);@/
+@!@:pausing_}{\&{pausing} primitive@>
+primitive("showstopping",internal_quantity,showstopping);@/
+@!@:showstopping_}{\&{showstopping} primitive@>
+primitive("fontmaking",internal_quantity,fontmaking);@/
+@!@:fontmaking_}{\&{fontmaking} primitive@>
+primitive("proofing",internal_quantity,proofing);@/
+@!@:proofing_}{\&{proofing} primitive@>
+primitive("smoothing",internal_quantity,smoothing);@/
+@!@:smoothing_}{\&{smoothing} primitive@>
+primitive("autorounding",internal_quantity,autorounding);@/
+@!@:autorounding_}{\&{autorounding} primitive@>
+primitive("granularity",internal_quantity,granularity);@/
+@!@:granularity_}{\&{granularity} primitive@>
+primitive("fillin",internal_quantity,fillin);@/
+@!@:fillin_}{\&{fillin} primitive@>
+primitive("turningcheck",internal_quantity,turning_check);@/
+@!@:turning_check_}{\&{turningcheck} primitive@>
+primitive("warningcheck",internal_quantity,warning_check);@/
+@!@:warning_check_}{\&{warningcheck} primitive@>
+primitive("boundarychar",internal_quantity,boundary_char);@/
+@!@:boundary_char_}{\&{boundarychar} primitive@>
+
+@ Well, we do have to list the names one more time, for use in symbolic
+printouts.
+
+@<Initialize table...@>=
+int_name[tracing_titles]:="tracingtitles";
+int_name[tracing_equations]:="tracingequations";
+int_name[tracing_capsules]:="tracingcapsules";
+int_name[tracing_choices]:="tracingchoices";
+int_name[tracing_specs]:="tracingspecs";
+int_name[tracing_pens]:="tracingpens";
+int_name[tracing_commands]:="tracingcommands";
+int_name[tracing_restores]:="tracingrestores";
+int_name[tracing_macros]:="tracingmacros";
+int_name[tracing_edges]:="tracingedges";
+int_name[tracing_output]:="tracingoutput";
+int_name[tracing_stats]:="tracingstats";
+int_name[tracing_online]:="tracingonline";
+int_name[year]:="year";
+int_name[month]:="month";
+int_name[day]:="day";
+int_name[time]:="time";
+int_name[char_code]:="charcode";
+int_name[char_ext]:="charext";
+int_name[char_wd]:="charwd";
+int_name[char_ht]:="charht";
+int_name[char_dp]:="chardp";
+int_name[char_ic]:="charic";
+int_name[char_dx]:="chardx";
+int_name[char_dy]:="chardy";
+int_name[design_size]:="designsize";
+int_name[hppp]:="hppp";
+int_name[vppp]:="vppp";
+int_name[x_offset]:="xoffset";
+int_name[y_offset]:="yoffset";
+int_name[pausing]:="pausing";
+int_name[showstopping]:="showstopping";
+int_name[fontmaking]:="fontmaking";
+int_name[proofing]:="proofing";
+int_name[smoothing]:="smoothing";
+int_name[autorounding]:="autorounding";
+int_name[granularity]:="granularity";
+int_name[fillin]:="fillin";
+int_name[turning_check]:="turningcheck";
+int_name[warning_check]:="warningcheck";
+int_name[boundary_char]:="boundarychar";
+
+@ The following procedure, which is called just before \MF\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+Note that the values are |scaled| integers. Hence \MF\ can no longer
+be used after the year 32767.
+
+@p procedure fix_date_and_time;
+begin internal[time]:=12*60*unity; {minutes since midnight}
+internal[day]:=4*unity; {fourth day of the month}
+internal[month]:=7*unity; {seventh month of the year}
+internal[year]:=1776*unity; {Anno Domini}
+end;
+
+@ \MF\ is occasionally supposed to print diagnostic information that
+goes only into the transcript file, unless |tracing_online| is positive.
+Now that we have defined |tracing_online| we can define
+two routines that adjust the destination of print commands:
+
+@<Basic printing...@>=
+procedure begin_diagnostic; {prepare to do some tracing}
+begin old_setting:=selector;
+if(internal[tracing_online]<=0)and(selector=term_and_log) then
+ begin decr(selector);
+ if history=spotless then history:=warning_issued;
+ end;
+end;
+@#
+procedure end_diagnostic(@!blank_line:boolean);
+ {restore proper conditions after tracing}
+begin print_nl("");
+if blank_line then print_ln;
+selector:=old_setting;
+end;
+
+@ Of course we had better declare another global variable, if the previous
+routines are going to work.
+
+@<Glob...@>=
+@!old_setting:0..max_selector;
+
+@ We will occasionally use |begin_diagnostic| in connection with line-number
+printing, as follows. (The parameter |s| is typically |"Path"| or
+|"Cycle spec"|, etc.)
+
+@<Basic printing...@>=
+procedure print_diagnostic(@!s,@!t:str_number;@!nuline:boolean);
+begin begin_diagnostic;
+if nuline then print_nl(s)@+else print(s);
+print(" at line "); print_int(line);
+print(t); print_char(":");
+end;
+
+@ The 256 |ASCII_code| characters are grouped into classes by means of
+the |char_class| table. Individual class numbers have no semantic
+or syntactic significance, except in a few instances defined here.
+There's also |max_class|, which can be used as a basis for additional
+class numbers in nonstandard extensions of \MF.
+
+@d digit_class=0 {the class number of \.{0123456789}}
+@d period_class=1 {the class number of `\..'}
+@d space_class=2 {the class number of spaces and nonstandard characters}
+@d percent_class=3 {the class number of `\.\%'}
+@d string_class=4 {the class number of `\."'}
+@d right_paren_class=8 {the class number of `\.)'}
+@d isolated_classes==5,6,7,8 {characters that make length-one tokens only}
+@d letter_class=9 {letters and the underline character}
+@d left_bracket_class=17 {`\.['}
+@d right_bracket_class=18 {`\.]'}
+@d invalid_class=20 {bad character in the input}
+@d max_class=20 {the largest class number}
+
+@<Glob...@>=
+@!char_class:array[ASCII_code] of 0..max_class; {the class numbers}
+
+@ If changes are made to accommodate non-ASCII character sets, they should
+follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}.
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+@^system dependencies@>
+
+@<Set init...@>=
+for k:="0" to "9" do char_class[k]:=digit_class;
+char_class["."]:=period_class;
+char_class[" "]:=space_class;
+char_class["%"]:=percent_class;
+char_class[""""]:=string_class;@/
+char_class[","]:=5;
+char_class[";"]:=6;
+char_class["("]:=7;
+char_class[")"]:=right_paren_class;
+for k:="A" to "Z" do char_class[k]:=letter_class;
+for k:="a" to "z" do char_class[k]:=letter_class;
+char_class["_"]:=letter_class;@/
+char_class["<"]:=10;
+char_class["="]:=10;
+char_class[">"]:=10;
+char_class[":"]:=10;
+char_class["|"]:=10;@/
+char_class["`"]:=11;
+char_class["'"]:=11;@/
+char_class["+"]:=12;
+char_class["-"]:=12;@/
+char_class["/"]:=13;
+char_class["*"]:=13;
+char_class["\"]:=13;@/
+char_class["!"]:=14;
+char_class["?"]:=14;@/
+char_class["#"]:=15;
+char_class["&"]:=15;
+char_class["@@"]:=15;
+char_class["$"]:=15;@/
+char_class["^"]:=16;
+char_class["~"]:=16;@/
+char_class["["]:=left_bracket_class;
+char_class["]"]:=right_bracket_class;@/
+char_class["{"]:=19;
+char_class["}"]:=19;@/
+for k:=0 to " "-1 do char_class[k]:=invalid_class;
+for k:=127 to 255 do char_class[k]:=invalid_class;
+
+@* \[13] The hash table.
+Symbolic tokens are stored and retrieved by means of a fairly standard hash
+table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
+in {\sl The Art of Computer Programming\/}). Once a symbolic token enters the
+table, it is never removed.
+
+The actual sequence of characters forming a symbolic token is
+stored in the |str_pool| array together with all the other strings. An
+auxiliary array |hash| consists of items with two halfword fields per
+word. The first of these, called |next(p)|, points to the next identifier
+belonging to the same coalesced list as the identifier corresponding to~|p|;
+and the other, called |text(p)|, points to the |str_start| entry for
+|p|'s identifier. If position~|p| of the hash table is empty, we have
+|text(p)=0|; if position |p| is either empty or the end of a coalesced
+hash list, we have |next(p)=0|.
+
+An auxiliary pointer variable called |hash_used| is maintained in such a
+way that all locations |p>=hash_used| are nonempty. The global variable
+|st_count| tells how many symbolic tokens have been defined, if statistics
+are being kept.
+
+The first 256 locations of |hash| are reserved for symbols of length one.
+
+There's a parallel array called |eqtb| that contains the current equivalent
+values of each symbolic token. The entries of this array consist of
+two halfwords called |eq_type| (a command code) and |equiv| (a secondary
+piece of information that qualifies the |eq_type|).
+
+@d next(#) == hash[#].lh {link for coalesced lists}
+@d text(#) == hash[#].rh {string number for symbolic token name}
+@d eq_type(#) == eqtb[#].lh {the current ``meaning'' of a symbolic token}
+@d equiv(#) == eqtb[#].rh {parametric part of a token's meaning}
+@d hash_base=257 {hashing actually starts here}
+@d hash_is_full == (hash_used=hash_base) {are all positions occupied?}
+
+@<Glob...@>=
+@!hash_used:pointer; {allocation pointer for |hash|}
+@!st_count:integer; {total number of known identifiers}
+
+@ Certain entries in the hash table are ``frozen'' and not redefinable,
+since they are used in error recovery.
+
+@d hash_top==hash_base+hash_size {the first location of the frozen area}
+@d frozen_inaccessible==hash_top {|hash| location to protect the frozen area}
+@d frozen_repeat_loop==hash_top+1 {|hash| location of a loop-repeat token}
+@d frozen_right_delimiter==hash_top+2 {|hash| location of a permanent `\.)'}
+@d frozen_left_bracket==hash_top+3 {|hash| location of a permanent `\.['}
+@d frozen_slash==hash_top+4 {|hash| location of a permanent `\./'}
+@d frozen_colon==hash_top+5 {|hash| location of a permanent `\.:'}
+@d frozen_semicolon==hash_top+6 {|hash| location of a permanent `\.;'}
+@d frozen_end_for==hash_top+7 {|hash| location of a permanent \&{endfor}}
+@d frozen_end_def==hash_top+8 {|hash| location of a permanent \&{enddef}}
+@d frozen_fi==hash_top+9 {|hash| location of a permanent \&{fi}}
+@d frozen_end_group==hash_top+10
+ {|hash| location of a permanent `\.{endgroup}'}
+@d frozen_bad_vardef==hash_top+11 {|hash| location of `\.{a bad variable}'}
+@d frozen_undefined==hash_top+12 {|hash| location that never gets defined}
+@d hash_end==hash_top+12 {the actual size of the |hash| and |eqtb| arrays}
+
+@<Glob...@>=
+@!hash: array[1..hash_end] of two_halves; {the hash table}
+@!eqtb: array[1..hash_end] of two_halves; {the equivalents}
+
+@ @<Set init...@>=
+next(1):=0; text(1):=0; eq_type(1):=tag_token; equiv(1):=null;
+for k:=2 to hash_end do
+ begin hash[k]:=hash[1]; eqtb[k]:=eqtb[1];
+ end;
+
+@ @<Initialize table entries...@>=
+hash_used:=frozen_inaccessible; {nothing is used}
+st_count:=0;@/
+text(frozen_bad_vardef):="a bad variable";
+text(frozen_fi):="fi";
+text(frozen_end_group):="endgroup";
+text(frozen_end_def):="enddef";
+text(frozen_end_for):="endfor";@/
+text(frozen_semicolon):=";";
+text(frozen_colon):=":";
+text(frozen_slash):="/";
+text(frozen_left_bracket):="[";
+text(frozen_right_delimiter):=")";@/
+text(frozen_inaccessible):=" INACCESSIBLE";@/
+eq_type(frozen_right_delimiter):=right_delimiter;
+
+@ @<Check the ``constant'' values...@>=
+if hash_end+max_internal>max_halfword then bad:=21;
+
+@ Here is the subroutine that searches the hash table for an identifier
+that matches a given string of length~|l| appearing in |buffer[j..
+(j+l-1)]|. If the identifier is not found, it is inserted; hence it
+will always be found, and the corresponding hash table address
+will be returned.
+
+@p function id_lookup(@!j,@!l:integer):pointer; {search the hash table}
+label found; {go here when you've found it}
+var @!h:integer; {hash code}
+@!p:pointer; {index in |hash| array}
+@!k:pointer; {index in |buffer| array}
+begin if l=1 then @<Treat special case of length 1 and |goto found|@>;
+@<Compute the hash code |h|@>;
+p:=h+hash_base; {we start searching here; note that |0<=h<hash_prime|}
+loop@+ begin if text(p)>0 then if length(text(p))=l then
+ if str_eq_buf(text(p),j) then goto found;
+ if next(p)=0 then
+ @<Insert a new symbolic token after |p|, then
+ make |p| point to it and |goto found|@>;
+ p:=next(p);
+ end;
+found: id_lookup:=p;
+end;
+
+@ @<Treat special case of length 1...@>=
+begin p:=buffer[j]+1; text(p):=p-1; goto found;
+end
+
+@ @<Insert a new symbolic...@>=
+begin if text(p)>0 then
+ begin repeat if hash_is_full then
+ overflow("hash size",hash_size);
+@:METAFONT capacity exceeded hash size}{\quad hash size@>
+ decr(hash_used);
+ until text(hash_used)=0; {search for an empty location in |hash|}
+ next(p):=hash_used; p:=hash_used;
+ end;
+str_room(l);
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string; str_ref[text(p)]:=max_str_ref;
+@!stat incr(st_count);@+tats@;@/
+goto found;
+end
+
+@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
+should be a prime number. The theory of hashing tells us to expect fewer
+than two table probes, on the average, when the search is successful.
+[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
+@^Vitter, Jeffrey Scott@>
+
+@<Compute the hash code |h|@>=
+h:=buffer[j];
+for k:=j+1 to j+l-1 do
+ begin h:=h+h+buffer[k];
+ while h>=hash_prime do h:=h-hash_prime;
+ end
+
+@ @<Search |eqtb| for equivalents equal to |p|@>=
+for q:=1 to hash_end do
+ begin if equiv(q)=p then
+ begin print_nl("EQUIV("); print_int(q); print_char(")");
+ end;
+ end
+
+@ We need to put \MF's ``primitive'' symbolic tokens into the hash
+table, together with their command code (which will be the |eq_type|)
+and an operand (which will be the |equiv|). The |primitive| procedure
+does this, in a way that no \MF\ user can. The global value |cur_sym|
+contains the new |eqtb| pointer after |primitive| has acted.
+
+@p @!init procedure primitive(@!s:str_number;@!c:halfword;@!o:halfword);
+var @!k:pool_pointer; {index into |str_pool|}
+@!j:small_number; {index into |buffer|}
+@!l:small_number; {length of the string}
+begin k:=str_start[s]; l:=str_start[s+1]-k;
+ {we will move |s| into the (empty) |buffer|}
+for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
+cur_sym:=id_lookup(0,l);@/
+if s>=256 then {we don't want to have the string twice}
+ begin flush_string(str_ptr-1); text(cur_sym):=s;
+ end;
+eq_type(cur_sym):=c; equiv(cur_sym):=o;
+end;
+tini
+
+@ Many of \MF's primitives need no |equiv|, since they are identifiable
+by their |eq_type| alone. These primitives are loaded into the hash table
+as follows:
+
+@<Put each of \MF's primitives into the hash table@>=
+primitive("..",path_join,0);@/
+@!@:.._}{\.{..} primitive@>
+primitive("[",left_bracket,0); eqtb[frozen_left_bracket]:=eqtb[cur_sym];@/
+@!@:[ }{\.{[} primitive@>
+primitive("]",right_bracket,0);@/
+@!@:] }{\.{]} primitive@>
+primitive("}",right_brace,0);@/
+@!@:]]}{\.{\char`\}} primitive@>
+primitive("{",left_brace,0);@/
+@!@:][}{\.{\char`\{} primitive@>
+primitive(":",colon,0); eqtb[frozen_colon]:=eqtb[cur_sym];@/
+@!@:: }{\.{:} primitive@>
+primitive("::",double_colon,0);@/
+@!@::: }{\.{::} primitive@>
+primitive("||:",bchar_label,0);@/
+@!@:::: }{\.{\char'174\char'174:} primitive@>
+primitive(":=",assignment,0);@/
+@!@::=_}{\.{:=} primitive@>
+primitive(",",comma,0);@/
+@!@:, }{\., primitive@>
+primitive(";",semicolon,0); eqtb[frozen_semicolon]:=eqtb[cur_sym];@/
+@!@:; }{\.; primitive@>
+primitive("\",relax,0);@/
+@!@:]]\\}{\.{\char`\\} primitive@>
+@#
+primitive("addto",add_to_command,0);@/
+@!@:add_to_}{\&{addto} primitive@>
+primitive("at",at_token,0);@/
+@!@:at_}{\&{at} primitive@>
+primitive("atleast",at_least,0);@/
+@!@:at_least_}{\&{atleast} primitive@>
+primitive("begingroup",begin_group,0); bg_loc:=cur_sym;@/
+@!@:begin_group_}{\&{begingroup} primitive@>
+primitive("controls",controls,0);@/
+@!@:controls_}{\&{controls} primitive@>
+primitive("cull",cull_command,0);@/
+@!@:cull_}{\&{cull} primitive@>
+primitive("curl",curl_command,0);@/
+@!@:curl_}{\&{curl} primitive@>
+primitive("delimiters",delimiters,0);@/
+@!@:delimiters_}{\&{delimiters} primitive@>
+primitive("display",display_command,0);@/
+@!@:display_}{\&{display} primitive@>
+primitive("endgroup",end_group,0);
+ eqtb[frozen_end_group]:=eqtb[cur_sym]; eg_loc:=cur_sym;@/
+@!@:endgroup_}{\&{endgroup} primitive@>
+primitive("everyjob",every_job_command,0);@/
+@!@:every_job_}{\&{everyjob} primitive@>
+primitive("exitif",exit_test,0);@/
+@!@:exit_if_}{\&{exitif} primitive@>
+primitive("expandafter",expand_after,0);@/
+@!@:expand_after_}{\&{expandafter} primitive@>
+primitive("from",from_token,0);@/
+@!@:from_}{\&{from} primitive@>
+primitive("inwindow",in_window,0);@/
+@!@:in_window_}{\&{inwindow} primitive@>
+primitive("interim",interim_command,0);@/
+@!@:interim_}{\&{interim} primitive@>
+primitive("let",let_command,0);@/
+@!@:let_}{\&{let} primitive@>
+primitive("newinternal",new_internal,0);@/
+@!@:new_internal_}{\&{newinternal} primitive@>
+primitive("of",of_token,0);@/
+@!@:of_}{\&{of} primitive@>
+primitive("openwindow",open_window,0);@/
+@!@:open_window_}{\&{openwindow} primitive@>
+primitive("randomseed",random_seed,0);@/
+@!@:random_seed_}{\&{randomseed} primitive@>
+primitive("save",save_command,0);@/
+@!@:save_}{\&{save} primitive@>
+primitive("scantokens",scan_tokens,0);@/
+@!@:scan_tokens_}{\&{scantokens} primitive@>
+primitive("shipout",ship_out_command,0);@/
+@!@:ship_out_}{\&{shipout} primitive@>
+primitive("skipto",skip_to,0);@/
+@!@:skip_to_}{\&{skipto} primitive@>
+primitive("step",step_token,0);@/
+@!@:step_}{\&{step} primitive@>
+primitive("str",str_op,0);@/
+@!@:str_}{\&{str} primitive@>
+primitive("tension",tension,0);@/
+@!@:tension_}{\&{tension} primitive@>
+primitive("to",to_token,0);@/
+@!@:to_}{\&{to} primitive@>
+primitive("until",until_token,0);@/
+@!@:until_}{\&{until} primitive@>
+
+@ Each primitive has a corresponding inverse, so that it is possible to
+display the cryptic numeric contents of |eqtb| in symbolic form.
+Every call of |primitive| in this program is therefore accompanied by some
+straightforward code that forms part of the |print_cmd_mod| routine
+explained below.
+
+@<Cases of |print_cmd_mod| for symbolic printing of primitives@>=
+add_to_command:print("addto");
+assignment:print(":=");
+at_least:print("atleast");
+at_token:print("at");
+bchar_label:print("||:");
+begin_group:print("begingroup");
+colon:print(":");
+comma:print(",");
+controls:print("controls");
+cull_command:print("cull");
+curl_command:print("curl");
+delimiters:print("delimiters");
+display_command:print("display");
+double_colon:print("::");
+end_group:print("endgroup");
+every_job_command:print("everyjob");
+exit_test:print("exitif");
+expand_after:print("expandafter");
+from_token:print("from");
+in_window:print("inwindow");
+interim_command:print("interim");
+left_brace:print("{");
+left_bracket:print("[");
+let_command:print("let");
+new_internal:print("newinternal");
+of_token:print("of");
+open_window:print("openwindow");
+path_join:print("..");
+random_seed:print("randomseed");
+relax:print_char("\");
+right_brace:print("}");
+right_bracket:print("]");
+save_command:print("save");
+scan_tokens:print("scantokens");
+semicolon:print(";");
+ship_out_command:print("shipout");
+skip_to:print("skipto");
+step_token:print("step");
+str_op:print("str");
+tension:print("tension");
+to_token:print("to");
+until_token:print("until");
+
+@ We will deal with the other primitives later, at some point in the program
+where their |eq_type| and |equiv| values are more meaningful. For example,
+the primitives for macro definitions will be loaded when we consider the
+routines that define macros.
+It is easy to find where each particular
+primitive was treated by looking in the index at the end; for example, the
+section where |"def"| entered |eqtb| is listed under `\&{def} primitive'.
+
+@* \[14] Token lists.
+A \MF\ token is either symbolic or numeric or a string, or it denotes
+a macro parameter or capsule; so there are five corresponding ways to encode it
+@^token@>
+internally: (1)~A symbolic token whose hash code is~|p|
+is represented by the number |p|, in the |info| field of a single-word
+node in~|mem|. (2)~A numeric token whose |scaled| value is~|v| is
+represented in a two-word node of~|mem|; the |type| field is |known|,
+the |name_type| field is |token|, and the |value| field holds~|v|.
+The fact that this token appears in a two-word node rather than a
+one-word node is, of course, clear from the node address.
+(3)~A string token is also represented in a two-word node; the |type|
+field is |string_type|, the |name_type| field is |token|, and the
+|value| field holds the corresponding |str_number|. (4)~Capsules have
+|name_type=capsule|, and their |type| and |value| fields represent
+arbitrary values (in ways to be explained later). (5)~Macro parameters
+are like symbolic tokens in that they appear in |info| fields of
+one-word nodes. The $k$th parameter is represented by |expr_base+k| if it
+is of type \&{expr}, or by |suffix_base+k| if it is of type \&{suffix}, or
+by |text_base+k| if it is of type \&{text}. (Here |0<=k<param_size|.)
+Actual values of these parameters are kept in a separate stack, as we will
+see later. The constants |expr_base|, |suffix_base|, and |text_base| are,
+of course, chosen so that there will be no confusion between symbolic
+tokens and parameters of various types.
+
+It turns out that |value(null)=0|, because |null=null_coords|;
+we will make use of this coincidence later.
+
+Incidentally, while we're speaking of coincidences, we might note that
+the `\\{type}' field of a node has nothing to do with ``type'' in a
+printer's sense. It's curious that the same word is used in such different ways.
+
+@d type(#) == mem[#].hh.b0 {identifies what kind of value this is}
+@d name_type(#) == mem[#].hh.b1 {a clue to the name of this value}
+@d token_node_size=2 {the number of words in a large token node}
+@d value_loc(#)==#+1 {the word that contains the |value| field}
+@d value(#)==mem[value_loc(#)].int {the value stored in a large token node}
+@d expr_base==hash_end+1 {code for the zeroth \&{expr} parameter}
+@d suffix_base==expr_base+param_size {code for the zeroth \&{suffix} parameter}
+@d text_base==suffix_base+param_size {code for the zeroth \&{text} parameter}
+
+@<Check the ``constant''...@>=
+if text_base+param_size>max_halfword then bad:=22;
+
+@ A numeric token is created by the following trivial routine.
+
+@p function new_num_tok(@!v:scaled):pointer;
+var @!p:pointer; {the new node}
+begin p:=get_node(token_node_size); value(p):=v;
+type(p):=known; name_type(p):=token; new_num_tok:=p;
+end;
+
+@ A token list is a singly linked list of nodes in |mem|, where
+each node contains a token and a link. Here's a subroutine that gets rid
+of a token list when it is no longer needed.
+
+@p procedure@?token_recycle; forward;@t\2@>@;@/
+procedure flush_token_list(@!p:pointer);
+var @!q:pointer; {the node being recycled}
+begin while p<>null do
+ begin q:=p; p:=link(p);
+ if q>=hi_mem_min then free_avail(q)
+ else begin case type(q) of
+ vacuous,boolean_type,known:do_nothing;
+ string_type:delete_str_ref(value(q));
+ unknown_types,pen_type,path_type,future_pen,picture_type,
+ pair_type,transform_type,dependent,proto_dependent,independent:
+ begin g_pointer:=q; token_recycle;
+ end;
+ othercases confusion("token")
+@:this can't happen token}{\quad token@>
+ endcases;@/
+ free_node(q,token_node_size);
+ end;
+ end;
+end;
+
+@ The procedure |show_token_list|, which prints a symbolic form of
+the token list that starts at a given node |p|, illustrates these
+conventions. The token list being displayed should not begin with a reference
+count. However, the procedure is intended to be fairly robust, so that if the
+memory links are awry or if |p| is not really a pointer to a token list,
+almost nothing catastrophic can happen.
+
+An additional parameter |q| is also given; this parameter is either null
+or it points to a node in the token list where a certain magic computation
+takes place that will be explained later. (Basically, |q| is non-null when
+we are printing the two-line context information at the time of an error
+message; |q| marks the place corresponding to where the second line
+should begin.)
+
+The generation will stop, and `\.{\char`\ ETC.}' will be printed, if the length
+of printing exceeds a given limit~|l|; the length of printing upon entry is
+assumed to be a given amount called |null_tally|. (Note that
+|show_token_list| sometimes uses itself recursively to print
+variable names within a capsule.)
+@^recursion@>
+
+Unusual entries are printed in the form of all-caps tokens
+preceded by a space, e.g., `\.{\char`\ BAD}'.
+
+@<Declare the procedure called |show_token_list|@>=
+procedure@?print_capsule; forward; @t\2@>@;@/
+procedure show_token_list(@!p,@!q:integer;@!l,@!null_tally:integer);
+label exit;
+var @!class,@!c:small_number; {the |char_class| of previous and new tokens}
+@!r,@!v:integer; {temporary registers}
+begin class:=percent_class;
+tally:=null_tally;
+while (p<>null) and (tally<l) do
+ begin if p=q then @<Do magic computation@>;
+ @<Display token |p| and set |c| to its class;
+ but |return| if there are problems@>;
+ class:=c; p:=link(p);
+ end;
+if p<>null then print(" ETC.");
+@.ETC@>
+exit:
+end;
+
+@ @<Display token |p| and set |c| to its class...@>=
+c:=letter_class; {the default}
+if (p<mem_min)or(p>mem_end) then
+ begin print(" CLOBBERED"); return;
+@.CLOBBERED@>
+ end;
+if p<hi_mem_min then @<Display two-word token@>
+else begin r:=info(p);
+ if r>=expr_base then @<Display a parameter token@>
+ else if r<1 then
+ if r=0 then @<Display a collective subscript@>
+ else print(" IMPOSSIBLE")
+@.IMPOSSIBLE@>
+ else begin r:=text(r);
+ if (r<0)or(r>=str_ptr) then print(" NONEXISTENT")
+@.NONEXISTENT@>
+ else @<Print string |r| as a symbolic token
+ and set |c| to its class@>;
+ end;
+ end
+
+@ @<Display two-word token@>=
+if name_type(p)=token then
+ if type(p)=known then @<Display a numeric token@>
+ else if type(p)<>string_type then print(" BAD")
+@.BAD@>
+ else begin print_char(""""); slow_print(value(p)); print_char("""");
+ c:=string_class;
+ end
+else if (name_type(p)<>capsule)or(type(p)<vacuous)or(type(p)>independent) then
+ print(" BAD")
+else begin g_pointer:=p; print_capsule; c:=right_paren_class;
+ end
+
+@ @<Display a numeric token@>=
+begin if class=digit_class then print_char(" ");
+v:=value(p);
+if v<0 then
+ begin if class=left_bracket_class then print_char(" ");
+ print_char("["); print_scaled(v); print_char("]");
+ c:=right_bracket_class;
+ end
+else begin print_scaled(v); c:=digit_class;
+ end;
+end
+
+@ Strictly speaking, a genuine token will never have |info(p)=0|.
+But we will see later (in the |print_variable_name| routine) that
+it is convenient to let |info(p)=0| stand for `\.{[]}'.
+
+@<Display a collective subscript@>=
+begin if class=left_bracket_class then print_char(" ");
+print("[]"); c:=right_bracket_class;
+end
+
+@ @<Display a parameter token@>=
+begin if r<suffix_base then
+ begin print("(EXPR"); r:=r-(expr_base);
+@.EXPR@>
+ end
+else if r<text_base then
+ begin print("(SUFFIX"); r:=r-(suffix_base);
+@.SUFFIX@>
+ end
+else begin print("(TEXT"); r:=r-(text_base);
+@.TEXT@>
+ end;
+print_int(r); print_char(")"); c:=right_paren_class;
+end
+
+@ @<Print string |r| as a symbolic token...@>=
+begin c:=char_class[so(str_pool[str_start[r]])];
+if c=class then
+ case c of
+ letter_class:print_char(".");
+ isolated_classes:do_nothing;
+ othercases print_char(" ")
+ endcases;
+slow_print(r);
+end
+
+@ The following procedures have been declared |forward| with no parameters,
+because the author dislikes \PASCAL's convention about |forward| procedures
+with parameters. It was necessary to do something, because |show_token_list|
+is recursive (although the recursion is limited to one level), and because
+|flush_token_list| is syntactically (but not semantically) recursive.
+@^recursion@>
+
+@<Declare miscellaneous procedures that were declared |forward|@>=
+procedure print_capsule;
+begin print_char("("); print_exp(g_pointer,0); print_char(")");
+end;
+@#
+procedure token_recycle;
+begin recycle_value(g_pointer);
+end;
+
+@ @<Glob...@>=
+@!g_pointer:pointer; {(global) parameter to the |forward| procedures}
+
+@ Macro definitions are kept in \MF's memory in the form of token lists
+that have a few extra one-word nodes at the beginning.
+
+The first node contains a reference count that is used to tell when the
+list is no longer needed. To emphasize the fact that a reference count is
+present, we shall refer to the |info| field of this special node as the
+|ref_count| field.
+@^reference counts@>
+
+The next node or nodes after the reference count serve to describe the
+formal parameters. They consist of zero or more parameter tokens followed
+by a code for the type of macro.
+
+@d ref_count==info {reference count preceding a macro definition or pen header}
+@d add_mac_ref(#)==incr(ref_count(#)) {make a new reference to a macro list}
+@d general_macro=0 {preface to a macro defined with a parameter list}
+@d primary_macro=1 {preface to a macro with a \&{primary} parameter}
+@d secondary_macro=2 {preface to a macro with a \&{secondary} parameter}
+@d tertiary_macro=3 {preface to a macro with a \&{tertiary} parameter}
+@d expr_macro=4 {preface to a macro with an undelimited \&{expr} parameter}
+@d of_macro=5 {preface to a macro with
+ undelimited `\&{expr} |x| \&{of}~|y|' parameters}
+@d suffix_macro=6 {preface to a macro with an undelimited \&{suffix} parameter}
+@d text_macro=7 {preface to a macro with an undelimited \&{text} parameter}
+
+@p procedure delete_mac_ref(@!p:pointer);
+ {|p| points to the reference count of a macro list that is
+ losing one reference}
+begin if ref_count(p)=null then flush_token_list(p)
+else decr(ref_count(p));
+end;
+
+@ The following subroutine displays a macro, given a pointer to its
+reference count.
+
+@p @t\4@>@<Declare the procedure called |print_cmd_mod|@>@;
+procedure show_macro(@!p:pointer;@!q,@!l:integer);
+label exit;
+var @!r:pointer; {temporary storage}
+begin p:=link(p); {bypass the reference count}
+while info(p)>text_macro do
+ begin r:=link(p); link(p):=null;
+ show_token_list(p,null,l,0); link(p):=r; p:=r;
+ if l>0 then l:=l-tally@+else return;
+ end; {control printing of `\.{ETC.}'}
+@.ETC@>
+tally:=0;
+case info(p) of
+general_macro:print("->");
+@.->@>
+primary_macro,secondary_macro,tertiary_macro:begin print_char("<");
+ print_cmd_mod(param_type,info(p)); print(">->");
+ end;
+expr_macro:print("<expr>->");
+of_macro:print("<expr>of<primary>->");
+suffix_macro:print("<suffix>->");
+text_macro:print("<text>->");
+end; {there are no other cases}
+show_token_list(link(p),q,l-tally,0);
+exit:end;
+
+@* \[15] Data structures for variables.
+The variables of \MF\ programs can be simple, like `\.x', or they can
+combine the structural properties of arrays and records, like `\.{x20a.b}'.
+A \MF\ user assigns a type to a variable like \.{x20a.b} by saying, for
+example, `\.{boolean} \.{x[]a.b}'. It's time for us to study how such
+things are represented inside of the computer.
+
+Each variable value occupies two consecutive words, either in a two-word
+node called a value node, or as a two-word subfield of a larger node. One
+of those two words is called the |value| field; it is an integer,
+containing either a |scaled| numeric value or the representation of some
+other type of quantity. (It might also be subdivided into halfwords, in
+which case it is referred to by other names instead of |value|.) The other
+word is broken into subfields called |type|, |name_type|, and |link|. The
+|type| field is a quarterword that specifies the variable's type, and
+|name_type| is a quarterword from which \MF\ can reconstruct the
+variable's name (sometimes by using the |link| field as well). Thus, only
+1.25 words are actually devoted to the value itself; the other
+three-quarters of a word are overhead, but they aren't wasted because they
+allow \MF\ to deal with sparse arrays and to provide meaningful diagnostics.
+
+In this section we shall be concerned only with the structural aspects of
+variables, not their values. Later parts of the program will change the
+|type| and |value| fields, but we shall treat those fields as black boxes
+whose contents should not be touched.
+
+However, if the |type| field is |structured|, there is no |value| field,
+and the second word is broken into two pointer fields called |attr_head|
+and |subscr_head|. Those fields point to additional nodes that
+contain structural information, as we shall see.
+
+@d subscr_head_loc(#) == #+1 {where |value|, |subscr_head|, and |attr_head| are}
+@d attr_head(#) == info(subscr_head_loc(#)) {pointer to attribute info}
+@d subscr_head(#) == link(subscr_head_loc(#)) {pointer to subscript info}
+@d value_node_size=2 {the number of words in a value node}
+
+@ An attribute node is three words long. Two of these words contain |type|
+and |value| fields as described above, and the third word contains
+additional information: There is an |attr_loc| field, which contains the
+hash address of the token that names this attribute; and there's also a
+|parent| field, which points to the value node of |structured| type at the
+next higher level (i.e., at the level to which this attribute is
+subsidiary). The |name_type| in an attribute node is `|attr|'. The
+|link| field points to the next attribute with the same parent; these are
+arranged in increasing order, so that |attr_loc(link(p))>attr_loc(p)|. The
+final attribute node links to the constant |end_attr|, whose |attr_loc|
+field is greater than any legal hash address. The |attr_head| in the
+parent points to a node whose |name_type| is |structured_root|; this
+node represents the null attribute, i.e., the variable that is relevant
+when no attributes are attached to the parent. The |attr_head| node
+has the fields of either
+a value node, a subscript node, or an attribute node, depending on what
+the parent would be if it were not structured; but the subscript and
+attribute fields are ignored, so it effectively contains only the data of
+a value node. The |link| field in this special node points to an attribute
+node whose |attr_loc| field is zero; the latter node represents a collective
+subscript `\.{[]}' attached to the parent, and its |link| field points to
+the first non-special attribute node (or to |end_attr| if there are none).
+
+A subscript node likewise occupies three words, with |type| and |value| fields
+plus extra information; its |name_type| is |subscr|. In this case the
+third word is called the |subscript| field, which is a |scaled| integer.
+The |link| field points to the subscript node with the next larger
+subscript, if any; otherwise the |link| points to the attribute node
+for collective subscripts at this level. We have seen that the latter node
+contains an upward pointer, so that the parent can be deduced.
+
+The |name_type| in a parent-less value node is |root|, and the |link|
+is the hash address of the token that names this value.
+
+In other words, variables have a hierarchical structure that includes
+enough threads running around so that the program is able to move easily
+between siblings, parents, and children. An example should be helpful:
+(The reader is advised to draw a picture while reading the following
+description, since that will help to firm up the ideas.)
+Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}'
+and `\.{x20b}' have been mentioned in a user's program, where
+\.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|,
+and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then
+|eq_type(h(x))=tag_token| and |equiv(h(x))=p|, where |p|~is a two-word value
+node with |name_type(p)=root| and |link(p)=h(x)|. We have |type(p)=structured|,
+|attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value
+node and |r| to a subscript node. (Are you still following this? Use
+a pencil to draw a diagram.) The lone variable `\.x' is represented by
+|type(q)| and |value(q)|; furthermore
+|name_type(q)=structured_root| and |link(q)=q1|, where |q1| points
+to an attribute node representing `\.{x[]}'. Thus |name_type(q1)=attr|,
+|attr_loc(q1)=collective_subscript=0|, |parent(q1)=p|,
+|type(q1)=structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|;
+|qq| is a three-word ``attribute-as-value'' node with |type(qq)=numeric_type|
+(assuming that \.{x5} is numeric, because |qq| represents `\.{x[]}'
+with no further attributes), |name_type(qq)=structured_root|,
+|attr_loc(qq)=0|, |parent(qq)=p|, and
+|link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is
+an attribute node representing `\.{x[][]}', which has never yet
+occurred; its |type| field is |undefined|, and its |value| field is
+undefined. We have |name_type(qq1)=attr|, |attr_loc(qq1)=collective_subscript|,
+|parent(qq1)=q1|, and |link(qq1)=qq2|. Since |qq2| represents
+`\.{x[]b}', |type(qq2)=unknown_boolean|; also |attr_loc(qq2)=h(b)|,
+|parent(qq2)=q1|, |name_type(qq2)=attr|, |link(qq2)=end_attr|.
+(Maybe colored lines will help untangle your picture.)
+ Node |r| is a subscript node with |type| and |value|
+representing `\.{x5}'; |name_type(r)=subscr|, |subscript(r)=5.0|,
+and |link(r)=r1| is another subscript node. To complete the picture,
+see if you can guess what |link(r1)| is; give up? It's~|q1|.
+Furthermore |subscript(r1)=20.0|, |name_type(r1)=subscr|,
+|type(r1)=structured|, |attr_head(r1)=qqq|, |subscr_head(r1)=qqq1|,
+and we finish things off with three more nodes
+|qqq|, |qqq1|, and |qqq2| hung onto~|r1|. (Perhaps you should start again
+with a larger sheet of paper.) The value of variable `\.{x20b}'
+appears in node~|qqq2=link(qqq1)|, as you can well imagine.
+Similarly, the value of `\.{x.a}' appears in node |q2=link(q1)|, where
+|attr_loc(q2)=h(a)| and |parent(q2)=p|.
+
+If the example in the previous paragraph doesn't make things crystal
+clear, a glance at some of the simpler subroutines below will reveal how
+things work out in practice.
+
+The only really unusual thing about these conventions is the use of
+collective subscript attributes. The idea is to avoid repeating a lot of
+type information when many elements of an array are identical macros
+(for which distinct values need not be stored) or when they don't have
+all of the possible attributes. Branches of the structure below collective
+subscript attributes do not carry actual values except for macro identifiers;
+branches of the structure below subscript nodes do not carry significant
+information in their collective subscript attributes.
+
+@d attr_loc_loc(#)==#+2 {where the |attr_loc| and |parent| fields are}
+@d attr_loc(#)==info(attr_loc_loc(#)) {hash address of this attribute}
+@d parent(#)==link(attr_loc_loc(#)) {pointer to |structured| variable}
+@d subscript_loc(#)==#+2 {where the |subscript| field lives}
+@d subscript(#)==mem[subscript_loc(#)].sc {subscript of this variable}
+@d attr_node_size=3 {the number of words in an attribute node}
+@d subscr_node_size=3 {the number of words in a subscript node}
+@d collective_subscript=0 {code for the attribute `\.{[]}'}
+
+@<Initialize table...@>=
+attr_loc(end_attr):=hash_end+1; parent(end_attr):=null;
+
+@ Variables of type \&{pair} will have values that point to four-word
+nodes containing two numeric values. The first of these values has
+|name_type=x_part_sector| and the second has |name_type=y_part_sector|;
+the |link| in the first points back to the node whose |value| points
+to this four-word node.
+
+Variables of type \&{transform} are similar, but in this case their
+|value| points to a 12-word node containing six values, identified by
+|x_part_sector|, |y_part_sector|, |xx_part_sector|, |xy_part_sector|,
+|yx_part_sector|, and |yy_part_sector|.
+
+When an entire structured variable is saved, the |root| indication
+is temporarily replaced by |saved_root|.
+
+Some variables have no name; they just are used for temporary storage
+while expressions are being evaluated. We call them {\sl capsules}.
+
+@d x_part_loc(#)==# {where the \&{xpart} is found in a pair or transform node}
+@d y_part_loc(#)==#+2 {where the \&{ypart} is found in a pair or transform node}
+@d xx_part_loc(#)==#+4 {where the \&{xxpart} is found in a transform node}
+@d xy_part_loc(#)==#+6 {where the \&{xypart} is found in a transform node}
+@d yx_part_loc(#)==#+8 {where the \&{yxpart} is found in a transform node}
+@d yy_part_loc(#)==#+10 {where the \&{yypart} is found in a transform node}
+@#
+@d pair_node_size=4 {the number of words in a pair node}
+@d transform_node_size=12 {the number of words in a transform node}
+
+@<Glob...@>=
+@!big_node_size:array[transform_type..pair_type] of small_number;
+
+@ The |big_node_size| array simply contains two constants that \MF\
+occasionally needs to know.
+
+@<Set init...@>=
+big_node_size[transform_type]:=transform_node_size;
+big_node_size[pair_type]:=pair_node_size;
+
+@ If |type(p)=pair_type| or |transform_type| and if |value(p)=null|, the
+procedure call |init_big_node(p)| will allocate a pair or transform node
+for~|p|. The individual parts of such nodes are initially of type
+|independent|.
+
+@p procedure init_big_node(@!p:pointer);
+var @!q:pointer; {the new node}
+@!s:small_number; {its size}
+begin s:=big_node_size[type(p)]; q:=get_node(s);
+repeat s:=s-2; @<Make variable |q+s| newly independent@>;
+name_type(q+s):=half(s)+x_part_sector; link(q+s):=null;
+until s=0;
+link(q):=p; value(p):=q;
+end;
+
+@ The |id_transform| function creates a capsule for the
+identity transformation.
+
+@p function id_transform:pointer;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin p:=get_node(value_node_size); type(p):=transform_type;
+name_type(p):=capsule; value(p):=null; init_big_node(p); q:=value(p);
+r:=q+transform_node_size;
+repeat r:=r-2;
+type(r):=known; value(r):=0;
+until r=q;
+value(xx_part_loc(q)):=unity; value(yy_part_loc(q)):=unity;
+id_transform:=p;
+end;
+
+@ Tokens are of type |tag_token| when they first appear, but they point
+to |null| until they are first used as the root of a variable.
+The following subroutine establishes the root node on such grand occasions.
+
+@p procedure new_root(@!x:pointer);
+var @!p:pointer; {the new node}
+begin p:=get_node(value_node_size); type(p):=undefined; name_type(p):=root;
+link(p):=x; equiv(x):=p;
+end;
+
+@ These conventions for variable representation are illustrated by the
+|print_variable_name| routine, which displays the full name of a
+variable given only a pointer to its two-word value packet.
+
+@p procedure print_variable_name(@!p:pointer);
+label found,exit;
+var @!q:pointer; {a token list that will name the variable's suffix}
+@!r:pointer; {temporary for token list creation}
+begin while name_type(p)>=x_part_sector do
+ @<Preface the output with a part specifier; |return| in the
+ case of a capsule@>;
+q:=null;
+while name_type(p)>saved_root do
+ @<Ascend one level, pushing a token onto list |q|
+ and replacing |p| by its parent@>;
+r:=get_avail; info(r):=link(p); link(r):=q;
+if name_type(p)=saved_root then print("(SAVED)");
+@.SAVED@>
+show_token_list(r,null,el_gordo,tally); flush_token_list(r);
+exit:end;
+
+@ @<Ascend one level, pushing a token onto list |q|...@>=
+begin if name_type(p)=subscr then
+ begin r:=new_num_tok(subscript(p));
+ repeat p:=link(p);
+ until name_type(p)=attr;
+ end
+else if name_type(p)=structured_root then
+ begin p:=link(p); goto found;
+ end
+else begin if name_type(p)<>attr then confusion("var");
+@:this can't happen var}{\quad var@>
+ r:=get_avail; info(r):=attr_loc(p);
+ end;
+link(r):=q; q:=r;
+found: p:=parent(p);
+end
+
+@ @<Preface the output with a part specifier...@>=
+begin case name_type(p) of
+x_part_sector: print_char("x");
+y_part_sector: print_char("y");
+xx_part_sector: print("xx");
+xy_part_sector: print("xy");
+yx_part_sector: print("yx");
+yy_part_sector: print("yy");
+capsule: begin print("%CAPSULE"); print_int(p-null); return;
+@.CAPSULE@>
+ end;
+end; {there are no other cases}
+print("part "); p:=link(p-2*(name_type(p)-x_part_sector));
+end
+
+@ The |interesting| function returns |true| if a given variable is not
+in a capsule, or if the user wants to trace capsules.
+
+@p function interesting(@!p:pointer):boolean;
+var @!t:small_number; {a |name_type|}
+begin if internal[tracing_capsules]>0 then interesting:=true
+else begin t:=name_type(p);
+ if t>=x_part_sector then if t<>capsule then
+ t:=name_type(link(p-2*(t-x_part_sector)));
+ interesting:=(t<>capsule);
+ end;
+end;
+
+@ Now here is a subroutine that converts an unstructured type into an
+equivalent structured type, by inserting a |structured| node that is
+capable of growing. This operation is done only when |name_type(p)=root|,
+|subscr|, or |attr|.
+
+The procedure returns a pointer to the new node that has taken node~|p|'s
+place in the structure. Node~|p| itself does not move, nor are its
+|value| or |type| fields changed in any way.
+
+@p function new_structure(@!p:pointer):pointer;
+var @!q,@!r:pointer; {list manipulation registers}
+begin case name_type(p) of
+root: begin q:=link(p); r:=get_node(value_node_size); equiv(q):=r;
+ end;
+subscr: @<Link a new subscript node |r| in place of node |p|@>;
+attr: @<Link a new attribute node |r| in place of node |p|@>;
+othercases confusion("struct")
+@:this can't happen struct}{\quad struct@>
+endcases;@/
+link(r):=link(p); type(r):=structured; name_type(r):=name_type(p);
+attr_head(r):=p; name_type(p):=structured_root;@/
+q:=get_node(attr_node_size); link(p):=q; subscr_head(r):=q;
+parent(q):=r; type(q):=undefined; name_type(q):=attr; link(q):=end_attr;
+attr_loc(q):=collective_subscript; new_structure:=r;
+end;
+
+@ @<Link a new subscript node |r| in place of node |p|@>=
+begin q:=p;
+repeat q:=link(q);
+until name_type(q)=attr;
+q:=parent(q); r:=subscr_head_loc(q); {|link(r)=subscr_head(q)|}
+repeat q:=r; r:=link(r);
+until r=p;
+r:=get_node(subscr_node_size);
+link(q):=r; subscript(r):=subscript(p);
+end
+
+@ If the attribute is |collective_subscript|, there are two pointers to
+node~|p|, so we must change both of them.
+
+@<Link a new attribute node |r| in place of node |p|@>=
+begin q:=parent(p); r:=attr_head(q);
+repeat q:=r; r:=link(r);
+until r=p;
+r:=get_node(attr_node_size); link(q):=r;@/
+mem[attr_loc_loc(r)]:=mem[attr_loc_loc(p)]; {copy |attr_loc| and |parent|}
+if attr_loc(p)=collective_subscript then
+ begin q:=subscr_head_loc(parent(p));
+ while link(q)<>p do q:=link(q);
+ link(q):=r;
+ end;
+end
+
+@ The |find_variable| routine is given a pointer~|t| to a nonempty token
+list of suffixes; it returns a pointer to the corresponding two-word
+value. For example, if |t| points to token \.x followed by a numeric
+token containing the value~7, |find_variable| finds where the value of
+\.{x7} is stored in memory. This may seem a simple task, and it
+usually is, except when \.{x7} has never been referenced before.
+Indeed, \.x may never have even been subscripted before; complexities
+arise with respect to updating the collective subscript information.
+
+If a macro type is detected anywhere along path~|t|, or if the first
+item on |t| isn't a |tag_token|, the value |null| is returned.
+Otherwise |p| will be a non-null pointer to a node such that
+|undefined<type(p)<structured|.
+
+@d abort_find==begin find_variable:=null; return;@+end
+
+@p function find_variable(@!t:pointer):pointer;
+label exit;
+var @!p,@!q,@!r,@!s:pointer; {nodes in the ``value'' line}
+@!pp,@!qq,@!rr,@!ss:pointer; {nodes in the ``collective'' line}
+@!n:integer; {subscript or attribute}
+@!save_word:memory_word; {temporary storage for a word of |mem|}
+@^inner loop@>
+begin p:=info(t); t:=link(t);
+if eq_type(p) mod outer_tag<>tag_token then abort_find;
+if equiv(p)=null then new_root(p);
+p:=equiv(p); pp:=p;
+while t<>null do
+ begin @<Make sure that both nodes |p| and |pp| are of |structured| type@>;
+ if t<hi_mem_min then
+ @<Descend one level for the subscript |value(t)|@>
+ else @<Descend one level for the attribute |info(t)|@>;
+ t:=link(t);
+ end;
+if type(pp)>=structured then
+ if type(pp)=structured then pp:=attr_head(pp)@+else abort_find;
+if type(p)=structured then p:=attr_head(p);
+if type(p)=undefined then
+ begin if type(pp)=undefined then
+ begin type(pp):=numeric_type; value(pp):=null;
+ end;
+ type(p):=type(pp); value(p):=null;
+ end;
+find_variable:=p;
+exit:end;
+
+@ Although |pp| and |p| begin together, they diverge when a subscript occurs;
+|pp|~stays in the collective line while |p|~goes through actual subscript
+values.
+
+@<Make sure that both nodes |p| and |pp|...@>=
+if type(pp)<>structured then
+ begin if type(pp)>structured then abort_find;
+ ss:=new_structure(pp);
+ if p=pp then p:=ss;
+ pp:=ss;
+ end; {now |type(pp)=structured|}
+if type(p)<>structured then {it cannot be |>structured|}
+ p:=new_structure(p) {now |type(p)=structured|}
+
+@ We want this part of the program to be reasonably fast, in case there are
+@^inner loop@>
+lots of subscripts at the same level of the data structure. Therefore
+we store an ``infinite'' value in the word that appears at the end of the
+subscript list, even though that word isn't part of a subscript node.
+
+@<Descend one level for the subscript |value(t)|@>=
+begin n:=value(t);
+pp:=link(attr_head(pp)); {now |attr_loc(pp)=collective_subscript|}
+q:=link(attr_head(p)); save_word:=mem[subscript_loc(q)];
+subscript(q):=el_gordo; s:=subscr_head_loc(p); {|link(s)=subscr_head(p)|}
+repeat r:=s; s:=link(s);
+until n<=subscript(s);
+if n=subscript(s) then p:=s
+else begin p:=get_node(subscr_node_size); link(r):=p; link(p):=s;
+ subscript(p):=n; name_type(p):=subscr; type(p):=undefined;
+ end;
+mem[subscript_loc(q)]:=save_word;
+end
+
+@ @<Descend one level for the attribute |info(t)|@>=
+begin n:=info(t);
+ss:=attr_head(pp);
+repeat rr:=ss; ss:=link(ss);
+until n<=attr_loc(ss);
+if n<attr_loc(ss) then
+ begin qq:=get_node(attr_node_size); link(rr):=qq; link(qq):=ss;
+ attr_loc(qq):=n; name_type(qq):=attr; type(qq):=undefined;
+ parent(qq):=pp; ss:=qq;
+ end;
+if p=pp then
+ begin p:=ss; pp:=ss;
+ end
+else begin pp:=ss; s:=attr_head(p);
+ repeat r:=s; s:=link(s);
+ until n<=attr_loc(s);
+ if n=attr_loc(s) then p:=s
+ else begin q:=get_node(attr_node_size); link(r):=q; link(q):=s;
+ attr_loc(q):=n; name_type(q):=attr; type(q):=undefined;
+ parent(q):=p; p:=q;
+ end;
+ end;
+end
+
+@ Variables lose their former values when they appear in a type declaration,
+or when they are defined to be macros or \&{let} equal to something else.
+A subroutine will be defined later that recycles the storage associated
+with any particular |type| or |value|; our goal now is to study a higher
+level process called |flush_variable|, which selectively frees parts of a
+variable structure.
+
+This routine has some complexity because of examples such as
+`\hbox{\tt numeric x[]a[]b}',
+which recycles all variables of the form \.{x[i]a[j]b} (and no others), while
+`\hbox{\tt vardef x[]a[]=...}'
+discards all variables of the form \.{x[i]a[j]} followed by an arbitrary
+suffix, except for the collective node \.{x[]a[]} itself. The obvious way
+to handle such examples is to use recursion; so that's what we~do.
+@^recursion@>
+
+Parameter |p| points to the root information of the variable;
+parameter |t| points to a list of one-word nodes that represent
+suffixes, with |info=collective_subscript| for subscripts.
+
+@p @t\4@>@<Declare subroutines for printing expressions@>@;@/
+@t\4@>@<Declare basic dependency-list subroutines@>@;
+@t\4@>@<Declare the recycling subroutines@>@;
+@t\4@>@<Declare the procedure called |flush_cur_exp|@>@;
+@t\4@>@<Declare the procedure called |flush_below_variable|@>@;
+procedure flush_variable(@!p,@!t:pointer;@!discard_suffixes:boolean);
+label exit;
+var @!q,@!r:pointer; {list manipulation}
+@!n:halfword; {attribute to match}
+begin while t<>null do
+ begin if type(p)<>structured then return;
+ n:=info(t); t:=link(t);
+ if n=collective_subscript then
+ begin r:=subscr_head_loc(p); q:=link(r); {|q=subscr_head(p)|}
+ while name_type(q)=subscr do
+ begin flush_variable(q,t,discard_suffixes);
+ if t=null then
+ if type(q)=structured then r:=q
+ else begin link(r):=link(q); free_node(q,subscr_node_size);
+ end
+ else r:=q;
+ q:=link(r);
+ end;
+ end;
+ p:=attr_head(p);
+ repeat r:=p; p:=link(p);
+ until attr_loc(p)>=n;
+ if attr_loc(p)<>n then return;
+ end;
+if discard_suffixes then flush_below_variable(p)
+else begin if type(p)=structured then p:=attr_head(p);
+ recycle_value(p);
+ end;
+exit:end;
+
+@ The next procedure is simpler; it wipes out everything but |p| itself,
+which becomes undefined.
+
+@<Declare the procedure called |flush_below_variable|@>=
+procedure flush_below_variable(@!p:pointer);
+var @!q,@!r:pointer; {list manipulation registers}
+begin if type(p)<>structured then
+ recycle_value(p) {this sets |type(p)=undefined|}
+else begin q:=subscr_head(p);
+ while name_type(q)=subscr do
+ begin flush_below_variable(q); r:=q; q:=link(q);
+ free_node(r,subscr_node_size);
+ end;
+ r:=attr_head(p); q:=link(r); recycle_value(r);
+ if name_type(p)<=saved_root then free_node(r,value_node_size)
+ else free_node(r,subscr_node_size);
+ {we assume that |subscr_node_size=attr_node_size|}
+ repeat flush_below_variable(q); r:=q; q:=link(q); free_node(r,attr_node_size);
+ until q=end_attr;
+ type(p):=undefined;
+ end;
+end;
+
+@ Just before assigning a new value to a variable, we will recycle the
+old value and make the old value undefined. The |und_type| routine
+determines what type of undefined value should be given, based on
+the current type before recycling.
+
+@p function und_type(@!p:pointer):small_number;
+begin case type(p) of
+undefined,vacuous:und_type:=undefined;
+boolean_type,unknown_boolean:und_type:=unknown_boolean;
+string_type,unknown_string:und_type:=unknown_string;
+pen_type,unknown_pen,future_pen:und_type:=unknown_pen;
+path_type,unknown_path:und_type:=unknown_path;
+picture_type,unknown_picture:und_type:=unknown_picture;
+transform_type,pair_type,numeric_type:und_type:=type(p);
+known,dependent,proto_dependent,independent:und_type:=numeric_type;
+end; {there are no other cases}
+end;
+
+@ The |clear_symbol| routine is used when we want to redefine the equivalent
+of a symbolic token. It must remove any variable structure or macro
+definition that is currently attached to that symbol. If the |saving|
+parameter is true, a subsidiary structure is saved instead of destroyed.
+
+@p procedure clear_symbol(@!p:pointer;@!saving:boolean);
+var @!q:pointer; {|equiv(p)|}
+begin q:=equiv(p);
+case eq_type(p) mod outer_tag of
+defined_macro,secondary_primary_macro,tertiary_secondary_macro,
+ expression_tertiary_macro: if not saving then delete_mac_ref(q);
+tag_token:if q<>null then
+ if saving then name_type(q):=saved_root
+ else begin flush_below_variable(q); free_node(q,value_node_size);
+ end;@;
+othercases do_nothing
+endcases;@/
+eqtb[p]:=eqtb[frozen_undefined];
+end;
+
+@* \[16] Saving and restoring equivalents.
+The nested structure provided by \&{begingroup} and \&{endgroup}
+allows |eqtb| entries to be saved and restored, so that temporary changes
+can be made without difficulty. When the user requests a current value to
+be saved, \MF\ puts that value into its ``save stack.'' An appearance of
+\&{endgroup} ultimately causes the old values to be removed from the save
+stack and put back in their former places.
+
+The save stack is a linked list containing three kinds of entries,
+distinguished by their |info| fields. If |p| points to a saved item,
+then
+
+\smallskip\hang
+|info(p)=0| stands for a group boundary; each \&{begingroup} contributes
+such an item to the save stack and each \&{endgroup} cuts back the stack
+until the most recent such entry has been removed.
+
+\smallskip\hang
+|info(p)=q|, where |1<=q<=hash_end|, means that |mem[p+1]| holds the former
+contents of |eqtb[q]|. Such save stack entries are generated by \&{save}
+commands.
+
+\smallskip\hang
+|info(p)=hash_end+q|, where |q>0|, means that |value(p)| is a |scaled|
+integer to be restored to internal parameter number~|q|. Such entries
+are generated by \&{interim} commands.
+
+\smallskip\noindent
+The global variable |save_ptr| points to the top item on the save stack.
+
+@d save_node_size=2 {number of words per non-boundary save-stack node}
+@d saved_equiv(#)==mem[#+1].hh {where an |eqtb| entry gets saved}
+@d save_boundary_item(#)==begin #:=get_avail; info(#):=0;
+ link(#):=save_ptr; save_ptr:=#;
+ end
+
+@<Glob...@>=@!save_ptr:pointer; {the most recently saved item}
+
+@ @<Set init...@>=save_ptr:=null;
+
+@ The |save_variable| routine is given a hash address |q|; it salts this
+address in the save stack, together with its current equivalent,
+then makes token~|q| behave as though it were brand new.
+
+Nothing is stacked when |save_ptr=null|, however; there's no way to remove
+things from the stack when the program is not inside a group, so there's
+no point in wasting the space.
+
+@p procedure save_variable(@!q:pointer);
+var @!p:pointer; {temporary register}
+begin if save_ptr<>null then
+ begin p:=get_node(save_node_size); info(p):=q; link(p):=save_ptr;
+ saved_equiv(p):=eqtb[q]; save_ptr:=p;
+ end;
+clear_symbol(q,(save_ptr<>null));
+end;
+
+@ Similarly, |save_internal| is given the location |q| of an internal
+quantity like |tracing_pens|. It creates a save stack entry of the
+third kind.
+
+@p procedure save_internal(@!q:halfword);
+var @!p:pointer; {new item for the save stack}
+begin if save_ptr<>null then
+ begin p:=get_node(save_node_size); info(p):=hash_end+q;
+ link(p):=save_ptr; value(p):=internal[q]; save_ptr:=p;
+ end;
+end;
+
+@ At the end of a group, the |unsave| routine restores all of the saved
+equivalents in reverse order. This routine will be called only when there
+is at least one boundary item on the save stack.
+
+@p procedure unsave;
+var @!q:pointer; {index to saved item}
+@!p:pointer; {temporary register}
+begin while info(save_ptr)<>0 do
+ begin q:=info(save_ptr);
+ if q>hash_end then
+ begin if internal[tracing_restores]>0 then
+ begin begin_diagnostic; print_nl("{restoring ");
+ slow_print(int_name[q-(hash_end)]); print_char("=");
+ print_scaled(value(save_ptr)); print_char("}");
+ end_diagnostic(false);
+ end;
+ internal[q-(hash_end)]:=value(save_ptr);
+ end
+ else begin if internal[tracing_restores]>0 then
+ begin begin_diagnostic; print_nl("{restoring ");
+ slow_print(text(q)); print_char("}");
+ end_diagnostic(false);
+ end;
+ clear_symbol(q,false);
+ eqtb[q]:=saved_equiv(save_ptr);
+ if eq_type(q) mod outer_tag=tag_token then
+ begin p:=equiv(q);
+ if p<>null then name_type(p):=root;
+ end;
+ end;
+ p:=link(save_ptr); free_node(save_ptr,save_node_size); save_ptr:=p;
+ end;
+p:=link(save_ptr); free_avail(save_ptr); save_ptr:=p;
+end;
+
+@* \[17] Data structures for paths.
+When a \MF\ user specifies a path, \MF\ will create a list of knots
+and control points for the associated cubic spline curves. If the
+knots are $z_0$, $z_1$, \dots, $z_n$, there are control points
+$z_k^+$ and $z_{k+1}^-$ such that the cubic splines between knots
+$z_k$ and $z_{k+1}$ are defined by B\'ezier's formula
+@:Bezier}{B\'ezier, Pierre Etienne@>
+$$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr
+&=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$
+for |0<=t<=1|.
+
+There is a 7-word node for each knot $z_k$, containing one word of
+control information and six words for the |x| and |y| coordinates
+of $z_k^-$ and $z_k$ and~$z_k^+$. The control information appears
+in the |left_type| and |right_type| fields, which each occupy
+a quarter of the first word in the node; they specify properties
+of the curve as it enters and leaves the knot. There's also a
+halfword |link| field, which points to the following knot.
+
+If the path is a closed contour, knots 0 and |n| are identical;
+i.e., the |link| in knot |n-1| points to knot~0. But if the path
+is not closed, the |left_type| of knot~0 and the |right_type| of knot~|n|
+are equal to |endpoint|. In the latter case the |link| in knot~|n| points
+to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used.
+
+@d left_type(#) == mem[#].hh.b0 {characterizes the path entering this knot}
+@d right_type(#) == mem[#].hh.b1 {characterizes the path leaving this knot}
+@d endpoint=0 {|left_type| at path beginning and |right_type| at path end}
+@d x_coord(#) == mem[#+1].sc {the |x| coordinate of this knot}
+@d y_coord(#) == mem[#+2].sc {the |y| coordinate of this knot}
+@d left_x(#) == mem[#+3].sc {the |x| coordinate of previous control point}
+@d left_y(#) == mem[#+4].sc {the |y| coordinate of previous control point}
+@d right_x(#) == mem[#+5].sc {the |x| coordinate of next control point}
+@d right_y(#) == mem[#+6].sc {the |y| coordinate of next control point}
+@d knot_node_size=7 {number of words in a knot node}
+
+@ Before the B\'ezier control points have been calculated, the memory
+space they will ultimately occupy is taken up by information that can be
+used to compute them. There are four cases:
+
+\yskip
+\textindent{$\bullet$} If |right_type=open|, the curve should leave
+the knot in the same direction it entered; \MF\ will figure out a
+suitable direction.
+
+\yskip
+\textindent{$\bullet$} If |right_type=curl|, the curve should leave the
+knot in a direction depending on the angle at which it enters the next
+knot and on the curl parameter stored in |right_curl|.
+
+\yskip
+\textindent{$\bullet$} If |right_type=given|, the curve should leave the
+knot in a nonzero direction stored as an |angle| in |right_given|.
+
+\yskip
+\textindent{$\bullet$} If |right_type=explicit|, the B\'ezier control
+point for leaving this knot has already been computed; it is in the
+|right_x| and |right_y| fields.
+
+\yskip\noindent
+The rules for |left_type| are similar, but they refer to the curve entering
+the knot, and to \\{left} fields instead of \\{right} fields.
+
+Non-|explicit| control points will be chosen based on ``tension'' parameters
+in the |left_tension| and |right_tension| fields. The
+`\&{atleast}' option is represented by negative tension values.
+@:at_least_}{\&{atleast} primitive@>
+
+For example, the \MF\ path specification
+$$\.{z0..z1..tension atleast 1..\{curl 2\}z2..z3\{-1,-2\}..tension
+ 3 and 4..p},$$
+where \.p is the path `\.{z4..controls z45 and z54..z5}', will be represented
+by the six knots
+\def\lodash{\hbox to 1.1em{\thinspace\hrulefill\thinspace}}
+$$\vbox{\halign{#\hfil&&\qquad#\hfil\cr
+|left_type|&\\{left} info&|x_coord,y_coord|&|right_type|&\\{right} info\cr
+\noalign{\yskip}
+|endpoint|&\lodash$,\,$\lodash&$x_0,y_0$&|curl|&$1.0,1.0$\cr
+|open|&\lodash$,1.0$&$x_1,y_1$&|open|&\lodash$,-1.0$\cr
+|curl|&$2.0,-1.0$&$x_2,y_2$&|curl|&$2.0,1.0$\cr
+|given|&$d,1.0$&$x_3,y_3$&|given|&$d,3.0$\cr
+|open|&\lodash$,4.0$&$x_4,y_4$&|explicit|&$x_{45},y_{45}$\cr
+|explicit|&$x_{54},y_{54}$&$x_5,y_5$&|endpoint|&\lodash$,\,$\lodash\cr}}$$
+Here |d| is the |angle| obtained by calling |n_arg(-unity,-two)|.
+Of course, this example is more complicated than anything a normal user
+would ever write.
+
+These types must satisfy certain restrictions because of the form of \MF's
+path syntax:
+(i)~|open| type never appears in the same node together with |endpoint|,
+|given|, or |curl|.
+(ii)~The |right_type| of a node is |explicit| if and only if the
+|left_type| of the following node is |explicit|.
+(iii)~|endpoint| types occur only at the ends, as mentioned above.
+
+@d left_curl==left_x {curl information when entering this knot}
+@d left_given==left_x {given direction when entering this knot}
+@d left_tension==left_y {tension information when entering this knot}
+@d right_curl==right_x {curl information when leaving this knot}
+@d right_given==right_x {given direction when leaving this knot}
+@d right_tension==right_y {tension information when leaving this knot}
+@d explicit=1 {|left_type| or |right_type| when control points are known}
+@d given=2 {|left_type| or |right_type| when a direction is given}
+@d curl=3 {|left_type| or |right_type| when a curl is desired}
+@d open=4 {|left_type| or |right_type| when \MF\ should choose the direction}
+
+@ Here is a diagnostic routine that prints a given knot list
+in symbolic form. It illustrates the conventions discussed above,
+and checks for anomalies that might arise while \MF\ is being debugged.
+
+@<Declare subroutines for printing expressions@>=
+procedure print_path(@!h:pointer;@!s:str_number;@!nuline:boolean);
+label done,done1;
+var @!p,@!q:pointer; {for list traversal}
+begin print_diagnostic("Path",s,nuline); print_ln;
+@.Path at line...@>
+p:=h;
+repeat q:=link(p);
+if (p=null)or(q=null) then
+ begin print_nl("???"); goto done; {this won't happen}
+@.???@>
+ end;
+@<Print information for adjacent knots |p| and |q|@>;
+p:=q;
+if (p<>h)or(left_type(h)<>endpoint) then
+ @<Print two dots, followed by |given| or |curl| if present@>;
+until p=h;
+if left_type(h)<>endpoint then print("cycle");
+done:end_diagnostic(true);
+end;
+
+@ @<Print information for adjacent knots...@>=
+print_two(x_coord(p),y_coord(p));
+case right_type(p) of
+endpoint: begin if left_type(p)=open then print("{open?}"); {can't happen}
+@.open?@>
+ if (left_type(q)<>endpoint)or(q<>h) then q:=null; {force an error}
+ goto done1;
+ end;
+explicit: @<Print control points between |p| and |q|, then |goto done1|@>;
+open: @<Print information for a curve that begins |open|@>;
+curl,given: @<Print information for a curve that begins |curl| or |given|@>;
+othercases print("???") {can't happen}
+@.???@>
+endcases;@/
+if left_type(q)<=explicit then print("..control?") {can't happen}
+@.control?@>
+else if (right_tension(p)<>unity)or(left_tension(q)<>unity) then
+ @<Print tension between |p| and |q|@>;
+done1:
+
+@ Since |n_sin_cos| produces |fraction| results, which we will print as if they
+were |scaled|, the magnitude of a |given| direction vector will be~4096.
+
+@<Print two dots...@>=
+begin print_nl(" ..");
+if left_type(p)=given then
+ begin n_sin_cos(left_given(p)); print_char("{");
+ print_scaled(n_cos); print_char(",");
+ print_scaled(n_sin); print_char("}");
+ end
+else if left_type(p)=curl then
+ begin print("{curl "); print_scaled(left_curl(p)); print_char("}");
+ end;
+end
+
+@ @<Print tension between |p| and |q|@>=
+begin print("..tension ");
+if right_tension(p)<0 then print("atleast");
+print_scaled(abs(right_tension(p)));
+if right_tension(p)<>left_tension(q) then
+ begin print(" and ");
+ if left_tension(q)<0 then print("atleast");
+ print_scaled(abs(left_tension(q)));
+ end;
+end
+
+@ @<Print control points between |p| and |q|, then |goto done1|@>=
+begin print("..controls "); print_two(right_x(p),right_y(p)); print(" and ");
+if left_type(q)<>explicit then print("??") {can't happen}
+@.??@>
+else print_two(left_x(q),left_y(q));
+goto done1;
+end
+
+@ @<Print information for a curve that begins |open|@>=
+if (left_type(p)<>explicit)and(left_type(p)<>open) then
+ print("{open?}") {can't happen}
+@.open?@>
+
+@ A curl of 1 is shown explicitly, so that the user sees clearly that
+\MF's default curl is present.
+
+@<Print information for a curve that begins |curl|...@>=
+begin if left_type(p)=open then print("??"); {can't happen}
+@.??@>
+if right_type(p)=curl then
+ begin print("{curl "); print_scaled(right_curl(p));
+ end
+else begin n_sin_cos(right_given(p)); print_char("{");
+ print_scaled(n_cos); print_char(","); print_scaled(n_sin);
+ end;
+print_char("}");
+end
+
+@ If we want to duplicate a knot node, we can say |copy_knot|:
+
+@p function copy_knot(@!p:pointer):pointer;
+var @!q:pointer; {the copy}
+@!k:0..knot_node_size-1; {runs through the words of a knot node}
+begin q:=get_node(knot_node_size);
+for k:=0 to knot_node_size-1 do mem[q+k]:=mem[p+k];
+copy_knot:=q;
+end;
+
+@ The |copy_path| routine makes a clone of a given path.
+
+@p function copy_path(@!p:pointer):pointer;
+label exit;
+var @!q,@!pp,@!qq:pointer; {for list manipulation}
+begin q:=get_node(knot_node_size); {this will correspond to |p|}
+qq:=q; pp:=p;
+loop@+ begin left_type(qq):=left_type(pp);
+ right_type(qq):=right_type(pp);@/
+ x_coord(qq):=x_coord(pp); y_coord(qq):=y_coord(pp);@/
+ left_x(qq):=left_x(pp); left_y(qq):=left_y(pp);@/
+ right_x(qq):=right_x(pp); right_y(qq):=right_y(pp);@/
+ if link(pp)=p then
+ begin link(qq):=q; copy_path:=q; return;
+ end;
+ link(qq):=get_node(knot_node_size); qq:=link(qq); pp:=link(pp);
+ end;
+exit:end;
+
+@ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
+returns a pointer to the first node of the copy, if the path is a cycle,
+but to the final node of a non-cyclic copy. The global
+variable |path_tail| will point to the final node of the original path;
+this trick makes it easier to implement `\&{doublepath}'.
+
+All node types are assumed to be |endpoint| or |explicit| only.
+
+@p function htap_ypoc(@!p:pointer):pointer;
+label exit;
+var @!q,@!pp,@!qq,@!rr:pointer; {for list manipulation}
+begin q:=get_node(knot_node_size); {this will correspond to |p|}
+qq:=q; pp:=p;
+loop@+ begin right_type(qq):=left_type(pp); left_type(qq):=right_type(pp);@/
+ x_coord(qq):=x_coord(pp); y_coord(qq):=y_coord(pp);@/
+ right_x(qq):=left_x(pp); right_y(qq):=left_y(pp);@/
+ left_x(qq):=right_x(pp); left_y(qq):=right_y(pp);@/
+ if link(pp)=p then
+ begin link(q):=qq; path_tail:=pp; htap_ypoc:=q; return;
+ end;
+ rr:=get_node(knot_node_size); link(rr):=qq; qq:=rr; pp:=link(pp);
+ end;
+exit:end;
+
+@ @<Glob...@>=
+@!path_tail:pointer; {the node that links to the beginning of a path}
+
+@ When a cyclic list of knot nodes is no longer needed, it can be recycled by
+calling the following subroutine.
+
+@<Declare the recycling subroutines@>=
+procedure toss_knot_list(@!p:pointer);
+var @!q:pointer; {the node being freed}
+@!r:pointer; {the next node}
+begin q:=p;
+repeat r:=link(q); free_node(q,knot_node_size); q:=r;
+until q=p;
+end;
+
+@* \[18] Choosing control points.
+Now we must actually delve into one of \MF's more difficult routines,
+the |make_choices| procedure that chooses angles and control points for
+the splines of a curve when the user has not specified them explicitly.
+The parameter to |make_choices| points to a list of knots and
+path information, as described above.
+
+A path decomposes into independent segments at ``breakpoint'' knots,
+which are knots whose left and right angles are both prespecified in
+some way (i.e., their |left_type| and |right_type| aren't both open).
+
+@p @t\4@>@<Declare the procedure called |solve_choices|@>@;
+procedure make_choices(@!knots:pointer);
+label done;
+var @!h:pointer; {the first breakpoint}
+@!p,@!q:pointer; {consecutive breakpoints being processed}
+@<Other local variables for |make_choices|@>@;
+begin check_arith; {make sure that |arith_error=false|}
+if internal[tracing_choices]>0 then
+ print_path(knots,", before choices",true);
+@<If consecutive knots are equal, join them explicitly@>;
+@<Find the first breakpoint, |h|, on the path;
+ insert an artificial breakpoint if the path is an unbroken cycle@>;
+p:=h;
+repeat @<Fill in the control points between |p| and the next breakpoint,
+ then advance |p| to that breakpoint@>;
+until p=h;
+if internal[tracing_choices]>0 then
+ print_path(knots,", after choices",true);
+if arith_error then @<Report an unexpected problem during the choice-making@>;
+end;
+
+@ @<Report an unexpected problem during the choice...@>=
+begin print_err("Some number got too big");
+@.Some number got too big@>
+help2("The path that I just computed is out of range.")@/
+ ("So it will probably look funny. Proceed, for a laugh.");
+put_get_error; arith_error:=false;
+end
+
+@ Two knots in a row with the same coordinates will always be joined
+by an explicit ``curve'' whose control points are identical with the
+knots.
+
+@<If consecutive knots are equal, join them explicitly@>=
+p:=knots;
+repeat q:=link(p);
+if x_coord(p)=x_coord(q) then if y_coord(p)=y_coord(q) then
+ if right_type(p)>explicit then
+ begin right_type(p):=explicit;
+ if left_type(p)=open then
+ begin left_type(p):=curl; left_curl(p):=unity;
+ end;
+ left_type(q):=explicit;
+ if right_type(q)=open then
+ begin right_type(q):=curl; right_curl(q):=unity;
+ end;
+ right_x(p):=x_coord(p); left_x(q):=x_coord(p);@/
+ right_y(p):=y_coord(p); left_y(q):=y_coord(p);
+ end;
+p:=q;
+until p=knots
+
+@ If there are no breakpoints, it is necessary to compute the direction
+angles around an entire cycle. In this case the |left_type| of the first
+node is temporarily changed to |end_cycle|.
+
+@d end_cycle=open+1
+
+@<Find the first breakpoint, |h|, on the path...@>=
+h:=knots;
+loop@+ begin if left_type(h)<>open then goto done;
+ if right_type(h)<>open then goto done;
+ h:=link(h);
+ if h=knots then
+ begin left_type(h):=end_cycle; goto done;
+ end;
+ end;
+done:
+
+@ If |right_type(p)<given| and |q=link(p)|, we must have
+|right_type(p)=left_type(q)=explicit| or |endpoint|.
+
+@<Fill in the control points between |p| and the next breakpoint...@>=
+q:=link(p);
+if right_type(p)>=given then
+ begin while (left_type(q)=open)and(right_type(q)=open) do q:=link(q);
+ @<Fill in the control information between
+ consecutive breakpoints |p| and |q|@>;
+ end;
+p:=q
+
+@ Before we can go further into the way choices are made, we need to
+consider the underlying theory. The basic ideas implemented in |make_choices|
+are due to John Hobby, who introduced the notion of ``mock curvature''
+@^Hobby, John Douglas@>
+at a knot. Angles are chosen so that they preserve mock curvature when
+a knot is passed, and this has been found to produce excellent results.
+
+It is convenient to introduce some notations that simplify the necessary
+formulas. Let $d_{k,k+1}=\vert z\k-z_k\vert$ be the (nonzero) distance
+between knots |k| and |k+1|; and let
+$${z\k-z_k\over z_k-z_{k-1}}={d_{k,k+1}\over d_{k-1,k}}e^{i\psi_k}$$
+so that a polygonal line from $z_{k-1}$ to $z_k$ to $z\k$ turns left
+through an angle of~$\psi_k$. We assume that $\vert\psi_k\vert\L180^\circ$.
+The control points for the spline from $z_k$ to $z\k$ will be denoted by
+$$\eqalign{z_k^+&=z_k+
+ \textstyle{1\over3}\rho_k e^{i\theta_k}(z\k-z_k),\cr
+ z\k^-&=z\k-
+ \textstyle{1\over3}\sigma\k e^{-i\phi\k}(z\k-z_k),\cr}$$
+where $\rho_k$ and $\sigma\k$ are nonnegative ``velocity ratios'' at the
+beginning and end of the curve, while $\theta_k$ and $\phi\k$ are the
+corresponding ``offset angles.'' These angles satisfy the condition
+$$\theta_k+\phi_k+\psi_k=0,\eqno(*)$$
+whenever the curve leaves an intermediate knot~|k| in the direction that
+it enters.
+
+@ Let $\alpha_k$ and $\beta\k$ be the reciprocals of the ``tension'' of
+the curve at its beginning and ending points. This means that
+$\rho_k=\alpha_k f(\theta_k,\phi\k)$ and $\sigma\k=\beta\k f(\phi\k,\theta_k)$,
+where $f(\theta,\phi)$ is \MF's standard velocity function defined in
+the |velocity| subroutine. The cubic spline $B(z_k^{\phantom+},z_k^+,
+z\k^-,z\k^{\phantom+};t)$
+has curvature
+@^curvature@>
+$${2\sigma\k\sin(\theta_k+\phi\k)-6\sin\theta_k\over\rho_k^2d_{k,k+1}}
+\qquad{\rm and}\qquad
+{2\rho_k\sin(\theta_k+\phi\k)-6\sin\phi\k\over\sigma\k^2d_{k,k+1}}$$
+at |t=0| and |t=1|, respectively. The mock curvature is the linear
+@^mock curvature@>
+approximation to this true curvature that arises in the limit for
+small $\theta_k$ and~$\phi\k$, if second-order terms are discarded.
+The standard velocity function satisfies
+$$f(\theta,\phi)=1+O(\theta^2+\theta\phi+\phi^2);$$
+hence the mock curvatures are respectively
+$${2\beta\k(\theta_k+\phi\k)-6\theta_k\over\alpha_k^2d_{k,k+1}}
+\qquad{\rm and}\qquad
+{2\alpha_k(\theta_k+\phi\k)-6\phi\k\over\beta\k^2d_{k,k+1}}.\eqno(**)$$
+
+@ The turning angles $\psi_k$ are given, and equation $(*)$ above
+determines $\phi_k$ when $\theta_k$ is known, so the task of
+angle selection is essentially to choose appropriate values for each
+$\theta_k$. When equation~$(*)$ is used to eliminate $\phi$~variables
+from $(**)$, we obtain a system of linear equations of the form
+$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
+where
+$$A_k={\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
+\qquad B_k={3-\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
+\qquad C_k={3-\beta\k\over\alpha_k^2d_{k,k+1}},
+\qquad D_k={\beta\k\over\alpha_k^2d_{k,k+1}}.$$
+The tensions are always $3\over4$ or more, hence each $\alpha$ and~$\beta$
+will be at most $4\over3$. It follows that $B_k\G{5\over4}A_k$ and
+$C_k\G{5\over4}D_k$; hence the equations are diagonally dominant;
+hence they have a unique solution. Moreover, in most cases the tensions
+are equal to~1, so that $B_k=2A_k$ and $C_k=2D_k$. This makes the
+solution numerically stable, and there is an exponential damping
+effect: The data at knot $k\pm j$ affects the angle at knot~$k$ by
+a factor of~$O(2^{-j})$.
+
+@ However, we still must consider the angles at the starting and ending
+knots of a non-cyclic path. These angles might be given explicitly, or
+they might be specified implicitly in terms of an amount of ``curl.''
+
+Let's assume that angles need to be determined for a non-cyclic path
+starting at $z_0$ and ending at~$z_n$. Then equations of the form
+$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta_{k+1}=R_k$$
+have been given for $0<k<n$, and it will be convenient to introduce
+equations of the same form for $k=0$ and $k=n$, where
+$$A_0=B_0=C_n=D_n=0.$$
+If $\theta_0$ is supposed to have a given value $E_0$, we simply
+define $C_0=1$, $D_0=0$, and $R_0=E_0$. Otherwise a curl
+parameter, $\gamma_0$, has been specified at~$z_0$; this means
+that the mock curvature at $z_0$ should be $\gamma_0$ times the
+mock curvature at $z_1$; i.e.,
+$${2\beta_1(\theta_0+\phi_1)-6\theta_0\over\alpha_0^2d_{01}}
+=\gamma_0{2\alpha_0(\theta_0+\phi_1)-6\phi_1\over\beta_1^2d_{01}}.$$
+This equation simplifies to
+$$(\alpha_0\chi_0+3-\beta_1)\theta_0+
+ \bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\theta_1=
+ -\bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\psi_1,$$
+where $\chi_0=\alpha_0^2\gamma_0/\beta_1^2$; so we can set $C_0=
+\chi_0\alpha_0+3-\beta_1$, $D_0=(3-\alpha_0)\chi_0+\beta_1$, $R_0=-D_0\psi_1$.
+It can be shown that $C_0>0$ and $C_0B_1-A_1D_0>0$ when $\gamma_0\G0$,
+hence the linear equations remain nonsingular.
+
+Similar considerations apply at the right end, when the final angle $\phi_n$
+may or may not need to be determined. It is convenient to let $\psi_n=0$,
+hence $\theta_n=-\phi_n$. We either have an explicit equation $\theta_n=E_n$,
+or we have
+$$\bigl((3-\beta_n)\chi_n+\alpha_{n-1}\bigr)\theta_{n-1}+
+(\beta_n\chi_n+3-\alpha_{n-1})\theta_n=0,\qquad
+ \chi_n={\beta_n^2\gamma_n\over\alpha_{n-1}^2}.$$
+
+When |make_choices| chooses angles, it must compute the coefficients of
+these linear equations, then solve the equations. To compute the coefficients,
+it is necessary to compute arctangents of the given turning angles~$\psi_k$.
+When the equations are solved, the chosen directions $\theta_k$ are put
+back into the form of control points by essentially computing sines and
+cosines.
+
+@ OK, we are ready to make the hard choices of |make_choices|.
+Most of the work is relegated to an auxiliary procedure
+called |solve_choices|, which has been introduced to keep
+|make_choices| from being extremely long.
+
+@<Fill in the control information between...@>=
+@<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$;
+ set $n$ to the length of the path@>;
+@<Remove |open| types at the breakpoints@>;
+solve_choices(p,q,n)
+
+@ It's convenient to precompute quantities that will be needed several
+times later. The values of |delta_x[k]| and |delta_y[k]| will be the
+coordinates of $z\k-z_k$, and the magnitude of this vector will be
+|delta[k]=@t$d_{k,k+1}$@>|. The path angle $\psi_k$ between $z_k-z_{k-1}$
+and $z\k-z_k$ will be stored in |psi[k]|.
+
+@<Glob...@>=
+@!delta_x,@!delta_y,@!delta:array[0..path_size] of scaled; {knot differences}
+@!psi:array[1..path_size] of angle; {turning angles}
+
+@ @<Other local variables for |make_choices|@>=
+@!k,@!n:0..path_size; {current and final knot numbers}
+@!s,@!t:pointer; {registers for list traversal}
+@!delx,@!dely:scaled; {directions where |open| meets |explicit|}
+@!sine,@!cosine:fraction; {trig functions of various angles}
+
+@ @<Calculate the turning angles...@>=
+k:=0; s:=p; n:=path_size;
+repeat t:=link(s);
+delta_x[k]:=x_coord(t)-x_coord(s);
+delta_y[k]:=y_coord(t)-y_coord(s);
+delta[k]:=pyth_add(delta_x[k],delta_y[k]);
+if k>0 then
+ begin sine:=make_fraction(delta_y[k-1],delta[k-1]);
+ cosine:=make_fraction(delta_x[k-1],delta[k-1]);
+ psi[k]:=n_arg(take_fraction(delta_x[k],cosine)+
+ take_fraction(delta_y[k],sine),
+ take_fraction(delta_y[k],cosine)-
+ take_fraction(delta_x[k],sine));
+ end;
+@:METAFONT capacity exceeded path size}{\quad path size@>
+incr(k); s:=t;
+if k=path_size then overflow("path size",path_size);
+if s=q then n:=k;
+until (k>=n)and(left_type(s)<>end_cycle);
+if k=n then psi[n]:=0@+else psi[k]:=psi[1]
+
+@ When we get to this point of the code, |right_type(p)| is either
+|given| or |curl| or |open|. If it is |open|, we must have
+|left_type(p)=end_cycle| or |left_type(p)=explicit|. In the latter
+case, the |open| type is converted to |given|; however, if the
+velocity coming into this knot is zero, the |open| type is
+converted to a |curl|, since we don't know the incoming direction.
+
+Similarly, |left_type(q)| is either |given| or |curl| or |open| or
+|end_cycle|. The |open| possibility is reduced either to |given| or to |curl|.
+
+@<Remove |open| types at the breakpoints@>=
+if left_type(q)=open then
+ begin delx:=right_x(q)-x_coord(q); dely:=right_y(q)-y_coord(q);
+ if (delx=0)and(dely=0) then
+ begin left_type(q):=curl; left_curl(q):=unity;
+ end
+ else begin left_type(q):=given; left_given(q):=n_arg(delx,dely);
+ end;
+ end;
+if (right_type(p)=open)and(left_type(p)=explicit) then
+ begin delx:=x_coord(p)-left_x(p); dely:=y_coord(p)-left_y(p);
+ if (delx=0)and(dely=0) then
+ begin right_type(p):=curl; right_curl(p):=unity;
+ end
+ else begin right_type(p):=given; right_given(p):=n_arg(delx,dely);
+ end;
+ end
+
+@ Linear equations need to be solved whenever |n>1|; and also when |n=1|
+and exactly one of the breakpoints involves a curl. The simplest case occurs
+when |n=1| and there is a curl at both breakpoints; then we simply draw
+a straight line.
+
+But before coding up the simple cases, we might as well face the general case,
+since we must deal with it sooner or later, and since the general case
+is likely to give some insight into the way simple cases can be handled best.
+
+When there is no cycle, the linear equations to be solved form a tri-diagonal
+system, and we can apply the standard technique of Gaussian elimination
+to convert that system to a sequence of equations of the form
+$$\theta_0+u_0\theta_1=v_0,\quad
+\theta_1+u_1\theta_2=v_1,\quad\ldots,\quad
+\theta_{n-1}+u_{n-1}\theta_n=v_{n-1},\quad
+\theta_n=v_n.$$
+It is possible to do this diagonalization while generating the equations.
+Once $\theta_n$ is known, it is easy to determine $\theta_{n-1}$, \dots,
+$\theta_1$, $\theta_0$; thus, the equations will be solved.
+
+The procedure is slightly more complex when there is a cycle, but the
+basic idea will be nearly the same. In the cyclic case the right-hand
+sides will be $v_k+w_k\theta_0$ instead of simply $v_k$, and we will start
+the process off with $u_0=v_0=0$, $w_0=1$. The final equation will be not
+$\theta_n=v_n$ but $\theta_n+u_n\theta_1=v_n+w_n\theta_0$; an appropriate
+ending routine will take account of the fact that $\theta_n=\theta_0$ and
+eliminate the $w$'s from the system, after which the solution can be
+obtained as before.
+
+When $u_k$, $v_k$, and $w_k$ are being computed, the three pointer
+variables |r|, |s|,~|t| will point respectively to knots |k-1|, |k|,
+and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are
+of type |fraction|; the $\theta$'s and $v$'s are of type |angle|.
+
+@<Glob...@>=
+@!theta:array[0..path_size] of angle; {values of $\theta_k$}
+@!uu:array[0..path_size] of fraction; {values of $u_k$}
+@!vv:array[0..path_size] of angle; {values of $v_k$}
+@!ww:array[0..path_size] of fraction; {values of $w_k$}
+
+@ Our immediate problem is to get the ball rolling by setting up the
+first equation or by realizing that no equations are needed, and to fit
+this initialization into a framework suitable for the overall computation.
+
+@<Declare the procedure called |solve_choices|@>=
+@t\4@>@<Declare subroutines needed by |solve_choices|@>@;
+procedure solve_choices(@!p,@!q:pointer;@!n:halfword);
+label found,exit;
+var @!k:0..path_size; {current knot number}
+@!r,@!s,@!t:pointer; {registers for list traversal}
+@<Other local variables for |solve_choices|@>@;
+begin k:=0; s:=p;
+loop@+ begin t:=link(s);
+ if k=0 then @<Get the linear equations started; or |return|
+ with the control points in place, if linear equations
+ needn't be solved@>
+ else case left_type(s) of
+ end_cycle,open:@<Set up equation to match mock curvatures
+ at $z_k$; then |goto found| with $\theta_n$
+ adjusted to equal $\theta_0$, if a cycle has ended@>;
+ curl:@<Set up equation for a curl at $\theta_n$
+ and |goto found|@>;
+ given:@<Calculate the given value of $\theta_n$
+ and |goto found|@>;
+ end; {there are no other cases}
+ r:=s; s:=t; incr(k);
+ end;
+found:@<Finish choosing angles and assigning control points@>;
+exit:end;
+
+@ On the first time through the loop, we have |k=0| and |r| is not yet
+defined. The first linear equation, if any, will have $A_0=B_0=0$.
+
+@<Get the linear equations started...@>=
+case right_type(s) of
+given: if left_type(t)=given then @<Reduce to simple case of two givens
+ and |return|@>
+ else @<Set up the equation for a given value of $\theta_0$@>;
+curl: if left_type(t)=curl then @<Reduce to simple case of straight line
+ and |return|@>
+ else @<Set up the equation for a curl at $\theta_0$@>;
+open: begin uu[0]:=0; vv[0]:=0; ww[0]:=fraction_one;
+ end; {this begins a cycle}
+end {there are no other cases}
+
+@ The general equation that specifies equality of mock curvature at $z_k$ is
+$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
+as derived above. We want to combine this with the already-derived equation
+$\theta_{k-1}+u_{k-1}\theta_k=v_{k-1}+w_{k-1}\theta_0$ in order to obtain
+a new equation
+$\theta_k+u_k\theta\k=v_k+w_k\theta_0$. This can be done by dividing the
+equation
+$$(B_k-u_{k-1}A_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k-A_kv_{k-1}
+ -A_kw_{k-1}\theta_0$$
+by $B_k-u_{k-1}A_k+C_k$. The trick is to do this carefully with
+fixed-point arithmetic, avoiding the chance of overflow while retaining
+suitable precision.
+
+The calculations will be performed in several registers that
+provide temporary storage for intermediate quantities.
+
+@<Other local variables for |solve_choices|@>=
+@!aa,@!bb,@!cc,@!ff,@!acc:fraction; {temporary registers}
+@!dd,@!ee:scaled; {likewise, but |scaled|}
+@!lt,@!rt:scaled; {tension values}
+
+@ @<Set up equation to match mock curvatures...@>=
+begin @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$,
+ $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$,
+ and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>;
+@<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>;
+uu[k]:=take_fraction(ff,bb);
+@<Calculate the values of $v_k$ and $w_k$@>;
+if left_type(s)=end_cycle then
+ @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>;
+end
+
+@ Since tension values are never less than 3/4, the values |aa| and
+|bb| computed here are never more than 4/5.
+
+@<Calculate the values $\\{aa}=...@>=
+if abs(right_tension(r))=unity then
+ begin aa:=fraction_half; dd:=2*delta[k];
+ end
+else begin aa:=make_fraction(unity,3*abs(right_tension(r))-unity);
+ dd:=take_fraction(delta[k],
+ fraction_three-make_fraction(unity,abs(right_tension(r))));
+ end;
+if abs(left_tension(t))=unity then
+ begin bb:=fraction_half; ee:=2*delta[k-1];
+ end
+else begin bb:=make_fraction(unity,3*abs(left_tension(t))-unity);
+ ee:=take_fraction(delta[k-1],
+ fraction_three-make_fraction(unity,abs(left_tension(t))));
+ end;
+cc:=fraction_one-take_fraction(uu[k-1],aa)
+
+@ The ratio to be calculated in this step can be written in the form
+$$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot
+ \\{cc}\cdot\\{dd},$$
+because of the quantities just calculated. The values of |dd| and |ee|
+will not be needed after this step has been performed.
+
+@<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>=
+dd:=take_fraction(dd,cc); lt:=abs(left_tension(s)); rt:=abs(right_tension(s));
+if lt<>rt then {$\beta_k^{-1}\ne\alpha_k^{-1}$}
+ if lt<rt then
+ begin ff:=make_fraction(lt,rt);
+ ff:=take_fraction(ff,ff); {$\alpha_k^2/\beta_k^2$}
+ dd:=take_fraction(dd,ff);
+ end
+ else begin ff:=make_fraction(rt,lt);
+ ff:=take_fraction(ff,ff); {$\beta_k^2/\alpha_k^2$}
+ ee:=take_fraction(ee,ff);
+ end;
+ff:=make_fraction(ee,ee+dd)
+
+@ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous
+equation was specified by a curl. In that case we must use a special
+method of computation to prevent overflow.
+
+Fortunately, the calculations turn out to be even simpler in this ``hard''
+case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence
+$-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$.
+
+@<Calculate the values of $v_k$ and $w_k$@>=
+acc:=-take_fraction(psi[k+1],uu[k]);
+if right_type(r)=curl then
+ begin ww[k]:=0;
+ vv[k]:=acc-take_fraction(psi[1],fraction_one-ff);
+ end
+else begin ff:=make_fraction(fraction_one-ff,cc); {this is
+ $B_k/(C_k+B_k-u_{k-1}A_k)<5$}
+ acc:=acc-take_fraction(psi[k],ff);
+ ff:=take_fraction(ff,aa); {this is $A_k/(C_k+B_k-u_{k-1}A_k)$}
+ vv[k]:=acc-take_fraction(vv[k-1],ff);
+ if ww[k-1]=0 then ww[k]:=0
+ else ww[k]:=-take_fraction(ww[k-1],ff);
+ end
+
+@ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k=
+v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of
+$\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$
+for |0<=k<n|, so that the cyclic case can be finished up just as if there
+were no cycle.
+
+The idea in the following code is to observe that
+$$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr
+&=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots
+ -u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0)\ldots{})\bigr),\cr}$$
+so we can solve for $\theta_n=\theta_0$.
+
+@<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>=
+begin aa:=0; bb:=fraction_one; {we have |k=n|}
+repeat decr(k);
+if k=0 then k:=n;
+aa:=vv[k]-take_fraction(aa,uu[k]);
+bb:=ww[k]-take_fraction(bb,uu[k]);
+until k=n; {now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$}
+aa:=make_fraction(aa,fraction_one-bb);
+theta[n]:=aa; vv[0]:=aa;
+for k:=1 to n-1 do vv[k]:=vv[k]+take_fraction(aa,ww[k]);
+goto found;
+end
+
+@ @d reduce_angle(#)==if abs(#)>one_eighty_deg then
+ if #>0 then #:=#-three_sixty_deg@+else #:=#+three_sixty_deg
+
+@<Calculate the given value of $\theta_n$...@>=
+begin theta[n]:=left_given(s)-n_arg(delta_x[n-1],delta_y[n-1]);
+reduce_angle(theta[n]);
+goto found;
+end
+
+@ @<Set up the equation for a given value of $\theta_0$@>=
+begin vv[0]:=right_given(s)-n_arg(delta_x[0],delta_y[0]);
+reduce_angle(vv[0]);
+uu[0]:=0; ww[0]:=0;
+end
+
+@ @<Set up the equation for a curl at $\theta_0$@>=
+begin cc:=right_curl(s); lt:=abs(left_tension(t)); rt:=abs(right_tension(s));
+if (rt=unity)and(lt=unity) then
+ uu[0]:=make_fraction(cc+cc+unity,cc+two)
+else uu[0]:=curl_ratio(cc,rt,lt);
+vv[0]:=-take_fraction(psi[1],uu[0]); ww[0]:=0;
+end
+
+@ @<Set up equation for a curl at $\theta_n$...@>=
+begin cc:=left_curl(s); lt:=abs(left_tension(s)); rt:=abs(right_tension(r));
+if (rt=unity)and(lt=unity) then
+ ff:=make_fraction(cc+cc+unity,cc+two)
+else ff:=curl_ratio(cc,lt,rt);
+theta[n]:=-make_fraction(take_fraction(vv[n-1],ff),
+ fraction_one-take_fraction(ff,uu[n-1]));
+goto found;
+end
+
+@ The |curl_ratio| subroutine has three arguments, which our previous notation
+encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is
+a somewhat tedious program to calculate
+$${(3-\alpha)\alpha^2\gamma+\beta^3\over
+ \alpha^3\gamma+(3-\beta)\beta^2},$$
+with the result reduced to 4 if it exceeds 4. (This reduction of curl
+is necessary only if the curl and tension are both large.)
+The values of $\alpha$ and $\beta$ will be at most~4/3.
+
+@<Declare subroutines needed by |solve_choices|@>=
+function curl_ratio(@!gamma,@!a_tension,@!b_tension:scaled):fraction;
+var @!alpha,@!beta,@!num,@!denom,@!ff:fraction; {registers}
+begin alpha:=make_fraction(unity,a_tension);
+beta:=make_fraction(unity,b_tension);@/
+if alpha<=beta then
+ begin ff:=make_fraction(alpha,beta); ff:=take_fraction(ff,ff);
+ gamma:=take_fraction(gamma,ff);@/
+ beta:=beta div @'10000; {convert |fraction| to |scaled|}
+ denom:=take_fraction(gamma,alpha)+three-beta;
+ num:=take_fraction(gamma,fraction_three-alpha)+beta;
+ end
+else begin ff:=make_fraction(beta,alpha); ff:=take_fraction(ff,ff);
+ beta:=take_fraction(beta,ff) div @'10000; {convert |fraction| to |scaled|}
+ denom:=take_fraction(gamma,alpha)+(ff div 1365)-beta;
+ {$1365\approx 2^{12}/3$}
+ num:=take_fraction(gamma,fraction_three-alpha)+beta;
+ end;
+if num>=denom+denom+denom+denom then curl_ratio:=fraction_four
+else curl_ratio:=make_fraction(num,denom);
+end;
+
+@ We're in the home stretch now.
+
+@<Finish choosing angles and assigning control points@>=
+for k:=n-1 downto 0 do theta[k]:=vv[k]-take_fraction(theta[k+1],uu[k]);
+s:=p; k:=0;
+repeat t:=link(s);@/
+n_sin_cos(theta[k]); st:=n_sin; ct:=n_cos;@/
+n_sin_cos(-psi[k+1]-theta[k+1]); sf:=n_sin; cf:=n_cos;@/
+set_controls(s,t,k);@/
+incr(k); s:=t;
+until k=n
+
+@ The |set_controls| routine actually puts the control points into
+a pair of consecutive nodes |p| and~|q|. Global variables are used to
+record the values of $\sin\theta$, $\cos\theta$, $\sin\phi$, and
+$\cos\phi$ needed in this calculation.
+
+@<Glob...@>=
+@!st,@!ct,@!sf,@!cf:fraction; {sines and cosines}
+
+@ @<Declare subroutines needed by |solve_choices|@>=
+procedure set_controls(@!p,@!q:pointer;@!k:integer);
+var @!rr,@!ss:fraction; {velocities, divided by thrice the tension}
+@!lt,@!rt:scaled; {tensions}
+@!sine:fraction; {$\sin(\theta+\phi)$}
+begin lt:=abs(left_tension(q)); rt:=abs(right_tension(p));
+rr:=velocity(st,ct,sf,cf,rt);
+ss:=velocity(sf,cf,st,ct,lt);
+if (right_tension(p)<0)or(left_tension(q)<0) then @<Decrease the velocities,
+ if necessary, to stay inside the bounding triangle@>;
+right_x(p):=x_coord(p)+take_fraction(
+ take_fraction(delta_x[k],ct)-take_fraction(delta_y[k],st),rr);
+right_y(p):=y_coord(p)+take_fraction(
+ take_fraction(delta_y[k],ct)+take_fraction(delta_x[k],st),rr);
+left_x(q):=x_coord(q)-take_fraction(
+ take_fraction(delta_x[k],cf)+take_fraction(delta_y[k],sf),ss);
+left_y(q):=y_coord(q)-take_fraction(
+ take_fraction(delta_y[k],cf)-take_fraction(delta_x[k],sf),ss);
+right_type(p):=explicit; left_type(q):=explicit;
+end;
+
+@ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and
+$\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$,
+$\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise
+there is no ``bounding triangle.''
+
+@<Decrease the velocities, if necessary...@>=
+if((st>=0)and(sf>=0))or((st<=0)and(sf<=0)) then
+ begin sine:=take_fraction(abs(st),cf)+take_fraction(abs(sf),ct);
+ if sine>0 then
+ begin sine:=take_fraction(sine,fraction_one+unity); {safety factor}
+ if right_tension(p)<0 then
+ if ab_vs_cd(abs(sf),fraction_one,rr,sine)<0 then
+ rr:=make_fraction(abs(sf),sine);
+ if left_tension(q)<0 then
+ if ab_vs_cd(abs(st),fraction_one,ss,sine)<0 then
+ ss:=make_fraction(abs(st),sine);
+ end;
+ end
+
+@ Only the simple cases remain to be handled.
+
+@<Reduce to simple case of two givens and |return|@>=
+begin aa:=n_arg(delta_x[0],delta_y[0]);@/
+n_sin_cos(right_given(p)-aa); ct:=n_cos; st:=n_sin;@/
+n_sin_cos(left_given(q)-aa); cf:=n_cos; sf:=-n_sin;@/
+set_controls(p,q,0); return;
+end
+
+@ @<Reduce to simple case of straight line and |return|@>=
+begin right_type(p):=explicit; left_type(q):=explicit;
+lt:=abs(left_tension(q)); rt:=abs(right_tension(p));
+if rt=unity then
+ begin if delta_x[0]>=0 then right_x(p):=x_coord(p)+((delta_x[0]+1) div 3)
+ else right_x(p):=x_coord(p)+((delta_x[0]-1) div 3);
+ if delta_y[0]>=0 then right_y(p):=y_coord(p)+((delta_y[0]+1) div 3)
+ else right_y(p):=y_coord(p)+((delta_y[0]-1) div 3);
+ end
+else begin ff:=make_fraction(unity,3*rt); {$\alpha/3$}
+ right_x(p):=x_coord(p)+take_fraction(delta_x[0],ff);
+ right_y(p):=y_coord(p)+take_fraction(delta_y[0],ff);
+ end;
+if lt=unity then
+ begin if delta_x[0]>=0 then left_x(q):=x_coord(q)-((delta_x[0]+1) div 3)
+ else left_x(q):=x_coord(q)-((delta_x[0]-1) div 3);
+ if delta_y[0]>=0 then left_y(q):=y_coord(q)-((delta_y[0]+1) div 3)
+ else left_y(q):=y_coord(q)-((delta_y[0]-1) div 3);
+ end
+else begin ff:=make_fraction(unity,3*lt); {$\beta/3$}
+ left_x(q):=x_coord(q)-take_fraction(delta_x[0],ff);
+ left_y(q):=y_coord(q)-take_fraction(delta_y[0],ff);
+ end;
+return;
+end
+
+@* \[19] Generating discrete moves.
+The purpose of the next part of \MF\ is to compute discrete approximations
+to curves described as parametric polynomial functions $z(t)$.
+We shall start with the low level first, because an efficient ``engine''
+is needed to support the high-level constructions.
+
+Most of the subroutines are based on variations of a single theme,
+namely the idea of {\sl bisection}. Given a Bernshte{\u\i}n polynomial
+@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
+$$B(z_0,z_1,\ldots,z_n;t)=\sum_k{n\choose k}t^k(1-t)^{n-k}z_k,$$
+we can conveniently bisect its range as follows:
+
+\smallskip
+\textindent{1)} Let $z_k^{(0)}=z_k$, for |0<=k<=n|.
+
+\smallskip
+\textindent{2)} Let $z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$, for
+|0<=k<n-j|, for |0<=j<n|.
+
+\smallskip\noindent
+Then
+$$B(z_0,z_1,\ldots,z_n;t)=B(z_0^{(0)},z_0^{(1)},\ldots,z_0^{(n)};2t)
+ =B(z_0^{(n)},z_1^{(n-1)},\ldots,z_n^{(0)};2t-1).$$
+This formula gives us the coefficients of polynomials to use over the ranges
+$0\L t\L{1\over2}$ and ${1\over2}\L t\L1$.
+
+In our applications it will usually be possible to work indirectly with
+numbers that allow us to deduce relevant properties of the polynomials
+without actually computing the polynomial values. We will deal with
+coefficients $Z_k=2^l(z_k-z_{k-1})$ for |1<=k<=n|, instead of
+the actual numbers $z_0$, $z_1$, \dots,~$z_n$, and the value of~|l| will
+increase by~1 at each bisection step. This technique reduces the
+amount of calculation needed for bisection and also increases the
+accuracy of evaluation (since one bit of precision is gained at each
+bisection). Indeed, the bisection process now becomes one level shorter:
+
+\smallskip
+\textindent{$1'$)} Let $Z_k^{(1)}=Z_k$, for |1<=k<=n|.
+
+\smallskip
+\textindent{$2'$)} Let $Z_k^{(j+1)}={1\over2}(Z_k^{(j)}+Z\k^{(j)})$, for
+|1<=k<=n-j|, for |1<=j<n|.
+
+\smallskip\noindent
+The relevant coefficients $(Z'_1,\ldots,Z'_n)$ and $(Z''_1,\ldots,Z''_n)$
+for the two subintervals after bisection are respectively
+$(Z_1^{(1)},Z_1^{(2)},\ldots,Z_1^{(n)})$ and
+$(Z_1^{(n)},Z_2^{(n-1)},\ldots,Z_n^{(1)})$.
+And the values of $z_0$ appropriate for the bisected interval are $z'_0=z_0$
+and $z''_0=z_0+(Z'_1+Z'_2+\cdots+Z'_n)/2^{l+1}$.
+
+Step $2'$ involves division by~2, which introduces computational errors
+of at most $1\over2$ at each step; thus after $l$~levels of bisection the
+integers $Z_k$ will differ from their true values by at most $(n-1)l/2$.
+This error rate is quite acceptable, considering that we have $l$~more
+bits of precision in the $Z$'s by comparison with the~$z$'s. Note also
+that the $Z$'s remain bounded; there's no danger of integer overflow, even
+though we have the identity $Z_k=2^l(z_k-z_{k-1})$ for arbitrarily large~$l$.
+
+In fact, we can show not only that the $Z$'s remain bounded, but also that
+they become nearly equal, since they are control points for a polynomial
+of one less degree. If $\vert Z\k-Z_k\vert\L M$ initially, it is possible
+to prove that $\vert Z\k-Z_k\vert\L\lceil M/2^l\rceil$ after $l$~levels
+of bisection, even in the presence of rounding errors. Here's the
+proof [cf.~Lane and Riesenfeld, {\sl IEEE Trans.\ on Pattern Analysis
+@^Lane, Jeffrey Michael@>
+@^Riesenfeld, Richard Franklin@>
+and Machine Intelligence\/ \bf PAMI-2} (1980), 35--46]: Assuming that
+$\vert Z\k-Z_k\vert\L M$ before bisection, we want to prove that
+$\vert Z\k-Z_k\vert\L\lceil M/2\rceil$ afterward. First we show that
+$\vert Z\k^{(j)}-Z_k^{(j)}\vert\L M$ for all $j$ and~$k$, by induction
+on~$j$; this follows from the fact that
+$$\bigl\vert\\{half}(a+b)-\\{half}(b+c)\bigr\vert\L
+ \max\bigl(\vert a-b\vert,\vert b-c\vert\bigr)$$
+holds for both of the rounding rules $\\{half}(x)=\lfloor x/2\rfloor$
+and $\\{half}(x)={\rm sign}(x)\lfloor\vert x/2\vert\rfloor$.
+(If $\vert a-b\vert$ and $\vert b-c\vert$ are equal, then
+$a+b$ and $b+c$ are both even or both odd. The rounding errors either
+cancel or round the numbers toward each other; hence
+$$\eqalign{\bigl\vert\\{half}(a+b)-\\{half}(b+c)\bigr\vert
+&\L\textstyle\bigl\vert{1\over2}(a+b)-{1\over2}(b+c)\bigr\vert\cr
+&=\textstyle\bigl\vert{1\over2}(a-b)+{1\over2}(b-c)\bigr\vert
+\L\max\bigl(\vert a-b\vert,\vert b-c\vert\bigr),\cr}$$
+as required. A simpler argument applies if $\vert a-b\vert$ and
+$\vert b-c\vert$ are unequal.) Now it is easy to see that
+$\vert Z_1^{(j+1)}-Z_1^{(j)}\vert\L\bigl\lfloor{1\over2}
+\vert Z_2^{(j)}-Z_1^{(j)}\vert+{1\over2}\bigr\rfloor
+\L\bigl\lfloor{1\over2}(M+1)\bigr\rfloor=\lceil M/2\rceil$.
+
+Another interesting fact about bisection is the identity
+$$Z_1'+\cdots+Z_n'+Z_1''+\cdots+Z_n''=2(Z_1+\cdots+Z_n+E),$$
+where $E$ is the sum of the rounding errors in all of the halving
+operations ($\vert E\vert\L n(n-1)/4$).
+
+@ We will later reduce the problem of digitizing a complex cubic
+$z(t)=B(z_0,z_1,z_2,z_3;t)$ to the following simpler problem:
+Given two real cubics
+$x(t)=B(x_0,x_1,x_2,x_3;t)$
+and $y(t)=B(y_0,y_1,y_2,y_3;t)$ that are monotone nondecreasing,
+determine the set of integer points
+$$P=\bigl\{\bigl(\lfloor x(t)\rfloor,\lfloor y(t)\rfloor\bigr)
+\bigm\vert 0\L t\L 1\bigr\}.$$
+Well, the problem isn't actually quite so clean as this; when the path
+goes very near an integer point $(a,b)$, computational errors may
+make us think that $P$ contains $(a-1,b)$ while in reality it should
+contain $(a,b-1)$. Furthermore, if the path goes {\sl exactly\/}
+through the integer points $(a-1,b-1)$ and
+$(a,b)$, we will want $P$ to contain one
+of the two points $(a-1,b)$ or $(a,b-1)$, so that $P$ can be described
+entirely by ``rook moves'' upwards or to the right; no diagonal
+moves from $(a-1,b-1)$ to~$(a,b)$ will be allowed.
+
+Thus, the set $P$ we wish to compute will merely be an approximation
+to the set described in the formula above. It will consist of
+$\lfloor x(1)\rfloor-\lfloor x(0)\rfloor$ rightward moves and
+$\lfloor y(1)\rfloor-\lfloor y(0)\rfloor$ upward moves, intermixed
+in some order. Our job will be to figure out a suitable order.
+
+The following recursive strategy suggests itself, when we recall that
+$x(0)=x_0$, $x(1)=x_3$, $y(0)=y_0$, and $y(1)=y_3$:
+
+\smallskip
+If $\lfloor x_0\rfloor=\lfloor x_3\rfloor$ then take
+$\lfloor y_3\rfloor-\lfloor y_0\rfloor$ steps up.
+
+Otherwise if $\lfloor y_0\rfloor=\lfloor y_3\rfloor$ then take
+$\lfloor x_3\rfloor-\lfloor x_0\rfloor$ steps to the right.
+
+Otherwise bisect the current cubics and repeat the process on both halves.
+
+\yskip\noindent
+This intuitively appealing formulation does not quite solve the problem,
+because it may never terminate. For example, it's not hard to see that
+no steps will {\sl ever\/} be taken if $(x_0,x_1,x_2,x_3)=(y_0,y_1,y_2,y_3)$!
+However, we can surmount this difficulty with a bit of care; so let's
+proceed to flesh out the algorithm as stated, before worrying about
+such details.
+
+The bisect-and-double strategy discussed above suggests that we represent
+$(x_0,x_1,x_2,x_3)$ by $(X_1,X_2,X_3)$, where $X_k=2^l(x_k-x_{k-1})$
+for some~$l$. Initially $l=16$, since the $x$'s are |scaled|.
+In order to deal with other aspects of the algorithm we will want to
+maintain also the quantities $m=\lfloor x_3\rfloor-\lfloor x_0\rfloor$
+and $R=2^l(x_0\bmod 1)$. Similarly,
+$(y_0,y_1,y_2,y_3)$ will be represented by $(Y_1,Y_2,Y_3)$,
+$n=\lfloor y_3\rfloor-\lfloor y_0\rfloor$,
+and $S=2^l(y_0\bmod 1)$. The algorithm now takes the following form:
+
+\smallskip
+If $m=0$ then take $n$ steps up.
+
+Otherwise if $n=0$ then take $m$ steps to the right.
+
+Otherwise bisect the current cubics and repeat the process on both halves.
+
+\smallskip\noindent
+The bisection process for $(X_1,X_2,X_3,m,R,l)$ reduces, in essence,
+to the following formulas:
+$$\vbox{\halign{$#\hfil$\cr
+X_2'=\\{half}(X_1+X_2),\quad
+X_2''=\\{half}(X_2+X_3),\quad
+X_3'=\\{half}(X_2'+X_2''),\cr
+X_1'=X_1,\quad
+X_1''=X_3',\quad
+X_3''=X_3,\cr
+R'=2R,\quad
+T=X_1'+X_2'+X_3'+R',\quad
+R''=T\bmod 2^{l+1},\cr
+m'=\lfloor T/2^{l+1}\rfloor,\quad
+m''=m-m'.\cr}}$$
+
+@ When $m=n=1$, the computation can be speeded up because we simply
+need to decide between two alternatives, (up,\thinspace right)
+versus (right,\thinspace up). There appears to be no simple, direct
+way to make the correct decision by looking at the values of
+$(X_1,X_2,X_3,R)$ and
+$(Y_1,Y_2,Y_3,S)$; but we can streamline the bisection process, and
+we can use the fact that only one of the two descendants needs to
+be examined after each bisection. Furthermore, we observed earlier
+that after several levels of bisection the $X$'s and $Y$'s will be nearly
+equal; so we will be justified in assuming that the curve is essentially a
+straight line. (This, incidentally, solves the problem of infinite
+recursion mentioned earlier.)
+
+It is possible to show that
+$$m=\bigl\lfloor(X_1+X_2+X_3+R+E)\,/\,2^l\bigr\rfloor,$$
+where $E$ is an accumulated rounding error that is at most
+$3\cdot(2^{l-16}-1)$ in absolute value. We will make sure that
+the $X$'s are less than $2^{28}$; hence when $l=30$ we must
+have |m<=1|. This proves that the special case $m=n=1$ is
+bound to be reached by the time $l=30$. Furthermore $l=30$ is
+a suitable time to make the straight line approximation,
+if the recursion hasn't already died out, because the maximum
+difference between $X$'s will then be $<2^{14}$; this corresponds
+to an error of $<1$ with respect to the original scaling.
+(Stating this another way, each bisection makes the curve two bits
+closer to a straight line, hence 14 bisections are sufficient for
+28-bit accuracy.)
+
+In the case of a straight line, the curve goes first right, then up,
+if and only if $(T-2^l)(2^l-S)>(U-2^l)(2^l-R)$, where
+$T=X_1+X_2+X_3+R$ and $U=Y_1+Y_2+Y_3+S$. For the actual curve
+essentially runs from $(R/2^l,S/2^l)$ to $(T/2^l,U/2^l)$, and
+we are testing whether or not $(1,1)$ is above the straight
+line connecting these two points. (This formula assumes that $(1,1)$
+is not exactly on the line.)
+
+@ We have glossed over the problem of tie-breaking in ambiguous
+cases when the cubic curve passes exactly through integer points.
+\MF\ finesses this problem by assuming that coordinates
+$(x,y)$ actually stand for slightly perturbed values $(x+\xi,y+\eta)$,
+where $\xi$ and~$\eta$ are infinitesimals whose signs will determine
+what to do when $x$ and/or~$y$ are exact integers. The quantities
+$\lfloor x\rfloor$ and~$\lfloor y\rfloor$ in the formulas above
+should actually read $\lfloor x+\xi\rfloor$ and $\lfloor y+\eta\rfloor$.
+
+If $x$ is a |scaled| value, we have $\lfloor x+\xi\rfloor=\lfloor x\rfloor$
+if $\xi>0$, and $\lfloor x+\xi\rfloor=\lfloor x-2^{-16}\rfloor$ if
+$\xi<0$. It is convenient to represent $\xi$ by the integer |xi_corr|,
+defined to be 0~if $\xi>0$ and 1~if $\xi<0$; then, for example, the
+integer $\lfloor x+\xi\rfloor$ can be computed as
+|floor_unscaled(x-xi_corr)|. Similarly, $\eta$ is conveniently
+represented by~|eta_corr|.
+
+In our applications the sign of $\xi-\eta$ will always be the same as
+the sign of $\xi$. Therefore it turns out that the rule for straight
+lines, as stated above, should be modified as follows in the case of
+ties: The line goes first right, then up, if and only if
+$(T-2^l)(2^l-S)+\xi>(U-2^l)(2^l-R)$. And this relation holds iff
+$|ab_vs_cd|(T-2^l,2^l-S,U-2^l,2^l-R)-|xi_corr|\ge0$.
+
+These conventions for rounding are symmetrical, in the sense that the
+digitized moves obtained from $(x_0,x_1,x_2,x_3,y_0,y_1,y_2,y_3,\xi,\eta)$
+will be exactly complementary to the moves that would be obtained from
+$(-x_3,-x_2,-x_1,-x_0,-y_3,-y_2,-y_1,-y_0,-\xi,-\eta)$, if arithmetic
+is exact. However, truncation errors in the bisection process might
+upset the symmetry. We can restore much of the lost symmetry by adding
+|xi_corr| or |eta_corr| when halving the data.
+
+@ One further possibility needs to be mentioned: The algorithm
+will be applied only to cubic polynomials $B(x_0,x_1,x_2,x_3;t)$ that
+are nondecreasing as $t$~varies from 0 to~1; this condition turns
+out to hold if and only if $x_0\L x_1$ and $x_2\L x_3$, and either
+$x_1\L x_2$ or $(x_1-x_2)^2\L(x_1-x_0)(x_3-x_2)$. If bisection were
+carried out with perfect accuracy, these relations would remain
+invariant. But rounding errors can creep in, hence the bisection
+algorithm can produce non-monotonic subproblems from monotonic
+initial conditions. This leads to the potential danger that $m$ or~$n$
+could become negative in the algorithm described above.
+
+For example, if we start with $(x_1-x_0,x_2-x_1,x_3-x_2)=
+(X_1,X_2,X_3)=(7,-16,39)$, the corresponding polynomial is
+monotonic, because $16^2<7\cdot39$. But the bisection algorithm
+produces the left descendant $(7,-5,3)$, which is nonmonotonic;
+its right descendant is~$(0,-1,3)$.
+
+\def\xt{{\tilde x}}
+Fortunately we can prove that such rounding errors will never cause
+the algorithm to make a tragic mistake. At every stage we are working
+with numbers corresponding to a cubic polynomial $B(\xt_0,
+\xt_1,\xt_2,\xt_3)$ that approximates some
+monotonic polynomial $B(x_0,x_1,x_2,x_3)$. The accumulated errors are
+controlled so that $\vert x_k-\xt_k\vert<\epsilon=3\cdot2^{-16}$.
+If bisection is done at some stage of the recursion, we have
+$m=\lfloor\xt_3\rfloor-\lfloor\xt_0\rfloor>0$, and the algorithm
+computes a bisection value $\bar x$ such that $m'=\lfloor\bar x\rfloor-
+\lfloor\xt_0\rfloor$
+and $m''=\lfloor\xt_3\rfloor-\lfloor\bar x\rfloor$. We want to prove
+that neither $m'$ nor $m''$ can be negative. Since $\bar x$ is an
+approximation to a value in the interval $[x_0,x_3]$, we have
+$\bar x>x_0-\epsilon$ and $\bar x<x_3+\epsilon$, hence $\bar x>
+\xt_0-2\epsilon$ and $\bar x<\xt_3+2\epsilon$.
+If $m'$ is negative we must have $\xt_0\bmod 1<2\epsilon$;
+if $m''$ is negative we must have $\xt_3\bmod 1>1-2\epsilon$.
+In either case the condition $\lfloor\xt_3\rfloor-\lfloor\xt_0\rfloor>0$
+implies that $\xt_3-\xt_0>1-2\epsilon$, hence $x_3-x_0>1-4\epsilon$.
+But it can be shown that if $B(x_0,x_1,x_2,x_3;t)$ is a monotonic
+cubic, then $B(x_0,x_1,x_2,x_3;{1\over2})$ is always between
+$.06[x_0,x_3]$ and $.94[x_0,x_3]$; and it is impossible for $\bar x$
+to be within~$\epsilon$ of such a number. Contradiction!
+(The constant .06 is actually $(2-\sqrt3\,)/4$; the worst case
+occurs for polynomials like $B(0,2-\sqrt3,1-\sqrt3,3;t)$.)
+
+@ OK, now that a long theoretical preamble has justified the
+bisection-and-doubling algorithm, we are ready to proceed with
+its actual coding. But we still haven't discussed the
+form of the output.
+
+For reasons to be discussed later, we shall find it convenient to
+record the output as follows: Moving one step up is represented by
+appending a `1' to a list; moving one step right is represented by
+adding unity to the element at the end of the list. Thus, for example,
+the net effect of ``(up, right, right, up, right)'' is to append
+$(3,2)$.
+
+The list is kept in a global array called |move|. Before starting the
+algorithm, \MF\ should check that $\\{move\_ptr}+\lfloor y_3\rfloor
+-\lfloor y_0\rfloor\L\\{move\_size}$, so that the list won't exceed
+the bounds of this array.
+
+@<Glob...@>=
+@!move:array[0..move_size] of integer; {the recorded moves}
+@!move_ptr:0..move_size; {the number of items in the |move| list}
+
+@ When bisection occurs, we ``push'' the subproblem corresponding
+to the right-hand subinterval onto the |bisect_stack| while
+we continue to work on the left-hand subinterval. Thus, the |bisect_stack|
+will hold $(X_1,X_2,X_3,R,m,Y_1,Y_2,Y_3,S,n,l)$ values for
+subproblems yet to be tackled.
+
+At most 15 subproblems will be on the stack at once (namely, for
+$l=15$,~16, \dots,~29); but the stack is bigger than this, because
+it is used also for more complicated bisection algorithms.
+
+@d stack_x1==bisect_stack[bisect_ptr] {stacked value of $X_1$}
+@d stack_x2==bisect_stack[bisect_ptr+1] {stacked value of $X_2$}
+@d stack_x3==bisect_stack[bisect_ptr+2] {stacked value of $X_3$}
+@d stack_r==bisect_stack[bisect_ptr+3] {stacked value of $R$}
+@d stack_m==bisect_stack[bisect_ptr+4] {stacked value of $m$}
+@d stack_y1==bisect_stack[bisect_ptr+5] {stacked value of $Y_1$}
+@d stack_y2==bisect_stack[bisect_ptr+6] {stacked value of $Y_2$}
+@d stack_y3==bisect_stack[bisect_ptr+7] {stacked value of $Y_3$}
+@d stack_s==bisect_stack[bisect_ptr+8] {stacked value of $S$}
+@d stack_n==bisect_stack[bisect_ptr+9] {stacked value of $n$}
+@d stack_l==bisect_stack[bisect_ptr+10] {stacked value of $l$}
+@d move_increment=11 {number of items pushed by |make_moves|}
+
+@<Glob...@>=
+@!bisect_stack:array[0..bistack_size] of integer;
+@!bisect_ptr:0..bistack_size;
+
+@ @<Check the ``constant'' values...@>=
+if 15*move_increment>bistack_size then bad:=31;
+
+@ The |make_moves| subroutine is given |scaled| values $(x_0,x_1,x_2,x_3)$
+and $(y_0,y_1,y_2,y_3)$ that represent monotone-nondecreasing polynomials;
+it makes $\lfloor x_3+\xi\rfloor-\lfloor x_0+\xi\rfloor$ rightward moves
+and $\lfloor y_3+\eta\rfloor-\lfloor y_0+\eta\rfloor$ upward moves, as
+explained earlier. (Here $\lfloor x+\xi\rfloor$ actually stands for
+$\lfloor x/2^{16}-|xi_corr|\rfloor$, if $x$ is regarded as an integer
+without scaling.) The unscaled integers $x_k$ and~$y_k$ should be less
+than $2^{28}$ in magnitude.
+
+It is assumed that $|move_ptr| + \lfloor y_3+\eta\rfloor -
+\lfloor y_0+\eta\rfloor < |move_size|$ when this procedure is called,
+so that the capacity of the |move| array will not be exceeded.
+
+The variables |r| and |s| in this procedure stand respectively for
+$R-|xi_corr|$ and $S-|eta_corr|$ in the theory discussed above.
+
+@p procedure make_moves(@!xx0,@!xx1,@!xx2,@!xx3,@!yy0,@!yy1,@!yy2,@!yy3:
+ scaled;@!xi_corr,@!eta_corr:small_number);
+label continue, done, exit;
+var @!x1,@!x2,@!x3,@!m,@!r,@!y1,@!y2,@!y3,@!n,@!s,@!l:integer;
+ {bisection variables explained above}
+@!q,@!t,@!u,@!x2a,@!x3a,@!y2a,@!y3a:integer; {additional temporary registers}
+begin if (xx3<xx0)or(yy3<yy0) then confusion("m");
+@:this can't happen m}{\quad m@>
+l:=16; bisect_ptr:=0;@/
+x1:=xx1-xx0; x2:=xx2-xx1; x3:=xx3-xx2;
+if xx0>=xi_corr then r:=(xx0-xi_corr) mod unity
+else r:=unity-1-((-xx0+xi_corr-1) mod unity);
+m:=(xx3-xx0+r) div unity;@/
+y1:=yy1-yy0; y2:=yy2-yy1; y3:=yy3-yy2;
+if yy0>=eta_corr then s:=(yy0-eta_corr) mod unity
+else s:=unity-1-((-yy0+eta_corr-1) mod unity);
+n:=(yy3-yy0+s) div unity;@/
+if (xx3-xx0>=fraction_one)or(yy3-yy0>=fraction_one) then
+ @<Divide the variables by two, to avoid overflow problems@>;
+loop@+ begin continue:@<Make moves for current subinterval;
+ if bisection is necessary, push the second subinterval
+ onto the stack, and |goto continue| in order to handle
+ the first subinterval@>;
+ if bisect_ptr=0 then return;
+ @<Remove a subproblem for |make_moves| from the stack@>;
+ end;
+exit: end;
+
+@ @<Remove a subproblem for |make_moves| from the stack@>=
+bisect_ptr:=bisect_ptr-move_increment;@/
+x1:=stack_x1; x2:=stack_x2; x3:=stack_x3; r:=stack_r; m:=stack_m;@/
+y1:=stack_y1; y2:=stack_y2; y3:=stack_y3; s:=stack_s; n:=stack_n;@/
+l:=stack_l
+
+@ Our variables |(x1,x2,x3)| correspond to $(X_1,X_2,X_3)$ in the notation
+of the theory developed above. We need to keep them less than $2^{28}$
+in order to avoid integer overflow in weird circumstances.
+For example, data like $x_0=-2^{28}+2^{16}-1$ and $x_1=x_2=x_3=2^{28}-1$
+would otherwise be problematical. Hence this part of the code is
+needed, if only to thwart malicious users.
+
+@<Divide the variables by two, to avoid overflow problems@>=
+begin x1:=half(x1+xi_corr); x2:=half(x2+xi_corr); x3:=half(x3+xi_corr);
+r:=half(r+xi_corr);@/
+y1:=half(y1+eta_corr); y2:=half(y2+eta_corr); y3:=half(y3+eta_corr);
+s:=half(s+eta_corr);@/
+l:=15;
+end
+
+@ @<Make moves...@>=
+if m=0 then @<Move upward |n| steps@>
+else if n=0 then @<Move to the right |m| steps@>
+else if m+n=2 then @<Make one move of each kind@>
+else begin incr(l); stack_l:=l;@/
+ stack_x3:=x3; stack_x2:=half(x2+x3+xi_corr); x2:=half(x1+x2+xi_corr);
+ x3:=half(x2+stack_x2+xi_corr); stack_x1:=x3;@/
+ r:=r+r+xi_corr; t:=x1+x2+x3+r;@/
+ q:=t div two_to_the[l]; stack_r:=t mod two_to_the[l];@/
+ stack_m:=m-q; m:=q;@/
+ stack_y3:=y3; stack_y2:=half(y2+y3+eta_corr); y2:=half(y1+y2+eta_corr);
+ y3:=half(y2+stack_y2+eta_corr); stack_y1:=y3;@/
+ s:=s+s+eta_corr; u:=y1+y2+y3+s;@/
+ q:=u div two_to_the[l]; stack_s:=u mod two_to_the[l];@/
+ stack_n:=n-q; n:=q;@/
+ bisect_ptr:=bisect_ptr+move_increment; goto continue;
+ end
+
+@ @<Move upward |n| steps@>=
+while n>0 do
+ begin incr(move_ptr); move[move_ptr]:=1; decr(n);
+ end
+
+@ @<Move to the right |m| steps@>=
+move[move_ptr]:=move[move_ptr]+m
+
+@ @<Make one move of each kind@>=
+begin r:=two_to_the[l]-r; s:=two_to_the[l]-s;@/
+while l<30 do
+ begin x3a:=x3; x2a:=half(x2+x3+xi_corr); x2:=half(x1+x2+xi_corr);
+ x3:=half(x2+x2a+xi_corr);
+ t:=x1+x2+x3; r:=r+r-xi_corr;@/
+ y3a:=y3; y2a:=half(y2+y3+eta_corr); y2:=half(y1+y2+eta_corr);
+ y3:=half(y2+y2a+eta_corr);
+ u:=y1+y2+y3; s:=s+s-eta_corr;@/
+ if t<r then if u<s then @<Switch to the right subinterval@>
+ else begin @<Move up then right@>; goto done;
+ end
+ else if u<s then
+ begin @<Move right then up@>; goto done;
+ end;
+ incr(l);
+ end;
+r:=r-xi_corr; s:=s-eta_corr;
+if ab_vs_cd(x1+x2+x3,s,y1+y2+y3,r)-xi_corr>=0 then @<Move right then up@>
+ else @<Move up then right@>;
+done:
+end
+
+@ @<Switch to the right subinterval@>=
+begin x1:=x3; x2:=x2a; x3:=x3a; r:=r-t;
+y1:=y3; y2:=y2a; y3:=y3a; s:=s-u;
+end
+
+@ @<Move right then up@>=
+begin incr(move[move_ptr]); incr(move_ptr); move[move_ptr]:=1;
+end
+
+@ @<Move up then right@>=
+begin incr(move_ptr); move[move_ptr]:=2;
+end
+
+@ After |make_moves| has acted, possibly for several curves that move toward
+the same octant, a ``smoothing'' operation might be done on the |move| array.
+This removes optical glitches that can arise even when the curve has been
+digitized without rounding errors.
+
+The smoothing process replaces the integers $a_0\ldots a_n$ in
+|move[b..t]| by ``smoothed'' integers $a_0'\ldots a_n'$ defined as
+follows:
+$$a_k'=a_k+\delta\k-\delta_k;\qquad
+\delta_k=\cases{+1,&if $1<k<n$ and $a_{k-2}\G a_{k-1}\ll a_k\G a\k$;\cr
+-1,&if $1<k<n$ and $a_{k-2}\L a_{k-1}\gg a_k\L a\k$;\cr
+0,&otherwise.\cr}$$
+Here $a\ll b$ means that $a\L b-2$, and $a\gg b$ means that $a\G b+2$.
+
+The smoothing operation is symmetric in the sense that, if $a_0\ldots a_n$
+smoothes to $a_0'\ldots a_n'$, then the reverse sequence $a_n\ldots a_0$
+smoothes to $a_n'\ldots a_0'$; also the complementary sequence
+$(m-a_0)\ldots(m-a_n)$ smoothes to $(m-a_0')\ldots(m-a_n')$.
+We have $a_0'+\cdots+a_n'=a_0+\cdots+a_n$ because $\delta_0=\delta_{n+1}=0$.
+
+@p procedure smooth_moves(@!b,@!t:integer);
+var@!k:1..move_size; {index into |move|}
+@!a,@!aa,@!aaa:integer; {original values of |move[k],move[k-1],move[k-2]|}
+begin if t-b>=3 then
+ begin k:=b+2; aa:=move[k-1]; aaa:=move[k-2];
+ repeat a:=move[k];
+ if abs(a-aa)>1 then
+ @<Increase and decrease |move[k-1]| and |move[k]| by $\delta_k$@>;
+ incr(k); aaa:=aa; aa:=a;
+ until k=t;
+ end;
+end;
+
+@ @<Increase and decrease |move[k-1]| and |move[k]| by $\delta_k$@>=
+if a>aa then
+ begin if aaa>=aa then if a>=move[k+1] then
+ begin incr(move[k-1]); move[k]:=a-1;
+ end;
+ end
+else begin if aaa<=aa then if a<=move[k+1] then
+ begin decr(move[k-1]); move[k]:=a+1;
+ end;
+ end
+
+@* \[20] Edge structures.
+Now we come to \MF's internal scheme for representing what the user can
+actually ``see,'' the edges between pixels. Each pixel has an integer
+weight, obtained by summing the weights on all edges to its left. \MF\
+represents only the nonzero edge weights, since most of the edges are
+weightless; in this way, the data storage requirements grow only linearly
+with respect to the number of pixels per point, even though two-dimensional
+data is being represented. (Well, the actual dependence on the underlying
+resolution is order $n\log n$, but the the $\log n$ factor is buried in our
+implicit restriction on the maximum raster size.) The sum of all edge
+weights in each row should be zero.
+
+The data structure for edge weights must be compact and flexible,
+yet it should support efficient updating and display operations. We
+want to be able to have many different edge structures in memory at
+once, and we want the computer to be able to translate them, reflect them,
+and/or merge them together with relative ease.
+
+\MF's solution to this problem requires one single-word node per
+nonzero edge weight, plus one two-word node for each row in a contiguous
+set of rows. There's also a header node that provides global information
+about the entire structure.
+
+@ Let's consider the edge-weight nodes first. The |info| field of such
+nodes contains both an $m$~value and a weight~$w$, in the form
+$8m+w+c$, where $c$ is a constant that depends on data found in the header.
+We shall consider $c$ in detail later; for now, it's best just to think
+of it as a way to compensate for the fact that $m$ and~$w$ can be negative,
+together with the fact that an |info| field must have a value between
+|min_halfword| and |max_halfword|. The $m$ value is an unscaled $x$~coordinate,
+so it satisfies $\vert m\vert<
+4096$; the $w$ value is always in the range $1\L\vert w\vert\L3$. We can
+unpack the data in the |info| field by fetching |ho(info(p))=
+info(p)-min_halfword| and dividing this nonnegative number by~8;
+the constant~$c$ will be chosen so that the remainder of this division
+is $4+w$. Thus, for example, a remainder of~3 will correspond to
+the edge weight $w=-1$.
+
+Every row of an edge structure contains two lists of such edge-weight
+nodes, called the |sorted| and |unsorted| lists, linked together by their
+|link| fields in the normal way. The difference between them is that we
+always have |info(p)<=info(link(p))| in the |sorted| list, but there's no
+such restriction on the elements of the |unsorted| list. The reason for
+this distinction is that it would take unnecessarily long to maintain
+edge-weight lists in sorted order while they're being updated; but when we
+need to process an entire row from left to right in order of the
+$m$~values, it's fairly easy and quick to sort a short list of unsorted
+elements and to merge them into place among their sorted cohorts.
+Furthermore, the fact that the |unsorted| list is empty can sometimes be
+used to good advantage, because it allows us to conclude that a particular
+row has not changed since the last time we sorted it.
+
+The final |link| of the |sorted| list will be |sentinel|, which points to
+a special one-word node whose |info| field is essentially infinite; this
+facilitates the sorting and merging operations. The final |link| of the
+|unsorted| list will be either |null| or |void|, where |void=null+1|
+is used to avoid redisplaying data that has not changed:
+A |void| value is stored at the head of the
+unsorted list whenever the corresponding row has been displayed.
+
+@d zero_w=4
+@d void==null+1
+
+@<Initialize table entries...@>=
+info(sentinel):=max_halfword; {|link(sentinel)=null|}
+
+@ The rows themselves are represented by row header nodes that
+contain four link fields. Two of these four, |sorted| and |unsorted|,
+point to the first items of the edge-weight lists just mentioned.
+The other two, |link| and |knil|, point to the headers of the two
+adjacent rows. If |p| points to the header for row number~|n|, then
+|link(p)| points up to the header for row~|n+1|, and |knil(p)| points
+down to the header for row~|n-1|. This double linking makes it
+convenient to move through consecutive rows either upward or downward;
+as usual, we have |link(knil(p))=knil(link(p))=p| for all row headers~|p|.
+
+The row associated with a given value of |n| contains weights for
+edges that run between the lattice points |(m,n)| and |(m,n+1)|.
+
+@d knil==info {inverse of the |link| field, in a doubly linked list}
+@d sorted_loc(#)==#+1 {where the |sorted| link field resides}
+@d sorted(#)==link(sorted_loc(#)) {beginning of the list of sorted edge weights}
+@d unsorted(#)==info(#+1) {beginning of the list of unsorted edge weights}
+@d row_node_size=2 {number of words in a row header node}
+
+@ The main header node |h| for an edge structure has |link| and |knil|
+fields that link it above the topmost row and below the bottommost row.
+It also has fields called |m_min|, |m_max|, |n_min|, and |n_max| that
+bound the current extent of the edge data: All |m| values in edge-weight
+nodes should lie between |m_min(h)-4096| and |m_max(h)-4096|, inclusive.
+Furthermore the topmost row header, pointed to by |knil(h)|,
+is for row number |n_max(h)-4096|; the bottommost row header, pointed to by
+|link(h)|, is for row number |n_min(h)-4096|.
+
+The offset constant |c| that's used in all of the edge-weight data is
+represented implicitly in |m_offset(h)|; its actual value is
+$$\hbox{|c=min_halfword+zero_w+8*m_offset(h)|.}$$
+Notice that it's possible to shift an entire edge structure by an
+amount $(\Delta m,\Delta n)$ by adding $\Delta n$ to |n_min(h)| and |n_max(h)|,
+adding $\Delta m$ to |m_min(h)| and |m_max(h)|, and subtracting
+$\Delta m$ from |m_offset(h)|;
+none of the other edge data needs to be modified. Initially the |m_offset|
+field is~4096, but it will change if the user requests such a shift.
+The contents of these five fields should always be positive and less than
+8192; |n_max| should, in fact, be less than 8191. Furthermore
+|m_min+m_offset-4096| and |m_max+m_offset-4096| must also lie strictly
+between 0 and 8192, so that the |info| fields of edge-weight nodes will
+fit in a halfword.
+
+The header node of an edge structure also contains two somewhat unusual
+fields that are called |last_window(h)| and |last_window_time(h)|. When this
+structure is displayed in window~|k| of the user's screen, after that
+window has been updated |t| times, \MF\ sets |last_window(h):=k| and
+|last_window_time(h):=t|; it also sets |unsorted(p):=void| for all row
+headers~|p|, after merging any existing unsorted weights with the sorted
+ones. A subsequent display in the same window will be able to avoid
+redisplaying rows whose |unsorted| list is still |void|, if the window
+hasn't been used for something else in the meantime.
+
+A pointer to the row header of row |n_pos(h)-4096| is provided in
+|n_rover(h)|. Most of the algorithms that update an edge structure
+are able to get by without random row references; they usually
+access rows that are neighbors of each other or of the current |n_pos| row.
+Exception: If |link(h)=h| (so that the edge structure contains
+no rows), we have |n_rover(h)=h|, and |n_pos(h)| is irrelevant.
+
+@d zero_field=4096 {amount added to coordinates to make them positive}
+@d n_min(#)==info(#+1) {minimum row number present, plus |zero_field|}
+@d n_max(#)==link(#+1) {maximum row number present, plus |zero_field|}
+@d m_min(#)==info(#+2) {minimum column number present, plus |zero_field|}
+@d m_max(#)==link(#+2) {maximum column number present, plus |zero_field|}
+@d m_offset(#)==info(#+3) {translation of $m$ data in edge-weight nodes}
+@d last_window(#)==link(#+3) {the last display went into this window}
+@d last_window_time(#)==mem[#+4].int {after this many window updates}
+@d n_pos(#)==info(#+5) {the row currently in |n_rover|, plus |zero_field|}
+@d n_rover(#)==link(#+5) {a row recently referenced}
+@d edge_header_size=6 {number of words in an edge-structure header}
+@d valid_range(#)==(abs(#-4096)<4096) {is |#| strictly between 0 and 8192?}
+@d empty_edges(#)==link(#)=# {are there no rows in this edge header?}
+
+@p procedure init_edges(@!h:pointer); {initialize an edge header to null values}
+begin knil(h):=h; link(h):=h;@/
+n_min(h):=zero_field+4095; n_max(h):=zero_field-4095;
+m_min(h):=zero_field+4095; m_max(h):=zero_field-4095;
+m_offset(h):=zero_field;@/
+last_window(h):=0; last_window_time(h):=0;@/
+n_rover(h):=h; n_pos(h):=0;@/
+end;
+
+@ When a lot of work is being done on a particular edge structure, we plant
+a pointer to its main header in the global variable |cur_edges|.
+This saves us from having to pass this pointer as a parameter over and
+over again between subroutines.
+
+Similarly, |cur_wt| is a global weight that is being used by several
+procedures at once.
+
+@<Glob...@>=
+@!cur_edges:pointer; {the edge structure of current interest}
+@!cur_wt:integer; {the edge weight of current interest}
+
+@ The |fix_offset| routine goes through all the edge-weight nodes of
+|cur_edges| and adds a constant to their |info| fields, so that
+|m_offset(cur_edges)| can be brought back to |zero_field|. (This
+is necessary only in unusual cases when the offset has gotten too
+large or too small.)
+
+@p procedure fix_offset;
+var @!p,@!q:pointer; {list traversers}
+@!delta:integer; {the amount of change}
+begin delta:=8*(m_offset(cur_edges)-zero_field);
+m_offset(cur_edges):=zero_field;
+q:=link(cur_edges);
+while q<>cur_edges do
+ begin p:=sorted(q);
+ while p<>sentinel do
+ begin info(p):=info(p)-delta; p:=link(p);
+ end;
+ p:=unsorted(q);
+ while p>void do
+ begin info(p):=info(p)-delta; p:=link(p);
+ end;
+ q:=link(q);
+ end;
+end;
+
+@ The |edge_prep| routine makes the |cur_edges| structure ready to
+accept new data whose coordinates satisfy |ml<=m<=mr| and |nl<=n<=nr-1|,
+assuming that |-4096<ml<=mr<4096| and |-4096<nl<=nr<4096|. It makes
+appropriate adjustments to |m_min|, |m_max|, |n_min|, and |n_max|,
+adding new empty rows if necessary.
+
+@p procedure edge_prep(@!ml,@!mr,@!nl,@!nr:integer);
+var @!delta:halfword; {amount of change}
+@!p,@!q:pointer; {for list manipulation}
+begin ml:=ml+zero_field; mr:=mr+zero_field;
+nl:=nl+zero_field; nr:=nr-1+zero_field;@/
+if ml<m_min(cur_edges) then m_min(cur_edges):=ml;
+if mr>m_max(cur_edges) then m_max(cur_edges):=mr;
+if not valid_range(m_min(cur_edges)+m_offset(cur_edges)-zero_field) or@|
+ not valid_range(m_max(cur_edges)+m_offset(cur_edges)-zero_field) then
+ fix_offset;
+if empty_edges(cur_edges) then {there are no rows}
+ begin n_min(cur_edges):=nr+1; n_max(cur_edges):=nr;
+ end;
+if nl<n_min(cur_edges) then
+ @<Insert exactly |n_min(cur_edges)-nl| empty rows at the bottom@>;
+if nr>n_max(cur_edges) then
+ @<Insert exactly |nr-n_max(cur_edges)| empty rows at the top@>;
+end;
+
+@ @<Insert exactly |n_min(cur_edges)-nl| empty rows at the bottom@>=
+begin delta:=n_min(cur_edges)-nl; n_min(cur_edges):=nl;
+p:=link(cur_edges);
+repeat q:=get_node(row_node_size); sorted(q):=sentinel; unsorted(q):=void;
+knil(p):=q; link(q):=p; p:=q; decr(delta);
+until delta=0;
+knil(p):=cur_edges; link(cur_edges):=p;
+if n_rover(cur_edges)=cur_edges then n_pos(cur_edges):=nl-1;
+end
+
+@ @<Insert exactly |nr-n_max(cur_edges)| empty rows at the top@>=
+begin delta:=nr-n_max(cur_edges); n_max(cur_edges):=nr;
+p:=knil(cur_edges);
+repeat q:=get_node(row_node_size); sorted(q):=sentinel; unsorted(q):=void;
+link(p):=q; knil(q):=p; p:=q; decr(delta);
+until delta=0;
+link(p):=cur_edges; knil(cur_edges):=p;
+if n_rover(cur_edges)=cur_edges then n_pos(cur_edges):=nr+1;
+end
+
+@ The |print_edges| subroutine gives a symbolic rendition of an edge
+structure, for use in `\&{show}' commands. A rather terse output
+format has been chosen since edge structures can grow quite large.
+
+@<Declare subroutines for printing expressions@>=
+@t\4@>@<Declare the procedure called |print_weight|@>@;@/
+procedure print_edges(@!s:str_number;@!nuline:boolean;@!x_off,@!y_off:integer);
+var @!p,@!q,@!r:pointer; {for list traversal}
+@!n:integer; {row number}
+begin print_diagnostic("Edge structure",s,nuline);
+p:=knil(cur_edges); n:=n_max(cur_edges)-zero_field;
+while p<>cur_edges do
+ begin q:=unsorted(p); r:=sorted(p);
+ if(q>void)or(r<>sentinel) then
+ begin print_nl("row "); print_int(n+y_off); print_char(":");
+ while q>void do
+ begin print_weight(q,x_off); q:=link(q);
+ end;
+ print(" |");
+ while r<>sentinel do
+ begin print_weight(r,x_off); r:=link(r);
+ end;
+ end;
+ p:=knil(p); decr(n);
+ end;
+end_diagnostic(true);
+end;
+
+@ @<Declare the procedure called |print_weight|@>=
+procedure print_weight(@!q:pointer;@!x_off:integer);
+var @!w,@!m:integer; {unpacked weight and coordinate}
+@!d:integer; {temporary data register}
+begin d:=ho(info(q)); w:=d mod 8; m:=(d div 8)-m_offset(cur_edges);
+if file_offset>max_print_line-9 then print_nl(" ")
+else print_char(" ");
+print_int(m+x_off);
+while w>zero_w do
+ begin print_char("+"); decr(w);
+ end;
+while w<zero_w do
+ begin print_char("-"); incr(w);
+ end;
+end;
+
+@ Here's a trivial subroutine that copies an edge structure. (Let's hope
+that the given structure isn't too gigantic.)
+
+@p function copy_edges(@!h:pointer):pointer;
+var @!p,@!r:pointer; {variables that traverse the given structure}
+@!hh,@!pp,@!qq,@!rr,@!ss:pointer; {variables that traverse the new structure}
+begin hh:=get_node(edge_header_size);
+mem[hh+1]:=mem[h+1]; mem[hh+2]:=mem[h+2];
+mem[hh+3]:=mem[h+3]; mem[hh+4]:=mem[h+4]; {we've now copied |n_min|, |n_max|,
+ |m_min|, |m_max|, |m_offset|, |last_window|, and |last_window_time|}
+n_pos(hh):=n_max(hh)+1;n_rover(hh):=hh;@/
+p:=link(h); qq:=hh;
+while p<>h do
+ begin pp:=get_node(row_node_size); link(qq):=pp; knil(pp):=qq;
+ @<Copy both |sorted| and |unsorted| lists of |p| to |pp|@>;
+ p:=link(p); qq:=pp;
+ end;
+link(qq):=hh; knil(hh):=qq;
+copy_edges:=hh;
+end;
+
+@ @<Copy both |sorted| and |unsorted|...@>=
+r:=sorted(p); rr:=sorted_loc(pp); {|link(rr)=sorted(pp)|}
+while r<>sentinel do
+ begin ss:=get_avail; link(rr):=ss; rr:=ss; info(rr):=info(r);@/
+ r:=link(r);
+ end;
+link(rr):=sentinel;@/
+r:=unsorted(p); rr:=temp_head;
+while r>void do
+ begin ss:=get_avail; link(rr):=ss; rr:=ss; info(rr):=info(r);@/
+ r:=link(r);
+ end;
+link(rr):=r; unsorted(pp):=link(temp_head)
+
+@ Another trivial routine flips |cur_edges| about the |x|-axis
+(i.e., negates all the |y| coordinates), assuming that at least
+one row is present.
+
+@p procedure y_reflect_edges;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin p:=n_min(cur_edges);
+n_min(cur_edges):=zero_field+zero_field-1-n_max(cur_edges);
+n_max(cur_edges):=zero_field+zero_field-1-p;
+n_pos(cur_edges):=zero_field+zero_field-1-n_pos(cur_edges);@/
+p:=link(cur_edges); q:=cur_edges; {we assume that |p<>q|}
+repeat r:=link(p); link(p):=q; knil(q):=p; q:=p; p:=r;
+until q=cur_edges;
+last_window_time(cur_edges):=0;
+end;
+
+@ It's somewhat more difficult, yet not too hard, to reflect about the |y|-axis.
+
+@p procedure x_reflect_edges;
+var @!p,@!q,@!r,@!s:pointer; {list manipulation registers}
+@!m:integer; {|info| fields will be reflected with respect to this number}
+begin p:=m_min(cur_edges);
+m_min(cur_edges):=zero_field+zero_field-m_max(cur_edges);
+m_max(cur_edges):=zero_field+zero_field-p;
+m:=(zero_field+m_offset(cur_edges))*8+zero_w+min_halfword+zero_w+min_halfword;
+m_offset(cur_edges):=zero_field;
+p:=link(cur_edges);
+repeat @<Reflect the edge-and-weight data in |sorted(p)|@>;
+@<Reflect the edge-and-weight data in |unsorted(p)|@>;
+p:=link(p);
+until p=cur_edges;
+last_window_time(cur_edges):=0;
+end;
+
+@ We want to change the sign of the weight as we change the sign of the
+|x|~coordinate. Fortunately, it's easier to do this than to negate
+one without the other.
+
+@<Reflect the edge-and-weight data in |unsorted(p)|@>=
+q:=unsorted(p);
+while q>void do
+ begin info(q):=m-info(q); q:=link(q);
+ end
+
+@ Reversing the order of a linked list is best thought of as the process of
+popping nodes off one stack and pushing them on another. In this case we
+pop from stack~|q| and push to stack~|r|.
+
+@<Reflect the edge-and-weight data in |sorted(p)|@>=
+q:=sorted(p); r:=sentinel;
+while q<>sentinel do
+ begin s:=link(q); link(q):=r; r:=q; info(r):=m-info(q); q:=s;
+ end;
+sorted(p):=r
+
+@ Now let's multiply all the $y$~coordinates of a nonempty edge structure
+by a small integer $s>1$:
+
+@p procedure y_scale_edges(@!s:integer);
+var @!p,@!q,@!pp,@!r,@!rr,@!ss:pointer; {list manipulation registers}
+@!t:integer; {replication counter}
+begin if (s*(n_max(cur_edges)+1-zero_field)>=4096) or@|
+ (s*(n_min(cur_edges)-zero_field)<=-4096) then
+ begin print_err("Scaled picture would be too big");
+@.Scaled picture...big@>
+ help3("I can't yscale the picture as requested---it would")@/
+ ("make some coordinates too large or too small.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_error;
+ end
+else begin n_max(cur_edges):=s*(n_max(cur_edges)+1-zero_field)-1+zero_field;
+ n_min(cur_edges):=s*(n_min(cur_edges)-zero_field)+zero_field;
+ @<Replicate every row exactly $s$ times@>;
+ last_window_time(cur_edges):=0;
+ end;
+end;
+
+@ @<Replicate...@>=
+p:=cur_edges;
+repeat q:=p; p:=link(p);
+for t:=2 to s do
+ begin pp:=get_node(row_node_size); link(q):=pp; knil(p):=pp;
+ link(pp):=p; knil(pp):=q; q:=pp;
+ @<Copy both |sorted| and |unsorted|...@>;
+ end;
+until link(p)=cur_edges
+
+@ Scaling the $x$~coordinates is, of course, our next task.
+
+@p procedure x_scale_edges(@!s:integer);
+var @!p,@!q:pointer; {list manipulation registers}
+@!t:0..65535; {unpacked |info| field}
+@!w:0..7; {unpacked weight}
+@!delta:integer; {amount added to scaled |info|}
+begin if (s*(m_max(cur_edges)-zero_field)>=4096) or@|
+ (s*(m_min(cur_edges)-zero_field)<=-4096) then
+ begin print_err("Scaled picture would be too big");
+@.Scaled picture...big@>
+ help3("I can't xscale the picture as requested---it would")@/
+ ("make some coordinates too large or too small.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_error;
+ end
+else if (m_max(cur_edges)<>zero_field)or(m_min(cur_edges)<>zero_field) then
+ begin m_max(cur_edges):=s*(m_max(cur_edges)-zero_field)+zero_field;
+ m_min(cur_edges):=s*(m_min(cur_edges)-zero_field)+zero_field;
+ delta:=8*(zero_field-s*m_offset(cur_edges))+min_halfword;
+ m_offset(cur_edges):=zero_field;@/
+ @<Scale the $x$~coordinates of each row by $s$@>;
+ last_window_time(cur_edges):=0;
+ end;
+end;
+
+@ The multiplications cannot overflow because we know that |s<4096|.
+
+@<Scale the $x$~coordinates of each row by $s$@>=
+q:=link(cur_edges);
+repeat p:=sorted(q);
+while p<>sentinel do
+ begin t:=ho(info(p)); w:=t mod 8; info(p):=(t-w)*s+w+delta; p:=link(p);
+ end;
+p:=unsorted(q);
+while p>void do
+ begin t:=ho(info(p)); w:=t mod 8; info(p):=(t-w)*s+w+delta; p:=link(p);
+ end;
+q:=link(q);
+until q=cur_edges
+
+@ Here is a routine that changes the signs of all the weights, without
+changing anything else.
+
+@p procedure negate_edges(@!h:pointer);
+label done;
+var @!p,@!q,@!r,@!s,@!t,@!u:pointer; {structure traversers}
+begin p:=link(h);
+while p<>h do
+ begin q:=unsorted(p);
+ while q>void do
+ begin info(q):=8-2*((ho(info(q))) mod 8)+info(q); q:=link(q);
+ end;
+ q:=sorted(p);
+ if q<>sentinel then
+ begin repeat info(q):=8-2*((ho(info(q))) mod 8)+info(q); q:=link(q);
+ until q=sentinel;
+ @<Put the list |sorted(p)| back into sort@>;
+ end;
+ p:=link(p);
+ end;
+last_window_time(h):=0;
+end;
+
+@ \MF\ would work even if the code in this section were omitted, because
+a list of edge-and-weight data that is sorted only by
+|m| but not~|w| turns out to be good enough for correct operation.
+However, the author decided not to make the program even trickier than
+it is already, since |negate_edges| isn't needed very often.
+The simpler-to-state condition, ``keep the |sorted| list fully sorted,''
+is therefore being preserved at the cost of extra computation.
+
+@<Put the list |sorted(p)|...@>=
+u:=sorted_loc(p); q:=link(u); r:=q; s:=link(r); {|q=sorted(p)|}
+loop@+ if info(s)>info(r) then
+ begin link(u):=q;
+ if s=sentinel then goto done;
+ u:=r; q:=s; r:=q; s:=link(r);
+ end
+ else begin t:=s; s:=link(t); link(t):=q; q:=t;
+ end;
+done: link(r):=sentinel
+
+@ The |unsorted| edges of a row are merged into the |sorted| ones by
+a subroutine called |sort_edges|. It uses simple insertion sort,
+followed by a merge, because the unsorted list is supposedly quite short.
+However, the unsorted list is assumed to be nonempty.
+
+@p procedure sort_edges(@!h:pointer); {|h| is a row header}
+label done;
+var @!k:halfword; {key register that we compare to |info(q)|}
+@!p,@!q,@!r,@!s:pointer;
+begin r:=unsorted(h); unsorted(h):=null;
+p:=link(r); link(r):=sentinel; link(temp_head):=r;
+while p>void do {sort node |p| into the list that starts at |temp_head|}
+ begin k:=info(p); q:=temp_head;
+ repeat r:=q; q:=link(r);
+ until k<=info(q);
+ link(r):=p; r:=link(p); link(p):=q; p:=r;
+ end;
+@<Merge the |temp_head| list into |sorted(h)|@>;
+end;
+
+@ In this step we use the fact that |sorted(h)=link(sorted_loc(h))|.
+
+@<Merge the |temp_head| list into |sorted(h)|@>=
+begin r:=sorted_loc(h); q:=link(r); p:=link(temp_head);
+loop@+ begin k:=info(p);
+ while k>info(q) do
+ begin r:=q; q:=link(r);
+ end;
+ link(r):=p; s:=link(p); link(p):=q;
+ if s=sentinel then goto done;
+ r:=p; p:=s;
+ end;
+done:end
+
+@ The |cull_edges| procedure ``optimizes'' an edge structure by making all
+the pixel weights either |w_out| or~|w_in|. The weight will be~|w_in| after the
+operation if and only if it was in the closed interval |[w_lo,w_hi]|
+before, where |w_lo<=w_hi|. Either |w_out| or |w_in| is zero, while the other is
+$\pm1$, $\pm2$, or $\pm3$. The parameters will be such that zero-weight
+pixels will remain of weight zero. (This is fortunate,
+because there are infinitely many of them.)
+
+The procedure also computes the tightest possible bounds on the resulting
+data, by updating |m_min|, |m_max|, |n_min|, and~|n_max|.
+
+@p procedure cull_edges(@!w_lo,@!w_hi,@!w_out,@!w_in:integer);
+label done;
+var @!p,@!q,@!r,@!s:pointer; {for list manipulation}
+@!w:integer; {new weight after culling}
+@!d:integer; {data register for unpacking}
+@!m:integer; {the previous column number, including |m_offset|}
+@!mm:integer; {the next column number, including |m_offset|}
+@!ww:integer; {accumulated weight before culling}
+@!prev_w:integer; {value of |w| before column |m|}
+@!n,@!min_n,@!max_n:pointer; {current and extreme row numbers}
+@!min_d,@!max_d:pointer; {extremes of the new edge-and-weight data}
+begin min_d:=max_halfword; max_d:=min_halfword;
+min_n:=max_halfword; max_n:=min_halfword;@/
+p:=link(cur_edges); n:=n_min(cur_edges);
+while p<>cur_edges do
+ begin if unsorted(p)>void then sort_edges(p);
+ if sorted(p)<>sentinel then
+ @<Cull superfluous edge-weight entries from |sorted(p)|@>;
+ p:=link(p); incr(n);
+ end;
+@<Delete empty rows at the top and/or bottom;
+ update the boundary values in the header@>;
+last_window_time(cur_edges):=0;
+end;
+
+@ The entire |sorted| list is returned to available memory in this step;
+a new list is built starting (temporarily) at |temp_head|.
+Since several edges can occur at the same column, we need to be looking
+ahead of where the actual culling takes place. This means that it's
+slightly tricky to get the iteration started and stopped.
+
+@<Cull superfluous...@>=
+begin r:=temp_head; q:=sorted(p); ww:=0; m:=1000000; prev_w:=0;
+loop@+ begin if q=sentinel then mm:=1000000
+ else begin d:=ho(info(q)); mm:=d div 8; ww:=ww+(d mod 8)-zero_w;
+ end;
+ if mm>m then
+ begin @<Insert an edge-weight for edge |m|, if the new pixel
+ weight has changed@>;
+ if q=sentinel then goto done;
+ end;
+ m:=mm;
+ if ww>=w_lo then if ww<=w_hi then w:=w_in
+ else w:=w_out
+ else w:=w_out;
+ s:=link(q); free_avail(q); q:=s;
+ end;
+done: link(r):=sentinel; sorted(p):=link(temp_head);
+if r<>temp_head then @<Update the max/min amounts@>;
+end
+
+@ @<Insert an edge-weight for edge |m|, if...@>=
+if w<>prev_w then
+ begin s:=get_avail; link(r):=s;
+ info(s):=8*m+min_halfword+zero_w+w-prev_w;
+ r:=s; prev_w:=w;
+ end
+
+@ @<Update the max/min amounts@>=
+begin if min_n=max_halfword then min_n:=n;
+max_n:=n;
+if min_d>info(link(temp_head)) then min_d:=info(link(temp_head));
+if max_d<info(r) then max_d:=info(r);
+end
+
+@ @<Delete empty rows at the top and/or bottom...@>=
+if min_n>max_n then @<Delete all the row headers@>
+else begin n:=n_min(cur_edges); n_min(cur_edges):=min_n;
+ while min_n>n do
+ begin p:=link(cur_edges); link(cur_edges):=link(p);
+ knil(link(p)):=cur_edges;
+ free_node(p,row_node_size); incr(n);
+ end;
+ n:=n_max(cur_edges); n_max(cur_edges):=max_n;
+ n_pos(cur_edges):=max_n+1; n_rover(cur_edges):=cur_edges;
+ while max_n<n do
+ begin p:=knil(cur_edges); knil(cur_edges):=knil(p);
+ link(knil(p)):=cur_edges;
+ free_node(p,row_node_size); decr(n);
+ end;
+ m_min(cur_edges):=((ho(min_d)) div 8)-m_offset(cur_edges)+zero_field;
+ m_max(cur_edges):=((ho(max_d)) div 8)-m_offset(cur_edges)+zero_field;
+ end
+
+@ We get here if the edges have been entirely culled away.
+
+@<Delete all the row headers@>=
+begin p:=link(cur_edges);
+while p<>cur_edges do
+ begin q:=link(p); free_node(p,row_node_size); p:=q;
+ end;
+init_edges(cur_edges);
+end
+
+
+@ The last and most difficult routine for transforming an edge structure---and
+the most interesting one!---is |xy_swap_edges|, which interchanges the
+r\^^Doles of rows and columns. Its task can be viewed as the job of
+creating an edge structure that contains only horizontal edges, linked
+together in columns, given an edge structure that contains only
+vertical edges linked together in rows; we must do this without changing
+the implied pixel weights.
+
+Given any two adjacent rows of an edge structure, it is not difficult to
+determine the horizontal edges that lie ``between'' them: We simply look
+for vertically adjacent pixels that have different weight, and insert
+a horizontal edge containing the difference in weights. Every horizontal
+edge determined in this way should be put into an appropriate linked
+list. Since random access to these linked lists is desirable, we use
+the |move| array to hold the list heads. If we work through the given
+edge structure from top to bottom, the constructed lists will not need
+to be sorted, since they will already be in order.
+
+The following algorithm makes use of some ideas suggested by John Hobby.
+@^Hobby, John Douglas@>
+It assumes that the edge structure is non-null, i.e., that |link(cur_edges)
+<>cur_edges|, hence |m_max(cur_edges)>=m_min(cur_edges)|.
+
+@p procedure xy_swap_edges; {interchange |x| and |y| in |cur_edges|}
+label done;
+var @!m_magic,@!n_magic:integer; {special values that account for offsets}
+@!p,@!q,@!r,@!s:pointer; {pointers that traverse the given structure}
+@<Other local variables for |xy_swap_edges|@>@;
+begin @<Initialize the array of new edge list heads@>;
+@<Insert blank rows at the top and bottom, and set |p| to the new top row@>;
+@<Compute the magic offset values@>;
+repeat q:=knil(p);@+if unsorted(q)>void then sort_edges(q);
+@<Insert the horizontal edges defined by adjacent rows |p,q|,
+ and destroy row~|p|@>;
+p:=q; n_magic:=n_magic-8;
+until knil(p)=cur_edges;
+free_node(p,row_node_size); {now all original rows have been recycled}
+@<Adjust the header to reflect the new edges@>;
+end;
+
+@ Here we don't bother to keep the |link| entries up to date, since the
+procedure looks only at the |knil| fields as it destroys the former
+edge structure.
+
+@<Insert blank rows at the top and bottom...@>=
+p:=get_node(row_node_size); sorted(p):=sentinel; unsorted(p):=null;@/
+knil(p):=cur_edges; knil(link(cur_edges)):=p; {the new bottom row}
+p:=get_node(row_node_size); sorted(p):=sentinel;
+knil(p):=knil(cur_edges); {the new top row}
+
+@ The new lists will become |sorted| lists later, so we initialize
+empty lists to |sentinel|.
+
+@<Initialize the array of new edge list heads@>=
+m_spread:=m_max(cur_edges)-m_min(cur_edges); {this is |>=0| by assumption}
+if m_spread>move_size then overflow("move table size",move_size);
+@:METAFONT capacity exceeded move table size}{\quad move table size@>
+for j:=0 to m_spread do move[j]:=sentinel
+
+@ @<Other local variables for |xy_swap_edges|@>=
+@!m_spread:integer; {the difference between |m_max| and |m_min|}
+@!j,@!jj:0..move_size; {indices into |move|}
+@!m,@!mm:integer; {|m| values at vertical edges}
+@!pd,@!rd:integer; {data fields from edge-and-weight nodes}
+@!pm,@!rm:integer; {|m| values from edge-and-weight nodes}
+@!w:integer; {the difference in accumulated weight}
+@!ww:integer; {as much of |w| that can be stored in a single node}
+@!dw:integer; {an increment to be added to |w|}
+
+@ At the point where we test |w<>0|, variable |w| contains
+the accumulated weight from edges already passed in
+row~|p| minus the accumulated weight from edges already passed in row~|q|.
+
+@<Insert the horizontal edges defined by adjacent rows |p,q|...@>=
+r:=sorted(p); free_node(p,row_node_size); p:=r;@/
+pd:=ho(info(p)); pm:=pd div 8;@/
+r:=sorted(q); rd:=ho(info(r)); rm:=rd div 8; w:=0;
+loop@+ begin if pm<rm then mm:=pm@+else mm:=rm;
+ if w<>0 then
+ @<Insert horizontal edges of weight |w| between |m| and~|mm|@>;
+ if pd<rd then
+ begin dw:=(pd mod 8)-zero_w;
+ @<Advance pointer |p| to the next vertical edge,
+ after destroying the previous one@>;
+ end
+ else begin if r=sentinel then goto done; {|rd=pd=ho(max_halfword)|}
+ dw:=-((rd mod 8)-zero_w);
+ @<Advance pointer |r| to the next vertical edge@>;
+ end;
+ m:=mm; w:=w+dw;
+ end;
+done:
+
+@ @<Advance pointer |r| to the next vertical edge@>=
+r:=link(r); rd:=ho(info(r)); rm:=rd div 8
+
+@ @<Advance pointer |p| to the next vertical edge...@>=
+s:=link(p); free_avail(p); p:=s; pd:=ho(info(p)); pm:=pd div 8
+
+@ Certain ``magic'' values are needed to make the following code work,
+because of the various offsets in our data structure. For now, let's not
+worry about their precise values; we shall compute |m_magic| and |n_magic|
+later, after we see what the code looks like.
+
+@ @<Insert horizontal edges of weight |w| between |m| and~|mm|@>=
+if m<>mm then
+ begin if mm-m_magic>=move_size then confusion("xy");
+@:this can't happen xy}{\quad xy@>
+ extras:=(abs(w)-1) div 3;
+ if extras>0 then
+ begin if w>0 then xw:=+3@+else xw:=-3;
+ ww:=w-extras*xw;
+ end
+ else ww:=w;
+ repeat j:=m-m_magic;
+ for k:=1 to extras do
+ begin s:=get_avail; info(s):=n_magic+xw;
+ link(s):=move[j]; move[j]:=s;
+ end;
+ s:=get_avail; info(s):=n_magic+ww;
+ link(s):=move[j]; move[j]:=s;@/
+ incr(m);
+ until m=mm;
+ end
+
+@ @<Other local variables for |xy...@>=
+@!extras:integer; {the number of additional nodes to make weights |>3|}
+@!xw:-3..3; {the additional weight in extra nodes}
+@!k:integer; {loop counter for inserting extra nodes}
+
+@ At the beginning of this step, |move[m_spread]=sentinel|, because no
+horizontal edges will extend to the right of column |m_max(cur_edges)|.
+
+@<Adjust the header to reflect the new edges@>=
+move[m_spread]:=0; j:=0;
+while move[j]=sentinel do incr(j);
+if j=m_spread then init_edges(cur_edges) {all edge weights are zero}
+else begin mm:=m_min(cur_edges);
+ m_min(cur_edges):=n_min(cur_edges);
+ m_max(cur_edges):=n_max(cur_edges)+1;
+ m_offset(cur_edges):=zero_field;
+ jj:=m_spread-1;
+ while move[jj]=sentinel do decr(jj);
+ n_min(cur_edges):=j+mm; n_max(cur_edges):=jj+mm; q:=cur_edges;
+ repeat p:=get_node(row_node_size); link(q):=p; knil(p):=q;
+ sorted(p):=move[j]; unsorted(p):=null; incr(j); q:=p;
+ until j>jj;
+ link(q):=cur_edges; knil(cur_edges):=q;
+ n_pos(cur_edges):=n_max(cur_edges)+1; n_rover(cur_edges):=cur_edges;
+ last_window_time(cur_edges):=0;
+ end;
+
+@ The values of |m_magic| and |n_magic| can be worked out by trying the
+code above on a small example; if they work correctly in simple cases,
+they should work in general.
+
+@<Compute the magic offset values@>=
+m_magic:=m_min(cur_edges)+m_offset(cur_edges)-zero_field;
+n_magic:=8*n_max(cur_edges)+8+zero_w+min_halfword
+
+@ Now let's look at the subroutine that merges the edges from a given
+edge structure into |cur_edges|. The given edge structure loses all its
+edges.
+
+@p procedure merge_edges(@!h:pointer);
+label done;
+var @!p,@!q,@!r,@!pp,@!qq,@!rr:pointer; {list manipulation registers}
+@!n:integer; {row number}
+@!k:halfword; {key register that we compare to |info(q)|}
+@!delta:integer; {change to the edge/weight data}
+begin if link(h)<>h then
+ begin if (m_min(h)<m_min(cur_edges))or(m_max(h)>m_max(cur_edges))or@|
+ (n_min(h)<n_min(cur_edges))or(n_max(h)>n_max(cur_edges)) then
+ edge_prep(m_min(h)-zero_field,m_max(h)-zero_field,
+ n_min(h)-zero_field,n_max(h)-zero_field+1);
+ if m_offset(h)<>m_offset(cur_edges) then
+ @<Adjust the data of |h| to account for a difference of offsets@>;
+ n:=n_min(cur_edges); p:=link(cur_edges); pp:=link(h);
+ while n<n_min(h) do
+ begin incr(n); p:=link(p);
+ end;
+ repeat @<Merge row |pp| into row |p|@>;
+ pp:=link(pp); p:=link(p);
+ until pp=h;
+ end;
+end;
+
+@ @<Adjust the data of |h| to account for a difference of offsets@>=
+begin pp:=link(h); delta:=8*(m_offset(cur_edges)-m_offset(h));
+repeat qq:=sorted(pp);
+while qq<>sentinel do
+ begin info(qq):=info(qq)+delta; qq:=link(qq);
+ end;
+qq:=unsorted(pp);
+while qq>void do
+ begin info(qq):=info(qq)+delta; qq:=link(qq);
+ end;
+pp:=link(pp);
+until pp=h;
+end
+
+@ The |sorted| and |unsorted| lists are merged separately. After this
+step, row~|pp| will have no edges remaining, since they will all have
+been merged into row~|p|.
+
+@<Merge row |pp|...@>=
+qq:=unsorted(pp);
+if qq>void then
+ if unsorted(p)<=void then unsorted(p):=qq
+ else begin while link(qq)>void do qq:=link(qq);
+ link(qq):=unsorted(p); unsorted(p):=unsorted(pp);
+ end;
+unsorted(pp):=null; qq:=sorted(pp);
+if qq<>sentinel then
+ begin if unsorted(p)=void then unsorted(p):=null;
+ sorted(pp):=sentinel; r:=sorted_loc(p); q:=link(r); {|q=sorted(p)|}
+ if q=sentinel then sorted(p):=qq
+ else loop@+begin k:=info(qq);
+ while k>info(q) do
+ begin r:=q; q:=link(r);
+ end;
+ link(r):=qq; rr:=link(qq); link(qq):=q;
+ if rr=sentinel then goto done;
+ r:=qq; qq:=rr;
+ end;
+ end;
+done:
+
+@ The |total_weight| routine computes the total of all pixel weights
+in a given edge structure. It's not difficult to prove that this is
+the sum of $(-w)$ times $x$ taken over all edges,
+where $w$ and~$x$ are the weight and $x$~coordinates stored in an edge.
+It's not necessary to worry that this quantity will overflow the
+size of an |integer| register, because it will be less than~$2^{31}$
+unless the edge structure has more than 174,762 edges. However, we had
+better not try to compute it as a |scaled| integer, because a total
+weight of almost $12\times 2^{12}$ can be produced by only four edges.
+
+@p function total_weight(@!h:pointer):integer; {|h| is an edge header}
+var @!p,@!q:pointer; {variables that traverse the given structure}
+@!n:integer; {accumulated total so far}
+@!m:0..65535; {packed $x$ and $w$ values, including offsets}
+begin n:=0; p:=link(h);
+while p<>h do
+ begin q:=sorted(p);
+ while q<>sentinel do
+ @<Add the contribution of node |q| to the total weight,
+ and set |q:=link(q)|@>;
+ q:=unsorted(p);
+ while q>void do
+ @<Add the contribution of node |q| to the total weight,
+ and set |q:=link(q)|@>;
+ p:=link(p);
+ end;
+total_weight:=n;
+end;
+
+@ It's not necessary to add the offsets to the $x$ coordinates, because
+an entire edge structure can be shifted without affecting its total weight.
+Similarly, we don't need to subtract |zero_field|.
+
+@<Add the contribution of node |q| to the total weight...@>=
+begin m:=ho(info(q)); n:=n-((m mod 8)-zero_w)*(m div 8);
+q:=link(q);
+end
+
+@ So far we've done lots of things to edge structures assuming that
+edges are actually present, but we haven't seen how edges get created
+in the first place. Let's turn now to the problem of generating new edges.
+
+\MF\ will display new edges as they are being computed, if |tracing_edges|
+is positive. In order to keep such data reasonably compact, only the
+points at which the path makes a $90^\circ$ or $180^\circ$ turn are listed.
+
+The tracing algorithm must remember some past history in order to suppress
+unnecessary data. Three variables |trace_x|, |trace_y|, and |trace_yy|
+provide this history: The last coordinates printed were |(trace_x,trace_y)|,
+and the previous edge traced ended at |(trace_x,trace_yy)|. Before anything
+at all has been traced, |trace_x=-4096|.
+
+@<Glob...@>=
+@!trace_x:integer; {$x$~coordinate most recently shown in a trace}
+@!trace_y:integer; {$y$~coordinate most recently shown in a trace}
+@!trace_yy:integer; {$y$~coordinate most recently encountered}
+
+@ Edge tracing is initiated by the |begin_edge_tracing| routine,
+continued by the |trace_a_corner| routine, and terminated by the
+|end_edge_tracing| routine.
+
+@p procedure begin_edge_tracing;
+begin print_diagnostic("Tracing edges","",true);
+print(" (weight "); print_int(cur_wt); print_char(")"); trace_x:=-4096;
+end;
+@#
+procedure trace_a_corner;
+begin if file_offset>max_print_line-13 then print_nl("");
+print_char("("); print_int(trace_x); print_char(","); print_int(trace_yy);
+print_char(")"); trace_y:=trace_yy;
+end;
+@#
+procedure end_edge_tracing;
+begin if trace_x=-4096 then print_nl("(No new edges added.)")
+@.No new edges added@>
+else begin trace_a_corner; print_char(".");
+ end;
+end_diagnostic(true);
+end;
+
+@ Just after a new edge weight has been put into the |info| field of
+node~|r|, in row~|n|, the following routine continues an ongoing trace.
+
+@p procedure trace_new_edge(@!r:pointer;@!n:integer);
+var @!d:integer; {temporary data register}
+@!w:-3..3; {weight associated with an edge transition}
+@!m,@!n0,@!n1:integer; {column and row numbers}
+begin d:=ho(info(r)); w:=(d mod 8)-zero_w; m:=(d div 8)-m_offset(cur_edges);
+if w=cur_wt then
+ begin n0:=n+1; n1:=n;
+ end
+else begin n0:=n; n1:=n+1;
+ end; {the edges run from |(m,n0)| to |(m,n1)|}
+if m<>trace_x then
+ begin if trace_x=-4096 then
+ begin print_nl(""); trace_yy:=n0;
+ end
+ else if trace_yy<>n0 then print_char("?") {shouldn't happen}
+ else trace_a_corner;
+ trace_x:=m; trace_a_corner;
+ end
+else begin if n0<>trace_yy then print_char("!"); {shouldn't happen}
+ if ((n0<n1)and(trace_y>trace_yy))or((n0>n1)and(trace_y<trace_yy)) then
+ trace_a_corner;
+ end;
+trace_yy:=n1;
+end;
+
+@ One way to put new edge weights into an edge structure is to use the
+following routine, which simply draws a straight line from |(x0,y0)| to
+|(x1,y1)|. More precisely, it introduces weights for the edges of the
+discrete path $\bigl(\lfloor t[x_0,x_1]+{1\over2}+\epsilon\rfloor,
+\lfloor t[y_0,y_1]+{1\over2}+\epsilon\delta\rfloor\bigr)$,
+as $t$ varies from 0 to~1, where $\epsilon$ and $\delta$ are extremely small
+positive numbers.
+
+The structure header is assumed to be |cur_edges|; downward edge weights
+will be |cur_wt|, while upward ones will be |-cur_wt|.
+
+Of course, this subroutine will be called only in connection with others
+that eventually draw a complete cycle, so that the sum of the edge weights
+in each row will be zero whenever the row is displayed.
+
+@p procedure line_edges(@!x0,@!y0,@!x1,@!y1:scaled);
+label done,done1;
+var @!m0,@!n0,@!m1,@!n1:integer; {rounded and unscaled coordinates}
+@!delx,@!dely:scaled; {the coordinate differences of the line}
+@!yt:scaled; {smallest |y| coordinate that rounds the same as |y0|}
+@!tx:scaled; {tentative change in |x|}
+@!p,@!r:pointer; {list manipulation registers}
+@!base:integer; {amount added to edge-and-weight data}
+@!n:integer; {current row number}
+begin n0:=round_unscaled(y0);
+n1:=round_unscaled(y1);
+if n0<>n1 then
+ begin m0:=round_unscaled(x0); m1:=round_unscaled(x1);
+ delx:=x1-x0; dely:=y1-y0;
+ yt:=n0*unity-half_unit; y0:=y0-yt; y1:=y1-yt;
+ if n0<n1 then @<Insert upward edges for a line@>
+ else @<Insert downward edges for a line@>;
+ n_rover(cur_edges):=p; n_pos(cur_edges):=n+zero_field;
+ end;
+end;
+
+@ Here we are careful to cancel any effect of rounding error.
+
+@<Insert upward edges for a line@>=
+begin base:=8*m_offset(cur_edges)+min_halfword+zero_w-cur_wt;
+if m0<=m1 then edge_prep(m0,m1,n0,n1)@+else edge_prep(m1,m0,n0,n1);
+@<Move to row |n0|, pointed to by |p|@>;
+y0:=unity-y0;
+loop@+ begin r:=get_avail; link(r):=unsorted(p); unsorted(p):=r;@/
+ tx:=take_fraction(delx,make_fraction(y0,dely));
+ if ab_vs_cd(delx,y0,dely,tx)<0 then decr(tx);
+ {now $|tx|=\lfloor|y0|\cdot|delx|/|dely|\rfloor$}
+ info(r):=8*round_unscaled(x0+tx)+base;@/
+ y1:=y1-unity;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ if y1<unity then goto done;
+ p:=link(p); y0:=y0+unity; incr(n);
+ end;
+done: end
+
+@ @<Insert downward edges for a line@>=
+begin base:=8*m_offset(cur_edges)+min_halfword+zero_w+cur_wt;
+if m0<=m1 then edge_prep(m0,m1,n1,n0)@+else edge_prep(m1,m0,n1,n0);
+decr(n0); @<Move to row |n0|, pointed to by |p|@>;
+loop@+ begin r:=get_avail; link(r):=unsorted(p); unsorted(p):=r;@/
+ tx:=take_fraction(delx,make_fraction(y0,dely));
+ if ab_vs_cd(delx,y0,dely,tx)<0 then incr(tx);
+ {now $|tx|=\lceil|y0|\cdot|delx|/|dely|\rceil$, since |dely<0|}
+ info(r):=8*round_unscaled(x0-tx)+base;@/
+ y1:=y1+unity;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ if y1>=0 then goto done1;
+ p:=knil(p); y0:=y0+unity; decr(n);
+ end;
+done1: end
+
+@ @<Move to row |n0|, pointed to by |p|@>=
+n:=n_pos(cur_edges)-zero_field; p:=n_rover(cur_edges);
+if n<>n0 then
+ if n<n0 then
+ repeat incr(n); p:=link(p);
+ until n=n0
+ else repeat decr(n); p:=knil(p);
+ until n=n0
+
+@ \MF\ inserts most of its edges into edge structures via the
+|move_to_edges| subroutine, which uses the data stored in the |move| array
+to specify a sequence of ``rook moves.'' The starting point |(m0,n0)|
+and finishing point |(m1,n1)| of these moves, as seen from the standpoint
+of the first octant, are supplied as parameters; the moves should, however,
+be rotated into a given octant. (We're going to study octant
+transformations in great detail later; the reader may wish to come back to
+this part of the program after mastering the mysteries of octants.)
+
+The rook moves themselves are defined as follows, from a |first_octant|
+point of view: ``Go right |move[k]| steps, then go up one, for |0<=k<n1-n0|;
+then go right |move[n1-n0]| steps and stop.'' The sum of |move[k]|
+for |0<=k<=n1-n0| will be equal to |m1-m0|.
+
+As in the |line_edges| routine, we use |+cur_wt| as the weight of
+all downward edges and |-cur_wt| as the weight of all upward edges,
+after the moves have been rotated to the proper octant direction.
+
+There are two main cases to consider: \\{fast\_case} is for moves that
+travel in the direction of octants 1, 4, 5, and~8, while \\{slow\_case}
+is for moves that travel toward octants 2, 3, 6, and~7. The latter directions
+are comparatively cumbersome because they generate more upward or downward
+edges; a curve that travels horizontally doesn't produce any edges at all,
+but a curve that travels vertically touches lots of rows.
+
+@d fast_case_up=60 {for octants 1 and 4}
+@d fast_case_down=61 {for octants 5 and 8}
+@d slow_case_up=62 {for octants 2 and 3}
+@d slow_case_down=63 {for octants 6 and 7}
+
+@p procedure move_to_edges(@!m0,@!n0,@!m1,@!n1:integer);
+label fast_case_up,fast_case_down,slow_case_up,slow_case_down,done;
+var @!delta:0..move_size; {extent of |move| data}
+@!k:0..move_size; {index into |move|}
+@!p,@!r:pointer; {list manipulation registers}
+@!dx:integer; {change in edge-weight |info| when |x| changes by 1}
+@!edge_and_weight:integer; {|info| to insert}
+@!j:integer; {number of consecutive vertical moves}
+@!n:integer; {the current row pointed to by |p|}
+debug @!sum:integer;@+gubed@;@/
+begin delta:=n1-n0;
+debug sum:=move[0]; for k:=1 to delta do sum:=sum+abs(move[k]);
+if sum<>m1-m0 then confusion("0");@+gubed@;@/
+@:this can't happen 0}{\quad 0@>
+@<Prepare for and switch to the appropriate case, based on |octant|@>;
+fast_case_up:@<Add edges for first or fourth octants, then |goto done|@>;
+fast_case_down:@<Add edges for fifth or eighth octants, then |goto done|@>;
+slow_case_up:@<Add edges for second or third octants, then |goto done|@>;
+slow_case_down:@<Add edges for sixth or seventh octants, then |goto done|@>;
+done: n_pos(cur_edges):=n+zero_field; n_rover(cur_edges):=p;
+end;
+
+@ The current octant code appears in a global variable. If, for example,
+we have |octant=third_octant|, it means that a curve traveling in a north to
+north-westerly direction has been rotated for the purposes of internal
+calculations so that the |move| data travels in an east to north-easterly
+direction. We want to unrotate as we update the edge structure.
+
+@<Glob...@>=
+@!octant:first_octant..sixth_octant; {the current octant of interest}
+
+@ @<Prepare for and switch to the appropriate case, based on |octant|@>=
+case octant of
+first_octant:begin dx:=8; edge_prep(m0,m1,n0,n1); goto fast_case_up;
+ end;
+second_octant:begin dx:=8; edge_prep(n0,n1,m0,m1); goto slow_case_up;
+ end;
+third_octant:begin dx:=-8; edge_prep(-n1,-n0,m0,m1); negate(n0);
+ goto slow_case_up;
+ end;
+fourth_octant:begin dx:=-8; edge_prep(-m1,-m0,n0,n1); negate(m0);
+ goto fast_case_up;
+ end;
+fifth_octant:begin dx:=-8; edge_prep(-m1,-m0,-n1,-n0); negate(m0);
+ goto fast_case_down;
+ end;
+sixth_octant:begin dx:=-8; edge_prep(-n1,-n0,-m1,-m0); negate(n0);
+ goto slow_case_down;
+ end;
+seventh_octant:begin dx:=8; edge_prep(n0,n1,-m1,-m0); goto slow_case_down;
+ end;
+eighth_octant:begin dx:=8; edge_prep(m0,m1,-n1,-n0); goto fast_case_down;
+ end;
+end; {there are only eight octants}
+
+@ @<Add edges for first or fourth octants, then |goto done|@>=
+@<Move to row |n0|, pointed to by |p|@>;
+if delta>0 then
+ begin k:=0;
+ edge_and_weight:=8*(m0+m_offset(cur_edges))+min_halfword+zero_w-cur_wt;
+ repeat edge_and_weight:=edge_and_weight+dx*move[k];
+ fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=link(p); incr(k); incr(n);
+ until k=delta;
+ end;
+goto done
+
+@ @<Add edges for fifth or eighth octants, then |goto done|@>=
+n0:=-n0-1; @<Move to row |n0|, pointed to by |p|@>;
+if delta>0 then
+ begin k:=0;
+ edge_and_weight:=8*(m0+m_offset(cur_edges))+min_halfword+zero_w+cur_wt;
+ repeat edge_and_weight:=edge_and_weight+dx*move[k];
+ fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=knil(p); incr(k); decr(n);
+ until k=delta;
+ end;
+goto done
+
+@ @<Add edges for second or third octants, then |goto done|@>=
+edge_and_weight:=8*(n0+m_offset(cur_edges))+min_halfword+zero_w-cur_wt;
+n0:=m0; k:=0; @<Move to row |n0|, pointed to by |p|@>;
+repeat j:=move[k];
+while j>0 do
+ begin fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=link(p); decr(j); incr(n);
+ end;
+edge_and_weight:=edge_and_weight+dx; incr(k);
+until k>delta;
+goto done
+
+@ @<Add edges for sixth or seventh octants, then |goto done|@>=
+edge_and_weight:=8*(n0+m_offset(cur_edges))+min_halfword+zero_w+cur_wt;
+n0:=-m0-1; k:=0; @<Move to row |n0|, pointed to by |p|@>;
+repeat j:=move[k];
+while j>0 do
+ begin fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=knil(p); decr(j); decr(n);
+ end;
+edge_and_weight:=edge_and_weight+dx; incr(k);
+until k>delta;
+goto done
+
+@ All the hard work of building an edge structure is undone by the following
+subroutine.
+
+@<Declare the recycling subroutines@>=
+procedure toss_edges(@!h:pointer);
+var @!p,@!q:pointer; {for list manipulation}
+begin q:=link(h);
+while q<>h do
+ begin flush_list(sorted(q));
+ if unsorted(q)>void then flush_list(unsorted(q));
+ p:=q; q:=link(q); free_node(p,row_node_size);
+ end;
+free_node(h,edge_header_size);
+end;
+
+@* \[21] Subdivision into octants.
+When \MF\ digitizes a path, it reduces the problem to the special
+case of paths that travel in ``first octant'' directions; i.e.,
+each cubic $z(t)=\bigl(x(t),y(t)\bigr)$ being digitized will have the property
+that $0\L y'(t)\L x'(t)$. This assumption makes digitizing simpler
+and faster than if the direction of motion has to be tested repeatedly.
+
+When $z(t)$ is cubic, $x'(t)$ and $y'(t)$ are quadratic, hence the four
+polynomials $x'(t)$, $y'(t)$, $x'(t)-y'(t)$, and $x'(t)+y'(t)$ cross
+through~0 at most twice each. If we subdivide the given cubic at these
+places, we get at most nine subintervals in each of which
+$x'(t)$, $y'(t)$, $x'(t)-y'(t)$, and $x'(t)+y'(t)$ all have a constant
+sign. The curve can be transformed in each of these subintervals so that
+it travels entirely in first octant directions, if we reflect $x\swap-x$,
+$y\swap-y$, and/or $x\swap y$ as necessary. (Incidentally, it can be
+shown that a cubic such that $x'(t)=16(2t-1)^2+2(2t-1)-1$ and
+$y'(t)=8(2t-1)^2+4(2t-1)$ does indeed split into nine subintervals.)
+
+@ The transformation that rotates coordinates, so that first octant motion
+can be assumed, is defined by the |skew| subroutine, which sets global
+variables |cur_x| and |cur_y| to the values that are appropriate in a
+given octant. (Octants are encoded as they were in the |n_arg| subroutine.)
+
+This transformation is ``skewed'' by replacing |(x,y)| by |(x-y,y)|,
+once first octant motion has been established. It turns out that
+skewed coordinates are somewhat better to work with when curves are
+actually digitized.
+
+@d set_two_end(#)==cur_y:=#;@+end
+@d set_two(#)==begin cur_x:=#; set_two_end
+
+@p procedure skew(@!x,@!y:scaled;@!octant:small_number);
+begin case octant of
+first_octant: set_two(x-y)(y);
+second_octant: set_two(y-x)(x);
+third_octant: set_two(y+x)(-x);
+fourth_octant: set_two(-x-y)(y);
+fifth_octant: set_two(-x+y)(-y);
+sixth_octant: set_two(-y+x)(-x);
+seventh_octant: set_two(-y-x)(x);
+eighth_octant: set_two(x+y)(-y);
+end; {there are no other cases}
+end;
+
+@ Conversely, the following subroutine sets |cur_x| and
+|cur_y| to the original coordinate values of a point, given an octant
+code and the point's coordinates |(x,y)| after they have been mapped into
+the first octant and skewed.
+
+@<Declare subroutines for printing expressions@>=
+procedure unskew(@!x,@!y:scaled;@!octant:small_number);
+begin case octant of
+first_octant: set_two(x+y)(y);
+second_octant: set_two(y)(x+y);
+third_octant: set_two(-y)(x+y);
+fourth_octant: set_two(-x-y)(y);
+fifth_octant: set_two(-x-y)(-y);
+sixth_octant: set_two(-y)(-x-y);
+seventh_octant: set_two(y)(-x-y);
+eighth_octant: set_two(x+y)(-y);
+end; {there are no other cases}
+end;
+
+@ @<Glob...@>=
+@!cur_x,@!cur_y:scaled;
+ {outputs of |skew|, |unskew|, and a few other routines}
+
+@ The conversion to skewed and rotated coordinates takes place in
+stages, and at one point in the transformation we will have negated the
+$x$ and/or $y$ coordinates so as to make curves travel in the first
+{\sl quadrant}. At this point the relevant ``octant'' code will be
+either |first_octant| (when no transformation has been done),
+or |fourth_octant=first_octant+negate_x| (when $x$ has been negated),
+or |fifth_octant=first_octant+negate_x+negate_y| (when both have been
+negated), or |eighth_octant=first_octant+negate_y| (when $y$ has been
+negated). The |abnegate| routine is sometimes needed to convert
+from one of these transformations to another.
+
+@p procedure abnegate(@!x,@!y:scaled;
+ @!octant_before,@!octant_after:small_number);
+begin if odd(octant_before)=odd(octant_after) then cur_x:=x
+ else cur_x:=-x;
+if (octant_before>negate_y)=(octant_after>negate_y) then cur_y:=y
+ else cur_y:=-y;
+end;
+
+@ Now here's a subroutine that's handy for subdivision: Given a
+quadratic polynomial $B(a,b,c;t)$, the |crossing_point| function
+returns the unique |fraction| value |t| between 0 and~1 at which
+$B(a,b,c;t)$ changes from positive to negative, or returns
+|t=fraction_one+1| if no such value exists. If |a<0| (so that $B(a,b,c;t)$
+is already negative at |t=0|), |crossing_point| returns the value zero.
+
+@d no_crossing==begin crossing_point:=fraction_one+1; return;
+ end
+@d one_crossing==begin crossing_point:=fraction_one; return;
+ end
+@d zero_crossing==begin crossing_point:=0; return;
+ end
+
+@p function crossing_point(@!a,@!b,@!c:integer):fraction;
+label exit;
+var @!d:integer; {recursive counter}
+@!x,@!xx,@!x0,@!x1,@!x2:integer; {temporary registers for bisection}
+begin if a<0 then zero_crossing;
+if c>=0 then
+ begin if b>=0 then
+ if c>0 then no_crossing
+ else if (a=0)and(b=0) then no_crossing
+ else one_crossing;
+ if a=0 then zero_crossing;
+ end
+else if a=0 then if b<=0 then zero_crossing;
+@<Use bisection to find the crossing point, if one exists@>;
+exit:end;
+
+@ The general bisection method is quite simple when $n=2$, hence
+|crossing_point| does not take much time. At each stage in the
+recursion we have a subinterval defined by |l| and~|j| such that
+$B(a,b,c;2^{-l}(j+t))=B(x_0,x_1,x_2;t)$, and we want to ``zero in'' on
+the subinterval where $x_0\G0$ and $\min(x_1,x_2)<0$.
+
+It is convenient for purposes of calculation to combine the values
+of |l| and~|j| in a single variable $d=2^l+j$, because the operation
+of bisection then corresponds simply to doubling $d$ and possibly
+adding~1. Furthermore it proves to be convenient to modify
+our previous conventions for bisection slightly, maintaining the
+variables $X_0=2^lx_0$, $X_1=2^l(x_0-x_1)$, and $X_2=2^l(x_1-x_2)$.
+With these variables the conditions $x_0\ge0$ and $\min(x_1,x_2)<0$ are
+equivalent to $\max(X_1,X_1+X_2)>X_0\ge0$.
+
+The following code maintains the invariant relations
+$0\L|x0|<\max(|x1|,|x1|+|x2|)$,
+$\vert|x1|\vert<2^{30}$, $\vert|x2|\vert<2^{30}$;
+it has been constructed in such a way that no arithmetic overflow
+will occur if the inputs satisfy
+$a<2^{30}$, $\vert a-b\vert<2^{30}$, and $\vert b-c\vert<2^{30}$.
+
+@<Use bisection to find the crossing point...@>=
+d:=1; x0:=a; x1:=a-b; x2:=b-c;
+repeat x:=half(x1+x2);
+if x1-x0>x0 then
+ begin x2:=x; double(x0); double(d);
+ end
+else begin xx:=x1+x-x0;
+ if xx>x0 then
+ begin x2:=x; double(x0); double(d);
+ end
+ else begin x0:=x0-xx;
+ if x<=x0 then if x+x2<=x0 then no_crossing;
+ x1:=x; d:=d+d+1;
+ end;
+ end;
+until d>=fraction_one;
+crossing_point:=d-fraction_one
+
+@ Octant subdivision is applied only to cycles, i.e., to closed paths.
+A ``cycle spec'' is a data structure that contains specifications of
+@!@^cycle spec@>
+cubic curves and octant mappings for the cycle that has been subdivided
+into segments belonging to single octants. It is composed entirely of
+knot nodes, similar to those in the representation of paths; but the
+|explicit| type indications have been replaced by positive numbers
+that give further information. Additional |endpoint| data is also
+inserted at the octant boundaries.
+
+Recall that a cubic polynomial is represented by four control points
+that appear in adjacent nodes |p| and~|q| of a knot list. The |x|~coordinates
+are |x_coord(p)|, |right_x(p)|, |left_x(q)|, and |x_coord(q)|; the
+|y|~coordinates are similar. We shall call this ``the cubic following~|p|''
+or ``the cubic between |p| and~|q|'' or ``the cubic preceding~|q|.''
+
+Cycle specs are circular lists of cubic curves mixed with octant
+boundaries. Like cubics, the octant boundaries are represented in
+consecutive knot nodes |p| and~|q|. In such cases |right_type(p)=
+left_type(q)=endpoint|, and the fields |right_x(p)|, |right_y(p)|,
+|left_x(q)|, and |left_y(q)| are replaced by other fields called
+|right_octant(p)|, |right_transition(p)|, |left_octant(q)|, and
+|left_transition(q)|, respectively. For example, when the curve direction
+moves from the third octant to the fourth octant, the boundary nodes say
+|right_octant(p)=third_octant|, |left_octant(q)=fourth_octant|,
+and |right_transition(p)=left_transition(q)=diagonal|. A |diagonal|
+transition occurs when moving between octants 1~\AM~2, 3~\AM~4, 5~\AM~6, or
+7~\AM~8; an |axis| transition occurs when moving between octants 8~\AM~1,
+2~\AM~3, 4~\AM~5, 6~\AM~7. (Such transition information is redundant
+but convenient.) Fields |x_coord(p)| and |y_coord(p)| will contain
+coordinates of the transition point after rotation from third octant
+to first octant; i.e., if the true coordinates are $(x,y)$, the
+coordinates $(y,-x)$ will appear in node~|p|. Similarly, a fourth-octant
+transformation will have been applied after the transition, so
+we will have |x_coord(q)=@t$-x$@>| and |y_coord(q)=y|.
+
+The cubic between |p| and |q| will contain positive numbers in the
+fields |right_type(p)| and |left_type(q)|; this makes cubics
+distinguishable from octant boundaries, because |endpoint=0|.
+The value of |right_type(p)| will be the current octant code,
+during the time that cycle specs are being constructed; it will
+refer later to a pen offset position, if the envelope of a cycle is
+being computed. A cubic that comes from some subinterval of the $k$th
+step in the original cyclic path will have |left_type(q)=k|.
+
+@d right_octant==right_x {the octant code before a transition}
+@d left_octant==left_x {the octant after a transition}
+@d right_transition==right_y {the type of transition}
+@d left_transition==left_y {ditto, either |axis| or |diagonal|}
+@d axis=0 {a transition across the $x'$- or $y'$-axis}
+@d diagonal=1 {a transition where $y'=\pm x'$}
+
+@ Here's a routine that prints a cycle spec in symbolic form, so that it
+is possible to see what subdivision has been made. The point coordinates
+are converted back from \MF's internal ``rotated'' form to the external
+``true'' form. The global variable~|cur_spec| should point to a knot just
+after the beginning of an octant boundary, i.e., such that
+|left_type(cur_spec)=endpoint|.
+
+@d print_two_true(#)==unskew(#,octant); print_two(cur_x,cur_y)
+
+@p procedure print_spec(@!s:str_number);
+label not_found,done;
+var @!p,@!q:pointer; {for list traversal}
+@!octant:small_number; {the current octant code}
+begin print_diagnostic("Cycle spec",s,true);
+@.Cycle spec at line...@>
+p:=cur_spec; octant:=left_octant(p); print_ln;
+print_two_true(x_coord(cur_spec),y_coord(cur_spec));
+print(" % beginning in octant `");
+loop@+ begin print(octant_dir[octant]); print_char("'");
+ loop@+ begin q:=link(p);
+ if right_type(p)=endpoint then goto not_found;
+ @<Print the cubic between |p| and |q|@>;
+ p:=q;
+ end;
+not_found: if q=cur_spec then goto done;
+ p:=q; octant:=left_octant(p); print_nl("% entering octant `");
+ end;
+@.entering the nth octant@>
+done: print_nl(" & cycle"); end_diagnostic(true);
+end;
+
+@ Symbolic octant direction names are kept in the |octant_dir| array.
+
+@<Glob...@>=
+@!octant_dir:array[first_octant..sixth_octant] of str_number;
+
+@ @<Set init...@>=
+octant_dir[first_octant]:="ENE";
+octant_dir[second_octant]:="NNE";
+octant_dir[third_octant]:="NNW";
+octant_dir[fourth_octant]:="WNW";
+octant_dir[fifth_octant]:="WSW";
+octant_dir[sixth_octant]:="SSW";
+octant_dir[seventh_octant]:="SSE";
+octant_dir[eighth_octant]:="ESE";
+
+@ @<Print the cubic between...@>=
+begin print_nl(" ..controls ");
+print_two_true(right_x(p),right_y(p));
+print(" and ");
+print_two_true(left_x(q),left_y(q));
+print_nl(" ..");
+print_two_true(x_coord(q),y_coord(q));
+print(" % segment "); print_int(left_type(q)-1);
+end
+
+@ A much more compact version of a spec is printed to help users identify
+``strange paths.''
+
+@p procedure print_strange(@!s:str_number);
+var @!p:pointer; {for list traversal}
+@!f:pointer; {starting point in the cycle}
+@!q:pointer; {octant boundary to be printed}
+@!t:integer; {segment number, plus 1}
+begin if interaction=error_stop_mode then wake_up_terminal;
+print_nl(">");
+@.>\relax@>
+@<Find the starting point, |f|@>;
+@<Determine the octant boundary |q| that precedes |f|@>;
+t:=0;
+repeat if left_type(p)<>endpoint then
+ begin if left_type(p)<>t then
+ begin t:=left_type(p); print_char(" "); print_int(t-1);
+ end;
+ if q<>null then
+ begin @<Print the turns, if any, that start at |q|, and advance |q|@>;
+ print_char(" "); print(octant_dir[left_octant(q)]); q:=null;
+ end;
+ end
+else if q=null then q:=p;
+p:=link(p);
+until p=f;
+print_char(" "); print_int(left_type(p)-1);
+if q<>null then @<Print the turns...@>;
+print_err(s);
+end;
+
+@ If the segment numbers on the cycle are $t_1$, $t_2$, \dots, $t_m$,
+and if |m<=max_quarterword|,
+we have $t_{k-1}\L t_k$ except for at most one value of~$k$. If there are
+no exceptions, $f$ will point to $t_1$; otherwise it will point to the
+exceptional~$t_k$.
+
+There is at least one segment number (i.e., we always have $m>0$), because
+|print_strange| is never called upon to display an entirely ``dead'' cycle.
+
+@<Find the starting point, |f|@>=
+p:=cur_spec; t:=max_quarterword+1;
+repeat p:=link(p);
+if left_type(p)<>endpoint then
+ begin if left_type(p)<t then f:=p;
+ t:=left_type(p);
+ end;
+until p=cur_spec
+
+@ @<Determine the octant boundary...@>=
+p:=cur_spec; q:=p;
+repeat p:=link(p);
+if left_type(p)=endpoint then q:=p;
+until p=f
+
+@ When two octant boundaries are adjacent, the path is simply changing direction
+without moving. Such octant directions are shown in parentheses.
+
+@<Print the turns...@>=
+if left_type(link(q))=endpoint then
+ begin print(" ("); print(octant_dir[left_octant(q)]); q:=link(q);
+ while left_type(link(q))=endpoint do
+ begin print_char(" "); print(octant_dir[left_octant(q)]); q:=link(q);
+ end;
+ print_char(")");
+ end
+
+@ The |make_spec| routine is what subdivides paths into octants:
+Given a pointer |cur_spec| to a cyclic path, |make_spec| mungs the path data
+and returns a pointer to the corresponding cyclic spec.
+All ``dead'' cubics (i.e., cubics that don't move at all from
+their starting points) will have been removed from the result.
+@!@^dead cubics@>
+
+The idea of |make_spec| is fairly simple: Each cubic is first
+subdivided, if necessary, into pieces belonging to single octants;
+then the octant boundaries are inserted. But some of the details of
+this transformation are not quite obvious.
+
+If |autorounding>0|, the path will be adjusted so that critical tangent
+directions occur at ``good'' points with respect to the pen called |cur_pen|.
+
+The resulting spec will have all |x| and |y| coordinates at most
+$2^{28}-|half_unit|-1-|safety_margin|$ in absolute value. The pointer
+that is returned will start some octant, as required by |print_spec|.
+
+@p @t\4@>@<Declare subroutines needed by |make_spec|@>@;
+function make_spec(@!h:pointer;
+ @!safety_margin:scaled;@!tracing:integer):pointer;
+ {converts a path to a cycle spec}
+label continue,done;
+var @!p,@!q,@!r,@!s:pointer; {for traversing the lists}
+@!k:integer; {serial number of path segment, or octant code}
+@!chopped:integer; {positive if data truncated,
+ negative if data dangerously large}
+@<Other local variables for |make_spec|@>@;
+begin cur_spec:=h;
+if tracing>0 then
+ print_path(cur_spec,", before subdivision into octants",true);
+max_allowed:=fraction_one-half_unit-1-safety_margin;
+@<Truncate the values of all coordinates that exceed |max_allowed|, and stamp
+ segment numbers in each |left_type| field@>;
+quadrant_subdivide; {subdivide each cubic into pieces belonging to quadrants}
+if (internal[autorounding]>0)and(chopped=0) then xy_round;
+octant_subdivide; {complete the subdivision}
+if (internal[autorounding]>unity)and(chopped=0) then diag_round;
+@<Remove dead cubics@>;
+@<Insert octant boundaries and compute the turning number@>;
+while left_type(cur_spec)<>endpoint do cur_spec:=link(cur_spec);
+if tracing>0 then
+ if (internal[autorounding]<=0)or(chopped<>0) then
+ print_spec(", after subdivision")
+ else if internal[autorounding]>unity then
+ print_spec(", after subdivision and double autorounding")
+ else print_spec(", after subdivision and autorounding");
+make_spec:=cur_spec;
+end;
+
+@ The |make_spec| routine has an interesting side effect, namely to set
+the global variable |turning_number| to the number of times the tangent
+vector of the given cyclic path winds around the origin.
+
+Another global variable |cur_spec| points to the specification as it is
+being made, since several subroutines must go to work on it.
+
+And there are two global variables that affect the rounding
+decisions, as we'll see later; they are called |cur_pen| and |cur_path_type|.
+The latter will be |double_path_code| if |make_spec| is being
+applied to a double path.
+
+@d double_path_code=0 {command modifier for `\&{doublepath}'}
+@d contour_code=1 {command modifier for `\&{contour}'}
+@d also_code=2 {command modifier for `\&{also}'}
+
+@<Glob...@>=
+@!cur_spec:pointer; {the principal output of |make_spec|}
+@!turning_number:integer; {another output of |make_spec|}
+@!cur_pen:pointer; {an implicit input of |make_spec|, used in autorounding}
+@!cur_path_type:double_path_code..contour_code; {likewise}
+@!max_allowed:scaled; {coordinates must be at most this big}
+
+@ First we do a simple preprocessing step. The segment numbers inserted
+here will propagate to all descendants of cubics that are split into
+subintervals. These numbers must be nonzero, but otherwise they are
+present merely for diagnostic purposes. The cubic from |p| to~|q|
+that represents ``time interval'' |(t-1)..t| usually has |left_type(q)=t|,
+except when |t| is too large to be stored in a quarterword.
+
+@d procrustes(#)==@+if abs(#)>=dmax then
+ if abs(#)>max_allowed then
+ begin chopped:=1;
+ if #>0 then #:=max_allowed@+else #:=-max_allowed;
+ end
+ else if chopped=0 then chopped:=-1
+
+@<Truncate the values of all coordinates that exceed...@>=
+p:=cur_spec; k:=1; chopped:=0; dmax:=half(max_allowed);
+repeat procrustes(left_x(p)); procrustes(left_y(p));
+procrustes(x_coord(p)); procrustes(y_coord(p));
+procrustes(right_x(p)); procrustes(right_y(p));@/
+p:=link(p); left_type(p):=k;
+if k<max_quarterword then incr(k)@+else k:=1;
+until p=cur_spec;
+if chopped>0 then
+ begin print_err("Curve out of range");
+@.Curve out of range@>
+ help4("At least one of the coordinates in the path I'm about to")@/
+ ("digitize was really huge (potentially bigger than 4095).")@/
+ ("So I've cut it back to the maximum size.")@/
+ ("The results will probably be pretty wild.");
+ put_get_error;
+ end
+
+@ We may need to get rid of constant ``dead'' cubics that clutter up
+the data structure and interfere with autorounding.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure remove_cubic(@!p:pointer); {removes the cubic following~|p|}
+var @!q:pointer; {the node that disappears}
+begin q:=link(p); right_type(p):=right_type(q); link(p):=link(q);@/
+x_coord(p):=x_coord(q); y_coord(p):=y_coord(q);@/
+right_x(p):=right_x(q); right_y(p):=right_y(q);@/
+free_node(q,knot_node_size);
+end;
+
+@ The subdivision process proceeds by first swapping $x\swap-x$, if
+necessary, to ensure that $x'\G0$; then swapping $y\swap-y$, if necessary,
+to ensure that $y'\G0$; and finally swapping $x\swap y$, if necessary,
+to ensure that $x'\G y'$.
+
+Recall that the octant codes have been defined in such a way that, for
+example, |third_octant=first_octant+negate_x+switch_x_and_y|. The program
+uses the fact that |negate_x<negate_y<switch_x_and_y| to handle ``double
+negation'': If |c| is an octant code that possibly involves |negate_x|
+and/or |negate_y|, but not |switch_x_and_y|, then negating~|y| changes~|c|
+either to |c+negate_y| or |c-negate_y|, depending on whether
+|c<=negate_y| or |c>negate_y|. Octant codes are always greater than zero.
+
+The first step is to subdivide on |x| and |y| only, so that horizontal
+and vertical autorounding can be done before we compare $x'$ to $y'$.
+
+@<Declare subroutines needed by |make_spec|@>=
+@t\4@>@<Declare the procedure called |split_cubic|@>@;
+procedure quadrant_subdivide;
+label continue,exit;
+var @!p,@!q,@!r,@!s,@!pp,@!qq:pointer; {for traversing the lists}
+@!first_x,@!first_y:scaled; {unnegated coordinates of node |cur_spec|}
+@!del1,@!del2,@!del3,@!del,@!dmax:scaled; {proportional to the control
+ points of a quadratic derived from a cubic}
+@!t:fraction; {where a quadratic crosses zero}
+@!dest_x,@!dest_y:scaled; {final values of |x| and |y| in the current cubic}
+@!constant_x:boolean; {is |x| constant between |p| and |q|?}
+begin p:=cur_spec; first_x:=x_coord(cur_spec); first_y:=y_coord(cur_spec);
+repeat continue: q:=link(p);
+@<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the right halfplane@>;
+@<Subdivide all cubics between |p| and |q| so that the results travel
+ toward the first quadrant; but |return| or |goto continue| if the
+ cubic from |p| to |q| was dead@>;
+p:=q;
+until p=cur_spec;
+exit:end;
+
+@ All three subdivision processes are similar, so it's possible to
+get the general idea by studying the first one (which is the simplest).
+The calculation makes use of the fact that the derivatives of
+Bernshte{\u\i}n polynomials satisfy
+$B'(z_0,z_1,\ldots,z_n;t)=nB(z_1-z_0,\ldots,z_n-z_{n-1};t)$.
+
+When this routine begins, |right_type(p)| is |explicit|; we should
+set |right_type(p):=first_octant|. However, no assignment is made,
+because |explicit=first_octant|. The author apologizes for using
+such trickery here; it is really hard to do redundant computations
+just for the sake of purity.
+
+@<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the right halfplane...@>=
+if q=cur_spec then
+ begin dest_x:=first_x; dest_y:=first_y;
+ end
+else begin dest_x:=x_coord(q); dest_y:=y_coord(q);
+ end;
+del1:=right_x(p)-x_coord(p); del2:=left_x(q)-right_x(p);
+del3:=dest_x-left_x(q);
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy;
+ also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
+if del=0 then constant_x:=true
+else begin constant_x:=false;
+ if del<0 then @<Complement the |x| coordinates of the
+ cubic between |p| and~|q|@>;
+ t:=crossing_point(del1,del2,del3);
+ if t<fraction_one then
+ @<Subdivide the cubic with respect to $x'$, possibly twice@>;
+ end
+
+@ If |del1=del2=del3=0|, it's impossible to obey the title of this
+section. We just set |del=0| in that case.
+@^inner loop@>
+
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>=
+if del1<>0 then del:=del1
+else if del2<>0 then del:=del2
+else del:=del3;
+if del<>0 then
+ begin dmax:=abs(del1);
+ if abs(del2)>dmax then dmax:=abs(del2);
+ if abs(del3)>dmax then dmax:=abs(del3);
+ while dmax<fraction_half do
+ begin double(dmax); double(del1); double(del2); double(del3);
+ end;
+ end
+
+@ During the subdivision phases of |make_spec|, the |x_coord| and |y_coord|
+fields of node~|q| are not transformed to agree with the octant
+stated in |right_type(p)|; they remain consistent with |right_type(q)|.
+But |left_x(q)| and |left_y(q)| are governed by |right_type(p)|.
+
+@<Complement the |x| coordinates...@>=
+begin negate(x_coord(p)); negate(right_x(p));
+negate(left_x(q));@/
+negate(del1); negate(del2); negate(del3);@/
+negate(dest_x);
+right_type(p):=first_octant+negate_x;
+end
+
+@ When a cubic is split at a |fraction| value |t|, we obtain two cubics
+whose B\'ezier control points are obtained by a generalization of the
+bisection process: The formula
+`$z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$' becomes
+`$z_k^{(j+1)}=t[z_k^{(j)},z\k^{(j)}]$'.
+
+It is convenient to define a \.{WEB} macro |t_of_the_way| such that
+|t_of_the_way(a)(b)| expands to |a-(a-b)*t|, i.e., to |t[a,b]|.
+
+If |0<=t<=1|, the quantity |t[a,b]| is always between |a| and~|b|, even in
+the presence of rounding errors. Our subroutines
+also obey the identity |t[a,b]+t[b,a]=a+b|.
+
+@d t_of_the_way_end(#)==#,t@=)@>
+@d t_of_the_way(#)==#-take_fraction@=(@>#-t_of_the_way_end
+
+@<Declare the procedure called |split_cubic|@>=
+procedure split_cubic(@!p:pointer;@!t:fraction;
+ @!xq,@!yq:scaled); {splits the cubic after |p|}
+var @!v:scaled; {an intermediate value}
+@!q,@!r:pointer; {for list manipulation}
+begin q:=link(p); r:=get_node(knot_node_size); link(p):=r; link(r):=q;@/
+left_type(r):=left_type(q); right_type(r):=right_type(p);@#
+v:=t_of_the_way(right_x(p))(left_x(q));
+right_x(p):=t_of_the_way(x_coord(p))(right_x(p));
+left_x(q):=t_of_the_way(left_x(q))(xq);
+left_x(r):=t_of_the_way(right_x(p))(v);
+right_x(r):=t_of_the_way(v)(left_x(q));
+x_coord(r):=t_of_the_way(left_x(r))(right_x(r));@#
+v:=t_of_the_way(right_y(p))(left_y(q));
+right_y(p):=t_of_the_way(y_coord(p))(right_y(p));
+left_y(q):=t_of_the_way(left_y(q))(yq);
+left_y(r):=t_of_the_way(right_y(p))(v);
+right_y(r):=t_of_the_way(v)(left_y(q));
+y_coord(r):=t_of_the_way(left_y(r))(right_y(r));
+end;
+
+@ Since $x'(t)$ is a quadratic equation, it can cross through zero
+at~most twice. When it does cross zero, we make doubly sure that the
+derivative is really zero at the splitting point, in case rounding errors
+have caused the split cubic to have an apparently nonzero derivative.
+We also make sure that the split cubic is monotonic.
+
+@<Subdivide the cubic with respect to $x'$, possibly twice@>=
+begin split_cubic(p,t,dest_x,dest_y); r:=link(p);
+if right_type(r)>negate_x then right_type(r):=first_octant
+else right_type(r):=first_octant+negate_x;
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p);
+left_x(r):=x_coord(r);
+if right_x(p)>x_coord(r) then right_x(p):=x_coord(r);
+ {we always have |x_coord(p)<=right_x(p)|}
+negate(x_coord(r)); right_x(r):=x_coord(r);
+negate(left_x(q)); negate(dest_x);@/
+del2:=t_of_the_way(del2)(del3);
+ {now |0,del2,del3| represent $x'$ on the remaining interval}
+if del2>0 then del2:=0;
+t:=crossing_point(0,-del2,-del3);
+if t<fraction_one then @<Subdivide the cubic a second time
+ with respect to $x'$@>
+else begin if x_coord(r)>dest_x then
+ begin x_coord(r):=dest_x; left_x(r):=-x_coord(r); right_x(r):=x_coord(r);
+ end;
+ if left_x(q)>dest_x then left_x(q):=dest_x
+ else if left_x(q)<x_coord(r) then left_x(q):=x_coord(r);
+ end;
+end
+
+@ @<Subdivide the cubic a second time with respect to $x'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);
+if x_coord(s)<dest_x then x_coord(s):=dest_x;
+if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r);
+right_type(s):=right_type(p);
+left_x(s):=x_coord(s); {now |x_coord(r)=right_x(r)<=left_x(s)|}
+if left_x(q)<dest_x then left_x(q):=-dest_x
+else if left_x(q)>x_coord(s) then left_x(q):=-x_coord(s)
+else negate(left_x(q));
+negate(x_coord(s)); right_x(s):=x_coord(s);
+end
+
+@ The process of subdivision with respect to $y'$ is like that with respect
+to~$x'$, with the slight additional complication that two or three cubics
+might now appear between |p| and~|q|.
+
+@<Subdivide all cubics between |p| and |q| so that the results travel
+ toward the first quadrant...@>=
+pp:=p;
+repeat qq:=link(pp);
+abnegate(x_coord(qq),y_coord(qq),right_type(qq),right_type(pp));
+dest_x:=cur_x; dest_y:=cur_y;@/
+del1:=right_y(pp)-y_coord(pp); del2:=left_y(qq)-right_y(pp);
+del3:=dest_y-left_y(qq);
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy;
+ also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
+if del<>0 then {they weren't all zero}
+ begin if del<0 then @<Complement the |y| coordinates of the
+ cubic between |pp| and~|qq|@>;
+ t:=crossing_point(del1,del2,del3);
+ if t<fraction_one then
+ @<Subdivide the cubic with respect to $y'$, possibly twice@>;
+ end
+else @<Do any special actions needed when |y| is constant;
+ |return| or |goto continue| if a dead cubic from |p| to |q| is removed@>;
+pp:=qq;
+until pp=q;
+if constant_x then @<Correct the octant code in segments with decreasing |y|@>
+
+@ @<Complement the |y| coordinates...@>=
+begin negate(y_coord(pp)); negate(right_y(pp));
+negate(left_y(qq));@/
+negate(del1); negate(del2); negate(del3);@/
+negate(dest_y);
+right_type(pp):=right_type(pp)+negate_y;
+end
+
+@ @<Subdivide the cubic with respect to $y'$, possibly twice@>=
+begin split_cubic(pp,t,dest_x,dest_y); r:=link(pp);
+if right_type(r)>negate_y then right_type(r):=right_type(r)-negate_y
+else right_type(r):=right_type(r)+negate_y;
+if y_coord(r)<y_coord(pp) then y_coord(r):=y_coord(pp);
+left_y(r):=y_coord(r);
+if right_y(pp)>y_coord(r) then right_y(pp):=y_coord(r);
+ {we always have |y_coord(pp)<=right_y(pp)|}
+negate(y_coord(r)); right_y(r):=y_coord(r);
+negate(left_y(qq)); negate(dest_y);@/
+if x_coord(r)<x_coord(pp) then x_coord(r):=x_coord(pp)
+else if x_coord(r)>dest_x then x_coord(r):=dest_x;
+if left_x(r)>x_coord(r) then
+ begin left_x(r):=x_coord(r);
+ if right_x(pp)>x_coord(r) then right_x(pp):=x_coord(r);
+ end;
+if right_x(r)<x_coord(r) then
+ begin right_x(r):=x_coord(r);
+ if left_x(qq)<x_coord(r) then left_x(qq):=x_coord(r);
+ end;
+del2:=t_of_the_way(del2)(del3);
+ {now |0,del2,del3| represent $y'$ on the remaining interval}
+if del2>0 then del2:=0;
+t:=crossing_point(0,-del2,-del3);
+if t<fraction_one then @<Subdivide the cubic a second time
+ with respect to $y'$@>
+else begin if y_coord(r)>dest_y then
+ begin y_coord(r):=dest_y; left_y(r):=-y_coord(r); right_y(r):=y_coord(r);
+ end;
+ if left_y(qq)>dest_y then left_y(qq):=dest_y
+ else if left_y(qq)<y_coord(r) then left_y(qq):=y_coord(r);
+ end;
+end
+
+@ @<Subdivide the cubic a second time with respect to $y'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);@/
+if y_coord(s)<dest_y then y_coord(s):=dest_y;
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r);
+right_type(s):=right_type(pp);
+left_y(s):=y_coord(s); {now |y_coord(r)=right_y(r)<=left_y(s)|}
+if left_y(qq)<dest_y then left_y(qq):=-dest_y
+else if left_y(qq)>y_coord(s) then left_y(qq):=-y_coord(s)
+else negate(left_y(qq));
+negate(y_coord(s)); right_y(s):=y_coord(s);
+if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r)
+else if x_coord(s)>dest_x then x_coord(s):=dest_x;
+if left_x(s)>x_coord(s) then
+ begin left_x(s):=x_coord(s);
+ if right_x(r)>x_coord(s) then right_x(r):=x_coord(s);
+ end;
+if right_x(s)<x_coord(s) then
+ begin right_x(s):=x_coord(s);
+ if left_x(qq)<x_coord(s) then left_x(qq):=x_coord(s);
+ end;
+end
+
+@ If the cubic is constant in $y$ and increasing in $x$, we have classified
+it as traveling in the first octant. If the cubic is constant
+in~$y$ and decreasing in~$x$, it is desirable to classify it as traveling
+in the fifth octant (not the fourth), because autorounding will be consistent
+with respect to doublepaths only if the octant number changes by four when
+the path is reversed. Therefore we negate the $y$~coordinates
+when they are constant but the curve is decreasing in~$x$; this gives
+the desired result except in pathological paths.
+
+If the cubic is ``dead,'' i.e., constant in both |x| and |y|, we remove
+it unless it is the only cubic in the entire path. We |goto continue|
+if it wasn't the final cubic, so that the test |p=cur_spec| does not
+falsely imply that all cubics have been processed.
+
+@<Do any special actions needed when |y| is constant...@>=
+if constant_x then {|p=pp|, |q=qq|, and the cubic is dead}
+ begin if q<>p then
+ begin remove_cubic(p); {remove the dead cycle and recycle node |q|}
+ if cur_spec<>q then goto continue
+ else begin cur_spec:=p; return;
+ end; {the final cubic was dead and is gone}
+ end;
+ end
+else if not odd(right_type(pp)) then {the $x$ coordinates were negated}
+ @<Complement the |y| coordinates...@>
+
+@ A similar correction to octant codes deserves to be made when |x| is
+constant and |y| is decreasing.
+
+@<Correct the octant code in segments with decreasing |y|@>=
+begin pp:=p;
+repeat qq:=link(pp);
+if right_type(pp)>negate_y then {the $y$ coordinates were negated}
+ begin right_type(pp):=right_type(pp)+negate_x;
+ negate(x_coord(pp)); negate(right_x(pp)); negate(left_x(qq));
+ end;
+pp:=qq;
+until pp=q;
+end
+
+@ Finally, the process of subdividing to make $x'\G y'$ is like the other
+two subdivisions, with a few new twists. We skew the coordinates at this time.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure octant_subdivide;
+var @!p,@!q,@!r,@!s:pointer; {for traversing the lists}
+@!del1,@!del2,@!del3,@!del,@!dmax:scaled; {proportional to the control
+ points of a quadratic derived from a cubic}
+@!t:fraction; {where a quadratic crosses zero}
+@!dest_x,@!dest_y:scaled; {final values of |x| and |y| in the current cubic}
+begin p:=cur_spec;
+repeat q:=link(p);@/
+x_coord(p):=x_coord(p)-y_coord(p);
+right_x(p):=right_x(p)-right_y(p);
+left_x(q):=left_x(q)-left_y(q);@/
+@<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the first octant@>;
+p:=q;
+until p=cur_spec;
+end;
+
+@ @<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the first octant@>=
+@<Set up the variables |(del1,del2,del3)| to represent $x'-y'$@>;
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy;
+ also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
+if del<>0 then {they weren't all zero}
+ begin if del<0 then @<Swap the |x| and |y| coordinates of the
+ cubic between |p| and~|q|@>;
+ t:=crossing_point(del1,del2,del3);
+ if t<fraction_one then
+ @<Subdivide the cubic with respect to $x'-y'$, possibly twice@>;
+ end
+
+@ @<Set up the variables |(del1,del2,del3)| to represent $x'-y'$@>=
+if q=cur_spec then
+ begin unskew(x_coord(q),y_coord(q),right_type(q));
+ skew(cur_x,cur_y,right_type(p)); dest_x:=cur_x; dest_y:=cur_y;
+ end
+else begin abnegate(x_coord(q),y_coord(q),right_type(q),right_type(p));
+ dest_x:=cur_x-cur_y; dest_y:=cur_y;
+ end;
+del1:=right_x(p)-x_coord(p); del2:=left_x(q)-right_x(p);
+del3:=dest_x-left_x(q)
+
+@ The swapping here doesn't simply interchange |x| and |y| values,
+because the coordinates are skewed. It turns out that this is easier
+than ordinary swapping, because it can be done in two assignment statements
+rather than three.
+
+@ @<Swap the |x| and |y| coordinates...@>=
+begin y_coord(p):=x_coord(p)+y_coord(p); negate(x_coord(p));@/
+right_y(p):=right_x(p)+right_y(p); negate(right_x(p));@/
+left_y(q):=left_x(q)+left_y(q); negate(left_x(q));@/
+negate(del1); negate(del2); negate(del3);@/
+dest_y:=dest_x+dest_y; negate(dest_x);@/
+right_type(p):=right_type(p)+switch_x_and_y;
+end
+
+@ A somewhat tedious case analysis is carried out here to make sure that
+nasty rounding errors don't destroy our assumptions of monotonicity.
+
+@<Subdivide the cubic with respect to $x'-y'$, possibly twice@>=
+begin split_cubic(p,t,dest_x,dest_y); r:=link(p);
+if right_type(r)>switch_x_and_y then right_type(r):=right_type(r)-switch_x_and_y
+else right_type(r):=right_type(r)+switch_x_and_y;
+if y_coord(r)<y_coord(p) then y_coord(r):=y_coord(p)
+else if y_coord(r)>dest_y then y_coord(r):=dest_y;
+if x_coord(p)+y_coord(r)>dest_x+dest_y then
+ y_coord(r):=dest_x+dest_y-x_coord(p);
+if left_y(r)>y_coord(r) then
+ begin left_y(r):=y_coord(r);
+ if right_y(p)>y_coord(r) then right_y(p):=y_coord(r);
+ end;
+if right_y(r)<y_coord(r) then
+ begin right_y(r):=y_coord(r);
+ if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+ end;
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p)
+else if x_coord(r)+y_coord(r)>dest_x+dest_y then
+ x_coord(r):=dest_x+dest_y-y_coord(r);
+left_x(r):=x_coord(r);
+if right_x(p)>x_coord(r) then right_x(p):=x_coord(r);
+ {we always have |x_coord(p)<=right_x(p)|}
+y_coord(r):=y_coord(r)+x_coord(r); right_y(r):=right_y(r)+x_coord(r);@/
+negate(x_coord(r)); right_x(r):=x_coord(r);@/
+left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@/
+dest_y:=dest_y+dest_x; negate(dest_x);
+if right_y(r)<y_coord(r) then
+ begin right_y(r):=y_coord(r);
+ if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+ end;
+del2:=t_of_the_way(del2)(del3);
+ {now |0,del2,del3| represent $x'-y'$ on the remaining interval}
+if del2>0 then del2:=0;
+t:=crossing_point(0,-del2,-del3);
+if t<fraction_one then
+ @<Subdivide the cubic a second time with respect to $x'-y'$@>
+else begin if x_coord(r)>dest_x then
+ begin x_coord(r):=dest_x; left_x(r):=-x_coord(r); right_x(r):=x_coord(r);
+ end;
+ if left_x(q)>dest_x then left_x(q):=dest_x
+ else if left_x(q)<x_coord(r) then left_x(q):=x_coord(r);
+ end;
+end
+
+@ @<Subdivide the cubic a second time with respect to $x'-y'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);@/
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r)
+else if y_coord(s)>dest_y then y_coord(s):=dest_y;
+if x_coord(r)+y_coord(s)>dest_x+dest_y then
+ y_coord(s):=dest_x+dest_y-x_coord(r);
+if left_y(s)>y_coord(s) then
+ begin left_y(s):=y_coord(s);
+ if right_y(r)>y_coord(s) then right_y(r):=y_coord(s);
+ end;
+if right_y(s)<y_coord(s) then
+ begin right_y(s):=y_coord(s);
+ if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+ end;
+if x_coord(s)+y_coord(s)>dest_x+dest_y then x_coord(s):=dest_x+dest_y-y_coord(s)
+else begin if x_coord(s)<dest_x then x_coord(s):=dest_x;
+ if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r);
+ end;
+right_type(s):=right_type(p);
+left_x(s):=x_coord(s); {now |x_coord(r)=right_x(r)<=left_x(s)|}
+if left_x(q)<dest_x then
+ begin left_y(q):=left_y(q)+dest_x; left_x(q):=-dest_x;@+end
+else if left_x(q)>x_coord(s) then
+ begin left_y(q):=left_y(q)+x_coord(s); left_x(q):=-x_coord(s);@+end
+else begin left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@+end;
+y_coord(s):=y_coord(s)+x_coord(s); right_y(s):=right_y(s)+x_coord(s);@/
+negate(x_coord(s)); right_x(s):=x_coord(s);@/
+if right_y(s)<y_coord(s) then
+ begin right_y(s):=y_coord(s);
+ if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+ end;
+end
+
+@ It's time now to consider ``autorounding,'' which tries to make horizontal,
+vertical, and diagonal tangents occur at places that will produce appropriate
+images after the curve is digitized.
+
+The first job is to fix things so that |x(t)| plus the horizontal pen offset
+is an integer multiple of the
+current ``granularity'' when the derivative $x'(t)$ crosses through zero.
+The given cyclic path contains regions where $x'(t)\G0$ and regions
+where $x'(t)\L0$. The |quadrant_subdivide| routine is called into action
+before any of the path coordinates have been skewed, but some of them
+may have been negated. In regions where $x'(t)\G0$ we have |right_type=
+first_octant| or |right_type=eighth_octant|; in regions where $x'(t)\L0$,
+we have |right_type=fifth_octant| or |right_type=fourth_octant|.
+
+Within any such region the transformed $x$ values increase monotonically
+from, say, $x_0$ to~$x_1$. We want to modify things by applying a linear
+transformation to all $x$ coordinates in the region, after which
+the $x$ values will increase monotonically from round$(x_0)$ to round$(x_1)$.
+
+This rounding scheme sounds quite simple, and it usually is. But several
+complications can arise that might make the task more difficult. In the
+first place, autorounding is inappropriate at cusps where $x'$ jumps
+discontinuously past zero without ever being zero. In the second place,
+the current pen might be unsymmetric in such a way that $x$ coordinates
+should round differently in different parts of the curve.
+These considerations imply that round$(x_0)$ might be greater
+than round$(x_1)$, even though $x_0\L x_1$; in such cases we do not want
+to carry out the linear transformation. Furthermore, it's possible to have
+round$(x_1)-\hbox{round} (x_0)$ positive but much greater than $x_1-x_0$;
+then the transformation might distort the curve drastically, and again we
+want to avoid it. Finally, the rounded points must be consistent between
+adjacent regions, hence we can't transform one region without knowing
+about its neighbors.
+
+To handle all these complications, we must first look at the whole
+cycle and choose rounded $x$ values that are ``safe.'' The following
+procedure does this: Given $m$~values $(b_0,b_1,\ldots,b_{m-1})$ before
+rounding and $m$~corresponding values $(a_0,a_1,\ldots,a_{m-1})$ that would
+be desirable after rounding, the |make_safe| routine sets $a$'s to $b$'s
+if necessary so that $0\L(a\k-a_k)/(b\k-b_k)\L2$ afterwards. It is
+symmetric under cyclic permutation, reversal, and/or negation of the inputs.
+(Instead of |a|, |b|, and~|m|, the program uses the names |after|,
+|before|, and |cur_rounding_ptr|.)
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure make_safe;
+var @!k:0..max_wiggle; {runs through the list of inputs}
+@!all_safe:boolean; {does everything look OK so far?}
+@!next_a:scaled; {|after[k]| before it might have changed}
+@!delta_a,@!delta_b:scaled; {|after[k+1]-after[k]| and |before[k+1]-before[k]|}
+begin before[cur_rounding_ptr]:=before[0]; {wrap around}
+node_to_round[cur_rounding_ptr]:=node_to_round[0];
+repeat after[cur_rounding_ptr]:=after[0]; all_safe:=true; next_a:=after[0];
+for k:=0 to cur_rounding_ptr-1 do
+ begin delta_b:=before[k+1]-before[k];
+ if delta_b>=0 then delta_a:=after[k+1]-next_a
+ else delta_a:=next_a-after[k+1];
+ next_a:=after[k+1];
+ if (delta_a<0)or(delta_a>abs(delta_b+delta_b)) then
+ begin all_safe:=false; after[k]:=before[k];
+ if k=cur_rounding_ptr-1 then after[0]:=before[0]
+ else after[k+1]:=before[k+1];
+ end;
+ end;
+until all_safe;
+end;
+
+@ The global arrays used by |make_safe| are accompanied by an array of
+pointers into the current knot list.
+
+@<Glob...@>=
+@!before,@!after:array[0..max_wiggle] of scaled; {data for |make_safe|}
+@!node_to_round:array[0..max_wiggle] of pointer; {reference back to the path}
+@!cur_rounding_ptr:0..max_wiggle; {how many are being used}
+@!max_rounding_ptr:0..max_wiggle; {how many have been used}
+
+@ @<Set init...@>=
+max_rounding_ptr:=0;
+
+@ New entries go into the tables via the |before_and_after| routine:
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure before_and_after(@!b,@!a:scaled;@!p:pointer);
+begin if cur_rounding_ptr=max_rounding_ptr then
+ if max_rounding_ptr<max_wiggle then incr(max_rounding_ptr)
+ else overflow("rounding table size",max_wiggle);
+@:METAFONT capacity exceeded rounding table size}{\quad rounding table size@>
+after[cur_rounding_ptr]:=a; before[cur_rounding_ptr]:=b;
+node_to_round[cur_rounding_ptr]:=p; incr(cur_rounding_ptr);
+end;
+
+@ A global variable called |cur_gran| is used instead of |internal[
+granularity]|, because we want to work with a number that's guaranteed to
+be positive.
+
+@<Glob...@>=
+@!cur_gran:scaled; {the current granularity (which normally is |unity|)}
+
+@ The |good_val| function computes a number |a| that's as close as
+possible to~|b|, with the property that |a+o| is a multiple of
+|cur_gran|.
+
+If we assume that |cur_gran| is even (since it will in fact be a multiple
+of |unity| in all reasonable applications), we have the identity
+|good_val(-b-1,-o)=-good_val(b,o)|.
+
+@<Declare subroutines needed by |make_spec|@>=
+function good_val(@!b,@!o:scaled):scaled;
+var @!a:scaled; {accumulator}
+begin a:=b+o;
+if a>=0 then a:=a-(a mod cur_gran)-o
+else a:=a+((-(a+1)) mod cur_gran)-cur_gran+1-o;
+if b-a<a+cur_gran-b then good_val:=a
+else good_val:=a+cur_gran;
+end;
+
+@ When we're rounding a doublepath, we might need to compromise between
+two opposing tendencies, if the pen thickness is not a multiple of the
+granularity. The following ``compromise'' adjustment, suggested by
+John Hobby, finds the best way out of the dilemma. (Only the value
+@^Hobby, John Douglas@>
+modulo |cur_gran| is relevant in our applications, so the result turns
+out to be essentially symmetric in |u| and~|v|.)
+
+@<Declare subroutines needed by |make_spec|@>=
+function compromise(@!u,@!v:scaled):scaled;
+begin compromise:=half(good_val(u+u,-u-v));
+end;
+
+@ Here, then, is the procedure that rounds $x$ coordinates as described;
+it does the same for $y$ coordinates too, independently.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure xy_round;
+var @!p,@!q:pointer; {list manipulation registers}
+@!b,@!a:scaled; {before and after values}
+@!pen_edge:scaled; {offset that governs rounding}
+@!alpha:fraction; {coefficient of linear transformation}
+begin cur_gran:=abs(internal[granularity]);
+if cur_gran=0 then cur_gran:=unity;
+p:=cur_spec; cur_rounding_ptr:=0;
+repeat q:=link(p);
+@<If node |q| is a transition point for |x| coordinates,
+ compute and save its before-and-after coordinates@>;
+p:=q;
+until p=cur_spec;
+if cur_rounding_ptr>0 then @<Transform the |x| coordinates@>;
+p:=cur_spec; cur_rounding_ptr:=0;
+repeat q:=link(p);
+@<If node |q| is a transition point for |y| coordinates,
+ compute and save its before-and-after coordinates@>;
+p:=q;
+until p=cur_spec;
+if cur_rounding_ptr>0 then @<Transform the |y| coordinates@>;
+end;
+
+@ When |x| has been negated, the |octant| codes are even. We allow
+for an error of up to .01 pixel (i.e., 655 |scaled| units) in the
+derivative calculations at transition nodes.
+
+@<If node |q| is a transition point for |x| coordinates...@>=
+if odd(right_type(p))<>odd(right_type(q)) then
+ begin if odd(right_type(q)) then b:=x_coord(q)@+else b:=-x_coord(q);
+ if (abs(x_coord(q)-right_x(q))<655)or@|
+ (abs(x_coord(q)+left_x(q))<655) then
+ @<Compute before-and-after |x| values based on the current pen@>
+ else a:=b;
+ if abs(a)>max_allowed then
+ if a>0 then a:=max_allowed@+else a:=-max_allowed;
+ before_and_after(b,a,q);
+ end
+
+@ When we study the data representation for pens, we'll learn that the
+|x|~coordinate of the current pen's west edge is
+$$\hbox{|y_coord(link(cur_pen+seventh_octant))|},$$
+and that there are similar ways to address other important offsets.
+
+@d north_edge(#)==y_coord(link(#+fourth_octant))
+@d south_edge(#)==y_coord(link(#+first_octant))
+@d east_edge(#)==y_coord(link(#+second_octant))
+@d west_edge(#)==y_coord(link(#+seventh_octant))
+
+@<Compute before-and-after |x| values based on the current pen@>=
+begin if cur_pen=null_pen then pen_edge:=0
+else if cur_path_type=double_path_code then
+ pen_edge:=compromise(east_edge(cur_pen),west_edge(cur_pen))
+else if odd(right_type(q)) then pen_edge:=west_edge(cur_pen)
+else pen_edge:=east_edge(cur_pen);
+a:=good_val(b,pen_edge);
+end
+
+@ The monotone transformation computed here with fixed-point arithmetic is
+guaranteed to take consecutive |before| values $(b,b')$ into consecutive
+|after| values $(a,a')$, even in the presence of rounding errors,
+as long as $\vert b-b'\vert<2^{28}$.
+
+@<Transform the |x| coordinates@>=
+begin make_safe;
+repeat decr(cur_rounding_ptr);
+if (after[cur_rounding_ptr]<>before[cur_rounding_ptr])or@|
+ (after[cur_rounding_ptr+1]<>before[cur_rounding_ptr+1]) then
+ begin p:=node_to_round[cur_rounding_ptr];
+ if odd(right_type(p)) then
+ begin b:=before[cur_rounding_ptr]; a:=after[cur_rounding_ptr];
+ end
+ else begin b:=-before[cur_rounding_ptr]; a:=-after[cur_rounding_ptr];
+ end;
+ if before[cur_rounding_ptr]=before[cur_rounding_ptr+1] then
+ alpha:=fraction_one
+ else alpha:=make_fraction(after[cur_rounding_ptr+1]-after[cur_rounding_ptr],@|
+ before[cur_rounding_ptr+1]-before[cur_rounding_ptr]);
+ repeat x_coord(p):=take_fraction(alpha,x_coord(p)-b)+a;
+ right_x(p):=take_fraction(alpha,right_x(p)-b)+a;
+ p:=link(p); left_x(p):=take_fraction(alpha,left_x(p)-b)+a;
+ until p=node_to_round[cur_rounding_ptr+1];
+ end;
+until cur_rounding_ptr=0;
+end
+
+@ When |y| has been negated, the |octant| codes are |>negate_y|. Otherwise
+these routines are essentially identical to the routines for |x| coordinates
+that we have just seen.
+
+@<If node |q| is a transition point for |y| coordinates...@>=
+if (right_type(p)>negate_y)<>(right_type(q)>negate_y) then
+ begin if right_type(q)<=negate_y then b:=y_coord(q)@+else b:=-y_coord(q);
+ if (abs(y_coord(q)-right_y(q))<655)or@|
+ (abs(y_coord(q)+left_y(q))<655) then
+ @<Compute before-and-after |y| values based on the current pen@>
+ else a:=b;
+ if abs(a)>max_allowed then
+ if a>0 then a:=max_allowed@+else a:=-max_allowed;
+ before_and_after(b,a,q);
+ end
+
+@ @<Compute before-and-after |y| values based on the current pen@>=
+begin if cur_pen=null_pen then pen_edge:=0
+else if cur_path_type=double_path_code then
+ pen_edge:=compromise(north_edge(cur_pen),south_edge(cur_pen))
+else if right_type(q)<=negate_y then pen_edge:=south_edge(cur_pen)
+else pen_edge:=north_edge(cur_pen);
+a:=good_val(b,pen_edge);
+end
+
+@ @<Transform the |y| coordinates@>=
+begin make_safe;
+repeat decr(cur_rounding_ptr);
+if (after[cur_rounding_ptr]<>before[cur_rounding_ptr])or@|
+ (after[cur_rounding_ptr+1]<>before[cur_rounding_ptr+1]) then
+ begin p:=node_to_round[cur_rounding_ptr];
+ if right_type(p)<=negate_y then
+ begin b:=before[cur_rounding_ptr]; a:=after[cur_rounding_ptr];
+ end
+ else begin b:=-before[cur_rounding_ptr]; a:=-after[cur_rounding_ptr];
+ end;
+ if before[cur_rounding_ptr]=before[cur_rounding_ptr+1] then
+ alpha:=fraction_one
+ else alpha:=make_fraction(after[cur_rounding_ptr+1]-after[cur_rounding_ptr],@|
+ before[cur_rounding_ptr+1]-before[cur_rounding_ptr]);
+ repeat y_coord(p):=take_fraction(alpha,y_coord(p)-b)+a;
+ right_y(p):=take_fraction(alpha,right_y(p)-b)+a;
+ p:=link(p); left_y(p):=take_fraction(alpha,left_y(p)-b)+a;
+ until p=node_to_round[cur_rounding_ptr+1];
+ end;
+until cur_rounding_ptr=0;
+end
+
+@ Rounding at diagonal tangents takes place after the subdivision into
+octants is complete, hence after the coordinates have been skewed.
+The details are somewhat tricky, because we want to round to points
+whose skewed coordinates are halfway between integer multiples of
+the granularity. Furthermore, both coordinates change when they are
+rounded; this means we need a generalization of the |make_safe| routine,
+ensuring safety in both |x| and |y|.
+
+In spite of these extra complications, we can take comfort in the fact
+that the basic structure of the routine is the same as before.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure diag_round;
+var @!p,@!q,@!pp:pointer; {list manipulation registers}
+@!b,@!a,@!bb,@!aa,@!d,@!c,@!dd,@!cc:scaled; {before and after values}
+@!pen_edge:scaled; {offset that governs rounding}
+@!alpha,@!beta:fraction; {coefficients of linear transformation}
+@!next_a:scaled; {|after[k]| before it might have changed}
+@!all_safe:boolean; {does everything look OK so far?}
+@!k:0..max_wiggle; {runs through before-and-after values}
+@!first_x,@!first_y:scaled; {coordinates before rounding}
+begin p:=cur_spec; cur_rounding_ptr:=0;
+repeat q:=link(p);
+@<If node |q| is a transition point between octants,
+ compute and save its before-and-after coordinates@>;
+p:=q;
+until p=cur_spec;
+if cur_rounding_ptr>0 then @<Transform the skewed coordinates@>;
+end;
+
+@ We negate the skewed |x| coordinates in the before-and-after table when
+the octant code is greater than |switch_x_and_y|.
+
+@<If node |q| is a transition point between octants...@>=
+if right_type(p)<>right_type(q) then
+ begin if right_type(q)>switch_x_and_y then b:=-x_coord(q)
+ else b:=x_coord(q);
+ if abs(right_type(q)-right_type(p))=switch_x_and_y then
+ if (abs(x_coord(q)-right_x(q))<655)or(abs(x_coord(q)+left_x(q))<655) then
+ @<Compute a good coordinate at a diagonal transition@>
+ else a:=b
+ else a:=b;
+ before_and_after(b,a,q);
+ end
+
+@ In octants whose code number is even, $x$~has been
+negated; we want to round ambiguous cases downward instead of upward,
+so that the rounding will be consistent with octants whose code
+number is odd. This downward bias can be achieved by
+subtracting~1 from the first argument of |good_val|.
+
+@d diag_offset(#)==x_coord(knil(link(cur_pen+#)))
+
+@<Compute a good coordinate at a diagonal transition@>=
+begin if cur_pen=null_pen then pen_edge:=0
+else if cur_path_type=double_path_code then @<Compute a compromise |pen_edge|@>
+else if right_type(q)<=switch_x_and_y then pen_edge:=diag_offset(right_type(q))
+else pen_edge:=-diag_offset(right_type(q));
+if odd(right_type(q)) then a:=good_val(b,pen_edge+half(cur_gran))
+else a:=good_val(b-1,pen_edge+half(cur_gran));
+end
+
+@ (It seems a shame to compute these compromise offsets repeatedly. The
+author would have stored them directly in the pen data structure, if the
+granularity had been constant.)
+
+@<Compute a compromise...@>=
+case right_type(q) of
+first_octant,second_octant:pen_edge:=compromise(diag_offset(first_octant),@|
+ -diag_offset(fifth_octant));
+fifth_octant,sixth_octant:pen_edge:=-compromise(diag_offset(first_octant),@|
+ -diag_offset(fifth_octant));
+third_octant,fourth_octant:pen_edge:=compromise(diag_offset(fourth_octant),@|
+ -diag_offset(eighth_octant));
+seventh_octant,eighth_octant:pen_edge:=-compromise(diag_offset(fourth_octant),@|
+ -diag_offset(eighth_octant));
+end {there are no other cases}
+
+@ @<Transform the skewed coordinates@>=
+begin p:=node_to_round[0]; first_x:=x_coord(p); first_y:=y_coord(p);
+@<Make sure that all the diagonal roundings are safe@>;
+for k:=0 to cur_rounding_ptr-1 do
+ begin a:=after[k]; b:=before[k];
+ aa:=after[k+1]; bb:=before[k+1];
+ if (a<>b)or(aa<>bb) then
+ begin p:=node_to_round[k]; pp:=node_to_round[k+1];
+ @<Determine the before-and-after values of both coordinates@>;
+ if b=bb then alpha:=fraction_one
+ else alpha:=make_fraction(aa-a,bb-b);
+ if d=dd then beta:=fraction_one
+ else beta:=make_fraction(cc-c,dd-d);
+ repeat x_coord(p):=take_fraction(alpha,x_coord(p)-b)+a;
+ y_coord(p):=take_fraction(beta,y_coord(p)-d)+c;
+ right_x(p):=take_fraction(alpha,right_x(p)-b)+a;
+ right_y(p):=take_fraction(beta,right_y(p)-d)+c;
+ p:=link(p); left_x(p):=take_fraction(alpha,left_x(p)-b)+a;
+ left_y(p):=take_fraction(beta,left_y(p)-d)+c;
+ until p=pp;
+ end;
+ end;
+end
+
+@ In node |p|, the coordinates |(b,d)| will be rounded to |(a,c)|;
+in node |pp|, the coordinates |(bb,dd)| will be rounded to |(aa,cc)|.
+(We transform the values from node |pp| so that they agree with the
+conventions of node |p|.)
+
+If |aa<>bb|, we know that |abs(right_type(p)-right_type(pp))=switch_x_and_y|.
+
+@<Determine the before-and-after values of both coordinates@>=
+if aa=bb then
+ begin if pp=node_to_round[0] then
+ unskew(first_x,first_y,right_type(pp))
+ else unskew(x_coord(pp),y_coord(pp),right_type(pp));
+ skew(cur_x,cur_y,right_type(p));
+ bb:=cur_x; aa:=bb; dd:=cur_y; cc:=dd;
+ if right_type(p)>switch_x_and_y then
+ begin b:=-b; a:=-a;
+ end;
+ end
+else begin if right_type(p)>switch_x_and_y then
+ begin bb:=-bb; aa:=-aa; b:=-b; a:=-a;
+ end;
+ if pp=node_to_round[0] then dd:=first_y-bb@+else dd:=y_coord(pp)-bb;
+ if odd(aa-bb) then
+ if right_type(p)>switch_x_and_y then cc:=dd-half(aa-bb+1)
+ else cc:=dd-half(aa-bb-1)
+ else cc:=dd-half(aa-bb);
+ end;
+d:=y_coord(p);
+if odd(a-b) then
+ if right_type(p)>switch_x_and_y then c:=d-half(a-b-1)
+ else c:=d-half(a-b+1)
+else c:=d-half(a-b)
+
+@ @<Make sure that all the diagonal roundings are safe@>=
+before[cur_rounding_ptr]:=before[0]; {cf.~|make_safe|}
+node_to_round[cur_rounding_ptr]:=node_to_round[0];
+repeat after[cur_rounding_ptr]:=after[0]; all_safe:=true; next_a:=after[0];
+for k:=0 to cur_rounding_ptr-1 do
+ begin a:=next_a; b:=before[k]; next_a:=after[k+1];
+ aa:=next_a; bb:=before[k+1];
+ if (a<>b)or(aa<>bb) then
+ begin p:=node_to_round[k]; pp:=node_to_round[k+1];
+ @<Determine the before-and-after values of both coordinates@>;
+ if (aa<a)or(cc<c)or(aa-a>2*(bb-b))or(cc-c>2*(dd-d)) then
+ begin all_safe:=false; after[k]:=before[k];
+ if k=cur_rounding_ptr-1 then after[0]:=before[0]
+ else after[k+1]:=before[k+1];
+ end;
+ end;
+ end;
+until all_safe
+
+@ Here we get rid of ``dead'' cubics, i.e., polynomials that don't move at
+all when |t|~changes, since the subdivision process might have introduced
+such things. If the cycle reduces to a single point, however, we are left
+with a single dead cubic that will not be removed until later.
+
+@<Remove dead cubics@>=
+p:=cur_spec;
+repeat continue: q:=link(p);
+if p<>q then
+ begin if x_coord(p)=right_x(p) then
+ if y_coord(p)=right_y(p) then
+ if x_coord(p)=left_x(q) then
+ if y_coord(p)=left_y(q) then
+ begin unskew(x_coord(q),y_coord(q),right_type(q));
+ skew(cur_x,cur_y,right_type(p));
+ if x_coord(p)=cur_x then if y_coord(p)=cur_y then
+ begin remove_cubic(p); {remove the cubic following |p|}
+ if q<>cur_spec then goto continue;
+ cur_spec:=p; q:=p;
+ end;
+ end;
+ end;
+p:=q;
+until p=cur_spec;
+
+@ Finally we come to the last steps of |make_spec|, when boundary nodes
+are inserted between cubics that move in different octants. The main
+complication remaining arises from consecutive cubics whose octants
+are not adjacent; we should insert more than one octant boundary
+at such sharp turns, so that the envelope-forming routine will work.
+
+For this purpose, conversion tables between numeric and Gray codes for
+octants are desirable.
+
+@<Glob...@>=
+@!octant_number:array[first_octant..sixth_octant] of 1..8;
+@!octant_code:array[1..8] of first_octant..sixth_octant;
+
+@ @<Set init...@>=
+octant_code[1]:=first_octant;
+octant_code[2]:=second_octant;
+octant_code[3]:=third_octant;
+octant_code[4]:=fourth_octant;
+octant_code[5]:=fifth_octant;
+octant_code[6]:=sixth_octant;
+octant_code[7]:=seventh_octant;
+octant_code[8]:=eighth_octant;
+for k:=1 to 8 do octant_number[octant_code[k]]:=k;
+
+@ The main loop for boundary insertion deals with three consecutive
+nodes |p,q,r|.
+
+@<Insert octant boundaries and compute the turning number@>=
+turning_number:=0;
+p:=cur_spec; q:=link(p);
+repeat r:=link(q);
+if (right_type(p)<>right_type(q))or(q=r) then
+ @<Insert one or more octant boundary nodes just before~|q|@>;
+p:=q; q:=r;
+until p=cur_spec;
+
+@ The |new_boundary| subroutine comes in handy at this point. It inserts
+a new boundary node just after a given node |p|, using a given octant code
+to transform the new node's coordinates. The ``transition'' fields are
+not computed here.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure new_boundary(@!p:pointer;@!octant:small_number);
+var @!q,@!r:pointer; {for list manipulation}
+begin q:=link(p); {we assume that |right_type(q)<>endpoint|}
+r:=get_node(knot_node_size); link(r):=q; link(p):=r;
+left_type(r):=left_type(q); {but possibly |left_type(q)=endpoint|}
+left_x(r):=left_x(q); left_y(r):=left_y(q);
+right_type(r):=endpoint; left_type(q):=endpoint;
+right_octant(r):=octant; left_octant(q):=right_type(q);
+unskew(x_coord(q),y_coord(q),right_type(q));
+skew(cur_x,cur_y,octant); x_coord(r):=cur_x; y_coord(r):=cur_y;
+end;
+
+@ The case |q=r| occurs if and only if |p=q=r=cur_spec|, when we want to turn
+$360^\circ$ in eight steps and then remove a solitary dead cubic.
+The program below happens to work in that case, but the reader isn't
+expected to understand why.
+
+@<Insert one or more octant boundary nodes just before~|q|@>=
+begin new_boundary(p,right_type(p)); s:=link(p);
+o1:=octant_number[right_type(p)]; o2:=octant_number[right_type(q)];
+case o2-o1 of
+1,-7,7,-1: goto done;
+2,-6: clockwise:=false;
+3,-5,4,-4,5,-3: @<Decide whether or not to go clockwise@>;
+6,-2: clockwise:=true;
+0:clockwise:=rev_turns;
+end; {there are no other cases}
+@<Insert additional boundary nodes, then |goto done|@>;
+done: if q=r then
+ begin q:=link(q); r:=q; p:=s; link(s):=q; left_octant(q):=right_octant(q);
+ left_type(q):=endpoint; free_node(cur_spec,knot_node_size); cur_spec:=q;
+ end;
+@<Fix up the transition fields and adjust the turning number@>;
+end
+
+@ @<Other local variables for |make_spec|@>=
+@!o1,@!o2:small_number; {octant numbers}
+@!clockwise:boolean; {should we turn clockwise?}
+@!dx1,@!dy1,@!dx2,@!dy2:integer; {directions of travel at a cusp}
+@!dmax,@!del:integer; {temporary registers}
+
+@ A tricky question arises when a path jumps four octants. We want the
+direction of turning to be counterclockwise if the curve has changed
+direction by $180^\circ$, or by something so close to $180^\circ$ that
+the difference is probably due to rounding errors; otherwise we want to
+turn through an angle of less than $180^\circ$. This decision needs to
+be made even when a curve seems to have jumped only three octants, since
+a curve may approach direction $(-1,0)$ from the fourth octant, then
+it might leave from direction $(+1,0)$ into the first.
+
+The following code solves the problem by analyzing the incoming
+direction |(dx1,dy1)| and the outgoing direction |(dx2,dy2)|.
+
+@<Decide whether or not to go clockwise@>=
+begin @<Compute the incoming and outgoing directions@>;
+unskew(dx1,dy1,right_type(p)); del:=pyth_add(cur_x,cur_y);@/
+dx1:=make_fraction(cur_x,del); dy1:=make_fraction(cur_y,del);
+ {$\cos\theta_1$ and $\sin\theta_1$}
+unskew(dx2,dy2,right_type(q)); del:=pyth_add(cur_x,cur_y);@/
+dx2:=make_fraction(cur_x,del); dy2:=make_fraction(cur_y,del);
+ {$\cos\theta_2$ and $\sin\theta_2$}
+del:=take_fraction(dx1,dy2)-take_fraction(dx2,dy1); {$\sin(\theta_2-\theta_1)$}
+if del>4684844 then clockwise:=false
+else if del<-4684844 then clockwise:=true
+ {$2^{28}\cdot\sin 1^\circ\approx4684844.68$}
+else clockwise:=rev_turns;
+end
+
+@ Actually the turnarounds just computed will be clockwise,
+not counterclockwise, if
+the global variable |rev_turns| is |true|; it is usually |false|.
+
+@<Glob...@>=
+@!rev_turns:boolean; {should we make U-turns in the English manner?}
+
+@ @<Set init...@>=
+rev_turns:=false;
+
+@ @<Compute the incoming and outgoing directions@>=
+dx1:=x_coord(s)-left_x(s); dy1:=y_coord(s)-left_y(s);
+if dx1=0 then if dy1=0 then
+ begin dx1:=x_coord(s)-right_x(p); dy1:=y_coord(s)-right_y(p);
+ if dx1=0 then if dy1=0 then
+ begin dx1:=x_coord(s)-x_coord(p); dy1:=y_coord(s)-y_coord(p);
+ end; {and they {\sl can't} both be zero}
+ end;
+dmax:=abs(dx1);@+if abs(dy1)>dmax then dmax:=abs(dy1);
+while dmax<fraction_one do
+ begin double(dmax); double(dx1); double(dy1);
+ end;
+dx2:=right_x(q)-x_coord(q); dy2:=right_y(q)-y_coord(q);
+if dx2=0 then if dy2=0 then
+ begin dx2:=left_x(r)-x_coord(q); dy2:=left_y(r)-y_coord(q);
+ if dx2=0 then if dy2=0 then
+ begin if right_type(r)=endpoint then
+ begin cur_x:=x_coord(r); cur_y:=y_coord(r);
+ end
+ else begin unskew(x_coord(r),y_coord(r),right_type(r));
+ skew(cur_x,cur_y,right_type(q));
+ end;
+ dx2:=cur_x-x_coord(q); dy2:=cur_y-y_coord(q);
+ end; {and they {\sl can't} both be zero}
+ end;
+dmax:=abs(dx2);@+if abs(dy2)>dmax then dmax:=abs(dy2);
+while dmax<fraction_one do
+ begin double(dmax); double(dx2); double(dy2);
+ end
+
+@ @<Insert additional boundary nodes...@>=
+loop@+ begin if clockwise then
+ if o1=1 then o1:=8@+else decr(o1)
+ else if o1=8 then o1:=1@+else incr(o1);
+ if o1=o2 then goto done;
+ new_boundary(s,octant_code[o1]);
+ s:=link(s); left_octant(s):=right_octant(s);
+ end
+
+@ Now it remains to insert the redundant
+transition information into the |left_transition|
+and |right_transition| fields between adjacent octants, in the octant
+boundary nodes that have just been inserted between |link(p)| and~|q|.
+The turning number is easily computed from these transitions.
+
+@<Fix up the transition fields and adjust the turning number@>=
+p:=link(p);
+repeat s:=link(p);
+o1:=octant_number[right_octant(p)]; o2:=octant_number[left_octant(s)];
+if abs(o1-o2)=1 then
+ begin if o2<o1 then o2:=o1;
+ if odd(o2) then right_transition(p):=axis
+ else right_transition(p):=diagonal;
+ end
+else begin if o1=8 then incr(turning_number)@+else decr(turning_number);
+ right_transition(p):=axis;
+ end;
+left_transition(s):=right_transition(p);
+p:=s;
+until p=q
+
+@* \[22] Filling a contour.
+Given the low-level machinery for making moves and for transforming a
+cyclic path into a cycle spec, we're almost able to fill a digitized path.
+All we need is a high-level routine that walks through the cycle spec and
+controls the overall process.
+
+Our overall goal is to plot the integer points $\bigl(\round(x(t)),
+\round(y(t))\bigr)$ and to connect them by rook moves, assuming that
+$\round(x(t))$ and $\round(y(t))$ don't both jump simultaneously from
+one integer to another as $t$~varies; these rook moves will be the edge
+of the contour that will be filled. We have reduced this problem to the
+case of curves that travel in first octant directions, i.e., curves
+such that $0\L y'(t)\L x'(t)$, by transforming the original coordinates.
+
+\def\xtilde{{\tilde x}} \def\ytilde{{\tilde y}}
+Another transformation makes the problem still simpler. We shall say that
+we are working with {\sl biased coordinates\/} when $(x,y)$ has been
+replaced by $(\xtilde,\ytilde)=(x-y,y+{1\over2})$. When a curve travels
+in first octant directions, the corresponding curve with biased
+coordinates travels in first {\sl quadrant\/} directions; the latter
+condition is symmetric in $x$ and~$y$, so it has advantages for the
+design of algorithms. The |make_spec| routine gives us skewed coordinates
+$(x-y,y)$, hence we obtain biased coordinates by simply adding $1\over2$
+to the second component.
+
+The most important fact about biased coordinates is that we can determine the
+rounded unbiased path $\bigl(\round(x(t)),\round(y(t))\bigr)$ from the
+truncated biased path $\bigl(\lfloor\xtilde(t)\rfloor,\lfloor\ytilde(t)\rfloor
+\bigr)$ and information about the initial and final endpoints. If the
+unrounded and unbiased
+path begins at $(x_0,y_0)$ and ends at $(x_1,y_1)$, it's possible to
+prove (by induction on the length of the truncated biased path) that the
+rounded unbiased path is obtained by the following construction:
+
+\yskip\textindent{1)} Start at $\bigl(\round(x_0),\round(y_0)\bigr)$.
+
+\yskip\textindent{2)} If $(x_0+{1\over2})\bmod1\G(y_0+{1\over2})\bmod1$,
+move one step right.
+
+\yskip\textindent{3)} Whenever the path
+$\bigl(\lfloor\xtilde(t)\rfloor,\lfloor\ytilde(t)\rfloor\bigr)$
+takes an upward step (i.e., when
+$\lfloor\xtilde(t+\epsilon)\rfloor=\lfloor\xtilde(t)\rfloor$ and
+$\lfloor\ytilde(t+\epsilon)\rfloor=\lfloor\ytilde(t)\rfloor+1$),
+move one step up and then one step right.
+
+\yskip\textindent{4)} Whenever the path
+$\bigl(\lfloor\xtilde(t)\rfloor,\lfloor\ytilde(t)\rfloor\bigr)$
+takes a rightward step (i.e., when
+$\lfloor\xtilde(t+\epsilon)\rfloor=\lfloor\xtilde(t)\rfloor+1$ and
+$\lfloor\ytilde(t+\epsilon)\rfloor=\lfloor\ytilde(t)\rfloor$),
+move one step right.
+
+\yskip\textindent{5)} Finally, if
+$(x_1+{1\over2})\bmod1\G(y_1+{1\over2})\bmod1$, move one step left (thereby
+cancelling the previous move, which was one step right). You will now be
+at the point $\bigl(\round(x_1),\round(y_1)\bigr)$.
+
+@ In order to validate the assumption that $\round(x(t))$ and $\round(y(t))$
+don't both jump simultaneously, we shall consider that a coordinate pair
+$(x,y)$ actually represents $(x+\epsilon,y+\epsilon\delta)$, where
+$\epsilon$ and $\delta$ are extremely small positive numbers---so small
+that their precise values never matter. This convention makes rounding
+unambiguous, since there is always a unique integer point nearest to any
+given scaled numbers~$(x,y)$.
+
+When coordinates are transformed so that \MF\ needs to work only in ``first
+octant'' directions, the transformations involve negating~$x$, negating~$y$,
+and/or interchanging $x$ with~$y$. Corresponding adjustments to the
+rounding conventions must be made so that consistent values will be
+obtained. For example, suppose that we're working with coordinates that
+have been transformed so that a third-octant curve travels in first-octant
+directions. The skewed coordinates $(x,y)$ in our data structure represent
+unskewed coordinates $(-y,x+y)$, which are actually $(-y+\epsilon,
+x+y+\epsilon\delta)$. We should therefore round as if our skewed coordinates
+were $(x+\epsilon+\epsilon\delta,y-\epsilon)$ instead of $(x,y)$. The following
+table shows how the skewed coordinates should be perturbed when rounding
+decisions are made:
+$$\vcenter{\halign{#\hfil&&\quad$#$\hfil&\hskip4em#\hfil\cr
+|first_octant|&(x+\epsilon-\epsilon\delta,y+\epsilon\delta)&
+ |fifth_octant|&(x-\epsilon+\epsilon\delta,y-\epsilon\delta)\cr
+|second_octant|&(x-\epsilon+\epsilon\delta,y+\epsilon)&
+ |sixth_octant|&(x+\epsilon-\epsilon\delta,y-\epsilon)\cr
+|third_octant|&(x+\epsilon+\epsilon\delta,y-\epsilon)&
+ |seventh_octant|&(x-\epsilon-\epsilon\delta,y+\epsilon)\cr
+|fourth_octant|&(x-\epsilon-\epsilon\delta,y+\epsilon\delta)&
+ |eighth_octant|&(x+\epsilon+\epsilon\delta,y-\epsilon\delta)\cr}}$$
+
+Four small arrays are set up so that the rounding operations will be
+fairly easy in any given octant.
+
+@<Glob...@>=
+@!y_corr,@!xy_corr,@!z_corr:array[first_octant..sixth_octant] of 0..1;
+@!x_corr:array[first_octant..sixth_octant] of -1..1;
+
+@ Here |xy_corr| is 1 if and only if the $x$ component of a skewed coordinate
+is to be decreased by an infinitesimal amount; |y_corr| is similar, but for
+the $y$ components. The other tables are set up so that the condition
+$$(x+y+|half_unit|)\bmod|unity|\G(y+|half_unit|)\bmod|unity|$$
+is properly perturbed to the condition
+$$(x+y+|half_unit|-|x_corr|-|y_corr|)\bmod|unity|\G
+ (y+|half_unit|-|y_corr|)\bmod|unity|+|z_corr|.$$
+
+@<Set init...@>=
+x_corr[first_octant]:=0; y_corr[first_octant]:=0;
+xy_corr[first_octant]:=0;@/
+x_corr[second_octant]:=0; y_corr[second_octant]:=0;
+xy_corr[second_octant]:=1;@/
+x_corr[third_octant]:=-1; y_corr[third_octant]:=1;
+xy_corr[third_octant]:=0;@/
+x_corr[fourth_octant]:=1; y_corr[fourth_octant]:=0;
+xy_corr[fourth_octant]:=1;@/
+x_corr[fifth_octant]:=0; y_corr[fifth_octant]:=1;
+xy_corr[fifth_octant]:=1;@/
+x_corr[sixth_octant]:=0; y_corr[sixth_octant]:=1;
+xy_corr[sixth_octant]:=0;@/
+x_corr[seventh_octant]:=1; y_corr[seventh_octant]:=0;
+xy_corr[seventh_octant]:=1;@/
+x_corr[eighth_octant]:=-1; y_corr[eighth_octant]:=1;
+xy_corr[eighth_octant]:=0;@/
+for k:=1 to 8 do z_corr[k]:=xy_corr[k]-x_corr[k];
+
+@ Here's a procedure that handles the details of rounding at the
+endpoints: Given skewed coordinates |(x,y)|, it sets |(m1,n1)|
+to the corresponding rounded lattice points, taking the current
+|octant| into account. Global variable |d1| is also set to 1 if
+$(x+y+{1\over2})\bmod1\G(y+{1\over2})\bmod1$.
+
+@p procedure end_round(@!x,@!y:scaled);
+begin y:=y+half_unit-y_corr[octant];
+x:=x+y-x_corr[octant];
+m1:=floor_unscaled(x); n1:=floor_unscaled(y);
+if x-unity*m1>=y-unity*n1+z_corr[octant] then d1:=1@+else d1:=0;
+end;
+
+@ The outputs |(m1,n1,d1)| of |end_round| will sometimes be moved
+to |(m0,n0,d0)|.
+
+@<Glob...@>=
+@!m0,@!n0,@!m1,@!n1:integer; {lattice point coordinates}
+@!d0,@!d1:0..1; {displacement corrections}
+
+@ We're ready now to fill the pixels enclosed by a given cycle spec~|h|;
+the knot list that represents the cycle is destroyed in the process.
+The edge structure that gets all the resulting data is |cur_edges|,
+and the edges are weighted by |cur_wt|.
+
+@p procedure fill_spec(@!h:pointer);
+var @!p,@!q,@!r,@!s:pointer; {for list traversal}
+begin if internal[tracing_edges]>0 then begin_edge_tracing;
+p:=h; {we assume that |left_type(h)=endpoint|}
+repeat octant:=left_octant(p);
+@<Set variable |q| to the node at the end of the current octant@>;
+if q<>p then
+ begin @<Determine the starting and ending
+ lattice points |(m0,n0)| and |(m1,n1)|@>;
+ @<Make the moves for the current octant@>;
+ move_to_edges(m0,n0,m1,n1);
+ end;
+p:=link(q);
+until p=h;
+toss_knot_list(h);
+if internal[tracing_edges]>0 then end_edge_tracing;
+end;
+
+@ @<Set variable |q| to the node at the end of the current octant@>=
+q:=p;
+while right_type(q)<>endpoint do q:=link(q)
+
+@ @<Determine the starting and ending lattice points |(m0,n0)| and |(m1,n1)|@>=
+end_round(x_coord(p),y_coord(p)); m0:=m1; n0:=n1; d0:=d1;@/
+end_round(x_coord(q),y_coord(q))
+
+@ Finally we perform the five-step process that was explained at
+the very beginning of this part of the program.
+
+@<Make the moves for the current octant@>=
+if n1-n0>=move_size then overflow("move table size",move_size);
+@:METAFONT capacity exceeded move table size}{\quad move table size@>
+move[0]:=d0; move_ptr:=0; r:=p;
+repeat s:=link(r);@/
+make_moves(x_coord(r),right_x(r),left_x(s),x_coord(s),@|
+ y_coord(r)+half_unit,right_y(r)+half_unit,left_y(s)+half_unit,
+ y_coord(s)+half_unit,@| xy_corr[octant],y_corr[octant]);
+r:=s;
+until r=q;
+move[move_ptr]:=move[move_ptr]-d1;
+if internal[smoothing]>0 then smooth_moves(0,move_ptr)
+
+@* \[23] Polygonal pens.
+The next few parts of the program deal with the additional complications
+associated with ``envelopes,'' leading up to an algorithm that fills a
+contour with respect to a pen whose boundary is a convex polygon. The
+mathematics underlying this algorithm is based on simple aspects of the
+theory of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge
+Stolfi [``A kinetic framework for computational geometry,''
+{\sl Proc.\ IEEE Symp.\ Foundations of Computer Science\/ \bf24} (1983),
+100--111].
+@^Guibas, Leonidas Ioannis@>
+@^Ramshaw, Lyle Harold@>
+@^Stolfi, Jorge@>
+
+If the vertices of the polygon are $w_0$, $w_1$, \dots, $w_{n-1}$, $w_n=w_0$,
+in counterclockwise order, the convexity condition requires that ``left
+turns'' are made at each vertex when a person proceeds from $w_0$ to
+$w_1$ to $\cdots$ to~$w_n$. The envelope is obtained if we offset a given
+curve $z(t)$ by $w_k$ when that curve is traveling in a direction
+$z'(t)$ lying between the directions $w_k-w_{k-1}$ and $w\k-w_k$.
+At times~$t$ when the curve direction $z'(t)$ increases past
+$w\k-w_k$, we temporarily stop plotting the offset curve and we insert
+a straight line from $z(t)+w_k$ to $z(t)+w\k$; notice that this straight
+line is tangent to the offset curve. Similarly, when the curve direction
+decreases past $w_k-w_{k-1}$, we stop plotting and insert a straight
+line from $z(t)+w_k$ to $z(t)+w_{k-1}$; the latter line is actually a
+``retrograde'' step, which won't be part of the final envelope under
+\MF's assumptions. The result of this construction is a continuous path
+that consists of alternating curves and straight line segments. The
+segments are usually so short, in practice, that they blend with the
+curves; after all, it's possible to represent any digitized path as
+a sequence of digitized straight lines.
+
+The nicest feature of this approach to envelopes is that it blends
+perfectly with the octant subdivision process we have already developed.
+The envelope travels in the same direction as the curve itself, as we
+plot it, and we need merely be careful what offset is being added.
+Retrograde motion presents a problem, but we will see that there is
+a decent way to handle it.
+
+@ We shall represent pens by maintaining eight lists of offsets,
+one for each octant direction. The offsets at the boundary points
+where a curve turns into a new octant will appear in the lists for
+both octants. This means that we can restrict consideration to
+segments of the original polygon whose directions aim in the first
+octant, as we have done in the simpler case when envelopes were not
+required.
+
+An example should help to clarify this situation: Consider the
+quadrilateral whose vertices are $w_0=(0,-1)$, $w_1=(3,-1)$,
+$w_2=(6,1)$, and $w_3=(1,2)$. A curve that travels in the first octant
+will be offset by $w_1$ or $w_2$, unless its slope drops to zero
+en route to the eighth octant; in the latter case we should switch to $w_0$ as
+we cross the octant boundary. Our list for the first octant will
+contain the three offsets $w_0$, $w_1$,~$w_2$. By convention we will
+duplicate a boundary offset if the angle between octants doesn't
+explicitly appear; in this case there is no explicit line of slope~1
+at the end of the list, so the full list is
+$$w_0\;w_1\;w_2\;w_2\;=\;(0,-1)\;(3,-1)\;(6,1)\;(6,1).$$
+With skewed coordinates $(u-v,v)$ instead of $(u,v)$ we obtain the list
+$$w_0\;w_1\;w_2\;w_2\;\mapsto\;(1,-1)\;(4,-1)\;(5,1)\;(5,1),$$
+which is what actually appears in the data structure. In the second
+octant there's only one offset; we list it twice (with coordinates
+interchanged, so as to make the second octant look like the first),
+and skew those coordinates, obtaining
+$$\tabskip\centering
+\halign to\hsize{$\hfil#\;\mapsto\;{}$\tabskip=0pt&
+ $#\hfil$&\quad in the #\hfil\tabskip\centering\cr
+w_2\;w_2&(-5,6)\;(-5,6)\cr
+\noalign{\vskip\belowdisplayskip
+\vbox{\noindent\strut as the list of transformed and skewed offsets to use
+when curves travel in the second octant. Similarly, we will have\strut}
+\vskip\abovedisplayskip}
+w_2\;w_2&(7,-6)\;(7,-6)&third;\cr
+w_2\;w_2\;w_3\;w_3&(-7,1)\;(-7,1)\;(-3,2)\;(-3,2)&fourth;\cr
+w_3\;w_3&(1,-2)\;(1,-2)&fifth;\cr
+w_3\;w_3\;w_0\;w_0&(-1,1)\;(-1,1)\;(1,0)\;(1,0)&sixth;\cr
+w_0\;w_0&(1,0)\;(1,0)&seventh;\cr
+w_0\;w_0&(-1,1)\;(-1,1)&eighth.\cr}$$
+Notice that $w_1$ is considered here to be internal to the first octant;
+it's not part of the eighth. We could equally well have taken $w_0$ out
+of the first octant list and put it into the eighth; then the first octant
+list would have been
+$$w_1\;w_1\;w_2\;w_2\;\mapsto\;(4,-1)\;(4,-1)\;(5,1)\;(5,1)$$
+and the eighth octant list would have been
+$$w_0\;w_0\;w_1\;\mapsto\;(-1,1)\;(-1,1)\;(2,1).$$
+
+Actually, there's one more complication: The order of offsets is reversed
+in even-numbered octants, because the transformation of coordinates has
+reversed counterclockwise and clockwise orientations in those octants.
+The offsets in the fourth octant, for example, are really $w_3$, $w_3$,
+$w_2$,~$w_2$, not $w_2$, $w_2$, $w_3$,~$w_3$.
+
+@ In general, the list of offsets for an octant will have the form
+$$w_0\;\;w_1\;\;\ldots\;\;w_n\;\;w_{n+1}$$
+(if we renumber the subscripts in each list), where $w_0$ and $w_{n+1}$
+are offsets common to the neighboring lists. We'll often have $w_0=w_1$
+and/or $w_n=w_{n+1}$, but the other $w$'s will be distinct. Curves
+that travel between slope~0 and direction $w_2-w_1$ will use offset~$w_1$;
+curves that travel between directions $w_k-w_{k-1}$ and $w\k-w_k$ will
+use offset~$w_k$, for $1<k<n$; curves between direction $w_n-w_{n-1}$
+and slope~1 (actually slope~$\infty$ after skewing) will use offset~$w_n$.
+In even-numbered octants, the directions are actually $w_k-w\k$ instead
+of $w\k-w_k$, because the offsets have been listed in reverse order.
+
+Each offset $w_k$ is represented by skewed coordinates $(u_k-v_k,v_k)$,
+where $(u_k,v_k)$ is the representation of $w_k$ after it has been rotated
+into a first-octant disguise.
+
+@ The top-level data structure of a pen polygon is a 10-word node containing
+a reference count followed by pointers to the eight offset lists, followed
+by an indication of the pen's range of values.
+@^reference counts@>
+
+If |p|~points to such a node, and if the
+offset list for, say, the fourth octant has entries $w_0$, $w_1$, \dots,
+$w_n$,~$w_{n+1}$, then |info(p+fourth_octant)| will equal~$n$, and
+|link(p+fourth_octant)| will point to the offset node containing~$w_0$.
+Memory location |p+fourth_octant| is said to be the {\sl header\/} of
+the pen-offset list for the fourth octant. Since this is an even-numbered
+octant, $w_0$ is the offset that goes with the fifth octant, and
+$w_{n+1}$ goes with the third.
+
+The elements of the offset list themselves are doubly linked 3-word nodes,
+containing coordinates in their |x_coord| and |y_coord| fields.
+The two link fields are called |link| and |knil|; if |w|~points to
+the node for~$w_k$, then |link(w)| and |knil(w)| point respectively
+to the nodes for $w\k$ and~$w_{k-1}$. If |h| is the list header,
+|link(h)| points to the node for~$w_0$ and |knil(link(h))| to the
+node for~$w_{n+1}$.
+
+The tenth word of a pen header node contains the maximum absolute value of
+an $x$ or $y$ coordinate among all of the unskewed pen offsets.
+
+The |link| field of a pen header node should be |null| if and only if
+the pen is a single point.
+
+@d pen_node_size=10
+@d coord_node_size=3
+@d max_offset(#)==mem[#+9].sc
+
+@ The |print_pen| subroutine illustrates these conventions by
+reconstructing the vertices of a polygon from \MF's complicated
+internal offset representation.
+
+@<Declare subroutines for printing expressions@>=
+procedure print_pen(@!p:pointer;@!s:str_number;@!nuline:boolean);
+var @!nothing_printed:boolean; {has there been any action yet?}
+@!k:1..8; {octant number}
+@!h:pointer; {offset list head}
+@!m,@!n:integer; {offset indices}
+@!w,@!ww:pointer; {pointers that traverse the offset list}
+begin print_diagnostic("Pen polygon",s,nuline);
+nothing_printed:=true; print_ln;
+for k:=1 to 8 do
+ begin octant:=octant_code[k]; h:=p+octant; n:=info(h); w:=link(h);
+ if not odd(k) then w:=knil(w); {in even octants, start at $w_{n+1}$}
+ for m:=1 to n+1 do
+ begin if odd(k) then ww:=link(w)@+else ww:=knil(w);
+ if (x_coord(ww)<>x_coord(w))or(y_coord(ww)<>y_coord(w)) then
+ @<Print the unskewed and unrotated coordinates of node |ww|@>;
+ w:=ww;
+ end;
+ end;
+if nothing_printed then
+ begin w:=link(p+first_octant); print_two(x_coord(w)+y_coord(w),y_coord(w));
+ end;
+print_nl(" .. cycle"); end_diagnostic(true);
+end;
+
+@ @<Print the unskewed and unrotated coordinates of node |ww|@>=
+begin if nothing_printed then nothing_printed:=false
+else print_nl(" .. ");
+print_two_true(x_coord(ww),y_coord(ww));
+end
+
+@ A null pen polygon, which has just one vertex $(0,0)$, is
+predeclared for error recovery. It doesn't need a proper
+reference count, because the |toss_pen| procedure below
+will never delete it from memory.
+@^reference counts@>
+
+@<Initialize table entries...@>=
+ref_count(null_pen):=null; link(null_pen):=null;@/
+info(null_pen+1):=1; link(null_pen+1):=null_coords;
+for k:=null_pen+2 to null_pen+8 do mem[k]:=mem[null_pen+1];
+max_offset(null_pen):=0;@/
+link(null_coords):=null_coords;
+knil(null_coords):=null_coords;@/
+x_coord(null_coords):=0;
+y_coord(null_coords):=0;
+
+@ Here's a trivial subroutine that inserts a copy of an offset
+on the |link| side of its clone in the doubly linked list.
+
+@p procedure dup_offset(@!w:pointer);
+var @!r:pointer; {the new node}
+begin r:=get_node(coord_node_size);
+x_coord(r):=x_coord(w);
+y_coord(r):=y_coord(w);
+link(r):=link(w); knil(link(w)):=r;
+knil(r):=w; link(w):=r;
+end;
+
+@ The following algorithm is somewhat more interesting: It converts a
+knot list for a cyclic path into a pen polygon, ignoring everything
+but the |x_coord|, |y_coord|, and |link| fields. If the given path
+vertices do not define a convex polygon, an error message is issued
+and the null pen is returned.
+
+@p function make_pen(@!h:pointer):pointer;
+label done,done1,not_found,found;
+var @!o,@!oo,@!k:small_number; {octant numbers---old, new, and current}
+@!p:pointer; {top-level node for the new pen}
+@!q,@!r,@!s,@!w,@!hh:pointer; {for list manipulation}
+@!n:integer; {offset counter}
+@!dx,@!dy:scaled; {polygon direction}
+@!mc:scaled; {the largest coordinate}
+begin @<Stamp all nodes with an octant code, compute the maximum offset,
+ and set |hh| to the node that begins the first octant;
+ |goto not_found| if there's a problem@>;
+if mc>=fraction_one-half_unit then goto not_found;
+p:=get_node(pen_node_size); q:=hh; max_offset(p):=mc; ref_count(p):=null;
+if link(q)<>q then link(p):=null+1;
+for k:=1 to 8 do @<Construct the offset list for the |k|th octant@>;
+goto found;
+not_found:p:=null_pen; @<Complain about a bad pen path@>;
+found: if internal[tracing_pens]>0 then print_pen(p," (newly created)",true);
+make_pen:=p;
+end;
+
+@ @<Complain about a bad pen path@>=
+if mc>=fraction_one-half_unit then
+ begin print_err("Pen too large");
+@.Pen too large@>
+ help2("The cycle you specified has a coordinate of 4095.5 or more.")@/
+ ("So I've replaced it by the trivial path `(0,0)..cycle'.");@/
+ end
+else begin print_err("Pen cycle must be convex");
+@.Pen cycle must be convex@>
+ help3("The cycle you specified either has consecutive equal points")@/
+ ("or turns right or turns through more than 360 degrees.")@/
+ ("So I've replaced it by the trivial path `(0,0)..cycle'.");@/
+ end;
+put_get_error
+
+@ There should be exactly one node whose octant number is less than its
+predecessor in the cycle; that is node~|hh|.
+
+The loop here will terminate in all cases, but the proof is somewhat tricky:
+If there are at least two distinct $y$~coordinates in the cycle, we will have
+|o>4| and |o<=4| at different points of the cycle. Otherwise there are
+at least two distinct $x$~coordinates, and we will have |o>2| somewhere,
+|o<=2| somewhere.
+
+@<Stamp all nodes...@>=
+q:=h; r:=link(q); mc:=abs(x_coord(h));
+if q=r then
+ begin hh:=h; right_type(h):=0; {this trick is explained below}
+ if mc<abs(y_coord(h)) then mc:=abs(y_coord(h));
+ end
+else begin o:=0; hh:=null;
+ loop@+ begin s:=link(r);
+ if mc<abs(x_coord(r)) then mc:=abs(x_coord(r));
+ if mc<abs(y_coord(r)) then mc:=abs(y_coord(r));
+ dx:=x_coord(r)-x_coord(q); dy:=y_coord(r)-y_coord(q);
+ if dx=0 then if dy=0 then goto not_found; {double point}
+ if ab_vs_cd(dx,y_coord(s)-y_coord(r),dy,x_coord(s)-x_coord(r))<0 then
+ goto not_found; {right turn}
+ @<Determine the octant code for direction |(dx,dy)|@>;
+ right_type(q):=octant; oo:=octant_number[octant];
+ if o>oo then
+ begin if hh<>null then goto not_found; {$>360^\circ$}
+ hh:=q;
+ end;
+ o:=oo;
+ if (q=h)and(hh<>null) then goto done;
+ q:=r; r:=s;
+ end;
+ done:end
+
+
+@ We want the octant for |(-dx,-dy)| to be
+exactly opposite the octant for |(dx,dy)|.
+
+@<Determine the octant code for direction |(dx,dy)|@>=
+if dx>0 then octant:=first_octant
+else if dx=0 then
+ if dy>0 then octant:=first_octant@+else octant:=first_octant+negate_x
+else begin negate(dx); octant:=first_octant+negate_x;
+ end;
+if dy<0 then
+ begin negate(dy); octant:=octant+negate_y;
+ end
+else if dy=0 then
+ if octant>first_octant then octant:=first_octant+negate_x+negate_y;
+if dx<dy then octant:=octant+switch_x_and_y
+
+@ Now |q| points to the node that the present octant shares with the previous
+octant, and |right_type(q)| is the octant code during which |q|~should advance.
+We have set |right_type(q)=0| in the special case that |q| should never advance
+(because the pen is degenerate).
+
+The number of offsets |n| must be smaller than |max_quarterword|, because
+the |fill_envelope| routine stores |n+1| in the |right_type| field
+of a knot node.
+
+@<Construct the offset list...@>=
+begin octant:=octant_code[k]; n:=0; h:=p+octant;
+loop@+ begin r:=get_node(coord_node_size);
+ skew(x_coord(q),y_coord(q),octant); x_coord(r):=cur_x; y_coord(r):=cur_y;
+ if n=0 then link(h):=r
+ else @<Link node |r| to the previous node@>;
+ w:=r;
+ if right_type(q)<>octant then goto done1;
+ q:=link(q); incr(n);
+ end;
+done1: @<Finish linking the offset nodes, and duplicate the
+ borderline offset nodes if necessary@>;
+if n>=max_quarterword then overflow("pen polygon size",max_quarterword);
+@:METAFONT capacity exceeded pen polygon size}{\quad pen polygon size@>
+info(h):=n;
+end
+
+@ Now |w| points to the node that was inserted most recently, and
+|k| is the current octant number.
+
+@<Link node |r| to the previous node@>=
+if odd(k) then
+ begin link(w):=r; knil(r):=w;
+ end
+else begin knil(w):=r; link(r):=w;
+ end
+
+@ We have inserted |n+1| nodes; it remains to duplicate the nodes at the
+ends, if slopes 0 and~$\infty$ aren't already represented. At the end of
+this section the total number of offset nodes should be |n+2|
+(since we call them $w_0$, $w_1$, \dots,~$w_{n+1}$).
+
+@<Finish linking the offset nodes, and duplicate...@>=
+r:=link(h);
+if odd(k) then
+ begin link(w):=r; knil(r):=w;
+ end
+else begin knil(w):=r; link(r):=w; link(h):=w; r:=w;
+ end;
+if (y_coord(r)<>y_coord(link(r)))or(n=0) then
+ begin dup_offset(r); incr(n);
+ end;
+r:=knil(r);
+if x_coord(r)<>x_coord(knil(r)) then dup_offset(r)
+else decr(n)
+
+@ Conversely, |make_path| goes back from a pen to a cyclic path that
+might have generated it. The structure of this subroutine is essentially
+the same as |print_pen|.
+
+@p @t\4@>@<Declare the function called |trivial_knot|@>@;
+function make_path(@!pen_head:pointer):pointer;
+var @!p:pointer; {the most recently copied knot}
+@!k:1..8; {octant number}
+@!h:pointer; {offset list head}
+@!m,@!n:integer; {offset indices}
+@!w,@!ww:pointer; {pointers that traverse the offset list}
+begin p:=temp_head;
+for k:=1 to 8 do
+ begin octant:=octant_code[k]; h:=pen_head+octant; n:=info(h); w:=link(h);
+ if not odd(k) then w:=knil(w); {in even octants, start at $w_{n+1}$}
+ for m:=1 to n+1 do
+ begin if odd(k) then ww:=link(w)@+else ww:=knil(w);
+ if (x_coord(ww)<>x_coord(w))or(y_coord(ww)<>y_coord(w)) then
+ @<Copy the unskewed and unrotated coordinates of node |ww|@>;
+ w:=ww;
+ end;
+ end;
+if p=temp_head then
+ begin w:=link(pen_head+first_octant);
+ p:=trivial_knot(x_coord(w)+y_coord(w),y_coord(w)); link(temp_head):=p;
+ end;
+link(p):=link(temp_head); make_path:=link(temp_head);
+end;
+
+@ @<Copy the unskewed and unrotated coordinates of node |ww|@>=
+begin unskew(x_coord(ww),y_coord(ww),octant);
+link(p):=trivial_knot(cur_x,cur_y); p:=link(p);
+end
+
+@ @<Declare the function called |trivial_knot|@>=
+function trivial_knot(@!x,@!y:scaled):pointer;
+var @!p:pointer; {a new knot for explicit coordinates |x| and |y|}
+begin p:=get_node(knot_node_size);
+left_type(p):=explicit; right_type(p):=explicit;@/
+x_coord(p):=x; left_x(p):=x; right_x(p):=x;@/
+y_coord(p):=y; left_y(p):=y; right_y(p):=y;@/
+trivial_knot:=p;
+end;
+
+@ That which can be created can be destroyed.
+
+@d add_pen_ref(#)==incr(ref_count(#))
+@d delete_pen_ref(#)==if ref_count(#)=null then toss_pen(#)
+ else decr(ref_count(#))
+
+@<Declare the recycling subroutines@>=
+procedure toss_pen(@!p:pointer);
+var @!k:1..8; {relative header locations}
+@!w,@!ww:pointer; {pointers to offset nodes}
+begin if p<>null_pen then
+ begin for k:=1 to 8 do
+ begin w:=link(p+k);
+ repeat ww:=link(w); free_node(w,coord_node_size); w:=ww;
+ until w=link(p+k);
+ end;
+ free_node(p,pen_node_size);
+ end;
+end;
+
+@ The |find_offset| procedure sets |(cur_x,cur_y)| to the offset associated
+with a given direction~|(x,y)| and a given pen~|p|. If |x=y=0|, the
+result is |(0,0)|. If two different offsets apply, one of them is
+chosen arbitrarily.
+
+@p procedure find_offset(@!x,@!y:scaled; @!p:pointer);
+label done,exit;
+var @!octant:first_octant..sixth_octant; {octant code for |(x,y)|}
+@!s:-1..+1; {sign of the octant}
+@!n:integer; {number of offsets remaining}
+@!h,@!w,@!ww:pointer; {list traversal registers}
+begin @<Compute the octant code; skew and rotate the coordinates |(x,y)|@>;
+if odd(octant_number[octant]) then s:=-1@+else s:=+1;
+h:=p+octant; w:=link(link(h)); ww:=link(w); n:=info(h);
+while n>1 do
+ begin if ab_vs_cd(x,y_coord(ww)-y_coord(w),@|
+ y,x_coord(ww)-x_coord(w))<>s then goto done;
+ w:=ww; ww:=link(w); decr(n);
+ end;
+done:unskew(x_coord(w),y_coord(w),octant);
+exit:end;
+
+@ @<Compute the octant code; skew and rotate the coordinates |(x,y)|@>=
+if x>0 then octant:=first_octant
+else if x=0 then
+ if y<=0 then
+ if y=0 then
+ begin cur_x:=0; cur_y:=0; return;
+ end
+ else octant:=first_octant+negate_x
+ else octant:=first_octant
+else begin x:=-x;
+ if y=0 then octant:=first_octant+negate_x+negate_y
+ else octant:=first_octant+negate_x;
+ end;
+if y<0 then
+ begin octant:=octant+negate_y; y:=-y;
+ end;
+if x>=y then x:=x-y
+else begin octant:=octant+switch_x_and_y; x:=y-x; y:=y-x;
+ end
+
+@* \[24] Filling an envelope.
+We are about to reach the culmination of \MF's digital plotting routines:
+Almost all of the previous algorithms will be brought to bear on \MF's
+most difficult task, which is to fill the envelope of a given cyclic path
+with respect to a given pen polygon.
+
+But we still must complete some of the preparatory work before taking such
+a big plunge.
+
+@ Given a pointer |c| to a nonempty list of cubics,
+and a pointer~|h| to the header information of a pen polygon segment,
+the |offset_prep| routine changes the list into cubics that are
+associated with particular pen offsets. Namely, the cubic between |p|
+and~|q| should be associated with the |k|th offset when |right_type(p)=k|.
+
+List |c| is actually part of a cycle spec, so it terminates at the
+first node whose |right_type| is |endpoint|. The cubics all have
+monotone-nondecreasing $x(t)$ and $y(t)$.
+
+@p @t\4@>@<Declare subroutines needed by |offset_prep|@>@;
+procedure offset_prep(@!c,@!h:pointer);
+label done,not_found;
+var @!n:halfword; {the number of pen offsets}
+@!p,@!q,@!r,@!lh,@!ww:pointer; {for list manipulation}
+@!k:halfword; {the current offset index}
+@!w:pointer; {a pointer to offset $w_k$}
+@<Other local variables for |offset_prep|@>@;
+begin p:=c; n:=info(h); lh:=link(h); {now |lh| points to $w_0$}
+while right_type(p)<>endpoint do
+ begin q:=link(p);
+ @<Split the cubic between |p| and |q|, if necessary, into cubics
+ associated with single offsets, after which |q| should
+ point to the end of the final such cubic@>;
+ @<Advance |p| to node |q|, removing any ``dead'' cubics that
+ might have been introduced by the splitting process@>;
+ end;
+end;
+
+@ @<Advance |p| to node |q|, removing any ``dead'' cubics...@>=
+repeat r:=link(p);
+if x_coord(p)=right_x(p) then if y_coord(p)=right_y(p) then
+ if x_coord(p)=left_x(r) then if y_coord(p)=left_y(r) then
+ if x_coord(p)=x_coord(r) then if y_coord(p)=y_coord(r) then
+ begin remove_cubic(p);
+ if r=q then q:=p;
+ r:=p;
+ end;
+p:=r;
+until p=q
+
+@ The splitting process uses a subroutine like |split_cubic|, but
+(for ``bulletproof'' operation) we check to make sure that the
+resulting (skewed) coordinates satisfy $\Delta x\G0$ and $\Delta y\G0$
+after splitting; |make_spec| has made sure that these relations hold
+before splitting. (This precaution is surely unnecessary, now that
+|make_spec| is so much more careful than it used to be. But who
+wants to take a chance? Maybe the hardware will fail or something.)
+
+@<Declare subroutines needed by |offset_prep|@>=
+procedure split_for_offset(@!p:pointer;@!t:fraction);
+var @!q:pointer; {the successor of |p|}
+@!r:pointer; {the new node}
+begin q:=link(p); split_cubic(p,t,x_coord(q),y_coord(q)); r:=link(p);
+if y_coord(r)<y_coord(p) then y_coord(r):=y_coord(p)
+else if y_coord(r)>y_coord(q) then y_coord(r):=y_coord(q);
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p)
+else if x_coord(r)>x_coord(q) then x_coord(r):=x_coord(q);
+end;
+
+@ If the pen polygon has |n| offsets, and if $w_k=(u_k,v_k)$ is the $k$th
+of these, the $k$th pen slope is defined by the formula
+$$s_k={v\k-v_k\over u\k-u_k},\qquad\hbox{for $0<k<n$}.$$
+In odd-numbered octants, the numerator and denominator of this fraction
+will be nonnegative; in even-numbered octants they will both be nonpositive.
+Furthermore we always have $0=s_0\le s_1\le\cdots\le s_n=\infty$. The goal of
+|offset_prep| is to find an offset index~|k| to associate with
+each cubic, such that the slope $s(t)$ of the cubic satisfies
+$$s_{k-1}\le s(t)\le s_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$
+We may have to split a cubic into as many as $2n-1$ pieces before each
+piece corresponds to a unique offset.
+
+@<Split the cubic between |p| and |q|, if necessary, into cubics...@>=
+if n<=1 then right_type(p):=1 {this case is easy}
+else begin @<Prepare for derivative computations;
+ |goto not_found| if the current cubic is dead@>;
+ @<Find the initial slope, |dy/dx|@>;
+ if dx=0 then @<Handle the special case of infinite slope@>
+ else begin @<Find the index |k| such that $s_{k-1}\L\\{dy}/\\{dx}<s_k$@>;
+ @<Complete the offset splitting process@>;
+ end;
+not_found: end
+
+@ The slope of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be
+calculated from the quadratic polynomials
+${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and
+${1\over3}y'(t)=B(y_1-y_0,y_2-y_1,y_3-y_2;t)$.
+Since we may be calculating slopes from several cubics
+split from the current one, it is desirable to do these calculations
+without losing too much precision. ``Scaled up'' values of the
+derivatives, which will be less tainted by accumulated errors than
+derivatives found from the cubics themselves, are maintained in
+local variables |x0|, |x1|, and |x2|, representing $X_0=2^l(x_1-x_0)$,
+$X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2|
+represent $Y_0=2^l(y_1-y_0)$, $Y_1=2^l(y_2-y_1)$, and $Y_2=2^l(y_3-y_2)$.
+To test whether the slope of the cubic is $\ge s$ or $\le s$, we will test
+the sign of the quadratic ${1\over3}2^l\bigl(y'(t)-sx'(t)\bigr)$ if $s\le1$,
+or ${1\over3}2^l\bigl(y'(t)/s-x'(t)\bigr)$ if $s>1$.
+
+@<Other local variables for |offset_prep|@>=
+@!x0,@!x1,@!x2,@!y0,@!y1,@!y2:integer; {representatives of derivatives}
+@!t0,@!t1,@!t2:integer; {coefficients of polynomial for slope testing}
+@!du,@!dv,@!dx,@!dy:integer; {for slopes of the pen and the curve}
+@!max_coef:integer; {used while scaling}
+@!x0a,@!x1a,@!x2a,@!y0a,@!y1a,@!y2a:integer; {intermediate values}
+@!t:fraction; {where the derivative passes through zero}
+@!s:fraction; {slope or reciprocal slope}
+
+@ @<Prepare for derivative computations...@>=
+x0:=right_x(p)-x_coord(p); {should be |>=0|}
+x2:=x_coord(q)-left_x(q); {likewise}
+x1:=left_x(q)-right_x(p); {but this might be negative}
+y0:=right_y(p)-y_coord(p); y2:=y_coord(q)-left_y(q);
+y1:=left_y(q)-right_y(p);
+max_coef:=abs(x0); {we take |abs| just to make sure}
+if abs(x1)>max_coef then max_coef:=abs(x1);
+if abs(x2)>max_coef then max_coef:=abs(x2);
+if abs(y0)>max_coef then max_coef:=abs(y0);
+if abs(y1)>max_coef then max_coef:=abs(y1);
+if abs(y2)>max_coef then max_coef:=abs(y2);
+if max_coef=0 then goto not_found;
+while max_coef<fraction_half do
+ begin double(max_coef);
+ double(x0); double(x1); double(x2);
+ double(y0); double(y1); double(y2);
+ end
+
+@ Let us first solve a special case of the problem: Suppose we
+know an index~$k$ such that either (i)~$s(t)\G s_{k-1}$ for all~$t$
+and $s(0)<s_k$, or (ii)~$s(t)\L s_k$ for all~$t$ and $s(0)>s_{k-1}$.
+Then, in a sense, we're halfway done, since one of the two inequalities
+in $(*)$ is satisfied, and the other couldn't be satisfied for
+any other value of~|k|.
+
+The |fin_offset_prep| subroutine solves the stated subproblem.
+It has a boolean parameter called |rising| that is |true| in
+case~(i), |false| in case~(ii). When |rising=false|, parameters
+|x0| through |y2| represent the negative of the derivative of
+the cubic following |p|; otherwise they represent the actual derivative.
+The |w| parameter should point to offset~$w_k$.
+
+@<Declare subroutines needed by |offset_prep|@>=
+procedure fin_offset_prep(@!p:pointer;@!k:halfword;@!w:pointer;
+ @!x0,@!x1,@!x2,@!y0,@!y1,@!y2:integer;@!rising:boolean;@!n:integer);
+label exit;
+var @!ww:pointer; {for list manipulation}
+@!du,@!dv:scaled; {for slope calculation}
+@!t0,@!t1,@!t2:integer; {test coefficients}
+@!t:fraction; {place where the derivative passes a critical slope}
+@!s:fraction; {slope or reciprocal slope}
+@!v:integer; {intermediate value for updating |x0..y2|}
+begin loop
+ begin right_type(p):=k;
+ if rising then
+ if k=n then return
+ else ww:=link(w) {a pointer to $w\k$}
+ else if k=1 then return
+ else ww:=knil(w); {a pointer to $w_{k-1}$}
+ @<Compute test coefficients |(t0,t1,t2)|
+ for $s(t)$ versus $s_k$ or $s_{k-1}$@>;
+ t:=crossing_point(t0,t1,t2);
+ if t>=fraction_one then return;
+ @<Split the cubic at $t$,
+ and split off another cubic if the derivative crosses back@>;
+ if rising then incr(k)@+else decr(k);
+ w:=ww;
+ end;
+exit:end;
+
+@ @<Compute test coefficients |(t0,t1,t2)| for $s(t)$ versus...@>=
+du:=x_coord(ww)-x_coord(w); dv:=y_coord(ww)-y_coord(w);
+if abs(du)>=abs(dv) then {$s_{k-1}\le1$ or $s_k\le1$}
+ begin s:=make_fraction(dv,du);
+ t0:=take_fraction(x0,s)-y0;
+ t1:=take_fraction(x1,s)-y1;
+ t2:=take_fraction(x2,s)-y2;
+ end
+else begin s:=make_fraction(du,dv);
+ t0:=x0-take_fraction(y0,s);
+ t1:=x1-take_fraction(y1,s);
+ t2:=x2-take_fraction(y2,s);
+ end
+
+@ The curve has crossed $s_k$ or $s_{k-1}$; its initial segment satisfies
+$(*)$, and it might cross again and return towards $s_{k-1}$ or $s_k$,
+respectively, yielding another solution of $(*)$.
+
+@<Split the cubic at $t$, and split off another...@>=
+begin split_for_offset(p,t); right_type(p):=k; p:=link(p);@/
+v:=t_of_the_way(x0)(x1); x1:=t_of_the_way(x1)(x2);
+x0:=t_of_the_way(v)(x1);@/
+v:=t_of_the_way(y0)(y1); y1:=t_of_the_way(y1)(y2);
+y0:=t_of_the_way(v)(y1);@/
+t1:=t_of_the_way(t1)(t2);
+if t1>0 then t1:=0; {without rounding error, |t1| would be |<=0|}
+t:=crossing_point(0,-t1,-t2);
+if t<fraction_one then
+ begin split_for_offset(p,t); right_type(link(p)):=k;@/
+ v:=t_of_the_way(x1)(x2); x1:=t_of_the_way(x0)(x1);
+ x2:=t_of_the_way(x1)(v);@/
+ v:=t_of_the_way(y1)(y2); y1:=t_of_the_way(y0)(y1);
+ y2:=t_of_the_way(y1)(v);
+ end;
+end
+
+@ Now we must consider the general problem of |offset_prep|, when
+nothing is known about a given cubic. We start by finding its
+slope $s(0)$ in the vicinity of |t=0|.
+
+If $z'(t)=0$, the given cubic is numerically unstable, since the
+slope direction is probably being influenced primarily by rounding
+errors. A user who specifies such cuspy curves should expect to generate
+rather wild results. The present code tries its best to believe the
+existing data, as if no rounding errors were present.
+
+@ @<Find the initial slope, |dy/dx|@>=
+dx:=x0; dy:=y0;
+if dx=0 then if dy=0 then
+ begin dx:=x1; dy:=y1;
+ if dx=0 then if dy=0 then
+ begin dx:=x2; dy:=y2;
+ end;
+ end
+
+@ The next step is to bracket the initial slope between consecutive
+slopes of the pen polygon. The most important invariant relation in the
+following loop is that |dy/dx>=@t$s_{k-1}$@>|.
+
+@<Find the index |k| such that $s_{k-1}\L\\{dy}/\\{dx}<s_k$@>=
+k:=1; w:=link(lh);
+loop@+ begin if k=n then goto done;
+ ww:=link(w);
+ if ab_vs_cd(dy,abs(x_coord(ww)-x_coord(w)),@|
+ dx,abs(y_coord(ww)-y_coord(w)))>=0 then
+ begin incr(k); w:=ww;
+ end
+ else goto done;
+ end;
+done:
+
+@ Finally we want to reduce the general problem to situations that
+|fin_offset_prep| can handle. If |k=1|, we already are in the desired
+situation. Otherwise we can split the cubic into at most three parts
+with respect to $s_{k-1}$, and apply |fin_offset_prep| to each part.
+
+@<Complete the offset splitting process@>=
+if k=1 then t:=fraction_one+1
+else begin ww:=knil(w); @<Compute test coeff...@>;
+ t:=crossing_point(-t0,-t1,-t2);
+ end;
+if t>=fraction_one then fin_offset_prep(p,k,w,x0,x1,x2,y0,y1,y2,true,n)
+else begin split_for_offset(p,t); r:=link(p);@/
+ x1a:=t_of_the_way(x0)(x1); x1:=t_of_the_way(x1)(x2);
+ x2a:=t_of_the_way(x1a)(x1);@/
+ y1a:=t_of_the_way(y0)(y1); y1:=t_of_the_way(y1)(y2);
+ y2a:=t_of_the_way(y1a)(y1);@/
+ fin_offset_prep(p,k,w,x0,x1a,x2a,y0,y1a,y2a,true,n); x0:=x2a; y0:=y2a;
+ t1:=t_of_the_way(t1)(t2);
+ if t1<0 then t1:=0;
+ t:=crossing_point(0,t1,t2);
+ if t<fraction_one then
+ @<Split off another |rising| cubic for |fin_offset_prep|@>;
+ fin_offset_prep(r,k-1,ww,-x0,-x1,-x2,-y0,-y1,-y2,false,n);
+ end
+
+@ @<Split off another |rising| cubic for |fin_offset_prep|@>=
+begin split_for_offset(r,t);@/
+x1a:=t_of_the_way(x1)(x2); x1:=t_of_the_way(x0)(x1);
+x0a:=t_of_the_way(x1)(x1a);@/
+y1a:=t_of_the_way(y1)(y2); y1:=t_of_the_way(y0)(y1);
+y0a:=t_of_the_way(y1)(y1a);@/
+fin_offset_prep(link(r),k,w,x0a,x1a,x2,y0a,y1a,y2,true,n);
+x2:=x0a; y2:=y0a;
+end
+
+@ @<Handle the special case of infinite slope@>=
+fin_offset_prep(p,n,knil(knil(lh)),-x0,-x1,-x2,-y0,-y1,-y2,false,n)
+
+@ OK, it's time now for the biggie. The |fill_envelope| routine generalizes
+|fill_spec| to polygonal envelopes. Its outer structure is essentially the
+same as before, except that octants with no cubics do contribute to
+the envelope.
+
+@p @t\4@>@<Declare the procedure called |skew_line_edges|@>@;
+@t\4@>@<Declare the procedure called |dual_moves|@>@;
+procedure fill_envelope(@!spec_head:pointer);
+label done, done1;
+var @!p,@!q,@!r,@!s:pointer; {for list traversal}
+@!h:pointer; {head of pen offset list for current octant}
+@!www:pointer; {a pen offset of temporary interest}
+@<Other local variables for |fill_envelope|@>@;
+begin if internal[tracing_edges]>0 then begin_edge_tracing;
+p:=spec_head; {we assume that |left_type(spec_head)=endpoint|}
+repeat octant:=left_octant(p); h:=cur_pen+octant;
+@<Set variable |q| to the node at the end of the current octant@>;
+@<Determine the envelope's starting and ending
+ lattice points |(m0,n0)| and |(m1,n1)|@>;
+offset_prep(p,h); {this may clobber node~|q|, if it becomes ``dead''}
+@<Set variable |q| to the node at the end of the current octant@>;
+@<Make the envelope moves for the current octant and insert them
+ in the pixel data@>;
+p:=link(q);
+until p=spec_head;
+if internal[tracing_edges]>0 then end_edge_tracing;
+toss_knot_list(spec_head);
+end;
+
+@ In even-numbered octants we have reflected the coordinates an odd number
+of times, hence clockwise and counterclockwise are reversed; this means that
+the envelope is being formed in a ``dual'' manner. For the time being, let's
+concentrate on odd-numbered octants, since they're easier to understand.
+After we have coded the program for odd-numbered octants, the changes needed
+to dualize it will not be so mysterious.
+
+It is convenient to assume that we enter an odd-numbered octant with
+an |axis| transition (where the skewed slope is zero) and leave at a
+|diagonal| one (where the skewed slope is infinite). Then all of the
+offset points $z(t)+w(t)$ will lie in a rectangle whose lower left and
+upper right corners are the initial and final offset points. If this
+assumption doesn't hold we can implicitly change the curve so that it does.
+For example, if the entering transition is diagonal, we can draw a
+straight line from $z_0+w_{n+1}$ to $z_0+w_0$ and continue as if the
+curve were moving rightward. The effect of this on the envelope is simply
+to ``doubly color'' the region enveloped by a section of the pen that
+goes from $w_0$ to $w_1$ to $\cdots$ to $w_{n+1}$ to~$w_0$. The additional
+straight line at the beginning (and a similar one at the end, where it
+may be necessary to go from $z_1+w_{n+1}$ to $z_1+w_0$) can be drawn by
+the |line_edges| routine; we are thereby saved from the embarrassment that
+these lines travel backwards from the current octant direction.
+
+Once we have established the assumption that the curve goes from
+$z_0+w_0$ to $z_1+w_{n+1}$, any further retrograde moves that might
+occur within the octant can be essentially ignored; we merely need to
+keep track of the rightmost edge in each row, in order to compute
+the envelope.
+
+Envelope moves consist of offset cubics intermixed with straight line
+segments. We record them in a separate |env_move| array, which is
+something like |move| but it keeps track of the rightmost position of the
+envelope in each row.
+
+@<Glob...@>=
+@!env_move:array[0..move_size] of integer;
+
+@ @<Determine the envelope's starting and ending...@>=
+w:=link(h);@+if left_transition(p)=diagonal then w:=knil(w);
+@!stat if internal[tracing_edges]>unity then
+ @<Print a line of diagnostic info to introduce this octant@>;
+tats@;@/
+ww:=link(h); www:=ww; {starting and ending offsets}
+if odd(octant_number[octant]) then www:=knil(www)@+else ww:=knil(ww);
+if w<>ww then skew_line_edges(p,w,ww);
+end_round(x_coord(p)+x_coord(ww),y_coord(p)+y_coord(ww));
+m0:=m1; n0:=n1; d0:=d1;@/
+end_round(x_coord(q)+x_coord(www),y_coord(q)+y_coord(www));
+if n1-n0>=move_size then overflow("move table size",move_size)
+@:METAFONT capacity exceeded move table size}{\quad move table size@>
+
+@ @<Print a line of diagnostic info to introduce this octant@>=
+begin print_nl("@@ Octant "); print(octant_dir[octant]);
+@:]]]\AT!_Octant}{\.{\AT! Octant...}@>
+print(" ("); print_int(info(h)); print(" offset");
+if info(h)<>1 then print_char("s");
+print("), from ");
+print_two_true(x_coord(p)+x_coord(w),y_coord(p)+y_coord(w));@/
+ww:=link(h);@+if right_transition(q)=diagonal then ww:=knil(ww);
+print(" to ");
+print_two_true(x_coord(q)+x_coord(ww),y_coord(q)+y_coord(ww));
+end
+
+@ A slight variation of the |line_edges| procedure comes in handy
+when we must draw the retrograde lines for nonstandard entry and exit
+conditions.
+
+@<Declare the procedure called |skew_line_edges|@>=
+procedure skew_line_edges(@!p,@!w,@!ww:pointer);
+var @!x0,@!y0,@!x1,@!y1:scaled; {from and to}
+begin if (x_coord(w)<>x_coord(ww))or(y_coord(w)<>y_coord(ww)) then
+ begin x0:=x_coord(p)+x_coord(w); y0:=y_coord(p)+y_coord(w);@/
+ x1:=x_coord(p)+x_coord(ww); y1:=y_coord(p)+y_coord(ww);@/
+ unskew(x0,y0,octant); {unskew and unrotate the coordinates}
+ x0:=cur_x; y0:=cur_y;@/
+ unskew(x1,y1,octant);@/
+ @!stat if internal[tracing_edges]>unity then
+ begin print_nl("@@ retrograde line from ");
+@:]]]\AT!_retro_}{\.{\AT! retrograde line...}@>
+ @.retrograde line...@>
+ print_two(x0,y0); print(" to "); print_two(cur_x,cur_y); print_nl("");
+ end;@+tats@;@/
+ line_edges(x0,y0,cur_x,cur_y); {then draw a straight line}
+ end;
+end;
+
+@ The envelope calculations require more local variables than we needed
+in the simpler case of |fill_spec|. At critical points in the computation,
+|w| will point to offset $w_k$; |m| and |n| will record the current
+lattice positions. The values of |move_ptr| after the initial and before
+the final offset adjustments are stored in |smooth_bot| and |smooth_top|,
+respectively.
+
+@<Other local variables for |fill_envelope|@>=
+@!m,@!n:integer; {current lattice position}
+@!mm0,@!mm1:integer; {skewed equivalents of |m0| and |m1|}
+@!k:integer; {current offset number}
+@!w,@!ww:pointer; {pointers to the current offset and its neighbor}
+@!smooth_bot,@!smooth_top:0..move_size; {boundaries of smoothing}
+@!xx,@!yy,@!xp,@!yp,@!delx,@!dely,@!tx,@!ty:scaled;
+ {registers for coordinate calculations}
+
+@ @<Make the envelope moves for the current octant...@>=
+if odd(octant_number[octant]) then
+ begin @<Initialize for ordinary envelope moves@>;
+ r:=p; right_type(q):=info(h)+1;
+ loop@+ begin if r=q then smooth_top:=move_ptr;
+ while right_type(r)<>k do
+ @<Insert a line segment to approach the correct offset@>;
+ if r=p then smooth_bot:=move_ptr;
+ if r=q then goto done;
+ move[move_ptr]:=1; n:=move_ptr; s:=link(r);@/
+ make_moves(x_coord(r)+x_coord(w),right_x(r)+x_coord(w),
+ left_x(s)+x_coord(w),x_coord(s)+x_coord(w),@|
+ y_coord(r)+y_coord(w)+half_unit,right_y(r)+y_coord(w)+half_unit,
+ left_y(s)+y_coord(w)+half_unit,y_coord(s)+y_coord(w)+half_unit,@|
+ xy_corr[octant],y_corr[octant]);@/
+ @<Transfer moves from the |move| array to |env_move|@>;
+ r:=s;
+ end;
+done: @<Insert the new envelope moves in the pixel data@>;
+ end
+else dual_moves(h,p,q);
+right_type(q):=endpoint
+
+@ @<Initialize for ordinary envelope moves@>=
+k:=0; w:=link(h); ww:=knil(w);
+mm0:=floor_unscaled(x_coord(p)+x_coord(w)-xy_corr[octant]);
+mm1:=floor_unscaled(x_coord(q)+x_coord(ww)-xy_corr[octant]);
+for n:=0 to n1-n0 do env_move[n]:=mm0;
+env_move[n1-n0]:=mm1; move_ptr:=0; m:=mm0
+
+@ At this point |n| holds the value of |move_ptr| that was current
+when |make_moves| began to record its moves.
+
+@<Transfer moves from the |move| array to |env_move|@>=
+repeat m:=m+move[n]-1;
+if m>env_move[n] then env_move[n]:=m;
+incr(n);
+until n>move_ptr
+
+@ Retrograde lines (when |k| decreases) do not need to be recorded in
+|env_move| because their edges are not the furthest right in any row.
+
+@<Insert a line segment to approach the correct offset@>=
+begin xx:=x_coord(r)+x_coord(w); yy:=y_coord(r)+y_coord(w)+half_unit;
+@!stat if internal[tracing_edges]>unity then
+ begin print_nl("@@ transition line "); print_int(k); print(", from ");
+@:]]]\AT!_trans_}{\.{\AT! transition line...}@>
+@.transition line...@>
+ print_two_true(xx,yy-half_unit);
+ end;@+tats@;@/
+if right_type(r)>k then
+ begin incr(k); w:=link(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ if yp<>yy then
+ @<Record a line segment from |(xx,yy)| to |(xp,yp)| in |env_move|@>;
+ end
+else begin decr(k); w:=knil(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ end;
+stat if internal[tracing_edges]>unity then
+ begin print(" to ");
+ print_two_true(xp,yp-half_unit);
+ print_nl("");
+ end;@+tats@;@/
+m:=floor_unscaled(xp-xy_corr[octant]);
+move_ptr:=floor_unscaled(yp-y_corr[octant])-n0;
+if m>env_move[move_ptr] then env_move[move_ptr]:=m;
+end
+
+@ In this step we have |xp>=xx| and |yp>=yy|.
+
+@<Record a line segment from |(xx,yy)| to |(xp,yp)| in |env_move|@>=
+begin ty:=floor_scaled(yy-y_corr[octant]); dely:=yp-yy; yy:=yy-ty;
+ty:=yp-y_corr[octant]-ty;
+if ty>=unity then
+ begin delx:=xp-xx; yy:=unity-yy;
+ loop@+ begin tx:=take_fraction(delx,make_fraction(yy,dely));
+ if ab_vs_cd(tx,dely,delx,yy)+xy_corr[octant]>0 then decr(tx);
+ m:=floor_unscaled(xx+tx);
+ if m>env_move[move_ptr] then env_move[move_ptr]:=m;
+ ty:=ty-unity;
+ if ty<unity then goto done1;
+ yy:=yy+unity; incr(move_ptr);
+ end;
+ done1:end;
+end
+
+@ @<Insert the new envelope moves in the pixel data@>=
+debug if (m<>mm1)or(move_ptr<>n1-n0) then confusion("1");@+gubed@;@/
+@:this can't happen /}{\quad 1@>
+move[0]:=d0+env_move[0]-mm0;
+for n:=1 to move_ptr do
+ move[n]:=env_move[n]-env_move[n-1]+1;
+move[move_ptr]:=move[move_ptr]-d1;
+if internal[smoothing]>0 then smooth_moves(smooth_bot,smooth_top);
+move_to_edges(m0,n0,m1,n1);
+if right_transition(q)=axis then
+ begin w:=link(h); skew_line_edges(q,knil(w),w);
+ end
+
+@ We've done it all in the odd-octant case; the only thing remaining
+is to repeat the same ideas, upside down and/or backwards.
+
+The following code has been split off as a subprocedure of |fill_envelope|,
+because some \PASCAL\ compilers cannot handle procedures as large as
+|fill_envelope| would otherwise be.
+
+@<Declare the procedure called |dual_moves|@>=
+procedure dual_moves(@!h,@!p,@!q:pointer);
+label done,done1;
+var @!r,@!s:pointer; {for list traversal}
+@<Other local variables for |fill_envelope|@>@;
+begin @<Initialize for dual envelope moves@>;
+r:=p; {recall that |right_type(q)=endpoint=0| now}
+loop@+ begin if r=q then smooth_top:=move_ptr;
+ while right_type(r)<>k do
+ @<Insert a line segment dually to approach the correct offset@>;
+ if r=p then smooth_bot:=move_ptr;
+ if r=q then goto done;
+ move[move_ptr]:=1; n:=move_ptr; s:=link(r);@/
+ make_moves(x_coord(r)+x_coord(w),right_x(r)+x_coord(w),
+ left_x(s)+x_coord(w),x_coord(s)+x_coord(w),@|
+ y_coord(r)+y_coord(w)+half_unit,right_y(r)+y_coord(w)+half_unit,
+ left_y(s)+y_coord(w)+half_unit,y_coord(s)+y_coord(w)+half_unit,@|
+ xy_corr[octant],y_corr[octant]);
+ @<Transfer moves dually from the |move| array to |env_move|@>;
+ r:=s;
+ end;
+done:@<Insert the new envelope moves dually in the pixel data@>;
+end;
+
+@ In the dual case the normal situation is to arrive with a |diagonal|
+transition and to leave at the |axis|. The leftmost edge in each row
+is relevant instead of the rightmost one.
+
+@<Initialize for dual envelope moves@>=
+k:=info(h)+1; ww:=link(h); w:=knil(ww);@/
+mm0:=floor_unscaled(x_coord(p)+x_coord(w)-xy_corr[octant]);
+mm1:=floor_unscaled(x_coord(q)+x_coord(ww)-xy_corr[octant]);
+for n:=1 to n1-n0+1 do env_move[n]:=mm1;
+env_move[0]:=mm0; move_ptr:=0; m:=mm0
+
+@ @<Transfer moves dually from the |move| array to |env_move|@>=
+repeat if m<env_move[n] then env_move[n]:=m;
+m:=m+move[n]-1;
+incr(n);
+until n>move_ptr
+
+@ Dual retrograde lines occur when |k| increases; the edges of such lines
+are not the furthest left in any row.
+
+@<Insert a line segment dually to approach the correct offset@>=
+begin xx:=x_coord(r)+x_coord(w); yy:=y_coord(r)+y_coord(w)+half_unit;
+@!stat if internal[tracing_edges]>unity then
+ begin print_nl("@@ transition line "); print_int(k); print(", from ");
+@:]]]\AT!_trans_}{\.{\AT! transition line...}@>
+@.transition line...@>
+ print_two_true(xx,yy-half_unit);
+ end;@+tats@;@/
+if right_type(r)<k then
+ begin decr(k); w:=knil(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ if yp<>yy then
+ @<Record a line segment from |(xx,yy)| to |(xp,yp)| dually in |env_move|@>;
+ end
+else begin incr(k); w:=link(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ end;
+stat if internal[tracing_edges]>unity then
+ begin print(" to ");
+ print_two_true(xp,yp-half_unit);
+ print_nl("");
+ end;@+tats@;@/
+m:=floor_unscaled(xp-xy_corr[octant]);
+move_ptr:=floor_unscaled(yp-y_corr[octant])-n0;
+if m<env_move[move_ptr] then env_move[move_ptr]:=m;
+end
+
+@ Again, |xp>=xx| and |yp>=yy|; but this time we are interested in the {\sl
+smallest\/} |m| that belongs to a given |move_ptr| position, instead of
+the largest~|m|.
+
+@<Record a line segment from |(xx,yy)| to |(xp,yp)| dually in |env_move|@>=
+begin ty:=floor_scaled(yy-y_corr[octant]); dely:=yp-yy; yy:=yy-ty;
+ty:=yp-y_corr[octant]-ty;
+if ty>=unity then
+ begin delx:=xp-xx; yy:=unity-yy;
+ loop@+ begin if m<env_move[move_ptr] then env_move[move_ptr]:=m;
+ tx:=take_fraction(delx,make_fraction(yy,dely));
+ if ab_vs_cd(tx,dely,delx,yy)+xy_corr[octant]>0 then decr(tx);
+ m:=floor_unscaled(xx+tx);
+ ty:=ty-unity; incr(move_ptr);
+ if ty<unity then goto done1;
+ yy:=yy+unity;
+ end;
+done1: if m<env_move[move_ptr] then env_move[move_ptr]:=m;
+ end;
+end
+
+@ Since |env_move| contains minimum values instead of maximum values, the
+finishing-up process is slightly different in the dual case.
+
+@<Insert the new envelope moves dually in the pixel data@>=
+debug if (m<>mm1)or(move_ptr<>n1-n0) then confusion("2");@+gubed@;@/
+@:this can't happen /}{\quad 2@>
+move[0]:=d0+env_move[1]-mm0;
+for n:=1 to move_ptr do
+ move[n]:=env_move[n+1]-env_move[n]+1;
+move[move_ptr]:=move[move_ptr]-d1;
+if internal[smoothing]>0 then smooth_moves(smooth_bot,smooth_top);
+move_to_edges(m0,n0,m1,n1);
+if right_transition(q)=diagonal then
+ begin w:=link(h); skew_line_edges(q,w,knil(w));
+ end
+
+@* \[25] Elliptical pens.
+To get the envelope of a cyclic path with respect to an ellipse, \MF\
+calculates the envelope with respect to a polygonal approximation to
+the ellipse, using an approach due to John Hobby (Ph.D. thesis,
+Stanford University, 1985).
+@^Hobby, John Douglas@>
+This has two important advantages over trying to obtain the ``exact''
+envelope:
+
+\yskip\textindent{1)}It gives better results, because the polygon has been
+designed to counteract problems that arise from digitization; the
+polygon includes sub-pixel corrections to an exact ellipse that make
+the results essentially independent of where the path falls on the raster.
+For example, the exact envelope with respect to a pen of diameter~1
+blackens a pixel if and only if the path intersects a circle of diameter~1
+inscribed in that pixel; the resulting pattern has ``blots'' when the path
+is travelling diagonally in unfortunate raster positions. A much better
+result is obtained when pixels are blackened only when the path intersects
+an inscribed {\sl diamond\/} of diameter~1. Such a diamond is precisely
+the polygon that \MF\ uses in the special case of a circle whose diameter is~1.
+
+\yskip\textindent{2)}Polygonal envelopes of cubic splines are cubic
+splines, hence it isn't necessary to introduce completely different
+routines. By contrast, exact envelopes of cubic splines with respect
+to circles are complicated curves, more difficult to plot than cubics.
+
+@ Hobby's construction involves some interesting number theory.
+If $u$ and~$v$ are relatively prime integers, we divide the
+set of integer points $(m,n)$ into equivalence classes by saying
+that $(m,n)$ belongs to class $um+vn$. Then any two integer points
+that lie on a line of slope $-u/v$ belong to the same class, because
+such points have the form $(m+tv,n-tu)$. Neighboring lines of slope $-u/v$
+that go through integer points are separated by distance $1/\psqrt{u^2+v^2}$
+from each other, and these lines are perpendicular to lines of slope~$v/u$.
+If we start at the origin and travel a distance $k/\psqrt{u^2+v^2}$ in
+direction $(u,v)$, we reach the line of slope~$-u/v$ whose points
+belong to class~$k$.
+
+For example, let $u=2$ and $v=3$. Then the points $(0,0)$, $(3,-2)$,
+$\ldots$ belong to class~0; the points $(-1,1)$, $(2,-1)$, $\ldots$ belong
+to class~1; and the distance between these two lines is $1/\sqrt{13}$.
+The point $(2,3)$ itself belongs to class~13, hence its distance from
+the origin is $13/\sqrt{13}=\sqrt{13}$ (which we already knew).
+
+Suppose we wish to plot envelopes with respect to polygons with
+integer vertices. Then the best polygon for curves that travel in
+direction $(v,-u)$ will contain the points of class~$k$ such that
+$k/\psqrt{u^2+v^2}$ is as close as possible to~$d$, where $d$ is the
+maximum distance of the given ellipse from the line $ux+vy=0$.
+
+The |fillin| correction assumes that a diagonal line has an
+apparent thickness $$2f\cdot\min(\vert u\vert,\vert v\vert)/\psqrt{u^2+v^2}$$
+greater than would be obtained with truly square pixels. (If a
+white pixel at an exterior corner is assumed to have apparent
+darkness $f_1$ and a black pixel at an interior corner is assumed
+to have apparent darkness $1-f_2$, then $f=f_1-f_2$ is the |fillin|
+parameter.) Under this assumption we want to choose $k$ so that
+$\bigl(k+2f\cdot\min(\vert u\vert,\vert v\vert)\bigr)\big/\psqrt{u^2+v^2}$
+is as close as possible to $d$.
+
+Integer coordinates for the vertices work nicely because the thickness of
+the envelope at any given slope is independent of the position of the
+path with respect to the raster. It turns out, in fact, that the same
+property holds for polygons whose vertices have coordinates that are
+integer multiples of~$1\over2$, because ellipses are symmetric about
+the origin. It's convenient to double all dimensions and require the
+resulting polygon to have vertices with integer coordinates. For example,
+to get a circle of {\sl diameter}~$r$, we shall compute integer
+coordinates for a circle of {\sl radius}~$r$. The circle of radius~$r$
+will want to be represented by a polygon that contains the boundary
+points $(0,\pm r)$ and~$(\pm r,0)$; later we will divide everything
+by~2 and get a polygon with $(0,\pm{1\over2}r)$ and $(\pm{1\over2}r,0)$
+on its boundary.
+
+@ In practice the important slopes are those having small values of
+$u$ and~$v$; these make regular patterns in which our eyes quickly
+spot irregularities. For example, horizontal and vertical lines
+(when $u=0$ and $\vert v\vert=1$, or $\vert u\vert=1$ and $v=0$)
+are the most important; diagonal lines (when $\vert u\vert=\vert v\vert=1$)
+are next; and then come lines with slope $\pm2$ or $\pm1/2$.
+
+The nicest way to generate all rational directions having small
+numerators and denominators is to generalize the Stern--Brocot tree
+[cf.~{\sl Concrete Mathematics}, section 4.5]
+@^Brocot, Achille@>
+@^Stern, Moritz Abraham@>
+to a ``Stern--Brocot wreath'' as follows: Begin with four nodes
+arranged in a circle, containing the respective directions
+$(u,v)=(1,0)$, $(0,1)$, $(-1,0)$, and~$(0,-1)$. Then between pairs of
+consecutive terms $(u,v)$ and $(u',v')$ of the wreath, insert the
+direction $(u+u',v+v')$; continue doing this until some stopping
+criterion is fulfilled.
+
+It is not difficult to verify that, regardless of the stopping
+criterion, consecutive directions $(u,v)$ and $(u',v')$ of this
+wreath will always satisfy the relation $uv'-u'v=1$. Such pairs
+of directions have a nice property with respect to the equivalence
+classes described above. Let $l$ be a line of equivalent integer points
+$(m+tv,n-tu)$ with respect to~$(u,v)$, and let $l'$ be a line of
+equivalent integer points $(m'+tv',n'-tu')$ with respect to~$(u',v')$.
+Then $l$ and~$l'$ intersect in an integer point $(m'',n'')$, because
+the determinant of the linear equations for intersection is $uv'-u'v=1$.
+Notice that the class number of $(m'',n'')$ with respect to $(u+u',v+v')$
+is the sum of its class numbers with respect to $(u,v)$ and~$(u',v')$.
+Moreover, consecutive points on~$l$ and~$l'$ belong to classes that
+differ by exactly~1 with respect to $(u+u',v+v')$.
+
+This leads to a nice algorithm in which we construct a polygon having
+``correct'' class numbers for as many small-integer directions $(u,v)$
+as possible: Assuming that lines $l$ and~$l'$ contain points of the
+correct class for $(u,v)$ and~$(u',v')$, respectively, we determine
+the intersection $(m'',n'')$ and compute its class with respect to
+$(u+u',v+v')$. If the class is too large to be the best approximation,
+we move back the proper number of steps from $(m'',n'')$ toward smaller
+class numbers on both $l$ and~$l'$, unless this requires moving to points
+that are no longer in the polygon; in this way we arrive at two points that
+determine a line~$l''$ having the appropriate class. The process continues
+recursively, until it cannot proceed without removing the last remaining
+point from the class for $(u,v)$ or the class for $(u',v')$.
+
+@ The |make_ellipse| subroutine produces a pointer to a cyclic path
+whose vertices define a polygon suitable for envelopes. The control
+points on this path will be ignored; in fact, the fields in knot nodes
+that are usually reserved for control points are occupied by other
+data that helps |make_ellipse| compute the desired polygon.
+
+Parameters |major_axis| and |minor_axis| define the axes of the ellipse;
+and parameter |theta| is an angle by which the ellipse is rotated
+counterclockwise. If |theta=0|, the ellipse has the equation
+$(x/a)^2+(y/b)^2=1$, where |a=major_axis/2| and |b=minor_axis/2|.
+In general, the points of the ellipse are generated in the complex plane
+by the formula $e^{i\theta}(a\cos t+ib\sin t)$, as $t$~ranges over all
+angles. Notice that if |major_axis=minor_axis=d|, we obtain a circle
+of diameter~|d|, regardless of the value of |theta|.
+
+The method sketched above is used to produce the elliptical polygon,
+except that the main work is done only in the halfplane obtained from
+the three starting directions $(0,-1)$, $(1,0)$,~$(0,1)$. Since the ellipse
+has circular symmetry, we use the fact that the last half of the polygon
+is simply the negative of the first half. Furthermore, we need to compute only
+one quarter of the polygon if the ellipse has axis symmetry.
+
+@p function make_ellipse(@!major_axis,@!minor_axis:scaled;
+ @!theta:angle):pointer;
+label done,done1,found;
+var @!p,@!q,@!r,@!s:pointer; {for list manipulation}
+@!h:pointer; {head of the constructed knot list}
+@!alpha,@!beta,@!gamma,@!delta:integer; {special points}
+@!c,@!d:integer; {class numbers}
+@!u,@!v:integer; {directions}
+@!symmetric:boolean; {should the result be symmetric about the axes?}
+begin @<Initialize the ellipse data structure by beginning with
+ directions $(0,-1)$, $(1,0)$, $(0,1)$@>;
+@<Interpolate new vertices in the ellipse data structure until
+ improvement is impossible@>;
+if symmetric then
+ @<Complete the half ellipse by reflecting the quarter already computed@>;
+@<Complete the ellipse by copying the negative of the half already computed@>;
+make_ellipse:=h;
+end;
+
+@ A special data structure is used only with |make_ellipse|: The
+|right_x|, |left_x|, |right_y|, and |left_y| fields of knot nodes
+are renamed |right_u|, |left_v|, |right_class|, and |left_length|,
+in order to store information that simplifies the necessary computations.
+
+If |p| and |q| are consecutive knots in this data structure, the
+|x_coord| and |y_coord| fields of |p| and~|q| contain current vertices
+of the polygon; their values are integer multiples
+of |half_unit|. Both of these vertices belong to equivalence class
+|right_class(p)| with respect to the direction
+$\bigl($|right_u(p),left_v(q)|$\bigr)$. The number of points of this class
+on the line from vertex~|p| to vertex~|q| is |1+left_length(q)|.
+In particular, |left_length(q)=0| means that |x_coord(p)=x_coord(q)|
+and |y_coord(p)=y_coord(q)|; such duplicate vertices will be
+discarded during the course of the algorithm.
+
+The contents of |right_u(p)| and |left_v(q)| are integer multiples
+of |half_unit|, just like the coordinate fields. Hence, for example,
+the point $\bigl($|x_coord(p)-left_v(q),y_coord(p)+right_u(p)|$\bigr)$
+also belongs to class number |right_class(p)|. This point is one
+step closer to the vertex in node~|q|; it equals that vertex
+if and only if |left_length(q)=1|.
+
+The |left_type| and |right_type| fields are not used, but |link|
+has its normal meaning.
+
+To start the process, we create four nodes for the three directions
+$(0,-1)$, $(1,0)$, and $(0,1)$. The corresponding vertices are
+$(-\alpha,-\beta)$, $(\gamma,-\beta)$, $(\gamma,\beta)$, and
+$(\alpha,\beta)$, where $(\alpha,\beta)$ is a half-integer approximation
+to where the ellipse rises highest above the $x$-axis, and where
+$\gamma$ is a half-integer approximation to the maximum $x$~coordinate
+of the ellipse. The fourth of these nodes is not actually calculated
+if the ellipse has axis symmetry.
+
+@d right_u==right_x {|u| value for a pen edge}
+@d left_v==left_x {|v| value for a pen edge}
+@d right_class==right_y {equivalence class number of a pen edge}
+@d left_length==left_y {length of a pen edge}
+
+@<Initialize the ellipse data structure...@>=
+@<Calculate integers $\alpha$, $\beta$, $\gamma$ for the vertex
+ coordinates@>;
+p:=get_node(knot_node_size); q:=get_node(knot_node_size);
+r:=get_node(knot_node_size);
+if symmetric then s:=null@+else s:=get_node(knot_node_size);
+h:=p; link(p):=q; link(q):=r; link(r):=s; {|s=null| or |link(s)=null|}
+@<Revise the values of $\alpha$, $\beta$, $\gamma$, if necessary,
+ so that degenerate lines of length zero will not be obtained@>;
+x_coord(p):=-alpha*half_unit;
+y_coord(p):=-beta*half_unit;
+x_coord(q):=gamma*half_unit;@/
+y_coord(q):=y_coord(p); x_coord(r):=x_coord(q);@/
+right_u(p):=0; left_v(q):=-half_unit;@/
+right_u(q):=half_unit; left_v(r):=0;@/
+right_u(r):=0;
+right_class(p):=beta; right_class(q):=gamma; right_class(r):=beta;@/
+left_length(q):=gamma+alpha;
+if symmetric then
+ begin y_coord(r):=0; left_length(r):=beta;
+ end
+else begin y_coord(r):=-y_coord(p); left_length(r):=beta+beta;@/
+ x_coord(s):=-x_coord(p); y_coord(s):=y_coord(r);@/
+ left_v(s):=half_unit; left_length(s):=gamma-alpha;
+ end
+
+@ One of the important invariants of the pen data structure is that
+the points are distinct. We may need to correct the pen specification
+in order to avoid this. (The result of \&{pencircle} will always be at
+least one pixel wide and one pixel tall, although \&{makepen} is
+capable of producing smaller pens.)
+
+@<Revise the values of $\alpha$, $\beta$, $\gamma$, if necessary...@>=
+if beta=0 then beta:=1;
+if gamma=0 then gamma:=1;
+if gamma<=abs(alpha) then
+ if alpha>0 then alpha:=gamma-1
+ else alpha:=1-gamma
+
+@ If $a$ and $b$ are the semi-major and semi-minor axes,
+the given ellipse rises highest above the $x$-axis at the point
+$\bigl((a^2-b^2)\sin\theta\cos\theta/\rho\bigr)+i\rho$, where
+$\rho=\sqrt{(a\sin\theta)^2+(b\cos\theta)^2}$. It reaches
+furthest to the right of~the $y$-axis at the point
+$\sigma+i(a^2-b^2)\sin\theta\cos\theta/\sigma$, where
+$\sigma=\sqrt{(a\cos\theta)^2+(b\sin\theta)^2}$.
+
+@<Calculate integers $\alpha$, $\beta$, $\gamma$...@>=
+if (major_axis=minor_axis)or(theta mod ninety_deg=0) then
+ begin symmetric:=true; alpha:=0;
+ if odd(theta div ninety_deg) then
+ begin beta:=major_axis; gamma:=minor_axis;
+ n_sin:=fraction_one; n_cos:=0; {|n_sin| and |n_cos| are used later}
+ end
+ else begin beta:=minor_axis; gamma:=major_axis; theta:=0;
+ end; {|n_sin| and |n_cos| aren't needed in this case}
+ end
+else begin symmetric:=false;
+ n_sin_cos(theta); {set up $|n_sin|=\sin\theta$ and $|n_cos|=\cos\theta$}
+ gamma:=take_fraction(major_axis,n_sin);
+ delta:=take_fraction(minor_axis,n_cos);
+ beta:=pyth_add(gamma,delta);
+ alpha:=take_fraction(take_fraction(major_axis,
+ make_fraction(gamma,beta)),n_cos)@|
+ -take_fraction(take_fraction(minor_axis,
+ make_fraction(delta,beta)),n_sin);
+ alpha:=(alpha+half_unit) div unity;
+ gamma:=pyth_add(take_fraction(major_axis,n_cos),
+ take_fraction(minor_axis,n_sin));
+ end;
+beta:=(beta+half_unit) div unity;
+gamma:=(gamma+half_unit) div unity
+
+@ Now |p|, |q|, and |r| march through the list, always representing
+three consecutive vertices and two consecutive slope directions.
+When a new slope is interpolated, we back up slightly, until
+further refinement is impossible; then we march forward again.
+The somewhat magical operations performed in this part of the
+algorithm are justified by the theory sketched earlier.
+Complications arise only from the need to keep zero-length lines
+out of the final data structure.
+
+@<Interpolate new vertices in the ellipse data structure...@>=
+loop@+ begin u:=right_u(p)+right_u(q); v:=left_v(q)+left_v(r);
+ c:=right_class(p)+right_class(q);@/
+ @<Compute the distance |d| from class~0 to the edge of the ellipse
+ in direction |(u,v)|, times $\psqrt{u^2+v^2}$,
+ rounded to the nearest integer@>;
+ delta:=c-d; {we want to move |delta| steps back
+ from the intersection vertex~|q|}
+ if delta>0 then
+ begin if delta>left_length(r) then delta:=left_length(r);
+ if delta>=left_length(q) then
+ @<Remove the line from |p| to |q|,
+ and adjust vertex~|q| to introduce a new line@>
+ else @<Insert a new line for direction |(u,v)| between |p| and~|q|@>;
+ end
+ else p:=q;
+ @<Move to the next remaining triple |(p,q,r)|, removing and skipping past
+ zero-length lines that might be present; |goto done| if all
+ triples have been processed@>;
+ end;
+done:
+
+@ The appearance of a zero-length line means that we should advance |p|
+past it. We must not try to straddle a missing direction, because the
+algorithm works only on consecutive pairs of directions.
+
+@<Move to the next remaining triple |(p,q,r)|...@>=
+loop@+ begin q:=link(p);
+ if q=null then goto done;
+ if left_length(q)=0 then
+ begin link(p):=link(q); right_class(p):=right_class(q);
+ right_u(p):=right_u(q); free_node(q,knot_node_size);
+ end
+ else begin r:=link(q);
+ if r=null then goto done;
+ if left_length(r)=0 then
+ begin link(p):=r; free_node(q,knot_node_size); p:=r;
+ end
+ else goto found;
+ end;
+ end;
+found:
+
+@ The `\&{div} 8' near the end of this step comes from
+the fact that |delta| is scaled by~$2^{15}$ and $d$~by~$2^{16}$,
+while |take_fraction| removes a scale factor of~$2^{28}$.
+We also make sure that $d\G\max(\vert u\vert,\vert v\vert)$, so that
+the pen will always include a circular pen of diameter~1 as a subset;
+then it won't be possible to get disconnected path envelopes.
+
+@<Compute the distance |d| from class~0 to the edge of the ellipse...@>=
+delta:=pyth_add(u,v);
+if major_axis=minor_axis then d:=major_axis {circles are easy}
+else begin if theta=0 then
+ begin alpha:=u; beta:=v;
+ end
+ else begin alpha:=take_fraction(u,n_cos)+take_fraction(v,n_sin);
+ beta:=take_fraction(v,n_cos)-take_fraction(u,n_sin);
+ end;
+ alpha:=make_fraction(alpha,delta);
+ beta:=make_fraction(beta,delta);
+ d:=pyth_add(take_fraction(major_axis,alpha),
+ take_fraction(minor_axis,beta));
+ end;
+alpha:=abs(u); beta:=abs(v);
+if alpha<beta then
+ begin alpha:=abs(v); beta:=abs(u);
+ end; {now $\alpha=\max(\vert u\vert,\vert v\vert)$,
+ $\beta=\min(\vert u\vert,\vert v\vert)$}
+if internal[fillin]<>0 then
+ d:=d-take_fraction(internal[fillin],make_fraction(beta+beta,delta));
+d:=take_fraction((d+4) div 8,delta); alpha:=alpha div half_unit;
+if d<alpha then d:=alpha
+
+@ At this point there's a line of length |<=delta| from vertex~|p|
+to vertex~|q|, orthogonal to direction $\bigl($|right_u(p),left_v(q)|$\bigr)$;
+and there's a line of length |>=delta| from vertex~|q| to
+to vertex~|r|, orthogonal to direction $\bigl($|right_u(q),left_v(r)|$\bigr)$.
+The best line to direction $(u,v)$ should replace the line from
+|p| to~|q|; this new line will have the same length as the old.
+
+@<Remove the line from |p| to |q|...@>=
+begin delta:=left_length(q);@/
+right_class(p):=c-delta; right_u(p):=u; left_v(q):=v;@/
+x_coord(q):=x_coord(q)-delta*left_v(r);
+y_coord(q):=y_coord(q)+delta*right_u(q);@/
+left_length(r):=left_length(r)-delta;
+end
+
+@ Here is the main case, now that we have dealt with the exception:
+We insert a new line of length |delta| for direction |(u,v)|, decreasing
+each of the adjacent lines by |delta| steps.
+
+@<Insert a new line for direction |(u,v)| between |p| and~|q|@>=
+begin s:=get_node(knot_node_size); link(p):=s; link(s):=q;@/
+x_coord(s):=x_coord(q)+delta*left_v(q);
+y_coord(s):=y_coord(q)-delta*right_u(p);@/
+x_coord(q):=x_coord(q)-delta*left_v(r);
+y_coord(q):=y_coord(q)+delta*right_u(q);@/
+left_v(s):=left_v(q); right_u(s):=u; left_v(q):=v;@/
+right_class(s):=c-delta;@/
+left_length(s):=left_length(q)-delta; left_length(q):=delta;
+left_length(r):=left_length(r)-delta;
+end
+
+@ Only the coordinates need to be copied, not the class numbers and other stuff.
+At this point either |link(p)| or |link(link(p))| is |null|.
+
+@<Complete the half ellipse...@>=
+begin s:=null; q:=h;
+loop@+ begin r:=get_node(knot_node_size); link(r):=s; s:=r;@/
+ x_coord(s):=x_coord(q); y_coord(s):=-y_coord(q);
+ if q=p then goto done1;
+ q:=link(q);
+ if y_coord(q)=0 then goto done1;
+ end;
+done1: if (link(p)<>null) then free_node(link(p),knot_node_size);
+link(p):=s; beta:=-y_coord(h);
+while y_coord(p)<>beta do p:=link(p);
+q:=link(p);
+end
+
+@ Now we use a somewhat tricky fact: The pointer |q| will be null if and
+only if the line for the final direction $(0,1)$ has been removed. If
+that line still survives, it should be combined with a possibly
+surviving line in the initial direction $(0,-1)$.
+
+@<Complete the ellipse by copying...@>=
+if q<>null then
+ begin if right_u(h)=0 then
+ begin p:=h; h:=link(h); free_node(p,knot_node_size);@/
+ x_coord(q):=-x_coord(h);
+ end;
+ p:=q;
+ end
+else q:=p;
+r:=link(h); {now |p=q|, |x_coord(p)=-x_coord(h)|, |y_coord(p)=-y_coord(h)|}
+repeat s:=get_node(knot_node_size); link(p):=s; p:=s;@/
+x_coord(p):=-x_coord(r); y_coord(p):=-y_coord(r); r:=link(r);
+until r=q;
+link(p):=h
+
+@* \[26] Direction and intersection times.
+A path of length $n$ is defined parametrically by functions $x(t)$ and
+$y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path
+reaches the point $\bigl(x(t),y(t)\bigr)$. In this section of the program
+we shall consider operations that determine special times associated with
+given paths: the first time that a path travels in a given direction, and
+a pair of times at which two paths cross each other.
+
+@ Let's start with the easier task. The function |find_direction_time| is
+given a direction |(x,y)| and a path starting at~|h|. If the path never
+travels in direction |(x,y)|, the direction time will be~|-1|; otherwise
+it will be nonnegative.
+
+Certain anomalous cases can arise: If |(x,y)=(0,0)|, so that the given
+direction is undefined, the direction time will be~0. If $\bigl(x'(t),
+y'(t)\bigr)=(0,0)$, so that the path direction is undefined, it will be
+assumed to match any given direction at time~|t|.
+
+The routine solves this problem in nondegenerate cases by rotating the path
+and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be
+to find when a given path first travels ``due east.''
+
+@p function find_direction_time(@!x,@!y:scaled;@!h:pointer):scaled;
+label exit,found,not_found,done;
+var @!max:scaled; {$\max\bigl(\vert x\vert,\vert y\vert\bigr)$}
+@!p,@!q:pointer; {for list traversal}
+@!n:scaled; {the direction time at knot |p|}
+@!tt:scaled; {the direction time within a cubic}
+@<Other local variables for |find_direction_time|@>@;
+begin @<Normalize the given direction for better accuracy;
+ but |return| with zero result if it's zero@>;
+n:=0; p:=h;
+loop@+ begin if right_type(p)=endpoint then goto not_found;
+ q:=link(p);
+ @<Rotate the cubic between |p| and |q|; then
+ |goto found| if the rotated cubic travels due east at some time |tt|;
+ but |goto not_found| if an entire cyclic path has been traversed@>;
+ p:=q; n:=n+unity;
+ end;
+not_found: find_direction_time:=-unity; return;
+found: find_direction_time:=n+tt;
+exit:end;
+
+@ @<Normalize the given direction for better accuracy...@>=
+if abs(x)<abs(y) then
+ begin x:=make_fraction(x,abs(y));
+ if y>0 then y:=fraction_one@+else y:=-fraction_one;
+ end
+else if x=0 then
+ begin find_direction_time:=0; return;
+ end
+else begin y:=make_fraction(y,abs(x));
+ if x>0 then x:=fraction_one@+else x:=-fraction_one;
+ end
+
+@ Since we're interested in the tangent directions, we work with the
+derivative $${1\over3}B'(x_0,x_1,x_2,x_3;t)=
+B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of
+$B(x_0,x_1,x_2,x_3;t)$ itself. The derived coefficients are also scaled up
+in order to achieve better accuracy.
+
+The given path may turn abruptly at a knot, and it might pass the critical
+tangent direction at such a time. Therefore we remember the direction |phi|
+in which the previous rotated cubic was traveling. (The value of |phi| will be
+undefined on the first cubic, i.e., when |n=0|.)
+
+@<Rotate the cubic between |p| and |q|; then...@>=
+tt:=0;
+@<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control
+ points of the rotated derivatives@>;
+if y1=0 then if x1>=0 then goto found;
+if n>0 then
+ begin @<Exit to |found| if an eastward direction occurs at knot |p|@>;
+ if p=h then goto not_found;
+ end;
+if (x3<>0)or(y3<>0) then phi:=n_arg(x3,y3);
+@<Exit to |found| if the curve whose derivatives are specified by
+ |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt|@>
+
+@ @<Other local variables for |find_direction_time|@>=
+@!x1,@!x2,@!x3,@!y1,@!y2,@!y3:scaled; {multiples of rotated derivatives}
+@!theta,@!phi:angle; {angles of exit and entry at a knot}
+@!t:fraction; {temp storage}
+
+@ @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples...@>=
+x1:=right_x(p)-x_coord(p); x2:=left_x(q)-right_x(p);
+x3:=x_coord(q)-left_x(q);@/
+y1:=right_y(p)-y_coord(p); y2:=left_y(q)-right_y(p);
+y3:=y_coord(q)-left_y(q);@/
+max:=abs(x1);
+if abs(x2)>max then max:=abs(x2);
+if abs(x3)>max then max:=abs(x3);
+if abs(y1)>max then max:=abs(y1);
+if abs(y2)>max then max:=abs(y2);
+if abs(y3)>max then max:=abs(y3);
+if max=0 then goto found;
+while max<fraction_half do
+ begin double(max); double(x1); double(x2); double(x3);
+ double(y1); double(y2); double(y3);
+ end;
+t:=x1; x1:=take_fraction(x1,x)+take_fraction(y1,y);
+y1:=take_fraction(y1,x)-take_fraction(t,y);@/
+t:=x2; x2:=take_fraction(x2,x)+take_fraction(y2,y);
+y2:=take_fraction(y2,x)-take_fraction(t,y);@/
+t:=x3; x3:=take_fraction(x3,x)+take_fraction(y3,y);
+y3:=take_fraction(y3,x)-take_fraction(t,y)
+
+@ @<Exit to |found| if an eastward direction occurs at knot |p|@>=
+theta:=n_arg(x1,y1);
+if theta>=0 then if phi<=0 then if phi>=theta-one_eighty_deg then goto found;
+if theta<=0 then if phi>=0 then if phi<=theta+one_eighty_deg then goto found
+
+@ In this step we want to use the |crossing_point| routine to find the
+roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$.
+Several complications arise: If the quadratic equation has a double root,
+the curve never crosses zero, and |crossing_point| will find nothing;
+this case occurs iff $y_1y_3=y_2^2$ and $y_1y_2<0$. If the quadratic
+equation has simple roots, or only one root, we may have to negate it
+so that $B(y_1,y_2,y_3;t)$ crosses from positive to negative at its first root.
+And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is
+identically zero.
+
+@ @<Exit to |found| if the curve whose derivatives are specified by...@>=
+if x1<0 then if x2<0 then if x3<0 then goto done;
+if ab_vs_cd(y1,y3,y2,y2)=0 then
+ @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
+ either |goto found| or |goto done|@>;
+if y1<=0 then
+ if y1<0 then
+ begin y1:=-y1; y2:=-y2; y3:=-y3;
+ end
+ else if y2>0 then
+ begin y2:=-y2; y3:=-y3;
+ end;
+@<Check the places where $B(y_1,y_2,y_3;t)=0$ to see if
+ $B(x_1,x_2,x_3;t)\ge0$@>;
+done:
+
+@ The quadratic polynomial $B(y_1,y_2,y_3;t)$ begins |>=0| and has at most
+two roots, because we know that it isn't identically zero.
+
+It must be admitted that the |crossing_point| routine is not perfectly accurate;
+rounding errors might cause it to find a root when $y_1y_3>y_2^2$, or to
+miss the roots when $y_1y_3<y_2^2$. The rotation process is itself
+subject to rounding errors. Yet this code optimistically tries to
+do the right thing.
+
+@d we_found_it==begin tt:=(t+@'4000) div @'10000; goto found;
+ end
+
+@<Check the places where $B(y_1,y_2,y_3;t)=0$...@>=
+t:=crossing_point(y1,y2,y3);
+if t>fraction_one then goto done;
+y2:=t_of_the_way(y2)(y3);
+x1:=t_of_the_way(x1)(x2);
+x2:=t_of_the_way(x2)(x3);
+x1:=t_of_the_way(x1)(x2);
+if x1>=0 then we_found_it;
+if y2>0 then y2:=0;
+tt:=t; t:=crossing_point(0,-y2,-y3);
+if t>fraction_one then goto done;
+x1:=t_of_the_way(x1)(x2);
+x2:=t_of_the_way(x2)(x3);
+if t_of_the_way(x1)(x2)>=0 then
+ begin t:=t_of_the_way(tt)(fraction_one); we_found_it;
+ end
+
+@ @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
+ either |goto found| or |goto done|@>=
+begin if ab_vs_cd(y1,y2,0,0)<0 then
+ begin t:=make_fraction(y1,y1-y2);
+ x1:=t_of_the_way(x1)(x2);
+ x2:=t_of_the_way(x2)(x3);
+ if t_of_the_way(x1)(x2)>=0 then we_found_it;
+ end
+else if y3=0 then
+ if y1=0 then
+ @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|@>
+ else if x3>=0 then
+ begin tt:=unity; goto found;
+ end;
+goto done;
+end
+
+@ At this point we know that the derivative of |y(t)| is identically zero,
+and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of
+traveling east.
+
+@<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|...@>=
+begin t:=crossing_point(-x1,-x2,-x3);
+if t<=fraction_one then we_found_it;
+if ab_vs_cd(x1,x3,x2,x2)<=0 then
+ begin t:=make_fraction(x1,x1-x2); we_found_it;
+ end;
+end
+
+@ The intersection of two cubics can be found by an interesting variant
+of the general bisection scheme described in the introduction to |make_moves|.\
+Given $w(t)=B(w_0,w_1,w_2,w_3;t)$ and $z(t)=B(z_0,z_1,z_2,z_3;t)$,
+we wish to find a pair of times $(t_1,t_2)$ such that $w(t_1)=z(t_2)$,
+if an intersection exists. First we find the smallest rectangle that
+encloses the points $\{w_0,w_1,w_2,w_3\}$ and check that it overlaps
+the smallest rectangle that encloses
+$\{z_0,z_1,z_2,z_3\}$; if not, the cubics certainly don't intersect.
+But if the rectangles do overlap, we bisect the intervals, getting
+new cubics $w'$ and~$w''$, $z'$~and~$z''$; the intersection routine first
+tries for an intersection between $w'$ and~$z'$, then (if unsuccessful)
+between $w'$ and~$z''$, then (if still unsuccessful) between $w''$ and~$z'$,
+finally (if thrice unsuccessful) between $w''$ and~$z''$. After $l$~successful
+levels of bisection we will have determined the intersection times $t_1$
+and~$t_2$ to $l$~bits of accuracy.
+
+\def\submin{_{\rm min}} \def\submax{_{\rm max}}
+As before, it is better to work with the numbers $W_k=2^l(w_k-w_{k-1})$
+and $Z_k=2^l(z_k-z_{k-1})$ rather than the coefficients $w_k$ and $z_k$
+themselves. We also need one other quantity, $\Delta=2^l(w_0-z_0)$,
+to determine when the enclosing rectangles overlap. Here's why:
+The $x$~coordinates of~$w(t)$ are between $u\submin$ and $u\submax$,
+and the $x$~coordinates of~$z(t)$ are between $x\submin$ and $x\submax$,
+if we write $w_k=(u_k,v_k)$ and $z_k=(x_k,y_k)$ and $u\submin=
+\min(u_0,u_1,u_2,u_3)$, etc. These intervals of $x$~coordinates
+overlap if and only if $u\submin\L x\submax$ and
+$x\submin\L u\submax$. Letting
+$$U\submin=\min(0,U_1,U_1+U_2,U_1+U_2+U_3),\;
+ U\submax=\max(0,U_1,U_1+U_2,U_1+U_2+U_3),$$
+we have $2^lu\submin=2^lu_0+U\submin$, etc.; the condition for overlap
+reduces to
+$$X\submin-U\submax\L 2^l(u_0-x_0)\L X\submax-U\submin.$$
+Thus we want to maintain the quantity $2^l(u_0-x_0)$; similarly,
+the quantity $2^l(v_0-y_0)$ accounts for the $y$~coordinates. The
+coordinates of $\Delta=2^l(w_0-z_0)$ must stay bounded as $l$ increases,
+because of the overlap condition; i.e., we know that $X\submin$,
+$X\submax$, and their relatives are bounded, hence $X\submax-
+U\submin$ and $X\submin-U\submax$ are bounded.
+
+@ Incidentally, if the given cubics intersect more than once, the process
+just sketched will not necessarily find the lexicographically smallest pair
+$(t_1,t_2)$. The solution actually obtained will be smallest in ``shuffled
+order''; i.e., if $t_1=(.a_1a_2\ldots a_{16})_2$ and
+$t_2=(.b_1b_2\ldots b_{16})_2$, then we will minimize
+$a_1b_1a_2b_2\ldots a_{16}b_{16}$, not
+$a_1a_2\ldots a_{16}b_1b_2\ldots b_{16}$.
+Shuffled order agrees with lexicographic order if all pairs of solutions
+$(t_1,t_2)$ and $(t_1',t_2')$ have the property that $t_1<t_1'$ iff
+$t_2<t_2'$; but in general, lexicographic order can be quite different,
+and the bisection algorithm would be substantially less efficient if it were
+constrained by lexicographic order.
+
+For example, suppose that an overlap has been found for $l=3$ and
+$(t_1,t_2)= (.101,.011)$ in binary, but that no overlap is produced by
+either of the alternatives $(.1010,.0110)$, $(.1010,.0111)$ at level~4.
+Then there is probably an intersection in one of the subintervals
+$(.1011,.011x)$; but lexicographic order would require us to explore
+$(.1010,.1xxx)$ and $(.1011,.00xx)$ and $(.1011,.010x)$ first. We wouldn't
+want to store all of the subdivision data for the second path, so the
+subdivisions would have to be regenerated many times. Such inefficiencies
+would be associated with every `1' in the binary representation of~$t_1$.
+
+@ The subdivision process introduces rounding errors, hence we need to
+make a more liberal test for overlap. It is not hard to show that the
+computed values of $U_i$ differ from the truth by at most~$l$, on
+level~$l$, hence $U\submin$ and $U\submax$ will be at most $3l$ in error.
+If $\beta$ is an upper bound on the absolute error in the computed
+components of $\Delta=(|delx|,|dely|)$ on level~$l$, we will replace
+the test `$X\submin-U\submax\L|delx|$' by the more liberal test
+`$X\submin-U\submax\L|delx|+|tol|$', where $|tol|=6l+\beta$.
+
+More accuracy is obtained if we try the algorithm first with |tol=0|;
+the more liberal tolerance is used only if an exact approach fails.
+It is convenient to do this double-take by letting `3' in the preceding
+paragraph be a parameter, which is first 0, then 3.
+
+@<Glob...@>=
+@!tol_step:0..6; {either 0 or 3, usually}
+
+@ We shall use an explicit stack to implement the recursive bisection
+method described above. In fact, the |bisect_stack| array is available for
+this purpose. It will contain numerous 5-word packets like
+$(U_1,U_2,U_3,U\submin,U\submax)$, as well as 20-word packets comprising
+the 5-word packets for $U$, $V$, $X$, and~$Y$.
+
+The following macros define the allocation of stack positions to
+the quantities needed for bisection-intersection.
+
+@d stack_1(#)==bisect_stack[#] {$U_1$, $V_1$, $X_1$, or $Y_1$}
+@d stack_2(#)==bisect_stack[#+1] {$U_2$, $V_2$, $X_2$, or $Y_2$}
+@d stack_3(#)==bisect_stack[#+2] {$U_3$, $V_3$, $X_3$, or $Y_3$}
+@d stack_min(#)==bisect_stack[#+3]
+ {$U\submin$, $V\submin$, $X\submin$, or $Y\submin$}
+@d stack_max(#)==bisect_stack[#+4]
+ {$U\submax$, $V\submax$, $X\submax$, or $Y\submax$}
+@d int_packets=20 {number of words to represent $U_k$, $V_k$, $X_k$, and $Y_k$}
+@#
+@d u_packet(#)==#-5
+@d v_packet(#)==#-10
+@d x_packet(#)==#-15
+@d y_packet(#)==#-20
+@d l_packets==bisect_ptr-int_packets
+@d r_packets==bisect_ptr
+@d ul_packet==u_packet(l_packets) {base of $U'_k$ variables}
+@d vl_packet==v_packet(l_packets) {base of $V'_k$ variables}
+@d xl_packet==x_packet(l_packets) {base of $X'_k$ variables}
+@d yl_packet==y_packet(l_packets) {base of $Y'_k$ variables}
+@d ur_packet==u_packet(r_packets) {base of $U''_k$ variables}
+@d vr_packet==v_packet(r_packets) {base of $V''_k$ variables}
+@d xr_packet==x_packet(r_packets) {base of $X''_k$ variables}
+@d yr_packet==y_packet(r_packets) {base of $Y''_k$ variables}
+@#
+@d u1l==stack_1(ul_packet) {$U'_1$}
+@d u2l==stack_2(ul_packet) {$U'_2$}
+@d u3l==stack_3(ul_packet) {$U'_3$}
+@d v1l==stack_1(vl_packet) {$V'_1$}
+@d v2l==stack_2(vl_packet) {$V'_2$}
+@d v3l==stack_3(vl_packet) {$V'_3$}
+@d x1l==stack_1(xl_packet) {$X'_1$}
+@d x2l==stack_2(xl_packet) {$X'_2$}
+@d x3l==stack_3(xl_packet) {$X'_3$}
+@d y1l==stack_1(yl_packet) {$Y'_1$}
+@d y2l==stack_2(yl_packet) {$Y'_2$}
+@d y3l==stack_3(yl_packet) {$Y'_3$}
+@d u1r==stack_1(ur_packet) {$U''_1$}
+@d u2r==stack_2(ur_packet) {$U''_2$}
+@d u3r==stack_3(ur_packet) {$U''_3$}
+@d v1r==stack_1(vr_packet) {$V''_1$}
+@d v2r==stack_2(vr_packet) {$V''_2$}
+@d v3r==stack_3(vr_packet) {$V''_3$}
+@d x1r==stack_1(xr_packet) {$X''_1$}
+@d x2r==stack_2(xr_packet) {$X''_2$}
+@d x3r==stack_3(xr_packet) {$X''_3$}
+@d y1r==stack_1(yr_packet) {$Y''_1$}
+@d y2r==stack_2(yr_packet) {$Y''_2$}
+@d y3r==stack_3(yr_packet) {$Y''_3$}
+@#
+@d stack_dx==bisect_stack[bisect_ptr] {stacked value of |delx|}
+@d stack_dy==bisect_stack[bisect_ptr+1] {stacked value of |dely|}
+@d stack_tol==bisect_stack[bisect_ptr+2] {stacked value of |tol|}
+@d stack_uv==bisect_stack[bisect_ptr+3] {stacked value of |uv|}
+@d stack_xy==bisect_stack[bisect_ptr+4] {stacked value of |xy|}
+@d int_increment=int_packets+int_packets+5 {number of stack words per level}
+
+@<Check the ``constant''...@>=
+if int_packets+17*int_increment>bistack_size then bad:=32;
+
+@ Computation of the min and max is a tedious but fairly fast sequence of
+instructions; exactly four comparisons are made in each branch.
+
+@d set_min_max(#)==
+ if stack_1(#)<0 then
+ if stack_3(#)>=0 then
+ begin if stack_2(#)<0 then stack_min(#):=stack_1(#)+stack_2(#)
+ else stack_min(#):=stack_1(#);
+ stack_max(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_max(#)<0 then stack_max(#):=0;
+ end
+ else begin stack_min(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_min(#)>stack_1(#) then stack_min(#):=stack_1(#);
+ stack_max(#):=stack_1(#)+stack_2(#);
+ if stack_max(#)<0 then stack_max(#):=0;
+ end
+ else if stack_3(#)<=0 then
+ begin if stack_2(#)>0 then stack_max(#):=stack_1(#)+stack_2(#)
+ else stack_max(#):=stack_1(#);
+ stack_min(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_min(#)>0 then stack_min(#):=0;
+ end
+ else begin stack_max(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_max(#)<stack_1(#) then stack_max(#):=stack_1(#);
+ stack_min(#):=stack_1(#)+stack_2(#);
+ if stack_min(#)>0 then stack_min(#):=0;
+ end
+
+@ It's convenient to keep the current values of $l$, $t_1$, and $t_2$ in
+the integer form $2^l+2^lt_1$ and $2^l+2^lt_2$. The |cubic_intersection|
+routine uses global variables |cur_t| and |cur_tt| for this purpose;
+after successful completion, |cur_t| and |cur_tt| will contain |unity|
+plus the |scaled| values of $t_1$ and~$t_2$.
+
+The values of |cur_t| and |cur_tt| will be set to zero if |cubic_intersection|
+finds no intersection. The routine gives up and gives an approximate answer
+if it has backtracked
+more than 5000 times (otherwise there are cases where several minutes
+of fruitless computation would be possible).
+
+@d max_patience=5000
+
+@<Glob...@>=
+@!cur_t,@!cur_tt:integer; {controls and results of |cubic_intersection|}
+@!time_to_go:integer; {this many backtracks before giving up}
+@!max_t:integer; {maximum of $2^{l+1}$ so far achieved}
+
+@ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and
+$B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,link(p))|
+and |(pp,link(pp))|, respectively.
+
+@p procedure cubic_intersection(@!p,@!pp:pointer);
+label continue, not_found, exit;
+var @!q,@!qq:pointer; {|link(p)|, |link(pp)|}
+begin time_to_go:=max_patience; max_t:=2;
+@<Initialize for intersections at level zero@>;
+loop@+ begin continue:
+ if delx-tol<=stack_max(x_packet(xy))-stack_min(u_packet(uv)) then
+ if delx+tol>=stack_min(x_packet(xy))-stack_max(u_packet(uv)) then
+ if dely-tol<=stack_max(y_packet(xy))-stack_min(v_packet(uv)) then
+ if dely+tol>=stack_min(y_packet(xy))-stack_max(v_packet(uv)) then
+ begin if cur_t>=max_t then
+ begin if max_t=two then {we've done 17 bisections}
+ begin cur_t:=half(cur_t+1); cur_tt:=half(cur_tt+1); return;
+ end;
+ double(max_t); appr_t:=cur_t; appr_tt:=cur_tt;
+ end;
+ @<Subdivide for a new level of intersection@>;
+ goto continue;
+ end;
+ if time_to_go>0 then decr(time_to_go)
+ else begin while appr_t<unity do
+ begin double(appr_t); double(appr_tt);
+ end;
+ cur_t:=appr_t; cur_tt:=appr_tt; return;
+ end;
+ @<Advance to the next pair |(cur_t,cur_tt)|@>;
+ end;
+exit:end;
+
+@ The following variables are global, although they are used only by
+|cubic_intersection|, because it is necessary on some machines to
+split |cubic_intersection| up into two procedures.
+
+@<Glob...@>=
+@!delx,@!dely:integer; {the components of $\Delta=2^l(w_0-z_0)$}
+@!tol:integer; {bound on the uncertainty in the overlap test}
+@!uv,@!xy:0..bistack_size; {pointers to the current packets of interest}
+@!three_l:integer; {|tol_step| times the bisection level}
+@!appr_t,@!appr_tt:integer; {best approximations known to the answers}
+
+@ We shall assume that the coordinates are sufficiently non-extreme that
+integer overflow will not occur.
+@^overflow in arithmetic@>
+
+@<Initialize for intersections at level zero@>=
+q:=link(p); qq:=link(pp); bisect_ptr:=int_packets;@/
+u1r:=right_x(p)-x_coord(p); u2r:=left_x(q)-right_x(p);
+u3r:=x_coord(q)-left_x(q); set_min_max(ur_packet);@/
+v1r:=right_y(p)-y_coord(p); v2r:=left_y(q)-right_y(p);
+v3r:=y_coord(q)-left_y(q); set_min_max(vr_packet);@/
+x1r:=right_x(pp)-x_coord(pp); x2r:=left_x(qq)-right_x(pp);
+x3r:=x_coord(qq)-left_x(qq); set_min_max(xr_packet);@/
+y1r:=right_y(pp)-y_coord(pp); y2r:=left_y(qq)-right_y(pp);
+y3r:=y_coord(qq)-left_y(qq); set_min_max(yr_packet);@/
+delx:=x_coord(p)-x_coord(pp); dely:=y_coord(p)-y_coord(pp);@/
+tol:=0; uv:=r_packets; xy:=r_packets; three_l:=0; cur_t:=1; cur_tt:=1
+
+@ @<Subdivide for a new level of intersection@>=
+stack_dx:=delx; stack_dy:=dely; stack_tol:=tol; stack_uv:=uv; stack_xy:=xy;
+bisect_ptr:=bisect_ptr+int_increment;@/
+double(cur_t); double(cur_tt);@/
+u1l:=stack_1(u_packet(uv)); u3r:=stack_3(u_packet(uv));
+u2l:=half(u1l+stack_2(u_packet(uv)));
+u2r:=half(u3r+stack_2(u_packet(uv)));
+u3l:=half(u2l+u2r); u1r:=u3l;
+set_min_max(ul_packet); set_min_max(ur_packet);@/
+v1l:=stack_1(v_packet(uv)); v3r:=stack_3(v_packet(uv));
+v2l:=half(v1l+stack_2(v_packet(uv)));
+v2r:=half(v3r+stack_2(v_packet(uv)));
+v3l:=half(v2l+v2r); v1r:=v3l;
+set_min_max(vl_packet); set_min_max(vr_packet);@/
+x1l:=stack_1(x_packet(xy)); x3r:=stack_3(x_packet(xy));
+x2l:=half(x1l+stack_2(x_packet(xy)));
+x2r:=half(x3r+stack_2(x_packet(xy)));
+x3l:=half(x2l+x2r); x1r:=x3l;
+set_min_max(xl_packet); set_min_max(xr_packet);@/
+y1l:=stack_1(y_packet(xy)); y3r:=stack_3(y_packet(xy));
+y2l:=half(y1l+stack_2(y_packet(xy)));
+y2r:=half(y3r+stack_2(y_packet(xy)));
+y3l:=half(y2l+y2r); y1r:=y3l;
+set_min_max(yl_packet); set_min_max(yr_packet);@/
+uv:=l_packets; xy:=l_packets;
+double(delx); double(dely);@/
+tol:=tol-three_l+tol_step; double(tol); three_l:=three_l+tol_step
+
+@ @<Advance to the next pair |(cur_t,cur_tt)|@>=
+not_found: if odd(cur_tt) then
+ if odd(cur_t) then @<Descend to the previous level and |goto not_found|@>
+ else begin incr(cur_t);
+ delx:=delx+stack_1(u_packet(uv))+stack_2(u_packet(uv))
+ +stack_3(u_packet(uv));
+ dely:=dely+stack_1(v_packet(uv))+stack_2(v_packet(uv))
+ +stack_3(v_packet(uv));
+ uv:=uv+int_packets; {switch from |l_packets| to |r_packets|}
+ decr(cur_tt); xy:=xy-int_packets; {switch from |r_packets| to |l_packets|}
+ delx:=delx+stack_1(x_packet(xy))+stack_2(x_packet(xy))
+ +stack_3(x_packet(xy));
+ dely:=dely+stack_1(y_packet(xy))+stack_2(y_packet(xy))
+ +stack_3(y_packet(xy));
+ end
+else begin incr(cur_tt); tol:=tol+three_l;
+ delx:=delx-stack_1(x_packet(xy))-stack_2(x_packet(xy))
+ -stack_3(x_packet(xy));
+ dely:=dely-stack_1(y_packet(xy))-stack_2(y_packet(xy))
+ -stack_3(y_packet(xy));
+ xy:=xy+int_packets; {switch from |l_packets| to |r_packets|}
+ end
+
+@ @<Descend to the previous level...@>=
+begin cur_t:=half(cur_t); cur_tt:=half(cur_tt);
+if cur_t=0 then return;
+bisect_ptr:=bisect_ptr-int_increment; three_l:=three_l-tol_step;
+delx:=stack_dx; dely:=stack_dy; tol:=stack_tol; uv:=stack_uv; xy:=stack_xy;@/
+goto not_found;
+end
+
+@ The |path_intersection| procedure is much simpler.
+It invokes |cubic_intersection| in lexicographic order until finding a
+pair of cubics that intersect. The final intersection times are placed in
+|cur_t| and~|cur_tt|.
+
+@p procedure path_intersection(@!h,@!hh:pointer);
+label exit;
+var @!p,@!pp:pointer; {link registers that traverse the given paths}
+@!n,@!nn:integer; {integer parts of intersection times, minus |unity|}
+begin @<Change one-point paths into dead cycles@>;
+tol_step:=0;
+repeat n:=-unity; p:=h;
+ repeat if right_type(p)<>endpoint then
+ begin nn:=-unity; pp:=hh;
+ repeat if right_type(pp)<>endpoint then
+ begin cubic_intersection(p,pp);
+ if cur_t>0 then
+ begin cur_t:=cur_t+n; cur_tt:=cur_tt+nn; return;
+ end;
+ end;
+ nn:=nn+unity; pp:=link(pp);
+ until pp=hh;
+ end;
+ n:=n+unity; p:=link(p);
+ until p=h;
+tol_step:=tol_step+3;
+until tol_step>3;
+cur_t:=-unity; cur_tt:=-unity;
+exit:end;
+
+@ @<Change one-point paths...@>=
+if right_type(h)=endpoint then
+ begin right_x(h):=x_coord(h); left_x(h):=x_coord(h);
+ right_y(h):=y_coord(h); left_y(h):=y_coord(h); right_type(h):=explicit;
+ end;
+if right_type(hh)=endpoint then
+ begin right_x(hh):=x_coord(hh); left_x(hh):=x_coord(hh);
+ right_y(hh):=y_coord(hh); left_y(hh):=y_coord(hh); right_type(hh):=explicit;
+ end;
+
+@* \[27] Online graphic output.
+\MF\ displays images on the user's screen by means of a few primitive
+operations that are defined below. These operations have deliberately been
+kept simple so that they can be implemented without great difficulty on a
+wide variety of machines. Since \PASCAL\ has no traditional standards for
+graphic output, some system-dependent code needs to be written in order to
+support this aspect of \MF; but the necessary routines are usually quite
+easy to write.
+@^system dependencies@>
+
+In fact, there are exactly four such routines:
+
+\yskip\hang
+|init_screen| does whatever initialization is necessary to
+support the other operations; it is a boolean function that returns
+|false| if graphic output cannot be supported (e.g., if the other three
+routines have not been written, or if the user doesn't have the
+right kind of terminal).
+
+\yskip\hang
+|blank_rectangle| updates a buffer area in memory so that
+all pixels in a specified rectangle will be set to the background color.
+
+\yskip\hang
+|paint_row| assigns values to specified pixels in a row of
+the buffer just mentioned, based on ``transition'' indices explained below.
+
+\yskip\hang
+|update_screen| displays the current screen buffer; the
+effects of |blank_rectangle| and |paint_row| commands may or may not
+become visible until the next |update_screen| operation is performed.
+(Thus, |update_screen| is analogous to |update_terminal|.)
+
+\yskip\noindent
+The \PASCAL\ code here is a minimum version of |init_screen| and
+|update_screen|, usable on \MF\ installations that don't
+support screen output. If |init_screen| is changed to return |true|
+instead of |false|, the other routines will simply log the fact
+that they have been called; they won't really display anything.
+The standard test routines for \MF\ use this log information to check
+that \MF\ is working properly, but the |wlog| instructions should be
+removed from production versions of \MF.
+
+@p function init_screen:boolean;
+begin init_screen:=false;
+end;
+@#
+procedure update_screen; {will be called only if |init_screen| returns |true|}
+begin @!init wlog_ln('Calling UPDATESCREEN');@+tini {for testing only}
+end;
+
+@ The user's screen is assumed to be a rectangular area, |screen_width|
+pixels wide and |screen_depth| pixels deep. The pixel in the upper left
+corner is said to be in column~0 of row~0; the pixel in the lower right
+corner is said to be in column |screen_width-1| of row |screen_depth-1|.
+Notice that row numbers increase from top to bottom, contrary to \MF's
+other coordinates.
+
+Each pixel is assumed to have two states, referred to in this documentation
+as |black| and |white|. The background color is called |white| and the
+other color is called |black|; but any two distinct pixel values
+can actually be used. For example, the author developed \MF\ on a
+system for which |white| was black and |black| was bright green.
+
+@d white=0 {background pixels}
+@d black=1 {visible pixels}
+
+@<Types...@>=
+@!screen_row=0..screen_depth; {a row number on the screen}
+@!screen_col=0..screen_width; {a column number on the screen}
+@!trans_spec=array[screen_col] of screen_col; {a transition spec, see below}
+@!pixel_color=white..black; {specifies one of the two pixel values}
+
+@ We'll illustrate the |blank_rectangle| and |paint_row| operations by
+pretending to declare a screen buffer called |screen_pixel|. This code
+is actually commented out, but it does specify the intended effects.
+
+@<Glob...@>=
+@{@!screen_pixel:array[screen_row,screen_col] of pixel_color;@+@}
+
+@ The |blank_rectangle| routine simply whitens all pixels that lie in
+columns |left_col| through |right_col-1|, inclusive, of rows
+|top_row| through |bot_row-1|, inclusive, given four parameters that satisfy
+the relations
+$$\hbox{|0<=left_col<=right_col<=screen_width|,\quad
+ |0<=top_row<=bot_row<=screen_depth|.}$$
+If |left_col=right_col| or |top_row=bot_row|, nothing happens.
+
+The commented-out code in the following procedure is for illustrative
+purposes only.
+@^system dependencies@>
+
+@p procedure blank_rectangle(@!left_col,@!right_col:screen_col;
+ @!top_row,@!bot_row:screen_row);
+var @!r:screen_row;
+@!c:screen_col;
+begin @{@+for r:=top_row to bot_row-1 do
+ for c:=left_col to right_col-1 do
+ screen_pixel[r,c]:=white;@+@}@/
+@!init wlog_cr; {this will be done only after |init_screen=true|}
+wlog_ln('Calling BLANKRECTANGLE(',left_col:1,',',
+ right_col:1,',',top_row:1,',',bot_row:1,')');@+tini
+end;
+
+@ The real work of screen display is done by |paint_row|. But it's not
+hard work, because the operation affects only
+one of the screen rows, and it affects only a contiguous set of columns
+in that row. There are four parameters: |r|~(the row),
+|b|~(the initial color),
+|a|~(the array of transition specifications),
+and |n|~(the number of transitions). The elements of~|a| will satisfy
+$$0\L a[0]<a[1]<\cdots<a[n]\L |screen_width|;$$
+the value of |r| will satisfy |0<=r<screen_depth|; and |n| will be positive.
+
+The general idea is to paint blocks of pixels in alternate colors;
+the precise details are best conveyed by means of a \PASCAL\
+program (see the commented-out code below).
+@^system dependencies@>
+
+@p procedure paint_row(@!r:screen_row;@!b:pixel_color;var @!a:trans_spec;
+ @!n:screen_col);
+var @!k:screen_col; {an index into |a|}
+@!c:screen_col; {an index into |screen_pixel|}
+begin @{ k:=0; c:=a[0];
+repeat incr(k);
+ repeat screen_pixel[r,c]:=b; incr(c);
+ until c=a[k];
+ b:=black-b; {$|black|\swap|white|$}
+ until k=n;@+@}@/
+@!init wlog('Calling PAINTROW(',r:1,',',b:1,';');
+ {this is done only after |init_screen=true|}
+for k:=0 to n do
+ begin wlog(a[k]:1); if k<>n then wlog(',');
+ end;
+wlog_ln(')');@+tini
+end;
+
+@ The remainder of \MF's screen routines are system-independent calls
+on the four primitives just defined.
+
+First we have a global boolean variable that tells if |init_screen|
+has been called, and another one that tells if |init_screen| has
+given a |true| response.
+
+@<Glob...@>=
+@!screen_started:boolean; {have the screen primitives been initialized?}
+@!screen_OK:boolean; {is it legitimate to call |blank_rectangle|,
+ |paint_row|, and |update_screen|?}
+
+@ @d start_screen==begin if not screen_started then
+ begin screen_OK:=init_screen; screen_started:=true;
+ end;
+ end
+
+@<Set init...@>=
+screen_started:=false; screen_OK:=false;
+
+@ \MF\ provides the user with 16 ``window'' areas on the screen, in each
+of which it is possible to produce independent displays.
+
+It should be noted that \MF's windows aren't really independent
+``clickable'' entities in the sense of multi-window graphic workstations;
+\MF\ simply maps them into subsets of a single screen image that is
+controlled by |init_screen|, |blank_rectangle|, |paint_row|, and
+|update_screen| as described above. Implementations of \MF\ on a
+multi-window workstation probably therefore make use of only two
+windows in the other sense: one for the terminal output and another
+for the screen with \MF's 16 areas. Henceforth we shall
+use the term window only in \MF's sense.
+
+@<Types...@>=
+@!window_number=0..15;
+
+@ A user doesn't have to use any of the 16 windows. But when a window is
+``opened,'' it is allocated to a specific rectangular portion of the screen
+and to a specific rectangle with respect to \MF's coordinates. The relevant
+data is stored in global arrays |window_open|, |left_col|, |right_col|,
+|top_row|, |bot_row|, |m_window|, and |n_window|.
+
+The |window_open| array is boolean, and its significance is obvious. The
+|left_col|, \dots, |bot_row| arrays contain screen coordinates that
+can be used to blank the entire window with |blank_rectangle|. And the
+other two arrays just mentioned handle the conversion between
+actual coordinates and screen coordinates: \MF's pixel in column~$m$
+of row~$n$ will appear in screen column |m_window+m| and in screen row
+|n_window-n|, provided that these lie inside the boundaries of the window.
+
+Another array |window_time| holds the number of times this window has
+been updated.
+
+@<Glob...@>=
+@!window_open:array[window_number] of boolean;
+ {has this window been opened?}
+@!left_col:array[window_number] of screen_col;
+ {leftmost column position on screen}
+@!right_col:array[window_number] of screen_col;
+ {rightmost column position, plus~1}
+@!top_row:array[window_number] of screen_row;
+ {topmost row position on screen}
+@!bot_row:array[window_number] of screen_row;
+ {bottommost row position, plus~1}
+@!m_window:array[window_number] of integer;
+ {offset between user and screen columns}
+@!n_window:array[window_number] of integer;
+ {offset between user and screen rows}
+@!window_time:array[window_number] of integer;
+ {it has been updated this often}
+
+@ @<Set init...@>=
+for k:=0 to 15 do
+ begin window_open[k]:=false; window_time[k]:=0;
+ end;
+
+@ Opening a window isn't like opening a file, because you can open it
+as often as you like, and you never have to close it again. The idea is
+simply to define special points on the current screen display.
+
+Overlapping window specifications may cause complex effects that can
+be understood only by scrutinizing \MF's display algorithms; thus it
+has been left undefined in the \MF\ user manual, although the behavior
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+is in fact predictable.
+
+Here is a subroutine that implements the command `\&{openwindow}~|k|
+\&{from}~$(\\{r0},\\{c0})$ \&{to}~$(\\{r1},\\{c1})$ \&{at}~$(x,y)$'.
+
+@p procedure open_a_window(@!k:window_number;@!r0,@!c0,@!r1,@!c1:scaled;
+ @!x,@!y:scaled);
+var @!m,@!n:integer; {pixel coordinates}
+begin @<Adjust the coordinates |(r0,c0)| and |(r1,c1)| so that
+ they lie in the proper range@>;
+window_open[k]:=true; incr(window_time[k]);@/
+left_col[k]:=c0; right_col[k]:=c1; top_row[k]:=r0; bot_row[k]:=r1;@/
+@<Compute the offsets between screen coordinates and actual coordinates@>;
+start_screen;
+if screen_OK then
+ begin blank_rectangle(c0,c1,r0,r1); update_screen;
+ end;
+end;
+
+@ A window whose coordinates don't fit the existing screen size will be
+truncated until they do.
+
+@<Adjust the coordinates |(r0,c0)| and |(r1,c1)|...@>=
+if r0<0 then r0:=0@+else r0:=round_unscaled(r0);
+r1:=round_unscaled(r1);
+if r1>screen_depth then r1:=screen_depth;
+if r1<r0 then
+ if r0>screen_depth then r0:=r1@+else r1:=r0;
+if c0<0 then c0:=0@+else c0:=round_unscaled(c0);
+c1:=round_unscaled(c1);
+if c1>screen_width then c1:=screen_width;
+if c1<c0 then
+ if c0>screen_width then c0:=c1@+else c1:=c0
+
+@ Three sets of coordinates are rampant, and they must be kept straight!
+(i)~\MF's main coordinates refer to the edges between pixels. (ii)~\MF's
+pixel coordinates (within edge structures) say that the pixel bounded by
+$(m,n)$, $(m,n+1)$, $(m+1,n)$, and~$(m+1,n+1)$ is in pixel row number~$n$
+and pixel column number~$m$. (iii)~Screen coordinates, on the other hand,
+have rows numbered in increasing order from top to bottom, as mentioned
+above.
+@^coordinates, explained@>
+
+The program here first computes integers $m$ and $n$ such that
+pixel column~$m$ of pixel row~$n$ will be at the upper left corner
+of the window. Hence pixel column |m-c0| of pixel row |n+r0|
+will be at the upper left corner of the screen.
+
+@<Compute the offsets between screen coordinates and actual coordinates@>=
+m:=round_unscaled(x); n:=round_unscaled(y)-1;@/
+m_window[k]:=c0-m; n_window[k]:=r0+n
+
+@ Now here comes \MF's most complicated operation related to window
+display: Given the number~|k| of an open window, the pixels of positive
+weight in |cur_edges| will be shown as |black| in the window; all other
+pixels will be shown as |white|.
+
+@p procedure disp_edges(@!k:window_number);
+label done,found;
+var @!p,@!q:pointer; {for list manipulation}
+@!already_there:boolean; {is a previous incarnation in the window?}
+@!r:integer; {row number}
+@<Other local variables for |disp_edges|@>@;
+begin if screen_OK then
+ if left_col[k]<right_col[k] then if top_row[k]<bot_row[k] then
+ begin already_there:=false;
+ if last_window(cur_edges)=k then
+ if last_window_time(cur_edges)=window_time[k] then
+ already_there:=true;
+ if not already_there then
+ blank_rectangle(left_col[k],right_col[k],top_row[k],bot_row[k]);
+ @<Initialize for the display computations@>;
+ p:=link(cur_edges); r:=n_window[k]-(n_min(cur_edges)-zero_field);
+ while (p<>cur_edges)and(r>=top_row[k]) do
+ begin if r<bot_row[k] then
+ @<Display the pixels of edge row |p| in screen row |r|@>;
+ p:=link(p); decr(r);
+ end;
+ update_screen;
+ incr(window_time[k]);
+ last_window(cur_edges):=k; last_window_time(cur_edges):=window_time[k];
+ end;
+end;
+
+@ Since it takes some work to display a row, we try to avoid recomputation
+whenever we can.
+
+@<Display the pixels of edge row |p| in screen row |r|@>=
+begin if unsorted(p)>void then sort_edges(p)
+else if unsorted(p)=void then if already_there then goto done;
+unsorted(p):=void; {this time we'll paint, but maybe not next time}
+@<Set up the parameters needed for |paint_row|;
+ but |goto done| if no painting is needed after all@>;
+paint_row(r,b,row_transition,n);
+done: end
+
+@ The transition-specification parameter to |paint_row| is always the same
+array.
+
+@<Glob...@>=
+@!row_transition:trans_spec; {an array of |black|/|white| transitions}
+
+@ The job remaining is to go through the list |sorted(p)|, unpacking the
+|info| fields into |m| and weight, then making |black| the pixels whose
+accumulated weight~|w| is positive.
+
+@<Other local variables for |disp_edges|@>=
+@!n:screen_col; {the highest active index in |row_transition|}
+@!w,@!ww:integer; {old and new accumulated weights}
+@!b:pixel_color; {status of first pixel in the row transitions}
+@!m,@!mm:integer; {old and new screen column positions}
+@!d:integer; {edge-and-weight without |min_halfword| compensation}
+@!m_adjustment:integer; {conversion between edge and screen coordinates}
+@!right_edge:integer; {largest edge-and-weight that could affect the window}
+@!min_col:screen_col; {the smallest screen column number in the window}
+
+@ Some precomputed constants make the display calculations faster.
+
+@<Initialize for the display computations@>=
+m_adjustment:=m_window[k]-m_offset(cur_edges);@/
+right_edge:=8*(right_col[k]-m_adjustment);@/
+min_col:=left_col[k]
+
+@ @<Set up the parameters needed for |paint_row|...@>=
+n:=0; ww:=0; m:=-1; w:=0;
+q:=sorted(p); row_transition[0]:=min_col;
+loop@+ begin if q=sentinel then d:=right_edge
+ else d:=ho(info(q));
+ mm:=(d div 8)+m_adjustment;
+ if mm<>m then
+ begin @<Record a possible transition in column |m|@>;
+ m:=mm; w:=ww;
+ end;
+ if d>=right_edge then goto found;
+ ww:=ww+(d mod 8)-zero_w;
+ q:=link(q);
+ end;
+found:@<Wind up the |paint_row| parameter calculation by inserting the
+ final transition; |goto done| if no painting is needed@>;
+
+@ Now |m| is a screen column |<right_col[k]|.
+
+@<Record a possible transition in column |m|@>=
+if w<=0 then
+ begin if ww>0 then if m>min_col then
+ begin if n=0 then
+ if already_there then
+ begin b:=white; incr(n);
+ end
+ else b:=black
+ else incr(n);
+ row_transition[n]:=m;
+ end;
+ end
+else if ww<=0 then if m>min_col then
+ begin if n=0 then b:=black;
+ incr(n); row_transition[n]:=m;
+ end
+
+@ If the entire row is |white| in the window area, we can omit painting it
+when |already_there| is false, since it has already been blanked out in
+that case.
+
+When the following code is invoked, |row_transition[n]| will be
+strictly less than |right_col[k]|.
+
+@<Wind up the |paint_row|...@>=
+if already_there or(ww>0) then
+ begin if n=0 then
+ if ww>0 then b:=black
+ else b:=white;
+ incr(n); row_transition[n]:=right_col[k];
+ end
+else if n=0 then goto done
+
+@* \[28] Dynamic linear equations.
+\MF\ users define variables implicitly by stating equations that should be
+satisfied; the computer is supposed to be smart enough to solve those equations.
+And indeed, the computer tries valiantly to do so, by distinguishing five
+different types of numeric values:
+
+\smallskip\hang
+|type(p)=known| is the nice case, when |value(p)| is the |scaled| value
+of the variable whose address is~|p|.
+
+\smallskip\hang
+|type(p)=dependent| means that |value(p)| is not present, but |dep_list(p)|
+points to a {\sl dependency list\/} that expresses the value of variable~|p|
+as a |scaled| number plus a sum of independent variables with |fraction|
+coefficients.
+
+\smallskip\hang
+|type(p)=independent| means that |value(p)=64s+m|, where |s>0| is a ``serial
+number'' reflecting the time this variable was first used in an equation;
+also |0<=m<64|, and each dependent variable
+that refers to this one is actually referring to the future value of
+this variable times~$2^m$. (Usually |m=0|, but higher degrees of
+scaling are sometimes needed to keep the coefficients in dependency lists
+from getting too large. The value of~|m| will always be even.)
+
+\smallskip\hang
+|type(p)=numeric_type| means that variable |p| hasn't appeared in an
+equation before, but it has been explicitly declared to be numeric.
+
+\smallskip\hang
+|type(p)=undefined| means that variable |p| hasn't appeared before.
+
+\smallskip\noindent
+We have actually discussed these five types in the reverse order of their
+history during a computation: Once |known|, a variable never again
+becomes |dependent|; once |dependent|, it almost never again becomes
+|independent|; once |independent|, it never again becomes |numeric_type|;
+and once |numeric_type|, it never again becomes |undefined| (except
+of course when the user specifically decides to scrap the old value
+and start again). A backward step may, however, take place: Sometimes
+a |dependent| variable becomes |independent| again, when one of the
+independent variables it depends on is reverting to |undefined|.
+
+@d s_scale=64 {the serial numbers are multiplied by this factor}
+@d new_indep(#)== {create a new independent variable}
+ begin if serial_no>el_gordo-s_scale then
+ overflow("independent variables",serial_no div s_scale);
+@:METAFONT capacity exceeded independent variables}{\quad independent variables@>
+ type(#):=independent; serial_no:=serial_no+s_scale;
+ value(#):=serial_no;
+ end
+
+@<Glob...@>=
+@!serial_no:integer; {the most recent serial number, times |s_scale|}
+
+@ @<Make variable |q+s| newly independent@>=new_indep(q+s)
+
+@ But how are dependency lists represented? It's simple: The linear combination
+$\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If
+|q=dep_list(p)| points to this list, and if |k>0|, then |value(q)=
+@t$\alpha_1$@>| (which is a |fraction|); |info(q)| points to the location
+of $v_1$; and |link(p)| points to the dependency list
+$\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|,
+then |value(q)=@t$\beta$@>| (which is |scaled|) and |info(q)=null|.
+The independent variables $v_1$, \dots,~$v_k$ have been sorted so that
+they appear in decreasing order of their |value| fields (i.e., of
+their serial numbers). \ (It is convenient to use decreasing order,
+since |value(null)=0|. If the independent variables were not sorted by
+serial number but by some other criterion, such as their location in |mem|,
+the equation-solving mechanism would be too system-dependent, because
+the ordering can affect the computed results.)
+
+The |link| field in the node that contains the constant term $\beta$ is
+called the {\sl final link\/} of the dependency list. \MF\ maintains
+a doubly-linked master list of all dependency lists, in terms of a permanently
+allocated node
+in |mem| called |dep_head|. If there are no dependencies, we have
+|link(dep_head)=dep_head| and |prev_dep(dep_head)=dep_head|;
+otherwise |link(dep_head)| points to the first dependent variable, say~|p|,
+and |prev_dep(p)=dep_head|. We have |type(p)=dependent|, and |dep_list(p)|
+points to its dependency list. If the final link of that dependency list
+occurs in location~|q|, then |link(q)| points to the next dependent
+variable (say~|r|); and we have |prev_dep(r)=q|, etc.
+
+@d dep_list(#)==link(value_loc(#))
+ {half of the |value| field in a |dependent| variable}
+@d prev_dep(#)==info(value_loc(#))
+ {the other half; makes a doubly linked list}
+@d dep_node_size=2 {the number of words per dependency node}
+
+@<Initialize table entries...@>= serial_no:=0;
+link(dep_head):=dep_head; prev_dep(dep_head):=dep_head;
+info(dep_head):=null; dep_list(dep_head):=null;
+
+@ Actually the description above contains a little white lie. There's
+another kind of variable called |proto_dependent|, which is
+just like a |dependent| one except that the $\alpha$ coefficients
+in its dependency list are |scaled| instead of being fractions.
+Proto-dependency lists are mixed with dependency lists in the
+nodes reachable from |dep_head|.
+
+@ Here is a procedure that prints a dependency list in symbolic form.
+The second parameter should be either |dependent| or |proto_dependent|,
+to indicate the scaling of the coefficients.
+
+@<Declare subroutines for printing expressions@>=
+procedure print_dependency(@!p:pointer;@!t:small_number);
+label exit;
+var @!v:integer; {a coefficient}
+@!pp,@!q:pointer; {for list manipulation}
+begin pp:=p;
+loop@+ begin v:=abs(value(p)); q:=info(p);
+ if q=null then {the constant term}
+ begin if (v<>0)or(p=pp) then
+ begin if value(p)>0 then if p<>pp then print_char("+");
+ print_scaled(value(p));
+ end;
+ return;
+ end;
+ @<Print the coefficient, unless it's $\pm1.0$@>;
+ if type(q)<>independent then confusion("dep");
+@:this can't happen dep}{\quad dep@>
+ print_variable_name(q); v:=value(q) mod s_scale;
+ while v>0 do
+ begin print("*4"); v:=v-2;
+ end;
+ p:=link(p);
+ end;
+exit:end;
+
+@ @<Print the coefficient, unless it's $\pm1.0$@>=
+if value(p)<0 then print_char("-")
+else if p<>pp then print_char("+");
+if t=dependent then v:=round_fraction(v);
+if v<>unity then print_scaled(v)
+
+@ The maximum absolute value of a coefficient in a given dependency list
+is returned by the following simple function.
+
+@p function max_coef(@!p:pointer):fraction;
+var @!x:fraction; {the maximum so far}
+begin x:=0;
+while info(p)<>null do
+ begin if abs(value(p))>x then x:=abs(value(p));
+ p:=link(p);
+ end;
+max_coef:=x;
+end;
+
+@ One of the main operations needed on dependency lists is to add a multiple
+of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point
+to dependency lists and |f| is a fraction.
+
+If the coefficient of any independent variable becomes |coef_bound| or
+more, in absolute value, this procedure changes the type of that variable
+to `|independent_needing_fix|', and sets the global variable |fix_needed|
+to~|true|. The value of $|coef_bound|=\mu$ is chosen so that
+$\mu^2+\mu<8$; this means that the numbers we deal with won't
+get too large. (Instead of the ``optimum'' $\mu=(\sqrt{33}-1)/2\approx
+2.3723$, the safer value 7/3 is taken as the threshold.)
+
+The changes mentioned in the preceding paragraph are actually done only if
+the global variable |watch_coefs| is |true|. But it usually is; in fact,
+it is |false| only when \MF\ is making a dependency list that will soon
+be equated to zero.
+
+Several procedures that act on dependency lists, including |p_plus_fq|,
+set the global variable |dep_final| to the final (constant term) node of
+the dependency list that they produce.
+
+@d coef_bound==@'4525252525 {|fraction| approximation to 7/3}
+@d independent_needing_fix=0
+
+@<Glob...@>=
+@!fix_needed:boolean; {does at least one |independent| variable need scaling?}
+@!watch_coefs:boolean; {should we scale coefficients that exceed |coef_bound|?}
+@!dep_final:pointer; {location of the constant term and final link}
+
+@ @<Set init...@>=
+fix_needed:=false; watch_coefs:=true;
+
+@ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be
+set to |proto_dependent| if |p| is a proto-dependency list. In this
+case |f| will be |scaled|, not a |fraction|. Similarly, the fifth parameter~|tt|
+should be |proto_dependent| if |q| is a proto-dependency list.
+
+List |q| is unchanged by the operation; but list |p| is totally destroyed.
+
+The final link of the dependency list or proto-dependency list returned
+by |p_plus_fq| is the same as the original final link of~|p|. Indeed, the
+constant term of the result will be located in the same |mem| location
+as the original constant term of~|p|.
+
+Coefficients of the result are assumed to be zero if they are less than
+a certain threshold. This compensates for inevitable rounding errors,
+and tends to make more variables `|known|'. The threshold is approximately
+$10^{-5}$ in the case of normal dependency lists, $10^{-4}$ for
+proto-dependencies.
+
+@d fraction_threshold=2685 {a |fraction| coefficient less than this is zeroed}
+@d half_fraction_threshold=1342 {half of |fraction_threshold|}
+@d scaled_threshold=8 {a |scaled| coefficient less than this is zeroed}
+@d half_scaled_threshold=4 {half of |scaled_threshold|}
+
+@<Declare basic dependency-list subroutines@>=
+function p_plus_fq(@!p:pointer;@!f:integer;@!q:pointer;
+ @!t,@!tt:small_number):pointer;
+label done;
+var @!pp,@!qq:pointer; {|info(p)| and |info(q)|, respectively}
+@!r,@!s:pointer; {for list manipulation}
+@!threshold:integer; {defines a neighborhood of zero}
+@!v:integer; {temporary register}
+begin if t=dependent then threshold:=fraction_threshold
+else threshold:=scaled_threshold;
+r:=temp_head; pp:=info(p); qq:=info(q);
+loop@+ if pp=qq then
+ if pp=null then goto done
+ else @<Contribute a term from |p|, plus |f| times the
+ corresponding term from |q|@>
+ else if value(pp)<value(qq) then
+ @<Contribute a term from |q|, multiplied by~|f|@>
+ else begin link(r):=p; r:=p; p:=link(p); pp:=info(p);
+ end;
+done: if t=dependent then
+ value(p):=slow_add(value(p),take_fraction(value(q),f))
+else value(p):=slow_add(value(p),take_scaled(value(q),f));
+link(r):=p; dep_final:=p; p_plus_fq:=link(temp_head);
+end;
+
+@ @<Contribute a term from |p|, plus |f|...@>=
+begin if tt=dependent then v:=value(p)+take_fraction(f,value(q))
+else v:=value(p)+take_scaled(f,value(q));
+value(p):=v; s:=p; p:=link(p);
+if abs(v)<threshold then free_node(s,dep_node_size)
+else begin if abs(v)>=coef_bound then if watch_coefs then
+ begin type(qq):=independent_needing_fix; fix_needed:=true;
+ end;
+ link(r):=s; r:=s;
+ end;
+pp:=info(p); q:=link(q); qq:=info(q);
+end
+
+@ @<Contribute a term from |q|, multiplied by~|f|@>=
+begin if tt=dependent then v:=take_fraction(f,value(q))
+else v:=take_scaled(f,value(q));
+if abs(v)>half(threshold) then
+ begin s:=get_node(dep_node_size); info(s):=qq; value(s):=v;
+ if abs(v)>=coef_bound then if watch_coefs then
+ begin type(qq):=independent_needing_fix; fix_needed:=true;
+ end;
+ link(r):=s; r:=s;
+ end;
+q:=link(q); qq:=info(q);
+end
+
+@ It is convenient to have another subroutine for the special case
+of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are
+both of the same type~|t| (either |dependent| or |proto_dependent|).
+
+@p function p_plus_q(@!p:pointer;@!q:pointer;@!t:small_number):pointer;
+label done;
+var @!pp,@!qq:pointer; {|info(p)| and |info(q)|, respectively}
+@!r,@!s:pointer; {for list manipulation}
+@!threshold:integer; {defines a neighborhood of zero}
+@!v:integer; {temporary register}
+begin if t=dependent then threshold:=fraction_threshold
+else threshold:=scaled_threshold;
+r:=temp_head; pp:=info(p); qq:=info(q);
+loop@+ if pp=qq then
+ if pp=null then goto done
+ else @<Contribute a term from |p|, plus the
+ corresponding term from |q|@>
+ else if value(pp)<value(qq) then
+ begin s:=get_node(dep_node_size); info(s):=qq; value(s):=value(q);
+ q:=link(q); qq:=info(q); link(r):=s; r:=s;
+ end
+ else begin link(r):=p; r:=p; p:=link(p); pp:=info(p);
+ end;
+done: value(p):=slow_add(value(p),value(q));
+link(r):=p; dep_final:=p; p_plus_q:=link(temp_head);
+end;
+
+@ @<Contribute a term from |p|, plus the...@>=
+begin v:=value(p)+value(q);
+value(p):=v; s:=p; p:=link(p); pp:=info(p);
+if abs(v)<threshold then free_node(s,dep_node_size)
+else begin if abs(v)>=coef_bound then if watch_coefs then
+ begin type(qq):=independent_needing_fix; fix_needed:=true;
+ end;
+ link(r):=s; r:=s;
+ end;
+q:=link(q); qq:=info(q);
+end
+
+@ A somewhat simpler routine will multiply a dependency list
+by a given constant~|v|. The constant is either a |fraction| less than
+|fraction_one|, or it is |scaled|. In the latter case we might be forced to
+convert a dependency list to a proto-dependency list.
+Parameters |t0| and |t1| are the list types before and after;
+they should agree unless |t0=dependent| and |t1=proto_dependent|
+and |v_is_scaled=true|.
+
+@p function p_times_v(@!p:pointer;@!v:integer;
+ @!t0,@!t1:small_number;@!v_is_scaled:boolean):pointer;
+var @!r,@!s:pointer; {for list manipulation}
+@!w:integer; {tentative coefficient}
+@!threshold:integer;
+@!scaling_down:boolean;
+begin if t0<>t1 then scaling_down:=true@+else scaling_down:=not v_is_scaled;
+if t1=dependent then threshold:=half_fraction_threshold
+else threshold:=half_scaled_threshold;
+r:=temp_head;
+while info(p)<>null do
+ begin if scaling_down then w:=take_fraction(v,value(p))
+ else w:=take_scaled(v,value(p));
+ if abs(w)<=threshold then
+ begin s:=link(p); free_node(p,dep_node_size); p:=s;
+ end
+ else begin if abs(w)>=coef_bound then
+ begin fix_needed:=true; type(info(p)):=independent_needing_fix;
+ end;
+ link(r):=p; r:=p; value(p):=w; p:=link(p);
+ end;
+ end;
+link(r):=p;
+if v_is_scaled then value(p):=take_scaled(value(p),v)
+else value(p):=take_fraction(value(p),v);
+p_times_v:=link(temp_head);
+end;
+
+@ Similarly, we sometimes need to divide a dependency list
+by a given |scaled| constant.
+
+@<Declare basic dependency-list subroutines@>=
+function p_over_v(@!p:pointer;@!v:scaled;
+ @!t0,@!t1:small_number):pointer;
+var @!r,@!s:pointer; {for list manipulation}
+@!w:integer; {tentative coefficient}
+@!threshold:integer;
+@!scaling_down:boolean;
+begin if t0<>t1 then scaling_down:=true@+else scaling_down:=false;
+if t1=dependent then threshold:=half_fraction_threshold
+else threshold:=half_scaled_threshold;
+r:=temp_head;
+while info(p)<>null do
+ begin if scaling_down then
+ if abs(v)<@'2000000 then w:=make_scaled(value(p),v*@'10000)
+ else w:=make_scaled(round_fraction(value(p)),v)
+ else w:=make_scaled(value(p),v);
+ if abs(w)<=threshold then
+ begin s:=link(p); free_node(p,dep_node_size); p:=s;
+ end
+ else begin if abs(w)>=coef_bound then
+ begin fix_needed:=true; type(info(p)):=independent_needing_fix;
+ end;
+ link(r):=p; r:=p; value(p):=w; p:=link(p);
+ end;
+ end;
+link(r):=p; value(p):=make_scaled(value(p),v);
+p_over_v:=link(temp_head);
+end;
+
+@ Here's another utility routine for dependency lists. When an independent
+variable becomes dependent, we want to remove it from all existing
+dependencies. The |p_with_x_becoming_q| function computes the
+dependency list of~|p| after variable~|x| has been replaced by~|q|.
+
+This procedure has basically the same calling conventions as |p_plus_fq|:
+List~|q| is unchanged; list~|p| is destroyed; the constant node and the
+final link are inherited from~|p|; and the fourth parameter tells whether
+or not |p| is |proto_dependent|. However, the global variable |dep_final|
+is not altered if |x| does not occur in list~|p|.
+
+@p function p_with_x_becoming_q(@!p,@!x,@!q:pointer;@!t:small_number):pointer;
+var @!r,@!s:pointer; {for list manipulation}
+@!v:integer; {coefficient of |x|}
+@!sx:integer; {serial number of |x|}
+begin s:=p; r:=temp_head; sx:=value(x);
+while value(info(s))>sx do
+ begin r:=s; s:=link(s);
+ end;
+if info(s)<>x then p_with_x_becoming_q:=p
+else begin link(temp_head):=p; link(r):=link(s); v:=value(s);
+ free_node(s,dep_node_size);
+ p_with_x_becoming_q:=p_plus_fq(link(temp_head),v,q,t,dependent);
+ end;
+end;
+
+@ Here's a simple procedure that reports an error when a variable
+has just received a known value that's out of the required range.
+
+@<Declare basic dependency-list subroutines@>=
+procedure val_too_big(@!x:scaled);
+begin if internal[warning_check]>0 then
+ begin print_err("Value is too large ("); print_scaled(x); print_char(")");
+@.Value is too large@>
+ help4("The equation I just processed has given some variable")@/
+ ("a value of 4096 or more. Continue and I'll try to cope")@/
+ ("with that big value; but it might be dangerous.")@/
+ ("(Set warningcheck:=0 to suppress this message.)");
+ error;
+ end;
+end;
+
+@ When a dependent variable becomes known, the following routine
+removes its dependency list. Here |p| points to the variable, and
+|q| points to the dependency list (which is one node long).
+
+@<Declare basic dependency-list subroutines@>=
+procedure make_known(@!p,@!q:pointer);
+var @!t:dependent..proto_dependent; {the previous type}
+begin prev_dep(link(q)):=prev_dep(p);
+link(prev_dep(p)):=link(q); t:=type(p);
+type(p):=known; value(p):=value(q); free_node(q,dep_node_size);
+if abs(value(p))>=fraction_one then val_too_big(value(p));
+if internal[tracing_equations]>0 then if interesting(p) then
+ begin begin_diagnostic; print_nl("#### ");
+@:]]]\#\#\#\#_}{\.{\#\#\#\#}@>
+ print_variable_name(p); print_char("="); print_scaled(value(p));
+ end_diagnostic(false);
+ end;
+if cur_exp=p then if cur_type=t then
+ begin cur_type:=known; cur_exp:=value(p);
+ free_node(p,value_node_size);
+ end;
+end;
+
+@ The |fix_dependencies| routine is called into action when |fix_needed|
+has been triggered. The program keeps a list~|s| of independent variables
+whose coefficients must be divided by~4.
+
+In unusual cases, this fixup process might reduce one or more coefficients
+to zero, so that a variable will become known more or less by default.
+
+@<Declare basic dependency-list subroutines@>=
+procedure fix_dependencies;
+label done;
+var @!p,@!q,@!r,@!s,@!t:pointer; {list manipulation registers}
+@!x:pointer; {an independent variable}
+begin r:=link(dep_head); s:=null;
+while r<>dep_head do
+ begin t:=r;
+ @<Run through the dependency list for variable |t|, fixing
+ all nodes, and ending with final link~|q|@>;
+ r:=link(q);
+ if q=dep_list(t) then make_known(t,q);
+ end;
+while s<>null do
+ begin p:=link(s); x:=info(s); free_avail(s); s:=p;
+ type(x):=independent; value(x):=value(x)+2;
+ end;
+fix_needed:=false;
+end;
+
+@ @d independent_being_fixed=1 {this variable already appears in |s|}
+
+@<Run through the dependency list for variable |t|...@>=
+r:=value_loc(t); {|link(r)=dep_list(t)|}
+loop@+ begin q:=link(r); x:=info(q);
+ if x=null then goto done;
+ if type(x)<=independent_being_fixed then
+ begin if type(x)<independent_being_fixed then
+ begin p:=get_avail; link(p):=s; s:=p;
+ info(s):=x; type(x):=independent_being_fixed;
+ end;
+ value(q):=value(q) div 4;
+ if value(q)=0 then
+ begin link(r):=link(q); free_node(q,dep_node_size); q:=r;
+ end;
+ end;
+ r:=q;
+ end;
+done:
+
+@ The |new_dep| routine installs a dependency list~|p| into the value node~|q|,
+linking it into the list of all known dependencies. We assume that
+|dep_final| points to the final node of list~|p|.
+
+@p procedure new_dep(@!q,@!p:pointer);
+var @!r:pointer; {what used to be the first dependency}
+begin dep_list(q):=p; prev_dep(q):=dep_head;
+r:=link(dep_head); link(dep_final):=r; prev_dep(r):=dep_final;
+link(dep_head):=q;
+end;
+
+@ Here is one of the ways a dependency list gets started.
+The |const_dependency| routine produces a list that has nothing but
+a constant term.
+
+@p function const_dependency(@!v:scaled):pointer;
+begin dep_final:=get_node(dep_node_size);
+value(dep_final):=v; info(dep_final):=null;
+const_dependency:=dep_final;
+end;
+
+@ And here's a more interesting way to start a dependency list from scratch:
+The parameter to |single_dependency| is the location of an
+independent variable~|x|, and the result is the simple dependency list
+`|x+0|'.
+
+In the unlikely event that the given independent variable has been doubled so
+often that we can't refer to it with a nonzero coefficient,
+|single_dependency| returns the simple list `0'. This case can be
+recognized by testing that the returned list pointer is equal to
+|dep_final|.
+
+@p function single_dependency(@!p:pointer):pointer;
+var @!q:pointer; {the new dependency list}
+@!m:integer; {the number of doublings}
+begin m:=value(p) mod s_scale;
+if m>28 then single_dependency:=const_dependency(0)
+else begin q:=get_node(dep_node_size);
+ value(q):=two_to_the[28-m]; info(q):=p;@/
+ link(q):=const_dependency(0); single_dependency:=q;
+ end;
+end;
+
+@ We sometimes need to make an exact copy of a dependency list.
+
+@p function copy_dep_list(@!p:pointer):pointer;
+label done;
+var @!q:pointer; {the new dependency list}
+begin q:=get_node(dep_node_size); dep_final:=q;
+loop@+ begin info(dep_final):=info(p); value(dep_final):=value(p);
+ if info(dep_final)=null then goto done;
+ link(dep_final):=get_node(dep_node_size);
+ dep_final:=link(dep_final); p:=link(p);
+ end;
+done:copy_dep_list:=q;
+end;
+
+@ But how do variables normally become known? Ah, now we get to the heart of the
+equation-solving mechanism. The |linear_eq| procedure is given a |dependent|
+or |proto_dependent| list,~|p|, in which at least one independent variable
+appears. It equates this list to zero, by choosing an independent variable
+with the largest coefficient and making it dependent on the others. The
+newly dependent variable is eliminated from all current dependencies,
+thereby possibly making other dependent variables known.
+
+The given list |p| is, of course, totally destroyed by all this processing.
+
+@p procedure linear_eq(@!p:pointer;@!t:small_number);
+var @!q,@!r,@!s:pointer; {for link manipulation}
+@!x:pointer; {the variable that loses its independence}
+@!n:integer; {the number of times |x| had been halved}
+@!v:integer; {the coefficient of |x| in list |p|}
+@!prev_r:pointer; {lags one step behind |r|}
+@!final_node:pointer; {the constant term of the new dependency list}
+@!w:integer; {a tentative coefficient}
+begin @<Find a node |q| in list |p| whose coefficient |v| is largest@>;
+x:=info(q); n:=value(x) mod s_scale;@/
+@<Divide list |p| by |-v|, removing node |q|@>;
+if internal[tracing_equations]>0 then @<Display the new dependency@>;
+@<Simplify all existing dependencies by substituting for |x|@>;
+@<Change variable |x| from |independent| to |dependent| or |known|@>;
+if fix_needed then fix_dependencies;
+end;
+
+@ @<Find a node |q| in list |p| whose coefficient |v| is largest@>=
+q:=p; r:=link(p); v:=value(q);
+while info(r)<>null do
+ begin if abs(value(r))>abs(v) then
+ begin q:=r; v:=value(r);
+ end;
+ r:=link(r);
+ end
+
+@ Here we want to change the coefficients from |scaled| to |fraction|,
+except in the constant term. In the common case of a trivial equation
+like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=dependent|.
+
+@<Divide list |p| by |-v|, removing node |q|@>=
+s:=temp_head; link(s):=p; r:=p;
+repeat if r=q then
+ begin link(s):=link(r); free_node(r,dep_node_size);
+ end
+else begin w:=make_fraction(value(r),v);
+ if abs(w)<=half_fraction_threshold then
+ begin link(s):=link(r); free_node(r,dep_node_size);
+ end
+ else begin value(r):=-w; s:=r;
+ end;
+ end;
+r:=link(s);
+until info(r)=null;
+if t=proto_dependent then value(r):=-make_scaled(value(r),v)
+else if v<>-fraction_one then value(r):=-make_fraction(value(r),v);
+final_node:=r; p:=link(temp_head)
+
+@ @<Display the new dependency@>=
+if interesting(x) then
+ begin begin_diagnostic; print_nl("## "); print_variable_name(x);
+@:]]]\#\#_}{\.{\#\#}@>
+ w:=n;
+ while w>0 do
+ begin print("*4"); w:=w-2;
+ end;
+ print_char("="); print_dependency(p,dependent); end_diagnostic(false);
+ end
+
+@ @<Simplify all existing dependencies by substituting for |x|@>=
+prev_r:=dep_head; r:=link(dep_head);
+while r<>dep_head do
+ begin s:=dep_list(r); q:=p_with_x_becoming_q(s,x,p,type(r));
+ if info(q)=null then make_known(r,q)
+ else begin dep_list(r):=q;
+ repeat q:=link(q);
+ until info(q)=null;
+ prev_r:=q;
+ end;
+ r:=link(prev_r);
+ end
+
+@ @<Change variable |x| from |independent| to |dependent| or |known|@>=
+if n>0 then @<Divide list |p| by $2^n$@>;
+if info(p)=null then
+ begin type(x):=known;
+ value(x):=value(p);
+ if abs(value(x))>=fraction_one then val_too_big(value(x));
+ free_node(p,dep_node_size);
+ if cur_exp=x then if cur_type=independent then
+ begin cur_exp:=value(x); cur_type:=known;
+ free_node(x,value_node_size);
+ end;
+ end
+else begin type(x):=dependent; dep_final:=final_node; new_dep(x,p);
+ if cur_exp=x then if cur_type=independent then cur_type:=dependent;
+ end
+
+@ @<Divide list |p| by $2^n$@>=
+begin s:=temp_head; link(temp_head):=p; r:=p;
+repeat if n>30 then w:=0
+else w:=value(r) div two_to_the[n];
+if (abs(w)<=half_fraction_threshold)and(info(r)<>null) then
+ begin link(s):=link(r);
+ free_node(r,dep_node_size);
+ end
+else begin value(r):=w; s:=r;
+ end;
+r:=link(s);
+until info(s)=null;
+p:=link(temp_head);
+end
+
+@ The |check_mem| procedure, which is used only when \MF\ is being
+debugged, makes sure that the current dependency lists are well formed.
+
+@<Check the list of linear dependencies@>=
+q:=dep_head; p:=link(q);
+while p<>dep_head do
+ begin if prev_dep(p)<>q then
+ begin print_nl("Bad PREVDEP at "); print_int(p);
+@.Bad PREVDEP...@>
+ end;
+ p:=dep_list(p); r:=inf_val;
+ repeat if value(info(p))>=value(r) then
+ begin print_nl("Out of order at "); print_int(p);
+@.Out of order...@>
+ end;
+ r:=info(p); q:=p; p:=link(q);
+ until r=null;
+ end
+
+@* \[29] Dynamic nonlinear equations.
+Variables of numeric type are maintained by the general scheme of
+independent, dependent, and known values that we have just studied;
+and the components of pair and transform variables are handled in the
+same way. But \MF\ also has five other types of values: \&{boolean},
+\&{string}, \&{pen}, \&{path}, and \&{picture}; what about them?
+
+Equations are allowed between nonlinear quantities, but only in a
+simple form. Two variables that haven't yet been assigned values are
+either equal to each other, or they're not.
+
+Before a boolean variable has received a value, its type is |unknown_boolean|;
+similarly, there are variables whose type is |unknown_string|, |unknown_pen|,
+|unknown_path|, and |unknown_picture|. In such cases the value is either
+|null| (which means that no other variables are equivalent to this one), or
+it points to another variable of the same undefined type. The pointers in the
+latter case form a cycle of nodes, which we shall call a ``ring.''
+Rings of undefined variables may include capsules, which arise as
+intermediate results within expressions or as \&{expr} parameters to macros.
+
+When one member of a ring receives a value, the same value is given to
+all the other members. In the case of paths and pictures, this implies
+making separate copies of a potentially large data structure; users should
+restrain their enthusiasm for such generality, unless they have lots and
+lots of memory space.
+
+@ The following procedure is called when a capsule node is being
+added to a ring (e.g., when an unknown variable is mentioned in an expression).
+
+@p function new_ring_entry(@!p:pointer):pointer;
+var q:pointer; {the new capsule node}
+begin q:=get_node(value_node_size); name_type(q):=capsule;
+type(q):=type(p);
+if value(p)=null then value(q):=p@+else value(q):=value(p);
+value(p):=q;
+new_ring_entry:=q;
+end;
+
+@ Conversely, we might delete a capsule or a variable before it becomes known.
+The following procedure simply detaches a quantity from its ring,
+without recycling the storage.
+
+@<Declare the recycling subroutines@>=
+procedure ring_delete(@!p:pointer);
+var @!q:pointer;
+begin q:=value(p);
+if q<>null then if q<>p then
+ begin while value(q)<>p do q:=value(q);
+ value(q):=value(p);
+ end;
+end;
+
+@ Eventually there might be an equation that assigns values to all of the
+variables in a ring. The |nonlinear_eq| subroutine does the necessary
+propagation of values.
+
+If the parameter |flush_p| is |true|, node |p| itself needn't receive a
+value; it will soon be recycled.
+
+@p procedure nonlinear_eq(@!v:integer;@!p:pointer;@!flush_p:boolean);
+var @!t:small_number; {the type of ring |p|}
+@!q,@!r:pointer; {link manipulation registers}
+begin t:=type(p)-unknown_tag; q:=value(p);
+if flush_p then type(p):=vacuous@+else p:=q;
+repeat r:=value(q); type(q):=t;
+case t of
+boolean_type: value(q):=v;
+string_type: begin value(q):=v; add_str_ref(v);
+ end;
+pen_type: begin value(q):=v; add_pen_ref(v);
+ end;
+path_type: value(q):=copy_path(v);
+picture_type: value(q):=copy_edges(v);
+end; {there ain't no more cases}
+q:=r;
+until q=p;
+end;
+
+@ If two members of rings are equated, and if they have the same type,
+the |ring_merge| procedure is called on to make them equivalent.
+
+@p procedure ring_merge(@!p,@!q:pointer);
+label exit;
+var @!r:pointer; {traverses one list}
+begin r:=value(p);
+while r<>p do
+ begin if r=q then
+ begin @<Exclaim about a redundant equation@>;
+ return;
+ end;
+ r:=value(r);
+ end;
+r:=value(p); value(p):=value(q); value(q):=r;
+exit:end;
+
+@ @<Exclaim about a redundant equation@>=
+begin print_err("Redundant equation");@/
+@.Redundant equation@>
+help2("I already knew that this equation was true.")@/
+ ("But perhaps no harm has been done; let's continue.");@/
+put_get_error;
+end
+
+@* \[30] Introduction to the syntactic routines.
+Let's pause a moment now and try to look at the Big Picture.
+The \MF\ program consists of three main parts: syntactic routines,
+semantic routines, and output routines. The chief purpose of the
+syntactic routines is to deliver the user's input to the semantic routines,
+while parsing expressions and locating operators and operands. The
+semantic routines act as an interpreter responding to these operators,
+which may be regarded as commands. And the output routines are
+periodically called on to produce compact font descriptions that can be
+used for typesetting or for making interim proof drawings. We have
+discussed the basic data structures and many of the details of semantic
+operations, so we are good and ready to plunge into the part of \MF\ that
+actually controls the activities.
+
+Our current goal is to come to grips with the |get_next| procedure,
+which is the keystone of \MF's input mechanism. Each call of |get_next|
+sets the value of three variables |cur_cmd|, |cur_mod|, and |cur_sym|,
+representing the next input token.
+$$\vbox{\halign{#\hfil\cr
+ \hbox{|cur_cmd| denotes a command code from the long list of codes
+ given earlier;}\cr
+ \hbox{|cur_mod| denotes a modifier of the command code;}\cr
+ \hbox{|cur_sym| is the hash address of the symbolic token that was
+ just scanned,}\cr
+ \hbox{\qquad or zero in the case of a numeric or string
+ or capsule token.}\cr}}$$
+Underlying this external behavior of |get_next| is all the machinery
+necessary to convert from character files to tokens. At a given time we
+may be only partially finished with the reading of several files (for
+which \&{input} was specified), and partially finished with the expansion
+of some user-defined macros and/or some macro parameters, and partially
+finished reading some text that the user has inserted online,
+and so on. When reading a character file, the characters must be
+converted to tokens; comments and blank spaces must
+be removed, numeric and string tokens must be evaluated.
+
+To handle these situations, which might all be present simultaneously,
+\MF\ uses various stacks that hold information about the incomplete
+activities, and there is a finite state control for each level of the
+input mechanism. These stacks record the current state of an implicitly
+recursive process, but the |get_next| procedure is not recursive.
+
+@<Glob...@>=
+@!cur_cmd: eight_bits; {current command set by |get_next|}
+@!cur_mod: integer; {operand of current command}
+@!cur_sym: halfword; {hash address of current symbol}
+
+@ The |print_cmd_mod| routine prints a symbolic interpretation of a
+command code and its modifier.
+It consists of a rather tedious sequence of print
+commands, and most of it is essentially an inverse to the |primitive|
+routine that enters a \MF\ primitive into |hash| and |eqtb|. Therefore almost
+all of this procedure appears elsewhere in the program, together with the
+corresponding |primitive| calls.
+
+@<Declare the procedure called |print_cmd_mod|@>=
+procedure print_cmd_mod(@!c,@!m:integer);
+begin case c of
+@t\4@>@<Cases of |print_cmd_mod| for symbolic printing of primitives@>@/
+othercases print("[unknown command code!]")
+endcases;
+end;
+
+@ Here is a procedure that displays a given command in braces, in the
+user's transcript file.
+
+@d show_cur_cmd_mod==show_cmd_mod(cur_cmd,cur_mod)
+
+@p procedure show_cmd_mod(@!c,@!m:integer);
+begin begin_diagnostic; print_nl("{");
+print_cmd_mod(c,m); print_char("}");
+end_diagnostic(false);
+end;
+
+@* \[31] Input stacks and states.
+The state of \MF's input mechanism appears in the input stack, whose
+entries are records with five fields, called |index|, |start|, |loc|,
+|limit|, and |name|. The top element of this stack is maintained in a
+global variable for which no subscripting needs to be done; the other
+elements of the stack appear in an array. Hence the stack is declared thus:
+
+@<Types...@>=
+@!in_state_record = record
+ @!index_field: quarterword;
+ @!start_field,@!loc_field, @!limit_field, @!name_field: halfword;
+ end;
+
+@ @<Glob...@>=
+@!input_stack : array[0..stack_size] of in_state_record;
+@!input_ptr : 0..stack_size; {first unused location of |input_stack|}
+@!max_in_stack: 0..stack_size; {largest value of |input_ptr| when pushing}
+@!cur_input : in_state_record; {the ``top'' input state}
+
+@ We've already defined the special variable |@!loc==cur_input.loc_field|
+in our discussion of basic input-output routines. The other components of
+|cur_input| are defined in the same way:
+
+@d index==cur_input.index_field {reference for buffer information}
+@d start==cur_input.start_field {starting position in |buffer|}
+@d limit==cur_input.limit_field {end of current line in |buffer|}
+@d name==cur_input.name_field {name of the current file}
+
+@ Let's look more closely now at the five control variables
+(|index|,~|start|,~|loc|,~|limit|,~|name|),
+assuming that \MF\ is reading a line of characters that have been input
+from some file or from the user's terminal. There is an array called
+|buffer| that acts as a stack of all lines of characters that are
+currently being read from files, including all lines on subsidiary
+levels of the input stack that are not yet completed. \MF\ will return to
+the other lines when it is finished with the present input file.
+
+(Incidentally, on a machine with byte-oriented addressing, it would be
+appropriate to combine |buffer| with the |str_pool| array,
+letting the buffer entries grow downward from the top of the string pool
+and checking that these two tables don't bump into each other.)
+
+The line we are currently working on begins in position |start| of the
+buffer; the next character we are about to read is |buffer[loc]|; and
+|limit| is the location of the last character present. We always have
+|loc<=limit|. For convenience, |buffer[limit]| has been set to |"%"|, so
+that the end of a line is easily sensed.
+
+The |name| variable is a string number that designates the name of
+the current file, if we are reading a text file. It is 0 if we
+are reading from the terminal for normal input, or 1 if we are executing a
+\&{readstring} command, or 2 if we are reading a string that was
+moved into the buffer by \&{scantokens}.
+
+@ Additional information about the current line is available via the
+|index| variable, which counts how many lines of characters are present
+in the buffer below the current level. We have |index=0| when reading
+from the terminal and prompting the user for each line; then if the user types,
+e.g., `\.{input font}', we will have |index=1| while reading
+the file \.{font.mf}. However, it does not follow that |index| is the
+same as the input stack pointer, since many of the levels on the input
+stack may come from token lists.
+
+The global variable |in_open| is equal to the |index|
+value of the highest non-token-list level. Thus, the number of partially read
+lines in the buffer is |in_open+1|, and we have |in_open=index|
+when we are not reading a token list.
+
+If we are not currently reading from the terminal,
+we are reading from the file variable |input_file[index]|. We use
+the notation |terminal_input| as a convenient abbreviation for |name=0|,
+and |cur_file| as an abbreviation for |input_file[index]|.
+
+The global variable |line| contains the line number in the topmost
+open file, for use in error messages. If we are not reading from
+the terminal, |line_stack[index]| holds the line number for the
+enclosing level, so that |line| can be restored when the current
+file has been read.
+
+If more information about the input state is needed, it can be
+included in small arrays like those shown here. For example,
+the current page or segment number in the input file might be
+put into a variable |@!page|, maintained for enclosing levels in
+`\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip'
+by analogy with |line_stack|.
+@^system dependencies@>
+
+@d terminal_input==(name=0) {are we reading from the terminal?}
+@d cur_file==input_file[index] {the current |alpha_file| variable}
+
+@<Glob...@>=
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@!open_parens : 0..max_in_open; {the number of open text files}
+@!input_file : array[1..max_in_open] of alpha_file;
+@!line : integer; {current line number in the current source file}
+@!line_stack : array[1..max_in_open] of integer;
+
+@ However, all this discussion about input state really applies only to the
+case that we are inputting from a file. There is another important case,
+namely when we are currently getting input from a token list. In this case
+|index>max_in_open|, and the conventions about the other state variables
+are different:
+
+\yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
+the node that will be read next. If |loc=null|, the token list has been
+fully read.
+
+\yskip\hang|start| points to the first node of the token list; this node
+may or may not contain a reference count, depending on the type of token
+list involved.
+
+\yskip\hang|token_type|, which takes the place of |index| in the
+discussion above, is a code number that explains what kind of token list
+is being scanned.
+
+\yskip\hang|name| points to the |eqtb| address of the control sequence
+being expanded, if the current token list is a macro not defined by
+\&{vardef}. Macros defined by \&{vardef} have |name=null|; their name
+can be deduced by looking at their first two parameters.
+
+\yskip\hang|param_start|, which takes the place of |limit|, tells where
+the parameters of the current macro or loop text begin in the |param_stack|.
+
+\yskip\noindent The |token_type| can take several values, depending on
+where the current token list came from:
+
+\yskip
+\indent|forever_text|, if the token list being scanned is the body of
+a \&{forever} loop;
+
+\indent|loop_text|, if the token list being scanned is the body of
+a \&{for} or \&{forsuffixes} loop;
+
+\indent|parameter|, if a \&{text} or \&{suffix} parameter is being scanned;
+
+\indent|backed_up|, if the token list being scanned has been inserted as
+`to be read again'.
+
+\indent|inserted|, if the token list being scanned has been inserted as
+part of error recovery;
+
+\indent|macro|, if the expansion of a user-defined symbolic token is being
+scanned.
+
+\yskip\noindent
+The token list begins with a reference count if and only if |token_type=
+macro|.
+@^reference counts@>
+
+@d token_type==index {type of current token list}
+@d token_state==(index>max_in_open) {are we scanning a token list?}
+@d file_state==(index<=max_in_open) {are we scanning a file line?}
+@d param_start==limit {base of macro parameters in |param_stack|}
+@d forever_text=max_in_open+1 {|token_type| code for loop texts}
+@d loop_text=max_in_open+2 {|token_type| code for loop texts}
+@d parameter=max_in_open+3 {|token_type| code for parameter texts}
+@d backed_up=max_in_open+4 {|token_type| code for texts to be reread}
+@d inserted=max_in_open+5 {|token_type| code for inserted texts}
+@d macro=max_in_open+6 {|token_type| code for macro replacement texts}
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack grows at a different rate from the others.
+
+@<Glob...@>=
+@!param_stack:array [0..param_size] of pointer;
+ {token list pointers for parameters}
+@!param_ptr:0..param_size; {first unused entry in |param_stack|}
+@!max_param_stack:integer;
+ {largest value of |param_ptr|}
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \MF's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |file_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@<Glob...@>=
+@!file_ptr:0..stack_size; {shallowest level shown by |show_context|}
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@p procedure show_context; {prints where the scanner is}
+label done;
+var @!old_setting:0..max_selector; {saved |selector| setting}
+@<Local variables for formatting calculations@>@/
+begin file_ptr:=input_ptr; input_stack[file_ptr]:=cur_input;
+ {store current state}
+loop@+begin cur_input:=input_stack[file_ptr]; {enter into the context}
+ @<Display the current context@>;
+ if file_state then
+ if (name>2) or (file_ptr=0) then goto done;
+ decr(file_ptr);
+ end;
+done: cur_input:=input_stack[input_ptr]; {restore original state}
+end;
+
+@ @<Display the current context@>=
+if (file_ptr=input_ptr) or file_state or
+ (token_type<>backed_up) or (loc<>null) then
+ {we omit backed-up token lists that have already been read}
+ begin tally:=0; {get ready to count characters}
+ old_setting:=selector;
+ if file_state then
+ begin @<Print location of current line@>;
+ @<Pseudoprint the line@>;
+ end
+ else begin @<Print type of token list@>;
+ @<Pseudoprint the token list@>;
+ end;
+ selector:=old_setting; {stop pseudoprinting}
+ @<Print two lines using the tricky pseudoprinted information@>;
+ end
+
+@ This routine should be changed, if necessary, to give the best possible
+indication of where the current line resides in the input file.
+For example, on some systems it is best to print both a page and line number.
+@^system dependencies@>
+
+@<Print location of current line@>=
+if name<=1 then
+ if terminal_input and(file_ptr=0) then print_nl("<*>")
+ else print_nl("<insert>")
+else if name=2 then print_nl("<scantokens>")
+else begin print_nl("l."); print_int(line);
+ end;
+print_char(" ")
+
+@ @<Print type of token list@>=
+case token_type of
+forever_text: print_nl("<forever> ");
+loop_text: @<Print the current loop value@>;
+parameter: print_nl("<argument> ");
+backed_up: if loc=null then print_nl("<recently read> ")
+ else print_nl("<to be read again> ");
+inserted: print_nl("<inserted text> ");
+macro: begin print_ln;
+ if name<>null then slow_print(text(name))
+ else @<Print the name of a \&{vardef}'d macro@>;
+ print("->");
+ end;
+othercases print_nl("?") {this should never happen}
+@.?\relax@>
+endcases
+
+@ The parameter that corresponds to a loop text is either a token list
+(in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}).
+We'll discuss capsules later; for now, all we need to know is that
+the |link| field in a capsule parameter is |void| and that
+|print_exp(p,0)| displays the value of capsule~|p| in abbreviated form.
+
+@<Print the current loop value@>=
+begin print_nl("<for("); p:=param_stack[param_start];
+if p<>null then
+ if link(p)=void then print_exp(p,0) {we're in a \&{for} loop}
+ else show_token_list(p,null,20,tally);
+print(")> ");
+end
+
+@ The first two parameters of a macro defined by \&{vardef} will be token
+lists representing the macro's prefix and ``at point.'' By putting these
+together, we get the macro's full name.
+
+@<Print the name of a \&{vardef}'d macro@>=
+begin p:=param_stack[param_start];
+if p=null then show_token_list(param_stack[param_start+1],null,20,tally)
+else begin q:=p;
+ while link(q)<>null do q:=link(q);
+ link(q):=param_stack[param_start+1];
+ show_token_list(p,null,20,tally);
+ link(q):=null;
+ end;
+end
+
+@ Now it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \MF's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
+|k<trick_count|, otherwise character |k| is dropped. Initially we set
+|tally:=0| and |trick_count:=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k=first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
+where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
+descriptive information on line~1, and set |n:=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
+unless |n+m>error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+@<Local variables for formatting...@>=
+@!i:0..buf_size; {index into |buffer|}
+@!l:integer; {length of descriptive information on line 1}
+@!m:integer; {context information gathered for line 2}
+@!n:0..error_line; {length of line 1}
+@!p: integer; {starting or ending place in |trick_buf|}
+@!q: integer; {temporary index}
+
+@ The following code tells the print routines to gather
+the desired information.
+
+@d begin_pseudoprint==
+ begin l:=tally; tally:=0; selector:=pseudo;
+ trick_count:=1000000;
+ end
+@d set_trick_count==
+ begin first_count:=tally;
+ trick_count:=tally+1+error_line-half_error_line;
+ if trick_count<error_line then trick_count:=error_line;
+ end
+
+@ And the following code uses the information after it has been gathered.
+
+@<Print two lines using the tricky pseudoprinted information@>=
+if trick_count=1000000 then set_trick_count;
+ {|set_trick_count| must be performed}
+if tally<trick_count then m:=tally-first_count
+else m:=trick_count-first_count; {context on line 2}
+if l+first_count<=half_error_line then
+ begin p:=0; n:=l+first_count;
+ end
+else begin print("..."); p:=l+first_count-half_error_line+3;
+ n:=half_error_line;
+ end;
+for q:=p to first_count-1 do print_char(trick_buf[q mod error_line]);
+print_ln;
+for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
+if m+n<=error_line then p:=first_count+m else p:=first_count+(error_line-n-3);
+for q:=first_count to p-1 do print_char(trick_buf[q mod error_line]);
+if m+n>error_line then print("...")
+
+@ But the trick is distracting us from our current goal, which is to
+understand the input state. So let's concentrate on the data structures that
+are being pseudoprinted as we finish up the |show_context| procedure.
+
+@<Pseudoprint the line@>=
+begin_pseudoprint;
+if limit>0 then for i:=start to limit-1 do
+ begin if i=loc then set_trick_count;
+ print(buffer[i]);
+ end
+
+@ @<Pseudoprint the token list@>=
+begin_pseudoprint;
+if token_type<>macro then show_token_list(start,loc,100000,0)
+else show_macro(start,loc,100000)
+
+@ Here is the missing piece of |show_token_list| that is activated when the
+token beginning line~2 is about to be shown:
+
+@<Do magic computation@>=set_trick_count
+
+@* \[32] Maintaining the input stacks.
+The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@d push_input==@t@> {enter a new input level, save the old}
+ begin if input_ptr>max_in_stack then
+ begin max_in_stack:=input_ptr;
+ if input_ptr=stack_size then overflow("input stack size",stack_size);
+@:METAFONT capacity exceeded input stack size}{\quad input stack size@>
+ end;
+ input_stack[input_ptr]:=cur_input; {stack the record}
+ incr(input_ptr);
+ end
+
+@ And of course what goes up must come down.
+
+@d pop_input==@t@> {leave an input level, re-enter the old}
+ begin decr(input_ptr); cur_input:=input_stack[input_ptr];
+ end
+
+@ Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t=macro|, the calling routine should
+set |name|, reset~|loc|, and increase the macro's reference count.
+
+@d back_list(#)==begin_token_list(#,backed_up) {backs up a simple token list}
+
+@p procedure begin_token_list(@!p:pointer;@!t:quarterword);
+begin push_input; start:=p; token_type:=t;
+param_start:=param_ptr; loc:=p;
+end;
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input.
+@^inner loop@>
+
+@p procedure end_token_list; {leave a token-list input level}
+label done;
+var @!p:pointer; {temporary register}
+begin if token_type>=backed_up then {token list to be deleted}
+ if token_type<=inserted then
+ begin flush_token_list(start); goto done;
+ end
+ else delete_mac_ref(start); {update reference count}
+while param_ptr>param_start do {parameters must be flushed}
+ begin decr(param_ptr);
+ p:=param_stack[param_ptr];
+ if p<>null then
+ if link(p)=void then {it's an \&{expr} parameter}
+ begin recycle_value(p); free_node(p,value_node_size);
+ end
+ else flush_token_list(p); {it's a \&{suffix} or \&{text} parameter}
+ end;
+done: pop_input; check_interrupt;
+end;
+
+@ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent
+token by the |cur_tok| routine.
+@^inner loop@>
+
+@p @t\4@>@<Declare the procedure called |make_exp_copy|@>@;@/
+function cur_tok:pointer;
+var @!p:pointer; {a new token node}
+@!save_type:small_number; {|cur_type| to be restored}
+@!save_exp:integer; {|cur_exp| to be restored}
+begin if cur_sym=0 then
+ if cur_cmd=capsule_token then
+ begin save_type:=cur_type; save_exp:=cur_exp;
+ make_exp_copy(cur_mod); p:=stash_cur_exp; link(p):=null;
+ cur_type:=save_type; cur_exp:=save_exp;
+ end
+ else begin p:=get_node(token_node_size);
+ value(p):=cur_mod; name_type(p):=token;
+ if cur_cmd=numeric_token then type(p):=known
+ else type(p):=string_type;
+ end
+else begin fast_get_avail(p); info(p):=cur_sym;
+ end;
+cur_tok:=p;
+end;
+
+@ Sometimes \MF\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again.
+If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant.
+
+@p procedure back_input; {undoes one token of input}
+var @!p:pointer; {a token list of length one}
+begin p:=cur_tok;
+while token_state and(loc=null) do end_token_list; {conserve stack space}
+back_list(p);
+end;
+
+@ The |back_error| routine is used when we want to restore or replace an
+offending token just before issuing an error message. We disable interrupts
+during the call of |back_input| so that the help message won't be lost.
+
+@p procedure back_error; {back up one token and call |error|}
+begin OK_to_interrupt:=false; back_input; OK_to_interrupt:=true; error;
+end;
+@#
+procedure ins_error; {back up one inserted token and call |error|}
+begin OK_to_interrupt:=false; back_input; token_type:=inserted;
+OK_to_interrupt:=true; error;
+end;
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@p procedure begin_file_reading;
+begin if in_open=max_in_open then overflow("text input levels",max_in_open);
+@:METAFONT capacity exceeded text input levels}{\quad text input levels@>
+if first=buf_size then overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+incr(in_open); push_input; index:=in_open;
+line_stack[index]:=line; start:=first;
+name:=0; {|terminal_input| is now |true|}
+end;
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@p procedure end_file_reading;
+begin first:=start; line:=line_stack[index];
+if index<>in_open then confusion("endinput");
+@:this can't happen endinput}{\quad endinput@>
+if name>2 then a_close(cur_file); {forget it}
+pop_input; decr(in_open);
+end;
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@p procedure clear_for_error_prompt;
+begin while file_state and terminal_input and@|
+ (input_ptr>0)and(loc=limit) do end_file_reading;
+print_ln; clear_terminal;
+end;
+
+@ To get \MF's whole input mechanism going, we perform the following
+actions.
+
+@<Initialize the input routines@>=
+begin input_ptr:=0; max_in_stack:=0;
+in_open:=0; open_parens:=0; max_buf_stack:=0;
+param_ptr:=0; max_param_stack:=0;
+first:=1;
+start:=1; index:=0; line:=0; name:=0;
+force_eof:=false;
+if not init_terminal then goto final_end;
+limit:=last; first:=last+1; {|init_terminal| has set |loc| and |last|}
+end;
+
+@* \[33] Getting the next token.
+The heart of \MF's input mechanism is the |get_next| procedure, which
+we shall develop in the next few sections of the program. Perhaps we
+shouldn't actually call it the ``heart,'' however; it really acts as \MF's
+eyes and mouth, reading the source files and gobbling them up. And it also
+helps \MF\ to regurgitate stored token lists that are to be processed again.
+
+The main duty of |get_next| is to input one token and to set |cur_cmd|
+and |cur_mod| to that token's command code and modifier. Furthermore, if
+the input token is a symbolic token, that token's |hash| address
+is stored in |cur_sym|; otherwise |cur_sym| is set to zero.
+
+Underlying this simple description is a certain amount of complexity
+because of all the cases that need to be handled.
+However, the inner loop of |get_next| is reasonably short and fast.
+
+@ Before getting into |get_next|, we need to consider a mechanism by which
+\MF\ helps keep errors from propagating too far. Whenever the program goes
+into a mode where it keeps calling |get_next| repeatedly until a certain
+condition is met, it sets |scanner_status| to some value other than |normal|.
+Then if an input file ends, or if an `\&{outer}' symbol appears,
+an appropriate error recovery will be possible.
+
+The global variable |warning_info| helps in this error recovery by providing
+additional information. For example, |warning_info| might indicate the
+name of a macro whose replacement text is being scanned.
+
+@d normal=0 {|scanner_status| at ``quiet times''}
+@d skipping=1 {|scanner_status| when false conditional text is being skipped}
+@d flushing=2 {|scanner_status| when junk after a statement is being ignored}
+@d absorbing=3 {|scanner_status| when a \&{text} parameter is being scanned}
+@d var_defining=4 {|scanner_status| when a \&{vardef} is being scanned}
+@d op_defining=5 {|scanner_status| when a macro \&{def} is being scanned}
+@d loop_defining=6 {|scanner_status| when a \&{for} loop is being scanned}
+
+@<Glob...@>=
+@!scanner_status:normal..loop_defining; {are we scanning at high speed?}
+@!warning_info:integer; {if so, what else do we need to know,
+ in case an error occurs?}
+
+@ @<Initialize the input routines@>=
+scanner_status:=normal;
+
+@ The following subroutine
+is called when an `\&{outer}' symbolic token has been scanned or
+when the end of a file has been reached. These two cases are distinguished
+by |cur_sym|, which is zero at the end of a file.
+
+@p function check_outer_validity:boolean;
+var @!p:pointer; {points to inserted token list}
+begin if scanner_status=normal then check_outer_validity:=true
+else begin deletions_allowed:=false;
+ @<Back up an outer symbolic token so that it can be reread@>;
+ if scanner_status>skipping then
+ @<Tell the user what has run away and try to recover@>
+ else begin print_err("Incomplete if; all text was ignored after line ");
+@.Incomplete if...@>
+ print_int(warning_info);@/
+ help3("A forbidden `outer' token occurred in skipped text.")@/
+ ("This kind of error happens when you say `if...' and forget")@/
+ ("the matching `fi'. I've inserted a `fi'; this might work.");
+ if cur_sym=0 then help_line[2]:=@|
+ "The file ended while I was skipping conditional text.";
+ cur_sym:=frozen_fi; ins_error;
+ end;
+ deletions_allowed:=true; check_outer_validity:=false;
+ end;
+end;
+
+@ @<Back up an outer symbolic token so that it can be reread@>=
+if cur_sym<>0 then
+ begin p:=get_avail; info(p):=cur_sym;
+ back_list(p); {prepare to read the symbolic token again}
+ end
+
+@ @<Tell the user what has run away...@>=
+begin runaway; {print the definition-so-far}
+if cur_sym=0 then print_err("File ended")
+@.File ended while scanning...@>
+else begin print_err("Forbidden token found");
+@.Forbidden token found...@>
+ end;
+print(" while scanning ");
+help4("I suspect you have forgotten an `enddef',")@/
+("causing me to read past where you wanted me to stop.")@/
+("I'll try to recover; but if the error is serious,")@/
+("you'd better type `E' or `X' now and fix your file.");@/
+case scanner_status of
+@t\4@>@<Complete the error message,
+ and set |cur_sym| to a token that might help recover from the error@>@;
+end; {there are no other cases}
+ins_error;
+end
+
+@ As we consider various kinds of errors, it is also appropriate to
+change the first line of the help message just given; |help_line[3]|
+points to the string that might be changed.
+
+@<Complete the error message,...@>=
+flushing: begin print("to the end of the statement");
+ help_line[3]:="A previous error seems to have propagated,";
+ cur_sym:=frozen_semicolon;
+ end;
+absorbing: begin print("a text argument");
+ help_line[3]:="It seems that a right delimiter was left out,";
+ if warning_info=0 then cur_sym:=frozen_end_group
+ else begin cur_sym:=frozen_right_delimiter;
+ equiv(frozen_right_delimiter):=warning_info;
+ end;
+ end;
+var_defining, op_defining: begin print("the definition of ");
+ if scanner_status=op_defining then slow_print(text(warning_info))
+ else print_variable_name(warning_info);
+ cur_sym:=frozen_end_def;
+ end;
+loop_defining: begin print("the text of a "); slow_print(text(warning_info));
+ print(" loop");
+ help_line[3]:="I suspect you have forgotten an `endfor',";
+ cur_sym:=frozen_end_for;
+ end;
+
+@ The |runaway| procedure displays the first part of the text that occurred
+when \MF\ began its special |scanner_status|, if that text has been saved.
+
+@<Declare the procedure called |runaway|@>=
+procedure runaway;
+begin if scanner_status>flushing then
+ begin print_nl("Runaway ");
+ case scanner_status of
+ absorbing: print("text?");
+ var_defining,op_defining: print("definition?");
+ loop_defining: print("loop?");
+ end; {there are no other cases}
+ print_ln; show_token_list(link(hold_head),null,error_line-10,0);
+ end;
+end;
+
+@ We need to mention a procedure that may be called by |get_next|.
+
+@p procedure@?firm_up_the_line; forward;
+
+@ And now we're ready to take the plunge into |get_next| itself.
+
+@d switch=25 {a label in |get_next|}
+@d start_numeric_token=85 {another}
+@d start_decimal_token=86 {and another}
+@d fin_numeric_token=87
+ {and still another, although |goto| is considered harmful}
+
+@p procedure get_next; {sets |cur_cmd|, |cur_mod|, |cur_sym| to next token}
+@^inner loop@>
+label restart, {go here to get the next input token}
+ exit, {go here when the next input token has been got}
+ found, {go here when the end of a symbolic token has been found}
+ switch, {go here to branch on the class of an input character}
+ start_numeric_token,start_decimal_token,fin_numeric_token,done;
+ {go here at crucial stages when scanning a number}
+var @!k:0..buf_size; {an index into |buffer|}
+@!c:ASCII_code; {the current character in the buffer}
+@!class:ASCII_code; {its class number}
+@!n,@!f:integer; {registers for decimal-to-binary conversion}
+begin restart: cur_sym:=0;
+if file_state then
+@<Input from external file; |goto restart| if no input found,
+ or |return| if a non-symbolic token is found@>
+else @<Input from token list; |goto restart| if end of list or
+ if a parameter needs to be expanded,
+ or |return| if a non-symbolic token is found@>;
+@<Finish getting the symbolic token in |cur_sym|;
+ |goto restart| if it is illegal@>;
+exit:end;
+
+@ When a symbolic token is declared to be `\&{outer}', its command code
+is increased by |outer_tag|.
+@^inner loop@>
+
+@<Finish getting the symbolic token in |cur_sym|...@>=
+cur_cmd:=eq_type(cur_sym); cur_mod:=equiv(cur_sym);
+if cur_cmd>=outer_tag then
+ if check_outer_validity then cur_cmd:=cur_cmd-outer_tag
+ else goto restart
+
+@ A percent sign appears in |buffer[limit]|; this makes it unnecessary
+to have a special test for end-of-line.
+@^inner loop@>
+
+@<Input from external file;...@>=
+begin switch: c:=buffer[loc]; incr(loc); class:=char_class[c];
+case class of
+digit_class: goto start_numeric_token;
+period_class: begin class:=char_class[buffer[loc]];
+ if class>period_class then goto switch
+ else if class<period_class then {|class=digit_class|}
+ begin n:=0; goto start_decimal_token;
+ end;
+@:. }{\..\ token@>
+ end;
+space_class: goto switch;
+percent_class: begin @<Move to next line of file,
+ or |goto restart| if there is no next line@>;
+ check_interrupt;
+ goto switch;
+ end;
+string_class: @<Get a string token and |return|@>;
+isolated_classes: begin k:=loc-1; goto found;
+ end;
+invalid_class: @<Decry the invalid character and |goto restart|@>;
+othercases do_nothing {letters, etc.}
+endcases;@/
+k:=loc-1;
+while char_class[buffer[loc]]=class do incr(loc);
+goto found;
+start_numeric_token:@<Get the integer part |n| of a numeric token;
+ set |f:=0| and |goto fin_numeric_token| if there is no decimal point@>;
+start_decimal_token:@<Get the fraction part |f| of a numeric token@>;
+fin_numeric_token:@<Pack the numeric and fraction parts of a numeric token
+ and |return|@>;
+found: cur_sym:=id_lookup(k,loc-k);
+end
+
+@ We go to |restart| instead of to |switch|, because we might enter
+|token_state| after the error has been dealt with
+(cf.\ |clear_for_error_prompt|).
+
+@<Decry the invalid...@>=
+begin print_err("Text line contains an invalid character");
+@.Text line contains...@>
+help2("A funny symbol that I can't read has just been input.")@/
+("Continue, and I'll forget that it ever happened.");@/
+deletions_allowed:=false; error; deletions_allowed:=true;
+goto restart;
+end
+
+@ @<Get a string token and |return|@>=
+begin if buffer[loc]="""" then cur_mod:=""
+else begin k:=loc; buffer[limit+1]:="""";
+ repeat incr(loc);
+ until buffer[loc]="""";
+ if loc>limit then @<Decry the missing string delimiter and |goto restart|@>;
+ if (loc=k+1) and (length(buffer[k])=1) then cur_mod:=buffer[k]
+ else begin str_room(loc-k);
+ repeat append_char(buffer[k]); incr(k);
+ until k=loc;
+ cur_mod:=make_string;
+ end;
+ end;
+incr(loc); cur_cmd:=string_token; return;
+end
+
+@ We go to |restart| after this error message, not to |switch|,
+because the |clear_for_error_prompt| routine might have reinstated
+|token_state| after |error| has finished.
+
+@<Decry the missing string delimiter and |goto restart|@>=
+begin loc:=limit; {the next character to be read on this line will be |"%"|}
+print_err("Incomplete string token has been flushed");
+@.Incomplete string token...@>
+help3("Strings should finish on the same line as they began.")@/
+ ("I've deleted the partial string; you might want to")@/
+ ("insert another by typing, e.g., `I""new string""'.");@/
+deletions_allowed:=false; error; deletions_allowed:=true; goto restart;
+end
+
+@ @<Get the integer part |n| of a numeric token...@>=
+n:=c-"0";
+while char_class[buffer[loc]]=digit_class do
+ begin if n<4096 then n:=10*n+buffer[loc]-"0";
+ incr(loc);
+ end;
+if buffer[loc]="." then if char_class[buffer[loc+1]]=digit_class then goto done;
+f:=0; goto fin_numeric_token;
+done: incr(loc)
+
+@ @<Get the fraction part |f| of a numeric token@>=
+k:=0;
+repeat if k<17 then {digits for |k>=17| cannot affect the result}
+ begin dig[k]:=buffer[loc]-"0"; incr(k);
+ end;
+incr(loc);
+until char_class[buffer[loc]]<>digit_class;
+f:=round_decimals(k);
+if f=unity then
+ begin incr(n); f:=0;
+ end
+
+@ @<Pack the numeric and fraction parts of a numeric token and |return|@>=
+if n<4096 then cur_mod:=n*unity+f
+else begin print_err("Enormous number has been reduced");
+@.Enormous number...@>
+ help2("I can't handle numbers bigger than about 4095.99998;")@/
+ ("so I've changed your constant to that maximum amount.");@/
+ deletions_allowed:=false; error; deletions_allowed:=true;
+ cur_mod:=@'1777777777;
+ end;
+cur_cmd:=numeric_token; return
+
+@ Let's consider now what happens when |get_next| is looking at a token list.
+@^inner loop@>
+
+@<Input from token list;...@>=
+if loc>=hi_mem_min then {one-word token}
+ begin cur_sym:=info(loc); loc:=link(loc); {move to next}
+ if cur_sym>=expr_base then
+ if cur_sym>=suffix_base then
+ @<Insert a suffix or text parameter and |goto restart|@>
+ else begin cur_cmd:=capsule_token;
+ cur_mod:=param_stack[param_start+cur_sym-(expr_base)];
+ cur_sym:=0; return;
+ end;
+ end
+else if loc>null then
+ @<Get a stored numeric or string or capsule token and |return|@>
+else begin {we are done with this token list}
+ end_token_list; goto restart; {resume previous level}
+ end
+
+@ @<Insert a suffix or text parameter...@>=
+begin if cur_sym>=text_base then cur_sym:=cur_sym-param_size;
+ {|param_size=text_base-suffix_base|}
+begin_token_list(param_stack[param_start+cur_sym-(suffix_base)],parameter);
+goto restart;
+end
+
+@ @<Get a stored numeric or string or capsule token...@>=
+begin if name_type(loc)=token then
+ begin cur_mod:=value(loc);
+ if type(loc)=known then cur_cmd:=numeric_token
+ else begin cur_cmd:=string_token; add_str_ref(cur_mod);
+ end;
+ end
+else begin cur_mod:=loc; cur_cmd:=capsule_token;
+ end;
+loc:=link(loc); return;
+end
+
+@ All of the easy branches of |get_next| have now been taken care of.
+There is one more branch.
+
+@<Move to next line of file, or |goto restart|...@>=
+if name>2 then @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>
+else begin if input_ptr>0 then
+ {text was inserted during error recovery or by \&{scantokens}}
+ begin end_file_reading; goto restart; {resume previous level}
+ end;
+ if selector<log_only then open_log_file;
+ if interaction>nonstop_mode then
+ begin if limit=start then {previous line was empty}
+ print_nl("(Please type a command or say `end')");
+@.Please type...@>
+ print_ln; first:=start;
+ prompt_input("*"); {input on-line into |buffer|}
+@.*\relax@>
+ limit:=last; buffer[limit]:="%";
+ first:=limit+1; loc:=start;
+ end
+ else fatal_error("*** (job aborted, no legal end found)");
+@.job aborted@>
+ {nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input}
+ end
+
+@ The global variable |force_eof| is normally |false|; it is set |true|
+by an \&{endinput} command.
+
+@<Glob...@>=
+@!force_eof:boolean; {should the next \&{input} be aborted early?}
+
+@ @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else force_eof:=true;
+ end;
+if force_eof then
+ begin print_char(")"); decr(open_parens);
+ update_terminal; {show user that file has been read}
+ force_eof:=false;
+ end_file_reading; {resume previous level}
+ if check_outer_validity then goto restart@+else goto restart;
+ end;
+buffer[limit]:="%"; first:=limit+1; loc:=start; {ready to read}
+end
+
+@ If the user has set the |pausing| parameter to some positive value,
+and if nonstop mode has not been selected, each line of input is displayed
+on the terminal and the transcript file, followed by `\.{=>}'.
+\MF\ waits for a response. If the response is null (i.e., if nothing is
+typed except perhaps a few blank spaces), the original
+line is accepted as it stands; otherwise the line typed is
+used instead of the line in the file.
+
+@p procedure firm_up_the_line;
+var @!k:0..buf_size; {an index into |buffer|}
+begin limit:=last;
+if internal[pausing]>0 then if interaction>nonstop_mode then
+ begin wake_up_terminal; print_ln;
+ if start<limit then for k:=start to limit-1 do print(buffer[k]);
+ first:=limit; prompt_input("=>"); {wait for user response}
+@.=>@>
+ if last>first then
+ begin for k:=first to last-1 do {move line down in buffer}
+ buffer[k+start-first]:=buffer[k];
+ limit:=start+last-first;
+ end;
+ end;
+end;
+
+@* \[34] Scanning macro definitions.
+\MF\ has a variety of ways to tuck tokens away into token lists for later
+use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.;
+repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}.
+All such operations are handled by the routines in this part of the program.
+
+The modifier part of each command code is zero for the ``ending delimiters''
+like \&{enddef} and \&{endfor}.
+
+@d start_def=1 {command modifier for \&{def}}
+@d var_def=2 {command modifier for \&{vardef}}
+@d end_def=0 {command modifier for \&{enddef}}
+@d start_forever=1 {command modifier for \&{forever}}
+@d end_for=0 {command modifier for \&{endfor}}
+
+@<Put each...@>=
+primitive("def",macro_def,start_def);@/
+@!@:def_}{\&{def} primitive@>
+primitive("vardef",macro_def,var_def);@/
+@!@:var_def_}{\&{vardef} primitive@>
+primitive("primarydef",macro_def,secondary_primary_macro);@/
+@!@:primary_def_}{\&{primarydef} primitive@>
+primitive("secondarydef",macro_def,tertiary_secondary_macro);@/
+@!@:secondary_def_}{\&{secondarydef} primitive@>
+primitive("tertiarydef",macro_def,expression_tertiary_macro);@/
+@!@:tertiary_def_}{\&{tertiarydef} primitive@>
+primitive("enddef",macro_def,end_def); eqtb[frozen_end_def]:=eqtb[cur_sym];@/
+@!@:end_def_}{\&{enddef} primitive@>
+@#
+primitive("for",iteration,expr_base);@/
+@!@:for_}{\&{for} primitive@>
+primitive("forsuffixes",iteration,suffix_base);@/
+@!@:for_suffixes_}{\&{forsuffixes} primitive@>
+primitive("forever",iteration,start_forever);@/
+@!@:forever_}{\&{forever} primitive@>
+primitive("endfor",iteration,end_for); eqtb[frozen_end_for]:=eqtb[cur_sym];@/
+@!@:end_for_}{\&{endfor} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+macro_def:if m<=var_def then
+ if m=start_def then print("def")
+ else if m<start_def then print("enddef")
+ else print("vardef")
+ else if m=secondary_primary_macro then print("primarydef")
+ else if m=tertiary_secondary_macro then print("secondarydef")
+ else print("tertiarydef");
+iteration: if m<=start_forever then
+ if m=start_forever then print("forever")@+else print("endfor")
+ else if m=expr_base then print("for")@+else print("forsuffixes");
+
+@ Different macro-absorbing operations have different syntaxes, but they
+also have a lot in common. There is a list of special symbols that are to
+be replaced by parameter tokens; there is a special command code that
+ends the definition; the quotation conventions are identical. Therefore
+it makes sense to have most of the work done by a single subroutine. That
+subroutine is called |scan_toks|.
+
+The first parameter to |scan_toks| is the command code that will
+terminate scanning (either |macro_def| or |iteration|).
+
+The second parameter, |subst_list|, points to a (possibly empty) list
+of two-word nodes whose |info| and |value| fields specify symbol tokens
+before and after replacement. The list will be returned to free storage
+by |scan_toks|.
+
+The third parameter is simply appended to the token list that is built.
+And the final parameter tells how many of the special operations
+\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#} are to be replaced by suffix parameters.
+When such parameters are present, they are called \.{(SUFFIX0)},
+\.{(SUFFIX1)}, and \.{(SUFFIX2)}.
+
+@p function scan_toks(@!terminator:command_code;
+ @!subst_list,@!tail_end:pointer;@!suffix_count:small_number):pointer;
+label done,found;
+var @!p:pointer; {tail of the token list being built}
+@!q:pointer; {temporary for link management}
+@!balance:integer; {left delimiters minus right delimiters}
+begin p:=hold_head; balance:=1; link(hold_head):=null;
+loop@+ begin get_next;
+ if cur_sym>0 then
+ begin @<Substitute for |cur_sym|, if it's on the |subst_list|@>;
+ if cur_cmd=terminator then
+ @<Adjust the balance; |goto done| if it's zero@>
+ else if cur_cmd=macro_special then
+ @<Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#}@>;
+ end;
+ link(p):=cur_tok; p:=link(p);
+ end;
+done: link(p):=tail_end; flush_node_list(subst_list);
+scan_toks:=link(hold_head);
+end;
+
+@ @<Substitute for |cur_sym|...@>=
+begin q:=subst_list;
+while q<>null do
+ begin if info(q)=cur_sym then
+ begin cur_sym:=value(q); cur_cmd:=relax; goto found;
+ end;
+ q:=link(q);
+ end;
+found:end
+
+@ @<Adjust the balance; |goto done| if it's zero@>=
+if cur_mod>0 then incr(balance)
+else begin decr(balance);
+ if balance=0 then goto done;
+ end
+
+@ Four commands are intended to be used only within macro texts: \&{quote},
+\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command
+code called |macro_special|.
+
+@d quote=0 {|macro_special| modifier for \&{quote}}
+@d macro_prefix=1 {|macro_special| modifier for \.{\#\AT!}}
+@d macro_at=2 {|macro_special| modifier for \.{\AT!}}
+@d macro_suffix=3 {|macro_special| modifier for \.{\AT!\#}}
+
+@<Put each...@>=
+primitive("quote",macro_special,quote);@/
+@!@:quote_}{\&{quote} primitive@>
+primitive("#@@",macro_special,macro_prefix);@/
+@!@:]]]\#\AT!_}{\.{\#\AT!} primitive@>
+primitive("@@",macro_special,macro_at);@/
+@!@:]]]\AT!_}{\.{\AT!} primitive@>
+primitive("@@#",macro_special,macro_suffix);@/
+@!@:]]]\AT!\#_}{\.{\AT!\#} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+macro_special: case m of
+ macro_prefix: print("#@@");
+ macro_at: print_char("@@");
+ macro_suffix: print("@@#");
+ othercases print("quote")
+ endcases;
+
+@ @<Handle quoted...@>=
+begin if cur_mod=quote then get_next
+else if cur_mod<=suffix_count then cur_sym:=suffix_base-1+cur_mod;
+end
+
+@ Here is a routine that's used whenever a token will be redefined. If
+the user's token is unredefinable, the `|frozen_inaccessible|' token is
+substituted; the latter is redefinable but essentially impossible to use,
+hence \MF's tables won't get fouled up.
+
+@p procedure get_symbol; {sets |cur_sym| to a safe symbol}
+label restart;
+begin restart: get_next;
+if (cur_sym=0)or(cur_sym>frozen_inaccessible) then
+ begin print_err("Missing symbolic token inserted");
+@.Missing symbolic token...@>
+ help3("Sorry: You can't redefine a number, string, or expr.")@/
+ ("I've inserted an inaccessible symbol so that your")@/
+ ("definition will be completed without mixing me up too badly.");
+ if cur_sym>0 then
+ help_line[2]:="Sorry: You can't redefine my error-recovery tokens."
+ else if cur_cmd=string_token then delete_str_ref(cur_mod);
+ cur_sym:=frozen_inaccessible; ins_error; goto restart;
+ end;
+end;
+
+@ Before we actually redefine a symbolic token, we need to clear away its
+former value, if it was a variable. The following stronger version of
+|get_symbol| does that.
+
+@p procedure get_clear_symbol;
+begin get_symbol; clear_symbol(cur_sym,false);
+end;
+
+@ Here's another little subroutine; it checks that an equals sign
+or assignment sign comes along at the proper place in a macro definition.
+
+@p procedure check_equals;
+begin if cur_cmd<>equals then if cur_cmd<>assignment then
+ begin missing_err("=");@/
+@.Missing `='@>
+ help5("The next thing in this `def' should have been `=',")@/
+ ("because I've already looked at the definition heading.")@/
+ ("But don't worry; I'll pretend that an equals sign")@/
+ ("was present. Everything from here to `enddef'")@/
+ ("will be the replacement text of this macro.");
+ back_error;
+ end;
+end;
+
+@ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily
+handled now that we have |scan_toks|. In this case there are
+two parameters, which will be \.{EXPR0} and \.{EXPR1} (i.e.,
+|expr_base| and |expr_base+1|).
+
+@p procedure make_op_def;
+var @!m:command_code; {the type of definition}
+@!p,@!q,@!r:pointer; {for list manipulation}
+begin m:=cur_mod;@/
+get_symbol; q:=get_node(token_node_size);
+info(q):=cur_sym; value(q):=expr_base;@/
+get_clear_symbol; warning_info:=cur_sym;@/
+get_symbol; p:=get_node(token_node_size);
+info(p):=cur_sym; value(p):=expr_base+1; link(p):=q;@/
+get_next; check_equals;@/
+scanner_status:=op_defining; q:=get_avail; ref_count(q):=null;
+r:=get_avail; link(q):=r; info(r):=general_macro;
+link(r):=scan_toks(macro_def,p,null,0);
+scanner_status:=normal; eq_type(warning_info):=m;
+equiv(warning_info):=q; get_x_next;
+end;
+
+@ Parameters to macros are introduced by the keywords \&{expr},
+\&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}.
+
+@<Put each...@>=
+primitive("expr",param_type,expr_base);@/
+@!@:expr_}{\&{expr} primitive@>
+primitive("suffix",param_type,suffix_base);@/
+@!@:suffix_}{\&{suffix} primitive@>
+primitive("text",param_type,text_base);@/
+@!@:text_}{\&{text} primitive@>
+primitive("primary",param_type,primary_macro);@/
+@!@:primary_}{\&{primary} primitive@>
+primitive("secondary",param_type,secondary_macro);@/
+@!@:secondary_}{\&{secondary} primitive@>
+primitive("tertiary",param_type,tertiary_macro);@/
+@!@:tertiary_}{\&{tertiary} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+param_type:if m>=expr_base then
+ if m=expr_base then print("expr")
+ else if m=suffix_base then print("suffix")
+ else print("text")
+ else if m<secondary_macro then print("primary")
+ else if m=secondary_macro then print("secondary")
+ else print("tertiary");
+
+@ Let's turn next to the more complex processing associated with \&{def}
+and \&{vardef}. When the following procedure is called, |cur_mod|
+should be either |start_def| or |var_def|.
+
+@p @t\4@>@<Declare the procedure called |check_delimiter|@>@;
+@t\4@>@<Declare the function called |scan_declared_variable|@>@;
+procedure scan_def;
+var @!m:start_def..var_def; {the type of definition}
+@!n:0..3; {the number of special suffix parameters}
+@!k:0..param_size; {the total number of parameters}
+@!c:general_macro..text_macro; {the kind of macro we're defining}
+@!r:pointer; {parameter-substitution list}
+@!q:pointer; {tail of the macro token list}
+@!p:pointer; {temporary storage}
+@!base:halfword; {|expr_base|, |suffix_base|, or |text_base|}
+@!l_delim,@!r_delim:pointer; {matching delimiters}
+begin m:=cur_mod; c:=general_macro; link(hold_head):=null;@/
+q:=get_avail; ref_count(q):=null; r:=null;@/
+@<Scan the token or variable to be defined;
+ set |n|, |scanner_status|, and |warning_info|@>;
+k:=n;
+if cur_cmd=left_delimiter then
+ @<Absorb delimited parameters, putting them into lists |q| and |r|@>;
+if cur_cmd=param_type then
+ @<Absorb undelimited parameters, putting them into list |r|@>;
+check_equals;
+p:=get_avail; info(p):=c; link(q):=p;
+@<Attach the replacement text to the tail of node |p|@>;
+scanner_status:=normal; get_x_next;
+end;
+
+@ We don't put `|frozen_end_group|' into the replacement text of
+a \&{vardef}, because the user may want to redefine `\.{endgroup}'.
+
+@<Attach the replacement text to the tail of node |p|@>=
+if m=start_def then link(p):=scan_toks(macro_def,r,null,n)
+else begin q:=get_avail; info(q):=bg_loc; link(p):=q;
+ p:=get_avail; info(p):=eg_loc;
+ link(q):=scan_toks(macro_def,r,p,n);
+ end;
+if warning_info=bad_vardef then flush_token_list(value(bad_vardef))
+
+@ @<Glob...@>=
+@!bg_loc,@!eg_loc:1..hash_end;
+ {hash addresses of `\.{begingroup}' and `\.{endgroup}'}
+
+@ @<Scan the token or variable to be defined;...@>=
+if m=start_def then
+ begin get_clear_symbol; warning_info:=cur_sym; get_next;
+ scanner_status:=op_defining; n:=0;
+ eq_type(warning_info):=defined_macro; equiv(warning_info):=q;
+ end
+else begin p:=scan_declared_variable;
+ flush_variable(equiv(info(p)),link(p),true);
+ warning_info:=find_variable(p); flush_list(p);
+ if warning_info=null then @<Change to `\.{a bad variable}'@>;
+ scanner_status:=var_defining; n:=2;
+ if cur_cmd=macro_special then if cur_mod=macro_suffix then {\.{\AT!\#}}
+ begin n:=3; get_next;
+ end;
+ type(warning_info):=unsuffixed_macro-2+n; value(warning_info):=q;
+ end {|suffixed_macro=unsuffixed_macro+1|}
+
+@ @<Change to `\.{a bad variable}'@>=
+begin print_err("This variable already starts with a macro");
+@.This variable already...@>
+help2("After `vardef a' you can't say `vardef a.b'.")@/
+ ("So I'll have to discard this definition.");
+error; warning_info:=bad_vardef;
+end
+
+@ @<Initialize table entries...@>=
+name_type(bad_vardef):=root; link(bad_vardef):=frozen_bad_vardef;
+equiv(frozen_bad_vardef):=bad_vardef; eq_type(frozen_bad_vardef):=tag_token;
+
+@ @<Absorb delimited parameters, putting them into lists |q| and |r|@>=
+repeat l_delim:=cur_sym; r_delim:=cur_mod; get_next;
+if (cur_cmd=param_type)and(cur_mod>=expr_base) then base:=cur_mod
+else begin print_err("Missing parameter type; `expr' will be assumed");
+@.Missing parameter type@>
+ help1("You should've had `expr' or `suffix' or `text' here.");
+ back_error; base:=expr_base;
+ end;
+@<Absorb parameter tokens for type |base|@>;
+check_delimiter(l_delim,r_delim);
+get_next;
+until cur_cmd<>left_delimiter
+
+@ @<Absorb parameter tokens for type |base|@>=
+repeat link(q):=get_avail; q:=link(q); info(q):=base+k;@/
+get_symbol; p:=get_node(token_node_size); value(p):=base+k; info(p):=cur_sym;
+if k=param_size then overflow("parameter stack size",param_size);
+@:METAFONT capacity exceeded parameter stack size}{\quad parameter stack size@>
+incr(k); link(p):=r; r:=p; get_next;
+until cur_cmd<>comma
+
+@ @<Absorb undelimited parameters, putting them into list |r|@>=
+begin p:=get_node(token_node_size);
+if cur_mod<expr_base then
+ begin c:=cur_mod; value(p):=expr_base+k;
+ end
+else begin value(p):=cur_mod+k;
+ if cur_mod=expr_base then c:=expr_macro
+ else if cur_mod=suffix_base then c:=suffix_macro
+ else c:=text_macro;
+ end;
+if k=param_size then overflow("parameter stack size",param_size);
+incr(k); get_symbol; info(p):=cur_sym; link(p):=r; r:=p; get_next;
+if c=expr_macro then if cur_cmd=of_token then
+ begin c:=of_macro; p:=get_node(token_node_size);
+ if k=param_size then overflow("parameter stack size",param_size);
+ value(p):=expr_base+k; get_symbol; info(p):=cur_sym;
+ link(p):=r; r:=p; get_next;
+ end;
+end
+
+@* \[35] Expanding the next token.
+Only a few command codes |<min_command| can possibly be returned by
+|get_next|; in increasing order, they are
+|if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|,
+|exit_test|, |relax|, |scan_tokens|, |expand_after|, and |defined_macro|.
+
+\MF\ usually gets the next token of input by saying |get_x_next|. This is
+like |get_next| except that it keeps getting more tokens until
+finding |cur_cmd>=min_command|. In other words, |get_x_next| expands
+macros and removes conditionals or iterations or input instructions that
+might be present.
+
+It follows that |get_x_next| might invoke itself recursively. In fact,
+there is massive recursion, since macro expansion can involve the
+scanning of arbitrarily complex expressions, which in turn involve
+macro expansion and conditionals, etc.
+@^recursion@>
+
+Therefore it's necessary to declare a whole bunch of |forward|
+procedures at this point, and to insert some other procedures
+that will be invoked by |get_x_next|.
+
+@p procedure@?scan_primary; forward;@t\2@>
+procedure@?scan_secondary; forward;@t\2@>
+procedure@?scan_tertiary; forward;@t\2@>
+procedure@?scan_expression; forward;@t\2@>
+procedure@?scan_suffix; forward;@t\2@>@/
+@t\4@>@<Declare the procedure called |macro_call|@>@;@/
+procedure@?get_boolean; forward;@t\2@>
+procedure@?pass_text; forward;@t\2@>
+procedure@?conditional; forward;@t\2@>
+procedure@?start_input; forward;@t\2@>
+procedure@?begin_iteration; forward;@t\2@>
+procedure@?resume_iteration; forward;@t\2@>
+procedure@?stop_iteration; forward;@t\2@>
+
+@ An auxiliary subroutine called |expand| is used by |get_x_next|
+when it has to do exotic expansion commands.
+
+@p procedure expand;
+var @!p:pointer; {for list manipulation}
+@!k:integer; {something that we hope is |<=buf_size|}
+@!j:pool_pointer; {index into |str_pool|}
+begin if internal[tracing_commands]>unity then if cur_cmd<>defined_macro then
+ show_cur_cmd_mod;
+case cur_cmd of
+if_test:conditional; {this procedure is discussed in Part 36 below}
+fi_or_else:@<Terminate the current conditional and skip to \&{fi}@>;
+input:@<Initiate or terminate input from a file@>;
+iteration:if cur_mod=end_for then
+ @<Scold the user for having an extra \&{endfor}@>
+ else begin_iteration; {this procedure is discussed in Part 37 below}
+repeat_loop: @<Repeat a loop@>;
+exit_test: @<Exit a loop if the proper time has come@>;
+relax: do_nothing;
+expand_after: @<Expand the token after the next token@>;
+scan_tokens: @<Put a string into the input buffer@>;
+defined_macro:macro_call(cur_mod,null,cur_sym);
+end; {there are no other cases}
+end;
+
+@ @<Scold the user...@>=
+begin print_err("Extra `endfor'");
+@.Extra `endfor'@>
+help2("I'm not currently working on a for loop,")@/
+ ("so I had better not try to end anything.");@/
+error;
+end
+
+@ The processing of \&{input} involves the |start_input| subroutine,
+which will be declared later; the processing of \&{endinput} is trivial.
+
+@<Put each...@>=
+primitive("input",input,0);@/
+@!@:input_}{\&{input} primitive@>
+primitive("endinput",input,1);@/
+@!@:end_input_}{\&{endinput} primitive@>
+
+@ @<Cases of |print_cmd_mod|...@>=
+input: if m=0 then print("input")@+else print("endinput");
+
+@ @<Initiate or terminate input...@>=
+if cur_mod>0 then force_eof:=true
+else start_input
+
+@ We'll discuss the complicated parts of loop operations later. For now
+it suffices to know that there's a global variable called |loop_ptr|
+that will be |null| if no loop is in progress.
+
+@<Repeat a loop@>=
+begin while token_state and(loc=null) do end_token_list; {conserve stack space}
+if loop_ptr=null then
+ begin print_err("Lost loop");
+@.Lost loop@>
+ help2("I'm confused; after exiting from a loop, I still seem")@/
+ ("to want to repeat it. I'll try to forget the problem.");@/
+ error;
+ end
+else resume_iteration; {this procedure is in Part 37 below}
+end
+
+@ @<Exit a loop if the proper time has come@>=
+begin get_boolean;
+if internal[tracing_commands]>unity then show_cmd_mod(nullary,cur_exp);
+if cur_exp=true_code then
+ if loop_ptr=null then
+ begin print_err("No loop is in progress");
+@.No loop is in progress@>
+ help1("Why say `exitif' when there's nothing to exit from?");
+ if cur_cmd=semicolon then error@+else back_error;
+ end
+ else @<Exit prematurely from an iteration@>
+else if cur_cmd<>semicolon then
+ begin missing_err(";");@/
+@.Missing `;'@>
+ help2("After `exitif <boolean exp>' I expect to see a semicolon.")@/
+ ("I shall pretend that one was there."); back_error;
+ end;
+end
+
+@ Here we use the fact that |forever_text| is the only |token_type| that
+is less than |loop_text|.
+
+@<Exit prematurely...@>=
+begin p:=null;
+repeat if file_state then end_file_reading
+else begin if token_type<=loop_text then p:=start;
+ end_token_list;
+ end;
+until p<>null;
+if p<>info(loop_ptr) then fatal_error("*** (loop confusion)");
+@.loop confusion@>
+stop_iteration; {this procedure is in Part 37 below}
+end
+
+@ @<Expand the token after the next token@>=
+begin get_next;
+p:=cur_tok; get_next;
+if cur_cmd<min_command then expand else back_input;
+back_list(p);
+end
+
+@ @<Put a string into the input buffer@>=
+begin get_x_next; scan_primary;
+if cur_type<>string_type then
+ begin disp_err(null,"Not a string");
+@.Not a string@>
+ help2("I'm going to flush this expression, since")@/
+ ("scantokens should be followed by a known string.");
+ put_get_flush_error(0);
+ end
+else begin back_input;
+ if length(cur_exp)>0 then @<Pretend we're reading a new one-line file@>;
+ end;
+end
+
+@ @<Pretend we're reading a new one-line file@>=
+begin begin_file_reading; name:=2;
+k:=first+length(cur_exp);
+if k>=max_buf_stack then
+ begin if k>=buf_size then
+ begin max_buf_stack:=buf_size;
+ overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+ end;
+ max_buf_stack:=k+1;
+ end;
+j:=str_start[cur_exp]; limit:=k;
+while first<limit do
+ begin buffer[first]:=so(str_pool[j]); incr(j); incr(first);
+ end;
+buffer[limit]:="%"; first:=limit+1; loc:=start; flush_cur_exp(0);
+end
+
+@ Here finally is |get_x_next|.
+
+The expression scanning routines to be considered later
+communicate via the global quantities |cur_type| and |cur_exp|;
+we must be very careful to save and restore these quantities while
+macros are being expanded.
+@^inner loop@>
+
+@p procedure get_x_next;
+var @!save_exp:pointer; {a capsule to save |cur_type| and |cur_exp|}
+begin get_next;
+if cur_cmd<min_command then
+ begin save_exp:=stash_cur_exp;
+ repeat if cur_cmd=defined_macro then macro_call(cur_mod,null,cur_sym)
+ else expand;
+ get_next;
+ until cur_cmd>=min_command;
+ unstash_cur_exp(save_exp); {that restores |cur_type| and |cur_exp|}
+ end;
+end;
+
+@ Now let's consider the |macro_call| procedure, which is used to start up
+all user-defined macros. Since the arguments to a macro might be expressions,
+|macro_call| is recursive.
+@^recursion@>
+
+The first parameter to |macro_call| points to the reference count of the
+token list that defines the macro. The second parameter contains any
+arguments that have already been parsed (see below). The third parameter
+points to the symbolic token that names the macro. If the third parameter
+is |null|, the macro was defined by \&{vardef}, so its name can be
+reconstructed from the prefix and ``at'' arguments found within the
+second parameter.
+
+What is this second parameter? It's simply a linked list of one-word items,
+whose |info| fields point to the arguments. In other words, if |arg_list=null|,
+no arguments have been scanned yet; otherwise |info(arg_list)| points to
+the first scanned argument, and |link(arg_list)| points to the list of
+further arguments (if any).
+
+Arguments of type \&{expr} are so-called capsules, which we will
+discuss later when we concentrate on expressions; they can be
+recognized easily because their |link| field is |void|. Arguments of type
+\&{suffix} and \&{text} are token lists without reference counts.
+
+@ After argument scanning is complete, the arguments are moved to the
+|param_stack|. (They can't be put on that stack any sooner, because
+the stack is growing and shrinking in unpredictable ways as more arguments
+are being acquired.) Then the macro body is fed to the scanner; i.e.,
+the replacement text of the macro is placed at the top of the \MF's
+input stack, so that |get_next| will proceed to read it next.
+
+@<Declare the procedure called |macro_call|@>=
+@t\4@>@<Declare the procedure called |print_macro_name|@>@;
+@t\4@>@<Declare the procedure called |print_arg|@>@;
+@t\4@>@<Declare the procedure called |scan_text_arg|@>@;
+procedure macro_call(@!def_ref,@!arg_list,@!macro_name:pointer);
+ {invokes a user-defined control sequence}
+label found;
+var @!r:pointer; {current node in the macro's token list}
+@!p,@!q:pointer; {for list manipulation}
+@!n:integer; {the number of arguments}
+@!l_delim,@!r_delim:pointer; {a delimiter pair}
+@!tail:pointer; {tail of the argument list}
+begin r:=link(def_ref); add_mac_ref(def_ref);
+if arg_list=null then n:=0
+else @<Determine the number |n| of arguments already supplied,
+ and set |tail| to the tail of |arg_list|@>;
+if internal[tracing_macros]>0 then
+ @<Show the text of the macro being expanded, and the existing arguments@>;
+@<Scan the remaining arguments, if any; set |r| to the first token
+ of the replacement text@>;
+@<Feed the arguments and replacement text to the scanner@>;
+end;
+
+@ @<Show the text of the macro...@>=
+begin begin_diagnostic; print_ln; print_macro_name(arg_list,macro_name);
+if n=3 then print("@@#"); {indicate a suffixed macro}
+show_macro(def_ref,null,100000);
+if arg_list<>null then
+ begin n:=0; p:=arg_list;
+ repeat q:=info(p);
+ print_arg(q,n,0);
+ incr(n); p:=link(p);
+ until p=null;
+ end;
+end_diagnostic(false);
+end
+
+@ @<Declare the procedure called |print_macro_name|@>=
+procedure print_macro_name(@!a,@!n:pointer);
+var @!p,@!q:pointer; {they traverse the first part of |a|}
+begin if n<>null then slow_print(text(n))
+else begin p:=info(a);
+ if p=null then slow_print(text(info(info(link(a)))))
+ else begin q:=p;
+ while link(q)<>null do q:=link(q);
+ link(q):=info(link(a));
+ show_token_list(p,null,1000,0);
+ link(q):=null;
+ end;
+ end;
+end;
+
+@ @<Declare the procedure called |print_arg|@>=
+procedure print_arg(@!q:pointer;@!n:integer;@!b:pointer);
+begin if link(q)=void then print_nl("(EXPR")
+else if (b<text_base)and(b<>text_macro) then print_nl("(SUFFIX")
+else print_nl("(TEXT");
+print_int(n); print(")<-");
+if link(q)=void then print_exp(q,1)
+else show_token_list(q,null,1000,0);
+end;
+
+@ @<Determine the number |n| of arguments already supplied...@>=
+begin n:=1; tail:=arg_list;
+while link(tail)<>null do
+ begin incr(n); tail:=link(tail);
+ end;
+end
+
+@ @<Scan the remaining arguments, if any; set |r|...@>=
+cur_cmd:=comma+1; {anything |<>comma| will do}
+while info(r)>=expr_base do
+ begin @<Scan the delimited argument represented by |info(r)|@>;
+ r:=link(r);
+ end;
+if cur_cmd=comma then
+ begin print_err("Too many arguments to ");
+@.Too many arguments...@>
+ print_macro_name(arg_list,macro_name); print_char(";");
+ print_nl(" Missing `"); slow_print(text(r_delim));
+@.Missing `)'...@>
+ print("' has been inserted");
+ help3("I'm going to assume that the comma I just read was a")@/
+ ("right delimiter, and then I'll begin expanding the macro.")@/
+ ("You might want to delete some tokens before continuing.");
+ error;
+ end;
+if info(r)<>general_macro then @<Scan undelimited argument(s)@>;
+r:=link(r)
+
+@ At this point, the reader will find it advisable to review the explanation
+of token list format that was presented earlier, paying special attention to
+the conventions that apply only at the beginning of a macro's token list.
+
+On the other hand, the reader will have to take the expression-parsing
+aspects of the following program on faith; we will explain |cur_type|
+and |cur_exp| later. (Several things in this program depend on each other,
+and it's necessary to jump into the circle somewhere.)
+
+@<Scan the delimited argument represented by |info(r)|@>=
+if cur_cmd<>comma then
+ begin get_x_next;
+ if cur_cmd<>left_delimiter then
+ begin print_err("Missing argument to ");
+@.Missing argument...@>
+ print_macro_name(arg_list,macro_name);
+ help3("That macro has more parameters than you thought.")@/
+ ("I'll continue by pretending that each missing argument")@/
+ ("is either zero or null.");
+ if info(r)>=suffix_base then
+ begin cur_exp:=null; cur_type:=token_list;
+ end
+ else begin cur_exp:=0; cur_type:=known;
+ end;
+ back_error; cur_cmd:=right_delimiter; goto found;
+ end;
+ l_delim:=cur_sym; r_delim:=cur_mod;
+ end;
+@<Scan the argument represented by |info(r)|@>;
+if cur_cmd<>comma then @<Check that the proper right delimiter was present@>;
+found: @<Append the current expression to |arg_list|@>
+
+@ @<Check that the proper right delim...@>=
+if (cur_cmd<>right_delimiter)or(cur_mod<>l_delim) then
+ if info(link(r))>=expr_base then
+ begin missing_err(",");
+@.Missing `,'@>
+ help3("I've finished reading a macro argument and am about to")@/
+ ("read another; the arguments weren't delimited correctly.")@/
+ ("You might want to delete some tokens before continuing.");
+ back_error; cur_cmd:=comma;
+ end
+ else begin missing_err(text(r_delim));
+@.Missing `)'@>
+ help2("I've gotten to the end of the macro parameter list.")@/
+ ("You might want to delete some tokens before continuing.");
+ back_error;
+ end
+
+@ A \&{suffix} or \&{text} parameter will have been scanned as
+a token list pointed to by |cur_exp|, in which case we will have
+|cur_type=token_list|.
+
+@<Append the current expression to |arg_list|@>=
+begin p:=get_avail;
+if cur_type=token_list then info(p):=cur_exp
+else info(p):=stash_cur_exp;
+if internal[tracing_macros]>0 then
+ begin begin_diagnostic; print_arg(info(p),n,info(r)); end_diagnostic(false);
+ end;
+if arg_list=null then arg_list:=p
+else link(tail):=p;
+tail:=p; incr(n);
+end
+
+@ @<Scan the argument represented by |info(r)|@>=
+if info(r)>=text_base then scan_text_arg(l_delim,r_delim)
+else begin get_x_next;
+ if info(r)>=suffix_base then scan_suffix
+ else scan_expression;
+ end
+
+@ The parameters to |scan_text_arg| are either a pair of delimiters
+or zero; the latter case is for undelimited text arguments, which
+end with the first semicolon or \&{endgroup} or \&{end} that is not
+contained in a group.
+
+@<Declare the procedure called |scan_text_arg|@>=
+procedure scan_text_arg(@!l_delim,@!r_delim:pointer);
+label done;
+var @!balance:integer; {excess of |l_delim| over |r_delim|}
+@!p:pointer; {list tail}
+begin warning_info:=l_delim; scanner_status:=absorbing;
+p:=hold_head; balance:=1; link(hold_head):=null;
+loop@+ begin get_next;
+ if l_delim=0 then @<Adjust the balance for an undelimited argument;
+ |goto done| if done@>
+ else @<Adjust the balance for a delimited argument;
+ |goto done| if done@>;
+ link(p):=cur_tok; p:=link(p);
+ end;
+done: cur_exp:=link(hold_head); cur_type:=token_list;
+scanner_status:=normal;
+end;
+
+@ @<Adjust the balance for a delimited argument...@>=
+begin if cur_cmd=right_delimiter then
+ begin if cur_mod=l_delim then
+ begin decr(balance);
+ if balance=0 then goto done;
+ end;
+ end
+else if cur_cmd=left_delimiter then if cur_mod=r_delim then incr(balance);
+end
+
+@ @<Adjust the balance for an undelimited...@>=
+begin if end_of_statement then {|cur_cmd=semicolon|, |end_group|, or |stop|}
+ begin if balance=1 then goto done
+ else if cur_cmd=end_group then decr(balance);
+ end
+else if cur_cmd=begin_group then incr(balance);
+end
+
+@ @<Scan undelimited argument(s)@>=
+begin if info(r)<text_macro then
+ begin get_x_next;
+ if info(r)<>suffix_macro then
+ if (cur_cmd=equals)or(cur_cmd=assignment) then get_x_next;
+ end;
+case info(r) of
+primary_macro:scan_primary;
+secondary_macro:scan_secondary;
+tertiary_macro:scan_tertiary;
+expr_macro:scan_expression;
+of_macro:@<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>;
+suffix_macro:@<Scan a suffix with optional delimiters@>;
+text_macro:scan_text_arg(0,0);
+end; {there are no other cases}
+back_input; @<Append the current expression to |arg_list|@>;
+end
+
+@ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>=
+begin scan_expression; p:=get_avail; info(p):=stash_cur_exp;
+if internal[tracing_macros]>0 then
+ begin begin_diagnostic; print_arg(info(p),n,0); end_diagnostic(false);
+ end;
+if arg_list=null then arg_list:=p@+else link(tail):=p;
+tail:=p;incr(n);
+if cur_cmd<>of_token then
+ begin missing_err("of"); print(" for ");
+@.Missing `of'@>
+ print_macro_name(arg_list,macro_name);
+ help1("I've got the first argument; will look now for the other.");
+ back_error;
+ end;
+get_x_next; scan_primary;
+end
+
+@ @<Scan a suffix with optional delimiters@>=
+begin if cur_cmd<>left_delimiter then l_delim:=null
+else begin l_delim:=cur_sym; r_delim:=cur_mod; get_x_next;
+ end;
+scan_suffix;
+if l_delim<>null then
+ begin if(cur_cmd<>right_delimiter)or(cur_mod<>l_delim) then
+ begin missing_err(text(r_delim));
+@.Missing `)'@>
+ help2("I've gotten to the end of the macro parameter list.")@/
+ ("You might want to delete some tokens before continuing.");
+ back_error;
+ end;
+ get_x_next;
+ end;
+end
+
+@ Before we put a new token list on the input stack, it is wise to clean off
+all token lists that have recently been depleted. Then a user macro that ends
+with a call to itself will not require unbounded stack space.
+
+@<Feed the arguments and replacement text to the scanner@>=
+while token_state and(loc=null) do end_token_list; {conserve stack space}
+if param_ptr+n>max_param_stack then
+ begin max_param_stack:=param_ptr+n;
+ if max_param_stack>param_size then
+ overflow("parameter stack size",param_size);
+@:METAFONT capacity exceeded parameter stack size}{\quad parameter stack size@>
+ end;
+begin_token_list(def_ref,macro); name:=macro_name; loc:=r;
+if n>0 then
+ begin p:=arg_list;
+ repeat param_stack[param_ptr]:=info(p); incr(param_ptr); p:=link(p);
+ until p=null;
+ flush_list(arg_list);
+ end
+
+@ It's sometimes necessary to put a single argument onto |param_stack|.
+The |stack_argument| subroutine does this.
+
+@p procedure stack_argument(@!p:pointer);
+begin if param_ptr=max_param_stack then
+ begin incr(max_param_stack);
+ if max_param_stack>param_size then
+ overflow("parameter stack size",param_size);
+@:METAFONT capacity exceeded parameter stack size}{\quad parameter stack size@>
+ end;
+param_stack[param_ptr]:=p; incr(param_ptr);
+end;
+
+@* \[36] Conditional processing.
+Let's consider now the way \&{if} commands are handled.
+
+Conditions can be inside conditions, and this nesting has a stack
+that is independent of other stacks.
+Four global variables represent the top of the condition stack:
+|cond_ptr| points to pushed-down entries, if~any; |cur_if| tells whether
+we are processing \&{if} or \&{elseif}; |if_limit| specifies
+the largest code of a |fi_or_else| command that is syntactically legal;
+and |if_line| is the line number at which the current conditional began.
+
+If no conditions are currently in progress, the condition stack has the
+special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
+Otherwise |cond_ptr| points to a two-word node; the |type|, |name_type|, and
+|link| fields of the first word contain |if_limit|, |cur_if|, and
+|cond_ptr| at the next level, and the second word contains the
+corresponding |if_line|.
+
+@d if_node_size=2 {number of words in stack entry for conditionals}
+@d if_line_field(#)==mem[#+1].int
+@d if_code=1 {code for \&{if} being evaluated}
+@d fi_code=2 {code for \&{fi}}
+@d else_code=3 {code for \&{else}}
+@d else_if_code=4 {code for \&{elseif}}
+
+@<Glob...@>=
+@!cond_ptr:pointer; {top of the condition stack}
+@!if_limit:normal..else_if_code; {upper bound on |fi_or_else| codes}
+@!cur_if:small_number; {type of conditional being worked on}
+@!if_line:integer; {line where that conditional began}
+
+@ @<Set init...@>=
+cond_ptr:=null; if_limit:=normal; cur_if:=0; if_line:=0;
+
+@ @<Put each...@>=
+primitive("if",if_test,if_code);@/
+@!@:if_}{\&{if} primitive@>
+primitive("fi",fi_or_else,fi_code); eqtb[frozen_fi]:=eqtb[cur_sym];@/
+@!@:fi_}{\&{fi} primitive@>
+primitive("else",fi_or_else,else_code);@/
+@!@:else_}{\&{else} primitive@>
+primitive("elseif",fi_or_else,else_if_code);@/
+@!@:else_if_}{\&{elseif} primitive@>
+
+@ @<Cases of |print_cmd_mod|...@>=
+if_test,fi_or_else: case m of
+ if_code:print("if");
+ fi_code:print("fi");
+ else_code:print("else");
+ othercases print("elseif")
+ endcases;
+
+@ Here is a procedure that ignores text until coming to an \&{elseif},
+\&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$
+nesting. After it has acted, |cur_mod| will indicate the token that
+was found.
+
+\MF's smallest two command codes are |if_test| and |fi_or_else|; this
+makes the skipping process a bit simpler.
+
+@p procedure pass_text;
+label done;
+var l:integer;
+begin scanner_status:=skipping; l:=0; warning_info:=line;
+loop@+ begin get_next;
+ if cur_cmd<=fi_or_else then
+ if cur_cmd<fi_or_else then incr(l)
+ else begin if l=0 then goto done;
+ if cur_mod=fi_code then decr(l);
+ end
+ else @<Decrease the string reference count,
+ if the current token is a string@>;
+ end;
+done: scanner_status:=normal;
+end;
+
+@ @<Decrease the string reference count...@>=
+if cur_cmd=string_token then delete_str_ref(cur_mod)
+
+@ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then
+if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if}
+condition has been evaluated, a colon will be inserted.
+A construction like `\.{if fi}' would otherwise get \MF\ confused.
+
+@<Push the condition stack@>=
+begin p:=get_node(if_node_size); link(p):=cond_ptr; type(p):=if_limit;
+name_type(p):=cur_if; if_line_field(p):=if_line;
+cond_ptr:=p; if_limit:=if_code; if_line:=line; cur_if:=if_code;
+end
+
+@ @<Pop the condition stack@>=
+begin p:=cond_ptr; if_line:=if_line_field(p);
+cur_if:=name_type(p); if_limit:=type(p); cond_ptr:=link(p);
+free_node(p,if_node_size);
+end
+
+@ Here's a procedure that changes the |if_limit| code corresponding to
+a given value of |cond_ptr|.
+
+@p procedure change_if_limit(@!l:small_number;@!p:pointer);
+label exit;
+var q:pointer;
+begin if p=cond_ptr then if_limit:=l {that's the easy case}
+else begin q:=cond_ptr;
+ loop@+ begin if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ if link(q)=p then
+ begin type(q):=l; return;
+ end;
+ q:=link(q);
+ end;
+ end;
+exit:end;
+
+@ The user is supposed to put colons into the proper parts of conditional
+statements. Therefore, \MF\ has to check for their presence.
+
+@p procedure check_colon;
+begin if cur_cmd<>colon then
+ begin missing_err(":");@/
+@.Missing `:'@>
+ help2("There should've been a colon after the condition.")@/
+ ("I shall pretend that one was there.");@;
+ back_error;
+ end;
+end;
+
+@ A condition is started when the |get_x_next| procedure encounters
+an |if_test| command; in that case |get_x_next| calls |conditional|,
+which is a recursive procedure.
+@^recursion@>
+
+@p procedure conditional;
+label exit,done,reswitch,found;
+var @!save_cond_ptr:pointer; {|cond_ptr| corresponding to this conditional}
+@!new_if_limit:fi_code..else_if_code; {future value of |if_limit|}
+@!p:pointer; {temporary register}
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;
+reswitch: get_boolean; new_if_limit:=else_if_code;
+if internal[tracing_commands]>unity then
+ @<Display the boolean value of |cur_exp|@>;
+found: check_colon;
+if cur_exp=true_code then
+ begin change_if_limit(new_if_limit,save_cond_ptr);
+ return; {wait for \&{elseif}, \&{else}, or \&{fi}}
+ end;
+@<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>;
+done: cur_if:=cur_mod; if_line:=line;
+if cur_mod=fi_code then @<Pop the condition stack@>
+else if cur_mod=else_if_code then goto reswitch
+else begin cur_exp:=true_code; new_if_limit:=fi_code; get_x_next; goto found;
+ end;
+exit:end;
+
+@ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo}
+\&{else}: \\{bar} \&{fi}', the first \&{else}
+that we come to after learning that the \&{if} is false is not the
+\&{else} we're looking for. Hence the following curious logic is needed.
+
+@<Skip to \&{elseif}...@>=
+loop@+ begin pass_text;
+ if cond_ptr=save_cond_ptr then goto done
+ else if cur_mod=fi_code then @<Pop the condition stack@>;
+ end
+
+
+@ @<Display the boolean value...@>=
+begin begin_diagnostic;
+if cur_exp=true_code then print("{true}")@+else print("{false}");
+end_diagnostic(false);
+end
+
+@ The processing of conditionals is complete except for the following
+code, which is actually part of |get_x_next|. It comes into play when
+\&{elseif}, \&{else}, or \&{fi} is scanned.
+
+@<Terminate the current conditional and skip to \&{fi}@>=
+if cur_mod>if_limit then
+ if if_limit=if_code then {condition not yet evaluated}
+ begin missing_err(":");
+@.Missing `:'@>
+ back_input; cur_sym:=frozen_colon; ins_error;
+ end
+ else begin print_err("Extra "); print_cmd_mod(fi_or_else,cur_mod);
+@.Extra else@>
+@.Extra elseif@>
+@.Extra fi@>
+ help1("I'm ignoring this; it doesn't match any if.");
+ error;
+ end
+else begin while cur_mod<>fi_code do pass_text; {skip to \&{fi}}
+ @<Pop the condition stack@>;
+ end
+
+@* \[37] Iterations.
+To bring our treatment of |get_x_next| to a close, we need to consider what
+\MF\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}.
+
+There's a global variable |loop_ptr| that keeps track of the \&{for} loops
+that are currently active. If |loop_ptr=null|, no loops are in progress;
+otherwise |info(loop_ptr)| points to the iterative text of the current
+(innermost) loop, and |link(loop_ptr)| points to the data for any other
+loops that enclose the current one.
+
+A loop-control node also has two other fields, called |loop_type| and
+|loop_list|, whose contents depend on the type of loop:
+
+\yskip\indent|loop_type(loop_ptr)=null| means that |loop_list(loop_ptr)|
+points to a list of one-word nodes whose |info| fields point to the
+remaining argument values of a suffix list and expression list.
+
+\yskip\indent|loop_type(loop_ptr)=void| means that the current loop is
+`\&{forever}'.
+
+\yskip\indent|loop_type(loop_ptr)=p>void| means that |value(p)|,
+|step_size(p)|, and |final_value(p)| contain the data for an arithmetic
+progression.
+
+\yskip\noindent In the latter case, |p| points to a ``progression node''
+whose first word is not used. (No value could be stored there because the
+link field of words in the dynamic memory area cannot be arbitrary.)
+
+@d loop_list_loc(#)==#+1 {where the |loop_list| field resides}
+@d loop_type(#)==info(loop_list_loc(#)) {the type of \&{for} loop}
+@d loop_list(#)==link(loop_list_loc(#)) {the remaining list elements}
+@d loop_node_size=2 {the number of words in a loop control node}
+@d progression_node_size=4 {the number of words in a progression node}
+@d step_size(#)==mem[#+2].sc {the step size in an arithmetic progression}
+@d final_value(#)==mem[#+3].sc {the final value in an arithmetic progression}
+
+@<Glob...@>=
+@!loop_ptr:pointer; {top of the loop-control-node stack}
+
+@ @<Set init...@>=
+loop_ptr:=null;
+
+@ If the expressions that define an arithmetic progression in
+a \&{for} loop don't have known numeric values, the |bad_for|
+subroutine screams at the user.
+
+@p procedure bad_for(@!s:str_number);
+begin disp_err(null,"Improper "); {show the bad expression above the message}
+@.Improper...replaced by 0@>
+print(s); print(" has been replaced by 0");
+help4("When you say `for x=a step b until c',")@/
+ ("the initial value `a' and the step size `b'")@/
+ ("and the final value `c' must have known numeric values.")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+put_get_flush_error(0);
+end;
+
+@ Here's what \MF\ does when \&{for}, \&{forsuffixes}, or \&{forever}
+has just been scanned. (This code requires slight familiarity with
+expression-parsing routines that we have not yet discussed; but it seems
+to belong in the present part of the program, even though the author
+didn't write it until later. The reader may wish to come back to it.)
+
+@p procedure begin_iteration;
+label continue,done,found;
+var @!m:halfword; {|expr_base| (\&{for}) or |suffix_base| (\&{forsuffixes})}
+@!n:halfword; {hash address of the current symbol}
+@!p,@!q,@!s,@!pp:pointer; {link manipulation registers}
+begin m:=cur_mod; n:=cur_sym; s:=get_node(loop_node_size);
+if m=start_forever then
+ begin loop_type(s):=void; p:=null; get_x_next; goto found;
+ end;
+get_symbol; p:=get_node(token_node_size); info(p):=cur_sym; value(p):=m;@/
+get_x_next;
+if (cur_cmd<>equals)and(cur_cmd<>assignment) then
+ begin missing_err("=");@/
+@.Missing `='@>
+ help3("The next thing in this loop should have been `=' or `:='.")@/
+ ("But don't worry; I'll pretend that an equals sign")@/
+ ("was present, and I'll look for the values next.");@/
+ back_error;
+ end;
+@<Scan the values to be used in the loop@>;
+found:@<Check for the presence of a colon@>;
+@<Scan the loop text and put it on the loop control stack@>;
+resume_iteration;
+end;
+
+@ @<Check for the presence of a colon@>=
+if cur_cmd<>colon then
+ begin missing_err(":");@/
+@.Missing `:'@>
+ help3("The next thing in this loop should have been a `:'.")@/
+ ("So I'll pretend that a colon was present;")@/
+ ("everything from here to `endfor' will be iterated.");
+ back_error;
+ end
+
+@ We append a special |frozen_repeat_loop| token in place of the
+`\&{endfor}' at the end of the loop. This will come through \MF's scanner
+at the proper time to cause the loop to be repeated.
+
+(If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} \&{endfor}',
+he will be foiled by the |get_symbol| routine, which keeps frozen
+tokens unchanged. Furthermore the |frozen_repeat_loop| is an \&{outer}
+token, so it won't be lost accidentally.)
+
+@ @<Scan the loop text...@>=
+q:=get_avail; info(q):=frozen_repeat_loop;
+scanner_status:=loop_defining; warning_info:=n;
+info(s):=scan_toks(iteration,p,q,0); scanner_status:=normal;@/
+link(s):=loop_ptr; loop_ptr:=s
+
+@ @<Initialize table...@>=
+eq_type(frozen_repeat_loop):=repeat_loop+outer_tag;
+text(frozen_repeat_loop):=" ENDFOR";
+
+@ The loop text is inserted into \MF's scanning apparatus by the
+|resume_iteration| routine.
+
+@p procedure resume_iteration;
+label not_found,exit;
+var @!p,@!q:pointer; {link registers}
+begin p:=loop_type(loop_ptr);
+if p>void then {|p| points to a progression node}
+ begin cur_exp:=value(p);
+ if @<The arithmetic progression has ended@> then goto not_found;
+ cur_type:=known; q:=stash_cur_exp; {make |q| an \&{expr} argument}
+ value(p):=cur_exp+step_size(p); {set |value(p)| for the next iteration}
+ end
+else if p<void then
+ begin p:=loop_list(loop_ptr);
+ if p=null then goto not_found;
+ loop_list(loop_ptr):=link(p); q:=info(p); free_avail(p);
+ end
+else begin begin_token_list(info(loop_ptr),forever_text); return;
+ end;
+begin_token_list(info(loop_ptr),loop_text);
+stack_argument(q);
+if internal[tracing_commands]>unity then @<Trace the start of a loop@>;
+return;
+not_found:stop_iteration;
+exit:end;
+
+@ @<The arithmetic progression has ended@>=
+((step_size(p)>0)and(cur_exp>final_value(p)))or@|
+ ((step_size(p)<0)and(cur_exp<final_value(p)))
+
+@ @<Trace the start of a loop@>=
+begin begin_diagnostic; print_nl("{loop value=");
+@.loop value=n@>
+if (q<>null)and(link(q)=void) then print_exp(q,1)
+else show_token_list(q,null,50,0);
+print_char("}"); end_diagnostic(false);
+end
+
+@ A level of loop control disappears when |resume_iteration| has decided
+not to resume, or when an \&{exitif} construction has removed the loop text
+from the input stack.
+
+@p procedure stop_iteration;
+var @!p,@!q:pointer; {the usual}
+begin p:=loop_type(loop_ptr);
+if p>void then free_node(p,progression_node_size)
+else if p<void then
+ begin q:=loop_list(loop_ptr);
+ while q<>null do
+ begin p:=info(q);
+ if p<>null then
+ if link(p)=void then {it's an \&{expr} parameter}
+ begin recycle_value(p); free_node(p,value_node_size);
+ end
+ else flush_token_list(p); {it's a \&{suffix} or \&{text} parameter}
+ p:=q; q:=link(q); free_avail(p);
+ end;
+ end;
+p:=loop_ptr; loop_ptr:=link(p); flush_token_list(info(p));
+free_node(p,loop_node_size);
+end;
+
+@ Now that we know all about loop control, we can finish up
+the missing portion of |begin_iteration| and we'll be done.
+
+The following code is performed after the `\.=' has been scanned in
+a \&{for} construction (if |m=expr_base|) or a \&{forsuffixes} construction
+(if |m=suffix_base|).
+
+@<Scan the values to be used in the loop@>=
+loop_type(s):=null; q:=loop_list_loc(s); link(q):=null; {|link(q)=loop_list(s)|}
+repeat get_x_next;
+if m<>expr_base then scan_suffix
+else begin if cur_cmd>=colon then if cur_cmd<=comma then goto continue;
+ scan_expression;
+ if cur_cmd=step_token then if q=loop_list_loc(s) then
+ @<Prepare for step-until construction and |goto done|@>;
+ cur_exp:=stash_cur_exp;
+ end;
+link(q):=get_avail; q:=link(q); info(q):=cur_exp; cur_type:=vacuous;
+continue: until cur_cmd<>comma;
+done:
+
+@ @<Prepare for step-until construction and |goto done|@>=
+begin if cur_type<>known then bad_for("initial value");
+pp:=get_node(progression_node_size); value(pp):=cur_exp;@/
+get_x_next; scan_expression;
+if cur_type<>known then bad_for("step size");
+step_size(pp):=cur_exp;
+if cur_cmd<>until_token then
+ begin missing_err("until");@/
+@.Missing `until'@>
+ help2("I assume you meant to say `until' after `step'.")@/
+ ("So I'll look for the final value and colon next.");
+ back_error;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then bad_for("final value");
+final_value(pp):=cur_exp; loop_type(s):=pp; goto done;
+end
+
+@* \[38] File names.
+It's time now to fret about file names. Besides the fact that different
+operating systems treat files in different ways, we must cope with the
+fact that completely different naming conventions are used by different
+groups of people. The following programs show what is required for one
+particular operating system; similar routines for other systems are not
+difficult to devise.
+@^system dependencies@>
+
+\MF\ assumes that a file name has three parts: the name proper; its
+``extension''; and a ``file area'' where it is found in an external file
+system. The extension of an input file is assumed to be
+`\.{.mf}' unless otherwise specified; it is `\.{.log}' on the
+transcript file that records each run of \MF; it is `\.{.tfm}' on the font
+metric files that describe characters in the fonts \MF\ creates; it is
+`\.{.gf}' on the output files that specify generic font information; and it
+is `\.{.base}' on the base files written by \.{INIMF} to initialize \MF.
+The file area can be arbitrary on input files, but files are usually
+output to the user's current area. If an input file cannot be
+found on the specified area, \MF\ will look for it on a special system
+area; this special area is intended for commonly used input files.
+
+Simple uses of \MF\ refer only to file names that have no explicit
+extension or area. For example, a person usually says `\.{input} \.{cmr10}'
+instead of `\.{input} \.{cmr10.new}'. Simple file
+names are best, because they make the \MF\ source files portable;
+whenever a file name consists entirely of letters and digits, it should be
+treated in the same way by all implementations of \MF. However, users
+need the ability to refer to other files in their environment, especially
+when responding to error messages concerning unopenable files; therefore
+we want to let them use the syntax that appears in their favorite
+operating system.
+
+@ \MF\ uses the same conventions that have proved to be satisfactory for
+\TeX. In order to isolate the system-dependent aspects of file names, the
+@^system dependencies@>
+system-independent parts of \MF\ are expressed in terms
+of three system-dependent
+procedures called |begin_name|, |more_name|, and |end_name|. In
+essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
+the system-independent driver program does the operations
+$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
+\,|end_name|.$$
+These three procedures communicate with each other via global variables.
+Afterwards the file name will appear in the string pool as three strings
+called |cur_name|\penalty10000\hskip-.05em,
+|cur_area|, and |cur_ext|; the latter two are null (i.e.,
+|""|), unless they were explicitly specified by the user.
+
+Actually the situation is slightly more complicated, because \MF\ needs
+to know when the file name ends. The |more_name| routine is a function
+(with side effects) that returns |true| on the calls |more_name|$(c_1)$,
+\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
+returns |false|; or, it returns |true| and $c_n$ is the last character
+on the current input line. In other words,
+|more_name| is supposed to return |true| unless it is sure that the
+file name has been completely scanned; and |end_name| is supposed to be able
+to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
+whether $|more_name|(c_n)$ returned |true| or |false|.
+
+@<Glob...@>=
+@!cur_name:str_number; {name of file just scanned}
+@!cur_area:str_number; {file area just scanned, or \.{""}}
+@!cur_ext:str_number; {file extension just scanned, or \.{""}}
+
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+
+@ Input files that can't be found in the user's area may appear in a standard
+system area called |MF_area|.
+This system area name will, of course, vary from place to place.
+@^system dependencies@>
+
+@d MF_area=="MFinputs:"
+@.MFinputs@>
+
+@ Here now is the first of the system-dependent routines for file name scanning.
+@^system dependencies@>
+
+@p procedure begin_name;
+begin area_delimiter:=0; ext_delimiter:=0;
+end;
+
+@ And here's the second.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+
+@ The third.
+@^system dependencies@>
+
+@p procedure end_name;
+begin if str_ptr+3>max_str_ptr then
+ begin if str_ptr+3>max_strings then
+ overflow("number of strings",max_strings-init_str_ptr);
+@:METAFONT capacity exceeded number of strings}{\quad number of strings@>
+ max_str_ptr:=str_ptr+3;
+ end;
+if area_delimiter=0 then cur_area:=""
+else begin cur_area:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=area_delimiter+1;
+ end;
+if ext_delimiter=0 then
+ begin cur_ext:=""; cur_name:=make_string;
+ end
+else begin cur_name:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string;
+ end;
+end;
+
+@ Conversely, here is a routine that takes three strings and prints a file
+name that might have produced them. (The routine is system dependent, because
+some operating systems put the file area last instead of first.)
+@^system dependencies@>
+
+@<Basic printing...@>=
+procedure print_file_name(@!n,@!a,@!e:integer);
+begin slow_print(a); slow_print(n); slow_print(e);
+end;
+
+@ Another system-dependent routine is needed to convert three internal
+\MF\ strings
+to the |name_of_file| value that is used to open files. The present code
+allows both lowercase and uppercase letters in the file name.
+@^system dependencies@>
+
+@d append_to_name(#)==begin c:=#; incr(k);
+ if k<=file_name_size then name_of_file[k]:=xchr[c];
+ end
+
+@p procedure pack_file_name(@!n,@!a,@!e:str_number);
+var @!k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:pool_pointer; {index into |str_pool|}
+begin k:=0;
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ A messier routine is also needed, since base file names must be scanned
+before \MF's string mechanism has been initialized. We shall use the
+global variable |MF_base_default| to supply the text for default system areas
+and extensions related to base files.
+@^system dependencies@>
+
+@d base_default_length=18 {length of the |MF_base_default| string}
+@d base_area_length=8 {length of its area part}
+@d base_ext_length=5 {length of its `\.{.base}' part}
+@d base_extension=".base" {the extension, as a \.{WEB} constant}
+
+@<Glob...@>=
+@!MF_base_default:packed array[1..base_default_length] of char;
+
+@ @<Set init...@>=
+MF_base_default:='MFbases:plain.base';
+@.MFbases@>
+@.plain@>
+@^system dependencies@>
+
+@ @<Check the ``constant'' values for consistency@>=
+if base_default_length>file_name_size then bad:=41;
+
+@ Here is the messy routine that was just mentioned. It sets |name_of_file|
+from the first |n| characters of |MF_base_default|, followed by
+|buffer[a..b]|, followed by the last |base_ext_length| characters of
+|MF_base_default|.
+
+We dare not give error messages here, since \MF\ calls this routine before
+the |error| routine is ready to roll. Instead, we simply drop excess characters,
+since the error will be detected in another way when a strange file name
+isn't found.
+@^system dependencies@>
+
+@p procedure pack_buffered_name(@!n:small_number;@!a,@!b:integer);
+var @!k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:integer; {index into |buffer| or |MF_base_default|}
+begin if n+b-a+1+base_ext_length>file_name_size then
+ b:=a+file_name_size-n-1-base_ext_length;
+k:=0;
+for j:=1 to n do append_to_name(xord[MF_base_default[j]]);
+for j:=a to b do append_to_name(buffer[j]);
+for j:=base_default_length-base_ext_length+1 to base_default_length do
+ append_to_name(xord[MF_base_default[j]]);
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ Here is the only place we use |pack_buffered_name|. This part of the program
+becomes active when a ``virgin'' \MF\ is trying to get going, just after
+the preliminary initialization, or when the user is substituting another
+base file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
+contains the first line of input in |buffer[loc..(last-1)]|, where
+|loc<last| and |buffer[loc]<>" "|.
+
+@<Declare the function called |open_base_file|@>=
+function open_base_file:boolean;
+label found,exit;
+var @!j:0..buf_size; {the first space after the file name}
+begin j:=loc;
+if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(base_file) then goto found;
+ pack_buffered_name(base_area_length,loc,j-1);
+ {now try the system base file area}
+ if w_open_in(base_file) then goto found;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that base;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ update_terminal;
+ end;
+ {now pull out all the stops: try for the system \.{plain} file}
+pack_buffered_name(base_default_length-base_ext_length,1,0);
+if not w_open_in(base_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN base file!');
+@.I can't find PLAIN...@>
+@.plain@>
+ open_base_file:=false; return;
+ end;
+found:loc:=j; open_base_file:=true;
+exit:end;
+
+@ Operating systems often make it possible to determine the exact name (and
+possible version number) of a file that has been opened. The following routine,
+which simply makes a \MF\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var @!k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+function a_make_name_string(var @!f:alpha_file):str_number;
+begin a_make_name_string:=make_name_string;
+end;
+function b_make_name_string(var @!f:byte_file):str_number;
+begin b_make_name_string:=make_name_string;
+end;
+function w_make_name_string(var @!f:word_file):str_number;
+begin w_make_name_string:=make_name_string;
+end;
+
+@ Now let's consider the ``driver''
+routines by which \MF\ deals with file names
+in a system-independent manner. First comes a procedure that looks for a
+file name in the input by taking the information from the input buffer.
+(We can't use |get_next|, because the conversion to tokens would
+destroy necessary information.)
+
+This procedure doesn't allow semicolons or percent signs to be part of
+file names, because of other conventions of \MF. The manual doesn't
+use semicolons or percents immediately after file names, but some users
+no doubt will find it natural to do so; therefore system-dependent
+changes to allow such characters in file names should probably
+be made with reluctance, and only when an entire file name that
+includes special characters is ``quoted'' somehow.
+@^system dependencies@>
+
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while buffer[loc]=" " do incr(loc);
+loop@+begin if (buffer[loc]=";")or(buffer[loc]="%") then goto done;
+ if not more_name(buffer[loc]) then goto done;
+ incr(loc);
+ end;
+done: end_name;
+end;
+
+@ The global variable |job_name| contains the file name that was first
+\&{input} by the user. This name is extended by `\.{.log}' and `\.{.gf}' and
+`\.{.base}' and `\.{.tfm}' in the names of \MF's output files.
+
+@<Glob...@>=
+@!job_name:str_number; {principal file name}
+@!log_opened:boolean; {has the transcript file been opened?}
+@!log_name:str_number; {full name of the log file}
+
+@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
+We have |job_name=0| if and only if the `\.{log}' file has not been opened,
+except of course for a short time just after |job_name| has become nonzero.
+
+@<Initialize the output...@>=job_name:=0; log_opened:=false;
+
+@ Here is a routine that manufactures the output file names, assuming that
+|job_name<>0|. It ignores and changes the current settings of |cur_area|
+and |cur_ext|.
+
+@d pack_cur_name==pack_file_name(cur_name,cur_area,cur_ext)
+
+@p procedure pack_job_name(@!s:str_number); {|s = ".log"|, |".gf"|,
+ |".tfm"|, or |base_extension|}
+begin cur_area:=""; cur_ext:=s;
+cur_name:=job_name; pack_cur_name;
+end;
+
+@ Actually the main output file extension is usually something like
+|".300gf"| instead of just |".gf"|; the additional number indicates the
+resolution in pixels per inch, based on the setting of |hppp| when
+the file is opened.
+
+@<Glob...@>=
+@!gf_ext:str_number; {default extension for the output file}
+
+@ If some trouble arises when \MF\ tries to open a file, the following
+routine calls upon the user to supply another file name. Parameter~|s|
+is used in the error message to identify the type of file; parameter~|e|
+is the default extension if none is given. Upon exit from the routine,
+variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
+ready for another attempt at file opening.
+
+@p procedure prompt_file_name(@!s,@!e:str_number);
+label done;
+var @!k:0..buf_size; {index into |buffer|}
+begin if interaction=scroll_mode then wake_up_terminal;
+if s="input file name" then print_err("I can't find file `")
+@.I can't find file x@>
+else print_err("I can't write on file `");
+@.I can't write on file x@>
+print_file_name(cur_name,cur_area,cur_ext); print("'.");
+if e=".mf" then show_context;
+print_nl("Please type another "); print(s);
+@.Please type...@>
+if interaction<scroll_mode then
+ fatal_error("*** (job aborted, file error in nonstop mode)");
+@.job aborted, file error...@>
+clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
+if cur_ext="" then cur_ext:=e;
+pack_cur_name;
+end;
+
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while (buffer[k]=" ")and(k<last) do incr(k);
+loop@+ begin if k=last then goto done;
+ if not more_name(buffer[k]) then goto done;
+ incr(k);
+ end;
+done:end_name;
+end
+
+@ The |open_log_file| routine is used to open the transcript file and to help
+it catch up to what has previously been printed on the terminal.
+
+@p procedure open_log_file;
+var @!old_setting:0..max_selector; {previous |selector| setting}
+@!k:0..buf_size; {index into |months| and |buffer|}
+@!l:0..buf_size; {end of first input line}
+@!m:integer; {the current month}
+@!months:packed array [1..36] of char; {abbreviations of month names}
+begin old_setting:=selector;
+if job_name=0 then job_name:="mfput";
+@.mfput@>
+pack_job_name(".log");
+while not a_open_out(log_file) do @<Try to get a different log file name@>;
+log_name:=a_make_name_string(log_file);
+selector:=log_only; log_opened:=true;
+@<Print the banner line, including the date and time@>;
+input_stack[input_ptr]:=cur_input; {make sure bottom level is in memory}
+print_nl("**");
+@.**@>
+l:=input_stack[0].limit_field-1; {last position of first line}
+for k:=1 to l do print(buffer[k]);
+print_ln; {now the transcript file contains the first line of input}
+selector:=old_setting+2; {|log_only| or |term_and_log|}
+end;
+
+@ Sometimes |open_log_file| is called at awkward moments when \MF\ is
+unable to print error messages or even to |show_context|.
+The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
+routine will not be invoked because |log_opened| will be false.
+
+The normal idea of |batch_mode| is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+Incidentally, the program always refers to the log file as a `\.{transcript
+file}', because some systems cannot use the extension `\.{.log}' for
+this file.
+
+@<Try to get a different log file name@>=
+begin selector:=term_only;
+prompt_file_name("transcript file name",".log");
+end
+
+@ @<Print the banner...@>=
+begin wlog(banner);
+slow_print(base_ident); print(" ");
+print_int(round_unscaled(internal[day])); print_char(" ");
+months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
+m:=round_unscaled(internal[month]);
+for k:=3*m-2 to 3*m do wlog(months[k]);
+print_char(" "); print_int(round_unscaled(internal[year])); print_char(" ");
+m:=round_unscaled(internal[time]);
+print_dd(m div 60); print_char(":"); print_dd(m mod 60);
+end
+
+@ Here's an example of how these file-name-parsing routines work in practice.
+We shall use the macro |set_output_file_name| when it is time to
+crank up the output file.
+
+@d set_output_file_name==
+ begin if job_name=0 then open_log_file;
+ pack_job_name(gf_ext);
+ while not b_open_out(gf_file) do
+ prompt_file_name("file name for output",gf_ext);
+ output_file_name:=b_make_name_string(gf_file);
+ end
+
+@<Glob...@>=
+@!gf_file: byte_file; {the generic font output goes here}
+@!output_file_name: str_number; {full name of the output file}
+
+@ @<Initialize the output...@>=output_file_name:=0;
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{input}' command is being processed.
+
+@p procedure start_input; {\MF\ will \.{input} something}
+label done;
+begin @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>;
+if cur_ext="" then cur_ext:=".mf";
+pack_cur_name;
+loop@+ begin begin_file_reading; {set up |cur_file| and new level of input}
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,MF_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+ end_file_reading; {remove the level that didn't work}
+ prompt_file_name("input file name",".mf");
+ end;
+done: name:=a_make_name_string(cur_file); str_ref[cur_name]:=max_str_ref;
+if job_name=0 then
+ begin job_name:=cur_name; open_log_file;
+ end; {|open_log_file| doesn't |show_context|, so |limit|
+ and |loc| needn't be set to meaningful values yet}
+if term_offset+length(name)>max_print_line-2 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+print_char("("); incr(open_parens); slow_print(name); update_terminal;
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string(name); name:=cur_name;
+ end;
+@<Read the first line of the new file@>;
+end;
+
+@ Here we have to remember to tell the |input_ln| routine not to
+start with a |get|. If the file is empty, it is considered to
+contain a single blank line.
+@^system dependencies@>
+
+@<Read the first line...@>=
+begin line:=1;
+if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+buffer[limit]:="%"; first:=limit+1; loc:=start;
+end
+
+@ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>=
+while token_state and(loc=null) do end_token_list;
+if token_state then
+ begin print_err("File names can't appear within macros");
+@.File names can't...@>
+ help3("Sorry...I've converted what follows to tokens,")@/
+ ("possibly garbaging the name you gave.")@/
+ ("Please delete the tokens and insert the name again.");@/
+ error;
+ end;
+if file_state then scan_file_name
+else begin cur_name:=""; cur_ext:=""; cur_area:="";
+ end
+
+@* \[39] Introduction to the parsing routines.
+We come now to the central nervous system that sparks many of \MF's activities.
+By evaluating expressions, from their primary constituents to ever larger
+subexpressions, \MF\ builds the structures that ultimately define fonts of type.
+
+Four mutually recursive subroutines are involved in this process: We call them
+$$\hbox{|scan_primary|, |scan_secondary|, |scan_tertiary|,
+and |scan_expression|.}$$
+@^recursion@>
+Each of them is parameterless and begins with the first token to be scanned
+already represented in |cur_cmd|, |cur_mod|, and |cur_sym|. After execution,
+the value of the primary or secondary or tertiary or expression that was
+found will appear in the global variables |cur_type| and |cur_exp|. The
+token following the expression will be represented in |cur_cmd|, |cur_mod|,
+and |cur_sym|.
+
+Technically speaking, the parsing algorithms are ``LL(1),'' more or less;
+backup mechanisms have been added in order to provide reasonable error
+recovery.
+
+@<Glob...@>=
+@!cur_type:small_number; {the type of the expression just found}
+@!cur_exp:integer; {the value of the expression just found}
+
+@ @<Set init...@>=
+cur_exp:=0;
+
+@ Many different kinds of expressions are possible, so it is wise to have
+precise descriptions of what |cur_type| and |cur_exp| mean in all cases:
+
+\smallskip\hang
+|cur_type=vacuous| means that this expression didn't turn out to have a
+value at all, because it arose from a \&{begingroup}$\,\ldots\,$\&{endgroup}
+construction in which there was no expression before the \&{endgroup}.
+In this case |cur_exp| has some irrelevant value.
+
+\smallskip\hang
+|cur_type=boolean_type| means that |cur_exp| is either |true_code|
+or |false_code|.
+
+\smallskip\hang
+|cur_type=unknown_boolean| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent booleans whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=string_type| means that |cur_exp| is a string number (i.e., an
+integer in the range |0<=cur_exp<str_ptr|). That string's reference count
+includes this particular reference.
+
+\smallskip\hang
+|cur_type=unknown_string| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent strings whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=pen_type| means that |cur_exp| points to a pen header node. This
+node contains a reference count, which takes account of this particular
+reference.
+
+\smallskip\hang
+|cur_type=unknown_pen| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent pens whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=future_pen| means that |cur_exp| points to a knot list that
+should eventually be made into a pen. Nobody else points to this particular
+knot list. The |future_pen| option occurs only as an output of |scan_primary|
+and |scan_secondary|, not as an output of |scan_tertiary| or |scan_expression|.
+
+\smallskip\hang
+|cur_type=path_type| means that |cur_exp| points to a the first node of
+a path; nobody else points to this particular path. The control points of
+the path will have been chosen.
+
+\smallskip\hang
+|cur_type=unknown_path| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent paths whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=picture_type| means that |cur_exp| points to an edges header node.
+Nobody else points to this particular set of edges.
+
+\smallskip\hang
+|cur_type=unknown_picture| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent pictures whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=transform_type| means that |cur_exp| points to a |transform_type|
+capsule node. The |value| part of this capsule
+points to a transform node that contains six numeric values,
+each of which is |independent|, |dependent|, |proto_dependent|, or |known|.
+
+\smallskip\hang
+|cur_type=pair_type| means that |cur_exp| points to a capsule
+node whose type is |pair_type|. The |value| part of this capsule
+points to a pair node that contains two numeric values,
+each of which is |independent|, |dependent|, |proto_dependent|, or |known|.
+
+\smallskip\hang
+|cur_type=known| means that |cur_exp| is a |scaled| value.
+
+\smallskip\hang
+|cur_type=dependent| means that |cur_exp| points to a capsule node whose type
+is |dependent|. The |dep_list| field in this capsule points to the associated
+dependency list.
+
+\smallskip\hang
+|cur_type=proto_dependent| means that |cur_exp| points to a |proto_dependent|
+capsule node . The |dep_list| field in this capsule
+points to the associated dependency list.
+
+\smallskip\hang
+|cur_type=independent| means that |cur_exp| points to a capsule node
+whose type is |independent|. This somewhat unusual case can arise, for
+example, in the expression
+`$x+\&{begingroup}\penalty0\,\&{string}\,x; 0\,\&{endgroup}$'.
+
+\smallskip\hang
+|cur_type=token_list| means that |cur_exp| points to a linked list of
+tokens.
+
+\smallskip\noindent
+The possible settings of |cur_type| have been listed here in increasing
+numerical order. Notice that |cur_type| will never be |numeric_type| or
+|suffixed_macro| or |unsuffixed_macro|, although variables of those types
+are allowed. Conversely, \MF\ has no variables of type |vacuous| or
+|token_list|.
+
+@ Capsules are two-word nodes that have a similar meaning
+to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|,
+and their |type| field is one of the possibilities for |cur_type| listed above.
+Also |link<=void| in capsules that aren't part of a token list.
+
+The |value| field of a capsule is, in most cases, the value that
+corresponds to its |type|, as |cur_exp| corresponds to |cur_type|.
+However, when |cur_exp| would point to a capsule,
+no extra layer of indirection is present; the |value|
+field is what would have been called |value(cur_exp)| if it had not been
+encapsulated. Furthermore, if the type is |dependent| or
+|proto_dependent|, the |value| field of a capsule is replaced by
+|dep_list| and |prev_dep| fields, since dependency lists in capsules are
+always part of the general |dep_list| structure.
+
+The |get_x_next| routine is careful not to change the values of |cur_type|
+and |cur_exp| when it gets an expanded token. However, |get_x_next| might
+call a macro, which might parse an expression, which might execute lots of
+commands in a group; hence it's possible that |cur_type| might change
+from, say, |unknown_boolean| to |boolean_type|, or from |dependent| to
+|known| or |independent|, during the time |get_x_next| is called. The
+programs below are careful to stash sensitive intermediate results in
+capsules, so that \MF's generality doesn't cause trouble.
+
+Here's a procedure that illustrates these conventions. It takes
+the contents of $(|cur_type|\kern-.3pt,|cur_exp|\kern-.3pt)$
+and stashes them away in a
+capsule. It is not used when |cur_type=token_list|.
+After the operation, |cur_type=vacuous|; hence there is no need to
+copy path lists or to update reference counts, etc.
+
+The special link |void| is put on the capsule returned by
+|stash_cur_exp|, because this procedure is used to store macro parameters
+that must be easily distinguishable from token lists.
+
+@<Declare the stashing/unstashing routines@>=
+function stash_cur_exp:pointer;
+var @!p:pointer; {the capsule that will be returned}
+begin case cur_type of
+unknown_types,transform_type,pair_type,dependent,proto_dependent,
+ independent:p:=cur_exp;
+othercases begin p:=get_node(value_node_size); name_type(p):=capsule;
+ type(p):=cur_type; value(p):=cur_exp;
+ end
+endcases;@/
+cur_type:=vacuous; link(p):=void; stash_cur_exp:=p;
+end;
+
+@ The inverse of |stash_cur_exp| is the following procedure, which
+deletes an unnecessary capsule and puts its contents into |cur_type|
+and |cur_exp|.
+
+The program steps of \MF\ can be divided into two categories: those in
+which |cur_type| and |cur_exp| are ``alive'' and those in which they are
+``dead,'' in the sense that |cur_type| and |cur_exp| contain relevant
+information or not. It's important not to ignore them when they're alive,
+and it's important not to pay attention to them when they're dead.
+
+There's also an intermediate category: If |cur_type=vacuous|, then
+|cur_exp| is irrelevant, hence we can proceed without caring if |cur_type|
+and |cur_exp| are alive or dead. In such cases we say that |cur_type|
+and |cur_exp| are {\sl dormant}. It is permissible to call |get_x_next|
+only when they are alive or dormant.
+
+The \\{stash} procedure above assumes that |cur_type| and |cur_exp|
+are alive or dormant. The \\{unstash} procedure assumes that they are
+dead or dormant; it resuscitates them.
+
+@<Declare the stashing/unstashing...@>=
+procedure unstash_cur_exp(@!p:pointer);
+begin cur_type:=type(p);
+case cur_type of
+unknown_types,transform_type,pair_type,dependent,proto_dependent,
+ independent: cur_exp:=p;
+othercases begin cur_exp:=value(p);
+ free_node(p,value_node_size);
+ end
+endcases;@/
+end;
+
+@ The following procedure prints the values of expressions in an
+abbreviated format. If its first parameter |p| is null, the value of
+|(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule
+containing the desired value. The second parameter controls the amount of
+output. If it is~0, dependency lists will be abbreviated to
+`\.{linearform}' unless they consist of a single term. If it is greater
+than~1, complicated structures (pens, pictures, and paths) will be displayed
+in full.
+@.linearform@>
+
+@<Declare subroutines for printing expressions@>=
+@t\4@>@<Declare the procedure called |print_dp|@>@;
+@t\4@>@<Declare the stashing/unstashing routines@>@;
+procedure print_exp(@!p:pointer;@!verbosity:small_number);
+var @!restore_cur_exp:boolean; {should |cur_exp| be restored?}
+@!t:small_number; {the type of the expression}
+@!v:integer; {the value of the expression}
+@!q:pointer; {a big node being displayed}
+begin if p<>null then restore_cur_exp:=false
+else begin p:=stash_cur_exp; restore_cur_exp:=true;
+ end;
+t:=type(p);
+if t<dependent then v:=value(p)@+else if t<independent then v:=dep_list(p);
+@<Print an abbreviated value of |v| with format depending on |t|@>;
+if restore_cur_exp then unstash_cur_exp(p);
+end;
+
+@ @<Print an abbreviated value of |v| with format depending on |t|@>=
+case t of
+vacuous:print("vacuous");
+boolean_type:if v=true_code then print("true")@+else print("false");
+unknown_types,numeric_type:@<Display a variable
+ that's been declared but not defined@>;
+string_type:begin print_char(""""); slow_print(v); print_char("""");
+ end;
+pen_type,future_pen,path_type,picture_type:@<Display a complex type@>;
+transform_type,pair_type:if v=null then print_type(t)
+ else @<Display a big node@>;
+known:print_scaled(v);
+dependent,proto_dependent:print_dp(t,v,verbosity);
+independent:print_variable_name(p);
+othercases confusion("exp")
+@:this can't happen exp}{\quad exp@>
+endcases
+
+@ @<Display a big node@>=
+begin print_char("("); q:=v+big_node_size[t];
+repeat if type(v)=known then print_scaled(value(v))
+else if type(v)=independent then print_variable_name(v)
+else print_dp(type(v),dep_list(v),verbosity);
+v:=v+2;
+if v<>q then print_char(",");
+until v=q;
+print_char(")");
+end
+
+@ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely
+in the log file only, unless the user has given a positive value to
+\\{tracingonline}.
+
+@<Display a complex type@>=
+if verbosity<=1 then print_type(t)
+else begin if selector=term_and_log then
+ if internal[tracing_online]<=0 then
+ begin selector:=term_only;
+ print_type(t); print(" (see the transcript file)");
+ selector:=term_and_log;
+ end;
+ case t of
+ pen_type:print_pen(v,"",false);
+ future_pen:print_path(v," (future pen)",false);
+ path_type:print_path(v,"",false);
+ picture_type:begin cur_edges:=v; print_edges("",false,0,0);
+ end;
+ end; {there are no other cases}
+ end
+
+@ @<Declare the procedure called |print_dp|@>=
+procedure print_dp(@!t:small_number;@!p:pointer;@!verbosity:small_number);
+var @!q:pointer; {the node following |p|}
+begin q:=link(p);
+if (info(q)=null) or (verbosity>0) then print_dependency(p,t)
+else print("linearform");
+@.linearform@>
+end;
+
+@ The displayed name of a variable in a ring will not be a capsule unless
+the ring consists entirely of capsules.
+
+@<Display a variable that's been declared but not defined@>=
+begin print_type(t);
+if v<>null then
+ begin print_char(" ");
+ while (name_type(v)=capsule) and (v<>p) do v:=value(v);
+ print_variable_name(v);
+ end;
+end
+
+@ When errors are detected during parsing, it is often helpful to
+display an expression just above the error message, using |exp_err|
+or |disp_err| instead of |print_err|.
+
+@d exp_err(#)==disp_err(null,#) {displays the current expression}
+
+@<Declare subroutines for printing expressions@>=
+procedure disp_err(@!p:pointer;@!s:str_number);
+begin if interaction=error_stop_mode then wake_up_terminal;
+print_nl(">> ");
+@.>>@>
+print_exp(p,1); {``medium verbose'' printing of the expression}
+if s<>"" then
+ begin print_nl("! "); print(s);
+@.!\relax@>
+ end;
+end;
+
+@ If |cur_type| and |cur_exp| contain relevant information that should
+be recycled, we will use the following procedure, which changes |cur_type|
+to |known| and stores a given value in |cur_exp|. We can think of |cur_type|
+and |cur_exp| as either alive or dormant after this has been done,
+because |cur_exp| will not contain a pointer value.
+
+@<Declare the procedure called |flush_cur_exp|@>=
+procedure flush_cur_exp(@!v:scaled);
+begin case cur_type of
+unknown_types,transform_type,pair_type,@|dependent,proto_dependent,independent:
+ begin recycle_value(cur_exp); free_node(cur_exp,value_node_size);
+ end;
+pen_type: delete_pen_ref(cur_exp);
+string_type:delete_str_ref(cur_exp);
+future_pen,path_type: toss_knot_list(cur_exp);
+picture_type:toss_edges(cur_exp);
+othercases do_nothing
+endcases;@/
+cur_type:=known; cur_exp:=v;
+end;
+
+@ There's a much more general procedure that is capable of releasing
+the storage associated with any two-word value packet.
+
+@<Declare the recycling subroutines@>=
+procedure recycle_value(@!p:pointer);
+label done;
+var @!t:small_number; {a type code}
+@!v:integer; {a value}
+@!vv:integer; {another value}
+@!q,@!r,@!s,@!pp:pointer; {link manipulation registers}
+begin t:=type(p);
+if t<dependent then v:=value(p);
+case t of
+undefined,vacuous,boolean_type,known,numeric_type:do_nothing;
+unknown_types:ring_delete(p);
+string_type:delete_str_ref(v);
+pen_type:delete_pen_ref(v);
+path_type,future_pen:toss_knot_list(v);
+picture_type:toss_edges(v);
+pair_type,transform_type:@<Recycle a big node@>;
+dependent,proto_dependent:@<Recycle a dependency list@>;
+independent:@<Recycle an independent variable@>;
+token_list,structured:confusion("recycle");
+@:this can't happen recycle}{\quad recycle@>
+unsuffixed_macro,suffixed_macro:delete_mac_ref(value(p));
+end; {there are no other cases}
+type(p):=undefined;
+end;
+
+@ @<Recycle a big node@>=
+if v<>null then
+ begin q:=v+big_node_size[t];
+ repeat q:=q-2; recycle_value(q);
+ until q=v;
+ free_node(v,big_node_size[t]);
+ end
+
+@ @<Recycle a dependency list@>=
+begin q:=dep_list(p);
+while info(q)<>null do q:=link(q);
+link(prev_dep(p)):=link(q);
+prev_dep(link(q)):=prev_dep(p);
+link(q):=null; flush_node_list(dep_list(p));
+end
+
+@ When an independent variable disappears, it simply fades away, unless
+something depends on it. In the latter case, a dependent variable whose
+coefficient of dependence is maximal will take its place.
+The relevant algorithm is due to Ignacio~A. Zabala, who implemented it
+as part of his Ph.D. thesis (Stanford University, December 1982).
+@^Zabala Salelles, Ignacio Andr\'es@>
+
+For example, suppose that variable $x$ is being recycled, and that the
+only variables depending on~$x$ are $y=2x+a$ and $z=x+b$. In this case
+we want to make $y$ independent and $z=.5y-.5a+b$; no other variables
+will depend on~$y$. If $\\{tracingequations}>0$ in this situation,
+we will print `\.{\#\#\# -2x=-y+a}'.
+
+There's a slight complication, however: An independent variable $x$
+can occur both in dependency lists and in proto-dependency lists.
+This makes it necessary to be careful when deciding which coefficient
+is maximal.
+
+Furthermore, this complication is not so slight when
+a proto-dependent variable is chosen to become independent. For example,
+suppose that $y=2x+100a$ is proto-dependent while $z=x+b$ is dependent;
+then we must change $z=.5y-50a+b$ to a proto-dependency, because of the
+large coefficient `50'.
+
+In order to deal with these complications without wasting too much time,
+we shall link together the occurrences of~$x$ among all the linear
+dependencies, maintaining separate lists for the dependent and
+proto-dependent cases.
+
+@<Recycle an independent variable@>=
+begin max_c[dependent]:=0; max_c[proto_dependent]:=0;@/
+max_link[dependent]:=null; max_link[proto_dependent]:=null;@/
+q:=link(dep_head);
+while q<>dep_head do
+ begin s:=value_loc(q); {now |link(s)=dep_list(q)|}
+ loop@+ begin r:=link(s);
+ if info(r)=null then goto done;
+ if info(r)<>p then s:=r
+ else begin t:=type(q); link(s):=link(r); info(r):=q;
+ if abs(value(r))>max_c[t] then
+ @<Record a new maximum coefficient of type |t|@>
+ else begin link(r):=max_link[t]; max_link[t]:=r;
+ end;
+ end;
+ end;
+done: q:=link(r);
+ end;
+if (max_c[dependent]>0)or(max_c[proto_dependent]>0) then
+ @<Choose a dependent variable to take the place of the disappearing
+ independent variable, and change all remaining dependencies
+ accordingly@>;
+end
+
+@ The code for independency removal makes use of three two-word arrays.
+
+@<Glob...@>=
+@!max_c:array[dependent..proto_dependent] of integer;
+ {max coefficient magnitude}
+@!max_ptr:array[dependent..proto_dependent] of pointer;
+ {where |p| occurs with |max_c|}
+@!max_link:array[dependent..proto_dependent] of pointer;
+ {other occurrences of |p|}
+
+@ @<Record a new maximum coefficient...@>=
+begin if max_c[t]>0 then
+ begin link(max_ptr[t]):=max_link[t]; max_link[t]:=max_ptr[t];
+ end;
+max_c[t]:=abs(value(r)); max_ptr[t]:=r;
+end
+
+@ @<Choose a dependent...@>=
+begin if (max_c[dependent] div @'10000 >=
+ max_c[proto_dependent]) then
+ t:=dependent
+else t:=proto_dependent;
+@<Determine the dependency list |s| to substitute for the independent
+ variable~|p|@>;
+t:=dependent+proto_dependent-t; {complement |t|}
+if max_c[t]>0 then {we need to pick up an unchosen dependency}
+ begin link(max_ptr[t]):=max_link[t]; max_link[t]:=max_ptr[t];
+ end;
+if t<>dependent then @<Substitute new dependencies in place of |p|@>
+else @<Substitute new proto-dependencies in place of |p|@>;
+flush_node_list(s);
+if fix_needed then fix_dependencies;
+check_arith;
+end
+
+@ Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$,
+and |info(s)| points to the dependent variable~|pp| of type~|t| from
+whose dependency list we have removed node~|s|. We must reinsert
+node~|s| into the dependency list, with coefficient $-1.0$, and with
+|pp| as the new independent variable. Since |pp| will have a larger serial
+number than any other variable, we can put node |s| at the head of the
+list.
+
+@<Determine the dep...@>=
+s:=max_ptr[t]; pp:=info(s); v:=value(s);
+if t=dependent then value(s):=-fraction_one@+else value(s):=-unity;
+r:=dep_list(pp); link(s):=r;
+while info(r)<>null do r:=link(r);
+q:=link(r); link(r):=null;
+prev_dep(q):=prev_dep(pp); link(prev_dep(pp)):=q;
+new_indep(pp);
+if cur_exp=pp then if cur_type=t then cur_type:=independent;
+if internal[tracing_equations]>0 then @<Show the transformed dependency@>
+
+@ Now $(-v)$ times the formerly independent variable~|p| is being replaced
+by the dependency list~|s|.
+
+@<Show the transformed...@>=
+if interesting(p) then
+ begin begin_diagnostic; print_nl("### ");
+@:]]]\#\#\#_}{\.{\#\#\#}@>
+ if v>0 then print_char("-");
+ if t=dependent then vv:=round_fraction(max_c[dependent])
+ else vv:=max_c[proto_dependent];
+ if vv<>unity then print_scaled(vv);
+ print_variable_name(p);
+ while value(p) mod s_scale>0 do
+ begin print("*4"); value(p):=value(p)-2;
+ end;
+ if t=dependent then print_char("=")@+else print(" = ");
+ print_dependency(s,t);
+ end_diagnostic(false);
+ end
+
+@ Finally, there are dependent and proto-dependent variables whose
+dependency lists must be brought up to date.
+
+@<Substitute new dependencies...@>=
+for t:=dependent to proto_dependent do
+ begin r:=max_link[t];
+ while r<>null do
+ begin q:=info(r);
+ dep_list(q):=p_plus_fq(dep_list(q),@|
+ make_fraction(value(r),-v),s,t,dependent);
+ if dep_list(q)=dep_final then make_known(q,dep_final);
+ q:=r; r:=link(r); free_node(q,dep_node_size);
+ end;
+ end
+
+@ @<Substitute new proto...@>=
+for t:=dependent to proto_dependent do
+ begin r:=max_link[t];
+ while r<>null do
+ begin q:=info(r);
+ if t=dependent then {for safety's sake, we change |q| to |proto_dependent|}
+ begin if cur_exp=q then if cur_type=dependent then
+ cur_type:=proto_dependent;
+ dep_list(q):=p_over_v(dep_list(q),unity,dependent,proto_dependent);
+ type(q):=proto_dependent; value(r):=round_fraction(value(r));
+ end;
+ dep_list(q):=p_plus_fq(dep_list(q),@|
+ make_scaled(value(r),-v),s,proto_dependent,proto_dependent);
+ if dep_list(q)=dep_final then make_known(q,dep_final);
+ q:=r; r:=link(r); free_node(q,dep_node_size);
+ end;
+ end
+
+@ Here are some routines that provide handy combinations of actions
+that are often needed during error recovery. For example,
+`|flush_error|' flushes the current expression, replaces it by
+a given value, and calls |error|.
+
+Errors often are detected after an extra token has already been scanned.
+The `\\{put\_get}' routines put that token back before calling |error|;
+then they get it back again. (Or perhaps they get another token, if
+the user has changed things.)
+
+@<Declare the procedure called |flush_cur_exp|@>=
+procedure flush_error(@!v:scaled);@+begin error; flush_cur_exp(v);@+end;
+@#
+procedure@?back_error; forward;@t\2@>@/
+procedure@?get_x_next; forward;@t\2@>@/
+@#
+procedure put_get_error;@+begin back_error; get_x_next;@+end;
+@#
+procedure put_get_flush_error(@!v:scaled);@+begin put_get_error;
+ flush_cur_exp(v);@+end;
+
+@ A global variable called |var_flag| is set to a special command code
+just before \MF\ calls |scan_expression|, if the expression should be
+treated as a variable when this command code immediately follows. For
+example, |var_flag| is set to |assignment| at the beginning of a
+statement, because we want to know the {\sl location\/} of a variable at
+the left of `\.{:=}', not the {\sl value\/} of that variable.
+
+The |scan_expression| subroutine calls |scan_tertiary|,
+which calls |scan_secondary|, which calls |scan_primary|, which sets
+|var_flag:=0|. In this way each of the scanning routines ``knows''
+when it has been called with a special |var_flag|, but |var_flag| is
+usually zero.
+
+A variable preceding a command that equals |var_flag| is converted to a
+token list rather than a value. Furthermore, an `\.{=}' sign following an
+expression with |var_flag=assignment| is not considered to be a relation
+that produces boolean expressions.
+
+
+@<Glob...@>=
+@!var_flag:0..max_command_code; {command that wants a variable}
+
+@ @<Set init...@>=
+var_flag:=0;
+
+@* \[40] Parsing primary expressions.
+The first parsing routine, |scan_primary|, is also the most complicated one,
+since it involves so many different cases. But each case---with one
+exception---is fairly simple by itself.
+
+When |scan_primary| begins, the first token of the primary to be scanned
+should already appear in |cur_cmd|, |cur_mod|, and |cur_sym|. The values
+of |cur_type| and |cur_exp| should be either dead or dormant, as explained
+earlier. If |cur_cmd| is not between |min_primary_command| and
+|max_primary_command|, inclusive, a syntax error will be signalled.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_primary;
+label restart, done, done1, done2;
+var @!p,@!q,@!r:pointer; {for list manipulation}
+@!c:quarterword; {a primitive operation code}
+@!my_var_flag:0..max_command_code; {initial value of |var_flag|}
+@!l_delim,@!r_delim:pointer; {hash addresses of a delimiter pair}
+@<Other local variables for |scan_primary|@>@;
+begin my_var_flag:=var_flag; var_flag:=0;
+restart:check_arith;
+@<Supply diagnostic information, if requested@>;
+case cur_cmd of
+left_delimiter:@<Scan a delimited primary@>;
+begin_group:@<Scan a grouped primary@>;
+string_token:@<Scan a string constant@>;
+numeric_token:@<Scan a primary that starts with a numeric token@>;
+nullary:@<Scan a nullary operation@>;
+unary,type_name,cycle,plus_or_minus:@<Scan a unary operation@>;
+primary_binary:@<Scan a binary operation with `\&{of}' between its operands@>;
+str_op:@<Convert a suffix to a string@>;
+internal_quantity:@<Scan an internal numeric quantity@>;
+capsule_token:make_exp_copy(cur_mod);
+tag_token:@<Scan a variable primary;
+ |goto restart| if it turns out to be a macro@>;
+othercases begin bad_exp("A primary"); goto restart;
+@.A primary expression...@>
+ end
+endcases;@/
+get_x_next; {the routines |goto done| if they don't want this}
+done: if cur_cmd=left_bracket then
+ if cur_type>=known then @<Scan a mediation construction@>;
+end;
+
+@ Errors at the beginning of expressions are flagged by |bad_exp|.
+
+@p procedure bad_exp(@!s:str_number);
+var save_flag:0..max_command_code;
+begin print_err(s); print(" expression can't begin with `");
+print_cmd_mod(cur_cmd,cur_mod); print_char("'");
+help4("I'm afraid I need some sort of value in order to continue,")@/
+ ("so I've tentatively inserted `0'. You may want to")@/
+ ("delete this zero and insert something else;")@/
+ ("see Chapter 27 of The METAFONTbook for an example.");
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+back_input; cur_sym:=0; cur_cmd:=numeric_token; cur_mod:=0; ins_error;@/
+save_flag:=var_flag; var_flag:=0; get_x_next;
+var_flag:=save_flag;
+end;
+
+@ @<Supply diagnostic information, if requested@>=
+debug if panicking then check_mem(false);@+gubed@;@/
+if interrupt<>0 then if OK_to_interrupt then
+ begin back_input; check_interrupt; get_x_next;
+ end
+
+@ @<Scan a delimited primary@>=
+begin l_delim:=cur_sym; r_delim:=cur_mod; get_x_next; scan_expression;
+if (cur_cmd=comma) and (cur_type>=known) then
+ @<Scan the second of a pair of numerics@>
+else check_delimiter(l_delim,r_delim);
+end
+
+@ The |stash_in| subroutine puts the current (numeric) expression into a field
+within a ``big node.''
+
+@p procedure stash_in(@!p:pointer);
+var @!q:pointer; {temporary register}
+begin type(p):=cur_type;
+if cur_type=known then value(p):=cur_exp
+else begin if cur_type=independent then
+ @<Stash an independent |cur_exp| into a big node@>
+ else begin mem[value_loc(p)]:=mem[value_loc(cur_exp)];
+ {|dep_list(p):=dep_list(cur_exp)| and |prev_dep(p):=prev_dep(cur_exp)|}
+ link(prev_dep(p)):=p;
+ end;
+ free_node(cur_exp,value_node_size);
+ end;
+cur_type:=vacuous;
+end;
+
+@ In rare cases the current expression can become |independent|. There
+may be many dependency lists pointing to such an independent capsule,
+so we can't simply move it into place within a big node. Instead,
+we copy it, then recycle it.
+
+@ @<Stash an independent |cur_exp|...@>=
+begin q:=single_dependency(cur_exp);
+if q=dep_final then
+ begin type(p):=known; value(p):=0; free_node(q,dep_node_size);
+ end
+else begin type(p):=dependent; new_dep(p,q);
+ end;
+recycle_value(cur_exp);
+end
+
+@ @<Scan the second of a pair of numerics@>=
+begin p:=get_node(value_node_size); type(p):=pair_type; name_type(p):=capsule;
+init_big_node(p); q:=value(p); stash_in(x_part_loc(q));@/
+get_x_next; scan_expression;
+if cur_type<known then
+ begin exp_err("Nonnumeric ypart has been replaced by 0");
+@.Nonnumeric...replaced by 0@>
+ help4("I thought you were giving me a pair `(x,y)'; but")@/
+ ("after finding a nice xpart `x' I found a ypart `y'")@/
+ ("that isn't of numeric type. So I've changed y to zero.")@/
+ ("(The y that I didn't like appears above the error message.)");
+ put_get_flush_error(0);
+ end;
+stash_in(y_part_loc(q));
+check_delimiter(l_delim,r_delim);
+cur_type:=pair_type; cur_exp:=p;
+end
+
+@ The local variable |group_line| keeps track of the line
+where a \&{begingroup} command occurred; this will be useful
+in an error message if the group doesn't actually end.
+
+@<Other local variables for |scan_primary|@>=
+@!group_line:integer; {where a group began}
+
+@ @<Scan a grouped primary@>=
+begin group_line:=line;
+if internal[tracing_commands]>0 then show_cur_cmd_mod;
+save_boundary_item(p);
+repeat do_statement; {ends with |cur_cmd>=semicolon|}
+until cur_cmd<>semicolon;
+if cur_cmd<>end_group then
+ begin print_err("A group begun on line ");
+@.A group...never ended@>
+ print_int(group_line);
+ print(" never ended");
+ help2("I saw a `begingroup' back there that hasn't been matched")@/
+ ("by `endgroup'. So I've inserted `endgroup' now.");
+ back_error; cur_cmd:=end_group;
+ end;
+unsave; {this might change |cur_type|, if independent variables are recycled}
+if internal[tracing_commands]>0 then show_cur_cmd_mod;
+end
+
+@ @<Scan a string constant@>=
+begin cur_type:=string_type; cur_exp:=cur_mod;
+end
+
+@ Later we'll come to procedures that perform actual operations like
+addition, square root, and so on; our purpose now is to do the parsing.
+But we might as well mention those future procedures now, so that the
+suspense won't be too bad:
+
+\smallskip
+|do_nullary(c)| does primitive operations that have no operands (e.g.,
+`\&{true}' or `\&{pencircle}');
+
+\smallskip
+|do_unary(c)| applies a primitive operation to the current expression;
+
+\smallskip
+|do_binary(p,c)| applies a primitive operation to the capsule~|p|
+and the current expression.
+
+@<Scan a nullary operation@>=do_nullary(cur_mod)
+
+@ @<Scan a unary operation@>=
+begin c:=cur_mod; get_x_next; scan_primary; do_unary(c); goto done;
+end
+
+@ A numeric token might be a primary by itself, or it might be the
+numerator of a fraction composed solely of numeric tokens, or it might
+multiply the primary that follows (provided that the primary doesn't begin
+with a plus sign or a minus sign). The code here uses the facts that
+|max_primary_command=plus_or_minus| and
+|max_primary_command-1=numeric_token|. If a fraction is found that is less
+than unity, we try to retain higher precision when we use it in scalar
+multiplication.
+
+@<Other local variables for |scan_primary|@>=
+@!num,@!denom:scaled; {for primaries that are fractions, like `1/2'}
+
+@ @<Scan a primary that starts with a numeric token@>=
+begin cur_exp:=cur_mod; cur_type:=known; get_x_next;
+if cur_cmd<>slash then
+ begin num:=0; denom:=0;
+ end
+else begin get_x_next;
+ if cur_cmd<>numeric_token then
+ begin back_input;
+ cur_cmd:=slash; cur_mod:=over; cur_sym:=frozen_slash;
+ goto done;
+ end;
+ num:=cur_exp; denom:=cur_mod;
+ if denom=0 then @<Protest division by zero@>
+ else cur_exp:=make_scaled(num,denom);
+ check_arith; get_x_next;
+ end;
+if cur_cmd>=min_primary_command then
+ if cur_cmd<numeric_token then {in particular, |cur_cmd<>plus_or_minus|}
+ begin p:=stash_cur_exp; scan_primary;
+ if (abs(num)>=abs(denom))or(cur_type<pair_type) then do_binary(p,times)
+ else begin frac_mult(num,denom);
+ free_node(p,value_node_size);
+ end;
+ end;
+goto done;
+end
+
+@ @<Protest division...@>=
+begin print_err("Division by zero");
+@.Division by zero@>
+help1("I'll pretend that you meant to divide by 1."); error;
+end
+
+@ @<Scan a binary operation with `\&{of}' between its operands@>=
+begin c:=cur_mod; get_x_next; scan_expression;
+if cur_cmd<>of_token then
+ begin missing_err("of"); print(" for "); print_cmd_mod(primary_binary,c);
+@.Missing `of'@>
+ help1("I've got the first argument; will look now for the other.");
+ back_error;
+ end;
+p:=stash_cur_exp; get_x_next; scan_primary; do_binary(p,c); goto done;
+end
+
+@ @<Convert a suffix to a string@>=
+begin get_x_next; scan_suffix; old_setting:=selector; selector:=new_string;
+show_token_list(cur_exp,null,100000,0); flush_token_list(cur_exp);
+cur_exp:=make_string; selector:=old_setting; cur_type:=string_type;
+goto done;
+end
+
+@ If an internal quantity appears all by itself on the left of an
+assignment, we return a token list of length one, containing the address
+of the internal quantity plus |hash_end|. (This accords with the conventions
+of the save stack, as described earlier.)
+
+@<Scan an internal...@>=
+begin q:=cur_mod;
+if my_var_flag=assignment then
+ begin get_x_next;
+ if cur_cmd=assignment then
+ begin cur_exp:=get_avail;
+ info(cur_exp):=q+hash_end; cur_type:=token_list; goto done;
+ end;
+ back_input;
+ end;
+cur_type:=known; cur_exp:=internal[q];
+end
+
+@ The most difficult part of |scan_primary| has been saved for last, since
+it was necessary to build up some confidence first. We can now face the task
+of scanning a variable.
+
+As we scan a variable, we build a token list containing the relevant
+names and subscript values, simultaneously following along in the
+``collective'' structure to see if we are actually dealing with a macro
+instead of a value.
+
+The local variables |pre_head| and |post_head| will point to the beginning
+of the prefix and suffix lists; |tail| will point to the end of the list
+that is currently growing.
+
+Another local variable, |tt|, contains partial information about the
+declared type of the variable-so-far. If |tt>=unsuffixed_macro|, the
+relation |tt=type(q)| will always hold. If |tt=undefined|, the routine
+doesn't bother to update its information about type. And if
+|undefined<tt<unsuffixed_macro|, the precise value of |tt| isn't critical.
+
+@ @<Other local variables for |scan_primary|@>=
+@!pre_head,@!post_head,@!tail:pointer;
+ {prefix and suffix list variables}
+@!tt:small_number; {approximation to the type of the variable-so-far}
+@!t:pointer; {a token}
+@!macro_ref:pointer; {reference count for a suffixed macro}
+
+@ @<Scan a variable primary...@>=
+begin fast_get_avail(pre_head); tail:=pre_head; post_head:=null; tt:=vacuous;
+loop@+ begin t:=cur_tok; link(tail):=t;
+ if tt<>undefined then
+ begin @<Find the approximate type |tt| and corresponding~|q|@>;
+ if tt>=unsuffixed_macro then
+ @<Either begin an unsuffixed macro call or
+ prepare for a suffixed one@>;
+ end;
+ get_x_next; tail:=t;
+ if cur_cmd=left_bracket then
+ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>;
+ if cur_cmd>max_suffix_token then goto done1;
+ if cur_cmd<min_suffix_token then goto done1;
+ end; {now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token|}
+done1:@<Handle unusual cases that masquerade as variables, and |goto restart|
+ or |goto done| if appropriate;
+ otherwise make a copy of the variable and |goto done|@>;
+end
+
+@ @<Either begin an unsuffixed macro call or...@>=
+begin link(tail):=null;
+if tt>unsuffixed_macro then {|tt=suffixed_macro|}
+ begin post_head:=get_avail; tail:=post_head; link(tail):=t;@/
+ tt:=undefined; macro_ref:=value(q); add_mac_ref(macro_ref);
+ end
+else @<Set up unsuffixed macro call and |goto restart|@>;
+end
+
+@ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>=
+begin get_x_next; scan_expression;
+if cur_cmd<>right_bracket then
+ @<Put the left bracket and the expression back to be rescanned@>
+else begin if cur_type<>known then bad_subscript;
+ cur_cmd:=numeric_token; cur_mod:=cur_exp; cur_sym:=0;
+ end;
+end
+
+@ The left bracket that we thought was introducing a subscript might have
+actually been the left bracket in a mediation construction like `\.{x[a,b]}'.
+So we don't issue an error message at this point; but we do want to back up
+so as to avoid any embarrassment about our incorrect assumption.
+
+@<Put the left bracket and the expression back to be rescanned@>=
+begin back_input; {that was the token following the current expression}
+back_expr; cur_cmd:=left_bracket; cur_mod:=0; cur_sym:=frozen_left_bracket;
+end
+
+@ Here's a routine that puts the current expression back to be read again.
+
+@p procedure back_expr;
+var @!p:pointer; {capsule token}
+begin p:=stash_cur_exp; link(p):=null; back_list(p);
+end;
+
+@ Unknown subscripts lead to the following error message.
+
+@p procedure bad_subscript;
+begin exp_err("Improper subscript has been replaced by zero");
+@.Improper subscript...@>
+help3("A bracketed subscript must have a known numeric value;")@/
+ ("unfortunately, what I found was the value that appears just")@/
+ ("above this error message. So I'll try a zero subscript.");
+flush_error(0);
+end;
+
+@ Every time we call |get_x_next|, there's a chance that the variable we've
+been looking at will disappear. Thus, we cannot safely keep |q| pointing
+into the variable structure; we need to start searching from the root each time.
+
+@<Find the approximate type |tt| and corresponding~|q|@>=
+@^inner loop@>
+begin p:=link(pre_head); q:=info(p); tt:=undefined;
+if eq_type(q) mod outer_tag=tag_token then
+ begin q:=equiv(q);
+ if q=null then goto done2;
+ loop@+ begin p:=link(p);
+ if p=null then
+ begin tt:=type(q); goto done2;
+ end;
+ if type(q)<>structured then goto done2;
+ q:=link(attr_head(q)); {the |collective_subscript| attribute}
+ if p>=hi_mem_min then {it's not a subscript}
+ begin repeat q:=link(q);
+ until attr_loc(q)>=info(p);
+ if attr_loc(q)>info(p) then goto done2;
+ end;
+ end;
+ end;
+done2:end
+
+@ How do things stand now? Well, we have scanned an entire variable name,
+including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and
+|cur_sym| represent the token that follows. If |post_head=null|, a
+token list for this variable name starts at |link(pre_head)|, with all
+subscripts evaluated. But if |post_head<>null|, the variable turned out
+to be a suffixed macro; |pre_head| is the head of the prefix list, while
+|post_head| is the head of a token list containing both `\.{\AT!}' and
+the suffix.
+
+Our immediate problem is to see if this variable still exists. (Variable
+structures can change drastically whenever we call |get_x_next|; users
+aren't supposed to do this, but the fact that it is possible means that
+we must be cautious.)
+
+The following procedure prints an error message when a variable
+unexpectedly disappears. Its help message isn't quite right for
+our present purposes, but we'll be able to fix that up.
+
+@p procedure obliterated(@!q:pointer);
+begin print_err("Variable "); show_token_list(q,null,1000,0);
+print(" has been obliterated");
+@.Variable...obliterated@>
+help5("It seems you did a nasty thing---probably by accident,")@/
+ ("but nevertheless you nearly hornswoggled me...")@/
+ ("While I was evaluating the right-hand side of this")@/
+ ("command, something happened, and the left-hand side")@/
+ ("is no longer a variable! So I won't change anything.");
+end;
+
+@ If the variable does exist, we also need to check
+for a few other special cases before deciding that a plain old ordinary
+variable has, indeed, been scanned.
+
+@<Handle unusual cases that masquerade as variables...@>=
+if post_head<>null then @<Set up suffixed macro call and |goto restart|@>;
+q:=link(pre_head); free_avail(pre_head);
+if cur_cmd=my_var_flag then
+ begin cur_type:=token_list; cur_exp:=q; goto done;
+ end;
+p:=find_variable(q);
+if p<>null then make_exp_copy(p)
+else begin obliterated(q);@/
+ help_line[2]:="While I was evaluating the suffix of this variable,";
+ help_line[1]:="something was redefined, and it's no longer a variable!";
+ help_line[0]:="In order to get back on my feet, I've inserted `0' instead.";
+ put_get_flush_error(0);
+ end;
+flush_node_list(q); goto done
+
+@ The only complication associated with macro calling is that the prefix
+and ``at'' parameters must be packaged in an appropriate list of lists.
+
+@<Set up unsuffixed macro call and |goto restart|@>=
+begin p:=get_avail; info(pre_head):=link(pre_head); link(pre_head):=p;
+info(p):=t; macro_call(value(q),pre_head,null); get_x_next; goto restart;
+end
+
+@ If the ``variable'' that turned out to be a suffixed macro no longer exists,
+we don't care, because we have reserved a pointer (|macro_ref|) to its
+token list.
+
+@<Set up suffixed macro call and |goto restart|@>=
+begin back_input; p:=get_avail; q:=link(post_head);
+info(pre_head):=link(pre_head); link(pre_head):=post_head;
+info(post_head):=q; link(post_head):=p; info(p):=link(q); link(q):=null;
+macro_call(macro_ref,pre_head,null); decr(ref_count(macro_ref));
+get_x_next; goto restart;
+end
+
+@ Our remaining job is simply to make a copy of the value that has been
+found. Some cases are harder than others, but complexity arises solely
+because of the multiplicity of possible cases.
+
+@<Declare the procedure called |make_exp_copy|@>=
+@t\4@>@<Declare subroutines needed by |make_exp_copy|@>@;
+procedure make_exp_copy(@!p:pointer);
+label restart;
+var @!q,@!r,@!t:pointer; {registers for list manipulation}
+begin restart: cur_type:=type(p);
+case cur_type of
+vacuous,boolean_type,known:cur_exp:=value(p);
+unknown_types:cur_exp:=new_ring_entry(p);
+string_type:begin cur_exp:=value(p); add_str_ref(cur_exp);
+ end;
+pen_type:begin cur_exp:=value(p); add_pen_ref(cur_exp);
+ end;
+picture_type:cur_exp:=copy_edges(value(p));
+path_type,future_pen:cur_exp:=copy_path(value(p));
+transform_type,pair_type:@<Copy the big node |p|@>;
+dependent,proto_dependent:encapsulate(copy_dep_list(dep_list(p)));
+numeric_type:begin new_indep(p); goto restart;
+ end;
+independent: begin q:=single_dependency(p);
+ if q=dep_final then
+ begin cur_type:=known; cur_exp:=0; free_node(q,dep_node_size);
+ end
+ else begin cur_type:=dependent; encapsulate(q);
+ end;
+ end;
+othercases confusion("copy")
+@:this can't happen copy}{\quad copy@>
+endcases;
+end;
+
+@ The |encapsulate| subroutine assumes that |dep_final| is the
+tail of dependency list~|p|.
+
+@<Declare subroutines needed by |make_exp_copy|@>=
+procedure encapsulate(@!p:pointer);
+begin cur_exp:=get_node(value_node_size); type(cur_exp):=cur_type;
+name_type(cur_exp):=capsule; new_dep(cur_exp,p);
+end;
+
+@ The most tedious case arises when the user refers to a
+\&{pair} or \&{transform} variable; we must copy several fields,
+each of which can be |independent|, |dependent|, |proto_dependent|,
+or |known|.
+
+@<Copy the big node |p|@>=
+begin if value(p)=null then init_big_node(p);
+t:=get_node(value_node_size); name_type(t):=capsule; type(t):=cur_type;
+init_big_node(t);@/
+q:=value(p)+big_node_size[cur_type]; r:=value(t)+big_node_size[cur_type];
+repeat q:=q-2; r:=r-2; install(r,q);
+until q=value(p);
+cur_exp:=t;
+end
+
+@ The |install| procedure copies a numeric field~|q| into field~|r| of
+a big node that will be part of a capsule.
+
+@<Declare subroutines needed by |make_exp_copy|@>=
+procedure install(@!r,@!q:pointer);
+var p:pointer; {temporary register}
+begin if type(q)=known then
+ begin value(r):=value(q); type(r):=known;
+ end
+else if type(q)=independent then
+ begin p:=single_dependency(q);
+ if p=dep_final then
+ begin type(r):=known; value(r):=0; free_node(p,dep_node_size);
+ end
+ else begin type(r):=dependent; new_dep(r,p);
+ end;
+ end
+ else begin type(r):=type(q); new_dep(r,copy_dep_list(dep_list(q)));
+ end;
+end;
+
+@ Expressions of the form `\.{a[b,c]}' are converted into
+`\.{b+a*(c-b)}', without checking the types of \.b~or~\.c,
+provided that \.a is numeric.
+
+@<Scan a mediation...@>=
+begin p:=stash_cur_exp; get_x_next; scan_expression;
+if cur_cmd<>comma then
+ begin @<Put the left bracket and the expression back...@>;
+ unstash_cur_exp(p);
+ end
+else begin q:=stash_cur_exp; get_x_next; scan_expression;
+ if cur_cmd<>right_bracket then
+ begin missing_err("]");@/
+@.Missing `]'@>
+ help3("I've scanned an expression of the form `a[b,c',")@/
+ ("so a right bracket should have come next.")@/
+ ("I shall pretend that one was there.");@/
+ back_error;
+ end;
+ r:=stash_cur_exp; make_exp_copy(q);@/
+ do_binary(r,minus); do_binary(p,times); do_binary(q,plus); get_x_next;
+ end;
+end
+
+@ Here is a comparatively simple routine that is used to scan the
+\&{suffix} parameters of a macro.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_suffix;
+label done;
+var @!h,@!t:pointer; {head and tail of the list being built}
+@!p:pointer; {temporary register}
+begin h:=get_avail; t:=h;
+loop@+ begin if cur_cmd=left_bracket then
+ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>;
+ if cur_cmd=numeric_token then p:=new_num_tok(cur_mod)
+ else if (cur_cmd=tag_token)or(cur_cmd=internal_quantity) then
+ begin p:=get_avail; info(p):=cur_sym;
+ end
+ else goto done;
+ link(t):=p; t:=p; get_x_next;
+ end;
+done: cur_exp:=link(h); free_avail(h); cur_type:=token_list;
+end;
+
+@ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>=
+begin get_x_next; scan_expression;
+if cur_type<>known then bad_subscript;
+if cur_cmd<>right_bracket then
+ begin missing_err("]");@/
+@.Missing `]'@>
+ help3("I've seen a `[' and a subscript value, in a suffix,")@/
+ ("so a right bracket should have come next.")@/
+ ("I shall pretend that one was there.");@/
+ back_error;
+ end;
+cur_cmd:=numeric_token; cur_mod:=cur_exp;
+end
+
+@* \[41] Parsing secondary and higher expressions.
+After the intricacies of |scan_primary|\kern-1pt,
+the |scan_secondary| routine is
+refreshingly simple. It's not trivial, but the operations are relatively
+straightforward; the main difficulty is, again, that expressions and data
+structures might change drastically every time we call |get_x_next|, so a
+cautious approach is mandatory. For example, a macro defined by
+\&{primarydef} might have disappeared by the time its second argument has
+been scanned; we solve this by increasing the reference count of its token
+list, so that the macro can be called even after it has been clobbered.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_secondary;
+label restart,continue;
+var @!p:pointer; {for list manipulation}
+@!c,@!d:halfword; {operation codes or modifiers}
+@!mac_name:pointer; {token defined with \&{primarydef}}
+begin restart:if(cur_cmd<min_primary_command)or@|
+ (cur_cmd>max_primary_command) then
+ bad_exp("A secondary");
+@.A secondary expression...@>
+scan_primary;
+continue: if cur_cmd<=max_secondary_command then
+ if cur_cmd>=min_secondary_command then
+ begin p:=stash_cur_exp; c:=cur_mod; d:=cur_cmd;
+ if d=secondary_primary_macro then
+ begin mac_name:=cur_sym; add_mac_ref(c);
+ end;
+ get_x_next; scan_primary;
+ if d<>secondary_primary_macro then do_binary(p,c)
+ else begin back_input; binary_mac(p,c,mac_name);
+ decr(ref_count(c)); get_x_next; goto restart;
+ end;
+ goto continue;
+ end;
+end;
+
+@ The following procedure calls a macro that has two parameters,
+|p| and |cur_exp|.
+
+@p procedure binary_mac(@!p,@!c,@!n:pointer);
+var @!q,@!r:pointer; {nodes in the parameter list}
+begin q:=get_avail; r:=get_avail; link(q):=r;@/
+info(q):=p; info(r):=stash_cur_exp;@/
+macro_call(c,q,n);
+end;
+
+@ The next procedure, |scan_tertiary|, is pretty much the same deal.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_tertiary;
+label restart,continue;
+var @!p:pointer; {for list manipulation}
+@!c,@!d:halfword; {operation codes or modifiers}
+@!mac_name:pointer; {token defined with \&{secondarydef}}
+begin restart:if(cur_cmd<min_primary_command)or@|
+ (cur_cmd>max_primary_command) then
+ bad_exp("A tertiary");
+@.A tertiary expression...@>
+scan_secondary;
+if cur_type=future_pen then materialize_pen;
+continue: if cur_cmd<=max_tertiary_command then
+ if cur_cmd>=min_tertiary_command then
+ begin p:=stash_cur_exp; c:=cur_mod; d:=cur_cmd;
+ if d=tertiary_secondary_macro then
+ begin mac_name:=cur_sym; add_mac_ref(c);
+ end;
+ get_x_next; scan_secondary;
+ if d<>tertiary_secondary_macro then do_binary(p,c)
+ else begin back_input; binary_mac(p,c,mac_name);
+ decr(ref_count(c)); get_x_next; goto restart;
+ end;
+ goto continue;
+ end;
+end;
+
+@ A |future_pen| becomes a full-fledged pen here.
+
+@p procedure materialize_pen;
+label common_ending;
+var @!a_minus_b,@!a_plus_b,@!major_axis,@!minor_axis:scaled; {ellipse variables}
+@!theta:angle; {amount by which the ellipse has been rotated}
+@!p:pointer; {path traverser}
+@!q:pointer; {the knot list to be made into a pen}
+begin q:=cur_exp;
+if left_type(q)=endpoint then
+ begin print_err("Pen path must be a cycle");
+@.Pen path must be a cycle@>
+ help2("I can't make a pen from the given path.")@/
+ ("So I've replaced it by the trivial path `(0,0)..cycle'.");
+ put_get_error; cur_exp:=null_pen; goto common_ending;
+ end
+else if left_type(q)=open then
+ @<Change node |q| to a path for an elliptical pen@>;
+cur_exp:=make_pen(q);
+common_ending: toss_knot_list(q); cur_type:=pen_type;
+end;
+
+@ We placed the three points $(0,0)$, $(1,0)$, $(0,1)$ into a \&{pencircle},
+and they have now been transformed to $(u,v)$, $(A+u,B+v)$, $(C+u,D+v)$;
+this gives us enough information to deduce the transformation
+$(x,y)\mapsto(Ax+Cy+u,Bx+Dy+v)$.
+
+Given ($A,B,C,D)$ we can always find $(a,b,\theta,\phi)$ such that
+$$\eqalign{A&=a\cos\phi\cos\theta-b\sin\phi\sin\theta;\cr
+B&=a\cos\phi\sin\theta+b\sin\phi\cos\theta;\cr
+C&=-a\sin\phi\cos\theta-b\cos\phi\sin\theta;\cr
+D&=-a\sin\phi\sin\theta+b\cos\phi\cos\theta.\cr}$$
+In this notation, the unit circle $(\cos t,\sin t)$ is transformed into
+$$\bigl(a\cos(\phi+t)\cos\theta-b\sin(\phi+t)\sin\theta,\;
+a\cos(\phi+t)\sin\theta+b\sin(\phi+t)\cos\theta\bigr)\;+\;(u,v),$$
+which is an ellipse with semi-axes~$(a,b)$, rotated by~$\theta$ and
+shifted by~$(u,v)$. To solve the stated equations, we note that it is
+necessary and sufficient to solve
+$$\eqalign{A-D&=(a-b)\cos(\theta-\phi),\cr
+B+C&=(a-b)\sin(\theta-\phi),\cr}
+\qquad
+\eqalign{A+D&=(a+b)\cos(\theta+\phi),\cr
+B-C&=(a+b)\sin(\theta+\phi);\cr}$$
+and it is easy to find $a-b$, $a+b$, $\theta-\phi$, and $\theta+\phi$
+from these formulas.
+
+The code below uses |(txx,tyx,txy,tyy,tx,ty)| to stand for
+$(A,B,C,D,u,v)$.
+
+@<Change node |q|...@>=
+begin tx:=x_coord(q); ty:=y_coord(q);
+txx:=left_x(q)-tx; tyx:=left_y(q)-ty;
+txy:=right_x(q)-tx; tyy:=right_y(q)-ty;
+a_minus_b:=pyth_add(txx-tyy,tyx+txy); a_plus_b:=pyth_add(txx+tyy,tyx-txy);
+major_axis:=half(a_minus_b+a_plus_b); minor_axis:=half(abs(a_plus_b-a_minus_b));
+if major_axis=minor_axis then theta:=0 {circle}
+else theta:=half(n_arg(txx-tyy,tyx+txy)+n_arg(txx+tyy,tyx-txy));
+free_node(q,knot_node_size);
+q:=make_ellipse(major_axis,minor_axis,theta);
+if (tx<>0)or(ty<>0) then @<Shift the coordinates of path |q|@>;
+end
+
+@ @<Shift the coordinates of path |q|@>=
+begin p:=q;
+repeat x_coord(p):=x_coord(p)+tx; y_coord(p):=y_coord(p)+ty; p:=link(p);
+until p=q;
+end
+
+@ Finally we reach the deepest level in our quartet of parsing routines.
+This one is much like the others; but it has an extra complication from
+paths, which materialize here.
+
+@d continue_path=25 {a label inside of |scan_expression|}
+@d finish_path=26 {another}
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_expression;
+label restart,done,continue,continue_path,finish_path,exit;
+var @!p,@!q,@!r,@!pp,@!qq:pointer; {for list manipulation}
+@!c,@!d:halfword; {operation codes or modifiers}
+@!my_var_flag:0..max_command_code; {initial value of |var_flag|}
+@!mac_name:pointer; {token defined with \&{tertiarydef}}
+@!cycle_hit:boolean; {did a path expression just end with `\&{cycle}'?}
+@!x,@!y:scaled; {explicit coordinates or tension at a path join}
+@!t:endpoint..open; {knot type following a path join}
+begin my_var_flag:=var_flag;
+restart:if(cur_cmd<min_primary_command)or@|
+ (cur_cmd>max_primary_command) then
+ bad_exp("An");
+@.An expression...@>
+scan_tertiary;
+continue: if cur_cmd<=max_expression_command then
+ if cur_cmd>=min_expression_command then
+ if (cur_cmd<>equals)or(my_var_flag<>assignment) then
+ begin p:=stash_cur_exp; c:=cur_mod; d:=cur_cmd;
+ if d=expression_tertiary_macro then
+ begin mac_name:=cur_sym; add_mac_ref(c);
+ end;
+ if (d<ampersand)or((d=ampersand)and@|
+ ((type(p)=pair_type)or(type(p)=path_type))) then
+ @<Scan a path construction operation;
+ but |return| if |p| has the wrong type@>
+ else begin get_x_next; scan_tertiary;
+ if d<>expression_tertiary_macro then do_binary(p,c)
+ else begin back_input; binary_mac(p,c,mac_name);
+ decr(ref_count(c)); get_x_next; goto restart;
+ end;
+ end;
+ goto continue;
+ end;
+exit:end;
+
+@ The reader should review the data structure conventions for paths before
+hoping to understand the next part of this code.
+
+@<Scan a path construction operation...@>=
+begin cycle_hit:=false;
+@<Convert the left operand, |p|, into a partial path ending at~|q|;
+ but |return| if |p| doesn't have a suitable type@>;
+continue_path: @<Determine the path join parameters;
+ but |goto finish_path| if there's only a direction specifier@>;
+if cur_cmd=cycle then @<Get ready to close a cycle@>
+else begin scan_tertiary;
+ @<Convert the right operand, |cur_exp|,
+ into a partial path from |pp| to~|qq|@>;
+ end;
+@<Join the partial paths and reset |p| and |q| to the head and tail
+ of the result@>;
+if cur_cmd>=min_expression_command then
+ if cur_cmd<=ampersand then if not cycle_hit then goto continue_path;
+finish_path:
+@<Choose control points for the path and put the result into |cur_exp|@>;
+end
+
+@ @<Convert the left operand, |p|, into a partial path ending at~|q|...@>=
+begin unstash_cur_exp(p);
+if cur_type=pair_type then p:=new_knot
+else if cur_type=path_type then p:=cur_exp
+else return;
+q:=p;
+while link(q)<>p do q:=link(q);
+if left_type(p)<>endpoint then {open up a cycle}
+ begin r:=copy_knot(p); link(q):=r; q:=r;
+ end;
+left_type(p):=open; right_type(q):=open;
+end
+
+@ A pair of numeric values is changed into a knot node for a one-point path
+when \MF\ discovers that the pair is part of a path.
+
+@p@t\4@>@<Declare the procedure called |known_pair|@>@;
+function new_knot:pointer; {convert a pair to a knot with two endpoints}
+var @!q:pointer; {the new node}
+begin q:=get_node(knot_node_size); left_type(q):=endpoint;
+right_type(q):=endpoint; link(q):=q;@/
+known_pair; x_coord(q):=cur_x; y_coord(q):=cur_y;
+new_knot:=q;
+end;
+
+@ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components
+of the current expression, assuming that the current expression is a
+pair of known numerics. Unknown components are zeroed, and the
+current expression is flushed.
+
+@<Declare the procedure called |known_pair|@>=
+procedure known_pair;
+var @!p:pointer; {the pair node}
+begin if cur_type<>pair_type then
+ begin exp_err("Undefined coordinates have been replaced by (0,0)");
+@.Undefined coordinates...@>
+ help5("I need x and y numbers for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ ("you might want to type `I ???' now.)");
+ put_get_flush_error(0); cur_x:=0; cur_y:=0;
+ end
+else begin p:=value(cur_exp);
+ @<Make sure that both |x| and |y| parts of |p| are known;
+ copy them into |cur_x| and |cur_y|@>;
+ flush_cur_exp(0);
+ end;
+end;
+
+@ @<Make sure that both |x| and |y| parts of |p| are known...@>=
+if type(x_part_loc(p))=known then cur_x:=value(x_part_loc(p))
+else begin disp_err(x_part_loc(p),
+ "Undefined x coordinate has been replaced by 0");
+@.Undefined coordinates...@>
+ help5("I need a `known' x value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ ("you might want to type `I ???' now.)");
+ put_get_error; recycle_value(x_part_loc(p)); cur_x:=0;
+ end;
+if type(y_part_loc(p))=known then cur_y:=value(y_part_loc(p))
+else begin disp_err(y_part_loc(p),
+ "Undefined y coordinate has been replaced by 0");
+ help5("I need a `known' y value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+ ("you might want to type `I ???' now.)");
+ put_get_error; recycle_value(y_part_loc(p)); cur_y:=0;
+ end
+
+@ At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|.
+
+@<Determine the path join parameters...@>=
+if cur_cmd=left_brace then
+ @<Put the pre-join direction information into node |q|@>;
+d:=cur_cmd;
+if d=path_join then @<Determine the tension and/or control points@>
+else if d<>ampersand then goto finish_path;
+get_x_next;
+if cur_cmd=left_brace then
+ @<Put the post-join direction information into |x| and |t|@>
+else if right_type(q)<>explicit then
+ begin t:=open; x:=0;
+ end
+
+@ The |scan_direction| subroutine looks at the directional information
+that is enclosed in braces, and also scans ahead to the following character.
+A type code is returned, either |open| (if the direction was $(0,0)$),
+or |curl| (if the direction was a curl of known value |cur_exp|), or
+|given| (if the direction is given by the |angle| value that now
+appears in |cur_exp|).
+
+There's nothing difficult about this subroutine, but the program is rather
+lengthy because a variety of potential errors need to be nipped in the bud.
+
+@p function scan_direction:small_number;
+var @!t:given..open; {the type of information found}
+@!x:scaled; {an |x| coordinate}
+begin get_x_next;
+if cur_cmd=curl_command then @<Scan a curl specification@>
+else @<Scan a given direction@>;
+if cur_cmd<>right_brace then
+ begin missing_err("}");@/
+@.Missing `\char`\}'@>
+ help3("I've scanned a direction spec for part of a path,")@/
+ ("so a right brace should have come next.")@/
+ ("I shall pretend that one was there.");@/
+ back_error;
+ end;
+get_x_next; scan_direction:=t;
+end;
+
+@ @<Scan a curl specification@>=
+begin get_x_next; scan_expression;
+if (cur_type<>known)or(cur_exp<0) then
+ begin exp_err("Improper curl has been replaced by 1");
+@.Improper curl@>
+ help1("A curl must be a known, nonnegative number.");
+ put_get_flush_error(unity);
+ end;
+t:=curl;
+end
+
+@ @<Scan a given direction@>=
+begin scan_expression;
+if cur_type>pair_type then @<Get given directions separated by commas@>
+else known_pair;
+if (cur_x=0)and(cur_y=0) then t:=open
+else begin t:=given; cur_exp:=n_arg(cur_x,cur_y);
+ end;
+end
+
+@ @<Get given directions separated by commas@>=
+begin if cur_type<>known then
+ begin exp_err("Undefined x coordinate has been replaced by 0");
+@.Undefined coordinates...@>
+ help5("I need a `known' x value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ ("you might want to type `I ???' now.)");
+ put_get_flush_error(0);
+ end;
+x:=cur_exp;
+if cur_cmd<>comma then
+ begin missing_err(",");@/
+@.Missing `,'@>
+ help2("I've got the x coordinate of a path direction;")@/
+ ("will look for the y coordinate next.");
+ back_error;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then
+ begin exp_err("Undefined y coordinate has been replaced by 0");
+ help5("I need a `known' y value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+ ("you might want to type `I ???' now.)");
+ put_get_flush_error(0);
+ end;
+cur_y:=cur_exp; cur_x:=x;
+end
+
+@ At this point |right_type(q)| is usually |open|, but it may have been
+set to some other value by a previous operation. We must maintain
+the value of |right_type(q)| in cases such as
+`\.{..\{curl2\}z\{0,0\}..}'.
+
+@<Put the pre-join...@>=
+begin t:=scan_direction;
+if t<>open then
+ begin right_type(q):=t; right_given(q):=cur_exp;
+ if left_type(q)=open then
+ begin left_type(q):=t; left_given(q):=cur_exp;
+ end; {note that |left_given(q)=left_curl(q)|}
+ end;
+end
+
+@ Since |left_tension| and |left_y| share the same position in knot nodes,
+and since |left_given| is similarly equivalent to |left_x|, we use
+|x| and |y| to hold the given direction and tension information when
+there are no explicit control points.
+
+@<Put the post-join...@>=
+begin t:=scan_direction;
+if right_type(q)<>explicit then x:=cur_exp
+else t:=explicit; {the direction information is superfluous}
+end
+
+@ @<Determine the tension and/or...@>=
+begin get_x_next;
+if cur_cmd=tension then @<Set explicit tensions@>
+else if cur_cmd=controls then @<Set explicit control points@>
+else begin right_tension(q):=unity; y:=unity; back_input; {default tension}
+ goto done;
+ end;
+if cur_cmd<>path_join then
+ begin missing_err("..");@/
+@.Missing `..'@>
+ help1("A path join command should end with two dots.");
+ back_error;
+ end;
+done:end
+
+@ @<Set explicit tensions@>=
+begin get_x_next; y:=cur_cmd;
+if cur_cmd=at_least then get_x_next;
+scan_primary;
+@<Make sure that the current expression is a valid tension setting@>;
+if y=at_least then negate(cur_exp);
+right_tension(q):=cur_exp;
+if cur_cmd=and_command then
+ begin get_x_next; y:=cur_cmd;
+ if cur_cmd=at_least then get_x_next;
+ scan_primary;
+ @<Make sure that the current expression is a valid tension setting@>;
+ if y=at_least then negate(cur_exp);
+ end;
+y:=cur_exp;
+end
+
+@ @d min_tension==three_quarter_unit
+
+@<Make sure that the current expression is a valid tension setting@>=
+if (cur_type<>known)or(cur_exp<min_tension) then
+ begin exp_err("Improper tension has been set to 1");
+@.Improper tension@>
+ help1("The expression above should have been a number >=3/4.");
+ put_get_flush_error(unity);
+ end
+
+@ @<Set explicit control points@>=
+begin right_type(q):=explicit; t:=explicit; get_x_next; scan_primary;@/
+known_pair; right_x(q):=cur_x; right_y(q):=cur_y;
+if cur_cmd<>and_command then
+ begin x:=right_x(q); y:=right_y(q);
+ end
+else begin get_x_next; scan_primary;@/
+ known_pair; x:=cur_x; y:=cur_y;
+ end;
+end
+
+@ @<Convert the right operand, |cur_exp|, into a partial path...@>=
+begin if cur_type<>path_type then pp:=new_knot
+else pp:=cur_exp;
+qq:=pp;
+while link(qq)<>pp do qq:=link(qq);
+if left_type(pp)<>endpoint then {open up a cycle}
+ begin r:=copy_knot(pp); link(qq):=r; qq:=r;
+ end;
+left_type(pp):=open; right_type(qq):=open;
+end
+
+@ If a person tries to define an entire path by saying `\.{(x,y)\&cycle}',
+we silently change the specification to `\.{(x,y)..cycle}', since a cycle
+shouldn't have length zero.
+
+@<Get ready to close a cycle@>=
+begin cycle_hit:=true; get_x_next; pp:=p; qq:=p;
+if d=ampersand then if p=q then
+ begin d:=path_join; right_tension(q):=unity; y:=unity;
+ end;
+end
+
+@ @<Join the partial paths and reset |p| and |q|...@>=
+begin if d=ampersand then
+ if (x_coord(q)<>x_coord(pp))or(y_coord(q)<>y_coord(pp)) then
+ begin print_err("Paths don't touch; `&' will be changed to `..'");
+@.Paths don't touch@>
+ help3("When you join paths `p&q', the ending point of p")@/
+ ("must be exactly equal to the starting point of q.")@/
+ ("So I'm going to pretend that you said `p..q' instead.");
+ put_get_error; d:=path_join; right_tension(q):=unity; y:=unity;
+ end;
+@<Plug an opening in |right_type(pp)|, if possible@>;
+if d=ampersand then @<Splice independent paths together@>
+else begin @<Plug an opening in |right_type(q)|, if possible@>;
+ link(q):=pp; left_y(pp):=y;
+ if t<>open then
+ begin left_x(pp):=x; left_type(pp):=t;
+ end;
+ end;
+q:=qq;
+end
+
+@ @<Plug an opening in |right_type(q)|...@>=
+if right_type(q)=open then
+ if (left_type(q)=curl)or(left_type(q)=given) then
+ begin right_type(q):=left_type(q); right_given(q):=left_given(q);
+ end
+
+@ @<Plug an opening in |right_type(pp)|...@>=
+if right_type(pp)=open then
+ if (t=curl)or(t=given) then
+ begin right_type(pp):=t; right_given(pp):=x;
+ end
+
+@ @<Splice independent paths together@>=
+begin if left_type(q)=open then if right_type(q)=open then
+ begin left_type(q):=curl; left_curl(q):=unity;
+ end;
+if right_type(pp)=open then if t=open then
+ begin right_type(pp):=curl; right_curl(pp):=unity;
+ end;
+right_type(q):=right_type(pp); link(q):=link(pp);@/
+right_x(q):=right_x(pp); right_y(q):=right_y(pp);
+free_node(pp,knot_node_size);
+if qq=pp then qq:=q;
+end
+
+@ @<Choose control points for the path...@>=
+if cycle_hit then
+ begin if d=ampersand then p:=q;
+ end
+else begin left_type(p):=endpoint;
+ if right_type(p)=open then
+ begin right_type(p):=curl; right_curl(p):=unity;
+ end;
+ right_type(q):=endpoint;
+ if left_type(q)=open then
+ begin left_type(q):=curl; left_curl(q):=unity;
+ end;
+ link(q):=p;
+ end;
+make_choices(p);
+cur_type:=path_type; cur_exp:=p
+
+@ Finally, we sometimes need to scan an expression whose value is
+supposed to be either |true_code| or |false_code|.
+
+@<Declare the basic parsing subroutines@>=
+procedure get_boolean;
+begin get_x_next; scan_expression;
+if cur_type<>boolean_type then
+ begin exp_err("Undefined condition will be treated as `false'");
+@.Undefined condition...@>
+ help2("The expression shown above should have had a definite")@/
+ ("true-or-false value. I'm changing it to `false'.");@/
+ put_get_flush_error(false_code); cur_type:=boolean_type;
+ end;
+end;
+
+@* \[42] Doing the operations.
+The purpose of parsing is primarily to permit people to avoid piles of
+parentheses. But the real work is done after the structure of an expression
+has been recognized; that's when new expressions are generated. We
+turn now to the guts of \MF, which handles individual operators that
+have come through the parsing mechanism.
+
+We'll start with the easy ones that take no operands, then work our way
+up to operators with one and ultimately two arguments. In other words,
+we will write the three procedures |do_nullary|, |do_unary|, and |do_binary|
+that are invoked periodically by the expression scanners.
+
+First let's make sure that all of the primitive operators are in the
+hash table. Although |scan_primary| and its relatives made use of the
+\\{cmd} code for these operators, the \\{do} routines base everything
+on the \\{mod} code. For example, |do_binary| doesn't care whether the
+operation it performs is a |primary_binary| or |secondary_binary|, etc.
+
+@<Put each...@>=
+primitive("true",nullary,true_code);@/
+@!@:true_}{\&{true} primitive@>
+primitive("false",nullary,false_code);@/
+@!@:false_}{\&{false} primitive@>
+primitive("nullpicture",nullary,null_picture_code);@/
+@!@:null_picture_}{\&{nullpicture} primitive@>
+primitive("nullpen",nullary,null_pen_code);@/
+@!@:null_pen_}{\&{nullpen} primitive@>
+primitive("jobname",nullary,job_name_op);@/
+@!@:job_name_}{\&{jobname} primitive@>
+primitive("readstring",nullary,read_string_op);@/
+@!@:read_string_}{\&{readstring} primitive@>
+primitive("pencircle",nullary,pen_circle);@/
+@!@:pen_circle_}{\&{pencircle} primitive@>
+primitive("normaldeviate",nullary,normal_deviate);@/
+@!@:normal_deviate_}{\&{normaldeviate} primitive@>
+primitive("odd",unary,odd_op);@/
+@!@:odd_}{\&{odd} primitive@>
+primitive("known",unary,known_op);@/
+@!@:known_}{\&{known} primitive@>
+primitive("unknown",unary,unknown_op);@/
+@!@:unknown_}{\&{unknown} primitive@>
+primitive("not",unary,not_op);@/
+@!@:not_}{\&{not} primitive@>
+primitive("decimal",unary,decimal);@/
+@!@:decimal_}{\&{decimal} primitive@>
+primitive("reverse",unary,reverse);@/
+@!@:reverse_}{\&{reverse} primitive@>
+primitive("makepath",unary,make_path_op);@/
+@!@:make_path_}{\&{makepath} primitive@>
+primitive("makepen",unary,make_pen_op);@/
+@!@:make_pen_}{\&{makepen} primitive@>
+primitive("totalweight",unary,total_weight_op);@/
+@!@:total_weight_}{\&{totalweight} primitive@>
+primitive("oct",unary,oct_op);@/
+@!@:oct_}{\&{oct} primitive@>
+primitive("hex",unary,hex_op);@/
+@!@:hex_}{\&{hex} primitive@>
+primitive("ASCII",unary,ASCII_op);@/
+@!@:ASCII_}{\&{ASCII} primitive@>
+primitive("char",unary,char_op);@/
+@!@:char_}{\&{char} primitive@>
+primitive("length",unary,length_op);@/
+@!@:length_}{\&{length} primitive@>
+primitive("turningnumber",unary,turning_op);@/
+@!@:turning_number_}{\&{turningnumber} primitive@>
+primitive("xpart",unary,x_part);@/
+@!@:x_part_}{\&{xpart} primitive@>
+primitive("ypart",unary,y_part);@/
+@!@:y_part_}{\&{ypart} primitive@>
+primitive("xxpart",unary,xx_part);@/
+@!@:xx_part_}{\&{xxpart} primitive@>
+primitive("xypart",unary,xy_part);@/
+@!@:xy_part_}{\&{xypart} primitive@>
+primitive("yxpart",unary,yx_part);@/
+@!@:yx_part_}{\&{yxpart} primitive@>
+primitive("yypart",unary,yy_part);@/
+@!@:yy_part_}{\&{yypart} primitive@>
+primitive("sqrt",unary,sqrt_op);@/
+@!@:sqrt_}{\&{sqrt} primitive@>
+primitive("mexp",unary,m_exp_op);@/
+@!@:m_exp_}{\&{mexp} primitive@>
+primitive("mlog",unary,m_log_op);@/
+@!@:m_log_}{\&{mlog} primitive@>
+primitive("sind",unary,sin_d_op);@/
+@!@:sin_d_}{\&{sind} primitive@>
+primitive("cosd",unary,cos_d_op);@/
+@!@:cos_d_}{\&{cosd} primitive@>
+primitive("floor",unary,floor_op);@/
+@!@:floor_}{\&{floor} primitive@>
+primitive("uniformdeviate",unary,uniform_deviate);@/
+@!@:uniform_deviate_}{\&{uniformdeviate} primitive@>
+primitive("charexists",unary,char_exists_op);@/
+@!@:char_exists_}{\&{charexists} primitive@>
+primitive("angle",unary,angle_op);@/
+@!@:angle_}{\&{angle} primitive@>
+primitive("cycle",cycle,cycle_op);@/
+@!@:cycle_}{\&{cycle} primitive@>
+primitive("+",plus_or_minus,plus);@/
+@!@:+ }{\.{+} primitive@>
+primitive("-",plus_or_minus,minus);@/
+@!@:- }{\.{-} primitive@>
+primitive("*",secondary_binary,times);@/
+@!@:* }{\.{*} primitive@>
+primitive("/",slash,over); eqtb[frozen_slash]:=eqtb[cur_sym];@/
+@!@:/ }{\.{/} primitive@>
+primitive("++",tertiary_binary,pythag_add);@/
+@!@:++_}{\.{++} primitive@>
+primitive("+-+",tertiary_binary,pythag_sub);@/
+@!@:+-+_}{\.{+-+} primitive@>
+primitive("and",and_command,and_op);@/
+@!@:and_}{\&{and} primitive@>
+primitive("or",tertiary_binary,or_op);@/
+@!@:or_}{\&{or} primitive@>
+primitive("<",expression_binary,less_than);@/
+@!@:< }{\.{<} primitive@>
+primitive("<=",expression_binary,less_or_equal);@/
+@!@:<=_}{\.{<=} primitive@>
+primitive(">",expression_binary,greater_than);@/
+@!@:> }{\.{>} primitive@>
+primitive(">=",expression_binary,greater_or_equal);@/
+@!@:>=_}{\.{>=} primitive@>
+primitive("=",equals,equal_to);@/
+@!@:= }{\.{=} primitive@>
+primitive("<>",expression_binary,unequal_to);@/
+@!@:<>_}{\.{<>} primitive@>
+primitive("substring",primary_binary,substring_of);@/
+@!@:substring_}{\&{substring} primitive@>
+primitive("subpath",primary_binary,subpath_of);@/
+@!@:subpath_}{\&{subpath} primitive@>
+primitive("directiontime",primary_binary,direction_time_of);@/
+@!@:direction_time_}{\&{directiontime} primitive@>
+primitive("point",primary_binary,point_of);@/
+@!@:point_}{\&{point} primitive@>
+primitive("precontrol",primary_binary,precontrol_of);@/
+@!@:precontrol_}{\&{precontrol} primitive@>
+primitive("postcontrol",primary_binary,postcontrol_of);@/
+@!@:postcontrol_}{\&{postcontrol} primitive@>
+primitive("penoffset",primary_binary,pen_offset_of);@/
+@!@:pen_offset_}{\&{penoffset} primitive@>
+primitive("&",ampersand,concatenate);@/
+@!@:!!!}{\.{\&} primitive@>
+primitive("rotated",secondary_binary,rotated_by);@/
+@!@:rotated_}{\&{rotated} primitive@>
+primitive("slanted",secondary_binary,slanted_by);@/
+@!@:slanted_}{\&{slanted} primitive@>
+primitive("scaled",secondary_binary,scaled_by);@/
+@!@:scaled_}{\&{scaled} primitive@>
+primitive("shifted",secondary_binary,shifted_by);@/
+@!@:shifted_}{\&{shifted} primitive@>
+primitive("transformed",secondary_binary,transformed_by);@/
+@!@:transformed_}{\&{transformed} primitive@>
+primitive("xscaled",secondary_binary,x_scaled);@/
+@!@:x_scaled_}{\&{xscaled} primitive@>
+primitive("yscaled",secondary_binary,y_scaled);@/
+@!@:y_scaled_}{\&{yscaled} primitive@>
+primitive("zscaled",secondary_binary,z_scaled);@/
+@!@:z_scaled_}{\&{zscaled} primitive@>
+primitive("intersectiontimes",tertiary_binary,intersect);@/
+@!@:intersection_times_}{\&{intersectiontimes} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+nullary,unary,primary_binary,secondary_binary,tertiary_binary,
+ expression_binary,cycle,plus_or_minus,slash,ampersand,equals,and_command:
+ print_op(m);
+
+@ OK, let's look at the simplest \\{do} procedure first.
+
+@p procedure do_nullary(@!c:quarterword);
+var @!k:integer; {all-purpose loop index}
+begin check_arith;
+if internal[tracing_commands]>two then
+ show_cmd_mod(nullary,c);
+case c of
+true_code,false_code:begin cur_type:=boolean_type; cur_exp:=c;
+ end;
+null_picture_code:begin cur_type:=picture_type;
+ cur_exp:=get_node(edge_header_size); init_edges(cur_exp);
+ end;
+null_pen_code:begin cur_type:=pen_type; cur_exp:=null_pen;
+ end;
+normal_deviate:begin cur_type:=known; cur_exp:=norm_rand;
+ end;
+pen_circle:@<Make a special knot node for \&{pencircle}@>;
+job_name_op: begin if job_name=0 then open_log_file;
+ cur_type:=string_type; cur_exp:=job_name;
+ end;
+read_string_op:@<Read a string from the terminal@>;
+end; {there are no other cases}
+check_arith;
+end;
+
+@ @<Make a special knot node for \&{pencircle}@>=
+begin cur_type:=future_pen; cur_exp:=get_node(knot_node_size);
+left_type(cur_exp):=open; right_type(cur_exp):=open;
+link(cur_exp):=cur_exp;@/
+x_coord(cur_exp):=0; y_coord(cur_exp):=0;@/
+left_x(cur_exp):=unity; left_y(cur_exp):=0;@/
+right_x(cur_exp):=0; right_y(cur_exp):=unity;@/
+end
+
+@ @<Read a string...@>=
+begin if interaction<=nonstop_mode then
+ fatal_error("*** (cannot readstring in nonstop modes)");
+begin_file_reading; name:=1; prompt_input("");
+str_room(last-start);
+for k:=start to last-1 do append_char(buffer[k]);
+end_file_reading; cur_type:=string_type; cur_exp:=make_string;
+end
+
+@ Things get a bit more interesting when there's an operand. The
+operand to |do_unary| appears in |cur_type| and |cur_exp|.
+
+@p @t\4@>@<Declare unary action procedures@>@;
+procedure do_unary(@!c:quarterword);
+var @!p,@!q:pointer; {for list manipulation}
+@!x:integer; {a temporary register}
+begin check_arith;
+if internal[tracing_commands]>two then
+ @<Trace the current unary operation@>;
+case c of
+plus:if cur_type<pair_type then
+ if cur_type<>picture_type then bad_unary(plus);
+minus:@<Negate the current expression@>;
+@t\4@>@<Additional cases of unary operators@>@;
+end; {there are no other cases}
+check_arith;
+end;
+
+@ The |nice_pair| function returns |true| if both components of a pair
+are known.
+
+@<Declare unary action procedures@>=
+function nice_pair(@!p:integer;@!t:quarterword):boolean;
+label exit;
+begin if t=pair_type then
+ begin p:=value(p);
+ if type(x_part_loc(p))=known then
+ if type(y_part_loc(p))=known then
+ begin nice_pair:=true; return;
+ end;
+ end;
+nice_pair:=false;
+exit:end;
+
+@ @<Declare unary action...@>=
+procedure print_known_or_unknown_type(@!t:small_number;@!v:integer);
+begin print_char("(");
+if t<dependent then
+ if t<>pair_type then print_type(t)
+ else if nice_pair(v,pair_type) then print("pair")
+ else print("unknown pair")
+else print("unknown numeric");
+print_char(")");
+end;
+
+@ @<Declare unary action...@>=
+procedure bad_unary(@!c:quarterword);
+begin exp_err("Not implemented: "); print_op(c);
+@.Not implemented...@>
+print_known_or_unknown_type(cur_type,cur_exp);
+help3("I'm afraid I don't know how to apply that operation to that")@/
+ ("particular type. Continue, and I'll simply return the")@/
+ ("argument (shown above) as the result of the operation.");
+put_get_error;
+end;
+
+@ @<Trace the current unary operation@>=
+begin begin_diagnostic; print_nl("{"); print_op(c); print_char("(");@/
+print_exp(null,0); {show the operand, but not verbosely}
+print(")}"); end_diagnostic(false);
+end
+
+@ Negation is easy except when the current expression
+is of type |independent|, or when it is a pair with one or more
+|independent| components.
+
+It is tempting to argue that the negative of an independent variable
+is an independent variable, hence we don't have to do anything when
+negating it. The fallacy is that other dependent variables pointing
+to the current expression must change the sign of their
+coefficients if we make no change to the current expression.
+
+Instead, we work around the problem by copying the current expression
+and recycling it afterwards (cf.~the |stash_in| routine).
+
+@<Negate the current expression@>=
+case cur_type of
+pair_type,independent: begin q:=cur_exp; make_exp_copy(q);
+ if cur_type=dependent then negate_dep_list(dep_list(cur_exp))
+ else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ if type(x_part_loc(p))=known then negate(value(x_part_loc(p)))
+ else negate_dep_list(dep_list(x_part_loc(p)));
+ if type(y_part_loc(p))=known then negate(value(y_part_loc(p)))
+ else negate_dep_list(dep_list(y_part_loc(p)));
+ end; {if |cur_type=known| then |cur_exp=0|}
+ recycle_value(q); free_node(q,value_node_size);
+ end;
+dependent,proto_dependent:negate_dep_list(dep_list(cur_exp));
+known:negate(cur_exp);
+picture_type:negate_edges(cur_exp);
+othercases bad_unary(minus)
+endcases
+
+@ @<Declare unary action...@>=
+procedure negate_dep_list(@!p:pointer);
+label exit;
+begin loop@+begin negate(value(p));
+ if info(p)=null then return;
+ p:=link(p);
+ end;
+exit:end;
+
+@ @<Additional cases of unary operators@>=
+not_op: if cur_type<>boolean_type then bad_unary(not_op)
+ else cur_exp:=true_code+false_code-cur_exp;
+
+@ @d three_sixty_units==23592960 {that's |360*unity|}
+@d boolean_reset(#)==if # then cur_exp:=true_code@+else cur_exp:=false_code
+
+@<Additional cases of unary operators@>=
+sqrt_op,m_exp_op,m_log_op,sin_d_op,cos_d_op,floor_op,
+ uniform_deviate,odd_op,char_exists_op:@t@>@;@/
+ if cur_type<>known then bad_unary(c)
+ else case c of
+ sqrt_op:cur_exp:=square_rt(cur_exp);
+ m_exp_op:cur_exp:=m_exp(cur_exp);
+ m_log_op:cur_exp:=m_log(cur_exp);
+ sin_d_op,cos_d_op:begin n_sin_cos((cur_exp mod three_sixty_units)*16);
+ if c=sin_d_op then cur_exp:=round_fraction(n_sin)
+ else cur_exp:=round_fraction(n_cos);
+ end;
+ floor_op:cur_exp:=floor_scaled(cur_exp);
+ uniform_deviate:cur_exp:=unif_rand(cur_exp);
+ odd_op: begin boolean_reset(odd(round_unscaled(cur_exp)));
+ cur_type:=boolean_type;
+ end;
+ char_exists_op:@<Determine if a character has been shipped out@>;
+ end; {there are no other cases}
+
+@ @<Additional cases of unary operators@>=
+angle_op:if nice_pair(cur_exp,cur_type) then
+ begin p:=value(cur_exp);
+ x:=n_arg(value(x_part_loc(p)),value(y_part_loc(p)));
+ if x>=0 then flush_cur_exp((x+8)div 16)
+ else flush_cur_exp(-((-x+8)div 16));
+ end
+ else bad_unary(angle_op);
+
+@ If the current expression is a pair, but the context wants it to
+be a path, we call |pair_to_path|.
+
+@<Declare unary action...@>=
+procedure pair_to_path;
+begin cur_exp:=new_knot; cur_type:=path_type;
+end;
+
+@ @<Additional cases of unary operators@>=
+x_part,y_part:if (cur_type<=pair_type)and(cur_type>=transform_type) then
+ take_part(c)
+ else bad_unary(c);
+xx_part,xy_part,yx_part,yy_part: if cur_type=transform_type then take_part(c)
+ else bad_unary(c);
+
+@ In the following procedure, |cur_exp| points to a capsule, which points to
+a big node. We want to delete all but one part of the big node.
+
+@<Declare unary action...@>=
+procedure take_part(@!c:quarterword);
+var @!p:pointer; {the big node}
+begin p:=value(cur_exp); value(temp_val):=p; type(temp_val):=cur_type;
+link(p):=temp_val; free_node(cur_exp,value_node_size);
+make_exp_copy(p+2*(c-x_part));
+recycle_value(temp_val);
+end;
+
+@ @<Initialize table entries...@>=
+name_type(temp_val):=capsule;
+
+@ @<Additional cases of unary...@>=
+char_op: if cur_type<>known then bad_unary(char_op)
+ else begin cur_exp:=round_unscaled(cur_exp) mod 256; cur_type:=string_type;
+ if cur_exp<0 then cur_exp:=cur_exp+256;
+ if length(cur_exp)<>1 then
+ begin str_room(1); append_char(cur_exp); cur_exp:=make_string;
+ end;
+ end;
+decimal: if cur_type<>known then bad_unary(decimal)
+ else begin old_setting:=selector; selector:=new_string;
+ print_scaled(cur_exp); cur_exp:=make_string;
+ selector:=old_setting; cur_type:=string_type;
+ end;
+oct_op,hex_op,ASCII_op: if cur_type<>string_type then bad_unary(c)
+ else str_to_num(c);
+
+@ @<Declare unary action...@>=
+procedure str_to_num(@!c:quarterword); {converts a string to a number}
+var @!n:integer; {accumulator}
+@!m:ASCII_code; {current character}
+@!k:pool_pointer; {index into |str_pool|}
+@!b:8..16; {radix of conversion}
+@!bad_char:boolean; {did the string contain an invalid digit?}
+begin if c=ASCII_op then
+ if length(cur_exp)=0 then n:=-1
+ else n:=so(str_pool[str_start[cur_exp]])
+else begin if c=oct_op then b:=8@+else b:=16;
+ n:=0; bad_char:=false;
+ for k:=str_start[cur_exp] to str_start[cur_exp+1]-1 do
+ begin m:=so(str_pool[k]);
+ if (m>="0")and(m<="9") then m:=m-"0"
+ else if (m>="A")and(m<="F") then m:=m-"A"+10
+ else if (m>="a")and(m<="f") then m:=m-"a"+10
+ else begin bad_char:=true; m:=0;
+ end;
+ if m>=b then
+ begin bad_char:=true; m:=0;
+ end;
+ if n<32768 div b then n:=n*b+m@+else n:=32767;
+ end;
+ @<Give error messages if |bad_char| or |n>=4096|@>;
+ end;
+flush_cur_exp(n*unity);
+end;
+
+@ @<Give error messages if |bad_char|...@>=
+if bad_char then
+ begin exp_err("String contains illegal digits");
+@.String contains illegal digits@>
+ if c=oct_op then
+ help1("I zeroed out characters that weren't in the range 0..7.")
+ else help1("I zeroed out characters that weren't hex digits.");
+ put_get_error;
+ end;
+if n>4095 then
+ begin print_err("Number too large ("); print_int(n); print_char(")");
+@.Number too large@>
+ help1("I have trouble with numbers greater than 4095; watch out.");
+ put_get_error;
+ end
+
+@ The length operation is somewhat unusual in that it applies to a variety
+of different types of operands.
+
+@<Additional cases of unary...@>=
+length_op: if cur_type=string_type then flush_cur_exp(length(cur_exp)*unity)
+ else if cur_type=path_type then flush_cur_exp(path_length)
+ else if cur_type=known then cur_exp:=abs(cur_exp)
+ else if nice_pair(cur_exp,cur_type) then
+ flush_cur_exp(pyth_add(value(x_part_loc(value(cur_exp))),@|
+ value(y_part_loc(value(cur_exp)))))
+ else bad_unary(c);
+
+@ @<Declare unary action...@>=
+function path_length:scaled; {computes the length of the current path}
+var @!n:scaled; {the path length so far}
+@!p:pointer; {traverser}
+begin p:=cur_exp;
+if left_type(p)=endpoint then n:=-unity@+else n:=0;
+repeat p:=link(p); n:=n+unity;
+until p=cur_exp;
+path_length:=n;
+end;
+
+@ The turning number is computed only with respect to null pens. A different
+pen might affect the turning number, in degenerate cases, because autorounding
+will produce a slightly different path, or because excessively large coordinates
+might be truncated.
+
+@<Additional cases of unary...@>=
+turning_op:if cur_type=pair_type then flush_cur_exp(0)
+ else if cur_type<>path_type then bad_unary(turning_op)
+ else if left_type(cur_exp)=endpoint then
+ flush_cur_exp(0) {not a cyclic path}
+ else begin cur_pen:=null_pen; cur_path_type:=contour_code;
+ cur_exp:=make_spec(cur_exp,
+ fraction_one-half_unit-1-el_gordo,0);
+ flush_cur_exp(turning_number*unity); {convert to |scaled|}
+ end;
+
+@ @d type_test_end== flush_cur_exp(true_code)
+ else flush_cur_exp(false_code);
+ cur_type:=boolean_type;
+ end
+@d type_range_end(#)==(cur_type<=#) then type_test_end
+@d type_range(#)==begin if (cur_type>=#) and type_range_end
+@d type_test(#)==begin if cur_type=# then type_test_end
+
+@<Additional cases of unary operators@>=
+boolean_type: type_range(boolean_type)(unknown_boolean);
+string_type: type_range(string_type)(unknown_string);
+pen_type: type_range(pen_type)(future_pen);
+path_type: type_range(path_type)(unknown_path);
+picture_type: type_range(picture_type)(unknown_picture);
+transform_type,pair_type: type_test(c);
+numeric_type: type_range(known)(independent);
+known_op,unknown_op: test_known(c);
+
+@ @<Declare unary action procedures@>=
+procedure test_known(@!c:quarterword);
+label done;
+var @!b:true_code..false_code; {is the current expression known?}
+@!p,@!q:pointer; {locations in a big node}
+begin b:=false_code;
+case cur_type of
+vacuous,boolean_type,string_type,pen_type,future_pen,path_type,picture_type,
+ known: b:=true_code;
+transform_type,pair_type:begin p:=value(cur_exp); q:=p+big_node_size[cur_type];
+ repeat q:=q-2;
+ if type(q)<>known then goto done;
+ until q=p;
+ b:=true_code;
+done: end;
+othercases do_nothing
+endcases;
+if c=known_op then flush_cur_exp(b)
+else flush_cur_exp(true_code+false_code-b);
+cur_type:=boolean_type;
+end;
+
+@ @<Additional cases of unary operators@>=
+cycle_op: begin if cur_type<>path_type then flush_cur_exp(false_code)
+ else if left_type(cur_exp)<>endpoint then flush_cur_exp(true_code)
+ else flush_cur_exp(false_code);
+ cur_type:=boolean_type;
+ end;
+
+@ @<Additional cases of unary operators@>=
+make_pen_op: begin if cur_type=pair_type then pair_to_path;
+ if cur_type=path_type then cur_type:=future_pen
+ else bad_unary(make_pen_op);
+ end;
+make_path_op: begin if cur_type=future_pen then materialize_pen;
+ if cur_type<>pen_type then bad_unary(make_path_op)
+ else begin flush_cur_exp(make_path(cur_exp)); cur_type:=path_type;
+ end;
+ end;
+total_weight_op: if cur_type<>picture_type then bad_unary(total_weight_op)
+ else flush_cur_exp(total_weight(cur_exp));
+reverse: if cur_type=path_type then
+ begin p:=htap_ypoc(cur_exp);
+ if right_type(p)=endpoint then p:=link(p);
+ toss_knot_list(cur_exp); cur_exp:=p;
+ end
+ else if cur_type=pair_type then pair_to_path
+ else bad_unary(reverse);
+
+@ Finally, we have the operations that combine a capsule~|p|
+with the current expression.
+
+@p @t\4@>@<Declare binary action procedures@>@;
+procedure do_binary(@!p:pointer;@!c:quarterword);
+label done,done1,exit;
+var @!q,@!r,@!rr:pointer; {for list manipulation}
+@!old_p,@!old_exp:pointer; {capsules to recycle}
+@!v:integer; {for numeric manipulation}
+begin check_arith;
+if internal[tracing_commands]>two then
+ @<Trace the current binary operation@>;
+@<Sidestep |independent| cases in capsule |p|@>;
+@<Sidestep |independent| cases in the current expression@>;
+case c of
+plus,minus:@<Add or subtract the current expression from |p|@>;
+@t\4@>@<Additional cases of binary operators@>@;
+end; {there are no other cases}
+recycle_value(p); free_node(p,value_node_size); {|return| to avoid this}
+exit:check_arith; @<Recycle any sidestepped |independent| capsules@>;
+end;
+
+@ @<Declare binary action...@>=
+procedure bad_binary(@!p:pointer;@!c:quarterword);
+begin disp_err(p,"");
+exp_err("Not implemented: ");
+@.Not implemented...@>
+if c>=min_of then print_op(c);
+print_known_or_unknown_type(type(p),p);
+if c>=min_of then print("of")@+else print_op(c);
+print_known_or_unknown_type(cur_type,cur_exp);@/
+help3("I'm afraid I don't know how to apply that operation to that")@/
+ ("combination of types. Continue, and I'll return the second")@/
+ ("argument (see above) as the result of the operation.");
+put_get_error;
+end;
+
+@ @<Trace the current binary operation@>=
+begin begin_diagnostic; print_nl("{(");
+print_exp(p,0); {show the operand, but not verbosely}
+print_char(")"); print_op(c); print_char("(");@/
+print_exp(null,0); print(")}"); end_diagnostic(false);
+end
+
+@ Several of the binary operations are potentially complicated by the
+fact that |independent| values can sneak into capsules. For example,
+we've seen an instance of this difficulty in the unary operation
+of negation. In order to reduce the number of cases that need to be
+handled, we first change the two operands (if necessary)
+to rid them of |independent| components. The original operands are
+put into capsules called |old_p| and |old_exp|, which will be
+recycled after the binary operation has been safely carried out.
+
+@<Recycle any sidestepped |independent| capsules@>=
+if old_p<>null then
+ begin recycle_value(old_p); free_node(old_p,value_node_size);
+ end;
+if old_exp<>null then
+ begin recycle_value(old_exp); free_node(old_exp,value_node_size);
+ end
+
+@ A big node is considered to be ``tarnished'' if it contains at least one
+independent component. We will define a simple function called `|tarnished|'
+that returns |null| if and only if its argument is not tarnished.
+
+@<Sidestep |independent| cases in capsule |p|@>=
+case type(p) of
+transform_type,pair_type: old_p:=tarnished(p);
+independent: old_p:=void;
+othercases old_p:=null
+endcases;
+if old_p<>null then
+ begin q:=stash_cur_exp; old_p:=p; make_exp_copy(old_p);
+ p:=stash_cur_exp; unstash_cur_exp(q);
+ end;
+
+@ @<Sidestep |independent| cases in the current expression@>=
+case cur_type of
+transform_type,pair_type:old_exp:=tarnished(cur_exp);
+independent:old_exp:=void;
+othercases old_exp:=null
+endcases;
+if old_exp<>null then
+ begin old_exp:=cur_exp; make_exp_copy(old_exp);
+ end
+
+@ @<Declare binary action...@>=
+function tarnished(@!p:pointer):pointer;
+label exit;
+var @!q:pointer; {beginning of the big node}
+@!r:pointer; {current position in the big node}
+begin q:=value(p); r:=q+big_node_size[type(p)];
+repeat r:=r-2;
+if type(r)=independent then
+ begin tarnished:=void; return;
+ end;
+until r=q;
+tarnished:=null;
+exit:end;
+
+@ @<Add or subtract the current expression from |p|@>=
+if (cur_type<pair_type)or(type(p)<pair_type) then
+ if (cur_type=picture_type)and(type(p)=picture_type) then
+ begin if c=minus then negate_edges(cur_exp);
+ cur_edges:=cur_exp; merge_edges(value(p));
+ end
+ else bad_binary(p,c)
+else if cur_type=pair_type then
+ if type(p)<>pair_type then bad_binary(p,c)
+ else begin q:=value(p); r:=value(cur_exp);
+ add_or_subtract(x_part_loc(q),x_part_loc(r),c);
+ add_or_subtract(y_part_loc(q),y_part_loc(r),c);
+ end
+ else if type(p)=pair_type then bad_binary(p,c)
+ else add_or_subtract(p,null,c)
+
+@ The first argument to |add_or_subtract| is the location of a value node
+in a capsule or pair node that will soon be recycled. The second argument
+is either a location within a pair or transform node of |cur_exp|,
+or it is null (which means that |cur_exp| itself should be the second
+argument). The third argument is either |plus| or |minus|.
+
+The sum or difference of the numeric quantities will replace the second
+operand. Arithmetic overflow may go undetected; users aren't supposed to
+be monkeying around with really big values.
+@^overflow in arithmetic@>
+
+@<Declare binary action...@>=
+@t\4@>@<Declare the procedure called |dep_finish|@>@;
+procedure add_or_subtract(@!p,@!q:pointer;@!c:quarterword);
+label done,exit;
+var @!s,@!t:small_number; {operand types}
+@!r:pointer; {list traverser}
+@!v:integer; {second operand value}
+begin if q=null then
+ begin t:=cur_type;
+ if t<dependent then v:=cur_exp@+else v:=dep_list(cur_exp);
+ end
+else begin t:=type(q);
+ if t<dependent then v:=value(q)@+else v:=dep_list(q);
+ end;
+if t=known then
+ begin if c=minus then negate(v);
+ if type(p)=known then
+ begin v:=slow_add(value(p),v);
+ if q=null then cur_exp:=v@+else value(q):=v;
+ return;
+ end;
+ @<Add a known value to the constant term of |dep_list(p)|@>;
+ end
+else begin if c=minus then negate_dep_list(v);
+ @<Add operand |p| to the dependency list |v|@>;
+ end;
+exit:end;
+
+@ @<Add a known value to the constant term of |dep_list(p)|@>=
+r:=dep_list(p);
+while info(r)<>null do r:=link(r);
+value(r):=slow_add(value(r),v);
+if q=null then
+ begin q:=get_node(value_node_size); cur_exp:=q; cur_type:=type(p);
+ name_type(q):=capsule;
+ end;
+dep_list(q):=dep_list(p); type(q):=type(p);
+prev_dep(q):=prev_dep(p); link(prev_dep(p)):=q;
+type(p):=known; {this will keep the recycler from collecting non-garbage}
+
+@ We prefer |dependent| lists to |proto_dependent| ones, because it is
+nice to retain the extra accuracy of |fraction| coefficients.
+But we have to handle both kinds, and mixtures too.
+
+@<Add operand |p| to the dependency list |v|@>=
+if type(p)=known then
+ @<Add the known |value(p)| to the constant term of |v|@>
+else begin s:=type(p); r:=dep_list(p);
+ if t=dependent then
+ begin if s=dependent then
+ if max_coef(r)+max_coef(v)<coef_bound then
+ begin v:=p_plus_q(v,r,dependent); goto done;
+ end; {|fix_needed| will necessarily be false}
+ t:=proto_dependent; v:=p_over_v(v,unity,dependent,proto_dependent);
+ end;
+ if s=proto_dependent then v:=p_plus_q(v,r,proto_dependent)
+ else v:=p_plus_fq(v,unity,r,proto_dependent,dependent);
+ done: @<Output the answer, |v| (which might have become |known|)@>;
+ end
+
+@ @<Add the known |value(p)| to the constant term of |v|@>=
+begin while info(v)<>null do v:=link(v);
+value(v):=slow_add(value(p),value(v));
+end
+
+@ @<Output the answer, |v| (which might have become |known|)@>=
+if q<>null then dep_finish(v,q,t)
+else begin cur_type:=t; dep_finish(v,null,t);
+ end
+
+@ Here's the current situation: The dependency list |v| of type |t|
+should either be put into the current expression (if |q=null|) or
+into location |q| within a pair node (otherwise). The destination (|cur_exp|
+or |q|) formerly held a dependency list with the same
+final pointer as the list |v|.
+
+@<Declare the procedure called |dep_finish|@>=
+procedure dep_finish(@!v,@!q:pointer;@!t:small_number);
+var @!p:pointer; {the destination}
+@!vv:scaled; {the value, if it is |known|}
+begin if q=null then p:=cur_exp@+else p:=q;
+dep_list(p):=v; type(p):=t;
+if info(v)=null then
+ begin vv:=value(v);
+ if q=null then flush_cur_exp(vv)
+ else begin recycle_value(p); type(q):=known; value(q):=vv;
+ end;
+ end
+else if q=null then cur_type:=t;
+if fix_needed then fix_dependencies;
+end;
+
+@ Let's turn now to the six basic relations of comparison.
+
+@<Additional cases of binary operators@>=
+less_than,less_or_equal,greater_than,greater_or_equal,equal_to,unequal_to:
+ begin@t@>@;
+ if (cur_type>pair_type)and(type(p)>pair_type) then
+ add_or_subtract(p,null,minus) {|cur_exp:=(p)-cur_exp|}
+ else if cur_type<>type(p) then
+ begin bad_binary(p,c); goto done;
+ end
+ else if cur_type=string_type then
+ flush_cur_exp(str_vs_str(value(p),cur_exp))
+ else if (cur_type=unknown_string)or(cur_type=unknown_boolean) then
+ @<Check if unknowns have been equated@>
+ else if (cur_type=pair_type)or(cur_type=transform_type) then
+ @<Reduce comparison of big nodes to comparison of scalars@>
+ else if cur_type=boolean_type then flush_cur_exp(cur_exp-value(p))
+ else begin bad_binary(p,c); goto done;
+ end;
+ @<Compare the current expression with zero@>;
+done: end;
+
+@ @<Compare the current expression with zero@>=
+if cur_type<>known then
+ begin if cur_type<known then
+ begin disp_err(p,"");
+ help1("The quantities shown above have not been equated.")@/
+ end
+ else help2("Oh dear. I can't decide if the expression above is positive,")@/
+ ("negative, or zero. So this comparison test won't be `true'.");
+ exp_err("Unknown relation will be considered false");
+@.Unknown relation...@>
+ put_get_flush_error(false_code);
+ end
+else case c of
+ less_than: boolean_reset(cur_exp<0);
+ less_or_equal: boolean_reset(cur_exp<=0);
+ greater_than: boolean_reset(cur_exp>0);
+ greater_or_equal: boolean_reset(cur_exp>=0);
+ equal_to: boolean_reset(cur_exp=0);
+ unequal_to: boolean_reset(cur_exp<>0);
+ end; {there are no other cases}
+ cur_type:=boolean_type
+
+@ When two unknown strings are in the same ring, we know that they are
+equal. Otherwise, we don't know whether they are equal or not, so we
+make no change.
+
+@<Check if unknowns have been equated@>=
+begin q:=value(cur_exp);
+while (q<>cur_exp)and(q<>p) do q:=value(q);
+if q=p then flush_cur_exp(0);
+end
+
+@ @<Reduce comparison of big nodes to comparison of scalars@>=
+begin q:=value(p); r:=value(cur_exp);
+rr:=r+big_node_size[cur_type]-2;
+loop@+ begin add_or_subtract(q,r,minus);
+ if type(r)<>known then goto done1;
+ if value(r)<>0 then goto done1;
+ if r=rr then goto done1;
+ q:=q+2; r:=r+2;
+ end;
+done1:take_part(x_part+half(r-value(cur_exp)));
+end
+
+@ Here we use the sneaky fact that |and_op-false_code=or_op-true_code|.
+
+@<Additional cases of binary operators@>=
+and_op,or_op: if (type(p)<>boolean_type)or(cur_type<>boolean_type) then
+ bad_binary(p,c)
+ else if value(p)=c+false_code-and_op then cur_exp:=value(p);
+
+@ @<Additional cases of binary operators@>=
+times: if (cur_type<pair_type)or(type(p)<pair_type) then bad_binary(p,times)
+ else if (cur_type=known)or(type(p)=known) then
+ @<Multiply when at least one operand is known@>
+ else if (nice_pair(p,type(p))and(cur_type>pair_type))
+ or(nice_pair(cur_exp,cur_type)and(type(p)>pair_type)) then
+ begin hard_times(p); return;
+ end
+ else bad_binary(p,times);
+
+@ @<Multiply when at least one operand is known@>=
+begin if type(p)=known then
+ begin v:=value(p); free_node(p,value_node_size);
+ end
+else begin v:=cur_exp; unstash_cur_exp(p);
+ end;
+if cur_type=known then cur_exp:=take_scaled(cur_exp,v)
+else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ dep_mult(x_part_loc(p),v,true);
+ dep_mult(y_part_loc(p),v,true);
+ end
+else dep_mult(null,v,true);
+return;
+end
+
+@ @<Declare binary action...@>=
+procedure dep_mult(@!p:pointer;@!v:integer;@!v_is_scaled:boolean);
+label exit;
+var @!q:pointer; {the dependency list being multiplied by |v|}
+@!s,@!t:small_number; {its type, before and after}
+begin if p=null then q:=cur_exp
+else if type(p)<>known then q:=p
+else begin if v_is_scaled then value(p):=take_scaled(value(p),v)
+ else value(p):=take_fraction(value(p),v);
+ return;
+ end;
+t:=type(q); q:=dep_list(q); s:=t;
+if t=dependent then if v_is_scaled then
+ if ab_vs_cd(max_coef(q),abs(v),coef_bound-1,unity)>=0 then t:=proto_dependent;
+q:=p_times_v(q,v,s,t,v_is_scaled); dep_finish(q,p,t);
+exit:end;
+
+@ Here is a routine that is similar to |times|; but it is invoked only
+internally, when |v| is a |fraction| whose magnitude is at most~1,
+and when |cur_type>=pair_type|.
+
+@p procedure frac_mult(@!n,@!d:scaled); {multiplies |cur_exp| by |n/d|}
+var @!p:pointer; {a pair node}
+@!old_exp:pointer; {a capsule to recycle}
+@!v:fraction; {|n/d|}
+begin if internal[tracing_commands]>two then
+ @<Trace the fraction multiplication@>;
+case cur_type of
+transform_type,pair_type:old_exp:=tarnished(cur_exp);
+independent:old_exp:=void;
+othercases old_exp:=null
+endcases;
+if old_exp<>null then
+ begin old_exp:=cur_exp; make_exp_copy(old_exp);
+ end;
+v:=make_fraction(n,d);
+if cur_type=known then cur_exp:=take_fraction(cur_exp,v)
+else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ dep_mult(x_part_loc(p),v,false);
+ dep_mult(y_part_loc(p),v,false);
+ end
+else dep_mult(null,v,false);
+if old_exp<>null then
+ begin recycle_value(old_exp); free_node(old_exp,value_node_size);
+ end
+end;
+
+@ @<Trace the fraction multiplication@>=
+begin begin_diagnostic; print_nl("{("); print_scaled(n); print_char("/");
+print_scaled(d); print(")*("); print_exp(null,0); print(")}");
+end_diagnostic(false);
+end
+
+@ The |hard_times| routine multiplies a nice pair by a dependency list.
+
+@<Declare binary action procedures@>=
+procedure hard_times(@!p:pointer);
+var @!q:pointer; {a copy of the dependent variable |p|}
+@!r:pointer; {the big node for the nice pair}
+@!u,@!v:scaled; {the known values of the nice pair}
+begin if type(p)=pair_type then
+ begin q:=stash_cur_exp; unstash_cur_exp(p); p:=q;
+ end; {now |cur_type=pair_type|}
+r:=value(cur_exp); u:=value(x_part_loc(r)); v:=value(y_part_loc(r));
+@<Move the dependent variable |p| into both parts of the pair node |r|@>;
+dep_mult(x_part_loc(r),u,true); dep_mult(y_part_loc(r),v,true);
+end;
+
+@ @<Move the dependent variable |p|...@>=
+type(y_part_loc(r)):=type(p);
+new_dep(y_part_loc(r),copy_dep_list(dep_list(p)));@/
+type(x_part_loc(r)):=type(p);
+mem[value_loc(x_part_loc(r))]:=mem[value_loc(p)];
+link(prev_dep(p)):=x_part_loc(r);
+free_node(p,value_node_size)
+
+@ @<Additional cases of binary operators@>=
+over: if (cur_type<>known)or(type(p)<pair_type) then bad_binary(p,over)
+ else begin v:=cur_exp; unstash_cur_exp(p);
+ if v=0 then @<Squeal about division by zero@>
+ else begin if cur_type=known then cur_exp:=make_scaled(cur_exp,v)
+ else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ dep_div(x_part_loc(p),v);
+ dep_div(y_part_loc(p),v);
+ end
+ else dep_div(null,v);
+ end;
+ return;
+ end;
+
+@ @<Declare binary action...@>=
+procedure dep_div(@!p:pointer;@!v:scaled);
+label exit;
+var @!q:pointer; {the dependency list being divided by |v|}
+@!s,@!t:small_number; {its type, before and after}
+begin if p=null then q:=cur_exp
+else if type(p)<>known then q:=p
+else begin value(p):=make_scaled(value(p),v); return;
+ end;
+t:=type(q); q:=dep_list(q); s:=t;
+if t=dependent then
+ if ab_vs_cd(max_coef(q),unity,coef_bound-1,abs(v))>=0 then t:=proto_dependent;
+q:=p_over_v(q,v,s,t); dep_finish(q,p,t);
+exit:end;
+
+@ @<Squeal about division by zero@>=
+begin exp_err("Division by zero");
+@.Division by zero@>
+help2("You're trying to divide the quantity shown above the error")@/
+ ("message by zero. I'm going to divide it by one instead.");
+put_get_error;
+end
+
+@ @<Additional cases of binary operators@>=
+pythag_add,pythag_sub: if (cur_type=known)and(type(p)=known) then
+ if c=pythag_add then cur_exp:=pyth_add(value(p),cur_exp)
+ else cur_exp:=pyth_sub(value(p),cur_exp)
+ else bad_binary(p,c);
+
+@ The next few sections of the program deal with affine transformations
+of coordinate data.
+
+@<Additional cases of binary operators@>=
+rotated_by,slanted_by,scaled_by,shifted_by,transformed_by,
+ x_scaled,y_scaled,z_scaled: @t@>@;@/
+ if (type(p)=path_type)or(type(p)=future_pen)or(type(p)=pen_type) then
+ begin path_trans(p,c); return;
+ end
+ else if (type(p)=pair_type)or(type(p)=transform_type) then big_trans(p,c)
+ else if type(p)=picture_type then
+ begin edges_trans(p,c); return;
+ end
+ else bad_binary(p,c);
+
+@ Let |c| be one of the eight transform operators. The procedure call
+|set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to
+|c| and the original value of |cur_exp|. (In particular, |cur_exp| doesn't
+change at all if |c=transformed_by|.)
+
+Then, if all components of the resulting transform are |known|, they are
+moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|;
+and |cur_exp| is changed to the known value zero.
+
+@<Declare binary action...@>=
+procedure set_up_trans(@!c:quarterword);
+label done,exit;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin if (c<>transformed_by)or(cur_type<>transform_type) then
+ @<Put the current transform into |cur_exp|@>;
+@<If the current transform is entirely known, stash it in global variables;
+ otherwise |return|@>;
+exit:end;
+
+@ @<Glob...@>=
+@!txx,@!txy,@!tyx,@!tyy,@!tx,@!ty:scaled; {current transform coefficients}
+
+@ @<Put the current transform...@>=
+begin p:=stash_cur_exp; cur_exp:=id_transform; cur_type:=transform_type;
+q:=value(cur_exp);
+case c of
+@<For each of the eight cases, change the relevant fields of |cur_exp|
+ and |goto done|;
+ but do nothing if capsule |p| doesn't have the appropriate type@>@;
+end; {there are no other cases}
+disp_err(p,"Improper transformation argument");
+@.Improper transformation argument@>
+help3("The expression shown above has the wrong type,")@/
+ ("so I can't transform anything using it.")@/
+ ("Proceed, and I'll omit the transformation.");
+put_get_error;
+done: recycle_value(p); free_node(p,value_node_size);
+end
+
+@ @<If the current transform is entirely known, ...@>=
+q:=value(cur_exp); r:=q+transform_node_size;
+repeat r:=r-2;
+if type(r)<>known then return;
+until r=q;
+txx:=value(xx_part_loc(q));
+txy:=value(xy_part_loc(q));
+tyx:=value(yx_part_loc(q));
+tyy:=value(yy_part_loc(q));
+tx:=value(x_part_loc(q));
+ty:=value(y_part_loc(q));
+flush_cur_exp(0)
+
+@ @<For each of the eight cases...@>=
+rotated_by:if type(p)=known then
+ @<Install sines and cosines, then |goto done|@>;
+slanted_by:if type(p)>pair_type then
+ begin install(xy_part_loc(q),p); goto done;
+ end;
+scaled_by:if type(p)>pair_type then
+ begin install(xx_part_loc(q),p); install(yy_part_loc(q),p); goto done;
+ end;
+shifted_by:if type(p)=pair_type then
+ begin r:=value(p); install(x_part_loc(q),x_part_loc(r));
+ install(y_part_loc(q),y_part_loc(r)); goto done;
+ end;
+x_scaled:if type(p)>pair_type then
+ begin install(xx_part_loc(q),p); goto done;
+ end;
+y_scaled:if type(p)>pair_type then
+ begin install(yy_part_loc(q),p); goto done;
+ end;
+z_scaled:if type(p)=pair_type then
+ @<Install a complex multiplier, then |goto done|@>;
+transformed_by:do_nothing;
+
+@ @<Install sines and cosines, then |goto done|@>=
+begin n_sin_cos((value(p) mod three_sixty_units)*16);
+value(xx_part_loc(q)):=round_fraction(n_cos);
+value(yx_part_loc(q)):=round_fraction(n_sin);
+value(xy_part_loc(q)):=-value(yx_part_loc(q));
+value(yy_part_loc(q)):=value(xx_part_loc(q));
+goto done;
+end
+
+@ @<Install a complex multiplier, then |goto done|@>=
+begin r:=value(p);
+install(xx_part_loc(q),x_part_loc(r));
+install(yy_part_loc(q),x_part_loc(r));
+install(yx_part_loc(q),y_part_loc(r));
+if type(y_part_loc(r))=known then negate(value(y_part_loc(r)))
+else negate_dep_list(dep_list(y_part_loc(r)));
+install(xy_part_loc(q),y_part_loc(r));
+goto done;
+end
+
+@ Procedure |set_up_known_trans| is like |set_up_trans|, but it
+insists that the transformation be entirely known.
+
+@<Declare binary action...@>=
+procedure set_up_known_trans(@!c:quarterword);
+begin set_up_trans(c);
+if cur_type<>known then
+ begin exp_err("Transform components aren't all known");
+@.Transform components...@>
+ help3("I'm unable to apply a partially specified transformation")@/
+ ("except to a fully known pair or transform.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_flush_error(0);
+ txx:=unity; txy:=0; tyx:=0; tyy:=unity; tx:=0; ty:=0;
+ end;
+end;
+
+@ Here's a procedure that applies the transform |txx..ty| to a pair of
+coordinates in locations |p| and~|q|.
+
+@<Declare binary action...@>=
+procedure trans(@!p,@!q:pointer);
+var @!v:scaled; {the new |x| value}
+begin v:=take_scaled(mem[p].sc,txx)+take_scaled(mem[q].sc,txy)+tx;
+mem[q].sc:=take_scaled(mem[p].sc,tyx)+take_scaled(mem[q].sc,tyy)+ty;
+mem[p].sc:=v;
+end;
+
+@ The simplest transformation procedure applies a transform to all
+coordinates of a path. The |null_pen| remains unchanged if it isn't
+being shifted.
+
+@<Declare binary action...@>=
+procedure path_trans(@!p:pointer;@!c:quarterword);
+label exit;
+var @!q:pointer; {list traverser}
+begin set_up_known_trans(c); unstash_cur_exp(p);
+if cur_type=pen_type then
+ begin if max_offset(cur_exp)=0 then if tx=0 then if ty=0 then return;
+ flush_cur_exp(make_path(cur_exp)); cur_type:=future_pen;
+ end;
+q:=cur_exp;
+repeat if left_type(q)<>endpoint then
+ trans(q+3,q+4); {that's |left_x| and |left_y|}
+trans(q+1,q+2); {that's |x_coord| and |y_coord|}
+if right_type(q)<>endpoint then
+ trans(q+5,q+6); {that's |right_x| and |right_y|}
+q:=link(q);
+until q=cur_exp;
+exit:end;
+
+@ The next simplest transformation procedure applies to edges.
+It is simple primarily because \MF\ doesn't allow very general
+transformations to be made, and because the tricky subroutines
+for edge transformation have already been written.
+
+@<Declare binary action...@>=
+procedure edges_trans(@!p:pointer;@!c:quarterword);
+label exit;
+begin set_up_known_trans(c); unstash_cur_exp(p); cur_edges:=cur_exp;
+if empty_edges(cur_edges) then return; {the empty set is easy to transform}
+if txx=0 then if tyy=0 then
+ if txy mod unity=0 then if tyx mod unity=0 then
+ begin xy_swap_edges; txx:=txy; tyy:=tyx; txy:=0; tyx:=0;
+ if empty_edges(cur_edges) then return;
+ end;
+if txy=0 then if tyx=0 then
+ if txx mod unity=0 then if tyy mod unity=0 then
+ @<Scale the edges, shift them, and |return|@>;
+print_err("That transformation is too hard");
+@.That transformation...@>
+help3("I can apply complicated transformations to paths,")@/
+ ("but I can only do integer operations on pictures.")@/
+ ("Proceed, and I'll omit the transformation.");
+put_get_error;
+exit:end;
+
+@ @<Scale the edges, shift them, and |return|@>=
+begin if (txx=0)or(tyy=0) then
+ begin toss_edges(cur_edges);
+ cur_exp:=get_node(edge_header_size); init_edges(cur_exp);
+ end
+else begin if txx<0 then
+ begin x_reflect_edges; txx:=-txx;
+ end;
+ if tyy<0 then
+ begin y_reflect_edges; tyy:=-tyy;
+ end;
+ if txx<>unity then x_scale_edges(txx div unity);
+ if tyy<>unity then y_scale_edges(tyy div unity);
+ @<Shift the edges by |(tx,ty)|, rounded@>;
+ end;
+return;
+end
+
+@ @<Shift the edges...@>=
+tx:=round_unscaled(tx); ty:=round_unscaled(ty);
+if (m_min(cur_edges)+tx<=0)or(m_max(cur_edges)+tx>=8192)or@|
+ (n_min(cur_edges)+ty<=0)or(n_max(cur_edges)+ty>=8191)or@|
+ (abs(tx)>=4096)or(abs(ty)>=4096) then
+ begin print_err("Too far to shift");
+@.Too far to shift@>
+ help3("I can't shift the picture as requested---it would")@/
+ ("make some coordinates too large or too small.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_error;
+ end
+else begin if tx<>0 then
+ begin if not valid_range(m_offset(cur_edges)-tx) then fix_offset;
+ m_min(cur_edges):=m_min(cur_edges)+tx;
+ m_max(cur_edges):=m_max(cur_edges)+tx;
+ m_offset(cur_edges):=m_offset(cur_edges)-tx;
+ last_window_time(cur_edges):=0;
+ end;
+ if ty<>0 then
+ begin n_min(cur_edges):=n_min(cur_edges)+ty;
+ n_max(cur_edges):=n_max(cur_edges)+ty;
+ n_pos(cur_edges):=n_pos(cur_edges)+ty;
+ last_window_time(cur_edges):=0;
+ end;
+ end
+
+@ The hard cases of transformation occur when big nodes are involved,
+and when some of their components are unknown.
+
+@<Declare binary action...@>=
+@t\4@>@<Declare subroutines needed by |big_trans|@>@;
+procedure big_trans(@!p:pointer;@!c:quarterword);
+label exit;
+var @!q,@!r,@!pp,@!qq:pointer; {list manipulation registers}
+@!s:small_number; {size of a big node}
+begin s:=big_node_size[type(p)]; q:=value(p); r:=q+s;
+repeat r:=r-2;
+if type(r)<>known then @<Transform an unknown big node and |return|@>;
+until r=q;
+@<Transform a known big node@>;
+exit:end; {node |p| will now be recycled by |do_binary|}
+
+@ @<Transform an unknown big node and |return|@>=
+begin set_up_known_trans(c); make_exp_copy(p); r:=value(cur_exp);
+if cur_type=transform_type then
+ begin bilin1(yy_part_loc(r),tyy,xy_part_loc(q),tyx,0);
+ bilin1(yx_part_loc(r),tyy,xx_part_loc(q),tyx,0);
+ bilin1(xy_part_loc(r),txx,yy_part_loc(q),txy,0);
+ bilin1(xx_part_loc(r),txx,yx_part_loc(q),txy,0);
+ end;
+bilin1(y_part_loc(r),tyy,x_part_loc(q),tyx,ty);
+bilin1(x_part_loc(r),txx,y_part_loc(q),txy,tx);
+return;
+end
+
+@ Let |p| point to a two-word value field inside a big node of |cur_exp|,
+and let |q| point to a another value field. The |bilin1| procedure
+replaces |p| by $p\cdot t+q\cdot u+\delta$.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure bilin1(@!p:pointer;@!t:scaled;@!q:pointer;@!u,@!delta:scaled);
+var @!r:pointer; {list traverser}
+begin if t<>unity then dep_mult(p,t,true);
+if u<>0 then
+ if type(q)=known then delta:=delta+take_scaled(value(q),u)
+ else begin @<Ensure that |type(p)=proto_dependent|@>;
+ dep_list(p):=p_plus_fq(dep_list(p),u,dep_list(q),proto_dependent,type(q));
+ end;
+if type(p)=known then value(p):=value(p)+delta
+else begin r:=dep_list(p);
+ while info(r)<>null do r:=link(r);
+ delta:=value(r)+delta;
+ if r<>dep_list(p) then value(r):=delta
+ else begin recycle_value(p); type(p):=known; value(p):=delta;
+ end;
+ end;
+if fix_needed then fix_dependencies;
+end;
+
+@ @<Ensure that |type(p)=proto_dependent|@>=
+if type(p)<>proto_dependent then
+ begin if type(p)=known then new_dep(p,const_dependency(value(p)))
+ else dep_list(p):=p_times_v(dep_list(p),unity,dependent,proto_dependent,true);
+ type(p):=proto_dependent;
+ end
+
+@ @<Transform a known big node@>=
+set_up_trans(c);
+if cur_type=known then @<Transform known by known@>
+else begin pp:=stash_cur_exp; qq:=value(pp);
+ make_exp_copy(p); r:=value(cur_exp);
+ if cur_type=transform_type then
+ begin bilin2(yy_part_loc(r),yy_part_loc(qq),
+ value(xy_part_loc(q)),yx_part_loc(qq),null);
+ bilin2(yx_part_loc(r),yy_part_loc(qq),
+ value(xx_part_loc(q)),yx_part_loc(qq),null);
+ bilin2(xy_part_loc(r),xx_part_loc(qq),
+ value(yy_part_loc(q)),xy_part_loc(qq),null);
+ bilin2(xx_part_loc(r),xx_part_loc(qq),
+ value(yx_part_loc(q)),xy_part_loc(qq),null);
+ end;
+ bilin2(y_part_loc(r),yy_part_loc(qq),
+ value(x_part_loc(q)),yx_part_loc(qq),y_part_loc(qq));
+ bilin2(x_part_loc(r),xx_part_loc(qq),
+ value(y_part_loc(q)),xy_part_loc(qq),x_part_loc(qq));
+ recycle_value(pp); free_node(pp,value_node_size);
+ end;
+
+@ Let |p| be a |proto_dependent| value whose dependency list ends
+at |dep_final|. The following procedure adds |v| times another
+numeric quantity to~|p|.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure add_mult_dep(@!p:pointer;@!v:scaled;@!r:pointer);
+begin if type(r)=known then
+ value(dep_final):=value(dep_final)+take_scaled(value(r),v)
+else begin dep_list(p):=
+ p_plus_fq(dep_list(p),v,dep_list(r),proto_dependent,type(r));
+ if fix_needed then fix_dependencies;
+ end;
+end;
+
+@ The |bilin2| procedure is something like |bilin1|, but with known
+and unknown quantities reversed. Parameter |p| points to a value field
+within the big node for |cur_exp|; and |type(p)=known|. Parameters
+|t| and~|u| point to value fields elsewhere; so does parameter~|q|,
+unless it is |null| (which stands for zero). Location~|p| will be
+replaced by $p\cdot t+v\cdot u+q$.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure bilin2(@!p,@!t:pointer;@!v:scaled;@!u,@!q:pointer);
+var @!vv:scaled; {temporary storage for |value(p)|}
+begin vv:=value(p); type(p):=proto_dependent;
+new_dep(p,const_dependency(0)); {this sets |dep_final|}
+if vv<>0 then add_mult_dep(p,vv,t); {|dep_final| doesn't change}
+if v<>0 then add_mult_dep(p,v,u);
+if q<>null then add_mult_dep(p,unity,q);
+if dep_list(p)=dep_final then
+ begin vv:=value(dep_final); recycle_value(p);
+ type(p):=known; value(p):=vv;
+ end;
+end;
+
+@ @<Transform known by known@>=
+begin make_exp_copy(p); r:=value(cur_exp);
+if cur_type=transform_type then
+ begin bilin3(yy_part_loc(r),tyy,value(xy_part_loc(q)),tyx,0);
+ bilin3(yx_part_loc(r),tyy,value(xx_part_loc(q)),tyx,0);
+ bilin3(xy_part_loc(r),txx,value(yy_part_loc(q)),txy,0);
+ bilin3(xx_part_loc(r),txx,value(yx_part_loc(q)),txy,0);
+ end;
+bilin3(y_part_loc(r),tyy,value(x_part_loc(q)),tyx,ty);
+bilin3(x_part_loc(r),txx,value(y_part_loc(q)),txy,tx);
+end
+
+@ Finally, in |bilin3| everything is |known|.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure bilin3(@!p:pointer;@!t,@!v,@!u,@!delta:scaled);
+begin if t<>unity then delta:=delta+take_scaled(value(p),t)
+else delta:=delta+value(p);
+if u<>0 then value(p):=delta+take_scaled(v,u)
+else value(p):=delta;
+end;
+
+@ @<Additional cases of binary operators@>=
+concatenate: if (cur_type=string_type)and(type(p)=string_type) then cat(p)
+ else bad_binary(p,concatenate);
+substring_of: if nice_pair(p,type(p))and(cur_type=string_type) then
+ chop_string(value(p))
+ else bad_binary(p,substring_of);
+subpath_of: begin if cur_type=pair_type then pair_to_path;
+ if nice_pair(p,type(p))and(cur_type=path_type) then
+ chop_path(value(p))
+ else bad_binary(p,subpath_of);
+ end;
+
+@ @<Declare binary action...@>=
+procedure cat(@!p:pointer);
+var @!a,@!b:str_number; {the strings being concatenated}
+@!k:pool_pointer; {index into |str_pool|}
+begin a:=value(p); b:=cur_exp; str_room(length(a)+length(b));
+for k:=str_start[a] to str_start[a+1]-1 do append_char(so(str_pool[k]));
+for k:=str_start[b] to str_start[b+1]-1 do append_char(so(str_pool[k]));
+cur_exp:=make_string; delete_str_ref(b);
+end;
+
+@ @<Declare binary action...@>=
+procedure chop_string(@!p:pointer);
+var @!a,@!b:integer; {start and stop points}
+@!l:integer; {length of the original string}
+@!k:integer; {runs from |a| to |b|}
+@!s:str_number; {the original string}
+@!reversed:boolean; {was |a>b|?}
+begin a:=round_unscaled(value(x_part_loc(p)));
+b:=round_unscaled(value(y_part_loc(p)));
+if a<=b then reversed:=false
+else begin reversed:=true; k:=a; a:=b; b:=k;
+ end;
+s:=cur_exp; l:=length(s);
+if a<0 then
+ begin a:=0;
+ if b<0 then b:=0;
+ end;
+if b>l then
+ begin b:=l;
+ if a>l then a:=l;
+ end;
+str_room(b-a);
+if reversed then
+ for k:=str_start[s]+b-1 downto str_start[s]+a do append_char(so(str_pool[k]))
+else for k:=str_start[s]+a to str_start[s]+b-1 do append_char(so(str_pool[k]));
+cur_exp:=make_string; delete_str_ref(s);
+end;
+
+@ @<Declare binary action...@>=
+procedure chop_path(@!p:pointer);
+var @!q:pointer; {a knot in the original path}
+@!pp,@!qq,@!rr,@!ss:pointer; {link variables for copies of path nodes}
+@!a,@!b,@!k,@!l:scaled; {indices for chopping}
+@!reversed:boolean; {was |a>b|?}
+begin l:=path_length; a:=value(x_part_loc(p)); b:=value(y_part_loc(p));
+if a<=b then reversed:=false
+else begin reversed:=true; k:=a; a:=b; b:=k;
+ end;
+@<Dispense with the cases |a<0| and/or |b>l|@>;
+q:=cur_exp;
+while a>=unity do
+ begin q:=link(q); a:=a-unity; b:=b-unity;
+ end;
+if b=a then @<Construct a path from |pp| to |qq| of length zero@>
+else @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>;
+left_type(pp):=endpoint; right_type(qq):=endpoint; link(qq):=pp;
+toss_knot_list(cur_exp);
+if reversed then
+ begin cur_exp:=link(htap_ypoc(pp)); toss_knot_list(pp);
+ end
+else cur_exp:=pp;
+end;
+
+@ @<Dispense with the cases |a<0| and/or |b>l|@>=
+if a<0 then
+ if left_type(cur_exp)=endpoint then
+ begin a:=0; if b<0 then b:=0;
+ end
+ else repeat a:=a+l; b:=b+l;
+ until a>=0; {a cycle always has length |l>0|}
+if b>l then if left_type(cur_exp)=endpoint then
+ begin b:=l; if a>l then a:=l;
+ end
+ else while a>=l do
+ begin a:=a-l; b:=b-l;
+ end
+
+@ @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>=
+begin pp:=copy_knot(q); qq:=pp;
+repeat q:=link(q); rr:=qq; qq:=copy_knot(q); link(rr):=qq; b:=b-unity;
+until b<=0;
+if a>0 then
+ begin ss:=pp; pp:=link(pp);
+ split_cubic(ss,a*@'10000,x_coord(pp),y_coord(pp)); pp:=link(ss);
+ free_node(ss,knot_node_size);
+ if rr=ss then
+ begin b:=make_scaled(b,unity-a); rr:=pp;
+ end;
+ end;
+if b<0 then
+ begin split_cubic(rr,(b+unity)*@'10000,x_coord(qq),y_coord(qq));
+ free_node(qq,knot_node_size);
+ qq:=link(rr);
+ end;
+end
+
+@ @<Construct a path from |pp| to |qq| of length zero@>=
+begin if a>0 then
+ begin qq:=link(q);
+ split_cubic(q,a*@'10000,x_coord(qq),y_coord(qq)); q:=link(q);
+ end;
+pp:=copy_knot(q); qq:=pp;
+end
+
+@ The |pair_value| routine changes the current expression to a
+given ordered pair of values.
+
+@<Declare binary action...@>=
+procedure pair_value(@!x,@!y:scaled);
+var @!p:pointer; {a pair node}
+begin p:=get_node(value_node_size); flush_cur_exp(p); cur_type:=pair_type;
+type(p):=pair_type; name_type(p):=capsule; init_big_node(p);
+p:=value(p);@/
+type(x_part_loc(p)):=known; value(x_part_loc(p)):=x;@/
+type(y_part_loc(p)):=known; value(y_part_loc(p)):=y;@/
+end;
+
+@ @<Additional cases of binary operators@>=
+point_of,precontrol_of,postcontrol_of: begin if cur_type=pair_type then
+ pair_to_path;
+ if (cur_type=path_type)and(type(p)=known) then
+ find_point(value(p),c)
+ else bad_binary(p,c);
+ end;
+pen_offset_of: begin if cur_type=future_pen then materialize_pen;
+ if (cur_type=pen_type)and nice_pair(p,type(p)) then
+ set_up_offset(value(p))
+ else bad_binary(p,pen_offset_of);
+ end;
+direction_time_of: begin if cur_type=pair_type then pair_to_path;
+ if (cur_type=path_type)and nice_pair(p,type(p)) then
+ set_up_direction_time(value(p))
+ else bad_binary(p,direction_time_of);
+ end;
+
+@ @<Declare binary action...@>=
+procedure set_up_offset(@!p:pointer);
+begin find_offset(value(x_part_loc(p)),value(y_part_loc(p)),cur_exp);
+pair_value(cur_x,cur_y);
+end;
+@#
+procedure set_up_direction_time(@!p:pointer);
+begin flush_cur_exp(find_direction_time(value(x_part_loc(p)),
+ value(y_part_loc(p)),cur_exp));
+end;
+
+@ @<Declare binary action...@>=
+procedure find_point(@!v:scaled;@!c:quarterword);
+var @!p:pointer; {the path}
+@!n:scaled; {its length}
+@!q:pointer; {successor of |p|}
+begin p:=cur_exp;@/
+if left_type(p)=endpoint then n:=-unity@+else n:=0;
+repeat p:=link(p); n:=n+unity;
+until p=cur_exp;
+if n=0 then v:=0
+else if v<0 then
+ if left_type(p)=endpoint then v:=0
+ else v:=n-1-((-v-1) mod n)
+else if v>n then
+ if left_type(p)=endpoint then v:=n
+ else v:=v mod n;
+p:=cur_exp;
+while v>=unity do
+ begin p:=link(p); v:=v-unity;
+ end;
+if v<>0 then @<Insert a fractional node by splitting the cubic@>;
+@<Set the current expression to the desired path coordinates@>;
+end;
+
+@ @<Insert a fractional node...@>=
+begin q:=link(p); split_cubic(p,v*@'10000,x_coord(q),y_coord(q)); p:=link(p);
+end
+
+@ @<Set the current expression to the desired path coordinates...@>=
+case c of
+point_of: pair_value(x_coord(p),y_coord(p));
+precontrol_of: if left_type(p)=endpoint then pair_value(x_coord(p),y_coord(p))
+ else pair_value(left_x(p),left_y(p));
+postcontrol_of: if right_type(p)=endpoint then pair_value(x_coord(p),y_coord(p))
+ else pair_value(right_x(p),right_y(p));
+end {there are no other cases}
+
+@ @<Additional cases of bin...@>=
+intersect: begin if type(p)=pair_type then
+ begin q:=stash_cur_exp; unstash_cur_exp(p);
+ pair_to_path; p:=stash_cur_exp; unstash_cur_exp(q);
+ end;
+ if cur_type=pair_type then pair_to_path;
+ if (cur_type=path_type)and(type(p)=path_type) then
+ begin path_intersection(value(p),cur_exp);
+ pair_value(cur_t,cur_tt);
+ end
+ else bad_binary(p,intersect);
+ end;
+
+@* \[43] Statements and commands.
+The chief executive of \MF\ is the |do_statement| routine, which
+contains the master switch that causes all the various pieces of \MF\
+to do their things, in the right order.
+
+In a sense, this is the grand climax of the program: It applies all the
+tools that we have worked so hard to construct. In another sense, this is
+the messiest part of the program: It necessarily refers to other pieces
+of code all over the place, so that a person can't fully understand what is
+going on without paging back and forth to be reminded of conventions that
+are defined elsewhere. We are now at the hub of the web.
+
+The structure of |do_statement| itself is quite simple. The first token
+of the statement is fetched using |get_x_next|. If it can be the first
+token of an expression, we look for an equation, an assignment, or a
+title. Otherwise we use a \&{case} construction to branch at high speed to
+the appropriate routine for various and sundry other types of commands,
+each of which has an ``action procedure'' that does the necessary work.
+
+The program uses the fact that
+$$\hbox{|min_primary_command=max_statement_command=type_name|}$$
+to interpret a statement that starts with, e.g., `\&{string}',
+as a type declaration rather than a boolean expression.
+
+@p @t\4@>@<Declare generic font output procedures@>@;
+@t\4@>@<Declare action procedures for use by |do_statement|@>@;
+procedure do_statement; {governs \MF's activities}
+begin cur_type:=vacuous; get_x_next;
+if cur_cmd>max_primary_command then @<Worry about bad statement@>
+else if cur_cmd>max_statement_command then
+ @<Do an equation, assignment, title, or
+ `$\langle\,$expression$\,\rangle\,$\&{endgroup}'@>
+else @<Do a statement that doesn't begin with an expression@>;
+if cur_cmd<semicolon then
+ @<Flush unparsable junk that was found after the statement@>;
+error_count:=0;
+end;
+
+@ The only command codes |>max_primary_command| that can be present
+at the beginning of a statement are |semicolon| and higher; these
+occur when the statement is null.
+
+@<Worry about bad statement@>=
+begin if cur_cmd<semicolon then
+ begin print_err("A statement can't begin with `");
+@.A statement can't begin with x@>
+ print_cmd_mod(cur_cmd,cur_mod); print_char("'");
+ help5("I was looking for the beginning of a new statement.")@/
+ ("If you just proceed without changing anything, I'll ignore")@/
+ ("everything up to the next `;'. Please insert a semicolon")@/
+ ("now in front of anything that you don't want me to delete.")@/
+ ("(See Chapter 27 of The METAFONTbook for an example.)");@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ back_error; get_x_next;
+ end;
+end
+
+@ The help message printed here says that everything is flushed up to
+a semicolon, but actually the commands |end_group| and |stop| will
+also terminate a statement.
+
+@<Flush unparsable junk that was found after the statement@>=
+begin print_err("Extra tokens will be flushed");
+@.Extra tokens will be flushed@>
+help6("I've just read as much of that statement as I could fathom,")@/
+("so a semicolon should have been next. It's very puzzling...")@/
+("but I'll try to get myself back together, by ignoring")@/
+("everything up to the next `;'. Please insert a semicolon")@/
+("now in front of anything that you don't want me to delete.")@/
+("(See Chapter 27 of The METAFONTbook for an example.)");@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+back_error; scanner_status:=flushing;
+repeat get_next;
+@<Decrease the string reference count...@>;
+until end_of_statement; {|cur_cmd=semicolon|, |end_group|, or |stop|}
+scanner_status:=normal;
+end
+
+@ If |do_statement| ends with |cur_cmd=end_group|, we should have
+|cur_type=vacuous| unless the statement was simply an expression;
+in the latter case, |cur_type| and |cur_exp| should represent that
+expression.
+
+@<Do a statement that doesn't...@>=
+begin if internal[tracing_commands]>0 then show_cur_cmd_mod;
+case cur_cmd of
+type_name:do_type_declaration;
+macro_def:if cur_mod>var_def then make_op_def
+ else if cur_mod>end_def then scan_def;
+@t\4@>@<Cases of |do_statement| that invoke particular commands@>@;
+end; {there are no other cases}
+cur_type:=vacuous;
+end
+
+@ The most important statements begin with expressions.
+
+@<Do an equation, assignment, title, or...@>=
+begin var_flag:=assignment; scan_expression;
+if cur_cmd<end_group then
+ begin if cur_cmd=equals then do_equation
+ else if cur_cmd=assignment then do_assignment
+ else if cur_type=string_type then @<Do a title@>
+ else if cur_type<>vacuous then
+ begin exp_err("Isolated expression");
+@.Isolated expression@>
+ help3("I couldn't find an `=' or `:=' after the")@/
+ ("expression that is shown above this error message,")@/
+ ("so I guess I'll just ignore it and carry on.");
+ put_get_error;
+ end;
+ flush_cur_exp(0); cur_type:=vacuous;
+ end;
+end
+
+@ @<Do a title@>=
+begin if internal[tracing_titles]>0 then
+ begin print_nl(""); slow_print(cur_exp); update_terminal;
+ end;
+if internal[proofing]>0 then
+ @<Send the current expression as a title to the output file@>;
+end
+
+@ Equations and assignments are performed by the pair of mutually recursive
+@^recursion@>
+routines |do_equation| and |do_assignment|. These routines are called when
+|cur_cmd=equals| and when |cur_cmd=assignment|, respectively; the left-hand
+side is in |cur_type| and |cur_exp|, while the right-hand side is yet
+to be scanned. After the routines are finished, |cur_type| and |cur_exp|
+will be equal to the right-hand side (which will normally be equal
+to the left-hand side).
+
+@<Declare action procedures for use by |do_statement|@>=
+@t\4@>@<Declare the procedure called |try_eq|@>@;
+@t\4@>@<Declare the procedure called |make_eq|@>@;
+procedure@?do_assignment; forward;@t\2@>@/
+procedure do_equation;
+var @!lhs:pointer; {capsule for the left-hand side}
+@!p:pointer; {temporary register}
+begin lhs:=stash_cur_exp; get_x_next; var_flag:=assignment; scan_expression;
+if cur_cmd=equals then do_equation
+else if cur_cmd=assignment then do_assignment;
+if internal[tracing_commands]>two then @<Trace the current equation@>;
+if cur_type=unknown_path then if type(lhs)=pair_type then
+ begin p:=stash_cur_exp; unstash_cur_exp(lhs); lhs:=p;
+ end; {in this case |make_eq| will change the pair to a path}
+make_eq(lhs); {equate |lhs| to |(cur_type,cur_exp)|}
+end;
+
+@ And |do_assignment| is similar to |do_equation|:
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_assignment;
+var @!lhs:pointer; {token list for the left-hand side}
+@!p:pointer; {where the left-hand value is stored}
+@!q:pointer; {temporary capsule for the right-hand value}
+begin if cur_type<>token_list then
+ begin exp_err("Improper `:=' will be changed to `='");
+@.Improper `:='@>
+ help2("I didn't find a variable name at the left of the `:=',")@/
+ ("so I'm going to pretend that you said `=' instead.");@/
+ error; do_equation;
+ end
+else begin lhs:=cur_exp; cur_type:=vacuous;@/
+ get_x_next; var_flag:=assignment; scan_expression;
+ if cur_cmd=equals then do_equation
+ else if cur_cmd=assignment then do_assignment;
+ if internal[tracing_commands]>two then @<Trace the current assignment@>;
+ if info(lhs)>hash_end then
+ @<Assign the current expression to an internal variable@>
+ else @<Assign the current expression to the variable |lhs|@>;
+ flush_node_list(lhs);
+ end;
+end;
+
+@ @<Trace the current equation@>=
+begin begin_diagnostic; print_nl("{("); print_exp(lhs,0);
+print(")=("); print_exp(null,0); print(")}"); end_diagnostic(false);
+end
+
+@ @<Trace the current assignment@>=
+begin begin_diagnostic; print_nl("{");
+if info(lhs)>hash_end then slow_print(int_name[info(lhs)-(hash_end)])
+else show_token_list(lhs,null,1000,0);
+print(":="); print_exp(null,0); print_char("}"); end_diagnostic(false);
+end
+
+@ @<Assign the current expression to an internal variable@>=
+if cur_type=known then internal[info(lhs)-(hash_end)]:=cur_exp
+else begin exp_err("Internal quantity `");
+@.Internal quantity...@>
+ slow_print(int_name[info(lhs)-(hash_end)]);
+ print("' must receive a known value");
+ help2("I can't set an internal quantity to anything but a known")@/
+ ("numeric value, so I'll have to ignore this assignment.");
+ put_get_error;
+ end
+
+@ @<Assign the current expression to the variable |lhs|@>=
+begin p:=find_variable(lhs);
+if p<>null then
+ begin q:=stash_cur_exp; cur_type:=und_type(p); recycle_value(p);
+ type(p):=cur_type; value(p):=null; make_exp_copy(p);
+ p:=stash_cur_exp; unstash_cur_exp(q); make_eq(p);
+ end
+else begin obliterated(lhs); put_get_error;
+ end;
+end
+
+
+@ And now we get to the nitty-gritty. The |make_eq| procedure is given
+a pointer to a capsule that is to be equated to the current expression.
+
+@<Declare the procedure called |make_eq|@>=
+procedure make_eq(@!lhs:pointer);
+label restart,done, not_found;
+var @!t:small_number; {type of the left-hand side}
+@!v:integer; {value of the left-hand side}
+@!p,@!q:pointer; {pointers inside of big nodes}
+begin restart: t:=type(lhs);
+if t<=pair_type then v:=value(lhs);
+case t of
+@t\4@>@<For each type |t|, make an equation and |goto done| unless |cur_type|
+ is incompatible with~|t|@>@;
+end; {all cases have been listed}
+@<Announce that the equation cannot be performed@>;
+done:check_arith; recycle_value(lhs); free_node(lhs,value_node_size);
+end;
+
+@ @<Announce that the equation cannot be performed@>=
+disp_err(lhs,""); exp_err("Equation cannot be performed (");
+@.Equation cannot be performed@>
+if type(lhs)<=pair_type then print_type(type(lhs))@+else print("numeric");
+print_char("=");
+if cur_type<=pair_type then print_type(cur_type)@+else print("numeric");
+print_char(")");@/
+help2("I'm sorry, but I don't know how to make such things equal.")@/
+ ("(See the two expressions just above the error message.)");
+put_get_error
+
+@ @<For each type |t|, make an equation and |goto done| unless...@>=
+boolean_type,string_type,pen_type,path_type,picture_type:
+ if cur_type=t+unknown_tag then
+ begin nonlinear_eq(v,cur_exp,false); unstash_cur_exp(cur_exp); goto done;
+ end
+ else if cur_type=t then
+ @<Report redundant or inconsistent equation and |goto done|@>;
+unknown_types:if cur_type=t-unknown_tag then
+ begin nonlinear_eq(cur_exp,lhs,true); goto done;
+ end
+ else if cur_type=t then
+ begin ring_merge(lhs,cur_exp); goto done;
+ end
+ else if cur_type=pair_type then if t=unknown_path then
+ begin pair_to_path; goto restart;
+ end;
+transform_type,pair_type:if cur_type=t then
+ @<Do multiple equations and |goto done|@>;
+known,dependent,proto_dependent,independent:if cur_type>=known then
+ begin try_eq(lhs,null); goto done;
+ end;
+vacuous:do_nothing;
+
+@ @<Report redundant or inconsistent equation and |goto done|@>=
+begin if cur_type<=string_type then
+ begin if cur_type=string_type then
+ begin if str_vs_str(v,cur_exp)<>0 then goto not_found;
+ end
+ else if v<>cur_exp then goto not_found;
+ @<Exclaim about a redundant equation@>; goto done;
+ end;
+print_err("Redundant or inconsistent equation");
+@.Redundant or inconsistent equation@>
+help2("An equation between already-known quantities can't help.")@/
+ ("But don't worry; continue and I'll just ignore it.");
+put_get_error; goto done;
+not_found: print_err("Inconsistent equation");
+@.Inconsistent equation@>
+help2("The equation I just read contradicts what was said before.")@/
+ ("But don't worry; continue and I'll just ignore it.");
+put_get_error; goto done;
+end
+
+@ @<Do multiple equations and |goto done|@>=
+begin p:=v+big_node_size[t]; q:=value(cur_exp)+big_node_size[t];
+repeat p:=p-2; q:=q-2; try_eq(p,q);
+until p=v;
+goto done;
+end
+
+@ The first argument to |try_eq| is the location of a value node
+in a capsule that will soon be recycled. The second argument is
+either a location within a pair or transform node pointed to by
+|cur_exp|, or it is |null| (which means that |cur_exp| itself
+serves as the second argument). The idea is to leave |cur_exp| unchanged,
+but to equate the two operands.
+
+@<Declare the procedure called |try_eq|@>=
+procedure try_eq(@!l,@!r:pointer);
+label done,done1;
+var @!p:pointer; {dependency list for right operand minus left operand}
+@!t:known..independent; {the type of list |p|}
+@!q:pointer; {the constant term of |p| is here}
+@!pp:pointer; {dependency list for right operand}
+@!tt:dependent..independent; {the type of list |pp|}
+@!copied:boolean; {have we copied a list that ought to be recycled?}
+begin @<Remove the left operand from its container, negate it, and
+ put it into dependency list~|p| with constant term~|q|@>;
+@<Add the right operand to list |p|@>;
+if info(p)=null then @<Deal with redundant or inconsistent equation@>
+else begin linear_eq(p,t);
+ if r=null then if cur_type<>known then if type(cur_exp)=known then
+ begin pp:=cur_exp; cur_exp:=value(cur_exp); cur_type:=known;
+ free_node(pp,value_node_size);
+ end;
+ end;
+end;
+
+@ @<Remove the left operand from its container, negate it, and...@>=
+t:=type(l);
+if t=known then
+ begin t:=dependent; p:=const_dependency(-value(l)); q:=p;
+ end
+else if t=independent then
+ begin t:=dependent; p:=single_dependency(l); negate(value(p));
+ q:=dep_final;
+ end
+else begin p:=dep_list(l); q:=p;
+ loop@+ begin negate(value(q));
+ if info(q)=null then goto done;
+ q:=link(q);
+ end;
+ done: link(prev_dep(l)):=link(q); prev_dep(link(q)):=prev_dep(l);
+ type(l):=known;
+ end
+
+@ @<Deal with redundant or inconsistent equation@>=
+begin if abs(value(p))>64 then {off by .001 or more}
+ begin print_err("Inconsistent equation");@/
+@.Inconsistent equation@>
+ print(" (off by "); print_scaled(value(p)); print_char(")");
+ help2("The equation I just read contradicts what was said before.")@/
+ ("But don't worry; continue and I'll just ignore it.");
+ put_get_error;
+ end
+else if r=null then @<Exclaim about a redundant equation@>;
+free_node(p,dep_node_size);
+end
+
+@ @<Add the right operand to list |p|@>=
+if r=null then
+ if cur_type=known then
+ begin value(q):=value(q)+cur_exp; goto done1;
+ end
+ else begin tt:=cur_type;
+ if tt=independent then pp:=single_dependency(cur_exp)
+ else pp:=dep_list(cur_exp);
+ end
+else if type(r)=known then
+ begin value(q):=value(q)+value(r); goto done1;
+ end
+ else begin tt:=type(r);
+ if tt=independent then pp:=single_dependency(r)
+ else pp:=dep_list(r);
+ end;
+if tt<>independent then copied:=false
+else begin copied:=true; tt:=dependent;
+ end;
+@<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>;
+if copied then flush_node_list(pp);
+done1:
+
+@ @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>=
+watch_coefs:=false;
+if t=tt then p:=p_plus_q(p,pp,t)
+else if t=proto_dependent then
+ p:=p_plus_fq(p,unity,pp,proto_dependent,dependent)
+else begin q:=p;
+ while info(q)<>null do
+ begin value(q):=round_fraction(value(q)); q:=link(q);
+ end;
+ t:=proto_dependent; p:=p_plus_q(p,pp,t);
+ end;
+watch_coefs:=true;
+
+@ Our next goal is to process type declarations. For this purpose it's
+convenient to have a procedure that scans a $\langle\,$declared
+variable$\,\rangle$ and returns the corresponding token list. After the
+following procedure has acted, the token after the declared variable
+will have been scanned, so it will appear in |cur_cmd|, |cur_mod|,
+and~|cur_sym|.
+
+@<Declare the function called |scan_declared_variable|@>=
+function scan_declared_variable:pointer;
+label done;
+var @!x:pointer; {hash address of the variable's root}
+@!h,@!t:pointer; {head and tail of the token list to be returned}
+@!l:pointer; {hash address of left bracket}
+begin get_symbol; x:=cur_sym;
+if cur_cmd<>tag_token then clear_symbol(x,false);
+h:=get_avail; info(h):=x; t:=h;@/
+loop@+ begin get_x_next;
+ if cur_sym=0 then goto done;
+ if cur_cmd<>tag_token then if cur_cmd<>internal_quantity then
+ if cur_cmd=left_bracket then @<Descend past a collective subscript@>
+ else goto done;
+ link(t):=get_avail; t:=link(t); info(t):=cur_sym;
+ end;
+done: if eq_type(x) mod outer_tag<>tag_token then clear_symbol(x,false);
+if equiv(x)=null then new_root(x);
+scan_declared_variable:=h;
+end;
+
+@ If the subscript isn't collective, we don't accept it as part of the
+declared variable.
+
+@<Descend past a collective subscript@>=
+begin l:=cur_sym; get_x_next;
+if cur_cmd<>right_bracket then
+ begin back_input; cur_sym:=l; cur_cmd:=left_bracket; goto done;
+ end
+else cur_sym:=collective_subscript;
+end
+
+@ Type declarations are introduced by the following primitive operations.
+
+@<Put each...@>=
+primitive("numeric",type_name,numeric_type);@/
+@!@:numeric_}{\&{numeric} primitive@>
+primitive("string",type_name,string_type);@/
+@!@:string_}{\&{string} primitive@>
+primitive("boolean",type_name,boolean_type);@/
+@!@:boolean_}{\&{boolean} primitive@>
+primitive("path",type_name,path_type);@/
+@!@:path_}{\&{path} primitive@>
+primitive("pen",type_name,pen_type);@/
+@!@:pen_}{\&{pen} primitive@>
+primitive("picture",type_name,picture_type);@/
+@!@:picture_}{\&{picture} primitive@>
+primitive("transform",type_name,transform_type);@/
+@!@:transform_}{\&{transform} primitive@>
+primitive("pair",type_name,pair_type);@/
+@!@:pair_}{\&{pair} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+type_name: print_type(m);
+
+@ Now we are ready to handle type declarations, assuming that a
+|type_name| has just been scanned.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_type_declaration;
+var @!t:small_number; {the type being declared}
+@!p:pointer; {token list for a declared variable}
+@!q:pointer; {value node for the variable}
+begin if cur_mod>=transform_type then t:=cur_mod@+else t:=cur_mod+unknown_tag;
+repeat p:=scan_declared_variable;
+flush_variable(equiv(info(p)),link(p),false);@/
+q:=find_variable(p);
+if q<>null then
+ begin type(q):=t; value(q):=null;
+ end
+else begin print_err("Declared variable conflicts with previous vardef");
+@.Declared variable conflicts...@>
+ help2("You can't use, e.g., `numeric foo[]' after `vardef foo'.")@/
+ ("Proceed, and I'll ignore the illegal redeclaration.");
+ put_get_error;
+ end;
+flush_list(p);
+if cur_cmd<comma then @<Flush spurious symbols after the declared variable@>;
+until end_of_statement;
+end;
+
+@ @<Flush spurious symbols after the declared variable@>=
+begin print_err("Illegal suffix of declared variable will be flushed");
+@.Illegal suffix...flushed@>
+help5("Variables in declarations must consist entirely of")@/
+ ("names and collective subscripts, e.g., `x[]a'.")@/
+ ("Are you trying to use a reserved word in a variable name?")@/
+ ("I'm going to discard the junk I found here,")@/
+ ("up to the next comma or the end of the declaration.");
+if cur_cmd=numeric_token then
+ help_line[2]:="Explicit subscripts like `x15a' aren't permitted.";
+put_get_error; scanner_status:=flushing;
+repeat get_next;
+@<Decrease the string reference count...@>;
+until cur_cmd>=comma; {either |end_of_statement| or |cur_cmd=comma|}
+scanner_status:=normal;
+end
+
+@ \MF's |main_control| procedure just calls |do_statement| repeatedly
+until coming to the end of the user's program.
+Each execution of |do_statement| concludes with
+|cur_cmd=semicolon|, |end_group|, or |stop|.
+
+@p procedure main_control;
+begin repeat do_statement;
+if cur_cmd=end_group then
+ begin print_err("Extra `endgroup'");
+@.Extra `endgroup'@>
+ help2("I'm not currently working on a `begingroup',")@/
+ ("so I had better not try to end anything.");
+ flush_error(0);
+ end;
+until cur_cmd=stop;
+end;
+
+@ @<Put each...@>=
+primitive("end",stop,0);@/
+@!@:end_}{\&{end} primitive@>
+primitive("dump",stop,1);@/
+@!@:dump_}{\&{dump} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+stop:if m=0 then print("end")@+else print("dump");
+
+@* \[44] Commands.
+Let's turn now to statements that are classified as ``commands'' because
+of their imperative nature. We'll begin with simple ones, so that it
+will be clear how to hook command processing into the |do_statement| routine;
+then we'll tackle the tougher commands.
+
+Here's one of the simplest:
+
+@<Cases of |do_statement|...@>=
+random_seed: do_random_seed;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_random_seed;
+begin get_x_next;
+if cur_cmd<>assignment then
+ begin missing_err(":=");
+@.Missing `:='@>
+ help1("Always say `randomseed:=<numeric expression>'.");
+ back_error;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then
+ begin exp_err("Unknown value will be ignored");
+@.Unknown value...ignored@>
+ help2("Your expression was too random for me to handle,")@/
+ ("so I won't change the random seed just now.");@/
+ put_get_flush_error(0);
+ end
+else @<Initialize the random seed to |cur_exp|@>;
+end;
+
+@ @<Initialize the random seed to |cur_exp|@>=
+begin init_randoms(cur_exp);
+if selector>=log_only then
+ begin old_setting:=selector; selector:=log_only;
+ print_nl("{randomseed:="); print_scaled(cur_exp); print_char("}");
+ print_nl(""); selector:=old_setting;
+ end;
+end
+
+@ And here's another simple one (somewhat different in flavor):
+
+@<Cases of |do_statement|...@>=
+mode_command: begin print_ln; interaction:=cur_mod;
+ @<Initialize the print |selector| based on |interaction|@>;
+ if log_opened then selector:=selector+2;
+ get_x_next;
+ end;
+
+@ @<Put each...@>=
+primitive("batchmode",mode_command,batch_mode);
+@!@:batch_mode_}{\&{batchmode} primitive@>
+primitive("nonstopmode",mode_command,nonstop_mode);
+@!@:nonstop_mode_}{\&{nonstopmode} primitive@>
+primitive("scrollmode",mode_command,scroll_mode);
+@!@:scroll_mode_}{\&{scrollmode} primitive@>
+primitive("errorstopmode",mode_command,error_stop_mode);
+@!@:error_stop_mode_}{\&{errorstopmode} primitive@>
+
+@ @<Cases of |print_cmd_mod|...@>=
+mode_command: case m of
+ batch_mode: print("batchmode");
+ nonstop_mode: print("nonstopmode");
+ scroll_mode: print("scrollmode");
+ othercases print("errorstopmode")
+ endcases;
+
+@ The `\&{inner}' and `\&{outer}' commands are only slightly harder.
+
+@<Cases of |do_statement|...@>=
+protection_command: do_protection;
+
+@ @<Put each...@>=
+primitive("inner",protection_command,0);@/
+@!@:inner_}{\&{inner} primitive@>
+primitive("outer",protection_command,1);@/
+@!@:outer_}{\&{outer} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+protection_command: if m=0 then print("inner")@+else print("outer");
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_protection;
+var @!m:0..1; {0 to unprotect, 1 to protect}
+@!t:halfword; {the |eq_type| before we change it}
+begin m:=cur_mod;
+repeat get_symbol; t:=eq_type(cur_sym);
+ if m=0 then
+ begin if t>=outer_tag then eq_type(cur_sym):=t-outer_tag;
+ end
+ else if t<outer_tag then eq_type(cur_sym):=t+outer_tag;
+ get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ \MF\ never defines the tokens `\.(' and `\.)' to be primitives, but
+plain \MF\ begins with the declaration `\&{delimiters} \.{()}'. Such a
+declaration assigns the command code |left_delimiter| to `\.{(}' and
+|right_delimiter| to `\.{)}'; the |equiv| of each delimiter is the
+hash address of its mate.
+
+@<Cases of |do_statement|...@>=
+delimiters: def_delims;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure def_delims;
+var l_delim,r_delim:pointer; {the new delimiter pair}
+begin get_clear_symbol; l_delim:=cur_sym;@/
+get_clear_symbol; r_delim:=cur_sym;@/
+eq_type(l_delim):=left_delimiter; equiv(l_delim):=r_delim;@/
+eq_type(r_delim):=right_delimiter; equiv(r_delim):=l_delim;@/
+get_x_next;
+end;
+
+@ Here is a procedure that is called when \MF\ has reached a point
+where some right delimiter is mandatory.
+
+@<Declare the procedure called |check_delimiter|@>=
+procedure check_delimiter(@!l_delim,@!r_delim:pointer);
+label exit;
+begin if cur_cmd=right_delimiter then if cur_mod=l_delim then return;
+if cur_sym<>r_delim then
+ begin missing_err(text(r_delim));@/
+@.Missing `)'@>
+ help2("I found no right delimiter to match a left one. So I've")@/
+ ("put one in, behind the scenes; this may fix the problem.");
+ back_error;
+ end
+else begin print_err("The token `"); slow_print(text(r_delim));
+@.The token...delimiter@>
+ print("' is no longer a right delimiter");
+ help3("Strange: This token has lost its former meaning!")@/
+ ("I'll read it as a right delimiter this time;")@/
+ ("but watch out, I'll probably miss it later.");
+ error;
+ end;
+exit:end;
+
+@ The next four commands save or change the values associated with tokens.
+
+@<Cases of |do_statement|...@>=
+save_command: repeat get_symbol; save_variable(cur_sym); get_x_next;
+ until cur_cmd<>comma;
+interim_command: do_interim;
+let_command: do_let;
+new_internal: do_new_internal;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure@?do_statement; forward;@t\2@>@/
+procedure do_interim;
+begin get_x_next;
+if cur_cmd<>internal_quantity then
+ begin print_err("The token `");
+@.The token...quantity@>
+ if cur_sym=0 then print("(%CAPSULE)")
+ else slow_print(text(cur_sym));
+ print("' isn't an internal quantity");
+ help1("Something like `tracingonline' should follow `interim'.");
+ back_error;
+ end
+else begin save_internal(cur_mod); back_input;
+ end;
+do_statement;
+end;
+
+@ The following procedure is careful not to undefine the left-hand symbol
+too soon, lest commands like `{\tt let x=x}' have a surprising effect.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_let;
+var @!l:pointer; {hash location of the left-hand symbol}
+begin get_symbol; l:=cur_sym; get_x_next;
+if cur_cmd<>equals then if cur_cmd<>assignment then
+ begin missing_err("=");
+@.Missing `='@>
+ help3("You should have said `let symbol = something'.")@/
+ ("But don't worry; I'll pretend that an equals sign")@/
+ ("was present. The next token I read will be `something'.");
+ back_error;
+ end;
+get_symbol;
+case cur_cmd of
+defined_macro,secondary_primary_macro,tertiary_secondary_macro,
+ expression_tertiary_macro: add_mac_ref(cur_mod);
+othercases do_nothing
+endcases;@/
+clear_symbol(l,false); eq_type(l):=cur_cmd;
+if cur_cmd=tag_token then equiv(l):=null
+else equiv(l):=cur_mod;
+get_x_next;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_new_internal;
+begin repeat if int_ptr=max_internal then
+ overflow("number of internals",max_internal);
+@:METAFONT capacity exceeded number of int}{\quad number of internals@>
+get_clear_symbol; incr(int_ptr);
+eq_type(cur_sym):=internal_quantity; equiv(cur_sym):=int_ptr;
+int_name[int_ptr]:=text(cur_sym); internal[int_ptr]:=0;
+get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ The various `\&{show}' commands are distinguished by modifier fields
+in the usual way.
+
+@d show_token_code=0 {show the meaning of a single token}
+@d show_stats_code=1 {show current memory and string usage}
+@d show_code=2 {show a list of expressions}
+@d show_var_code=3 {show a variable and its descendents}
+@d show_dependencies_code=4 {show dependent variables in terms of independents}
+
+@<Put each...@>=
+primitive("showtoken",show_command,show_token_code);@/
+@!@:show_token_}{\&{showtoken} primitive@>
+primitive("showstats",show_command,show_stats_code);@/
+@!@:show_stats_}{\&{showstats} primitive@>
+primitive("show",show_command,show_code);@/
+@!@:show_}{\&{show} primitive@>
+primitive("showvariable",show_command,show_var_code);@/
+@!@:show_var_}{\&{showvariable} primitive@>
+primitive("showdependencies",show_command,show_dependencies_code);@/
+@!@:show_dependencies_}{\&{showdependencies} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+show_command: case m of
+ show_token_code:print("showtoken");
+ show_stats_code:print("showstats");
+ show_code:print("show");
+ show_var_code:print("showvariable");
+ othercases print("showdependencies")
+ endcases;
+
+@ @<Cases of |do_statement|...@>=
+show_command:do_show_whatever;
+
+@ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine:
+If it's |show_code|, complicated structures are abbreviated, otherwise
+they aren't.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_show;
+begin repeat get_x_next; scan_expression;
+print_nl(">> ");
+@.>>@>
+print_exp(null,2); flush_cur_exp(0);
+until cur_cmd<>comma;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure disp_token;
+begin print_nl("> ");
+@.>\relax@>
+if cur_sym=0 then @<Show a numeric or string or capsule token@>
+else begin slow_print(text(cur_sym)); print_char("=");
+ if eq_type(cur_sym)>=outer_tag then print("(outer) ");
+ print_cmd_mod(cur_cmd,cur_mod);
+ if cur_cmd=defined_macro then
+ begin print_ln; show_macro(cur_mod,null,100000);
+ end; {this avoids recursion between |show_macro| and |print_cmd_mod|}
+@^recursion@>
+ end;
+end;
+
+@ @<Show a numeric or string or capsule token@>=
+begin if cur_cmd=numeric_token then print_scaled(cur_mod)
+else if cur_cmd=capsule_token then
+ begin g_pointer:=cur_mod; print_capsule;
+ end
+else begin print_char(""""); slow_print(cur_mod); print_char("""");
+ delete_str_ref(cur_mod);
+ end;
+end
+
+@ The following cases of |print_cmd_mod| might arise in connection
+with |disp_token|, although they don't necessarily correspond to
+primitive tokens.
+
+@<Cases of |print_cmd_...@>=
+left_delimiter,right_delimiter: begin if c=left_delimiter then print("lef")
+ else print("righ");
+ print("t delimiter that matches "); slow_print(text(m));
+ end;
+tag_token:if m=null then print("tag")@+else print("variable");
+defined_macro: print("macro:");
+secondary_primary_macro,tertiary_secondary_macro,expression_tertiary_macro:
+ begin print_cmd_mod(macro_def,c); print("'d macro:");
+ print_ln; show_token_list(link(link(m)),null,1000,0);
+ end;
+repeat_loop:print("[repeat the loop]");
+internal_quantity:slow_print(int_name[m]);
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_token;
+begin repeat get_next; disp_token;
+get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_stats;
+begin print_nl("Memory usage ");
+@.Memory usage...@>
+@!stat print_int(var_used); print_char("&"); print_int(dyn_used);
+if false then@+tats@t@>@;@/
+print("unknown");
+print(" ("); print_int(hi_mem_min-lo_mem_max-1);
+print(" still untouched)"); print_ln;
+print_nl("String usage ");
+print_int(str_ptr-init_str_ptr); print_char("&");
+print_int(pool_ptr-init_pool_ptr);
+print(" (");
+print_int(max_strings-max_str_ptr); print_char("&");
+print_int(pool_size-max_pool_ptr); print(" still untouched)"); print_ln;
+get_x_next;
+end;
+
+@ Here's a recursive procedure that gives an abbreviated account
+of a variable, for use by |do_show_var|.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure disp_var(@!p:pointer);
+var @!q:pointer; {traverses attributes and subscripts}
+@!n:0..max_print_line; {amount of macro text to show}
+begin if type(p)=structured then @<Descend the structure@>
+else if type(p)>=unsuffixed_macro then @<Display a variable macro@>
+else if type(p)<>undefined then
+ begin print_nl(""); print_variable_name(p); print_char("=");
+ print_exp(p,0);
+ end;
+end;
+
+@ @<Descend the structure@>=
+begin q:=attr_head(p);
+repeat disp_var(q); q:=link(q);
+until q=end_attr;
+q:=subscr_head(p);
+while name_type(q)=subscr do
+ begin disp_var(q); q:=link(q);
+ end;
+end
+
+@ @<Display a variable macro@>=
+begin print_nl(""); print_variable_name(p);
+if type(p)>unsuffixed_macro then print("@@#"); {|suffixed_macro|}
+print("=macro:");
+if file_offset>=max_print_line-20 then n:=5
+else n:=max_print_line-file_offset-15;
+show_macro(value(p),null,n);
+end
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_var;
+label done;
+begin repeat get_next;
+if cur_sym>0 then if cur_sym<=hash_end then
+ if cur_cmd=tag_token then if cur_mod<>null then
+ begin disp_var(cur_mod); goto done;
+ end;
+disp_token;
+done:get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_dependencies;
+var @!p:pointer; {link that runs through all dependencies}
+begin p:=link(dep_head);
+while p<>dep_head do
+ begin if interesting(p) then
+ begin print_nl(""); print_variable_name(p);
+ if type(p)=dependent then print_char("=")
+ else print(" = "); {extra spaces imply proto-dependency}
+ print_dependency(dep_list(p),type(p));
+ end;
+ p:=dep_list(p);
+ while info(p)<>null do p:=link(p);
+ p:=link(p);
+ end;
+get_x_next;
+end;
+
+@ Finally we are ready for the procedure that governs all of the
+show commands.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_show_whatever;
+begin if interaction=error_stop_mode then wake_up_terminal;
+case cur_mod of
+show_token_code:do_show_token;
+show_stats_code:do_show_stats;
+show_code:do_show;
+show_var_code:do_show_var;
+show_dependencies_code:do_show_dependencies;
+end; {there are no other cases}
+if internal[showstopping]>0 then
+ begin print_err("OK");
+@.OK@>
+ if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+ else help1("This isn't an error message; I'm just showing something.");
+ if cur_cmd=semicolon then error@+else put_get_error;
+ end;
+end;
+
+@ The `\&{addto}' command needs the following additional primitives:
+
+@d drop_code=0 {command modifier for `\&{dropping}'}
+@d keep_code=1 {command modifier for `\&{keeping}'}
+
+@<Put each...@>=
+primitive("contour",thing_to_add,contour_code);@/
+@!@:contour_}{\&{contour} primitive@>
+primitive("doublepath",thing_to_add,double_path_code);@/
+@!@:double_path_}{\&{doublepath} primitive@>
+primitive("also",thing_to_add,also_code);@/
+@!@:also_}{\&{also} primitive@>
+primitive("withpen",with_option,pen_type);@/
+@!@:with_pen_}{\&{withpen} primitive@>
+primitive("withweight",with_option,known);@/
+@!@:with_weight_}{\&{withweight} primitive@>
+primitive("dropping",cull_op,drop_code);@/
+@!@:dropping_}{\&{dropping} primitive@>
+primitive("keeping",cull_op,keep_code);@/
+@!@:keeping_}{\&{keeping} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+thing_to_add:if m=contour_code then print("contour")
+ else if m=double_path_code then print("doublepath")
+ else print("also");
+with_option:if m=pen_type then print("withpen")
+ else print("withweight");
+cull_op:if m=drop_code then print("dropping")
+ else print("keeping");
+
+@ @<Declare action procedures for use by |do_statement|@>=
+function scan_with:boolean;
+var @!t:small_number; {|known| or |pen_type|}
+@!result:boolean; {the value to return}
+begin t:=cur_mod; cur_type:=vacuous; get_x_next; scan_expression;
+result:=false;
+if cur_type<>t then @<Complain about improper type@>
+else if cur_type=pen_type then result:=true
+else @<Check the tentative weight@>;
+scan_with:=result;
+end;
+
+@ @<Complain about improper type@>=
+begin exp_err("Improper type");
+@.Improper type@>
+help2("Next time say `withweight <known numeric expression>';")@/
+ ("I'll ignore the bad `with' clause and look for another.");
+if t=pen_type then
+ help_line[1]:="Next time say `withpen <known pen expression>';";
+put_get_flush_error(0);
+end
+
+@ @<Check the tentative weight@>=
+begin cur_exp:=round_unscaled(cur_exp);
+if (abs(cur_exp)<4)and(cur_exp<>0) then result:=true
+else begin print_err("Weight must be -3, -2, -1, +1, +2, or +3");
+@.Weight must be...@>
+ help1("I'll ignore the bad `with' clause and look for another.");
+ put_get_flush_error(0);
+ end;
+end
+
+@ One of the things we need to do when we've parsed an \&{addto} or
+similar command is set |cur_edges| to the header of a supposed \&{picture}
+variable, given a token list for that variable.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure find_edges_var(@!t:pointer);
+var @!p:pointer;
+begin p:=find_variable(t); cur_edges:=null;
+if p=null then
+ begin obliterated(t); put_get_error;
+ end
+else if type(p)<>picture_type then
+ begin print_err("Variable "); show_token_list(t,null,1000,0);
+@.Variable x is the wrong type@>
+ print(" is the wrong type ("); print_type(type(p)); print_char(")");
+ help2("I was looking for a ""known"" picture variable.")@/
+ ("So I'll not change anything just now."); put_get_error;
+ end
+else cur_edges:=value(p);
+flush_node_list(t);
+end;
+
+@ @<Cases of |do_statement|...@>=
+add_to_command: do_add_to;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_add_to;
+label done, not_found;
+var @!lhs,@!rhs:pointer; {variable on left, path on right}
+@!w:integer; {tentative weight}
+@!p:pointer; {list manipulation register}
+@!q:pointer; {beginning of second half of doubled path}
+@!add_to_type:double_path_code..also_code; {modifier of \&{addto}}
+begin get_x_next; var_flag:=thing_to_add; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin lhs:=cur_exp; add_to_type:=cur_mod;@/
+ cur_type:=vacuous; get_x_next; scan_expression;
+ if add_to_type=also_code then @<Augment some edges by others@>
+ else @<Get ready to fill a contour, and fill it@>;
+ end;
+end;
+
+@ @<Abandon edges command because there's no variable@>=
+begin exp_err("Not a suitable variable");
+@.Not a suitable variable@>
+help4("At this point I needed to see the name of a picture variable.")@/
+ ("(Or perhaps you have indeed presented me with one; I might")@/
+ ("have missed it, if it wasn't followed by the proper token.)")@/
+ ("So I'll not change anything just now.");
+put_get_flush_error(0);
+end
+
+@ @<Augment some edges by others@>=
+begin find_edges_var(lhs);
+if cur_edges=null then flush_cur_exp(0)
+else if cur_type<>picture_type then
+ begin exp_err("Improper `addto'");
+@.Improper `addto'@>
+ help2("This expression should have specified a known picture.")@/
+ ("So I'll not change anything just now."); put_get_flush_error(0);
+ end
+else begin merge_edges(cur_exp); flush_cur_exp(0);
+ end;
+end
+
+@ @<Get ready to fill a contour...@>=
+begin if cur_type=pair_type then pair_to_path;
+if cur_type<>path_type then
+ begin exp_err("Improper `addto'");
+@.Improper `addto'@>
+ help2("This expression should have been a known path.")@/
+ ("So I'll not change anything just now.");
+ put_get_flush_error(0); flush_token_list(lhs);
+ end
+else begin rhs:=cur_exp; w:=1; cur_pen:=null_pen;
+ while cur_cmd=with_option do
+ if scan_with then
+ if cur_type=known then w:=cur_exp
+ else @<Change the tentative pen@>;
+ @<Complete the contour filling operation@>;
+ delete_pen_ref(cur_pen);
+ end;
+end
+
+@ We could say `|add_pen_ref(cur_pen)|; |flush_cur_exp(0)|' after changing
+|cur_pen| here. But that would have no effect, because the current expression
+will not be flushed. Thus we save a bit of code (at the risk of being too
+tricky).
+
+@<Change the tentative pen@>=
+begin delete_pen_ref(cur_pen); cur_pen:=cur_exp;
+end
+
+@ @<Complete the contour filling...@>=
+find_edges_var(lhs);
+if cur_edges=null then toss_knot_list(rhs)
+else begin lhs:=null; cur_path_type:=add_to_type;
+ if left_type(rhs)=endpoint then
+ if cur_path_type=double_path_code then @<Double the path@>
+ else @<Complain about non-cycle and |goto not_found|@>
+ else if cur_path_type=double_path_code then lhs:=htap_ypoc(rhs);
+ cur_wt:=w; rhs:=make_spec(rhs,max_offset(cur_pen),internal[tracing_specs]);
+ @<Check the turning number@>;
+ if max_offset(cur_pen)=0 then fill_spec(rhs)
+ else fill_envelope(rhs);
+ if lhs<>null then
+ begin rev_turns:=true;
+ lhs:=make_spec(lhs,max_offset(cur_pen),internal[tracing_specs]);
+ rev_turns:=false;
+ if max_offset(cur_pen)=0 then fill_spec(lhs)
+ else fill_envelope(lhs);
+ end;
+not_found: end
+
+@ @<Double the path@>=
+if link(rhs)=rhs then @<Make a trivial one-point path cycle@>
+else begin p:=htap_ypoc(rhs); q:=link(p);@/
+ right_x(path_tail):=right_x(q); right_y(path_tail):=right_y(q);
+ right_type(path_tail):=right_type(q);
+ link(path_tail):=link(q); free_node(q,knot_node_size);@/
+ right_x(p):=right_x(rhs); right_y(p):=right_y(rhs);
+ right_type(p):=right_type(rhs);
+ link(p):=link(rhs); free_node(rhs,knot_node_size);@/
+ rhs:=p;
+ end
+
+@ @<Make a trivial one-point path cycle@>=
+begin right_x(rhs):=x_coord(rhs); right_y(rhs):=y_coord(rhs);
+left_x(rhs):=x_coord(rhs); left_y(rhs):=y_coord(rhs);
+left_type(rhs):=explicit; right_type(rhs):=explicit;
+end
+
+@ @<Complain about non-cycle...@>=
+begin print_err("Not a cycle");
+@.Not a cycle@>
+help2("That contour should have ended with `..cycle' or `&cycle'.")@/
+ ("So I'll not change anything just now."); put_get_error;
+toss_knot_list(rhs); goto not_found;
+end
+
+@ @<Check the turning number@>=
+if turning_number<=0 then
+ if cur_path_type<>double_path_code then if internal[turning_check]>0 then
+ if (turning_number<0)and(link(cur_pen)=null) then negate(cur_wt)
+ else begin if turning_number=0 then
+ if (internal[turning_check]<=unity)and(link(cur_pen)=null) then goto done
+ else print_strange("Strange path (turning number is zero)")
+@.Strange path...@>
+ else print_strange("Backwards path (turning number is negative)");
+@.Backwards path...@>
+ help3("The path doesn't have a counterclockwise orientation,")@/
+ ("so I'll probably have trouble drawing it.")@/
+ ("(See Chapter 27 of The METAFONTbook for more help.)");
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ put_get_error;
+ end;
+done:
+
+@ @<Cases of |do_statement|...@>=
+ship_out_command: do_ship_out;
+display_command: do_display;
+open_window: do_open_window;
+cull_command: do_cull;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+@t\4@>@<Declare the function called |tfm_check|@>@;
+procedure do_ship_out;
+label exit;
+var @!c:integer; {the character code}
+begin get_x_next; var_flag:=semicolon; scan_expression;
+if cur_type<>token_list then
+ if cur_type=picture_type then cur_edges:=cur_exp
+ else begin @<Abandon edges command because there's no variable@>;
+ return;
+ end
+else begin find_edges_var(cur_exp); cur_type:=vacuous;
+ end;
+if cur_edges<>null then
+ begin c:=round_unscaled(internal[char_code]) mod 256;
+ if c<0 then c:=c+256;
+ @<Store the width information for character code~|c|@>;
+ if internal[proofing]>=0 then ship_out(c);
+ end;
+flush_cur_exp(0);
+exit:end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_display;
+label not_found,common_ending,exit;
+var @!e:pointer; {token list for a picture variable}
+begin get_x_next; var_flag:=in_window; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin e:=cur_exp; cur_type:=vacuous;
+ get_x_next; scan_expression;
+ if cur_type<>known then goto common_ending;
+ cur_exp:=round_unscaled(cur_exp);
+ if cur_exp<0 then goto not_found;
+ if cur_exp>15 then goto not_found;
+ if not window_open[cur_exp] then goto not_found;
+ find_edges_var(e);
+ if cur_edges<>null then disp_edges(cur_exp);
+ return;
+ not_found: cur_exp:=cur_exp*unity;
+ common_ending: exp_err("Bad window number");
+@.Bad window number@>
+ help1("It should be the number of an open window.");
+ put_get_flush_error(0); flush_token_list(e);
+ end;
+exit:end;
+
+@ The only thing difficult about `\&{openwindow}' is that the syntax
+allows the user to go astray in many ways. The following subroutine
+helps keep the necessary program reasonably short and sweet.
+
+@<Declare action procedures for use by |do_statement|@>=
+function get_pair(@!c:command_code):boolean;
+var @!p:pointer; {a pair of values that are known (we hope)}
+@!b:boolean; {did we find such a pair?}
+begin if cur_cmd<>c then get_pair:=false
+else begin get_x_next; scan_expression;
+ if nice_pair(cur_exp,cur_type) then
+ begin p:=value(cur_exp);
+ cur_x:=value(x_part_loc(p)); cur_y:=value(y_part_loc(p));
+ b:=true;
+ end
+ else b:=false;
+ flush_cur_exp(0); get_pair:=b;
+ end;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_open_window;
+label not_found,exit;
+var @!k:integer; {the window number in question}
+@!r0,@!c0,@!r1,@!c1:scaled; {window coordinates}
+begin get_x_next; scan_expression;
+if cur_type<>known then goto not_found;
+k:=round_unscaled(cur_exp);
+if k<0 then goto not_found;
+if k>15 then goto not_found;
+if not get_pair(from_token) then goto not_found;
+r0:=cur_x; c0:=cur_y;
+if not get_pair(to_token) then goto not_found;
+r1:=cur_x; c1:=cur_y;
+if not get_pair(at_token) then goto not_found;
+open_a_window(k,r0,c0,r1,c1,cur_x,cur_y); return;
+not_found:print_err("Improper `openwindow'");
+@.Improper `openwindow'@>
+help2("Say `openwindow k from (r0,c0) to (r1,c1) at (x,y)',")@/
+ ("where all quantities are known and k is between 0 and 15.");
+put_get_error;
+exit:end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_cull;
+label not_found,exit;
+var @!e:pointer; {token list for a picture variable}
+@!keeping:drop_code..keep_code; {modifier of |cull_op|}
+@!w,@!w_in,@!w_out:integer; {culling weights}
+begin w:=1;
+get_x_next; var_flag:=cull_op; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin e:=cur_exp; cur_type:=vacuous; keeping:=cur_mod;
+ if not get_pair(cull_op) then goto not_found;
+ while (cur_cmd=with_option)and(cur_mod=known) do
+ if scan_with then w:=cur_exp;
+ @<Set up the culling weights,
+ or |goto not_found| if the thresholds are bad@>;
+ find_edges_var(e);
+ if cur_edges<>null then
+ cull_edges(floor_unscaled(cur_x+unity-1),floor_unscaled(cur_y),w_out,w_in);
+ return;
+ not_found: print_err("Bad culling amounts");
+@.Bad culling amounts@>
+ help1("Always cull by known amounts that exclude 0.");
+ put_get_error; flush_token_list(e);
+ end;
+exit:end;
+
+@ @<Set up the culling weights, or |goto not_found| if the thresholds are bad@>=
+if cur_x>cur_y then goto not_found;
+if keeping=drop_code then
+ begin if (cur_x>0)or(cur_y<0) then goto not_found;
+ w_out:=w; w_in:=0;
+ end
+else begin if (cur_x<=0)and(cur_y>=0) then goto not_found;
+ w_out:=0; w_in:=w;
+ end
+
+@ The \&{everyjob} command simply assigns a nonzero value to the global variable
+|start_sym|.
+
+@<Cases of |do_statement|...@>=
+every_job_command: begin get_symbol; start_sym:=cur_sym; get_x_next;
+ end;
+
+@ @<Glob...@>=
+@!start_sym:halfword; {a symbolic token to insert at beginning of job}
+
+@ @<Set init...@>=
+start_sym:=0;
+
+@ Finally, we have only the ``message'' commands remaining.
+
+@d message_code=0
+@d err_message_code=1
+@d err_help_code=2
+
+@<Put each...@>=
+primitive("message",message_command,message_code);@/
+@!@:message_}{\&{message} primitive@>
+primitive("errmessage",message_command,err_message_code);@/
+@!@:err_message_}{\&{errmessage} primitive@>
+primitive("errhelp",message_command,err_help_code);@/
+@!@:err_help_}{\&{errhelp} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+message_command: if m<err_message_code then print("message")
+ else if m=err_message_code then print("errmessage")
+ else print("errhelp");
+
+@ @<Cases of |do_statement|...@>=
+message_command: do_message;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_message;
+var @!m:message_code..err_help_code; {the type of message}
+begin m:=cur_mod; get_x_next; scan_expression;
+if cur_type<>string_type then
+ begin exp_err("Not a string");
+@.Not a string@>
+ help1("A message should be a known string expression.");
+ put_get_error;
+ end
+else case m of
+ message_code:begin print_nl(""); slow_print(cur_exp);
+ end;
+ err_message_code:@<Print string |cur_exp| as an error message@>;
+ err_help_code:@<Save string |cur_exp| as the |err_help|@>;
+ end; {there are no other cases}
+flush_cur_exp(0);
+end;
+
+@ The global variable |err_help| is zero when the user has most recently
+given an empty help string, or if none has ever been given.
+
+@<Save string |cur_exp| as the |err_help|@>=
+begin if err_help<>0 then delete_str_ref(err_help);
+if length(cur_exp)=0 then err_help:=0
+else begin err_help:=cur_exp; add_str_ref(err_help);
+ end;
+end
+
+@ If \&{errmessage} occurs often in |scroll_mode|, without user-defined
+\&{errhelp}, we don't want to give a long help message each time. So we
+give a verbose explanation only once.
+
+@<Glob...@>=
+@!long_help_seen:boolean; {has the long \&{errmessage} help been used?}
+
+@ @<Set init...@>=long_help_seen:=false;
+
+@ @<Print string |cur_exp| as an error message@>=
+begin print_err(""); slow_print(cur_exp);
+if err_help<>0 then use_err_help:=true
+else if long_help_seen then help1("(That was another `errmessage'.)")
+else begin if interaction<error_stop_mode then long_help_seen:=true;
+ help4("This error message was generated by an `errmessage'")@/
+ ("command, so I can't give any explicit help.")@/
+ ("Pretend that you're Miss Marple: Examine all clues,")@/
+@^Marple, Jane@>
+ ("and deduce the truth by inspired guesses.");
+ end;
+put_get_error; use_err_help:=false;
+end
+
+@* \[45] Font metric data.
+\TeX\ gets its knowledge about fonts from font metric files, also called
+\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
+but other programs know about them too. One of \MF's duties is to
+write \.{TFM} files so that the user's fonts can readily be
+applied to typesetting.
+@:TFM files}{\.{TFM} files@>
+@^font metric files@>
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of~4, we could
+also regard the file as a sequence of 32-bit words, but \MF\ uses the
+byte interpretation. The format of \.{TFM} files was designed by
+Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
+@^Ramshaw, Lyle Harold@>
+of information in a compact but useful form.
+
+@<Glob...@>=
+@!tfm_file:byte_file; {the font metric output goes here}
+@!metric_file_name: str_number; {full name of the font metric file}
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|lf|&length of the entire file, in words;\cr
+|lh|&length of the header data, in words;\cr
+|bc|&smallest character code in the font;\cr
+|ec|&largest character code in the font;\cr
+|nw|&number of words in the width table;\cr
+|nh|&number of words in the height table;\cr
+|nd|&number of words in the depth table;\cr
+|ni|&number of words in the italic correction table;\cr
+|nl|&number of words in the lig/kern table;\cr
+|nk|&number of words in the kern table;\cr
+|ne|&number of words in the extensible character table;\cr
+|np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+|ne<=256|, and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
+and as few as 0 characters (if |bc=ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@!@^BigEndian order@>
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\tabskip\centering
+\halign to\displaywidth{\hfil\\{#}\tabskip=0pt&$\,:\,$\arr#\hfil
+ \tabskip\centering\cr
+header&|[0..lh-1]@t\\{stuff}@>|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&|[1..np]fix_word|\cr}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+|header[0]| and |header[1]|, whose meaning is explained below. Additional
+header information of use to other software routines might also be
+included, and \MF\ will generate it if the \.{headerbyte} command occurs.
+For example, 16 more words of header information are in use at the Xerox
+Palo Alto Research Center; the first ten specify the character coding
+scheme used (e.g., `\.{XEROX TEXT}' or `\.{TEX MATHSY}'), the next five
+give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
+last gives the ``face byte.''
+
+\yskip\hang|header[0]| is a 32-bit check sum that \MF\ will copy into
+the \.{GF} output file. This helps ensure consistency between files,
+since \TeX\ records the check sums from the \.{TFM}'s it reads, and these
+should match the check sums on actual fonts that are used. The actual
+relation between this check sum and the rest of the \.{TFM} file is not
+important; the check sum is simply an identification number with the
+property that incompatible fonts almost always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of the
+font, in units of \TeX\ points. This number must be at least 1.0; it is
+fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
+font, i.e., a font that was designed to look best at a 10-point size,
+whatever that really means. When a \TeX\ user asks for a font `\.{at}
+$\delta$ \.{pt}', the effect is to override the design size and replace it
+by $\delta$, and to multiply the $x$ and~$y$ coordinates of the points in
+the font image by a factor of $\delta$ divided by the design size. {\sl
+All other dimensions in the\/ \.{TFM} file are |fix_word|\kern-1pt\
+numbers in design-size units.} Thus, for example, the value of |param[6]|,
+which defines the \.{em} unit, is often the |fix_word| value $2^{20}=1.0$,
+since many fonts have a design size equal to one em. The other dimensions
+must be less than 16 design-size units in absolute value; thus,
+|header[1]| and |param[1]| are the only |fix_word| entries in the whole
+\.{TFM} file whose first byte might be something besides 0 or 255.
+@^design size@>
+
+@ Next comes the |char_info| array, which contains one |@!char_info_word|
+per character. Each word in this part of the file contains six fields
+packed into four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+ (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+ (2~bits)\par
+\hang fourth byte: |@!remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero. The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\def\hangg#1 {\hang\hbox{#1 }}
+\yskip\hangg|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hangg|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at location |remainder| in the |lig_kern| array.\par
+\hangg|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|remainder| field gives the character code of the next larger character.\par
+\hangg|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[remainder]|.\par
+\yskip\noindent
+Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
+unless they are used in special circumstances in math formulas. For example,
+\TeX's \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
+operation looks for both |list_tag| and |ext_tag|.
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
+the condition
+$$\hbox{|256*op_byte+remainder<nl|.}$$
+If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+command is performed.
+
+@d stop_flag=128+min_quarterword
+ {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128+min_quarterword {op code for a kern step}
+@d skip_byte(#)==lig_kern[#].b0
+@d next_char(#)==lig_kern[#].b1
+@d op_byte(#)==lig_kern[#].b2
+@d rem_byte(#)==lig_kern[#].b3
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this
+order). These bytes are the character codes of individual pieces used to
+build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
+if the piece isn't present. Then the extensible characters have the form
+$TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
+in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
+The width of the extensible character is the width of $R$; and the
+height-plus-depth is the sum of the individual height-plus-depths of the
+components used, since the pieces are butted together in a vertical list.
+
+@d ext_top(#)==exten[#].b0 {|top| piece in a recipe}
+@d ext_mid(#)==exten[#].b1 {|mid| piece in a recipe}
+@d ext_bot(#)==exten[#].b2 {|bot| piece in a recipe}
+@d ext_rep(#)==exten[#].b3 {|rep| piece in a recipe}
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it is the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+@^design size@>
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character @'40 in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the size of one ex in the font; it is also
+the height of letters for which accents don't have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+\yskip\noindent
+If fewer than seven parameters are present, \TeX\ sets the missing parameters
+to zero.
+
+@d slant_code=1
+@d space_code=2
+@d space_stretch_code=3
+@d space_shrink_code=4
+@d x_height_code=5
+@d quad_code=6
+@d extra_space_code=7
+
+@ So that is what \.{TFM} files hold. One of \MF's duties is to output such
+information, and it does this all at once at the end of a job.
+In order to prepare for such frenetic activity, it squirrels away the
+necessary facts in various arrays as information becomes available.
+
+Character dimensions (\&{charwd}, \&{charht}, \&{chardp}, and \&{charic})
+are stored respectively in |tfm_width|, |tfm_height|, |tfm_depth|, and
+|tfm_ital_corr|. Other information about a character (e.g., about
+its ligatures or successors) is accessible via the |char_tag| and
+|char_remainder| arrays. Other information about the font as a whole
+is kept in additional arrays called |header_byte|, |lig_kern|,
+|kern|, |exten|, and |param|.
+
+@d undefined_label==lig_table_size {an undefined local label}
+
+@<Glob...@>=
+@!bc,@!ec:eight_bits; {smallest and largest character codes shipped out}
+@!tfm_width:array[eight_bits] of scaled; {\&{charwd} values}
+@!tfm_height:array[eight_bits] of scaled; {\&{charht} values}
+@!tfm_depth:array[eight_bits] of scaled; {\&{chardp} values}
+@!tfm_ital_corr:array[eight_bits] of scaled; {\&{charic} values}
+@!char_exists:array[eight_bits] of boolean; {has this code been shipped out?}
+@!char_tag:array[eight_bits] of no_tag..ext_tag; {|remainder| category}
+@!char_remainder:array[eight_bits] of 0..lig_table_size; {the |remainder| byte}
+@!header_byte:array[1..header_size] of -1..255;
+ {bytes of the \.{TFM} header, or $-1$ if unset}
+@!lig_kern:array[0..lig_table_size] of four_quarters; {the ligature/kern table}
+@!nl:0..32767-256; {the number of ligature/kern steps so far}
+@!kern:array[0..max_kerns] of scaled; {distinct kerning amounts}
+@!nk:0..max_kerns; {the number of distinct kerns so far}
+@!exten:array[eight_bits] of four_quarters; {extensible character recipes}
+@!ne:0..256; {the number of extensible characters so far}
+@!param:array[1..max_font_dimen] of scaled; {\&{fontinfo} parameters}
+@!np:0..max_font_dimen; {the largest \&{fontinfo} parameter specified so far}
+@!nw,@!nh,@!nd,@!ni:0..256; {sizes of \.{TFM} subtables}
+@!skip_table:array[eight_bits] of 0..lig_table_size; {local label status}
+@!lk_started:boolean; {has there been a lig/kern step in this command yet?}
+@!bchar:integer; {right boundary character}
+@!bch_label:0..lig_table_size; {left boundary starting location}
+@!ll,@!lll:0..lig_table_size; {registers used for lig/kern processing}
+@!label_loc:array[0..256] of -1..lig_table_size; {lig/kern starting addresses}
+@!label_char:array[1..256] of eight_bits; {characters for |label_loc|}
+@!label_ptr:0..256; {highest position occupied in |label_loc|}
+
+@ @<Set init...@>=
+for k:=0 to 255 do
+ begin tfm_width[k]:=0; tfm_height[k]:=0; tfm_depth[k]:=0; tfm_ital_corr[k]:=0;
+ char_exists[k]:=false; char_tag[k]:=no_tag; char_remainder[k]:=0;
+ skip_table[k]:=undefined_label;
+ end;
+for k:=1 to header_size do header_byte[k]:=-1;
+bc:=255; ec:=0; nl:=0; nk:=0; ne:=0; np:=0;@/
+internal[boundary_char]:=-unity;
+bch_label:=undefined_label;@/
+label_loc[0]:=-1; label_ptr:=0;
+
+@ @<Declare the function called |tfm_check|@>=
+function tfm_check(@!m:small_number):scaled;
+begin if abs(internal[m])>=fraction_half then
+ begin print_err("Enormous "); print(int_name[m]);
+@.Enormous charwd...@>
+@.Enormous chardp...@>
+@.Enormous charht...@>
+@.Enormous charic...@>
+@.Enormous designsize...@>
+ print(" has been reduced");
+ help1("Font metric dimensions must be less than 2048pt.");
+ put_get_error;
+ if internal[m]>0 then tfm_check:=fraction_half-1
+ else tfm_check:=1-fraction_half;
+ end
+else tfm_check:=internal[m];
+end;
+
+@ @<Store the width information for character code~|c|@>=
+if c<bc then bc:=c;
+if c>ec then ec:=c;
+char_exists[c]:=true;
+gf_dx[c]:=internal[char_dx]; gf_dy[c]:=internal[char_dy];
+tfm_width[c]:=tfm_check(char_wd);
+tfm_height[c]:=tfm_check(char_ht);
+tfm_depth[c]:=tfm_check(char_dp);
+tfm_ital_corr[c]:=tfm_check(char_ic)
+
+@ Now let's consider \MF's special \.{TFM}-oriented commands.
+
+@<Cases of |do_statement|...@>=
+tfm_command: do_tfm_command;
+
+@ @d char_list_code=0
+@d lig_table_code=1
+@d extensible_code=2
+@d header_byte_code=3
+@d font_dimen_code=4
+
+@<Put each...@>=
+primitive("charlist",tfm_command,char_list_code);@/
+@!@:char_list_}{\&{charlist} primitive@>
+primitive("ligtable",tfm_command,lig_table_code);@/
+@!@:lig_table_}{\&{ligtable} primitive@>
+primitive("extensible",tfm_command,extensible_code);@/
+@!@:extensible_}{\&{extensible} primitive@>
+primitive("headerbyte",tfm_command,header_byte_code);@/
+@!@:header_byte_}{\&{headerbyte} primitive@>
+primitive("fontdimen",tfm_command,font_dimen_code);@/
+@!@:font_dimen_}{\&{fontdimen} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+tfm_command: case m of
+ char_list_code:print("charlist");
+ lig_table_code:print("ligtable");
+ extensible_code:print("extensible");
+ header_byte_code:print("headerbyte");
+ othercases print("fontdimen")
+ endcases;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+function get_code:eight_bits; {scans a character code value}
+label found;
+var @!c:integer; {the code value found}
+begin get_x_next; scan_expression;
+if cur_type=known then
+ begin c:=round_unscaled(cur_exp);
+ if c>=0 then if c<256 then goto found;
+ end
+else if cur_type=string_type then if length(cur_exp)=1 then
+ begin c:=so(str_pool[str_start[cur_exp]]); goto found;
+ end;
+exp_err("Invalid code has been replaced by 0");
+@.Invalid code...@>
+help2("I was looking for a number between 0 and 255, or for a")@/
+ ("string of length 1. Didn't find it; will use 0 instead.");
+put_get_flush_error(0); c:=0;
+found: get_code:=c;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure set_tag(@!c:halfword;@!t:small_number;@!r:halfword);
+begin if char_tag[c]=no_tag then
+ begin char_tag[c]:=t; char_remainder[c]:=r;
+ if t=lig_tag then
+ begin incr(label_ptr); label_loc[label_ptr]:=r; label_char[label_ptr]:=c;
+ end;
+ end
+else @<Complain about a character tag conflict@>;
+end;
+
+@ @<Complain about a character tag conflict@>=
+begin print_err("Character ");
+if (c>" ")and(c<127) then print(c)
+else if c=256 then print("||")
+else begin print("code "); print_int(c);
+ end;
+print(" is already ");
+@.Character c is already...@>
+case char_tag[c] of
+lig_tag: print("in a ligtable");
+list_tag: print("in a charlist");
+ext_tag: print("extensible");
+end; {there are no other cases}
+help2("It's not legal to label a character more than once.")@/
+ ("So I'll not change anything just now.");
+put_get_error; end
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_tfm_command;
+label continue,done;
+var @!c,@!cc:0..256; {character codes}
+@!k:0..max_kerns; {index into the |kern| array}
+@!j:integer; {index into |header_byte| or |param|}
+begin case cur_mod of
+char_list_code: begin c:=get_code;
+ {we will store a list of character successors}
+ while cur_cmd=colon do
+ begin cc:=get_code; set_tag(c,list_tag,cc); c:=cc;
+ end;
+ end;
+lig_table_code: @<Store a list of ligature/kern steps@>;
+extensible_code: @<Define an extensible recipe@>;
+header_byte_code, font_dimen_code: begin c:=cur_mod; get_x_next;
+ scan_expression;
+ if (cur_type<>known)or(cur_exp<half_unit) then
+ begin exp_err("Improper location");
+@.Improper location@>
+ help2("I was looking for a known, positive number.")@/
+ ("For safety's sake I'll ignore the present command.");
+ put_get_error;
+ end
+ else begin j:=round_unscaled(cur_exp);
+ if cur_cmd<>colon then
+ begin missing_err(":");
+@.Missing `:'@>
+ help1("A colon should follow a headerbyte or fontinfo location.");
+ back_error;
+ end;
+ if c=header_byte_code then @<Store a list of header bytes@>
+ else @<Store a list of font dimensions@>;
+ end;
+ end;
+end; {there are no other cases}
+end;
+
+@ @<Store a list of ligature/kern steps@>=
+begin lk_started:=false;
+continue: get_x_next;
+if(cur_cmd=skip_to)and lk_started then
+ @<Process a |skip_to| command and |goto done|@>;
+if cur_cmd=bchar_label then
+ begin c:=256; cur_cmd:=colon;@+end
+else begin back_input; c:=get_code;@+end;
+if(cur_cmd=colon)or(cur_cmd=double_colon)then
+ @<Record a label in a lig/kern subprogram and |goto continue|@>;
+if cur_cmd=lig_kern_token then @<Compile a ligature/kern command@>
+else begin print_err("Illegal ligtable step");
+@.Illegal ligtable step@>
+ help1("I was looking for `=:' or `kern' here.");
+ back_error; next_char(nl):=qi(0); op_byte(nl):=qi(0); rem_byte(nl):=qi(0);@/
+ skip_byte(nl):=stop_flag+1; {this specifies an unconditional stop}
+ end;
+if nl=lig_table_size then overflow("ligtable size",lig_table_size);
+@:METAFONT capacity exceeded ligtable size}{\quad ligtable size@>
+incr(nl);
+if cur_cmd=comma then goto continue;
+if skip_byte(nl-1)<stop_flag then skip_byte(nl-1):=stop_flag;
+done:end
+
+@ @<Put each...@>=
+primitive("=:",lig_kern_token,0);
+@!@:=:_}{\.{=:} primitive@>
+primitive("=:|",lig_kern_token,1);
+@!@:=:/_}{\.{=:\char'174} primitive@>
+primitive("=:|>",lig_kern_token,5);
+@!@:=:/>_}{\.{=:\char'174>} primitive@>
+primitive("|=:",lig_kern_token,2);
+@!@:=:/_}{\.{\char'174=:} primitive@>
+primitive("|=:>",lig_kern_token,6);
+@!@:=:/>_}{\.{\char'174=:>} primitive@>
+primitive("|=:|",lig_kern_token,3);
+@!@:=:/_}{\.{\char'174=:\char'174} primitive@>
+primitive("|=:|>",lig_kern_token,7);
+@!@:=:/>_}{\.{\char'174=:\char'174>} primitive@>
+primitive("|=:|>>",lig_kern_token,11);
+@!@:=:/>_}{\.{\char'174=:\char'174>>} primitive@>
+primitive("kern",lig_kern_token,128);
+@!@:kern_}{\&{kern} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+lig_kern_token: case m of
+0:print("=:");
+1:print("=:|");
+2:print("|=:");
+3:print("|=:|");
+5:print("=:|>");
+6:print("|=:>");
+7:print("|=:|>");
+11:print("|=:|>>");
+othercases print("kern")
+endcases;
+
+@ Local labels are implemented by maintaining the |skip_table| array,
+where |skip_table[c]| is either |undefined_label| or the address of the
+most recent lig/kern instruction that skips to local label~|c|. In the
+latter case, the |skip_byte| in that instruction will (temporarily)
+be zero if there were no prior skips to this label, or it will be the
+distance to the prior skip.
+
+We may need to cancel skips that span more than 127 lig/kern steps.
+
+@d cancel_skips(#)==ll:=#;
+ repeat lll:=qo(skip_byte(ll)); skip_byte(ll):=stop_flag; ll:=ll-lll;
+ until lll=0
+@d skip_error(#)==begin print_err("Too far to skip");
+@.Too far to skip@>
+ help1("At most 127 lig/kern steps can separate skipto1 from 1::.");
+ error; cancel_skips(#);
+ end
+
+@<Process a |skip_to| command and |goto done|@>=
+begin c:=get_code;
+if nl-skip_table[c]>128 then
+ begin skip_error(skip_table[c]); skip_table[c]:=undefined_label;
+ end;
+if skip_table[c]=undefined_label then skip_byte(nl-1):=qi(0)
+else skip_byte(nl-1):=qi(nl-skip_table[c]-1);
+skip_table[c]:=nl-1; goto done;
+end
+
+@ @<Record a label in a lig/kern subprogram and |goto continue|@>=
+begin if cur_cmd=colon then
+ if c=256 then bch_label:=nl
+ else set_tag(c,lig_tag,nl)
+else if skip_table[c]<undefined_label then
+ begin ll:=skip_table[c]; skip_table[c]:=undefined_label;
+ repeat lll:=qo(skip_byte(ll));
+ if nl-ll>128 then
+ begin skip_error(ll); goto continue;
+ end;
+ skip_byte(ll):=qi(nl-ll-1); ll:=ll-lll;
+ until lll=0;
+ end;
+goto continue;
+end
+
+@ @<Compile a ligature/kern...@>=
+begin next_char(nl):=qi(c); skip_byte(nl):=qi(0);
+if cur_mod<128 then {ligature op}
+ begin op_byte(nl):=qi(cur_mod); rem_byte(nl):=qi(get_code);
+ end
+else begin get_x_next; scan_expression;
+ if cur_type<>known then
+ begin exp_err("Improper kern");
+@.Improper kern@>
+ help2("The amount of kern should be a known numeric value.")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+ put_get_flush_error(0);
+ end;
+ kern[nk]:=cur_exp;
+ k:=0;@+while kern[k]<>cur_exp do incr(k);
+ if k=nk then
+ begin if nk=max_kerns then overflow("kern",max_kerns);
+@:METAFONT capacity exceeded kern}{\quad kern@>
+ incr(nk);
+ end;
+ op_byte(nl):=kern_flag+(k div 256);
+ rem_byte(nl):=qi((k mod 256));
+ end;
+lk_started:=true;
+end
+
+@ @d missing_extensible_punctuation(#)==
+ begin missing_err(#);
+@.Missing `\char`\#'@>
+ help1("I'm processing `extensible c: t,m,b,r'."); back_error;
+ end
+
+@<Define an extensible recipe@>=
+begin if ne=256 then overflow("extensible",256);
+@:METAFONT capacity exceeded extensible}{\quad extensible@>
+c:=get_code; set_tag(c,ext_tag,ne);
+if cur_cmd<>colon then missing_extensible_punctuation(":");
+ext_top(ne):=qi(get_code);
+if cur_cmd<>comma then missing_extensible_punctuation(",");
+ext_mid(ne):=qi(get_code);
+if cur_cmd<>comma then missing_extensible_punctuation(",");
+ext_bot(ne):=qi(get_code);
+if cur_cmd<>comma then missing_extensible_punctuation(",");
+ext_rep(ne):=qi(get_code);
+incr(ne);
+end
+
+@ @<Store a list of header bytes@>=
+repeat if j>header_size then overflow("headerbyte",header_size);
+@:METAFONT capacity exceeded headerbyte}{\quad headerbyte@>
+header_byte[j]:=get_code; incr(j);
+until cur_cmd<>comma
+
+@ @<Store a list of font dimensions@>=
+repeat if j>max_font_dimen then overflow("fontdimen",max_font_dimen);
+@:METAFONT capacity exceeded fontdimen}{\quad fontdimen@>
+while j>np do
+ begin incr(np); param[np]:=0;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then
+ begin exp_err("Improper font parameter");
+@.Improper font parameter@>
+ help1("I'm zeroing this one. Proceed, with fingers crossed.");
+ put_get_flush_error(0);
+ end;
+param[j]:=cur_exp; incr(j);
+until cur_cmd<>comma
+
+@ OK: We've stored all the data that is needed for the \.{TFM} file.
+All that remains is to output it in the correct format.
+
+An interesting problem needs to be solved in this connection, because
+the \.{TFM} format allows at most 256~widths, 16~heights, 16~depths,
+and 64~italic corrections. If the data has more distinct values than
+this, we want to meet the necessary restrictions by perturbing the
+given values as little as possible.
+
+\MF\ solves this problem in two steps. First the values of a given
+kind (widths, heights, depths, or italic corrections) are sorted;
+then the list of sorted values is perturbed, if necessary.
+
+The sorting operation is facilitated by having a special node of
+essentially infinite |value| at the end of the current list.
+
+@<Initialize table entries...@>=
+value(inf_val):=fraction_four;
+
+@ Straight linear insertion is good enough for sorting, since the lists
+are usually not terribly long. As we work on the data, the current list
+will start at |link(temp_head)| and end at |inf_val|; the nodes in this
+list will be in increasing order of their |value| fields.
+
+Given such a list, the |sort_in| function takes a value and returns a pointer
+to where that value can be found in the list. The value is inserted in
+the proper place, if necessary.
+
+At the time we need to do these operations, most of \MF's work has been
+completed, so we will have plenty of memory to play with. The value nodes
+that are allocated for sorting will never be returned to free storage.
+
+@d clear_the_list==link(temp_head):=inf_val
+
+@p function sort_in(@!v:scaled):pointer;
+label found;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin p:=temp_head;
+loop@+ begin q:=link(p);
+ if v<=value(q) then goto found;
+ p:=q;
+ end;
+found: if v<value(q) then
+ begin r:=get_node(value_node_size); value(r):=v; link(r):=q; link(p):=r;
+ end;
+sort_in:=link(p);
+end;
+
+@ Now we come to the interesting part, where we reduce the list if necessary
+until it has the required size. The |min_cover| routine is basic to this
+process; it computes the minimum number~|m| such that the values of the
+current sorted list can be covered by |m|~intervals of width~|d|. It
+also sets the global value |perturbation| to the smallest value $d'>d$
+such that the covering found by this algorithm would be different.
+
+In particular, |min_cover(0)| returns the number of distinct values in the
+current list and sets |perturbation| to the minimum distance between
+adjacent values.
+
+@p function min_cover(@!d:scaled):integer;
+var @!p:pointer; {runs through the current list}
+@!l:scaled; {the least element covered by the current interval}
+@!m:integer; {lower bound on the size of the minimum cover}
+begin m:=0; p:=link(temp_head); perturbation:=el_gordo;
+while p<>inf_val do
+ begin incr(m); l:=value(p);
+ repeat p:=link(p);
+ until value(p)>l+d;
+ if value(p)-l<perturbation then perturbation:=value(p)-l;
+ end;
+min_cover:=m;
+end;
+
+@ @<Glob...@>=
+@!perturbation:scaled; {quantity related to \.{TFM} rounding}
+@!excess:integer; {the list is this much too long}
+
+@ The smallest |d| such that a given list can be covered with |m| intervals
+is determined by the |threshold| routine, which is sort of an inverse
+to |min_cover|. The idea is to increase the interval size rapidly until
+finding the range, then to go sequentially until the exact borderline has
+been discovered.
+
+@p function threshold(@!m:integer):scaled;
+var @!d:scaled; {lower bound on the smallest interval size}
+begin excess:=min_cover(0)-m;
+if excess<=0 then threshold:=0
+else begin repeat d:=perturbation;
+ until min_cover(d+d)<=m;
+ while min_cover(d)>m do d:=perturbation;
+ threshold:=d;
+ end;
+end;
+
+@ The |skimp| procedure reduces the current list to at most |m| entries,
+by changing values if necessary. It also sets |info(p):=k| if |value(p)|
+is the |k|th distinct value on the resulting list, and it sets
+|perturbation| to the maximum amount by which a |value| field has
+been changed. The size of the resulting list is returned as the
+value of |skimp|.
+
+@p function skimp(@!m:integer):integer;
+var @!d:scaled; {the size of intervals being coalesced}
+@!p,@!q,@!r:pointer; {list manipulation registers}
+@!l:scaled; {the least value in the current interval}
+@!v:scaled; {a compromise value}
+begin d:=threshold(m); perturbation:=0;
+q:=temp_head; m:=0; p:=link(temp_head);
+while p<>inf_val do
+ begin incr(m); l:=value(p); info(p):=m;
+ if value(link(p))<=l+d then
+ @<Replace an interval of values by its midpoint@>;
+ q:=p; p:=link(p);
+ end;
+skimp:=m;
+end;
+
+@ @<Replace an interval...@>=
+begin repeat p:=link(p); info(p):=m;
+decr(excess);@+if excess=0 then d:=0;
+until value(link(p))>l+d;
+v:=l+half(value(p)-l);
+if value(p)-v>perturbation then perturbation:=value(p)-v;
+r:=q;
+repeat r:=link(r); value(r):=v;
+until r=p;
+link(q):=p; {remove duplicate values from the current list}
+end
+
+@ A warning message is issued whenever something is perturbed by
+more than 1/16\thinspace pt.
+
+@p procedure tfm_warning(@!m:small_number);
+begin print_nl("(some "); print(int_name[m]);
+@.some charwds...@>
+@.some chardps...@>
+@.some charhts...@>
+@.some charics...@>
+print(" values had to be adjusted by as much as ");
+print_scaled(perturbation); print("pt)");
+end;
+
+@ Here's an example of how we use these routines.
+The width data needs to be perturbed only if there are 256 distinct
+widths, but \MF\ must check for this case even though it is
+highly unusual.
+
+An integer variable |k| will be defined when we use this code.
+The |dimen_head| array will contain pointers to the sorted
+lists of dimensions.
+
+@<Massage the \.{TFM} widths@>=
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ tfm_width[k]:=sort_in(tfm_width[k]);
+nw:=skimp(255)+1; dimen_head[1]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_wd)
+
+@ @<Glob...@>=
+@!dimen_head:array[1..4] of pointer; {lists of \.{TFM} dimensions}
+
+@ Heights, depths, and italic corrections are different from widths
+not only because their list length is more severely restricted, but
+also because zero values do not need to be put into the lists.
+
+@<Massage the \.{TFM} heights, depths, and italic corrections@>=
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ if tfm_height[k]=0 then tfm_height[k]:=zero_val
+ else tfm_height[k]:=sort_in(tfm_height[k]);
+nh:=skimp(15)+1; dimen_head[2]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_ht);
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ if tfm_depth[k]=0 then tfm_depth[k]:=zero_val
+ else tfm_depth[k]:=sort_in(tfm_depth[k]);
+nd:=skimp(15)+1; dimen_head[3]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_dp);
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ if tfm_ital_corr[k]=0 then tfm_ital_corr[k]:=zero_val
+ else tfm_ital_corr[k]:=sort_in(tfm_ital_corr[k]);
+ni:=skimp(63)+1; dimen_head[4]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_ic)
+
+@ @<Initialize table entries...@>=
+value(zero_val):=0; info(zero_val):=0;
+
+@ Bytes 5--8 of the header are set to the design size, unless the user has
+some crazy reason for specifying them differently.
+@^design size@>
+
+Error messages are not allowed at the time this procedure is called,
+so a warning is printed instead.
+
+The value of |max_tfm_dimen| is calculated so that
+$$\hbox{|make_scaled(16*max_tfm_dimen,internal[design_size])|}
+ < \\{three\_bytes}.$$
+
+@d three_bytes==@'100000000 {$2^{24}$}
+
+@p procedure fix_design_size;
+var @!d:scaled; {the design size}
+begin d:=internal[design_size];
+if (d<unity)or(d>=fraction_half) then
+ begin if d<>0 then
+ print_nl("(illegal design size has been changed to 128pt)");
+@.illegal design size...@>
+ d:=@'40000000; internal[design_size]:=d;
+ end;
+if header_byte[5]<0 then if header_byte[6]<0 then
+ if header_byte[7]<0 then if header_byte[8]<0 then
+ begin header_byte[5]:=d div @'4000000;
+ header_byte[6]:=(d div 4096) mod 256;
+ header_byte[7]:=(d div 16) mod 256;
+ header_byte[8]:=(d mod 16)*16;
+ end;
+max_tfm_dimen:=16*internal[design_size]-1-internal[design_size] div @'10000000;
+if max_tfm_dimen>=fraction_half then max_tfm_dimen:=fraction_half-1;
+end;
+
+@ The |dimen_out| procedure computes a |fix_word| relative to the
+design size. If the data was out of range, it is corrected and the
+global variable |tfm_changed| is increased by~one.
+
+@p function dimen_out(@!x:scaled):integer;
+begin if abs(x)>max_tfm_dimen then
+ begin incr(tfm_changed);
+ if x>0 then x:=max_tfm_dimen@+else x:=-max_tfm_dimen;
+ end;
+x:=make_scaled(x*16,internal[design_size]);
+dimen_out:=x;
+end;
+
+@ @<Glob...@>=
+@!max_tfm_dimen:scaled; {bound on widths, heights, kerns, etc.}
+@!tfm_changed:integer; {the number of data entries that were out of bounds}
+
+@ If the user has not specified any of the first four header bytes,
+the |fix_check_sum| procedure replaces them by a ``check sum'' computed
+from the |tfm_width| data relative to the design size.
+@^check sum@>
+
+@p procedure fix_check_sum;
+label exit;
+var @!k:eight_bits; {runs through character codes}
+@!b1,@!b2,@!b3,@!b4:eight_bits; {bytes of the check sum}
+@!x:integer; {hash value used in check sum computation}
+begin if header_byte[1]<0 then if header_byte[2]<0 then
+ if header_byte[3]<0 then if header_byte[4]<0 then
+ begin @<Compute a check sum in |(b1,b2,b3,b4)|@>;
+ header_byte[1]:=b1; header_byte[2]:=b2;
+ header_byte[3]:=b3; header_byte[4]:=b4; return;
+ end;
+for k:=1 to 4 do if header_byte[k]<0 then header_byte[k]:=0;
+exit:end;
+
+@ @<Compute a check sum in |(b1,b2,b3,b4)|@>=
+b1:=bc; b2:=ec; b3:=bc; b4:=ec; tfm_changed:=0;
+for k:=bc to ec do if char_exists[k] then
+ begin x:=dimen_out(value(tfm_width[k]))+(k+4)*@'20000000; {this is positive}
+ b1:=(b1+b1+x) mod 255;
+ b2:=(b2+b2+x) mod 253;
+ b3:=(b3+b3+x) mod 251;
+ b4:=(b4+b4+x) mod 247;
+ end
+
+@ Finally we're ready to actually write the \.{TFM} information.
+Here are some utility routines for this purpose.
+
+@d tfm_out(#)==write(tfm_file,#) {output one byte to |tfm_file|}
+
+@p procedure tfm_two(@!x:integer); {output two bytes to |tfm_file|}
+begin tfm_out(x div 256); tfm_out(x mod 256);
+end;
+@#
+procedure tfm_four(@!x:integer); {output four bytes to |tfm_file|}
+begin if x>=0 then tfm_out(x div three_bytes)
+else begin x:=x+@'10000000000; {use two's complement for negative values}
+ x:=x+@'10000000000;
+ tfm_out((x div three_bytes) + 128);
+ end;
+x:=x mod three_bytes; tfm_out(x div unity);
+x:=x mod unity; tfm_out(x div @'400);
+tfm_out(x mod @'400);
+end;
+@#
+procedure tfm_qqqq(@!x:four_quarters); {output four quarterwords to |tfm_file|}
+begin tfm_out(qo(x.b0)); tfm_out(qo(x.b1)); tfm_out(qo(x.b2));
+tfm_out(qo(x.b3));
+end;
+
+@ @<Finish the \.{TFM} file@>=
+if job_name=0 then open_log_file;
+pack_job_name(".tfm");
+while not b_open_out(tfm_file) do
+ prompt_file_name("file name for font metrics",".tfm");
+metric_file_name:=b_make_name_string(tfm_file);
+@<Output the subfile sizes and header bytes@>;
+@<Output the character information bytes, then
+ output the dimensions themselves@>;
+@<Output the ligature/kern program@>;
+@<Output the extensible character recipes and the font metric parameters@>;
+@!stat if internal[tracing_stats]>0 then
+ @<Log the subfile sizes of the \.{TFM} file@>;@;@+tats@/
+print_nl("Font metrics written on "); slow_print(metric_file_name);
+print_char(".");
+@.Font metrics written...@>
+b_close(tfm_file)
+
+@ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use
+this code.
+
+@<Output the subfile sizes and header bytes@>=
+k:=header_size;
+while header_byte[k]<0 do decr(k);
+lh:=(k+3) div 4; {this is the number of header words}
+if bc>ec then bc:=1; {if there are no characters, |ec=0| and |bc=1|}
+@<Compute the ligature/kern program offset and implant the
+ left boundary label@>;
+tfm_two(6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+lk_offset+nk+ne+np);
+ {this is the total number of file words that will be output}
+tfm_two(lh); tfm_two(bc); tfm_two(ec); tfm_two(nw); tfm_two(nh);
+tfm_two(nd); tfm_two(ni); tfm_two(nl+lk_offset); tfm_two(nk); tfm_two(ne);
+tfm_two(np);
+for k:=1 to 4*lh do
+ begin if header_byte[k]<0 then header_byte[k]:=0;
+ tfm_out(header_byte[k]);
+ end
+
+@ @<Output the character information bytes...@>=
+for k:=bc to ec do
+ if not char_exists[k] then tfm_four(0)
+ else begin tfm_out(info(tfm_width[k])); {the width index}
+ tfm_out((info(tfm_height[k]))*16+info(tfm_depth[k]));
+ tfm_out((info(tfm_ital_corr[k]))*4+char_tag[k]);
+ tfm_out(char_remainder[k]);
+ end;
+tfm_changed:=0;
+for k:=1 to 4 do
+ begin tfm_four(0); p:=dimen_head[k];
+ while p<>inf_val do
+ begin tfm_four(dimen_out(value(p))); p:=link(p);
+ end;
+ end
+
+@ We need to output special instructions at the beginning of the
+|lig_kern| array in order to specify the right boundary character
+and/or to handle starting addresses that exceed 255. The |label_loc|
+and |label_char| arrays have been set up to record all the
+starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots
+\le|label_loc|[|label_ptr]|$.
+
+@<Compute the ligature/kern program offset...@>=
+bchar:=round_unscaled(internal[boundary_char]);
+if(bchar<0)or(bchar>255)then
+ begin bchar:=-1; lk_started:=false; lk_offset:=0;@+end
+else begin lk_started:=true; lk_offset:=1;@+end;
+@<Find the minimum |lk_offset| and adjust all remainders@>;
+if bch_label<undefined_label then
+ begin skip_byte(nl):=qi(255); next_char(nl):=qi(0);
+ op_byte(nl):=qi(((bch_label+lk_offset)div 256));
+ rem_byte(nl):=qi(((bch_label+lk_offset)mod 256));
+ incr(nl); {possibly |nl=lig_table_size+1|}
+ end
+
+@ @<Find the minimum |lk_offset|...@>=
+k:=label_ptr; {pointer to the largest unallocated label}
+if label_loc[k]+lk_offset>255 then
+ begin lk_offset:=0; lk_started:=false; {location 0 can do double duty}
+ repeat char_remainder[label_char[k]]:=lk_offset;
+ while label_loc[k-1]=label_loc[k] do
+ begin decr(k); char_remainder[label_char[k]]:=lk_offset;
+ end;
+ incr(lk_offset); decr(k);
+ until lk_offset+label_loc[k]<256;
+ {N.B.: |lk_offset=256| satisfies this when |k=0|}
+ end;
+if lk_offset>0 then
+ while k>0 do
+ begin char_remainder[label_char[k]]
+ :=char_remainder[label_char[k]]+lk_offset;
+ decr(k);
+ end
+
+@ @<Output the ligature/kern program@>=
+for k:=0 to 255 do if skip_table[k]<undefined_label then
+ begin print_nl("(local label "); print_int(k); print(":: was missing)");
+@.local label l:: was missing@>
+ cancel_skips(skip_table[k]);
+ end;
+if lk_started then {|lk_offset=1| for the special |bchar|}
+ begin tfm_out(255); tfm_out(bchar); tfm_two(0);
+ end
+else for k:=1 to lk_offset do {output the redirection specs}
+ begin ll:=label_loc[label_ptr];
+ if bchar<0 then
+ begin tfm_out(254); tfm_out(0);
+ end
+ else begin tfm_out(255); tfm_out(bchar);
+ end;
+ tfm_two(ll+lk_offset);
+ repeat decr(label_ptr);
+ until label_loc[label_ptr]<ll;
+ end;
+for k:=0 to nl-1 do tfm_qqqq(lig_kern[k]);
+for k:=0 to nk-1 do tfm_four(dimen_out(kern[k]))
+
+@ @<Output the extensible character recipes...@>=
+for k:=0 to ne-1 do tfm_qqqq(exten[k]);
+for k:=1 to np do
+ if k=1 then
+ if abs(param[1])<fraction_half then tfm_four(param[1]*16)
+ else begin incr(tfm_changed);
+ if param[1]>0 then tfm_four(el_gordo)
+ else tfm_four(-el_gordo);
+ end
+ else tfm_four(dimen_out(param[k]));
+if tfm_changed>0 then
+ begin if tfm_changed=1 then print_nl("(a font metric dimension")
+@.a font metric dimension...@>
+ else begin print_nl("("); print_int(tfm_changed);
+@.font metric dimensions...@>
+ print(" font metric dimensions");
+ end;
+ print(" had to be decreased)");
+ end
+
+@ @<Log the subfile sizes of the \.{TFM} file@>=
+begin wlog_ln(' ');
+if bch_label<undefined_label then decr(nl);
+wlog_ln('(You used ',nw:1,'w,',@| nh:1,'h,',@| nd:1,'d,',@| ni:1,'i,',@|
+ nl:1,'l,',@| nk:1,'k,',@| ne:1,'e,',@|
+ np:1,'p metric file positions');
+wlog_ln(' out of ',@| '256w,16h,16d,64i,',@|
+ lig_table_size:1,'l,',max_kerns:1,'k,256e,',@|
+ max_font_dimen:1,'p)');
+end
+
+@* \[46] Generic font file format.
+The most important output produced by a typical run of \MF\ is the
+``generic font'' (\.{GF}) file that specifies the bit patterns of the
+characters that have been drawn. The term {\sl generic\/} indicates that
+this file format doesn't match the conventions of any name-brand manufacturer;
+but it is easy to convert \.{GF} files to the special format required by
+almost all digital phototypesetting equipment. There's a strong analogy
+between the \.{DVI} files written by \TeX\ and the \.{GF} files written
+by \MF; and, in fact, the file formats have a lot in common.
+
+A \.{GF} file is a stream of 8-bit bytes that may be
+regarded as a series of commands in a machine-like language. The first
+byte of each command is the operation code, and this code is followed by
+zero or more bytes that provide parameters to the command. The parameters
+themselves may consist of several consecutive bytes; for example, the
+`|boc|' (beginning of character) command has six parameters, each of
+which is four bytes long. Parameters are usually regarded as nonnegative
+integers; but four-byte-long parameters can be either positive or
+negative, hence they range in value from $-2^{31}$ to $2^{31}-1$.
+As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order,
+and negative numbers appear in two's complement notation.
+
+A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or
+more ``characters,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that introduce the file; this must come
+first. Each ``character'' consists of a |boc| command, followed by any
+number of other commands that specify ``black'' pixels,
+followed by an |eoc| command. The characters appear in the order that \MF\
+generated them. If we ignore no-op commands (which are allowed between any
+two commands in the file), each |eoc| command is immediately followed by a
+|boc| command, or by a |post| command; in the latter case, there are no
+more characters in the file, and the remaining bytes form the postamble.
+Further details about the postamble will be explained later.
+
+Some parameters in \.{GF} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first file byte is number~0, then comes number~1, and so on.
+
+@ The \.{GF} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+relative instead of absolute. When a \.{GF}-reading program reads the
+commands for a character, it keeps track of two quantities: (a)~the current
+column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit
+signed integers, although most actual font formats produced from \.{GF}
+files will need to curtail this vast range because of practical
+limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert
+n\vert$ to get extremely large, but the \.{GF} format tries to be more general.)
+
+How do \.{GF}'s row and column numbers correspond to the conventions
+of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's
+view, is considered to be at the lower left corner of the pixel in row~0
+and column~0. This point is the intersection of the baseline with the left
+edge of the type; it corresponds to location $(0,0)$ in \MF\ programs.
+Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the
+region of the plane whose coordinates both lie between 0 and~1. The
+pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\
+coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of
+|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference
+point; negative values of |n| and~|y| correspond to rows of pixels {\sl
+below\/} the baseline.
+
+Besides |m| and |n|, there's also a third aspect of the current
+state, namely the @!|paint_switch|, which is always either |black| or
+|white|. Each \\{paint} command advances |m| by a specified amount~|d|,
+and blackens the intervening pixels if |paint_switch=black|; then
+the |paint_switch| changes to the opposite state. \.{GF}'s commands are
+designed so that |m| will never decrease within a row, and |n| will never
+increase within a character; hence there is no way to whiten a pixel that
+has been blackened.
+
+@ Here is a list of all the commands that may appear in a \.{GF} file. Each
+command is specified by its symbolic name (e.g., |boc|), its opcode byte
+(e.g., 67), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|d[2]|' means that parameter |d| is two bytes long.
+
+\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does
+nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa.
+
+\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63).
+These are \\{paint} commands with |d=1| to~63, defined as follows: If
+|paint_switch=black|, blacken |d|~pixels of the current row~|n|,
+in columns |m| through |m+d-1| inclusive. Then, in any case,
+complement the |paint_switch| and advance |m| by~|d|.
+
+\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified
+value of~|d|; \MF\ uses it to paint when |64<=d<256|.
+
+\yskip\hang|@!paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high
+as~65535.
+
+\yskip\hang|@!paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high
+as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine
+anybody making practical use of it; surely a more compact encoding will be
+desirable when characters can be this large. But the command is there,
+anyway, just in case.
+
+\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]|
+|max_n[4]|. Beginning of a character: Here |c| is the character code, and
+|p| points to the previous character beginning (if any) for characters having
+this code number modulo 256. (The pointer |p| is |-1| if there was no
+prior character with an equivalent code.) The values of registers |m| and |n|
+defined by the instructions that follow for this character must
+satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and
+|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading
+program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to
+initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and
+|paint_switch:=white|.
+
+\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|.
+Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m|
+and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|.
+The one-byte parameters must be between 0 and 255, inclusive.
+\ (This abbreviated |boc| saves 19~bytes per character, in common cases.)
+
+\yskip\hang|eoc| 69. End of character: All pixels blackened so far
+constitute the pattern for this character. In particular, a completely
+blank character might have |eoc| immediately following |boc|.
+
+\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=white|. \ (This finishes one row and begins another,
+ready to whiten the leftmost pixel in the new row.)
+
+\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set
+|paint_switch:=white|. This is a way to produce |d| all-white rows.
+
+\yskip\hang|@!skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large
+as 65535.
+
+\yskip\hang|@!skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large
+as $2^{24}-1$. \MF\ obviously never needs this command.
+
+\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=black|. \ (This finishes one row and begins another,
+ready to {\sl blacken\/} the leftmost pixel in the new row.)
+
+\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as
+|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively.
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string; this occurs in the \.{GF} file only between
+characters, after the preamble, and before the postamble. However,
+\\{xxx} commands might appear within characters,
+in \.{GF} files generated by other
+processors. It is recommended that |x| be a string having the form of a
+keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+\MF\ uses this when sending a \&{special} string whose length exceeds~255.
+
+\yskip\hang|@!xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be
+ridiculously large; |k| mustn't be negative.
+
+\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general;
+it functions as a 5-byte |no_op| unless special \.{GF}-reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a
+result of \&{numspecial} commands; the intent is to provide numeric
+parameters to \\{xxx} commands that immediately precede.
+
+\yskip\hang|@!no_op| 244. No operation, do nothing. Any number of |no_op|'s
+may occur between \.{GF} commands, but a |no_op| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|.
+This command will appear only in the postamble, which will be explained shortly.
+
+\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|.
+Same as |char_loc|, except that |dy| is assumed to be zero, and the value
+of~|dx| is taken to be |65536*dm|, where |0<=dm<256|.
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameter |i| is an identifying number for \.{GF} format, currently
+131. The other information is merely commentary; it is not given
+special interpretation like \\{xxx} commands are. (Note that \\{xxx}
+commands may immediately follow the preamble, before the first |boc|.)
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@d gf_id_byte=131 {identifies the kind of \.{GF} files described here}
+
+@ \MF\ refers to the following opcodes explicitly.
+
+@d paint_0=0 {beginning of the \\{paint} commands}
+@d paint1=64 {move right a given number of columns, then
+ black${}\swap{}$white}
+@d boc=67 {beginning of a character}
+@d boc1=68 {short form of |boc|}
+@d eoc=69 {end of a character}
+@d skip0=70 {skip no blank rows}
+@d skip1=71 {skip over blank rows}
+@d new_row_0=74 {move down one row and then right}
+@d max_new_row=164 {the largest \\{new\_row} command is |new_row_164|}
+@d xxx1=239 {for \&{special} strings}
+@d xxx3=241 {for long \&{special} strings}
+@d yyy=243 {for \&{numspecial} numbers}
+@d char_loc=245 {character locators in the postamble}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+
+@ The last character in a \.{GF} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \MF\ has
+accumulated. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]|
+ |@!min_m[4]| |@!max_m[4]| |@!min_n[4]| |@!max_n[4]|\cr
+ $\langle\,$character locators$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the byte following the final |eoc| in the file
+(or to the byte following the preamble, if there are no characters);
+it can be used to locate the beginning of \\{xxx} commands
+that might have preceded the postamble. The |ds| and |cs| parameters
+@^design size@> @^check sum@>
+give the design size and check sum, respectively, which are exactly the
+values put into the header of the \.{TFM} file that \MF\ produces (or
+would produce) on this run. Parameters |hppp| and |vppp| are the ratios of
+pixels per point, horizontally and vertically, expressed as |scaled| integers
+(i.e., multiplied by $2^{16}$); they can be used to correlate the font
+with specific device resolutions, magnifications, and ``at sizes.'' Then
+come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that
+registers |m| and~|n| assume in all characters in this \.{GF} file.
+(These bounds need not be the best possible; |max_m| and |min_n| may, on the
+other hand, be tighter than the similar bounds in |boc| commands. For
+example, some character may have |min_n=-100| in its |boc|, but it might
+turn out that |n| never gets lower than |-50| in any character; then
+|min_n| can have any value |<=-50|. If there are no characters in the file,
+it's possible to have |min_m>max_m| and/or |min_n>max_n|.)
+
+@ Character locators are introduced by |char_loc| commands,
+which specify a character residue~|c|, character escapements (|dx,dy|),
+a character width~|w|, and a pointer~|p|
+to the beginning of that character. (If two or more characters have the
+same code~|c| modulo 256, only the last will be indicated; the others can be
+located by following backpointers. Characters whose codes differ by a
+multiple of 256 are assumed to share the same font metric information,
+hence the \.{TFM} file contains only residues of character codes modulo~256.
+This convention is intended for oriental languages, when there are many
+character shapes but few distinct widths.)
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+The character escapements (|dx,dy|) are the values of \MF's \&{chardx}
+and \&{chardy} parameters; they are in units of |scaled| pixels;
+i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in
+vertical pixel units times $2^{16}$. This is the intended amount of
+displacement after typesetting the character; for \.{DVI} files, |dy|
+should be zero, but other document file formats allow nonzero vertical
+escapement.
+
+The character width~|w| duplicates the information in the \.{TFM} file; it
+is a |fix_word| value relative to the design size, and it should be
+independent of magnification.
+
+The backpointer |p| points to the character's |boc|, or to the first of
+a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that
+immediately precede the |boc|, if such commands exist; such ``special''
+commands essentially belong to the characters, while the special commands
+after the final character belong to the postamble (i.e., to the font
+as a whole). This convention about |p| applies also to the backpointers
+in |boc| commands, even though it wasn't explained in the description
+of~|boc|. @^backpointers@>
+
+Pointer |p| might be |-1| if the character exists in the \.{TFM} file
+but not in the \.{GF} file. This unusual situation can arise in \MF\ output
+if the user had |proofing<0| when the character was being shipped out,
+but then made |proofing>=0| in order to get a \.{GF} file.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the character locators, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~131, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{GF} file makes it feasible for
+\.{GF}-reading programs to find the postamble first, on most computers,
+even though \MF\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{GF} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{GF} reader can discover all the information needed for individual characters.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{GF}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{GF} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back. This will
+be adequate for most applications. However, the postamble-first approach
+would facilitate a program that merges two \.{GF} files, replacing data
+from one that is overridden by corresponding data in the other.
+
+@* \[47] Shipping characters out.
+The |ship_out| procedure, to be described below, is given a pointer to
+an edge structure. Its mission is to describe the positive pixels
+in \.{GF} form, outputting a ``character'' to |gf_file|.
+
+Several global variables hold information about the font file as a whole:\
+|gf_min_m|, |gf_max_m|, |gf_min_n|, and |gf_max_n| are the minimum and
+maximum \.{GF} coordinates output so far; |gf_prev_ptr| is the byte number
+following the preamble or the last |eoc| command in the output;
+|total_chars| is the total number of characters (i.e., |boc..eoc| segments)
+shipped out. There's also an array, |char_ptr|, containing the starting
+positions of each character in the file, as required for the postamble. If
+character code~|c| has not yet been output, |char_ptr[c]=-1|.
+
+@<Glob...@>=
+@!gf_min_m,@!gf_max_m,@!gf_min_n,@!gf_max_n:integer; {bounding rectangle}
+@!gf_prev_ptr:integer; {where the present/next character started/starts}
+@!total_chars:integer; {the number of characters output so far}
+@!char_ptr:array[eight_bits] of integer; {where individual characters started}
+@!gf_dx,@!gf_dy:array[eight_bits] of integer; {device escapements}
+
+@ @<Set init...@>=
+gf_prev_ptr:=0; total_chars:=0;
+
+@ The \.{GF} bytes are output to a buffer instead of being sent
+byte-by-byte to |gf_file|, because this tends to save a lot of
+subroutine-call overhead. \MF\ uses the same conventions for |gf_file|
+as \TeX\ uses for its \\{dvi\_file}; hence if system-dependent
+changes are needed, they should probably be the same for both programs.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |gf_buf[0..half_buf-1]| constitute the first half, and those in
+|gf_buf[half_buf..gf_buf_size-1]| constitute the second. The global
+variable |gf_ptr| points to the position that will receive the next
+output byte. When |gf_ptr| reaches |gf_limit|, which is always equal
+to one of the two values |half_buf| or |gf_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |gf_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{GF} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |gf_offset+gf_ptr|.
+
+@<Types...@>=
+@!gf_index=0..gf_buf_size; {an index into the output buffer}
+
+@ Some systems may find it more efficient to make |gf_buf| a |packed|
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+@<Glob...@>=
+@!gf_buf:array[gf_index] of eight_bits; {buffer for \.{GF} output}
+@!half_buf:gf_index; {half of |gf_buf_size|}
+@!gf_limit:gf_index; {end of the current half buffer}
+@!gf_ptr:gf_index; {the next available buffer address}
+@!gf_offset:integer; {|gf_buf_size| times the number of times the
+ output buffer has been fully emptied}
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@<Set init...@>=
+half_buf:=gf_buf_size div 2; gf_limit:=gf_buf_size; gf_ptr:=0;
+gf_offset:=0;
+
+@ The actual output of |gf_buf[a..b]| to |gf_file| is performed by calling
+|write_gf(a,b)|. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_gf(a,b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+
+@<Declare generic font output procedures@>=
+procedure write_gf(@!a,@!b:gf_index);
+var k:gf_index;
+begin for k:=a to b do write(gf_file,gf_buf[k]);
+end;
+
+@ To put a byte in the buffer without paying the cost of invoking a procedure
+each time, we use the macro |gf_out|.
+
+@d gf_out(#)==@+begin gf_buf[gf_ptr]:=#; incr(gf_ptr);
+ if gf_ptr=gf_limit then gf_swap;
+ end
+
+@<Declare generic font output procedures@>=
+procedure gf_swap; {outputs half of the buffer}
+begin if gf_limit=gf_buf_size then
+ begin write_gf(0,half_buf-1); gf_limit:=half_buf;
+ gf_offset:=gf_offset+gf_buf_size; gf_ptr:=0;
+ end
+else begin write_gf(half_buf,gf_buf_size-1); gf_limit:=gf_buf_size;
+ end;
+end;
+
+@ Here is how we clean out the buffer when \MF\ is all through; |gf_ptr|
+will be a multiple of~4.
+
+@<Empty the last bytes out of |gf_buf|@>=
+if gf_limit=half_buf then write_gf(half_buf,gf_buf_size-1);
+if gf_ptr>0 then write_gf(0,gf_ptr-1)
+
+@ The |gf_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@<Declare generic font output procedures@>=
+procedure gf_four(@!x:integer);
+begin if x>=0 then gf_out(x div three_bytes)
+else begin x:=x+@'10000000000;
+ x:=x+@'10000000000;
+ gf_out((x div three_bytes) + 128);
+ end;
+x:=x mod three_bytes; gf_out(x div unity);
+x:=x mod unity; gf_out(x div @'400);
+gf_out(x mod @'400);
+end;
+
+@ Of course, it's even easier to output just two or three bytes.
+
+@<Declare generic font output procedures@>=
+procedure gf_two(@!x:integer);
+begin gf_out(x div @'400); gf_out(x mod @'400);
+end;
+@#
+procedure gf_three(@!x:integer);
+begin gf_out(x div unity); gf_out((x mod unity) div @'400);
+gf_out(x mod @'400);
+end;
+
+@ We need a simple routine to generate a \\{paint}
+command of the appropriate type.
+
+@<Declare generic font output procedures@>=
+procedure gf_paint(@!d:integer); {here |0<=d<65536|}
+begin if d<64 then gf_out(paint_0+d)
+else if d<256 then
+ begin gf_out(paint1); gf_out(d);
+ end
+else begin gf_out(paint1+1); gf_two(d);
+ end;
+end;
+
+@ And |gf_string| outputs one or two strings. If the first string number
+is nonzero, an \\{xxx} command is generated.
+
+@<Declare generic font output procedures@>=
+procedure gf_string(@!s,@!t:str_number);
+var @!k:pool_pointer;
+@!l:integer; {length of the strings to output}
+begin if s<>0 then
+ begin l:=length(s);
+ if t<>0 then l:=l+length(t);
+ if l<=255 then
+ begin gf_out(xxx1); gf_out(l);
+ end
+ else begin gf_out(xxx3); gf_three(l);
+ end;
+ for k:=str_start[s] to str_start[s+1]-1 do gf_out(so(str_pool[k]));
+ end;
+if t<>0 then for k:=str_start[t] to str_start[t+1]-1 do gf_out(so(str_pool[k]));
+end;
+
+@ The choice between |boc| commands is handled by |gf_boc|.
+
+@d one_byte(#)== #>=0 then if #<256
+
+@<Declare generic font output procedures@>=
+procedure gf_boc(@!min_m,@!max_m,@!min_n,@!max_n:integer);
+label exit;
+begin if min_m<gf_min_m then gf_min_m:=min_m;
+if max_n>gf_max_n then gf_max_n:=max_n;
+if boc_p=-1 then if one_byte(boc_c) then
+ if one_byte(max_m-min_m) then if one_byte(max_m) then
+ if one_byte(max_n-min_n) then if one_byte(max_n) then
+ begin gf_out(boc1); gf_out(boc_c);@/
+ gf_out(max_m-min_m); gf_out(max_m);
+ gf_out(max_n-min_n); gf_out(max_n); return;
+ end;
+gf_out(boc); gf_four(boc_c); gf_four(boc_p);@/
+gf_four(min_m); gf_four(max_m); gf_four(min_n); gf_four(max_n);
+exit: end;
+
+@ Two of the parameters to |gf_boc| are global.
+
+@<Glob...@>=
+@!boc_c,@!boc_p:integer; {parameters of the next |boc| command}
+
+@ Here is a routine that gets a \.{GF} file off to a good start.
+
+@d check_gf==@t@>@+if output_file_name=0 then init_gf
+
+@<Declare generic font output procedures@>=
+procedure init_gf;
+var @!k:eight_bits; {runs through all possible character codes}
+@!t:integer; {the time of this run}
+begin gf_min_m:=4096; gf_max_m:=-4096; gf_min_n:=4096; gf_max_n:=-4096;
+for k:=0 to 255 do char_ptr[k]:=-1;
+@<Determine the file extension, |gf_ext|@>;
+set_output_file_name;
+gf_out(pre); gf_out(gf_id_byte); {begin to output the preamble}
+old_setting:=selector; selector:=new_string; print(" METAFONT output ");
+print_int(round_unscaled(internal[year])); print_char(".");
+print_dd(round_unscaled(internal[month])); print_char(".");
+print_dd(round_unscaled(internal[day])); print_char(":");@/
+t:=round_unscaled(internal[time]);
+print_dd(t div 60); print_dd(t mod 60);@/
+selector:=old_setting; gf_out(cur_length);
+gf_string(0,make_string); decr(str_ptr);
+pool_ptr:=str_start[str_ptr]; {flush that string from memory}
+gf_prev_ptr:=gf_offset+gf_ptr;
+end;
+
+@ @<Determine the file extension...@>=
+if internal[hppp]<=0 then gf_ext:=".gf"
+else begin old_setting:=selector; selector:=new_string; print_char(".");
+ print_int(make_scaled(internal[hppp],59429463));
+ {$2^{32}/72.27\approx59429463.07$}
+ print("gf"); gf_ext:=make_string; selector:=old_setting;
+ end
+
+@ With those preliminaries out of the way, |ship_out| is not especially
+difficult.
+
+@<Declare generic font output procedures@>=
+procedure ship_out(@!c:eight_bits);
+label done;
+var @!f:integer; {current character extension}
+@!prev_m,@!m,@!mm:integer; {previous and current pixel column numbers}
+@!prev_n,@!n:integer; {previous and current pixel row numbers}
+@!p,@!q:pointer; {for list traversal}
+@!prev_w,@!w,@!ww:integer; {old and new weights}
+@!d:integer; {data from edge-weight node}
+@!delta:integer; {number of rows to skip}
+@!cur_min_m:integer; {starting column, relative to the current offset}
+@!x_off,@!y_off:integer; {offsets, rounded to integers}
+begin check_gf; f:=round_unscaled(internal[char_ext]);@/
+x_off:=round_unscaled(internal[x_offset]);
+y_off:=round_unscaled(internal[y_offset]);
+if term_offset>max_print_line-9 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+print_char("["); print_int(c);
+if f<>0 then
+ begin print_char("."); print_int(f);
+ end;
+update_terminal;
+boc_c:=256*f+c; boc_p:=char_ptr[c]; char_ptr[c]:=gf_prev_ptr;@/
+if internal[proofing]>0 then @<Send nonzero offsets to the output file@>;
+@<Output the character represented in |cur_edges|@>;
+gf_out(eoc); gf_prev_ptr:=gf_offset+gf_ptr; incr(total_chars);
+print_char("]"); update_terminal; {progress report}
+if internal[tracing_output]>0 then
+ print_edges(" (just shipped out)",true,x_off,y_off);
+end;
+
+@ @<Send nonzero offsets to the output file@>=
+begin if x_off<>0 then
+ begin gf_string("xoffset",0); gf_out(yyy); gf_four(x_off*unity);
+ end;
+if y_off<>0 then
+ begin gf_string("yoffset",0); gf_out(yyy); gf_four(y_off*unity);
+ end;
+end
+
+@ @<Output the character represented in |cur_edges|@>=
+prev_n:=4096; p:=knil(cur_edges); n:=n_max(cur_edges)-zero_field;
+while p<>cur_edges do
+ begin @<Output the pixels of edge row |p| to font row |n|@>;
+ p:=knil(p); decr(n);
+ end;
+if prev_n=4096 then @<Finish off an entirely blank character@>
+else if prev_n+y_off<gf_min_n then
+ gf_min_n:=prev_n+y_off
+
+@ @<Finish off an entirely blank...@>=
+begin gf_boc(0,0,0,0);
+if gf_max_m<0 then gf_max_m:=0;
+if gf_min_n>0 then gf_min_n:=0;
+end
+
+@ In this loop, |prev_w| represents the weight at column |prev_m|, which is
+the most recent column reflected in the output so far; |w| represents the
+weight at column~|m|, which is the most recent column in the edge data.
+Several edges might cancel at the same column position, so we need to
+look ahead to column~|mm| before actually outputting anything.
+
+@<Output the pixels of edge row |p| to font row |n|@>=
+if unsorted(p)>void then sort_edges(p);
+q:=sorted(p); w:=0; prev_m:=-fraction_one; {$|fraction_one|\approx\infty$}
+ww:=0; prev_w:=0; m:=prev_m;
+repeat if q=sentinel then mm:=fraction_one
+else begin d:=ho(info(q)); mm:=d div 8; ww:=ww+(d mod 8)-zero_w;
+ end;
+if mm<>m then
+ begin if prev_w<=0 then
+ begin if w>0 then @<Start black at $(m,n)$@>;
+ end
+ else if w<=0 then @<Stop black at $(m,n)$@>;
+ m:=mm;
+ end;
+w:=ww; q:=link(q);
+until mm=fraction_one;
+if w<>0 then {this should be impossible}
+ print_nl("(There's unbounded black in character shipped out!)");
+@.There's unbounded black...@>
+if prev_m-m_offset(cur_edges)+x_off>gf_max_m then
+ gf_max_m:=prev_m-m_offset(cur_edges)+x_off
+
+
+@ @<Start black at $(m,n)$@>=
+begin if prev_m=-fraction_one then @<Start a new row at $(m,n)$@>
+else gf_paint(m-prev_m);
+prev_m:=m; prev_w:=w;
+end
+
+@ @<Stop black at $(m,n)$@>=
+begin gf_paint(m-prev_m); prev_m:=m; prev_w:=w;
+end
+
+@ @<Start a new row at $(m,n)$@>=
+begin if prev_n=4096 then
+ begin gf_boc(m_min(cur_edges)+x_off-zero_field,
+ m_max(cur_edges)+x_off-zero_field,@|
+ n_min(cur_edges)+y_off-zero_field,n+y_off);
+ cur_min_m:=m_min(cur_edges)-zero_field+m_offset(cur_edges);
+ end
+else if prev_n>n+1 then @<Skip down |prev_n-n| rows@>
+else @<Skip to column $m$ in the next row and |goto done|, or skip zero rows@>;
+gf_paint(m-cur_min_m); {skip to column $m$, painting white}
+done:prev_n:=n;
+end
+
+@ @<Skip to column $m$ in the next row...@>=
+begin delta:=m-cur_min_m;
+if delta>max_new_row then gf_out(skip0)
+else begin gf_out(new_row_0+delta); goto done;
+ end;
+end
+
+@ @<Skip down...@>=
+begin delta:=prev_n-n-1;
+if delta<@'400 then
+ begin gf_out(skip1); gf_out(delta);
+ end
+else begin gf_out(skip1+1); gf_two(delta);
+ end;
+end
+
+@ Now that we've finished |ship_out|, let's look at the other commands
+by which a user can send things to the \.{GF} file.
+
+@<Cases of |do_statement|...@>=
+special_command: do_special;
+
+@ @<Put each...@>=
+primitive("special",special_command,string_type);@/
+@!@:special_}{\&{special} primitive@>
+primitive("numspecial",special_command,known);@/
+@!@:num_special_}{\&{numspecial} primitive@>
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_special;
+var @!m:small_number; {either |string_type| or |known|}
+begin m:=cur_mod; get_x_next; scan_expression;
+if internal[proofing]>=0 then
+ if cur_type<>m then @<Complain about improper special operation@>
+ else begin check_gf;
+ if m=string_type then gf_string(cur_exp,0)
+ else begin gf_out(yyy); gf_four(cur_exp);
+ end;
+ end;
+flush_cur_exp(0);
+end;
+
+@ @<Complain about improper special operation@>=
+begin exp_err("Unsuitable expression");
+@.Unsuitable expression@>
+help1("The expression shown above has the wrong type to be output.");
+put_get_error;
+end
+
+@ @<Send the current expression as a title to the output file@>=
+begin check_gf; gf_string("title ",cur_exp);
+@.title@>
+end
+
+@ @<Cases of |print_cmd...@>=
+special_command:if m=known then print("numspecial")
+ else print("special");
+
+@ @<Determine if a character has been shipped out@>=
+begin cur_exp:=round_unscaled(cur_exp) mod 256;
+if cur_exp<0 then cur_exp:=cur_exp+256;
+boolean_reset(char_exists[cur_exp]); cur_type:=boolean_type;
+end
+
+@ At the end of the program we must finish things off by writing the postamble.
+The \.{TFM} information should have been computed first.
+
+An integer variable |k| and a |scaled| variable |x| will be declared for
+use by this routine.
+
+@<Finish the \.{GF} file@>=
+begin gf_out(post); {beginning of the postamble}
+gf_four(gf_prev_ptr); gf_prev_ptr:=gf_offset+gf_ptr-5; {|post| location}
+gf_four(internal[design_size]*16);
+for k:=1 to 4 do gf_out(header_byte[k]); {the check sum}
+gf_four(internal[hppp]);
+gf_four(internal[vppp]);@/
+gf_four(gf_min_m); gf_four(gf_max_m);
+gf_four(gf_min_n); gf_four(gf_max_n);
+for k:=0 to 255 do if char_exists[k] then
+ begin x:=gf_dx[k] div unity;
+ if (gf_dy[k]=0)and(x>=0)and(x<256)and(gf_dx[k]=x*unity) then
+ begin gf_out(char_loc+1); gf_out(k); gf_out(x);
+ end
+ else begin gf_out(char_loc); gf_out(k);
+ gf_four(gf_dx[k]); gf_four(gf_dy[k]);
+ end;
+ x:=value(tfm_width[k]);
+ if abs(x)>max_tfm_dimen then
+ if x>0 then x:=three_bytes-1@+else x:=1-three_bytes
+ else x:=make_scaled(x*16,internal[design_size]);
+ gf_four(x); gf_four(char_ptr[k]);
+ end;
+gf_out(post_post); gf_four(gf_prev_ptr); gf_out(gf_id_byte);@/
+k:=4+((gf_buf_size-gf_ptr) mod 4); {the number of 223's}
+while k>0 do
+ begin gf_out(223); decr(k);
+ end;
+@<Empty the last bytes out of |gf_buf|@>;
+print_nl("Output written on "); slow_print(output_file_name);
+@.Output written...@>
+print(" ("); print_int(total_chars); print(" character");
+if total_chars<>1 then print_char("s");
+print(", "); print_int(gf_offset+gf_ptr); print(" bytes).");
+b_close(gf_file);
+end
+
+@* \[48] Dumping and undumping the tables.
+After \.{INIMF} has seen a collection of macros, it
+can write all the necessary information on an auxiliary file so
+that production versions of \MF\ are able to initialize their
+memory at high speed. The present section of the program takes
+care of such output and input. We shall consider simultaneously
+the processes of storing and restoring,
+so that the inverse relation between them is clear.
+@.INIMF@>
+
+The global variable |base_ident| is a string that is printed right
+after the |banner| line when \MF\ is ready to start. For \.{INIMF} this
+string says simply `\.{(INIMF)}'; for other versions of \MF\ it says,
+for example, `\.{(preloaded base=plain 1984.2.29)}', showing the year,
+month, and day that the base file was created. We have |base_ident=0|
+before \MF's tables are loaded.
+
+@<Glob...@>=
+@!base_ident:str_number;
+
+@ @<Set init...@>=
+base_ident:=0;
+
+@ @<Initialize table entries...@>=
+base_ident:=" (INIMF)";
+
+@ @<Declare act...@>=
+@!init procedure store_base_file;
+var @!k:integer; {all-purpose index}
+@!p,@!q: pointer; {all-purpose pointers}
+@!x: integer; {something to dump}
+@!w: four_quarters; {four ASCII codes}
+begin @<Create the |base_ident|, open the base file,
+ and inform the user that dumping has begun@>;
+@<Dump constants for consistency check@>;
+@<Dump the string pool@>;
+@<Dump the dynamic memory@>;
+@<Dump the table of equivalents and the hash table@>;
+@<Dump a few more things and the closing check word@>;
+@<Close the base file@>;
+end;
+tini
+
+@ Corresponding to the procedure that dumps a base file, we also have a function
+that reads~one~in. The function returns |false| if the dumped base is
+incompatible with the present \MF\ table sizes, etc.
+
+@d off_base=6666 {go here if the base file is unacceptable}
+@d too_small(#)==begin wake_up_terminal;
+ wterm_ln('---! Must increase the ',#);
+@.Must increase the x@>
+ goto off_base;
+ end
+
+@p @t\4@>@<Declare the function called |open_base_file|@>@;
+function load_base_file:boolean;
+label off_base,exit;
+var @!k:integer; {all-purpose index}
+@!p,@!q: pointer; {all-purpose pointers}
+@!x: integer; {something undumped}
+@!w: four_quarters; {four ASCII codes}
+begin @<Undump constants for consistency check@>;
+@<Undump the string pool@>;
+@<Undump the dynamic memory@>;
+@<Undump the table of equivalents and the hash table@>;
+@<Undump a few more things and the closing check word@>;
+load_base_file:=true; return; {it worked!}
+off_base: wake_up_terminal;
+ wterm_ln('(Fatal base file error; I''m stymied)');
+@.Fatal base file error@>
+load_base_file:=false;
+exit:end;
+
+@ Base files consist of |memory_word| items, and we use the following
+macros to dump words of different types:
+
+@d dump_wd(#)==begin base_file^:=#; put(base_file);@+end
+@d dump_int(#)==begin base_file^.int:=#; put(base_file);@+end
+@d dump_hh(#)==begin base_file^.hh:=#; put(base_file);@+end
+@d dump_qqqq(#)==begin base_file^.qqqq:=#; put(base_file);@+end
+
+@<Glob...@>=
+@!base_file:word_file; {for input or output of base information}
+
+@ The inverse macros are slightly more complicated, since we need to check
+the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
+read an integer value |x| that is supposed to be in the range |a<=x<=b|.
+
+@d undump_wd(#)==begin get(base_file); #:=base_file^;@+end
+@d undump_int(#)==begin get(base_file); #:=base_file^.int;@+end
+@d undump_hh(#)==begin get(base_file); #:=base_file^.hh;@+end
+@d undump_qqqq(#)==begin get(base_file); #:=base_file^.qqqq;@+end
+@d undump_end_end(#)==#:=x;@+end
+@d undump_end(#)==(x>#) then goto off_base@+else undump_end_end
+@d undump(#)==begin undump_int(x); if (x<#) or undump_end
+@d undump_size_end_end(#)==too_small(#)@+else undump_end_end
+@d undump_size_end(#)==if x># then undump_size_end_end
+@d undump_size(#)==begin undump_int(x);
+ if x<# then goto off_base; undump_size_end
+
+@ The next few sections of the program should make it clear how we use the
+dump/undump macros.
+
+@<Dump constants for consistency check@>=
+dump_int(@$);@/
+dump_int(mem_min);@/
+dump_int(mem_top);@/
+dump_int(hash_size);@/
+dump_int(hash_prime);@/
+dump_int(max_in_open)
+
+@ Sections of a \.{WEB} program that are ``commented out'' still contribute
+strings to the string pool; therefore \.{INIMF} and \MF\ will have
+the same strings. (And it is, of course, a good thing that they do.)
+@.WEB@>
+@^string pool@>
+
+@<Undump constants for consistency check@>=
+x:=base_file^.int;
+if x<>@$ then goto off_base; {check that strings are the same}
+undump_int(x);
+if x<>mem_min then goto off_base;
+undump_int(x);
+if x<>mem_top then goto off_base;
+undump_int(x);
+if x<>hash_size then goto off_base;
+undump_int(x);
+if x<>hash_prime then goto off_base;
+undump_int(x);
+if x<>max_in_open then goto off_base
+
+@ @d dump_four_ASCII==
+ w.b0:=qi(so(str_pool[k])); w.b1:=qi(so(str_pool[k+1]));
+ w.b2:=qi(so(str_pool[k+2])); w.b3:=qi(so(str_pool[k+3]));
+ dump_qqqq(w)
+
+@<Dump the string pool@>=
+dump_int(pool_ptr);
+dump_int(str_ptr);
+for k:=0 to str_ptr do dump_int(str_start[k]);
+k:=0;
+while k+4<pool_ptr do
+ begin dump_four_ASCII; k:=k+4;
+ end;
+k:=pool_ptr-4; dump_four_ASCII;
+print_ln; print_int(str_ptr); print(" strings of total length ");
+print_int(pool_ptr)
+
+@ @d undump_four_ASCII==
+ undump_qqqq(w);
+ str_pool[k]:=si(qo(w.b0)); str_pool[k+1]:=si(qo(w.b1));
+ str_pool[k+2]:=si(qo(w.b2)); str_pool[k+3]:=si(qo(w.b3))
+
+@<Undump the string pool@>=
+undump_size(0)(pool_size)('string pool size')(pool_ptr);
+undump_size(0)(max_strings)('max strings')(str_ptr);
+for k:=0 to str_ptr do
+ begin undump(0)(pool_ptr)(str_start[k]); str_ref[k]:=max_str_ref;
+ end;
+k:=0;
+while k+4<pool_ptr do
+ begin undump_four_ASCII; k:=k+4;
+ end;
+k:=pool_ptr-4; undump_four_ASCII;
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr
+
+@ By sorting the list of available spaces in the variable-size portion of
+|mem|, we are usually able to get by without having to dump very much
+of the dynamic memory.
+
+We recompute |var_used| and |dyn_used|, so that \.{INIMF} dumps valid
+information even when it has not been gathering statistics.
+
+@<Dump the dynamic memory@>=
+sort_avail; var_used:=0;
+dump_int(lo_mem_max); dump_int(rover);
+p:=mem_min; q:=rover; x:=0;
+repeat for k:=p to q+1 do dump_wd(mem[k]);
+x:=x+q+2-p; var_used:=var_used+q-p;
+p:=q+node_size(q); q:=rlink(q);
+until q=rover;
+var_used:=var_used+lo_mem_max-p; dyn_used:=mem_end+1-hi_mem_min;@/
+for k:=p to lo_mem_max do dump_wd(mem[k]);
+x:=x+lo_mem_max+1-p;
+dump_int(hi_mem_min); dump_int(avail);
+for k:=hi_mem_min to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-hi_mem_min;
+p:=avail;
+while p<>null do
+ begin decr(dyn_used); p:=link(p);
+ end;
+dump_int(var_used); dump_int(dyn_used);
+print_ln; print_int(x);
+print(" memory locations dumped; current usage is ");
+print_int(var_used); print_char("&"); print_int(dyn_used)
+
+@ @<Undump the dynamic memory@>=
+undump(lo_mem_stat_max+1000)(hi_mem_stat_min-1)(lo_mem_max);
+undump(lo_mem_stat_max+1)(lo_mem_max)(rover);
+p:=mem_min; q:=rover;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>lo_mem_max)or((q>=rlink(q))and(rlink(q)<>rover)) then goto off_base;
+q:=rlink(q);
+until q=rover;
+for k:=p to lo_mem_max do undump_wd(mem[k]);
+undump(lo_mem_max+1)(hi_mem_stat_min)(hi_mem_min);
+undump(null)(mem_top)(avail); mem_end:=mem_top;
+for k:=hi_mem_min to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used)
+
+@ A different scheme is used to compress the hash table, since its lower region
+is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output three
+words: |p|, |hash[p]|, and |eqtb[p]|. The hash table is, of course, densely
+packed for |p>=hash_used|, so the remaining entries are output in~a~block.
+
+@<Dump the table of equivalents and the hash table@>=
+dump_int(hash_used); st_count:=frozen_inaccessible-1-hash_used;
+for p:=1 to hash_used do if text(p)<>0 then
+ begin dump_int(p); dump_hh(hash[p]); dump_hh(eqtb[p]); incr(st_count);
+ end;
+for p:=hash_used+1 to hash_end do
+ begin dump_hh(hash[p]); dump_hh(eqtb[p]);
+ end;
+dump_int(st_count);@/
+print_ln; print_int(st_count); print(" symbolic tokens")
+
+@ @<Undump the table of equivalents and the hash table@>=
+undump(1)(frozen_inaccessible)(hash_used); p:=0;
+repeat undump(p+1)(hash_used)(p); undump_hh(hash[p]); undump_hh(eqtb[p]);
+until p=hash_used;
+for p:=hash_used+1 to hash_end do
+ begin undump_hh(hash[p]); undump_hh(eqtb[p]);
+ end;
+undump_int(st_count)
+
+@ We have already printed a lot of statistics, so we set |tracing_stats:=0|
+to prevent them from appearing again.
+
+@<Dump a few more things and the closing check word@>=
+dump_int(int_ptr);
+for k:=1 to int_ptr do
+ begin dump_int(internal[k]); dump_int(int_name[k]);
+ end;
+dump_int(start_sym); dump_int(interaction); dump_int(base_ident);
+dump_int(bg_loc); dump_int(eg_loc); dump_int(serial_no); dump_int(69069);
+internal[tracing_stats]:=0
+
+@ @<Undump a few more things and the closing check word@>=
+undump(max_given_internal)(max_internal)(int_ptr);
+for k:=1 to int_ptr do
+ begin undump_int(internal[k]);
+ undump(0)(str_ptr)(int_name[k]);
+ end;
+undump(0)(frozen_inaccessible)(start_sym);
+undump(batch_mode)(error_stop_mode)(interaction);
+undump(0)(str_ptr)(base_ident);
+undump(1)(hash_end)(bg_loc);
+undump(1)(hash_end)(eg_loc);
+undump_int(serial_no);@/
+undump_int(x);@+if (x<>69069)or eof(base_file) then goto off_base
+
+@ @<Create the |base_ident|...@>=
+selector:=new_string;
+print(" (preloaded base="); print(job_name); print_char(" ");
+print_int(round_unscaled(internal[year])); print_char(".");
+print_int(round_unscaled(internal[month])); print_char(".");
+print_int(round_unscaled(internal[day])); print_char(")");
+if interaction=batch_mode then selector:=log_only
+else selector:=term_and_log;
+str_room(1); base_ident:=make_string; str_ref[base_ident]:=max_str_ref;@/
+pack_job_name(base_extension);
+while not w_open_out(base_file) do
+ prompt_file_name("base file name",base_extension);
+print_nl("Beginning to dump on file ");
+@.Beginning to dump...@>
+slow_print(w_make_name_string(base_file)); flush_string(str_ptr-1);
+print_nl(""); slow_print(base_ident)
+
+@ @<Close the base file@>=
+w_close(base_file)
+
+@* \[49] The main program.
+This is it: the part of \MF\ that executes all those procedures we have
+written.
+
+Well---almost. We haven't put the parsing subroutines into the
+program yet; and we'd better leave space for a few more routines that may
+have been forgotten.
+
+@p @<Declare the basic parsing subroutines@>@;
+@<Declare miscellaneous procedures that were declared |forward|@>@;
+@<Last-minute procedures@>
+
+@ We've noted that there are two versions of \MF84. One, called \.{INIMF},
+@.INIMF@>
+has to be run first; it initializes everything from scratch, without
+reading a base file, and it has the capability of dumping a base file.
+The other one is called `\.{VIRMF}'; it is a ``virgin'' program that needs
+@.VIRMF@>
+to input a base file in order to get started. \.{VIRMF} typically has
+a bit more memory capacity than \.{INIMF}, because it does not need the
+space consumed by the dumping/undumping routines and the numerous calls on
+|primitive|, etc.
+
+The \.{VIRMF} program cannot read a base file instantaneously, of course;
+the best implementations therefore allow for production versions of \MF\ that
+not only avoid the loading routine for \PASCAL\ object code, they also have
+a base file pre-loaded. This is impossible to do if we stick to standard
+\PASCAL; but there is a simple way to fool many systems into avoiding the
+initialization, as follows:\quad(1)~We declare a global integer variable
+called |ready_already|. The probability is negligible that this
+variable holds any particular value like 314159 when \.{VIRMF} is first
+loaded.\quad(2)~After we have read in a base file and initialized
+everything, we set |ready_already:=314159|.\quad(3)~Soon \.{VIRMF}
+will print `\.*', waiting for more input; and at this point we
+interrupt the program and save its core image in some form that the
+operating system can reload speedily.\quad(4)~When that core image is
+activated, the program starts again at the beginning; but now
+|ready_already=314159| and all the other global variables have
+their initial values too. The former chastity has vanished!
+
+In other words, if we allow ourselves to test the condition
+|ready_already=314159|, before |ready_already| has been
+assigned a value, we can avoid the lengthy initialization. Dirty tricks
+rarely pay off so handsomely.
+@^dirty \PASCAL@>
+@^system dependencies@>
+
+On systems that allow such preloading, the standard program called \.{MF}
+should be the one that has \.{plain} base preloaded, since that agrees
+with {\sl The {\logos METAFONT\/}book}. Other versions, e.g., \.{CMMF},
+should also be provided for commonly used bases such as \.{cmbase}.
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+@.cmbase@>
+@.plain@>
+
+@<Glob...@>=
+@!ready_already:integer; {a sacrifice of purity for economy}
+
+@ Now this is really it: \MF\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@^system dependencies@>
+
+@p begin @!{|start_here|}
+history:=fatal_error_stop; {in case we quit during initialization}
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_MF;
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin wterm_ln('Ouch---my internal constants have been clobbered!',
+ '---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+initialize; {set global variables to their starting values}
+@!init if not get_strings_started then goto final_end;
+init_tab; {initialize the tables}
+init_prim; {call |primitive| for each primitive}
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr; fix_date_and_time;
+tini@/
+ready_already:=314159;
+start_of_MF: @<Initialize the output routines@>;
+@<Get the first line of input and prepare to start@>;
+history:=spotless; {ready to go!}
+if start_sym>0 then {insert the `\&{everyjob}' symbol}
+ begin cur_sym:=start_sym; back_input;
+ end;
+main_control; {come to life}
+final_cleanup; {prepare for death}
+end_of_MF: close_files_and_terminate;
+final_end: ready_already:=0;
+end.
+
+@ Here we do whatever is needed to complete \MF's job gracefully on the
+local operating system. The code here might come into play after a fatal
+error; it must therefore consist entirely of ``safe'' operations that
+cannot produce error messages. For example, it would be a mistake to call
+|str_room| or |make_string| at this time, because a call on |overflow|
+might lead to an infinite loop.
+@^system dependencies@>
+
+This program doesn't bother to close the input files that may still be open.
+
+@<Last-minute...@>=
+procedure close_files_and_terminate;
+var @!k:integer; {all-purpose index}
+@!lh:integer; {the length of the \.{TFM} header, in words}
+@!lk_offset:0..256; {extra words inserted at beginning of |lig_kern| array}
+@!p:pointer; {runs through a list of \.{TFM} dimensions}
+@!x:scaled; {a |tfm_width| value being output to the \.{GF} file}
+begin
+@!stat if internal[tracing_stats]>0 then
+ @<Output statistics about this job@>;@;@+tats@/
+wake_up_terminal; @<Finish the \.{TFM} and \.{GF} files@>;
+if log_opened then
+ begin wlog_cr;
+ a_close(log_file); selector:=selector-2;
+ if selector=term_only then
+ begin print_nl("Transcript written on ");
+@.Transcript written...@>
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+end;
+
+@ We want to finish the \.{GF} file if and only if it has already been started;
+this will be true if and only if |gf_prev_ptr| is positive.
+We want to produce a \.{TFM} file if and only if |fontmaking| is positive.
+The \.{TFM} widths must be computed if there's a \.{GF} file, even if
+there's going to be no \.{TFM}~file.
+
+We reclaim all of the variable-size memory at this point, so that
+there is no chance of another memory overflow after the memory capacity
+has already been exceeded.
+
+@<Finish the \.{TFM} and \.{GF} files@>=
+if (gf_prev_ptr>0)or(internal[fontmaking]>0) then
+ begin @<Make the dynamic memory into one big available node@>;
+ @<Massage the \.{TFM} widths@>;
+ fix_design_size; fix_check_sum;
+ if internal[fontmaking]>0 then
+ begin @<Massage the \.{TFM} heights, depths, and italic corrections@>;
+ internal[fontmaking]:=0; {avoid loop in case of fatal error}
+ @<Finish the \.{TFM} file@>;
+ end;
+ if gf_prev_ptr>0 then @<Finish the \.{GF} file@>;
+ end
+
+@ @<Make the dynamic memory into one big available node@>=
+rover:=lo_mem_stat_max+1; link(rover):=empty_flag; lo_mem_max:=hi_mem_min-1;
+if lo_mem_max-rover>max_halfword then lo_mem_max:=max_halfword+rover;
+node_size(rover):=lo_mem_max-rover; llink(rover):=rover; rlink(rover):=rover;
+link(lo_mem_max):=null; info(lo_mem_max):=null
+
+@ The present section goes directly to the log file instead of using
+|print| commands, because there's no need for these strings to take
+up |str_pool| memory when a non-{\bf stat} version of \MF\ is being used.
+
+@<Output statistics...@>=
+if log_opened then
+ begin wlog_ln(' ');
+ wlog_ln('Here is how much of METAFONT''s memory',' you used:');
+@.Here is how much...@>
+ wlog(' ',max_str_ptr-init_str_ptr:1,' string');
+ if max_str_ptr<>init_str_ptr+1 then wlog('s');
+ wlog_ln(' out of ', max_strings-init_str_ptr:1);@/
+ wlog_ln(' ',max_pool_ptr-init_pool_ptr:1,' string characters out of ',
+ pool_size-init_pool_ptr:1);@/
+ wlog_ln(' ',lo_mem_max-mem_min+mem_end-hi_mem_min+2:1,@|
+ ' words of memory out of ',mem_end+1-mem_min:1);@/
+ wlog_ln(' ',st_count:1,' symbolic tokens out of ',
+ hash_size:1);@/
+ wlog_ln(' ',max_in_stack:1,'i,',@|
+ int_ptr:1,'n,',@|
+ max_rounding_ptr:1,'r,',@|
+ max_param_stack:1,'p,',@|
+ max_buf_stack+1:1,'b stack positions out of ',@|
+ stack_size:1,'i,',
+ max_internal:1,'n,',
+ max_wiggle:1,'r,',
+ param_size:1,'p,',
+ buf_size:1,'b');
+ end
+
+@ We get to the |final_cleanup| routine when \&{end} or \&{dump} has
+been scanned.
+
+@<Last-minute...@>=
+procedure final_cleanup;
+label exit;
+var c:small_number; {0 for \&{end}, 1 for \&{dump}}
+begin c:=cur_mod;
+if job_name=0 then open_log_file;
+while input_ptr>0 do
+ if token_state then end_token_list@+else end_file_reading;
+while loop_ptr<>null do stop_iteration;
+while open_parens>0 do
+ begin print(" )"); decr(open_parens);
+ end;
+while cond_ptr<>null do
+ begin print_nl("(end occurred when ");@/
+@.end occurred...@>
+ print_cmd_mod(fi_or_else,cur_if);
+ {`\.{if}' or `\.{elseif}' or `\.{else}'}
+ if if_line<>0 then
+ begin print(" on line "); print_int(if_line);
+ end;
+ print(" was incomplete)");
+ if_line:=if_line_field(cond_ptr);
+ cur_if:=name_type(cond_ptr); loop_ptr:=cond_ptr;
+ cond_ptr:=link(cond_ptr); free_node(loop_ptr,if_node_size);
+ end;
+if history<>spotless then
+ if ((history=warning_issued)or(interaction<error_stop_mode)) then
+ if selector=term_and_log then
+ begin selector:=term_only;
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+ selector:=term_and_log;
+ end;
+if c=1 then
+ begin @!init store_base_file; return;@+tini@/
+ print_nl("(dump is performed only by INIMF)"); return;
+@.dump...only by INIMF@>
+ end;
+exit:end;
+
+@ @<Last-minute...@>=
+@!init procedure init_prim; {initialize all the primitives}
+begin
+@<Put each...@>;
+end;
+@#
+procedure init_tab; {initialize other tables}
+var @!k:integer; {all-purpose index}
+begin @<Initialize table entries (done by \.{INIMF} only)@>@;
+end;
+tini
+
+@ When we begin the following code, \MF's tables may still contain garbage;
+the strings might not even be present. Thus we must proceed cautiously to get
+bootstrapped in.
+
+But when we finish this part of the program, \MF\ is ready to call on the
+|main_control| routine to do its work.
+
+@<Get the first line...@>=
+begin @<Initialize the input routines@>;
+if (base_ident=0)or(buffer[loc]="&") then
+ begin if base_ident<>0 then initialize; {erase preloaded base}
+ if not open_base_file then goto final_end;
+ if not load_base_file then
+ begin w_close(base_file); goto final_end;
+ end;
+ w_close(base_file);
+ while (loc<limit)and(buffer[loc]=" ") do incr(loc);
+ end;
+buffer[limit]:="%";@/
+fix_date_and_time; init_randoms((internal[time] div unity)+internal[day]);@/
+@<Initialize the print |selector|...@>;
+if loc<limit then if buffer[loc]<>"\" then start_input; {\&{input} assumed}
+end
+
+@* \[50] Debugging.
+Once \MF\ is working, you should be able to diagnose most errors with
+the \.{show} commands and other diagnostic features. But for the initial
+stages of debugging, and for the revelation of really deep mysteries, you
+can compile \MF\ with a few more aids, including the \PASCAL\ runtime
+checks and its debugger. An additional routine called |debug_help|
+will also come into play when you type `\.D' after an error message;
+|debug_help| also occurs just before a fatal error causes \MF\ to succumb.
+@^debugging@>
+@^system dependencies@>
+
+The interface to |debug_help| is primitive, but it is good enough when used
+with a \PASCAL\ debugger that allows you to set breakpoints and to read
+variables and change their values. After getting the prompt `\.{debug \#}', you
+type either a negative number (this exits |debug_help|), or zero (this
+goes to a location where you can set a breakpoint, thereby entering into
+dialog with the \PASCAL\ debugger), or a positive number |m| followed by
+an argument |n|. The meaning of |m| and |n| will be clear from the
+program below. (If |m=13|, there is an additional argument, |l|.)
+@.debug \#@>
+
+@d breakpoint=888 {place where a breakpoint is desirable}
+
+@<Last-minute...@>=
+@!debug procedure debug_help; {routine to display various things}
+label breakpoint,exit;
+var @!k,@!l,@!m,@!n:integer;
+begin loop begin wake_up_terminal;
+ print_nl("debug # (-1 to exit):"); update_terminal;
+@.debug \#@>
+ read(term_in,m);
+ if m<0 then return
+ else if m=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: m:=0; @{'BREAKPOINT'@}@\
+ end
+ else begin read(term_in,n);
+ case m of
+ @t\4@>@<Numbered cases for |debug_help|@>@;
+ othercases print("?")
+ endcases;
+ end;
+ end;
+exit:end;
+gubed
+
+@ @<Numbered cases...@>=
+1: print_word(mem[n]); {display |mem[n]| in all forms}
+2: print_int(info(n));
+3: print_int(link(n));
+4: begin print_int(eq_type(n)); print_char(":"); print_int(equiv(n));
+ end;
+5: print_variable_name(n);
+6: print_int(internal[n]);
+7: do_show_dependencies;
+9: show_token_list(n,null,100000,0);
+10: slow_print(n);
+11: check_mem(n>0); {check wellformedness; print new busy locations if |n>0|}
+12: search_mem(n); {look for pointers to |n|}
+13: begin read(term_in,l); print_cmd_mod(n,l);
+ end;
+14: for k:=0 to n do print(buffer[k]);
+15: panicking:=not panicking;
+
+@* \[51] System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \MF\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* \[52] Index.
+Here is where you can find all uses of each identifier in the program,
+with underlined entries pointing to where the identifier was defined.
+If the identifier is only one letter long, however, you get to see only
+the underlined entries. {\sl All references are to section numbers instead of
+page numbers.}
+
+This index also lists error messages and other aspects of the program
+that you might want to look up some day. For example, the entry
+for ``system dependencies'' lists all sections that should receive
+special attention from people who are installing \MF\ in a new
+operating environment. A list of various things that can't happen appears
+under ``this can't happen''.
+Approximately 25 sections are listed under ``inner loop''; these account
+for more than 60\pct! of \MF's running time, exclusive of input and output.
diff --git a/systems/knuth/dist/mf/mfbook.tex b/systems/knuth/dist/mf/mfbook.tex
new file mode 100644
index 0000000000..c1692a6f52
--- /dev/null
+++ b/systems/knuth/dist/mf/mfbook.tex
@@ -0,0 +1,20160 @@
+% This manual is copyright (C) 1986 by the American Mathematical Society.
+% All rights are reserved!
+% The file is distributed only for people to see its examples of TeX input,
+% not for use in the preparation of books like The METAFONTbook.
+% Permission for any other use of this file must be obtained in writing
+% from the copyright holder and also from the publisher (Addison-Wesley).
+\let\MFmanual=\!
+\loop\iftrue
+ \errmessage{This manual is copyrighted and should not be TeXed}\repeat
+\pausing1 \input manmac
+\ifproofmode\message{Proof mode is on!}\pausing1\fi
+ % halftitle
+\titlepage
+\pageno=-1985
+\null\bigskip
+\line{\titlefont The {\manual ()*+,-.*}book}
+\vfill
+\ifproofmode
+\rightline{The fine print in the upper right-hand}
+\rightline{corner of each page is a draft of intended}
+\rightline{index entries; it won't appear in the real book.}
+\rightline{Some index entries will be in |typewriter type|}
+\rightline{and/or enclosed in \<$\ldots$>, etc;}
+\rightline{such typographic distinctions aren't shown here.}
+\rightline{An index entry often extends for several pages;}
+\rightline{the actual scope will be determined later.}
+\rightline{Please note things that should be indexed but aren't.}
+\medskip
+\rightline{Apology: The xeroxed illustrations are often hard to see;}
+\rightline{they will be done professionally in the real book.}
+\fi
+\eject
+\titlepage\null\vfill\eject % blank page
+ % title
+\pageno=-1 % the front matter is numbered with roman numerals
+\font\auth=cmssdc10 scaled\magstep4 % used only on the title page
+\font\elevenbf=cmbx10 scaled\magstephalf % ditto
+\font\elevenit=cmti10 scaled\magstephalf % ditto
+\font\elevenrm=cmr10 scaled\magstephalf % ditto
+\titlepage
+\null\bigskip
+\line{\titlefont The {\manual ()*+,-.*}book}
+^^{Knuth, Donald Ervin}
+^^{Bibby, Duane Robert}
+\vskip 2pc
+\baselineskip 13pt \elevenbf
+\halign to\hsize{#\hfil\tabskip 0pt plus 1fil&#\hfil\tabskip0pt\cr
+\kern2.5mm\auth DONALD \kern+0pt E. \kern+0pt KNUTH&
+ \elevenit Stanford University\cr
+\noalign{\vskip 11pc}
+&\elevenit I\kern.7ptllustrations by\cr
+&DU\kern-1ptANE BIBBY\cr
+\noalign{\vfill}
+&\setbox0=\hbox{\manual77}%
+\setbox2=\hbox to\wd0{\hss\manual6\hss}%
+\raise2.3mm\box2\kern-\wd0\box0\cr % A-W logo
+&ADDISON\kern.1em--WESLEY\cr
+%&PUBLISHING COMP\kern-.13emANY\kern-1.5mm\cr
+\noalign{\vskip.5pc \global\elevenrm}
+&Upper Saddle River, NJ\cr
+&Boston\enspace$\cdot$\enspace Indianapolis\cr
+&San Francisco\enspace$\cdot$\enspace New York\cr
+&Toronto\enspace$\cdot$\enspace Montr\'eal\cr
+&London\enspace$\cdot$\enspace Munich\cr
+&Paris\enspace$\cdot$\enspace Madrid\cr
+&Capetown\enspace$\cdot$\enspace Sydney\enspace$\cdot$\enspace Tokyo\cr
+&Singapore\enspace$\cdot$\enspace Mexico City\cr}
+\kern24pt
+\eject
+ % copyright
+\titlepage
+\eightpoint
+\vbox to 8pc{}
+\noindent\strut
+%The quotation on page xxx is copyright $\copyright$ 19xx by Xxxx,
+%and used by permission.
+%\medskip
+%\noindent
+This manual describes \MF\ Version 2.0. Some
+of the advanced features mentioned here are absent from earlier versions.
+\medskip
+\noindent
+The joke on page 8 is due to Richard S. ^{Palais}.
+\medskip
+\noindent
+The ^{Wilkins} quotation on page 283 was suggested by Georgia K. M. ^{Tobin}.
+\medskip
+\noindent
+{\manual opqrstuq} is a trademark of Addison\kern.1em--Wesley
+ Publishing Company.
+\medskip
+\noindent
+\TeX\ is a trademark of the American Mathematical Society.
+\bigskip\medskip
+\noindent
+{\bf Library of Congress cataloging in publication data}
+\medskip
+{\tt\halign{#\hfil\cr
+Knuth, Donald Ervin, 1938-\cr
+\ \ \ The METAFONTbook.\cr
+\noalign{\medskip}
+\ \ \ (Computers \& Typesetting ; C)\cr
+\ \ \ Includes index.\cr
+\ \ \ 1.~METAFONT (Computer system).\ \ 2.~Type and type-\cr
+founding--Data processing.\ \ I.~Title.\ \ II.~Series:\cr
+Knuth, Donald Ervin, 1938-\ \ \ \ .\ \ Computers \&\cr
+typesetting ; C.\cr
+Z250.8.M46K58\ \ 1986\ \ \ \ \ \ \ \ \ 686.2\char13 24\ \ \ \ \ \ 85-28675\cr
+ISBN 0-201-13445-4\cr
+ISBN 0-201-13444-6 (soft)\cr}}
+\vfill
+\noindent
+%{\sl \kern-1pt Incorporates the final corrections made in 1995,
+% and a few dozen more.}
+{\sl \kern-1pt Incorporates all corrections known in 2013.}
+\smallskip
+\noindent
+Internet page {\tt http://www-cs-faculty.stanford.edu/\char`\~
+ knuth/abcde.html}
+contains current information about this book and related books.
+\smallskip
+\noindent
+Copyright $\copyright$ 1986 by the American Mathematical Society
+\smallskip
+\noindent
+This book is published jointly by the American Mathematical Society
+and Addison\kern.1em--Wesley Publishing Company.
+All rights reserved. No part of this publication may be reproduced, stored in
+a retrieval system, or transmitted, in any form or by any means,
+electronic, mechanical, photocopying, recording, or otherwise, without
+the prior written permission of the publishers. Printed in the United
+States of America. % Published simultaneously in Canada.
+\medskip
+\noindent
+%ISBN 0-201-13444-6\par % paperback
+%ISBN 0-201-13445-4\par % hardcover
+ISBN-13 \enspace 978-0-201-13445-2\par\noindent
+ISBN-10 \enspace\phantom{978-}0-201-13445-4\par\noindent
+ISBN-13 \enspace 978-0-201-13444-5 (soft)\par\noindent
+ISBN-10 \enspace\phantom{978-}0-201-13444-6 (soft)\par
+%11 12 13 14 15 16--CRS--07 06 05 04 03 02 % paperback
+%7 8 9 10 11 12 13--CRS--07 06 05 04 03 02 01 % hardcover
+\smallskip\noindent
+Text printed in the United States
+ at Courier Westford in Westford, Massachusetts.\par\noindent
+Eighth Printing, February 2012\par\noindent
+Twelfth Printing, February 2012 (soft)
+^^{Knuth, Donald Ervin}
+\eject
+ % dedication
+\titlepage
+\vbox to 8pc{}
+\rightline{\strut\eightssi To Hermann Zapf:}
+^^{Zapf, Hermann}
+\vskip2pt
+\rightline{\eightssi Whose strokes are the best}
+\vfill
+\eject
+ % blank page
+\titlepage
+\null\vfill
+\eject
+ % the preface
+\titlepage
+\def\rhead{Preface}
+\vbox to 8pc{
+\rightline{\titlefont Preface}\vss}
+{\topskip 9pc % this makes equal sinkage throughout the Preface
+\vskip-\parskip
+\tenpoint
+\noindent\hang\hangafter-2
+\smash{\lower12pt\hbox to 0pt{\hskip-\hangindent\cmman G\hfill}}\hskip-16pt
+{\sc ENERATION} {\sc OF} {\sc LETTERFORMS} \strut by mathematical means
+was first tried in the fifteenth century; it became popular in the
+sixteenth and seventeenth centuries; and it was abandoned (for good
+reasons) during the eighteenth century. Perhaps the twentieth century
+will turn out to be the right time for this idea to make a comeback,
+now that mathematics has advanced and computers are able to
+do the calculations.
+
+Modern printing equipment based on raster lines---in which metal ``type''
+has been replaced by purely combinatorial patterns of zeroes and ones
+that specify the desired position of ink in a discrete way---makes
+mathematics and computer science increasingly relevant to printing.
+We now have the ability to give a completely precise definition of letter
+shapes that will produce essentially equivalent results on all raster-based
+machines. Moreover, the shapes can be defined in terms of variable
+parameters; computers can ``draw'' new fonts of characters
+in seconds, making it possible for designers to perform valuable experiments
+that were previously unthinkable.
+
+\MF\ is a system for the design of alphabets suited to raster-based
+devices that print or display text. The characters that you are reading
+were all designed with \MF\!, in a completely precise way; and they
+were developed rather hastily by the author of the system, who is a rank
+amateur at such things. It seems clear that further work with \MF\ has
+the potential of producing typefaces of real ^{beauty}. This manual has
+been written for people who would like to help advance the art of
+mathematical type design.
+
+A top-notch designer of typefaces needs to have an unusually good eye
+and a highly developed sensitivity to the nuances of shapes.
+A top-notch user of computer languages needs to have an unusual
+talent for abstract reasoning and a highly developed ability to
+express intuitive ideas in formal terms. Very few people have both
+of these unusual combinations of skills; hence the best products of
+\MF\ will probably be collaborative efforts between two
+people who complement each other's abilities. Indeed, this situation
+isn't very different from the way types have been created for many
+generations, except that the r\^ole of ``punch-cutter'' is now being
+played by skilled computer specialists instead of by skilled
+metalworkers.
+
+A \MF\ user writes a ``program'' for each letter or symbol of a typeface.
+These programs are different from ordinary computer programs,
+because they are essentially {\sl declarative\/} rather than imperative.
+In the \MF\ language you explain where the major components of a
+desired shape are to be located, and how they relate to each other,
+but you don't have to work out the details of exactly where the lines
+cross, etc.; the computer takes over the work of solving equations as it
+deduces the consequences of your specifications. One of the advantages of
+\MF\ is that it provides a discipline according to which the principles
+of a particular alphabet design can be stated precisely. The underlying
+intelligence does not remain hidden in the mind of the designer; it is
+spelled out in the programs. Thus consistency can readily be obtained
+where consistency is desirable, and a font can readily be extended to
+new symbols that are compatible with the existing ones.
+
+It would be nice if a system like \MF\ were to simplify the task of type
+design to the point where beautiful new alphabets could be created in a
+few hours. This, alas, is impossible; an enormous amount of subtlety lies
+behind the seemingly simple letter shapes that we see every day, and the
+designers of high-quality typefaces have done their work so well that we
+don't notice the underlying complexity. One of the disadvantages of \MF\
+is that a person can easily use it to produce poor alphabets, cheaply and
+in great quantity. Let us hope that such experiments will have educational
+value as they reveal why the subtle tricks of the trade are important, but
+let us also hope that they will not cause bad workmanship to proliferate.
+Anybody can now produce a book in which all of the type is home-made, but
+a person or team of persons should expect to spend a year or more on the
+project if the type is actually supposed to look right. \MF\ won't put
+today's type designers out of work; on the contrary, it will tend to make
+them heroes and heroines, as more and more people come to appreciate their
+skills.
+
+Although there is no royal road to type design, there are some things that
+can, in fact, be done well with \MF\ in an afternoon. Geometric designs
+are rather easy; and it doesn't take long to make modifications to letters
+or symbols that have previously been expressed in \MF\ form. Thus,
+although comparatively few users of \MF\ will have the courage to do an
+entire alphabet from scratch, there will be many who will enjoy
+customizing someone else's design.
+
+This book is not a text about mathematics or about computers. But if
+you know the rudiments of those subjects (namely, contemporary high school
+mathematics, together with the knowledge of how to use the text
+editing or word processing facilities on your computing machine),
+you should be able to use \MF\ with little difficulty after reading
+what follows. Some parts of the exposition in the text are more obscure
+than others, however, since the author has tried to satisfy experienced
+\MF ers as well as beginners and casual users with a single manual.
+Therefore a special symbol has been used to warn about esoterica: When you
+see the sign
+$$\vbox{\hbox{\dbend}\vskip 11pt}$$
+at the beginning of a paragraph, watch out for a ``^{dangerous bend}''
+in the train of thought---don't read such a paragraph unless you need to.
+You will be able to use \MF\ reasonably well, even to design characters like
+the dangerous-bend symbol itself, without reading the fine print in such
+advanced sections.
+
+Some of the paragraphs in this manual are so far out that they are rated
+$$\vcenter{\hbox{\dbend\kern1pt\dbend}\vskip 11pt}\;;$$
+everything that was said about single dangerous-bend signs goes double
+for these. You should probably have at least a month's experience with
+\MF\ before you attempt to fathom such doubly dangerous depths
+of the system; in fact, most people will never need to know \MF\
+in this much detail, even if they use it every day. After all, it's
+possible to fry an egg without knowing anything about biochemistry.
+Yet the whole story is here in case you're curious. \ (About \MF\!, not eggs.)
+
+The reason for such different levels of complexity is that people change
+as they grow accustomed to any powerful tool. When you first try to use
+\MF\!, you'll find that some parts of it are very easy, while other things
+will take some getting used to. At first you'll probably try to control
+the shapes too rigidly, by overspecifying data that has been copied from
+some other medium. But later, after you have begun to get a feeling for
+what the machine can do well, you'll be a different person, and you'll be
+willing to let \MF\ help contribute to your designs as they are being
+developed. As you gain more and more experience working with this unusual
+apprentice, your perspective will continue to change and you will
+run into different sorts of challenges. That's the way it is with any
+powerful tool: There's always more to learn, and there are always better
+ways to do what you've done before. At every stage in the development
+you'll want a slightly different sort of manual. You may even want to
+write one yourself. By paying attention to the dangerous bend signs in
+this book you'll be better able to focus on the level that interests you
+at a particular time.
+
+Computer system manuals usually make dull reading, but take heart:
+This one contains {\sc ^{JOKES}} every once in a while. You might actually
+enjoy reading it. \ (However, most of the jokes can only be appreciated
+properly if you understand a technical point that is being made---so
+read {\sl carefully}.)
+
+Another noteworthy characteristic of this book is that it doesn't
+always tell the ^{truth}. When certain concepts of \MF\ are introduced
+informally, general rules will be stated; afterwards you will find that the
+rules aren't strictly true. In general, the later chapters contain more
+reliable information than the earlier ones do. The author feels that this
+technique of deliberate lying will actually make it easier for you to
+learn the ideas. Once you understand a simple but false rule, it will not
+be hard to supplement that rule with its exceptions.
+
+In order to help you internalize what you're reading,
+{\sc ^{EXERCISES}} are sprinkled through this manual. It is generally intended
+that every reader should try every exercise, except for questions that appear
+in the ``dangerous bend'' areas. If you can't solve a problem, you
+can always look up the answer.
+But please, try first to solve it by yourself; then you'll learn more
+and you'll learn faster. Furthermore, if you think you do know the solution,
+you should turn to Appendix~A and check it out, just to make sure.
+
+\bigskip
+\hrule
+\line{\vrule\hss\vbox{\medskip\ninepoint
+\leftskip=\parindent \rightskip=\parindent
+\noindent\strut W{\sc ARNING}: Type design can be hazardous to your other
+interests. Once you get hooked, you will develop intense feelings about
+letterforms; the medium will intrude on the messages that you read. And you
+will perpetually be thinking of improvements to the fonts that you see
+everywhere, especially those of your own design.
+\strut\medskip}\hss\vrule}
+\hrule
+
+\bigskip
+
+The \MF\ language described here has very little in common with the
+author's previous attempt at a language for alphabet design, because
+five years of experience with the old system has made it clear that a
+completely different approach is preferable. Both languages have
+been called \MF; but henceforth the old language should be called
+\MF\kern.05em79, and its use should rapidly fade away. Let's keep the name
+\MF\ for the language described here, since it is so much better, and
+since it will never change again. ^^{MF79}
+
+I wish to thank the hundreds of people who have helped me to formulate
+this ``definitive edition'' of \MF\!, based on their experiences with
+preliminary versions of the system. In particular, John ^{Hobby}
+discovered many of the algorithms that have made the new language
+possible. My work at Stanford has been generously supported by the
+^{National Science Foundation}, the ^{Office of Naval Research}, the ^{IBM
+Corporation}, and the ^{System Development Foundation}. I also wish to
+thank the ^{American Mathematical Society} for its encouragement and for
+publishing the {\sl ^{TUGboat}\/} newsletter (see Appendix~J\null).
+Above all, I deeply thank my wife, Jill, for the inspiration, ^^{Knuth, Jill}
+understanding, comfort, and support she has given me for more than
+25~years, especially during the eight years that I have been
+working intensively on mathematical typography.
+
+\medskip
+\line{{\sl Stanford, California}\hfil--- D. E. K.}^^{Knuth, Don}
+\line{\sl September 1985\hfil}
+
+} % end of the special \topskip
+\endchapter
+
+It is hoped that Divine Justice may find
+some suitable affliction for the malefactors
+who invent variations upon the alphabet of our fathers.~.\thinspace.\thinspace.
+The type-founder, worthy mechanic, has asserted himself
+with an overshadowing individuality,
+defacing with his monstrous creations and revivals
+every publication in the land.
+\author AMBROSE ^{BIERCE}, {\sl The Opinionator.~Alphab\^etes\/} %
+ (1911) % vol 10 of his collected works, p69
+ % probably written originally in 1898 or 1899
+
+\bigskip
+
+Can the new process yield a result that, say,
+a Club of Bibliophiles would recognise as a work of art
+comparable to the choice books they have in their cabinets?
+\author STANLEY ^{MORISON}, {\sl Typographic Design in Relation to
+ Photographic Composition\/} (1958) % pp 4--5
+
+\eject
+ % the table of contents
+\titlepage
+\vbox to 8pc{
+\rightline{\titlefont Contents}
+\vfill}
+^^{Contents of this manual, table}
+\def\rhead{Contents}
+\tenpoint
+\begingroup
+\countdef\counter=255
+\def\diamondleaders{\global\advance\counter by 1
+ \ifodd\counter \kern-10pt \fi
+ \leaders\hbox to 20pt{\ifodd\counter \kern13pt \else\kern3pt \fi
+ .\hss}}
+\baselineskip 15pt plus 5pt
+\def\\#1. #2. #3.{\line{\strut
+ \hbox to\parindent{\bf\hbox to 1em{\hss#1}\hss}%
+ \rm#2\diamondleaders\hfil\hbox to 2em{\hss#3}}}
+\\1. The Name of the Game. 1.
+\\2. Coordinates. 5.
+\\3. Curves. 13.
+\\4. Pens. 21.
+\\5. Running \MF\!\null. 31.
+\\6. How \MF\ Reads What You Type. 49.
+\\7. Variables. 53.
+\\8. Algebraic Expressions. 59.
+\\9. Equations. 75.
+\\10. Assignments. 87.
+\\11. Magnification and Resolution. 91.
+\\12. Boxes. 101.
+\\13. Drawing, Filling, and Erasing. 109.
+\\14. Paths. 123.
+\\15. Transformations. 141.
+\\16. Calligraphic Effects. 147.
+\\17. Grouping. 155.
+\\18. Definitions (also called Macros). 159.
+\\19. Conditions and Loops. 169.
+\\20. More about Macros. 175.
+\\21. Random Numbers. 183.
+\\22. Strings. 187.
+\\23. Online Displays. 191.
+\eject
+\vbox to 8pc{}
+\\24. Discreteness and Discretion. 195.
+\\25. Summary of Expressions. 209.
+\\26. Summary of the Language. 217.
+\\27. Recovery from Errors. 223.
+\null
+\leftline{\indent\bf Appendices}
+\\A. Answers to All the Exercises. 233.
+\\B. Basic Operations. 257.
+\\C. Character Codes. 281.
+\\D. Dirty Tricks. 285.
+\\E. Examples. 301.
+\\F. Font Metric Information. 315.
+\\G. Generic Font Files. 323.
+\\H. Hardcopy Proofs. 327.
+\\I\hskip 1pt. Index. 345.
+\\J\hskip 1pt. Joining the \TeX\ Community. 361.
+\null % 17 lines so far to balance the 23 on the other page
+\null % 18
+\null % 19
+\null % 20
+\null % 21
+\null % 22
+\null % 23
+\eject
+\endgroup
+ \beginchapter Chapter 1. The Name of\\the Game
+
+\pageno=1 % This is page number 1, number 1,
+This is a book about a computer system called \MF\!, \kern1pt just as
+\kern-1pt {\sl The \TeX
+book\/} is about \TeX. \MF\ and \TeX\ are good friends who intend to live
+together for a long time. Between them they take care of the two most
+fundamental tasks of typesetting: \TeX\ puts characters into the proper
+positions on a page, while \MF\ determines the shapes of the characters
+themselves. ^^{TeX} ^^{METAFONT, the name}
+
+Why is the system called \MF\thinspace? The `-{\manual FONT}\thinspace'
+part is easy to understand, because sets of related characters that are
+used in typesetting are traditionally known as fonts of type. The
+`{\manual META}-' part is more interesting: It indicates that we are
+interested in making high-level descriptions that transcend any of the
+individual fonts being described.
+
+Newly coined words beginning with `meta-' generally reflect our contemporary
+inclination to view things from outside or above, at a more abstract level than
+before, with what we feel is a more mature understanding. We now have
+metapsychology (the study of how the mind relates to its containing body),
+metahistory (the study of principles that control the course of events),
+metamathematics (the study of mathematical reasoning), metafiction
+(literary works that explicitly acknowledge their own forms), and so on.
+A metamathematician proves metatheorems (theorems about theorems);
+a computer scientist often works with metalanguages (languages for
+describing languages). Similarly, a ^{meta-font} is a schematic description
+of the shapes in a family of related fonts; the letterforms change
+appropriately as their underlying parameters change.
+
+Meta-design is much more difficult than design; it's easier to draw something
+than to explain how to draw it. One of the problems is that different sets
+of potential specifications can't easily be envisioned all at once.
+Another is that a computer has to be told absolutely everything.
+However, once we have successfully explained how to draw something
+in a sufficiently general manner, the same explanation will work for
+related shapes, in different circumstances; so~the time spent in formulating
+a precise explanation turns out to be worth it.
+
+Typefaces intended for text are normally seen small, and our eyes can read
+them best when the letters have been designed specifically for the size at
+which they are actually used. Although it is tempting to get 7-point fonts
+by simply making a 70\% reduction from the 10-point size, this shortcut
+leads to a serious degradation of quality. Much better results can be
+obtained by incorporating parametric variations into a meta-design. In
+fact, there are advantages to built-in variability even when you want to
+produce only one font of type in a single size, because it allows you to
+postpone making decisions about many aspects of your design. If you leave
+certain things undefined, treating them as parameters instead of
+``freezing'' the specifications at an early stage, the computer will be
+able to draw lots of examples with different settings of the parameters,
+and you will be able to see the results of all those experiments at the final
+size. This will greatly increase your ability to edit and fine-tune the font.
+
+If meta-fonts are so much better than plain old ordinary fonts, why weren't
+they developed long ago? The main reason is that computers did not exist until
+recently. People find it difficult and dull to carry out calculations with
+a multiplicity of parameters, while today's machines do such tasks with ease.
+The introduction of parameters is a natural outgrowth of automation.
+
+OK, let's grant that meta-fonts sound good, at least in theory. There's still
+the practical problem about how to achieve them. How can we actually
+specify shapes that depend on unspecified parameters?
+
+If only one parameter is varying, it's fairly easy to solve the problem in
+a visual way, by overlaying a series of drawings that show graphically how
+the shape changes. For example, if the parameter varies from 0 to~1, we
+might prepare five sketches, corresponding to the parameter values 0,
+$1\over4$, $1\over2$, $3\over4$, and~1. If these sketches follow a
+consistent pattern, we can readily ^{interpolate} to find the shape for a
+value like~$2\over3$ that lies between two of the given ones. We might
+even try extrapolating to parameter values like 1$1\over4$.
+
+But if there are two or more independent parameters, a purely visual solution
+becomes too cumbersome. We must go to a verbal approach, using some sort
+of language to describe the desired drawings. Let's imagine, for example,
+that we want to explain the shape of a certain letter `a' to a friend in
+a distant country, using only a telephone for communication; our friend
+is supposed to be able to reconstruct exactly the shape we have in mind.
+Once we figure out a sufficiently natural way to do that, for a particular
+fixed shape, it isn't much of a trick to go further and make our verbal
+description more general, by including variable parameters instead of
+restricting ourselves to constants.
+
+An analogy to cooking might make this point clearer. Suppose you have just
+baked a delicious berry pie, and your friends ask you to tell them the
+^{recipe} so that they can bake one too. If you have developed your cooking
+skills entirely by intuition, you might find it difficult to record exactly
+what you did. But there is a traditional language of recipes in which you
+could communicate the steps you followed; and if you take careful measurements,
+you might find that you used, say, 1$1\over4$ cups of sugar. The next step,
+if you were instructing a computer-controlled cooking machine, would be to
+go to a meta-recipe in which you use, say, $.25x$ cups of sugar for $x$
+cups of berries; or $.3x+.2y$ cups for $x$~cups of boysenberries and
+$y$~cups of blackberries.
+
+In other words, going from design to meta-design is essentially like
+going from arithmetic to elementary algebra. Numbers are replaced
+by simple formulas that involve unknown quantities. We will see
+many examples of this.
+
+A \MF\ definition of a complete typeface generally consists of three
+main parts. First there is a rather mundane set of subroutines that take care
+of necessary administrative details, such as assigning code numbers
+to individual characters; each character must also
+be positioned properly inside an invisible ``box,'' so that typesetting
+systems will produce the correct spacing. Next comes a more interesting
+collection of subroutines, designed to draw the basic strokes characteristic
+of the typeface (e.g., the serifs, bowls, arms, arches, and so on).
+These subroutines will typically be described in terms of their own special
+parameters, so that they can produce a variety of related strokes;
+a serif subroutine will, for example, be able to draw serifs of
+different lengths, although all of the serifs it draws should have the
+same ``feeling.'' Finally, there are routines for each of the characters.
+If the subroutines in the first and second parts have been chosen well,
+the routines of the third part will be fairly high-level descriptions
+that don't concern themselves unnecessarily with details; for example, it
+may be possible to substitute a different serif-drawing subroutine without
+changing any of the programs that use that subroutine, thereby obtaining
+a typeface of quite a different flavor. [A particularly striking example
+of this approach has been worked out by John~D. ^{Hobby} and ^{Gu} Guoan
+in ``A Chinese Meta-Font,'' {\sl TUGboat\/ \bf5} (1984), 119--136. By
+changing a set of 13 basic stroke subroutines, they were able to draw 128
+sample ^{Chinese characters} in three different styles (Song, Long Song,
+and Bold), using the same programs for the characters.]
+
+A well-written \MF\ program will express the designer's intentions more
+clearly than mere drawings ever can, because the language of algebra has
+simple ``idioms'' that make it possible to elucidate many visual relationships.
+Thus, \MF\ programs can be used to communicate knowledge
+about type design, just as recipes convey the expertise of a chef. But
+algebraic formulas are not easy to understand in isolation; \MF\ descriptions
+are meant to be read with an accompanying illustration, just as the
+constructions in geometry textbooks are accompanied by diagrams.
+Nobody is ever expected to read the text of a \MF\ program and say,
+``Ah, what a beautiful letter!'' But with one or more enlarged pictures
+of the letter, based on one or more settings of the parameters, a reader
+of the \MF\ program should be able to say, ``Ah, I~understand how this
+beautiful letter was drawn!'' We shall see that the \MF\ system makes it
+fairly easy to obtain annotated proof drawings that you can hold in your
+hand as you are working with a program.
+
+Although \MF\ is intended to provide a relatively painless way to describe
+meta-fonts, you can, of course, use it also to describe unvarying shapes that
+have no ``meta-ness'' at all. Indeed, you need not even use it to produce
+fonts; the system will happily draw geometric designs that have no relation
+to the characters or glyphs of any alphabet or script. The author
+occasionally uses \MF\ simply as a pocket calculator, to do elementary
+arithmetic in an interactive way. A computer doesn't mind if its
+programs are put to purposes that don't match their names.
+
+\endchapter
+
+[Tinguely] made some large, brightly coloured open reliefs,
+juxtaposing stationary and mobile shapes.
+He later gave them names like\/ %
+{\rm Meta-^{Kandinsky}}\kern-1pt\ and\/ {\rm Meta-^{Herbin}}\kern-.5pt,
+to clarify the ideas and attitudes %
+that lay at the root of their conception.
+\author K. G. PONTUS ^{HULT\'EN}, {\sl Jean ^{Tinguely}: M\'eta\/} (1972)
+ % translated from German by Mary Whittall, 1975, p46
+
+\bigskip
+
+The idea of a meta-font should now be clear. But what good is it?
+The ability to manipulate lots of parameters may be interesting and fun,
+but does anybody really need a 6\/{\manual\seventh}\kern1pt-point font
+that is one fourth of the way between Baskerville and Helvetica?
+\author DONALD E. ^{KNUTH}, {\sl The Concept of a Meta-Font\/} (1982)
+ % Visible Language 16, p19
+
+\eject
+ \beginchapter Chapter 2. Coordinates
+
+If we want to tell a computer how to draw a particular shape, we need a way to
+explain where the key points of that shape are supposed to be.
+\MF\ uses standard {\sl ^{Cartesian} ^{coordinates}\/} for this purpose:
+The location of a point is defined by specifying its $x$~coordinate, which
+is the number of units to the right of some reference point, and its
+$y$~coordinate, which is the number of units upward from the reference
+point. First we determine the horizontal (left/right) component of a
+point's position, then we determine the vertical (up/down) component.
+\MF's world is two-dimensional, so two coordinates are enough.%
+^^{x coordinate} ^^{y coordinate}
+
+For example, let's consider the following six points:
+\displayfig 2a (4.75pc)
+\MF's names for the positions of these points are
+\begindisplay
+$(x_1,y_1)=(0,100)$;&$(x_2,y_2)=(100,100)$;&$(x_3,y_3)=(200,100)$;\cr
+$(x_4,y_4)=(0,\hfill0)$;&$(x_5,y_5)=(100,\hfill0)$;&
+ $(x_6,y_6)=(200,\hfill0)$.\cr
+\enddisplay
+Point 4 is the same as the reference point, since both of its coordinates
+are zero; to get to point~$3=(200,100)$, you start at the reference point
+and go 200~steps right and 100~up; and so on.
+
+\exercise Which of the six example points is closest to the point $(60,30)$?
+\answer Point $5=(100,0)$ is closer than any of the others. \ (See
+the diagram below.)
+
+\exercise True or false: All points that lie on a given horizontal straight
+line have the same $x$~coordinate.
+\answer \decreasehsize 15pc
+\rightfig A2a (13pc x 5pc) ^9pt
+False. But they all do have the same $y$~coordinate.
+
+\exercise Explain where the point $(-5,15)$ is located.
+\answer 5 units to the {\sl left\/} of the reference point, and 15 units up.
+
+\exercise What are the coordinates of a point that lies exactly
+60~units below point~6 in the diagram above?
+(``Below'' means ``down the page,'' not ``under the page.'')
+\answer \restorehsize $(200,-60)$.
+
+In a typical application of \MF\!, you prepare a rough sketch of the shape
+you plan to define, on a piece of ^{graph paper}, and you label important
+points on that sketch with any convenient numbers. Then you write a \MF\
+program that explains (i)~the coordinates of those key points, and
+(ii)~the lines or curves that are supposed to go between them.
+
+\MF\ has its own internal graph paper, which forms a so-called ^{raster}
+or ^{grid} consisting of square ``^{pixels}.'' ^^{pel, see pixel}
+The output of \MF\ will \hbox{specify} that certain of the pixels are ``black''
+and that the others are ``white''; thus, the computer essentially converts
+shapes into binary patterns like the designs a~person can make when doing
+needlepoint with two colors of yarn.
+
+Coordinates are lengths, but we haven't discussed yet what the units of
+length actually are. It's important to choose convenient units,
+and \MF's coordinates are given in units of pixels. The little squares
+illustrated on the previous page, which correspond to differences
+of 10~units in an $x$~coordinate or a $y$~coordinate, therefore represent
+$10\times10$ arrays of pixels, and the rectangle enclosed by our six
+example points contains 20,000 pixels altogether.\footnote*{We
+sometimes use the term ``pixel'' to mean a square picture element,
+but sometimes we use it to signify a one-dimensional unit of length.
+A square pixel is one pixel-unit wide and one pixel-unit tall.}
+
+Coordinates don't have to be whole numbers. You can refer, for example,
+to point $(31.5,42.5)$, which lies smack in the middle of the pixel
+whose corners are at $(31,42)$, $(31,43)$, $(32,42)$, and~$(32,43)$.
+The computer works internally with coordinates that are integer multiples
+of ${1\over65536}\approx0.00002$ of the width of a pixel, so it is
+capable of making very fine distinctions. But \MF\ will never make
+a pixel half black; it's all or nothing, as far as the output is concerned.
+
+The fineness of a grid is usually called its {\sl ^{resolution}}, and
+resolution is usually expressed in pixel units per inch (in America)
+or pixel units per millimeter (elsewhere). For example, the type you
+are now reading was prepared by \MF\ with a resolution of slightly
+more than 700 pixels to the inch, but with slightly fewer than 30 pixels
+per~mm. For the time being we shall assume that the pixels are so tiny
+that the operation of rounding to whole pixels is unimportant;
+later we will consider the important questions that arise when \MF\ is
+producing low-resolution output.
+
+It's usually desirable to write \MF\ programs that can manufacture fonts
+at many different resolutions, so that a variety of low-resolution printing
+devices will be able to make proofs that are compatible with a variety of
+high-resolution devices. Therefore the key points in \MF\ programs are rarely
+specified in terms of pure numbers like `100'\thinspace; we generally make
+the coordinates relative to some other resolution-dependent quantity, so
+that changes will be easy to make. For example, it would have been better
+to use a definition something like the following, for the six points
+considered earlier:
+\begindisplay
+$(x_1,y_1)=(0,b)$;&$(x_2,y_2)=(a,b)$;&$(x_3,y_3)=(2a,b)$;\cr
+$(x_4,y_4)=(0,0)$;&$(x_5,y_5)=(a,0)$;&$(x_6,y_6)=(2a,0)$;\cr
+\enddisplay
+then the quantities $a$ and $b$ can be defined in some way appropriate to
+the desired resolution. We had $a=b=100$ in our previous example, but
+such constant values leave us with little or no flexibility.
+
+Notice the quantity `$2a$' in the definitions of $x_3$ and $x_6$; \MF\
+understands enough algebra to know that this means twice the value of~$a$,
+whatever $a$~is. We observed in Chapter~1 that simple uses of algebra give
+\MF\ its meta-ness. Indeed, it is interesting to note from a historical
+standpoint that ^{Cartesian} coordinates are named after Ren\'e
+^{Descartes}, not because he invented the idea of coordinates, but because
+he showed how to get much more out of that idea by applying algebraic
+methods. People had long since been using coordinates for such things as
+latitudes and longitudes, but Descartes observed that by putting unknown
+quantities into the coordinates it became possible to describe infinite
+sets of related points, and to deduce properties of curves that were
+extremely difficult to work out using geometrical methods alone.
+
+So far we have specified some points, but we haven't actually done
+anything with them. Let's suppose that we want to draw a straight line
+from point~1 to point~6, obtaining
+\displayfig 2b (5pc)
+One way to do this with \MF\ is to say
+\begindisplay
+@draw@ $(x_1,y_1)\to(x_6,y_6)$.
+\enddisplay
+The `$\to$' ^^{..} here tells the computer to connect two points.
+
+It turns out that we often want to write formulas like `$(x_1,y_1)$', so
+it will be possible to save lots of time if we have a special abbreviation
+for such things. Henceforth we shall use the notation $z_1$ to stand for
+$(x_1,y_1)$; and in general, ^^{z convention}
+$z_k$ with an arbitrary subscript will stand for the point $(x_k,y_k)$.
+The `@draw@' command above can therefore be written more simply as
+\begindisplay
+^@draw@ $z_1\to z_6$.
+\enddisplay
+Adding two more straight lines by saying, `@draw@ $z_2\to z_5$' and
+`@draw@ $z_3\to z_4$', we obtain a design that is slightly reminiscent of
+the ^{Union Jack}:
+\displayfig 2c (5.5pc)
+We shall call this a ^{hex symbol}, because it has six endpoints. Notice
+that the straight lines here have some thickness, and they are rounded at
+the ends as if they had been drawn with a felt-tip pen having a circular
+nib. \MF\ provides many ways to control the thicknesses of lines and to
+vary the terminal shapes, but we shall discuss such things in later
+chapters because our main concern right now is to learn about coordinates.
+
+If the hex symbol is scaled down so that its height parameter $b$
+is exactly equal to the height of the letters in this paragraph,
+it looks like this: `\thinspace{\manual\hexa}\thinspace'. Just for fun,
+let's try to typeset ten of them in a row:
+\begindisplay
+{\manual\hexa\hexa\hexa\hexa\hexa\hexa\hexa\hexa\hexa\hexa}
+\enddisplay
+How easy it is to do this!\footnote*{Now that authors have
+for the first time the power to invent new symbols with great ease, and to
+have those characters printed in their manuscripts on a wide variety of
+typesetting devices, we must face the question of how much experimentation
+is desirable. Will font freaks abuse this toy by overdoing it? Is it wise
+to introduce new symbols by the thousands? Such questions are beyond
+the scope of this book; but it is easy to imagine an epidemic of
+fontomania occurring, once people realize how much fun it is to design
+their own characters, hence it may be necessary to perform fontal
+lobotomies.} % This joke due to Richard Palais, commenting on draft in 1979
+
+Let's look a bit more closely at this new character.
+The {\manual\hexa} is a bit too tall, because it extends above points
+1, 2, and~3 when the thickness of the lines is taken into account;
+similarly, it sinks a bit too much below the baseline (i.e., below
+the line $y=0$ that contains points 4, 5, and~6). In order to correct
+this, we want to move the key points slightly. For example, point~$z_1$
+should not be exactly at $(0,b)$; we ought to arrange things so that
+the top of the pen is at $(0,b)$ when the center of the pen is at~$z_1$.
+We can express this condition for the top three points as follows:
+\begindisplay
+$"top"\,z_1=(0,b)$;&$"top"\,z_2=(a,b)$;&$"top"\,z_3=(2a,b)$;\cr
+\noalign{\vskip\belowdisplayskip
+\leftline{similarly, the remedy for points 4, 5, and 6 is to specify
+ the equations}
+\vskip\abovedisplayskip}
+$"bot"\,z_4=(0,0)$;&$"bot"\,z_5=(a,0)$;&$"bot"\,z_6=(2a,0)$.\cr
+\enddisplay
+The resulting squashed-in character is
+\displayfig 2d (4.5pc)
+(shown here with the original weight `\thinspace{\manual\hexb}\thinspace'
+and also in a bolder version `\thinspace{\manual\hexc}\thinspace').
+
+\exercise Ten of these bold hexes produce `\thinspace{\manual
+\hexc\hexc\hexc\hexc\hexc\hexc\hexc\hexc\hexc\hexc}\thinspace'; notice that
+adjacent symbols overlap each other. The reason is that each character
+has width $2a$, hence point~3 of one character coincides with point~1
+of the next. Suppose that we actually want the characters to be
+completely confined to a rectangular box of width~$2a$, so that
+adjacent characters come just shy of touching (\thinspace{\manual
+\hexd\hexd\hexd\hexd\hexd\hexd\hexd\hexd\hexd\hexd}\thinspace).
+Try to guess how the point-defining equations above could be modified
+to make this happen, assuming that
+\MF\ has operations `"lft"' and `"rt"' analogous to `"top"' and `"bot"'.
+\answer $"top"\,"lft"\,z_1=(0,b)$; \ $"top"\,z_2=(a,b)$; \
+$"top"\,"rt"\,z_3=(2a-1,b)$; \ $"bot"\,"lft"\,z_4=(0,0)$; \
+$"bot"\,z_5=(a,0)$; \ $"bot"\,"rt"\,z_6=(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 specified here to have the $x$~coordinate $2a-1$.
+
+Pairs of coordinates can be thought of as ``^{vectors}'' or ``displacements''
+as well as points. For example, $(15,8)$ can be regarded as a command to
+go right~15 and up~8; then point $(15,8)$ is the position we get to after
+starting at the reference point and obeying the command $(15,8)$. This
+interpretation works out nicely when we consider addition of vectors:
+If we move according to the vector $(15,8)$ and then move according to
+$(7,-3)$, the result is the same as if we move $(15,8)+(7,-3)=
+(15+7,8-3)=(22,5)$. The sum of two vectors $z_1=(x_1,y_1)$ and $z_2=
+(x_2,y_2)$ is the vector $z_1+z_2=(x_1+x_2,y_1+y_2)$ obtained by adding
+$x$ and $y$ components separately. This vector represents the result of
+moving by vector $z_1$ and then moving by vector $z_2$; alternatively,
+$z_1+z_2$ represents the point you get~to by starting at point~$z_1$
+^^{addition of vectors}
+and moving by vector~$z_2$.
+
+\exercise Consider the four fundamental vectors $(0,1)$, $(1,0)$,
+$(0,-1)$, and $(-1,0)$. Which of them corresponds to moving one pixel unit
+(a)~to the right? (b)~to the left? (c)~down? (d)~up?
+\answer $"right"=(1,0)$; $"left"=(-1,0)$; $"down"=(0,-1)$; $"up"=(0,1)$.
+
+Vectors can be subtracted as well as added; the value of $z_1-z_2$ is simply
+$(x_1-x_2,y_1-y_2)$. Furthermore it is natural to multiply a vector
+by a single number~$c$: The quantity $c$~times $(x,y)$, which is written
+$c(x,y)$, equals $(cx,cy)$. Thus, for example, $2z=2(x,y)=(2x,2y)$ turns
+out to be equal to $z+z$. ^^{multiplication of vector by scalar}
+In the special case $c=-1$, we write $-(x,y)=(-x,-y)$. ^^{negation of vectors}
+
+Now we come to an important notion, based on the fact that subtraction
+is the opposite of addition. {\sl If $z_1$ and $z_2$ are any two points,
+then $z_2-z_1$ is the vector that corresponds to moving from $z_1$ to~$z_2$.}
+The reason is simply that $z_2-z_1$ is what we must add to~$z_1$ in order
+to get~$z_2$: i.e., $z_1+(z_2-z_1)=z_2$. We shall call this the
+{\sl ^{vector subtraction principle}}. ^^{subtraction of vectors}
+It is used frequently in \MF\ programs when the designer wants to specify the
+direction and/or distance of one point from another.
+
+\MF\ programs often use another idea to express relations between points.
+Suppose we start at point~$z_1$ and travel in a straight line from there
+in the direction of point~$z_2$, but we don't go all the way. There's a
+special notation for this, using square brackets: ^^{bracket notation}
+\begindisplay \advance\baselineskip by 3pt
+${1\over3}[z_1,z_2]$ is the point one-third of the way from $z_1$ to $z_2$,\cr
+${1\over2}[z_1,z_2]$ is the point midway between $z_1$ and $z_2$,\cr
+$.8[z_1,z_2]$ is the point eight-tenths of the way from $z_1$ to $z_2$,\cr
+\enddisplay
+and, in general, $t[z_1,z_2]$ stands for the point that lies a fraction
+$t$ of the way from $z_1$ to~$z_2$. We call this the operation of {\sl
+^{mediation}\/} between points, or (informally) the ``^{of-the-way
+function}.'' If the fraction~$t$ increases from 0 to~1, the expression
+$t[z_1,z_2]$ traces out a straight line from $z_1$ to~$z_2$. According to
+the vector subtraction principle, we must move $z_2-z_1$ in order to go all
+the way from $z_1$ to~$z_2$, hence the point $t$~of~the~way between them is
+\begindisplay
+$t[z_1,z_2]\;=\;z_1+t(z_2-z_1)$.
+\enddisplay
+This is a general formula by which we can calculate $t[z_1,z_2]$ for any
+given values of $t$, $z_1$, and~$z_2$. But \MF\ has this formula built~in,
+so we can use the bracket notation explicitly.
+
+For example, let's go back to our first six example points, and suppose
+that we want to refer to the point that's 2/5 of the way from
+$z_2=(100,100)$ to $z_6=(200,0)$. In \MF\ we can write this simply as
+$.4[z_2,z_6]$. And if we need to compute the exact coordinates for some
+reason, we can always work them out from the general formula, getting
+$z_2+.4(z_6-z_2)=(100,100)+.4\bigl((200,0)-(100,100)\bigr)=(100,100)
++.4(100,-100)=(100,100)+(40,-40)=(140,60)$.
+
+\exercise True or false: The direction vector from $(5,-2)$ to $(2,3)$
+is $(-3,5)$.
+\answer True; this is $(2,3)-(5,-2)$.
+
+\exercise Explain what the notation `$0[z_1,z_2]$' means, if anything.
+What about `$1[z_1,z_2]$'? And `$2[z_1,z_2]$'? And `$(-.5)[z_1,z_2]$'?
+\answer $0[z_1,z_2]=z_1$, because we move none of the way towards~$z_2$;
+similarly $1[z_1,z_2]$ simplifies to~$z_2$, 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 $z_1$ to~$z_2$, we get to $2[z_1,z_2]$. But if we
+start at point~$z_1$ and face~$z_2$, then back up exactly half the distance
+between them, we wind up at $(-.5)[z_1,z_2]$.
+
+\exercise True or false, for mathematicians: (a)~${1\over2}[z_1,z_2]=
+{1\over2}(z_1+z_2)$; \ (b)~${1\over3}[z_1,z_2]={1\over3}z_1+{2\over3}z_2$;
+\ (c)~$t[z_1,z_2]=(1-t)[z_2,z_1]$.
+\answer (a)~True; both are equal to $z_1+{1\over2}(z_2-z_1)$.
+(b)~False, but close; the right-hand side should be
+${2\over3}z_1+{1\over3}z_2$. (c)~True; both are equal to $(1-t)z_1+tz_2$.
+
+\setbox0=\vtop{\kern -6pt
+ \rightline{\rlap{\vbox to 250\apspix{
+ \setbox2=\vbox{\kern-1pt
+ \hbox{\tenex\char'77} % vertical arrow extension module
+ \kern-1pt}
+ \offinterlineskip
+ \vbox{\hbox{\tenex\char'170}\kern-1pt} % arrowhead at top
+ \cleaders\copy2\vfill
+ \kern3pt
+ \hbox to\wd2{\hss$b$\hss}
+ \kern3pt
+ \cleaders\copy2\vfill
+ \vbox{\kern-1pt\hbox{\tenex\char'171}\kern0pt} % arrowhead at bottom
+ }}\kern 30\apspix
+ \vbox{%\kern-.2pt \hrule \kern-.2pt
+ \hbox{%\kern-.2pt \vrule \kern-.2pt
+ \kern30\apspix\figbox{2e}{150\apspix}{250\apspix}\vbox
+% \kern30\apspix\kern-.2pt\vrule \kern-.2pt}
+% \kern-.2pt \hrule \kern-.2pt}\quad}
+ \kern30\apspix}
+ }\quad}
+ \kern2pt
+ \rightline{\hbox to 30\apspix{\kern-.2pt\vrule height 7pt depth 2pt
+ \hfil$s$\hfil\vrule\kern-.2pt}%
+ \hbox to 150\apspix{\leftarrowfill$\,a\,$\rightarrowfill}%
+ \hbox to 30\apspix{\kern-.2pt\vrule height 7pt depth 2pt
+ \hfil$s$\hfil\vrule\kern-.2pt}\quad}}
+\dp0=0pt
+
+\hangindent-300\apspix \hangafter-13
+Let's conclude \strut\vadjust{\box0}%
+this chapter by using mediation
+to help specify the five points in the stick-figure `{\manual\Aa}'
+shown enlarged at the right. The distance between points 1 and~5
+should be~$a$, and point~3 should be $b$ pixels above the baseline;
+these values $a$ and~$b$ have been predetermined by some method
+that doesn't concern us here, and so has a ``^{sidebar}'' parameter~$s$
+that specifies the horizontal distance of points 1 and~5 from the
+edges of the type. We shall assume that we don't know for sure what
+the height of the bar line should be; point~2 should be somewhere on the
+straight line from point~1 to point~3, and point~4 should be in the
+corresponding place between 5 and~3, but we want to try several
+possibilities before we make a decision.
+
+The width of the character will be $s+a+s$, and we can specify points
+$z_1$ and $z_5$ by the equations
+\begindisplay
+$"bot"\,z_1=(s,0)$;\qquad $z_5=z_1+(a,0)$.
+\enddisplay
+There are other ways to do the job, but these formulas clearly express
+our intention to have the bottom of the pen at the baseline, $s$ pixels
+to the right of the reference point, when the pen is at~$z_1$,
+and to have $z_5$ exactly $a$~pixels to the right of~$z_1$.
+Next, we can say
+\begindisplay
+$z_3=\bigl({1\over2}[x_1,x_5],b\bigr)$;
+\enddisplay
+this means that the $x$ coordinate of point 3 should be halfway between
+the $x$~coordinates of points 1 and~5, and that $y_3=b$. Finally, let's say
+\begindisplay
+$z_2="alpha"[z_1,z_3]$;\qquad $z_4="alpha"[z_5,z_3]$;
+\enddisplay
+the parameter "alpha" is a number between 0 and~1 that governs the
+position of the bar line, and it will be supplied later. When "alpha"
+has indeed received a value, we can say
+\begindisplay
+@draw@ $z_1\to z_3$;\qquad @draw@ $z_3\to z_5$;\qquad @draw@ $z_2\to z_4$.
+\enddisplay
+\MF\ will draw the characters `{\manual\sevenAs}' when "alpha" varies
+from 0.2 to 0.5 in steps of 0.05 and when $a=150$, $b=250$, $s=30$.
+The illustration on the previous page has $"alpha"=(3-\sqrt5\,)/2\approx
+0.38197$; this value makes the ratio of the area below the bar to the area
+above it equal to $(\sqrt5+1)/2\approx1.61803$, the so-called ``^{golden
+ratio}'' of classical Greek mathematics.
+
+\danger (Are you sure you should be reading this paragraph? The
+``^{dangerous bend}'' sign here is meant to warn you about material that
+ought to be skipped on first reading. And maybe also on second reading.
+The reader-beware paragraphs sometimes refer to concepts that aren't
+explained until later chapters.)
+
+\dangerexercise Why is it better to define $z_3$ as $\bigl({1\over2}[x_1,
+x_5],b\bigr)$, rather than to work out the explicit coordinates
+$z_3=(s+{1\over2}a,\,b)$ that are implied by the other equations?
+\answer There are several reasons. (1)~The equations in a \MF\ 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 specifications for $z_1$ and $z_5$ change,
+the formula $\bigl({1\over2}[x_1,x_5],b\bigr)$
+still gives a reasonable value for~$z_3$. It's
+almost always good to anticipate the need for subsequent modifications.\par
+However, the stated formula for $z_3$ isn't the only reasonable way to
+proceed. We could, for example, give two equations
+\begindisplay
+$x_3-x_1=x_5-x_3$;\qquad $y_3=b$;
+\enddisplay
+the first 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
+\MF\ is able to solve a wide variety of equations.
+
+\ninepoint % all dangerous from here
+\ddangerexercise Given $z_1$, $z_3$, and $z_5$ as above, explain how
+to define $z_2$ and~$z_4$ so that all of the following conditions hold
+simultaneously:
+\enddanger
+
+\smallskip
+\item\bull the line from $z_2$ to $z_4$ slopes upward at a $20^\circ$ angle;
+
+\item\bull the $y$ coordinate of that line's midpoint is 2/3 of the
+way from $y_3$ to $y_1$;
+
+\item\bull $z_2$ and $z_4$ are on the respective lines $z_1\to z_3$ and
+$z_3\to z_5$.
+
+\smallskip\noindent
+(If you solve this exercise, you deserve an `{\manual\Az}'.)
+\answer The following four equations suffice to define the four
+unknown quantities $x_2$, $y_2$, $x_4$, and $y_4$:
+$z_4-z_2="whatever"\ast{\rm dir}\,20$;
+${1\over2}[y_2,y_4]={2\over3}[y_3,y_1]$;
+$z_2="whatever"[z_1,z_3]$;
+$z_4="whatever"[z_3,z_5]$. ^^"whatever" ^^{dir}
+
+\endchapter
+
+Here, where we reach the sphere of mathematics,
+we are among processes which seem to some
+the most inhuman of all human activities
+and the most remote from poetry.
+Yet it is here that the artist has the fullest scope for his imagination.
+\author HAVELOCK ^{ELLIS}, {\sl The Dance of Life\/} (1923) % pp 138--139
+
+\bigskip
+
+To anyone who has lived in a modern American city (except Boston)
+at least one of the underlying ideas of ^{Descartes}' analytic geometry
+will seem ridiculously evident. Yet, as remarked,
+it took mathematicians all of two thousand years
+to arrive at this simple thing.
+\author ERIC TEMPLE ^{BELL}, {\sl Mathematics: Queen and Servant of %
+ Science\/} (1951) % p123
+
+\eject
+ \beginchapter Chapter 3. Curves
+
+Albrecht ^{D\"urer} and other Renaissance men attempted to establish
+mathematical principles of type design, but the letters they came up with
+were not especially beautiful. Their methods failed because they
+restricted themselves to ``ruler and compass'' constructions, which cannot
+adequately express the nuances of good calligraphy. \MF\ gets around this
+problem by using more powerful mathematical techniques, which provide the
+necessary flexibility without really being too complicated. The purpose of
+the present chapter is to explain the simple principles by which a
+computer is able to draw ``pleasing'' ^{curves}.
+
+The basic idea is to start with four points $(z_1,z_2,z_3,z_4)$ and to
+^^{four-point method for curves}
+construct the three ^{midpoints} $z_{12}={1\over2}[z_1,z_2]$,
+$z_{23}={1\over2}[z_2,z_3]$, $z_{34}={1\over2}[z_3,z_4]$:
+\displayfig 3a (5pc)
+Then take those three midpoints $(z_{12},z_{23},z_{34})$ and construct
+two second-order midpoints $z_{123}={1\over2}[z_{12},z_{23}]$ and
+$z_{234}={1\over2}[z_{23},z_{34}]$; finally, construct the third-order
+midpoint $z_{1234}={1\over2}[z_{123},z_{234}]$:
+\displayfig 3b (5pc)
+This point $z_{1234}$ is one of the points of the curve determined by
+$(z_1,z_2,z_3,z_4)$. To get the remaining points of that curve,
+repeat the same construction on $(z_1,z_{12},z_{123},z_{1234})$ and
+on $(z_{1234},z_{234},z_{34},z_4)$, ad infinitum:
+\displayfig 3c (4.5pc)
+The process converges quickly, and the preliminary scaffolding
+(which appears above the limiting curve in our example) is ultimately discarded.
+The limiting curve has the following important properties:
+
+\smallskip
+\item\bull It begins at $z_1$, heading in the direction from $z_1$ to $z_2$.
+
+\item\bull It ends at $z_4$, heading in the direction from $z_3$ to $z_4$.
+
+\item\bull It stays entirely within the so-called convex hull of $z_1$,
+$z_2$, $z_3$, and $z_4$; i.e., all points of the curve lie ``between'' the
+defining points.
+
+\danger The recursive midpoint rule for curve-drawing was discovered in 1959
+by Paul ^{de Casteljau}, who showed that the curve could be described
+algebraically by the remarkably simple formula
+\begindisplay
+$z(t)\;=\;(1-t)^3z_1+3(1-t)^2t\,z_2+3(1-t)t^2z_3+t^3z_4$,
+\enddisplay
+as the parameter $t$ varies from 0 to 1. This polynomial of degree~3 in~$t$
+is called a {\sl ^{Bernshte{\u\i}n polynomial}}, because Serge\u\i~N.
+^{Bernshte{\u\i}n} introduced such functions in 1912 as part of his
+pioneering work on approximation theory. Curves traced out by Bernshte{\u\i}n
+polynomials of degree~3 are often called {\sl B\'ezier cubics}, after
+Pierre ^{B\'ezier} who realized their importance for computer-aided design
+during the 1960s.
+
+\danger It is interesting to observe that the Bernshte\u\i n polynomial
+of degree~1, i.e., the function $z(t)=(1-t)\,z_1+t\,z_2$, is precisely the
+^{mediation} operator $t[z_1,z_2]$ that we discussed in the previous chapter.
+Indeed, if the geometric construction we have just seen is changed to
+use $t$-of-the-way points instead of midpoints (i.e., if $z_{12}=
+t[z_1,z_2]$ and $z_{23}=t[z_2,z_3]$, etc.), then $z_{1234}$ turns out
+to be precisely $z(t)$ in the formula above.
+
+No matter what four points $(z_1,z_2,z_3,z_4)$ are given, the construction
+on the previous page defines a curved line that runs from $z_1$ to~$z_4$.
+This curve is not always interesting or beautiful; for example, if all
+four of the given points lie on a straight line, the entire ``curve''
+that they define will also be contained in that same line. We obtain
+rather different curves from the same four starting points if we
+number the points differently:
+\displayfig 3d (7.05pc)
+Some discretion is evidently advisable when the $z$'s are chosen. But the
+four-point method is good enough to obtain satisfactory approximations to
+any curve we want, provided that we break the desired curve into short
+enough segments and give four suitable control points for each segment.
+It turns out, in fact, that we can usually get by with only a few segments.
+For example, the four-point method can produce an approximate
+quarter-circle with less than 0.06\% error; it never yields an exact
+circle, but the differences between four such quarter-circles and a true
+circle are imperceptible.
+
+All of the curves that \MF\ draws are based on four points, as just
+described. But it isn't necessary for a user to specify all of those
+points, because the computer is usually able to figure out good values of
+$z_2$ and $z_3$ by itself. Only the endpoints $z_1$ and~$z_4$, through
+which the curve is actually supposed to pass, are usually mentioned
+explicitly in a \MF\ program.
+
+For example, let's return to the six points that were used to introduce the
+ideas of coordinates in Chapter~2. We said `@draw@ $z_1\to z_6$' in that
+chapter, in order to draw a straight line from point~$z_1$ to point~$z_6$.
+In general, if three or more points are listed instead of two, \MF\ will draw a
+^^{..} smooth curve through all the points. For example, the commands
+`@draw@ $z_4\to z_1\to z_2\to z_6$' and `@draw@ $z_5\to z_4\to z_1
+\to z_3\to z_6\to z_5$' will produce the respective results
+\displayfig 3e (7.75pc)
+(Unlabeled points in these diagrams are ^{control points} that \MF\ has
+supplied automatically so that it can use the four-point scheme to draw
+curves between each pair of adjacent points on the specified paths.)
+
+Notice that the curve is not smooth at $z_5$ in the right-hand example,
+because $z_5$~appears at both ends of that particular path. In order to
+get a completely smooth curve that returns to its starting point, you can
+say `@draw@ $z_5\to z_4\to z_1\to z_3\to z_6\to \cycle$' instead:
+\displayfig 3f (7.25pc)
+The word `^{cycle}' at the end of a path refers to the starting point
+of that path.
+\MF\ believes that this ^{bean-like shape}
+is the nicest way to connect the given points in the given cyclic order;
+but of course there are many decent curves that satisfy the specifications,
+and you may have another one in mind. You can obtain finer control
+by giving hints to the machine in various ways. For example, the
+bean curve can be ``pulled tighter'' between $z_1$ and~$z_3$ if you say
+\begindisplay
+@draw@ $z_5\to z_4\to z_1\to\tension1.2\to z_3\to z_6\to \cycle$;
+\enddisplay
+the so-called ^{tension} between points is normally 1, and an increase
+to 1.2 yields
+\displayfig 3g (5.75pc)
+
+\danger An asymmetric effect can be obtained by increasing the tension
+only at point~1 but not at points 3~or~4; the shape
+\displayfig 3h (6.5pc)
+comes from
+%\begindisplay
+%@draw@ $z_5\to z_4\to\tension1\and1.5\to z_1\to
+% \tension1.5\and1\to z_3$\cr
+%\hskip6em$\to z_6\to \cycle$.
+%\enddisplay
+`@draw@ $z_5\to z_4\to\tension1\and1.5\to z_1\to
+ \tension1.5\and1\to z_3\to z_6\to \cycle$'.
+The effect of tension has been achieved in this example by moving two of
+the anonymous control points closer to point~1.
+
+It's possible to control a curve in another way, by telling \MF\ what
+direction to travel at some or all of the points. Such directions are
+given inside curly braces; for example,
+\begindisplay
+@draw@ $z_5\to z_4\{"left"\}\to z_1\to z_3\to z_6\{"left"\}\to\cycle$
+\enddisplay
+says that the curve should be traveling leftward at points 4 and 6. The
+resulting curve is perfectly straight from $z_6$ to~$z_5$ to~$z_4$:
+\displayfig 3i (5.8pc)
+We will see later that `"left"' is an abbreviation for the vector $(-1,0)$,
+which stands for one unit of travel in a leftward direction. Any desired
+direction can be specified by enclosing a vector in $\{\ldots\}$'s; for
+example, the command `@draw@ $z_4\to z_2\{z_3-z_4\}\to z_3$' will draw a
+curve from $z_4$ to~$z_2$ to~$z_3$ such that the tangent direction at
+$z_2$ is parallel to the line $z_4\to z_3$, because $z_3-z_4$ is the
+vector that represents travel from $z_4$ to~$z_3$:
+\displayfig 3j (4.7pc)
+The same result would have been obtained from a command such as `@draw@
+$z_4\to z_2 \{10(z_3-z_4)\}\to z_3$', because the vector $10(z_3-z_4)$ has
+the same direction as $z_3-z_4$. \MF\ ignores the magnitudes of vectors
+when they are simply being used to specify directions.
+
+\exercise What do you think will be the result of
+`@draw@ $z_4\to z_2\{z_4-z_3\}\to z_3$', when points $z_2$, $z_3$,~$z_4$
+are the same as they have been in the last several examples?
+\answer The direction at $z_2$ is parallel to the line $z_4\to z_3$, but
+the vector $z_4-z_3$ specifies a direction towards $z_4$, which is
+$180^\circ$ different from the direction $z_3-z_4$ that was discussed in
+the text. Thus, we have a difficult specification to meet, and \MF\ draws
+a pretzel-shaped curve that loops around in a way that's too ugly to show
+here. The first part of the path, from $z_4$ to $z_2$, is mirror symmetric
+about the line~$z_1\to z_5$ that bisects $z_4\to z_2$, so it starts out in a
+south-by-southwesterly direction; the second part is mirror symmetric about
+the vertical line that bisects $z_2\to z_3$, so when the curve ends at~$z_3$
+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.
+
+\exercise Explain how to get \MF\ to draw the wiggly shape
+\displayfig 3k (5pc)
+in which the curve aims directly at point 2 when it's at point~6, but
+directly away from point~2 when it's at point~4. [{\sl Hint:\/} No
+tension changes are needed; it's merely necessary to specify directions
+at $z_4$ and~$z_6$.]
+\answer @draw@ $z_5\to z_4\{z_4-z_2\}\to z_1\to z_3\to z_6\{z_2-z_6\}
+\to\cycle$.
+
+\MF\ allows you to change the shape of a curve at its endpoints by
+specifying different amounts of ``^{curl}.'' For example, the two commands
+\begindisplay
+@draw@ $z_4\{\curl0\}\to z_2\{z_3-z_4\}\to\{\curl0\}\,z_3$;\cr
+@draw@ $z_4\{\curl2\}\to z_2\{z_3-z_4\}\to\{\curl2\}\,z_3$\cr
+\enddisplay
+give the respective curves
+\displayfig 3l (5pc)
+which can be compared with the one shown earlier when no special curl was
+requested. \ (The specification `$\curl1$' is assumed at an endpoint
+if no explicit curl or direction has been mentioned, just as
+`$\tension1$' is implied between points when no tension has
+been explicitly given.) \ Chapter 14 explains more about~this.
+
+It's possible to get curved lines instead of straight lines even when
+only two points are named, if a direction has been prescribed at one or
+both of the points. For example,
+\begindisplay
+@draw@ $z_4\{z_2-z_4\}\to\{"down"\}\,z_6$\cr
+\enddisplay
+asks \MF\ for a curve that starts traveling towards $z_2$ but finishes
+in a downward direction:
+\displayfig 3m (4pc)
+
+\danger Here are some of the curves that \MF\ draws between two points, when
+it is asked to move outward from the left-hand point at an angle of
+$60^\circ$, and to approach the right-hand point at various angles:
+\displayfig 3aa (2.6cm)
+This diagram was produced by the \MF\ program ^^@for@ ^^@step@ ^^@until@ ^^"cm"
+\begindisplay
+@for@ $d=0$ @step@ 10 @until@ 120:\cr
+\indent @draw@ $(0,0)\{{\rm dir}\,60\}\to\{{\rm dir}\,{-d}\}(6"cm",0)$;
+ @endfor@;\cr
+\enddisplay
+the `^{dir}' function specifies a direction measured in degrees
+counterclockwise from a horizontal rightward line, hence `${\rm dir}\,{-d}$'
+gives a direction that is $d^\circ$ below the horizon. The lowest curves
+in the illustration correspond to small values of $d$, and the highest
+curves correspond to values near $120^\circ$.
+
+\danger A car that drives along the upper paths in the diagram above
+is always turning to the right, but in the lower paths it comes to a
+point where it needs to turn to the left in order to reach its destination
+from the specified direction.
+The place where a path changes its curvature from right to left or
+vice versa is called an ``^{inflection point}.'' \MF\ introduces
+inflection points when it seems better to change the curvature than
+to make a sharp turn; indeed, when $d$ is negative there is no way to
+avoid points of inflection, and the curves for small positive~$d$ ought to
+be similar to those obtained when $d$~has small negative values. The program
+\begindisplay
+@for@ $d=0$ @step@ $-10$ @until@ $-90$:\cr
+\indent @draw@ $(0,0)\{{\rm dir}\,60\}\to\{{\rm dir}\,{-d}\}(6"cm",0)$;
+ @endfor@\cr
+\enddisplay
+shows what \MF\ does when $d$ is negative:
+\displayfig 3bb (2.8cm)
+
+\danger It is sometimes desirable to avoid points of inflection, when $d$ is
+positive, and to require the curve to remain inside the triangle
+determined by its initial and final directions. This can be achieved
+^^{...}
+by using three dots instead of two when you specify a curve: The program
+\begindisplay
+@for@ $d=0$ @step@ 10 @until@ 120:\cr
+\indent @draw@ $(0,0)\{{\rm dir}\,60\}\ldots\{{\rm dir}\,{-d}\}(6"cm",0)$;
+ @endfor@\cr
+\enddisplay
+generates the curves
+\displayfig 3cc (2.6cm)
+which are the same as before except that inflection points do not occur
+for the small values of~$d$. The `$\ldots$' specification keeps the
+curve ``^{bounded}'' inside the triangle that is defined by the endpoints
+and directions; but it has no effect when there is
+no such triangle. More precisely, suppose that the curve goes from $z_0$
+to~$z_1$; if there's a point~$z$ such that the initial direction is from
+$z_0$ to~$z$ and the final direction is from $z$ to~$z_1$, then the curve
+specified by `$\ldots$' will stay entirely within the triangle whose
+corners are $z_0$, $z_1$, and~$z$. But if there's no such triangle
+(e.g., if $d<0$ or $d>120$ in our example program), both `$\ldots$'
+and~`$\to$' will produce the same curves.
+
+In this chapter we have seen lots of different ways to get \MF\ to draw
+curves. And there's one more way, which subsumes all of the others.
+If changes to tensions, curls, directions, and/or boundedness
+aren't enough to produce the sort of curve that a person wants, it's
+always possible as a last resort to specify all four of the points in the
+four-point method. For example, the command
+\begindisplay
+@draw@ $z_4\to\controls z_1\and z_2\to z_6$
+\enddisplay
+will draw the following curve from $z_4$ to $z_6$:^^{controls}
+\displayfig 3n (5pc)
+
+
+\endchapter
+
+And so I think I have omitted nothing
+% Et ainsi ie pense n'auoir rien omis des elemens,
+that is necessary to an understanding of curved lines.
+% qui sont necessaires pour la connoissance des lignes courbes.
+\author REN\'E ^{DESCARTES}, {\sl La G\'eom\'etrie\/} (1637) % p369
+
+\bigskip
+
+Rules or substitutes for the artist's hand must necessarily be inadequate,
+although, when set down by such men as
+^{D\"urer}, ^{Tory}, ^{Da Vinci}, ^{Serlio}, and others,
+they probably do establish canons of proportion and construction
+which afford a sound basis upon which to present new expressions.
+\author FREDERIC W. ^{GOUDY}, {\sl Typologia\/} (1940) % p 138f
+
+\eject
+ \beginchapter Chapter 4. Pens
+
+Our examples so far have involved straight lines or curved lines that look
+as if they were drawn by a felt-tip ^{pen}, where the ^{nib} of that pen
+was perfectly round. A mathematical ``line'' has no thickness, so it's
+invisible; but when we plot circular dots at each point of an infinitely
+thin line, we get a visible line that has constant thickness.
+
+Lines of constant thickness have their uses, but \MF\ also provides
+several other kinds of scrivener's tools, and we shall take a look at some
+of them in this chapter. We'll see not only that the sizes and shapes of
+pen nibs can be varied, but also that characters can be built up in such a
+way that the outlines of each stroke are precisely controlled.
+
+\def\kk{\kern2pt } % kidney-bean kern
+First let's consider the simplest extensions of what we have seen before.
+The letter `{\manual\Aa}' of Chapter~2 and the kidney-^{bean}
+`\kk{\manual\beana}\kk' of Chapter~3 were drawn with circular pen nibs of
+diameter $0.4\pt$, where `pt' stands for a printer's point;\footnote*{$
+1\,{\rm in}=2.54\,{\rm cm}=72.27\pt$ exactly, as explained in
+{\sl The \TeX book}.} $0.4\pt$ is the standard thickness of a ruled line
+`$\,\vcenter{\hrule width 2em}\,$' drawn by \TeX. Such a penpoint can be
+specified by telling \MF\ to
+\begindisplay
+\pickup @pencircle@ ^{scaled} $0.4"pt"$;
+\enddisplay
+\MF\ will use the pen it has most recently picked up ^^@pickup@
+whenever it is asked to `^@draw@' anything. A ^@pencircle@ is a
+circular pen whose diameter is the width of one pixel. Scaling it
+by $0.4"pt"$ will change it to the size that corresponds
+to $0.4\pt$ in the output, because ^"pt" is the number of pixels
+in $1\pt$. If the key points $(z_1,z_2,z_3,z_4,z_5,z_6)$ of Chapters 2 and~3
+have already been defined, the \MF\ commands
+\begindisplay
+\pickup @pencircle@ scaled $0.8"pt"$;\cr
+@draw@ $z_5\to z_4\to z_1\to z_3\to z_6\to \cycle$\cr
+\enddisplay
+will produce a bean shape twice as thick as before: `\kk{\manual\beanb}\kk'
+instead of `\kk{\manual\beana}\kk'.
+
+More interesting effects arise when we use non-circular pen nibs. For example,
+the command
+\begindisplay
+\pickup @pencircle@ ^{xscaled} $0.8"pt"$ ^{yscaled} $0.2"pt"$
+\enddisplay
+picks up a pen whose tip has the shape of an ellipse, $0.8\pt$ wide and
+$0.2\pt$ tall; magnified 10 times, it looks like this:
+`$\,\vcenter{\hbox{\manual\niba}}\,$'.
+\ (The operation of ``xscaling'' multiplies $x$~coordinates by a specified
+amount but leaves $y$~coordinates unchanged, and the operation of
+``yscaling'' is similar.) \ Using such a pen, the `\kk{\manual\beana}\kk'
+becomes `\kk{\manual\beanc}\kk', and `{\manual\Aa}' becomes `{\manual\Ab}'.
+Furthermore,
+\begindisplay
+\pickup @pencircle@ xscaled $0.8"pt"$ yscaled $0.2"pt"$ ^{rotated} 30
+\enddisplay
+takes that ellipse and rotates it $30^\circ$ counterclockwise, obtaining the nib
+`$\vcenter{\hbox{\manual\nibb}}$'; this changes `\kk{\manual\beanc}\kk' into
+`\kk{\manual\beand}\kk' and `{\manual\Ab}' into `{\manual\Ac}'. An
+enlarged view of the bean shape shows more clearly what is going on:
+\displayfig 4a (7pc)
+The right-hand example was obtained by eliminating the clause
+`yscaled~$0.2"pt"$'; this makes the pen almost razor thin, only
+one pixel tall before rotation.
+
+\exercise Describe the pen shapes defined by
+(a)~@pencircle@ xscaled~$0.2"pt"$ yscaled~$0.8"pt"$;
+\ (b)~@pencircle@ scaled~$0.8"pt"$ rotated~30;
+\ (c)~@pencircle@ xscaled~.25 scaled~$0.8"pt"$.
+\answer (a)~An ellipse $0.8\pt$ tall and $0.2\pt$ wide
+(`$\,\vcenter{\hbox{\manual\nibc}}\,$');
+\ (b)~a~circle of diameter $0.8\pt$ (rotation doesn't change a circle!);
+\ (c)~same as~(a).
+
+\exercise We've seen many examples of `^@draw@'
+used with two or more points. What do you think \MF\ will do
+if you ask it to perform the following commands?
+\begindisplay
+@draw@ $z_1$;\ @draw@ $z_2$; \ @draw@ $z_3$; \ @draw@ $z_4$;
+ \ @draw@ $z_5$; \ @draw@ $z_6$.
+\enddisplay
+\answer 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}.
+
+\def\hidecoords(#1,#2){\hbox to 0pt{\hss$\scriptstyle(#1,#2)$\hss}}
+\setbox0=\vtop{\kern 42pt
+ \rightline{\vbox{\hbox to 208\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4b}{208\apspix}{216\apspix}\vbox
+ \kern-3pt
+ \hbox to 208\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+
+\hangindent-125pt \hangafter4
+\indent\strut\vadjust{\box0}%
+Let's turn now to the design of a real letter that has already appeared
+many times in this manual, namely the `\thinspace{\manual ^{T}}\thinspace' of
+`\MF'. All seven of ^^{METAFONT logo} the distinct letters in `\MF' will
+be used to illustrate various ideas as we get into the details of the
+language; we might as well start with~`\thinspace{\manual T}\thinspace',
+because it occurs twice, and (especially) because it's the simplest. An
+enlarged version of this letter is shown at the right of this paragraph,
+including the locations of its four key points $(z_1,z_2,z_3,z_4)$ and its
+^{bounding box}. Typesetting systems like \TeX\ are based on the
+assumption that each character fits in a rectangular ^{box}; we shall
+discuss boxes in detail later, but for now we will be content simply to
+know that such boundaries do exist.\footnote*{Strictly speaking, the
+bounding box doesn't actually have to ``bound'' the black pixels of a
+character; for example, the `\thinspace{\manual q}\thinspace' protrudes
+slightly below the baseline at point~4, and italic letters frequently
+extend rather far to the right of their boxes. However, \TeX\ positions
+all characters by lumping boxes together as if they were pieces of metal
+type that contain all of the ink.} Numbers $h$ and~$w$ ^^"h" ^^"w" will
+have been computed so that the corners of the box are at positions
+$(0,0)$, $(0,h)$, $(w,0)$, and~$(w,h)$ as shown.
+
+\hangindent-125pt
+\hangafter\prevgraf \advance\hangafter by -16 % 4+12 (12 lines for the figure)
+Each of the letters in `\MF' is drawn with a pen whose nib is an unrotated
+ellipse, 90\% as tall as it is wide. In the 10-point size, which is used
+for the main text of this book, the pen is $2/3\pt$ wide, so it has
+been specified by the command
+\begindisplay
+\pickup @pencircle@ scaled $2\over3$"pt" yscaled $9\over10$
+\enddisplay
+or something equivalent to this.
+
+We shall assume that a special value `$o$' has been computed so that the
+bottom of the vertical stroke in `\thinspace{\manual T}\thinspace' should
+descend exactly $o$~pixels below the baseline; ^^"o" this is called the
+amount of ``^{overshoot}.'' Given $h$, $w$, and~$o$, it is a simple matter
+to define the four key points and to draw the
+`\thinspace{\manual T}\thinspace': ^^"top" ^^"lft" ^^"rt" ^^"bot"
+\begindisplay
+$"top"\,"lft"\,z_1=(0,h)$; \quad $"top"\,"rt"\,z_2=(w,h)$;\cr
+$"top"\,z_3=(.5w,h)$; \quad $"bot"\,z_4=(.5w,-o)$;\cr
+@draw@ $z_1\to z_2$; \quad @draw@ $z_3\to z_4$.\cr
+\enddisplay
+
+\danger Sometimes it is easier and/or clearer to define the $x$ and~$y$
+^{coordinates} separately. For example, the key points of
+the~`\thinspace{\manual j}\thinspace'
+could also be specified thus:
+\begindisplay
+$"lft"\,x_1=0$;&$w-x_2=x_1$;&$x_3=x_4=.5w$;\cr
+$"top"\,y_1=h$;&$"bot"\,y_4=-o$;&$y_1=y_2=y_3$.\cr
+\enddisplay
+The equation $w-x_2=x_1$ expresses the fact that $x_2$ is just as far from
+the right edge of the bounding box as $x_1$ is from the left edge.
+
+\danger What exactly does `"top"\!' mean in a \MF\ equation? If the
+currently-picked-up pen extends $l$~pixels to the left of its center,
+$r$~pixels to the right, $t$~pixels upward and $b$~downward, then
+\begindisplay
+$"top"\,z=z+(0,t)$,\kern-1em&$"bot"\,z=z-(0,b)$,\kern-1em&
+$"lft"\,z=z-(l,0)$,\kern-1em&$"rt"\,z=z+(r,0)$,\cr
+\noalign{\vskip\belowdisplayskip
+\vbox{\noindent\strut
+when $z$ is a pair of coordinates. But---as the previous paragraph
+shows, if you study it carefully---we also have
+\strut}\vskip\abovedisplayskip}
+$"top"\,y=y+t$,&$"bot"\,y=y-b$,&
+$"lft"\,x=x-l$,&$"rt"\,x=x+r$,\cr
+\enddisplay
+when $x$ and $y$ are single values instead of coordinate pairs.
+You shouldn't apply `"top"\!' or `"bot"\!' to $x$~coordinates,
+nor `"lft"\!' or `"rt"\!' to $y$~coordinates.
+
+\dangerexercise True or false: $"top"\,"bot"\,z=z$, whenever $z$
+is a pair of coordinates.
+\answer 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".
+
+\setbox0=\vtop{\kern -12pt
+ \rightline{\vbox{\hbox to 288\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4c}{288\apspix}{216\apspix}\vbox
+ \kern-3pt
+ \hbox to 288\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+\begingroup\decreasehsize 165pt
+\dangerexercise An enlarged \strut\vadjust{\box0}%
+picture of \MF's `{\manual h}' shows that it has five key points. Assuming ^^{M}
+that special values $ss$ and~"ygap" have been precomputed and that the equations
+\begindisplay
+$x_1=ss=w-x_5$;\quad$y_3-y_1="ygap"$\cr
+\enddisplay
+have already been given, what further equations and `@draw@' ^^{METAFONT
+logo} commands will complete the specification of this letter? \ (The
+value of~$w$ will be greater for~`\thinspace{\manual h}\thinspace' than it was
+for~`\thinspace{\manual j}\thinspace'; it
+stands for the pixel width of whatever character is currently being drawn.)
+\answer $x_2=x_1$; $x_3={1\over2}[x_2,x_4]$; $x_4=x_5$; $"bot"\,y_1=-o$;
+$"top"\,y_2=h+o$; $y_4=y_2$; $y_5=y_1$; @draw@ $z_1\to z_2$;
+@draw@ $z_2\to z_3$; @draw@ $z_3\to z_4$; @draw@ $z_4\to z_5$.
+We will learn later that the four @draw@ commands can be replaced by
+\begindisplay
+@draw@ $z_1\dashto z_2\dashto z_3\dashto z_4\dashto z_5$;
+\enddisplay
+in fact, this will make \MF\ run slightly faster. ^^{--}
+
+\endgroup % end of the diminished \hsize
+
+\MF's ability to `@draw@' allows it to produce character shapes that are
+satisfactory for many applications, but the shapes are inherently limited
+by the fact that the simulated pen nib must stay the same through an
+entire stroke. Human penpushers are able to get richer effects by
+using different amounts of pressure and/or by rotating the pen as they draw.
+
+We can obtain finer control over the characters we produce if we specify
+their outlines, instead of working only with key points that lie somewhere
+in the middle. In fact, \MF\ works internally with outlines, and the
+computer finds it much easier to fill a region with solid black than to
+figure out what pixels are blackened by a moving pen. There's a `^@fill@'
+command that does region filling; for example, the solid ^{bean} shape
+\displayfig 4d (6.5pc)
+can be obtained from our six famous example points by giving the command
+\begindisplay
+@fill@ $z_5\to z_4\to z_1\to z_3\to z_6\to \cycle$.
+\enddisplay
+The filled region is essentially what would be cut out by an
+infinitely sharp ^{knife} blade if it traced over the given curve while
+cutting a piece of thin film. A @draw@ command needs to add thickness to
+its curve, because the result would otherwise be invisible; but a @fill@
+command adds no thickness.
+
+The curve in a @fill@ command must end with `^{cycle}', because an
+entire region must be filled. It wouldn't make sense to say, e.g.,
+`@fill@ $z_1\to z_2$'. The cycle being filled shouldn't cross itself,
+either; \MF\ would have lots of trouble trying to figure out how to
+obey a command like `@fill@ $z_1\to z_6\to z_3\to z_4\to\cycle$'.
+
+\dangerexercise Chapter 3 discusses the curve $z_5\to z_4\to z_1\to
+z_3\to z_6\to z_5$, which isn't smooth at~$z_5$. Since this curve
+doesn't end with `cycle', you can't use it in a @fill@ command.
+But it does define a closed region. How can \MF\ be instructed
+to fill that region?
+\answer Either say `@fill@ $z_5\to z_4\to z_1\to z_3\to z_6\to z_5\to
+\cycle$', which doubles point~$z_5$ and abandons smoothness there,
+or `@fill@ $z_5\{\curl1\}\to z_4\to z_1\to z_3\to z_6\to
+\{\curl1\}\cycle$'. In the latter case you can omit either one of
+the ^{curl} specifications, but not both.
+
+The black ^{triangle} `{\manual\char'170}' that appears in the statement of
+exercises in this book was drawn with the command
+\begindisplay
+@fill@ $z_1\dashto z_2\dashto z_3\dashto\cycle$
+\enddisplay
+after appropriate corner points $z_1$, $z_2$, and $z_3$ had been specified.
+In this case the outline of the region to be filled was specified in terms
+of the symbol `$\dashto$' instead of `$\to$'; ^^{--}^^{..}
+this is a convention we haven't discussed before. Each `$\dashto$'
+introduces a straight line segment, which is independent of the rest of
+^^{polygonal path}
+the path that it belongs to; thus it is quite different from `$\to$', which
+specifies a possibly curved line segment that connects smoothly with neighboring
+points and lines of a path. In this case `$\dashto$' was used so that the
+triangular region would have straight edges and sharp corners. We might say
+informally that `$\to$' means ``Connect the points with a nice curve,''
+while `$\dashto$' means ``Connect the points with a straight line.''
+
+\setbox0=\vtop{\kern -9pt
+ \rightline{\vbox{\hbox to 180\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4e}{180\apspix}{225\apspix}\vbox
+ \kern-3pt
+ \hbox to 180\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+\begingroup\decreasehsize 111pt
+
+\danger \strut\vadjust{\box0}%
+The corner points $z_1$, $z_2$, and $z_3$ were defined carefully
+so that the triangle would be {\sl^{equilateral}}, i.e., so that all three
+of its sides would have the same length. Since an equilateral triangle
+has $60^\circ$ angles, the following equations did the job:
+\begindisplay
+$x_1=x_2=w-x_3=s$;\cr
+$y_3=.5h$;\cr
+$z_1-z_2=(z_3-z_2)$ ^{rotated} 60.\cr
+\enddisplay
+Here $w$ and $h$ represent the character's width and height, and $s$~is
+the distance of the triangle from the left and right edges of the type.
+
+\endgroup % end of the diminished \hsize
+
+\danger The @fill@ command has a companion called ^@unfill@, which changes
+pixels from black to white inside a given region. For example, the solid
+bean shape on the previous page can be changed to
+\displayfig 4f (6.5pc)
+if we say also `@unfill@ ${1\over4}[z_4,z_2]\to{3\over4}[z_4,z_2]\to\cycle$;
+\ @unfill@ ${1\over4}[z_6,z_2]\to{3\over4}[z_6,z_2]\to\cycle$'.
+This example shows, incidentally, that \MF\ converts a two-point specification
+like `$z_1\to z_2\to\cycle$' into a more-or-less circular path, even though
+two points by themselves define only a straight line.
+
+\dangerexercise Let $z_0$ be the point $(.8[x_1,x_2],.5[y_1,y_4])$,
+and introduce six new points by letting $z'_k=.2[z_k,z_0]$ for $k=1,$ 2,
+\dots,~6. Explain how to obtain the shape
+\displayfig 4g (7.0pc)
+in which the interior region is defined by $z'_1\ldots z'_6$ instead of
+by $z_1\ldots z_6$.
+\answer After the six original points have been defined, say
+\begindisplay
+@fill@ $z_5\to z_4\to z_1\to z_3\to z_6\to\cycle$;\cr
+$z_0=(.8[x_1,x_2],.5[y_1,y_4])$;\cr
+@for@ $k=1$ @upto@ 6: $z_k'=.2[z_k,z_0]$; @endfor@\cr
+@unfill@ $z_5'\to z_4'\to z_1'\to z_3'\to z_6'\to\cycle$.\cr
+\enddisplay
+
+The ability to fill between outlines makes it possible to pretend that we
+have ^{broad-edge pens} that change in direction and pressure as they
+glide over the paper, if we consider the separate paths traced out by the
+pen's left edge and right edge. For example, the stroke
+\displayfig 4h (3.5pc)
+can be regarded as drawn by a pen that starts at the left, inclined
+at a $30^\circ$ angle; as the pen moves, it turns gradually until its
+^^{angle of pen} edge is strictly vertical by the time it reaches the
+right end. The pen motion was horizontal at positions 2 and~3. This stroke
+was actually obtained by the command
+\begindisplay
+@fill@ $z_{1l}\to z_{2l}\{"right"\}\to\{"right"\}\,z_{3l}$\cr
+$\hskip4em\dashto z_{3r}\{"left"\}\to\{"left"\}\,z_{2r}\to z_{1r}$\cr
+$\hskip4em\dashto\cycle$;
+\enddisplay
+i.e., \MF\ was asked to fill a region bounded by a ``left path'' from
+$z_{1l}$ to $z_{2l}$ to $z_{3l}$, followed by a straight line ^^{--}
+to~$z_{3r}$, then a reversed ``right path'' from $z_{3r}$ to $z_{2r}$ to
+$z_{1r}$, and finally a straight line back to the starting point~$z_{1l}$.
+
+Key positions of the ``pen'' are represented in this example by sets of
+three points, like $(z_{1l},z_1,z_{1r})$, which stand for the pen's left edge,
+its midpoint, and its right edge. The midpoint doesn't actually occur in the
+specification of the outline, but we'll see examples of its usefulness.
+The relationships between such triples of points are established by a
+`^"penpos"' command, which states the breadth of the pen and its angle of
+inclination at a particular position. For example, positions 1, 2, and~3
+in the stroke above were established by saying
+\begindisplay
+$\penpos1(1.2"pt",30)$;&
+$\penpos2(1.0"pt",45)$;&
+$\penpos3(0.8"pt",90)$;\cr
+\enddisplay
+this made the pen $1.2\pt$ broad and tipped $30^\circ$ with respect to
+the horizontal at position~1, etc. In general the idea is to specify
+`$\penpos k(b,d)$',
+where $k$ is the position number or position name, $b$ is the breadth (in
+pixels), and $d$~is the angle (in degrees). Pen angles are measured
+counterclockwise from the horizontal. Thus, an angle of~0 makes the right
+edge of the pen exactly $b$~pixels to the right of the left edge; an angle
+of~90 makes the right pen edge exactly $b$~pixels above the left; an angle
+of~$-90$ makes it exactly $b$~pixels below. An angle of 45 makes the right
+edge $b/{\sqrt2}$ pixels above and $b/{\sqrt2}$ pixels to the right of the
+left edge; an angle of~$-45$ makes it $b/{\sqrt2}$ pixels below and
+$b/{\sqrt2}$ to the right. When the pen angle is between $90^\circ$ and
+$180^\circ$, the ``right'' edge actually lies to the left of the ``left''
+edge. In terms of ^{compass directions} on a conventional map, an angle
+of~$0^\circ$ points due East, while $90^\circ$ points North and $-90^\circ$
+points South. The angle corresponding to Southwest is $-135^\circ$,
+also known as $+225^\circ$.
+
+\exercise What angle corresponds to the direction North-Northwest?
+\answer ${1\over2}\bigl["North",{1\over2}["North","West"]\bigr]=
+{1\over2}\bigl[90,{1\over2}[90,180]\bigr]={1\over2}[90,135]=112.5$.
+
+\begingroup \decreasehsize 9pc
+\exercise \xdef\circlex{4.\number\exno}%
+\rightfig 4i (7pc x 7pc) ^20pt
+What are the pen angles at positions 1, 2, 3, and~4 in
+the circular shape shown here? [{\sl Hint:\/} Each angle is a multiple
+of $30^\circ$. Note that $z_{3r}$ lies to the left of $z_{3l}$.]
+\answer $30^\circ$, $60^\circ$, $210^\circ$, and $240^\circ$. Since it's
+possible to add or subtract $360^\circ$ without changing the meaning,
+the answers $-330^\circ$, $-300^\circ$, $-150^\circ$, and $-120^\circ$
+are also correct.
+
+\exercise What are the coordinates of $z_{1l}$ and $z_{1r}$ after the
+command `$\penpos1(10,-90)$', if $z_1=(25,25)$?
+\answer $z_{1l}=(25,30)$, $z_{1r}=(25,20)$.
+
+\endgroup % end of the diminished \hsize
+\danger The statement `$\penpos k(b,d)$' is simply an abbreviation for
+two equations, `$z_k={1\over2}[z_{kl},z_{kr}]$' and
+`$z_{kr}=z_{kl}+(b,0)$ ^{rotated}~$d\,$'. You might want to use other
+equations to define the relationship between $z_{kl}$, $z_k$, and
+$z_{kr}$, instead of giving a "penpos" command, if an alternative
+formulation turns out to be more convenient.
+
+After `"penpos"' has specified the relations between three points, we still
+don't know exactly where they are; we only know their positions relative
+to each other. Another equation or two is needed in order to fix the
+horizontal and vertical locations of each triple. For example, the three
+"penpos" commands that led to the pen stroke on the previous page were
+accompanied by the equations
+\begindisplay
+$z_1=(0,2"pt")$;&$z_2=(4"pt",0)$;&$x_3=9"pt"$;&$y_{3l}=y_{2r}$;
+\enddisplay
+these made the information complete. There should be one $x$~equation and
+one $y$~equation for each position; or you can use a $z$~equation, which
+defines both $x$ and~$y$ simultaneously.
+
+It's a nuisance to write long-winded @fill@ commands when broad-edge
+pens are being simulated in this way, so \MF\ provides a convenient
+abbreviation: You can write simply
+\begindisplay
+^@penstroke@ $z_{1e}\to z_{2e}\{"right"\}\to\{"right"\}z_{3e}$
+\enddisplay
+instead of the command `\thinspace@fill@ $z_{1l}\to
+z_{2l}\{"right"\}\to\{"right"\}\,z_{3l} \dashto
+z_{3r}\{"left"\}\to\{"left"\}\,z_{2r}\to z_{1r}\dashto\cycle$' that was
+stated earlier. The letter `$e$' ^^"e" stands for the pen's edge. A @penstroke@
+command fills the region `$p.l\dashto \reverse p.r\dashto\cycle$', where
+$p.l$ and~$p.r$ are the left and right paths formed by changing each~`$e$'
+into `$l$' or~`$r$', respectively.
+
+\danger The @penstroke@ abbreviation can be used to draw cyclic paths
+as well as ordinary ones. For example, the circle in exercise \circlex\
+was created by saying simply `@penstroke@ $z_{1e}\to z_{2e}\to z_{3e}\to
+z_{4e}\to\cycle$'. This type of penstroke essentially expands into
+\begindisplay
+@fill@ $z_{1r}\to z_{2r}\to z_{3r}\to z_{4r}\to\cycle$;\cr
+@unfill@ $z_{1l}\to z_{2l}\to z_{3l}\to z_{4l}\to\cycle$;\cr
+\enddisplay
+or the operations `@fill@' and `@unfill@' are reversed, if points
+$(z_{1r},z_{2r}, z_{3r},z_{4r})$ are on the inside and
+$(z_{1l},z_{2l},z_{3l},z_{4l})$ are on the outside.
+
+\dangerexercise The circle of exercise \circlex\ was actually drawn with
+a slightly more complicated @penstroke@ command than just claimed: The
+edges of the curve were forced to be vertical at
+positions 1 and~3, horizontal at 2 and~4. How did the author do this?
+\answer He said `@penstroke@
+$z_{1e}\{"up"\}\to z_{2e}\{"left"\}\to z_{3e}\{"down"\}
+ \to z_{4e}\{"right"\}\to\cycle$'.
+
+\setbox0=\vtop{\kern 21pt
+ \rightline{\vbox{\hbox to 126\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern6pt
+ \figbox{4j}{126\apspix}{252\apspix}\vbox
+ \kern-3pt
+ \hbox to 126\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\qquad}}
+\dp0=0pt
+
+\hangindent-100pt \hangafter2
+\indent\strut\vadjust{\box0}%
+Here's an example of how this new sort of pen can be used to draw a
+sans-serif letter `{\manual\IOI}'. As usual, we assume ^^{I}
+that two variables, $h$ and~$w$, have been set up to give the height and
+width of the character in pixels. We shall also assume that there's a
+"stem" parameter, which specifies the nominal pen breadth. The breadth
+decreases to .9"stem" in the middle of the stroke, and the
+pen angle changes from $15^\circ$ to~$10^\circ$:
+\begindisplay
+$\penpos1("stem",15)$; \ $\penpos2(.9"stem",12)$;\cr
+$\penpos3("stem",10)$; \ $x_1=x_2=x_3=.5w$;\cr
+$y_1=h$; \ $y_2=.55h$; \ $y_3=0$;\cr
+$x_{2l}:={1\over6}[x_{2l},x_2]$;\cr
+@penstroke@ $z_{1e}\to z_{2e}\{down\}\to z_{3e}$.\cr
+\enddisplay
+Setting $x_1=x_2=x_3=.5w$ centers the stroke; setting $y_1=h$ and $y_3=0$
+makes it sit in the type box, protruding just slightly at the top and bottom.
+
+The second-last line of this program is something that we haven't seen
+before: It resets $x_{2l}$ to a value 1/6 of the way towards the center
+of the pen, thereby making the stroke ^{taper} a bit at the left.
+The `$:=$' operation is called an {\sl^{assignment}\/}; we shall
+^^{:=} study the differences between `$:=$' and~`$=$' in Chapter~10.
+
+\danger It is important to note that these simulated pens
+have a serious limitation compared to the way a real calligrapher's pen
+works: The left and right edges of a "penpos"-made pen must never cross,
+hence it is necessary to turn the pen when going around a curve.
+Consider, for example, the following two curves:
+\displayfig 4k (6pc)
+The left-hand circle was drawn with a broad-edge pen of fixed breadth,
+held at a fixed angle; consequently the left edge of the pen was responsible
+for the outer boundary on the left, but the inner boundary on the right.
+\ (This curve was produced by saying `\pickup @pencircle@ xscaled~0.8"pt"
+rotated~25; @draw@ $z_1\to z_2\to\cycle$'.) \ The right-hand shape
+was produced by `$\penpos1(0.8"pt",25)$; $\penpos2(0.8"pt",25)$;
+@penstroke@ $z_{1e}\to z_{2e}\to\cycle$'; important chunks of the shape
+are missing at the crossover points, because they don't lie on either of
+the circles $z_{1l}\to z_{2l}\to\cycle$ or $z_{1r}\to z_{2r}\to\cycle$.
+
+\danger To conclude this chapter we shall improve the ^{hex} character
+{\manual\hexb} of Chapter~2, which is too dark in the middle because it has
+been drawn with a pen of uniform thickness. The main trouble with unvarying
+pens is that they tend to produce black blotches where two strokes meet,
+unless the pens are comparatively thin or unless the strokes are nearly
+perpendicular. We want to thin out the lines at the center just enough
+to cure the darkness problem, without destroying the illusion that the lines
+still seem (at first glance) to have uniform thickness.
+
+\setbox0=\vtop{\kern 69pt
+ \rightline{\vbox{\hbox to 200\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4l}{200\apspix}{100\apspix}\vbox
+ \kern-3pt
+ \hbox to 200\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+
+\danger \strut\vadjust{\box0}%
+It isn't difficult to produce `\thinspace
+{\manual\hexe\hexe\hexe\hexe\hexe\hexe\hexe\hexe\hexe\hexe}\thinspace'
+instead of `\thinspace
+{\manual\hexb\hexb\hexb\hexb\hexb\hexb\hexb\hexb\hexb\hexb}\thinspace'
+when we work with dynamic pens:
+\begindisplay
+\pickup @pencircle@ scaled $b$;\cr
+$"top"\,z_1=(0,h)$; \ $"top"\,z_2=(.5w,h)$; \ $"top"\,z_3=(w,h)$;\cr
+$"bot"\,z_4=(0,0)$; \ $"bot"\,z_5=(.5w,0)$; \ $"bot"\,z_6=(w,0)$; \
+ @draw@ $z_2\to z_5$;\cr
+$z_{1'}=.25[z_1,z_6]$; \ $z_{6'}=.75[z_1,z_6]$; \
+$z_{3'}=.25[z_3,z_4]$; \ $z_{4'}=.75[z_3,z_4]$;\cr
+$"theta"_1:=\angle(z_6-z_1)+90$;\cr
+$"theta"_3:=\angle(z_4-z_3)+90$;\cr
+$\penpos{1'}(b,"theta"_1)$; \ $\penpos{6'}(b,"theta"_1)$;\cr
+$\penpos{3'}(b,"theta"_3)$; \ $\penpos{4'}(b,"theta"_3)$;\cr
+$\penpos7(.6b,"theta"_1)$; \ $\penpos8(.6b,"theta"_3)$;\cr
+$z_7=z_8=.5[z_1,z_6]$;\cr
+@draw@ $z_1\to z_{1'}$; \ @draw@ $z_{6'}\to z_6$;\cr
+@draw@ $z_3\to z_{3'}$; \ @draw@ $z_{4'}\to z_4$;\cr
+@penstroke@ $z_{1'e}\{z_{6'}-z_{1'}\}\to z_{7e}\to\{z_{6'}-z_{1'}\}z_{6'e}$;\cr
+@penstroke@ $z_{3'e}\{z_{4'}-z_{3'}\}\to z_{8e}\to\{z_{4'}-z_{3'}\}z_{4'e}$.\cr
+\enddisplay
+Here $b$ is the diameter of the pen at the terminal points;
+`^{angle}' computes the direction angle of a given vector.
+Adding $90^\circ$ to a direction angle gives a ^{perpendicular}
+direction (see the definitions of $"theta"_1$ and~$"theta"_3$).
+It isn't necessary to take anything off of the vertical stroke $z_2\to z_5$,
+because the two diagonal strokes fill more than the width of the vertical
+stroke at the point where they intersect.
+
+\setbox0=\vtop{\kern -30pt
+ \rightline{\vbox{\hbox to 200\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern6pt
+% \figbox{4m}{200\apspix}{100\apspix}\vbox
+ \figbox{4m}{200\apspix}{105\apspix}\vbox
+ \kern0pt
+ \hbox to 200\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+
+\begingroup \decreasehsize 125pt
+\dangerexercise \strut\vadjust{\box0}%
+Modify the hex character so that its ends are cut
+sharply and confined to the bounding box, as shown.
+\answer We use angles ^{perpendicular} to $(w,h)$ and $(w,-h)$ at the
+diagonal endpoints:
+\begindisplay
+$x_{1l}=x_{4l}=0$;\cr
+$x_2=x_5=.5w$;\cr
+$x_{3r}=x_{6r}=w$;\cr
+$y_{1r}=y_2=y_{3l}=h$;\cr
+$y_{4r}=y_5=y_{6l}=0$;\cr
+$z_{1'}=.25[z_1,z_6]$; \ $z_{6'}=.75[z_1,z_6]$;\cr
+$theta_1:=\angle(w,-h)+90$;\cr
+$\penpos1(b,theta_1)$; \ $\penpos6(b,theta_1)$;\cr
+$z_7=.5[z_1,z_6]$; \ $\penpos7(.6b,theta_1)$;\cr
+$\penpos{1'}(b,theta_1)$; \ $\penpos{6'}(b,theta_1)$;\cr
+@penstroke@ $z_{1e}\to z_{1'e}\{z_{6'}-z_{1'}\}\to z_{7e}\to
+\{z_{6'}-z_{1'}\}z_{6'e}\to z_{6e}$;\cr
+$z_{3'}=.25[z_3,z_4]$; \ $z_{4'}=.75[z_3,z_4]$;\cr
+$theta_3:=\angle(-w,-h)+90$;\cr
+$\penpos3(b,theta_3)$; \ $\penpos4(b,theta_3)$;\cr
+$z_8=.5[z_1,z_6]$; \ $\penpos8(.6b,theta_3)$;\cr
+$\penpos{3'}(b,theta_3)$; \ $\penpos{4'}(b,theta_3)$;\cr
+@penstroke@ $z_{3e}\to z_{3'e}\{z_{4'}-z_{3'}\}\to z_{8e}\to
+\{z_{4'}-z_{3'}\}z_{4'e}\to z_{4e}$;\cr
+$\penpos2(b,0)$; \ $\penpos5(b,0)$; \ @penstroke@ $z_{2e}\to z_{5e}$.\cr
+\enddisplay
+
+\endgroup % end of the diminished \hsize
+
+\endchapter
+
+It is very important that the nib be cut ``sharp,''
+and as often as its edge wears blunt it must be resharpened.
+It is impossible to make ``clean cut'' strokes with a blunt pen.
+\author EDWARD ^{JOHNSTON}, {\sl Writing \& Illuminating, %
+ \& Lettering\/} (1906)
+
+\bigskip
+
+I might compare the high-speed computing machine
+to a remarkably large and awkward pencil
+which takes a long time to sharpen and
+cannot be held in the fingers in the usual manner so that it
+gives the illusion of responding to my thoughts,
+but is fitted with a rather delicate engine
+and will write like a mad thing
+provided I am willing to let it dictate pretty much
+the subjects on which it writes.
+\author R. H. ^{BRUCK}, {\sl Computational Aspects of Certain
+ Combinatorial Problems\/} (1956) % AMS Symp Appl Math 6, p31
+
+\eject
+ \beginchapter Chapter 5. Running\\\MF
+
+It's high time now for you to stop reading and to start playing with the
+computer, since \MF\ is an interactive system that is best learned by
+trial and error. \ (In fact, one of the nicest things about computer graphics
+is that errors are often more interesting and more fun than ``successes.'')
+
+You probably will have to ask somebody how to deal with the idiosyncrasies
+of your particular version of the system, even though \MF\ itself works in
+essentially the same way on all machines; different computer terminals and
+different hardcopy devices make it necessary to have somewhat different
+interfaces. In~this chapter we shall assume that you have a computer
+terminal with a reasonably high-resolution graphics display; that you have
+access to a (possibly low-resolution) output device; and that you can
+rather easily get that device to work with newly created fonts.
+
+OK, are you ready to run the program? First you need to log in, of course;
+then start \MF\!, which is usually called ^|mf| for short. Once you've figured
+out how to do it, you'll be welcomed by a message something like
+$$\def\\{{\rm\ }} % take a wee bit off of the \tt spaces
+\vtop{\line{\indent \tt
+This\\is\\METAFONT,\\Version\\2.0\\(preloaded\\base=plain 89.11.8)}
+\leftline{\indent \tt **}}$$
+The `^|**|' is \MF's way of asking you for an input file name.
+% Incidentally, 89.11.8 was Hermann's 71st birthday.
+
+Now type `|\relax|'---that's ^{backslash}, |r|, |e|, |l|, |a|, |x|---and
+hit ^\<return> (or~whatever stands for ``end-of-line'' on your keyboard).
+\MF\ is all geared up for action, ready to make a big font; but you're
+saying that it's all right to take things easy, since this is going to
+be a real simple run. The backslash means that \MF\ should not read a file,
+it should get instructions from the keyboard; the `^|relax|' means
+``do nothing.''
+
+The machine will respond by typing a single asterisk: `^|*|'. This means
+it's ready to accept instructions (not the name of a file). Type the
+following, just for fun:
+\begintt
+drawdot (35,70); showit;
+\endtt
+and \<return>---don't forget to type the semicolons along with the other
+stuff. A more-or-less circular dot should now appear on your screen! And
+you should also be prompted with another asterisk.
+Type
+\begintt
+drawdot (65,70); showit;
+\endtt
+and \<return>, to get another dot. \ (Henceforth we won't keep mentioning
+the necessity of \<return>ing after each line of keyboard input.) \ Finally,
+type
+\begintt
+draw (20,40)..(50,25)..(80,40); showit; shipit; end.
+\endtt
+This draws a curve through three given points, displays the result,
+^^|showit| ^^|shipit| ^^|end|
+ships it to an output file, and stops. \MF\ should respond with `|[0]|',
+meaning that it has shipped out a character whose number is zero, in the
+``font'' just made; and it should also tell you that it has created
+an output file called `|mfput.2602gf|'. \ (The name ^|mfput| is used when
+you haven't specified any better name in response to the ^|**| at the
+beginning. The suffix |2602|^|gf| stands for ``^{generic font} at
+2602 pixels per inch.'' The data in |mfput.2602gf| can be converted into
+fonts suitable for a wide assortment of typographical output devices;
+since it doesn't match the font file conventions of any name-brand
+manufacturer, we call it generic.)
+
+This particular file won't make a very interesting font,
+because it contains only one character, and because it probably doesn't
+have the correct resolution for your output device. However, it does
+have the right resolution for hardcopy proofs of characters; your next
+step should therefore be to convert the data of |mfput.2602gf| into a
+picture, suitable for framing. There should be a program called
+^|GFtoDVI| on your computer. Apply it to |mfput.2602gf|, thereby
+obtaining a file called |mfput.dvi| ^^|dvi| that can be printed.
+Your friendly local computer hackers will tell you how to run
+|GFtoDVI| and how to print |mfput.dvi|; then you'll have a marvelous
+souvenir of your very first encounter with \MF\!. \looseness=-1
+
+\smallskip
+Once you have made a complete test run as just described, you will
+know how to get through the whole cycle, so you'll be ready to tackle
+a more complex project. Our next experiment will therefore be
+to work from a file, instead of typing the input online.
+
+Use your favorite text editor to create a file called |io.mf| that
+contains the following 23 lines of text (no more, no less):
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ }&#\hfil\cr
+1&|mode_setup;|\cr\noalign{^^@mode\_setup@}
+2&| em#:=10pt#; cap#:=7pt#;|\cr
+3&| thin#:=1/3pt#; thick#:=5/6pt#;|\cr
+4&| o#:=1/5pt#;|\cr
+5&|define_pixels(em,cap);|\cr
+6&|define_blacker_pixels(thin,thick);|\cr
+7&|define_corrected_pixels(o);|\cr
+8&| curve_sidebar=round 1/18em;|\cr
+9&|beginchar("O",0.8em#,cap#,0); "The letter O";|\cr
+10&| penpos1(thick,10); penpos2(.1[thin,thick],90-10);|\cr
+11&| penpos3(thick,180+10); penpos4(thin,270-10);|\cr
+12&| x1l=w-x3l=curve_sidebar; x2=x4=.5w;|\cr
+13&| y1=.49h; y2l=-o; y3=.51h; y4l=h+o;|\cr
+14&| penstroke z1e{down}..z2e{right}|\cr
+15&| ..z3e{up}..z4e{left}..cycle;|\cr
+16&| penlabels(1,2,3,4); endchar;|\cr
+17&|def test_I(expr code,trial_stem,trial_width) =|\cr
+18&| stem#:=trial_stem*pt#; define_blacker_pixels(stem);|\cr
+19&| beginchar(code,trial_width*em#,cap#,0); "The letter I";|\cr
+20&| penpos1(stem,15); penpos2(.9stem,12); penpos3(stem,10);|\cr
+21&| x1=x2=x3=.5w; y1=h; y2=.55h; y3=0; x2l:=1/6[x2l,x2];|\cr
+22&| penstroke z1e..z2e{down}..z3e;|\cr
+23&| penlabels(1,2,3); endchar; enddef;|\cr}$$
+(But don't type the numbers at the left of these lines; they're
+only for reference.)
+
+This example file is dedicated to ^{Io}, the Greek goddess of input
+and output. It's a trifle long, but you'll be able to get worthwhile
+experience by typing it; so go ahead and type it now. For your own
+good. And think about what you're typing, as you go; the example
+introduces several important features of \MF\ that you can learn
+as you're creating the file.
+
+Here's a brief explanation of what you've just typed: Line~1 contains a
+command that usually appears near the beginning of every \MF\ file;
+it tells the computer to get ready to work in whatever ``mode'' is
+currently desired. \ (A file like |io.mf| can be used to generate
+proofsheets as well as to make fonts for a variety of devices at a
+variety of magnifications, and `@mode\_setup@' is what adapts \MF\
+to the task at hand.) \ Lines 2--8 define parameters that will be used
+to draw the letters in the font. Lines 9--16 give a complete program
+for the letter `O'; and lines 17--23 give a program that will draw
+the letter~`I' in a number of related ways.
+
+It all looks pretty frightening at first glance, but a closer look
+shows that Io is not so mysterious once we penetrate her disguise.
+Let's spend a few minutes studying the file in more detail.
+
+Lines 2--4 define dimensions that are independent of the mode; the `|#|'
+^^{sharpsign} signs are meant to imply ``sharp'' or ``true'' ^{units of
+measure}, which remain the same whether we are making a font at high or
+low resolution. For example, one `|pt#|' is a true printer's point, one
+72.27th of an inch. This is quite different from the `^"pt"' we have
+discussed in previous chapters, because `"pt"' is the number of pixels
+that happen to correspond to a printer's point when the current resolution
+is taken into account. The value of `|pt#|' never changes, but
+@mode\_setup@ establishes the appropriate value of `"pt"'.
+
+The ^{assignments} `|em#:=10pt#|' and `|cap#:=7pt#|' in line~2 mean that
+the Io font has two parameters, called "em" and "cap", whose mode-independent
+values are 10 and~7 points, respectively. The statement ^^@define\_pixels@
+`|define_pixels(em,cap)|' on line~5 converts these values into pixel
+units. For example, if we are working at the comparatively low resolution
+of 3~pixels per~pt, the values of "em" and "cap" after the computer has
+performed the instructions on line~5 will be $"em"=30$ and $"cap"=21$.
+\ (We will see later that the widths of characters in this font are
+expressed in terms of ems, and that "cap" is the height of the capital
+letters. A change to line~2 will therefore affect the widths and/or heights
+of all the letters.)
+
+Similarly, the Io font has parameters called "thin" and "thick", defined
+on line~3 and converted to pixel units in line~6. These are used to control
+the breadth of a simulated pen when it draws the letter~O. Experience has
+shown that \MF\ produces better results on certain output devices if
+pixel-oriented pens are made slightly broader than the true dimensions would
+imply, because black pixels sometimes tend to ``burn off'' in the process
+of printing. The command on line~6, `|define_blacker_pixels|',
+^^@define\_blacker\_pixels@ adds a correction based on the device for which
+the font is being prepared. For example, if the resolution is 3~pixels
+per point, the value of "thin" when converted from true units to pixels
+by @define\_pixels@ would be~1, but @define\_blacker\_pixels@ might set
+"thin" to a value closer to~2.
+
+The `|o|' parameter ^^"o" on line 4 represents the amount by which curves will
+^{overshoot} their boundaries. This is converted to pixels in yet another
+way on line~7, so as to avoid yet another problem that arises in low-resolution
+printing. The author apologizes for letting such real-world considerations
+intrude into a textbook example; let's not get bogged down in fussy details
+now, since these refinements will be explained in Chapter~11 after we have
+mastered the basics.
+
+For now, the important point is simply that a typeface
+design usually involves parameters that represent physical lengths. The
+true, ``sharped'' forms of these parameters need to be converted to
+``unsharped'' pixel-oriented quantities, and best results are obtained when
+such conversions are done carefully. After \MF\ has obeyed line~7 of the
+example, the pixel-oriented parameters "em", "cap", "thin", "thick",
+and~"o" are ready to be used as we draw letters of the font.
+
+Line 8 defines a quantity called "curve\_sidebar" ^^{sidebar} that will
+measure the distance of the left and right edges of the `O' from the
+bounding box. It is computed by ^{rounding} ${1\over18}"em"$ to the nearest
+integer number of pixels. For example, if $"em"=30$ then ${30\over18}=
+{5\over3}$ yields the rounded value $"curve\_sidebar"=2$; there will be
+two all-white columns of pixels at the left and right of the `O',
+when we work at this particular resolution.
+
+Before we go any further, we ought to discuss the strange collection
+of words and pseudo-words in the file |io.mf|. Which of the terms
+`|mode_setup|', `|em|', `|curve_sidebar|' and so forth are part of
+the \MF\ language, and which of them are made up specifically for
+the Io example? Well, it turns out that almost {\sl nothing\/} in this
+example is written in the pure \MF\ language that the computer understands!
+\MF\ is really a low-level language that has been designed to allow easy
+adaptation to many different styles of programming, and |io.mf|
+illustrates just one of countless ways to use it. Most of the terms
+in |io.mf| are conventions of ``^{plain} \MF\!,'' which is a collection
+of subroutines found in Appendix~B\null. \MF's primitive capabilities are
+not meant to be used directly, because that would force a particular style
+on all users. A ``base file'' is generally loaded into the computer
+at the beginning of a run, so that a standard set of conventions is
+readily available. \MF's welcoming message, quoted at the
+beginning of this chapter, says `|preloaded| |base=plain|'; it
+means that the primitive \MF\ language has been extended to include the
+features of the plain base file. This book is not only about \MF; it also
+explains how to use the conventions of \MF's plain base. Similarly, {\sl
+The \TeX book\/} describes a standard extension of \TeX\ called ``plain
+\TeX\ format''; ^^{TeX} the ``plain'' extensions of \TeX\ and \MF\ are
+completely analogous to each other.
+
+The notions of @mode\_setup@, @define\_pixels@, @beginchar@, "penpos",
+and many other things found in |io.mf| are aspects
+of plain \MF\ but they are not hardwired into \MF\ itself. Appendix~B
+defines all of these things, as well as the relations between ``sharped''
+and ``unsharped'' variables. Even the fact that $z_1$ stands for
+$(x_1,y_1)$ is defined in Appendix~B\null; \MF\ does not have this built~in.
+You are free to define even fancier bases as you gain more experience,
+but the plain base is a suitable starting point for a novice.
+
+\danger If you have important applications that make use of a different
+base file, it's possible to create a version of \MF\ that has any desired
+base preloaded. Such a program is generally called by a special name,
+since the nickname `^|mf|' is reserved for the version that includes the
+standard plain base assumed in this book. For example, the author has made
+a special version called `^|cmmf|' just for the ^{Computer Modern} typefaces
+he has been developing, so that the Computer Modern base file does not
+have to be loaded each time he makes a new experiment.
+
+\danger There's a simple way to change the base file from the one that has
+been preloaded: If the first character you type in response to `^|**|' is
+an ^{ampersand} (\thinspace`|&|'\thinspace), \MF\ will replace its memory
+with a specified base file before proceeding. If, for example, there is a
+base file called `|cm.base|' but not a special program called `|cmmf|',
+you can substitute the Computer Modern base for the plain base in |mf| by
+typing `|&cm|' at the very beginning of a run. If you are working with a
+program that doesn't have the plain base preloaded, the first experiment
+in this chapter won't work as described, but you can do it by starting
+with `|&plain \relax|' instead of just `|\relax|'. These conventions are
+exactly the same as those of \TeX.
+
+Our Ionian example uses the following words that are not part of plain
+\MF: "em", "cap", "thin", "thick", "o", "curve\_sidebar", "test\_I", "code",
+"trial\_stem", "trial\_width", and "stem". If you change these to some other
+words or symbols---for example, if you replace `|thin|' and `|thick|' by
+`|t|' and `|T|' respectively, in lines 3, 6, 10, and~11---the results will
+be unchanged, unless your substitutions just happen to clash with something
+that plain \MF\ has already pre\"empted. In general, the best policy is to
+choose descriptive terms for the quantities in your programs, since they
+are not likely to conflict with reserved pseudo-words like "penpos" and
+@endchar@.
+
+We have already noted that lines 9--16 of the file represent a program
+for the letter `O'. The main part of this program, in lines 10--15,
+uses the ideas of Chapter~4, but we haven't seen the stuff in lines 9
+and~16 before. Plain \MF\ makes it convenient to define letters by starting
+each one with
+\begindisplay
+$@beginchar@\kern1pt($\<code>, \<width>, \<height>, \<depth>);^^@beginchar@
+\enddisplay
+here \<code> is either a quoted single character like |"O"| or a number that
+represents the character's position in the final font. The other three
+quantities \<width>, \<height>, and \<depth> say how big the ^{bounding box}
+is, so that typesetting systems like \TeX\ will be able to use the character.
+These three dimensions must be given in device-independent units, i.e.,
+in ``^{sharped}'' form.
+
+\exercise What are the height and width of the bounding box described
+in the @beginchar@ command on line~9 of |io.mf|, given the parameter
+values defined on line~2? Give your answer in terms of printer's points.
+\answer 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$.)
+
+Each @beginchar@ operation assigns values to special variables called
+$w$, $h$, and~$d$, ^^"w" ^^"h" ^^"d" which represent the respective
+width, height, and depth of the current character's bounding box,
+^{rounded} to the nearest integer number of pixels. Our example file
+uses $w$ and~$h$ to help establish the locations of several pen positions
+(see lines 12, 13, and~21 of |io.mf|).
+
+\exercise Continuing the previous exercise, what will be the values of
+$w$ and~$h$ if there are exactly 3.6 pixels per point?
+\answer $8\times3.6=28.8$ rounds to the value $w=29$; similarly, $h=25$.
+\ (And $d=0$.)
+
+There's a quoted phrase |"The| |letter| |O"| at the end of line~9; this is
+simply a title that will be used in printouts.
+
+The `|endchar|' ^^@endchar@ on line 16 finishes the character that was
+begun on line~9, by writing it to an output file and possibly displaying
+it on your screen. We will want
+to see the positions of the control points $z_1$, $z_2$,
+$z_3$, and~$z_4$ that are used in its design, together with the auxiliary
+points $(z_{1l},z_{2l},z_{3l},z_{4l})$ and $(z_{1r},z_{2r},z_{3r},z_{4r})$
+that come with the "penpos" conventions; the statement `|penlabels(1,2,3,4)|'
+^^"penlabels" takes care of labeling these points on the proofsheets.
+
+So much for the letter O. Lines 17--23 are analogous to what we've seen
+before, except that there's a new wrinkle: They contain a little program
+^^@def@ enclosed by `|def...enddef|', which means that a
+{\sl^{subroutine}\/} is being defined. In other words, those lines set up
+a whole bunch of \MF\ commands that we will want to execute several times
+with minor variations. The subroutine is called "test\_I" and it has three
+parameters called "code", "trial\_stem", and "trial\_width" (see line~17).
+The idea is that we'll want to draw several different versions of an `I',
+having different stem widths and character widths; but we want to type the
+program only once. Line~18 defines "stem"\0 and "stem", given a value of
+"trial\_stem"; and lines 19--23 complete the program for the letter~I
+(copying it from Chapter~4).
+
+\smallskip
+Oops---we've been talking much too long about |io.mf|. It's time to stop
+rambling and to begin Experiment~2 in earnest, because it will be much
+more fun to see what the computer actually does with that file.
+
+Are you brave enough to try Experiment 2? Sure.
+Get \MF\ going again, but this time when the machine says `^|**|' you should
+say `|io|', since that's the name of the file you have prepared so
+laboriously. \ (The file could also be specified by giving its full name
+`|io.mf|', but \MF\ automatically adds `|.mf|' ^^|mf| ^^{file names} when
+no suffix has been given explicitly.)
+
+If all goes well, the computer should now flash its lights a bit
+and---presto---a big `{\manual\IOO}' should be drawn on your screen.
+But if your luck is as good as the author's, something will probably go wrong
+the first time, most likely because of a typographic error in the file.
+A \MF\ program contains lots of data with comparatively little redundancy,
+so a single error can make a drastic change in the meaning. Check that
+you've typed everything perfectly: Be sure to notice the difference between
+the letter~`|l|' and the numeral~`|1|' (especially in line~12, where it
+says `|x1l|', not `|x11|' or~`|xll|'); be sure to distinguish between
+the letter~`|O|' and the numeral~`|0|' (especially in line~9); be sure to
+type the ``underline'' characters in words like `|mode_setup|'. We'll see
+later that \MF\ can recover gracefully from most errors, but your job for
+now is to make sure that you've got |io.mf| correct.
+
+Once you have a working file, the computer will draw you an `{\manual\IOO}'
+and it will also say something like this:
+\begintt
+(io.mf
+The letter O [79])
+*
+\endtt
+What does this mean? Well, `|(io.mf|' means that it has started to read your
+file, and `|The| |letter|~|O|' was printed when the title was found in
+line~9. Then when \MF\ got to the |endchar| on line~16, it said
+`|[79]|' to tell you that it had just output character number~79.
+\ (This is the ^{ASCII} code for the letter~|O|; Appendix~C lists all
+of these codes, if you need to know them.) The `|)|' after `|[79]|'
+means that \MF\ subsequently finished reading the file, and the `^|*|'
+means that it wants another instruction.
+
+Hmmm. The file contains programs for both I and O; why did we get only
+an~O? Answer: Because lines 17--23 simply define the subroutine "test\_I";
+they don't actually {\sl do\/} anything with that subroutine. We need to
+activate "test\_I" if we're going to see what it does. So let's type
+\begintt
+test_I("I",5/6,1/3);
+\endtt
+this invokes the subroutine, with $"code"=\null$|"I"|,
+$"trial\_stem"={5\over6}$, and $"trial\_width"={1\over3}$. The computer will
+now draw an~I corresponding to these values,\footnote*{Unless, of course,
+there was a typing error in lines 17--23, where "test\_I" is defined.} and
+it will prompt us for another command.
+
+It's time to type `^|end|' now, after which \MF\ should tell us that it has
+completed this run and made an output file called `|io.2602gf|'. Running this
+file through ^|GFtoDVI| as in Experiment~1 will produce two proofsheets,
+showing the `{\manual\IOO}' and the `{\manual\IOI}' we have created.
+The output won't be shown here, but you can see the results by doing
+the experiment personally.
+
+Look at those proofsheets now, because they provide instructive examples
+of the simulated broad-edge pen constructions introduced in Chapter~4.
+Compare the `{\manual\IOO}' with the program that drew it: Notice that
+the $\penpos2$ in line~10 makes the curve slightly thicker at the ^^"penpos"
+bottom than at the top; that the equation `$x_{1l}=w-x_{3l}="curve\_sidebar"$'
+in line~12 makes the right edge of the curve as far from the right of the
+bounding box as the left edge is from the left; that line~13 places point~1
+slightly lower than point~3. The proofsheet for `{\manual\IOI}' should look
+very much like the corresponding illustration near the end of Chapter~4,
+but it will be somewhat larger.
+
+\danger Your proof copy of the `{\manual\IOO}' should show twelve dots
+for key points; but only ten of them will be labeled, because there isn't
+room enough to put labels on points 2 and~4. The missing ^{labels} usually
+^^{overflow labels} appear in the upper right corner, where it might say, e.g.,
+`|4|~|=|~|4l|~|+|~|(-1,-5.9)|'; this
+means that point $z_4$ is one pixel to the left and 5.9 pixels down
+from point~$z_{4l}$, which is labeled. \ (Some implementations omit this
+information, because there isn't always room for it.)
+
+The proofsheets obtained in Experiment~2 show the key points and the
+bounding boxes, but this extra information can interfere with our
+perception of the character shape itself. There's a simple way to
+get proofs that allow a viewer to criticize the results from an aesthetic
+rather than a logical standpoint; the creation of such proofs will be the
+goal of our next experiment.
+
+Here's how to do Experiment~3: Start \MF\ as usual, then type
+\begintt
+\mode=smoke; input io
+\endtt
+in response to the `^|**|'. This will input file |io.mf| again,
+after establishing ``smoke'' mode. \ (As in Experiment~1, the command line
+begins with `|\|' so that the computer knows you aren't starting with
+the name of a file.) \ Then complete the run exactly ^^{backslash}
+as in Experiment~2, by typing `|test_I("I",5/6,1/3);| |end|';
+and apply |GFtoDVI| to the resulting file |io.2602gf|.
+
+This time the proofsheets will contain the same characters as before, but
+they will be darker and without labeled points. The bounding boxes will
+be indicated only by small markings at the corners; you can put these
+boxes next to each other and tack the results up on the wall, then stand
+back to see how the characters will look when set by a high-resolution
+typesetter. \ (This way of working is called ^"smoke" mode because it's
+analogous to the ``smoke proofs'' that punch-cutters traditionally used to
+test their handiwork. They held the newly cut type over a candle flame so
+that it would be covered with carbon; then they pressed it on paper to
+make a clean impression of the character, in order to see whether changes
+were needed.)
+
+\danger Incidentally, many systems allow you to invoke \MF\ by typing
+a one-line command like `|mf|~|io|' in the case of Experiment~2; you
+don't have to wait for the `|**|' before giving a file name. Similarly,
+the one-liners `|mf|~|\relax|' and `|mf|~|\mode=smoke;| |input|~|io|' can be
+used on many systems at the beginning of Experiments 1 and~3. You might want
+to try this, to see if it works on your computer; or you might ask
+somebody if there's a similar shortcut.
+
+Experiments 1, 2, and 3 have demonstrated how to make proof drawings of
+test characters, but they don't actually produce new fonts that can be
+used in typesetting. For this, we move onward to Experiment~4, in which
+we put ourselves in the position of a person who is just starting to
+design a new typeface. Let's imagine that we're happy with the~O of
+|io.mf|, and that we want a ``sans serif'' I in the general style produced
+by "test\_I", but we aren't sure about how thick the stem of the~I
+should be in order to make it blend properly with the~O. Moreover, we aren't
+sure how much white space to leave at the sides of the~I. So~we want to do
+some typesetting experiments, using a sequence of different I's.
+
+The ideal way to do this would be to produce a high-resolution test font and to
+view the output at its true size. But this may be too expensive, because fine
+printing equipment is usually available only for large production runs.
+The next-best alternative is to use a low-resolution printer but to magnify
+the output, so that the resolution is effectively increased. We shall adopt
+the latter strategy, because it gives us a chance to learn about
+^{magnification} as well as fontmaking.
+
+After starting \MF\ again, you can begin Experiment 4 by typing
+\begintt
+\mode=localfont; mag=4; input io
+\endtt
+in response to the `|**|'. The ^{plain base} at your installation is supposed
+to recognize ^|localfont| as the name of the mode that makes fonts for your
+``standard'' output device. The equation `|mag=4|' means that this run will
+produce a font that is magnified fourfold; i.e., the results will be
+4~times bigger than usual.
+
+The computer will read |io.mf| as before, but this time it won't display an~`O';
+characters are normally not displayed in fontmaking modes, because we usually
+want the computer to run as fast as possible when it's generating a font
+that has already been designed. All you'll see is `|(io.mf| |[79])|',
+followed by~`^|*|'. Now the fun starts: You should type
+\begintt
+code=100;
+for s=7 upto 10:
+ for w=5 upto 8:
+ test_I(incr code,s/10,w/20);
+endfor endfor end.
+\endtt
+(Here `^|upto|' must be typed as a single word.) \ We'll learn about
+repeating things with `^|for||...|^|endfor|' in Chapter~19. This little
+program produces 16 versions of the letter~I, with stem widths of
+$7\over10$, $8\over10$, $9\over10$, and~${10\over10}\pt$, and with
+character widths of $5\over20$, $6\over20$, $7\over20$, and~${8\over20}\,
+\rm em$. The sixteen trial characters will appear in positions 101 through~116
+of the font; it turns out that these are the ^{ASCII} codes for lowercase
+letters |e| through~|t| inclusive. \ (Other codes would have been used if
+`|code|' had been started at a value different from~100. The construction
+`|incr|~|code|' increases the value of |code| by~1 and produces the new value;
+thus, each use of |test_I| has a different code number.) ^^"incr"
+
+This run of \MF\ will not only produce a generic font |io.nnngf|, it will also
+create a file called |io.tfm|, the ``^{font metric file}'' that tells
+^^{output of METAFONT} ^^|tfm|
+typesetting systems like \TeX\ how to make use of the new font. The remaining
+part of Experiment~4 will be to put \TeX\ to work: We shall make some test
+patterns from the new font, in order to determine which `I' is best.
+
+You may need to ask a local system wizard for help at this point, because
+it may be necessary to move the file |io.tfm| to some special place where
+\TeX\ and the other typesetting software can find it. Furthermore, you'll
+need to run a program that converts |io.nnngf| to the font format used by your
+local output device. But with luck, these will both be fairly simple
+operations, and a new font called `|io|' will effectively be installed
+on your system. This font will contain seventeen letters, namely an |O| and
+sixteen |I|'s, where the |I|'s happen to be in the positions normally occupied
+by |e|, |f|, \dots,~|t|. Furthermore, the font will be magnified fourfold.
+
+\danger The magnification of the font will be reflected in its file name.
+For example, if "localfont" mode is for a device with 200 pixels per inch,
+the |io| font at 4$\times$ magnification will be called `|io.800gf|'.
+
+You can use \TeX\ to typeset from this font like any other, but for the
+purposes of Experiment~4 it's best to use a special \TeX\ package that has
+been specifically designed for font testing. All you need to do is to
+run \TeX---which is just like running \MF\!, except that you call it `|tex|'
+instead of `|mf|'; and you simply type `^|testfont|' in reply to \TeX's
+`|**|'. \ (The |testfont| routine should be available on your system; if
+not, you or somebody else can type it in, by copying the relevant material
+from Appendix~H\null.) \ You will then be asked for the name of the font
+you wish to test. Type
+\begintt
+io scaled 4000
+\endtt
+(which means the |io| font magnified by 4, in \TeX's jargon),
+since this is what \MF\ just created. The machine will now ask you for
+a test command, and you should reply
+\begintt
+\mixture
+\endtt
+to get the ``^{mixture}'' test. \ (Don't forget the ^{backslash}.) \
+You'll be asked for a ^{background letter}, a starting letter, and an
+ending letter; type `|O|', `|e|', and `|t|', respectively. This will
+produce sixteen lines of typeset output, in which the first line contains
+a mixture of |O| with~|e|, the second contains a mixture of |O|~with~|f|,
+and so on. To complete Experiment~4, type `|\end|' to \TeX, and print the
+file |testfont.dvi| ^^|dvi| that \TeX\ gives you.
+
+\setbox0=\hbox{\kern.5pt I\kern.5pt} \def\\{\copy0}
+If all goes well, you'll have sixteen lines that say `O\\OO\\\\OOO\\\\\\O\\',
+but with a different I on each line. In order to choose the line that looks
+best, without being influenced by neighboring lines, it's convenient to take
+two sheets of blank paper and use them to mask out all of the lines
+except the one you're studying. Caution: These letters are four times
+larger than the size at which the final font is meant to be viewed,
+so you should look at the samples from afar. Xerographic reductions may
+introduce distortions that will give misleading results. Sometimes when
+you stare at things like this too closely, they all look wrong, or
+they all look right; first impressions are usually more significant
+than the results of logical reflection. At any rate, you should be able
+to come up with an informed judgment about what values to use for the
+stem width and the character width of a decent `I'; these can then be
+incorporated into the program, the `|def|' and `|enddef|' parts of
+|io.mf| can be removed, and you can go on to design other characters
+that go with your I and~O. Furthermore you can always go back and make
+editorial changes after you see your letters in more contexts.
+
+\ddangerexercise The goddess Io was known in Egypt as ^{Isis}.
+Design an `{\manual\IOS}' for her.
+\answer Here's one way, using a variable "slab" to control the
+\rightfig A5a ({200\apspix} x 252\apspix) ^-71pt
+^^{S} pen breadth at the ends of the stroke:
+\begintt
+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];
+.5[x7l,x7]=.04em; y7l=.12h-o;
+path trial; trial=z3{down}..z4..{down}z5;
+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;
+\endtt
+Notice that the pen angle at point 4 has been found by letting \MF\
+^^{direction} construct a ^{trial path} through the center points,
+then using the ^{perpendicular} direction. The letters work reasonably
+well at their true size: `{\manual\IOS\IOO} {\manual\IOI\IOO}
+{\manual\IOI\IOS} {\manual\IOI\IOS\IOI\IOS}.'
+
+Well, this isn't a book about type design; the example of |io.mf| is
+simply intended to illustrate how a type designer might want to operate,
+and to provide a run-through of the complete process from design of
+type to its use in a document. We must go back now to the world of
+computerese, and study a few more practical details about the use of \MF\!.
+
+This has been a long chapter, but take heart: There's only one more
+experiment to do, and then you will know enough about \MF\ to run it
+fearlessly by yourself forever after. The only thing you are still missing
+is some information about how to cope with error messages. Sometimes
+\MF\ stops and asks you what to do next. Indeed, this may have already
+happened, and you may have panicked.
+
+Error messages can be terrifying when you aren't prepared for them;
+but they can be fun when you have the right attitude. Just remember that
+you really haven't hurt the computer's feelings, and that nobody will
+hold the errors against you. Then you'll find that running \MF\ might
+actually be a creative experience instead of something to dread.
+
+The first step in Experiment 5 is to plant some intentional mistakes
+in the input file. Make a copy of |io.mf| and call it |badio.mf|; then
+change line~1 of |badio.mf| to
+\begintt
+mode setup; % an intentional error!
+\endtt
+(thereby omitting the underline character in |mode_setup|).
+Also change the first semicolon (\thinspace`|;|'\thinspace) on line~2
+to a colon (\thinspace`|:|'\thinspace);
+change `|thick,10|' to `|thick,l0|' on line~10 (i.e., replace the numeral~`|1|'
+by the letter~`|l|'\thinspace); and change `|thin|' to `|thinn|' on line~11.
+These four changes introduce typical typographic errors, and it will be
+instructive to see if they lead to any disastrous consequences.
+
+Now start \MF\ up again; but instead of cooperating with the computer, type
+`|mumble|' in reply to the~`|**|'. \ (As long as you're going to make
+intentional mistakes, you might as well make some dillies.) \
+\MF\ will say that it can't find any file called |mumble.mf|,
+and it will ask you for another name. Just hit \<return> this time;
+you'll see that you had better give the name of a real file.
+So type `|badio|' and wait for \MF\ to find one of the {\sl faux pas\/}
+in that messed-up travesty.
+
+Ah yes, the machine will soon stop, after typing something like this:
+\begintt
+>> mode.setup
+! Isolated expression.
+<to be read again>
+ ;
+l.1 mode setup;
+ % an intentional error!
+?
+\endtt
+\MF\ begins its error messages with `|!|', and it sometimes precedes them
+with one or two related mathematical expressions that are displayed on
+lines starting with `^|>>|'. Each error message is also followed by lines
+of context that show what the computer was reading at the time of the
+error. Such context lines occur in pairs; the top line of the pair (e.g.,
+`|mode| |setup;|'\thinspace) shows what \MF\ has looked at so far, and
+where it came from (`|l.1|', i.e., line number~1); the bottom line (here
+`|%|~|an| |intentional| |error!|'\thinspace) shows what \MF\ has yet to
+read. In this case there are two pairs of context lines; the top pair
+refers to a semicolon that \MF\ has read once but will be reading again,
+because it didn't belong with the preceding material.
+
+You don't have to take out pencil and paper in order to write down the
+error messages that you get before they disappear from view, since \MF\
+always writes a ``^{transcript}'' or ``^{log file}'' that records what
+happened during each session. For example, you should now have a file
+called |io.log| containing the transcript of Experiment~4, as well as a file
+|mfput.log| that contains the transcript of Experiment~1. \ (The old
+transcript of Experiment~2 was probably overwritten when you did
+Experiment~3, and again when you did Experiment~4, because all three
+transcripts were called |io.log|.) \ At the end of Experiment~5 you'll
+have a file |badio.log| that will serve as a helpful reminder of
+what errors need to be fixed up.
+
+The `^|?|' that appears after the context display means that \MF\ wants
+advice about what to do next. If you've never seen an error message before,
+or if you've forgotten what sort of response is expected, you can type
+`|?|' now (go ahead and try it!); \MF\ will respond as follows:
+\begintt
+Type <return> to proceed, S to scroll future error messages,
+R to run without stopping, Q to run quietly,
+I to insert something, E to edit your file,
+1 or ... or 9 to ignore the next 1 to 9 tokens of input,
+H for help, X to quit.
+\endtt
+This is your menu of options. You may choose to continue in various ways:
+
+\smallskip\item{1.}
+Simply type \<return>. \MF\ will resume its processing, after
+attempting to recover from the error as best it can.
+
+\smallbreak\item{2.} Type `|S|'. \MF\ will proceed without
+pausing for instructions if further errors arise. Subsequent error messages
+will flash by on your terminal, possibly faster than you can read them, and
+they will appear in your log file where you can scrutinize them at your
+leisure. Thus, `|S|'~is sort of like typing \<return> to every message.
+
+\smallbreak\item{3.} Type `|R|'. This is like `|S|' but even stronger,
+since it tells \MF\ not to stop for any reason, not even if a file name
+can't be found.
+
+\smallbreak\item{4.} Type `|Q|'. This is like `|R|' but even more so,
+since it tells \MF\ not only to proceed without stopping but also to
+suppress all further output to your terminal. It is a fast, but somewhat
+reckless, way to proceed (intended for running \MF\ with no operator in
+attendance).
+
+\smallbreak\item{5.} Type `|I|', followed by some text that you want to
+insert. \MF\ will read this text before encountering what it
+would ordinarily see ^^{inserting text online}
+^^{online interaction, see interaction} ^^{interacting with MF}
+next.
+
+\smallbreak\item{6.} Type a small number (less than 100). \MF\ will
+delete this many ^{tokens} from whatever it is
+about to read next, and it will pause again to give you another chance to
+look things over. ^^{deleting tokens}
+\ (A~``token'' is a name, number, or symbol that \MF\ reads as a unit;
+e.g., `|mode|' and `|setup|' and `|;|' are the first three tokens
+of |badio.mf|, but `|mode_setup|' is the first token of |io.mf|.
+Chapter~6 explains this concept precisely.)
+
+\smallbreak\item{7.} Type `|H|'. This is what you should do now and whenever
+you are faced with an error message that you haven't seen for a~while. \MF\
+has two messages built in for each perceived error: a formal one and an
+informal one. The formal message is printed first (e.g., `|!|~|Isolated|
+|expression.|'\thinspace); the informal one is printed if you request
+more help by typing `|H|', and it also appears in your log file if you
+are scrolling error messages. The informal message tries to complement the
+formal one by explaining what \MF\ thinks the trouble is, and often
+by suggesting a strategy for recouping your losses.^^{help messages}
+
+\smallbreak\item{8.} Type `|X|'. This stands for ``exit.'' It causes \MF\
+to stop working on your job, after putting the finishing touches on your
+|log| file and on any characters that have already been output to your |gf|
+and/or |tfm| files. The current (incomplete) character will not be output.
+
+\smallbreak\item{9.} Type `|E|'. This is like `|X|', but it also prepares
+the computer to edit the file that \MF\ is currently reading, at the
+current position, so that you can conveniently make a change before
+trying again.
+
+\smallbreak\noindent
+After you type `|H|' (or `|h|', which also works), you'll get a message
+that tries to explain the current problem: The mathematical quantity just
+read by \MF\ (i.e., |mode.setup|) was not followed by `|=|' or `|:=|', so
+there was nothing for the computer to do with it. Chapter~6 explains that
+a ^{space} between tokens (e.g., `|mode|~|setup|'\thinspace) is equivalent to
+a ^{period} between tokens (e.g., `|mode.setup|'\thinspace). The correct
+spelling `|mode_setup|' would be recognized as a preloaded subroutine of
+plain \MF\!, but plain \MF\ doesn't have any built-in meaning for
+|mode.setup|. Hence |mode.setup| appears as a sort of orphan, and \MF\
+realizes that something is amiss.
+
+In this case, it's OK to go ahead and type \<return>, because we really
+don't need to do the operations of @mode\_setup@ when no special mode
+has been selected. \MF\ will continue by forgetting the isolated expression,
+and it will ignore the rest of line~1 because everything after a
+^^{percent} `|%|'~sign is always ignored. \ (This is another thing that
+will be explained in Chapter~6; it's a handy way to put ^{comments}
+into your \MF\ programs.) \ The changes that were made to line~1 of |badio.mf|
+therefore have turned out to be relatively harmless. But \MF\ will
+almost immediately encounter the mutilated semicolon in line~2:
+\begintt
+! Extra tokens will be flushed.
+<to be read again>
+ :
+l.2 em#:=10pt#:
+ cap#:=7pt#;
+?
+\endtt
+What does this mean? Type `|H|' to find out. \MF\ has no idea what to
+do with a `|:|' at this place in the file, so it plans to recover by
+``^{flushing}'' or getting rid of everything it sees, until coming to a
+semicolon. It would be a bad idea to type \<return> now, since you'd lose
+the important assignment `|cap#:=7pt#|', and that would lead to worse errors.
+
+You might type `|X|' or `|E|' at this point, to exit from \MF\ and to fix
+the errors in lines 1 and~2 before trying again. But it's usually best
+to keep going, trying to detect and correct as many mistakes as possible
+in each run, since that increases your productivity while
+decreasing your computer bills. An experienced \MF\ user will quit
+after an error only if the error is unfixable, or if there's almost no
+chance that additional errors are present.
+
+The solution in this case is to proceed in two steps: First type `|1|',
+which tells \MF\ to delete the next token (the unwanted `|:|'); then type
+`|I;|', which inserts a semicolon. This semicolon protects the rest of line~2
+from being flushed away,
+so all will go well until \MF\ reaches another garbled line.
+
+The next error message is more elaborate, because it is detected while
+\MF\ is trying to carry out a "penpos" command; "penpos" is not a
+primitive operation (it is defined in plain \MF), hence a lot more
+context is given:
+\begintt
+>> l0
+! Improper transformation argument.
+<to be read again>
+ ;
+penpos->...(EXPR3),0)rotated(EXPR4);
+ x(SUFFIX2)=0.5(x(SUFF...
+l.10 penpos1(thick,l0)
+ ; penpos2(.1[thin,thick],90-10);
+?
+\endtt
+At first, such error messages will appear to be complete nonsense to you,
+because much of what you see is low-level \MF\ code that you never wrote. But
+you can overcome this hangup by getting a feeling for the way \MF\ operates.
+
+The bottom line shows how much progress \MF\ has made so far in the |badio|
+file: It has read `|penpos1(thick,l0)|' but not yet the semicolon, on line~10.
+The "penpos" routine expands into a long list of tokens; indeed, this list
+is so long that it can't all be shown on two lines, and the appearances of
+`^|...|' indicate that the definition of "penpos" has been truncated here.
+Parameter values are often inserted into the expansion of a high-level
+routine; in this case, for example, `|(EXPR3)|' and `|(EXPR4)|' correspond
+to the respective parameters `|thick|' and `|l0|', and `|(SUFFIX2)|'
+corresponds to~`|1|'. ^^|EXPR| ^^|SUFFIX|
+\MF\ detected an error just after encountering the phrase `|rotated(EXPR4)|';
+the value of |(EXPR4)| was an undefined quantity (namely `|l0|',
+which \MF\ treats as the subscripted variable~`$l_0$'\thinspace), and
+^{rotation} is permitted only when a known numeric value has been supplied.
+Rotations are particular instances of what \MF\ calls {\sl^{transformations}\/};
+hence \MF\ describes this particular error by saying that an ``improper
+transformation argument'' was present.
+
+When you get a multiline error message like this, the best clues about the
+source of the trouble are usually on the bottom line (since that is what
+you typed) and on the top line (since that is what triggered the error
+message). Somewhere in there you can usually spot the problem.
+
+If you type `|H|' now, you'll find that
+\MF\ has simply decided to continue without doing the requested rotation.
+Thus, if you respond by typing \<return>, \MF\ will go on as if the program
+had said `|penpos1(thick,0)|'. Comparatively little harm has been done;
+but there's actually a way to fix the error perfectly before proceeding:
+Insert the correct rotation by typing
+\begintt
+I rotated 10
+\endtt
+and \MF\ will rotate by 10 degrees as if `|l0|' had been `|10|'.
+
+What happens next in Experiment 5? \MF\ will hiccup on the remaining
+bug that we planted in the file. This time, however, the typo will
+not be discovered until much later, because there's nothing wrong
+with line~11 as it stands. \ (The variable |thinn| is not defined,
+but undefined quantities are no problem unless you're doing something
+complicated like rotation. Indeed, \MF\ programs typically
+consist of equations in which there are lots of unknowns;
+variables get more and more defined as time goes on. Hence spelling
+errors cannot possibly be detected until the last minute.) \
+Finally comes the moment of truth, when |badio| tries to draw a
+path through an unknown point; and you will get an error message
+that's even scarier than the previous one:
+\begintt
+>> 0.08682thinn+144
+! Undefined x coordinate has been replaced by 0.
+<to be read again>
+ {
+<for(l)> ...FFIX0){up}..z4(SUFFIX0){
+ left}..cycle; ENDFOR
+penstroke->...ath_.e:=(TEXT0);endfor
+ .if.cycle.path_.l:cyc...
+<to be read again>
+ ;
+l.15 ... ..z3e{up}..z4e{left}..cycle;
+|quad
+?
+\endtt
+Wow; what's this? The expansion of @penstroke@ involves a ``@for@ loop,''
+and the error was detected in the midst of it. The
+expression `|0.08682thinn+144|' just above the error message implies that
+the culprit in this case was a misspelled `|thin|'. If that hadn't been
+enough information, you could have gleaned another clue from the fact that
+`|z4(SUFFIX0)|' has just been read; |(SUFFIX0)| is the current loop value
+and `|<for(l)>|' indicates that the value in question is `|l|', hence
+$z_{4l}$ is under suspicion. \ (Sure enough, the undefined $x$~coordinate
+that provoked this error can be shown to be $x_{4l}=0.08682"thinn"+144$.)
+
+In any event the mistake on line~11 has propagated too far to be fixable,
+so you're justified in typing `|X|' or~`|E|' at this point. But type~`|S|'
+instead, just for fun: This tells \MF\ to plunge ahead, correcting all
+remaining errors as best it can. \ (There will be a few more problems,
+since several variables still depend on `|thinn|'.) \ \MF\ will draw a
+very strange letter~O before it gets to the end of the file. Then you
+should type `|end|' to terminate the run.
+
+If you try to edit |badio.mf| again, you'll notice that line~2 still
+contains ^^{editing} a colon instead of a semicolon. The fact that you
+told \MF\ to delete the colon and to insert additional material doesn't
+mean that your file has changed in any way. However, the transcript file
+|badio.log| has a record of all the errors, so it's a handy reference when
+you want to correct mistakes. \ (Why not look at
+|badio.log| now, and |io.log| too, in order to get familiar with log files?)
+
+\dangerexercise Suppose you were doing Experiment 3 with |badio| instead
+of~|io|, so you began by saying `|\mode=smoke|; |input| |badio|'. Then you
+would want to recover from the error on line~1 by inserting a correct
+@mode\_setup@ command, instead of by simply \<return>ing, because
+@mode\_setup@ is what really establishes "smoke" mode. Unfortunately if you
+try typing `|I|~|mode_setup|' in response to the ``isolated expression''
+error, it doesn't work. What should you type instead?
+\answer After an ``isolated expression,'' \MF\ thinks it is at the end of
+a statement or command, so it expects to see a semicolon next. You should
+type, e.g., `|I;|~|mode_setup|' to keep \MF\ happy.
+
+By doing the five experiments in this chapter you have learned at first hand
+(1)~how to produce proofsheets of various kinds, including ``smoke proofs'';
+(2)~how to make a new font and test it; (3)~how to keep calm when \MF\
+issues stern warnings. Congratulations! You're on the threshold of being able to
+do lots more. As you read the following chapters, the best strategy
+will be for you to continue making trial runs, using experiments
+of your own design.
+
+\exercise However, this has been an extremely long chapter,
+so you should go outside now and get some {\sl real\/} exercise.
+\answer Yes.
+
+\endchapter
+
+Let us learn how Io's frenzy came---
+She telling her disasters manifold.
+\author \AE SCHYLUS, ^^{Aeschylus} %
+ {\sl Prometheus Bound\/} (c.\thinspace470 B.C.) % verse 801
+ % This is the translation by Morshead
+
+\bigskip
+
+To the student who wishes to use graphical methods as a tool,
+it can not be emphasized too strongly that practice in the use of that tool
+is as essential as a knowledge of how to use it.
+The oft-repeated pedagogical phrase, ``we learn by doing,'' is applicable here.
+\author THEODORE ^{RUNNING}, {\sl Graphical Mathematics\/} (1927) % p viii
+
+\eject
+ \beginchapter Chapter 6. How \MF\\Reads What You\\Type
+
+So far in this book we've seen lots of things that \MF\ can do, but we haven't
+discussed what \MF\ can't do. We have looked at many examples of commands that
+\MF\ can understand, but we haven't dwelt on the fact that the computer will
+find many phrases unintelligible. It's time now to adopt a more systematic
+approach and to study the exact rules of \MF's language. Then we'll know what
+makes sense to the machine, and we'll also know how to avoid ungrammatical
+utterances.
+
+A \MF\ program consists of one or more lines of text, where each line is made
+up of letters, numbers, punctuation marks, and other symbols that appear on
+a standard computer keyboard. A total of 95 different characters can be
+employed, namely a blank space plus the 94 visible symbols of standard ^{ASCII}.
+\ (Appendix~C describes the American Standard Code for Information
+Interchange, popularly known as ``ASCII,'' under which code numbers 33
+through~126 have been assigned to 94 specific symbols. This particular
+coding scheme is not important to a \MF\ programmer; the only relevant thing
+is that 94 different nonblank symbols can be used.)
+
+\MF\ converts each line of text into a series of {\sl ^{tokens}}, and a
+programmer should understand exactly how this conversion takes place.
+Tokens are the individual lexical units that govern the computer's
+activities. They are the basic building blocks from which meaningful
+sequences of instructions can be constructed. We discussed tokens briefly
+at the end of the previous chapter; now we shall consider them in detail.
+Line~9 of the file |io.mf| in that chapter is a typical example of what
+the machine might encounter:
+\begintt
+beginchar("O",0.8em#,cap#,0); "The letter O";
+\endtt
+When \MF\ reads these ASCII characters it finds sixteen tokens:
+\begindisplay \chardef\"=`\" \openup 2pt
+\ttok{beginchar}\quad\ttok{(}\quad\ttok{\"O\"}\quad
+ \ttok{,}\quad\ttok{0.8}\quad\ttok{em}\quad\ttok{\#}\quad\ttok{,}\cr
+\ttok{cap}\quad\ttok{\#}\quad\ttok{,}\quad\ttok{0}\quad
+ \ttok{)}\quad\ttok{;}\quad\ttok{\"The letter O\"}\quad\ttok{;}\cr
+\enddisplay
+Two of these, |"O"| and |"The| |letter| |O"|, are called {\sl^{string tokens}\/}
+because they represent strings of characters. Two of them, `|0.8|' and `|0|',
+are called {\sl^{numeric tokens}\/} because they represent numbers. The
+other twelve---`|beginchar|', `|(|', etc.---are called {\sl^{symbolic
+tokens}\/}; such tokens can change their meaning while a \MF\ program runs,
+but string tokens and numeric tokens always have a predetermined significance.
+Notice that clusters of letters like `|beginchar|' are treated as a unit;
+the same holds with respect to letters mixed with ^{underline} characters,
+as in `|mode_setup|'. Indeed,
+the rules we are about to study will explain that clusters of other
+characters like `|0.8|' and `|:=|' are also considered to be
+indecomposable tokens. \MF\ has a definite way of deciding where one
+token stops and another one begins.
+
+It's often convenient to discuss ^{grammatical rules} by formulating them in
+a special notation that was introduced about 1960 by John ^{Backus} and
+Peter ^{Naur}. Parts of speech are represented by named quantities in
+^{angle brackets}, and {\sl^{syntax rules}\/} are used to express the ways
+in which those quantities can be built~up from simpler units. For example,
+here are three syntax rules that completely describe the possible forms of
+numeric tokens:
+\def\\#1{\thinspace{\tt#1}\thinspace}
+\beginsyntax
+<decimal digit>\is\\0\alt\\1\alt\\2\alt\\3\alt\\4\alt\\5\alt\\6%
+ \alt\\7\alt\\8\alt\\9
+<digit string>\is<decimal digit>\alt<digit string><decimal digit>
+<numeric token>\is<digit string>\alt[.]<digit string>
+ \alt<digit string>\\.<digit string>
+\endsyntax
+The first rule says that a \<decimal digit> is either `|0|' or `|1|' or
+$\cdots$ or `|9|'; thus it must be one of the ten numerals. The next
+rule says that a \<digit string> is either a \<decimal digit> or a
+\<digit string> followed by a \<decimal digit>; thus it must be a sequence
+of one or more digits. Finally, a \<numeric token> has one of three forms,
+exemplified respectively by `|15|', `|.05|', and `|3.14159|'.
+
+Syntax rules explain only the surface structure of a language, not the
+underlying meanings of things. For example, the rules above tell us that
+`|15|' is a \<numeric token>, but they don't imply that `|15|' has
+any connection with the number fifteen. Therefore syntax rules are
+generally accompanied by rules of {\sl^{semantics}}, which ascribe
+meanings to the strings of symbols that meet the conditions of the syntax.
+In the case of numeric tokens, the principles of ordinary decimal notation
+define the semantics, except that \MF\ deals only with numbers in a
+limited range: A numeric token must be less than 4096, and its value is
+always rounded to the nearest multiple of $1\over65536$. Thus, for example,
+`|.1|'~does not mean $1\over10$, it means $6554\over65536$ (which is
+slightly greater than $1\over10$). It turns out that the tokens
+`|.099999|' and `|0.10001|' both have exactly the same meaning as
+^^{numeric tokens, rounded values} ^^{numeric tokens, maximum value}
+`|.1|', because all three tokens represent the value $6554\over65536$.
+
+\dangerexercise Are the following pairs of numeric tokens equivalent
+to each other, when they appear in \MF\ programs?
+\ (a)~|0| and |0.00001|; \ (b)~|0.00001| and |0.00002|;
+\ (c)~|0.00002| and |0.00003|; \ (d)~|04095.999999| and |10000|?
+\answer (a) No, the second token represents $1\over65536$. \ (A token has
+the same meaning as~`|0|' ^^{zero} if and only if its decimal value
+is strictly less than $2^{-17}=.00000\,76293\,94531\,25$.) \ (b)~Yes; both
+tokens represent $1\over65536$, because 1~is the nearest integer to both
+$.00001\times65536=.65536$ and $0.00002\times65536=1.31072$. \ (c)~No,
+|0.00003| represents $2\over65536$. \ (d)~Yes, they both mean ``^{enormous
+number} that needs to be reduced''; \MF\ complains in both
+cases and substitutes the largest legal numeric token. \ (Rounding
+4095.999999 to the nearest multiple of $1\over65536$ yields 4096,
+which is too big.)
+
+\MF\ converts each line of text into a sequence of tokens by repeating
+the following rules until no more characters remain on the line:
+\smallskip
+\hang\textindent{1)}If the next character is a ^{space}, or if it's a ^{period}
+(\thinspace`|.|'\thinspace) that isn't ^^{decimal point} followed by a
+decimal digit or a period, ignore it and move on.
+
+\hang\textindent{2)}If the next character is a ^{percent sign}
+(\thinspace`|%|'\thinspace), ignore it and also ignore everything else
+that remains on the current line. \ (Percent signs therefore allow you to
+write ^{comments} that are unseen by \MF\!.)
+
+\hang\textindent{3)}If the next character is a ^{decimal digit} or a period
+that's followed by a decimal digit, the next token is a numeric token,
+consisting of the longest sequence of contiguous characters starting at
+the current place that satisfies the syntax for \<numeric token> above.
+
+\hang\textindent{4)}If the next character is a ^{double-quote mark} (\thinspace
+`|"|'\thinspace), the next token is a string token, consisting of all
+characters from the current place to the next double-quote, inclusive.
+\ (There must be at least one more double-quote remaining on the line,
+otherwise \MF\ will complain about an ``^{incomplete string}.'') \ A string
+token represents the sequence of characters between the double-quotes.
+
+\hang\textindent{5)}If the next character is a ^{parenthesis} (\thinspace
+`|(|' or `|)|'\thinspace), a comma (\thinspace`|,|'\thinspace), or a
+semicolon (\thinspace`|;|'\thinspace), the next token is a symbolic token
+consisting of that single character.
+
+\hang\textindent{6)}Otherwise the next token is a symbolic token consisting
+of the next character together with all immediately following characters
+that appear in the same row of the following
+^^{table of character classes} table:
+\begindisplay \displayindent=0pt
+|ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz|\hidewidth\cr
+|<=>:|\|\cr
+|`'|\cr
+|+-|\cr
+|/*\|\cr
+|!?|\cr
+|#&@$|\cr
+|^~|\cr
+|[|\cr
+|]|\cr
+|{}|\cr
+|.|&(see rules 1, 3, 6)\cr
+|, ; ( )|&(see rule 5; these characters are ``loners'')\cr
+|"|&(see rule 4 for details about string tokens)\cr
+|0123456789|&(see rule 3 for details about numeric tokens)\cr
+|%|&(see rule 2 for details about comments)\cr
+\enddisplay
+
+\noindent
+The best way to learn the six rules about tokens is to work the following
+exercise, after which you'll be able to read any input file just as the
+computer does.
+
+\exercise What tokens does \MF\ find in the (ridiculous) line
+\begindisplay
+|xx3.1.6..[[a+-bc_d.e] ]"a %" <|\||>(($1. 5"+-""" % weird?|
+\enddisplay
+\answer \cstok{xx}, \cstok{3.1} (a numeric token), \cstok{.6} (another
+numeric token), \cstok{..}, \cstok{[[}, \cstok{a}, \cstok{+-},
+\cstok{bc\_d}, \cstok{e}, \cstok{]}, \cstok{]}, {\chardef\"=`\"\cstok{\"a
+\%\"} (a string token), \cstok{<\|>}, \cstok{(} (see rule~5), \cstok{(},
+\cstok{\$}, \cstok{1} (a numeric token), \cstok{5} (likewise numeric),
+\cstok{\"+-\"} (a string token), and \cstok{\"\"}} (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 \MF\ finds precisely these tokens is to prepare a
+test file that says `|isolated| |expression;|' on its first line and that
+contains the stated text on its second line. Then respond to \MF's
+error message by repeatedly typing `|1|', so that one token is deleted
+at a time.)
+
+\exercise Criticize the following statement: \MF\ ignores all spaces in the
+input.
+\answer The statement is basically true but potentially misleading. You can
+insert any number of spaces {\sl between\/} tokens without changing the
+meaning of a program, but you cannot insert a space in the {\sl middle\/}
+of any token without changing something. You can delete spaces between
+tokens {\sl unless\/} that would ``glue'' two adjacent tokens together.
+
+\dangerexercise True or false: If the syntax for \<numeric token> were
+changed to include a fourth alternative, `\<digit string>|.|', the meaning
+of \MF\ programs would not change in any way.
+\answer 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|'!
+
+
+\endchapter
+
+Yet wee with all our seeking could see no tokens.
+ % of any such Wall.
+\author PHILEMON ^{HOLLAND}, {\sl ^{Camden}'s Brittania\/} (1610)
+% OED says page 518, but I couldn't find it there in the 1637 edition
+
+\bigskip
+
+Unpropitious tokens interfered.
+\author WILLIAM ^{COWPER}, {\sl ^{Homer}'s Iliad\/} (1791) % Book 4 verse 455
+
+\eject
+ \beginchapter Chapter 7. Variables
+
+One of \MF's most important concepts is the notion of a
+{\sl^{variable}\/}---something that can take on a variety of different
+values. Indeed, this is one of the most important concepts in all of
+mathematics, and variables play a prominent r\^ole in almost all
+computer languages. The basic idea is that a program manipulates data,
+and the data values are stored in little compartments of a computer's
+memory. Each little compartment is a variable, and we refer to an item
+of data by giving its compartment a name.
+
+For example, the |io.mf| program for the letter {\manual\IOO} in Chapter~5
+contains lots of variables. Some of these, like `|x1l|' and `|y1|', represent
+coordinates. Others, like `|up|', represent
+directions. The variables `|em#|' and `|thin#|' stand for physical,
+machine-independent distances; the analogous variables `|em|' and `|thin|'
+stand for the corresponding machine-dependent distances in units of pixels.
+
+These examples indicate that different variables are often related to each
+other. There's an implicit connection between `|em#|' and `|em|',
+between `|x1|' and `|y1|'; the `"penpos"' convention
+sets up relationships between `|x1l|', `|x1|', and `|x1r|'. By choosing
+the names of variables carefully, programmers can make their programs
+much easier to understand, because the relationships between variables
+can be made to correspond to the ^^{data structure} structure
+of their names.
+
+In the previous chapter we discussed tokens, the atomic elements from which
+all \MF\ programs are made. We learned that there are three kinds of
+tokens: numeric (representing numbers), string (representing text), and
+symbolic (representing everything else). Symbolic tokens have no
+intrinsic meaning; any symbolic token can stand for whatever a programmer
+wants it to represent.
+
+Some symbolic tokens do, however, have predefined {\sl^{primitive}\/}
+meanings, when \MF\ begins its operations. For example, `|+|' stands
+initially for ``plus,'' and `|;|' stands for ``finish the current
+statement and move on to the next part of the program.'' It is customary
+to let such tokens retain their primitive meanings, but any symbolic token
+can actually be assigned a new meaning as a program is performed. For
+example, the definition of `|test_I|' in |io.mf| makes that token stand
+for a {\sl^{macro}}, i.e., a subroutine. We'll see later that you can
+instruct \MF\ to `|let| |plus=+|', after which `|plus|' will act just
+like `|+|' did.
+
+\MF\ divides symbolic tokens into two categories, depending on their
+current meaning. If the symbolic token currently stands for one of \MF's
+primitive operations, or if it has been defined to be a macro, it is
+called a {\sl^{spark}\/}; otherwise it is called a {\sl^{tag}}. Almost
+all symbolic tokens are tags, because only a few are defined to be sparks;
+however, \MF\ programs typically involve lots of sparks, because sparks
+are what make things happen. The symbolic tokens on the first five lines
+of |io.mf| include the following sparks:
+\begintt
+mode_setup ; := / define_pixels ( , )
+\endtt
+and the following tags:
+\begintt
+em # pt cap thin thick o
+\endtt
+(some of which appear several times). Tags are used to designate variables,
+but sparks cannot be used within a variable's name.
+
+Some variables, like `|em#|', have names that are made from more than one token;
+in fact, the variable `|x1l|' is named by three tokens, one of which is
+numeric. \MF\ has been designed so that it is easy to make compound names
+that correspond to the relations between variables. Conventional programming
+languages like ^{Pascal} would refer to `|x1l|' by the more
+cumbersome notation `|x[1].l|'; it turns out that `|x[1].l|' is an
+acceptable way to designate the variable |x1l| in a \MF\ program, but the
+shorthand form `|x1l|' is a great convenience because such variables
+are used frequently.
+
+Here are the formal rules of syntax by which \MF\ understands the names of
+variables:
+\def\\#1{\thinspace{\tt#1}\thinspace}
+\beginsyntax
+<variable>\is<tag><suffix>
+<suffix>\is<empty>\alt<suffix><subscript>\alt<suffix><tag>
+<subscript>\is<numeric token>\alt\\{\char`\[}<numeric expression>\\]
+\endsyntax
+First comes a tag, like `|x|'; then comes a {\sl^{suffix}\/} to the tag,
+like `|1l|'.
+The suffix might be empty, or it might consist of one or more subscripts
+or tags that are tacked on to the original tag. A {\sl^{subscript}\/} is
+a numeric index that permits you to construct ^{arrays} of related
+variables. The subscript is either a single numeric token, or it is a formula
+enclosed in square ^{brackets}; in the latter case the formula should produce a
+^^|[| numeric value. For example, `|x[1]|' and `|x[k]|' and `|x[3-2k]|' all mean
+^^|]| the same thing as `|x1|', if\/ |k|~is a variable whose value is~1. But
+`|x.k|' is not the same; it is the tag~`|x|' suffixed by the tag~`|k|',
+not the tag~`|x|' subscripted by the value of variable~|k|.
+
+\danger The variables `|x1|' and `|x01|' and `|x1.00|' are identical.
+Since any numeric token can be used as a subscript, fractional indices
+are possible; for example, `|x1.5|' is the same as `|x[3/2]|'. Notice,
+however, that `|B007|' and `|B.007|' are {\sl not\/} the same variable,
+because the latter has a fractional subscript.
+
+\danger \MF\ makes each \<suffix> as long as possible. In other words,
+a \<suffix> is always extended if it is followed by a \<subscript>
+or a~\<tag>.
+
+\dangerexercise Explain how to type a reference to the doubly subscripted
+variable `|a[1][5]|' without using square brackets.
+\answer 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.)
+
+\dangerexercise Is it possible to refer to {\sl any\/} variable without
+using square brackets?
+\answer 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.)
+
+\ddangerexercise Jonathan H. ^{Quick} (a student) used `|a.plus1|' as the name
+of a variable at the beginning of his program; later he said `|let|
+|plus=+|'. How could he refer to the variable `|a.plus1|' after that?
+\answer 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|'.)
+
+\danger \MF\ has several special variables called {\sl^{internal
+quantities}\/} that are intimately wired-in to the computer's behavior.
+For example, there's an internal quantity called `^|fontmaking|' that controls
+whether or not a |tfm| file is produced; another one called `^|tracingtitles|'
+governs whether or not titles like |"The| |letter|~|O"| appear on your
+terminal; still another one called `^|smoothing|' affects the digitization of
+curves. \ (A complete list of \MF's internal quantities appears in
+Chapter~25.) \ The name of an internal quantity acts like a tag, but
+internal quantities cannot be suffixed or subscripted.
+Thus, the syntax rule for \<variable>
+should actually be replaced by a slightly more complicated pair of rules:
+\beginsyntax
+<variable>\is<external tag><suffix>\alt<internal quantity>
+<tag>\is<external tag>\alt<internal quantity>
+\endsyntax
+
+\dangerexercise True or false: Every \<variable> is a legal \<suffix>.
+\answer False. After `|newinternal x;|' you can't say
+`|x|\<tag>' in a \<suffix list>.
+
+\ddanger The `|[|' and `|]|' that appear in the syntax for \<subscript>
+stand for any symbolic tokens whose current meanings are the same as
+\MF's primitive meanings of left and right bracket, respectively;
+those tokens don't necessarily have to be brackets. Conversely, if the
+meanings of the tokens `|[|' and `|]|' have been changed, brackets cannot
+be used to delimit subscripts. Similar remarks apply to all of the
+symbolic tokens in all of the syntax rules from now on. \MF\ doesn't look
+at the form of a token; it considers only a token's current meaning.
+
+The examples of \MF\ programs in this book have used two different
+typographic conventions. Sometimes we refer to variables by using
+^{italic type} and/or genuine subscripts, e.g., `"em"' and `$x_{2r}$';
+but sometimes we refer to those same variables by using a ^{typewriter}-like
+style of type, e.g., `|em|' and~`|x2r|'. In general, the typewriter style
+is used when we are mainly concerned with the way a programmer is supposed
+to type something that will appear on the terminal or in a file; but fancier
+typography is used when we are focusing on the meaning of a program rather
+than its ASCII representation. It should be clear how to convert the fancier
+form into tokens that \MF\ can actually understand.
+
+\danger In general, we shall use italic type only for tags (e.g., "em",
+"x", "r"), while boldface and roman type will be used for sparks
+(e.g., @draw@, @fill@, cycle, rotated, sqrt). Tags that consist of special
+characters instead of letters will sometimes get special treatment;
+for example, |em#| and |z2'| might be rendered $"em"\0$ and $z'_2$,
+respectively.
+
+The variables we've discussed so far have almost always had numbers as their
+values, but in fact \MF's variables are allowed to assume values of eight
+different ^{types}. A variable can be of type
+\nobreak\smallskip
+\item\bull^{boolean}, representing the values `^{true}' or `^{false}';
+\item\bull^{string}, representing sequences of ASCII characters;
+\item\bull^{path}, representing a (possibly curved) line;
+\item\bull^{pen}, representing the shape of a pen nib;
+\item\bull^{picture}, representing an entire pattern of pixels;
+\item\bull^{transform}, representing the operations of scaling, rotating,
+ shifting, reflecting, and/or slanting;
+\item\bull^{pair}, representing two numbers (e.g., a point or a vector);
+\item\bull^{numeric}, representing a single number.
+\smallskip\noindent
+If you want a variable to represent something besides a number, you must
+first give a {\sl^{type declaration}\/} ^^{declarations} that states
+what the type will be. But if you refer to a variable whose type has not
+been declared, \MF\ won't complain, unless you try to use it in a way that
+demands a value that isn't numeric.
+
+Type declarations are easy. You simply name one of the eight types,
+then you list the variables that you wish to declare for that type.
+For example, the declaration
+\begindisplay
+@pair@ "right", "left", $a.p$
+\enddisplay
+says that "right" and "left" and $a.p$ will be variables of type @pair@,
+so that equations like
+\begindisplay
+$"right"=-"left"=2a.p=(1,0)$
+\enddisplay
+can be given later. These equations, incidentally, define the values
+$"right"=(1,0)$, $"left"=(-1,0)$, and $a.p=(.5,0)$. \ (Plain \MF\
+has the stated values of "right" and "left" already built~in.)
+
+The rules for declarations are slightly trickier when subscripts are
+involved, because \MF\ insists that all variables whose names are identical
+except for subscript values must have the same type. It's possible to
+set things up so that, for example, $a$~is numeric, $a.p$ is a pair,
+$a.q$ is a pen, $a.r$ is a path, and $a_1$ is a string; but if $a_1$
+is a string, then all other variables $a_2$, $a_3$, etc., must also be
+strings. In order to enforce this restriction, \MF\ allows only
+``collective'' subscripts, represented by empty brackets `^|[]|',
+to appear in type declarations. ^^{collective subscripts} For example,
+\begintt
+path r, r[], x[]arc, f[][]
+\endtt
+declares $r$ and all variables of the forms $r[i]$, $x[i]"arc"$,
+and $f[i][j]$ to be path variables. This declaration doesn't affect
+the types or values of other variables like $r[\,]"arc"$; it affects
+only the variables that are specifically mentioned.
+
+Declarations destroy all previous values of the variables being defined.
+For example, the path declaration above makes $r$ and $r[i]$ and $x[i]"arc"$
+and $f[i][j]$ undefined, even if those variables previously had paths
+as their values. The idea is that all such variables will start out with a
+clean slate so that they can receive appropriate new values based on
+subsequent equations. ^^{value, disappearance of}
+
+\exercise Numeric variables don't need to be declared. Therefore is there
+ever any reason for saying `|numeric| |x|'\thinspace?
+\answer 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 affect other variables like `|x2|' or `|x.x|' that might be present.)
+
+\danger The formal syntax rules for type declarations explain these
+grammatical conventions precisely. If the symbolic token that begins a
+declared variable was previously a spark, it loses its former meaning and
+immediately becomes a tag.
+\beginsyntax
+<declaration>\is<type><declaration list>
+<type>\is[boolean]\alt[string]\alt[path]\alt[pen]
+ \alt[picture]\alt[transform]\alt[pair]\alt[numeric]
+<declaration list>\is<declared variable>
+ \alt<declaration list>[,]<declared variable>
+<declared variable>\is<symbolic token><declared suffix>
+<declared suffix>\is<empty>\alt<declared suffix><tag>
+ \alt<declared suffix>\\{\char`\[}\\]
+\endsyntax
+
+\dangerexercise Find three errors in the supposed declaration
+`|transform| |t42,24t,,t,path|'.
+\answer (a)~The `|42|' is illegal because subscripts must be collective.
+\ (b)~The `|24|' is illegal because a \<declared variable> must start with
+a \<symbolic token>, not a numeric 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 \MF\ tries to declare
+the variable `|,t,path|'. However, `|path|' cannot appear in a suffix,
+since it's a spark. \ (Yes, this is admittedly tricky. Computers follow rules.)
+
+\endchapter
+
+Beings low in the scale of nature are
+more variable than those which are higher.
+\author CHARLES ^{DARWIN}, {\sl On the Origin of Species\/} (1859) % p149
+
+\bigskip
+
+Among the variables, {\rm Beta ({\cmman\char'14\/}) Persei}, or\/ {\rm^{Algol}},
+is perhaps the most interesting, as its period is short.
+\author J. NORMAN ^{LOCKYER}, {\sl Elements of Astronomy\/} (1870)
+ % American edition, p40
+
+\eject
+ \beginchapter Chapter 8. Algebraic\\Expressions
+
+\MF\ programmers express themselves algebraically by writing algebraic
+formulas called {\sl^{expressions}}. The formulas are algebraic in the
+sense that they involve variables as well as constants. By combining
+variables and constants with appropriate mathematical operations, a
+programmer can specify an amazing variety of things with comparative ease.
+
+We have already seen many examples of expressions; our goal now is to make
+a more systematic study of what is possible. The general idea is that an
+expression is either a ^{variable} (e.g., `$x_1$'\thinspace) or a
+^{constant} (e.g., `20'\thinspace), or it consists of an ^{operator}
+(e.g., `$+$'\thinspace) together with its ^{operands} (e.g.,
+`$x_1+20$'\thinspace). The operands are, in turn, expressions built~up in
+the same way, perhaps enclosed in ^{parentheses}. For example,
+`$(x_1+20)/(x_2-20)$' is an expression that stands for the quotient of two
+subexpressions. It is possible to concoct extremely complicated algebraic
+expressions, but even the most intricate constructions are built from
+simple parts in simple ways.
+
+Mathematicians spent hundreds of years developing good ways to write formulas;
+then computer scientists came along and upset all the time-honored traditions.
+The main reason for making a change was the fact that computers find it
+difficult to deal with two-dimensional constructions like
+\begindisplay
+$\displaystyle{x_1+20\over x_2-20}+\sqrt{a^2-{2\over3}\sqrt b}.$
+\enddisplay
+One-dimensional sequences of tokens are much easier to input and to decode;
+hence programming languages generally put such formulas all on one line,
+^^{sqrt} by inserting parentheses, brackets, and asterisks as follows:
+\begintt
+(x[1]+20)/(x[2]-20)+sqrt(a**2-(2/3)*sqrt(b)).
+\endtt
+\MF\ will understand this formula, but it also accepts a notation that
+is shorter and closer to the standard conventions of mathematics:
+\begintt
+(x1+20)/(x2-20)+sqrt(a**2-2/3sqrt b).
+\endtt
+We observed in the previous chapter that \MF\ allows you to write `|x2|'
+instead of `|x[2]|'; similarly, you can write `|2x|' instead of `|2*x|'
+and `|2/3x|' instead of `|(2/3)*x|'. Such operations are extremely common
+in \MF\ programs, hence the language has been set up to facilitate them.
+On the other hand, \MF\ doesn't free you from all the inconveniences of
+computer languages; you must still write `|x*k|' for the ^{product} of
+$x$ times~$k$, and `|x[k]|' for the variable $x$~subscripted by~$k$,
+in order to avoid confusion with the suffixed variable `|x.k|'.
+
+We learned in the previous chapter that there are eight types of
+variables: numeric, boolean, string, and so~on. The same types apply
+to expressions; \MF\ deals not only with numeric expressions but also
+with boolean expressions, string expressions, and the others. For example,
+`$(0,0)\to(x_1,y_1)$'
+is a path-valued expression, formed by applying the operator `$\to$' to the
+subexpressions `$(0,0)$' and `$(x_1,y_1)$'; these subexpressions, in turn,
+have values of type ``pair,'' and they have been built up from values of
+type ``numeric.'' Each operation produces a result whose type can be
+determined from the types of the operands; furthermore, the simplest
+expressions (variables and constants) always have a definite type.
+Therefore the machine always knows what type of quantity it is dealing
+with, after it has evaluated an expression.
+
+If an expression contains several operators, \MF\ has to decide which
+^^{order of operations}
+operation should be done first. For example, in the expression `$a-b+c$'
+it is important to compute `$a-b$' first, then to add~$c$; if `$b+c$' were
+computed first, the result `$a-(b+c)$' would be quite different from the
+usual conventions of mathematics. On the other hand, mathematicians
+usually expect `$b/c$' to be computed first in an expression like
+`$a-b/c$'; multiplications and divisions are usually performed before
+additions and subtractions, unless the contrary is specifically indicated
+by parentheses as in `$(a-b)/c$'. The general rule is to evaluate
+subexpressions in parentheses first, then to do operations in order of
+their ``^{precedence}''; if two operations have the same precedence, the
+left one is done first. For example, `$a-b/c$' is equivalent to
+`$a-(b/c)$' because division takes precedence over subtraction; but
+`$a-b+c$' is equivalent to `$(a-b)+c$' because left-to-right order is
+used on operators of equal precedence.
+
+It's convenient to think of operators as if they are tiny ^{magnets} that
+attract their operands; the magnets for `$\ast$' and `/' are stronger
+than the magnets for `$+$' and `$-$', so they stick to their operands more
+tightly and we want to perform them first.
+
+\MF\ distinguishes four (and only four) levels of precedence. The
+strongest magnets are those that join `2' to~`$x$' and `sqrt' to `$b$'
+in expressions like `$2x$' and `sqrt$\,b$'. The next strongest are
+multiplicative operators like `$\ast$' and~`/'; then come the additive
+operators like `$+$' and~`$-$'. The weakest magnets are operators like
+`$\to$' or `$<$'. For example, the expression
+\begindisplay
+$a+{\rm sqrt}\,b/2x<c$
+\enddisplay
+is equivalent to the fully parenthesized formula
+\begindisplay
+$\bigl(a+\bigl(({\rm sqrt}\,b)/(2x)\bigr)\bigr)<c$.
+\enddisplay
+
+\exercise Insert parentheses into the formula `|z1+z2..z3/4*5..z6-7*8z9|',
+to show explicitly in what order \MF\ will do the operations.
+\answer |((z1+z2)..((z3/4)*5))..(z6-(7*(8z9)))|.
+
+\danger High-school algebra texts often avoid parentheses inside of
+parentheses by using ^{braces} and ^{brackets}. Therefore many people
+have been trained to write
+\begindisplay
+$\{a+[({\rm sqrt}\,b)/(2x)]\}<c$
+\enddisplay
+instead of the fully parenthesized formula above. However, professional
+mathematicians usually stick to only one kind of parentheses, because
+braces and brackets have other meanings that are more important. In this
+respect \MF\ is like the professionals: It reserves curly braces `|{}|'
+and square brackets `|[]|' for special purposes, so you should not
+try to substitute them for parentheses.
+
+\ddanger If you really want alternatives to parentheses, there is actually
+a way to get them. You can say, for example,
+\begintt
+delimiters [[ ]]; delimiters {{ }}
+\endtt
+after which double brackets and braces can be used in formulas like
+\begintt
+{{a+[[(sqrt b)/(2x)]]}}<c.
+\endtt
+The symbolic token `|{{|' has no relation to `|{|', and it
+has no primitive meaning, hence you are free to define it in any way you
+like; the ^@delimiters@ command defines a new pair of delimiters. In formulas
+with mixed delimiters as defined here, \MF\ will check that `|[[|' matches only
+with~`|]]|', `|{{|'~only with~`|}}|', and `|(|'~only with~`|)|'; thus you
+can more easily detect errors in large expressions. However, it's usually
+unnecessary to have any delimiters other than parentheses, because large
+expressions are rare, and because the rules of operator precedence make
+most parentheses superfluous.
+
+If you're reading this chapter carefully, you may be thinking, ``Hey wait!
+Isn't there a contradiction? A minute ago I was told that `|2/3x|' stands
+for `|(2/3)*x|', but now the rules of precedence appear to state that
+`|2/3x|' really stands for `|2/(3x)|'. What gives?'' Indeed, you have an
+excellent point; but there is no contradiction, because of another rule that
+hasn't been mentioned yet. When two {\sl numeric tokens\/} are divided, the
+^^{division of numeric tokens} magnetism of `|/|' is stronger than usual;
+in this case `|/|' has the same precedence as the implied multiplication
+operator in `|3x|'. Hence the operations in `|2/3x|' are carried out from
+left to right, as stated previously. \ (This is a good rule because it
+is almost always what a \MF\ programmer wants. However, one should bear
+in mind that `|a/3x|' means `|a/(3x)|' when |a| is {\sl not\/} a numeric token.)
+
+Because of the rule in the previous paragraph, the \MF\ programs in this
+book often say `${2\over3}x$' for what would be typed `|2/3x|' in a file.
+Such built-up ^{fractions} are never used except when the numerator and
+denominator are both numbers; a construction like `|a/3x|' will always be
+rendered as `$a/3x$', not~`$\,{a\over3x}\,$'.
+
+\MF\ knows how to do dozens of operations that haven't been mentioned yet
+in this book. Let's take a look at some of them, so that we will know
+they are available in case of need. It will be most instructive and
+most fun to learn about expressions by interacting with the computer;
+^^"tracingonline" ^^@scrollmode@ ^^@forever@ ^^@scantokens@ ^^{readstring}
+^^@message@
+therefore you should prepare the following short file, called ^|expr.mf|:
+\begintt
+string s[]; s1="abra";
+path p[]; p1=(0,0)..(3,3); p2=(0,0)..(3,3)..cycle;
+tracingonline:=1; scrollmode;
+forever: message "gimme an expr: "; s0:=readstring;
+show scantokens s0; endfor
+\endtt
+
+\danger You don't need to understand what's in |expr.mf| when you read this
+chapter for the first time, because the file uses \MF\ in ways that will be
+explained carefully later. But here is a translation, in case you're
+curious: Line~1 declares all variables of the form $s_k$ to be strings, and
+sets $s_1$ to the value |"abra"|. Line~2 declares all variables of the
+form~$p_k$ to be paths, and sets $p_1$ and~$p_2$ to simple example paths.
+Line~3 tells \MF\ to print diagnostic information ^{online}, i.e., on the
+terminal as well as in the ^{log file}; it also establishes
+`@scrollmode@', which means that the computer won't stop after error
+messages. Lines 4 and~5 set up an infinite loop in which \MF\ reads an
+expression from the terminal and shows the corresponding value.
+
+\outer\def\begindemo{$$\advance\baselineskip by2pt
+ \catcode`\"=\other
+ \halign\bgroup\indent\hbox to 160pt{\tt##\hfil}&\tt##\hfil\cr
+ \noalign{\vskip-2pt}}
+\outer\def\enddemo{\egroup$$}
+\def\werror{\ \rm(with error message)}
+\def\werrors{\ \rm(with error messages)}
+\def\demohead{\it\kern-2pt You type&\it\kern-1pt And the result is\cr
+ \noalign{\nobreak\vskip2pt}}
+
+If you start \MF\ and type `|expr|' when it asks for an input file name,
+it will read the file |expr.mf| and then it will say `^|gimme|
+|an|~|expr|'. Here's where the fun starts: You can type any expression,
+and \MF\ will compute
+and display its value. Try it; type `|2+2|' and \<return>, obtaining the
+value~`|>>|~|4|'. Isn't that amazing? Here are some more things to try:
+\begindemo
+\demohead
+1.2-2.3&-1.1\cr
+1.3-2.4&-1.09999\cr
+1.3*1000&1300.00305\cr
+2.4*1000&2399.9939\cr
+3/8&0.375\cr
+.375*1000&375\cr
+1/3&0.33333\cr
+1/3*3&0.99998\cr
+0.99999&0.99998\cr
+1-epsilon&0.99998\cr
+1/(1/3)&3.00005\cr
+1/3.00005&0.33333\cr
+.1*10&1.00006\cr
+1+4epsilon&1.00006\cr
+\enddemo
+These examples illustrate the small errors that occur because \MF\ does
+``fixed binary'' ^{arithmetic} using integer multiples of $1\over65536$.
+The result of $1.3-2.4$ is not quite the same as $-1.1$, because |1.3| is
+a little bit larger than~$13\over10$ and |2.4| is a little smaller
+than~$24\over10$. Small errors get magnified when they are multiplied by
+1000, but even after magnification the discrepancies are negligible because
+they are just tiny fractions of a pixel. You may be surprised that
+1/3~times~3 comes out to be .99998 instead of .99999; the truth is that both
+|0.99999| and |0.99998| represent the same value, namely $65535\over65536$; \MF\
+displays this value as |0.99998| because it is closer to .99998 than to
+.99999. Plain \MF\ defines ^"epsilon" to be $1\over65536$, the smallest
+representable number that is greater than zero; therefore |1-epsilon|
+is $65535\over65536$, and |1+4epsilon| is $65540\over65536$.
+\begindemo
+\demohead
+4096&4095.99998\werror\cr
+infinity&4095.99998\cr
+1000*1000&32767.99998\werror\cr
+infinity+epsilon&4096\cr
+100*100&10000\cr
+.1(100*100)&1000.06104\cr
+(100*100)/3&3333.33333\cr
+\enddemo
+\MF\ will complain that an `|Enormous| ^^{enormous number} |number| |has|
+|been| |reduced|' when you try to introduce constants that are 4096 or~more.
+Plain \MF\ defines ^"infinity" to be $4096-"epsilon"$, which is the largest
+legal numeric token. On the other hand, it turns out that larger numbers
+can actually arise when an expression is being evaluated; \MF\ doesn't
+worry about this unless the resulting magnitude is at least 32768.
+
+\dangerexercise If you try `|100*100/3|' instead of `|(100*100)/3|', you
+get `|3333.33282|'. Why?
+\answer The fraction |100/3| is evaluated first (because such divisions
+take precedence); the rounding error in this fraction is then magnified by~100.
+
+\ddanger Sometimes \MF\ will compute things more accurately than you would
+expect from the examples above, because many of its internal calculations
+are done with multiples of $2^{-28}$ instead of $2^{-16}$. For example,
+if $t=3$ the result of `|1/3t|' will be exactly~1
+(not 0.99998); the same thing happens if you write `|1/3(3)|'.
+
+Now let's try some more complicated expressions, using undefined
+variables as well as constants. \ (Are you actually trying these
+examples, or are you just reading the book? It's far better to type
+them yourself and to watch what happens; in fact, you're also allowed
+to type things that {\sl aren't\/} in the book!)
+\begindemo
+\demohead
+b+a&a+b\cr
+a+b&a+b\cr
+b+a-2b&a-b\cr
+2(a-b+.5)&2a-2b+1\cr
+.5(b-a)&-0.5a+0.5b\cr
+.5[a,b]&0.5a+0.5b\cr
+1/3[a,b]&0.66667a+0.33333b\cr
+0[a,b]&a\cr
+a[2,3]&a+2\cr
+t[a,a+1]&t+a\cr
+a*b&b\werror\cr
+1/b&b\werror\cr
+\enddemo
+\MF\ has a preferred way to arrange variables in order when they are added
+together; therefore `$a+b$' and `$b+a$' give the same result. Notice that
+the ^{mediation} construction `$.5[a,b]$' specifies a number that's halfway
+between $a$ and~$b$, as explained in Chapter~2. \MF\ does not allow you to
+^{multiply} two unknown numeric quantities together, nor can you ^{divide} by an
+unknown numeric; all of the unknown expressions that \MF\ works with must be
+``^{linear forms},'' i.e., they must be sums of variables with constant
+coefficients, plus an optional constant. \ (You might want to try typing
+`|t[a,b]|' now, in order to see what error message is given.)
+\begindemo
+\demohead
+sqrt 2&1.41422\cr
+sqrt 100&10\cr
+sqrt 100*100&1000\cr
+sqrt(100*100)&100\cr
+sqrt 100(100)&100\cr
+sqrt sqrt 100(100)&10\cr
+sqrt .01&0.09998\cr
+0.09998**2&0.01\cr
+2**1/2&1.41422\cr
+sqrt 2**2&2\cr
+sqrt -1&0\werror\cr
+sqrt a&a\werror\cr
+\enddemo
+Since ^|sqrt| has more ``magnetism'' than |*|, the formula |sqrt|~|100*100|
+^^{square roots}
+is evaluated as |(sqrt|~|100)*100|; but in `|sqrt|~|100(100)|' the
+|100(100)| is computed first. The reason is that `|(sqrt|~|100)(100)|' isn't
+a legal expression, so the operations in `|sqrt|~|100(100)|' must be carried
+out from right to left. If you are unsure about the order of evaluation,
+^^|**| you can always insert parentheses; but you'll find that \MF's rules of
+precedence are fairly natural as you gain experience.
+
+\exercise Is `|sqrt|~|2**2|' computed as `|(sqrt|~|2)**2|' or as
+`|sqrt(2**2)|'\thinspace?
+\answer A |sqrt| takes precedence over any operation with two operands, hence
+the machine computes `|(sqrt|~|2)**2|'; \MF\ was somewhat lucky that the
+answer turned out to be exactly~2. \ (The |sqrt| operation computes the
+nearest multiple of $1\over65536$, and the rounding error in this quantity
+is magnified when it is squared. If you try |sqrt|~|3**2|, you'll get
+|3.00002|; also |sqrt|~|2**4| turns out to be |4.00002|.) \ Incidentally,
+the ^|**| operation of plain \MF\ 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 {\eightrm ^{FORTRAN}}.
+
+Some \MF\ expressions have `^{true}' or `^{false}' values, instead of numbers;
+we will see later that they can be used to adapt \MF\ programs to special
+conditions.
+\begindemo
+\demohead
+0<1&true\cr
+0=1&false\cr
+a+1>a&true\cr
+a>=b&false\werror\cr
+"abc"<="b"&true\cr
+"B">"a!"&false\cr
+"b">"a?"&true\cr
+(1,2)<>(0,4)&true\cr
+(1,2)<(0,4)&false\cr
+(1,a)>(0,b)&true\cr
+numeric a&true\cr
+known a&false\cr
+not pen a&true\cr
+known "a" and numeric 1&true\cr
+(0>1) or (a<a)&false\cr
+0>1 or a<a&a\werrors\cr
+\enddemo
+^^{not} ^^@and@ ^^@or@ ^^{comparison}
+The tokens `^|>=|', `^|<=|', and `^|<>|' stand respectively for the
+^{relations} ^{greater-than-or-equal-to}, ^{less-than-or-equal-to}, and
+^{unequal-to}. When strings are compared, \MF\ uses the order of words in
+a dictionary, except that it uses ASCII code to define ordering of individual
+characters; thus, all uppercase letters are considered to be less than all
+lowercase letters. \ (See Appendix~C\null.) \ When pairs of numbers are
+compared, \MF\ considers only the $x$~coordinates, unless the $x$~coordinates
+are equal; in the latter case it compares the $y$~coordinates. The type
+of an expression can be ascertained by an expression like `|pair|~|a|',
+which is true if and only if |a|~is a pair. ^^{pair} ^^{numeric} ^^{pen}
+The expression `|known|~|a|' ^^{known} is true if and only if the value
+of~|a| is fully known.
+
+\dangerexercise What causes the error messages in `|0>1|~|or|~|a<a|'\thinspace?
+\answer Since `^@or@' has stronger precedence than `$<$' or `$>$', ^^|<| ^^|>|
+\MF\thinspace\ tries to evaluate this expression by putting things in
+parentheses as follows: `$(0>(1\mathbin{\bf or}a))<a$'. Now
+`$1\mathbin{\bf or}a$' makes no sense, because `@or@' operates only on
+booleans; in such cases \MF\ uses the right operand~`$a$' as the result. Then
+`$\mkern1mu0>a$' is indeterminate because $a$~is unknown; \MF\ treats this as
+false. Finally `${\rm false}<a$' is another illegal combination of types.
+
+\danger The rest of this chapter is entirely preceded by ``dangerous bend''
+signs, so you can safely omit it on first reading (unless you're hooked
+and can't stop).
+
+\danger \MF\ expressions can include many operations that are
+less familiar but still useful. For example, the ^{max} and ^{min}
+operations compute the ^{maximum} and ^{minimum} of numbers, strings,
+or pairs:
+\begindemo
+\demohead
+max(1,-2,4)&4\cr
+min(1,-2,4)&-2\cr
+max("a","b","ab")&"b"\cr
+min("a","b","ab")&"a"\cr
+max((1,5),(0,6),(1,4))&(1,5)\cr
+min((1,5),(0,6),(1,4))&(0,6)\cr
+max(.5a+1,.5a-1)&0.5a+1\cr
+\enddemo
+Numbers can be converted to ^{integers} in a variety of ways:
+\begindemo
+\demohead
+floor 3.14159&3\cr
+floor -3.14159&-4\cr
+floor -epsilon&-1\cr
+floor infinity&4095\cr
+ceiling 3.14159&4\cr
+ceiling -3.14159&-3\cr
+round 3.14159&3\cr
+round -3.14159&-3\cr
+round(1.1,2.8)&(1,3)\cr
+round(3.5,-3.5)&(4,-3)\cr
+round a&a+0.5\werror\cr
+8 mod 3&2\cr
+-8 mod 3&1\cr
+.8 mod .3&0.2\cr
+\enddemo
+The `^{floor}' operation computes the ^{greatest integer} that is less than
+or equal to its operand; this quantity is often denoted by $\lfloor x\rfloor$
+in mathematics texts. Plain \MF\ also includes the analogous `^{ceiling}'
+operation $\lceil x\rceil$, which is the ^{least integer} greater than or
+equal to~$x$. Furthermore, `^{round}$\,x$' is the integer nearest to~$x$;
+plain \MF\ computes this by using the formula $\lfloor x+.5\rfloor$, and
+applies it to both components of a pair if a pair is being rounded. The
+^{remainder} of $x$ with respect to~$y$, written `$x\bmod y$', ^^{mod} is
+calculated by using the formula $x-y\lfloor x/y\rfloor$.
+\begindemo
+\demohead
+abs -7&7\cr
+abs(3,4)&5\cr
+length(3,4)&5\cr
+3++4&5\cr
+300++400&500\cr
+sqrt(300**2 + 400**2)&181.01933\werrors\cr
+1++1&1.4142\cr
+0 ++ -7&7\cr
+5+-+4&3\cr
+\enddemo
+^^{abs} ^^{length} ^^{absolute value}
+\def\stt{\vbox to7.25pt{}}%
+The `^|++|' operation is called {\sl^{Pythagorean addition}\/}; $a\pyth+b$
+is the same thing as $\sqrt{\stt a^2+b^2}$. Most of the ^{square root}
+operations in computer programs could probably be avoided if $++$ were
+more widely available, because people seem to want square roots primarily
+when they are computing distances. Notice that $a\pyth+b\pyth+c=
+\sqrt{\stt a^2+b^2+c^2}$; we have the identity $(a\pyth+b)\pyth+c=a\pyth+(
+b\pyth+c)$ as well as $a\pyth+b=b\pyth+a$. It is better to use Pythagorean
+addition than to calculate $\sqrt{\stt a^2+b^2}$, because the computation
+of $a^2$ and $b^2$ might produce numbers that are too large even when
+$a\pyth+b$ is rather small. There's also an inverse operation,
+^{Pythagorean subtraction}, which is denoted by `^|+-+|'; the quantity
+$a\mathbin{+{-}+}b$ is equal to $\sqrt{\stt a^2-b^2}$.
+
+\dangerexercise When the author was preparing these examples he typed
+`|0++-7|' and was surprised to get the answer `|0|'. Why should this not
+have been a surprise?
+\answer The token `|++-|' is undefined, so it is a tag; therefore
+|++-7| is a subscripted variable, which was multiplied by zero.
+
+\ddangerexercise (For mathematicians.) \ Although the Pythagorean addition
+operation is associative and commutative, \MF\ says that
+$5\pyth+4\pyth+2\pyth+2=7=2\pyth+2\pyth+4\pyth+5$ yet
+$2\pyth+4\pyth+5\pyth+2=6.99998$. Why?
+\answer The associative law is valid for exact computations, but not
+for rounded computations. For example, it fails even in the case of
+multiplication, since $(.1\ast.1)\ast10=0.09995$ while $.1\ast(.1\ast10)=.1$
+when products are rounded to the nearest multiples of $1\over65536$.
+However, this observation doesn't quite explain the stated example, which
+would have yielded 7 in all cases if \MF\ had computed $2\pyth+4$ with
+full accuracy! The closest approximation to $\sqrt{20}$ is ^^{accuracy}
+$4{30942\over65536}$, but $2\pyth+4$ turns out to be $4{30941\over65536}$
+instead. \MF\ 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.
+
+\danger \MF\ uses the names `^{sind}' and `^{cosd}' for the ^{trigonometric}
+functions ^{sine} and ^{cosine}, because \MF's operations are designed to
+deal with angles expressed in degrees. But it turns out that programmers
+rarely need to refer to sines and cosines explicitly, because the `^{dir}'
+and `^{angle}' functions provide most of what a font designer needs.
+\begindemo
+\demohead
+sind 30&0.5\cr
+cosd 30&0.86603\cr
+sind -30&-0.5\cr
+cosd 360&1\cr
+sind 10 ++ cosd 10&1\cr
+dir 30&(0.86603,0.5)\cr
+dir -90&(0,-1)\cr
+angle(1,1)&45\cr
+angle(1,2)&63.43495\cr
+angle(1,-2)&-63.43495\cr
+sind 63.43495 / cosd 63.43495&1.99997\cr
+angle up&90\cr
+angle left&180\cr
+angle(-1000,-epsilon)&-180\cr
+angle dir 60&60.00008\cr
+angle(0,0)&0\werror\cr
+\enddemo
+Plain \MF\ defines `dir$\,x$' to be the pair of values $(\mathop{\rm cosd}x,
+\mathop{\rm sind}x)$; this is a vector, which points $x$~degrees above the
+rightward horizon. Conversely, the `angle' operator determines the angle
+that corresponds to a given vector.
+
+\ddanger Logarithms and exponentials are computed with respect to an
+unusual base, designed to enhance the accuracy of calculations
+involving fixed-radix numbers in \MF's range. The values ^{mlog}$\,x=256\ln x$
+and ^{mexp}$\,x=e^{x/256}$ produce reasonably good results when
+$x\mathbin{\ast\ast}y$ is computed by the formula mexp$(y\ast\mathop{\rm
+mlog} x)$.
+\begindemo
+\demohead
+mlog 2&177.44568\cr
+mexp mlog 2&2\cr
+mexp 8 mlog 2&256\cr
+mexp 256&2.71828\cr
+mlog 2.71828&255.99954\cr
+mlog 2.71829&256.00098\cr
+%mlog mexp 2&1.99998\cr
+15 mlog 2&2661.68518\cr
+mexp 2661.68518&32767.99998\cr
+mexp 2661.68519&32767.99998\werror\cr
+mexp-2661.68519&0.00003\cr
+\enddemo
+
+\danger \MF\ also generates two flavors of random numbers. It is very
+unlikely that you will get the particular values shown in the following
+examples, when you do the experiment yourself, because the results come
+out different each time the computer is asked for a new random number
+(unless you have specified a ``seed value'' as explained in Chapter~21).
+\begindemo
+\it\kern-2pt You type&\it\kern-1pt And the result might be\cr
+\noalign{\vskip2pt}
+uniformdeviate 100&47.4241\cr
+uniformdeviate 100&97.28148\cr
+uniformdeviate -100&-36.16279\cr
+(normaldeviate,normaldeviate)&(0.46236,-1.87648)\cr
+\enddemo
+The value of `uniformdeviate\thinspace100' is a random number between 0 and~100;
+^^{uniformdeviate} ^^{normaldeviate}
+the value of `normaldeviate' is a normally distributed random number whose
+mean value is zero and whose standard deviation is unity. Chapter~21 explains
+what this means and gives several applications.
+
+\danger Besides all of these operations on numbers, \MF\ has a rich collection
+^^{scaled} ^^{xscaled} ^^{yscaled} ^^{dir}
+of operations on pairs, some of which are indicated in the following examples:
+\begindemo
+\demohead
+right&(1,0)\cr
+(1,2)+(3,4)&(4,6)\cr
+1/3(3,10)&(1,3.33333)\cr
+z2-z1&(-x1+x2,-y1+y2)\cr
+.2[z1,z2]&(0.8x1+0.2x2,0.8y1+0.2y2)\cr
+3z&(3x,3y)\cr
+z scaled 3&(3x,3y)\cr
+z xscaled 2 yscaled 1/2&(2x,0.5y)\cr
+z shifted (2,3)&(x+2,y+3)\cr
+z shifted 3right&(x+3,y)\cr
+z slanted 1/6&(x+0.16667y,y)\cr
+z rotated 90&(-y,x)\cr
+z rotated 30&(-0.5y+0.86603x,0.86603y+0.5x)\cr
+xpart(z rotated 30)&-0.5y+0.86603x\cr
+ypart(z rotated 30)&0.86603y+0.5x\cr
+(1,2)*(3,4)&(3,4)\werror\cr
+(1,2)zscaled(3,4)&(-5,10)\cr
+(a,b)zscaled(3,4)&(3a-4b,4a+3b)\cr
+(a,b)zscaled dir 30&(0.86603a-0.5b,0.5a+0.86603b)\cr
+(1,2)dotprod(3,4)&11\cr
+(a,b)dotprod(3,4)&3a+4b\cr
+dir 21 dotprod dir 51&0.86603\cr
+(3,4)dotprod((30,40)rotated 90)&0\cr
+\enddemo
+(Recall that plain \MF\ converts `|z$|' into `|(x$,y$)|' when |$| is any
+\<suffix>.) \ ^^{xpart} ^^{ypart} ^^{shifted} ^^"right" ^^{slanted}
+^^{zscaled} ^^{dotprod} ^^"z" The operations exhibited here are almost
+all self-evident. When a point or vector is ^{rotated}, it is moved
+counterclockwise about $(0,0)$ through a given number
+of degrees. \MF\ computes the rotated coordinates by using
+^{sines} and ^{cosines} in an appropriate way; you don't have to
+remember the formulas! Although you cannot use `|*|' to multiply
+a pair by a pair, you can use `^{zscaled}' to get the effect of
+^{complex number} multiplication: Since $(1+2i)$ times $(3+4i)$ is
+$-5+10i$, we have $(1,2)\mathbin{\rm zscaled}(3,4)=(-5,10)$.
+There's also a ^{multiplication} that converts pairs into numbers:
+$(a,b)\mathbin{\rm dotprod}(c,d\mkern1mu)=ac+bd$. This is the
+``^{dot product},'' often written `$(a,b)\cdot(c,d\mkern1mu)$' in
+mathematics texts; it turns out to be equal to $a\pyth+b$ times
+$c\pyth+d$ times the cosine of the angle between the vectors $(a,b)$ and
+$(c,d)$. Since cosd$\,90^\circ=0$, two vectors are
+^{perpendicular} to each other if and only if their dot ^{product} is zero.
+
+\danger There are operations on strings, paths, and the other types too;
+we shall study such things carefully in later chapters. For now, it will
+suffice to give a few examples, keeping in mind that the file |expr.mf|
+defines |s| with any subscript to be a ^{string}, while |p| with any subscript
+is a path. Furthermore $s_1$ has been given the value |"abra"|, while
+$p_1$ is `$(0,0)\to(3,3)$' and $p_2$ is `$(0,0)\to(3,3)\to\cycle$'.
+\begindemo
+\demohead
+s2&unknown string s2\cr
+s1\&"cad"\&s1&"abracadabra"\cr
+length s1&4\cr
+length p1&1\cr
+length p2&2\cr
+cycle p1&false\cr
+cycle p2&true\cr
+substring (0,2) of s1&"ab"\cr
+substring (2,infinity) of s1&"ra"\cr
+point 0 of p1&(0,0)\cr
+point 1 of p1&(3,3)\cr
+point .5 of p1&(1.5,1.5)\cr
+point infinity of p1&(3,3)\cr
+point .5 of p2&(3,0)\cr
+point 1.5 of p2&(0,3)\cr
+point 2 of p2&(0,0)\cr
+point 2+epsilon of p2&(0.00009,-0.00009)\cr
+point -epsilon of p2&(-0.00009,0.00009)\cr
+point -1 of p1&(0,0)\cr
+direction 0 of p1&(1,1)\cr
+direction 0 of p2&(4,-4)\cr
+direction 1 of p2&(-4,4)\cr
+\enddemo
+^^{point} ^^{direction}
+The ^{length} of a path is the number of `$\to$' steps that it contains;
+the construction `^|cycle|~\<path>' can be used to tell whether or not a
+particular path is cyclic. If you say just `|p1|' you get to see
+path~$p_1$ with its ^{control points}:
+\begintt
+(0,0)..controls (1,1) and (2,2)
+ ..(3,3)
+\endtt
+Similarly, `|p2|' is
+\begintt
+(0,0)..controls (2,-2) and (5,1)
+ ..(3,3)..controls (1,5) and (-2,2)
+ ..cycle
+\endtt
+and `|subpath| |(0,1)| |of| |p2|' is analogous to a ^{substring}:^^{subpath}
+\begintt
+(0,0)..controls (2,-2) and (5,1)
+ ..(3,3)
+\endtt
+The expression `point $t$ of $p_2$' gives the position of a point that
+moves along path~$p_2$, starting with the initial point $(0,0)$ at $t=0$,
+then reaching point $(3,3)$ at $t=1$, etc.;
+ the value at $t=1/2$ is the
+third-order midpoint obtained by the construction of Chapter~3, using
+intermediate control points $(2,-2)$ and $(5,1)$.
+Since $p_2$ is a cyclic path of length~2,
+point $(t+2)$ of~$p_2$ is the same as point~$t$. Path $p_1$ is not
+cyclic, so its points turn out to be identical to point~0 when $t<0$,
+and identical to point~1 when $t>1$. The expression `direction~$t$
+of~\<path>' is similar to `point~$t$ of \<path>'; it yields a vector for the
+direction of travel at time~$t$.
+
+{\ninepoint
+\medbreak
+\parshape 14 3pc 12pc 3pc 12pc
+0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc
+0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 29pc
+\noindent
+\hbox to0pt{\hskip-3pc\dbend\hfill}%
+\rightfig 8a (12pc x 12pc) ^16pt
+Paths are not necessarily traversed at constant speed. For example,
+the diagram at the right shows point $t$ of~$p_2$ at twenty equally
+spaced values of~$t$.
+\MF\ moves faster in this case at time~1.0 than at time 1.2; but the
+points are spread out fairly well, so the concept of fractional
+time can be useful. The diagram shows, incidentally, that
+path~$p_2$ is not an especially good approximation to
+a circle; there is no left-right symmetry, although the curve from point~1
+to point~2 is a mirror image of the curve from point~0 to point~1.
+This lack of circularity is not surprising, since
+$p_2$ was defined by simply specifying two points, $(0,0)$ and~$(3,3)$;
+at least four points are needed to get a path that is convincingly round.
+\parfillskip=0pt\par}
+
+\ddanger The ^{ampersand} operation `|&|' can be used to splice paths
+together in much the same way as it concatenates strings. For example, if
+you type `|p2|~|&|~|p1|', you get the path of length~3 that is obtained by
+breaking the cyclic connection at the end of path~$p_2$ and attaching~$p_1$:
+\begintt
+(0,0)..controls (2,-2) and (5,1)
+ ..(3,3)..controls (1,5) and (-2,2)
+ ..(0,0)..controls (1,1) and (2,2)
+ ..(3,3)
+\endtt
+Concatenated paths must have identical endpoints at the junction.
+
+\ddanger You can even ``slow down the clock'' by concatenating subpaths
+that have non-integer time specifications. For example, here's what you
+get if you ask for `|subpath|~|(0,.5)| |of|~|p2| |&| |subpath| |(.5,2)|
+|of|~|p2| |&| |cycle|':
+\begintt
+(0,0)..controls (1,-1) and (2.25,-0.75)
+ ..(3,0)..controls (3.75,0.75) and (4,2)
+ ..(3,3)..controls (1,5) and (-2,2)
+ ..cycle
+\endtt
+When $t$ goes from 0 to 1 in subpath $(0,.5)$ of $p_2$, you get the same
+points as when $t$ goes from 0 to~.5 in $p_2$; when $t$ goes from 0 to 1
+in subpath $(.5,2)$ of~$p_2$, you get the same points as when $t$ goes
+from .5 to~1 in~$p_2$; but when $t$ goes from 1 to~2 in subpath
+$(.5,2)$ of~$p_2$, it's the same as the segment from 1 to~2 in~$p_2$.
+
+\danger Let's conclude this chapter by discussing the exact rules of
+^{precedence} by which \MF\ decides what operations to do first. The
+informal notion of ``magnetism'' gives a good intuitive picture of what
+happens, but syntax rules express things unambiguously in borderline cases.
+
+\danger The four levels of precedence correspond to four kinds of formulas,
+which are called primaries, secondaries, tertiaries, and
+expressions. A {\sl^{primary}\/} is a~variable or a constant or a
+tightly bound unit like `|2x|' or `|sqrt 2|'; a {\sl^{secondary}\/}
+is~a primary or a sequence of primaries connected by multiplicative
+operators like `|*|' or `|scaled|'; a {\sl^{tertiary}\/} is a secondary
+or a sequence of secondaries connected by additive operators like `|+|'
+or `|++|'; an {\sl^{expression}\/} is a tertiary or a sequence of
+tertiaries connected by external operators like `|<|' or `|..|'. For example,
+the expression
+\begintt
+a+b/2>3c*sqrt4d
+\endtt
+is composed of the primaries `|a|', `|b|', `|2|', `|3c|', and `|sqrt4d|';
+the last of these is a primary containing `|4d|' as a primary within itself.
+The subformulas `|a|', `|b/2|', and `|3c*sqrt4d|' are secondaries; the
+subformulas `|a+b/2|' and `|3c*sqrt4d|' are tertiaries.
+
+\danger If an expression is enclosed in parentheses, it becomes a primary
+that can be used to build up larger secondaries, tertiaries, etc.
+
+\danger The full syntax for expressions is quite long, but most of it
+falls into a simple pattern. If $\alpha$, $\beta$, and~$\gamma$ are
+any ``types''---numeric, boolean, string, etc.---then \<$\alpha$ variable>
+refers to a variable of type~$\alpha$, \<$\beta$ primary> refers to a
+primary of type~$\beta$, and so on. Almost all of the syntax rules fit into
+the following general framework:
+\beginsyntax
+<$\alpha$ primary>\is<$\alpha$ variable>\alt<$\alpha$ constant>%
+ \alt[(]<$\alpha$ expression>[)]
+ \alt<operator that takes type $\beta$ to type $\alpha$><$\beta$ primary>
+<$\alpha$ secondary>\is\<$\alpha$ primary>
+ \alt<$\beta$ secondary><multiplicative op taking types $\beta$ and %
+ $\gamma$ to $\alpha$><$\gamma$ primary>\kern-1pt
+<$\alpha$ tertiary>\is\<$\alpha$ secondary>
+ \alt<$\beta$ tertiary><additive op taking types $\beta$ and %
+ $\gamma$ to $\alpha$><$\gamma$ secondary>
+<$\alpha$ expression>\is<$\alpha$ tertiary>
+ \alt<$\beta$ expression><external op taking types $\beta$ and %
+ $\gamma$ to $\alpha$><$\gamma$ tertiary>
+\endsyntax
+These schematic rules don't give the whole story, but they do give the
+general structure of the plot.
+
+\danger Chapter 25 spells out all of the syntax rules for all types of
+expressions. We shall consider only a portion of the numeric and pair
+cases here, in order to have a foretaste of the complete menu:
+\def\\#1{\thinspace{\tt#1}\thinspace}
+\beginsyntax
+<numeric primary>\is<numeric atom>
+ \alt<numeric atom>[\char'133]<numeric expression>%
+ [,]<numeric expression>[\char'135]
+ \alt[length]<string primary>
+ \alt[length]<path primary>
+ \alt[length]<pair primary>
+ \alt[angle]<pair primary>
+ \alt[xpart]<pair primary>
+ \alt[ypart]<pair primary>
+ \alt<numeric operator><numeric primary>
+<numeric atom>\is<numeric variable>
+ \alt<numeric token primary>
+ \alt[(]<numeric expression>[)]
+ \alt[normaldeviate]
+<numeric token primary>\is<numeric token>[/]<numeric token>
+ \alt<numeric token not followed by %
+ `{\tt/}$\thinspace\langle$numeric token$\rangle$'\thinspace>
+<numeric operator>\is[sqrt]\alt[sind]\alt[cosd]\alt[mlog]\alt[mexp]
+ \alt[floor]\alt[uniformdeviate]\alt<scalar multiplication operator>
+<scalar multiplication operator>\is<plus or minus>
+ \alt<numeric token primary not followed by %
+ {\tt+} or {\tt-} or a numeric token>
+<numeric secondary>\is<numeric primary>
+ \alt<numeric secondary><times or over><numeric primary>
+<times or over>\is[*]\alt[/]
+<numeric tertiary>\is<numeric secondary>
+ \alt<numeric tertiary><plus or minus><numeric secondary>
+ \alt<numeric tertiary><Pythagorean plus or minus><numeric secondary>
+<plus or minus>\is[+]\alt[-]
+<Pythagorean plus or minus>\is[++]\alt[+-+]
+<numeric expression>\is<numeric tertiary>
+\endsyntax
+All of the finicky details about ^{fractions} and such things are made
+explicit by this syntax. For example, we can use the rules to deduce that
+`|sind-1/3x-2|' is interpreted as `|(sind(-(1/3x)))-2|'; notice that the
+first minus sign in this formula is considered to be a ``scalar multiplication
+operator,'' which comes in at the primary level, while the second one denotes
+subtraction and enters in the construction of \<numeric tertiary>. The
+^{mediation} or ``^{of-the-way}'' operation `$t[a,b]$' is handled at the
+primary level.
+
+\danger Several operations that haven't been discussed yet do not appear
+in the syntax above, but they fit into the same general pattern; for example,
+we will see later that `^|ASCII|\<string primary>' and `^|xxpart|\<transform
+primary>' are additional cases of the syntax for \<numeric primary>.
+On the other hand, several operations that we have discussed in this chapter
+do not appear in the syntax, because they are not primitives of \MF\ itself;
+they are defined in the plain \MF\ base (Appendix B\null). For example,
+`^|ceiling|' is analogous to `|floor|', and `^|**|' is analogous to~`|*|'.
+Chapter~20 explains how \MF\ allows extensions to its built-in syntax,
+so that additional operations can be added at will.
+
+\dangerexercise How does \MF\ interpret `|2|~|2|'\thinspace?
+\ (There's a space between the 2's.)
+\answer It's impossible to make an expression from `\<numeric token>
+\<numeric token>', because the rule for \<scalar multiplication operator>
+specifically prohibits this. \MF\ will recognize the first `|2|' as
+a \<numeric primary>, which is ultimately regarded as a \<numeric
+expression>; the other `|2|' will probably be an extra token that is
+flushed away after an error message has been given.
+
+\ddangerexercise According to |expr.mf|, the value of `|1/2/3/4|' is
+|0.66667|; the value of `|a/2/3/4|' is |0.375a|. Explain why.
+\answer 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 using the first 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|'.
+
+\danger The rules of \<pair expression> are similar to those for
+\<numeric expression>, so it's convenient to learn them both at the same time.
+\beginsyntax
+<pair primary>\is<pair variable>
+ \alt[(]<numeric expression>[,]<numeric expression>[)]
+ \alt[(]<pair expression>[)]
+ \alt<numeric atom>[\char'133]<pair expression>%
+ [,]<pair expression>[\char'135]
+ \alt[point]<numeric expression>[of]<path primary>
+ \alt<scalar multiplication operator><pair primary>
+<pair secondary>\is<pair primary>
+ \alt<pair secondary><times or over><numeric primary>
+ \alt<numeric secondary>[*]<pair primary>
+ \alt<pair secondary><transformer>
+<transformer>\is[rotated]<numeric primary>
+ \alt[scaled]<numeric primary>
+ \alt[shifted]<pair primary>
+ \alt[slanted]<numeric primary>
+ \alt[transformed]<transform primary>
+ \alt[xscaled]<numeric primary>
+ \alt[yscaled]<numeric primary>
+ \alt[zscaled]<pair primary>
+<pair tertiary>\is<pair secondary>
+ \alt<pair tertiary><plus or minus><pair secondary>
+<pair expression>\is<pair tertiary>
+\endsyntax
+
+\dangerexercise Try to guess the syntax rules for \<string primary>,
+\<string secondary>, $\langle$string tertiary$\rangle$, and \<string
+expression>, based solely on the examples that have appeared in this
+chapter. \ [{\sl Hint:}\/ The `|&|' operation has the same precedence
+as `|..|'.]
+\answer \<string primary>\is\<string variable>\parbreak
+\qquad\alt\<string token>\parbreak
+\def\\#1{\thinspace{\tt#1}\thinspace}%
+\qquad\alt\\(\<string expression>\\)\parbreak
+\qquad\alt\\{substring}\<pair expression>\\{of}\<string primary>\parbreak
+\<string secondary>\is\<string primary>\parbreak
+\<string tertiary>\is\<string secondary>\parbreak
+\<string expression>\is\<string tertiary>\parbreak
+\qquad\alt\<string expression>\\{\char`\&}\<string tertiary>\par
+\medskip\noindent
+(The full syntax in Chapter~25 includes several more varieties of
+\<string primary> that haven't been hinted at yet.)
+
+\endchapter
+
+A maiden was sitting there who was lovely as any picture,
+% ein bildsch\"one Jungfrau,
+nay, so beautiful that no words can express it.
+% nein so sch\"on, dass es nicht so sagen ist.
+\author JAKOB and WILHELM ^{GRIMM}, {\sl Fairy Tales\/} (1815)
+ % Kinder- und hausm\"archen, vol 2, #166; translated by Margaret Hunt
+ % in Strong Hans (Der starke Hans), about 4/5 of the way through
+ % This quote and the next were found by online computer search at SAIL
+ % in the files GRIMM[lib,doc] and WUTHER[lib,doc]
+
+\bigskip
+
+He looked astonished at the expression.
+ % my face assumed... middle of chapter 13
+\author EMILY ^{BRONT\"E}, {\sl Wuthering Heights\/} (1847)
+
+\eject
+ \beginchapter Chapter 9. Equations
+
+The variables in a \MF\ program receive their values by appearing in
+{\sl^{equations}}, which express relationships that the programmer
+wants to achieve. We've seen in the previous chapter that algebraic
+expressions provide a rich language for dealing with both numerical
+and graphical relationships. Thus it is possible to express a great
+variety of design objectives in precise form by stating that certain
+algebraic expressions should be equal to each other.
+
+The most important things a \MF\ programmer needs to know about
+equations are (1)~how to translate intuitive design concepts into
+formal equations, and (2)~how to translate formal equations into
+intuitive design concepts. In other words, it's important to be able
+to {\sl write\/} equations, and it's also important to be able to
+{\sl read\/} equations that you or somebody else has written. This
+is not nearly as difficult as it might seem at first. The best way
+to learn~(1) is to get a lot of practice with~(2) and to generalize
+from specific examples. Therefore we shall begin this chapter by
+translating a lot of equations into ``simple English.''
+
+\newdimen\longesteq
+\setbox0=\hbox{\indent$z_{12}-z_{11}=z_{14}-z_{13}$\quad}
+\longesteq=\wd0
+\def\\#1\\{\medbreak\noindent
+ \hbox to\longesteq{\indent#1\hfil}%
+ \hangindent\longesteq\ignorespaces}
+\medskip
+\noindent\hbox to\longesteq{\indent\kern-1pt\sl Equation\hfil}%
+\kern-1pt{\sl Translation}\smallskip
+
+\\$a=3.14$\\
+The value of $a$ should be 3.14.
+
+\\$3.14=a$\\
+The number 3.14 should be the value of $a$. \ (This means the same
+thing as `$a=3.14$'; the left and right sides of an equation can be
+interchanged without affecting the meaning of that equation in any way.)
+
+\\$"mode"="smoke"$\\
+The value of ^"mode" should be equal to the value of ^"smoke". \
+(Plain \MF\ assigns a special meaning to `"smoke"', so that if
+^@mode\_setup@ is invoked when $"mode"="smoke"$ the computer will
+prepare ``smoke proofs'' as explained in Chapter~5 and Appendix~H.)
+
+\\$y_3=0$\\
+The $y$ coordinate of point 3 should be zero; i.e., point~3 should
+be at the ^{baseline}. \ (Point~3 is also known as~$z_3$, which is an
+abbreviation for the pair of coordinates $(x_3,y_3)$, if you are
+using the conventions of plain \MF\!.)
+
+\\$x_9=0$\\
+The $x$ coordinate of point 9 should be zero; i.e., point~9 should
+be at the left edge of the type box that encloses the current character.
+
+\\$x_{1l}="curve\_sidebar"$\\
+The $x$ coordinate of point $1l$ should be equal to the value of the
+variable called "curve\_sidebar". This puts $z_{1l}$ a certain
+distance from the left edge~of the type.
+
+\\$x_1=x_2$\\
+Points 1 and 2 should have the same $x$ coordinate; i.e., they should
+have the same horizontal position, so that one will lie directly
+above or below the other.
+
+\\$y_4=y_5+1$\\
+Point 4 should be one pixel higher than point~5.
+\ (However, points 4 and~5 might be far apart; this equation
+says nothing about the relation between $x_4$ and~$x_5$.)
+
+\\$y_6=y_7+2"mm"$\\
+Point 6 should be two millimeters higher than point~7. \ (Plain \MF's
+^@mode\_setup@ routine sets variable ^"mm" to the number of pixels in a
+millimeter, based on the resolution determined by "mode" and "mag".)
+
+\\$x_4=w-.01"in"$\\
+Point 4 should be one-hundredth of an inch inside the right edge of
+the type. \ (Plain \MF's ^@beginchar@ routine sets variable~^"w" equal
+to the width of whatever character is currently being drawn, expressed in
+pixels.)
+
+\\$y_4=.5h$\\
+Point 4 should be halfway between the baseline and the top of the type.
+\ (Plain \MF's @beginchar@ sets ^"h" to the height of the
+current character, in pixels.)
+
+\\$y_6=-d$\\
+Point 6 should be below the baseline, at the bottom edge of the type.
+\ (Each character has a ``^{bounding box}'' that runs from $(0,h)$
+at the upper left and $(w,h)$ at the upper right to $(0,-d)$ and~$(w,-d)$
+at the lower left and lower right; variable~^"d" represents the depth of
+the type. The values of $w$, $h$, and~$d$ might change from character to
+character, since the individual pieces of type in a computer-produced
+font need not have the same size.)
+
+\\$y_8=.5[h,-d]$\\
+Point 8 should be halfway between the top and bottom edges of the type.
+
+\\$w-x_5={2\over3}x_6$\\
+The distance from point 5 to the right edge of the type should be
+two-thirds of the distance from point~6 to the left edge of the~type.
+\ (Since $w$ is at the right edge, $w-x_5$ is the ^{distance} from
+point~5 to the right edge.)
+
+\\$z_0=(0,0)$\\
+Point 0 should be at the ^{reference point} of the current character,
+i.e., it should be on the baseline at the left edge of the type.
+This equation is an abbreviation for two equations, `$x_0=0$' and `$y_0=0$',
+because an equation between pairs of coordinates implies that the $x$
+and~$y$ coordinates must both agree. \ (Incidentally, plain \MF\
+defines a variable called ^"origin" whose value is $(0,0)$; hence
+this equation could also have been written `$z_0="origin"$'.)
+
+\\$z_9=(w,h)$\\
+Point 9 should be at the upper right corner of the current character's
+bounding box.
+
+\\$"top"\,z_8=(.5w,h)$\\
+If the pen that has currently been ``picked up'' is placed at point~8,
+its top edge should be at the top edge of the type. Furthermore,
+$x_8$~should be $.5w$; i.e., point~8 should be centered between the
+left and right edges of the type. \ (Chapter~4 contains further
+examples of `^"top"', as well as the corresponding operations
+`"bot"', `"lft"', and `"rt"'.)
+
+\\$z_4={3\over7}[z_5,z_6]$\\
+Point 4 should be three-sevenths of the way from point~5 to point~6.
+
+\\$z_{12}-z_{11}=z_{14}-z_{13}$\\
+The ^{vector} that moves from point 11 to point~12 should be the same
+as the vector that moves from point~13 to point~14. In other words,
+point~12 should have the same direction and distance from point~11
+as point~14 has from point~13.
+
+\\\smash{\vtop{\hbox{$z_3-z_2=$}
+ \hbox{\quad$(z_4\!-\!z_2)$\thinspace rotated\thinspace 15}}}\\
+Points 3 and 4 should be at the same distance from point~2, but
+the direction to point~3 should be 15~degrees counterclockwise from
+the direction to point~4.
+
+\exercise Translate the following equations into ``simple English'':
+\ (a)~$x_7-9=x_1$; \ (b)~$z_7=(x_4,.5[y_4,y_5])$; \
+(c)~$"lft"\,z_{21}="rt"\,z_{20}+1$.
+\answer (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 $y_1$ or $y_7$. \ (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.)
+
+\exercise Now see if your knowledge of equation reading gives you the
+ability to write equations that correspond to the following objectives:
+\ (a)~Point~13 should be just as far below the baseline as point~11 is
+above the baseline. \ (b)~Point~10 should be one millimeter to the right
+of, and one pixel below, point~12. \ (c)~Point~43 should be one-third of
+the way from the top left corner of the type to the bottom right corner
+of the type.
+\answer (a) $y_{13}=-y_{11}$ (or $-y_{13}=y_{11}$, or $y_{13}+y_{11}=0$).
+\ (b)~$z_{10}=z_{12}+("mm",-1)$. \ (c)~$z_{43}={1\over3}[(0,h),(w,-d)]$.
+
+Let's return now to the six example points $(z_1,z_2,z_3,z_4,z_5,z_6)$
+that were used so often in Chapters 2 and~3. Changing the notation
+slightly, we might say that the points are
+\begindisplay
+$(x_1,y_1)=(0,h)$;&$(x_2,y_2)=(.5w,h)$;&$(x_3,y_3)=(w,h)$;\cr
+$(x_4,y_4)=(0,0)$;&$(x_5,y_5)=(.5w,0)$;&$(x_6,y_6)=(w,0)$.\cr
+\enddisplay
+There are many ways to specify these points by writing a series of
+equations. For example, the six equations just given would do fine;
+or the short names $z_1$ through~$z_6$ could be used instead of the
+long names $(x_1,y_1)$ through~$(x_6,y_6)$. But there are several
+other ways to specify those points and at the same time to ``explain''
+the relations they have to each other. One way is to define the
+$x$ and~$y$ coordinates separately:
+\begindisplay
+$x_1=x_4=0;\qquad x_2=x_5=.5w;\qquad x_3=x_6=w;$\cr
+$y_1=y_2=y_3=h;\qquad y_4=y_5=y_6=0$.\cr
+\enddisplay
+\MF\ allows you to state several equations at once, by using more than
+^^{=} one equality sign; for example, `$y_1=y_2=y_3=h$' stands for three
+equations, `$y_1=y_2$', `$y_2=y_3$', and `$y_3=h$'.
+
+In order to define the coordinates of six points, it's necessary to
+write twelve equations, because each equation contributes to the
+definition of one value, and because six points have twelve coordinates
+in all. However, an equation between pairs of coordinates counts as
+two equations between single numbers; that's why we were able to get by
+with only six `$=$'~signs in the first set of equations, while twelve
+were used in the second.
+
+Let's look at yet another way to specify those six points, by giving
+equations for their positions relative to each other:
+\begindisplay
+$z_1-z_4=z_2-z_5=z_3-z_6$\cr
+$z_2-z_1=z_3-z_2=z_5-z_4=z_6-z_5$\cr
+$z_4="origin"$; \ $z_3=(w,h)$.\cr
+\enddisplay
+^^"origin" First we say that the vectors from $z_4$ to~$z_1$,
+from $z_5$ to~$z_2$, and from $z_6$ to~$z_3$, are equal to each other;
+then we say the same thing for the vectors from $z_1$ to~$z_2$,
+$z_2$ to~$z_3$, $z_4$ to~$z_5$, and $z_5$ to~$z_6$. Finally the
+corner points $z_4$ and $z_3$ are given explicitly. That's a total
+of seven equations between pairs of coordinates, so it should be
+more than enough to define the six points of interest.
+
+However, it turns out that those seven equations are not enough!
+For example, the six points
+\begindisplay
+$z_1=z_4=(0,0)$; \ $z_2=z_5=(.5w,.5h)$; \ $z_3=z_6=(w,h)$
+\enddisplay
+also satisfy the same equations. A closer look explains why:
+The two formulas
+\begindisplay
+$z_1-z_4=z_2-z_5$\qquad and\qquad $z_2-z_1=z_5-z_4$
+\enddisplay
+actually say exactly the same thing. \ (Add $z_5-z_1$ to both sides
+of the first equation and you get `$z_5-z_4=z_2-z_1$'.) \ Similarly,
+$z_2-z_5=z_3-z_6$ is the same as $z_3-z_2=z_6-z_5$. Two of the
+seven equations give no new information, so we really have specified
+only five equations; that isn't enough. An additional relation
+such as `$z_1=(0,h)$' is needed to make the solution unique.
+
+\dangerexercise (For mathematicians.) \ Find a solution to the seven
+equations such that $z_1=z_2$. Also find another solution in which
+$z_1=z_6$.
+\answer (a) $z_1=z_2=z_3=(w,h)$; $z_4=z_5=z_6=(0,0)$.
+\ (b)~$z_1=z_6=(.5w,.5h)$; $z_2=(.75w,.75h)$; $z_3=(w,h)$;
+$z_4=(0,0)$; $z_5=(.25w,.25h)$.
+
+At the beginning of a \MF\ program, variables have no values,
+except that plain \MF\ has assigned special values to variables
+like "smoke" and "origin". Furthermore, when you begin a new
+character with @beginchar@, any previous values that may have been
+assigned to $x$ or $y$ variables are obliterated and forgotten.
+Values are gradually established as the computer reads equations and
+tries to solve them, together with any other equations that have already
+appeared in the program.
+
+It takes ten equations to define the values of ten variables.
+If you have given only nine equations it may turn out that none of
+the ten variables has yet been determined; for example, the
+nine equations
+\begindisplay
+$g_0=g_1=g_2=g_3=g_4=g_5=g_6=g_7=g_8=g_9$
+\enddisplay
+don't tell us any of the $g$ values. However, the further equation
+\begindisplay
+$g_0+g_1=1$
+\enddisplay
+will cause \MF\ to deduce that all ten of the $g$'s are equal to $1\over2$.
+
+\MF\ always computes the values of as many variables as possible, based
+on the equations it has seen so far. For example, after the two equations
+\begindisplay
+$a+b+2c=3$;\cr
+$a-b-2c=1$\cr
+\enddisplay
+the machine will know that $a=2$ (because the sum of these two equations is
+`$2a=4$'); but all it will know about $b$ and~$c$ is that $b+2c=1$.
+
+At any point in a program a variable is said to be either ``^{known}''
+or ``^{unknown},'' depending on whether or not its value can be
+deduced uniquely from the equations that have been stated so far.
+The sample expressions in Chapter~8 indicate that \MF\ can compute a
+variety of things with unknown variables; but sometimes a quantity
+must be known before it can be used. For example, \MF\ can multiply
+an unknown numeric or pair variable by a known numeric value, but it
+cannot multiply two unknowns.
+
+Equations can be given in any order, except that you might sometimes
+need to put certain equations first in order to make critical
+values known in the others. For example, \MF\ will find the
+solution $(a,b,c)=(2,7,-3)$ to the equations `$a+b+2c=3$;
+$a-b-2c=1$; $b+c=4$' if you give those equations in any other order,
+like `$b+c=4$; $a-b-2c=1$; $a+b+2c=3$'. But if the equations had
+been `$a+b+2c=3$; $a-b-2c=1$; $a\ast(b+c)=8$', you would not have
+been able to give the last one first, because \MF\ would have refused
+to multiply the unknown quantity~$a$ by another unknown quantity $b+c$.
+Here are the main things that \MF\ can do with unknown quantities:
+\begindisplay
+$-\<unknown>$\cr
+$\<unknown>+\<unknown>$\cr
+$\<unknown>-\<unknown>$\cr
+$\<unknown>\ast\<known>$\cr
+$\<known>\ast\<unknown>$\cr
+$\<unknown>/\<known>$\cr
+$\<known>[\<unknown>,\<unknown>]$\cr
+$\<unknown>[\<known>,\<known>]$\cr
+\enddisplay
+Some of the operations of plain \MF\!, defined in Appendix~B\null, also work
+with unknown quantities. For example, it's possible to say
+^"top"\thinspace\<unknown>, ^"bot"\thinspace\<unknown>,
+^"lft"\thinspace\<unknown>, ^"rt"\thinspace\<unknown>, and even
+\begindisplay
+@penpos@\<suffix>(\<unknown>,\thinspace\<known>).
+\enddisplay
+
+\danger A \MF\ program can say `\<unknown>$[a,b\mkern1mu]$' when $a-b$ is
+known, and variable~$a$ can be compared to variable~$b$ in boolean
+expressions ^^{comparison} like `$a<b$' when $a-b$ is known. The quantity
+$a-b$ might be known even when $a$ and~$b$ aren't known by themselves.
+
+\danger You might wonder how \MF\ is able to keep its knowledge up-to-date,
+based on scraps of partial information that it receives from miscellaneous
+equations. The best way to understand this is to watch how it happens,
+by asking the computer to show certain calculations that it usually keeps
+to itself. Here's one way to do it: Run \MF\ and say
+\begintt
+\tracingequations:=tracingonline:=1;
+\endtt
+^^"tracingequations" ^^"tracingonline"
+in response to the opening `|**|'. \ (Be sure to type the backslash `|\|',
+and to use `|:=|' instead of `|=|'. We will see in Chapter~27 that \MF\
+can be asked to ``trace'' many aspects of what it's doing.) \ Now type
+\begintt
+a+b+2c=3;
+\endtt
+the machine will reply by saying
+\begintt
+## c=-0.5b-0.5a+1.5
+\endtt
+since that is how it has digested your equation. \ (The `|##|' in this
+^^{hash hash} line identifies diagnostic information that comes from
+"tracingequations".) \ Now type
+\begintt
+a-b-2c=1;
+\endtt
+\MF\ will read this as if you had said `|a-b-2(-0.5b-0.5a+1.5)=1|',
+since it has previously learned how to replace |c| by an expression
+that involves only |a| and~|b|. This new equation can be simplified by
+multiplying out the left-hand side and collecting terms. The result is
+`|2a-3=1|', hence \MF\ will respond with
+\begintt
+## a=2
+\endtt
+and it will be your turn to type something again. Say
+\begintt
+showdependencies;
+\endtt
+^^@showdependencies@ \MF's response will be
+\begintt
+c=-0.5b+0.5
+\endtt
+indicating that there is only one variable whose value depends on others,
+and that its equation of dependency is now `$c=-0.5b+0.5$'. \ (The previous
+dependency equation `$c=-0.5b-0.5a+1.5$' has
+been simplified to take account of the newly discovered value, $a=2$.) \
+Finally type
+\begintt
+b+c=4;
+\endtt
+this spurs the computer on to say
+\begintt
+## b=7
+#### c=-3
+\endtt
+A line that begins with `|##|' states what \MF\ has deduced from
+the equation it has just read; a line that begins with `|####|' states
+^^{hash hash hash hash} an indirect consequence of that direct result,
+if some previously dependent variable has now become known.
+
+\danger It's interesting to continue the computer experiment just begun
+by typing the following lines, one at a time, and watching what happens:
+\begintt
+a'+b'+.5c'=3;
+a'-b'-.5c'=1;
+g0=g1=g2=g3=g4;
+showdependencies;
+g0+g1=1;
+z1-z4=z2-z5=z3-z6;
+z2-z1=z3-z2=z5-z4=z6-z5;
+z4=origin;
+z3=(w,h);
+x1=0;
+y6=0;
+w=2h=100;
+end.
+\endtt
+Notice that on the sixth line (\thinspace`$z_1-z_4=\cdots\,$'\thinspace)
+\MF\ reports four equations, but on the next line
+(\thinspace`$z_2-z_1=\cdots\,$'\thinspace) it reports only two. This
+happens because most of that line is redundant, as we have already
+observed.
+
+\danger This computer session indicates that \MF\ deals with two kinds
+of unknown numeric variables: {\sl^{dependent}\/} variables and
+{\sl^{independent}} ones.
+Every variable is independent at the beginning of its life, but every
+equation causes one of the independent variables to become dependent
+or ^{known}. Each `|##|' line emitted by "tracingequations" shows a
+newly dependent-or-known variable, together with an equivalent expression
+that involves only independent variables. For example, the line
+`|##|~|c=-0.5b-0.5a+1.5|'
+means that variable~$c$ has just become dependent and that it equals
+$-{1\over2}b-{1\over2}a+1.5$, where variables $b$ and~$a$ are independent.
+Similarly, `|##|~|a=2|' means that $a$~has just changed from
+independent to known. When an independent variable~$v$ changes to dependent
+or known, the equivalents of all dependent variables are updated so that
+they no longer depend on~$v$; in this updating process some or all of them
+may change from dependent to known, whereupon a `|####|' line will be printed.
+
+\ddanger When \MF\ reads a numeric equation it replaces all known variables
+by their numeric values and all dependent variables by their equivalents.
+The resulting equation can be converted into the form
+\begindisplay
+$c_1v_1+\cdots+c_mv_m=\alpha$
+\enddisplay
+where the $c$'s are nonzero constants and the $v$'s are independent variables;
+$\alpha$~is a numeric constant that might be zero. If some $c_k$ is so
+small that it probably would have been zero in a calculation free of
+rounding errors, it is replaced by zero and the corresponding $v_k$ is
+removed from the equation. Now if $m=0$, the equation is considered to be
+either {\sl^{redundant}\/} (if $\alpha$ is zero or extremely small)
+or {\sl^{inconsistent}\/} (otherwise). But if $m>0$, \MF\ chooses an
+independent variable~$v_k$ for which $c_k$ is maximum, and rewrites
+the equation in the form
+\begindisplay
+{\tt\#\#} $v_k=(\alpha-c_1v_1-\cdots-c_{k-1}v_{k-1}-c_{k+1}v_{k+1}-
+ \cdots-c_mv_m)/c_k$.
+\enddisplay
+Variable $v_k$ becomes dependent (if $m>1$) or known (if $m=1$).
+
+\danger Inconsistent equations are equations that have no solutions.
+For example, if you say `$0=1$', \MF\ will issue an error message
+^^{off by x}
+saying that the equation is ``off by~1.'' A less blatant inconsistency
+arises if you say, e.g., `$a=b+1$; $b=c+1$; $c=a+1$'; this last equation
+is off by three, for the former equations imply that $c=b-1=a-2$.
+The computer will simply ignore an inconsistent equation when you
+resume processing after such an error.
+
+\danger Redundant equations are equations that say nothing new.
+For example, `$0=0$' is redundant, and so is `$a=b+c$' if you have
+previously said that $c=a-b$. \MF\ stops with an error message if
+you give it a redundant equation between two numeric expressions,
+because this usually indicates an oversight in the program. However,
+no error is reported when an equation between pairs leads to one or
+two redundant equations between numerics. For example, the equation
+`$z_3=(0,h)$' will not trigger an error message when the program
+has previously established that $x_3=0$ or that $y_3=h$ or both.
+
+\danger Sometimes you might have to work a little bit to put an equation
+into a form that \MF\ can handle. For example, you can't say
+\begindisplay
+$x/y=2$
+\enddisplay
+when $y$ is independent or dependent, because \MF\ allows ^{division}
+only by known quantities. The alternative
+\begindisplay
+$x=2y$
+\enddisplay
+says the same thing and causes the computer no difficulties;
+furthermore it is a correct equation even when $y=0$.
+
+\ddanger \MF's ability to remember previous equations is limited to
+``linear'' dependencies ^^{linear dependencies} as explained above.
+A mathematician might want to introduce the condition $x\ge0$ by giving an
+equation such as `$x=\mathop{\rm abs}x$'; but \MF\ is incapable
+of dealing with such a constraint. Similarly, \MF\ can't cope with
+an equation like `$x=\mathop{\rm floor}x$', which states that
+$x$~is an integer. Systems of equations that involve the ^{absolute
+value} and/or ^{floor} operation can be extremely difficult to solve,
+and \MF\ doesn't pretend to be a mathematical genius.
+
+\ddanger The rules given earlier explain how an independent variable
+can become dependent or known; conversely, it's possible for a
+dependent variable to become independent again, in unusual circumstances.
+For example, suppose that the equation $a+b+2c=3$ in our example above
+had been followed by the equation $d=b+c+a/4$. Then there would be
+two dependent variables,
+\begintt
+## c=-0.5b-0.5a+1.5
+## d=0.5b-0.25a+1.5
+\endtt
+Now suppose that the next statement is `|numeric|~|a|', meaning that the
+old value of variable~$a$ should be discarded. \MF\ can't simply delete
+an independent variable that has things depending on it, so it
+chooses a dependent variable to take $a$'s place; the computer prints out
+\begintt
+### 0.5a=-0.5b-c+1.5
+\endtt
+^^{hash hash hash} meaning that $0.5a$ will be replaced by $-c-{1\over2}b
++{3\over2}$ in all dependencies, before $a$ is discarded. Variable $c$ is
+now independent again; `^@showdependencies@' will reveal that the only
+dependent variable is now $d$, which equals $0.75b+0.5c+0.75$. \ (This
+is correct, for if the variable~$a$ is eliminated from the two given
+equations we obtain $4d=3b+2c+3$.) \ The variable chosen for independence
+is one that has the greatest coefficient of dependency with respect
+to the variable that will disappear.
+
+\danger A designer often wants to stipulate that a certain point lies on
+a certain line. ^^{line, point to be on} This can be done easily by
+using a special feature of plain \MF\ called `^"whatever"', which
+stands for an anonymous numeric variable that has a different unknown
+value each time you use it. For example,
+\begindisplay
+$z_1="whatever"[z_2,z_3]$
+\enddisplay
+states that point 1 appears somewhere on the straight line that passes
+through points 2 and~3. \ (The expression $t[z_2,z_3]$ represents that
+entire straight line, as $t$ runs through all values from $-\infty$ to
+$+\infty$. We want $z_1$ to be equal to $t[z_2,z_3]$ for some value of~$t$,
+but we don't care what value it is.) \ The expression `"whatever"$[z_2,z_3]$'
+is legal whenever the difference $z_2-z_3$ is known; it's usually used
+only when $z_2$ and $z_3$ are both known, i.e., when both points have been
+determined by prior equations.
+
+\danger Here are a few more examples of equations that involve
+`"whatever"', together with their translations into English. These
+equations are more fun than the ``tame'' ones we considered at the
+beginning of this chapter, because they show off more of the
+computer's amazing ability to deduce explicit values from implicit
+statements.
+
+\ninepoint % it's all dangerous from here on!
+\setbox0=\hbox{\indent$z_7-z_6="whatever"\ast(z_3-z_2)$\quad}
+\longesteq=\wd0
+\noindent\hbox to\longesteq{\indent\kern-1pt\sl Equation\hfil}%
+\kern-1pt{\sl Translation}\smallskip
+
+\\$z_5-z_4="whatever"\ast\mathop{\rm dir}30$\\
+The angle between points 4 and~5 will be $30^\circ$ above the horizon.
+\ (This equation can also be written `$z_4=z_5+"whatever"\ast\mathop{\rm
+dir}30$', which states that point~4 is obtained by starting at point~5
+and moving by some unspecified multiple of ^{dir}$\,30$.)
+
+\\$z_7-z_6="whatever"\ast(z_3-z_2)$\\
+The line from point~6 to point~7 should be ^{parallel} to the
+line from point~2 to point~3.
+
+\\$\penpos8("whatever",60)$\\
+The simulated pen angle at point~8 should be 60 degrees; the breadth
+of the pen is unspecified, so it will be determined by other equations.
+
+\dangerexercise If $z_1$, $z_2$, $z_3$, and $z_4$ are known points,
+how can you tell \MF\ to compute the point $z$ that lies on the
+^{intersection} of the lines $z_1\to z_2$ and $z_3\to z_4$?
+\answer $z="whatever"[z_1,z_2]$; $z="whatever"[z_3,z_4]$. \ (Incidentally,
+it's interesting to watch this computation in action. Run \MF\ with
+|\tracingequations:=|\allowbreak|tracingonline:=1| and say, for example,
+\begintt
+z=whatever[(1,5),(8,19)]; z=whatever[(0,17),(6,1)];
+\endtt
+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".)
+
+\dangerexercise Given five points $z_1$, $z_2$, $z_3$, $z_4$, and $z_5$,
+explain how to compute $z$ on the line $z_1\to z_2$ such that the line
+$z\to z_3$ is parallel to the line $z_4\to z_5$.
+\answer $z="whatever"[z_1,z_2]$; $z-z_3="whatever"\ast(z_5-z_4)$.
+
+\dangerexercise What \MF\ equation says that the line between points
+11 and~12 is {\sl^{perpendicular}\/} to the line between points 13 and~14?
+\answer $z_{11}-z_{12}="whatever"\ast(z_{13}-z_{14})$ ^{rotated} 90,
+assuming that $z_{13}-z_{14}$ is known. \ (It's also possible to say
+`$(z_{11}-z_{12})\mathbin{\rm dotprod} (z_{13}-z_{14})=0$', ^^{dotprod}
+although this risks overflow if the coordinates are large.)
+
+\dangerexercise (For mathematicians.) \ Given three points $z_1$, $z_2$,
+and $z_3$, explain how to compute the distance from $z_1$ to the straight
+line through $z_2$ and $z_3$.
+\answer One solution constructs the point $z_4$ on $z_2\to z_3$ such
+that $z_4\to z_1$ is perpendicular to $z_2\to z_3$, using ideas like
+those in the previous two exercises: `$z_4="whatever"[z_2,z_3]$;
+$z_4-z_1="whatever"\ast(z_3-z_2)$ rotated 90'. Then the requested distance
+^^{abs} ^^{ypart}^^{angle}
+is ${\rm length}(z_4-z_1)$. But there's a slicker solution: Just calculate
+$$\hbox{abs ypart$((z_1-z_2)\mathbin{\rm rotated}-{\rm angle}(z_3-z_2))$.}$$
+
+\ddangerexercise (For mathematicians.) \ Given three points $z_1$,
+$z_2$, $z_3$, and a length~$l$, explain how to compute the two points
+on the line $z_2\to z_3$ that are at distance~$l$ from $z_1$. \ (Assume
+that $l$~is greater than the distance from $z_1$ to the line.)
+\answer It would be nice to say simply `$z="whatever"[z_2,z_3]$' and
+then to be able to say either `length$(z-z_1)=l$' or `$z-z_1=(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
+flavor, 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 $z_4$ as in the previous exercise, ^^{pythagorean subtraction} and
+then to let
+$v=(l\mathbin{+{-}+}\mathop{\rm length} (z_4-z_1))\ast\mathop{\rm
+unitvector}(z_3-z_2)$; ^^{unitvector} ^^{length}
+the desired points are then $z_4+v$ and $z_4-v$.
+
+\ddangerexercise The applications of "whatever" that we have seen so far
+have been in equations between {\sl pairs\/} of numeric values, not
+in equations between simple numerics. Explain why an equation like
+`$a+2b="whatever"$' would be useless.
+\answer Such an equation tells us nothing new about $a$ or $b$. Indeed,
+each use of "whatever" introduces a new independent variable, and
+each new independent variable ``uses up'' one equation, since we need
+$n$ equations to determine the values of $n$~unknowns. On the other hand
+an equation between pairs counts as two equations; so there's a net
+gain of one, when "whatever" appears in an equation between pairs.
+
+\danger All of the equations so far in this chapter have been between numeric
+expressions or pair expressions. But \MF\ actually allows equations
+between any of the eight types of quantities. For example, you can write
+\begintt
+s1="go"; s1&s1=s2
+\endtt
+if $s_1$ and $s_2$ are string variables; this makes $s_1=\null$|"go"|
+and $s_2=\null$|"gogo"|. Moreover, the subsequent equations
+\begintt
+s3=s4; s5=s6; s3=s5; s4=s1&"sh"
+\endtt
+will make it possible for the machine to deduce that $s_6=\null$|"gosh"|.
+
+\danger But nonnumeric equations are not as versatile as numeric
+ones, because \MF\ does not perform operations on unknown quantities
+^^{unknown quantities, nonnumeric}
+of other types. For example, the equation
+\begintt
+"h"&s7="heck"
+\endtt
+cannot be used to define $s_7=\null$|"eck"|, because the ^{concatenation}
+operator~|&| works only with strings that are already known.
+
+\ddanger After the declaration `|string| |s[]|' and the equations
+`|s1=s2=s3|', the statement `|show|~|s0|' will produce the result
+`|unknown| |string| |s0|'; but `|show|~|s1|' will produce `|unknown|
+|string| |s2|'. Similarly, `|show|~|s2|' and `|show|~|s3|' will produce
+`|unknown| |string| |s3|' and `|unknown| |string| |s1|', respectively. In
+general, when several nonnumeric variables have been equated, they will
+point to each other in some cyclic order.
+
+\endchapter
+
+Let ``X'' equal my father's signature.
+\author FRED ^{ALLEN}, {\sl Vogues\/} (1924) % NYT review of show, Mar 28'24
+ % quoted in Much Ado About Me, p288
+
+\bigskip
+
+ALL ANIMALS ARE EQUAL
+BUT SOME ANIMALS ARE MORE EQUAL THAN OTHERS
+\author GEORGE ^{ORWELL}, {\sl Animal Farm\/} (1945) % Chapter 10
+
+\eject
+ \beginchapter Chapter 10. Assignments
+
+Variables usually get values by appearing in equations, as described in
+the preceding chapter. But there's also another way, in which `^|:=|'
+is used instead of~`|=|'. For example, the |io.mf| program in Chapter~5
+said
+\begintt
+stem# := trial_stem * pt#
+\endtt
+when it wanted to define the value of |stem#|.
+
+The ^{colon-equal} operator `|:=|' means ``discard the previous value of
+the variable and assign a new one''; we call this an {\sl^{assignment}\/}
+operation. It was convenient for |io.mf| to define |stem#| with an
+assignment instead of an equation, because |stem#| was getting several
+different values within a single font. The alternative would have been to say
+\begintt
+numeric stem#; stem# = trial_stem * pt#
+\endtt
+(thereby specifically undefining the previous value of |stem#| before using
+it in an equation); this is more cumbersome.
+
+The variable at the left of `|:=|' might appear also in the expression on
+the right. For example,
+\begintt
+code := code + 1
+\endtt
+means ``increase the value of "code" by 1.'' This assignment would make no
+sense as an equation, since `$"code"="code"+1$' is inconsistent. The former
+value of "code" is still relevant on the right-hand side when `$"code"+1$'
+is evaluated in this example, because old values are not discarded until
+the last minute; they are retained until just before a new assignment is made.
+
+\dangerexercise Is it possible to achieve the effect of `$"code":="code"+1$'
+by using equations and @numeric@ declarations but not assignments?
+\answer Yes, but it must be done in two steps: `@numeric@ "newcode";
+$"newcode"="code"+1$; @numeric@ "code"; $"code"="newcode"$'.
+
+Assignments are permitted only when the quantity at the left of the `|:=|'
+is a variable. For example, you can't say `|code+1:=code|'. More
+significantly, things like `|(x,y):=(0,0)|' are not permitted, although
+you can say `|w:=(0,0)|' if~$w$~has been declared to be a variable of
+type @pair@. This means that a statement like `|z1:=z2|' is illegal, because
+it's an abbreviation for the inadmissible construction `|(x1,y1):=(x2,y2)|';
+we must remember that |z1| is not really a variable, it's a pair of variables.
+
+The restriction in the previous paragraph is not terribly significant, because
+assignments play a relatively minor r\^ole in \MF\ programs. The best
+programming strategy is usually to specify equations instead of
+assignments, because equations indicate the relationships between
+variables in a declarative ^^{declarative versus imperative} ^^{imperative
+versus declarative} manner. A person who makes too many assignments is
+still locked into the habits of old-style ``imperative'' programming
+languages in which it is necessary to tell the computer exactly how to do
+everything; \MF's equation
+mechanism liberates us from that more complicated style of programming,
+because it lets the computer take over the job of solving equations.
+
+The use of assignments often imposes a definite order on the statements of
+a program, because the value of a variable is different before and after
+an assignment takes place. Equations are simpler than assignments because
+they can usually be written down in any order that comes naturally to you.
+
+Assignments do have their uses; otherwise \MF\ wouldn't bother with
+`|:=|' at all. But experienced \MF\ programmers introduce assignments
+sparingly---only when there's a good reason for doing so---because
+equations are generally easier to write and more enlightening to read.
+
+\danger \MF's ^{internal quantities} like "tracingequations" always have
+known numeric values, so there's no way to change them except by giving
+assignments. The computer experiment in Chapter~9 began with
+\begintt
+\tracingequations:=tracingonline:=1;
+\endtt
+this illustrates the fact that multiple assignments are possible, just
+like multiple equations. Here is the complete syntax for equations
+and assignments:
+\beginsyntax
+<equation>\is<expression>[=]<right-hand side>
+<assignment>\is<variable>[:=]<right-hand side>
+<right-hand side>\is<expression>\alt<equation>\alt<assignment>
+\endsyntax
+Notice that the syntax permits mixtures like `$a+b=c:=d+e$'; this is
+the same as the assignment `$c:=d+e$' and the equation `$a+b=c$'.
+
+\ddanger In a mixed equation/assignment like `$a+b=b:=b+1$', the old
+value of~$b$ is used to evaluate the expressions. For example, if $b$ equals~3
+before that statement, the result will be the same as `$a+3=b:=3+1$';
+therefore $b$ will be set to~4 and $a$~will be set to~1.
+
+\dangerexercise Suppose that you want variable $x_3$ to become ``like new,''
+^^{variables, reinitializing} ^^{reinitializing} ^^{independent variables}
+completely independent of any value that it formerly had; but you don't
+want to destroy the values of~$x_1$ and~$x_2$. You can't say `^@numeric@
+$x[\,]$', because that would obliterate all the $x_k$'s. What can you do
+instead? \checkequals\xwhat\exno
+\answer The assignment `$x_3:=\null$^"whatever"' does exactly what you want.
+
+\ddangerexercise Apply \MF\ to the short program
+\begindisplay
+@string@ $s[\,]$; \ $s_1=s_2=s_3=s_4$; \ $s_5=s_6$; \ $s_2:=s_5$; \
+ @showvariable@ $s$;
+\enddisplay
+and explain the results you get.
+\answer The result shows that $s_1=s_3=s_4$ and $s_2=s_5=s_6$ now:
+\begintt
+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
+\endtt
+(The assignment $s_2:=s_5$ broke $s_2$'s former relationship with $s_1$,
+$s_3$, and $s_4$.)
+
+\ddanger If other variables depend on $v$ when $v$ is assigned a new value,
+the other variables do not change to reflect the new assignment; they still
+act as if they depended on the previous (unknown) value of~$v$. For example,
+if the equations `$2u=3v=w$' are followed by the assignment `$w:=6$', the
+values of $u$ and~$v$ won't become known, but \MF\ will still remember the
+fact that $v=.66667u$. \ (This is not a new rule; it's a consequence of
+the rules already stated. When an independent variable is discarded, a
+dependent variable may become independent in its place, as described in
+Chapter~9.)
+
+\ddangerexercise Apply \MF\ to the program
+\begindisplay
+$"tracingequations":="tracingonline":=1$;\cr
+$a=1$; \ $a:=a+b$; \ $a:=a+b$; \ $a:=a+b$;\cr
+@show@ $a,b$;\cr
+\enddisplay
+and explain the results you get.
+\answer The results are
+\begindisplay
+|## a=1|\cr
+|## a=b+1|&(after the first assignment)\cr
+|## b=0.5a-0.5|&(after the second assignment)\cr
+|### -1.5a=-%CAPSULEnnnn-0.5|&(after the third assignment)\cr
+|## a=%CAPSULEnnnn|&(after the third, see below)\cr
+|>> a|&(after `@show@'; variable $a$ is independent)\cr
+|>> 0.33333a-0.33333|&(this is the final value of $b$)\cr
+\enddisplay
+^^|CAPSULE| Let $a_k$ denote the value of $a$ after $k$ assignments were made.
+Thus, $a_0=1$, and $a_1$ was dependent on the independent variable~$b$.
+Then $a_1$ was discarded and $b$ became dependent on the independent
+variable~$a_2$. The right-hand side of the third assignment was
+therefore $a_2+b$. At the time $a_2$ was about to be discarded, \MF\
+had two dependencies $b=0.5a_2-0.5$ and $\kappa=1.5a_2-0.5$, where
+$\kappa$ was a nameless ``^{capsule}'' inside of the computer, representing
+the new value to be assigned. Since $\kappa$ had a higher coefficient
+of dependency than~$b$, \MF\ chose to make $\kappa$ an independent variable,
+after which $-1.5a_2$ was replaced by $-\kappa-0.5$ in all dependencies; hence
+$b$ was equal to $0.33333\kappa-0.33333$. After the third
+assignment was finished, $\kappa$ disappeared and $a_3$ became independent
+in its place. \ (The line `|##| |a=%CAPSULEnnnn|' means that $a$~was
+temporarily dependent on $\kappa$, before $\kappa$ was discarded. If
+the equation $a=\kappa$ had happened to make $\kappa$ dependent on~$a$, rather
+than vice versa, no ^^{hash hash} `|##|' 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"$\null>0$.)
+
+\endchapter
+
+At first his assignment had pleased,
+but as hour after hour passed
+with growing weariness,
+he chafed more and more.
+\author C. E. ^{MULFORD}, {\sl Hopalong Cassidy\/} (1910) % Chap 17 p154
+
+\bigskip
+
+\<left part> ::= \<variable> :=
+\<left part list> ::= \<left part> $\vert$ \<left part list>\<left part>
+\<assignment statement> ::= \<left part list>\<arithmetic expression> $\vert$
+\<left part list>\<Boolean expression>
+\author PETER ^{NAUR} et al., {\sl Report %
+ on the Algorithmic language ALGOL 60\/} (1960) % section 4.2.1
+
+\eject
+ \beginchapter Chapter 11. Magnification\\and\\Resolution
+
+A single \MF\ program can produce fonts of type for many different kinds
+of printing equipment, if the programmer has set things up so that the
+^{resolution} can be varied. The ``plain \MF\thinspace'' base file described
+in Appendix~B establishes a set of conventions that make such variability
+quite simple; the purpose of the present chapter is to explain those
+conventions.
+
+For concreteness let's assume that our computer has two output devices.
+One of them, called ^"cheapo", has a resolution of 200 pixels per
+inch (approximately 8 per millimeter); the other, called ^"luxo",
+has a resolution of 2000 pixels per inch. We would like to write \MF\
+programs that are able to produce fonts for both devices. For example,
+if the file |newface.mf| contains a program for a new typeface, we'd
+like to generate a low-resolution font by invoking \MF\ with
+\begintt
+\mode=cheapo; input newface
+\endtt
+and the same file should also produce a high-resolution font if we start with
+\begintt
+\mode=luxo; input newface
+\endtt
+instead. Other people with different printing equipment should also be
+able to use |newface.mf| with their own favorite ^"mode" values.
+
+The way to do this with plain \MF\ is to call ^@mode\_setup@ near the
+beginning of |newface.mf|; this routine establishes the values of
+variables like ^"pt" and ^"mm", which represent the respective numbers of
+pixels in a point and a millimeter. For example, when $"mode"= "cheapo"$,
+the values will be $"pt"=2.7674$ and $"mm"=7.87402$; when $"mode"="luxo"$,
+they will be $"pt"=27.674$ and $"mm"=78.74017$. The |newface.mf| program
+should be written in terms of such variables, so that the pixel patterns
+for characters will be about 10~times narrower and 10~times shorter in
+"cheapo" mode than they are in "luxo" mode. For example, a line that's
+drawn from $(0,0)$ to $(3"mm",0)$ will produce a line that's about 23.6
+pixels long in "cheapo" mode, and about 236.2 pixels long in "luxo" mode;
+the former line will appear to be 3\thinspace mm long when printed by
+"cheapo", while the latter will look 3\thinspace mm long when printed by
+"luxo".
+
+A further complication occurs when a typeface is being ^{magnified}; in such
+cases the font does not correspond to its normal size. For example, we might
+want to have a set of fonts for "cheapo" that are twice as big as usual,
+so that users can make transparencies for overhead projectors. \ (Such
+output could also be reduced to 50\% of its size as printed,
+on suitable reproduction equipment, thereby increasing the effective
+resolution from 200 to 400.) \ \TeX\ allows entire jobs to be magnified
+by a factor of~2 if the user says `|\magnification=2000|'; individual
+fonts can also be magnified in a \TeX\ job by saying, e.g., ^^{TeX}
+`|\font\f=newface| |scaled| |2000|'. The standard way to produce a font
+with two-fold magnification using the conventions of plain \MF\ is to say, e.g.,
+\begintt
+\mode=cheapo; mag=2; input newface;
+\endtt
+this will make $"pt"=5.5348$ and $"mm"=15.74803$.
+
+The @mode\_setup@ routine looks to see if ^"mag" has a known value;
+if not, it sets $"mag"=1$. Similarly, if "mode" is unknown,
+^^"proof" @mode\_setup@ sets $"mode"="proof"$.
+
+Plain \MF\ also computes the values of several other dimension-oriented
+values in addition to "pt" and "mm", corresponding to the dimensions
+that are understood by \TeX. Here is the complete list:
+\begindisplay \openup 1pt
+"pt"&printer's point&($\rm72.27\,pt=1\,in$)\cr
+^"pc"&pica&($\rm1\,pc=12\,pt$)\cr
+^"in"&inch&($\rm1\,in=2.54\,cm$)\cr
+^"bp"&big point&($\rm72\,bp=1\,in$)\cr
+^"cm"&centimeter&($\rm100\,cm=1\,meter$)\cr
+"mm"&millimeter&($\rm10\,mm=1\,cm$)\cr
+^"dd"&didot point&($\rm1157\,dd=1238\,pt$)\cr
+^"cc"&cicero&($\rm1\,cc=12\,dd$)\cr
+\enddisplay
+In each case the values are rounded to the nearest $1\over65536$th of a pixel.
+
+Although such standard physical ^{dimensions} are available, they haven't
+been used very much in traditional typefaces; designers usually specify
+other units like `"em"' or `"x\_height"' in order to define the sizes
+of letters, and such quantities generally have ad hoc values that vary
+from font to font. Plain \MF\ makes it easy to introduce ^{ad hoc
+dimensions} that will vary with the resolution and the magnification just
+as "pt" and "mm" do; all you have to do is define ``^{sharped}''
+dimensions that have the same name as your pixel-oriented dimensions, but
+with `|#|' ^^{hash} tacked on as a suffix. For example, $"em"\0$ and
+$"x\_height"\0$ (typed `|em#|' and `|x_height#|'\thinspace) would be the
+^{sharped dimensions} corresponding to "em" and "x\_height". Plain \MF\ has
+already defined the quantities $"pt"\0$, $"pc"\0$, $"in"\0$, $"bp"\0$,
+$"cm"\0$, $"mm"\0$, $"dd"\0$, and $"cc"\0$ for the standard units named above.
+
+Sharped dimensions like $"em"\0$ and $"x\_height"\0$ should always be
+defined in terms of resolution-independent dimension variables like $"pt"\0$,
+$"in"\0$, etc., so that their values do not change in any way when "mode"
+and "mag" are varied. The `|#|' sign implies unchangeability.
+After @mode\_setup@ has been called,
+the pixel-oriented dimensions can be calculated by simply saying
+\begindisplay
+^@define\_pixels@("em", "x\_height").
+\enddisplay
+This statement is an abbreviation for
+\begindisplay
+$"em":="em"\0\ast"hppp"$;&$"x\_height":="x\_height"\0\ast"hppp"$
+\enddisplay
+where ^"hppp" is an internal variable of \MF\ that represents the number
+of pixels per point in the horizontal dimension. Any number of ad hoc
+dimensions can be listed in a single @define\_pixels@ statement.
+Notice that `\#' is not an operator that could convert "em" to $"em"\0$;
+rounding errors would be mode-dependent.
+
+Chapter 5's demonstration program |io.mf| contains several examples of ad hoc
+dimensions defined in this way, and it also contains the statement
+\begindisplay
+^@define\_blacker\_pixels@("thin", "thick");
+\enddisplay
+what's this? Well, Appendix B makes that statement an abbreviation for
+\begindisplay
+$"thin":="thin"\0\ast"hppp"+"blacker"$;&
+$"thick":="thick"\0\ast"hppp"+"blacker"$;\cr
+\enddisplay
+in other words, the sharped dimensions are being unsharped in this case
+by converting them to pixels and then adding `"blacker"'. The variable
+^"blacker" is a special correction intended to help adapt a font to the
+idiosyncrasies of the current output device; @mode\_setup@ uses the value
+of "mode" to establish the value of "blacker". For example, "cheapo" mode
+might want $"blacker"=0.65$, while "luxo" mode might give best results
+when $"blacker"=0.1$. The general convention is to add "blacker" to
+pixel-oriented variables that determine the breadth of pens and the
+thickness of stems, so that the letters will be slightly darker on machines
+that otherwise would make them appear too light. Different machines treat
+pixels quite differently, because they are often based on quite different
+physical principles. For example, the author once worked with an extremely
+high-resolution device that tended to shrink stem lines rather drastically
+when it used a certain type of photographic paper, and it was necessary
+to set $"blacker"=4$ to get proper results on that machine; another
+high-resolution device seems to want "blacker" to be only~$0.2$. Experimentation
+is necessary to tune \MF's output to particular devices, but the author's
+experience suggests strongly that such a correction is worthwhile. When
+^^"proof" $"mode"="proof"$ or ^"smoke", the value of "blacker" is taken to
+be zero, since the output in these modes is presumably undistorted.
+
+\exercise Does `$"mode"="cheapo"$; $"mag"=10$' produce exactly the same
+font as `$"mode"="luxo"$', under the assumptions of this chapter?
+\answer 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 defined via @define\_blacker\_pixels@ will be slightly
+different, because "cheapo" mode has $"blacker"=0.65$ while "luxo" mode
+has $"blacker"=0.1$ (since the "luxo" printer has different physical
+characteristics). Similarly, @define\_corrected\_pixels@ (which we are
+just about to discuss) will produce slightly different results in the two
+given modes.
+
+\danger Line 7 of |io.mf| says `^@define\_corrected\_pixels@($o$)', and
+this is yet a third way of converting from true physical dimensions to
+pixel-oriented values. According to Appendix~B\null, variable~$o$ is
+defined by the assignment
+\begindisplay
+$o:=\round(o\0\ast"hppp"\ast"o\_correction")+"eps"$
+\enddisplay
+^^{round} ^^"eps" ^^"o"
+where ^"o\_correction", like "blacker", is a magic number that depends on
+the output device for which fonts are being made. On a high-resolution
+device like "luxo", the appropriate value for the "o\_correction" factor
+is~1; but on a low-resolution device like "cheapo", the author has obtained
+more satisfactory results with $"o\_correction"=0.4$. The reason is that
+`$o$' is used to specify the number of pixels by which certain features
+of characters ``^{overshoot}'' the baseline or some other line to which
+they are visually related. High-resolution curves look better when they
+overshoot in this way, but low-resolution curves do not; therefore it is
+usually wise to curtail the amount of overshoot by applying the
+"o\_correction" factor. In "proof" and "smoke" modes the factor is
+equal to 1.0, since these modes correspond to high resolution.
+
+%\danger Plain \MF\ also provides a fourth way to define unsharped
+%dimensions from sharped ones, if you want the unsharped dimensions
+%to be rounded to the nearest integer number of pixels: Just say
+%`^@define\_whole\_pixels@'. For example,
+%\begindisplay
+%@define\_whole\_pixels@("foo")
+%\enddisplay
+%stands for `$"foo":=\round("foo"\0\ast"hppp")$'.
+
+\ddanger The properties of output devices are modeled also by a
+parameter that's called ^"fillin", which represents the amount by which
+diagonal strokes tend to be darker than horizontal or vertical strokes.
+More precisely, let us say that a ``^{corner}'' pixel is one whose color
+matches the color of five of its neighbors but not the other three, where the
+three exceptions include one horizontal neighbor, one vertical neighbor,
+and the diagonal neighbor between them. If a white corner pixel has
+apparent darkness $f_1$ and if a black corner pixel has apparent darkness
+$1-f_2$, then the "fillin" is $f_1-f_2$. \ (A ``true'' raster image would
+have $f_1=f_2=0$, but physical properties often cause pixels to influence
+their neighbors.)
+
+\ddanger Each output device for which you will be generating fonts should
+be represented by a symbolic ^"mode" name in the implementation of \MF\
+that you are using. Since these mode names vary from place to place, they
+are not standard aspects of the \MF\ language; for example, it is doubtful
+whether the hypothetical "cheapo" and "luxo" modes discussed in this
+chapter actually exist anywhere. The plain \MF\ base is intended to be
+extended to additional modes in a disciplined way, as described at the
+end of Appendix~B.
+
+\ddanger It's easy to create a new symbolic mode, using plain \MF's
+`^@mode\_def@\kern.75pt' convention. For example, the "luxo" mode we have been
+talking about could be defined by saying
+\begindisplay
+@mode\_def@ "luxo" $=$\cr
+\quad$"pixels\_per\_inch":=2000$;&|%| high res, almost 30 per point\cr
+\quad$"blacker":=.1$;&|%| make pens a teeny bit blacker\cr
+\quad$"o\_correction":=1$;&|%| keep the full overshoot\cr
+\quad$"fillin":=0.1$;&|%| compensate for darkened corners\cr
+\quad$"proofing":=0$;&|%| no, we're not making proofs\cr
+\quad$"fontmaking":=1$;&|%| yes, we are making a font\cr
+\quad$"tracingtitles":=1$; \ @enddef@;&|%| yes, show titles online\cr
+\enddisplay
+The name of the mode should be a single symbolic token. The resolution
+should be specified by assigning a value to "pixels\_per\_inch"; all other
+dimension values ("pt", "mm", etc.)\ will be computed from this one by
+@mode\_setup@. A mode definition should also assign values to the
+internal variables "blacker", "o\_correction", and "fillin" (which describe
+the device characteristics), as well as ^"proofing", ^"fontmaking", and
+^"tracingtitles" (which affect the amount of output that will be produced).
+In general, "proofing" and "fontmaking" are usually
+set to 0 and~1, respectively, in modes that are intended for font
+production rather than initial font design; "tracingtitles" is usually
+0~for low-resolution fonts (which are generated quickly), but 1~for
+high-resolution fonts (which go more slowly), because detailed online
+progress reports are desirable when comparatively long jobs are running.
+
+\ddanger Besides the seven mandatory quantities `"pixels\_per\_inch"',
+\dots, `"tracingtitles"' just discussed, a mode definition might assign
+a value to `^"aspect\_ratio"'. In the normal case when no
+"aspect\_ratio" is specified, it means that the fonts to be output
+are assumed to have square pixels. But if, for
+example, the @mode\_def@ sets $"aspect\_ratio":=5/4$,
+it means that the output pixels
+are assumed to be ^{nonsquare} in the ratio of 5 to~4; i.e.,
+5~vertical pixel units are equal to 4~horizontal pixel units. The
+pixel-oriented dimensions of plain \MF\ are given in terms of horizontal
+pixel units, so an aspect ratio of 5/4 together with 2000 pixels per inch
+would mean that there are 2500 vertical pixel units per inch; a square
+inch would consist of 2500 rows of pixels, with 2000 pixels in each row. \
+(Stating this another way, each pixel would be $1\over2000$ inches wide and
+$1\over2500$ inches high.) \ In such a case, plain \MF\ will set the
+^"currenttransform" variable so that all @draw@ and @fill@ commands
+stretch the curves by a factor of 5/4 in the vertical dimension; this
+compensates for the nonsquare pixels, so the typeface designer doesn't have to
+be aware of the fact that pixels aren't square.
+
+%\ddanger A mode definition might also do other things besides setting
+%the values of numeric variables like "blacker" or "aspect\_ratio".
+%For example, the @mode\_def@ for "smoke" in Appendix~B includes the
+%statements `@grayfont@ black; @let@ $"makebox"="maketicks"$';
+%this changes the style of proofsheets that you get in ^"smoke" mode.
+
+Let's look now at a concrete example, so that it will be clear how the
+ideas of device-independent font design can be implemented in practice.
+We shall study a file |logo.mf| that generates the seven letters of
+\MF's ^{logo}. There also are ``^{parameter}'' files |logo10.mf|, |logo9.mf|,
+etc., which use |logo.mf| to produce fonts in various sizes. For
+example, a font containing the 10-point characters `\thinspace\MF\thinspace'
+could be generated for the hypothetical "luxo" printer by running \MF\ with
+the command line
+\begintt
+\mode=luxo; input logo10
+\endtt
+if "luxo" mode really existed.
+
+The main purpose of |logo10.mf| is to establish the ``sharped'' values of
+several ad hoc dimensions; then it inputs |logo.mf|, which does the
+rest of the work. Here is the entire file |logo10.mf|:
+\begintt
+% 10-point METAFONT logo|smallskip
+font_size 10pt#; % the "design size" of this font
+ht#:=6pt#; % height of characters
+xgap#:=0.6pt#; % horizontal adjustment
+u#:=4/9pt#; % unit width
+s#:=0; % extra space at the left and the right
+o#:=1/9pt#; % overshoot
+px#:=2/3pt#; % horizontal thickness of pen
+input logo % now generate the font
+end % and stop.
+\endtt
+Similar files |logo9.mf| and |logo8.mf| will produce 9-point
+`\thinspace{\manual hijklmnj}\thinspace' and \hbox{8-point}
+`\thinspace{\manual opqrstuq}\thinspace'; the letters get a little
+wider in relation to their height, and the inter-character spacing
+gets significantly wider, as the size gets smaller:
+\begintt
+% 9-point METAFONT logo % 8-point METAFONT logo|smallskip
+font_size 9pt#; font_size 8pt#;
+ht#:=.9*6pt#; ht#:=.8*6pt#;
+xgap#:=.9*0.6pt#; xgap#:=.8*0.6pt#;
+u#:=.91*4/9pt#; u#:=.82*4/9pt#;
+s#:=.08pt#; s#:=.2pt#;
+o#:=1/10pt#; o#:=1/12pt#;
+px#:=.9*2/3pt#; px#:=.8*2/3pt#;
+input logo input logo
+end end
+\endtt
+It is interesting to compare the font generated by |logo10.mf| to the
+font generated by |logo8.mf| with |mag=10/8|: Both fonts will have
+the same values of "ht", "xgap", and "px", when the magnification has been
+taken into account. But the magnified 8-point font has a slightly larger
+value of "u" and a positive value of "s"; this changes
+`\thinspace\MF\thinspace' to `\thinspace{\manual/0123451}\thinspace'.
+
+\danger Every font has a ``^{design size},'' which is a more-or-less
+arbitrary number that reflects the size of type it is intended to blend
+with. ^^{TeX} Users of \TeX\ select magnified fonts in two ways, either
+by specifying an ``at size'' or by specifying a scale factor (times 1000).
+For example, the 8-point \MF\ logo can be used at 10/8 magnification by
+referring either to `|logo8| |at|~|10pt|' or to `|logo8| |scaled|~|1250|'
+in a \TeX\ document. When an ``^{at size}'' is specified, the amount of
+magnification is the stated size divided by the design~size. A typeface
+designer can specify the design size by using plain \MF's `^@font\_size@'
+command as illustrated on the previous page. \ (If no design size is
+specified, \MF\ will set it to $128\pt$, by default.)
+
+The file |logo.mf| itself begins by defining three more ad hoc dimensions
+in terms of the parameters that were set by the parameter file; these
+dimensions will be used in several of the programs for individual letters.
+Then |logo.mf| makes the conversion to pixel units:
+\begintt
+% Routines for the METAFONT logo
+% (logo10.mf is a typical parameter file)
+mode_setup;
+ygap#:=(ht#/13.5u#)*xgap#; % vertical adjustment
+leftstemloc#:=2.5u#+s#; % position of left stems
+barheight#:=.45ht#; % height of bar lines
+define_pixels(s,u,xgap,ygap,leftstemloc,barheight);
+py#:=.9px#; define_blacker_pixels(px,py); % pen dimensions
+pickup pencircle xscaled px yscaled py; logo_pen:=savepen;
+define_corrected_pixels(o);
+\endtt
+There's nothing new here except the use of `^"savepen"' in the
+second-last line; this, as we will see in Chapter~16, makes the
+currently-picked-up pen available for repeated use in the
+subsequent program.
+
+After the initial definitions just shown, |logo.mf| continues with
+programs for each of the seven letters. For example,
+here is the program for `{\manual ^{E}}', which illustrates the
+\rightfig 11a ({224\apspix} x {216\apspix}) ^-11pt
+use of $u\0$, $s\0$, $"ht"\0$, "leftstemloc", "barheight", "xgap",
+and "logo\_pen":
+\begintt
+beginchar("E",14u#+2s#,ht#,0);
+pickup logo_pen;
+x1=x2=x3=leftstemloc;
+x4=x6=w-x1+o; x5=x4-xgap;
+y1=y6; y2=y5; y3=y4;
+bot y1=0; top y3=h;
+y2=barheight;
+draw z6--z1--z3--z4; draw z2--z5;
+labels(1,2,3,4,5,6);
+endchar;
+\endtt
+We have seen the essentials of the {\manual M} and the {\manual T} in
+Chapter~4; programs for the other letters will appear later.
+
+\exercise The ad hoc dimensions $"ht"\0$, $"xgap"\0$, $u\0$, $s\0$,
+$o\0$, and $"px"\0$ defined in the parameter files all affect the letter
+`{\manual E}' defined by this program. For each of these dimensions,
+tell what would happen to the `{\manual E}' if that dimension were
+increased slightly while all the others stayed the same.
+\answer Increasing $"ht"\0$ would make the letter shape and the bounding
+box taller; increasing $"xgap"\0$ would move point~5 to the left, thereby
+making the middle bar shorter; increasing $u\0$ would make the shape and
+its bounding box wider; increasing $s\0$ would widen the bounding box
+at both sides without changing the letter shape; increasing $o\0$ would
+move points 4,~5, and~6 to the right; increasing $"px"\0$ 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).
+
+\dangerexercise Guess the program for `{\manual l}' (which is ^^{F}
+almost the same as `{\manual i}'\thinspace).
+\answer The only possible surprise is the position of $y_1$,
+which should match similar details in the `{\manual h}'
+and the~`\kern1pt{\manual j}\kern1pt' of Chapter~4:
+\begintt
+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;
+\endtt
+
+\dangerexercise Write the complete programs for `{\manual h}' ^^{M} ^^{T}
+and `\kern1pt{\manual j}\kern1pt', based on the information in Chapter~4,
+but using the style of the program for `{\manual E}' above. The character
+widths should be $18u\0+2s\0$ and $13u\0+2s\0$, respectively.
+\checkequals\metaT\exno
+\answer The quantity called "ss" in Chapter~4 is now "leftstemloc".
+\begintt
+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;|smallskip
+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;
+\endtt
+
+\danger The file |logo.mf| also contains the following cryptic instructions,
+which cause the letter pairs `\kern1pt{\manual jk}' and `{\manual lm}' to
+be typeset closer together than their bounding boxes would imply:
+\begintt
+ligtable "T": "A" kern -.5u#;
+ligtable "F": "O" kern -u#;|smallskip
+\endtt
+Without these corrections `\MF\kern1pt' would be ^^{kerning} ^^@kern@
+`{\manual hij\/kl\/mnj}\kern1pt'. Uppercase letters are often subject to
+such spacing corrections, especially in logos; \TeX\ will adjust the spacing
+if the typeface designer has supplied ^@ligtable@ information like this.
+
+\danger Finally, |logo.mf| closes with four more commands, which provide
+further information about how to typeset with this font:
+\begintt
+font_quad 18u#+2s#;
+font_normal_space 6u#+2s#;
+font_normal_stretch 3u#;
+font_normal_shrink 2u#;
+\endtt
+A ^@font\_quad@ is the unit of measure that a \TeX\ user calls one `|em|'
+when this font is selected. The normal space, stretch, and shrink parameters
+^^@font\_normal\_space@ ^^@font\_normal\_stretch@ ^^@font\_normal\_shrink@
+define the interword spacing when text is being typeset in this font.
+Actually a font like |logo10| is rarely used to typeset anything except
+the one word, `\MF\kern1pt'; but the spacing parameters have been
+included just in case somebody wants to typeset a sentence like
+`{\manual kn illiji jmhkjm ml hmnjknk mljin kji nmnlkj jmllii}'.
+
+\danger An optional `^|=|' or `^|:=|' sign may be typed after `@font\_size@',
+`@font\_quad@', etc., in case you think the file looks better that way.
+
+\danger Notice that ``sharped'' units must be given in the ^@ligtable@
+kerning commands and in the definition of device-independent
+parameters like @font\_size@
+and @font\_quad@. Appendix~F discusses the complete rules of @ligtable@
+and other commands by which \MF\ programs can send important information
+to typesetting systems like \TeX. Adding these extra bits of information
+to a \MF\ program after a font has been designed is something like
+adding an index to a book after that book has been written and proofread.
+
+\ddangerexercise What's the longest English word that can be typeset
+with the font |logo9|?
+\answer `{\manual nmnkjmnihinj\/}'; possibly also `{\manual hijklmmjnmji}';
+and Georgia ^{Tobin} suggests that `{\manual knjiinlimllhinj\/}'
+might be a legal term.
+
+\ninepoint % nothing but danger from here on, folks
+\danger Let's summarize the general contents of |logo.mf|, now that we
+have seen it all, because it provides an example of a complete typeface
+description (even though there are only seven letters):\enddanger
+
+\smallskip
+\item\bull The file begins by defining ad hoc dimensions and converting
+them to pixel units, using @mode\_setup@, @define\_pixels@, etc.
+
+\smallskip
+\item\bull Then come programs for individual letters. \ (These programs
+are often preceded by macro definitions for subroutines that occur several
+times. For example, we will see later that the `{\manual k}' and the
+`{\manual m}' of the logo are drawn with the help of a subroutine that makes
+half of a superellipse; the definition of this macro actually comes near
+the beginning of |logo.mf|, just before the programs for the letters.)
+
+\smallskip
+\item\bull Finally there are special commands like ^@ligtable@ and
+^@font\_quad@, to define parameters of the font that are helpful
+when typesetting.
+
+\smallskip
+\item\bull The file is accompanied by parameter files that define
+ad hoc dimensions for different incarnations of the typeface.
+
+\smallskip\noindent
+We could make lots of different parameter files, which would produce
+lots of different (but related) variations on the \MF\ logo; thus, |logo.mf|
+defines a ``^{meta-font}'' in the sense of Chapter~1.
+
+\dangerexercise What changes would be necessary to generalize the |logo|
+routines so that the bar-line height is not always 45 per~cent of the
+character height?
+\answer Delete the line of |logo.mf| that defines |barheight#|, and
+insert that line into each of the parameter files |logo10.mf|, |logo9.mf|,
+|logo8.mf|. Then other bar-line heights are possible by providing new
+parameter files; another degree of ``meta-ness'' has therefore been added
+to the meta-font.
+
+\danger ^{Assignments} (\thinspace`|:=|'\thinspace) have been used instead
+of equations (\thinspace`|=|'\thinspace) in the parameter files |logo10.mf|,
+|logo9.mf|, and |logo8.mf|, as well as
+in the opening lines of |io.mf| in Chapter~5; this contradicts the
+advice in Chapter~10, where we are told to stick to equations unless
+assignments are absolutely necessary. The author has found it convenient
+to develop the habit of using assignments whenever ad hoc dimensions
+are being defined, because he often makes experimental files in which
+the ad hoc dimensions are changed several times. For example, it's a good
+idea to test a particular letter with respect to a variety of different
+parameter settings when that letter is first being designed; such
+experiments can be done easily by copying the ad hoc parameter definitions
+from parameter files into a test file, provided that the parameters
+have been defined with assignments instead of equations.
+
+\danger \TeX\ users have found it convenient to have fonts in a series
+of magnifications that form a geometric series. A font is said
+to be scaled by `^{magstep}~1' if it has been magnified by~1.2;
+it is scaled by `magstep~2' if it has been magnified by $1.2\times1.2=1.44$;
+it is scaled by `magstep~3' if it has been magnified by $1.2\times1.2\times1.2=
+1.728$; and so on. Thus, if a job uses a font that is scaled by magstep~2,
+and if that entire job is magnified by magstep~1, the font actually used
+for printing will be scaled by magstep~3. The additive nature of magsteps
+makes it more likely that fonts will exist at the desired sizes when
+jobs are magnified. Plain \MF\ supports this convention by allowing
+constructions like
+\begintt
+\mode=cheapo; mag=magstep 2; input logo9
+\endtt
+if you want to generate the 9-point \MF\ logo for the "cheapo" printer,
+magnified by 1.44 (i.e., by magstep~2). You can also write `|magstep|~|0.5|'
+^^{TeX} for what \TeX\ calls `|\magstephalf|'; this magnifies by $\sqrt{1.2}$.
+
+\ddanger The sharped forms of dimensions are actually represented by plain
+\MF\ in terms of printer's points, so that `$"pt"\0$' turns out to be
+equal to~1. However, it is best for programmers not to make use of this
+fact; a program ought to say, e.g., `$"em"\0:=10"pt"\0$', even though
+the `$"pt"\0$' in this construction is redundant, and even though the
+computer would run a few microseconds faster without it.
+
+\ddangerexercise Suppose you want to simulate a low-resolution printer
+on a high resolution device; for concreteness, let's say that
+"luxo" is supposed to produce the output of "cheapo", with each black
+"cheapo" pixel replaced by a $10\times10$ square of black "luxo" pixels.
+Explain how to do this to the |logo10| font, by making appropriate
+changes to |logo.mf|. Your output file should be called |cheaplogo10.2000gf|.
+\answer (This is tricky.) \ Insert the lines
+\begintt
+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
+\endtt
+right after `|mode_setup|' in |logo.mf|, and also include the line
+\begintt
+if known pixmag: hppp:=pixmag*hppp; vppp:=pixmag*vppp; fi
+\endtt
+at the very end of that file. Then run \MF\ with
+\begintt
+\mode="cheapo"; input cheaplogo10
+\endtt
+where the file `|cheaplogo10.mf|' says simply `|pixmag=10;| |input| |logo10|'.
+\ (The interim "hppp" setting and the ^@special@ command are
+used to fool \MF\ into giving the appropriate extension to the
+^|gf| file name. Incidentally, you could print with this font on "cheapo"
+at ten-fold magnification if you told \TeX\ to use the font `|cheaplogo10|
+|scaled| |10000|'; but on "luxo" you would simply call this font
+`|cheaplogo10|'.)
+
+\endchapter
+
+A great Temptation must be withstood with great Resolution.
+\author WILLIAM ^{BURKITT}, {\sl Expository Notes on the New Testament\/} %
+ (c.\thinspace1700) % commenting on Matt 4:10
+ % I examined only the fifth edition (1712), title page says `New-Testament'
+ % Another edition printed at New Haven in 1794 says `should' not `must'!
+
+\bigskip
+
+What some invent, the rest enlarge.
+\author JONATHAN ^{SWIFT}, {\sl Journal of a Modern Lady\/} (1729) % line 145
+
+\eject
+ \beginchapter Chapter 12. Boxes
+
+\looseness=-1
+Let's pause now to take a closer look at the ``bounding boxes'' that enclose
+individual characters. In olden days, metal type was cast on a
+rectangular body in which each piece of type had the same vertical
+extent, although the type widths would vary from character to character.
+Nowadays we are free of the mechanical constraints imposed by metal type,
+but the former metaphors are still useful: A~typesetting system like ^^{TeX}
+\TeX\ imagines that each character fits into a rectangular box, and words are
+typeset by putting such boxes snugly next to each other.
+
+% Here are some macros borrowed from The TeXbook
+\def\dolist{\afterassignment\dodolist\let\next= }%
+\def\dodolist{\ifx\next\endlist \let\next\relax
+ \else \\\let\next\dolist \fi
+ \next}
+\def\endlist{\endlist}
+\def\\{\expandafter\if\space\next\ \else \setbox0=\hbox{\next}\maketypebox\fi}
+\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%
+ \copy0\kern-\wd0\makelightbox}
+ The main difference
+between the old conventions and the new~ones is that type boxes are now
+allowed to vary in height as well as in width. For example, when \TeX\
+typesets `A~line~of~type.' it puts boxes together that essentially look
+like this: `\thinspace\demobox{A line of type.}\thinspace'. \ (The `A'
+appears in a box `\thinspace\setbox0\hbox{A}\maketypebox\thinspace' that
+sits on a given baseline, while the `y' appears in a box
+`\thinspace\setbox0\hbox{y}\maketypebox\thinspace' that descends below the
+baseline.) \ \TeX\ never looks inside a box to see what character actually
+appears there; \TeX's job is to put boxes together in the right places
+on a page, based only on the box sizes. It is a typeface designer's job
+to decide how big the boxes should be and to create the characters inside
+the boxes.
+
+Boxes are two-dimensional objects, but we ascribe three dimensions to them
+because the vertical component is divided into two quantities, the
+{\sl^{height}\/} (above the ^{baseline}) and the {\sl^{depth}\/}
+(below the baseline). The horizontal dimension is, of course, called
+the {\sl^{width}}. Here is a picture of a typical box, showing its
+so-called ^{reference point} and baseline:
+
+{\eightpoint
+\setbox0=\hbox{$\uparrow$}
+\setbox1=\hbox to \wd0{$\hss\mid\hss$} % with luck, they'll line up
+\setbox2=\vbox{\copy0
+ \nointerlineskip \kern-.5pt \copy1
+ \nointerlineskip \kern-.5pt \copy1
+ \moveleft 1em\hbox{height}
+ \copy1 \nointerlineskip \kern-.5pt
+ \copy1 \nointerlineskip \kern-.5pt
+ \hbox{$\downarrow$}
+ \kern.2pt}
+\setbox3=\vbox{\kern.2pt\copy0
+ \moveleft 1em\hbox{depth}
+ \hbox{$\downarrow$}
+ \kern0pt}
+\setbox4=\vtop{\kern-3pt % this cancels the null text above the samplebox
+ \hbox{\samplebox{\ht2}{\ht3}{6em}{}%
+ \kern-6em
+ \raise3pt\hbox to 6em{\hss Baseline\hss}}
+ \kern3pt
+ \arrows{6em}{width}}
+\medskip\indent
+\setbox0=\hbox{$\vcenter{}$}% \ht0 is the axis height
+\lower\ht0\hbox{Reference point$-$\kern-.2em$\rightarrow$\kern2pt}%
+\raise\ht2\box4
+\kern1.5em
+\raise\ht2\vtop{\kern0pt\box2\nointerlineskip\box3}}
+
+\medskip\noindent
+The example characters in previous chapters have all had zero depth, but
+we will soon be seeing examples in which both height and depth are relevant.
+
+A character shape need not fit inside the boundaries of its box. Indeed,
+{\it italic\/} and {\sl slanted\/} letters are put into ordinary boxes
+just as if they were not slanted, so they frequently stick out at the right.
+For example, the letter `g\/' in the font you are now reading (^|cmr10|)
+can be compared with the `{\sl g\/}' in the corresponding slanted
+font (^|cmsl10|):
+\begindisplay
+\vbox to 40pt{\ifproofmode\hrule\vfill
+ \hsize=2.5in \baselineskip 6pt \fiverm\noindent
+ (A figure will be inserted here; too bad you can't see it now.
+ It shows two g's, as claimed. In fact, the same figure appeared
+ on page 63 of The TeXbook.)
+ \vfill\hrule\fi}
+\enddisplay
+The slanted `{\sl g\/}' has been drawn as if its box were skewed right at the
+top and left at the bottom, keeping the baseline fixed; but \TeX\ is told
+in both cases that the box is $5\pt$ wide, $4.3055\pt$ high, and $1.9444\pt$
+deep. Slanted letters will be spaced properly in spite of the fact that their
+boxes have been straightened up, because the letters will match correctly
+at the baseline.
+
+\danger Boxes also have a fourth dimension called the {\sl^{italic
+correction}}, which gives \TeX\ additional information about whether or
+not a letter protrudes at the right. For example, the italic correction
+for an unslanted `g\/' in |cmr10| is $0.1389\pt$, while the corresponding
+slanted letter in |cmsl10| has an italic correction of $0.8565\pt$. The
+italic correction is added to a box's width when math formulas like ${\rm
+g}^2$ or ${\sl g}^2$ are being typeset, and also in other cases as
+explained in {\sl The \TeX book}.
+
+Plain \MF's ^@beginchar@ command establishes the width, height, and depth
+of a box. These dimensions should be given in terms of ``^{sharped}''
+quantities that do not vary with the resolution or magnification, because
+the size of a character's type box should not depend in any way on the device
+that will be used to output that character. It is important to be able to
+define documents that will not change even though the technology for printing
+those documents is continually evolving. \MF\ can be used to produce fonts for
+new devices by introducing new ``modes,'' as we have seen in Chapter~11,
+but the new fonts should still give the same box dimensions to each character.
+Then the device-independent files output by \TeX\ will not have to be
+changed in any way when they are printed or displayed with the help of
+new equipment.
+
+The three dimensions in a @beginchar@ command are given in reverse
+alphabetical order: First comes the width, then the height, then the depth.
+The @beginchar@ routine converts these quantities into pixel units
+and assigns them to the three variables ^"w", ^"h", and~^"d". In fact,
+@beginchar@ rounds these dimensions to the nearest whole number of
+pixels; hence $w$, $h$, and~$d$ will always be integers.
+
+\MF's pixels are like squares on ^{graph paper}, with pixel boundaries
+at points with integer coordinates. The left edge of the type box lies
+on the line $x=0$, and the right edge lies on the line $x=w$; we have
+$y=h$ on the top edge and $y=-d$ on the bottom edge. There are $w$ pixels
+in each row and $h+d$ in each column, so there are exactly $wh+wd$ pixels
+inside the type box.
+
+Since $w$, $h$, and $d$ are integers, they probably do not exactly match
+the box dimensions that are assumed by device-independent typesetting
+systems like \TeX. Some characters will be a fraction of a pixel too wide;
+others will be a fraction of a pixel too narrow. However, it's still possible
+to obtain satisfactory results if the pixel boxes are stacked together
+based on their $w$ values and if the accumulated error is removed in the
+spaces between words, provided that the box positions do not ^{drift}
+too far away from their true device-independent locations. A designer should
+strive to obtain letterforms that work well together when they are placed
+together in boxes that are an integer number of pixels wide.
+
+\ddanger You might not like the value of $w$ that @beginchar@ computes by
+rounding the device-independent width to the nearest pixel boundary.
+For example, you might want to make the letter~`m' one pixel wider, at
+certain resolutions, so that its three stems are equally spaced or so that
+it will go better with your `n'. In such a case you can assign a new value
+to~$w$, at any time between @beginchar@ and ^@endchar@. This new value
+will not affect the device-independent box width assumed by \TeX, but it
+should be respected by the software that typesets ^|dvi| files using your font.
+
+\def\hidecoords(#1,#2){\hbox to 0pt{\hss$\scriptstyle(#1,#2)$\hss}}
+\setbox0=\vtop{\kern -94pt
+ \rightline{\vbox{\hbox to 140\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{12a}{140\apspix}{360\apspix}\vbox
+ \kern-3pt
+ \hbox to 140\apspix{\hidecoords(0,-d)\hfil
+ \hidecoords(w\mkern-2mu,-d)}}\quad}}
+\dp0=0pt
+
+Here's an example of a character that has nonzero width, height, and depth;
+it's the left ^{parenthesis} in ^{Computer Modern} fonts like |cmr10|.
+Computer Modern typefaces are generated by \MF\ programs that involve
+lots of parameters, so this example also illustrates the principles of
+``^{meta-design}'': Many different varieties of left parentheses can be
+drawn by this one program. But let's focus our attention first on the
+comparatively simple way in which the box dimensions are established and
+used, before looking into the details of how a meta-parenthesis has
+actually been specified.
+% "hair", "thin", "thick" are actually "vair", "hair", "stem" in the code
+\def\xs(#1,#2){\{(z_{#1}-z_{#2})\,{\rm xscaled}\,3\}}%
+\begindisplay
+|"Left parenthesis"|;\cr
+@numeric@ $"ht"\0$, $"dp"\0$;\cr
+$"ht"\0="body\_height"\0$; \ $.5["ht"\0,-"dp"\0]="axis"\0$;\cr
+@beginchar@\kern1pt(|"("|$,7u\0,"ht"\0,"dp"\0)$;\cr
+@italcorr@ $"ht"\0\ast"slant"-.5u\0$;\cr
+@pickup@ "fine.nib";\cr
+$\penpos1("hair"-"fine",0)$;\strut\vadjust{\box0}\cr
+$\penpos2(.75["thin","thick"]-"fine",0)$;\cr
+$\penpos3("hair"-"fine",0)$;\cr
+$\mathop{"rt"}x_{1r}=\mathop{"rt"}x_{3r}= w-u$; \
+ $\mathop{"lft"}x_{2l}=x_1-4u$;\cr
+$\mathop{"top"}y_1=h$; \
+ $y_2=.5[y_1,y_3]="axis"$;\cr
+@filldraw@ $z_{1l}\xs(2l,1l)\ldots z_{2l}$\cr
+\qquad$\ldots\xs(3l,2l)z_{3l}$\cr
+\qquad$\dashto z_{3r}\xs(2r,3r)\ldots z_{2r}$\cr
+\qquad$\ldots\xs(1r,2r)z_{1r}\dashto\cycle$;\cr
+@penlabels@$(1,2,3)$; \ @endchar@;\cr
+\enddisplay
+
+The width of this left parenthesis is $7u\0$, where $u\0$
+is an ad hoc parameter that figures in all the widths of the Computer
+Modern characters. The height and depth have been calculated in such a way
+that the top and bottom of the bounding box are equally distant from an
+imaginary line called the {\sl^{axis}}, which is important in mathematical
+typesetting. \ (For example, \TeX\ puts the bar line at the axis
+in fractions like $1\over2$; many symbols like `$+$' and `$=$', as well as
+parentheses, are centered on the axis line.) \ Our example program puts the
+axis midway between the top and bottom of the type by saying that
+`$.5["ht"\0,-"dp"\0]="axis"\0$'. We also place the top at position
+`$"ht"\0="body\_height"\0$'\thinspace; here $"body\_height"\0$ is the
+height of the tallest characters in the entire typeface.
+It turns out that $"body\_height"\0$ is exactly $7.5"pt"\0$ in |cmr10|, and
+$"axis"\0=2.5"pt"\0$; hence $"dp"\0=2.5"pt"\0$,
+and the parenthesis is exactly $10\pt$ tall.
+
+The program for `(' uses a ^@filldraw@ command, which we haven't
+seen before in this book; it's basically a combination of @fill@
+and @draw@, where the filling is done with the currently-picked-up pen.
+Some of the Computer Modern fonts have characters with ``^{soft}'' edges
+while others have ``^{crisp}'' edges; the difference is due to the pen that
+is used to @filldraw@ the shapes. This pen is a circle whose diameter
+is called ^"fine"; when "fine" is fairly large, @filldraw@ will produce
+rounded corners, but when $"fine"=0$ (as it is in |cmr10|) the corners
+will be sharp.
+% (actually it isn't zero in cmr10, but this makes a better example)
+
+The statement `$\penpos1("hair"-"fine",0)$' makes the breadth of a
+simulated broad-edge pen equal to $"hair"-"fine"$ at position~1; i.e.,
+the distance between $z_{1l}$ and $z_{1r}$ will be $"hair"-"fine"$.
+We will be filling a region between $z_{1l}$ and $z_{1r}$ with a
+circle-shaped pen nib whose diameter is "fine"; the center of that
+nib will pass through $z_{1l}$ and $z_{1r}$, hence the pen will
+effectively add ${1\over2}"fine"$ to the breadth of the stroke at
+either side. The overall breadth at position~1 will therefore be
+${1\over2}"fine"+("hair"-"fine")+{1\over2}"fine"\;=\;"hair"$.
+(Computer Modern's ``^{hairline} thickness'' parameter, which governs
+the breadth of the thinnest strokes, is called "hair".) \ Similarly,
+the statement `$\penpos2(.75["thin","thick"]-"fine",0)$' makes the
+overall breadth of the pen at position~2 equal to $.75["thin","thick"]$,
+which is $3\over4$ of the way between two other parameters that govern
+stroke breadths in Computer Modern routines. If "fine" is increased while
+"hair", "thin", and "thick" stay the same, the effect will simply be to
+produce more rounded corners at positions 1 and~3, with little or no effect
+on the rest of the shape, provided that "fine" doesn't get so large
+that it exceeds "hair".
+
+\def\paren #1 #2 #3 #4 #5 #6 #7 #8 #9
+{\vbox{\dimen0=#3\apspix \hsize=7\dimen0
+ \centerline{\tt#1}
+ \medskip \kern3pt \kern270\apspix \kern-#4\apspix
+ \dimen2=#4\apspix \advance\dimen2 by -#5\apspix
+ \figbox{#2}{7\dimen0}{2\dimen2}\vbox
+ \kern-2\dimen2 \kern#4\apspix \kern90\apspix \kern-3pt \medskip
+ \tabskip 0pt plus 1fil
+ \halign to\hsize{$##$\cr
+ u=\hfil#3\cr
+ "ht"=\hfil#4\cr
+ "axis"=\hfil#5\cr
+ "fine"=\hfil#6\cr
+ "hair"=\hfil#7\cr
+ "thin"=\hfil#8\cr
+ "thick"=\hfil#9\cr}}}
+
+Here, for example, are five different left parentheses, drawn by our example
+program with various settings of the parameters:
+$$\line{\paren cmr10 12a 20 270 90 0 8 9 25
+ \hfil\paren cmbx10 12b 23 270 90 0 13 17 41
+ \hfil\paren cmvtt10 12c 21 250 110 22 22 25 25
+ \hfil\paren cmssdc10 12d 19 270 95 8 23 40 40
+ \hfil\paren cmti10 12e 18.4 270 90 7 8 11 23 }$$
+Parameter values are shown here in ^"proof"\kern-1pt\ mode pixel units,
+36 to the point. \ (Thus, for example, the value of $u\0$ in |cmr10| is
+${20\over36}"pt"\0$.) \ Since |cmbx10| is a ``bold extended'' font,
+its unit width~$u$ is slightly larger than the unit width of |cmr10|,
+and its pen widths (especially "thick") are significantly larger.
+The ``variable-width typewriter'' font |cmvtt10| has soft edges and
+strokes of almost uniform thickness, because "fine" and "hair" are almost
+as large as "thin" and "thick". This font also has a raised axis and a smaller
+height. An intermediate situation occurs in |cmssdc10|, a ``sans serif
+demibold condensed'' font that is similar to the type used in the chapter titles
+of this book; $"thick"="thin"$ in this font, but hairlines are noticeably
+thinner, and "fine" provides slightly rounded corners. The ``text italic''
+font |cmti10| has rounded ends, and the character shape has been ^{slanted}
+by .25; this means that each point $(x,y)$ has been moved to position
+$(x+.25y,y)$, in the path that is filled by @filldraw@.
+
+\danger The vertical line just to the right of the italic left parenthesis
+shows the ^{italic correction} of that character, i.e., the fourth box
+dimension mentioned earlier. This quantity was defined by the statement
+`^@italcorr@ $"ht"\0\ast"slant"-.5u\0$' in our program; here ^"slant" is
+a parameter of Computer Modern that is zero in all the unslanted fonts,
+but $"slant"=.25$ in the case of |cmti10|. The expression following
+@italcorr@ should always be given in sharped units. If the value is
+negative, the italic correction will be zero; otherwise the italic
+correction will be the stated amount.
+
+\danger The author has obtained satisfactory results by making the italic
+correction roughly equal to $.5u$ plus the maximum amount by which the
+character sticks out to the right of its box. For example, the top right
+end of the left parenthesis will be nearly at position $(w-u,"ht")$ before
+slanting, so its $x$~coordinate after slanting will be $w-u+"ht"\ast"slant"$;
+this will be the rightmost point of the
+character, if we assume that $"slant"\ge0$. Adding $.5u$, subtracting~$w$,
+and rewriting in terms of sharped units gives the stated formula. Notice
+that when $"slant"=0$ the statement reduces to `@italcorr@ $-.5u\0$';
+this means that unslanted left parentheses will have an italic correction
+of zero.
+
+\dangerexercise Write a program for right parentheses, to go with these
+left parentheses.
+\answer The changes are straightforward, except for the italic correction
+(for which a rough estimate like the one shown here is good enough):
+\def\xs(#1,#2){\{(z_{#1}-z_{#2})\,{\rm xscaled}\,3\}}%
+\begindisplay
+|"Right parenthesis"|;\cr
+@numeric@ $"ht"\0,"dp"\0$; \
+ $"ht"\0="body\_height"\0$; \
+ $.5["ht"\0,-"dp"\0]="axis"\0$;\cr
+@beginchar@\kern1pt(|")"|$,7u\0,"ht"\0,"dp"\0)$;
+ \ @italcorr@ $"axis"\0\ast"slant"-.5u\0$;\cr
+@pickup@ "fine.nib"; \ $\penpos1("hair"-"fine",0)$;\cr
+$\penpos2(.75["thin","thick"]-"fine",0)$; \ $\penpos3("hair"-"fine",0)$;\cr
+$\mathop{"lft"}x_{1l}=\mathop{"lft"}x_{3l}=u$; \
+ $\mathop{"rt"}x_{2r}=x_1+4u$; \
+$\mathop{"top"}y_1=h$; \
+ $y_2=.5[y_1,y_3]="axis"$;\cr
+@filldraw@ $z_{1l}\xs(2l,1l)\ldots z_{2l}\ldots\xs(3l,2l)z_{3l}$\cr
+\qquad$\dashto z_{3r}\xs(2r,3r)
+ \ldots z_{2r}\ldots\xs(1r,2r)z_{1r}\dashto\cycle$;\cr
+@penlabels@$(1,2,3)$; \ @endchar@;\cr
+\enddisplay
+We will see in Chapter 15 that it's possible to guarantee perfect symmetry
+between left and right parentheses by using picture transformations.
+
+The reader should bear in mind that the conventions of plain \MF\ and of
+Computer Modern are not hardwired into the \MF\ language; they are merely
+examples of how a person might use the system, and other typefaces may well
+be better served by quite different approaches. Our program for left
+parentheses makes use of @beginchar@, @endchar@, @italcorr@, @penlabels@,
+@pickup@, "penpos", "lft", "rt", "top", "z", and @filldraw@, all of which
+are defined somewhat arbitrarily in Appendix~B as part of the plain base;
+it also uses the quantities "u", "body\_height", "axis", "fine", "hair",
+"thin", "thick", and "slant", all of which are arbitrary parameters that
+the author decided to introduce in his programs for Computer Modern. Once
+you understand how to use arbitrary conventions like these, you will be
+able to modify them to suit your own purposes.
+
+\exercise (For people who know \TeX.) \ It's fairly clear that the width of
+a type box is important for typesetting, but what use does \TeX\ make of
+the height and depth?
+\answer 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.
+
+\ddanger The primitive commands by which \MF\ actually learns the dimensions
+of each box are rarely used directly, since they are intended to be embedded
+in higher-level commands like @beginchar@ and @italcorr@. But if you must
+know how things are done at the low level, here is the secret: There are
+four internal quantities called ^"charwd", ^"charht", ^"chardp", and ^"charic",
+whose values at the time of every ^@shipout@ command are assumed to be the
+box dimensions for the character being shipped out, in units of printer's
+points. \ (See
+the definitions of @beginchar@ and @italcorr@ in Appendix~B for examples
+of how these quantities can be manipulated.)
+
+\ninepoint % all dangerous from here on
+\ddanger Besides "charwd" and its cousins, \MF\ also has four other
+internal variables whose values are recorded at the time of every
+@shipout@:\enddanger
+
+\smallskip\textindent\bull^"charcode" is rounded to the nearest integer
+and then converted to a number between 0 and~255, by adding or subtracting
+multiples of~256 if necessary; this ``$c$~code'' is the ^{location} of the
+^^{c code} character within its font.
+
+\smallskip\textindent\bull^"charext" is rounded to the nearest integer;
+the resulting number is a secondary code that can be used to distinguish
+between two or more characters with equal $c$ codes. \ (\TeX\ ignores
+"charext" and assumes that each font contains at most 256 characters; but
+extensions to \TeX\ for ^{oriental} languages can use "charext" to handle
+much larger fonts.)
+
+\smallskip\textindent\bull^"chardx" and "chardy" represent horizontal and
+vertical {\sl escapement\/} in units of pixels. \ (Some typesetting
+systems use both of these device-dependent amounts to change their current
+position on a page, just after typesetting each character. Other systems,
+like the ^|dvi| software associated with \TeX, assume that $"chardy"=0$
+but use "chardx" as the horizontal escapement whenever a horizontal
+movement by "chardx" does not cause the subsequent position to ^{drift}
+too far from the device-independent position defined by accumulated
+"charwd" values. Plain \MF's @endchar@ routine keeps $"chardy"=0$, but
+sets $"chardx":=w$ just before shipping a character to the output. This
+explains why a change to~^"w" will affect the spacing between adjacent
+letters, as discussed earlier.) \looseness=-1
+
+\ddanger Two characters with the same $c$ code
+should have the same box dimensions and escapements; otherwise
+the second character will override the specifications of the first. The boolean
+expression `^{charexists}~$c$' can be used to determine whether or not
+a character with a particular $c$~code has already been shipped out.
+
+\danger Let's conclude this chapter by contemplating a \MF\ program that
+generates the ``^{dangerous bend}'' symbol, since that symbol appears so
+often in this book. It's a custom-made character intended to be used only at
+the very beginnings of paragraphs in which the baselines of the text are
+exactly $11\pt$ apart. Therefore it extends below its baseline by $11\pt$;
+but it is put into a box of depth zero, because \TeX\ would otherwise
+think that the first line of the paragraph contains an extremely deep
+character, and such depth would cause the second line to be moved down.
+$$\def\comment{\hfill{\tt\%} }
+\halign{\hbox to\hsize{\indent#\hfil}\cr
+$"baselinedistance"\0:=11"pt"\0$; \ ^@define\_pixels@("baselinedistance");\cr
+$"heavyline"\0:=50/36"pt"\0$; \ ^@define\_blacker\_pixels@("heavyline");\cr
+$@beginchar@\kern1pt(127,25u\0,"h\_height"\0+"border"\0,0)$; \
+ |"Dangerous bend symbol"|;\cr
+\pickup @pencircle@ scaled "rulethickness";
+ \ $\mathop{"top"}y_1={25\over27}h$; \ $\mathop{"lft"}x_4=0$;\cr
+$x_1+x_1=x_{1a}+x_{1b}=x_{4b}+x_{2a}=x_4+x_2=x_{4a}+x_{2b}=x_{3b}+x_{3a}=
+ x_3+x_3=w$;\cr
+$x_{4a}=x_{4b}=x_4+u$; \ $x_{3b}=x_{1a}=x_1-2u$;\cr
+$y_4+y_4=y_{4a}+y_{4b}=y_{3b}+y_{1a}=y_3+y_1=y_{3a}+y_{1b}=y_{2b}+y_{2a}=
+ y_2+y_2=0$;\cr
+$y_{1a}=y_{1b}=y_1-{2\over27}h$; \ $y_{4b}=y_{2a}=
+ y_4+{4\over27}h$;\cr
+@draw@ $z_{1a}\to z_1\to z_{1b}\ddashto z_{2a}\to z_2\to z_{2b}\ddashto$\cr
+\indent $z_{3a}\to z_3\to z_{3b}\ddashto z_{4a}\to z_4\to z_{4b}
+ \ddashto \rm cycle$;\comment the signboard\cr
+$x_{10}=x_{11}=x_{12}=x_{13}=.5w-u$;
+ \ $x_{14}=x_{15}=x_{16}=x_{17}=w-x_{10}$;\cr
+$y_{10}=y_{14}={28\over27}h$; \ $\mathop{"bot"}y_{13}=-"baselinedistance"$;\cr
+$z_{11}=(z_{10}\to z_{13})\;{\rm intersectionpoint}\;
+ (z_{1a}\{z_{1a}-z_{4b}\}\to z_1\{"right"\})$;\cr
+$y_{15}=y_{11}$; \ $y_{16}=y_{12}=-y_{11}$; \ $y_{17}=y_{20}=y_{21}=y_{13}$;\cr
+@draw@ $z_{11}\dashto z_{10}\dashto z_{14}\dashto z_{15}$;
+ @draw@ $z_{12}\dashto z_{13}$;
+ @draw@ $z_{16}\dashto z_{17}$; \comment the signpost\cr
+$x_{20}=w-x_{21}$; \ $x_{21}-x_{20}=16u$;
+ \ @draw@ $z_{20}\dashto z_{21}$; \comment ground level\cr
+$x_{36}=w-x_{31}$; \ $x_{36}-x_{31}=8u$;
+ \ $x_{32}=x_{33}=x_{36}$; \ $x_{31}=x_{34}=x_{35}$;\cr
+$y_{31}=-y_{36}={12\over27}h$; \ $y_{32}=-y_{35}={9\over27}h$;
+ \ $y_{33}=-y_{34}={3\over27}h$;\cr
+\pickup @pencircle@ scaled "heavyline";\cr
+@draw@ $z_{32}\{z_{32}-z_{31}\}\to z_{33}\ddashto
+ z_{34}\to z_{35}\{z_{36}-z_{35}\}$;
+ \comment the dangerous bend\cr
+\pickup ^@penrazor@ xscaled "heavyline"
+ ^{rotated} (^{angle}$(z_{32}-z_{31})+90$);\cr
+@draw@ $z_{31}\dashto z_{32}$;
+ \ @draw@ $z_{35}\dashto z_{36}$; \comment upper and lower bars\cr
+^@labels@$(1a,1b,2a,2b,3a,3b,4a,4b,@range@ 1 @thru@ 36)$; \ @endchar@;
+^^@range@^^@thru@\cr
+}$$
+
+\setbox0=\vtop{\kern -5pt
+ \figbox{12f}{500\apspix}{4.2in}\vbox}
+\dp0=0pt
+\vskip 18pt
+
+{\tolerance=2000 \hbadness=2000 \spaceskip=.3333em plus .25em minus .12em
+\hangindent 515\apspix
+\noindent\hbox to 515\apspix{\box0\hfil}%
+This program has several noteworthy points of~interest:
+(1)~The first parameter to ^@beginchar@ here is 127, not a
+string; this puts the character into font ^{location}~127. \ (2)~A sequence
+of equations like `$a=w-b$; $a'=w-b'$' can conveniently be shortened to
+`$a+b=a'+b'=w$'. \ (3)~Three hyphens `$\ddashto$' is an abbreviation for a
+line with ``infinite'' tension, ^^{---} i.e., an almost straight line that
+connects smoothly to its curved neighbors. \ (4)~An `intersectionpoint'
+operation finds out where ^^{intersectionpoint}
+two paths cross; we'll learn more about this in Chapter~14.\par}
+
+\endchapter
+
+Well, we are in the same box.
+\author RIDER ^{HAGGARD}, {\sl Dawn\/} (1884) % beginning of chapter 47
+
+\bigskip
+
+A story, too,
+may be boxed.
+\author DOROTHY ^{COLBURN}, {\sl Newspaper Nomenclature\/} (1927)
+ % American Speech v2 Feb 27 p240
+
+\eject
+ \beginchapter Chapter 13. Drawing, Filling,\\and Erasing
+
+The pictures that \MF\ produces are made up of tiny pixels that are either
+``on'' or ``off''; therefore you might imagine that the computer works
+behind the scenes with some sort of ^{graph paper}, and that it darkens some
+of the squares whenever you tell it to @draw@ a line or to @fill@ a region.
+
+\newdimen\tinypix \setbox0=\hbox{\sixrm0} \tinypix=5pt
+\newdimen\pixcorr \pixcorr=\tinypix \advance\pixcorr by-\wd0
+\def\spread#1{\if#1!\let\next\relax\else#1\kern\pixcorr\let\next\spread\fi
+ \next}
+\def\beginpixdisplay{$$\advance\abovedisplayskip by 2pt
+ \advance\belowdisplayskip by-2pt
+ \baselineskip=\tinypix
+ \halign\bgroup\sixrm\indent\spread##!\hfil\cr}
+\MF's internal graph paper is actually more sophisticated than this.
+Pixels aren't simply ``on'' or ``off'' when \MF\ is working on a picture;
+they can be ``doubly on'' or ``triply off.'' Each pixel contains a
+small {\sl integer\/} value, and when a character is finally shipped out
+to a font the black pixels are those whose value is greater than zero.
+For example, the two commands
+\begindisplay
+^@fill@ $(0,3)\dashto(9,3)\dashto(9,6)\dashto(0,6)\dashto\cycle$;\cr
+@fill@ $(3,0)\dashto(3,9)\dashto(6,9)\dashto(6,0)\dashto\cycle$
+\enddisplay
+yield the following $9\times9$ pattern of pixel values:
+\beginpixdisplay
+000111000\cr
+000111000\cr
+000111000\cr
+111222111\cr
+111222111\cr
+111222111\cr
+000111000\cr
+000111000\cr
+000111000\cr
+\enddisplay
+Pixels that have been filled twice now have a value of 2.
+
+When a simple region is ``filled,'' its pixel values are all increased by~1;
+when it is ``unfilled,'' they are all decreased by~1. The command
+\begindisplay
+^@unfill@ $(1,4)\dashto(8,4)\dashto(8,5)\dashto(1,5)\dashto\cycle$
+\enddisplay
+will therefore change the pattern above to
+\beginpixdisplay
+000111000\cr
+000111000\cr
+000111000\cr
+111222111\cr
+100111001\cr
+111222111\cr
+000111000\cr
+000111000\cr
+000111000\cr
+\enddisplay
+The pixels in the center have not been erased (i.e., they will still be
+black if this picture is output to a font), because they still have a
+positive value.
+
+Incidentally, this example illustrates the fact that the edges between
+\MF's pixels are lines that have integer ^{coordinates}, just as the
+squares on graph paper do. For example, the lower left `{\sixrm0}' in
+the $9\times9$ array above corresponds to the pixel whose boundary is
+`$(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto\cycle$'. The $(x,y)$
+coordinates of the points inside this pixel lie between 0 and~1.
+
+\exercise What are the $(x,y)$ coordinates of the four corners of the
+{\sl middle\/} pixel in the $9\times9$ array?
+\answer $(4,4)$, $(4,5)$, $(5,5)$, $(5,4)$. \ (Therefore the command
+\begindisplay
+@unfill@ $(4,4)\dashto(4,5)\dashto(5,5)\dashto(5,4)\dashto\cycle$
+\enddisplay
+will decrease the value of this pixel by 1.)
+
+\exercise What picture would have been obtained if the @unfill@ command
+had been given {\sl before\/} the two @fill@ commands in the examples
+above?
+\answer The result would be exactly the same; @fill@ and @unfill@ commands
+can be given in any order. \ (After an initial @unfill@ command, some
+pixel values will be $-1$, the others will be zero.)
+
+\exercise Devise an @unfill@ command that will produce the pixel values
+\beginpixdisplay
+000111000\cr
+000101000\cr
+000101000\cr
+111212111\cr
+100101001\cr
+111212111\cr
+000101000\cr
+000101000\cr
+000111000\cr
+\enddisplay
+when it is used just after the @fill@ and @unfill@ commands already given.
+\answer @unfill@ $(4,1)\dashto(4,8)\dashto(5,8)\dashto(5,1)\dashto\cycle$.
+
+A ``simple'' region is one whose boundary does not intersect itself; more
+complicated effects occur when the boundary lines cross. For example,
+\begindisplay
+@fill@ $(0,1)\dashto(9,1)\dashto(9,4)\dashto(4,4)\dashto$\cr
+\indent$(4,0)\dashto(6,0)\dashto
+ (6,3)\dashto(8,3)\dashto(8,2)\dashto(0,2)\dashto\cycle$\cr
+\enddisplay
+produces the pixel pattern
+\beginpixdisplay
+000011111\cr
+000011001\cr
+111122111\cr
+000011000\cr
+\enddisplay
+Notice that some pixels receive the value 2, because they're ``^{doubly
+filled}.'' There's also a ``^{hole}'' where the pixel values remain zero,
+even though they are surrounded by filled pixels; the pixels in that hole
+are not considered to be in the region, but the doubly filled pixels
+are considered to be in the region twice.
+
+\exercise Show that the first $9\times9$ cross pattern on the previous
+page can be generated by a single @fill@ command. \ (The nine pixel
+values in the center should be~2, as if two separate regions had been
+filled, even though you are doing only one @fill@.)
+\answer Here are two of the many solutions:
+\begindisplay
+@fill@ $(0,3)\dashto(9,3)\dashto(9,6)\dashto(6,6)\dashto(6,9)\dashto$\cr
+\indent $(3,9)\dashto(3,0)\dashto(6,0)\dashto(6,6)\dashto(0,6)\dashto\cycle$;\cr
+@fill@ $(0,3)\dashto(9,3)\dashto(9,6)\dashto(0,6)\dashto(0,3)\dashto$\cr
+\indent $(3,3)\dashto(3,0)\dashto(6,0)\dashto(6,9)\dashto(3,9)\dashto
+ (3,3)\dashto\cycle$.\cr
+\enddisplay
+(It turns out that {\sl any\/} pixel pattern can be obtained by a single,
+sufficiently hairy @fill@ command. But unnatural commands are usually also
+inefficient and unreadable.)
+
+\exercise What do you think is the result of `@fill@ $(0,0)\dashto(1,0)\dashto
+(1,1)\dashto(0,1)\dashto(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto
+\cycle$'\thinspace?
+\answer The value of the enclosed pixel is increased by 2. \ (We'll see later
+that there's a simpler way to do this.)
+
+A @fill@ command can produce even stranger effects when its boundary lines
+cross in only one place. If you say, for example,
+\begindisplay
+@fill@ $(0,2)\dashto(4,2)\dashto(4,4)\dashto(2,4)\dashto(2,0)
+ \dashto(0,0)\dashto\cycle$
+\enddisplay
+\MF\ will produce the $4\times4$ pattern
+\setbox0=\hbox to\tinypix{\hss
+ $\scriptscriptstyle{\hbox to3pt{}\over}$\hss\kern\pixcorr}
+\dp0=0pt
+\beginpixdisplay
+0011\cr
+0011\cr
+!\copy0\copy0 \spread00\cr
+!\copy0\copy0 \spread00\cr
+\enddisplay
+where `$\hbox to3pt{}\over$' stands for the value $-1$. Furthermore the
+machine will report that you have a ``^{strange path}'' whose ``^{turning
+number}'' is zero! What does this mean? Basically, it means that your
+path loops around on itself something like a figure~8; this causes a
+breakdown in \MF's usual rules for distinguishing the ``inside'' and
+``outside'' of a curve.
+
+\danger Every cyclic path has a {\sl turning number\/} that can be understood
+as follows. Imagine that you are driving a car along the path and that you
+have a digital compass that tells in what direction you're heading. For
+example, if the path is
+\begindisplay
+$(0,0)\dashto(2,0)\dashto(2,2)\dashto(0,2)\dashto\cycle$
+\enddisplay
+you begin driving in direction $0^\circ$, then you make four left turns.
+After the first turn, your compass heading is $90^\circ$; after the
+second, it is $180^\circ$; and after the third it is $270^\circ$. \ (The
+compass direction increases when you turn left and decreases when you turn
+right; therefore it now reads $270^\circ$, not $-90^\circ$.) \ At the
+end of this cycle the compass will read $360^\circ$, and if you go around
+again the reading will be $720^\circ$. Similarly, if you had traversed the
+path
+\begindisplay
+$(0,0)\dashto(0,2)\dashto(2,2)\dashto(2,0)\dashto\cycle$
+\enddisplay
+(which is essentially the same, but in the opposite direction), your compass
+heading would have started at $90^\circ$ and ended at $-270^\circ$;
+in this case each circuit would have {\sl decreased\/} the reading
+by~$360^\circ$. It is clear that a drive around any cyclic path will
+change the compass heading by some multiple of~$360^\circ$, since you
+end in the same direction you started. The turning number of a path is
+defined to be $t$ if the compass heading changes by exactly $t$~times
+$360^\circ$ when the path is traversed. Thus, the two example cycles we have
+just discussed have turning numbers of $+1$ and $-1$, respectively; and
+the ``strange path'' on the previous page that produced both positive and
+negative pixel values does indeed have a turning number of~0.
+
+\danger Here's how \MF\ actually implements a @fill@ command, assuming that
+the cyclic path being filled has a {\sl positive\/} turning number:
+The path is first ``^{digitized},'' if necessary, so that it lies entirely on
+the edges of pixels; in other words, it is distorted slightly so that it
+is confined to the lines between pixels on graph paper. \ (Our examples so
+far in this chapter have not needed any such adjustments.) \ Then each
+individual pixel value is increased by~$j$ and decreased by~$k$ if an
+infinite horizontal line to the left of that pixel intersects the
+digitized path $j$~times when the path is traveling downward and $k$~times
+when it is traveling upward. For example, let's look more closely at the
+non-simple path on the previous page that enclosed a hole:
+$$\def\\#1{\hbox to 11pt{\hss$#1$\hss}}
+\def\up{\hbox to0pt{\hss\lower3pt\vbox to 11pt{
+ \hbox{\tenex\char'77}\vss\hbox{\tenex\char'170}\kern0pt}\hss}}
+\def\down{\hbox to0pt{\hss\lower3pt\vbox to 11pt{
+ \hbox{\tenex\char'171}\vss\hbox{\tenex\char'77}\kern0pt}\hss}}
+\def\under{\smash{\rlap{\lower3.2pt\vbox{\hrule width 11pt}}}}
+\def\over{\smash{\rlap{\raise7.8pt\vbox{\hrule width 11pt}}}}
+\halign{\indent#\cr
+\\a\\a\\a\\a\over\down\\b\over\\b\over\under\\b\over\under\\b\over\\b\up\cr
+\under\\a\under\\a\under\\a\under\\a\down\under\\b\under\\b\up
+ \under\\c\under\\c\down\\d\up\cr
+\down\under\\e\under\\e\under\\e\under\\e\down\under\\f\under\\f\up
+ \under\\g\under\\g\under\\g\up\cr
+\\a\\a\\a\\a\down\under\\b\under\\b\up\\h\\h\\h\cr}$$
+Pixel $d$ has $j=2$ descending edges and $k=1$ ascending edges to its left,
+so its net value increases by $j-k=1$; pixels~$g$ are similar.
+Pixels~$c$ have $j=k=1$, so they lie in a ``hole'' that is unfilled;
+pixels~$f$ have $j=2$ and $k=0$, so they are doubly filled. This rule
+works because, intuitively, the inside of a region lies at the {\sl left\/}
+of a path whose turning number is positive.
+
+\dangerexercise True or false: When the turning number of a cyclic path is
+positive, a @fill@ command increases each individual pixel value by $l-m$,
+if an infinite horizontal line to the {\sl right\/} of that pixel intersects
+the digitized path $l$~times when the path is traveling upward and $m$~times
+when it is traveling downward. \ (For example, pixels~$e$ have $l=2$ and
+$m=1$; pixels~$c$ have $l=m=1$.)
+\answer True; $j-k=l-m$, since $k+l=j+m$. \ (What comes up must go down.)
+
+\danger When the turning number is negative, a similar rule applies,
+except that the pixel values are {\sl decreased\/} by~$j$ and {\sl
+increased\/} by~$k$; in this case the inside of the region lies at the
+{\sl right\/} of the path.
+
+\danger But when the turning number is zero, the inside of the region
+lies sometimes at the left, sometimes at the right. \MF\ uses the rule
+for positive turning number and reports that the path is ``strange.''
+You can avoid this error message by setting `$"turningcheck":=0$';
+^^"turningcheck" in this case the rule for positive turning number is
+always used for filling, even when the turning number is negative.
+
+Plain \MF's ^@draw@ command is different from @fill@ in two important ways.
+First, it uses the currently-picked-up pen, thereby ``thickening'' the path.
+Second, it does not require that the path be cyclic. There is also a third
+difference, which needs to be mentioned although it is not quite as important:
+A @draw@ command may increase the value of certain pixels by more than~1,
+even if the shape being drawn is fairly simple. For example, the pixel pattern
+{\parindent=0pt
+\beginpixdisplay
+0000000000000000000000000000000000000000000000000000000000000000000000\cr
+0000001111122222111110000000000000000000000000011111111000000000000000\cr
+0000111111111211111111100000000000000000000011111111111111000000000000\cr
+0001111111111011111111110000000000000000001111111111111111110000000000\cr
+0001111111111011111111110000000000000000111111111111111111111100000000\cr
+0011111111110001111111111000000000000001111111111111111111111110000000\cr
+0011111111110001111111111000000000000011111111111111111111111111000000\cr
+0011111111110001111111111000000000000111111111111111111111111111100000\cr
+0111111111100000111111111100000000001111111111111111111111111111110000\cr
+0111111111100000111111111100000000001111111111111111111111111111110000\cr
+0111111111100000111111111100000000011111111111111111111111111111111000\cr
+0111111111100000111111111100000000011111111111111111111111111111111000\cr
+0111111111100000111111111100000000111111111111111112111111111111111100\cr
+0111111111100000111111111100000000111111111111111112111111111111111100\cr
+0111111111100000111111111100000001111111111111111122111111111111111110\cr
+0111111111100000111111111100000001111111111111211121111211111111111110\cr
+0111111111100000111111111100000001111111111111112122221111111111111110\cr
+0111111111100000111111111100000001111111111111111100111111111111111110\cr
+0111111111100000111111111100000001111111111111112000011111111111111110\cr
+0111111111100000111111111100000001111111111112211000011211111111111110\cr
+0111111111100000111111111100000000111111111111110000001111111111111100\cr
+0111111111100000111111111100000000111111111111110000001111111111111100\cr
+0111111111100000111111111100000000011111111111100000000111111111111000\cr
+0111111111100000111111111100000000001111111111000000000011111111110000\cr
+0111111111100000111111111100000000000011111100000000000000111111000000\cr
+0000000000000000000000000000000000000000000000000000000000000000000000\cr
+\enddisplay}%
+was produced by two @draw@ commands. The left-hand shape came from
+\begindisplay
+\pickup ^@penrazor@ scaled 10;\quad \% a pen of width 10 and height 0\cr
+@draw@ $(6,1)\{"up"\}\to(13.5,25)\to\{"down"\}(21,1)$;\cr
+\enddisplay
+it's not difficult to imagine why some of the top pixels get the value~2
+here because an actual razor-thin pen would cover those pixels twice as it
+follows the given path. But the right-hand shape, which came from
+\begindisplay
+\pickup @pencircle@ scaled 16; \ @draw@ $(41,9)\to(51,17)\to(61,9)$
+\enddisplay
+is harder to explain; there seems to be no rhyme or reason to the pattern
+of 2's in that case. \MF's method for drawing curves with thick pens is
+too complicated to explain here, so we shall just regard it as a curious
+process that occasionally shoots out extra spurts of ink in the interior
+of the shape that it's filling. Sometimes a pixel value even gets as high
+as 3~or more; but if we ignore such anomalies and simply consider the set
+of pixels that receive a positive value, we find that a reasonable shape
+has been drawn.
+
+The left-parenthesis example in Chapter 12 illustrates the ^@filldraw@
+command, which is like @fill@ in that it requires a cyclic path, and like
+@draw@ in that it uses the current pen. Pixel values are increased inside
+the region that you would obtain by drawing the specified path with the current
+pen and then filling in the interior. Some of the pixel values in this
+region may increase by 2~or more. The turning number of the path
+should be nonzero.
+
+Besides @fill@, @draw@, and @filldraw@, you can also say `^@drawdot@',
+as illustrated at the beginning of Chapter~5. In this case you should specify
+only a single point; the currently-picked-up pen will be used to increase
+pixel values by~1 around that point. Chapter~24 explains that this gives
+slightly better results than if you were to draw a one-point path.
+
+\danger There's also an ^@undraw@ command, analogous to @unfill@; it
+decreases pixel values by the same amount that @draw@ would increase them.
+Furthermore---as you might expect---^@unfilldraw@ and ^@undrawdot@ are the
+respective opposites of @filldraw@ and @drawdot@.
+
+\danger If you try to use @unfill@ and/or @undraw@ in connection with
+@fill@ and/or @draw@, you'll soon discover that something else is
+necessary. Plain \MF\ has a ^@cullit@ command that replaces all
+negative pixel values by~0 and all positive pixel values by~1. This
+``^{culling}'' operation makes it possible to erase unwanted sections
+of a picture in spite of the vagaries of @draw@ and @undraw@, and in spite of
+the fact that overlapping regions may be doubly filled.
+
+\danger The command `^@erase@ @fill@ $c$' is an abbreviation for
+`@cullit@; @unfill@~$c$; @cullit@'; this zeros out the pixel values inside
+the cyclic path~$c$, and sets other pixel values to~1 if they were positive
+before erasing took place. \ (It works because the initial @cullit@ makes
+all the values 0 or~1, then the @unfill@ changes the values inside~$c$
+to 0 or negative. The final @cullit@ gets rid of the negative values,
+so that they won't detract from future filling and drawing.) \ You can
+also use `@draw@', `@filldraw@', or `@drawdot@' with `@erase@'; for example,
+`@erase@ @draw@~$p$' is an abbreviation for `@cullit@; @undraw@~$p$;
+@cullit@', which uses the currently-picked-up pen as if it were an
+eraser applied to path~$p$.
+
+{\ninepoint
+\medbreak
+\parshape 7 3pc 17pc 3pc 17pc
+0pc 20pc 0pc 20pc 0pc 20pc 0pc 20pc 0pc 29pc
+\noindent
+\hbox to0pt{\hskip-3pc\dbend\hfill}%
+\rightfig 13a ({166.66667\apspix} x {133.33333\apspix}) ^9pt
+The cube at the right of this paragraph illustrates one of the effects that
+is easily obtained by erasing. First the eight points are defined, and
+the ``back'' square is drawn; then two lines of the ``front'' square are
+erased, using a somewhat thicker pen; finally the remaining lines are
+drawn with the ordinary pen:
+\begindisplay
+$s\0:=5"pt"\0$; \ @define\_pixels@$(s)$; \ |%| side of the square\cr
+$z_1=(0,0)$; \ $z_2=(s,0)$; \ $z_3=(0,s)$; $z_4=(s,s)$;\cr
+^@for@ $k=1$ @upto@ 4: $z_{k+4}=z_k+({2\over3}s,{1\over3}s)$; \ @endfor@\cr
+\pickup @pencircle@ scaled $.4"pt"$; \
+@draw@ $z_5\dashto z_6\dashto z_8\dashto z_7\dashto \cycle$;\cr
+\pickup @pencircle@ scaled $1.6"pt"$; \
+@erase@ @draw@ $z_2\dashto z_4\dashto z_3$;\cr
+\pickup @pencircle@ scaled $.4"pt"$; \
+@draw@ $z_1\dashto z_2\dashto z_4\dashto z_3\dashto \cycle$;\cr
+@for@ $k=1$ @upto@ 4: @draw@ $z_k\dashto z_{k+4}$; \ @endfor@.\cr
+\enddisplay
+At its true size the resulting ^{cube} looks like this:
+`\thinspace{\manual\cubea}\thinspace'.\par}
+
+\dangerexercise Modify the draw-and-erase construction in the preceding
+paragraph so that you get the {\sl^{impossible cube}\/}
+`\thinspace{\manual\cubeb}\thinspace' instead.
+\answer The tricky part is to remember that `@erase@ @draw@ $z_i\dashto z_j$'
+will erase pixels near $z_i$ and $z_j$. Therefore if $z_3\dashto z_4$ is
+drawn before $z_4\dashto z_2$, we can't erase $z_4\dashto z_2$ without losing
+some of $z_3\dashto z_4$; it's necessary to erase only part of one line.
+One way to solve the problem is to do the following, after defining the
+points and picking up the pen as before:
+\begindisplay
+@draw@ $z_3\dashto z_4$; \ @draw@ $z_5\dashto z_6$;\cr
+^@cullit@; \ \pickup @pencircle@ scaled $1.6"pt"$;\cr
+^@undraw@ $z_7\dashto {1\over2}[z_7,z_5]$; \
+ @undraw@ $z_2\dashto {1\over2}[z_2,z_4]$;\cr
+@cullit@; \ \pickup @pencircle@ scaled $.4"pt"$;\cr
+@draw@ $z_3\dashto z_1\dashto z_2\dashto z_4$; \
+ @draw@ $z_5\dashto z_7\dashto z_8\dashto z_6$;\cr
+@for@ $k=1$ @upto@ 4: \ @draw@ $z_k\dashto z_{k+4}$; \ @endfor@.\cr
+\enddisplay
+(Note that it would not be quite enough to erase only from $z_7$ to
+${1\over3}[z_7,z_5]$!)\par
+It's also possible to solve this problem without partial erasing, if we
+use additional features of \MF\ that haven't been explained yet. Let's
+consider only the job of drawing $z_7\dashto z_5\dashto z_6$ and
+$z_3\dashto z_4\dashto z_2$, since the other eight lines can easily be
+added later. Alternative Solution~1 uses picture operations:
+\begindisplay
+@pen@ "eraser"; \ $"eraser"=@pencircle@$ scaled $1.6"pt"$;\cr
+@draw@ $z_3\dashto z_4$; \
+@erase@ @draw@ $z_7\dashto z_5$ ^@withpen@ "eraser"; \
+@draw@ $z_7\dashto z_5$;\cr
+@picture@ "savedpicture"; \ $"savedpicture"="currentpicture"$; \ ^@clearit@;\cr
+@draw@ $z_6\dashto z_5$; \
+@erase@ @draw@ $z_2\dashto z_4$ ^@withpen@ "eraser"; \
+@draw@ $z_2\dashto z_4$;\cr
+^@addto@ "currentpicture" @also@ "savedpicture".\cr
+\enddisplay
+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:
+\begindisplay
+@draw@ $z_3\dashto z_4$; \
+^@undraw@ $z_7\dashto z_5$ @withpen@ "eraser";\cr
+@draw@ $z_7\dashto z_5$ @withweight@ 2; \
+^@cullit@ @withweight@ 2;\cr
+@draw@ $z_6\dashto z_5$; \
+^@undraw@ $z_2\dashto z_4$ @withpen@ "eraser";\cr
+@draw@ $z_2\dashto z_4$ @withweight@ 2;\cr
+\enddisplay
+(These alternative solutions were suggested by Bruce ^{Leban}.)
+
+\dangerexercise Write a \MF\ program to produce the symbol
+`{\manual\bicentennial}'. \ [{\sl Hints:\/} The character is $10\pt$
+wide, $7\pt$ high, and $2\pt$ deep. The starlike path can be defined by
+five points connected by ``tense'' lines as follows:
+\begindisplay
+@pair@ "center"; \ $"center"=(.5w,2"pt")$;\cr
+@numeric@ "radius"; \ $"radius"=5"pt"$;\cr
+@for@ $k=0$ @upto@ 4: \ $z_k="center"+("radius",0)$
+ ^{rotated}$(90+{360\over5}k)$; \ @endfor@\cr
+@def@ :: = ^^{tension} $\to\tension 5\to$ @enddef@;\cr
+@path@ "star"; \ $"star"=z_0::z_2::z_4::z_1::z_3::\cycle$;\cr
+\enddisplay
+You probably want to work with ^{subpaths} of ^"star" instead of drawing the
+whole path at once, in order to give the illusion that the curves cross over
+and under each other.]
+\answer Here's an analog of the first solution to the previous
+exercise:
+\begindisplay
+@beginchar@\kern1pt(|"*"|$,10"pt"\0,7"pt"\0,2"pt"\0)$;\cr
+@pair@ "center"; \dots \<as in the hint>\cr
+\pickup @pencircle@ scaled $.4"pt"$; \ @draw@ "star";\cr
+@cullit@; \ \pickup @pencircle@ scaled $1.6"pt"$;\cr
+@for@ $k=0$ @upto@ 4:
+ \ @undraw@ subpath$(k+.55,k+.7)$ @of@ "star"; \ @endfor@\cr
+@cullit@; \ \pickup @pencircle@ scaled $.4"pt"$;\cr
+@for@ $k=0$ @upto@ 4: \ @draw@ subpath$(k+.47,k+.8)$ @of@ "star"; \ @endfor@\cr
+@labels@(0,1,2,3,4); \ @endchar@.\cr
+\enddisplay
+However, as in the previous case, there's an Alternate Solution~1
+by Bruce ^{Leban} that is preferable because it doesn't depend
+on magic constants like .55 and~.47:
+\begindisplay
+@beginchar@ $\ldots$ \<as above> $\ldots$ scaled $.4"pt"$;\cr
+@picture@ "savedpicture"; \ $"savedpicture"=@nullpicture@$;\cr
+@pen@ "eraser"; \ $"eraser":=@pencircle@$ scaled $1.6"pt"$;\cr
+@for@ $k=0$ @upto@ 4:\cr
+\indent @draw@ subpath$(k,k+1)$ @of@ "star"; @cullit@;\cr
+\indent @undraw@ subpath$(k+3,k+4)$ @of@ "star" @withpen@ "eraser"; @cullit@;\cr
+\indent @addto@ "savedpicture" @also@ "currentpicture"; @clearit@; @endfor@\cr
+$"currentpicture":="savedpicture"$; \ @labels@(0,1,2,3,4); \ @endchar@.\cr
+\enddisplay
+
+\dangerexercise What does the command `@fill@ "star"' do, if "star" is the
+path defined above?
+\answer It increases pixel values by 1 in the five lobes of the star, and by~2
+in the central pentagon-like region.
+
+\decreasehsize 6pc
+\dangerexercise Devise a ^{macro} called `^@overdraw@' such that the command
+\rightfig 13aa (50pt x 100pt) ^11pt
+`@overdraw@~$c$' will erase the inside of region~$c$ and will then draw the
+boundary of~$c$ with the currently-picked-up pen, assuming that $c$~is a
+cyclic path that doesn't intersect itself. \ (Your macro could be used, for
+example, in the program
+\begindisplay
+@path@ $S$; \ $S=((0,1)\to(2,0)\to(4,2)\to$\cr
+\indent$(2,5.5)\to(0,8)\to(2,10)\to(3.5,9))$ scaled $9"pt"$;\cr
+@for@ $k=0$ @upto@ 35: @overdraw@ ^"fullcircle" scaled 3"mm"\cr
+\indent shifted ^{point} $k/35\ast \mathop{\rm length} S$ @of@ $S$;
+ @endfor@\cr
+\enddisplay
+to create the curious ^{S} shown here.)
+\answer @def@ @overdraw@ @expr@ $c$ = @erase@ @fill@ $c$; @draw@ $c$ @enddef@.
+
+\restorehsize
+\ddangerexercise The ^{M\"obius} Watchband Corporation has a logo that
+looks like this:
+\displayfig 13bb (.5in)
+Explain how to produce it (or something very similar) with \MF\!.
+\answer 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:
+\begindisplay
+@def@ @overdraw@ @expr@ $c$ = ^@begingroup@\cr
+\indent@picture@ "region"; $"region":=@nullpicture@$;\cr
+\indent^@interim@ $"turningcheck":=0$; ^@addto@ "region" @contour@ $c$;\cr
+\indent^@cull@ "region" @dropping@ $(0,0)$;\cr
+\indent^@cullit@; @addto@ "currentpicture" ^@also@ $-"region"$; @cullit@;\cr
+\indent@draw@ $c$ ^@endgroup@ @enddef@;\cr
+\enddisplay
+(This code uses operations defined later in this chapter; it erases the
+"region" of pixels that would be made nonzero by the command `@fill@~$c$'.)
+\ The watchband is now formed by overdrawing its links, one at a time,
+doing first the ones that are underneath:
+\begindisplay
+@beginchar@$("M",1.25"in"\0,.5"in"\0,0)$; \
+ \pickup @pencircle@ scaled .4"pt";\cr
+$z_1=(20,-13)$; \ $z_2=(30,-6)$; \ $z_3=(20,1)$; $z_4=(4,-7)$;\cr
+\indent $z_5=(-12,-13)$; \ $z_6=(-24,-4)$; \ $z_7=(-15,6)$;\cr
+@path@ $M$; $M=("origin"\to z1\to z2\to z3\to z4\to z5\to z6\to z7\to$\cr
+\indent$"origin"\to -z7\to -z6\to -z5\to -z4\to -z3\to -z2\to -z1\to\cycle)$\cr
+^^"origin" \indent\indent scaled $(h/26)$ shifted $(.5w,.5h)$;\cr
+@def@ @link@(@expr@ $n$) =\cr
+\indent @overdraw@ subpath ${1\over3}(n,n+1)$ of $M\;\dashto$\cr
+\indent\indent subpath ${1\over3}(n+25,n+24)$ of $M\;\dashto\;\cycle\;$
+ @enddef@;\cr
+@for@ $k=1$ @upto@ 12: @link@$(k+11)$; @link@$(12-k)$; @endfor@
+@endchar@;\cr
+\enddisplay
+
+\danger Chapter 7 points out that variables can be of type `^@picture@',
+and Chapter~8 mentions that expressions can be of type `@picture@', but
+we still haven't seen any examples of picture variables or picture
+expressions. Plain \MF\ keeps the currently-worked-on picture in a
+picture variable called ^"currentpicture", and you can copy it by
+equating it to a picture variable of your own. For example, if you
+say `@picture@ $v[\,]$' at the beginning of your program, you can write
+equations like
+\begindisplay
+$v_1="currentpicture"$;
+\enddisplay
+this makes $v_1$ equal to the picture that has been drawn so far; i.e.,
+it gives $v_1$ the same array of pixel values that "currentpicture" now has.
+
+\begingroup\def\dbend{{\manual\char0}} % reverse-video dangerous bend sign
+\danger Pictures can be added or subtracted; for example, $v_1+v_2$
+^^{sum of pictures} ^^{negative of a picture} ^^{inverse video}
+stands for the picture whose pixel values are the sums of the pixel
+values of $v_1$ and~$v_2$. The ``^{reverse-video} ^{dangerous bend}'' sign that
+heads this paragraph was made by substituting the following code for
+the `@endchar@' in the program at the end of Chapter~12:
+\begindisplay
+@picture@ "dbend"; \ $"dbend"="currentpicture"$;\cr
+@endchar@; \ |%| end of the normal dangerous bend sign\cr
+@beginchar@$(0,25u\0,"h\_height"\0+"border"\0,0)$;\cr
+@fill@ $(0,-11"pt")\dashto(w,-11"pt")\dashto(w,h)\dashto(0,h)\dashto\cycle$;\cr
+$"currentpicture":="currentpicture"-"dbend"$;\cr
+@endchar@;\ |%| end of the reversed dangerous bend sign\cr
+\enddisplay
+^^{black/white reversal} The pixel values in "dbend" are all zero or more;
+thus the pixels with a positive value, after "dbend" has been subtracted from
+a filled rectangle, will be those that are inside the rectangle
+but zero in "dbend".
+
+\endgroup % back to normal \dbend
+
+\danger We will see in Chapter 15 that pictures can also be shifted,
+reflected, and rotated by multiples of $90^\circ$. For example,
+the statement `$"currentpicture":="currentpicture"$~shifted~3"right"'
+shifts the entire current picture three pixels to the right.
+
+\danger There's a ``constant'' picture called ^@nullpicture@, whose pixel
+values are all zero;
+plain \MF\ defines `^@clearit@' to be an abbreviation for the
+assignment `"currentpicture":=@nullpicture@'. The current picture is
+cleared automatically by every ^@beginchar@ and ^@mode\_setup@ command,
+so you usually don't have to say `@clearit@' in your own programs.
+
+\danger Here's the formal syntax for picture expressions. Although \MF\ has
+comparatively few built-in operations that deal with entire pictures,
+the operations that do exist have the same syntax as the similar operations
+we have seen applied to numbers and pairs.
+\beginsyntax
+<picture primary>\is<picture variable>
+ \alt[nullpicture]
+ \alt[(]<picture expression>[)]
+ \alt<plus or minus><picture primary>
+<picture secondary>\is<picture primary>
+ \alt<picture secondary><transformer>
+<picture tertiary>\is<picture secondary>
+ \alt<picture tertiary><plus or minus><picture secondary>
+<picture expression>\is<picture tertiary>
+\endsyntax
+
+\danger The ``total weight'' of a picture is the sum of all its pixel
+values, divided by 65536; you can compute this numeric quantity by
+saying
+\begindisplay
+^|totalweight| \<picture primary>.
+\enddisplay
+\MF\ divides by 65536 in order to avoid overflow in case of huge pictures.
+If the totalweight function returns a number whose absolute
+value is less than~.5, as it almost always is, you can safely divide that number
+by ^"epsilon" to obtain the integer sum of all pixel values
+(since $"epsilon"=1/65536$).
+
+\danger Let's turn to the computer again and try to evaluate some simple
+picture expressions interactively, using the general routine |expr.mf|
+of Chapter~8. When \MF\ says `|gimme|', you can type
+\begintt
+hide(fill unitsquare) currentpicture
+\endtt
+and the machine will respond as follows:
+\begintt
+>> Edge structure at line 5:
+row 0: 0+ 1- ||
+\endtt
+What does this mean? Well, `^@hide@' is plain \MF's sneaky way to insert
+a command or sequence of commands into the middle of an expression; such
+commands are executed before the rest of the expression is looked at. In
+this case the command `@fill@ "unitsquare"' sets one pixel value of the
+current picture to~1, because ^"unitsquare" is plain \MF's abbreviation
+for the path $(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto\cycle$. The
+value of "currentpicture" is displayed as `|row|~|0:| |0+|~|1-|', because
+this means
+``in row~0, the pixel value increases at $x=0$ and decreases at $x=1$.''
+
+\danger \MF\ represents pictures internally by remembering only the vertical
+^{edges} where pixel values change. For example, the picture just displayed
+has just two edges, both in row~0, i.e., both in the row between $y$~coordinates
+0 and~1. \ (Row~$k$ contains vertical edges whose $x$~coordinates are integers
+and whose $y$~coordinates run between $k$ and $k+1$.) \ The fact that edges
+are represented, rather than entire arrays of pixels, makes it possible for
+\MF\ to operate efficiently at high resolutions, because the number of edges
+in a picture is essentially proportional to the ^{resolution} while the total
+number of pixels is proportional to the resolution {\sl squared}. A ten-fold
+increase in resolution therefore calls for only a ten-fold (rather than a
+hundred-fold) increase in memory space and execution time.
+
+\def\pixpat#1#2#3#4{\vcenter{\sixrm\baselineskip=\tinypix
+ \hbox{#1\kern\pixcorr#2}\hbox{#3\kern\pixcorr#4}}}
+\ddanger Continuing our computer experiments, let's declare a picture variable
+and fill a few more pixels:
+\begintt
+hide(picture V; fill unitsquare scaled 2; V=currentpicture) V
+\endtt
+The resulting picture has pixel values $\pixpat1121\,$,
+and its edges are shown thus:
+\begintt
+>> Edge structure at line 5:
+row 1: 0+ 2- ||
+row 0: 0+ 2- 0+ 1- ||
+\endtt
+If we now type `|-V|', the result is similar but with the signs changed:
+\begintt
+>> Edge structure at line 5:
+row 1: 0- 2+ ||
+row 0: 0- 2+ 0- 1+ ||
+\endtt
+(You should be doing the experiments as you read this.) \ A more interesting
+picture transformation occurs if we ask for `|V|~|rotated-90|'; the picture
+$\pixpat2111$ appears below the baseline, hence the following edges are shown:
+\begintt
+>> Edge structure at line 5:
+row -1: || 0++ 1- 2-
+row -2: || 0+ 2-
+\endtt
+Here `^|++|' denotes an edge where the weight increases by 2. The edges appear
+^^|+++| {\sl after\/} ^{vertical line}s `\|' in this case, while they appeared
+{\sl before\/} vertical lines in the previous examples; this means that \MF\
+has sorted the edges by their $x$~coordinates. Each @fill@ or @draw@ instruction
+contributes new edges to a picture, and unsorted edges accumulate until
+\MF\ needs to look at them in left-to-right order. \ (Type
+\begintt
+V rotated-90 rotated 90
+\endtt
+to see what $V$ itself looks like when its edges have been sorted.) \ The
+expression
+\begintt
+V + V rotated 90 shifted 2right
+\endtt
+produces an edge structure with both sorted and unsorted edges:
+\begintt
+>> Edge structure at line 5:
+row 1: 0+ 2- || 0+ 2-
+row 0: 0+ 2- 0+ 1- || 0+ 1+ 2--
+\endtt
+In general, addition of pictures is accomplished by simply combining the
+unsorted and sorted edges of each row separately.
+
+\ddangerexercise Guess what will happen if you type `|hide(cullit)|
+|currentpicture|' now; and verify your guess by actually doing the experiment.
+\answer The pixel pattern $\pixpat1121$ is culled to $\pixpat1111\,$,
+and \MF\ needs to sort the edges as it does this; so the result is simply
+\begintt
+row 1: || 0+ 2-
+row 0: || 0+ 2-
+\endtt
+
+\ddangerexercise Guess (and verify) what will happen when you type the
+expression
+\begintt
+(V + V + V rotated 90 shifted 2right
+ - V rotated-90 shifted 2up) rotated 90.
+\endtt
+[You must type this monstrous formula all on one line, even though it's too
+long to fit on a single line in this book.]
+\answer The pixel pattern is $\pixpat1121+\pixpat1121+\pixpat1112-\pixpat2111
+=\pixpat1243$ before the final rotation, with the reference point at the
+lower left corner of the~4; after rotation it is $\pixpat2314\,$, with the
+reference point at the lower {\sl right\/} corner of the~4. Rotation causes
+\MF\ to sort the edges, but the transition values per edge are never
+more than $\pm3$. You weren't expected to know about this limit of $\pm3$,
+but it accounts for what is actually reported:
+\begintt
+row 1: || -2++ -1+ 0---
+row 0: || -2+ -1+++ 0--- 0-
+\endtt
+
+\ddanger If you ask for `|V| |rotated| |45|', \MF\ will complain that
+$45^\circ$ rotation is too hard. \ (Try it.) \ After all, square pixels
+can't be ^{rotated} unless the angle of rotation is a multiple of $90^\circ$.
+On the other hand, `|V|~|scaled-1|' does work; you get
+\begintt
+>> Edge structure at line 5:
+row -1: 0- -2+ 0- -1+ ||
+row -2: 0- -2+ ||
+\endtt
+
+\ddangerexercise Why is `|V| |scaled-1|' different from `|-V|'\thinspace?
+\answer `|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 reflections `|V|~^|xscaled-1|' and
+`|V|~^|yscaled-1|' both work, and that `|V|~|scaled-1|' is the same as
+`|V|~|xscaled-1| |yscaled-1|'.)
+
+\ddangerexercise Experiment with `|V| |shifted| |(1.5,3.14159)|' and
+^^{shifted} explain what happens.
+\answer 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.
+
+\ddangerexercise Guess and verify the result of `|V| |scaled| |2|'.
+\answer |row 3: 0+ 4- |\|\parbreak
+|row 2: 0+ 4- |\|\parbreak
+|row 1: 0+ 4- 0+ 2- |\|\parbreak
+|row 0: 0+ 4- 0+ 2- |\|\par\nobreak
+\smallskip\noindent
+(Scaling of pictures must be by an integer.)
+
+\ddangerexercise Why does the machine always speak of an ^{edge structure}
+`|at| |line|~|5|'\thinspace?
+\answer \MF\ is currently executing instructions after having read
+as far as line~5 of the file |expr.mf|.
+
+\ddanger That completes our computer experiments. But before you log off,
+you might want to try typing `|totalweight V/epsilon|', just to verify
+that the sum of all pixel values in~$V$ is~5.
+
+\danger The commands we have discussed so far in this chapter---@fill@,
+@draw@, @filldraw@, @unfill@, etc.---are not really primitives of \MF;
+they are macros of plain \MF\!, defined in Appendix~B\null. Let's look now
+at the low-level operations on pictures that \MF\ actually performs
+behind the scenes. Here is the syntax:
+\beginsyntax
+<picture command>\is<addto command>\alt<cull command>
+<addto command>\is[addto]<picture variable>[also]<picture expression>
+ \alt[addto]<picture variable>[contour]<path expression><with list>
+ \alt[addto]<picture variable>[doublepath]<path expression><with list>
+<with list>\is<empty>\alt<with list><with clause>
+<with clause>\is[withpen]<pen expression>%
+ \alt[withweight]<numeric expression>\kern-3.5pt
+<cull command>\is[cull]<picture variable><keep or drop><pair expression>
+ \alt<cull command>[withweight]<numeric expression>
+<keep or drop>\is[keeping]\alt[dropping]
+\endsyntax
+The \<picture variable> in these commands should contain a known picture;
+the command modifies that picture, and assigns the resulting new value
+to the variable.
+
+\danger The first form of \<addto command>, `@addto@ $V$ @also@~$P$',
+has essentially the same meaning as `$V:=V+P$'. But the @addto@ statement
+is more efficient, because it destroys the old value of~$V$ as it adds~$P$;
+this saves both time and space. Earlier in this chapter we discussed
+the ^{reverse-video} ^{dangerous bend}, which was said to have been
+formed by the statement `$"currentpicture":="currentpicture"-"dbend"$'.
+That was a little white lie; the actual command was
+`@addto@ "currentpicture" @also@ $-"dbend"$'.
+
+\danger The details of the other forms of `@addto@' are slightly more
+complex, but (informally) they work like this, when $V="currentpicture"$
+and $q=\null$^"currentpen":
+\begindisplay
+Plain \MF&Corresponding \MF\ primitives\cr
+\noalign{\smallskip}
+^@fill@ $c$&@addto@ $V$ @contour@ $c$\cr
+^@unfill@ $c$&@addto@ $V$ @contour@ $c$ @withweight@ $-1$\cr
+^@draw@ $p$&@addto@ $V$ @doublepath@ $p$ @withpen@ $q$\cr
+^@undraw@ $p$&@addto@ $V$ @doublepath@ $p$ @withpen@ $q$ @withweight@ $-1$\cr
+^@filldraw@ $c$&@addto@ $V$ @contour@ $c$ @withpen@ $q$\cr
+^@unfilldraw@ $c$&@addto@ $V$ @contour@ $c$ @withpen@ $q$ @withweight@ $-1$\cr
+\enddisplay
+
+\ddanger The second form of \<addto command> is `@addto@ $V$ @contour@ $p$',
+followed by optional clauses that say either `@withpen@~$q$' or
+`@withweight@~$w$'. In this case $p$~must be a cyclic path; each pen~$q$
+must be known; and each weight~$w$ must be either $-3$,~$-2$, $-1$, $+1$,
+$+2$, or~$+3$, when rounded to the nearest integer. If more than one pen or
+weight is given, the last specification overrides all previous ones. If no
+pen is given, the pen is assumed to be `@nullpen@'; if no weight is given,
+the weight is assumed to be~$+1$. Thus, the second form of \<addto command>
+basically identifies a picture variable~$V$, a cyclic path~$p$, a pen~$q$,
+and a weight~$w$; and it has the following meaning, assuming that
+"turningcheck" is $\le0$: If~$q$~is the null pen, path~$p$ is digitized
+and each pixel value is increased by $(j-k)w$, where $j$ and~$k$ are the
+respective numbers of downward and upward path edges lying to the left
+of the pixel (as explained earlier in this chapter). If $q$ is not the
+null pen, the action is basically the same except that $p$ is converted to
+another path that ``^{envelope}s'' $p$ with respect to the shape of~$q$;
+this modified path is digitized and filled as before. \ (The modified path
+may cross itself in unusual ways, producing strange squirts of ink as
+illustrated earlier. But it will be well behaved if path~$p$ defines a
+^{convex} region, i.e., if a car that drives counterclockwise
+around $p$ never turns toward the right at any time.)
+
+\ddanger If $"turningcheck">0$ when an `$@addto@\ldots@contour@$' command
+^^"turningcheck" is being performed, the action is the same as just
+described, provided that path~$p$ has a positive ^{turning number}.
+However, if $p$'s turning number is negative, the action depends on
+whether or not pen~$q$ is simple or complex; a complex pen is one whose
+boundary contains at least two points. If the turning number is negative
+and the pen is simple, the weight~$w$ is changed to~$-w$. If the turning
+number is negative and the pen is complex, you get an error message about
+a ``^{backwards path}.'' Finally, if the turning number is zero, you get
+an error message about a ``^{strange path},'' unless the pen is simple and
+$"turningcheck"<=1$. Plain \MF\ sets $"turningcheck":=2$; the ^@filldraw@
+macro in Appendix~B avoids the ``backwards path'' error by explicitly
+reversing a path whose turning number is negative.
+
+\danger We mentioned that the command `@fill@ $(0,2)\dashto(4,2)\dashto
+(4,4)\dashto(2,4)\dashto(2,0)\dashto(0,0)\dashto\cycle$' causes \MF\
+to complain about a strange path; let's take a closer look at the
+error message that you get:
+\begintt
+> 0 ENE 1 NNE 2 (NNW WNW) WSW 3 SSW 4 WSW 5 (WNW NNW) NNE 0
+! Strange path (turning number is zero).
+\endtt
+What does this mean? The numbers represent ``^time'' on the cyclic path,
+from the starting point at time~0, to the next key point at time~1,
+and so on, finally returning to the starting point. Code names like
+`^|ENE|' stand for ^{compass directions} like ``East by North East'';
+\MF\ decides in which of eight ``^{octants}'' each part of a path travels,
+and |ENE| stands for all directions between the angles~$0^\circ$
+and~$45^\circ$, inclusive. Thus, this particular strange path starts in
+octant |ENE| at time~0, then it turns to octant ^|NNE| after time~1.
+An octant name is parenthesized when the path turns through that octant
+without moving; thus, for example, octants ^|NNW| and ^|WNW| are bypassed
+on the way to octant ^|WSW|. It's possible to compute the turning number
+from the given ^^|SSW| sequence of octants; therefore, if you don't think
+your path is really strange, the abbreviated octant codes should reveal
+where \MF\ has decided to take an unexpected turn. \ (Chapter~27 explains
+more about strange paths.)
+
+\ddanger The third form of \<addto command> is `@addto@ $V$ @doublepath@~$p$',
+followed by optional clauses that define a pen~$q$ and a weight~$w$ as in
+the second case. If $p$ is not a cyclic path, this case reduces to the
+second case, with $p$ replaced by the doubled-up path
+`$p\mathbin{\&}\mathop{\rm reverse}p \mathbin{\&}\cycle$' (unless $p$
+consists of only a single point, when the new path is simply
+`$p\to\cycle$'\thinspace). On the other hand if $p$ is a cyclic
+path, this case reduces to {\sl two\/} addto commands of the second type,
+in one of which $p$ is reversed; "turningcheck" is ignored during both of
+those commands.
+
+\danger An anomalous result may occur in the statement `@draw@~$p$'
+or, more generally, in `@addto@~$V$ @doublepath@~$p$ @withpen@~$q$' when
+$p$~is a very small cyclic path and the current pen~$q$ is very large: Pixels
+that would be covered by the pen regardless of where it is placed on~$p$
+might retain their original value. If this unusual circumstance hits you,
+the cure is simply to include the additional statement `@draw@~$z$' or
+`@addto@~$V$ @doublepath@~$z$ @withpen@~$q$', where $z$ is any point
+of~$p$, since this will cover all of the potentially uncovered pixels.
+
+\danger The ^@cull@ command transforms a picture variable so that
+all of its pixel values are either 0 or a specified weight~$w$, where $w$~is
+determined as in an @addto@ command. A pair of numbers $(a,b)$ is given,
+where $a$ must be less than or equal to~$b$. To cull ``@keeping@ $(a,b)$''
+means that each new pixel value is $w$ if and only if the corresponding
+old pixel value~$v$ was included in the range $a\le v\le b$; to cull
+``@dropping@ $(a,b)$'' means that each new pixel value is $w$ if and only
+if the corresponding old pixel value~$v$ was {\sl not\/} in that range.
+Thus, for example, `^@cullit@' is an abbreviation for
+\begindisplay \advance\belowdisplayskip by -4pt
+@cull@ "currentpicture" @keeping@ $(1,"infinity")$
+\enddisplay
+or for
+\begindisplay \advance\abovedisplayskip by -4pt
+@cull@ "currentpicture" @dropping@ $(-"infinity",0)$
+\enddisplay
+(which both mean the same thing). A more complicated example is
+\begindisplay
+@cull@ $V_5$ @dropping@ $(-3,2)$ @withweight@ $-2$;
+\enddisplay
+this changes the pixel values of $V_5$ to $-2$ if they were $-4$ or less,
+or if they were 3 or~more; pixel values between $-3$ and $+2$, inclusive,
+are zeroed.
+
+\danger A cull command must not change pixel values from zero to nonzero.
+For example, \MF\ doesn't let you say `@cull@ $V_1$ @keeping@ $(0,0)$',
+since that would give a value of~1 to infinitely many pixels.
+
+\dangerexercise What is the effect of the following sequence of commands?
+\begindisplay
+@picture@ $V[\,]$;\cr
+$V_1=V_2="currentpicture"$;\cr
+@cull@ $V_1$ @dropping@ $(0,0)$;\cr
+@cull@ $V_2$ @dropping@ $(-1,1)$;\cr
+$"currentpicture":=V_1-V_2$;\cr
+\enddisplay
+\answer The pixel values of "currentpicture" become 1 if they were $\pm1$,
+otherwise they become~0.
+
+\dangerexercise Given two picture variables $V_1$ and $V_2$, all of whose
+pixel values are known to be either 0 or~1, explain how to replace $V_1$ by
+(a)~$V_1\cap V_2$; \ (b)~$V_1\cup V_2$; \ (c)~$V_1\oplus V_2$. \ [The
+{\sl^{intersection}\/} $V_1\cap V_2$ has 1's where $V_1$ and $V_2$ both are~1;
+the {\sl^{union}\/} $V_1\cup V_2$ has 0's where $V_1$ and $V_2$ both are~0;
+the {\sl^{symmetric difference}\/} or {\sl^{selective complement}\/} ^^{xor}
+$V_1\oplus V_2$ has 1's where $V_1$ and $V_2$ are unequal.]
+\answer (a) @addto@ $V_1$ @also@ $V_2$; @cull@ $V_1$
+@keeping@ $(2,2)$. \ (b) Same, but cull keeping $(1,2)$.
+\ (c)~Same, but cull keeping $(1,1)$.
+
+\ddangerexercise Explain how to test whether or not two picture variables
+are equal.
+\answer Subtract one from the other, and cull the result dropping $(0,0)$;
+then test to see if the total weight is zero.
+
+\ddangerexercise Look at the definitions of @fill@, @draw@, etc., in
+Appendix~B and determine the effect of the following statements:
+\begindisplay
+\llap{a) }@draw@ $p$ @withpen@ $q$;\cr
+\llap{b) }@draw@ $p$ @withweight@ 3;\cr
+\llap{c) }@undraw@ $p$ @withweight@ $w$;\cr
+\llap{d) }@fill@ $c$ @withweight@ $-2$ @withpen@ $q$;\cr
+\llap{e) }@erase@ @fill@ $c$ @withweight@ 2 @withpen@ "currentpen";\cr
+\llap{f) }@cullit@ @withweight@ 2.\cr
+\enddisplay
+\answer (a)~Same as `@draw@ $p$', but using $q$ instead of the
+currently-picked-up pen. \ (b)~Same effect 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 `@unfilldraw@~$c$; @unfilldraw@~$c$',
+ but using $q$ instead of "currentpen".
+\ (e)~Same as `@erase@ @filldraw@~$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 effect as `@cullit@; @addto@ "currentpicture" @also@
+"currentpicture"' (but faster).
+
+\ddangerexercise Devise a ^@safefill@ macro such that `@safefill@ $c$' increases
+the pixel values of "currentpicture" by~1 in all pixels whose value would
+be changed by the command `@fill@~$c$'. \ (Unlike @fill@, the @safefill@ command
+never stops with a ``^{strange path}'' error; furthermore, it never increases
+a pixel value by more than~1, nor does it decrease any pixel values, even
+when the cycle~$c$ is quite wild.)
+\answer @vardef@ @safefill@ @expr@ $c$ $=$ ^@save@ "region";\parbreak
+\quad@picture@ "region"; "region"=@nullpicture@;\parbreak
+\quad^@interim@ ^"turningcheck"$\null:=0$;\parbreak
+\quad @addto@ "region" @contour@ $c$; \
+ @cull@ "region" @dropping@ $(0,0)$;\parbreak
+\quad @addto@ "currentpicture" @also@ "region" @enddef@.
+
+\ddangerexercise Explain how to replace a character by its ``^{outline}'':
+All black pixels whose four closest neighbors are also
+black should be changed to white, because they are in the interior.
+\ (Diagonally adjacent neighbors don't count.)
+\answer @cull@ "currentpicture" @keeping@ $(1,"infinity")$;\parbreak
+@picture@ $v$; \ $v:="currentpicture"$;\parbreak
+@cull@ "currentpicture" @keeping@ $(1,1)$ @withweight@ 3;\parbreak
+@addto@ "currentpicture" @also@
+ $v\;-\;v$ shifted "right"\parbreak
+\qquad $\null-\;v$ shifted "left"
+ $\null-\;v$ shifted "up"
+ $\null-\;v$ shifted "down";\parbreak
+@cull@ "currentpicture" @keeping@ $(1,4)$.
+
+\ddangerexercise In John ^{Conway}'s ``Game of ^{Life},'' pixels are said to
+be either alive or dead. Each pixel is in contact with eight neighbors.
+The live pixels in the $(n+1)$st generation are those who were dead and
+had exactly three live neighbors in the $n$th generation, or those
+who were alive and had exactly two or three live neighbors in the $n$th
+generation. Write a short \MF\ program that displays successive
+generations on your screen.
+\answer (We assume that "currentpicture" initially has some configuration
+in which all pixel values are zero or one; one means ``alive.'')
+\begindisplay
+@picture@ $v$; @def@ "c" $=$ "currentpicture" @enddef@;\cr
+@forever@: \ $v:=c$; \ @showit@;\cr
+\quad @addto@ $c$ @also@ $c$ shifted "left" $+$ "c" shifted "right";\cr
+\quad @addto@ $c$ @also@ $c$ shifted "up" $+$ "c" shifted "down";\cr
+\quad @addto@ $c$ @also@ $c-v$; \ @cull@ $c$ @keeping@ $(5,7)$; \ @endfor@.\cr
+\enddisplay
+(It is wise not to waste too much computer time watching this program.)
+
+\endchapter
+
+Blot out, correct, insert, refine,
+Enlarge, diminish, interline;
+Be mindful, when Invention fails,
+To scratch your Head, and bite your Nails.
+\author JONATHAN ^{SWIFT}, {\sl On Poetry: A Rapsody\/} (1733) % lines 87--90
+% Rapsody: stet!
+
+\bigskip
+
+The understanding that can be gained from computer drawings
+is more valuable than mere production.
+\author IVAN E. ^{SUTHERLAND}, {\sl Sketchpad\/} (1963) % chapter 9, section E
+
+\eject
+ \beginchapter Chapter 14. Paths
+
+The ^{boundaries} of regions to be filled, and the ^{trajectories} of
+moving pens, are ``^{paths}'' that can be specified by the general methods
+introduced in Chapter~3. \MF\ allows variables and expressions to be of
+type @path@, so that a designer can build new paths from old ones in many
+ways. Our purpose in this chapter will be to complete what Chapter~3
+began; we shall look first at some special features of plain \MF\ that
+facilitate the creation of paths, then we shall go into the details of
+everything that \MF\ knows about pathmaking.
+
+A few handy paths have been predefined in Appendix~B as part of plain \MF\!,
+because they turn out to be useful in a variety of applications. For example,
+^"quartercircle" is a path that represents one-fourth of a ^{circle} of
+diameter~1; it runs from point $(0.5,0)$ to point~$(0,0.5)$.
+The \MF\ program
+\begindisplay
+@beginchar@\kern1pt(|"a"|$,5"pt"\0,5"pt"\0,0)$;\cr
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\cr
+@draw@ "quartercircle" scaled 10"pt"; \ @endchar@;\cr
+\enddisplay
+therefore produces the character `\kern1pt{\manual\circa}' in position
+`{\tt a}' of a font.
+
+\exercise
+Write a program that puts a {\sl filled\/} quarter-circle
+`\kern1pt{\manual\circb}' into font position~`{\tt b}'.
+\answer @beginchar@\kern1pt(|"b"|$,5"pt"\0,5"pt"\0,0)$;\parbreak
+@fill@ $((0,0)\dashto"quartercircle"\dashto{\rm cycle})$
+scaled 10"pt"; \ @endchar@.
+
+\exercise
+Why are the `\kern1pt{\manual\circa}' and `\kern1pt{\manual\circb}'
+characters of these examples only $5\,$pt wide and $5\,$pt high, although
+they are made with the path `"quartercircle" scaled 10"pt"'?
+\answer A "quartercircle" corresponds to a circle whose diameter
+is~1; the radius is~$1\over2$.
+
+\dangerexercise
+Use a {\sl rotated\/} quarter-circle to produce `{\manual\circc}\kern1pt'
+in font position `{\tt c}'.
+\answer @beginchar@\kern1pt(|"c"|$,5"pt"\0,5"pt"\0,0)$;\parbreak
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\parbreak
+@draw@ "quartercircle" rotated 90 scaled 10"pt" shifted $(5"pt",0)$;
+ \ @endchar@.
+
+\dangerexercise
+Use "quartercircle" to produce `\kern1pt{\manual\circd}\kern1pt'
+in font position `{\tt d}'.
+\answer @beginchar@\kern1pt(|"d"|$,5"pt"\0\ast\rmsqrt2,5"pt"\0,0)$;\parbreak
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\parbreak
+@draw@ $((0,0)\dashto"quartercircle"\dashto{\rm cycle})$
+ rotated 45 scaled 10"pt" shifted $(.5w,0)$;\parbreak
+@endchar@.
+
+Plain \MF\ also provides a path called ^"halfcircle" that gives you
+`{\manual\circc\circa}'; this path is actually made from two
+quarter-circles, by defining
+\begindisplay
+"halfcircle" $=$ "quartercircle" \& $"quartercircle"\,{\rm rotated}\,90$.
+\enddisplay
+And of course there's also ^"fullcircle", a complete circle of unit diameter:
+\begindisplay
+"fullcircle" $=$ "halfcircle" \& $"halfcircle"\,{\rm rotated}\,180$ \& cycle.
+\enddisplay
+You can draw a circle of diameter $D$ centered at $(x,y)$ by saying
+\begindisplay
+@draw@ "fullcircle" scaled $D$ shifted $(x,y)$;
+\enddisplay
+similarly,\kern-.4pt\ `@draw@ "fullcircle" \kern-.5pt
+xscaled \kern-1pt$A$ yscaled \kern-1pt$B$'
+yields an ^{ellipse} with axes $A$~and~$B$\kern-1.3pt.\kern-.5pt
+
+Besides circles and parts of circles, there's also a standard square path
+called "unitsquare"; this is a cycle that runs from $(0,0)$ to $(1,0)$
+to $(1,1)$ to $(0,1)$ and back to~$(0,0)$. For example, the command
+`@fill@ "unitsquare"' adds~1 to a single pixel value, as discussed in
+the previous chapter.
+
+\exercise Use "fullcircle" and "unitsquare" to produce the characters
+`{\manual\circe}' and `{\manual\circf}' in font positions `{\tt e}'
+and~`{\tt f}', respectively. These characters should be $10\,$pt wide
+and $10\,$pt tall, and their centers should be $2.5\,$pt above
+the baseline.
+\answer @beginchar@\kern1pt(|"e"|$,10"pt"\0,7.5"pt"\0,2.5"pt"\0)$;\parbreak
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\parbreak
+@for@ $D=.2w,.6w,w$: \
+@draw@ "fullcircle" scaled $D$ shifted $(.5w,.5[-d,h])$;\parbreak
+@endfor@ @endchar@.
+\par\medskip\noindent
+The program for `{\manual\circf}' is similar, but `"fullcircle"
+scaled~$D$' is replaced by
+\begindisplay
+"unitsquare" shifted $-(.5,.5)$ rotated 45 scaled $(D/\rmsqrt2)$.
+\enddisplay
+
+\hrule
+\medskip
+\line{\figbox{14a}{220\apspix}{690\apspix}\vbox \hfil
+\vbox{\hsize=18pc \def\\{\vskip1.5pt} \parindent=0pt
+\eightpoint
+\obeylines
+\leavevmode @path@ $"branch"[\,]$, "trunk";
+\\
+$"branch"_1= "flex"((0,660),(-9,633),(-22,610))$
+ \quad\& "flex"$((-22,610),(-3,622),(17,617))$
+ \quad\& "flex"$((17,617),(7,637),(0,660))$ \& cycle;
+\\
+$"branch"_2="flex"((30,570),(10,590),(-1,616))$
+ \quad\& "flex"$((-1,616),(-11,592),(-29,576),(-32,562))$
+ \quad\& "flex"$((-32,562),(-10,577),(30,570))$ \& cycle;
+\\
+$"branch"_3="flex"((-1,570),(-17,550),(-40,535))$
+ \quad\& "flex"$((-40,535),(-45,510),(-60,477))$
+ \quad\& "flex"$((-60,477),(-20,510),(40,512))$
+ \quad\& "flex"$((40,512),(31,532),(8,550),(-1,570))$ \& cycle;
+\\
+$"branch"_4="flex"((0,509),(-14,492),(-32,481))$
+ \quad\& "flex"$((-32,481),(-42,455),(-62,430))$
+ \quad\& "flex"$((-62,430),(-20,450),(42,448))$
+ \quad\& "flex"$((42,448),(38,465),(4,493),(0,509))$ \& cycle;
+\\
+$"branch"_5="flex"((-22,470),(-23,435),(-44,410))$
+ \quad\& "flex"$((-44,410),(-10,421),(35,420))$
+ \quad\& "flex"$((35,420),(15,455),(-22,470))$ \& cycle;
+\\
+$"branch"_6="flex"((18,375),(9,396),(5,420))$
+ \quad\& "flex"$((5,420),(-5,410),(-50,375),(-50,350))$
+ \quad\& "flex"$((-50,350),(-25,375),(18,375))$ \& cycle;
+\\
+$"branch"_7="flex"((0,400),(-13,373),(-30,350))$
+ \quad\& "flex"$((-30,350),(0,358),(30,350))$
+ \quad\& "flex"$((30,350),(13,373),(0,400))$ \& cycle;
+\\
+$"branch"_8="flex"((50,275),(45,310),(3,360))$
+ \quad\& "flex"$((3,360),(-20,330),(-70,300),(-100,266))$
+ \quad\& "flex"$((-100,266),(-75,278),(-60,266))$
+ \quad\& "flex"$((-60,266),(0,310),(50,275))$ \& cycle;
+\\
+$"branch"_9="flex"((10,333),(-15,290),(-43,256))$
+ \quad\& "flex"$((-43,256),(8,262),(58,245))$
+ \quad\& "flex"$((58,245),(34,275),(10,333))$ \& cycle;
+\\
+$"branch"_{10}="flex"((8,262),(-21,249),(-55,240))$
+ \quad\& "flex"$((-55,240),(-51,232),(-53,220))$
+ \quad\& "flex"$((-53,220),(-28,229),(27,235))$
+ \quad\& "flex"$((27,235),(16,246),(8,262))$ \& cycle;
+\\
+$"branch"_{11}="flex"((0,250),(-25,220),(-70,195))$
+ \quad\& "flex"$((-70,195),(-78,180),(-90,170))$
+ \quad\& "flex"$((-90,170),(-5,188),(74,183))$
+ \quad\& "flex"$((74,183),(34,214),(0,250))$ \& cycle;
+\\
+$"branch"_{12}="flex"((8,215),(-35,175),(-72,155))$
+ \quad\& "flex"$((-72,155),(-75,130),(-92,110),(-95,88))$
+ \quad\& "flex"$((-95,88),(-65,117),(-54,104))$
+ \quad\& "flex"$((-54,104),(10,151),(35,142))$
+ \qquad$\to"flex"((42,130),(60,123),(76,124))$
+ \quad\& "flex"$((76,124),(62,146),(26,180),(8,215))$ \& cycle;
+\\
+$"trunk"=(0,660)\ddashto(-12,70)\to\{\curl 5\}(-28,-8)$
+ \quad\& "flex"$((-28,-8),(-16,-4),(-10,-11))$
+ \quad\& "flex"$((-10,-11),(0,-5),(14,-10))$
+ \quad\& "flex"$((14,-10),(20,-6),(29,-11))$
+ \quad\& $(29,-11)\{\curl 4\}\to(10,100)\ddashto{\rm cycle}$;
+}}
+
+Sometimes it's necessary to draw rather complicated curves, and plain \MF\
+provides a `^"flex"' operation that can simplify this task. The
+construction `$"flex"(z_1,z_2,z_3)$' stands for the path
+`$z_1\to z_2\{z_3-z_1\}\to z_3$',
+and similarly `$"flex"(z_1,z_2,z_3,z_4)$' stands for
+`$z_1\to z_2\{z_4-z_1\}\to z_3\{z_4-z_1\}\to z_4$'; in general
+\begindisplay
+$"flex"(z_1,z_2,\ldots,z_{n-1},z_n)$
+\enddisplay
+is an abbreviation for the path
+\begindisplay
+$z_1\to z_2\{z_n-z_1\}\to\;\cdots\;\to z_{n-1}\{z_n-z_1\}\to z_n$.
+\enddisplay
+The idea is to specify two endpoints, $z_1$ and $z_n$, together with
+one or more intermediate points where the path is traveling in the
+same direction as the straight line from $z_1$ to~$z_n$; these
+intermediate points are easy to see on a typical curve, so they
+are natural candidates for key points.
+
+For example, the command
+\begindisplay
+@fill@ \ $"flex"(z_1,z_2,z_3)$ \& $"flex"(z_3,z_4,z_5)$\cr
+\indent\& $"flex"(z_5,z_6,z_7)$ \& $"flex"(z_7,z_8,z_9,z_1)$ \& cycle\cr
+\enddisplay
+will fill the shape
+\displayfig 14b (7pc)
+after the points $z_1$, \dots, $z_9$ have been suitably defined. This
+shape occurs as the fourth branch from the top of ``^{El Palo Alto},''
+a tree that is often used to symbolize ^{Stanford University}. The thirteen
+paths on the opposite page were defined by simply sketching the tree on
+a piece of graph paper, then reading off approximate values of key
+points ``by eye'' while typing the code into a computer. \ (A good radio
+or television program helps to stave off boredom when you're typing
+a bunch of data like this.) \ The entire
+figure involves a total of 47~flexes, most of which are pretty mundane;
+but $"branch"_{12}$ does contain an interesting subpath of the form
+\begindisplay
+$"flex"(z_1,z_2,z_3)\to"flex"(z_4,z_5,z_6)$,
+\enddisplay
+which is an abbreviation for
+\begindisplay
+$z_1\to z_2\{z_3-z_1\}\to z_3\to z_4\to z_5\{z_6-z_4\}\to z_6$.
+\enddisplay
+Since $z_3\ne z_4$ in this example, a smooth curve runs through all six
+points, although two different flexes are involved.
+
+\hangindent -1in \hangafter-2
+Once the paths have been defined,
+\rightfig 14aa (.5in x 1.25in) ^-8pt
+it's easy to use them to make
+symbols like the white-on-black medallion shown here:
+\begindisplay
+@beginchar@\kern1pt(|"T"|$,.5"in"\0,1.25"in"\0,0)$;\cr
+\<Define the thirteen paths on the preceding pages>;\cr
+@fill@ "superellipse"$((w,.5h),(.5w,h),(0,.5h),(.5w,0),.8)$;\cr
+$"branch"_0="trunk"$;\cr
+@for@ $n=0$ @upto@ 12:\cr
+\quad ^@unfill@ $"branch"[n]$ shifted $(150,50)$ scaled $(w/300)$;\cr
+@endfor@ @endchar@;\cr
+\enddisplay
+The oval shape that encloses this tree is a ^"superellipse", which is
+another special kind of path provided by plain \MF\!\null. To get a general
+shape of this kind, you can write
+\begindisplay
+"superellipse"$("right\_point","top\_point","left\_point","bottom\_point",
+ "superness")$
+\enddisplay
+where `"superness"' controls the amount by which the curve differs from a
+true ^{ellipse}. For example, here are four superellipses, drawn with varying
+amounts of ^{superness}, using a
+@pencircle@ xscaled~0.7"pt" yscaled 0.2"pt" rotated~30:
+\displayfig 14c (150\apspix)
+The "superness" should be between 0.5 (when you get a diamond) and 1.0
+(when you get a square); values in the vicinity of 0.75 are usually preferred.
+The zero symbol `{\tt 0}' in this book's typewriter font was
+drawn as a superellipse of superness $2^{-.5}\approx.707$, which
+corresponds to a normal ellipse; the uppercase letter `{\tt O}' was
+drawn with superness $2^{-.25}\approx.841$, to help distinguish it
+from the zero. The ambiguous symbol `{\cmman0}' (which is not in the
+font, but \MF\ can of course draw it) lies between these two extremes; its
+superness is 0.77.
+
+\ddanger A mathematical superellipse satisfies the equation $\vert
+x/a\vert^\beta+\vert y/b\vert^\beta=1$, for some exponent $\beta$. It has
+extreme points $(\pm a,0)$ and $(0,\pm b)$, as well as the ``corner''
+points $(\pm\sigma a,\pm\sigma b)$, where $\sigma=2^{-1/\beta}$ is the
+superness. The tangent to the curve at $(\sigma a,\sigma b)$ runs in the
+direction $(-a,b)$, hence it is parallel to a line from $(a,0)$ to
+$(0,b)$. Gabriel ^{Lam\'e} invented the superellipse in 1818, and
+Piet ^{Hein} popularized the special case
+$\beta=2.5$ [see Martin ^{Gardner}, {\sl Mathematical
+Carnival\/} (New York: Knopf, 1975), 240--254]; this special case
+corresponds to a superness of $2^{-.4}\approx.7578582832552$. Plain \MF's
+"superellipse" routine does not produce a perfect superellipse, nor does
+^"fullcircle" yield a true circle, but the results are close enough for
+practical purposes.
+
+\ddangerexercise Try "superellipse" with superness values less than 0.5
+or greater than~1.0; explain why you get weird shapes in such cases.
+\answer There are inflection points, because there are no bounding triangles
+for the `$\ldots$' operations in the "superellipse" macro of Appendix~B,
+unless $.5\le s\le1$.
+
+Let's look now at the symbols that are used between key points, when we
+specify a path. There are five such tokens in plain \MF:
+\begindisplay
+$\to$&free curve;\cr
+$\ldots$&bounded curve;\cr
+$\dashto$&straight line;\cr
+$\ddashto$&``tense'' line;\cr
+\&&splice.\cr
+\enddisplay
+^^{..}^^{...}^^{--}^^{---}^^{ampersand}
+In general, when you write `$z_0\to z_1\to\<etc.>\to z_{n-1}\to z_n$',
+\MF\ will compute the path of length~$n$ that represents its idea of the
+``most pleasing curve'' through the given points $z_0$ through~$z_n$.
+The symbol `$\ldots$' is essentially the same as `$\to$'\thinspace, except
+that it confines the path to a bounding triangle whenever possible, as
+explained in Chapter~3. A straight line segment `$z_{k-1}\dashto z_k$'
+usually causes the path to change course abruptly at $z_{k-1}$ and $z_k$.
+By contrast, a segment specified by `$z_{k-1}\ddashto z_k$' will be a
+straight line that blends smoothly with the neighboring curves; i.e., the
+path will enter $z_{k-1}$ and leave~$z_k$ in the direction of
+$z_k-z_{k-1}$. \ (The "trunk" of El Palo Alto makes use of this option,
+and we have also used it to draw the signboard of the dangerous bend
+symbol at the end of Chapter~12.) \ Finally, the `\&' operation joins two
+independent paths together at a common point, just as `\&' concatenates
+two strings together.
+
+Here, for example, is a somewhat silly path that illustrates all five
+basic types of joinery:
+\displayfig 14d (120\apspix)
+\begindisplay
+$z_0=(0,100)$; \ $z_1=(50,0)$; \ $z_2=(180,0)$;\cr
+@for@ $n=3$ @upto@ 9: $z[n]=z[n-3]+(200,0)$; \ @endfor@\cr
+@draw@ $z_0\to z_1\ddashto z_2\ldots\{"up"\}z_3$\cr
+\qquad\& $z_3\to z_4\dashto z_5\ldots\{"up"\}z_6$\cr
+\qquad\& $z_6\ldots z_7\ddashto z_8\to\{"up"\}z_9$.\cr
+\enddisplay
+
+\danger The `$\ldots$' operation is usually used only when one or both of the
+adjacent directions have been specified (like `$\{"up"\}$' in this example).
+Plain \MF's ^"flex" construction actually uses `$\ldots$'\thinspace,
+not `$\to$' as stated earlier, because this avoids inflection points in
+certain situations.
+
+\danger A path like `$z_0\ddashto z_1\ddashto z_2$' is almost indistinguishable
+from the broken line `$z_0\dashto z_1\dashto z_2$', except that if you
+enlarge the former path you will see that its lines aren't perfectly
+straight; they bend just a little, so that the curve is ``smooth'' at
+$z_1$ although there's a rather sharp turn there. \ (This means that
+the ^{autorounding} operations discussed in Chapter~24 will apply.) \
+For example, the path $(0,3)\ddashto(0,0)\ddashto(3,0)$ is equivalent to
+\begindisplay
+$(0,3)\to \controls\,(-0.0002,2.9998)\and (-0.0002,0.0002)$\cr
+$\quad\to(0,0)\to \controls\,(0.0002,-0.0002) \and (2.9998,-0.0002)\to(3,0)$\cr
+\enddisplay
+while $(0,3)\dashto(0,0)\dashto(3,0)$ consists of two perfectly straight
+segments:
+\begindisplay
+$(0,3)\to \controls\,(0,2)\and (0,1)$\cr
+$\quad\to(0,0)\to \controls\,(1,0) \and (2,0)\to(3,0)$.\cr
+\enddisplay
+
+\dangerexercise Plain \MF's ^"unitsquare" path is defined to be
+`$(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto\cycle$'.
+Explain how the same path could have been defined using only `$\to$' and~`\&',
+not `$\dashto$' or explicit directions.
+\answer $(0,0)\to(1,0)\;\&\;(1,0)\to(1,1)\;\&\;(1,1)\to(0,1)
+\;\&\;(0,1)\to(0,0)\;\&\;\cycle$. Incidentally, if each `\&' in this path
+is changed to `$\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\le t\le2$,
+$3\le t\le4$, 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.
+
+\ddanger Sometimes it's desirable to take a path and change all its
+connecting links to `$\ddashto$', regardless of what they were originally;
+the key points are left unchanged. Plain \MF\ has a ^"tensepath" operation
+that does this. For example, "tensepath"~"unitsquare"~$=$
+$(0,0)\ddashto(1,0)\ddashto(1,1)\ddashto(0,1)\ddashto\cycle$.
+
+When \MF\ is deciding what curves should be drawn in place of `$\to$' or
+`$\ldots$', it has to give special consideration to the beginning and
+ending points, so that the path will start and finish as gracefully as
+possible. The solution that usually works out best is to make the first
+and last path segments very nearly the same as arcs of circles; an
+unadorned path of length~2 like `$z_0\to z_1\to z_2$' will therefore turn
+out to be a good approximation to the unique circular arc that passes
+through $(z_0,z_1,z_2)$, except in extreme cases. You can change this
+default behavior at the endpoints either by specifying an explicit
+direction or by specifying an amount of ``^{curl}.'' If you call for
+curliness less than~1, the path will decrease its curvature in the
+vicinity of the endpoint (i.e., it will begin to turn less sharply); if
+you specify curliness greater than~1, the curvature will increase.
+\ (See the definition of El Palo Alto's "trunk", earlier in this chapter.)
+
+Here, for example, are some pairs of parentheses that were drawn using
+various amounts of curl. In each case the shape was drawn by a statement
+of the form `@penstroke@ $z_{0e}\{\curl c\}\to z_{1e}\to\{\curl c\}z_{2e}$';
+different values of $c$ produce different-looking parentheses:\def\\{\kern1pt}
+\begindisplay
+curl value\hidewidth&\hfil0&\hfil1&\hfil2&\hfil4&\kern-10pt"infinity"\cr
+yields\quad&\cmman 1\\2&\cmman 3\\4&\cmman 5\\6&\cmman 7\\8&\cmman 9\\:\cr
+\enddisplay
+(The parentheses of Computer Modern typefaces are defined by the
+somewhat more general scheme described in Chapter~12; explicit directions are
+specified at the endpoints, instead of curls, because this produces
+better results in unusual cases when the characters are extremely
+tall or extremely wide.)
+
+\danger The amount of curl should not be negative. When the curl is
+very large, \MF\ doesn't actually make an extremely sharp turn at the endpoint;
+instead, it changes the rest of the path so that there is comparatively
+little curvature at the neighboring point.
+
+\danger Chapter 3 points out that we can change \MF's default curves
+by specifying nonstandard ``^{tension}'' between points, or even by
+specifying explicit control points to be used in the four-point method.
+Let us now study the full syntax of path expressions, so that we
+can come to a complete understanding of the paths that \MF\ is able to make.
+Here are the general rules:
+\beginsyntax
+<path primary>\is<pair primary>\alt<path variable>
+ \alt[(]<path expression>[)]
+ \alt[reverse]<path primary>
+ \alt[subpath]<pair expression>[of]<path primary>
+<path secondary>\is<pair secondary>\alt<path primary>
+ \alt<path secondary><transformer>
+<path tertiary>\is<pair tertiary>\alt<path secondary>
+<path expression>\is<pair expression>\alt<path tertiary>
+ \alt<path subexpression><direction specifier>
+ \alt<path subexpression><path join>[cycle]
+<path subexpression>\is<path expression not ending with direction specifier>\kern-5pt\null
+ \alt<path subexpression><path join><path tertiary>
+<path join>\is<direction specifier><basic path join><direction specifier>
+<direction specifier>\is<empty>
+ \alt[\char'173][curl]<numeric expression>[\char'175]
+ \alt[\char'173]<pair expression>[\char'175]
+ \alt[\char'173]<numeric expression>[,]<numeric expression>[\char'175]
+<basic path join>\is[\&]\alt[..]\alt[..]<tension>[..]\alt[..]<controls>[..]
+<tension>\is[tension]<tension amount>
+ \alt[tension]<tension amount>[and]<tension amount>
+<tension amount>\is<numeric primary>
+ \alt[atleast]<numeric primary>
+<controls>\is[controls]<pair primary>
+ \alt[controls]<pair primary>[and]<pair primary>
+\endsyntax
+The operations `$\ldots$' and `$\dashto$' and `$\ddashto$' are conspicuously
+absent from this syntax; that is because Appendix~B defines them as macros:
+\begindisplay
+$\ldots$&is an abbreviation for `$\to\tension\atleast1\to$'\thinspace;\cr
+$\dashto$&is an abbreviation for `$\{\curl1\}\to\{\curl1\}$'\thinspace;\cr
+$\ddashto$&is an abbreviation for `$\to\tension"infinity"\to$'\thinspace.\cr
+\enddisplay
+
+\danger These syntax rules specify a wide variety of possibilities, even though
+they don't mention `$\dashto$' and such things explicitly, so we shall
+now spend a little while looking carefully at their implications.
+A path expression essentially has the form
+\begindisplay
+$p_0\quad j_1\quad p_1\quad j_2\quad\cdots\quad j_n\quad p_n$
+\enddisplay
+where each $p_k$ is a tertiary expression of type pair or path, and where
+each $j_k$ is a ``path join.'' A path join begins and ends with a
+``direction specifier,'' and has a ``basic path join'' in the middle.
+A direction specifier can be empty, or it can be `$\{\curl c\}$'
+for some $c\ge0$, or it can be a direction vector enclosed in braces.
+For example, `$\{"up"\}$' specifies an upward direction, because plain
+\MF\ defines ^"up" to be the pair $(0,1)$. This same direction could be
+specified by `$\{(0,1)\}$' or `$\{(0,10)\}$', or without parentheses as
+`$\{0,1\}$'. If a specified direction vector turns out to be $(0,0)$,
+\MF\ behaves as if no direction had been specified; i.e., `$\{0,0\}$'
+is equivalent to `\<empty>'. An empty direction specifier is implicitly
+filled in by rules that we shall discuss later.
+
+\danger A basic path join has three essential forms: \ (1)~`\&' simply
+concatenates two paths, which must share a common endpoint.
+\ (2)~`$\to\tension\alpha\and\beta\to$' means that a curve should be
+defined, having respective ``tensions'' $\alpha$ and~$\beta$.
+Both $\alpha$ and~$\beta$ must be equal to 3/4 or~more;
+we shall discuss ^{tension} later in this chapter.
+\ (3)~`$\to\controls u\and v\to$' defines a curve with intermediate
+control points $u$ and~$v$.
+
+\danger Special abbreviations are also allowed, so that the long forms
+of basic path joins can usually be avoided: `$\to$' by itself stands for
+`$\to\tension 1\and1\to$'\thinspace,
+ while `$\to\tension\alpha\to$' stands for
+`$\to\tension\alpha\and\alpha\to$'\thinspace,
+ and `$\to\controls u\to$' stands for
+`$\to\controls u\and u\to$'\thinspace.
+
+\danger Our examples so far have always constructed paths from points;
+but the syntax shows that it's also possible to write, e.g.,
+`$p_0\to p_1\to p_2$' when the $p$'s themselves are paths. What does
+this mean? Well, every such path will already have been changed into a
+sequence of curves with explicit control points; \MF\ expands such
+paths into the corresponding sequence of points and basic path joins
+of type~(3). For example, `$((0,0)\to(3,0))\to(3,3)$' is essentially
+the same as `$(0,0)\to\controls\,(1,0)\and(2,0)\to(3,0)\to(3,3)$',
+because `$(0,0)\to(3,0)$' is the path
+`$(0,0)\to\controls\,(1,0)\and(2,0)\to(3,0)$'.
+If a cycle is expanded into a subpath in this way, its cyclic
+nature will be lost; its last point will simply be a copy of its first point.
+
+\danger Now let's consider the rules by which empty direction specifiers
+can inherit specifications from their environment.
+An empty direction specifier at the beginning or end of a path, or just next
+to the `\&' operator, is effectively replaced by `$\{\curl1\}$'.
+This rule should be interpreted properly with respect to cyclic paths, which
+have no beginning or end; for example, `$z_0\to z_1\,\&\,z_1\to z_2\to\cycle$'
+is equivalent to `$z_0\to z_1\{\curl1\}\&\{\curl1\}z_1\to z_2\to\cycle$'.
+
+\danger If there's a nonempty direction specifier after a point but not
+before it, the nonempty one is copied into both places. Thus, for example,
+`$\to z\{w\}$' is treated as if it were `$\to\{w\}z\{w\}$'. If there's
+a nonempty direction specifier before a point but not after it, the
+nonempty one is duplicated in a similar way. A~basic path join
+`$\to\controls u\and v\to$' specifies explicit control points that
+override any direction specifiers that may immediately surround it.
+
+\danger An empty direction specifier next to an explicit control point
+inherits the direction of the adjacent path segment. More precisely,
+`$\to z\to\controls u\and v\to$' is treated as if it were
+`$\to\{u-z\}z\to\controls u\and v\to$' if $u\ne z$, or as if it were
+`$\to\{\curl1\}z\to\controls u\and v\to$' if $u=z$. Similarly,
+`$\to\controls u\and v\to z\to$' is treated as if $z$ were followed by
+$\{z-v\}$ if $z\ne v$, by $\{\curl1\}$ otherwise.
+
+\ddanger After the previous three rules have been applied, we might still
+be left with cases in which there are points surrounded on both sides
+by empty direction specifiers. \MF\ must choose appropriate directions
+at such points, and it does so by applying the following algorithm
+due to John ^{Hobby} [{\sl Discrete and Computational Geometry\/ \bf1}
+(1986), 123--140]: Given a sequence
+\begindisplay
+$z_0\{d_0\}\to\tension\alpha_0\and\beta_1\to z_1
+ \to\tension\alpha_1\and\beta_2\to z_2$\cr
+$\hskip5em\<etc.>\;z_{n-1}\to\tension\alpha_{n-1}\and\beta_n\to\{d_n\}z_n$\cr
+\enddisplay
+for which interior directions need to be determined, we will regard the
+$z$'s as if they were complex numbers. Let $l_k=\vert z_k-z_{k-1}\vert$ be
+the distance from $z_{k-1}$ to $z_k$, and let
+$\psi_k=\arg\bigl((z_{k+1}-z_k)/(z_k-z_{k-1} )\bigr)$ be the turning angle
+at~$z_k$. We wish to find direction vectors $w_0$, $w_1$, \dots,~$w_n$ so
+that the given sequence can effectively be replaced by
+\begindisplay
+$z_0\{w_0\}\to\tension\alpha_0\and\beta_1\to\{w_1\}z_1
+ \{w_1\}\to\tension\alpha_1\and\beta_2\to\{w_2\}z_2$\cr
+$\hskip5em\<etc.>\;z_{n-1}\{w_{n-1}\}\to
+ \tension\alpha_{n-1}\and\beta_n\to\{w_n\}z_n$.\cr
+\enddisplay
+Since only the directions of the $w$'s are significant, not the magnitudes,
+it suffices to determine the angles $\theta_k=\arg\bigl(w_k/(z_{k+1}-z_k
+)\bigr)$. For convenience, we also let $\phi_k=\arg\bigl((z_k-z_{k-1})/w_k
+\bigr)$, so that
+$$\line{\indent$\theta_k+\phi_k+\psi_k\;=\;0$.\hfil$(\ast)$}$$
+Hobby's paper introduces the notion of ``^{mock curvature}'' according to
+which the following equations should hold at interior points:
+$$\line{\indent$\beta_k^2l_k^{-1}\bigl(\alpha_{k-1}^{-1}(\theta_{k-1}
++\phi_k)-3\phi_k\bigr)=\alpha_k^2l_{k+1}^{-1}\bigl(\beta_{k+1}^{-1}
+(\theta_k+\phi_{k+1})-3\theta_k\bigr)$.\hfil$({\ast}{\ast})$}$$
+We also need to consider boundary conditions. If $d_0$ is an explicit
+direction vector~$w_0$, we know $\theta_0$; otherwise $d_0$ is
+`$\curl\gamma_0$' and we set up the equation
+$$\line{\indent$\alpha_0^2\bigl(\beta_1^{-1}(\theta_0+\phi_1)-3\theta_0\bigr)
+=\gamma_0\beta_1^2\bigl(\alpha_0^{-1}(\theta_0+\phi_1)-3\phi_1\bigr)$.
+\hfil$({\ast}{\ast}{\ast})$}$$
+If $d_n$ is an explicit vector~$w_n$, we know $\phi_n$; otherwise
+$d_n$ is `$\curl\gamma_n$' and we set
+$$\line{\indent$\beta_n^2\bigl(\alpha_{n-1}^{-1}(\theta_{n-1}+\phi_n)-3\phi_n
+\bigr)=\gamma_n\alpha_{n-1}^2\bigl(\beta_n^{-1}(\theta_{n-1}+\phi_n)-3
+\theta_{n-1}\bigr)$.\hfil$({\ast}{\ast}{\ast}')$}$$
+It can be shown that the conditions $\alpha_k\ge3/4$, $\beta_k\ge
+3/4$, $\gamma_k\ge0$ imply that there is a unique solution to the
+system of equations consisting of $(\ast)$ and $({\ast}{\ast})$ for $0<k<n$
+plus the two boundary equations; hence the desired quantities $\theta_0$,
+\dots,~$\theta_{n-1}$ and $\phi_1$, \dots,~$\phi_n$ are uniquely determined.
+\ (The only exception is the degenerate case $n=\gamma_0\gamma_1=1$.)
+
+\ddanger A similar scheme works for cycles, when there is no `$\{d_0\}$'
+or `$\{d_n\}$'. In this case equations $(\ast)$ and $({\ast}{\ast})$
+hold for all~$k$.
+
+\ddangerexercise Write out the equations that determine the directions chosen
+for the general cycle
+`$z_0\to\tension\alpha_0\and\beta_1\to
+ z_1\to\tension\alpha_1\and\beta_2\to
+ z_2\to\tension\alpha_2\and\beta_3\to\cycle$'
+of length~3. \ (You needn't try to solve the equations.)
+\answer Let $\delta_1=z_1-z_0$, $\delta_2=z_2-z_1$, $\delta_3=z_0-z_2$;
+$l_1=\vert\delta_1\vert$, $l_2=\vert\delta_2\vert$, $l_3=\vert\delta_3\vert$;
+$\psi_1=\arg(\delta_2/\delta_1)$, $\psi_2=\arg(\delta_3/\delta_2)$,
+$\psi_3=\arg(\delta_1/\delta_3)$. The equations to be solved are
+$(\ast)$ and $({\ast}{\ast})$ for $1\le k\le3$, where $\alpha_3=\alpha_0$
+and $\beta_4=\beta_1$. These six equations determine
+$\theta_1,\theta_2,\theta_3$ and $\phi_1,\phi_2,\phi_3$.
+
+\ddanger Whew\thinspace---\thinspace
+these rules have determined the directions at all points.
+To complete the job of path specification, we need merely explain how
+to change a segment like `$z_0\{w_0\}\to\tension\alpha\and\beta\to\{w_1\}
+z_1$' into a segment of the form
+`$z_0\to\controls u\and v\to z_1$'\thinspace;
+i.e., we finally want to know \MF's
+magic recipe for choosing the control points $u$ and~$v$.
+If $\theta=\arg\bigl(w_0/(z_1-z_0)\bigr)$ and
+$\phi=\arg\bigl((z_1-z_0)/w_1\bigr)$, the control points are
+\begindisplay
+$u=z_0+e^{i\theta}(z_1-z_0)f(\theta,\phi)/\alpha,\qquad
+v=z_1-e^{-i\phi}(z_1-z_0)f(\phi,\theta)/\beta$,
+\enddisplay
+where $f(\theta,\phi)$ is another formula due to John Hobby:
+\begindisplay
+$\displaystyle f(\theta,\phi)=
+{2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi)
+ (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\over
+3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)}.$
+\enddisplay
+
+\ddanger There's yet one more complication. If the tensions $\alpha$ and/or
+$\beta$ have been preceded by the keyword `^{atleast}', the values of
+$\alpha$ and/or $\beta$ are increased, if necessary, to the minimum
+values such that $u$ and~$v$ do not lie outside the ``^{bounding triangle},''
+which is discussed near the end of Chapter~3.
+
+\danger What do these complex rules imply, for \MF\ users who aren't ``into''
+mathematics? The most important fact is that the rules for paths are
+invariant under shifting, scaling, and rotation. In other words, if the
+key points $z_k$ of a path are all shifted, scaled, and/or rotated in the
+same way, the resulting path will be the same as you would get by
+shifting, scaling, and/or rotating the path defined by the unmodified
+$z_k$'s (except of course for possible rounding errors). However,
+this invariance property does not hold if the points or paths are
+xscaled and yscaled by separate amounts.
+
+\danger Another consequence of the rules is that ^{tension} specifications
+have a fairly straightforward interpretation in terms of control points,
+when the adjacent directions have been given: The formulas for $u$ and~$v$
+simply involve division by $\alpha$ and~$\beta$. This means, for example,
+that a tension of~2 brings the control points halfway~in towards the
+neighboring key points, and a tension of "infinity" makes the points very
+close indeed; contrariwise, tensions less than~1 move the control
+points out.
+
+\danger Tension and curl specifications also influence \MF's choices of
+directions at the key points. That is why, for example, the construction
+`$z_{k-1}\ddashto z_k$' (which means `$z_{k-1}\to\tension"infinity"\to
+z_k$'\thinspace) affects the direction of a larger path as it enters
+$z_{k-1}$ and leaves $z_k$.
+
+\danger The rules imply that a change in the position of point~$z_n$
+causes a change in the curve near point~$z_0$, when \MF\ has to choose
+directions at all points between $z_0$ and $z_n$. However, this effect
+is generally negligible except in the vicinity of the changed point.
+You can verify this by looking, for example, at the control
+points that \MF\ chooses for the path `$(0,0)\to(1,0)\to(2,0)\to
+(3,0)\to(4,0)\ldots\{"up"\}(5,y)$', as $y$ varies.
+
+\ddangerexercise Run \MF\ on the `|expr|' file of Chapter~8, and ask
+to see the path expression `^"unitsquare" shifted~$(0,1)\;\to\;$
+"unitsquare" shifted~$(1,0)$'. Account for the results that you get.
+\answer The path is of length~9, and it is equivalent to
+`$(0,1)\dashto(1,1)\dashto(1,2)\dashto(0,2)\dashto(0,1)\{"down"\}
+\to\{"right"\}(1,0)\dashto(2,0)\dashto(2,1)\dashto(1,1)\dashto(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.
+
+\ddangerexercise We've said that `$\dashto$' is plain \MF's abbreviation
+for `$\{\curl1\}\to\{\curl1\}$'. Would there be any essential difference
+if `$\dashto$' were defined to mean `$\{\curl2\}\to\{\curl2\}$'\thinspace?
+\answer Yes; for example, `$z_0\to z_1\to z_2\dashto z_3$' would be
+equivalent to `$z_0\to z_1\to\{\curl2\}z_2\{\curl2\}\to\{\curl2\}z_3$'.
+But a path like $z_0\dashto z_1\dashto z_2\dashto z_3$ would not be
+affected, because all directions would turn out to be the same as before.
+(The path `$z_0\{\curl a\}\to\{\curl b\}z_1$' is a straight line regardless
+of the values of $a$ and~$b$, because equations $({\ast}{\ast}{\ast})$
+and $({\ast}{\ast}{\ast}')$ always have the solution $\theta_0=\phi_1=0$
+when $n=1$.)
+
+\ddangerexercise Look closely at the syntax of \<path expression> and
+explain what \MF\ does with the specification `$(0,0)\to(3,3)\to\cycle
+\{\curl1\}$'.
+\answer It treats this as `$((0,0)\to(3,3)\to\cycle)\{\curl1\}$'; 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)\to\controls\,(2,-2)\and(5,1)\to(3,3)\to\controls\,(1,5)\and
+(-2,2)\to(0,0)\{\curl1\}$'. Finally the `$\{\curl1\}$' 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 \MF\ uses the last two
+alternatives in the definition of \<path expression>.)
+
+\danger Now let's come back to simpler topics relating to paths.
+Once a path has been specified, there are lots of things you can
+do with it, besides drawing and filling and suchlike. For example,
+if $p$ is a path, you can reverse its direction by saying `reverse~$p$';
+the ^{reverse} of `$z_0\to\controls u\and v\to z_1$' is
+`$z_1\to\controls v\and u\to z_0$'.
+
+\dangerexercise True or false: length reverse $p$ $=$ length $p$,
+for all paths~$p$.
+\answer True. The length of a path is the number of
+`$z_k\to\controls u_k\and v_{k+1}\to z_{k+1}$' segments that it contains,
+after all control points have been chosen.
+
+\danger It's convenient to associate ``^{time}'' with paths,
+by imagining that we move along a path of length~$n$ as time passes
+from 0 to~$n$. \ (Chapter~8 has already illustrated this notion, with
+respect to an almost-but-not-quite-circular path called~|p2|; it's a good idea
+to review the discussion of paths and ^{subpaths} in Chapter~8 now before
+you read further.) \ Given a path
+\begindisplay
+$p=z_0\to\controls u_0\and v_1\to z_1\,\<etc.>\,z_{n-1}\to
+ \controls u_{n-1}\and v_n\to z_n$
+\enddisplay
+and a number $t$, \MF\ determines `point $t$ of $p$' as follows:
+If $t\le0$, the result is~$z_0$; if $t\ge n$, the result is~$z_n$;
+otherwise if $k\le t<k+1$, it is $(t-k)[z_k,u_k,v_{k+1},z_{k+1}]$,
+where we generalize the ^^{mediation} `$t[\alpha,\beta]$' notation
+so that $t[\alpha,\beta,\gamma]$ means
+$t\bigl[t[\alpha,\beta],t[\beta,\gamma]\bigr]$
+and $t[\alpha,\beta,\gamma,\delta]$ means
+$t\bigl[t[\alpha,\beta,\gamma],t[\beta,\gamma,\delta]\bigr]$. \ (This
+is a ^{Bernshte\u\i n} polynomial in~$t$, cf.~Chapter~3.) \
+Given a cyclic path
+\begindisplay
+$c=z_0\to\controls u_0\and v_1\to z_1\,\<etc.>\,z_{n-1}\to
+ \controls u_{n-1}\and v_n\to\cycle$
+\enddisplay
+and a number $t$, \MF\ determines `point $t$ of $c$' in essentially the
+same way, except that $t$ is first reduced modulo~$n$ so as to lie
+in the range $0\le t<n$.
+
+\ddangerexercise True or false:\quad point $t$ of $(z_0\dashto z_1)$ $=$
+$t[z_0,z_1]$.
+\answer True if $0\le t\le1$, except perhaps for rounding errors;
+otherwise false. The path $z_0\dashto z_1$ expands into `$z_0\to
+\controls1/3[z_0,z_1]\and2/3[z_0,z_1]\to z_1$', and the ^{Bernshte\u\i n}
+polynomial simplifies because $t[w,w+\delta,w+2\delta,w+3\delta]=w+3t\delta$.
+Incidentally, `point~$t$ of $(z_0\ddashto z_1)$' is usually quite
+different from $t[z_0,z_1]$.
+
+\danger Given a path $p$ and two time values $t_1\le t_2$,
+`subpath~$(t_1,t_2)$ of~$p$' contains all the values
+`point~$t$ of~$p$' as $t$ varies from $t_1$ to~$t_2$. There's no problem
+understanding how to define this subpath when $t_1$ and $t_2$ are integers;
+for example,
+\begindisplay
+subpath $(2,4)$ of $p$ $=$ $z_2\to\controls u_2\and v_3\to z_3
+ \to\controls u_3\and v_4\to z_4$
+\enddisplay
+in the notation above, if we assume that $n\ge 4$. The fractional case is
+handled by ``stretching time'' in one segment of the curve; for example,
+if $0<t<1$ we have
+\begindisplay
+subpath $(0,t)$ of $p$ $=$ $z_0\to\controls t[z_0,u_0]\and
+ t[z_0,u_0,v_1]\to t[z_0,u_0,v_1,z_1]$;\cr
+subpath $(t,1)$ of $p$ $=$ $t[z_0,u_0,v_1,z_1]\to\controls
+ t[u_0,v_1,z_1]\and t[v_1,z_1]\to z_1$.\cr
+\enddisplay
+These two subpaths together account for all points of
+`$z_0\to\controls u_0\and v_1\to z_1$'. To get subpath~$(t_1,t_2)$ of~$p$
+when $0<t_1<t_2<1$, \MF\ applies this construction twice, by computing
+subpath~$(t_1/t_2,1)$ of subpath~$(0,t_2)$ of~$p$.
+
+\ddanger The operation `subpath $(t_1,t_2)$ of $p$' is defined for all
+combinations of times $(t_1,t_2)$ and paths~$p$ by the following rules:
+Let $n={\rm length}\,p$. \ (1)~If $t_1>t_2$, subpath~$(t_1,t_2)$ of~$p$~$=$
+reverse subpath~$(t_2,t_1)$ of~$p$. Henceforth we shall assume that
+$t_1\le t_2$. \ (2)~If $t_1=t_2$, subpath~$(t_1,t_2)$ of~$p$~$=$
+point~$t_1$ of~$p$, a path of length zero. Henceforth we shall assume that
+$t_1<t_2$.
+\ (3)~If $t_1<0$ and $p$ is a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1+n,t_2+n)$ of~$p$. If $t_1<0$ and $p$ is not a cycle,
+ subpath~$(t_1,t_2)$ of~$p$~$=$ subpath~$\bigl(0,\max(0,t_2)\bigr)$ of~$p$.
+Henceforth we shall assume that $t_1\ge0$.
+\ (4)~If $t_1\ge n$ and $p$ is a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1-n,t_2-n)$ of~$p$.
+If $t_1<n<t_2$ and $p$ is a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1,t_2)$ of~$(p\,\&\,p\,\&\,\cycle)$.
+If $t_2>n$ and $p$ is not a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$\bigl(\min(t_1,n),n\bigr)$ of~$p$.
+Henceforth we shall assume that $0\le t_1<t_2\le n$.
+\ (5)~If $t_1\ge1$, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1-1,t_2-1)$ of subpath~$(1,n)$ of~$p$, where
+subpath~$(1,n)$ of~$p$ is obtained by removing the first segment of~$p$.
+Henceforth we shall assume that $0\le t_1<1$.
+\ (6)~If $t_2>1$, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1,1)$ of~$p$~\& subpath~$(1,t_2)$ of~$p$.
+Henceforth we shall assume that $0\le t_1<t_2\le 1$.
+\ (7)~The remaining cases were defined in the preceding paragraph.
+
+\ddangerexercise What is the length of
+ `subpath $(2.718,3.142)$ of~$p$'\thinspace?
+\answer If $p$ is a cycle, or if $p$ is a path of length $\ge4$, the
+stated subpath has length~2. Otherwise the length is
+$\max(0,{\rm length}\,p-2)$.
+
+\danger Besides `point $t$ of $p$', \MF\ allows you to speak of
+`^{postcontrol}~$t$ of~$p$' and `^{precontrol}~$t$ of~$p$';
+this gives access to the control points of a path. Let
+\begindisplay
+$p=z_0\to\controls u_0\and v_1\to z_1\,\<etc.>\,z_{n-1}\to
+ \controls u_{n-1}\and v_n\to z_n$.
+\enddisplay
+If $t<n$, postcontrol $t$ of $p$ is the first control point in
+subpath~$(t,n)$ of~$p$; if $t\ge n$, postcontrol~$t$ of~$p$ is~$z_n$.
+If $t>0$, precontrol~$t$ of~$p$ is the last control point in
+subpath~$(0,t)$ of~$p$; if $t\le 0$, precontrol~$t$ of~$p$ is~$z_0$.
+In particular, if $t$ is an integer, postcontrol~$t$ of~$p$ is $u_t$
+for $0\le t<n$, and precontrol~$t$ of~$p$ is $v_t$ for $0<t\le n$.
+
+\danger The ability to extract key points and control points makes it
+possible to define interesting operations such as plain \MF's ^"interpath"
+function, which allows you to ^{interpolate between paths}. For example,
+`"interpath"$(1/3,p,q)$' will produce a path of length~$n$ whose
+points are 1/3[point~$t$~of~$p,\,$~point~$t$~of~$q$] for $0\le t\le n$,
+given any paths $p$ and~$q$ of length~$n$. It can be defined by a
+fairly simple program:
+\begindisplay
+@vardef@ "interpath"(@expr@ $a,p,q) =$\cr
+\quad @for@ $t=0$ @upto@ length$\,p\;-1$: $a$[point $t$ of $p,\,$
+ point $t$ of $q$]\cr
+\qquad$\to\controls$ $a$[postcontrol $t$ of $p,\,$
+ postcontrol $t$ of $q$]\cr
+\qquad\quad and $a$[precontrol $t+1$ of $p,\,$
+ precontrol $t+1$ of $q$] $\to$ @endfor@\cr
+\quad @if@ cycle $p$: cycle\qquad\% assume that $p,q$ are both cycles
+ or both noncycles\cr
+\quad @else@: $a$[point "infinity" of $p$, point "infinity" of $q$]
+ @fi@ @enddef@;\cr
+\enddisplay
+
+\danger On February 14, 1979, the author ^^{Knuth, D E}
+bought a box of chocolates and placed the box on a piece of
+graph paper (after suitably disposing of the contents). ^^{Knuth, J C}
+The experimental data gathered in this way led to a ``definitive''
+^^{valentine} heart shape:
+\begindisplay
+$"heart"=(100,162)\to(140,178)\{"right"\}\to(195,125)\{"down"\}$\cr
+\qquad$\to(100,0)\{\curl0\}
+ \to\{"up"\}(5,125)\to\{"right"\}(60,178)\to(100,162)$;\cr
+\enddisplay
+it is interesting to interpolate between ^"heart" and other paths, by using
+a program like
+\begindisplay
+@for@ $n=0$ @upto@ 10: @draw@ "interpath"$(n/10,p,"heart")$; @endfor@.
+\enddisplay
+For example, the left illustration below was obtained by taking
+\begindisplay
+$p=(100,0)\dashto(300,0)\dashto(200,0)\dashto(100,0)\dashto(0,0)
+ \dashto(-100,0)\dashto(100,0)$;
+\enddisplay
+notice that "interpath" doesn't necessarily preserve smoothness at the key
+points. The right illustration was obtained by duplicating point
+$(100,0)$ in~$heart$ (thereby making it a path of length~7) and taking
+\begindisplay
+$p=(100,200)\dashto(200,200)\dashto(200,100)$\cr
+\qquad$\dashto(200,0)\dashto(0,0)\dashto(0,100)\dashto(0,200)\dashto(100,200)$.
+\enddisplay
+\displayfig 14bb\&cc (1in)
+
+\danger Plain \MF\ allows you to say `^{direction}~$t$ of~$p$' in order
+to determine the direction in which path~$p$ is moving at time~$t$. This is
+simply an abbreviation for `(postcontrol~$t$~of~$p)-($precontrol~$t$~of~$p$)'.
+Sometimes a path veers abruptly and has no unique direction; in this case
+the direction function gives a result somewhere between the two possible
+extremes. For example, the "heart" path above turns a corner at
+time~3; `direction~3 of~"heart"' turns out to be
+$(-93.29172,0)$, but `direction~$3-"epsilon"$ of~"heart"' is
+$(-46.64589,-31.63852)$ and `direction~$3+"epsilon"$ of~"heart"' is
+$(-46.64589,31.63852)$.
+
+\outer\def\begindemo#1{$$\advance\baselineskip by2pt
+ \catcode`\"=\other
+ \halign\bgroup\indent\hbox to #1{\tt##\hfil}&\tt##\hfil\cr
+ \noalign{\vskip-2pt}}
+\outer\def\enddemo{\egroup$$}
+\def\demohead{\it\kern-2pt You type&\it\kern-1pt And the result is\cr
+ \noalign{\nobreak\vskip2pt}}
+
+\danger Conversely, \MF\ can tell you when a path heads in
+a given direction. You just ask for `^{directiontime}~$w$ of~$p$', where
+$w$~is a direction vector and $p$~is a path. This operation is best
+understood by looking at examples, so let's resume our dialog with the
+computer by applying \MF\ to the `|expr|' file as in Chapter~8. When
+\MF\ first says `|gimme|', our opening strategy this time will be
+to type
+\begintt
+hide(p3 = (0,0){right}..{up}(1,1)) p3
+\endtt
+so that we have a new path to play with. Now the fun begins:
+^^{dir}^^{angle}
+\begindemo{230pt}
+\demohead
+directiontime right of p3&0\cr
+directiontime up of p3&1\cr
+directiontime down of p3&-1\cr
+directiontime (1,1) of p3&0.5\cr
+directiontime left of reverse p3&1\cr
+direction directiontime (1,2) of p3 of p3&(0.23126,0.46251)\cr
+directiontime right of subpath(epsilon,1) of p3&0\cr
+directiontime right of subpath(2epsilon,1)of p3&-1\cr
+directiontime (1,1) of subpath(epsilon,1) of p3&0.49998\cr
+direction epsilon of p3&(0.55226,0)\cr
+direction 2epsilon of p3&(0.55229,0.00003)\cr
+directiontime dir 30 of p3&0.32925\cr
+angle direction 0.32925 of p3&29.99849\cr
+angle direction 0.32925+epsilon of p3&30.00081\cr
+directionpoint up of p3&(1,1)\cr
+\enddemo
+Note that directiontime yields $-1$ if the specified direction doesn't occur.
+At time ^"epsilon", path~$p_3$ is still traveling right, but at time
+2"epsilon" it has begun to turn upward. The `^{directionpoint}' operation
+is analogous to directiontime, but it gives the point on the path rather
+than the time of arrival. ^^"fullcircle"
+\begindemo{230pt}
+\demohead
+directiontime up of fullcircle&0\cr
+directiontime left of fullcircle&2\cr
+directiontime right of fullcircle&6\cr
+directiontime (-1,1) of fullcircle&1\cr
+directiontime (epsilon,infinity) of fullcircle&8\cr
+directiontime right of unitsquare&0\cr
+directiontime up of unitsquare&1\cr
+directiontime (1,1) of unitsquare&1\cr
+directiontime (-1,1) of unitsquare&2\cr
+\enddemo
+If a path travels in a given direction more than once, directiontime
+reports only the first time. The ^"unitsquare" path has sharp turns at
+the corners; directiontime considers that all directions between the
+incoming and outgoing ones are instantaneously present.
+
+\ddanger It's possible to construct pathological paths in which unusual
+things happen. For example, the path $p=(0,0)\to\controls\,(1,1)\and(0,1)
+\to(1,0)$ has a ``^{cusp}'' at time~0.5, when it comes to a dead stop and
+turns around. \ $\bigl($If you ask for `direction~0.5 of~$p$', the answer
+is zero, while direction~$0.5-\epsilon$ of~$p$ is $(0,2\epsilon)$ and
+direction~$0.5+\epsilon$ of~$p$ is $(0,-2\epsilon)$.$\bigr)$ \ The
+directiontime operation assumes that all possible directions actually
+occur when a path comes to a standstill, hence `directiontime~"right"
+of~$p$' will be 0.5 in this case even though it might be argued that
+$p$~never turns to the right. Paths with cusps are numerically unstable,
+and they might become ``^{strange}'' after transformations are applied,
+because rounding errors might change their ^{turning numbers}. The path~$p$
+in this example has control points that correspond to tensions of only
+0.28 with respect to the initial and final directions; since \MF\ insists
+that ^{tension}s be at least~0.75, this anomalous path could never have arisen
+if the control points hadn't been given explicitly.
+
+\ddangerexercise Write macros called ^"posttension" and ^"pretension"
+that determine the effective tensions of a path's control points at
+integer times~$t$. For example, `"pretension"~1 of ($z_0\to
+\tension\alpha\and\beta\to z_1$)' should be $\beta$ (approximately).
+Test your macro by computing "posttension"~0 of $\bigl((0,0)\{"right"\}
+\ldots\{"up"\}(1,10)\bigr)$.
+\answer @vardef@ "posttension" @expr@ $t$ of $p$ $=$\parbreak
+\quad@save@ $q$; @path@ $q$;\parbreak
+\quad$q={\rm point}\,t\,{\rm of}\,p\,\{{\rm direction}\,t\,{\rm of}\,p\}
+ \to\{{\rm direction}\,t\!+\!1\,{\rm of}\,p\}\,
+ {\rm point}\,t\!+\!1\,{\rm of}\,p$;\parbreak
+\quad length(postcontrol 0 of $q$ $-$ point 0 of $q$)\parbreak
+\qquad/length(postcontrol $t$ of $p$ $-$ point $t$ of $p$) @enddef@;\parbreak
+@vardef@ "pretension" @expr@ $t$ of $p$ $=$\parbreak
+\quad@save@ $q$; @path@ $q$;\parbreak
+\quad$q={\rm point}\,t\!-\!1\,{\rm of}\,p\,\{{\rm direction}\,
+ t\!-\!1\,{\rm of}\,p\}\to\{{\rm direction}\,t\,{\rm of}\,p\}\,
+ {\rm point}\,t\,{\rm of}\,p$;\parbreak
+\quad length(precontrol 1 of $q$ $-$ point 1 of $q$)\parbreak
+\qquad/length(precontrol $t$ of $p$ $-$ point $t$ of $p$) @enddef@;
+\par\nobreak\smallskip\noindent
+The stated posttension turns out to be 4.54019.
+
+\danger We have now discussed almost all of the things that \MF\ can do
+with paths; but there's one more important operation to consider,
+namely ^{intersection}. Given two paths $p$ and~$q$, you can write
+\begindisplay
+$p$ intersectiontimes $q$
+\enddisplay
+and the result will be a pair of times $(t,u)$ such that point~$t$
+of~$p$~$\approx$ point~$u$ of~$q$. For example, using the
+|expr| routine,^^"halfcircle"
+\begindemo{245pt}
+\demohead
+unitsquare intersectiontimes fullcircle&(0.50002,0)\cr
+unitsquare intersectiontimes fullcircle rotated 90&(0.50002,6)\cr
+reverse unitsquare intersectiontimes fullcircle&(0.50002,2)\cr
+fullcircle intersectiontimes unitsquare&(0,0.50002)\cr
+halfcircle rotated 45 intersectiontimes unitsquare&(1,3.5)\cr
+halfcircle rotated 89 intersectiontimes unitsquare&(0.02196,3.5)\cr
+halfcircle rotated 90 intersectiontimes unitsquare&(0,3.50002)\cr
+halfcircle rotated 91 intersectiontimes unitsquare&(-1,-1)\cr
+halfcircle rotated 45 intersectiontimes fullcircle&(0,1)\cr
+fullcircle intersectiontimes (-0.5,0)&(4,0)\cr
+unitsquare intersectionpoint fullcircle&(0.5,0)\cr
+reverse unitsquare intersectionpoint fullcircle&(0,0.5)\cr
+\enddemo
+Notice that the result is $(-1,-1)$ if the paths don't intersect.
+The last two examples illustrate the `^{intersectionpoint}'
+operator, which yields the common point of intersection. Both
+intersectiontimes and intersectionpoint apply at the ^{tertiary level} of
+^{precedence}, hence parentheses were not needed in these examples.
+
+\dangerexercise J. H. ^{Quick} (a student) wanted to construct a path~$r$
+that started on some previously defined path~$p$ and proceeded
+up to the point where it touched another path~$q$, after which $r$ was
+supposed to continue on path~$q$. So he wrote
+\begindisplay
+@path@ $r$; \ @numeric@ $t,u$; \ $(t,u)=p$ intersectiontimes $q$;\cr
+$r={\rm subpath}\,(0,t)\,{\rm of}\,p\;\;\&\;\;
+ {\rm subpath}\,(u,"infinity")\,{\rm of}\,q$;\cr
+\enddisplay
+but it didn't work. Why not?
+\answer The `\&' had to be changed to `$\to$', because point~$t$ of~$p$
+might not be exactly equal to point~$u$ of~$q$.
+
+\ddanger If the paths intersect more than once, \MF\ has a somewhat
+peculiar way of deciding what times $(t,u)$ should be reported by
+`$p$~intersectiontimes~$q$'. Suppose $p$ has length~$m$ and $q$ has
+length~$n$. \ (Paths of length~0 are first changed into motionless paths
+of length~1.) \ \MF\ proceeds to examine subpath~$(k,k+1)$ of~$p$
+versus subpath~$(l,l+1)$ of~$q$, for $k=0$, \dots,~$m-1$ and $l=0$,
+\dots,~$n-1$, with $l$ varying most rapidly. This reduces the general
+problem to the special case of paths of length~1, and the times $(t,u)$
+for the first such intersection found are added to $(k,l)$. But within
+paths of length~1 the search for intersection times is somewhat
+different: Instead of reporting the ``lexicographically smallest'' pair
+$(t,u)$ that corresponds to an intersection, \MF\ finds the $(t,u)$
+whose ``^{shuffled binary}'' representation $(.t_1u_1t_2u_2\ldots\,)_2$
+is minimum, where $(.t_1t_2\ldots\,)_2$ and $(.u_1u_2\ldots\,)_2$ are
+the radix-2 representations of $t$ and~$u$.
+
+\ddangerexercise (A mathematical puzzle.) \ The path
+$p=(0,0)\to\controls\,(2,2)\and(0,1)\to(1,0)$ loops on itself, so there
+are times $t<u$ such that point~$t$ of~$p$~$\approx$ point~$u$ of~$p$.
+Devise a simple way to compute $(t,u)$ in a \MF\ program, without
+using the subpath operation.
+\answer Since $p$ intersects itself infinitely often at times $(t,t)$,
+the task may seem impossible; but \MF's shuffled-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$.
+
+\danger Let's conclude this chapter by applying what we've learned about
+paths to a real-life example. The {\sl^{Journal of Algorithms}\/} was
+published for many years by Academic Press, and its cover page carried the
+following ^{logo}, which was designed by J.~C. Knuth ^^{Knuth, J C}
+to blend with the style of type % namely, `Cairoli' by Chartpak
+used elsewhere on that page:
+\displayfig 14dd (25mm)
+A \MF\ program to produce this logo made it possible for the editors
+of the journal to use it on letterheads in their correspondence.
+Here is one way to write that program, without needing to erase anything:
+^^"superellipse" ^^"whatever" ^^{rotatedaround} ^^{reflectedabout}
+^^@forsuffixes@
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ \ }&#\hfil\cr
+1&@beginchar@\kern1pt(|"A"|$,29"mm"\0,25"mm"\0,0)$; \
+ $"thick"\0:=2"mm"\0$; \ $"thin"\0:=5/4"mm"\0$;\cr
+2&@define\_whole\_blacker\_pixels@$("thick","thin")$;\cr
+3&@forsuffixes@ $\$=a,b,c$: \ @transform@ \$;\cr
+4&\quad @forsuffixes@ $e=l,r$: \ @path@ $\$e,\$'e$; \
+ @numeric@ $t\$[\,]e$; \ @endfor@ @endfor@\cr
+5&$\penpos1("thick",0)$; $\penpos2("thick",90)$;
+ $\penpos3("thick",180)$; $\penpos4("thick",270)$;\cr
+6&$\penpos5("thick",0)$; $\penpos6("thick",90)$;
+ $\penpos7("thick",180)$; $\penpos8("thick",270)$;\cr
+7&$x_2=x_4=x_6=x_8=.5[x_5,x_7]=.5w$; \ $x_{1r}=w$; \ $x_{3r}=0$; \
+ $x_5-x_7=y_6-y_8$;\cr
+8&$y_1=y_3=y_5=y_7=.5[y_6,y_8]=.5h$; \ $y_{2r}=h$; \ $y_{4r}=0$; \
+ $y_{6r}=.75h$;\cr
+9&@forsuffixes@ $e=l,r$: \ $a.e=b'e=c'e="superellipse"
+ (z_{1e},z_{2e},z_{3e},z_{4e},.75)$;\cr
+10&\quad $a'e=b.e=c.e="superellipse"
+ (z_{5e},z_{6e},z_{7e},z_{8e},.72)$; \ @endfor@\cr
+11&$\penpos{a1}("thin",0)$; \ $\penpos{a5}("whatever",-90)$; \
+ $\penpos{a9}("thin",180)$;\cr
+12&$x_{a1l}-x_{a9l}=1/3(x_{5l}-x_{7l})$;\ $x_{a5}=.5w$; \
+ $y_{a1}=y_{a9}$; \ $y_{a5r}=4/7h$;\cr
+13&$x_{a3l}=x_{a1l}$; \ $x_{a3r}=x_{a1r}$; \ $x_{a4r}=1/6[x_{a3r},x_{1l}]$; \
+ $x_0=.5w$; \ $y_0=.52h$;\cr
+14&$x_{a6l}+x_{a4l}=x_{a6r}+x_{a4r}=
+ x_{a7l}+x_{a3l}=x_{a7r}+x_{a3r}=x_{a9}+x_{a1}=w$;\cr
+15&\thickmuskip=4mu $y_{a3r}=y_{a4r}=y_{a6r}=y_{a7r}=.2[y_{2l},y_0]$; \
+ $y_{a3l}=y_{a4l}=y_{a6l}=y_{a7l}=y_{a3r}-"thin"$;\cr
+16&$z_{a4l}=z_{a4r}
+ +("thin",0)\,{\rm rotated}({\rm angle}(z_{a4r}-z_{a5r})+90)$\cr
+17&\qquad$\null+"whatever"\ast(z_{a4r}-z_{a5r})$; \
+ $z_{a4l}-z_{a5l}="whatever"\ast(z_{a4r}-z_{a5r})$;\cr
+18&$z=a.r\;\hbox{intersectionpoint}\;(z_0\dashto(w,0))$; \
+ $y_{a1}-y_{a5}=\hbox{length}(z-z_0)$;\cr
+19&$b="identity"$ shifted $(0,y_0-y_{a1})$
+ rotatedaround$(z_0,90-\hbox{angle}(z_0-(w,0)))$;\cr
+20&$c=b$ reflectedabout $(z_2,z_4)$;\cr
+21&@for@ $n=1,3,4,5,6,7,9$:
+ \ @forsuffixes@ $e=l,,r$: \ @forsuffixes@ $\$=b,c$:\cr
+22&\quad $z_{\$[n]e}=z_{a[n]e}$ transformed \$; \ @endfor@ @endfor@ @endfor@\cr
+23&@forsuffixes@ $e=l,r$: \ @forsuffixes@ $\$=a,b,c$:\cr
+24&\quad $z_{\$2e}=\$r$ intersectionpoint $(z_{\$1e}\dashto z_{\$3e})$;\cr
+25&\quad $z_{\$8e}=\$r$ intersectionpoint $(z_{\$9e}\dashto z_{\$7e})$;\cr
+26&\quad $t_{\$1e}=\hbox{xpart}(\$e$
+ intersectiontimes $(z_{\$1l}\dashto z_{\$3l}))$;\cr
+27&\quad $t_{\$9e}=\hbox{xpart}(\$e$
+ intersectiontimes $(z_{\$9l}\dashto z_{\$7l}))$;\cr
+28&\quad $t_{\$4e}=\hbox{xpart}(\$'e$
+ intersectiontimes $(z_{\$5r}\dashto z_{\$4l}))$;\cr
+29&\quad $t_{\$6e}=\hbox{xpart}(\$'e$
+ intersectiontimes $(z_{\$5r}\dashto z_{\$6l}))$; \ @endfor@ @endfor@\cr
+30&^@penstroke@ subpath$(t_{a9e},t_{b6e})$ of $a.e$;\cr
+31&@penstroke@ subpath$(t_{b4e},t_{c4e})$ of $b'e$;\cr
+32&@penstroke@ subpath$(t_{c6e},t_{a1e}+8)$ of $c'e$;\cr
+33&@penstroke@ subpath$(t_{a6e},t_{b9e})$ of $a'e$;\cr
+34&@penstroke@ subpath$(t_{b1e},t_{c1e})$ of $b.e$;\cr
+35&@penstroke@ subpath$(t_{c9e},t_{a4e}+8)$ of $c.e$;\cr
+36&@forsuffixes@ $\$=a,b,c$: \
+ @penlabels@$(\$1,\$2,\$3,\$4,\$5,\$6,\$7,\$8,\$9)$;\cr
+37&\quad @penstroke@ $z_{\$2e}\dashto z_{\$3e}\dashto z_{\$4e}\dashto
+ z_{\$5e}\dashto z_{\$6e}\dashto z_{\$7e}\dashto z_{\$8e}$; \ @endfor@\cr
+38&@penlabels@(^@range@ 0 ^@thru@ 8); \ @endchar@;\cr
+}$$
+Lines 5--10 of this program define the main superellipses of the figure.
+The outer superellipse is eventually drawn as three separate strokes
+in lines 30--32, and the inner one is drawn as three strokes in lines 33--35.
+The rest of the figure consists of three arrows, whose point labels are
+prefaced by the respective labels $a,b,c$. Lines 11--18 define the `$a$'
+arrow; then lines 19--22 transform these points into the `$b$' and~`$c$'
+arrows, anticipating some of the things we shall discuss in Chapter~15.
+Thirty-six intersections between arrows and superellipses are computed
+in lines 23--29, and the arrows are finally drawn by the penstrokes
+specified in lines 36--37.
+
+\displayfig 14e (4.5in)
+
+\endchapter
+
+% se se\~nala con puntos el camino,
+The route is indicated by dots,
+% se expressan por numeros las jornadas,
+the days' journeys are expressed by numbers,
+% y se distinguen por abecedario los lugares \^o parages particulares.
+and letters are used to locate notable places and sites.
+% ... Hasta llegar al arroyo de S$\rm^n$ Francisco,
+.\thinspace.\thinspace. We arrived at the Arroyo de San Francisco,
+% en cuya orilla esta el pinabete que dixe ayer,
+beside which stream is the redwood tree ^^{El Palo Alto} I spoke of yesterday;
+% cuya altura medi con el Grafometro
+I measured its height with the Graphometer
+% y lo halle a poco mas o menos segun el calculo que hize,
+% de unas cincuenta varas de alto.
+and reckoned it to be fifty yards high, more or less.
+\author FRAY PEDRO ^{FONT}, {\sl Diary\/} (1776)
+ % from the second paragraph, then skipping to the entry for March 30
+
+\bigskip
+
+The practical teaching of the masters of Art %
+was summed by the O of ^{Giotto}.
+\author JOHN ^{RUSKIN}, {\sl The Cestus of Aglaia\/} (1865)
+ % in Art Journal, new series, vol 4, p197
+ % later published in section 144 of his Queen of the Air (1869)
+
+\eject
+ \beginchapter Chapter 15. Transformations
+
+Points, paths, pens, and pictures can be shifted, scaled, rotated,
+and revamped in a variety of ways. Our aim in this chapter will be to
+learn all about the built-in metamorphoses of \MF\kern-1pt, because
+they can make programs simpler and more versatile.
+
+The basic ^{transformations} have already appeared in many examples, but let's
+start by reviewing them here:
+\begindisplay
+$(x,y)$ ^{shifted} $(a,b)$&$=(x+a,y+b)$;\cr
+$(x,y)$ ^{scaled} $s$&$=(sx,sy)$;\cr
+$(x,y)$ ^{xscaled} $s$&$=(sx,y)$;\cr
+$(x,y)$ ^{yscaled} $s$&$=(x,sy)$;\cr
+$(x,y)$ ^{slanted} $s$&$=(x+sy,y)$;\cr
+$(x,y)$ ^{rotated} $\theta$&$=(x\cos\theta-y\sin\theta,
+ x\sin\theta+y\cos\theta)$;\cr
+$(x,y)$ ^{zscaled} $(u,v)$&$=(xu-yv,xv+yu)$.\cr
+\enddisplay
+One of the nice things about \MF\ is that you don't have to remember the
+sine-and-cosine formulas of trigonometry; you just have to know that
+`$(x,y)$~rotated~$\theta$' means `the vector~$(x,y)$ rotated $\theta$~degrees
+counterclockwise around~$(0,0)$', and the computer does all the necessary
+calculations by itself. The operation of zscaling may look a bit strange,
+but it is simply a combination of rotating by angle$\,(u,v)$ and scaling
+by length$\,(u,v)$.
+
+Plain \MF\ provides two more transformations that are commonly needed: You can
+say `$(x,y)$ ^{rotatedaround} $(z_0,\theta\mkern1mu)$' if you want to rotate
+around point~$z_0$ instead of point~$(0,0)$. And you can say
+`$(x,y)$~^{reflectedabout}~$(z_1,z_2)$' if you want to find the point directly
+opposite $(x,y)$ on the other side of the straight line that runs through
+$z_1$ and~$z_2$.
+
+All of these operations are special manifestations of a single glorious
+maneuver that can be written in the general form
+\begindisplay
+$(x,y)$ ^{transformed} $t$.
+\enddisplay
+Here $t$ is a variable (or primary expression) of type ^@transform@; it
+stands for any desired sequence of shiftings, scalings, slantings, etc.,
+all in one fell swoop.
+
+You can give ^{equations} between transforms, just as you can give equations
+between other types of things in \MF\ programs. Thus, for example,
+you might say
+\begindisplay
+@transform@ $t[\,]$; \ $t_2=t_1$ shifted $(2,2)$ rotated 30;
+\enddisplay
+then an expression like `$(x,y)$ transformed $t_1$ shifted $(2,2)$ rotated 30'
+can be abbreviated to `$(x,y)$ transformed $t_2$', which is simpler and faster.
+
+There's a special transform variable called ^"identity" with the amazing
+property that
+\begindisplay
+$(x,y)$ transformed "identity" $=$ $(x,y)$
+\enddisplay
+for all $x$ and $y$. You might think that "identity" is useless, since it
+does nothing, but in fact it's a natural starting point for building other
+transforms. For example, line~19 of the program at the end of the previous
+chapter says
+\begindisplay
+$b="identity"$ shifted $(0,y_0-y_{a1})$ rotatedaround$(z_0,"theta")$;
+\enddisplay
+this defines the transform variable $b$ to be a compound transformation
+that is used on lines 21 and~22 to construct the lower left arrow
+as a shifted and rotated copy of the upper arrow, in the character being drawn.
+
+\danger A @transform@ variable $t$ represents six numbers
+$(t_x,t_y,t_{xx},t_{xy},t_{yx},t_{yy})$, in much the same way
+as a @pair@ variable represents two numbers $(x,y)$. The general
+transformation `$(x,y)$~transformed~$t$' is simply an abbreviation for
+\begindisplay
+$(t_x+x\,t_{xx}+y\,t_{xy},\;t_y+x\,t_{yx}+y\,t_{yy})$;
+\enddisplay
+thus, for example, `$t_{xy}$' appears in the xpart of the transform as the
+coefficient of~$y$. If you say `^@show@~$t$' when $t$~is a completely
+unknown transform, the computer will type
+\begintt
+>> (xpart t,ypart t,xxpart t,xypart t,yxpart t,yypart t)
+\endtt
+just as it would type `{\tt>> (xpart u,ypart u)}' for a completely
+unknown variable~$u$ of type @pair@. You can access individual components
+of a transform by referring to `^{xpart}~$t$', `^{ypart}~$t$',
+^^{xypart}^^{yxpart}^^{yypart}
+`^{xxpart}~$t$', etc.
+
+\outer\def\begindemo#1{$$\advance\baselineskip by2pt
+ \catcode`\"=\other
+ \halign\bgroup\indent\hbox to #1{\tt##\hfil}&\tt##\hfil\cr
+ \noalign{\vskip-2pt}}
+\outer\def\enddemo{\egroup$$}
+\def\demohead{\it\kern-2pt You type&\it\kern-1pt And the result is\cr
+ \noalign{\nobreak\vskip2pt}}
+
+\danger Once again, we can learn best by computer experiments with the
+|expr| file (cf.~Chapter~8); this time the idea is to play with transforms:
+\begindemo{175pt}
+\demohead
+identity&(0,0,1,0,0,1)\cr
+identity shifted (a,b)&(a,b,1,0,0,1)\cr
+identity scaled s&(0,0,s,0,0,s)\cr
+identity xscaled s&(0,0,s,0,0,1)\cr
+identity yscaled s&(0,0,1,0,0,s)\cr
+identity slanted s&(0,0,1,s,0,1)\cr
+identity rotated 90&(0,0,0,-1,1,0)\cr
+identity rotated 30&(0,0,0.86603,-0.5,0.5,0.86603)\cr
+identity rotatedaround ((2,3),90)&(5,1,0,-1,1,0)\cr
+(x,y) rotatedaround ((2,3),90)&(-y+5,x+1)\cr
+(x,y) reflectedabout ((0,0),(0,1))&(-x,y)\cr
+(x,y) reflectedabout ((0,0),(1,1))&(y,x)\cr
+(x,y) reflectedabout ((5,0),(0,10))&(-0.8y-0.6x+8,0.6y-0.8x+4)\cr
+\enddemo
+
+\dangerexercise Guess the result of `|(x,y) reflectedabout ((0,0),(1,0))|'.
+\answer |(x,-y)|.
+
+\dangerexercise What transform takes $(x,y)$ into $(-x,-y)$?
+\answer $(x,y)$ rotated 180, or $(x,y)$ scaled $-1$.
+
+\dangerexercise True or false:\quad $\bigl(-(x,y)\bigr)$ transformed $t$
+$=$ $-\bigl((x,y)$ transformed $t\bigr)$.
+\answer True if and only if ${\rm xpart}\,t={\rm 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, \MF\ interprets `$-(x,y)$ transformed~$t$'
+as $\bigl(-(x,y)\bigr)$ transformed~$t$. \ (Incidentally, mathematicians
+call \MF's transformers ``^{affine transformations},'' and the special case in
+which the xpart and ypart are zero is called ``^{homogeneous}.'')
+
+\danger In order to have some transform variables to work with, it's necessary
+to `^{hide}' some declarations and commands before giving the next |expr|s:
+\begindemo{175pt}
+\demohead
+hide(transform t[]) t1&(xpart t1,ypart t1,xxpart...)\cr
+hide(t1=identity zscaled(1,2)) t1&(0,0,1,-2,2,1)\cr
+hide(t2=t1 shifted (1,2)) t2&(1,2,1,-2,2,1)\cr
+t2 xscaled s&(s,2,s,-2s,2,1)\cr
+unknown t2&false\cr
+transform t2&true\cr
+t1=t2&false\cr
+t1<t2&true\cr
+inverse t2&(-1,0,0.2,0.4,-0.4,0.2)\cr
+inverse t2 transformed t2&(0,0,0.99998,0,0,0.99998)\cr
+hide(t3 transformed t2=identity) t3&(-1,0,0.2,0.4,-0.4,0.2)\cr
+\enddemo
+The ^"inverse" function finds the transform that undoes the work
+of another; the equation that defines $t_3$ above shows how to
+calculate an inverse indirectly, without using "inverse".
+
+\danger Like numeric expressions and pair expressions, transform
+expressions can be either ``^{known}'' or ``^{unknown}'' at any given
+point in a program. \ (If any component of a transform is unknown, the
+whole transform is regarded as unknown.) \ You are always allowed to use
+the constructions
+\begindisplay
+\<known> transformed \<known>\cr
+\<unknown> transformed \<known>\cr
+\<known> transformed \<unknown>\cr
+\enddisplay
+but \MF\ will balk at `\<unknown> transformed \<unknown>'. This is
+not the most lenient rule that could have been implemented, but it
+does have the virtue of being easily remembered.
+
+\dangerexercise If $z_1$ and $z_2$ are unknown pairs, you can't
+say `$z_1$ shifted~$z_2$', because `shifted~$z_2$' is an unknown
+transform. What can you legally say instead?
+\answer $z_1+z_2$.
+
+\begingroup\def\dbend{{\manual\char126}} % lefty dangerous bend sign
+\dangerexercise Suppose "dbend" is a picture variable that contains
+a normal dangerous bend sign, as in the ``reverse-video'' example
+of Chapter~13. Explain how to transform it into the ^{left-handed
+dangerous bend} that heads this paragraph.
+\answer @beginchar@$(126,25u\0,"hheight"\0+"border"\0,0)$;
+|"Dangerous left bend"|;\parbreak
+$"currentpicture":="dbend"$ reflectedabout $\bigl((.5w,0),(.5w,h)\bigr)$; \
+@endchar@;\medskip\noindent
+The same idea can be used to create right ^{parentheses} as perfect mirror
+images of left parentheses, etc., if the parentheses aren't slanted.
+
+\endgroup
+
+\danger The next three lines illustrate the fact that you can specify
+a transform completely by specifying the images of three points:
+\begindemo{175pt}
+\demohead
+hide((0,0)transformed t4=(1,2)) t4&(1,2,xxpart t4,xypart t4,...)\cr
+hide((1,0)transformed t4=(4,5)) t4&(1,2,3,xypart t4,3,yypart t4)\cr
+hide((1,4)transformed t4=(0,0)) t4&(1,2,3,-1,3,-1.25)\cr
+\enddemo
+The points at which the transform is given shouldn't all lie on
+a straight line.
+
+\danger Now let's use transformation to make a little ^{ornament}, based
+on a `{\manual\oneu\kern1pt}' shape replicated four times:
+\qquad\xleaders\hbox{$\vcenter{\hbox{\manual\fouru}}$}\hfill\
+
+\vskip-6mm
+\displayfig 15a (396\apspix)
+
+\begingroup\ninepoint\noindent The following program merits careful study:
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ \ }&#\hfil\cr
+1&@beginchar@\kern1pt(|"4"|$,11"pt"\0,11"pt"\0,0)$;\cr
+2&@pickup@ @pencircle@ scaled 3/4"pt" yscaled 1/3 rotated 30;\cr
+3&@transform@ $t$;\cr
+4&$t="identity"$ ^{rotatedaround}$\bigl((.5w,.5h),-90\bigr)$;\cr
+5&$x_2=.35w$; \ $x_3=.6w$;\cr
+6&$y_2=.1h$; \ $"top"\,y_3=.4h$;\cr
+7&@path@ $p$; \ $p=z_2\{"right"\}\ldots\{"up"\}z_3$;\cr
+8&$"top"\,z_1$ $=$ point .5 of $p$ transformed $t$;\cr
+9&@draw@ $z_1\ldots p$;\cr
+10&@addto@ "currentpicture" @also@ "currentpicture" transformed $t$;\cr
+11&@addto@ "currentpicture" @also@ "currentpicture"
+ transformed ($t$ transformed $t$);\cr
+12&@labels@$(1,2,3)$; \ @endchar@;\cr
+}$$
+^^@addto@
+Lines 3 and 4 compute the transform that moves each
+`{\manual\oneu\kern1pt}' to its clockwise neighbor. Lines 5--7 compute the
+right half of the `{\manual\oneu\kern1pt}'. Line~8 is the most
+interesting: It puts point $z_1$ on the rotated path. Line~9 draws the
+`{\manual\oneu\kern1pt}', line~10 changes it into two, and line~11 changes
+two into four. The parentheses on line~11 could have been omitted, but it
+is much faster to transform a transform than to transform a picture.
+
+\endgroup
+
+\ddanger \MF\ will transform a ^{picture} expression only when $t_{xx}$,
+$t_{xy}$, $t_{yx}$, and~$t_{yy}$ are integers and either $t_{xy}=t_{yx}=0$
+or $t_{xx}=t_{yy}=0$; furthermore, the values of $t_x$ and~$t_y$ are
+rounded to the nearest integers. Otherwise the transformation would not
+take pixel boundaries into pixel boundaries.
+
+\ddangerexercise Explain how to rotate the ornament by $45^\circ$.
+\qquad\xleaders\hbox{\kern1pt$\vcenter{\hbox{\manual\fourc}}$}\hfill\
+\answer Change line 9 to
+\begindisplay
+@draw@ $(z_1\ldots p)$ rotatedaround$\bigl((.5w,.5h),-45\bigr)$\cr
+\quad @withpen@ @pencircle@ scaled 3/4"pt" yscaled 1/3 rotated $-15$;\cr
+\enddisplay
+
+Plain \MF\ maintains a special variable called ^"currenttransform",
+behind the scenes. Every ^@fill@ and ^@draw@ command is affected by this
+variable; for example, the statement `@fill@~$p$' actually fills the
+interior of the path
+\begindisplay
+$p$ transformed "currenttransform"
+\enddisplay
+instead of $p$ itself. We haven't mentioned this before, because
+"currenttransform" is usually equal to "identity"; but nonstandard
+settings of "currenttransform" can be used for special effects that
+are occasionally desired. For example, it's possible to change
+`\MF\kern1pt' to `{\manual 89:;<=>:}\kern3pt' by simply saying
+\begindisplay
+$"currenttransform":="identity"$ slanted 1/4
+\enddisplay
+and executing the programs of |logo.mf| that are described in Chapter~11;
+no other changes to those programs are necessary.
+
+It's worth noting that the pen nib used to draw `{\manual 89:;<=>:}\kern3pt'
+was not slanted when "currenttransform" was changed; only the ``tracks'' of
+the pen, the paths in @draw@ commands, were modified. Thus the slanted image
+was not simply obtained by slanting the unslanted image.
+
+\ddanger When fonts are being made for devices with ^{nonsquare pixels},
+plain \MF\ will set "currenttransform" to `"identity" yscaled
+^"aspect\_ratio"', and ^@pickup@ will similarly yscale the pen nibs
+that are used for drawing. In this case the slanted
+`{\manual 89:;<=>:}\kern3pt' letters should be drawn with
+\begindisplay
+$"currenttransform":="identity"$ slanted 1/4 yscaled "aspect\_ratio".
+\enddisplay
+
+\ddangerexercise Our program for
+`\kern1pt\lower2.5pt\hbox{\manual\fouru}\kern1pt' doesn't work when pixels
+aren't square. Fix it so that it handles a general "aspect\_ratio".
+\answer Replace line 10 by
+\begindisplay
+@pickup@ @pencircle@ scaled 3/4"pt" yscaled 1/3 rotated $-60$;\cr
+@draw@ ($z_1\ldots p$) transformed $t$;\cr
+\enddisplay
+
+\endchapter
+
+Change begets change. Nothing propagates so fast.
+\author CHARLES ^{DICKENS}, {\sl Martin Chuzzlewit\/} (1843)
+ % opening lines of chapter 18
+
+\bigskip
+
+There are some that never know how to change.
+\author MARK ^{TWAIN}, {\sl Joan of Arc\/} (1896)
+ % book 2, chapter 26, second page
+
+\eject
+ \beginChapter Chapter 16. Calligraphic\\Effects
+
+^{Pens} were introduced in Chapter 4, and we ought to make a systematic study
+of what \MF\ can do with them before we spill any more ink. The purpose
+of this chapter will be to explore the uses of ``fixed'' pen nibs---i.e.,
+variables and expressions of type ^@pen@---rather than to consider
+the creation of shapes by means of outlines or penstrokes.
+
+When you say `^@pickup@ ^\<pen expression>', the macros of plain \MF\ do
+several things for you: They create a representation of the specified
+pen~nib, and assign it to a pen variable called ^"currentpen"; then they
+store away information about the top, bottom, left, and right extents of
+that pen, for use in ^"top", ^"bot", ^"lft", and ^"rt" operations.
+A ^@draw@ or ^@drawdot@ or ^@filldraw@ command will make use of
+"currentpen" to modify the current picture.
+
+You can also say `@pickup@ \<numeric expression>'; in this case the numeric
+expression designates the code number of a previously picked-up pen
+that was saved by `^"savepen"'. For example, the |logo.mf| file in Chapter~11
+begins by picking up the pen that's used to draw `\MF\kern1pt', then
+it says `$"logo\_pen":="savepen"$'. Every character program later in that
+file begins with the command `@pickup@ "logo\_pen"', which is a fast
+operation because it doesn't require the generation of a new
+pen representation inside the computer.
+
+\danger Caution: Every time you use "savepen", it produces a new integer
+value and stashes away another pen for later use. If you keep doing this,
+\MF's memory will become cluttered with the representations of pens
+that you may never need again. The command `^@clear\_pen\_memory@'
+discards all previously saved pens and lets \MF\ start afresh.
+
+\danger But what is a \<pen expression>? Good question. So far in this book,
+almost everything that we've picked up was a pencircle followed by
+some sequence of transformations; for example, the "logo\_pen" of
+Chapter~11 was `@pencircle@ xscaled~"px" yscaled~"py"'. Chapter~13
+also made brief mention of another kind of pen, when it said
+\begindisplay
+@pickup@ ^@penrazor@ scaled 10;
+\enddisplay
+this command picks up an infinitely thin pen that runs from point
+$(-5,0)$ to point $(5,0)$ with respect to its center. Later in this
+chapter we shall make use of pens like
+\begindisplay
+^@pensquare@ xscaled 30 yscaled 3 rotated 30;
+\enddisplay
+this pen has a rectangular boundary measuring 30 pixels $\times$ 3 pixels,
+inclined at an angle of $30^\circ$ to the baseline.
+
+\danger You can define pens of any ^{convex polygon}al shape by saying
+`^@makepen@~$p$', where $p$ is a cyclic path. It turns out that \MF\
+looks only at the key points of~$p$, not the control points, so we may
+as well assume that $p$ has the form $z_0\dashto z_1\dashto\<etc.>\dashto
+\cycle$. This path must have the property that it turns left at every
+key point (i.e., $z_{k+1}$ must lie to the left of the line from $z_{k-1}$
+to~$z_k$, for all~$k$), unless the cycle contains fewer than three key
+points; furthermore the path must have a ^{turning number} of~1 (i.e.,
+it must not make more than one counterclockwise loop). Plain \MF's
+@penrazor@ stands for
+`@makepen@ $\bigl((-.5,0)\dashto(.5,0)\dashto \cycle\bigr)$',
+and @pensquare@ is an abbreviation for
+`@makepen@ $\bigl("unitsquare"$ shifted $-(.5,.5)\bigr)$'.
+But @pencircle@ is not defined via @makepen@; it is a
+primitive operation of \MF. It represents a true ^{circle} of diameter~1,
+% no need for `\MF\!.' here
+passing through the points $(\pm.5,0)$ and $(0,\pm.5)$.
+
+\danger The complete syntax for pen expressions is rather short, because
+you can't really do all that much with pens. But it also contains a
+surprise:
+\beginsyntax
+<pen primary>\is<pen variable>
+ \alt[(]<pen expression>[)]
+ \alt[nullpen]
+<future pen primary>\is[pencircle]
+ \alt[makepen]<path primary>
+<pen secondary>\is<pen primary>
+<future pen secondary>\is<future pen primary>
+ \alt<future pen secondary><transformer>
+ \alt<pen secondary><transformer>
+<pen tertiary>\is<pen secondary>
+ \alt<future pen secondary>
+<pen expression>\is<pen tertiary>
+\endsyntax
+The constant `^@nullpen@' is just the single point $(0,0)$, which is
+invisible---unless you use it in ^@filldraw@, which then reduces to
+^@fill@. \ (A ^@beginchar@ command initializes "currentpen" to @nullpen@,
+in order to reduce potentially dangerous dependencies between the programs
+for different characters.) \
+The surprise in these rules is the notion of a ``^{future pen},''
+which stands for a path or an ellipse that has not yet been converted
+into \MF's internal representation of a true pen. The conversion process
+is rather complicated, so \MF\ procrastinates until being sure that no
+more transformations are going to be made. A true pen is formed at the
+tertiary level, when future pens are no longer permitted in the syntax.
+
+\danger The distinction between pens and future pens would make no
+difference to a user, except for another surprising fact: All of \MF's
+pens are convex polygons, even the pens that are made from @pencircle@
+and its variants! Thus, for example, the pen you get from an
+untransformed pencircle is identical to the pen you get by specifying
+the ^{diamond-shaped nib}
+\begindisplay
+@makepen@$\,\bigl((.5,0)\dashto(0,.5)\dashto(-.5,0)\dashto
+ (0,-.5)\dashto\cycle\bigr)$.
+\enddisplay
+And the pens you get from `@pencircle@ scaled 20' and `@pencircle@
+xscaled~30 yscaled~20' are polygons with 32 and 40 sides, respectively:
+\displayfig 16a\&b (220\apspix)
+The vertices of the polygons, shown as heavy dots in this illustration,
+all have ``half-integer'' coordinates; i.e., each coordinate is either
+an integer or an integer plus 1/2. Every polygon that comes from a
+@pencircle@ is symmetric under $180^\circ$ rotation; furthermore,
+there will be reflective left/right and top/bottom symmetry if the future
+pen is a circle, or if it's an ellipse that has not been rotated.
+
+\danger This conversion to polygons explains why future pens must, in
+general, be distinguished from ordinary ones. For example, the extra
+parentheses in `(@pencircle@ xscaled~30) yscaled~20' will yield
+a result quite different from the elliptical polygon just illustrated.
+The parentheses force conversion of `@pencircle@ xscaled~30' from
+future pen to pen, and this polygon turns out to be
+\begindisplay
+$(12.5,-0.5) \dashto (15,0) \dashto (12.5,0.5)$\cr
+\qquad$\dashto (-12.5,0.5) \dashto
+(-15,0) \dashto (-12.5,-0.5) \dashto\cycle$,\cr
+\enddisplay
+an approximation to a $30\times1$ ellipse. Then yscaling by 20 yields
+\displayfig 16c (220\apspix)
+
+\danger Why does \MF\ work with polygonal approximations to circles,
+instead of true circles? That's another good question. The main reason is
+that suitably chosen polygons give better results than the real thing,
+when ^{digitization} is taken into account. For example, suppose we want
+to draw a straight line of slope 1/2 that's exactly one pixel thick, from
+$(0,y)$ to $(200,y+100)$. The image of a perfectly circular pen of
+diameter~1 that travels along this line has outlines that run from
+$(0,y\pm\alpha)$ to $(200,y+100\pm\alpha)$, where
+$\alpha=\sqrt5/4\approx0.559$. If we digitize these outlines and fill the
+region between them, we find that for some values of~$y$ (e.g., $y=0.1$)
+the result is a repeating pixel pattern like
+`\smash{\hbox{$\vcenter{\offinterlineskip
+\setbox4=\hbox{\manual R}
+\hbox{\hphantom{$\,\ldots\,$}\kern5\wd4\copy4$\,\ldots\,$}
+\hbox{\hphantom{$\,\ldots\,$}\kern3\wd4\copy4\copy4}
+\hbox{\hphantom{$\,\ldots\,$}\kern\wd4\copy4\copy4}
+\hbox{\smash{$\,\ldots\,$}\copy4}}$}}'; but for other values of~$y$ (e.g.,
+$y=0.3$) the repeating pattern of pixels is \vbox to11pt{}50 percent darker:
+`\smash{\raise2pt\hbox{$\vcenter{\offinterlineskip
+\setbox4=\hbox{\manual R}
+\hbox{\hphantom{$\,\ldots\,$}\kern4\wd4\copy4\copy4$\,\ldots\,$}
+\hbox{\hphantom{$\,\ldots\,$}\kern2\wd4\copy4\copy4\copy4}
+\hbox{\hphantom{$\,\ldots\,$}\copy4\copy4\copy4}
+\hbox{\smash{$\,\ldots\,$}\copy4}}$}}'. Similarly, some diagonal
+lines of slope~1 digitize to be twice as dark as others, when a truly
+circular pen is considered. But the diamond-shaped nib that \MF\ uses
+for a pencircle of diameter~1 does not have this defect; all straight
+lines of the same slope will digitize to lines of uniform darkness.
+Moreover, curved lines drawn with the diamond nib always yield one pixel per
+column when they move more-or-less horizontally (with slopes between $+1$
+and $-1$), and they always yield one pixel per row when they move vertically.
+By contrast, the outlines of curves drawn with circular pens produce
+occasional ``blots.'' Circles and ellipses of all diameters can profitably
+be replaced by polygons whose sub-pixel corrections to the ideal shape
+will produce better digitizations; \MF\ does this in accordance with the
+interesting theory developed by John~D. ^{Hobby} in his Ph.D.
+dissertation (Stanford University, 1985).
+
+\ddanger It's much easier to compute the outlines of a polygonal pen that
+follows a given curve than to figure out the corresponding outlines of
+a truly circular pen; thus polygons win over circles with respect
+to both quality and speed. When a curve is traveling in a
+direction between the edge vectors $z_{k+1}-z_k$ and~$z_k-z_{k-1}$ of
+a polygonal pen, the curve's outline will be offset from its center
+by~$z_k$. If you want fine control over this curve-drawing process,
+\MF\ provides the primitive operation `^{penoffset}~$w$ of~$p$', where
+$w$~is a vector and $p$~is a pen. If $w=(0,0)$, the result is $(0,0)$;
+if the direction of~$w$ lies strictly between $z_{k+1}-z_k$ and $z_k
+-z_{k-1}$, the result is~$z_k$; and if $w$ has the same direction as
+$z_{k+1}-z_k$ for some~$k$, the result is either $z_k$ or~$z_{k+1}$,
+whichever \MF\ finds most convenient to compute.
+
+\ddangerexercise Explain how to use penoffset to find the point or
+points at the ``top'' of a pen (i.e., the point or points with largest
+$y$~coordinate).
+\answer If there are two points $z_k$ and $z_{k+1}$ with maximum
+$y$~coordinate, the value of `penoffset $(-"infinity","epsilon")$ of~$p$'
+will be~$z_k$ and `penoffset $(-"infinity",-"epsilon")$ of~$p$' will
+be~$z_{k+1}$; `penoffset~"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 \MF\ 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 penoffset to discover the first and the last, as above;
+furthermore, if you really want to find them all, ^@makepath@ will produce
+a path from which they can be deduced in a straightforward manner.)
+
+\ddanger The primitive operation `^@makepath@ $p$', where $p$ is
+a (polygonal) pen whose vertices are $z_0$, $z_1$, \dots,~$z_{n-1}$,
+produces the path `$z_0\to\controls z_0\and z_1\to z_1\to\<etc.>\to
+z_{n-1}\to\controls z_{n-1}\and z_0\to\cycle$', which is one of the
+paths that might have generated~$p$. This gives access to all the
+offsets of a pen.
+
+\ddanger When a @pencircle@ is transformed by any of the operations
+in Chapter~15, it changes into an ellipse of some sort, since all of
+\MF's transformations preserve ellipse-hood. The diameter of the
+ellipse in each direction~$\theta$ is decreased by $2\min\bigl(
+\vert\sin\theta\vert,\vert\cos\theta\vert\bigr)$ times the current
+value of~^"fillin", before converting to a polygon; this helps to
+compensate for the variation in thickness of diagonal strokes with
+respect to horizontal or vertical strokes, on certain output devices.
+\ (\MF\ uses "fillin" only when creating polygons from ellipses,
+but users can of course refer to "fillin" within their own routines
+for drawing strokes.) \ The final polygon will never be perfectly flat
+like ^@penrazor@, even if you say `xscaled~0' and/or `yscaled~0';
+its center will always be surrounded at least by the basic diamond nib
+that corresponds to a circle of diameter~1.
+
+\dangerexercise Run \MF\ on the |expr| file of Chapter~8 and look at
+what is typed when you ask for `|pencircle|' and `|pencircle|
+|scaled|~|1.1|'. \ (The first will exhibit the diamond nib, while
+the second will show a polygon that's equivalent to @pensquare@.) \
+Continue experimenting until you find the ``threshold'' diameter where
+\MF\ decides to switch between these two polygons.
+\answer `@pencircle@ scaled 1.06060' is the diamond but
+`@pencircle@ scaled 1.06061' is~the square. \ (This assumes that
+^"fillin"$\null=0$. If, for example, $"fillin"=.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 first.
+
+\danger \MF's polygonal pens work well for drawing lines and curves,
+but this pleasant fact has an unpleasant corollary: They do not always
+digitize well at the ^{endpoints}, where curves start and stop. The
+reason for this is explored further in Chapter~24; polygon vertices that
+give nice uniform stroke widths might also be ``ambiguous'' points that
+cause difficulties when we consider rounding to the raster. Therefore a
+special ^@drawdot@ routine is provided for drawing one-point paths.
+It is sometimes advantageous to apply @drawdot@ to the first and last
+points of a path~$p$, after having said `^@draw@~$p$'; this can
+fatten up the endpoints slightly, making them look more consistent with
+each other.
+
+\danger Plain \MF\ also provides two routines that can be used to clean~up
+endpoints in a different way: The command `^@cutoff@$\,(z,\theta)$'
+removes half of the ^"currentpen" image at point~$z$, namely all points
+of the pen that lie in directions between $(\theta-90)^\circ$ and
+$(\theta+90)^\circ$ from the center point. And the command `^@cutdraw@~$p$'
+is an abbreviation for the following three commands:
+\begindisplay
+@draw@ $p$; \ @cutoff@\thinspace(point 0 of $p$, $180+\null$angle
+direction 0 of $p$);\cr
+@cutoff@\thinspace(point "infinity" of $p$, angle
+direction "infinity" of $p$).\cr
+\enddisplay
+The effect is to draw a curve whose ends are clipped perpendicular to the
+starting and ending directions. For example, the command
+\begindisplay
+@cutdraw@ $z_4\to\controls z_1\and z_2\to z_6$
+\enddisplay
+produces the following curve, which invites comparison with the corresponding
+uncut version at the end of Chapter~3:
+\displayfig 16d (5pc)
+
+\decreasehsize 48mm
+\danger Here's another example of @cutoff@, in which the endpoints of
+\rightfig 16e ({208\apspix} x {216\apspix}) ^15pt
+\MF's~`^{T}' have been cropped at $10^\circ$ angles to the
+perpendicular of the stroke direction:
+\begintt
+pickup logo_pen;
+top lft z1=(0,h); top rt z2=(w,h);
+top z3=(.5w,h); z4=(.5w,0);
+draw z1--z2;
+cutoff(z1,170); cutoff(z2,-10);
+draw z3--z4; cutoff(z4,-80).
+\endtt
+
+\restorehsize
+\ddanger The @cutoff@ macro of Appendix~B deals with several things
+that we've been studying recently, so it will be instructive to look
+at it now (slightly simplified):
+\begindisplay
+@def@ @cutoff@\thinspace(@expr@ $z,"theta"$) $=$\cr
+\quad$"cut\_pic":=@nullpicture@$;\cr
+\quad^@addto@ "cut\_pic" @doublepath@ $z$ @withpen@ "currentpen";\cr
+\quad@addto@ "cut\_pic" @contour@
+ $((0,-1)\dashto(1,-1)\dashto(1,1)\dashto(0,1)\dashto\cycle)$\cr
+\qquad scaled $1.42(1+\max(-"pen\_lft","pen\_rt","pen\_top",-"pen\_bot"))$\cr
+\qquad rotated "theta" shifted "z";\cr
+\quad^@cull@ "cut\_pic" @keeping@ $(2,2)$ @withweight@ $-1$;\cr
+\quad@addto@ "currentpicture" @also@ "cut\_pic" @enddef@.\cr
+\enddisplay
+The main work is done in a separate ^{picture} variable called "cut\_pic",
+so that neighboring strokes won't be affected. First "cut\_pic" is set to
+the full digitized pen image (by making a ^@doublepath@ from a single
+point). Then a rectangle that includes the cutoff region is added in;
+^"pen\_lft", "pen\_rt", "pen\_top", and "pen\_bot" are the quantities used
+to compute the functions ^"lft", ^"rt", ^"top", and ^"bot", so they bound
+the size of the pen. The culling operation produces the intersection of
+pen and rectangle, which is finally subtracted from "currentpicture".
+
+\ddanger We shall conclude this chapter by studying two examples of how
+\MF's pen-and-curve-drawing facilities can combine in interesting ways.
+First, let's examine two ``^{tilde}'' characters
+\displayfig 16f\&g (50\apspix)
+which were both created by a single command of the form
+\begindisplay
+@draw@ $z_1\to\controls z_2\and z_3\to z_4$.
+\enddisplay
+The left example was done with a ^@pencircle@ xscaled .8"pt" yscaled .2"pt"
+rotated~50, and the right example was exactly the same but with ^@pensquare@.
+The control points $z_2$ and~$z_3$ that made this work were defined by
+\begindisplay
+$y_2-y_1=y_4-y_3=3(y_4-y_1)$;\cr
+$z_2-z_1=z_4-z_3="whatever"\ast{\rm dir}\,50$.\cr % partly redundant
+\enddisplay
+The second pair of equations is an old calligrapher's trick, namely to start
+and finish a~stroke in the direction of the pen you're holding.
+The first pair of equations is a mathematician's trick, based on the
+fact that the ^{Bernshte{\u\i}n polynomial} $t[0,3,-2,1]$ goes from
+0~to~1 to~0~to~1 as $t$ goes from 0 to~.25 to~.75~to~1.
+
+\ddanger Next, let's try to draw a fancy ^{serif} with
+the same two pens, holding them at a $20^\circ$~angle instead of a
+$50^\circ$~angle. Here are two examples
+\displayfig 16h\&i (195\apspix)
+that can be created by `^@filldraw@' commands:
+\begindisplay
+@filldraw@ $z_1\to\controls z_2\to z_3$\cr
+\qquad$\dashto("flex"(z_3,.5[z_3,z_4]+"dishing",z_4))$
+ shifted$\,(0,-"epsilon")$\cr
+\qquad$\dashto z_4\to\controls z_5\to z_6\dashto\cycle$.\cr
+\enddisplay
+The ^"dishing" parameter causes a slight rise between $z_3$ and~$z_4$;
+the ^"flex" has been lowered by ^"epsilon" in order to avoid the danger
+of ``^{strange paths},'' which might otherwise be caused by tiny loops
+at $z_3$ or~$z_4$. But the most interesting thing about this example
+is the use of double control points, $z_2$ and~$z_5$, in two of the
+path segments. \ (Recall that `$\controls z_2$' means the same thing
+^^{controls} as `$\controls z_2\and z_2$'.) \ These points were determined
+by the equations
+\begindisplay
+$x_2=x_1$; \ $z_2=z_3+"whatever"\ast{\rm dir}\,20$;\cr
+$x_5=x_6$; \ $z_5=z_4+"whatever"\ast{\rm dir}\,{-20}$;\cr
+\enddisplay
+thus, they make the strokes vertical at $z_1$ and $z_6$, parallel to the
+pen angle at~$z_3$, and parallel to the complementary angle at~$z_4$.
+
+
+\endchapter
+
+The pen, probably more than any other tool,
+has had the strongest influence upon lettering
+in respect of serif design .\thinspace.\thinspace.
+It is probable that the letters [of the Trajan column]
+were painted before they were incised,
+and though their main structure is attributed to the pen
+and their ultimate design to the technique of the chisel,
+they undoubtedly owe much of their freedom
+to the influence of the brush.
+\author L. C. ^{EVETTS}, {\sl Roman Lettering\/} (1938) % pp 3 and 13
+
+\bigskip
+
+Remember that it takes time, patience, critical practice
+and knowledge to learn any art or craft.
+No ``art experience'' is going to result from any busy work
+for a few hours experimenting with the edged pen.
+.\thinspace.\thinspace. Take as much time as you require,
+and do not become impatient.
+If it takes a month to get it,
+then be happy that it takes only a month.
+\author LLOYD ^{REYNOLDS}, {\sl Italic Calligraphy \& Handwriting\/} (1969)
+
+\eject
+ \beginchapter Chapter 17. Grouping
+
+We have now covered all the visual, graphic aspects of \MF---its
+points, paths, pens, and pictures; but we still don't know everything
+about \MF's organizational, administrative aspects---its programs.
+The next few chapters of this book therefore concentrate on
+how to put programs together effectively.
+
+A \MF\ program is a sequence of statements separated by semicolons and
+followed by `^@end@'. More precisely, the syntax rules
+\beginsyntax
+<program>\is<statement list>[end]
+<statement list>\is<empty>\alt<statement>[;]<statement list>
+\endsyntax
+define a \<program> in terms of a \<statement>.
+
+But what are ^{statements}? Well, they are of various kinds. An ``equation''
+states that two expressions are supposed to be equal. An ``assignment''
+assigns the value of an expression to a variable. A ``declaration''
+states that certain variables will have a certain type.
+A ``definition'' defines a macro. A ``title'' gives a descriptive name to
+the character that is to follow. A ``command'' orders \MF\ to do some
+specific operation, immediately. The ``^{empty statement}'' tells \MF\ to
+do absolutely nothing. And a ``^{compound statement}'' is a list of other
+statements treated as a ^{group}.
+\beginsyntax
+<statement>\is<equation>\alt<assignment>\alt<declaration>
+ \alt<definition>\alt<title>\alt<command>\alt<empty>
+ \alt[begingroup] <statement list> <statement> [endgroup]
+\endsyntax
+We've given the syntax for \<equation> and \<assignment> in Chapter~10;
+the syntax for \<declaration> appeared in Chapter~7; \<definition> and
+\<title> and \<command> will appear in later chapters. Our main concern
+just now is with the final type of \<statement>, where @begingroup@
+and @endgroup@ bind other statements into a unit, just as parentheses
+add structure to the elements of an algebraic expression.
+
+The main purpose of grouping is to protect the values of variables in
+one part of the program from being clobbered in another. A symbolic token
+can be given a new meaning inside a group, without changing the
+meaning it had outside that group. \ (Recall that \MF\ deals with
+three basic kinds of tokens, as discussed in Chapter~6; it is impossible
+to change the meaning of a numeric token or a string token, but
+symbolic tokens can change meanings~freely.)
+
+There are two ways to protect the values of variables in a group. One
+is called a \<save command>, and the other is called an \<interim command>:
+\beginsyntax
+<save command>\is[save]<symbolic token list>
+<symbolic token list>\is<symbolic token>
+ \alt<symbolic token list>[,]<symbolic token>
+<interim command>\is\kern-1.5pt[interim]%
+ <internal quantity>[:=]<right-hand side>\kern-1pt
+\endsyntax
+The symbolic tokens in a @save@ command all lose their current meanings, but
+those old meanings are put into a safe place and restored at the end of
+the current group. Each token becomes undefined, as if it had never
+appeared before. For example, the command
+\begindisplay
+@save@ $x,y$
+\enddisplay
+effectively causes all previously known variables like $x_1$ and $y_{5r}$ to
+become inaccessible; the variable $x_1$ could now appear in a new equation,
+where it would have no connection with its out-of-group value. You could
+also give the silly command
+\begindisplay
+@save@ @save@;
+\enddisplay
+this would make the token `|save|' itself into a ^\<tag> instead of a
+^\<spark>, so you couldn't use it to save anything else until the group ended.
+
+\danger An @interim@ command is more restrictive than a @save@, since it
+applies only to an ^\<internal quantity>. \ (Recall that internal
+quantities are special variables like "tracingequations" that take numeric
+values only; a complete list of all the standard internal quantities can
+be found in Chapter~25, but that list isn't exhaustive because you can
+define new ones for your own use.) \ \MF\ treats an interim command just
+like an ordinary assignment, except that it undoes the assignment when the
+group~ends.
+
+\danger If you save something two or more times in the same group,
+the first saved value takes precedence. For example, in the construction
+\begindisplay
+@begingroup@\cr
+\noalign{\vskip-3pt}\dots\cr
+@interim@ $"autorounding":=0$; \ @save@ $x$;\cr
+\noalign{\vskip-3pt}\dots\cr
+@interim@ $"autorounding":=1$; \ @save@ $x$;\cr
+\noalign{\vskip-3pt}\dots\cr
+@endgroup@\cr
+\enddisplay
+the values of "autorounding" and $x$ after the end of the group will be
+their previous values just before the statement `@interim@ $"autorounding":=0$'.
+(Incidentally, these might not be the values they had upon entry to the group.)
+
+\danger Tokens and internal quantities regain their old meanings and
+values at the end of a group only if they were explicitly saved in a
+@save@ or @interim@ command. All other changes in meaning and/or value
+will survive outside the group.
+
+\danger The ^@beginchar@ operation of plain \MF\ includes a @begingroup@,
+and ^@endchar@ includes @endgroup@. Thus, for example, interim assignments
+can be made in a program for one character without any effect on other
+characters.
+
+\danger A \<save command> that's not in a group simply clears the meanings
+of the symbolic tokens specified; their old meanings are not actually saved,
+because they never will have to be restored. An \<interim command>
+outside a group acts just like a normal assignment.
+
+\danger If you set the internal quantity ^"tracingrestores" to a positive
+value, \MF\ will make a note in your transcript file whenever it is
+restoring the former value of a symbolic token or internal quantity.
+This can be useful when you're debugging a program that doesn't seem
+to make sense.
+
+Groups can also be used within algebraic expressions. This is
+the other important reason for grouping; it allows \MF\ to do arbitrarily
+complicated things while in the middle of other calculations, thereby
+greatly increasing the power of macro definitions (which we shall study
+in the next chapter). A {\sl^{group expression}\/} has the general form
+\begindisplay
+{\tt begingroup}\thinspace\<statement list>\thinspace\<expression>
+\thinspace{\tt endgroup}
+\enddisplay
+and it fits into the syntax of expressions at the primary level. The
+meaning of a group expression is: ``Perform the list of statements,
+then evaluate the expression, then restore anything that was saved
+in this group.''
+
+\danger Group expressions belong in the syntax rules for each type
+of expression, but they were not mentioned in previous chapters because
+it would have been unnecessarily distracting. Thus, for example, the syntax for
+\<numeric primary> actually includes the additional alternative
+\begindisplay
+|begingroup|\thinspace\<statement list>\<numeric expression>%
+ \thinspace|endgroup|.
+\enddisplay
+The same goes for \<pair primary>, \<picture primary>, etc.; Chapter~25
+has the complete rules of syntax for all types of expressions.
+
+\dangerexercise What is the value of the expression
+\begintt
+begingroup x:=x+1; x endgroup + begingroup x:=2x; x endgroup
+\endtt
+if $x$ initially has the value $a$? What would the value have been if
+the two group expressions had appeared in the opposite order?
+Verify your answers using the |expr| routine of Chapter~8.
+\answer $(a+1)+(2a+2)=3a+3$ and $(2a)+(2a+1)=4a+1$, respectively.
+The final value of~$x$ in the first 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|.\par
+This example shows that $\alpha+\beta$ is not necessarily equal
+to ^^{commutativity} $\beta+\alpha$, when $\alpha$ and~$\beta$ involve
+group expressions. \MF\ evaluates expressions strictly from left to
+right, performing the statements within groups as they appear.
+
+\dangerexercise Appendix B defines ^"whatever" to be an abbreviation for
+the group expression `@begingroup@ @save@ ?; ? @endgroup@'. Why
+does this work? \checkequals\Xwhat\exno
+\answer 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.
+
+\ddangerexercise What is the value of `@begingroup@ @save@ ?; \
+$(?,?)$ @endgroup@'\thinspace?
+\answer It's a nameless pair whose xpart and ypart are equal; thus it
+is essentially equivalent to `$"whatever"\ast(1,1)$'.
+
+\ddangerexercise According to exercise 10.\xwhat, the assignment
+`$x_3:="whatever"$' will make the numeric variable $x_3$ behave like new,
+without affecting other variables like $x_2$. Devise a similar stratagem
+that works for arrays of @picture@ variables.
+\answer `$v_3:=@begingroup@$ @save@ ?; @picture@ ?; ?\ @endgroup@'
+refreshes the picture variable~$v_3$ without changing other variables
+like~$v_2$. This construction works also for pairs, pens, strings, etc.
+
+\endchapter
+
+It is often difficult
+to account for some beginners grouping right away
+and others proving almost hopeless.
+\author A. G. ^{FULTON}, {\sl Notes on Rifle Shooting\/} (1913)
+ % according to OED Supplement, but this pamphlet has vanished from their files!
+
+\bigskip
+
+Rock bands prefer San Francisco groupies to New York groupies.
+\author ELLEN ^{WILLIS}, {\sl But Now I'm Gonna Move\/} (1971)
+ % New Yorker, 23 Oct 71, p170
+
+\eject
+ \beginchapter Chapter 18. Definitions\\(also called Macros)
+
+You can often save time writing \MF\ programs by letting single tokens
+stand for sequences of other tokens that are used repeatedly. For example,
+Appendix~B defines `$\ddashto$' to be an abbreviation for ^^{---}
+`$\to\tension"infinity"\to$', and this definition is preloaded as
+part of the plain \MF\ base. Programs that use such definitions are not
+only easier to write, they're also easier to read. But Appendix~B
+doesn't contain every definition that every programmer might want;
+the present chapter therefore explains how you can make ^{definitions}
+of your own.
+
+In the simplest case, you just say
+\begindisplay
+@def@ \<symbolic token> $=$ \<replacement text> @enddef@
+\enddisplay
+and the symbolic token will henceforth expand into the tokens of the
+replacement text. For example, Appendix~B says
+\begintt
+def --- = ..tension infinity.. enddef;
+\endtt
+it makes `$z_1\ddashto z_2$' become `$z_1\to\tension"infinity"\to z_2$'.
+The ^{replacement text} can be any sequence of tokens not including
+`@enddef@\kern1pt'; or it can include entire subdefinitions like
+`@def@~$\ldots$~@enddef@\kern1pt', according to certain rules
+that we shall explain later.
+
+Definitions get more interesting when they include {\sl^{parameters}},
+which are replaced by {\sl^{arguments}\/} when the definition is expanded.
+For example, Appendix~B also says
+\begintt
+def rotatedaround(expr z,theta) =
+ shifted -z rotated theta shifted z enddef;
+\endtt
+this means that an expression like `$z_1$ ^{rotatedaround}$\,(z_2,30)$' will
+expand into `$z_1$ shifted~$-z_2$ rotated~30 shifted~$z_2$'.
+
+The parameters `|z|' and `|theta|' in this definition could have been any
+symbolic tokens whatever; there's no connection between them and
+appearances of `|z|' and `|theta|' outside the definition. \ (For example,
+`|z|'~would ordinarily stand for `|(x,y)|', but it's just a simple token
+here.) \ The definition could even have been written with ``primitive''
+tokens as parameters, like
+\begintt
+def rotatedaround(expr;,+) =
+ shifted-; rotated+shifted; enddef;
+\endtt
+the effect would be exactly the same. \ (Of course, there's no point in
+doing such a thing unless you are purposely trying to make your
+definition inscrutable.)
+
+When `|rotatedaround|' is used, the arguments that are substituted for |z|
+and |theta| are first evaluated and put into ``^{capsules},'' so that they
+will behave like primary expressions. Thus, for example, `$z_1$
+rotatedaround$\,(z_2+z_3,30)$' will not expand into `$z_1$ shifted~$-z_2+z_3$
+rotated~30 shifted~$z_2+z_3$'---which means something entirely different---but
+rather into `$z_1$ shifted~$-\alpha$ rotated~30 shifted~$\alpha$', where
+$\alpha$ is a nameless internal variable that contains the value of
+$z_2+z_3$.
+
+\danger A capsule value cannot be changed, so an @expr@ parameter should not
+^^{:=} appear at the left of the ^{assignment} operator `$:=$'.
+
+\danger Macros are great when they work, but complicated macros sometimes
+surprise their creators. \MF\ provides ``tracing'' facilities so that you
+can see what the computer thinks it's doing, when you're trying to
+diagnose the reasons for unexpected behavior. If you say
+`^"tracingmacros"$\null:=1$', the transcript file of your run will record
+every macro that is subsequently expanded, followed by the values of its
+arguments as soon as they have been computed.
+For example, `rotatedaround$\,("up",30)$' might produce the
+^^|EXPR0| following lines of diagnostic information:
+\begintt
+rotatedaround(EXPR0)(EXPR1)->shifted-(EXPR0)rotated(EXPR1)sh
+ifted(EXPR0)
+(EXPR0)<-(0,1)
+(EXPR1)<-30
+\endtt
+
+\danger Here's another example from Appendix B\null. It illustrates the
+usefulness of ^{group expressions} in macro definitions:
+\begindisplay
+@def@ ^{reflectedabout}$\,(@expr@\ p,q)$ $=$\cr
+\quad transformed @begingroup@\cr
+\qquad ^@save@ $T$; \ ^@transform@ $T$;\cr
+\qquad $p$ transformed $T$ $=$ $p$;\cr
+\qquad $q$ transformed $T$ $=$ $q$;\cr
+\qquad ^{xxpart} $T$ $=$ $-$^{yypart} $T$;\cr
+\qquad ^{xypart} $T$ $=$ ^{yxpart} $T$;\cr
+\qquad $T$ @endgroup@ @enddef@;\cr
+\enddisplay
+thus a new transform, $T$, is computed in the midst of another expression,
+and the macro `reflectedabout($p,q$)' essentially expands into
+`transformed $T$'.
+
+Some macros, like `rotatedaround', are meant for general-purpose use.
+But it's also convenient to write ^{special-purpose macros} that simplify
+the development of particular typefaces. For example, let's consider the
+\MF\ logo from this standpoint. The program for `{\manual E}' in
+Chapter~11 starts with
+\begintt
+beginchar("E",14u#+2s#,ht#,0); pickup logo_pen;
+\endtt
+and the programs for `{\manual M}', `\kern1pt{\manual T}\kern1pt',
+etc., all have almost the same beginning. Therefore we might as
+well put the following definition near the top of the file |logo.mf|:
+\begintt
+def beginlogochar(expr code, unit_width) =
+ beginchar(code,unit_width*u#+2s#,ht#,0);
+ pickup logo_pen enddef;
+\endtt
+Then we can start the `{\manual E}' by saying simply ^^|beginlogochar|
+\begintt
+beginlogochar("E",14);
+\endtt
+similar simplifications apply to all seven letters. Notice from
+this example that macros can be used inside macros (since `|beginchar|'
+and `|pickup|' are themselves macros, defined in Appendix~B\null); once you
+have defined a macro, you have essentially extended the \MF\ language.
+Notice also that ^@expr@ parameters can be expressions of any type;
+for example, |"E"| is a string, and the first parameter of
+`rotatedaround' is a pair.
+
+\decreasehsize 48mm
+Chapter 11 didn't give the programs for `{\manual A}' or `{\manual O}'.
+\rightfig 18a ({240\apspix} x {216\apspix}) ^15pt
+It turns out that those programs can be simplified if we write
+them in terms of an auxiliary subroutine called `|super_half|'.
+For example, here is how the `{\manual O}' is made:
+\begintt
+beginlogochar("O",15);
+x1=x4=.5w; top y1=h+o; bot y4=-o;
+x2=w-x3=1.5u+s; y2=y3=barheight;
+super_half(2,1,3);
+super_half(2,4,3);
+labels(1,2,3,4); endchar;
+\endtt
+
+\restorehsize\medbreak\noindent
+The |super_half| routine is supposed to draw half of a ^{superellipse},
+through three points whose subscripts are specified.
+
+\restorehsize
+We could define |super_half| as a macro with three @expr@ parameters,
+referring to the first point as `|z[i]|', say; but there's a better way.
+Parameters to macros can be classified as suffixes, by saying ^@suffix@
+instead of @expr@. In this case
+the actual arguments may be any ^\<suffix>, i.e., any sequence of
+subscripts and tags that complete the name of a variable as explained
+in Chapter~7. Here's what |super_half| looks like, using this idea:
+\begintt
+def super_half(suffix i,j,k) =
+ draw z.i{0,y.j-y.i}
+ ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
+ ... z.j{x.k-x.i,0}
+ ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
+ ... z.k{0,y.k-y.j} enddef;
+\endtt
+
+\exercise Would the program for `{\manual O}' still work if the two calls of
+|super_half| had been `|super_half(3,1,2)|' and `|super_half(3,4,2)|'\thinspace?
+\answer Yes; the direction at |z.j| will be either "left" or "right".
+
+\exercise Guess the program for \MF's `{\manual A}', which has the
+same width as `{\manual O}'.
+\answer |beginlogochar("A",15);|
+\rightfig A18a ({240\apspix} x {216\apspix}) ^3pt \parbreak
+|x1=.5w;|\parbreak
+|x2=x4=leftstemloc;|\parbreak
+|x3=x5=w-x2;|\parbreak
+|top y1=h+o;|\parbreak
+|y2=y3=barheight;|\parbreak
+|bot y4=bot y5=-o;|\parbreak
+|draw z4--z2--z3--z5;|\parbreak
+|super_half(2,1,3);|\parbreak
+|labels(1,2,3,4,5);|\parbreak
+|endchar;|\par\smallskip\noindent
+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.
+
+\danger Besides parameters of type @expr@ and @suffix@, \MF\ also
+allows a third type called ^@text@. In this case the actual argument
+is any sequence of tokens, and this sequence is not evaluated
+beforehand; a text argument is simply copied in place of the
+corresponding parameter. This makes it possible to write macros that
+deal with lists of things. For example, Appendix~B's `@define\_pixels@'
+macro is defined thus:
+\begintt
+def define_pixels(text t) =
+ forsuffixes a=t: a := a# * hppp; endfor enddef;
+\endtt
+this means that `|define_pixels(em,cap)|' will expand into
+\begintt
+forsuffixes a=em,cap: a := a# * hppp; endfor
+\endtt
+which, in turn, expands into the tokens `|em|~|:=|~|em#|~|*|~|hppp;|
+|cap|~|:=|~|cap#|~|*|~|hppp;|' as we will see in Chapter~19.
+
+\danger Let's look now at a subroutine for drawing ^{serifs}, since
+this typifies the sort of special-purpose macro one expects to see
+in the design of a meta-typeface. Serifs can take many forms,
+so we must choose from myriads of possibilities. We shall consider
+two rather different approaches, one based on outline-filling and the
+other based on the use of a fixed pen nib. In both cases it will be
+necessary to omit some of the refinements that would be desirable
+in a complete typeface design, to keep the examples from
+getting too complicated.
+
+\danger
+\parshape 13
+3pc 13pc
+3pc 13pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 29pc
+Our first example is a serif routine that
+constructs six points $z_{\$a}$, $z_{\$b}$, \dots,~$z_{\$\mkern-1muf}$ around a
+\rightfig 18b (48mm x 40mm) ^26pt
+given triple of ``^{penpos}'' points $z_{\$l}$, $z_{\$}$, $z_{\$r}$; here
+\$ is a suffix that's a parameter to the "serif" macro. Other parameters
+are: "breadth", the distance between the parallel lines that run from
+$z_{\$l}$ to $z_{\$a}$ and from $z_{\$r}$ to $z_{\$\mkern-1muf}$; "theta", the
+direction angle of those two lines; "left\_jut", the distance from
+$z_{\$l}$ to $z_{\$b}$; and "right\_jut", the distance from $z_{\$r}$ to
+$z_{\$e}$. \ (The serif ``juts out'' by the amounts of the
+^{jut} parameters.) \ There's also a "serif\_edge" macro, which constructs
+the path shown. The routines refer to three variables that are assumed to
+apply to all serifs: "slab", the vertical distance from $z_{\$b}$~%
+and~$z_{\$e}$ to $z_{\$c}$~and~$z_{\$d}$; "bracket", the vertical distance
+from $z_{\$a}$~and~$z_{\$\mkern-1muf}$ to $z_{\$l}$~and~$z_{\$r}$; and
+"serif\_darkness", a fraction that controls how much of the triangular
+regions $(z_{\$a},z_{\$l},z_{\$b})$ and $(z_{\$\mkern-1muf},z_{\$r},z_{\$e})$
+^^{]]} will be filled in.
+\begindisplay
+@def@ "serif"\thinspace(@suffix@ \$)(@expr@
+ $"breadth","theta","left\_jut","right\_jut")=$\cr
+\quad $\penpos\$("breadth"/{\rm abs\,sind}\,"theta",0)$;\cr
+\quad $z_{\$a}-z_{\$l}=z_{\$\mkern-1muf}-z_{\$r}=
+ ("bracket"/{\rm abs\,sind}\,"theta")\ast {\rm dir}\,"theta"$;\cr
+\quad $y_{\$c}=y_{\$d}$; \ $y_{\$b}=y_{\$e}=y_\$$; \
+ $y_{\$b}-y_{\$c}=@if@\;"theta"<0:\;{-}\;@fi@\;"slab"$;\cr
+\quad $x_{\$b}=x_{\$c}=x_{\$l}-"left\_jut"$; \
+ $x_{\$d}=x_{\$e}=x_{\$r}+"right\_jut"$;\cr
+\quad @labels@$(\$a,\$b,\$c,\$d,\$e,\$\mkern-1muf)$ @enddef@;\cr
+\noalign{\smallskip}
+@def@ "serif\_edge" @suffix@ \$ =\cr
+\quad $\bigl("serif\_bracket"(\$a,\$l,\$b)\dashto z_{\$c}$\cr
+\qquad $\dashto z_{\$d}\dashto {\rm reverse}\,
+ "serif\_bracket"(\$\mkern-1muf,\$r,\$e)\bigr)$ @enddef@;\cr
+\noalign{\smallskip}
+@def@ "serif\_bracket"(@suffix@ $i,j,k$) $=$\cr
+\quad $\bigl(z.i\{z.j-z.i\}
+ \ldots"serif\_darkness"[z.j,.5[z.i,z.k]\,]\{z.k-z.i\}$\cr
+\qquad$\ldots z.k\{z.k-z.j\}\bigr)$ @enddef@;\cr
+\enddisplay
+
+\dangerexercise Under what circumstances will the "serif\_edge"
+go through points $z_{\$l}$ and $z_{\$r}$?
+\answer 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 `$\ldots$' triangle, ^^{...} which might not
+be numerically stable in the presence of rounding errors.)
+Another case, not really desirable, is $"left\_jut"="right\_jut"=0$.
+
+
+\dangerexercise Should this "serif" macro be used before
+points $z_{\$l}$, $z_\$$, and $z_{\$r}$ have been defined, or should those
+points be defined first?
+\answer That's a strange question. The "serif" routine includes a
+"penpos" that defines $z_{\$l}$, $z_\$$, and $z_{\$r}$ relative
+to each other, and it defines 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
+\MF\ has an easier job if it's done beforehand.
+
+\danger Here are two sample letters that show how these serif routines
+might be used. The programs assume that the font has several additional
+ad~hoc parameters: $u$,~a~unit of character width; "ht",~the character
+height; "thin" and "thick", the two stroke weights; and "jut", the amount
+by which serifs protrude on a ``normal'' letter like `H'.
+
+\begingroup\ninepoint\noindent
+\displayfig 18c (252\apspix)
+$$\halign to\hsize\bgroup\indent#\hfil\tabskip1em plus1fil minus1fil
+ &\tabskip0pt\hfil\%\ #\cr
+@beginchar@\kern1pt(|"A"|$,13u\0,ht\0,0)$;\cr
+$z_1=(.5w,1.05h)$;&top point\cr
+$x_{4l}=w-x_{5r}=u$; \ $y_{4l}=y_{5r}="slab"$;&bottom points\cr
+@numeric@ $"theta"[\,]$;\cr
+$"theta"_4={\rm angle}(z_1-z_{4l})$;&left stroke angle\cr
+$"theta"_5={\rm angle}(z_1-z_{5r})$;&right stroke angle\cr
+$"serif"(4,"thin","theta"_4,.6"jut","jut")$;&left serifs\cr
+$"serif"(5,"thick","theta"_5,"jut",.6"jut")$;&right serifs\cr
+$z_0=z_{4r}+"whatever"\ast{\rm dir}\,"theta"_4$\cr
+\qquad$=z_{5l}+"whatever"\ast{\rm dir}\,"theta"_5$;&inside top point\cr
+@fill@ $z_1\dashto "serif\_edge"_4\dashto z_0$&the left stroke\cr
+\qquad$\&\;z_0\dashto "serif\_edge"_5\dashto z_1\;\&\;\cycle$;&the
+ right stroke\cr
+$\penpos2("whatever","theta"_4)$;\cr
+$\penpos3("whatever","theta"_5)$;\cr
+$y_{2r}=y_{3r}=.5[y_4,y_0]$;&crossbar height\cr
+$y_{2l}=y_{3l}=y_{2r}-"thin"$;&crossbar thickness\cr
+$z_2="whatever"[z_1,z_{4r}]$;\cr
+$z_3="whatever"[z_1,z_{5l}]$;\cr
+@penstroke@ $z_{2e}\dashto z_{3e}$;&the crossbar\cr
+@penlabels@$(0,1,2,3,4,5)$; \ @endchar@;\cr
+\noalign{\medskip}
+@beginchar@\kern1pt(|"I"|$,6u\0,ht\0,0)$;\cr
+$x_1=x_2=.5w$;\cr
+$y_1=h-y_2$; \ $y_2="slab"$;\cr
+"serif"$(1,"thick",-90,1.1jut,1.1jut)$;&upper serifs\cr
+"serif"$(2,"thick",90,1.1jut,1.1jut)$;&lower serifs\cr
+@fill@ $"serif\_edge"_2\dashto{\rm reverse}\,"serif\_edge"_1\dashto\cycle$;
+ &the stroke\cr
+@penlabels@$(1,2)$; \ @endchar@;\cr
+\enddisplay
+The illustration was prepared with $"thin"=.5"pt"$, $"thick"=1.1"pt"$,
+$u=.6"pt"$, $"ht"=7"pt"$, $"slab"=.25"pt"$, $"jut"=.9"pt"$, $"bracket"="pt"$,
+and $"serif\_darkness"=1/3$.
+\par\endgroup
+
+\dangerexercise Could the equations defining $y_1$ and $y_2$ in the program
+for~|"I"| have been replaced by `$y_{1c}=h$' and `$y_{2c}=0$'?
+\answer Yes; see the previous exercise. \ (But in the program for |"A"|
+it's necessary to define $y_{4l}$ and $y_{5r}$, so that $"theta"_4$
+and~$"theta"_5$ can be calculated.)
+
+\dangerexercise Write the program for an |"H"| to go with these letters.
+\answer \rightfig A18b (48mm x 43mm) ^10pt
+@beginchar@\kern1pt(|"H"|$,13u\0,"ht"\0,0)$;\parbreak
+$x_1=x_2=x_5=3u$;\parbreak
+$x_3=x_4=x_6=w-x_1$;\parbreak
+$y_{1c}=y_{3c}=h$; \ $y_{2c}=y_{4c}=0$;\parbreak
+$"serif"(1,"thick",-90,"jut","jut")$;\parbreak
+$"serif"(2,"thick",90,"jut","jut")$;\parbreak
+$"serif"(3,"thick",-90,"jut","jut")$;\parbreak
+$"serif"(4,"thick",90,"jut","jut")$;\parbreak
+@fill@ $"serif\_edge"_2$\parbreak
+\quad$\dashto{\rm reverse}\,"serif\_edge"_1\dashto\cycle$;\parbreak
+@fill@ $"serif\_edge"_4$\parbreak
+\quad$\dashto{\rm reverse}\,"serif\_edge"_3\dashto\cycle$;\parbreak
+$\penpos5("thin",90)$; \ $\penpos6("thin",90)$;\parbreak
+$y_5=y_6=.52h$; \ @penstroke@ $z_{5e}\dashto z_{6e}$;\parbreak
+@penlabels@$(1,2,3,4,5,6)$; \ @endchar@.
+
+\ddanger A second approach to serifs can be based on the example at
+the end of Chapter~16. In this case we assume that "broad\_pen" is
+a `@pensquare@ xscaled~"px" yscaled~"py" rotated~"phi"' for
+some $"px">"py"$ and some small angle~"phi". Thicker strokes will
+be made by using this pen to fill a larger region; the serif routine
+is given the distance "xx" between $z_{\$l}$ and $z_{\$r}$.
+There's a pair variable called "dishing" that
+controls the curvature between $z_{\$c}$ and~$z_{\$d}$. Top and
+bottom serifs are similar, but they are sufficiently different that it's
+easier to write separate macros for each case.
+\begindisplay
+@def@ "bot\_serif"(@suffix@ \$)(@expr@ $"xx","theta",
+ "left\_jut","right\_jut")=$\cr
+\quad $\penpos\$("xx",0)$; \
+ $z_{\$a}-z_{\$l}=z_{\$\mkern-1muf}-z_{\$r}=
+ ("bracket"/{\rm abs\,sind\,}"theta")\ast{\rm dir}\,"theta"$;\cr
+\quad $y_{\$c}="top"\,y_{\$l}$; \ $y_{\$d}=y_{\$r}$; \
+ $x_{\$c}=x_{\$l}-"left\_jut"$; \ $x_{\$d}=x_{\$r}+"right\_jut"$;\cr
+\quad $z_{\$b}=z_{\$l}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$c}+"whatever"\ast{\rm dir}\,"phi"$;\cr
+\quad $z_{\$e}=z_{\$r}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$d}+"whatever"\ast{\rm dir}\,{-"phi"}$;\cr
+\quad @labels@$(\$a,\$b,\$c,\$d,\$e,\$\mkern-1muf)$ @enddef@;\cr
+\noalign{\smallskip}
+@def@ "bot\_serif\_edge" @suffix@ \$ $=$\cr
+\quad $\bigl(z_{\$a}\to\controls z_{\$b}\to z_{\$c}$\cr
+\qquad $\dashto("flex"(z_{\$c},.5[z_{\$c},z_{\$d}]+"dishing",
+ z_{\$d}))$ shifted $(0,-"epsilon")$\cr
+\qquad $\dashto z_{\$d}\to\controls z_{\$e}\to z_{\$\mkern-1muf}
+ \bigr)$ @enddef@;\cr
+\enddisplay
+\displayfig 18d (272\apspix)
+\begindisplay
+@beginchar@\kern1pt(|"A"|$,13u\0,"ht"\0,0)$; \ @pickup@ "broad\_pen";\cr
+$z_1=(.5w,"top"\,h)$; \ $"lft"\,x_{4l}=w-"rt"\,x_{5r}=1.2u$; \
+ $y_{4l}=y_{5r}=0$;\cr
+@numeric@ $"theta"[\,]$; \ $"theta"_4={\rm angle}(z_1-z_{4l})$; \
+ $"theta"_5={\rm angle}(z_1-z_{5r})$;\cr
+@numeric@ "xxx";
+\hbox spread-8pt{%
+$"px"\ast{\rm sind}("theta"_5-"phi")+"xxx"\ast{\rm sind}\,"theta"_5
+ = "px"\ast{\rm cosd}\,"phi"+"xx"$};\cr
+$"bot\_serif"(4,0,"theta"_4,.8"jut",.8"jut")$; \
+$"bot\_serif"(5,"xxx","theta"_5,.6"jut",.8"jut")$;\cr
+$z_0=z_{4r}+"whatever"\ast{\rm dir}\,"theta"_4
+ =z_{5l}+"whatever"\ast{\rm dir}\,"theta"_5$;\cr
+@filldraw@ $z_1\dashto "bot\_serif\_edge"_4
+ \dashto z_0\;\&\;z_0\dashto "bot\_serif\_edge"_5
+ \dashto z_1\;\&\;\cycle$;\cr
+$"top"\,y_2="top"\,y_3=.45"bot"\,y_0$; \
+ $z_2="whatever"[z_1,z_{4r}]$; \ $z_3="whatever"[z_1,z_{5l}]$;\cr
+@draw@ $z_2\dashto z_3$; \ @penlabels@$(0,1,2,3,4,5)$; @endchar@;\cr
+\noalign{\medskip}
+@beginchar@\kern1pt(|"I"|$,6u\0,"ht"\0,0)$; \ @pickup@ "broad\_pen";\cr
+$x_1=x_2=.5w$; \ $y_1=h$; \ $y_2=0$;\cr
+$"top\_serif"(1,"xx",-90,1.1"jut",1.1"jut")$; \
+$"bot\_serif"(2,"xx",90,1.1"jut",1.1"jut")$;\cr
+@filldraw@ $"bot\_serif\_edge"_2\dashto
+ {\rm reverse}\,"top\_serif\_edge"_1\dashto\cycle$;\cr
+@penlabels@$(1,2)$; \ @endchar@;\cr
+\enddisplay
+In the illustration, $"px"=.8"pt"$, $"py"=.2"pt"$, $"phi"=20$,
+$"xx"=.3"pt"$, $u=.6"pt"$, $"ht"=7"pt"$, $"jut"=.9"pt"$, $"bracket"="pt"$,
+and $"dishing"=(.25"pt",0)$ rotated~20.
+
+\ddangerexercise Write the missing code for "top\_serif" and
+"top\_serif\_edge".
+\answer @def@ "top\_serif"(@suffix@ \$)(@expr@ $"xx","theta",
+ "left\_jut","right\_jut")=$\parbreak
+\quad $\penpos\$("xx",0)$; \
+$z_{\$a}-z_{\$l}=z_{\$\mkern-1muf}-z_{\$r}=
+ ("bracket"/{\rm abs\,sind\,}"theta")\ast{\rm dir}\,"theta"$;\parbreak
+\quad $y_{\$c}=y_{\$d}=y_\$$; \
+ $x_{\$c}=x_{\$l}-"left\_jut"$; \ $x_{\$d}=x_{\$r}+"right\_jut"$;\parbreak
+\quad $z_{\$b}=z_{\$l}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$c}+"whatever"\ast{\rm dir}\,{-"phi"}$;\parbreak
+\quad $z_{\$e}=z_{\$r}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$d}+"whatever"\ast{\rm dir}\,"phi"$;\parbreak
+\quad @labels@$(\$a,\$b,\$c,\$d,\$e,\$\mkern-1muf)$ @enddef@;\par
+\smallskip\indent
+@def@ "top\_serif\_edge" @suffix@ \$ $=$\parbreak
+\quad $\bigl(z_{\$a}\to\controls z_{\$b}\to z_{\$c}$\parbreak
+\qquad $\dashto("flex"(z_{\$c},.5[z_{\$c},z_{\$d}]-"dishing",
+ z_{\$d}))$ shifted $(0,+"epsilon")$\parbreak
+\qquad $\dashto z_{\$d}\to\controls z_{\$e}\to z_{\$\mkern-1muf}
+ \bigr)$ @enddef@;
+
+\ddangerexercise (For mathematicians.) \
+Explain the equation for "xxx" in the program for~|"A"|.
+\answer Assuming that $"py"=0$, the effective right stroke weight would be
+$"px"\cdot\sin(\theta_5-\phi)$ if it were drawn with one stroke of "broad\_pen",
+and $"xxx"\cdot\sin\theta_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 ($\theta=90^\circ$), 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.)
+
+\ddangerexercise Write the program for an |"H"| to go with these letters.
+\answer \rightfig A18c (48mm x 45mm) ^10pt
+@beginchar@\kern1pt(|"H"|$,13u\0,"ht"\0,0)$;\parbreak
+$x_1=x_2=x_5=3u$;\parbreak
+$x_3=x_4=x_6=w-x_1$;\parbreak
+$y_1=y_3=h$; \ $y_2=y_4=0$;\parbreak
+$"top\_serif"(1,"xx",-90,"jut","jut")$;\parbreak
+$"bot\_serif"(2,"xx",90,"jut","jut")$;\parbreak
+$"top\_serif"(3,"xx",-90,"jut","jut")$;\parbreak
+$"bot\_serif"(4,"xx",90,"jut","jut")$;\parbreak
+@filldraw@ $"bot\_serif\_edge"_2$\parbreak
+\quad$\dashto{\rm reverse}\,"top\_serif\_edge"_1\dashto\cycle$;\parbreak
+@fill@ $"bot\_serif\_edge"_4$\parbreak
+\quad$\dashto{\rm reverse}\,"top\_serif\_edge"_3\dashto\cycle$;\parbreak
+$y_5=y_6=.52h$; \ @draw@ $z_5\dashto z_6$;\parbreak
+@penlabels@$(1,2,3,4,5,6)$; \ @endchar@.
+
+\danger A close look at the "serif\_edge" routines in these examples
+will reveal that some parentheses are curiously lacking: We said
+`@def@ "serif\_edge" @suffix@~\$' instead of
+`@def@ "serif\_edge"(@suffix@~\$)', and we used the macro by saying
+`$"serif\_edge"_5$' instead of
+`$"serif\_edge"(5)$'. The reason is that \MF\ allows the final parameter
+of a macro to be without delimiters; this is something that could not
+have been guessed from a study of previous examples. It is time now
+to stop looking at specific cases and to start examining the complete
+set of rules for macro definitions. Here is the syntax:
+\beginsyntax
+<definition>\is<definition heading><is><replacement text>[enddef]
+<is>\is[=]\alt[:=]
+<definition heading>\is[def]<symbolic token><parameter heading>
+ \alt<vardef heading>
+ \alt<leveldef heading>
+<parameter heading>\is<delimited parameters><undelimited parameters>
+<delimited parameters>\is<empty>
+ \alt<delimited parameters>[(]<parameter type><parameter tokens>[)]
+<parameter type>\is[expr]
+ \alt[suffix]
+ \alt[text]
+<parameter tokens>\is<symbolic token>
+ \alt<parameter tokens>[,]<symbolic token>
+<undelimited parameters>\is<empty>
+ \alt[primary]<symbolic token>
+ \alt[secondary]<symbolic token>
+ \alt[tertiary]<symbolic token>
+ \alt[expr]<symbolic token>
+ \alt[expr]<symbolic token>[of]<symbolic token>
+ \alt[suffix]<symbolic token>
+ \alt[text]<symbolic token>
+\endsyntax
+(We'll discuss ^\<vardef heading> and ^\<leveldef heading> in Chapter~20.)
+\ The basic idea is that we name the macro to be defined, then we name
+zero or more delimited parameters (i.e., parameters in parentheses),
+then we name zero or more undelimited parameters. Then comes an `$=$'~sign,
+followed by the replacement text, and @enddef@. The `$=$'~sign might also
+be~`$:=$'\thinspace; both mean the same thing.
+
+\danger Delimited parameters are of type @expr@, @suffix@, or @text@;
+two or more parameters of the same type may be listed together, separated
+by commas. For example, `(@expr@~$a,b$)' means exactly the same thing as
+`(@expr@~$a$)(@expr@~$b$)'. Undelimited parameters have eight possible
+forms, as shown in the syntax.
+
+\ninepoint % all dangerous from here on
+
+\danger The \<replacement text> is simply filed away for future use,
+not interpreted, when \MF\ reads a definition. But a few tokens are
+treated specially:\enddanger\nobreak
+
+\medskip
+\item\bull @def@, ^@vardef@, ^@primarydef@, ^@secondarydef@, and
+^@tertiarydef@ are considered to introduce definitions inside definitions.
+
+\smallskip
+\item\bull @enddef@ ends the replacement text, unless it matches a
+previous @def@-like token (as listed in the preceding rule).
+
+\smallskip
+\item\bull Each \<symbolic token> that stands for a parameter, by
+virtue of its appearance in the \<parameter heading> or \<leveldef
+heading>, is changed to a special in\-ternal ``parameter
+token'' wherever it occurs in the
+replacement text. Whenever this special token is subsequently encountered,
+\MF\ will substitute the appropriate argument.
+
+\smallskip
+\item\bull ^@quote@ disables any special interpretation of the immediately
+following token. A~`@quote@' doesn't survive in the replacement text
+(unless, of course, it has been quoted).
+
+\dangerexercise Check your understanding of these rules by
+figuring out what the replacement text is, in the following weird definition:
+\begintt
+def foo(text t) expr e of p :=
+ def t = e enddef; quote def quote t = p enddef
+\endtt
+\answer The replacement text contains ten tokens,
+\begindisplay
+\ttok{def}\quad\<t>\quad\ttok{=}\quad\<e>\quad\ttok{enddef}
+\quad\ttok{;}\quad\ttok{def}\quad\ttok{t}\quad\ttok{=}\quad\<p>
+\enddisplay
+where \<t>, \<e>, and \<p> are placeholders for argument insertion.
+When this macro is expanded with $"tracingmacros">0$, \MF\ will type
+\begintt
+foo(TEXT0)<expr>of<primary>->def(TEXT0)=(EXPR1)enddef;def.t=(EXPR2)
+\endtt
+followed by the arguments |(TEXT0)|, |(EXPR1)|, and |(EXPR2)|.
+
+\danger \MF\ does not expand macros when it reads a \<definition>;
+but at almost all other times it will replace a defined token by the
+corresponding replacement text, after finding all the arguments.
+The replacement text will then be read as if it had been present
+in the program all along.
+
+\danger How does \MF\ determine the arguments to a macro? Well,
+it knows what kinds of arguments to expect, based on the parameter
+heading. Let's consider delimited arguments first:\enddanger\nobreak
+
+\medskip
+\item\bull A delimited
+@expr@ argument should be of the form `(\<expression>)'; the expression
+is evaluated and put into a special ``^{capsule}'' token that will be
+substituted for the parameter wherever it appears in the replacement text.
+
+\smallskip
+\item\bull A delimited @suffix@ argument should be of the form
+`(\<suffix>)'; subscripts that occur in the suffix are evaluated
+and replaced by numeric tokens. The result is a list of zero or more
+tokens that will be substituted for the parameter wherever it appears
+in the replacement text.
+
+\smallskip
+\item\bull A delimited @text@ argument should be of the form
+`(\<text>)', where \<text> is any sequence of tokens that is balanced
+with respect to the delimiters surrounding it. This sequence of tokens
+will be substituted for the parameter wherever it appears in the
+replacement text.
+
+\smallskip
+\item\bull When there are two or more delimited parameters, you can
+separate the arguments by commas instead of putting parentheses around
+each one. For example, three delimited arguments could be written
+either as `$(a)(b)(c)$' or `$(a,b)(c)$' or `$(a)(b,c)$' or `$(a,b,c)$'.
+However, this abbreviation doesn't work after text arguments, which
+must be followed by~`)' because text arguments can include commas.
+
+\ddanger Chapter 8 points out that you can use other ^{delimiters}
+besides parentheses. In general, a comma following a delimited
+@expr@ or @suffix@ argument is equivalent to two tokens `)\thinspace(',
+corresponding to whatever delimiters enclose that comma.
+
+\ddangerexercise After `|def| |f(expr| |a)(text| |b,c)=...enddef|'
+and `|delimiters|~|{{|~|}}|', what are the arguments in
+`|f{{x,(,}}((}}))|'?
+\answer According to the rule just stated, the first comma is an
+abbreviation for `|}}|~|{{|'. Hence the first argument is a capsule
+containing the value of~$x$; the second is the text `|(,|'\thinspace;
+the third is the text `|(}})|'.
+
+\danger The rules for undelimited arguments are similar. An
+undelimited @primary@, @secondary@, @tertiary@, or @expr@ is the
+longest syntactically correct ^\<primary>, ^\<secondary>, ^\<tertiary>,
+or ^\<expression> that immediately follows the delimited arguments.
+An undelimited `@expr@~$x$~^{of}~$y$' specifies two arguments, found
+by taking the longest syntactically correct \<expression>~of~\<primary>.
+In each of these cases, the expression might also be preceded by an
+optional `^{=}' or~`^{:=}'. An undelimited @suffix@ is the longest
+\<suffix> that immediately follows the delimited arguments; \MF\ also
+allows `(\<suffix>)' in this case, but not `=\<suffix>' or `:=\<suffix>'.
+An undelimited @text@ essentially runs to the end of the current
+statement; more precisely, it runs to the first `;'\ or `^@endgroup@' or
+`^@end@' that is not part of a ^{group} within the argument.
+
+\danger Appendix B contains lots of macros that illustrate these
+rules. For example,
+\begindisplay
+@def@ ^@fill@ @expr@ $c$ $=$ @addto@ "currentpicture" @contour@ $c$ @enddef@;\cr
+@def@ ^@erase@ @text@ $t$ $=$ @cullit@; \ $t$ @withweight@ $-1$;
+ @cullit@ @enddef@;\cr
+\enddisplay
+these are slight simplifications of the real definitions, but they retain the
+basic ideas. The command `@erase@~@fill@~$p$' causes `@fill@~$p$' to be
+the @text@ argument to~@erase@, after which `$p$' becomes the @expr@
+argument to~@fill@.
+
+\ddangerexercise The `@pickup@' macro in Appendix B starts with
+`@def@~@pickup@~@secondary@~$q$'; why is the argument a secondary
+instead of an expression?
+\answer This snares ^{future pen}s before they're converted to pens, because
+@pickup@ wants to yscale by "aspect\_ratio" before ellipses change to
+polygons.
+
+\ddangerexercise Explain why the following `^"hide"' macro allows you to
+hide any sequence of statements in the midst of an expression:
+\begindisplay
+@def@ "hide"(@text@ $t)="gobble"@begingroup@\,t;$ @endgroup@ @enddef@;\cr
+@def@ "gobble" @primary@ $g=@enddef@$;\cr
+\enddisplay
+\answer The construction `"hide"\thinspace(\<statement list>)' expands into
+`"gobble" @begingroup@ \<statement list>; @endgroup@', so the
+argument to "gobble" must be evaluated. The @begingroup@ causes \MF\
+to start executing statements. When that has been done, the final
+statement turns out to be \<empty>, so the argument to "gobble"
+turns out to be a ^{vacuous} expression (cf.\ Chapter~25). Finally,
+"gobble"'s replacement text is empty, so the hidden text has indeed
+disappeared. \ (The "hide" macro in Appendix~B is actually a bit
+more efficient, but a bit trickier.)
+
+\endchapter
+
+DEFINI\/$'$\kern-.5ptTION, {\rm s. \ [definitio}, Latin.{\rm]}
+1. A short description of a thing by its properties.
+\author SAMUEL ^{JOHNSON}, {\sl A Dictionary of the English Language\/} (1755)
+
+\bigskip
+
+DEFINI\/$''$\kern-.5ptTION, {\rm n. \ [{\sl L.} definitio}. See\/ {\rm Define.]}
+1. A brief description of a thing by its properties;
+as a\/ {\rm definition} \kern-.5pt of wit or of a circle.
+\author NOAH~^{WEBSTER}, {\sl An~American~%
+ Dictionary~of~the~English~Language\/}~(1828)
+
+\eject
+ \beginchapter Chapter 19. Conditions\\and Loops
+
+If decisions never had to be made, life would be much easier, and so would
+programming. But sometimes it is necessary to choose between alternatives,
+and \MF\ allows programs to take different paths depending on the circumstances.
+You just say something like
+\begindisplay
+@if@ not "decisions": \ $"life":="programming":="easier"("much")$\cr
+@elseif@ $"choice"=a$: \ "program\_a"\cr
+@else@: \ "program\_b" \ @fi@\cr
+\enddisplay
+which reduces, for example, to `"program\_b"' if and only if
+$"decisions"=@true@$ and $"choice"\ne a$. The normal left-to-right
+order of program interpretation can also be modified by specifying
+``^{loops},'' which tell the computer to read certain tokens repeatedly,
+with minor variations, until some ^{condition} becomes true. We have
+seen many examples of these mechanisms already; the purpose of the
+present chapter is to discuss the entire range of possibilities.
+
+\MF's conditions and loops are different from those in most other
+programming languages, because the conditional or iterated code does
+not have to fit into the syntactic structure. For example, you can
+write strange things like
+\begintt
+p = (if b: 0,0)..(1,5 else: u,v fi)
+\endtt
+where the conditional text `$0,0)\to(1,5$' makes no sense by itself,
+although it becomes meaningful when read in context. In this respect
+conditions and loops behave like macros. They specify rules of
+token transformation that can be said to take place in \MF's ``^{mouth}''
+before the tokens are actually digested in the computer's ``^{stomach}.''
+
+The first conditional example above has three alternatives, in the form
+\begindisplay
+@if@ \<boolean$_1$>: \<text$_1$> \
+@elseif@ \<boolean$_2$>: \<text$_2$> \
+@else@: \<text$_3$> \ @fi@
+\enddisplay
+and the second example has just two; there can be any number of
+`^@elseif@\kern1pt' clauses before `^@else@:'. Only one of the conditional
+texts will survive, namely the first one whose condition is true;
+`@else@:'\ is always true. You can also omit `@else@:'\
+entirely, in which case `@else@:\thinspace\<empty>' is implied just before
+the closing `^@fi@'. For example, plain \MF's @mode\_setup@ routine
+includes the conditional~command
+\begindisplay
+@if@ unknown "mag": \ $"mag":=1$; \ @fi@
+\enddisplay
+whose effect is to set "mag" equal to 1 if it hasn't already received
+a value; in this case there's only one alternative.
+
+\exercise Would it be wrong to put the `;' after the `@fi@' in the example
+just given?
+\answer Then \MF's ``stomach'' would see `;' if "mag" is known, but there
+would be no change if "mag" is unknown. An extra semicolon is harmless,
+since \MF\ statements can be \<empty>. But it's wise to get in the habit
+of putting `;' before @fi@, because it saves a wee bit of time and because
+`;' definitely belongs before ^@endfor@.
+
+\danger The informal rules just stated can, of course, be expressed more
+formally as rules of syntax:
+\beginsyntax
+<condition>\is[if]<boolean expression>[:]<conditional text><alternatives>[fi]
+<alternatives>\is<empty>
+ \alt[else][:]<conditional text>
+ \alt[elseif]<boolean expression>[:]<conditional text><alternatives>
+\endsyntax
+Every conditional construction begins with `^@if@\kern1pt' and ends with
+`@fi@'. The conditional texts are any sequences of tokens that are
+balanced with respect to `@if@\kern1pt' and~`@fi@'; furthermore,
+`@elseif@\kern1pt' and `@else@' can occur in a conditional text only when
+enclosed by `@if@\kern1pt' and~`@fi@'.
+
+\danger Each `@if@\kern1pt' and `@elseif@\kern1pt' must be followed by a
+\<boolean expression>, i.e., by an expression whose value is either
+`@true@' or `@false@'. ^{Boolean expressions} are named after George
+^{Boole}, the founder of algebraic approaches to logic. Chapter~7 points
+out that variables can be of type ^@boolean@, and numerous examples of
+boolean expressions appear in Chapter~8. It's time now to be more
+systematic, so that we will know the facts about boolean expressions just
+as we have become well-versed in numeric expressions, pair expressions,
+picture expressions, path expressions, transform expressions, and pen
+expressions. Here are the relevant syntax rules:
+\beginsyntax
+<boolean primary>\is<boolean variable>
+ \alt[true]\alt[false]
+ \alt[(]<boolean expression>[)]
+ \alt[begingroup]<statement list><boolean expression>[endgroup]
+ \alt[known]<primary>\alt[unknown]<primary>
+ \alt<type><primary>\alt[cycle]<primary>
+ \alt[odd]<numeric primary>
+ \alt[not]<boolean primary>
+<boolean secondary>\is<boolean primary>
+ \alt<boolean secondary>[and]<boolean primary>
+<boolean tertiary>\is<boolean secondary>
+ \alt<boolean tertiary>[or]<boolean secondary>
+<boolean expression>\is<boolean tertiary>
+ \alt<numeric expression><relation><numeric tertiary>
+ \alt<pair expression><relation><pair tertiary>
+ \alt<transform expression><relation><transform tertiary>
+ \alt<boolean expression><relation><boolean tertiary>
+ \alt<string expression><relation><string tertiary>
+<relation>\is[\char'74]\alt[\char'74=]\alt[>]\alt[>=]\alt[=]\alt[\char'74>]
+\endsyntax
+Most of these operations were already explained in Chapter~8, so it's only
+necessary to mention the more subtle points now. A ^\<primary> of any
+type can be tested to see whether it has a specific type, and whether it
+has a known or unknown value based on the equations so far. In these tests,
+a ^\<future pen primary> is considered to be of type ^@pen@. The test
+`cycle~$p$' is true if and only if $p$~is a cyclic path. The `odd' function
+first rounds its argument to an integer, then tests to see if the integer
+is odd. The `not' function changes true to false and vice versa. The `and'
+function yields true only if both arguments are true; the `or' function
+yields true unless both arguments are false. Relations on pairs, transforms,
+or strings are decided by the first unequal component from left to right.
+\ (A ^{transform} is considered to be a 6-tuple as in Chapter~15.) \
+
+\dangerexercise What do you think: Is @false@ $>$ @true@?
+\answer No; that would be shocking.
+
+\dangerexercise Could `(odd $n$) and not (odd $-n$)' possibly be true?
+\answer Yes, if and only if $n-{1\over2}$ is a nonnegative even integer.
+\ (Because ambiguous values are rounded upwards.)
+
+\dangerexercise Could `(cycle $p$) and not (known $p$)' possibly be true?
+\answer No.
+
+\dangerexercise Define an `even' macro such that `even~$n$' is true if
+and only if round$(n)$ is an even integer. \ [{\sl Hint:\/} There's a
+slick answer.]
+\answer @def@ even $=$ not odd @enddef@.
+
+\ddanger Boolean expressions beginning with a ^\<type> should not come
+at the very beginning of a statement, because \MF\ will think that
+a ^\<declaration> is coming up instead of an \<expression>. Thus, for
+example, if $b$~is a boolean variable, the equation `$@path@\,p=b$'
+should be rewritten either as `$b=@path@\,p$' or as `$(@path@\,p)=b$'.
+
+\ddanger A boolean expression like `$x=y$' that involves the ^{equality}
+relation looks very much like an ^{equation}. \MF\ will consider `$=$'
+to be a \<relation> unless the expression to its left occurs at the
+very beginning of a ^\<statement> or the very beginning of a ^\<right-hand
+side>. If you want to change an equation into a relation,
+just insert parentheses, as in `$(x=y)=b$' or `$b=(x=y)$'.
+
+\ddanger After a ^\<path join>, the token `^{cycle}' is not considered
+to be the beginning of a \<boolean primary>. \ (Cf.\ Chapter~14.)
+
+\ddanger The boolean expression `^@path@ $((0,0))$' is false, even
+though `$((0,0))$' meets Chapter~14's syntax rules for
+\<path primary>, via (\<path expression>) and
+(\<path tertiary>) and
+(\<pair tertiary>). A ^{pair expression} is not considered to be
+of type @path@ unless the path interpretation is mandatory.
+
+\ddangerexercise Evaluate `length $((3,4))$' and `length $((3,4)\{0,0\})$'
+and `length reverse~$(3,4)$'.
+\answer The first is~5, because the pair is not considered to be a path.
+The second and third are~0, because the pair is forced to become a path.
+
+OK, that covers all there is to be said about conditions. What about
+loops? It's easiest to explain loops by giving the syntax first:
+\beginsyntax
+<loop>\is<loop header>[:]<loop text>[endfor]
+<loop header>\is[for]<symbolic token><is><for list>
+ \alt[for]<symbolic token><is><progression>
+ \alt[forsuffixes]<symbolic token><is><suffix list>
+ \alt[forever]
+<is>\is[=]\alt[:=]
+<for list>\is<expression>\alt<empty>
+ \alt<for list>[,]<expression>\alt<for list>[,]<empty>
+<suffix list>\is<suffix>
+ \alt<suffix list>[,]<suffix>
+<progression>\is<initial value>[step]<step size>[until]<limit value>
+<initial value>\is<numeric expression>
+<step size>\is<numeric expression>
+<limit value>\is<numeric expression>
+<exit clause>\is[exitif]<boolean expression>[;]
+\endsyntax
+As in macro definitions, `$=$' and `$:=$' are interchangeable here.
+
+This syntax shows that loops can be of four kinds, which we might
+indicate schematically as follows:
+\begindisplay
+@for@ $x=\epsilon_1,\epsilon_2,\epsilon_3$: text($x$) @endfor@\cr
+\noalign{\vskip 1pt plus 1pt}
+@for@ $x=\nu_1$ @step@ $\nu_2$ @until@ $\nu_3$: text($x$) @endfor@\cr
+\noalign{\vskip 1pt plus 1pt}
+@forsuffixes@ $s=\sigma_1,\sigma_2,\sigma_3$: text($s$) @endfor@\cr
+\noalign{\vskip 1pt plus 1pt}
+@forever@: text @endfor@\cr
+\enddisplay
+The first case expands to
+`text($\epsilon_1$) text($\epsilon_2$) text($\epsilon_3$)'; the
+$\epsilon$'s here are expressions of any type, not necessarily ``known,''
+and they are evaluated and put into ^{capsules} before being substituted
+for~$x$. The $\epsilon$'s might also be empty, in which case
+text($\epsilon$) is omitted.
+The second case is more complicated, and it will be explained carefully
+below; simple cases like `1~@step@~2 @until@~7' are equivalent to
+short lists like `$1,3,5,7$'. The third case expands to
+`text($\sigma_1$) text($\sigma_2$) text($\sigma_3$)'; the $\sigma$'s here
+are arbitrary suffixes (possibly empty), in which subscripts will have been
+evaluated and changed to numeric tokens before being substituted for~$s$.
+The final case expands into the sequence `text~text~text~$\ldots$',
+ad~infinitum; there's an escape from this (and from the other three kinds
+of loop) if an \<exit clause> appears in the text, as explained below.
+
+Notice that if the loop text is a single statement that's supposed to
+be repeated several times, you should put a `^{;}' just before the
+@endfor@, not just after it; \MF's loops do not insert ^{semicolons}
+automatically, because they are intended to be used in the midst of
+expressions as well as with statements that are being iterated.
+
+Plain \MF\ defines `^@upto@' as an abbreviation for `@step@~1~@until@',
+and `^@downto@' as an abbreviation for `@step@~$-1$~@until@'. Therefore
+you can say, e.g., `\thinspace@for@ $x=1$ @upto@~9:\thinspace' instead of
+`\thinspace@for@ $x=1,2,3,4,5,6,7,8,9$:\thinspace'.
+
+\danger When you say `@for@ $x=\nu_1$ @step@ $\nu_2$ @until@~$\nu_3$',
+\MF\ evaluates the three numeric expressions, which must have known values.
+Then it reads the loop text. If $\nu_2>0$ and $\nu_1>\nu_3$, or if
+$\nu_2<0$ and $\nu_1<\nu_3$, the loop is not performed at all. Otherwise
+text($\nu_1$) is performed, $\nu_1$ is replaced by $\nu_1+\nu_2$, and
+the same process is repeated with the new value of $\nu_1$.
+
+\dangerexercise Read the rules in the previous paragraph carefully, then
+explain for what values of~$x$ the loop is performed if you say
+(a)~`\thinspace@for@~$x=1$ @step@~2 @until@~0'\thinspace. \
+(b)~`\thinspace@for@~$x=1$ @step@~$-2$ @until@~0\thinspace'. \
+(c)~`\thinspace@for@~$x=1$ @step@~0 @until@~0\thinspace'. \
+(d)~`\thinspace@for@~$x=0$ @step@~.1 @until@~1\thinspace'.
+\answer (a) The loop text is never executed. \
+(b)~It's executed only once, for $x=1$. \
+(c)~It's executed infinitely often, for $x=1,1,1,\ldots\,$. \
+(d)~Since ten times \MF's internal representation of
+.1 is slightly larger than 1, the answer
+is not what you probably expect! The loop text is executed for
+$x=0$,~0.1, 0.20001, 0.30002, 0.40002, 0.50003, 0.60004, 0.70004, 0.80005,
+and 0.90005 only. \ (If you want the values $(0,.1,.2,\ldots,1)$, say
+`\thinspace@for@ $"xx"=0$ @upto@~10: $x:="xx"/10$; \<text> @endfor@' instead.)
+
+\danger A \<loop text> is rather like the \<replacement text> of a macro.
+It is any sequence of tokens that is balanced with respect to
+un^{quote}d appearances of @for@/@forsuffixes@/@forever@ and @endfor@
+delimiters. \MF\ reads the entire loop text quickly and stores it away
+before trying to perform it or to expand macros within it. All occurrences
+of the controlled \<symbolic token> in the loop text are changed to
+special internal parameter tokens that mean ``insert an argument here,''
+where the argument is of type @expr@ in the case of @for@, of
+type @suffix@ in the case of @forsuffixes@. This rule implies, in
+particular, that the symbolic token has no connection with similarly
+named variables elsewhere in the program.
+
+\dangerexercise What values are shown by the following program?
+\begintt
+n=0; for n=1: m=n; endfor show m,n; end.
+\endtt
+\answer $m=1$, $n=0$.
+
+\danger The ^"flex" routine described in Chapter~14 provides an interesting
+example of how loops can be used inside of macros inside of expressions:
+\begindisplay
+@pair@ $"z\_"\,[\,]$, $"dz\_"$; \ @numeric@ "n\_"\thinspace;
+ &\% private variables\cr
+@def@ "flex"(@text@ $t$) $=$&\% $t$ is a list of pairs\cr
+\quad^"hide"$\bigl(\,"n\_":=0$;\cr
+\qquad @for@ $z=t$: $"z\_"\,[{\rm incr}\,"n\_"]:=z$; @endfor@\cr
+\qquad $"dz\_":="z\_"\,["n\_"]-"z\_"\,[1]\,\bigr)$\cr
+\quad $"z\_"\,[1]$ @for@ $k=2$ @upto@ $"n\_"-1$:
+ $\ldots"z\_"\,[k]\{"dz\_"\}$ @endfor@\hidewidth\cr
+\qquad $\ldots"z\_"\,["n\_"]$ @enddef@;\cr
+\enddisplay
+The first loop stores the given pairs temporarily in an array, and it also
+counts how many there are; this calculation is ``hidden.'' Then
+the actual flex-path is contributed to the program with the help of
+a second loop. \ (Appendix~B uses the convention that symbolic tokens
+ending in `^{\_}' should not appear in a user's program; this often
+makes it unnecessary to `^@save@' tokens.)
+
+\danger When \MF\ encounters the construction `^@exitif@ \<boolean
+expression>;', it evaluates the boolean expression. If the
+expression is true, the (innermost) loop being iterated is terminated
+abruptly. Otherwise, nothing special happens.
+
+\dangerexercise Define an `^@exitunless@' macro such that
+`@exitunless@ \<boolean expression>;' will exit the current loop
+if the boolean expression is false.
+\answer @def@ @exitunless@ @expr@ $b$ $=$ @exitif@ not $b$ @enddef@.
+\ (The simpler alternative `@def@ @exitunless@ $=$ @exitif@ not
+@enddef@\kern1pt' wouldn't work, since `not' applies only to the following
+\<boolean primary>.)
+
+\ddangerexercise Write a \MF\ program that sets $p[k]$ to the $k$th
+^{prime number}, for $1\le k\le30$. Thus, $p[1]$ should be~2,
+$p[2]=3$, etc.
+\answer |numeric p[]; boolean n_is_prime; p[1]=2; k:=1;|\parbreak
+|for n=3 step 2 until infinity:|\parbreak
+| n_is_prime:=true;|\parbreak
+| for j=2 upto k: if n mod p[j]=0: n_is_prime:=false; fi|\parbreak
+| exitif n/p[j]<p[j]; endfor|\parbreak
+| if n_is_prime: p[incr k]:=n; exitif k=30; fi|\parbreak
+| endfor fi|\parbreak
+^^@show@^^@str@
+|show for k=1 upto 30: str p[k]&"="&decimal p[k], endfor "done" end.|
+
+\ddangerexercise When you run \MF\ on the file `|expr.mf|' of
+Chapter~8, you get into a `^@forever@' loop that can be stopped
+if you type, e.g., `|0|~|end|'. But what can you type to get out
+of the loop without ending the run? \ (The goal is to make
+\MF\ type~`|*|', without incurring any error messages.)
+\answer `|0; exitif true;|'.
+
+\endchapter
+
+If? thou Protector of this damned Strumpet,
+Talk'st thou to me of Ifs: thou art a Traytor,
+Off with his Head.
+\author WILLIAM ^{SHAKESPEARE}, {\sl Richard the Third\/} (1593)
+
+\bigskip
+
+% When ye pray,
+Use not vain repetitions.
+\author {\sl ^{Matthew} 6\thinspace:\thinspace7\/} (c.~70 A.D.)
+
+\eject
+ \beginchapter Chapter 20. More\\About\\Macros
+
+Chapter 18 gave the basic facts about macro definitions, but it didn't
+tell the whole story. It's time now for the Ultimate Truth to be revealed.
+
+\ninepoint
+\danger But this whole chapter consists of ``dangerous bend'' paragraphs,
+since the subject matter will be appreciated best by people who have
+worked with \MF\ for a little while.
+We shall discuss the following topics:\enddanger
+\smallskip
+\item\bull Definitions that begin with `@vardef@\kern1pt'; these embed macros
+into the variables of a program and extend the unary operators of
+\MF\ expressions.
+
+\item\bull Definitions that begin with `@primarydef@\kern.3pt',
+`@secondarydef@\kern.3pt', or `@tertiarydef@\kern.3pt'; these extend the
+binary operators of \MF\ expressions.
+
+\item\bull Other primitives of \MF\ that expand into sequences of tokens
+in a macro-like way, including `@input@' and `@scantokens@'.
+
+\item\bull Rules that explain when tokens are subject to expansion
+and when they aren't.
+
+\danger First let's consider the \<vardef heading> that was left
+undefined in Chapter~18. The ordinary macros discussed in that chapter
+begin with
+\begindisplay
+@def@ \<symbolic token>\<parameter heading>
+\enddisplay
+and then comes `$=$', etc. You can also begin a definition by saying
+\begindisplay
+^@vardef@ \<declared variable>\<parameter heading>
+\enddisplay
+instead; in this case the ^\<declared variable> might consist of
+several tokens, and you are essentially defining a variable whose
+``value'' is of type ``macro.'' For example, suppose you decide to say
+\begindisplay
+@pair@ $a.p$; \ @pen@ $a.q$; \ @path@ $a.r$; \
+@vardef@ $a.s=\ldots$ @enddef@;
+\enddisplay
+then $a.p$, $a.q$, and $a.r$ will be variables of types @pair@, @pen@,
+and @path@, but $a.s$ will expand into a sequence of tokens. \
+(The language {\eightrm^{SIMULA67}} demonstrated that it is advantageous
+to include procedures as parts of variable data structures; \MF\ does an
+analogous thing with macros.)
+
+\danger After a definition like `@def@ $t=\ldots$', the token $t$ becomes
+a ``^{spark}''; i.e., you can't use it in a suffix. But after
+`@vardef@ $t=\ldots$', the token~$t$ remains a ``^{tag},'' because
+macro expansion will take place only when $t$~is the first token in
+a variable name. Some of the definitions in Appendix~B are vardefs
+instead of defs for just that reason; for example,
+\begindisplay
+@vardef@ dir @primary@ $d$ $=$ "right" rotated $d$ @enddef@
+\enddisplay
+allows a user to have variable names like `|p5dir|'.
+
+\danger A variable is syntactically a primary expression, and \MF\ would
+get unnecessarily confused if the replacement texts of vardef macros
+were very different from primary expressions. Therefore, the
+tokens `^@begingroup@'
+and `^@endgroup@' are automatically inserted at the beginning and end
+of every vardef replacement text. If you say `^@showvariable@~$a$'
+just after making the declarations and definition above, the machine
+will reply as follows:
+\begintt
+a.p=pair
+a.q=unknown pen
+a.r=unknown path
+a.s=macro:->begingroup...endgroup
+\endtt
+
+\danger The `^{incr}' macro of Appendix B increases its argument by~1
+and produces the increased value as its result. The inserted `@begingroup@'
+and `@endgroup@' come in handy here:
+\begindisplay
+@vardef@ incr @suffix@ \$ $=$ $\$:=\$+1$; \ \$ @enddef@.
+\enddisplay
+Notice that the argument is a ^@suffix@, not an @expr@, because
+every variable name is a special case of a ^\<suffix>, and because
+an ^@expr@ parameter should never appear to the left ^^{:=} of~`$:=$'.
+Incidentally, according to the rules for ^{undelimited suffix parameters}
+in Chapter~18, you're allowed to say either `incr~$v$' or `incr$(v)$' when
+applying incr to~$v$.
+
+\danger There's another kind of vardef, in which the variable name being
+defined can have any additional suffix when it is used; this suffix is
+treated as an argument to the macro. In this case you write
+\begindisplay
+@vardef@ \<declared variable>|@#| \<parameter heading>
+\enddisplay
+^^{at sharp} and you can use |@#| in the replacement text (where it
+behaves like any other @suffix@ parameter). For example, Appendix~B says
+\begindisplay
+@vardef@ $z$|@#| $=$ $(x$|@#|$,y$|@#|) @enddef@;
+\enddisplay
+this is the magic definition that makes `$z_{3r}$' equivalent to
+`$(x_{3r},y_{3r})$', etc. In fact, we now know that `|z3r|' actually
+expands into eleven tokens:
+\begintt
+begingroup (x3r, y3r) endgroup
+\endtt
+
+\ddangerexercise True or false: After `|vardef| |a@#| |suffix| |b| |=|
+$\ldots$~|enddef|', the suffix argument~|b| will always be empty.
+\answer False; consider `|a1(2)|'.
+
+\ddanger Plain \MF\ includes a ^"solve" macro that uses ^{binary search}
+to find numerical solutions to ^{nonlinear equations}, which are too
+difficult to resolve in the ordinary way. ^^{equations, nonlinear}
+To use "solve", you first define a macro $f$ such that $f(x)$ is either
+@true@ or @false@; then you say
+\begindisplay
+"solve" $f("true\_x","false\_x")$
+\enddisplay
+where "true\_x" and "false\_x" are values such that $f("true\_x")=@true@$
+and $f("false\_x")=@false@$. The resulting value~$x$ will be at the cutting
+edge between truth and falsity, in the sense that $x$~will be within a
+given ^"tolerance" of values for which $f$ yields both outcomes.
+\begindisplay
+@vardef@ "solve"|@#|(@expr@ $"true\_x","false\_x"$) $=$\cr
+\quad $"tx\_":="true\_x"$; \ $"fx\_":="false\_x"$;\cr
+\quad^@forever@: $"x\_":=.5["tx\_","fx\_"]$; \
+ ^@exitif@ abs$("tx\_"-"fx\_")\le"tolerance"$;\cr
+\quad @if@ |@#|$("x\_"):\ "tx\_" \ @else@:\ "fx\_"\ @fi@$
+ :=\ "x\_"\thinspace; @endfor@;\cr
+\quad "x\_" @enddef@;\cr
+\enddisplay
+
+\ddanger For example, the "solve" routine makes it possible to solve the
+following interesting problem posed by Richard ^{Southall}: Given
+points $z_1$,~$z_2$, $z_3$,~$z_4$ such that $x_1<x_2<x_3<x_4$ and
+$y_1<y_2=y_3>y_4$, find the point~$z$ between $z_2$ and~$z_3$ such that
+\MF\ will choose to travel "right" at~$z$ in the path
+\begindisplay
+$z_1\,\{z_2-z_1\}\to z\to\{z_4-z_3\}\,z_4$.
+\enddisplay
+If we try $z=z_2$, \MF\ will choose a direction at $z$ that has a positive
+(upward) $y$-component; but at $z=z_3$, \MF's chosen direction will have a
+negative (downward) $y$-component. Somewhere in between is a ``^{nice}''
+value of~$z$ for which the curve will not rise above the line $y=y_2$.
+What is this~$z$?
+\displayfig 20a (115\apspix)
+Chapter 14 gives equations from which $z$ could be computed, in principle,
+but those equations involve trigonometry in a complicated fashion.
+It's nice to know that we can find~$z$ rather easily in spite of those
+complexities:
+\begindisplay
+@vardef@ "upward"(@expr@ $x$) $=$\cr
+\quad ypart direction 1 of $\bigl(z_1\{z_2-z_1\}
+ \to(x,y_2)\to\{z_4-z_3\}z_4\bigr)>0$ @enddef@;\cr
+$z=\bigl("solve"\,"upward"(x_2,x_3),y_2\bigr)$.\cr
+\enddisplay
+
+\ddangerexercise It might happen in unusual cases that $"upward"(x)$
+is @false@ for all $x_2\le x\le x_3$, hence "solve" is being invoked
+under invalid assumptions. What result does it give~then?
+\answer A value very close to $z_2$.
+
+\ddangerexercise Use "solve" to find $\root3\of{10}$, and compare
+the answer to the ^{cube root} obtained in the normal way.
+\answer |vardef lo_cube(expr x)=x*x*x<10 enddef;|\parbreak
+|show solve lo_cube(0,10), 10**1/3; end.|\par\nobreak\medskip\noindent
+^^{**} With the default ^"tolerance" of 0.1,
+this will show the respective values |2.14844| and |2.1544|.
+A more general routine could also be written, with `10' as a parameter:
+\begintt
+vardef lo_cube[](expr x)=x*x*x<@ enddef;
+show solve lo_cube10(0,10);
+\endtt
+if we ask for minimum tolerance ($"tolerance":="epsilon"$), the
+result is |2.15445|; the true value is $\approx 2.15443469$.
+
+\ddanger The syntax for \<declared variable> in Chapter~7 allows for
+^{collective subscripts} as well as tags in the name of the variable
+being declared. Thus, you can say
+\begindisplay
+@vardef@ $a[\,]b[\,]=\ldots$ @enddef@;
+\enddisplay
+what does this mean? Well, it means that all variables like |a1b2|
+are macros with a common replacement text. Every vardef has two
+^^{at} ^^{sharp at}
+implicit suffix parameters, `|#@|' and~`|@|', which can be used in
+the replacement text to discover what subscripts have actually been
+used. Parameter~`|@|' is the final token of the variable name
+(`|2|' in this example); parameter `|#@|' is everything preceding
+the final token (in this case `|a1b|'). These notations are supposed to
+be memorable because `|@|' is where you're ``at,'' while `|#@|' is
+everything before and `|@#|' is everything after.
+
+\ddangerexercise After `|vardef| |p[]dir=(#@dx,#@dy)| |enddef|', what's
+the expansion of `|p5dir|'\thinspace?
+\answer |begingroup(p5dx,p5dy)endgroup|.
+
+\ddangerexercise Explain how it's possible to retrieve the first subscript
+in the replacement text of |vardef|~|a[]b[]| (thereby obtaining,
+for example, `|1|' instead of `|a1b|').
+\answer Say `|first#@|' after defining `|vardef| |first.a[]@#=@| |enddef|'.
+\ (There are other solutions, e.g., using substrings of ^@str@~|#@|,
+but this one is perhaps the most instructive.)
+
+\ddangerexercise Say `^|showvariable| |incr,z|' to \MF\ and explain
+^^{incr} ^^{z} the machine's reply.
+\answer The machine answers thus:
+\begintt
+incr=macro:<suffix>->
+ begingroup(SUFFIX2):=(SUFFIX2)+1;(SUFFIX2)endgroup
+z@#=macro:->begingroup(x(SUFFIX2),y(SUFFIX2))endgroup
+\endtt
+Parameters to a macro are numbered sequentially, starting with zero,
+and classified as either ^|(EXPR|$_n$|)|, ^|(SUFFIX|$_n$|)|, or
+^|(TEXT|$_n$|)|. In a vardef, |(SUFFIX0)| and |(SUFFIX1)| are always
+reserved for the implicit parameters |#@| and~|@|; |(SUFFIX2)| will
+be |@#|, if it is used in the parameter heading, otherwise it will be the
+^^{at sharp} ^^{at} ^^{sharp at} first explicit parameter, if
+it happens to be a suffix parameter.
+
+\ddanger A vardef wipes out all type declarations and macro definitions
+for variables whose name begins with the newly defined macro variable name.
+For example, `|vardef|~|a|' causes variables like |a.p|
+and |a1b2| to disappear silently; `|vardef|~|a.s|' wipes out
+|a.s.p|, etc. Moreover, after `|vardef|~|a|' is
+in effect, you are not allowed to say `|pair|~|a.p|' or `|vardef|~|a[]|',
+since such variables would be inaccessible.
+
+\ddanger The syntax for \<definition> in Chapter 18 was incomplete,
+because $\langle$vardef heading$\rangle$ and \<leveldef heading> were
+omitted. Here are the missing rules:
+\beginsyntax
+<vardef heading>\is[vardef]<declared variable><parameter heading>
+ \alt[vardef]<declared variable>[\char'100\#]<parameter heading>
+<leveldef heading>\is<leveldef><parameter><symbolic token><parameter>
+<leveldef>\is[primarydef]\alt[secondarydef]\alt[tertiarydef]
+<parameter>\is<symbolic token>
+\endsyntax
+The new things here are @primarydef@, @secondarydef@, and @tertiarydef@,
+which permit you to extend \MF's repertoire of binary operators. For example,
+the `dotprod' operator is defined as follows in Appendix~B:
+\begindisplay
+@primarydef@ $w$ dotprod $z$ $=$\cr
+\quad $({\rm xpart}\,w\ast{\rm xpart}\,z\;+\;
+ {\rm ypart}\,w\ast{\rm ypart}\,z)$ @enddef@.\cr
+\enddisplay
+\MF's syntax for expressions has effectively gained a new rule
+\beginsyntax
+<numeric secondary>\is<pair secondary>[dotprod]<pair primary>
+\endsyntax
+in addition to the other forms of \<numeric secondary>, because of this
+primarydef.
+
+\ddanger The names `@primarydef@\kern1pt', `@secondarydef@\kern1pt',
+and `@tertiarydef@\kern1pt' may
+seem off by one, because they define operators at one level higher up:
+A primarydef defines a binary operator that forms a secondary expression
+from a secondary and a primary; such operators are at the same level
+as `$\ast$' and `rotated'.
+A secondarydef defines a binary operator that forms a tertiary expression
+from a tertiary and a secondary; such operators are at the same level
+as~`$+$'~and~`or'.
+A tertiarydef defines a binary operator that forms an expression
+from an expression and a tertiary; such operators are at the same level
+as~`$<$'~and~`\&'.
+
+\ddanger Plain \MF's `^{intersectionpoint}' macro is defined by a
+@secondarydef@ because it is analogous to `^{intersectiontimes}', which
+occurs at the same level (namely the secondary~$\rightarrow$~tertiary level):
+\begindisplay
+@secondarydef@ $p$ intersectionpoint $q$ $=$\cr
+\quad @begingroup@ ^@save@ $"x\_","y\_"$; \
+ $("x\_","y\_")=p$ intersectiontimes $q$;\cr
+\quad @if@ $"x\_"<0$: ^@errmessage@(|"The paths don't intersect"|);
+ \ $(0,0)$\cr
+\quad @else@: .5[point "x\_" of $p$,
+ point "y\_" of $q$] @fi@ @endgroup@ @enddef@.\cr
+\enddisplay
+Notice that ^@begingroup@ and ^@endgroup@ are necessary here; they aren't
+inserted automatically as they would have been in a @vardef@.
+
+\ddangerexercise Define a `^{transum}' macro operation that yields
+the ^{sum} of two ^{transforms}. \ (If $t_3=t_1$~transum~$t_2$, then
+$z$~transformed~$t_3=z$~transformed~$t_1+z$~transformed~$t_2$,
+for~all~pairs~$z$.)
+\answer |secondarydef t transum tt =|\parbreak
+| begingroup save T; transform T;|\parbreak
+| for z=origin,up,right:|^^"origin"\parbreak
+| z transformed t + z transformed tt = z transformed T; endfor|\parbreak
+| T endgroup enddef.|
+
+\ddanger \looseness=-1
+Now we've covered all the types of \<definition>, and it's time to
+take stock and think about the total picture. \MF's ^{mastication} process
+converts an input file into a long sequence of tokens, as explained in
+Chapter~6, and its digestive processes work strictly on those tokens.
+When a symbolic token is about to be digested, \MF\ looks up the token's
+current meaning, and in certain cases \MF\ will expand that token into
+a sequence of other tokens before continuing; this ``^{expansion process}''
+applies to macros and to @if@ and~@for@, as well as to certain other
+special primitives that we shall consider momentarily. Expansion
+continues until an unexpandable token is found; then the ^{digestion process}
+can continue. Sometimes, however, the expansion is not carried out; for
+example, after \MF\ has digested a @def@ token, it stops all expansion until
+just after it reaches the corresponding @enddef@. A complete list of
+all occasions when tokens are not expanded appears later in this chapter.
+
+\ddanger Let's consider all the tokens that cause expansion to occur,
+whenever expansion hasn't been inhibited:\enddanger
+
+\nobreak\medskip
+\textindent\bull Macros. When a macro is expanded, \MF\ first reads and
+evaluates the arguments (if any), as already explained.
+\ (Expansion continues while @expr@ and @suffix@ arguments are
+being evaluated, but it is suppressed within @text@ arguments.) \
+Then \MF\ replaces the macro and its arguments by the replacement text.
+
+\smallbreak
+\textindent\bull ^{Conditions}. When `^@if@\kern1pt' is expanded, \MF\
+reads and evaluates the boolean expression, then skips ahead, if necessary,
+until coming to either `^@fi@' or a condition that's true; then it will
+continue to read the next token. When `^@elseif@\kern1pt' or `^@else@'
+or `@fi@' is expanded, a conditional text has just ended, so \MF\
+skips to the closing `@fi@' and the expansion is empty.
+
+\smallbreak
+\textindent\bull ^{Loops}. When `^@for@' or `^@forsuffixes@' or
+`^@forever@' is expanded, \MF\ reads the specifications up to the colon,
+then reads the loop text (without expansion) up to the @endfor@.
+Finally it rereads the loop text repeatedly, with expansion. When
+`^@exitif@\kern1pt' is expanded, \MF\ evaluates the following boolean
+expression and throws away the semicolon; if the expression proves
+to be true, the current loop is terminated.
+
+\smallbreak
+\textindent\bull ^@scantokens@ \<string primary>. When `@scantokens@'
+is expanded, \MF\ evaluates the following primary expression, which
+should be of type @string@. This string is converted to tokens by the
+rules of Chapter~6, as if
+it had been input from a file containing just one line of text.
+
+\smallbreak
+\textindent\bull ^@input@ ^\<filename>. When `@input@' is expanded,
+the expansion is null, but \MF\ prepares to read from the specified
+file before looking at any more tokens from its current source.
+A \<filename> is subject to special restrictions explained on the
+next page.
+
+\smallbreak
+\textindent\bull ^@endinput@. When `@endinput@' is expanded, the
+expansion is null. But the next time \MF\ gets to the end of an
+input line, it will stop reading from the file containing that line.
+
+\smallbreak
+\textindent\bull ^@expandafter@. When `@expandafter@' is expanded,
+\MF\ first reads one more token, without expanding it; let's
+call this token~$t$. Then \MF\ reads the token that comes after~$t$
+(and possibly more tokens, if that token takes an argument),
+replacing it by its expansion. Finally, \MF\ puts~$t$ back in front
+of that expansion.
+
+\nobreak\smallskip
+\textindent\bull ^^{backslash} |\|. When `|\|' is expanded, the
+expansion is null, i.e., empty.
+
+\ddanger The syntax for \<filename> is not standard in \MF\!, because
+different operating systems have different conventions. You should
+ask your local system wizards for details on just how they have
+decided to implement ^{file names}. The situation is complicated by
+the fact that \MF's process of converting to tokens is irreversible;
+for example, `|x01|' and `|x1.0|' both yield identical sequences
+of tokens. Therefore \MF\ doesn't even try to convert a file name
+to tokens; an ^|input| operation must appear only in a text file, not
+in a list of tokens like the replacement text of a macro! \ (You can get
+around this restriction by saying
+\begindisplay
+^@scantokens@ |"input foo"|
+\enddisplay
+or, more generally,
+\begindisplay
+^@scantokens@ (|"input "| \& "fname")
+\enddisplay
+if "fname" is a string variable containing the \<filename> you want to
+input.) \ Although file names have nonstandard syntax, a sequence of six
+or fewer ordinary letters and/or digits should be a
+file name that works in essentially the same way on all installations of
+\MF\!\null. Uppercase letters are considered to be distinct from their
+lowercase counterparts, on many systems.
+
+\ddanger Here now is the promised list of all cases when expandable
+tokens are not expanded. Some of the situations involve primitives
+that haven't been discussed yet, but we'll get to them eventually.
+Expansion is suppressed at the following times:\enddanger
+
+\nobreak\medskip\item\bull
+When tokens are being deleted during error recovery (see Chapter~5).
+
+\smallskip\item\bull
+When tokens are being skipped because conditional text is being ignored.
+
+\smallskip\item\bull
+When \MF\ is reading the definition of a macro.
+
+\smallskip\item\bull
+When \MF\ is reading a loop text, or the symbolic token that
+immediately follows @for@ or @forsuffixes@.
+
+\smallskip\item\bull
+When \MF\ is reading the @text@ argument of a macro.
+
+\smallskip\item\bull
+When \MF\ is reading the initial symbolic token of a \<declared variable>
+in a type declaration.
+
+\smallskip\item\bull
+When \MF\ is reading the symbolic tokens to be defined by ^@delimiters@,
+^@inner@, ^@let@, ^@newinternal@, or ^@outer@.
+
+\smallskip\item\bull
+When \MF\ is reading the symbolic tokens to be shown by ^@showtoken@
+or ^@showvariable@.
+
+\smallskip\item\bull
+When \MF\ is reading the symbolic tokens to be saved by ^@save@.
+
+\smallskip\item\bull
+When \MF\ is reading the token after ^@expandafter@, ^@everyjob@,
+or the `$=$' following @let@.
+
+\medskip\noindent
+The expansion process is not suppressed while reading the suffix that
+follows the initial token of a \<declared variable>, not even in a
+\<vardef heading>.
+
+\endchapter
+
+% quam oppressis, qui novas res moliebantur, ...
+The two lieutenants,
+Fonteius Capito in Germany,
+% in Germania, Fonteio Capitone;
+and Claudius Macro in Africa,
+% in Africa, Clodio Macro, legatis.
+who opposed his advancement,
+were put down.
+\author ^{SUETONIUS}, %
+ {\sl Sergius Sulpicius Galba\/} (c.\thinspace125 A.D.) % chapter 11
+% from the translation by Alexander Thomson
+% (he says Macer, not Macro, but other translators call this man Macro)
+
+\bigskip
+
+By introducing macro instructions in the source language,
+the designer can bring about the same ease of programming
+as could be achieved by giving the computer
+a more powerful operation list than it really has.
+But naturally, one does not get the same advantages
+in terms of economy of memory space and computer time
+as would be obtained if the more powerful instructions
+were really built into the machine.
+\author O. ^{DOPPING}, {\sl Computers \& Data Processing\/} (1970) % ch19 p312
+
+\eject
+ \beginchapter Chapter 21. Random\\Numbers
+
+\newcount\n \n=93 \def\nextn{\global\advance\n1 \rand\char\n}%
+\def\threenextn{\nextn&\nextn&\nextn}%
+It's fun to play games with
+{\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn}
+by writing programs that incorporate
+an element of ^{chance}. You can generate unpredictable shapes, and
+you can add patternless perturbations to break up the rigid symmetry that
+is usually associated with mathematical constructions.
+ Musicians who use computers to
+synthesize their compositions have found that ^{music} has more ``life'' if
+its rhythms are slightly irregular and offbeat; perfect 1--2--3--4 pulses
+sound pretty dull by contrast. The same phenomenon might prove to
+be true in typography.
+
+\MF\ allows you to introduce controlled indeterminacy in two ways:
+(1)~`^{uniformdeviate}~$t$' gives a number~$u$ that's randomly distributed
+between 0 and~$t$; \ (2)~`^{normaldeviate}' gives a ^{random number}~$x$
+that has the so-called normal distribution with mean zero and variance one.
+
+\danger More precisely, if $t>0$ and $u=\null$uniformdeviate~$t$, we will
+have $0\le u<t$, and for each fraction $0\le p\le1$ we will have
+$0\le u<pt$ with approximate probability~$p$. If $t<0$, the results are
+similar but negated, with $0\ge u>t$. Finally if $t=0$, we always have
+$u=0$; this is the only case where $u=t$ is possible.
+
+\danger A normaldeviate, $x$, will be positive about half the time and
+negative about half the time. Its distribution is ``^{bell-shaped}'' in
+the sense that a particular value $x$ occurs with probability roughly
+proportional to $e^{-x^2/2}$; the graph of this function looks something
+like a bell. The probability is about 68\% that $\vert x\vert<1$,
+about 95\% that $\vert x\vert<2$, and about 99.7\% that $\vert x\vert<3$.
+It's a pretty safe bet that $\vert x\vert<4$.
+
+Instead of relying on mathematical formulas to explain this random
+behavior, we can actually see the results graphically by letting \MF\
+draw some ``^{scatter plots}.'' Consider the following program, which
+draws a $10\pt\times10\pt$ square and puts 100 little dots inside it:
+\begindisplay
+@beginchar@$\,(@incr@ "code",10"pt"\0,10"pt"\0,0)$;\cr
+@pickup@ @pencircle@ scaled .3"pt"; \ @draw@ "unitsquare" scaled $w$;\cr
+@pickup@ @pencircle@ scaled 1"pt";\cr
+@for@ $k=1$ @upto@ 100:\cr
+\quad @drawdot@(uniformdeviate $w,\,$uniformdeviate $w$);
+ \ @endfor@ @endchar@.\cr
+\enddisplay
+The resulting ``characters,'' if we repeat the experiment ten times,
+\n=-1 look like~this:
+\begindisplay
+\threenextn&\threenextn&\threenextn&\nextn\rm\quad.
+\enddisplay
+And if we replace `uniformdeviate $w$' by `$.5w+w/6\ast\null$normaldeviate',
+we get
+\begindisplay
+\threenextn&\threenextn&\threenextn&\nextn\rm\quad.
+\enddisplay
+Finally, if we say `@drawdot@(uniformdeviate $w,\,.5w+w/6\ast\null
+$normaldeviate)' the results are a mixture of the other two cases:
+\begindisplay
+\threenextn&\threenextn&\threenextn&\nextn\rm\quad.
+\enddisplay
+
+\exercise Consider the program fragment `@if@ uniformdeviate$\,1\kern-1pt<
+\kern-1pt1/3$:\
+"case\_a" @else@:~"case\_b"~@fi@'\kern-.2pt. True or false:
+"case\_b" will occur about three times as often as "case\_a".
+\answer False; about twice as often (2/3 versus 1/3).
+
+\exercise \MF's uniformdeviate operator usually doesn't give you an integer.
+Explain how to generate random integers between 1 and~$n$, in such a way
+that each value will be about equally likely.
+\answer |1+floor uniformdeviate n|.
+
+\exercise What does the formula `(uniformdeviate 1)[$z_1,z_2$]' represent?
+\answer A random point on the straight line segment from $z_1$ to $z_2$.
+\ (The point $z_1$ itself will occur with probability about 1/65536;
+but point $z_2$ will never occur.)
+
+\exercise Guess what the following program will produce:
+\begintt
+beginchar(incr code,100pt#,10pt#,0);
+for n:=0 upto 99:
+ fill unitsquare xscaled 1pt yscaled uniformdeviate h
+ shifted (n*pt,0); endfor endchar.
+\endtt
+\answer A random ``^{skyline}'' texture, $100\pt$ wide $\times$ $10\pt$ tall:
+{\rand\char127} The density decreases uniformly as you go up in altitude.
+
+\dangerexercise And what does this puzzle program draw?
+\begintt
+beginchar(incr code,24pt#,10pt#,0);
+numeric count[];
+pickup pencircle scaled 1pt;
+for n:=1 upto 100:
+ x:=.5w+w/6*normaldeviate;
+ y:=floor(x/pt);
+ if unknown count[y]: count[y]:=-1; fi
+ drawdot(x,pt*incr count[y]); endfor endchar.
+\endtt
+\answer A more-or-less bell-shaped ^{histogram}: {\rand\char126}
+
+\danger Let's try now to put more ``life'' in the \MF\ ^{logo}, by
+asking Lady Luck to add small perturbations to each of the key points.
+First we define "noise",
+\begindisplay
+@vardef@ "noise" $=$ normaldeviate$\null\ast"craziness"$ @enddef@;
+\enddisplay
+the ^"craziness" parameter will control the degree of haphazard variation.
+\rightfig 21a ({240\apspix} x {216\apspix}) ^-20pt
+Then we can write the following program for the logo's `{\manual n}':
+\begindisplay
+@beginlogochar@\thinspace(|"N"|$,15)$;\cr
+$x_1="leftstemloc"+"noise"$;\cr
+$x_2="leftstemloc"+"noise"$;\cr
+$w-x_4="leftstemloc"+"noise"$;\cr
+$w-x_5="leftstemloc"+"noise"$;\cr
+$"bot"\,y_1="noise"-"o"$;\cr
+$"top"\,y_2=h+o+"noise"$;\cr
+$y_3=y_4+"ygap"+"noise"$;\cr
+$"bot"\,y_4="noise"-"o"$;\cr
+$"top"\,y_5=h+o+"noise"$;\cr
+$z_3="whatever"[z_4,z_5]$;\cr
+@draw@ $z_1\dashto z_2\dashto z_3$; \
+@draw@ $z_4\dashto z_5$; \ @labels@$(1,2,3,4,5)$; \ @endchar@.
+\enddisplay
+The illustration here was drawn with $"craziness"=0$, so there was no noise.
+
+\danger Three trials of the $9\pt$ `{\manual n}' with $"craziness"=.1"pt"$
+gave the following results:
+\displayfig 21b\&c\&d (195\apspix)
+And here's what happens if you do similar things to all the
+letters of \MF\!, with "craziness" decreasing from $.45"pt"$ to zero in
+steps of $.05"pt"$:
+\begindisplay \global\advance\n by 8
+% we haven't room for craziness .5!
+%\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+{\manual METAFONT}\cr
+\enddisplay
+
+\danger Every time you run a program that refers to random numbers,
+you'll get different results, because \MF\ uses the date and time of day
+to change its generator. This unpredictable behavior
+is normally what you want, but it can be troublesome if your
+program draws a lovely shape that you'd like to see again.
+Or perhaps one of your runs will uncover a program bug; you won't be able to
+diagnose the problem, because it probably won't recur!
+The solution is to say
+\begindisplay
+^@randomseed@ $:=$ \<numeric expression>
+\enddisplay
+and to remember the value of that numeric expression. \ (The value
+will automatically be recorded in the transcript file of your run.) \
+You will get the same sequence of uniform and normal deviates on
+any two runs that begin with the same @randomseed@, because \MF's
+numbers are only ``pseudo-random.''
+
+\endchapter
+
+% En musiker, som jag k\"ande, roade sig med
+A musician whom I knew amused himself
+% att st\"amma sitt piano hur som helst utan rim och reson.
+%by tuning his piano arbitrarily, without any rhyme or reason.
+by tuning his piano haphazardly, without any rhyme or reason.
+% D\"arefter spelade han Beethovens Sonate path\'etique utantill.
+Afterwards he played ^{Beethoven}'s\/ {\rm Sonate Path\'etique} by heart.
+% Det var en otrolig fr\"ojd att h\"ora ett gammlt stycke leva upp igen.
+It was an unbelievable delight to hear an old piece come back to life.
+% Jag hade h\"ort denna sonat spelas under tjugo \aa r, st\"andigt
+%^^{beauty} I had heard this sonata for twenty years,
+^^{beauty} How often had I previously heard this sonata, always the same way,
+% utan hopp att se den utvecklas; fixerad, of\"orm\"ogen att n\aa\ l\"angre.
+%never dreaming that it was capable of being developed further.
+never dreaming that it was capable of being developed further!
+% utan hopp att se den utvecklas; fixerad, of\"orm\"ogen att n\aa\ l\"angre.
+\author AUGUST ^{STRINDBERG}, {\sl Chance in Artistic Creation} (1894)
+% The original was in French, but I was unable to locate anything
+% but the above Swedish translation, from Modern Museet catalog 28 (1962)
+% Nye Konstriktningar! eller Slumpen i det konstn\"arliga skapandet
+% [New Directions in Art! or, Chance in Artistic Creation]
+% 2009.10.21 here's the original French!:
+% J'ai connu un musicien qui se plaisait \'a accorder son piano
+% au petit bonheur, sans rime ni raison. Puis il jouait par c{\oe}ur
+% la {\it Sonate path\'etique\/} de Beethoven, et c'\'etait une
+% jouissance incroyable d'entendre ce vieux morceau se rajeunir.
+% Que de fois je 'lavais entendu ex\'ecuter devant moi, cetta sonate,
+% toujours le m\^eme, fix\'ee, sans esp\'erance de la voir se
+% d\'evelopper en d'autres sons, jamais, incapable d'une \'evolution\thinspace!
+
+\bigskip
+
+[Education] must lead us from chance and arbitrariness
+to rational clarity and intellectual order.
+\author L. ^{MIES VAN DER ROHE}, {\sl Inaugural Address\/} (1938)
+
+\eject
+ \beginchapter Chapter 22. Strings
+
+\MF\ is not a word processor, but a \MF\ programmer can process words and
+other short strings of symbols in rudimentary ways. Strings can help
+explain what a program is doing; for example, the |io.mf| file of
+Chapter~5 mentions |"The|~|letter|~|O"| as a title that should appear
+on proofsheets, and it also says |"O"| in order to identify the
+position of a character in the output font.
+
+Chapter 6 points out that a \<string token> is any sequence of
+characters enclosed in double-quote (|"|) marks, except that you're
+not allowed to use the double-quote character itself in this way.
+If you need that character, plain \MF\ provides it in a string of
+length~1 called ^"ditto". Thus
+\begintt
+"A string expression can contain a `" & ditto & "' mark"
+\endtt
+even though a \<string token> cannot.
+
+A string expression can be used all by itself as a statement, just as
+if it were an equation or declaration or command. Such a statement is called
+a ^\<title>, provided that it is immediately followed by a~`|;|'.
+If ^"tracingtitles"$\null>0$ when a title is encountered, \MF\
+will type the title on the user's terminal. If ^"proofing"$\null>0$
+when a title is encountered, \MF\ will copy the title into the output
+file, so that it can be put onto proofsheets by postprocessors such
+as the ^|GFtoDVI| program described in Appendix~H.
+
+\danger Appendix H explains how to specify the strings that are used as
+^{labels} for the key points on proofsheets.
+
+\ddanger Here's the full syntax for string expressions. All of the
+activity except for ^{concatenation} (`\&') ^^{ampersand} takes
+place at the primary level:
+\beginsyntax
+<string primary>\is<string token>
+ \alt<string variable>
+ \alt[(]<string expression>[)]
+ \alt[begingroup]<statement list><string expression>[endgroup]
+ \alt[jobname]
+ \alt[readstring]
+ \alt[str]<suffix>
+ \alt[char]<numeric primary>
+ \alt[decimal]<numeric primary>
+ \alt[substring]<pair primary>[of]<string primary>
+<string secondary>\is<string primary>
+<string tertiary>\is<string secondary>
+<string expression>\is<string tertiary>
+ \alt<string expression>[\&]<string tertiary>
+\endsyntax
+The new features here are |jobname|, |readstring|, |str|, |char|,
+|decimal|, and |substring|; we shall consider each of them in turn.
+
+\ddanger The name of your job (\kern1pt@jobname@) is the name of the first
+file you input, provided that the first line of instructions to \MF\
+(the `^|**|' line or ^{command line}) causes input of some file.
+Otherwise the job name is ^|mfput|, as in Experiment~1 of Chapter~5.
+
+\ddanger When you say `^@readstring@', \MF\ stops and waits for the user
+to type a line at the terminal. The value of @readstring@ is the contents
+of this line, with trailing spaces eliminated.
+\ (You probably should use the @message@ command first, to give the
+user a clue about what to type; for example, see the |expr.mf| file
+of Chapter~8, which gets its input expressions via @readstring@.
+The ^@stop@ macro of Appendix~B makes use of the fact that @readstring@
+halts the computer; it doesn't actually look at the string.)
+
+\ddanger An arbitrary ^\<suffix> is converted to a string by ^@str@,
+using the method by which \MF\ displays suffix arguments in
+diagnostic typeouts. Negative subscripts are enclosed in
+square brackets; spaces or dots are inserted between tokens whose
+characters belong to the same class (according to the table in
+Chapter~6). For example, if $n=1$ then `@str@~$x[n]a$' is |"x1a"|;
+`@str@~$x\,n\,a$' is |"x.n.a"|.
+
+\ddanger The result of `^@char@~$n$' is a string of length~1,
+representing the character whose ^{ASCII} code is~$n$.
+\ (Appendix~C explains this code.) \ The value of~$n$ is first
+rounded to the nearest integer, then multiples of~256 are
+added or subtracted if necessary until $0\le n<256$; this
+defines @char@~$n$ in all cases.
+
+\ddanger The ^{decimal representation} of a known numeric value~$x$
+is available in string form as `@decimal@~$x$'. If $x$ is negative,
+the first character of this string will be~`|-|'. If $x$ is not
+an integer, a decimal point will be included, followed by as
+many digits as are necessary to characterize the value. \ (These
+conventions are the same as those illustrated in the example
+outputs of Chapter~8.)
+
+\ddanger The rules for ^{substring} are like the rules for ^{subpath}
+in Chapter~14. \MF\ thinks of a string as if its characters were
+written in the squares of a piece of ^{graph paper}, between
+coordinates $x=0$ and $x=n$, where $n$~is the length of the string.
+In simple cases, substring$\,(a,b)$
+then refers to the characters between $x=a$ and~$x=b$. The
+rules for the general case are slightly more involved: If $b<a$,
+the result will be the ^{reverse} of substring$\,(b,a)$.
+Otherwise $a$ and~$b$ are replaced respectively by
+$\max\bigl(0,\min(n,\round a)\bigr)$ and
+$\max\bigl(0,\min(n,\round b)\bigr)$; this leads to the simple
+case $0\le a\le b\le n$ described above, when the resulting
+string has length $b-a$.
+
+\ddanger Strings can be converted into numbers, although Chapter~8
+didn't mention this fact in its syntax for \<numeric primary>. The
+primitive operations are
+\begindisplay
+{\tt ASCII}\thinspace\<string primary>\alt
+\thinspace{\tt oct}\thinspace\<string primary>\alt
+\thinspace{\tt hex}\thinspace\<string primary>
+\enddisplay
+where `^{ASCII}' returns the ASCII code of the first character of the
+string, `^{oct}' computes an integer from a string representing
+^{octal notation} (radix~8), and `^{hex}' computes an integer from
+a string representing ^{hexadecimal notation} (radix~16). For example,
+\begindisplay
+ASCII |"100"| $=$ 49;\qquad oct |"100"| $=$ 64;\qquad hex |"100"| $=$ 256.
+\enddisplay
+Several exceptional conditions need to be mentioned:
+(1)~ASCII~|""|~$=-1$; otherwise ASCII yields an integer between 0 and~255.
+\ (2)~The characters in the string argument to `oct' must all be
+digits in the range |0|--|7|.
+\ (3)~The characters in the string argument to `hex' must all be
+digits in the range |0|--|9|, |A|--|F|, or |a|--|f|.
+\ (4)~The number that results from `oct' or `hex' must be less than 4096.
+Thus, `oct~|"7777"|' and `hex~|"FFF"|' are the maximum legal values.
+
+\ddangerexercise Under what circumstances is (a) ASCII @char@ $n=n$?
+\ (b)~@char@~ASCII~$s=s$?
+\answer (a) Iff $n$ is an integer between 0 and 255.
+(b) Iff $s$ is a string of length~1.
+
+\ddangerexercise Why are there primitive operations to convert from
+strings to numbers assuming octal notation and hexadecimal notation,
+but not assuming decimal notation?
+\answer Whoever says that there's no such primitive operation has
+forgotten about @scantokens@.
+
+\ddangerexercise Write an "octal" macro that converts a nonnegative
+integer to an octal string.
+\answer |vardef octal primary n =|\parbreak
+| save m,s; m:=abs round n; string s; s=decimal(m mod 8);|\parbreak
+| forever: m:=m div 8; exitif m=0;|\parbreak
+| s:=decimal(m mod 8) & s; endfor|\parbreak
+| s enddef;|\par\nobreak\medskip\noindent
+`|str[m mod 8]|' could also be used instead of `|decimal(m mod 8)|'.
+
+\ddanger A ^\<message command> allows you to communicate directly
+or indirectly with the user. It has the general syntax
+\beginsyntax
+<message command>\is<message op><string expression>
+<message op>\is[message]\alt[errmessage]\alt[errhelp]
+\endsyntax
+If you say `@message@~$s$', the characters of $s$ will be typed on the
+terminal, at the beginning of a new line; `@errmessage@~$s$' is
+similar, but the string will be preceded by |"! "| and followed
+by~|"."|, followed by lines of context as in \MF's normal error messages.
+If the user asks for ^{help} after an @errmessage@ error,
+the most recent @errhelp@ string will be typed (unless it was empty).
+
+\ddanger \MF\ doesn't allow you to have an array of different
+macros $m[i]$; but you can have an array of strings that have
+macro-like behavior, via ^@scantokens@. The ^@mode\_def@ construction
+of Appendix~B exploits this idea.
+
+\endchapter
+
+Many other useful Practises
+mecanicks perform by this Theo.
+as the finding the length of strings.
+\author WILLIAM ^{ALINGHAM}, {\sl Geometry Epitomized\/} (1695)
+ % p51 acc to OED; but British Library doesn't have this edition!
+ % they have 1701 `An epitome of geometry', as does Yale
+
+\bigskip
+
+Forgive me, if my trembling Pen displays
+What never yet was sung in mortal Lays.
+But how shall I attempt such arduous String?
+\author JAMES ^{THOMSON}, {\sl The Castle of Indolence\/} (1748)
+ % canto 1 verse 31
+
+\eject
+ \beginchapter Chapter 23. Online\\Displays
+
+How do you get pictures to appear on your screen? Plain \MF\ provides
+the `^@showit@' command, which displays the ^"currentpicture".
+Furthermore you can ask for `^@screenchars@'; this automatically
+does a @showit@ at the time of each ^@endchar@. And you can see all
+the action by asking for `^@screenstrokes@'; this automatically
+does a @showit@ after every @draw@ or @fill@.
+
+\ddanger The above-described features of plain \MF\ are implemented
+from low-level primitive commands, by macros that appear in Appendix~B\null.
+At the lowest level, \MF\ obeys commands such as `@display@
+"currentpicture" @inwindow@~1'; there's also an `@openwindow@'
+command that defines a correspondence between \MF\ coordinates and
+screen coordinates. The syntax is
+\beginsyntax
+<display command>\is[display]<picture variable>[inwindow]<window>
+<window>\is<numeric expression>
+<openwindow command>\is[openwindow]<window><window spec>
+<window spec>\is<screen place>[at]<pair expression>
+<screen place>\is[from]<screen coordinates>[to]<screen coordinates>
+<screen coordinates>\is<pair expression>
+\endsyntax
+A \<window> is an integer between 0 and 15, inclusive; it represents
+one of sixteen ``windows'' or ``portholes'' that \MF\ provides
+between its pictures and the outside world. The \<window> mentioned
+in a @display@ command must previously have been ``opened'' by
+an @openwindow@ command.
+
+\ddanger \MF's windows should not be confused with the so-called
+windows provided by many modern operating systems. If you have
+such a system, you'll probably find that all of \MF's pictorial
+output appears in one operating-system window, and all of its
+terminal I/O appears in another, and you might be running other
+jobs (like the system editor) in another. \MF's windows are not so
+fancy as this; they are just internal subwindows of one big
+picture window.
+
+\ddanger The command `@openwindow@ $k$ @from@ $(r_0,c_0)$ @to@ $(r_1,c_1)$
+@at@~$(x,y)$' associates a rectangular area of the user's screen
+(or of the user's big picture window) with pixels in \MF's coordinate
+system. All of the numbers in this command (namely $k$, $r_0$,~$c_0$,
+$r_1$,~$c_1$, $x$, and~$y$) are rounded to the nearest integer if they
+aren't integers already. Furthermore $r_0$ is replaced by
+$\max\bigl(0,\min("maxr",r_0)\bigr)$ and $r_1$ is replaced by
+$\max\bigl(r_0,\min("maxr",r_1)\bigr)$, where "maxr" is the maximum
+number of rows on the screen; similar adjustments are made to $c_0$
+and~$c_1$. The two $(r,c)$ values are row and column
+numbers on the screen; the topmost row is conventionally taken to be
+row zero, and the leftmost column is taken to be column zero.
+\ (These conventions for screen coordinates are quite different from
+the normal ^{Cartesian} coordinate system used everywhere else
+in \MF\!, but somehow they seem appropriate when applied to screens.) \
+Point~$(x,y)$ of \MF's raster will be equated to the upper left
+corner of the rectangle, i.e., to the upper left corner of the pixel
+in screen column~$c_0$ of screen row~$r_0$. The window itself
+occupies $r_1-r_0$ rows and $c_1-c_0$ columns. It follows that
+the pixel in column~$c_1$ of row~$r_1$ is not in the window itself,
+but it is the screen pixel diagonally just below and to the right of the
+lower right corner of the window.
+
+\ddangerexercise What are the \MF\ coordinates of the boundary of
+such a window?
+\answer Point $(x,y)$ is the upper left corner, ${(x+c_1-c_0,y)}$ is the
+upper right corner, ${(x,y-r_1+r_0)}$ is the lower left corner, and
+${(x+c_1-c_0,y-r_1+r_0)}$ is the lower right corner. \ (Pixels
+outside this rectangle will not be displayed.)
+
+\danger If you run \MF\ on a system that doesn't support general
+bitmap displays, the @display@ and @openwindow@ commands will do
+nothing. You'll have to look at hardcopy output, off\/line.
+\ (But your \MF\ might run a bit faster.)
+
+\ddanger The syntax for @display@ insists that you display a
+\<picture variable>, not a \<picture expression>; thus, you
+can't `@display@ ^@nullpicture@'. Plain \MF\ defines a special
+variable ^"blankpicture" that's entirely blank, just so that
+you can easily display nothing whenever you like.
+
+\ddanger A window may be opened any number of times, hence moved
+to different locations on the screen. Opening a window blanks the
+corresponding screen rectangle as if you had displayed "blankpicture".
+
+\ddanger The effect of overlapping windows is undefined, because \MF\
+does not always repaint pixels that have remained unchanged between
+displays.
+
+\ddanger Changes to a picture do not change the displays that were
+generated from it, until you give another display command explicitly.
+Thus, the images emblazoned on your screen might not exist any longer
+in \MF's picture memory.
+
+\ddanger Plain \MF\ has an `@openit@' macro that opens
+^"currentwindow"; this variable "currentwindow" is always zero
+unless you change it yourself. The @showit@ macro displays
+"currentpicture" in "currentwindow"; and it's also designed
+to call @openit@---but only the very first time @showit@ is invoked.
+This means that the screen normally won't be touched until the moment you
+first try to display something.
+
+\ddanger Appendix E explains how to manage a more elaborate scheme
+in which six windows can be used to show how ^{meta-characters} vary
+under six different font-parameter settings. The author ^^{Knuth}
+used such a six-window system when developing the Computer Modern
+typefaces; here is a typical example of what appeared on his
+^^{a} terminal when the letter~`a' was being refined:
+\displayfig 23 (68mm)
+
+\ddangerexercise The @openit@ macro in Appendix~B specifies $(-50,300)$
+as the upper left corner point of the window used for showing
+all the pictures. This might clip off the bottom of a large character,
+if your screen is limited to, say, 360 rows. How could you change
+@openit@ so that the character images will be raised 20 rows higher
+than they would be in the standard setting?
+\answer Redefine @openit@ so that it puts the top left at $(-50,280)$.
+
+\ddangerexercise Design a `^@new\_window@' routine that allocates
+windows 1, 2, \dots,~15. If the user says `|new_window $(u,v)|',
+where |$|~is any suffix and |u,v| are pairs of coordinates for
+two opposite corners of a rectangle, your macro should map that
+rectangle to the next available screen rectangle and open it as
+window number |window$|. The allocation should be left to right,
+top to bottom; assume that the screen is an infinite rectangle,
+^"screen\_cols" wide.
+\answer (This routine is due to John ^{Hobby}.)
+\begintt
+newinternal n_windows; % the number of windows allocated so far
+newinternal screen_bot; % the first untouched screen row
+pair screen_corner; % the upper left corner of next window
+def wipescreen = % do this to initialize or reinitialize
+ for i:=1 upto n_windows: display blankpicture inwindow i; endfor
+ n_windows := screen_bot := 0; screen_corner := origin enddef;
+wipescreen;
+vardef new_window@#(expr u,v) = save r,c,up_lft; pair up_lft;
+ if n_windows=15: errmessage "No more windows left"
+ else: window@# := incr n_windows;
+ up_lft = (min(xpart u,xpart v), max(ypart u, ypart v));
+ (r,c) = (u+v-2up_lft) rotated 90;
+ if ypart screen_corner + c > screen_cols:
+ screen_corner:=(screen_bot,0); fi
+ openwindow window@# from screen_corner
+ to screen_corner+(r,c) at up_lft;
+ screen_bot := max(screen_bot,xpart screen_corner + r);
+ screen_corner := screen_corner + (0,c) fi; enddef;
+\endtt
+
+\endchapter
+
+Editing will be done on-line with a display scope and keyboard.
+\author RICHARD L. ^{VENEZKY}, in {\sl American Documentation\/} (1968)
+ % p72; ``Storage, Retrieval, and Editing of Information for a Dictionary''
+
+\bigskip
+
+In future I might be obliged to turn for material to the tube.
+\author IGOR ^{STRAVINSKY}, in {\sl Harper's\/} (1970) % April, p112
+ % that year he wrote a regular column called Performing Arts
+
+\eject
+ \beginchapter Chapter 24. Discreteness\\and Discretion
+
+Pixel patterns are indistinguishable from continuous curves, when the
+pixels are small enough. After all, the human eye is composed of
+discrete receptors, and visible light has a finite wavelength.
+Our hypothetical ^"luxo" printer of Chapter~11, with its resolution
+of 2000 pixels per inch, would surely be able to produce printed
+pages of high quality, if it existed; the physical properties of ink
+would smooth out all the tiny bumps, obliterating all the evidence that
+the letterforms had been digitized. However, it will always be less
+expensive to work with devices of lower resolution, and we want the output
+of \MF\ to look as good as possible on the machines that we can afford to
+buy. The purpose of this chapter is to discuss the principles of
+``discreet ^{rounding},'' i.e., to consider the tasteful application of
+mathematical techniques by which \MF\ can be made to produce satisfactory
+shapes even when the resolution is rather coarse.
+
+The technical material in this chapter is entirely marked with danger
+signs, since careful rounding tends to make \MF\ programs more complex; a
+novice user will not wish to worry about such details. On the other hand,
+an expert \MF er will take pains to round things properly even when
+preparing high-resolution fonts, since the subtle refinements we are about
+to discuss will often lead to significantly better letterforms.
+
+We should realize before we begin that it would be a mistake to
+set our hopes too high. Mechanically generated letters that are untouched
+by human hands and unseen by human eyes can never be expected to compete
+with alphabets that are carefully crafted to look best on a particular
+device. There's no substitute for actually looking at the letters
+and changing their pixels until the result looks right. Therefore our
+goal should not be to make ^{hand-tuning} obsolete; it should rather be
+to make hand-tuning tolerable. Let us try to create meta-designs so
+that we would never want to change more than a few pixels per character,
+say half a dozen, regardless of the resolution. At low resolutions, six
+pixels will of course be a significant percentage of the whole, and at higher
+resolutions six well-considered pixel changes can still lead to worthwhile
+improvements. The point is that if our design comes close enough, a
+person with a good bitmap-editing program will be able to optimize an
+entire font in less than an hour. This is an attainable goal, if rounding
+is done judiciously.
+
+\danger \MF\ tries to adjust curves automatically, so that they are
+well adapted to the ^{raster}, if the internal quantities ^"autorounding"
+and/or ^"smoothing" have positive values. \ (Plain \MF\ sets
+$"autorounding":=2$ and $"smoothing":=1$, so you generally get these
+features unless you turn them off yourself.) \ But all the examples in
+this chapter will be generated with $"autorounding":="smoothing":=0$
+unless otherwise mentioned, because this will keep \MF's automatic
+mechanisms from interfering with our experiments. We shall discuss the
+pros and cons of automatic rounding after we have explored the general
+problem in more detail.
+
+\danger The first thing we need to understand about rounding is \MF's
+procedure for ^{digitizing} a path. A path of length~$n$ can be regarded
+as a trajectory~$z(t)$ that is traced out as $t$~varies from 0 to~$n$. In
+these terms, the corresponding digitized path is most easily described by
+the formula `round~$z(t)$' for $0\le t\le n$; each $z(t)$ is rounded to
+the nearest point with integer coordinates. For example, if a path goes
+through point~$(3.1,5.7)$, its digitization will go through point~$(3,6)$.
+The digitized trajectory makes discrete jumps at certain values of $t$,
+when round~$z(t)$ hops from one point to another; the two points will be
+one pixel apart, and we can imagine that the digitized path traverses the
+horizontal or vertical edge between them when it jumps.
+
+\danger When an ordinary region is being filled, this rule for
+digitizing paths boils down to a simple criterion that's easy to
+visualize: {\sl A pixel belongs to the digitized region if and only if
+its center point lies inside the original undigitized path.} For example,
+two versions of Chapter~5's Ionian `{\manual\IOO}' are shown here
+at a resolution of 200 pixels per inch, using the characteristics
+of ^"lowres" mode in Appendix~B:
+\displayfig 24a\&b (190\apspix)
+The heavy broken lines are digitized paths, and the pixels inside these
+ragged boundaries are those whose centers lie in the shaded regions.
+
+\danger The `{\manual\IOO}' on the left has digitized well; but the
+one on the right has problems, because it was based on curves that
+were generated without taking the raster into account. The difference
+between these two letters is entirely due to line~8 of the program
+in Chapter~5, which says
+\begindisplay
+"curve\_sidebar" $=$ round $1/18"em"$;
+\enddisplay
+this equation determines the position of the leftmost and rightmost
+edges of the `{\manual\IOO}' before digitization, and it leads to
+the nice digitized form in the left-hand example. Without the word
+`^{round}', we get the inferior right-hand example, which was
+obtained by exactly the same \MF\ program except that "curve\_sidebar"
+was set to $1/18"em"$ exactly. One little token---which changed an exact
+calculation to an approximate, rounded calculation---made all the difference!
+
+\danger Curves that are placed in arbitrary positions on
+a raster can lead to digital disasters, even though the curves themselves
+aren't bad. For example, suppose we take the right-hand example above
+and shift it just 0.05 and 0.10 pixels to the right:
+\displayfig 24c\&d (190\apspix)
+The first shift of 0.05 pixels causes a tiny ^{pimple} to appear
+at the right edge; after another small shift the pimple has grown into a
+mole, and the left edge has become too ^{flat}. \looseness=-1
+
+\danger A designer who is asked to make a digital `O' that is 22 pixels
+wide will certainly have pixels in mind when making the design. Therefore
+it's not surprising that our program to generate a digital~`O' should
+pay attention to actual pixel positions by rounding "curve\_sidebar" as
+in this example. We have distorted the infinite-resolution curve
+slightly so that it will digitize well, before digitizing it.
+
+\danger A path $z(t)$ will digitize well if the digitization process doesn't
+change it too much; thus, we want $z(t)$ to be essentially the same as
+round$\,z(t)$, at all the important places. But what places are ``important''?
+Experience shows that the most critical points are those where the path
+travels horizontally or vertically, i.e., where it runs parallel to
+the raster lines. It's best to arrange things so that a curve becomes
+parallel to the raster lines just when it touches or nearly touches those
+lines; then it will appear to have the right curvature after digitization.
+The worst case occurs when a curve becomes parallel to the raster just
+when it's halfway between raster lines; then it gets a pimple or a flat spot.
+
+\ddanger Diagonal slopes, where a curve has a $\pm45^\circ$ tangent angle,
+are also potential sources of unwanted pimples and flats. Similarly, at
+higher resolutions it is sometimes possible to detect small glitches
+when a curve travels with slopes of $\pm1/2$ or $\pm2/1$. Rational
+slopes $m/n$ where $m$ and~$n$ are small integers turn out to be
+somewhat dangerous. But diagonals are of secondary importance; horizontal
+and vertical slopes lead to more severe problems.
+
+\danger These considerations suggest a simple general principle for adapting
+the outlines of shapes to be digitized: {\sl If you know that the outline
+will have a vertical tangent at some point, round the $x$~coordinate to an
+integer and leave the $y$~coordinate unchanged. If you know that the
+outline will have a horizontal tangent at some point, round the
+$y$~coordinate to an integer and leave the $x$~coordinate unchanged.}
+
+\ddanger Incidentally, the horizontal tangent points in our `{\manual\IOO}'
+examples were taken care~of by the fact that `^@define\_corrected\_pixels@'
+makes the ^{overshoot} parameter~$o$ nearly an integer, together with
+the fact that ^@beginchar@ makes $h$ an integer. If the $y$~coordinates
+had not been rounded at the horizontal tangent points,
+our bad examples would have looked even worse.
+
+\danger Before we go further into the study of rounding, we had better
+face up to a technicality that's sometimes important: We said that the
+pixels of a digitized region are those whose centers lie inside the
+undigitized region; but this rule is vague about what happens when the
+centers happen to fall precisely on the undigitized boundary. Similarly,
+when we said that round$\,z(t)$ jumps from one point to an adjacent point,
+we ignored the fact that a curve such as $z(t)=(t,t)$ actually
+jumps from $(0,0)$ to $(1,1)$ when it is rounded as $t$ passes 1/2;
+those points are not adjacent.
+\MF\ skirts both of these problems in an interesting way:
+It shifts all of its paths to the
+right by an infinitesimal amount~$\delta$, and it also shifts them
+upward by an even smaller
+infinitesimal amount~$\delta\epsilon$, so that no path actually
+touches a pixel center. Here $\delta$ and~$\epsilon$ are positive numbers
+that are chosen to be so small that their actual values don't matter.
+For example, the path $z(t)=(t,t)$ becomes $(t+\delta,t+\delta\epsilon)$,
+which jumps from $(0,0)$ to $(1,0)$ to $(1,1)$ because it momentarily
+rounds to $(1,0)$ when $t=1/2-2\delta\epsilon$.
+
+\danger Points of the form $(m+1/2,n+1/2)$, where $m$ and $n$ are integers,
+lie in the centers of their pixels. They are called ``ambiguous'' points
+because we can't round them to the nearest integer neighbor without
+deciding which of four adjacent points is to be considered the nearest.
+If we imagine taking a curved outline and shifting it slowly to the
+right, the digitized image makes abrupt transitions when the outline
+passes over an ^{ambiguous point}. When a path comes near an ambiguous
+point, the path is farthest away from its digitization. Thus the
+ambiguous points are points of instability, and digitizing works best
+when paths don't get too close to them.
+
+\danger Let's consider now what happens when we ^@draw@ with a pen,
+instead of filling an outline. It may seem that the simplest possible @draw@
+command would be something like this:
+\begindisplay
+@pickup@ @pencircle@; \ @draw@ $(0,0)\to(10,0)$;
+\enddisplay
+what could be easier? But a closer look shows that this is actually
+about the worst case that could be imagined! A circular pen of
+diameter~1 that goes from $(0,0)$ to $(10,0)$ has upper and lower
+boundaries that go from $(0,\pm1/2)$ to $(10,\pm1/2)$,
+and both of these boundaries run smack through lots of
+ambiguous points. \MF\ has to decide whether to fill the row of pixels
+with $0\le y\le1$ or the lower row with $-1\le y\le0$, neither of which is
+centered on the given line. According to the rule stated earlier, \MF\
+shifts the path very slightly to the right and very, very slightly up;
+thus the pixels actually filled are bounded by
+$(0,0)\dashto(10,0)\dashto(10,1)\dashto(0,1)\dashto\cycle$.
+
+\dangerexercise Continuing this example, what pixels would have been
+filled if the path had been `$(0,0)\to(10,-"epsilon")$'\thinspace?
+\answer The entire path now has negative $y$~coordinates except at
+point~$(0,0)$, so the outline of the filled region is
+ $(0,-1)\dashto(10,-1)\dashto(10,0)\dashto(0,0)\dashto(0,1)
+\dashto\cycle$. \ $\bigl($Notice that the
+digitized outline actually goes up to $(0,1)$ before coming straight down
+again. This fills no pixels, but \MF\ correctly puts ``cancelling'' edges
+from $(0,0)$ to $(0,1)$ and back to $(0,0)$ into its edge structure, because the
+point $(0,.5)$ is on the boundary and rounds to $(0,1).\bigr)$
+
+\danger In general when we @draw@ with a fixed pen, good digitizations
+depend on where the edges of the pen happen to fall, not on the
+path followed by the pen's center. Thus, for example, if the path we're
+drawing has a vertical tangent at point~$z_1$, we don't necessarily
+want $x_1$~to be an integer; we want "lft"$\,x_1$ and "rt"$\,x_1$
+to be integers. If there's a horizontal tangent at~$z_2$, we want
+"top"$\,y_2$ and "bot"$\,y_2$ to be integers. The pens created by
+^@pencircle@ always have the property that $("lft"\,x)-("rt"\,x)$
+and $("top"\,y)-("bot"\,y)$ are integers; hence both edges will
+be in good or bad positions simultaneously.
+
+\danger Suppose that we want $x_1$ to be approximately equal to~$\alpha$,
+and we also want it to be at a good place for vertical tangents with respect
+to the pen that has currently been picked up. One way to define $x_1$ is to say
+\begindisplay
+$"lft"\,x_1=\round("lft"\,\alpha)$;
+\enddisplay
+this does the right thing, because it makes "lft"$\,x_1$ an integer and
+it also makes $x_1\approx\alpha$. Similarly, to make~$y_2\approx\beta$
+good for horizontal tangents, we can say
+\begindisplay
+$"top"\,y_2=\round("top"\,\beta)$.
+\enddisplay
+Such operations occur frequently in practice, so plain \MF\ provides
+^^"good.x" ^^"good.y" ^^{gumdrop} convenient abbreviations: We can say simply
+\begindisplay
+$x_1="good.x"\,\alpha$; \ $y_2="good.y"\,\beta$
+\enddisplay
+instead of using indirect equations for $"lft"\,x_1$ and $"top"\,y_2$.
+
+\danger Let's look one last time at the letters of the \MF\ logo, in
+order to make them round properly. Chapter~11 describes a file ^|logo.mf|
+that draws the seven characters, but we can improve the results by
+making pixel-oriented refinements. In the first place, we can replace
+the command
+\begindisplay
+@define\_pixels@($s,u,"xgap","ygap","leftstemloc","barheight"$)
+\enddisplay
+by something better: Looking at the uses of these ad hoc dimensions,
+we see that ^"xgap" and ^"ygap" ought to be integers; ^"leftstemloc"
+should be a "good.x" value for "logo\_pen"; and ^"barheight" should
+be a "good.y" value. Therefore we say
+\begindisplay
+^@define\_pixels@$(s,u)$;\cr
+^@define\_whole\_pixels@$("xgap","ygap")$;\cr
+^@define\_good\_x\_pixels@$("leftstemloc")$;\cr
+^@define\_good\_y\_pixels@$("barheight")$;\cr
+\enddisplay
+these commands, provided by plain \MF\!, will do the right thing.
+\ (The "logo\_pen" should be picked up before the last two commands are
+given.) \ These few changes, and a change to the `{\manual m}', suffice to
+fix all the letters except `\kern1pt{\manual j}\kern1pt'.
+
+\dangerexercise The program for \MF's `{\manual m}' ^^{O}
+appears in Chapter~18. What changes would you suggest to make
+it digitize well?
+\answer The horizontal tangents are already taken care of by the equations
+$"top"\,y_1=h+o$ and $"bot"\,y_4=-o$, so nothing needs to be done there.
+We should, however, say
+\begindisplay
+$x_2=w-x_3="good.x"(1.5u+s)$
+\enddisplay
+so that vertical tangents will occur in good places. Since $w$~is an
+integer, and since the "logo\_pen" has left-right symmetry,
+$w-x_3$ will be good if and only if $x_3$ is.
+
+\danger The `\kern1pt{\manual j}\kern1pt' ^^{T} presents a new problem,
+because we want it to be symmetric between left and right. If the pen
+breadth is odd, we want the character width~$w$ to be odd, so that there
+will be as many pixels to the left of the stem as there are to the right.
+If the pen breadth is even, we want $w$ to be even. Therefore we have a
+50-50 chance of being unhappy with the value of~$w$ that is computed by
+^@beginchar@.
+
+\dangerexercise Prove that the value of $w$ is satisfactory for
+`\kern1pt{\manual j}\kern1pt' with respect to the "logo\_pen" if and
+only if $.5w$ is a good $x$~value for vertical strokes.
+\answer Let $b$ be the pen breadth. Then $.5w$ is a good $x$ value if and only
+if $"lft"\,.5w$ is an integer; but $"lft"\,.5w=.5w-.5b$, and this is an
+integer if and only if $w-b$ is even.
+
+\danger If $w$ is not a good value, we want to replace it by either
+$w+1$ or~$w-1$, whichever is closer to the device-independent width
+from which $w$ was rounded. For example, if $w$ was rounded to 22 from
+the ideal width~21.7, we want to change it to 21 rather than~23.
+Plain \MF's ^@change\_width@ routine does this. Hence we have the
+following program for `\kern1pt{\manual j}\kern1pt', in place of the
+\rightfig 4b ({208\apspix} x {216\apspix}) ^-18pt
+simpler version found in exercise 11.\metaT:
+\begindisplay
+@beginlogochar@(|"T"|$,13)$;\cr
+@if@ $.5w<>"good.x"\,.5w$: @change\_width@; @fi@\cr
+$"lft"\,x_1=-"eps"$;\cr
+$x_2=w-x_1$;\cr
+$x_3=x_4=.5w$;\cr
+$y_1=y_2=y_3$; \ $"top"\,y_1=h$; \ $"bot"\,y_4=-o$;\cr
+@draw@ $z_1\dashto z_2$; \ @draw@ $z_3\dashto z_4$;\cr
+@labels@$(1,2,3,4)$; \ @endchar@.\cr
+\enddisplay
+\decreasehsize 44mm
+Chapter 4 said that `\kern1pt{\manual j}\kern1pt' was the simplest of the
+seven logo letters, but it has turned out to be the trickiest.
+
+\restorehsize
+\ddanger This program has one unexplained feature. Why was $"lft"\,x_1$
+set to $-"eps"$ instead of zero? The answer requires an understanding
+of the pen polygons discussed in Chapter~16. The edges of those polygons
+are highly likely to pass through ambiguous points when the center of
+the pen has integer or half-integer coordinates. \MF\ shifts paths slightly
+to the right and up, in order to resolve ambiguities; therefore if
+ambiguous points occur at the left and right edges of the
+`\kern1pt{\manual j}\kern1pt', some pixels will be lost at the left but
+gained at the right. The constant ^"eps" is 0.00049, which is small but
+positive enough that \MF\ will surely notice it. Subtracting "eps"
+from~$x_1$ and adding "eps" to~$x_2$ avoids ambiguous edge points and
+keeps the result symmetric.
+
+\ddanger Since the ^{overshoot} `$o$' is always "eps" more than an
+integer, it is unnecessary to do anything similar at point~$z_4$;
+the equation `$"bot"\,y_4=-o$' is sufficient.
+
+\ddanger Point $z_3$ in the middle of the `{\manual h}' ^^{M} is in
+a satisfactory position because $"bot"\,y_3="ygap"-"o"$.
+If $"bot"\,y_3$ were exactly an integer, the~`{\manual h}' would often turn
+out to be unsymmetric, because of ambiguous points on the boundary
+at~$z_3$.
+
+\ddangerexercise True or false: If "currentpen" is @pencircle@ xscaled "px"
+yscaled~"py", the command `@draw@ $(-"epsilon",0)\to(+"epsilon",0)$'
+will produce an image that has both left-right and top-bottom symmetry.
+\ (Assume that "autorounding"="smoothing"=0.)
+\answer There are no ambiguous points on the outlines of this stroke,
+except perhaps on the top and bottom edges; the latter can occur only if
+$\round"py"$ is odd. Hence there is always left-right symmetry, but
+top-bottom symmetry might fail because of a missing row at the bottom
+(e.g., when $"px"="py"=3$). In a case like the `\kern1pt{\manual j}\kern1pt'
+we do have both symmetries, because $y_1$ and $x_4$ are in good positions.
+
+\ddangerexercise The polygon for `^@pencircle@ scaled 3' is an octagon
+whose vertices are at the points $(\pm0.5,\pm1.5)$ and $(\pm1.5,\pm0.5)$.
+Prove that if you `^@draw@~$(x,y)$' with this pen, the result never has
+both top-bottom and left-right symmetry.
+\answer No matter where you place the octagon so that it isn't touching
+any ambiguous points, exactly seven ambiguous points are inside it; hence
+every one-point ^@draw@ fills exactly seven pixels. \ (In fact,
+you always get one of the patterns
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{\kern\Blankpix RR}\hbox{RRR}\hbox{\kern\Blankpix RR}\kern1pt}}$,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{\kern\Blankpix R}\hbox{RRR}\hbox{RRR}\kern1pt}}$,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{RR}\hbox{RRR}\hbox{RR}\kern1pt}}$, or
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{RRR}\hbox{RRR}\hbox{\kern\Blankpix R}\kern1pt}}$.)
+
+\ddanger Rounding can also help to position points at which we don't
+have horizontal or vertical tangents. For example, consider the
+``^{sharp sign}'' or ``^{hash mark}'' character that's drawn by the
+\rightfig 24e ({300\apspix} x {320\apspix}) ^-60pt
+following program:
+\begindisplay
+$u\0:={10\over18}"pt"\0$; \ @define\_pixels@$(u)$;\cr
+@beginchar@$\,(0,15u\0,{250\over36}"pt"\0,{70\over36}"pt"\0)$;\cr
+@pickup@ @pencircle@\cr
+\qquad scaled $(.4"pt"+"blacker")$;\cr
+$"lft"\,x_1=\round u-"eps"$;\cr
+$x_3=x_1$;\cr
+$x_2=x_4=w-x_1$;\cr
+$y_1=y_2="good.y"(.5[-d,h]+"pt")$;\cr
+$y_3=y_4=h-d-y_1$;\cr
+@draw@ $z_1\dashto z_2$; \ @draw@ $z_3\dashto z_4$;\cr
+$"lft"\,x_6=\round 3u$;\cr
+$x_7=w-x_6$;\cr
+$x_8="good.x"\,.5w$;\cr
+$x_5-x_6=x_7-x_8$;\cr
+$"top"\,y_5="top"\,y_7=h+"eps"$;\cr
+$"bot"\,y_6="bot"\,y_8=-d-"eps"$;\cr
+@draw@ $z_5\dashto z_6$; \ @draw@ $z_7\dashto z_8$;\cr
+@labels@(^@range@ 1 ^@thru@ 8);\cr
+@endchar@.\cr
+\enddisplay
+If we digitize this character according to ^"lowres" mode at 200
+pixels per inch, we get the following results:
+\begindisplay
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+SSSSSSSSSSSRSSSSSRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRSSSSSRSSSSSSSSSSS\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+}}
+\enddisplay
+The left-hand example was obtained by omitting the `round' and `"good.x"'
+instructions in the equations for $x_6$ and~$x_8$. This meant that points
+$z_6$ and $z_8$ fell into different, possibly unlucky, raster positions,
+so the two diagonal strokes digitized differently even though they
+came from essentially identical undigitized lines. The middle example
+was produced by the given program without changes. And the right-hand
+example was produced by drawing the diagonals in a more complicated way:
+The commands `@draw@~$z_5\dashto z_6$; @draw@~$z_7\dashto z_8$;' were
+replaced by
+\begindisplay
+$y_{15}=y_1$; \ $z_{15}="whatever"[z_5,z_6]$; \
+ $y_{36}=y_3$; \ $z_{36}="whatever"[z_5,z_6]$;\cr
+$y_{27}=y_2$; \ $z_{27}="whatever"[z_7,z_8]$; \
+ $y_{48}=y_4$; \ $z_{48}="whatever"[z_7,z_8]$;\cr
+\noalign{\smallskip}
+@draw@ $z_5\dashto("good.x"(x_{15}+.5),y_1)\dashto("good.x"(x_{15}-.5),y_1)$\cr
+\qquad\qquad$\dashto("good.x"(x_{36}+.5),y_3)\dashto("good.x"(x_{36}-.5),y_3)
+ \dashto z_6$;\cr
+@draw@ $z_7\dashto("good.x"(x_{27}+.5),y_2)\dashto("good.x"(x_{27}-.5),y_2)$\cr
+\qquad\qquad$\dashto("good.x"(x_{48}+.5),y_4)\dashto("good.x"(x_{48}-.5),y_4)
+ \dashto z_8$;\cr
+\enddisplay
+The idea here was to control the goodness of the points where the
+diagonals intersect the horizontal bar lines, and to hide one of the
+``^{jaggies}'' inside each bar line. If we do the same three experiments
+but triple the resolution, we get similar results but the differences are
+not quite so obvious:
+\begindisplay
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}}
+\enddisplay
+
+\danger When letters are drawn by filling outlines, the left and right
+outlines are digitized independently; therefore corresponding outlines
+should usually be offset from each other by an integer amount whenever
+possible. For example, suppose that the letter~`^{n}' is being drawn
+with commands like
+\begindisplay
+$\penpos2("stem",0)$; \ $\penpos4("stem",0)$
+\enddisplay
+to specify the stroke widths at the base of the two ^{stems}.
+We will therefore have $x_{2r}-x_{2l}=x_{4r}-x_{4l}="stem"$. If
+"stem" is not an integer, say $"stem"=2.7$, we might have
+$x_{2l}=2.1$, $x_{2r}=4.8$, $x_{4l}=9.6$, $x_{4r}=12.3$;
+then $x_{2r}-x_{2l}$ will digitize to $5-2=3$, so the left stem
+will be three pixels wide, but the right stem will be only
+$12-10=2$ pixels wide. We could get around this problem by
+insisting that either $x_{2l}$ or~$x_{2r}$ be an integer,
+and that either $x_{4l}$ or~$x_{4r}$ be an integer; then both stems
+would be three pixels wide. But other quantities calculated from "stem"
+(e.g., the breadth of diagonal strokes) would then be based on a
+value of~2.7 instead of the stem width~3 that an observer of the
+font actually perceives. Therefore it is best to make "stem" an integer.
+The proper way to do this is generally to say
+\begindisplay
+^@define\_whole\_blacker\_pixels@("stem");
+\enddisplay
+this command computes "stem" from $"stem"\0$ by the formula
+\begindisplay
+$"stem":=max\bigl(1,\,\round("stem"\0\ast"hppp"+"blacker")\bigr)$.
+\enddisplay
+(Notice that this rounding operation is not allowed to reduce "stem"
+to zero at low resolutions.)
+
+\danger Even when the "stem" width is an integer in the `n' example,
+we probably want to arrange things so that $x_{2l}$, $x_{2r}$, $x_{4l}$,
+and~$x_{4r}$ are integers, because this will give the least distortion
+under digitization. Suppose, however, that it's most convenient to define
+the pen position at the center of the stroke instead of at the edge; i.e.,
+the program would say just `$x_2=\alpha$' if rounding were not taken into
+account. How should $x_2$ be defined, when we want $x_{2l}$ to be an
+integer? We could say
+\begindisplay
+$x_2=\alpha$; \ $x_{2l}:=\round x_{2l}$; \ $x_{2r}:=\round x_{2r}$; \
+$x_2:=.5[x_{2l},x_{2r}]$
+\enddisplay
+but that's too complicated; moreover, it will fail if any other
+variables depend on $x_2$, $x_{2l}$, or $x_{2r}$, because such
+dependencies are forgotten when new values are assigned.
+In the case of fixed pens we solved this problem by saying
+`$x_2="good.x"\,\alpha$'; but the "good.x" function doesn't know
+about "stem". One solution is to say
+\begindisplay
+$x_{2l}=\round(\alpha-.5"stem")$,
+\enddisplay
+or equivalently, `$x_{2r}=\round(\alpha+.5"stem")$'. This does the
+job all right, but it isn't completely satisfying. It requires
+knowledge of the breadth that was specified in the $\penpos2$ command,
+and it works only when the penpos angle is~0. If the penpos command
+is changed, the corresponding equation for rounding must be
+changed too. There's another solution that's more general and more
+attractive once you get used to it:
+\begindisplay
+$x_{2l}=\round\bigl(x_{2l}-(x_2-\alpha)\bigr)$.
+\enddisplay
+Why does this work? The argument to `^{round}' must be a known value,
+but both $x_{2l}$ and~$x_2$ are unknown. Fortunately, their difference
+$x_{2l}-x_2$ is known, because of the $\penpos2$ command. The
+rounding operation makes $x_2\approx\alpha$ because it makes $x_{2l}$
+approximately equal to the value of $x_{2l}$ minus the difference
+between $x_2$ and~$\alpha$.
+
+\ddangerexercise The generality of this technique can be appreciated
+by considering the following more difficult problem that the author
+faced while designing a~`^{w}': Suppose you want $x_1-x_2$ to be
+an integer and $x_3\approx x_4$, and suppose that $x_2$, $x_3-x_1$,
+and~$x_4+x_1$ are known; but $x_1$ is unknown, hence $x_3$ and~$x_4$
+are also unknown. According to our general idea, we want to specify an
+equation of the form `$x_1-x_2=\round(x_1-x_2+f)$', where $x_1-x_2+f$
+is known and $f$~is a formula that should be approximately zero.
+In this case $x_3-x_4$ is approximately zero, and $(x_3-x_1)-(x_4+x_1)$
+is known; what value of~$f$ should we choose?
+\answer $f=.5(x_4-x_3)$; the desired equation is
+`$x_1-x_2=\round\bigl(x_1-x_2+.5(x_4-x_3)\bigr)$'.
+
+\ddanger In many fonts, such as the one you are now reading,
+curved lines swell out so that the thick parts of~`^{o}' are actually
+a bit broader than the stems of~`n'. Therefore the ^{Computer Modern}
+font routines discussed in Appendix~E have two parameters,
+$"stem"\0$ and $"curve"\0$, to govern the stroke thickness.
+For example, the font ^|cmr9| used in the present paragraph has
+$"stem"\0=2/3"pt"\0$ and $"curve"\0=7/9"pt"\0$. Both of these should
+be integers, hence the ^@font\_setup@ macro in Appendix~E
+dutifully says
+\begindisplay
+@define\_whole\_blacker\_pixels@$("stem","curve")$.
+\enddisplay
+Although this looks good on paper, it can cause problems at certain
+low resolutions, because the rounding operation might make ^"stem" and
+^"curve" rather different from each other even though $"stem"\0$ and
+$"curve"\0$ are fairly close. For example, the resolution might be
+just at the value where |cmr9|'s "stem" turns out to be only~2
+but "curve" is~3. Curves shouldn't be that much darker than stems;
+they would look too splotchy. Therefore plain \MF\
+has a `^@lowres\_fix@' subroutine, and Appendix~E says
+\begindisplay
+@lowres\_fix@("stem","curve") 1.2
+\enddisplay
+after "stem" and "curve" have been defined as above. In this particular
+case @lowres\_fix@ will reset $"curve":="stem"$ if it turns out that the
+ratio $"curve"/"stem"$ is greater than 1.2 times the ratio
+$"curve"\0/"stem"\0$. Since $"curve"\0/"stem"\0=7/6$ in the case of |cmr9|,
+this means that the ratio $"curve"/"stem"$ after rounding is allowed
+to be at most~1.4; if $"curve"=3$ and $"stem"=2$, the "curve" parameter
+will be lowered to~2. In general the command
+\begindisplay
+@lowres\_fix@($d_1,d_2,\ldots,d_n$) $r$
+\enddisplay
+will set $d_n:=\cdots d_2:=d_1$ if $\max(d_1,d_2,\ldots,d_n)/\!
+\min(d_1,d_2,\ldots,d_n)$ is greater than
+$r\cdot\max(d_1\0,d_2\0,\ldots,d_n\0)/\!\min(d_1\0,d_2\0,\ldots,d_n\0)$.
+
+\ddangerexercise
+\parshape 12
+3pc 201pt
+3pc 201pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 29pc
+\rightfig 4e ({180\apspix} x {225\apspix}) ^15pt
+Good digitization can also require attention to the shapes of the
+digitized angles where straight lines meet. The purpose of
+the present exercise is to illustrate the relevant ideas by
+studying the `\kern1pt{\manual\char'170}' symbol, for which a program
+appears in Chapter~4. If that program is used without change to produce
+low-resolution ^{triangle}s, the results might turn out to be unsatisfactory
+because, for example, point~3 at the right of the triangle
+might digitize into a snubnosed or asymmetric shape.
+If $y_3$ is an integer, the triangle will be top-bottom symmetric, but
+the right-hand tip will be two pixels tall and this will look too blunt.
+Therefore we should choose~$y_3$ to be an integer plus~1/2.
+Given this value of~$y_3$, what will be the shape of the rightmost
+four columns of the digitized tip, as $x_3$ varies?
+\answer Let $x_3=n+{1\over2}+\theta$, where $n$ is an integer and
+$0\le\theta<1$. By drawing lines of slope~$30^\circ$ from the pixel
+centers, we find that there are three cases for the rightmost four
+columns:
+\begindisplay
+Case A,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{RR}\hbox{RRRR}\hbox{RR}\kern1pt}}$;\qquad
+Case B,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RR}\hbox{RRRR}\hbox{RR}\hbox{R}\kern1pt}}$;\qquad
+Case C,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RRR}\hbox{RRRR}\hbox{RRR}\hbox{R}\kern1pt}}$.
+\enddisplay
+Case A occurs for $0\le\theta<2\sqrt3-3$; Case B occurs for
+$2\sqrt3-3\le\theta<\sqrt3-1$; Case~C occurs for
+$\sqrt3-1\le\theta<1$. The tip in Case~A looks a bit too sharp,
+and Case~C looks too blunt, so Case~B seems best. This case occurs
+when $x_3$ is near an integer, so it's OK to let $x_3$ be an integer.
+
+\ddangerexercise Continuing the previous exercise, assume that $x_1$
+is an integer. What value of~$y_1$ will make the upper tip of the
+triangle look like
+`\thinspace$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RR}\hbox{RRRR}\kern1pt}}$' after digitization?
+\answer Let $y_1=n+\theta$. If $\theta$ lies between
+${1\over2}\sqrt3-{1\over2}$ and ${1\over6}\sqrt3+{1\over2}$,
+the top row after digitization will contain two black pixels.
+If $\theta$ lies between ${1\over6}\sqrt3+{1\over2}$ and
+${5\over6}\sqrt3-{1\over2}$, we get the desired shape. Otherwise
+we get `\thinspace$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RRR}\hbox{RRRR}\kern1pt}}$'.
+
+\ddangerexercise Concluding the previous exercise, modify the
+program of Chapter 4 so that the upper tip and
+the upper part of the right tip both digitize to the shape
+`\thinspace$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RR}\hbox{RRRR}\kern1pt}}$'.
+\answer (We choose $\theta={1\over2}\sqrt3$ in the previous exercise, since
+^^{floor} this is the midpoint of the desirable interval.) The equations
+are changed to
+\begindisplay
+$x_1=x_2=w-x_3=\round s$;\cr
+$y_3=.5+{\rm floor}\,.5h$;\cr
+$z_1-z_2=(z_3-z_2)$ rotated 60;\cr
+$y_1:=.5\rmsqrt3+\round(y_1-.5\rmsqrt3)$;\cr
+$y_2:=h-y_1$;\cr
+\enddisplay
+and then we @fill@ $z_1\dashto z_2\dashto z_3\dashto\cycle$ as before.
+
+\ddanger So far in this chapter we've assumed that pixels are square. But
+sometimes ^^{nonsquare} we need to prepare output for devices with
+general rectangular pixels, and this adds an extra dimension of
+complexity to rounding. Plain \MF\ sets things up so that
+^"currenttransform" multiplies all $y$~coordinates by
+^"aspect\_ratio", when paths are filled or drawn, or when pens are
+picked up. Furthermore the ^"top" and ^"bot" functions divide the
+amount of offset by "aspect\_ratio". This means that \MF\ programs
+can still be written as if pixels were square;
+the normal `angle' and `direction' functions, etc., can be used.
+But the good places for rounding horizontal tangents are not at
+integer values of~$y$ in general, they are actually at values that
+will become integers after multiplication by the aspect ratio.
+
+\ddanger The ^"vround" function rounds its argument to the nearest
+$y$~coordinate that corresponds to a pixel boundary in the
+general case. Thus if $"aspect\_ratio"=1$, "vround" simply rounds
+to the nearest integer, just like `round'; but if, say,
+$"aspect\_ratio"=4/3$, then "vround" will round to the nearest
+multiple of~$3/4$. Plain \MF\ uses "vround" instead of `round'
+when it computes an ^{overshoot} correction, and also when ^@beginchar@
+computes the values of ^"h" and~^"d". The ^"good.y" function produces
+a good $y$~value that takes "aspect\_ratio" properly into account.
+
+\ddangerexercise Without looking at Appendix B\null, try to guess how
+the "vround" and "good.y" macros are defined.
+\answer @vardef@ "vround" @primary@ $v$ $=$\parbreak
+\qquad floor$(v\ast"aspect\_ratio"+.5)/"aspect\_ratio"$ @enddef@;\parbreak
+@vardef@ "good.y" @primary@ $y$ $=$\parbreak
+\qquad "vround"$(y+"pen\_top")-"pen\_top"$ @enddef@.
+
+\ddangerexercise What are the ``ambiguous points'' when pixels
+are not square?
+\answer $\bigl(m+1/2,(n+1/2)/"aspect\_ratio"\bigr)$. These are the points
+that "currenttransform" maps into pixel centers.
+
+\ddanger The \MF\ ^{logo} as we have described it so far will round
+properly with respect to arbitrary aspect ratios if we make only
+a few more refinements. The value of "ygap" should be vrounded
+instead of rounded, so we initialize it by saying
+\begindisplay
+^@define\_whole\_vertical\_pixels@("ygap").
+\enddisplay
+Furthermore we should say
+\begindisplay
+$"ho"\0:="o"\0$; \ ^@define\_horizontal\_corrected\_pixels@("ho");
+\enddisplay
+and "ho" should replace ^"o" in the equations for $x_4$ in the programs
+for `{\manual i}' and~`{\manual l}'. ^^{E} ^^{F}
+Everything else should work satisfactorily as it stands.
+
+\ddanger Appendix B includes macros ^"good.top", ^"good.bot", ^"good.lft",
+and ^"good.rt" that take pairs as arguments. If you say, for example,
+`$z_3="good.top"(\alpha,\beta)$' it means that $z_3$ will be near
+$(\alpha,\beta)$ and that when $z_3$ is modified by ^"currenttransform"
+the top point of ^"currentpen" placed at the transformed point will
+be in a good raster position.
+
+\danger \MF's `^"autorounding"' feature tries to adjust curves to the
+raster for you, but it is a mixed blessing. Here's how it works:
+If the internal quantity "autorounding" is positive, the $x$~coordinates
+of all paths that are filled or drawn are rounded to good raster positions
+wherever there's a vertical tangent; and the $y$~coordinates
+are rounded to good raster positions wherever there's a horizontal
+tangent. The rest of the curve is distorted appropriately, as if
+the raster were stretching or shrinking slightly. If $"autorounding">1$,
+you get even more changes: Paths are perturbed slightly at $\pm45^\circ$
+tangent directions, so that second-order ^{pimples} and flat spots don't
+appear there.
+
+\danger For example, if we return to the Ionian `{\manual\IOO}' with
+which we began this chapter, let's suppose that "curve\_sidebar" was left
+unrounded. We saw that the result was bad when "autorounding" was~0;
+when $"autorounding"=1$ and~2 we get this:
+\displayfig 24f\&g (190\apspix)
+The stroke has gotten a lot thinner at the sides, by comparison with
+the original design (which, incidentally, can be seen in the illustrations
+below). Although autorounding has produced a fairly recognizable O~shape,
+the character of the original has been lost, especially in the case
+$"autorounding"=2$; indeed, the inner outline has been brought towards the
+center, in the upper left and lower right sectors, and this has made the
+digitized inner boundary perfectly symmetric!
+
+\ddanger There's an internal quantity called ^"granularity", normally
+equal to~1, which affects autorounding by effectively scaling~up
+the raster size. If, for example, $"granularity"=4$, the autorounded
+$x$~coordinates and $y$~coordinates will become multiples of~4 instead
+of simply integers. The illustrations above were produced by
+setting $"granularity"=10$ and $"mag"=10$; this made the
+effects of autorounding visible. The granularity should always be an integer.
+
+\ddanger Besides "autorounding", there's a `smoothing' feature
+that becomes active when ^"smoothing"$\null>0$. The basic idea is
+to try to make the edges of a curve follow a regular progression
+instead of wobbling. A complete discussion of the smoothing algorithm
+is beyond the scope of this manual, but an example should make the
+general idea clear: Let's use the letters $R$ and~$D$ to stand for
+single-pixel steps to the right and down, respectively. If a digitized
+path goes `"RDDRDRDDD"', say, the number of downward steps per
+rightward step is first decreasing, then increasing; the "smoothing"
+process changes this to `"RDDRDDRDD"'. If smoothing is applied to the
+Ionian `{\manual\IOO}' shapes above, nothing happens; but if we go back
+to the original obtained with $"autorounding"=0$, we get a few changes:
+\displayfig 24b\&h (190\apspix)
+Three pixels have been added by "smoothing" in the right-hand illustration;
+e.g., a pattern "RDRDDDDRDD" has become "RDDRDDDRDD".
+
+\danger If you do your own rounding, it turns out that autorounding
+and smoothing usually change very few pixels, if any; thus your
+safest strategy is probably to turn them off in such cases. If you
+define your strokes by outlines, autorounding and smoothing
+apply independently to the left and right edges, so they may
+hurt as often as they help; again, they should probably be turned off.
+But if you are drawing with fixed pens, autorounding generally
+works well and saves a lot of fuss. If the pens are circles or
+nearly circles, smoothing is also helpful; but if the pens are
+more ``calligraphic,'' they are supposed to produce nonsmooth
+edges occasionally, so you had better set $"smoothing":=0$.
+
+\ddanger If you ``^{slant}'' a font by modifying "currenttransform"
+as described in Chapter~15, positions of horizontal tangency will
+remain the same. But positions of vertical tangency will change
+drastically, and they will probably not fall in known parts
+of your design. This means, for example, that autorounding will be
+helpful in a slanted pen-generated font like the
+`{\manual 89:;<=>:}\kern2pt' logo. However, the author ^^{Knuth}
+found that the outline-generated letters of ^{Computer Modern}
+{\it^{italic}\/} came out better with $"autorounding"=0$, because
+autorounding tended to make some characters too dark and others too light.
+
+\ninepoint
+
+\ddanger The effect of autorounding can be studied numerically
+if you set ^"tracingspecs" to a positive value; this displays \MF's
+internal calculations as it finds horizontal, vertical, and diagonal
+tangent points. \ (\MF\ prepares to digitize paths by first
+subdividing each B\'ezier segment into pieces that travel in only one
+``^{octant}'' direction.) \ For example, if $"autorounding"=0$
+and $"tracingspecs"=1$, and if "curve\_sidebar" is left unrounded,
+the file |io.log| will contain the following information about the
+outer curve of the `{\manual\IOO}':
+\beginlines \advance\hsize.71pt
+|Path at line 15, before subdivision into octants:|
+|(1.53745,9.05345)..controls (1.53745,4.00511) and (5.75409,-0.00049)|
+| ..(10.85147,-0.00049)..controls (16.2217,-0.00049) and (20.46255,4.51297)|%
+ \kern.5em\null
+| ..(20.46255,9.94655)..controls (20.46255,14.99713) and (16.23842,19.00049)|
+ \kern-.71pt
+| ..(11.13652,19.00049)..controls (5.77066,19.00049) and (1.53745,14.48491)|%
+ \kern.5em\null
+| ..cycle|
+\smallskip
+|Cycle spec at line 15, after subdivision:|
+|(1.53745,9.05345) % beginning in octant `SSE'|
+| ..controls (1.53745,6.58786) and (2.54324,4.371)|
+| ..(4.16621,2.74803) % segment 0|
+|% entering octant `ESE'|
+| ..controls (5.8663,1.04794) and (8.24362,-0.00049)|
+| ..(10.85147,-0.00049) % segment 0|
+|% entering octant `ENE'|
+\endlines
+$\ldots$ and so on; there are lots more numbers! What does this all mean?
+^^|ENE|^^|ESE|^^|SSE|^^{compass directions}
+Well, the first segment of the curve, from $(1.53745,9.05345)$ to
+$(10.85147,-0.00049)$,
+has been subdivided into two parts at the place where the slope is $-1$.
+The first of these parts travels basically `South by South East' and
+the second travels `East by South East'. The other three segments are
+subdivided in a similar way (not shown here). If you try the same
+experiment but with $"autorounding"=1$, some rather different numbers
+emerge: \looseness=-1
+
+\goodbreak
+\beginlines
+|Cycle spec at line 15, after subdivision and autorounding:|
+|(2,9.05348) % beginning in octant `SSE'|
+| ..controls (2,6.50526) and (3.02194,4.22272)|
+| ..(4.6577,2.58696) % segment 0|
+|% entering octant `ESE'|
+| ..controls (6.2624,0.98225) and (8.45786,0)|
+| ..(10.85873,0) % segment 0|
+|% entering octant `ENE'|
+\endlines
+Point $(1.53745,9.05345)$, where there was a vertical tangent, has been
+rounded to $(2,9.05348)$; point $(10.85147,-.00049)$, where there was
+a horizontal tangent, has been rounded to $(10.85873,0)$; the intermediate
+control points have been adjusted accordingly. \ (Rounding of $x$~coordinates
+has been done separately from $y$~coordinates.) \ Finally, with
+$"autorounding"=2$, additional adjustments are made so that the
+$45^\circ$ transition point will occur at what \MF\ thinks is a good spot:
+\beginlines
+|Cycle spec at line 15, after subdivision and double autorounding:|
+|(2,9.05348) % beginning in octant `SSE'|
+| ..controls (2,6.6761) and (3.07103,4.42897)|
+| ..(4.78537,2.71463) % segment 0|
+|% entering octant `ESE'|
+| ..controls (6.46927,1.03073) and (8.62749,0)|
+| ..(10.85873,0) % segment 0|
+|% entering octant `ENE'|
+\endlines
+(Notice that $4.78537+2.71463=7.50000$; when the slope
+is~$-1$ at a transition point $(x,y)$, the curve stays as far away as
+possible from ambiguous points near the transition if $x+y+.5$ is an integer.)
+
+\endchapter
+
+\rightline{\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+SRRRRRRRRRSSSSSSSSSSSRRRRRRSSSSSSSRRRRRRRRRSSSSSSS\cr
+SSRRRSSSSSRRSSSSSSSRRSSSSSSRRSSSSSSRRRSSSSRRSSSSSS\cr
+SSSRRSSSSSSRRSSSSSRRSSSSSSSSRRSSSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSSSRRSSSSSSSSSSRRSSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSRRSSSSSSSSSSSSSSRRSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSRRSSSRRSSSSSSSSSSSSSSRRSSSRRSSSSRRSSSSSS\cr
+SSSRRSSRRRRSSSSRRSSSSSSSSSSSSSSRRSSSRRRRRRSSSSSSSS\cr
+SSSRRSSSSSSSSSSRRSSSSSSSSSSSSSSRRSSSRRSSRRRSSSSSSS\cr
+SSSRRSSSSSSSSSSRRSSSSSSSSSSSSSSRRSSSRRSSSRRRSSSSSS\cr
+SSSRRSSSSSSSSSSRRSSSSSSSSSSSSSSRRSSSRRSSSSRRRSSSSS\cr
+SSSRRSSSSSSSSSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSRRRSSSS\cr
+SSSRRSSSSSSSSSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSSRRSSSS\cr
+SSSRRSSSSSSSSSSSSRRSSSSSSSSSSRRSSSSSRRSSSSSSRRRSSS\cr
+SSSRRSSSSSSSSSSSSSRRSSSSSSSSRRSSSSSSRRSSSSSSSRRRSS\cr
+SSRRRRSSSSSSSSSSSSSRRSSSSSSRRSSSSSSRRRRSSSSSSSRRRS\cr
+SRRRRRRSSSSSSSSSSSSSSRRRRRRSSSSSSSRRRRRRSSSSSSSRRR\cr
+\kern23\Blankpix RRR\cr
+\kern24\Blankpix RRR\cr
+\kern25\Blankpix RRR\cr
+\kern26\Blankpix RRRR\cr
+}}}
+\author PIERRE ^{LE B\'E}, {\sl B\'ele Pr\'erie\/} (1601)
+ % (an extract from his third alphabet)
+
+\bigskip
+\bigskip
+
+\rightline{\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}\kern7\blankpix}\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+\kern19\blankpix PPPPPPPPPP\cr
+QQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPPPQQQQQQPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQPPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPPQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQPQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQPPQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQPPPPQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPPQQQQQQQQPPPPPPQQQQQQQPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQPPPPPPPPQQQQQQPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQPPPPPPPPPQQQQQQPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQPPPPPPPPPPPQQQQQPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQPPPPPPPPPPQQQQPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPPQQQQQQQPPPPPPPPPPQQQPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPQQQPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQPPPPPPPPQQQPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPPPQQQQQQQQPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQQQQPPPPPPPPPPPPPPPQQQQQQPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPQQPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPQQPPPPPPPPPQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPPPPPPPPPPQQQQPPPPPPPPPPQQQQ\cr
+\kern19\blankpix PPPPPPP\kern9\blankpix PPPPPPPPPPP\cr
+\kern36\blankpix PPPPPPPPP\cr
+\kern37\blankpix PPPPPPP\cr
+\kern37\blankpix PPPPPP\cr
+\kern38\blankpix PPPP\cr
+\kern39\blankpix PP\cr
+\kern39\blankpix P\cr
+}}\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPQPPPPPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQPPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPPPQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPPPQ\cr
+}\kern7\blankpix}}
+\author MATTHEW ^{CARTER}, {\sl Bell Centennial\/} (1978)
+ % from the 6pt `name and number' font as he digitized it by hand
+ % reference: Type & Technology Manuscript No.1, Cooper Union (1982)
+
+\eject
+ \beginChapter Chapter 25. Summary of\\Expressions
+
+We've seen that \MF\ can handle a wide variety of algebraic ^{expressions};
+now it's time to consolidate what we have learned. The purpose of this
+chapter and the one that follows is to present a precise and concise
+summary of everything that \MF\ knows how to do.
+
+We shall be concerned here solely with \MF's {\sl^{primitive}\/} operations,
+rather than with the higher-level features of the plain \MF\ base that
+comprise the bulk of typical programs. Therefore novice users should put
+off reading Chapters 25 and~26 until they feel a need to know what
+goes on at the more mundane levels inside the computer. Appendix~B contains
+a summary of the features of plain \MF\!, together with a ready-reference guide
+to the things that most people want to know about \MF\ usage.
+
+\ninepoint\medskip
+The remainder of this chapter is set in small type, like that of the
+present paragraph, since it is analogous to material that is marked
+``doubly dangerous'' in other chapters. Instead of using dangerous
+bend signs repeatedly, let us simply agree that Chapters 25 and~26 are
+dangerous by definition.
+
+Chapter 8 introduced the general idea of expressions and the four-fold
+``primary, secondary, tertiary, expression'' ^{hierarchy} on which
+their syntax is based. \MF's variables can have any of eight types:
+@boolean@, @numeric@, @pair@, @path@, @pen@, @picture@, @string@,
+and @transform@. Its expressions can actually have nine different
+types, although the ninth one---``^{vacuous}''---is not particularly
+interesting since it has only one possible value. Here is the overall
+syntax:
+\beginsyntax
+<primary>\is<boolean primary>\alt<numeric primary>
+ \alt<pair primary>\alt<path primary>
+ \alt<pen primary>\alt<future pen primary>
+ \alt<picture primary>\alt<string primary>
+ \alt<transform primary>\alt<vacuous primary>\endgraf\medskip
+<secondary>\is<boolean secondary>\alt<numeric secondary>
+ \alt<pair secondary>\alt<path secondary>
+ \alt<pen secondary>\alt<future pen secondary>
+ \alt<picture secondary>\alt<string secondary>
+ \alt<transform secondary>\alt<vacuous secondary>\endgraf\medskip
+<tertiary>\is<boolean tertiary>\alt<numeric tertiary>
+ \alt<pair tertiary>\alt<path tertiary>
+ \alt<pen tertiary>\alt<picture tertiary>
+ \alt<string tertiary>\alt<transform tertiary>
+ \alt<vacuous tertiary>\endgraf\medskip
+<expression>\is<boolean expression>\alt<numeric expression>
+ \alt<pair expression>\alt<path expression>
+ \alt<pen expression>\alt<picture expression>
+ \alt<string expression>\alt<transform expression>
+ \alt<vacuous expression>
+\endsyntax
+We shall discuss the different types of expressions in alphabetic order;
+thus, if you are dying to know what a ``vacuous'' expression is,
+you should skip to the end of the chapter. \looseness=-1
+
+\medbreak
+\textindent\bull
+Boolean expressions were discussed in Chapter 19. The full syntax has
+one more operation, `charexists', that was not mentioned there:
+\beginsyntax
+<boolean primary>\is<boolean variable>\alt<boolean argument>
+ \alt[true]\alt[false]
+ \alt[(]<boolean expression>[)]
+ \alt[begingroup]<statement list><boolean expression>[endgroup]
+ \alt[known]<primary>\alt[unknown]<primary>
+ \alt<type><primary>\alt[cycle]<primary>
+ \alt[odd]<numeric primary>
+ \alt[charexists]<numeric primary>
+ \alt[not]<boolean primary>
+<boolean secondary>\is<boolean primary>
+ \alt<boolean secondary>[and]<boolean primary>
+<boolean tertiary>\is<boolean secondary>
+ \alt<boolean tertiary>[or]<boolean secondary>
+<boolean expression>\is<boolean tertiary>
+ \alt<numeric expression><relation><numeric tertiary>
+ \alt<pair expression><relation><pair tertiary>
+ \alt<transform expression><relation><transform tertiary>
+ \alt<boolean expression><relation><boolean tertiary>
+ \alt<string expression><relation><string tertiary>
+<relation>\is[\char'74]\alt[\char'74=]\alt[>]\alt[>=]\alt[=]\alt[\char'74>]
+\endsyntax
+The expression `charexists $x$' is true if and only if a ^@shipout@
+command has previously been done with ^"charcode"$\null=x$. \ (The value
+of~$x$ is first rounded to an integer, and reduced to the range
+$0\le x<256$ by adding or subtracting multiples of~256.)
+
+In these rules, tokens like `|true|' that appear in typewriter type stand for
+any ^{tokens} whose current meaning is the same as the meaning of `|true|'
+when \MF\ starts from scratch; the particular token `|true|'---whose
+meaning may indeed change as a program runs---is not really involved.
+
+The special tokens `|(|' and~`|)|' in these rules do not refer to
+^{parentheses}; they refer to any matching pair of ^{delimiters} defined
+by a ^@delimiters@ command.
+
+A \<boolean variable> denotes a ^\<variable> whose type is @boolean@; a
+$\langle$numeric variable$\rangle$ is a \<variable> whose type is
+@numeric@; and so~on. The syntax for \<variable> was discussed in
+Chapter~7. A \<boolean argument> is an ^@expr@ ^{argument} to a macro,
+where the value of the expression is of type @boolean@;
+@expr@ arguments are put into special ``^{capsule}''
+tokens as explained in Chapter~18.
+
+\medbreak
+\textindent\bull
+Numeric expressions have the richest syntax of all, because they form the
+nucleus of the entire \MF\ language:
+\beginsyntax
+<numeric atom>\is<numeric variable>\alt<numeric argument>
+ \alt<numeric token primary>
+ \alt<internal quantity>
+ \alt[normaldeviate]
+ \alt[(]<numeric expression>[)]
+ \alt[begingroup]<statement list><numeric expression>[endgroup]
+<numeric token primary>\is<numeric token>[/]<numeric token>
+ \alt<numeric token not followed by `{\tt/}$\langle$numeric token$\rangle$'>
+<numeric primary>\is<numeric atom>
+ \alt<numeric atom>[\char'133]<numeric expression>%
+ [,]<numeric expression>[\char'135]
+ \alt[length]<numeric primary>\alt[length]<pair primary>
+ \alt[length]<path primary>\alt[length]<string primary>
+ \alt[ASCII]<string primary>\alt[oct]<string primary>\alt[hex]<string primary>
+ \alt<pair part><pair primary>\alt<transform part><transform primary>
+ \alt[angle]<pair primary>
+ \alt[turningnumber]<path primary>\alt[totalweight]<picture primary>
+ \alt<numeric operator><numeric primary>
+ \alt[directiontime]<pair expression>[of]<path primary>
+<pair part>\is[xpart]\alt[ypart]
+<transform part>\is<pair part>\alt[xxpart]\alt[xypart]\alt[yxpart]\alt[yypart]
+<numeric operator>\is[sqrt]\alt[sind]\alt[cosd]\alt[mlog]\alt[mexp]
+ \alt[floor]\alt[uniformdeviate]\alt<scalar multiplication operator>
+<scalar multiplication operator>\is<plus or minus>
+ \alt<numeric token primary not followed by {\tt+} or {\tt-} or a numeric token>
+<numeric secondary>\is<numeric primary>
+ \alt<numeric secondary><times or over><numeric primary>
+<times or over>\is[*]\alt[/]
+<numeric tertiary>\is<numeric secondary>
+ \alt<numeric tertiary><plus or minus><numeric secondary>
+ \alt<numeric tertiary><Pythagorean plus or minus><numeric secondary>
+<plus or minus>\is[+]\alt[-]
+<Pythagorean plus or minus>\is[++]\alt[+-+]
+<numeric expression>\is<numeric tertiary>
+\endsyntax
+Each of the operations mentioned in this syntax has already been explained
+somewhere in this book; Appendix~I tells where.
+
+\medbreak
+This is a good time to list all of the internal quantities that are
+initially present in \MF:
+\begindisplay
+^"tracingtitles"&show titles online when they appear\cr
+^"tracingequations"\hidewidth&show each variable when it becomes known\cr
+^"tracingcapsules"\hidewidth&show capsules as well as variables\cr
+^"tracingchoices"&show the control points chosen for paths\cr
+^"tracingspecs"&show subdivision of paths into octants before digitizing\cr
+^"tracingpens"&show vertices of pens as they are made from future pens\cr
+^"tracingcommands"\hidewidth
+ &show commands and operations before they're performed\cr
+^"tracingrestores"&show when a symbol or internal quantity is restored\cr
+^"tracingmacros"&show macros before they are expanded\cr
+^"tracingedges"&show digitized edges as they are computed\cr
+^"tracingoutput"&show digitized edges as they are output\cr
+^"tracingonline"&show long diagnostics on the terminal and in the log\cr
+^"tracingstats"&log the memory usage at end of job\cr
+^"pausing"&show lines on the terminal before they are read\cr
+^"showstopping"&stop after each @show@ command\cr
+^"fontmaking"&produce font metric output\cr
+^"proofing"&produce proof mode output\cr
+^"turningcheck"&reorient clockwise paths, flag strange ones\cr
+^"warningcheck"&advise when a variable value gets large\cr
+^"smoothing"&remove certain glitches from digitized curves\cr
+^"autorounding"&move paths to ``good'' tangent points\cr
+^"granularity"&the pixel size for "autorounding"\cr
+^"fillin"&the extra darkness of diagonals (to be counteracted)\cr
+^"year"&the current year (e.g., 1986)\cr
+^"month"&the current month (e.g., 3 $\equiv$ March)\cr
+^"day"&the current day of the month\cr
+^"time"&the number of minutes past midnight when job started\cr
+^"charcode"&the number of the next character to be output\cr
+^"charext"&the extension code of the next character to be output\cr
+^"charwd"&the width of the next character to be output, in points\cr
+^"charht"&the height of the next character to be output, in points\cr
+^"chardp"&the depth of the next character to be output, in points\cr
+^"charic"&the italic correction of the next character, in points\cr
+^"chardx"&the device's $x$ movement for the next character, in pixels\cr
+^"chardy"&the device's $y$ movement for the next character, in pixels\cr
+^"designsize"&the approximate size of the current typeface, in points\cr
+^"hppp"&the number of horizontal pixels per point\cr
+^"vppp"&the number of vertical pixels per point\cr
+^"xoffset"&the horizontal displacement of shipped-out characters\cr
+^"yoffset"&the vertical displacement of shipped-out characters\cr
+^"boundarychar"&the right boundary character for ligatures and kerns\cr
+\enddisplay
+All of these quantities are numeric. They are initially zero at the
+start of a job, except for "year", "month", "day", and "time", which
+are initialized to the time the run began; furthermore, "boundarychar" is
+initially~$-1$. A "granularity" of zero is equivalent to $"granularity"=1$.
+A preloaded base file like plain \MF\ will usually give nonzero values to
+several other internal quantities on this list.
+
+\medbreak
+\textindent\bull
+Now we come to expressions of type @pair@, which are the second most
+important elements of \MF\ programs:
+\beginsyntax
+<pair primary>\is<pair variable>\alt<pair argument>
+ \alt[(]<numeric expression>[,]<numeric expression>[)]
+ \alt[(]<pair expression>[)]
+ \alt[begingroup]<statement list><pair expression>[endgroup]
+ \alt<numeric atom>[\char'133]<pair expression>[,]<pair expression>[\char'135]
+ \alt<scalar multiplication operator><pair primary>
+ \alt[point]<numeric expression>[of]<path primary>
+ \alt[precontrol]<numeric expression>[of]<path primary>
+ \alt[postcontrol]<numeric expression>[of]<path primary>
+ \alt[penoffset]<pair expression>[of]<pen primary>
+ \alt[penoffset]<pair expression>[of]<future pen primary>
+<pair secondary>\is<pair primary>
+ \alt<pair secondary><times or over><numeric primary>
+ \alt<numeric secondary>[*]<pair primary>
+ \alt<pair secondary><transformer>
+<transformer>\is[rotated]<numeric primary>
+ \alt[scaled]<numeric primary>
+ \alt[shifted]<pair primary>
+ \alt[slanted]<numeric primary>
+ \alt[transformed]<transform primary>
+ \alt[xscaled]<numeric primary>
+ \alt[yscaled]<numeric primary>
+ \alt[zscaled]<pair primary>
+<pair tertiary>\is<pair secondary>
+ \alt<pair tertiary><plus or minus><pair secondary>
+ \alt<path tertiary>[intersectiontimes]<path secondary>
+<pair expression>\is<pair tertiary>
+\endsyntax
+A pair is a special case of a path (namely, it's a path of length zero);
+Chapter 19 explains that \MF\ doesn't change the type from pair to path
+unless there is no other way to meet the syntax rules.
+
+\medbreak
+\textindent\bull
+Speaking of paths, they come next in our survey:
+\beginsyntax
+<path primary>\is<pair primary>\alt<path variable>\alt<path argument>
+ \alt[(]<path expression>[)]
+ \alt[begingroup]<statement list><path expression>[endgroup]
+ \alt[makepath]<pen primary>\alt[makepath]<future pen primary>
+ \alt[reverse]<path primary>
+ \alt[subpath]<pair expression>[of]<path primary>
+<path secondary>\is<pair secondary>\alt<path primary>
+ \alt<path secondary><transformer>
+<path tertiary>\is<pair tertiary>\alt<path secondary>
+<path expression>\is<pair expression>\alt<path tertiary>
+ \alt<path subexpression><direction specifier>
+ \alt<path subexpression><path join>[cycle]
+<path subexpression>\is<path expression not ending with direction specifier>\kern-5pt\null
+ \alt<path subexpression><path join><path tertiary>
+<path join>\is<direction specifier><basic path join><direction specifier>
+<direction specifier>\is<empty>
+ \alt[\char'173][curl]<numeric expression>[\char'175]
+ \alt[\char'173]<pair expression>[\char'175]
+ \alt[\char'173]<numeric expression>[,]<numeric expression>[\char'175]
+<basic path join>\is[\&]
+ \alt[..]
+ \alt[..]<tension>[..]
+ \alt[..]<controls>[..]
+<tension>\is[tension]<tension amount>
+ \alt[tension]<tension amount>[and]<tension amount>
+<tension amount>\is<numeric primary>
+ \alt[atleast]<numeric primary>
+<controls>\is[controls]<pair primary>
+ \alt[controls]<pair primary>[and]<pair primary>
+\endsyntax
+Chapter 14 tells all about path creation.
+
+\medbreak
+\textindent\bull
+Pens and future pens coexist as follows:
+\beginsyntax
+<pen primary>\is<pen variable>\alt<pen argument>
+ \alt[nullpen]
+ \alt[(]<pen expression>[)]
+ \alt[begingroup]<statement list><pen expression>[endgroup]
+<future pen primary>\is[pencircle]
+ \alt[makepen]<path primary>
+<pen secondary>\is<pen primary>
+<future pen secondary>\is<future pen primary>
+ \alt<future pen secondary><transformer>
+ \alt<pen secondary><transformer>
+<pen tertiary>\is<pen secondary>
+ \alt<future pen secondary>
+<pen expression>\is<pen tertiary>
+\endsyntax
+See Chapter 16 for a thorough discussion of pen usage.
+
+\medbreak
+\textindent\bull
+Pictures can be null, added, or subtracted:
+\beginsyntax
+<picture primary>\is<picture variable>\alt<picture argument>
+ \alt[nullpicture]
+ \alt[(]<picture expression>[)]
+ \alt[begingroup]<statement list><picture expression>[endgroup]
+ \alt<plus or minus><picture primary>
+<picture secondary>\is<picture primary>
+ \alt<picture secondary><transformer>
+<picture tertiary>\is<picture secondary>
+ \alt<picture tertiary><plus or minus><picture secondary>
+<picture expression>\is<picture tertiary>
+\endsyntax
+Chapter 13 is the definitive reference for picture operations.
+
+\medbreak
+\textindent\bull
+Strings are still fresh in our minds from Chapter 22, but we should
+repeat the syntax again for completeness here.
+\beginsyntax
+<string primary>\is<string variable>\alt<string argument>
+ \alt<string token>
+ \alt[jobname]
+ \alt[readstring]
+ \alt[(]<string expression>[)]
+ \alt[begingroup]<statement list><string expression>[endgroup]
+ \alt[str]<suffix>
+ \alt[char]<numeric primary>
+ \alt[decimal]<numeric primary>
+ \alt[substring]<pair primary>[of]<string primary>
+<string secondary>\is<string primary>
+<string tertiary>\is<string secondary>
+<string expression>\is<string tertiary>
+ \alt<string expression>[\&]<string tertiary>
+\endsyntax
+There's nothing more to say about strings.
+
+\medbreak
+\textindent\bull
+Chapter 15 explains transforms, but gives no formal syntax. The rules are:
+\beginsyntax
+<transform primary>\is<transform variable>\alt<transform argument>
+ \alt[(]<transform expression>[)]
+ \alt[begingroup]<statement list><transform expression>[endgroup]
+<transform secondary>\is<transform primary>
+ \alt<transform secondary><transformer>
+<transform tertiary>\is<transform secondary>
+<transform expression>\is<transform tertiary>
+\endsyntax
+Note that ^"identity" doesn't appear here; it is a variable defined
+in Appendix~B\null, not a primitive of the language.
+
+\medbreak
+\textindent\bull
+Finally, we come to the new kind of expression, which wasn't mentioned
+in previous chapters because it is so trivial.
+\beginsyntax
+<vacuous primary>\is<vacuous argument>
+ \alt<compound>
+ \alt[(]<vacuous expression>[)]
+ \alt[begingroup]<statement list><vacuous expression>[endgroup]
+<vacuous secondary>\is<vacuous primary>
+<vacuous tertiary>\is<vacuous secondary>
+<vacuous expression>\is<vacuous tertiary>
+\endsyntax
+A \<compound> is defined in Chapter 26.
+
+\ddangerexercise Construct minimal examples of each of the
+nine types of expression (boolean, numeric, \dots,~vacuous).
+You should use only ``^{sparks}'' in your constructions, not \<tag>
+tokens or capsules; in particular, variables are not permitted
+(otherwise this exercise would be too easy). Your expressions should
+be as short as possible in the sense of {\sl fewest tokens\/}; the number
+of keystrokes needed to type them is irrelevant.
+\answer By looking at the syntax rules, we find, for example,
+\begindisplay
+\<boolean expression>&|true|\cr
+\<numeric expression>&|0|\cr
+\<pair expression>&|(0,0)|\cr
+\<path expression>&|makepath pencircle|\cr
+\<pen expression>&|nullpen|\cr
+\<picture expression>&|nullpicture|\cr
+\<string expression>&|""|\cr
+\<transform expression>&Impossible!\cr
+\<vacuous expression>&|begingroup endgroup|\cr
+\enddisplay
+Every \<transform expression> includes either a variable or a capsule.
+Incidentally, there are some amusing alternative 5-token solutions for
+\<pair expression>:
+\begintt
+postcontrol 0 of makepath nullpen
+makepath pencircle intersectiontimes makepath nullpen
+\endtt
+
+\endchapter
+
+This is of you very well remembred,
+and well and sommaryly rehersed.
+\author THOMAS ^{MORE}, {\sl A Dialogue Concernynge Heresyes\/} (1529)
+ % Bk 2, Ch 1
+ % p178 ll C7--8 in 1557 edition, where the spelling is slightly different
+
+\bigskip
+
+Below the tomato blobs was a band of white with vertical black stripes,
+to which he could assign no meaning whatever,
+till some one else came by, murmuring:
+``What expression he gets with his foreground!''
+.\thinspace.\thinspace. %
+Ah, they were all Expressionists now, he had heard, on the Continent.
+So it was coming here too, was it?
+\author JOHN ^{GALSWORTHY}, {\sl To Let\/} (1921) % Chapter 1, p13
+
+\eject
+ \beginchapter Chapter 26. Summary of\\the Language
+
+The grand tour of \MF's syntax that was begun in the previous chapter
+is concluded in this one, so that a complete reference guide is
+available for people who need to know the details.
+\ (Another summary appears in Appendix~B.)
+
+\ninepoint\medskip
+\MF\ actually has a few features that didn't seem to be worth mentioning
+in earlier chapters, so they will be introduced here as part of our
+exhaustive survey. If there is any disagreement between something that
+was said previously and something that will be said below, the facts
+in the present chapter should be regarded as better approximations
+to the ^{truth}.
+
+We shall study \MF's digestive processes, i.e., what \MF\ does in
+response to the tokens that arrive in its ``stomach.''
+^^{anatomy of METAFONT}
+Chapter~6 describes the process by which input files are converted to
+lists of tokens in \MF's ``mouth,'' and Chapters 18--20 explain how
+expandable tokens are converted to unexpandable ones in \MF's ``gullet''
+by a process similar to regurgitation. In particular, conditions and
+loops are handled by the expansion mechanism, and we need not
+discuss them further. When unexpandable tokens
+finally reach \MF's gastro-intestinal tract, the real activities
+begin; expressions are evaluated, equations are solved, variables are
+declared, and commands are executed. In this chapter we shall discuss the
+primitive operations that actually draw pictures and produce output.
+
+Let's start by looking at the full syntax for \<program> and for
+\<statement>:
+\beginsyntax
+<program>\is<statement list>[end]\alt<statement list>[dump]
+<statement list>\is<empty>\alt<statement>[;]<statement list>
+<statement>\is<empty>\alt<title>
+ \alt<equation>\alt<assignment>
+ \alt<declaration>\alt<definition>
+ \alt<compound>\alt<command>
+<title>\is<string expression>
+<compound>\is[begingroup]<statement list><non-title statement>[endgroup]
+<command>\is<save command>
+ \alt<interim command>
+ \alt<newinternal command>
+ \alt<randomseed command>
+ \alt<let command>
+ \alt<delimiters command>
+ \alt<protection command>
+ \alt<everyjob command>
+ \alt<show command>
+ \alt<message command>
+ \alt<mode command>
+ \alt<picture command>
+ \alt<display command>
+ \alt<openwindow command>
+ \alt<shipout command>
+ \alt<special command>
+ \alt<font metric command>
+\endsyntax
+The \<empty> statement does nothing, but it is very handy because you can
+always feel safe when you put extra semicolons between statements.
+A \<title> does almost nothing, but it provides useful documentation
+as explained in Chapter~22.
+The syntax of \<equation> and \<assignment> can be found in Chapter~10;
+\<declaration> is in Chapter~7; \<definition> is in Chapters 18 and~20.
+We shall concentrate in this chapter on the various types of {\sl
+^{commands}}, especially on those that haven't been mentioned before.
+\beginsyntax
+<save command>\is[save]<symbolic token list>
+<symbolic token list>\is<symbolic token>
+ \alt<symbolic token list>[,]<symbolic token>
+<interim command>\is\kern-1.5pt[interim]%
+ <internal quantity>[:=]<right-hand side>\kern-1pt
+\endsyntax
+The @save@ and @interim@ commands cause values to be restored at the end
+of the current group, as discussed in Chapter~17.
+\beginsyntax
+<newinternal command>\is[newinternal]<symbolic token list>
+\endsyntax
+Each of the symbolic tokens specified in a @newinternal@ command will
+henceforth behave exactly as an \<internal quantity>, initially zero.
+Thus, they can be used in @interim@ commands; they are ^{tags} but not
+^{external tags} (see Chapter~7). Since \MF\ can access internal
+quantities quickly, you can use them to gain efficiency.
+\beginsyntax
+<randomseed command>\is[randomseed][:=]<numeric expression>
+\endsyntax
+The @randomseed@ command specifies a ``seed'' value that defines
+the pseudo-random numbers to be delivered by
+`uniformdeviate' and `normaldeviate' (cf.~Chapter~21).
+The default value, if you don't specify your own seed, is
+^^"day" ^^"time" $"day"+"time"\ast"epsilon"$.
+\beginsyntax
+<let command>\is[let]<symbolic token><is><symbolic token>
+<is>\is[=]\alt[:=]
+\endsyntax
+The @let@ command changes the current meaning of the left-hand token
+to the current meaning of the right-hand token. For example,
+after `@let@ $"diamonds"=@forever@$', the token "diamonds" will
+introduce loops. If the left-hand token was the first token of
+any variable names, those variables all disappear. If the right-hand
+token was the first token in any variable names, those variables
+remain unchanged, and the left-hand token becomes
+an unknown, independent variable. \ (The purpose of @let@ is to redefine
+primitive meanings or macro meanings, not to equate variables in any way.)
+\ If the right-hand symbol is one of a pair of matching delimiters,
+the subsequent behavior of the left-hand symbol is undefined.
+For example, it's a bad idea to say `@let@~$[\,[=($;~@let@~$]\,]=)$'.
+\beginsyntax
+<delimiters command>\is[delimiters]<symbolic token><symbolic token>
+\endsyntax
+The @delimiters@ command gives new meanings to the two symbolic tokens;
+henceforth they will match each other (and only each other). For example,
+Appendix~B says `@delimiters@~()'; without this command, parentheses
+would be ordinary symbolic tokens. Any distinct symbolic tokens can be
+defined to act as delimiters, and many different pairs of delimiters
+can be in use simultaneously.
+\beginsyntax
+<protection command>\is[outer]<symbolic token list>
+ \alt[inner]<symbolic token list>
+\endsyntax
+A ``^{forbidden}'' stamp is added to or removed from symbolic tokens
+by an @outer@ or @inner@ command, without changing the essential meanings
+of those tokens. A token that has been called @outer@ should not appear
+when \MF\ is skipping over tokens at high speed; the program will stop
+and insert an appropriate delimiter, if an @outer@ token is sensed in
+the wrong place, since such tokens are supposed to occur only at
+``quiet'' times. \ (Unquiet times occur when \MF\ is skipping tokens
+because of a false ^{condition}, or because it is reading the ^{replacement
+text} of a macro or the ^{loop text} of a loop, or because it is scanning
+the ^{text argument} to a macro, or because it is ^{flushing} erroneous
+tokens that were found at the end of a statement.) \ Without such
+protection, a missing right delimiter could cause \MF\ to eat up your
+whole program before any error was detected; @outer@ tokens keep such
+errors localized. An @inner@ command undoes the effect of @outer@; so
+does `@let@', and so does any other command or definition that changes the
+meaning of a symbolic token. All tokens are initially @inner@.
+\beginsyntax
+<everyjob command>\is[everyjob]<symbolic token>
+\endsyntax
+The command `@everyjob@$\,S$' tells \MF\ that token $S$ should be inserted
+first, just before the input file is read, when a job starts. \ (This
+is meaningful only in a base file that will be loaded or preloaded
+at the beginning of a run; it is analogous to \TeX's |\everyjob| command.)
+\beginsyntax
+<show command>\is[show]<expression list>
+ \alt[showvariable]<symbolic token list>
+ \alt[showtoken]<symbolic token list>
+ \alt[showdependencies]
+ \alt[showstats]
+\endsyntax
+A simple @show@ command displays the value of each expression, in turn.
+Paths, pens, and pictures are shown only in the transcript file, unless
+^"tracingonline" is positive. The @showvariable@ command gives the
+structure of all variables that begin with a given external tag,
+together with their values in an abbreviated form; this allows you to see
+which of its subscripts and attributes have occurred. For example, if you're
+using plain \MF\ conventions, `@showvariable@~$x,y$' will show all
+coordinates that have been defined since the last @beginchar@. The @showtoken@
+command gives the current meaning of a token, so that you can tell whether
+it is primitive or not, @outer@ or not. (If @showvariable@ is applied to
+a spark instead of a tag, it gives the same information as @showtoken@.)
+\ Every unknown numeric variable that's currently dependent is shown by
+@showdependencies@ (except that unknown capsules are shown only
+when ^"tracingcapsules" is positive). And finally, @showstats@ gives
+information about \MF's current memory usage.
+Each of these commands will stop and say `|!|~^|OK.|', if the internal
+quantity "showstopping" has a positive value; this gives you a chance
+to enter more @show@ commands ^{interactive}ly, while you're trying to
+debug a program.
+\beginsyntax
+<message command>\is<message op><string expression>
+<message op>\is[message]\alt[errmessage]\alt[errhelp]
+\endsyntax
+Communication with the user is possible via @message@, @errmessage@,
+and @errhelp@, as discussed in Chapter~22.
+\beginsyntax
+<mode command>\is[batchmode]\alt[nonstopmode]
+ \alt[scrollmode]\alt[errorstopmode]
+\endsyntax
+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} \MF; @scrollmode@,
+@nonstopmode@, and @batchmode@ are the modes you get into by hitting
+`|S|', `|R|', or `|Q|', respectively, in response to error messages
+(cf.~Chapter~5).
+\beginsyntax
+<picture command>\is<addto command>\alt<cull command>
+<addto command>\is[addto]<picture variable>[also]<picture expression>
+ \alt[addto]<picture variable>[contour]<path expression><with list>
+ \alt[addto]<picture variable>[doublepath]<path expression><with list>
+<with list>\is<empty>\alt<with list><with clause>
+<with clause>\is[withpen]<pen expression>%
+ \alt[withweight]<numeric expression>\kern-3.5pt
+<cull command>\is[cull]<picture variable><keep or drop><pair expression>
+ \alt<cull command>[withweight]<numeric expression>
+<keep or drop>\is[keeping]\alt[dropping]
+\endsyntax
+The @addto@ and @cull@ commands are the principal means of making
+changes to pictures; they are discussed fully in Chapter~13.
+\beginsyntax
+<display command>\is[display]<picture variable>[inwindow]<window>
+<window>\is<numeric expression>
+<openwindow command>\is[openwindow]<window><window spec>
+<window spec>\is<screen place>[at]<pair expression>
+<screen place>\is[from]<screen coordinates>[to]<screen coordinates>
+<screen coordinates>\is<pair expression>
+\endsyntax
+Chapter~23 explains how to display stuff on your screen via @display@
+and @openwindow@\kern-1pt.
+\beginsyntax
+<shipout command>\is[shipout]<picture expression>
+\endsyntax
+You may have wondered how \MF\ actually gets pictorial information into
+a font. Here at last is the answer: `@shipout@~$v$' puts the pixels of
+positive weight, as defined by the picture expression~$v$, into a ^{generic
+font} output file, where they will be the bitmap image associated with
+character number $"charcode"\bmod256+"charext"\ast256$. The pixels of~$v$
+are shifted by $("xoffset","yoffset")$ as they are shipped out.
+\ (However, no output is done if ^"proofing"$\null<0$. The values of
+^"xoffset", ^"yoffset", ^"charcode", and ^"charext" are first rounded to
+integers, if necessary.) \ This command also saves the values of
+^"charwd", ^"charht", ^"chardp", ^"charic", ^"chardx", and "chardy"; they
+will be associated with the current "charcode" when ^{font metric
+information} is produced. \ (See Appendices F and~G for the basic
+principles of font metric information and generic font files.)
+\beginsyntax
+<special command>\is[special]<string expression>
+ \alt[numspecial]<numeric expression>
+\endsyntax
+The @special@ and @numspecial@ commands send alphabetic and numeric
+information
+to the generic font output file, if "proofing" is nonnegative.
+For example, the labels on proofsheets are specified in this
+way by macros of plain \MF\!\null. Appendices G and~H provide further details.
+
+\medbreak
+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 defining \<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 \MF\ logo program of Chapter~11.
+
+\medbreak
+And there's one more loose end to tie up, going back to the very
+first syntax rule in this chapter: The token `^@dump@' can be
+substituted for `^@end@', if a special version of \MF\ called
+`^|INIMF|' is being used. This writes a file containing the macros
+defined so far, together with the current values of variables and
+the current meanings of symbolic tokens, so
+that they can be loaded as a base file. \ (It is analogous to
+\TeX's |\dump| command.) \ Base files are discussed at the end of
+Appendix~B.
+
+\ddangerexercise Run \MF\ with the input ^^@outer@ ^^@delimiters@ ^^@showtoken@
+\begintt
+\newinternal a;
+let b=a; outer a,b,c;
+let c=b; delimiters a::;
+showtoken a,b,c; end
+\endtt
+and explain the computer's responses.
+\answer The responses are
+\begintt
+> a=left delimiter that matches ::
+> b=(outer) a
+> c=a
+\endtt
+because: $a$ has been redefined from internal quantity to delimiter;
+$b$~is still an internal quantity (named~$a$), and it has been stamped
+@outer@; $c$~denotes the same internal quantity, but it hasn't got outerness.
+
+\endchapter
+
+Our life is frittered away by detail.
+An honest man has hardly need
+to count more than his ten fingers,
+or in extreme cases he may add his ten toes,
+and lump the rest. Simplicity, simplicity, simplicity!
+I say, let your affairs be as two or three,
+and not a hundred or a thousand .\thinspace.\thinspace.
+Simplify, simplify.
+\author HENRY DAVID ^{THOREAU}, {\sl Walden\/} (1854) % 1st ed, ch2, graf15
+
+\bigskip
+
+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 fingertips
+will be acted upon unerringly.
+\author HERMANN ^{ZAPF}, {\sl The Ten Commandments of %
+ Photo\kern1pt-\kern-1ptTypesetting\/} (1982) % 2nd Commandment
+
+\eject
+ \beginchapter Chapter 27. Recovery\\from\\Errors
+
+OK, everything you need to know about \MF\ has been explained---unless you
+happen to be fallible. If you don't plan to make any errors, don't bother to
+read this chapter. Otherwise you might find it helpful to make use of some
+of the ways that \MF\ tries to pinpoint bugs in your programs.
+
+In the trial runs you did when reading Chapter 5, you learned the general
+form of ^{error messages}, and you also learned the various ways in which
+you can respond to \MF's complaints. With practice, you will be able to
+correct most errors ``online,'' as soon as \MF\ 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, \MF\ doesn't always
+diagnose your errors correctly, since the number of ways to misunderstand
+the rules is vast; \MF\ 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 \MF\ 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 \MF's reasons for its actions.
+
+Ideally you'll be in a mellow mood when you approach \MF\!, and you will
+regard any error
+messages as amusing puzzles---``Why did the machine do
+that?''---rather than as personal insults.
+\MF\ knows how to issue more than a hundred different 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 file of Chapter~5, since it
+has more to teach us. If you have a better memory than the author, you'll
+recall that the first error message was
+\begintt
+>> mode.setup
+! Isolated expression.
+<to be read again>
+ ;
+l.1 mode setup;
+ % an intentional error!
+?
+\endtt
+In Chapter 5 we just charged ahead at this point, but it would be more
+^^{!} ^^{to be read again}
+normal for a mature \MF er to think ``Shucks, I meant to type
+`|mode_setup|', but I forgot the underscore. Luckily this didn't cause
+any harm; \MF\ 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~$\ldots$~sorry.
+Lots of error messages occur before \MF\ has read a ^{semicolon} in
+preparation for another ^{statement}; the important clue in this case
+comes from the two lines
+\begintt
+<to be read again>
+ ;
+\endtt
+which tell us that the semicolon is still pending. So the correct
+response would have been to type `|I;|~|mode_setup|' instead. Without
+the semicolon, you get what appears at first to be a horrible mess:
+\begintt
+! Extra tokens will be flushed.
+<to be read again>
+ warningcheck
+mode_setup->warningcheck
+ :=0;if.unknown.mode:mode=proof;fi...
+<insert> mode_setup
+|quad
+<to be read again>
+ ;
+l.1 mode setup;
+ % an intentional error!
+?
+\endtt
+But relax, there's a simple way out. The help message says
+^^|Extra tokens will be flushed| ^^{flushing}
+`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 effect will be the same as if you had correctly inserted a semicolon
+before |mode_setup| in the first place.
+
+The moral of this story is: {\sl When you insert a new statement during
+error recovery, you frequently need to put a semicolon just ahead of~it.}
+But if you forget, \MF\ gives you another chance.
+
+After proceeding through |badio| with the interactions suggested in
+^^|Undefined coordinate| ^^{misspelling} ^^{typographic errors}
+Chap\-ter~5, we will come again to the error
+\begintt
+>> 0.08682thinn+144
+! Undefined x coordinate has been replaced by 0.
+\endtt
+(This is where the erroneous `|thinn|' was detected.) \ The help message for
+this error has some curious advice:
+\begintt
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+\endtt
+Chapter 27? That's us! What happens if we do type `|I ???|' now? We get
+\begintt
+x4l=0.08682thinn+144
+y4=-0.4924thinn+259.0005
+x4r=-0.08682thinn+144
+y4r=-0.9848thinn+259.0005
+! OK.
+\endtt
+It is now abundantly clear that `|thin|' was misspelled. Plain \MF\
+defines `^|???|' 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.
+
+One more example of online error correction should suffice to make
+the general strategy clear. Suppose you accidentally type square brackets
+instead of parentheses; the computer will scream:
+\begintt
+! A primary expression can't begin with `['.
+<inserted text>
+ 0
+<to be read again>
+ [
+<*> show round[
+ 1 + sqrt43];
+?
+\endtt
+(By coincidence, the help message for this particular error also refers to
+Chapter~27.) \ When \MF\ 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
+\begintt
+<*> show round[1 + sqrt43]
+ ;
+?
+\endtt
+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 Why was `|7|' the right number of tokens to delete?
+\answer We want to delete
+\begindisplay
+\ttok{0}\quad\ttok{[}\quad\ttok{1}\quad\ttok{+}\quad\ttok{sqrt}\quad
+\ttok{43}\quad\ttok{]}
+\enddisplay
+from the sequence of tokens that \MF\ is about to read next, in order to
+get rid of the right bracket, which we can see is going to be just as
+erroneous as the left bracket was. However, there is another way to
+proceed (and indeed, this alternative would be preferable to counting
+tokens, if the bracketed expression were longer): We could simply
+^^{delimiter} ^^|Missing token has been inserted|
+delete 2~tokens, then `|I(|'. This would produce another error stop,
+\begintt
+! Missing `)' has been inserted.
+<to be read again>
+ ]
+<*> show round[1 + sqrt43]
+ ;
+? h
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+?
+\endtt
+after which it's easy to delete the `|]|' and continue successfully.
+
+\dangerexercise If the user hadn't deleted or inserted anything, but had
+just plunged ahead, \MF\ would have come up with another error:
+\begintt
+>> 0
+! Extra tokens will be flushed.
+<to be read again>
+ [
+<to be read again>
+ (7.55743)
+<to be read again>
+ ]
+<*> show round[1 + sqrt43]
+ ;
+?
+\endtt
+Explain what happened. What should be done next?
+\answer \MF\ looked ahead, to see if the expression being evaluated
+was going to be something like `|round 0[1+sqrt43,x]|'. But when it
+found no comma, it put back several tokens so that they could be
+read again. \ (The subexpression |1+sqrt43| had already been evaluated,
+so a ``^{capsule}'' for its value, 7.55743, was inserted among the
+tokens to be reread.) \ The expression ended with `0', and `round~0' was
+shown. Then \MF\ found extra tokens following the @show@ command; a
+semicolon should have come next. To continue, the user should just plunge
+ahead recklessly once again, letting \MF\ delete those unwanted tokens.
+
+It's wise to remember that the first error in your program may well spawn
+spurious ``errors'' later on, because anomalous commands can inflict
+serious injury on \MF's ability to cope with the subsequent material.
+But most of the time you will find that a single run through the
+machine will locate all of the places in which your input conflicts
+with \MF's rules.
+
+\danger Sometimes an error is so bad that \MF\ is forced to quit
+prematurely. For example, if you are running in ^@batchmode@ or
+^@nonstopmode@, \MF\ makes an ``^{emergency stop}'' if it needs
+input from the terminal; this happens when a necessary file can't
+be opened, or when no ^@end@ was found in the input.
+Here are some of the messages you might get just before
+\MF\ gives up the ghost: \enddanger
+
+{\ninepoint
+\def\fatal#1. {\medbreak{\tt#1.}\par\nobreak\smallskip\noindent\ignorespaces}
+\fatal
+Fatal base file error; I'm stymied.
+^^|Fatal base file error|
+This means that the preloaded base you have specified cannot be used,
+because it was prepared for a different version of \MF\!.
+\fatal
+That makes 100 errors; please try again.
+\MF\ has scrolled past 100 errors since the last statement ended, so
+it's probably in an~endless ^{loop}. ^^{infinite loop}
+\fatal
+I can't go on meeting you like this.
+^^|I can't go on|
+A previous error has gotten \MF\ out of whack. Fix it and try again.
+\fatal
+This can't happen.
+^^|This can't happen|
+Something is wrong with the \MF\ you are using. Complain fiercely.
+\goodbreak}
+
+\danger There's also a dreadful message that \MF\ issues only with
+great reluctance. But it can happen:
+\begintt
+METAFONT capacity exceeded, sorry.
+\endtt
+^^|METAFONT capacity exceeded|
+This, alas, means that you have tried to stretch \MF\ too far. The
+message will tell you what part of \MF's memory has become overloaded;
+one of the following nineteen things will be mentioned:
+\begindisplay
+|number of strings|\qquad(strings and names of symbolic tokens and files)\cr
+|pool size|\qquad(the characters in such strings)\cr
+|main memory size|\qquad(pairs, paths, pens, pictures, token lists,
+ transforms, etc.)\cr
+|hash size|\qquad(symbolic token names)\cr
+|input stack size|\qquad(simultaneous input sources)\cr
+|number of internals|\qquad(internal quantities)\cr
+|rounding table size|\qquad(transitions between octants in cycles)\cr
+|parameter stack size|\qquad(macro parameters)\cr
+|buffer size|\qquad(characters in lines being read from files)\cr
+|text input levels|\qquad(@input@ files and error insertions)\cr
+|path size|\qquad(key points per path)\cr
+|move table size|\qquad(rows of picture being simultaneously accessed)\cr
+|pen polygon size|\qquad(pen offsets per octant)\cr
+|ligtable size|\qquad(accumulated @ligtable@ instructions)\cr
+|kern|\qquad(distinct kern amounts)\cr
+|extensible|\qquad(built-up characters)\cr
+|headerbyte|\qquad(largest @headerbyte@ address)\cr
+|fontdimen|\qquad(largest @fontdimen@ address)\cr
+|independent variables|\qquad(distinct numeric variables)\cr
+\enddisplay
+The current amount of memory available will also be shown.
+
+\danger If you have a job that doesn't overflow \MF's capacity, yet
+you want to see just how closely you have approached the limits,
+just set ^"tracingstats" to a positive value before the end of your
+job. The log file will then conclude with a report on your actual
+usage of the first nine things named above (i.e., the number of strings,
+\dots, the buffer size), in that order. ^^{stack positions}
+Furthermore, the @showstats@ command can be used to discover the current
+string memory and main ^{memory usage} at any time 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.
+
+\danger What can be done if \MF'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 \MF\!\null.
+If you have an especially important application, you may be able
+to convince your local system people to provide you with a special
+\MF\ whose capacities have been hand-tailored to your needs.
+But before taking such a drastic step, be sure that you are using
+\MF\ properly. If you have specified a gigantic picture that has
+lots of transitions between black and white pixels, you should
+change your approach, because \MF\ has to remember every change between
+adjacent pixel values in every currently accessible picture.
+If you keep saving different pens, you might be wasting memory as
+discussed in Chapter~16. If you have built up an enormous macro library,
+you should realize that \MF\ has to remember all of the replacement texts
+that you define; therefore if memory space is in short supply, you should
+load only the macros that you need.
+
+\danger Some erroneous \MF\ programs will overflow any finite
+memory capacity. For example, after `|def recurse=(recurse)enddef|', the
+^^{recursion} use of |recurse| will immediately bomb out:
+\begintt
+! METAFONT capacity exceeded, sorry [input stack size=30].
+recurse->(recurse
+ )
+recurse->(recurse
+ )
+recurse->(recurse
+ )
+...
+\endtt
+The same sort of error will obviously occur no matter how much you increase
+\MF's input stack size.
+
+\danger Most implementations of \MF\ allow you to ^{interrupt} the program
+in some way. This makes it possible to diagnose the causes of ^{infinite
+loops}, if the machine doesn't stop because of memory limitations.
+\MF\ 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
+commands, for example by typing
+\begintt
+I hide(showstopping:=1; alpha:=2; show x)
+\endtt
+so that you don't mess up the expression \MF\ is currently evaluating.
+Interruption can also give you a feeling for where \MF\ is spending most
+of its time, if you happen to be using an inefficient macro, since random
+interrupts will tend to occur in whatever place \MF\ visits most often.
+
+\danger \MF's second most frustrating error messages are its occasional
+claims that you have ``^{strange}'' paths. Sometimes a glance at your
+output will make it clear that you did indeed specify a path that
+crossed over itself, something like a figure-8; but sometimes a path
+that looks fine to you will be rejected by the computer. In such
+cases you need to decipher \MF's octant codes, which look scary at
+first 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:
+\begintt
+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;
+\endtt
+If the number |450| in the third ^{flex} had been |452| instead,
+\MF\ would have stopped and told you this:
+\begintt
+> 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;
+?
+\endtt
+The `|for(4)|' in the fifth-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.) \
+^^{compass directions} ^^|SSE|^^|ESE|^^|WSW|^^|WNW|^^|NNE|^^|NNW|^^|ENE|
+You probably also have a proofmode diagram:
+\displayfig 27a (34mm)
+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 first flex).
+Then it goes |SSW| again, and |WSW| again (that's the second flex).
+But at time~4, the path makes a sharp turn through the directions
+|WNW| and |NNW|, {\sl without moving\/} (because these octant codes are in
+parentheses). Aha! That's where the path was supposed to turn
+^{counterclockwise}, through |SSW| and~|SSE| and~|ESE|; \MF\ turned clockwise
+because it was the shortest way to go. The path actually makes a little
+loop at time~4, between the end of the second flex and the beginning of
+the third. Therefore its turning number is indeed zero, and the path is
+strange by definition.
+
+\dangerexercise At what point do the second and third flexes cross,
+in this example?
+\answer The little program
+\begintt
+path p,q; p=flex((-32,481),(-42,455),(-62,430));
+q=flex((-62,430),(-20,452),(42,448));
+show p intersectiontimes q, p intersectionpoint q,
+ angle -direction 2 of p, angle direction 0 of q; end
+\endtt
+gives the following results:
+\begintt
+>> (1.88403,0.07692)
+>> (-59.32149,432.59523)
+>> 43.14589
+>> 45.47263
+\endtt
+(Actually, the paths would also cross if |452| were |451|, but
+it's such a close call that \MF\ doesn't call the path strange;
+\MF\ prefers to turn ^{counterclockwise} when the amount of turn
+is close enough to $180^\circ$, even if it's slightly more.)
+
+\danger 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 fill all cycles counterclockwise, so that you can set
+^"turningcheck"$\null:=0$; this means that \MF\ won't check for
+strange paths, but that's OK because tiny little loops won't hurt anything
+if you are filling cycles in the correct direction.
+
+\ddanger 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 \MF\ to process your
+whole file without stopping once to complain. But maybe the output
+still won't be right; the mere fact that \MF\ didn't stop doesn't mean
+that you can avoid looking at proofsheets. At this stage it's usually easy to
+see how to fix 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 find 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 file that fails in the same way as the
+original. The shorter the file, the easier it will be for you or somebody
+else to pinpoint the problem.
+
+\danger 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 file. \
+(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 file and use it directly in \MF\ input,
+thereby avoiding all the complexity of equations that might have been
+involved in that path's original creation.
+
+\danger We've just talked about "tracingstats" and "tracingspecs";
+\MF\ 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-fledged @pen@; or ^"tracingoutput",
+which shows every picture that's shipped out, using edge-transitions
+to represent the pixel values as illustrated in Chapter~13. Each of
+these types of tracing is enabled by assigning a positive value to the
+corresponding internal quantity; for example, you can simply set
+$"tracingpens":=1$ (or~^@interim@ $"tracingpens":=1$)
+if you want the data about pens.
+
+\danger If ^"tracingcommands"$\null=1$, \MF\ shows every ^{command}
+just before it is carried out. If $"tracingcommands"=2$, \MF\ also shows
+every ^{ex\-pand\-able token} just before it is expanded (except that
+macros are separate, they're traced only when $"tracingmacros">0$). And if
+$"tracingcommands"=3$, \MF\ also shows every ^{algebraic} ^{operation}
+just before it is evaluated. Thus you can get ``stream of
+consciousness'' information about everything \MF\ is doing.
+
+\begingroup\ninepoint
+\danger ^{Digitized output} can be monitored by setting ^"tracingedges"%
+$\null=1$. For example, if we ask \MF\ to draw the Ionian `{\manual\IOO}'
+of Chapter~5 at a resolution of 100~pixels per inch (^"lowres" mode
+with ^"mag"$\null=.5$), "tracingedges" will report as follows:\enddanger
+\beginlines
+|Tracing edges at line 15: (weight 1)|
+|(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).|
+\smallskip
+|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).|
+\endlines
+By following these edges (and negating their weights on the inner boundary)
+we find that the character at this low resolution is symmetric:
+\begindisplay
+\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+SSSRRRRRSSS\cr
+SSRRSSSRRSS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SSRRSSSRRSS\cr
+SSSRRRRRSSS\cr}}
+\enddisplay
+
+\endgroup
+\ddanger Further information about digitization comes out when
+$"tracingedges">1$, if fixed pens are used to ^@draw@ or ^@filldraw@ a
+shape. In this case detailed information is presented about the activity
+in each ^{octant} direction; straight line ``^{transition}'' edges are
+also reported whenever \MF\ changes from one ^{penoffset} to another.
+
+\ddanger The "tracing"$\ldots$ commands put all of their output into your log
+file, unless the ^"tracingonline" parameter is positive; in the latter
+case, all diagnostic information goes to the terminal as well as to the
+log file. Plain \MF\ 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"$\null:=1$ so
+that you can do interactive debugging via ^@show@ commands. 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 off every form of tracing after you've had enough.
+
+\ddanger Some production versions of \MF\ 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 \MF\ runs faster when it doesn't have
+to maintain statistics or keep tabs on whether tracing is required.
+If you want all of \MF's diagnostic tools, you should be sure to
+use the right version.
+
+\ddanger If you set ^"pausing"$\null:=1$, \MF\ will give you a chance to edit
+each line of input as it is read from the file. In this way you can
+make temporary patches (e.g., you can insert @show@$\ldots$ commands)
+while troubleshooting, without changing the actual contents
+of the file, and you can keep \MF\ running at human speed.
+
+Final hint: When working on a large font, it's best to prepare
+only a few characters at a time. Set up a ``{test}'' file and a ``{master}''
+file, and do your work in the test file. \ (Appendix~E suggests a
+convenient way to prepare control files 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 file; and you can run the master file through \MF\ occasionally,
+in order to see how the font is shaping up. Characters can always be
+moved back to the test file if you have to fix some unexpected problems.
+
+\ddangerexercise Final exercise: Find all of the ^{lies} in this
+manual, and all of the ^{jokes}.
+\answer If this exercise isn't just a joke, the title of this
+appendix is a lie. \ (When you've solved this exercise you might also
+try to find all the lies and/or jokes that are the same in both
+this book and {\sl The \TeX book}.)
+
+\line{Final exhortation: G{\sc O} {\sc FORTH} now and create
+{\sl masterpieces of digital typography!\/}}
+
+\endchapter
+
+% Advierto tambien que en quanto \^a los rumbos del camino
+With respect to the directions of the route
+% puedo haver tenido alguna equivocacion.
+I may have made some errors.
+\author FRAY PEDRO ^{FONT}, {\sl Diary\/} (1776)
+ % opening remarks
+
+\bigskip
+
+The road to wisdom? Well, it's plain
+and simple to express:
+\tabskip\centering\halign to\hsize{#\hfil\tabskip=0pt\cr%
+ Err\cr%
+ and err\cr%
+ and err again\cr%
+ but less\cr%
+ and less\cr%
+ and less.\cr}%
+\author PIET ^{HEIN}, {\sl Grooks\/} (1966) % p34
+
+\eject
+ \beginchapter Appendix A. Answers to\\All the\\Exercises
+
+The preface to this manual points out the wisdom of trying to figure out
+each 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.
+
+\immediate\closeout\ans % this makes the answers file ready
+\ninepoint
+\input answers
+
+\endchapter
+
+Looke into this Businesse thorowly,
+And call these foule Offendors to their Answeres.
+\author WILLIAM ^{SHAKESPEARE}, %
+ {\sl Second Part of Henry the Sixth\/} (1594) % Act 2 Sc 1 ll 198--199
+
+\bigskip
+
+If you can't solve a problem,
+you can always look up the answer.
+But please, try first to solve it by yourself;
+then you'll learn more and you'll learn faster.
+\author DONALD E. ^{KNUTH}, {\sl The %
+ {\manual \char`\\]\char`\^\char`\_efg\char`\^}\kern1ptbook\/} (1986)
+
+\eject
+ \beginchapter Appendix B. Basic\\Operations
+
+This appendix defines the macros of the plain \MF\ base. Let's begin
+^^{table, useful}
+with an informal ^{inventory} of all the features that are available.
+
+\def\bb{$\,\left\{\vcenter\bgroup\halign\bgroup\hfil##\hfil\cr}
+\def\ee{\crcr\egroup\egroup\right\}\,$}
+\def\\{\hfil\break}
+\begingroup\lineskip=3pt plus .5pt
+
+\medbreak\textindent\bull {\it ^{Boolean} things:\/} \
+|true|, |false|; \ \ \bb|known|\cr|unknown|\cr|cycle|\ee\<expression>;\\
+\lower2pt\vbox to 7pt{}%
+\smash{\raise3pt\hbox{{\tt odd} \<numeric>; \ \ {\tt charexists} \<numeric>;}}\\
+\bb|boolean|\cr|numeric|\cr|pair|\cr|path|\cr
+|pen|\cr|picture|\cr|string|\cr|transform|\ee\<expression>; \
+\bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<string>\cr\<transform>\ee
+ \bb|<|\cr|<=|\cr|=|\cr|<>|\cr|>=|\cr|>|\ee
+ \bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<string>\cr\<transform>\ee;\\
+\raise3pt\hbox{\strut}%
+|not| \<boolean>; \ \<boolean> |and| \<boolean>; \ \<boolean> |or| \<boolean>.
+
+\medbreak\textindent\bull {\it ^{Numeric} things:\/} \
+|tracingtitles|, \dots, |yoffset| (see Chapter~25);\\
+|eps|, |epsilon|, |infinity|; \ |tolerance|, |join_radius|, |displaying|; \
+\<constant>;\\
+\bb|sqrt|\cr|sind|\cr|cosd|\cr|mlog|\cr|mexp|\ee\<numeric>; \
+\bb|floor|\cr|round|\cr|hround|\cr|vround|\cr|ceiling|\ee\<numeric>; \
+\bb|lft|\cr|rt|\cr|top|\cr|bot|\cr|good.x|\cr|good.y|\ee\<numeric>;\\
+\bb|xpart|\cr|ypart|\ee\bb\<pair>\cr\<transform>\ee; \
+\bb|xxpart|\cr|xypart|\cr|yxpart|\cr|yypart|\ee\<transform>; \
+\bb|ASCII|\cr|oct|\cr|hex|\ee\<string>;\\
+|normaldeviate|; \ |uniformdeviate| \<numeric>; \ |whatever|;\\
+\lower6pt\null
+|angle| \<pair>; \ |turningnumber| \<cycle>; \ |totalweight| \<picture>;\\
+\bb|+|\cr\noalign{\kern-2pt}|-|\cr\noalign{\kern-2pt}\<constant>\ee\<numeric>; \
+\bb|incr|\cr|decr|\ee\<variable>; \
+|byte|\bb\<numeric>\cr\<string>\ee;\\
+\<numeric>\bb|+|\cr|-|\ee\<numeric>; \
+\<numeric>\bb|++|\cr|+-+|\ee\<numeric>;\\
+\vbox to24pt{}%
+\smash{\<numeric>\bb\tt*\cr\tt/\cr\tt**\ee\<numeric>}; \
+\<numeric>\bb|mod|\cr|div|\ee\<numeric>;\\
+\<pair> |dotprod| \<pair>; \
+\bb|max|\cr|min|\ee|(|\<numerics>|)|; \
+\bb|abs|\cr|length|\ee\bb\<numeric>\cr\<pair>\cr\<path>\cr\<string>\ee;\\
+\<numeric>|[|\<numeric>|,|\<numeric>|]|; \
+|solve|\<function>|(|\<numeric>|,|\<numeric>|)|;\\
+|directiontime| \<pair> |of| \<path>.
+
+\medbreak\textindent\bull {\it ^{Pair} things:\/} \
+|left|, |right|, |up|, |down|, |origin|; \
+|(|\<numeric>|,|\<numeric>|)|;\\
+|z|\<suffix>; \ |dir| \<numeric>; \ |unitvector| \<pair>; \ |round| \<pair>;\\
+\bb|lft|\cr|rt|\cr|top|\cr|bot|\ee\<pair>; \
+\bb|good.lft|\cr|good.rt|\cr|good.top|\cr|good.bot|\ee\<pair>; \
+\bb|point|\cr|precontrol|\cr|postcontrol|\cr|direction|\ee%
+ \<numeric> |of| \<path>;\\
+\bb|+|\cr\noalign{\kern-2pt}|-|\cr\noalign{\kern-2pt}\<constant>\ee\<pair>; \
+\<pair>\bb|+|\cr|-|\ee\<pair>; \
+\<numeric>|[|\<pair>|,|\<pair>|]|;\\
+\<numeric>|*|\<pair>; \
+\<pair>\bb|*|\cr|/|\ee\<numeric>; \
+\<pair>\<transformer>;\\
+\<path>\bb|intersectionpoint|\cr|intersectiontimes|\ee\<path>; \
+\bb|max|\cr|min|\ee|(|\<pairs>|)|;\\
+\raise3pt\hbox{\strut}%
+|penoffset| \<pair> |of| \<pen>; \
+|directionpoint| \<pair> |of| \<path>.
+
+\medbreak\textindent\bull {\it ^{Path} things:\/} \
+|quartercircle|, |halfcircle|, |fullcircle|;\\
+|unitsquare|; \
+|flex(|\<pairs>|)|; \
+|makepath| \<pen>;\\
+|superellipse(|\<pair>|,|\<pair>|,|\<pair>|,|\<pair>|,|\<numeric>|)|;\\
+|reverse| \<path>; \
+|counterclockwise| \<path>; \
+|tensepath| \<path>;\\
+\<path>\<transformer>; \
+|interpath(|\<numeric>|,|\<path>|,|\<path>|)|;\\
+\bb\<pair>\cr\<path>\ee
+\bb|{|\<pair>|}|\cr|{|\<curl>|}|\cr\<empty>\ee
+\bb\strut|..|\cr|...|\cr|..|\<tension>|..|\cr|..|\<controls>|..|\cr
+ |--|\cr|---|\cr|&|\cr|softjoin|\ee
+\bb|{|\<pair>|}|\cr|{|\<curl>|}|\cr\<empty>\ee
+\bb\<pair>\cr\<path>\cr|cycle|\ee;\\
+|subpath| \<pair> |of| \<path>.
+
+\medbreak\textindent\bull {\it ^{Pen} things:\/} \
+|pencircle|, |pensquare|, |penrazor|, |penspeck|;\\
+|nullpen|; \ |currentpen|; \
+|makepen| \<path>; \
+\<pen>\<transformer>.
+
+\medbreak\textindent\bull {\it ^{Picture} things:\/} \
+|nullpicture|, |blankpicture|; \ |unitpixel|;\\
+|currentpicture|; \
+\bb|+|\cr|-|\ee\<picture>; \
+\<picture>\bb|+|\cr|-|\ee\<picture>;\\
+\<picture>\<transformer>.
+
+\medbreak\textindent\bull {\it ^{String} things:\/} \
+|"constant"|; \ |ditto|; \ |jobname|; \ |readstring|;\\
+|str|\<suffix>; \
+|decimal| \<numeric>; \
+|char| \<numeric>;\\
+\<string> |&| \<string>; \
+\bb|max|\cr|min|\ee|(|\<strings>|)|; \
+|substring| \<pair> |of| \<string>.
+
+\medbreak\textindent\bull {\it ^{Transform} things:\/} \
+|identity|; \ |currenttransform|;\\
+|inverse| \<transform>; \
+\<transform>\<transformer>.
+
+\advance\lineskip by 3pt
+\advance\medskipamount by 3pt
+\medbreak\textindent\bull {\it ^{Transformers}:\/} \
+|transformed| \<transform>;\\
+\bb|rotated|\cr|slanted|\ee\<numeric>; \
+\bb|scaled|\cr|xscaled|\cr|yscaled|\ee\<numeric>; \
+\bb|shifted|\cr|zscaled|\ee\<pair>;\\
+|reflectedabout(|\<pair>|,|\<pair>|)|; \
+|rotatedaround(|\<pair>|,|\<numeric>|)|.
+
+\medbreak\textindent\bull {\it ^{Conditions}:\/}\\
+|if| \<boolean>|: |\<text> \bb|elseif|\<boolean>|: |\<text>\ee$^{\ge0}$%
+\bb|else:| \<text>\cr\<empty>\ee|fi|.
+
+\smallbreak\textindent\bull {\it ^{Loops}:\/} \ |forever:| \<text> |endfor|;\\
+|for| $\nu$ \bb|=|\cr|:=|\ee
+\bb\<numeric> |upto| \<numeric>\cr
+ \<numeric> |downto| \<numeric>\cr
+ \<numeric>\thinspace|step|\thinspace
+ \<numeric>\thinspace|until|\thinspace\<numeric>\ee
+|:| \<text$(\nu)$> |endfor|;\\
+|for| $\epsilon$ \bb|=|\cr|:=|\ee
+ \<expressions>|:| \<text$(\epsilon)$> |endfor|;\\
+|forsuffixes| $\sigma$ \bb|=|\cr|:=|\ee
+ \<suffixes>|:| \<text$(\sigma)$> |endfor|;\\
+|exitif| \<boolean>|;| ; \quad
+|exitunless| \<boolean>|;| .
+
+\medbreak\textindent\bull {\it ^{Diagnostic things}:\/} \
+|???|; \ |interact|; \
+|hide(|\<statements>|)|;\\
+|loggingall|, |tracingall|, |tracingnone|.
+
+\textindent\bull {\it ^{Starting a job}:\/} \
+|\mode=|\<modename>; \ |mag=|\bb\<numeric>\cr|magstep|\<numeric>\ee;\\
+|screenchars|; \ |screenstrokes|; \ |imagerules|; \ |gfcorners|; \
+|nodisplays|;\\
+|notransforms|; \ |input| \<filename>.
+
+\medbreak\textindent\bull {\it ^{Conversion to pixel units}:\/} \
+|mode_setup|; \ |fix_units|;\\
+|pixels_per_inch|, |blacker|, |fillin|, |o_correction|;\\
+|mm#|, |cm#|, |pt#|, |pc#|, |dd#|, |cc#|, |bp#|, |in#|;\\
+|mm|, |cm|, |pt|, |pc|, |dd|, |cc|, |bp|, |in|;\\
+|mode_def|; \ |extra_setup|;\\
+\bb|define_pixels|\cr
+|define_whole_pixels|\cr
+|define_whole_vertical_pixels|\cr
+|define_good_x_pixels|\cr
+|define_good_y_pixels|\cr
+|define_blacker_pixels|\cr
+|define_whole_blacker_pixels|\cr
+|define_whole_vertical_blacker_pixels|\cr
+|define_corrected_pixels|\cr
+|define_horizontal_corrected_pixels|\cr
+|lowres_fix|\ee|(|\<names>|)|.
+
+\advance\lineskip by-4pt
+\advance\medskipamount by-4pt
+\medbreak\textindent\bull {\it Character and font administration:\/}\\
+|beginchar(|\<code>|,|\<width$\0$>|,|\<height$\0$>|,|\<depth$\0$>|)|; \ \
+|extra_beginchar|;\\
+|italcorr| \<numeric$\0$>; \ |change_width|; \ |endchar|; \ \
+|extra_endchar|;\\
+\bb|font_size|\cr|font_slant|\cr|font_normal_space|\cr
+ |font_normal_stretch|\cr|font_normal_shrink|\cr|font_x_height|\cr
+ |font_quad|\cr|font_extra_space|\ee
+\bb|=|\cr\noalign{\kern-2pt}|:=|\cr\noalign{\kern-2pt}\<empty>\ee
+\<numeric$\0$>; \
+\bb|ligtable|\<ligs/kerns>\cr|charlist|\<codes>\cr|extensible|\<codes>\cr
+ |fontdimen|\<info>\cr|headerbytes|\<info>\ee;\\
+\bb|font_identifier|\cr|font_coding_scheme|\ee
+\smash{\bb\tt=\cr\noalign{\kern-2pt}\tt:=\cr\noalign{\kern-2pt}\<empty>\ee}%
+\<string>.
+
+\medbreak\textindent\bull {\it ^{Drawing}:\/} \
+|penpos|\<suffix>|(|\<length>|,|\<angle>|)|; \
+|penstroke| \<path($e$)>;\\
+|pickup|\bb\<pen>\cr\<saved pen number>\ee; \
+\<pen number>|:=savepen|; \ |clear_pen_memory|;\\
+\lower6pt\null|pen_lft|, |pen_rt|, |pen_top|, |pen_bot|;\\
+\bb|fill|\cr|unfill|\cr|filldraw|\cr|unfilldraw|\ee\<cycle>; \
+\bb|draw|\cr|undraw|\cr|cutdraw|\ee\<path>; \
+\bb|drawdot|\cr|undrawdot|\ee\<pair>;\\
+\vbox to 10pt{}|erase| \<picture command>; \
+|cutoff(|\<pair>|,|\<angle>|)|;\\
+|addto| \<picture variable> |also| \<picture>;\\
+|addto| \<picture variable>\bb|contour| \<cycle>\cr|doublepath| \<path>\ee
+ $\hbox{\bb|withpen|\<pen>\cr|withweight|\<numeric>\ee}^
+ {\smash{\lower3pt\hbox{$\scriptstyle\ge0$}}}\!$;\\
+|cull| \<picture variable>\bb|keeping|\cr|dropping|\ee\<pair>%
+ \bb|withweight|\<numeric>\cr\<empty>\ee.
+
+\medbreak\textindent\bull {\it ^{Screen display}:\/} \
+|currentwindow|; \
+|screen_rows|, |screen_cols|;\\
+|openwindow| \<numeric> |from| \<screen pair> |to| \<screen pair>
+ |at| \<pair>;\\
+|display| \<picture variable> |inwindow| \<numeric>.
+
+\advance\lineskip by 4pt
+\advance\medskipamount by 2pt
+\medbreak\textindent\bull {\it ^{Statements}:\/} \
+\<empty>; \ \<string>; \ |begingroup| \<statements> |endgroup|;\\
+\bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<path>\cr
+ \<pen>\cr\<picture>\cr\<string>\cr\<transform>\ee
+$\hbox{\bb\bb|=|\cr|:=|\ee
+ \bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<path>\cr
+ \<pen>\cr\<picture>\cr\<string>\cr\<transform>\ee\ee}^
+ {\smash{\lower6pt\hbox{$\scriptstyle\ge1$}}}\!\!$; \qquad
+\bb|boolean|\cr|numeric|\cr|pair|\cr|path|\cr
+|pen|\cr|picture|\cr|string|\cr\thinspace|transform|\thinspace\ee\<names>;\\
+|save| \<names>; \
+|interim| \<internal> |:=| \<numeric>; \
+|let| \<name>\bb|=|\cr|:=|\ee\<name>;\\
+\bb|def|\cr|vardef|\ee\<name>\<parameters>\bb|=|\cr|:=|\ee
+ \<text>\thinspace|enddef|;\\
+\vbox to24pt{}\bb|primarydef|\cr|secondarydef|\cr|tertiarydef|\ee
+ \ $\alpha$ \<name> $\beta$ \bb|=|\cr|:=|\ee
+ \<text$(\alpha,\beta)$>\thinspace|enddef|;\\
+\strut|showit|; \ |shipit|; \ |cullit|; \ |openit|; \
+|clearit|; \ |clearxy|; \ |clearpen|;\\
+|stop| \<string>; \
+|show| \<expressions>; \
+\bb|message|\cr|errmessage|\cr|errhelp|\ee\<string>;\\
+\bb|showvariable|\cr|showtoken|\ee\<names>; \
+\bb|showdependencies|\cr|showstats|\ee;\\
+\strut see also Chapter 26 for some more exotic commands.
+
+\advance\lineskip by -1pt
+\advance\medskipamount by 1pt
+\medbreak\textindent\bull {\it ^{Proofsheet} information:\/}\\
+\bb|labels|\cr|penlabels|\ee
+ \bb|top|\cr|lft|\cr|rt|\cr|bot|\cr\<empty>\ee
+ \bb|nodot|\cr\<empty>\ee
+ |(|\<suffixes>|)|;\\
+|makelabel|\bb|top|\cr|lft|\cr|rt|\cr|bot|\cr\<empty>\ee
+ \bb|nodot|\cr\<empty>\ee
+ |(|\<string>|,|\<pair>|)|; \
+\bb|titlefont|\cr|labelfont|\cr|grayfont|\cr|slantfont|\ee \<name>;\\
+\bb|proofrule|\cr|screenrule|\ee|(|\<pair>|,|\<pair>|)|; \
+|makegrid(|\<pairs>|)(|\<pairs>|)|;\\
+|proofrulethickness| \<numeric>; \ |proofoffset| \<pair>.
+
+\medbreak\textindent\bull {\it Hacks:\/} \ |gobble|, |gobbled|, |killtext|; \
+|capsule_def|; \ |numtok|.
+
+\medbreak
+\endgroup % end of special \lineskip
+\ninepoint
+\danger The remainder of this appendix contains an edited transcript
+of the ``plain ^{base file},'' which is a set of macros that come with
+normal implementations of \MF\!\null. These macros serve three basic purposes:
+\ (1)~They make \MF\ usable, because \MF's primitive capabilities
+operate at a very low level. A~``virgin'' \MF\ system that has no
+macros is like a newborn baby that has an immense amount to learn about
+the real world; but it is capable of learning fast. \ (2)~The plain
+\MF\ macros provide a basis for more elaborate and powerful bases
+tailored to individual tastes and applications. You can do a lot with
+plain \MF\!, but pretty soon you'll want to do even more. \ (3)~The macros
+also serve to illustrate how additional bases can be designed. \enddanger
+
+Somewhere in your computer system you should be able to find a file called
+^|plain.mf| that contains what has been preloaded into the
+running \MF\ system that you use. That file should match the
+code discussed below, except that it might do some things in an
+equivalent but slightly more efficient manner.
+
+When we come to macros whose use has not yet been explained---for
+example, somehow |softjoin| and |stop| never made it
+into Chapters 1 through~27---we shall consider them from a user's
+viewpoint. But most of the comments that follow are addressed to a
+potential base-file designer.
+
+A special program called ^|INIMF| is used to install \MF; |INIMF| is
+just like \MF\ except that it is able to `^@dump@' a base file
+suitable for preloading. This operation requires additional program
+space, so |INIMF| generally has less memory available
+than you would expect to find in a production version of \MF\!.
+
+\subsection Getting started. A base file has to have a ^@delimiters@
+command near the beginning, since |INIMF| doesn't have any delimiters
+built~in. The first few lines usually also give the base file a name and
+version number as shown here.
+\beginlines
+|% This is the plain METAFONT base that's described in The METAFONTbook.|
+|% N.B.: Please change "base_version" whenever this file is modified!|
+|% And don't modify the file under any circumstances.|
+|string base_name, base_version; base_name="plain"; base_version="2.71";|
+\smallskip
+^|message|| "Preloading the plain base, version " & base_version;|
+\smallskip
+|delimiters (); % this makes parentheses behave like parentheses|
+\endlines
+
+Next we define some of the simplest macros, which provide ``syntactic sugar''
+for commonly occurring idioms. ^^{blash blash}
+For example, `@stop@ |"hello"|' displays `|hello|' on the terminal and waits
+until \<return> is typed.
+\beginlines
+|def |^|upto|| = step 1 until enddef; def |^|downto|| = step -1 until enddef;|
+|def |^|exitunless|| expr c = exitif not c enddef;|
+|let |^|relax|| = \; % ignore the word `relax', as in TeX|
+|let \\ = \; % double relaxation is like single|
+|def |^|]]|| = ] ] enddef; % right brackets should be loners|
+|def |^|--|| = {curl 1}..{curl 1} enddef;|
+|def |^|---|| = .. tension infinity .. enddef;|
+|def |^|...|| = .. tension atleast 1 .. enddef;|
+\smallskip
+|def |^|gobble|| primary g = enddef; def |^|killtext|| text t = enddef;|
+|primarydef g |^|gobbled|| gg = enddef;|
+|def |^|hide||(text t) = exitif numeric begingroup t; endgroup; enddef;|
+|def |^|???|| = hide(interim showstopping:=1; showdependencies) enddef;|
+|def |^|stop|| expr s = message s; gobble readstring enddef;|
+\endlines
+(Chapter 20 points out that `|\|' is an expandable token that expands
+into nothing. Plain \MF\ allows also `|\\|', because there's a
+formatting program called ^|MFT| that uses~|\\| to insert extra spacing
+in a ^{pretty-printed} listing.) \ The ``clever'' code for @hide@
+is based on the fact that a ^{vacuous} expression is not numeric;
+hence no loop is exited, ^^@exitif@ and the computer doesn't mind the
+fact that we may not be in a loop at all.
+
+The values of ^{internal quantities} are next on the agenda:
+\beginlines
+^|smoothing||:=1; |^|autorounding||:=2; % this adjusts curves to the raster|
+^|turningcheck||:=2; % this will warn about a "strange path"|
+^|granularity||:=1; % this says that pixels are pixels|
+\smallskip
+|def |^|interact|| = % prepares to make "show" commands stop|
+| hide(showstopping:=1; tracingonline:=1) enddef;|
+|def |^|loggingall|| = % puts tracing info into the log|
+| tracingcommands:=3; tracingedges:=2; tracingtitles:=1;|
+| tracingequations:=1; tracingcapsules:=1; tracingspecs:=1;|
+| tracingpens:=1; tracingchoices:=1; tracingstats:=1;|
+| tracingoutput:=1; tracingmacros:=1; tracingrestores:=1;|
+| enddef;|
+|def |^|tracingall|| = % turns on every form of tracing|
+| tracingonline:=1; showstopping:=1; loggingall enddef;|
+|def |^|tracingnone|| = % turns off every form of tracing|
+| tracingcommands:=0; tracingonline:=0; showstopping:=0;|
+| tracingedges:=0; tracingtitles:=0; tracingequations:=0;|
+| tracingcapsules:=0; tracingspecs:=0; tracingpens:=0;|
+| tracingchoices:=0; tracingstats:=0; tracingoutput:=0;|
+| tracingmacros:=0; tracingrestores:=0; enddef;|
+\endlines
+The user can say @interact@ in the midst of a statement; but
+@loggingall@, @tracingall@, and @tracingnone@ should come
+between statements. \ (You don't need a ^{semicolon} after them,
+because they come equipped with their own closing `|;|'.)
+
+\subsection Math routines. The second major part of |plain.mf|
+contains the definitions of basic constants and mathematical
+macros that extend the primitive capabilities of \MF's expressions.
+\beginlines
+|% numeric constants|
+|newinternal eps,epsilon,infinity;|
+^|eps|| := .00049; % this is a pretty small positive number|
+^|epsilon|| := 1/256/256; % but this is the smallest|
+^|infinity|| := 4095.99998; % and this is the largest|
+\smallbreak
+|% pair constants|
+|pair right,left,up,down,origin;|
+^|origin||=(0,0); |^|up||=-|^|down||=(0,1); |^|right||=-|^|left||=(1,0);|
+\smallbreak
+|% path constants|
+|path quartercircle,halfcircle,fullcircle,unitsquare;|
+^|quartercircle||=(right{up}..(right+up)/sqrt2..up{left}) scaled .5;|
+^|halfcircle||=quartercircle & quartercircle rotated 90;|
+^|fullcircle||=halfcircle & halfcircle rotated 180 & cycle;|
+^|unitsquare||=(0,0)--(1,0)--(1,1)--(0,1)--cycle;|
+\smallbreak
+|% transform constants|
+|transform identity;|
+|for z=origin,right,up: z transformed |^|identity|| = z; endfor|
+\smallbreak
+|% picture constants|
+|picture blankpicture,unitpixel;|
+^|blankpicture||=nullpicture; % `display blankpicture...'|
+^|unitpixel||=nullpicture; addto unitpixel contour unitsquare;|
+\smallbreak
+|% string constants|
+|string ditto; |^|ditto|| = char 34; % ASCII double-quote mark|
+\smallbreak
+|% pen constants|
+|def capsule_def(suffix s) primary u = def s = u enddef enddef;|
+|capsule_def(pensquare) makepen(unitsquare shifted -(.5,.5));|
+|capsule_def(penrazor) makepen((-.5,0)--(.5,0)--cycle);|
+|pen penspeck; penspeck=pensquare scaled eps;|
+\endlines
+The ^@pensquare@ and ^@penrazor@ constants are defined here in a
+surprisingly roundabout way, just so that they can be ^{future pens}
+instead of pens. \MF\ can transform a future pen much faster than a
+pen, since pens have a complex internal data structure, so this
+trick saves time. But how does it work? Well, a variable cannot
+be a future pen, but a ^{capsule} can; hence @pensquare@ and @penrazor@
+are defined, via ^@capsule\_def@, to be macros that expand into single capsules.
+Incidentally, ^@penspeck@ is an extremely tiny little pen that is used by the
+@drawdot@ macro. Since it is not intended to be transformed,
+we are better off making it a pen; then it's immediately ready for use.
+
+Now that the basic constants have been defined, we turn to
+mathematical operations. There's one operation that has no arguments:
+\beginlines
+|% nullary operators|
+|vardef |^|whatever|| = save ?; ? enddef;|
+\endlines
+The reasoning behind this is discussed in exercise 17.\Xwhat.
+
+Operations that take one argument are introduced next.
+\beginlines
+|% unary operators|
+|let |^|abs|| = length;|
+\smallskip
+|vardef |^|round|| primary u =|
+| if numeric u: floor(u+.5)|
+| elseif pair u: (hround xpart u, vround ypart u)|
+| else: u fi enddef;|
+\smallskip
+|vardef |^|hround|| primary x = floor(x+.5) enddef;|
+|vardef |^|vround|| primary y = floor(y.o_+.5)_o_ enddef;|
+\smallskip
+|vardef |^|ceiling|| primary x = -floor(-x) enddef;|
+\smallbreak
+|vardef |^|byte|| primary s = if string s: ASCII fi s enddef;|
+\smallbreak
+|vardef |^|dir|| primary d = right rotated d enddef;|
+\smallskip
+|vardef |^|unitvector|| primary z = z/abs z enddef;|
+\smallbreak
+|vardef |^|inverse|| primary T =|
+| transform T_; T_ transformed T = identity; T_ enddef;|
+\smallbreak
+|vardef |^|counterclockwise|| primary c =|
+| if turningcheck>0:|
+| interim |^|autorounding||:=0;|
+| if |^|turningnumber|| c <= 0: reverse fi fi c enddef;|
+\smallbreak
+|vardef |^|tensepath|| expr r =|
+| for k=0 upto length r - 1: point k of r --- endfor|
+| if cycle r: cycle else: point infinity of r fi enddef;|
+\endlines
+Notice that the variable `|T_|' was not saved by the "inverse"
+function. The plain base routines gain ^{efficiency} by
+using ``^{private}'' tokens that are assumed to be distinct
+from any of the user's tokens; these private tokens always
+end with the ^{underscore} character,~`|_|'. If ordinary user programs
+never contain such token names, no surprises will occur,
+provided that different macro designers who combine their routines are
+careful that their private names are not in conflict.
+
+The private tokens `|o_|' and `|_o_|' used in |vround| stand
+for `|*aspect_ratio|' and `|/aspect_ratio|', respectively,
+as we shall see shortly.
+
+Now we define `mod' and `div', being careful to do this in such a way that
+the identities $a(x\;\hbox{mod}\;y)=(ax)\;\hbox{mod}\;(ay)$ and
+$(ax)\;\hbox{div}\;(ay)=x\;\hbox{div}\;y$ are valid.
+\beginlines
+|% binary operators|
+|primarydef x |^|mod|| y = (x-y*floor(x/y)) enddef;|
+|primarydef x |^|div|| y = floor(x/y) enddef;|
+|primarydef w |^|dotprod|| z = (xpart w * xpart z + ypart w * ypart z) enddef;|
+\endlines
+
+The `|**|' operator is designed to be most efficient when it's used
+for squaring. A separate `^|takepower|' routine is used for exponents
+other than~2, so that \MF\ doesn't have to skip over lots of tokens
+in the common case. The |takepower| routine is careful to give the
+correct answer in expressions like `|(-2)**(-3)|' and `|0**0|'.
+\beginlines
+|primarydef x |^|**|| y = if y=2: x*x else: takepower y of x fi enddef;|
+|def takepower expr y of x =|
+| if x>0: mexp(y*mlog x)|
+| elseif (x=0) and (y>0): 0|
+| else: 1|
+| if y=floor y:|
+| if y>=0: for n=1 upto y: *x endfor|
+| else: for n=-1 downto y: /x endfor fi|
+| else: hide(errmessage "Undefined power: " & decimal x&"**"&decimal y)|
+| fi fi enddef;|
+\endlines
+
+\MF's primitive path operations have been defined in such a way that the
+following higher-level operations are easy:
+\beginlines
+|vardef |^|direction|| expr t of p =|
+| postcontrol t of p - precontrol t of p enddef;|
+\smallskip
+|vardef |^|directionpoint|| expr z of p =|
+| a_:=|^|directiontime|| z of p;|
+| if a_<0: errmessage("The direction doesn't occur"); fi|
+| point a_ of p enddef;|
+\smallskip
+|secondarydef p |^|intersectionpoint|| q =|
+| begingroup save x_,y_; (x_,y_)=p |^|intersectiontimes|| q;|
+| if x_<0: errmessage("The paths don't intersect"); (0,0)|
+| else: .5[point x_ of p, point y_ of q] fi endgroup|
+|enddef;|
+\weakendlines
+The private token `|a_|' will be declared as an ^{internal quantity}.
+Internal quantities are more ^{efficient} than ordinary numeric variables.
+
+Plain \MF's `^{softjoin}' operation provides a way to hook paths together
+without the abrupt change of direction implied by~`|&|'. Assuming that
+the final point of~$p$ is the first point of~$q$, the path `$p$~softjoin~$q$'
+begins on~$p$ until coming within "join\_radius" of this common point;
+then it curves over and finishes~$q$ in essentially the same way.
+The internal quantity ^"join\_radius" should be set to the desired
+value before softjoin is applied. \ (This routine is due to N.~N. ^{Billawala}.)
+\beginlines
+|tertiarydef p softjoin q =|
+| begingroup c_:=|^|fullcircle|| scaled 2join_radius shifted point 0 of q;|
+| a_:=ypart(c_ intersectiontimes p); b_:=ypart(c_ intersectiontimes q);|
+| if a_<0:point 0 of p{direction 0 of p} else: subpath(0,a_) of p fi|
+| ... if b_<0:{direction infinity of q}point infinity of q|
+| else: subpath(b_,infinity) of q fi endgroup enddef;|
+|newinternal join_radius,a_,b_; path c_;|
+\endlines
+
+The remaining math operators don't fall into the ordinary patterns; something
+is unusual about each of them. First we have `|incr|' and `|decr|', which apply
+only to variables; they have the side effect of changing the variable's value.
+\beginlines
+% special operators
+|vardef |^|incr|| suffix $ = $:=$+1; $ enddef;|
+|vardef |^|decr|| suffix $ = $:=$-1; $ enddef;|
+\weakendlines
+You can say either `|incr|~|x|' or `|incr|~|(x)|', within
+an expression; but `|incr|~|x|' by itself is not a valid statement.
+
+To reflect about a line, we compute a ^{transform} on the fly:
+\beginlines
+|def |^|reflectedabout||(expr w,z) = % reflects about the line w..z|
+| transformed|
+| begingroup transform T_;|
+| w transformed T_ = w; z transformed T_ = z;|
+| xxpart T_ = -yypart T_; xypart T_ = yxpart T_; % T_ is a reflection|
+| T_ endgroup enddef;|
+\smallskip
+|def |^|rotatedaround||(expr z, d) = % rotates d degrees around z|
+| shifted -z rotated d shifted z enddef;|
+|let |^|rotatedabout|| = rotatedaround; % for roundabout people|
+\endlines
+
+Now we come to an interesting trick: The user writes something like
+`min$(a,b)$' or `max$(a,b,c,d)$', and
+\MF's notation for macro calls makes it easy to separate the first argument
+from the rest---assuming that at least two arguments are present.
+\beginlines
+|vardef |^|max||(expr u)(text t) = % t is a list of numerics, pairs, or strings|
+| save u_; setu_ u; for uu = t: if uu>u_: u_:=uu; fi endfor|
+| u_ enddef;|
+\smallskip
+|vardef |^|min||(expr u)(text t) = % t is a list of numerics, pairs, or strings|
+| save u_; setu_ u; for uu = t: if uu<u_: u_:=uu; fi endfor|
+| u_ enddef;|
+\smallskip
+|def setu_ primary u =|
+| if pair u: pair u_ elseif string u: string u_ fi;|
+| u_=u enddef;|
+\weakendlines
+^^"setu\_" Appendix D discusses some variations on this theme.
+
+The ^|flex| routine defines part of a path whose directions at the
+endpoints will depend on the environment, because this path is not
+enclosed in parentheses.
+\beginlines
+|def flex(text t) = % t is a list of pairs|
+| hide(n_:=0; for z=t: z_[incr n_]:=z; endfor|
+| dz_:=z_[n_]-z_1)|
+| z_1 for k=2 upto n_-1: ...z_[k]{dz_} endfor ...z_[n_] enddef;|
+|newinternal n_; pair z_[],dz_;|
+\endlines
+
+The five parameters to `superellipse' are the right, the top, the left,
+the bottom, and the superness.
+\beginlines
+|def |^|superellipse||(expr r,t,l,b,s)=|
+| r{up}...(s[xpart t,xpart r],s[ypart r,ypart t]){t-r}...|
+| t{left}...(s[xpart t,xpart l],s[ypart l,ypart t]){l-t}...|
+| l{down}...(s[xpart b,xpart l],s[ypart l,ypart b]){b-l}...|
+| b{right}...(s[xpart b,xpart r],s[ypart r,ypart b]){r-b}...cycle enddef;|
+\endlines
+
+Chapter~14 illustrates the `interpath' routine, which interpolates
+between paths to find a path that would be written `$a[p,q]$' if
+\MF's macro notation were more general.
+\beginlines
+|vardef |^|interpath||(expr a,p,q) =|
+| for t=0 upto length p-1: a[point t of p, point t of q]|
+| ..controls a[postcontrol t of p, postcontrol t of q]|
+| and a[precontrol t+1 of p, precontrol t+1 of q] .. endfor|
+| if cycle p: cycle|
+| else: a[point infinity of p, point infinity of q] fi enddef;|
+\endlines
+
+Finally we come to the "solve" macro, which has already been presented
+in Chapter~20. Appendix~D gives further illustrations of its use.
+\beginlines
+|vardef |^|solve||@#(expr true_x,false_x)= % @#(true_x)=true, @#(false_x)=false|
+| tx_:=true_x; fx_:=false_x;|
+| forever: x_:=.5[tx_,fx_]; exitif abs(tx_-fx_)<=tolerance;|
+| if @#(x_): tx_ else: fx_ fi :=x_; endfor|
+| x_ enddef; % now x_ is near where @# changes from true to false|
+|newinternal |^|tolerance||, tx_,fx_,x_; tolerance:=.1;|
+\finalendlines
+
+\subsection Conversion to pixels. The next main subdivision of |plain.mf|
+contains macros and constants that help convert dimensions from
+device-independent ``sharped'' or ``true'' units into the pixel units
+corresponding to a particular device. First comes a subroutine that
+computes eight basic units, assuming that the number
+^^{mm} ^^{cm} ^^{pt} ^^{pc} ^^{dd} ^^{cc} ^^{bp} ^^{in} ^^@fix\_units@
+of ^"pixels\_per\_inch" is known:
+\beginlines
+|def fix_units = % define the conversion factors, given pixels_per_inch|
+| mm:=pixels_per_inch/25.4; cm:=pixels_per_inch/2.54;|
+| pt:=pixels_per_inch/72.27; pc:=pixels_per_inch/6.0225;|
+| dd:=1238/1157pt; cc:=12dd;|
+| bp:=pixels_per_inch/72; in:=pixels_per_inch;|
+| hppp:=pt; % horizontal pixels per point|
+| vppp:=aspect_ratio*hppp; % vertical pixels per point|
+| enddef;|
+\endlines
+
+^{Sharped units} are actually expressed in terms of points, but a virtuous
+user will not write programs that exploit this fact.
+\beginlines
+|mm#=2.84528; pt#=1; dd#=1.07001; bp#=1.00375;|
+|cm#=28.45276; pc#=12; cc#=12.84010; in#=72.27;|
+\endlines
+
+A particular device is supposed to be modeled by four parameters, called
+^"pixels\_per\_inch", ^"blacker", ^"o\_correction", and ^"fillin", as discussed
+in Chapter~11. Appropriate
+values will be assigned to these internal quantities by @mode\_setup@.
+\beginlines
+|newinternal pixels_per_inch; % the given resolution|
+|newinternal blacker, o_correction; % device-oriented corrections|
+\endlines
+(The fourth parameter, "fillin", is already an internal quantity of \MF\!.)
+
+Here are the ten principal ways to convert from
+^^{define\_pixels (and nine others)} sharped units to pixels:
+\beginlines
+|def define_pixels(text t) =|
+| forsuffixes $=t: $:=$.#*hppp; endfor enddef;|
+|def define_whole_pixels(text t) =|
+| forsuffixes $=t: $:=hround($.#*hppp); endfor enddef;|
+|def define_whole_vertical_pixels(text t) =|
+| forsuffixes $=t: $:=vround($.#*hppp); endfor enddef;|
+|def define_good_x_pixels(text t) =|
+| forsuffixes $=t: $:=good.x($.#*hppp); endfor enddef;|
+|def define_good_y_pixels(text t) =|
+| forsuffixes $=t: $:=good.y($.#*hppp); endfor enddef;|
+|def define_blacker_pixels(text t) =|
+| forsuffixes $=t: $:=$.#*hppp+blacker; endfor enddef;|
+|def define_whole_blacker_pixels(text t) =|
+| forsuffixes $=t: $:=hround($.#*hppp+blacker);|
+| if $<=0: $:=1; fi endfor enddef;|
+|def define_whole_vertical_blacker_pixels(text t) =|
+| forsuffixes $=t: $:=vround($.#*hppp+blacker);|
+| if $<=0: $:=1_o_; fi endfor enddef;|
+|def define_corrected_pixels(text t) =|
+| forsuffixes $=t: $:=vround($.#*hppp*o_correction)+eps; endfor enddef;|
+|def define_horizontal_corrected_pixels(text t) =|
+| forsuffixes $=t: $:=hround($.#*hppp*o_correction)+eps; endfor enddef;|
+\endlines
+
+Chapter 24 discusses the ^@lowres\_fix@ routine, which helps to correct
+anomalies that may have occurred when sharped dimensions were rounded
+to whole pixels.
+\beginlines
+|def lowres_fix(text t) expr ratio =|
+| begingroup save min,max,first;|
+| forsuffixes $=t:|
+| if unknown min: min=max=first=$; min#=max#=$.#;|
+| elseif $.#<min#: min:=$; min#:=$.#;|
+| elseif $.#>max#: max:=$; max#:=$.#; fi endfor|
+| if max/min>ratio*max#/min#: forsuffixes $=t: $:=first; endfor fi|
+| endgroup enddef;|
+\finalendlines
+
+\subsection Modes of operation. The standard way to create a font with
+plain \MF\ is to start~up the program by saying
+\begindisplay
+|\mode=|\<mode name>|; mag=|\<magnification>|; input |%
+ \<font file name>
+\enddisplay
+in response to \MF's initial `^|**|'.
+The ^|mag| is omitted if the magnification is~1, and the ^|mode| is omitted
+if |mode=proof|. Additional commands like `|screenchars|' might be
+given before the `^|input|'; we shall discuss them later. If you are
+using another base file, called say the `|super|' base, this whole
+command line should be preceded by `|&super|'. The mode name should have
+been predeclared in your base file, by the |mode_def| routine below.
+If, however, you need a special mode that isn't in the base, you can put
+its commands into a file (e.g., `|specmode.mf|') and invoke it
+by saying
+\begindisplay
+^|\smode||="specmode"; mag=|$\,\cdots$
+\enddisplay
+instead of giving a predeclared mode name.
+
+Here is the ^@mode\_setup@ routine,
+which is usually one of the first macros to be called in a \MF\ program:
+\beginlines
+|def mode_setup =|
+| warningcheck:=0;|
+| if unknown mode: mode=proof; fi|
+| numeric aspect_ratio; transform currenttransform;|
+| scantokens if string mode:("input "&mode) else: mode_name[mode] fi;|
+| if unknown mag: mag=1; fi|
+| if unknown aspect_ratio: aspect_ratio=1; fi|
+| displaying:=proofing;|
+| pixels_per_inch:=pixels_per_inch*mag;|
+| if aspect_ratio=1: let o_=\; let _o_=\|
+| else: def o_=*aspect_ratio enddef; def _o_=/aspect_ratio enddef fi;|
+| fix_units;|
+| scantokens extra_setup; % the user's special last-minute adjustments|
+| currenttransform:=|
+| if unknown currenttransform: identity else: currenttransform fi|
+| yscaled aspect_ratio;|
+| clearit;|
+| pickup pencircle scaled (.4pt+blacker);|
+| warningcheck:=1; enddef;|
+|def |^|smode|| = string mode; mode enddef;|
+|string extra_setup, mode_name[];|
+|extra_setup=""; % usually there's nothing special to do|
+|newinternal |^|displaying||; % if positive, endchar will `showit'|
+\endlines
+^^"extra\_setup" ^^"mode\_name"
+The first `^@scantokens@' in @mode\_setup@ either reads a special
+file or calls a macro that expands into commands defining the mode.
+Notice that "aspect\_ratio" is always cleared to an undefined value
+when these commands are performed; you can't simply give a value to
+"aspect\_ratio" when you set "mode" and~"mag". If the aspect ratio
+isn't assigned a definite value by the mode routine, it will become unity,
+and the `|o_|' and `|_o_|' operations will be omitted from subsequent
+calculations. Notice also that the mode commands might do something special
+to "mag", since "mag" isn't examined until after the mode routine has
+acted. The "currenttransform" might also be given a special value. \MF's
+^"warningcheck" is temporarily disabled during these computations, since
+there might be more than 4096 pixels per inch. After @mode\_setup@ is
+finished, the "currentpicture" will be null, the "currenttransform"
+will take the "aspect\_ratio" into account, and the "currentpen" will be a
+circular nib with the standard default thickness of $0.4\pt$. \ (You should
+save this pen if you want to use it in a character, because @beginchar@
+will clear it away.)
+
+Plain \TeX\ has a convention for magnifying fonts in terms of ``magsteps,''
+where magstep~$m=1.2^m$. A geometric progression of font sizes is
+convenient, because scaling by magstep~$m$ followed by magstep~$n$ is
+^^{mexp} equivalent to scaling by magstep~$m+n$.
+\beginlines
+|vardef |^|magstep|| primary m = mexp(46.67432m) enddef;|
+\endlines
+
+When a mode is defined (e.g., `|proof|'), a numeric variable of that
+name is created and assigned a unique number (e.g.,~1). Then an
+^{underscore} character is appended, and a macro is defined for the
+resulting name (e.g., `|proof_|'). The "mode\_name" array is used to
+convert between number and name (e.g., "mode\_name"$_1=\null$|"proof_"|).
+\beginlines
+|def mode_def suffix $ =|
+| $:=incr number_of_modes;|
+| mode_name[$]:=str$ & "_";|
+| |^|expandafter|| |^|quote|| def scantokens mode_name[$] enddef;|
+|newinternal number_of_modes;|
+\endlines
+(This ^@mode\_def@ strategy was suggested by Bruce ^{Leban}.)
+
+Three basic modes are now defined, starting with two for proofing:
+\beginlines
+|% proof mode: for initial design of characters|
+|mode_def |^|proof|| =|
+| proofing:=2; % yes, we're making full proofs|
+| fontmaking:=0; % no, we're not making a font|
+| tracingtitles:=1; % yes, show titles online|
+| pixels_per_inch:=2601.72; % that's 36 pixels per pt|
+| blacker:=0; % no additional blackness|
+| fillin:=0; % no compensation for fillin|
+| o_correction:=1; % no reduction in overshoot|
+| enddef;|
+\smallbreak
+|% smoke mode: for label-free proofs to mount on the wall|
+|mode_def |^|smoke|| =|
+| proof_; % same as proof mode, except:|
+| proofing:=1; % yes, we're making unlabeled proofs|
+| extra_setup:=extra_setup&"grayfont black"; % with solid black pixels|
+| let makebox=maketicks; % make the boxes less obtrusive|
+| enddef;|
+\weakendlines
+Notice that "smoke" mode saves a lot of fuss by calling on `|proof_|';
+this is the macro that was defined by the first @mode\_def@.
+
+A typical mode for font generation appears next. ^^"fontmaking"
+\beginlines
+|% lowres mode: for certain devices that print 200 pixels per inch|
+|mode_def |^|lowres|| =|
+| proofing:=0; % no, we're not making proofs|
+| fontmaking:=1; % yes, we are making a font|
+| tracingtitles:=0; % no, don't show titles at all|
+| pixels_per_inch:=200; % that's pretty low resolution|
+| blacker:=.65; % make pens a bit blacker|
+| fillin:=.2; % compensate for diagonal fillin|
+| o_correction:=.4; % but don't overshoot as much|
+| enddef;|
+\smallskip
+|localfont:=lowres; % the mode most commonly used to make fonts|
+\endlines
+Installations of \MF\ typically have several more predefined modes, and they
+generally set "localfont" to something else. Such alterations should
+not be made in the master file |plain.mf|; they should appear in a separate
+file, as discussed below.
+
+\subsection Drawing and filling. Now we come to the macros that provide
+an interface between the user and \MF's primitive picture commands.
+^^"currentpen" ^^"currentpicture" ^^"currenttransform"
+First, some important program variables are introduced:
+\beginlines
+|pen currentpen;|
+|path currentpen_path;|
+|picture currentpicture;|
+|transform currenttransform;|
+|def t_ = transformed currenttransform enddef;|
+\endlines
+
+The key macros are ^@fill@, ^@draw@, ^@filldraw@, and ^@drawdot@.
+\beginlines
+|def fill expr c = addto_currentpicture contour c.t_ enddef;|
+|def addto_currentpicture = addto currentpicture enddef;|
+|def draw expr p =|
+| addto_currentpicture doublepath p.t_ withpen currentpen enddef;|
+|def filldraw expr c = fill counterclockwise c withpen currentpen enddef;|
+|def drawdot expr z = if unknown currentpen_path: def_pen_path_ fi|
+| addto_currentpicture contour|
+| currentpen_path shifted (z.t_) withpen penspeck enddef;|
+|def def_pen_path_ =|
+| hide(currentpen_path=tensepath makepath currentpen) enddef;|
+\endlines
+
+And they have negative counterparts:
+\beginlines
+|def |^|unfill|| expr c = fill c withweight -1 enddef;|
+|def |^|undraw|| expr p = draw p withweight -1 enddef;|
+|def |^|unfilldraw|| expr c = filldraw c withweight -1 enddef;|
+|def |^|undrawdot|| expr z = drawdot z withweight -1 enddef;|
+|def |^|erase|| text t = begingroup interim default_wt_:=-1;|
+| cullit; t withweight -1; cullit; endgroup enddef;|
+|newinternal default_wt_; default_wt_:=1;|
+\endlines
+
+It's more difficult to cut off the ends of a stroke, but the
+following macros (discussed near the end of Chapter~16) do the job:
+\beginlines
+|def |^|cutdraw|| expr p = % caution: you may need autorounding=0|
+| cutoff(point 0 of p, 180+angle direction 0 of p);|
+| cutoff(point infinity of p, angle direction infinity of p);|
+| culldraw p enddef;|
+\smallbreak
+|def |^|culldraw|| expr p = addto pic_ doublepath p.t_ withpen currentpen;|
+| cull pic_ dropping(-infinity,0) withweight default_wt_;|
+| addto_currentpicture also pic_; pic_:=nullpicture; killtext enddef;|
+|vardef |^|cutoff||(expr z,theta) =|
+| interim autorounding := 0; interim smoothing := 0;|
+| addto pic_ doublepath z.t_ withpen currentpen;|
+| addto pic_ contour|
+| (cut_ scaled (1+max(-pen_lft,pen_rt,pen_top,-pen_bot))|
+| rotated theta shifted z)t_;|
+| cull pic_ keeping (2,2) withweight -default_wt_;|
+| addto currentpicture also pic_; pic_:=nullpicture enddef;|
+|picture pic_; pic_:=nullpicture;|
+|path cut_; cut_ = ((0,-1)--(1,-1)--(1,1)--(0,1)--cycle) scaled 1.42;|
+\weakendlines
+The use of ^"default\_wt\_" here makes `^@erase@ @cutdraw@' work. The
+private variable "pic\_" is usually kept equal to @nullpicture@ in
+order to conserve memory space.
+
+Picking up a pen not only sets "currentpen", it also establishes
+the values of ^"pen\_lft", ^"pen\_rt", ^"pen\_top", and ^"pen\_bot",
+which are used by "lft", "rt", "top", and "bot".
+\beginlines
+|def |^|pickup|| secondary q =|
+| if numeric q: numeric_pickup_ else: pen_pickup_ fi q enddef;|
+|def numeric_pickup_ primary q =|
+| if unknown pen_[q]: errmessage "Unknown pen"; clearpen|
+| else: currentpen:=pen_[q];|
+| pen_lft:=pen_lft_[q]; pen_rt:=pen_rt_[q];|
+| pen_top:=pen_top_[q]; pen_bot:=pen_bot_[q];|
+| currentpen_path:=pen_path_[q] fi; enddef;|
+|def pen_pickup_ primary q =|
+| currentpen:=q yscaled aspect_ratio;|
+| pen_lft:=xpart penoffset down of currentpen;|
+| pen_rt:=xpart penoffset up of currentpen;|
+| pen_top:=(ypart penoffset left of currentpen)_o_;|
+| pen_bot:=(ypart penoffset right of currentpen)_o_;|
+| path currentpen_path; enddef;|
+|newinternal pen_lft,pen_rt,pen_top,pen_bot,pen_count_;|
+\endlines
+And saving a pen saves all the relevant values for later retrieval.
+\beginlines
+|vardef |^|savepen|| = pen_[incr pen_count_]=currentpen;|
+| pen_lft_[pen_count_]=pen_lft;|
+| pen_rt_[pen_count_]=pen_rt;|
+| pen_top_[pen_count_]=pen_top;|
+| pen_bot_[pen_count_]=pen_bot;|
+| pen_path_[pen_count_]=currentpen_path;|
+| pen_count_ enddef;|
+\smallbreak
+|def |^|clearpen|| = currentpen:=nullpen;|
+| pen_lft:=pen_rt:=pen_top:=pen_bot:=0;|
+| path currentpen_path; enddef;|
+\smallbreak
+|def clear_pen_memory =|^^@clear\_pen\_memory@
+| pen_count_:=0;|
+| numeric pen_lft_[],pen_rt_[],pen_top_[],pen_bot_[];|
+| pen currentpen,pen_[];|
+| path currentpen_path, pen_path_[];|
+| enddef;|
+\endlines
+
+The four basic pen-edge functions offer no surprises:
+^^"lft"^^"rt"^^"top"^^"bot"
+\beginlines
+|vardef lft primary x = x + if pair x: (pen_lft,0) else: pen_lft fi enddef;|
+|vardef rt primary x = x + if pair x: (pen_rt,0) else: pen_rt fi enddef;|
+|vardef top primary y = y + if pair y: (0,pen_top) else: pen_top fi enddef;|
+|vardef bot primary y = y + if pair y: (0,pen_bot) else: pen_bot fi enddef;|
+\endlines
+There are six functions that ^{round} to good positions for pen placement.
+\beginlines
+|vardef |^|good.x|| primary x = hround(x+pen_lft)-pen_lft enddef;|
+|vardef |^|good.y|| primary y = vround(y+pen_top)-pen_top enddef;|
+|vardef |^|good.lft|| primary z = save z_; pair z_;|
+| (z_+(pen_lft,0))t_=round((z+(pen_lft,0))t_); z_ enddef;|
+|vardef |^|good.rt|| primary z = save z_; pair z_;|
+| (z_+(pen_rt,0))t_=round((z+(pen_rt,0))t_); z_ enddef;|
+|vardef |^|good.top|| primary z = save z_; pair z_;|
+| (z_+(0,pen_top))t_=round((z+(0,pen_top))t_); z_ enddef;|
+|vardef |^|good.bot|| primary z = save z_; pair z_;|
+| (z_+(0,pen_bot))t_=round((z+(0,pen_bot))t_); z_ enddef;|
+\endlines
+
+So much for fixed pens. When pen-like strokes are defined by
+outlines, the ^"penpos" macro is of primary importance. Since "penpos"
+may be used quite frequently, we might as well write out the $x$ and~$y$
+coordinates explicitly instead of using the (somewhat slower) $z$~convention:
+\beginlines
+|vardef penpos@#(expr b,d) =|
+| (x@#r-x@#l,y@#r-y@#l)=(b,0) rotated d;|
+| x@#=.5(x@#l+x@#r); y@#=.5(y@#l+y@#r) enddef;|
+\endlines
+
+Simulated pen strokes are provided by the convenient ^@penstroke@ command.
+\beginlines
+|def penstroke text t =|
+| forsuffixes e = l,r: path_.e:=t; endfor|
+| if cycle path_.l: cyclestroke_|
+| else: fill path_.l -- reverse path_.r -- cycle fi enddef;|
+|def cyclestroke_ =|
+| begingroup interim turningcheck:=0;|
+| addto pic_ contour path_.l.t_ withweight 1;|
+| addto pic_ contour path_.r.t_ withweight -1;|
+| cull pic_ dropping origin withweight default_wt_;|
+| addto_currentpicture also pic_;|
+| pic_:=nullpicture endgroup enddef;|
+|path path_.l,path_.r;|
+\finalendlines
+
+\subsection Proof labels and rules. The next main section of |plain.mf|
+is devoted to macros for the annotations on proofsheets. These macros
+are discussed in Appendix~H\null, and they use the ^@special@ and ^@numspecial@
+commands discussed in Appendix~G.
+
+Labels are generated at the lowest level by @makelabel@\kern1pt:^^"lcode\_"
+\beginlines
+|vardef |^|makelabel||@#(expr s,z) = % puts string s at point z|
+| if known z: special lcode_@# & s;|
+| numspecial xpart(z.t_); numspecial ypart(z.t_) fi enddef;|
+\smallskip
+|string lcode_,lcode_.top,lcode_.lft,lcode_.rt,lcode_.bot,|
+| lcode_.top.nodot,lcode_.lft.nodot,lcode_.rt.nodot,lcode_.bot.nodot;|
+|lcode_.top=" 1"; lcode_.lft=" 2"; lcode_.rt=" 3"; lcode_.bot=" 4";|
+|lcode_=" 0"; % change to " /" to avoid listing in overflow column|
+|lcode_.top.nodot=" 5"; lcode_.lft.nodot=" 6";|
+|lcode_.rt.nodot=" 7"; lcode_.bot.nodot=" 8";|
+\endlines
+
+Users generally don't invoke @makelabel@ directly, because there's a convenient
+shorthand. For example, `@labels@$(1,2,3)$' expands into
+`@makelabel@\kern1pt(|"1"|$,z_1$); @makelabel@\kern1pt(|"2"|$,z_2$);
+@makelabel@\kern1pt(|"3"|$,z_3$)'.
+\ (But nothing happens if ^"proofing"$\null\le1$.)
+\beginlines
+|vardef |^|labels||@#(text t) =|
+| if proofing>1: forsuffixes $=t: makelabel@#(str$,z$); endfor fi enddef;|
+|vardef |^|penlabels||@#(text t) =|
+| if proofing>1: forsuffixes $$=l,,r: forsuffixes $=t:|
+| makelabel@#(str$.$$,z$.$$); endfor endfor fi enddef;|
+\endlines
+When there are lots of purely numeric labels, you can say, e.g.,
+\begindisplay
+@labels@(1, @range@ 5 @thru@ 9, @range@ 100 @thru@ 124, 223)
+\enddisplay
+which is equivalent to `@labels@$(1,5,6,7,8,9,100,101,\ldots,124,223)$'.
+Labels are omitted from the proofsheets if the corresponding $z$ value
+isn't known, so it doesn't hurt (much) to include unused subscript numbers
+in a range.
+\beginlines
+|def |^|range|| expr x = numtok[x] enddef;|
+|def |^|numtok|| suffix x=x enddef;|
+|tertiarydef m |^|thru|| n =|
+| m for x=m+1 step 1 until n: , numtok[x] endfor enddef;|
+\weakendlines
+(This @range@ abbreviation will work in any ^@forsuffixes@ list;
+and in a `@for@' list you can even omit the word `@range@'.
+But you might fill~up \MF's main memory if too many values are involved.)
+
+A straight line will be drawn on the proofsheet by @proofrule@.
+Although @makelabel@ takes the current transform into account,
+@proofrule@ does not. There's also a corresponding routine `@screenrule@'
+that puts a straight line in the current picture, so that design
+guidelines will be visible on your screen:
+\beginlines
+|def |^|proofrule||(expr w,z) =|
+| special "rule"; numspecial xpart w; numspecial ypart w;|
+| numspecial xpart z; numspecial ypart z enddef;|
+|def |^|screenrule||(expr w,z) =|
+| addto currentpicture doublepath w--z withpen rulepen enddef;|
+|pen rulepen; rulepen = pensquare scaled 2;|
+\endlines
+(The ^"rulepen" is two pixels wide, because screen rules are usually
+drawn exactly over raster lines. A two-pixel-wide pen straddles the pixel
+edges so that you can ``see'' the correct line position. If
+a two-pixel-wide line proves to be too dark, you can redefine
+"rulepen" to be simply ^@pensquare@; then \MF\ will draw the
+thinnest possible screen rule, but it will be
+a half-pixel too high and a half-pixel too far to the right.)
+
+You can produce lots of proof rules with ^@makegrid@, which connects
+an arbitrary list of $x$~coordinates with an arbitrary list
+of $y$~coordinates:
+\beginlines
+|def makegrid(text xlist,ylist) =|
+| xmin_ := min(xlist); xmax_ := max(xlist);|
+| ymin_ := min(ylist); ymax_ := max(ylist);|
+| for x=xlist: proofrule((x,ymin_), (x,ymax_)); endfor|
+| for y=ylist: proofrule((xmin_,y), (xmax_,y)); endfor|
+| enddef;|
+\endlines
+
+Finally we have a few macros that allow further communication with
+the hardcopy proof-drawing routine of Appendix~H\null. You can change the
+fonts, the thickness of proof rules, and the position of the image
+on its page.
+\beginlines
+|vardef |^|titlefont|| suffix $ = special "titlefont "&str$ enddef;|
+|vardef |^|labelfont|| suffix $ = special "labelfont "&str$ enddef;|
+|vardef |^|grayfont|| suffix $ = special "grayfont "&str$ enddef;|
+|vardef |^|slantfont|| suffix $ = special "slantfont "&str$ enddef;|
+|def |^|proofoffset|| primary z = % shift proof output by z|
+| special "offset"; numspecial xpart z; numspecial ypart z enddef;|
+|vardef |^|proofrulethickness|| expr x =|
+| special "rulethickness"; numspecial x enddef;|
+\finalendlines
+
+\subsection Character and font administration.
+After this elaborate preparation, we're finally ready
+to consider the @beginchar@$\,\ldots\,$@endchar@
+framework for the individual characters of a font. Each ^@beginchar@ begins
+a group, which should end at the next ^@endchar@. Then @beginchar@
+stores the given character code and device-independent
+box dimensions in \MF's internal variables ^"charcode", ^"charwd",
+^"charht", and ^"chardp". Then it computes the device-dependent box
+dimensions ^"w", ^"h", and~^"d". Finally it
+clears the $z$ variables, the current picture, and the
+current pen.
+\beginlines
+|def beginchar(expr c,w_sharp,h_sharp,d_sharp) =|
+| begingroup|
+| charcode:=if known c: byte c else: 0 fi;|
+| charwd:=w_sharp; charht:=h_sharp; chardp:=d_sharp;|
+| w:=hround(charwd*hppp); h:=vround(charht*hppp); d:=vround(chardp*hppp);|
+| charic:=0; clearxy; clearit; clearpen; scantokens extra_beginchar;|
+| enddef;|
+\endlines
+The ^{italic correction} is normally zero, unless the user gives an
+`^@italcorr@' command; even then, the correction stays zero unless
+the given value is positive:
+\beginlines
+|def italcorr expr x_sharp = if x_sharp>0: charic:=x_sharp fi enddef;|
+\endlines
+When we want to change the pixel width $w$ from even to odd or vice
+versa, the ^@change\_width@ macro does the right thing.
+\beginlines
+|def change_width =|
+| w:=w if w>charwd*hppp:- else:+ fi 1 enddef;|
+\endlines
+(The user might also decide to change $w$ in some other way.) \ The
+current value of~$w$ at the time of @endchar@ will be the
+``official'' pixel width of the character, ^"chardx", that is
+shipped to the |gf| output file.
+\beginlines
+|def endchar =|
+| scantokens extra_endchar;|
+| if proofing>0: makebox(proofrule); fi|
+| chardx:=w; % desired width of the character in pixels|
+| shipit;|
+| if displaying>0: makebox(screenrule); showit; fi|
+| endgroup enddef;|
+\endlines
+Extensions to these routines can be provided by putting commands in the
+string variables ^"extra\_beginchar" and ^"extra\_endchar".
+\beginlines
+|string extra_beginchar, extra_endchar;|
+|extra_beginchar=extra_endchar="";|
+\endlines
+
+A ``^{bounding box}'' that surrounds the character according to the
+specifications given in @beginchar@ is produced by ^@makebox@, which
+takes into account the possibility that pixels might not be square.
+An extra line is drawn to mark the width of the character with its
+^{italic correction} included, if this correction is nonzero.
+\beginlines
+|def makebox(text r) =|
+| for y=0,h.o_,-d.o_: r((0,y),(w,y)); endfor % horizontals|
+| for x=0,w: r((x,-d.o_),(x,h.o_)); endfor % verticals|
+| if charic<>0: r((w+charic*hppp,h.o_),(w+charic*hppp,.5h.o_)); fi|
+| enddef;|
+\endlines
+
+The ^@maketicks@ routine is an alternative to @makebox@ that draws less
+conspicuous lines. This makes it easier to visualize a character's
+appearance near the edges of its bounding box.
+\beginlines
+|def maketicks(text r) =|
+| for y=0,h.o_,-d.o_: r((0,y),(10,y)); r((w-10,y),(w,y)); endfor|
+| for x=0,w: r((x,10-d.o_),(x,-d.o_)); r((x,h.o_-10),(x,h.o_)); endfor|
+| if charic<>0: r((w+charic*hppp,h.o_-10),(w+charic*hppp,h.o_)); fi|
+| enddef;|
+\endlines
+
+Overall information about the font as a whole is generally supplied
+^^@font\_size\_etc@
+by the following commands, which are explained in Appendix~F\null.
+\beginlines
+|def font_size expr x = designsize:=x enddef;|
+|def font_slant expr x = fontdimen 1: x enddef;|
+|def font_normal_space expr x = fontdimen 2: x enddef;|
+|def font_normal_stretch expr x = fontdimen 3: x enddef;|
+|def font_normal_shrink expr x = fontdimen 4: x enddef;|
+|def font_x_height expr x = fontdimen 5: x enddef;|
+|def font_quad expr x = fontdimen 6: x enddef;|
+|def font_extra_space expr x = fontdimen 7: x enddef;|
+\smallskip
+|def font_identifier expr x = font_identifier_:=x enddef;|
+|def font_coding_scheme expr x = font_coding_scheme_:=x enddef;|
+|string font_identifier_, font_coding_scheme_;|
+|font_identifier_=font_coding_scheme_="UNSPECIFIED";|
+\finalendlines
+\bigskip
+
+\subsection The endgame. What have we left out? A few miscellaneous
+things still need to be handled. First, we almost forgot to define the
+^"z"~convention for points:
+\beginlines
+|vardef z@#=(x@#,y@#) enddef;|
+\endlines
+Then we need to do something rudimentary about \MF's ``windows.''
+^^"screen\_rows" ^^"screen\_cols"
+\beginlines
+|newinternal screen_rows, screen_cols, currentwindow;|
+|screen_rows:=400; % these values should be corrected,|
+|screen_cols:=500; % by reading in a separate file after plain.mf|
+\smallskip
+|def |^|openit|| = openwindow currentwindow|
+| from origin to (screen_rows,screen_cols) at (-50,300) enddef;|
+|def |^|showit|| = openit; let showit=showit_; showit enddef; % first time only|
+|def showit_ = display currentpicture inwindow currentwindow enddef;|
+\endlines
+Plain \MF\ has several other terse commands like `@openit@' and `@showit@':
+\beginlines
+|def |^|clearxy|| = save x,y enddef;|
+|def |^|clearit|| = currentpicture:=nullpicture enddef;|
+|def |^|shipit|| = shipout currentpicture enddef;|
+|def |^|cullit|| = cull currentpicture dropping (-infinity,0) enddef;|
+\endlines
+
+\medbreak
+The next several macros are handy things to put on your ^{command line}
+when you are starting a \MF\ job (i.e., just before `|input| \<font file
+name>'):
+
+\smallskip\item\bull |screenchars|. Say this when you're making a font
+but want the characters to be displayed just before they are shipped out.
+\item\bull |screenstrokes|. Say this when you're in "proof" mode
+and want to see each stroke as it's added to the current picture.
+\item\bull |imagerules|. Say this when you want to include the bounding box
+in the current character, before you begin to draw it.
+\item\bull |gfcorners|. Say this when you expect to make proofsheets
+with large pixels, from a low-resolution font.
+\item\bull |nodisplays|. Say this to save computer time when you don't
+want "proof" mode to display each character automatically.
+\item\bull |notransforms|. Say this to save computer time when you know
+that the current transform is the identity.
+\beginlines
+|def |^|screenchars|| = % endchar should `showit'|
+| extra_endchar:=extra_endchar&"showit;" enddef;|
+\smallskip
+|def |^|screenstrokes|| = % every stroke should `showit'|
+| def addto_currentpicture text t=|
+| addto currentpicture t; showit enddef; enddef;|
+\smallbreak
+|def |^|imagerules|| = % a box should be part of the character image|
+| extra_beginchar:=extra_beginchar & "makebox(screenrule);" enddef;|
+\smallbreak
+|def |^|gfcorners|| = % `maketicks' should send rules to the gf file|
+| extra_setup:=extra_setup & "let makebox=maketicks;proofing:=1;" enddef;|
+\smallbreak
+|def |^|nodisplays|| = % endchar shouldn't `showit'|
+| extra_setup:=extra_setup & "displaying:=0;" enddef;|
+\smallskip
+|def |^|notransforms|| = % currenttransform should not be used|
+| let t_ = \ enddef;|
+\endlines
+
+We make `^@bye@' synonymous with `^@end@', just in case \TeX\ users expect
+\MF\ programs to end like \TeX\ documents do.
+\beginlines
+|let bye = end; outer end,bye;|
+\endlines
+
+And finally, we provide the default environment that a user gets when
+^^@clear\_pen\_memory@ ^^@mode\_setup@
+simple experiments like those at the beginning of Chapter~5 are desired.
+\beginlines
+|clear_pen_memory; % initialize the `savepen' mechanism|
+|mode_setup; % establish proof mode as the default|
+|numeric |^|mode||,|^|mag||; % but leave mode and mag undefined|
+\weakendlines
+Whew! That's the end of the |plain.mf| file.
+
+\subsection Adapting to local conditions. In order to make plain \MF\
+programs interchangeable between different computers, everybody should use
+the same |plain.mf| base. But there are some things that clearly should
+be customized at each installation:
+
+\smallskip\item\bull Additional modes should be defined, so that fonts
+can be made for whatever output devices are of interest.
+
+\item\bull The proper ^"localfont" mode should be established.
+
+\item\bull The correct numbers should be assigned to
+^"screen\_rows" and ^"screen\_cols".
+
+\smallbreak
+\noindent
+Here's an example of a supplementary file `|local.mf|'
+that would be appropriate for a computer center with the
+hypothetical "cheapo" and "luxo" printers described in Chapter~11.
+We assume that "cheapo" mode is to be identical to "lowres" mode,
+except that the "cheapo" fonts should be generated with a {\sl negative\/}
+value of ^"fillin" (because "cheapo" tends to make diagonal lines lighter
+than normal, not heavier). The terminal screens are assumed to be
+768 pixels wide and 512 pixels high.
+\beginlines
+|% A file to be loaded after "plain.mf".|
+|base_version:=base_version&"/drofnats";|
+\smallskip
+|screen_rows:=512; screen_cols:=768;|
+\smallskip
+|mode_def cheapo = % cheapo mode: to generate fonts for cheapo|
+| lowres_; % do as in lowres mode, except:|
+| fillin:=-.1; % compensate for lighter diagonals|
+| enddef;|
+\smallskip
+|mode_def luxo = % luxo mode: to generate fonts for luxo|
+| proofing:=0; % no, we're not making proofs|
+| fontmaking:=1; % yes, we are making a font|
+| tracingtitles:=1; % yes, show titles online|
+| pixels_per_inch:=2000; % almost 30 pixels per pt|
+| blacker:=.2; % make pens a teeny bit blacker|
+| fillin:=.1; % but compensate for heavy diagonals|
+| o_correction:=1; % and keep the full overshoot|
+| enddef;|
+\smallskip
+|localfont:=cheapo;|
+\weakendlines
+The macro `^@bye@' might also be redefined, as suggested at the close
+of Appendix~F.
+
+To prepare a preloaded base file at this installation, a suitably
+privileged person should run ^|INIMF| in the following way:
+\begintt
+This is METAFONT, Version 2.0 (INIMF) 8 NOV 1989 10:09
+**plain
+(plain.mf
+Preloading the plain base, version 2.0)
+*input local
+(local.mf)
+*dump
+Beginning to dump on file plain.base
+\endtt
+^^{dump}(The stuff after `|**|' or `|*|' is typed by the user; everything
+else is typed by the system. A few more messages actually come out.)
+
+Notice that |local.mf| does not include any new macros or features that a
+programmer could use in a special way. Therefore it doesn't make plain
+\MF\ incompatible with implementations at other computing centers.
+
+Changes and/or extensions to the |plain.mf| macros should never be made,
+unless the resulting base file is clearly distinguished from the standard
+plain base. But new, differently named bases are welcome.
+For example, the author prepared a special base for the
+^{Computer Modern} fonts, so that they could be generated without first
+reading the same 700 lines of macro definitions each time. To load
+this base at high speed, he can type `|&cm|' after \MF's initial
+`|**|'. ^^{asterisk} ^^{ampersand} (Or, on some machines, he has a special
+version called `|cmmf|' in which the new base is already present.)
+
+\endchapter
+
+None but the Base, in baseness doth delight.
+\author MICHAEL ^{DRAYTON}, {\sl Robert, Duke of Normandy\/} (1605)
+ % line 262; quite different in the 1596 version
+
+\bigskip
+
+So far all was plain sailing, as the saying is;
+but Mr.\thinspace Till knew that his main difficulties were yet to come.
+\author FRANCIS E. ^{PAGET}, {\sl Milford Malvoisin\/} (1842) % p209
+ % in the 22nd paragraph of Chapter 9
+
+\eject
+ \beginchapter Appendix C. Character\\Codes
+
+Different computers tend to have different ways of representing the
+characters in files of text, but \MF\ gives the same results on
+all machines, because it converts everything to a standard internal
+code when it reads a file. \MF\ also converts back from its internal
+representation to the appropriate external code, when it writes
+a file of text; therefore most users need not be aware of the fact
+that the ^{codes} have actually switched back and forth inside the machine.
+
+The purpose of this appendix is to define \MF's internal code,
+which has the same characteristics on all implementations of \MF\!\null.
+The existence of such a code is important, because it
+makes \MF\ programs portable.
+\MF's scheme is based on the American Standard Code for
+Information Interchange, known popularly as ``^{ASCII}.'' There are
+128 codes, numbered 0~to~127; we conventionally express the numbers
+in ^{oct}al notation, from |oct"000"| to |oct"177"|, or in
+^{hex}adecimal notation, from |hex"00"| to |hex"7F"|. Thus, the value of
+|ASCII"b"| is normally called |oct"142"| or |hex"62"|, not 98. In the
+^{ASCII} scheme, codes |oct"000"| through |oct"037"| and
+code |oct"177"| are~assigned to special functions; for example,
+code |oct"007"| is called |BEL|, and it means ``Ring the bell.''
+The other 95 codes are assigned to visible symbols and to the
+blank space character. Here is a
+chart that shows ASCII codes in such a way that octal and hexadecimal
+equivalents can easily be read off:
+\beginchart{\global\count255='41\postdisplaypenalty=0\tentt
+ \def\chartstrut{\lower4.3pt\vbox to13.6pt{}}}
+&\oct{00x}&&NUL&&SOH&&STX&&ETX&&EOT&&ENQ&&ACK&&BEL&&\oddline0
+&\oct{01x}&&BS&&HT&&LF&&VT&&FF&&CR&&SO&&SI&\evenline
+&\oct{02x}&&DLE&&DC1&&DC2&&DC3&&DC4&&NAK&&SYN&&ETB&&\oddline1
+&\oct{03x}&&CAN&&EM&&SUB&&ESC&&FS&&GS&&RS&&US&\evenline
+&\oct{04x}&& &&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline2
+&\oct{05x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{06x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline3
+&\oct{07x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{10x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline4
+&\oct{11x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{12x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline5
+&\oct{13x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{14x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline6
+&\oct{15x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{16x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline7
+&\oct{17x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&DEL&\evenline
+\endchart
+
+Ever since ASCII was established in the early 1960s, people have had
+different ideas about what to do with positions |oct"000"| thru |oct"037"| and
+|oct"177"|, because most of the functions assigned to those codes are
+appropriate only for special purposes like file transmission, not for
+applications to printing or to interactive computing.
+Manufacturers soon started producing line printers that were capable of
+generating 128 characters, 33~of~which were tailored to the special needs
+of particular customers; part of the advantage of a standard code was
+therefore lost.
+An extended ASCII code intended for text editing and interactive computing
+ was developed at several universities about 1965, and
+for many years there have been terminals in use at Stanford, MIT,
+Carnegie-Mellon, and elsewhere that have 120 or~121 symbols, not just~95.
+For example, the author ^^{Knuth} developed \MF\ on a keyboard that
+includes the symbols `{\tentex\char'32}', `{\tentex\char'34}',
+`{\tentex\char'35}', and `{\tentex\char'30}', which are easier to use than
+the character pairs `{\tentex^{<>}}', `{\tentex^{<=}}', `{\tentex^{>=}}',
+and `{\tentex^{:=}}'. The full character set looks like this:
+\beginchart{\tentex\postdisplaypenalty=0}
+\normalchart
+\endchart
+\MF\ can also be configured to accept any or all of the character codes
+128--255.
+However, \MF\ programs that make use of anything in addition to the 95
+standard ASCII characters cannot be expected to run on other systems, so
+the use of extended character sets is discouraged.
+
+A possible middle ground has been suggested, based on the fact that
+it's easy to write a
+program that converts extended-character files into standard files by
+substituting `|<>|' for `{\tentex\char'32}', etc. In the author's
+implementation at Stanford, the symbols
+`{\tentex\char'32}', `{\tentex\char'34}', `{\tentex\char'35}',
+and `{\tentex\char'30}' are considered to be in the same class as
+`{\tentex<}', `{\tentex=}', `{\tentex:}', and `{\tentex>}' when tokens are
+formed (see Chapter~6). Tokens like `{\tentex\char'32=}'
+and `{\tentex<\char'35}' are therefore distinct, although
+they both become `{\tentex<>=}' after
+conversion. As long as such tokens are avoided,
+the author's programs can easily be expurgated into a
+portable form for general distribution. \ (Another feasible approach would
+have been to convert nonstandard codes to character pairs during \MF's
+input process; that would have been slightly less efficient.)
+
+Computers with non-ASCII character sets should specify a correspondence
+between 95 distinct characters and the standard ASCII codes |oct"040"|
+thru |oct"176"|. \MF\ programs written on any such machines will be
+completely interchangeable with each other.
+
+\endchapter
+
+If any shall suggest, that some of the Enquiries here insisted upon
+(as particularly those about the Letters of the Alphabet)
+do seem too minute and trivial, for any prudent Man
+to bestow his serious thoughts and time about.
+Such Persons may know, that the discovery
+of the true nature and Cause of any the most minute thing,
+doth promote real Knowledge,
+and therefore cannot be unfit for any Mans endeauours,
+who is willing to contribute to the advancement of Learning.
+\author JOHN ^{WILKINS}, {\sl Towards a Real Character\/} (1668)
+ % preface to the reader
+
+\bigskip
+
+Clearly even the simple A.B.C.~is a thing of mystery.
+Like all codes, it should not be trifled with,
+but it is to be feared that in modern times
+it has not always been respected.
+\author STANLEY ^{MORISON}, {\sl On Type Faces\/} (1923)
+ % from the introduction, near the beginning
+\eject
+ \beginchapter Appendix D. Dirty Tricks
+
+Any powerful computer language can be used in ways that go considerably
+beyond what the language designer originally had in mind, especially
+when macro expansion is possible. Sometimes the unexpected constructions
+are just amusing; sometimes they are disgustingly arcane. But
+sometimes they turn out to be quite useful, and they graduate from ``tricks''
+to the status of ``techniques.'' \ (For example, several of the macros
+now in Appendix~B started out as suggestions for Appendix~D\null.) \
+In any case, gurus of a language always like to explore its limits.
+The depths of \MF\ have hardly been plumbed, but this appendix probably
+reached a new low at the time it was written.
+
+Acknowledgment: More than half of the ideas in this appendix are due to
+John ^{Hobby}, who has been a tireless and inspiring co-worker during the
+entire development of the new \MF\ system.
+
+\advance\medskipamount by -.5pt
+\advance\abovedisplayskip by -.5pt
+\advance\belowdisplayskip by -.5pt
+
+\ninepoint\medskip
+\setbox0=\hbox spread-7\fontdimen4\font % that removes all the shrinkability
+ {\strut Please don't read this material until you've had}
+\setbox1=\hbox to\wd0{\strut plenty of experience with plain \MF\!.}
+\line{\leaders\hbox{\dbend\kern2pt}\hfil\vtop{\box0\box1}}
+\nointerlineskip
+\noindent\strut After you have read and understood the secrets below, you'll
+know all sorts of devious combinations of \MF\ commands,
+and you will often be tempted to write inscrutable macros. Always remember,
+however, that there's usually a simpler and better way to do something
+than the first way that pops into your head. You may not have to
+resort to any subterfuge at all, since \MF\ is able to do lots of things
+in a straightforward way. Try for simple solutions first.
+
+\subsection Macro madness. If you need to write complicated ^{macros}, you'll
+need to be familiar with the fine points in Chapter~20. \MF's symbolic tokens
+are divided into two main categories, ``expandable'' and ``unexpandable'';
+the former category includes all macros and @if@$\,\ldots\,$@fi@ tests and
+@for@$\,\ldots\,$@endfor@ loops, as well as special operations like @input@,
+while the latter category includes the primitive operators and commands
+listed in Chapters 25 and~26. The expansion of expandable tokens takes place
+in \MF's ``^{mouth},'' but primitive statements (including equations,
+declarations, and the various types of commands) are done in \MF's
+``^{stomach}.'' There's a communication between the two, since the stomach
+evaluates expressions that are needed as arguments to the mouth's macros;
+any statement can be embedded in a group expression, so arbitrarily
+complicated things can be done as part of the ^{expansion} process.
+
+Let's begin by considering a toy problem that is treated at the beginning
+of Appendix~D in {\sl The \TeX book}, in case some readers are interested in
+comparing \TeX\ to \MF\!\null. Given a numeric variable $n\ge0$, we wish to
+define a macro |asts| whose replacement text consists of precisely
+$n$~asterisks. This task is somewhat tricky because expansion is suppressed
+when a replacement text is being read; we want to use a ^@for@ loop, but
+loops are special cases of expansion. In other words,
+\begintt
+def asts = for x=1 upto n: * endfor enddef
+\endtt
+defines |asts| to be a macro with a @for@ loop in its replacement text;
+\advance\belowdisplayskip by -1.5pt
+in practice, |asts| would behave as if it contained $n$ asterisks (using
+possibly different values of~$n$), but
+we have not solved the stated problem. The alternative
+\begintt
+def makedef primary n =
+ def asts = for x=1 upto n: * endfor enddef enddef;
+makedef n
+\endtt
+``freezes'' the present value of $n$; but this doesn't solve the problem either.
+
+\advance\medskipamount by .5pt
+\advance\abovedisplayskip by .5pt
+\advance\belowdisplayskip by 2pt
+
+\goodbreak
+
+One solution is to build up the definition by adding one asterisk at a time,
+using ^@expandafter@ as follows:
+\begintt
+def asts = enddef;
+for x=1 upto n:
+ expandafter def expandafter asts expandafter = asts * enddef;
+endfor.
+\endtt
+The three @expandafter@s provide a ``finger'' into the replacement text,
+before @def@ suppresses expansion; without them the replacement text
+would turn out to be `|asts|~|*|', causing infinite recursion.
+
+This solution involves a running time proportional to $n^2$, so the
+reader might wonder why a simpler approach like
+\begintt
+expandafter def expandafter asts expandafter =
+ for x = 1 upto n: * endfor enddef
+\endtt
+wasn't suggested? The reason is that this doesn't work, unless $n=0$!
+A @for@ loop isn't entirely expanded by @expandafter@; only \MF's first
+step in loop expansion is carried out. Namely, the loop text is read,
+and a special ^{inaccessible} token `^|ENDFOR|' is placed at its end.
+Later on when \MF's mouth encounters `|ENDFOR|' (which incidentally is an
+expandable token, but it wasn't listed in Chapter~20), the loop text is
+re-inserted into the input stream, unless of course the loop has finished.
+The special |ENDFOR| is an `^@outer@' token, hence it should not
+appear in replacement texts; \MF\ will therefore stop with a ``^{forbidden
+token}'' error if you try the above with $n\ge1$.
+^^@inner@ You might try to defeat the outerness by saying
+\begintt
+for x=1: inner endfor;
+\endtt
+but \MF\ won't let you. And even if this had worked, it wouldn't have
+solved the problem; it would simply have put |ENDFOR| into the
+replacement text of |asts|, because expansion is inhibited when the
+replacement text is being read.
+
+There's another way to solve the problem that seems to have running
+time proportional to~$n$ rather than~$n^2$:
+\begintt
+scantokens("def asts=" for x=1 upto n: & "* " endfor) enddef;
+\endtt
+^^{*} but actually \MF's string ^{concatenation}
+operation takes time proportional to the length of the strings it
+deals with, so the running time is still order~$n^2$.
+Furthermore, the ^{string} operations in \MF\ are rather primitive,
+because this isn't a major aspect of the language; so it turns out that
+this approach uses order~$n^2$ storage cells in the string pool, although
+they are recycled later. Even if the ^{pool size} were infinite, \MF's
+``^{buffer size}'' would be exceeded for large~$n$, because ^@scantokens@
+puts the string into the input buffer before scanning it.
+
+Is there a solution of order $n$? Yes, of course. For example,
+\begintt
+def a=a* enddef;
+for x=0 upto n:
+ if x=n: def a=quote quote def asts = enddef; fi
+ expandafter endfor a enddef;
+showtoken asts.
+\endtt
+(The first `^@quote@' is removed by the @for@, hence one will survive until
+$a$~is redefined. If you don't understand this program, try ^^{debugging tricks}
+running it with $n=3$; insert an isolated expression `|0;|' just before
+the~`|if|', and look at the lines of context that are shown when
+\MF\ gives you four error messages.) \ The only flaw in this method is
+that it uses~up $n$ cells of stack space; \MF's ^{input stack size}
+may have to be increased, if $n$ is bigger than 25~or~so.
+
+\smallbreak
+The asterisk problem is just a puzzle; let's turn now to a genuine
+application. Suppose we want to define a macro called `"ten"' ^^{Derek}
+whose replacement text is the contents of the parameter file ^|logo10.mf|
+in Chapter~11, up to but {\sl not\/} including the last two lines of
+that file. Those last two lines say
+\begintt
+input logo % now generate the font
+end % and stop.
+\endtt
+The "ten" macro will make it possible to set up the 10-point parameters
+repeatedly (perhaps alternating with 9-point parameters in a "nine" macro);
+Appendix~E explains how to create a meta-design tool via such macros.
+
+One idea would be to try to input the entire file |logo10.mf| as the
+replacement text for "ten". We could nullify the effect of the last three
+unwanted tokens by saying
+\begintt
+save input,logo,end;
+forsuffixes s=input,logo,end: let s=\; endfor
+\endtt
+just before "ten" is used. To get the entire file as a replacement text,
+we can try one of the approaches that worked in the asterisk problem, say
+\begintt
+expandafter def expandafter ten expandafter = input logo10 enddef.
+\endtt
+But this first attempt runs awry if we haven't already redefined `^@end@';
+Appendix~B makes `@end@' an `^@outer@' token, preventing its appearance
+in replacement texts. So we say `^@inner@ @end@'
+and try again, only to discover an unwritten law that somehow never
+came up in Chapters 20 or~26:
+\begintt
+Runaway definition?
+font_size10pt#;ht#:=6pt#;xgap#:=0.6pt#;u#:=4/9pt#;s#:=0;o#:=1/ ETC.
+! File ended while scanning the definition of ten.
+<inserted text>
+ enddef
+l.2 ...fter ten expandafter = input logo10
+ enddef;
+\endtt
+^^|Runaway| ^^|File ended...|
+The ^{end of a file} is invisible; but it's treated like an `@outer@'
+token, in the sense that a file should never end when \MF\ is passing
+rapidly over text.
+
+Therefore this whole approach is doomed to failure. We'll have to find a
+way to stop the replacement text before the file ends.
+OK, we'll redefine `@input@' so that it means `@enddef@\kern1pt', and
+redefine "logo" so that it means `^@endinput@'.
+\begintt
+let INPUT = input; let input = enddef; let logo = endinput;
+expandafter def expandafter ten expandafter = INPUT logo10;
+showtoken ten.
+\endtt
+It works! By the way, the line with three expandafters can be replaced by
+a more elegant construction that uses @scantokens@ as follows:
+\begintt
+scantokens "def ten=" INPUT logo10;
+\endtt
+This does the job because \MF\ always looks ahead and expands the token
+immediately following an expression that is being evaluated. \ (The
+expression in this case is the string |"def|~|ten="|, which is an argument
+to @scantokens@. The token that immediately follows an expression
+almost always needs to be examined in order to be sure that the expression
+has ended, so \MF\ always examines it.) \ Curiously, the @expandafter@
+alternative causes "ten"'s replacement text to begin with the tokens
+`|font_size10pt#;ht#:=...|', while the @scantokens@ way makes it start
+with `|designsize:=(10);ht#:=...|'. Do you see why? In the second case,
+expansion continued until an unexpandable token (`|designsize|') was
+found, so the |font_size| macro was changed into its replacement text; but
+@expandafter@ just expanded `|INPUT|'.
+
+Now let's make the problem a bit harder. Suppose we know that `|input|'
+comes at the end of where we want to read, but we don't know that `|logo|'
+will follow. We know that some program file name will be there, but
+it might not be for the logo font. Furthermore, let's assume that `|end|'
+might not be present; therefore we can't simply redefine it to be @enddef@.
+In this case we can make `|input|' into a right ^{delimiter}, and
+read the file as a {\sl delimited ^{text argument}\/}; ^^{argument}
+that will give us enough time to insert other tokens, which will
+terminate the input and flush the unwanted file name. But the construction
+is more complex:
+\begintt
+let INPUT = input; delimiters begintext input;
+def makedef(expr name)(text t) =
+ expandafter def scantokens name = t enddef;
+ endinput flushfilename enddef;
+def flushfilename suffix s = enddef;
+makedef("ten") expandafter begintext INPUT logo10;
+showtoken ten.
+\endtt
+This example merits careful study, perhaps with `^@tracingall@' to
+show exactly how \MF\ proceeds. We have assumed that the unknown file
+name can be parsed as a suffix; this solves the problem that a file cannot
+end inside of a @text@ parameter or a false condition. \ (If we knew that
+`@end@' were present, we could have replaced `|endinput|~|flushfilename|' by
+`|if|~|false:|' and redefined `|end|' to be `|fi|'.)
+
+Let's turn now to a simpler problem. \MF\ allows you to consider the
+`^{and}' of two Boolean expressions, but it always evaluates both
+expressions. This is problematical in situations like
+\begintt
+if pair x and (x>(0,0)): A else: B fi
+\endtt
+because the expression `|x>(0,0)|' will stop with an error message
+unless $x$ is of type @pair@. The obvious way to avoid this error,
+\begintt
+if pair x: if x>(0,0): A else: B fi else: B fi
+\endtt
+is cumbersome and requires |B| to appear twice. What we want is a
+``^{conditional and}'' operation in which the second Boolean expression is
+evaluated only if the first one turns out to be true; then we can safely write
+\begintt
+if pair x cand (x>(0,0)): A else: B fi.
+\endtt
+Similarly we might want ``^{conditional or}'' in which the second operand is
+evaluated only if the first is false, for situations like
+\begintt
+if unknown x cor (x<0): A else: B fi.
+\endtt
+Such ^|cand| and ^|cor| macros can be defined as follows:
+\begintt
+def cand(text q) = startif true q else: false fi enddef;
+def cor(text q) = startif true true else: q fi enddef;
+tertiarydef p startif true = if p: enddef;
+\endtt
+the ^{text arguments} are now evaluated only when necessary. We have essentially
+^^@if@ replaced the original line by
+\begintt
+if if pair x: x>(0,0) else: false fi: A else: B fi.
+\endtt
+This construction has one catch; namely, the right-hand operands of |cand| and
+|cor| must be explicitly enclosed in delimiters. But delimiters are only a
+minor nuisance, because the operands to `and' and `or' usually need them
+anyway. It would be impossible to make |cand| and |cor| obey the normal
+expression ^{hierarchy}; when macros make primary/secondary/tertiary
+distinctions, they evaluate their arguments, and such evaluation is
+precisely what |cand| and |cor| want to avoid.
+
+If these |cand| and |cor| macros were changed so that they took
+{\sl undelimited\/} text arguments, the text argument wouldn't stop at a colon.
+We could, however, use such modified macros with ^{group delimiters}
+instead. For example, after
+\begintt
+let {{ = begingroup; let }} = endgroup;
+def cand text q = startif true q else: false fi enddef
+\endtt
+we could write things like
+\begintt
+if {{pair x cand x>(0,0)}}: A else: B fi.
+\endtt
+(Not that this buys us anything; it just illustrates a property of
+undelimited text arguments.) \ Group delimiters
+are not valid delimiters of {\sl delimited\/} text arguments.
+
+Speaking of group delimiters, the gratuitous ^@begingroup@ and ^@endgroup@
+tokens added by ^@vardef@ are usually helpful, but they can be a nuisance.
+For example, suppose we want to write a |zz|~macro such that
+`|zz1..zz2..zz3|' expands into
+\begintt
+z1{dz1}..z2{dz2}..z3{dz3}
+\endtt
+It would be trivial to do this with @def@:
+\begintt
+def zz suffix $ = z${dz$} enddef;
+\endtt
+but this makes |zz| a ``^{spark}.'' Let's suppose that we want to use
+@vardef@, so that |zz| will be usable in suffixes of variable names.
+Additional @begingroup@ and @endgroup@ delimiters will mess up the
+syntax for paths, so we need to get rid of them. Here's one way to
+finesse the problem:
+\begintt
+vardef zz@# =
+ endgroup gobbled true z@#{dz@#} gobble begingroup enddef.
+\endtt
+The ^|gobbled| and ^|gobble| functions of Appendix~B will remove the ^{vacuous
+expressions} `@begingroup@~@endgroup@' at the beginning and end of
+the replacement text.
+
+(The initial @begingroup@ @endgroup@ won't be gobbled if the vardef is
+being read as a primary instead of as a secondary, tertiary, or
+expression. But in such cases you probably don't mind having @begingroup@
+present.)
+
+\subsection Fortuitous loops.
+The `^{max}' and `^{min}' macros in Appendix~B make use of the fact
+that commas are like `|)(|' in argument lists. Although the
+definition heading is
+\begintt
+def max(expr x)(text t)
+\endtt
+we can write `max$(a,b,c)$' and this makes $x=a$ and $t=\null$`$b,c$'.
+Of course, a person isn't supposed to say `max$(a)(b)(c)$'.
+
+Here are two more applications of the idea: We want `^{inorder}$(a,b,c)$'
+to be true if and only if $a\le b\le c$; and we want
+`^@equally\_spaced@$(x_1,x_2,x_3)\,"dx"$' to produce the equations
+`$x_2-x_1=x_3-x_2=dx$'.
+\begintt
+def inorder(expr x)(text t) =
+ ((x for u=t: <= u)
+ and (u endfor gobbled true true)) enddef;
+def equally_spaced(expr x)(text t) expr dx =
+ x for u=t: - u = u endfor gobbled true
+ - dx enddef.
+\endtt
+Isn't this fun? \ (Look closely.)
+
+There is a problem, however, if we try to use these macros with
+loops in the arguments. Consider the expressions
+\begintt
+inorder(for n=1 upto 10: a[n], endfor infinity),
+inorder(a[1] for n=2 upto 10: ,a[n] endfor),
+inorder(a[1],a[2] for n=3 upto 10: ,a[n] endfor);
+\endtt
+the first two give error messages, but the third one works!
+The reason is that, in the first two cases,
+the @for@ loop begins to be expanded before \MF\ begins to read the
+^{text argument}, hence ^|ENDFOR| rears its ugly head again.
+We can avoid this problem by rewriting the macros in a more complicated
+way that doesn't try to single out the first argument~$x$:
+\begintt
+def inorder(text t) =
+ expandafter startinorder for u=t:
+ <= u endgroup and begingroup u endfor
+ gobbled true true endgroup) enddef;
+def startinorder text t =
+ (begingroup true enddef;
+def equally_spaced(text t) expr dx =
+ if pair dx: (whatever,whatever) else: whatever fi
+ for u=t: - u = u endfor gobbled true
+ - dx enddef;
+\endtt
+Two separate tricks have been used here: (1)~The `^@endgroup@' within
+`inorder' will stop an undelimited text argument; this gets rid
+of the unwanted `|<=|~|u|' at the beginning. (2)~A throwaway variable,
+`^"whatever"', nullifies an unwanted equation at the beginning of
+`@equally\_spaced@'. With the new definitions,
+all three of the expressions above will be understood,
+and so will things like
+\begintt
+equally_spaced(for n=1 upto 10: x[n], endfor whatever) dx.
+\endtt
+Furthermore the single-argument cases now work:
+`inorder($a$)' will always be true, and
+`@equally\_spaced@($x)\,"dx"$' will produce no new equations.
+
+If we want to improve ^{max} and ^{min} in the same way, so that a person can
+specify loop arguments like
+\begintt
+max(a[1] for n=2 upto 10: ,a[n] endfor)
+\endtt
+and so that `max($a)=a$' in the case of a single argument, we have to
+work harder, because max and min treat their first argument in quite
+a special way; they need to apply the special macro ^"setu\_", which defines
+the type of the auxiliary variable "u\_". The fastest way to solve this
+problem is probably to use a token whose meaning changes during the
+first time through the loop:
+\begintt
+vardef max(text t) =
+ let switch_ = firstset_;
+ for u=t: switch_ u>u_: u_ := u ;fi endfor
+ u_ enddef;
+vardef min(text t) =
+ let switch_ = firstset_;
+ for u=t: switch_ u<u_: u_ := u ;fi endfor
+ u_ enddef;
+def firstset_ primary u =
+ setu_ u; let switch_ = if; if false: enddef.
+\endtt
+Incidentally, the author's ^^{Knuth} first programs for max and min
+contained an interesting bug. They started with `@save@ "u\_"', and they
+tried to recognize the first time through the loop by testing if "u\_" was
+unknown. This failed because "u\_" could be constantly unknown in
+well-defined cases like max$(x,x+1,x+2)$.
+
+\subsection Types. Our programs for |inorder|, |equally_spaced|, and
+|max| are careful not to make unnecessary assumptions about the type
+of an expression. The `round' and `byte' functions in Appendix~B
+are further examples of macros that change behavior based
+on the types of their @expr@ arguments. Let's look more closely at
+applications of type testing. \looseness=-1
+
+When the author was developing macros for plain \MF\!, his first
+``correct'' solution for |max| had the following form:
+\begintt
+vardef max(text t) =
+ save u_; boolean u_;
+ for u=t: if boolean u_: setu_ u
+ elseif u_<u: u_ := u fi; endfor
+ u_ enddef.
+\endtt
+This was interesting because it showed that there was no need to
+set "u\_" to true or false; the simple fact that it was boolean
+was enough to indicate the first time through the loop. \ (A slightly
+different "setu\_" macro was used at that time.)
+
+We might want to generalize the `^{scaled}' operation of \MF\ so that
+`scaled~$(x,y)$' is shorthand for `^{xscaled}~$x$ ^{yscaled}~$y$'.
+That's pretty easy:
+\begintt
+let SCALED = scaled;
+def scaled primary z =
+ if pair z: xscaled xpart z yscaled ypart z
+ else: SCALED z fi enddef;
+\endtt
+It's better to keep the primitive operation `|SCALED| |z|' here than to replace
+it by the slower variant `|xscaled| |z| |yscaled| |z|'.
+
+\MF\ allows you to compare booleans, numerics, pairs, strings, and
+transforms for equality; but it doesn't allow the expression
+`$p=q$' where $p$ and~$q$ are paths or pens or pictures. Let's
+write a general ^{equality test} macro ^^{==} such that `$p==q$'
+will be true if and only if $p$ and $q$ are known and equal,
+whatever their type.
+\begintt
+tertiarydef p == q =
+ if unknown p or unknown q: false
+ elseif boolean p and boolean q: p=q
+ elseif numeric p and numeric q: p=q
+ elseif pair p and pair q: p=q
+ elseif string p and string q: p=q
+ elseif transform p and transform q: p=q
+ elseif path p and path q:
+ if (cycle p = cycle q) and (length p = length q)
+ and (point 0 of p = point 0 of q): patheq p of q
+ else: false fi
+ elseif pen p and pen q: (makepath p == makepath q)
+ elseif picture p and picture q: piceq p of q
+ elseif vacuous p and vacuous q: true
+ else: false fi enddef;
+vardef vacuous primary p =
+ not(boolean p or numeric p or pair p or path p
+ or pen p or picture p or string p or transform p) enddef;
+vardef patheq expr p of q =
+ save t; boolean t; t=true;
+ for k=1 upto length p:
+ t := (postcontrol k-1 of p = postcontrol k-1 of q)
+ and (precontrol k of p = precontrol k of q)
+ and (point k of p = point k of q);
+ exitunless t; endfor
+ t enddef;
+vardef piceq expr p of q =
+ save t; picture t;
+ t=p; addto t also -q;
+ cull t dropping origin;
+ (totalweight t=0) enddef;
+\endtt
+If $p$ and $q$ are numeric or pair expressions, we could relax the condition
+that they both be known by saying `@if@ known $p-q$: $p=q$ @else@:~@false@ @fi@';
+transforms could be handled similarly by testing each of their six parts.
+But there's no way to tell if booleans, paths, etc., have been equated
+when they're both unknown, without the risk of irrevocably changing the
+values of other variables.
+
+\subsection ^{Nonlinear equations}. \MF\ has a built-in solution mechanism
+for linear equations, but it balks at nonlinear ones.
+You might be able to solve a set of nonlinear equations yourself by
+means of algebra or calculus, but in difficult cases it is probably
+simplest to use the `^"solve"' macro of plain \MF\!\null. This makes it
+possible to solve $n$~equations in $n$~unknowns, provided that at most
+one of the equations is nonlinear when one of the unknowns is fixed.
+
+The general technique will be illustrated here in the case $n=3$.
+Let us try to find numbers $a$, $b$, and~$c$ such that
+$$\eqalign{-2a+3b/c&=c-3;\cr
+ ac+2b&=c^3-20;\cr
+ a^3+b^3&=c^2.\cr}$$
+When $c$ is fixed, the first two equations are linear in $a$ and~$b$.
+We make an inequality out of the remaining equation by changing `$=$'
+to~`$<$', then we embed the system in a boolean-valued function:
+\begintt
+vardef f(expr c) = save a,b;
+ -2a + 3b/c = c - 3;
+ a*c + 2b = c*c*c - 20;
+ a*a*a + b*b*b < c*c enddef;
+c = solve f(1,7);
+-2a + 3b/c = c - 3;
+a*c + 2b = c*c*c - 20;
+show a, b, c.
+\endtt
+If we set ^"tolerance"$\null="epsilon"$ (which is the minimum value
+that avoids infinite looping in the "solve" routine), the values
+$a=1$, $b=2$, and $c=3$ are shown (so it is obvious that the example
+was rigged). If "tolerance" has its default value~0.1, we get
+$a=1.05061$, $b=2.1279$, $c=3.01563$; this would probably be close
+enough for practical purposes, assuming that the numbers represent
+pixels. \ (Increasing the tolerance saves time because it
+decreases the number of iterations within "solve"; you have to
+balance time versus necessary accuracy.)
+
+The only tricky thing about this use of solve was the choice of the
+numbers 1 and~7 in `$f(1,7)$'. In typical applications we'll usually have
+obvious values of the unknown where $f$ will be true and false,
+but a bit of experimentation was necessary for the problem considered
+here. In fact,
+it turns out that $f(-3)$ is~true and $f(-1)$ is false, in this
+particular system; setting $c="solve"\,f(-3,-1)$ leads to
+another solution: $a=7.51442$, $b=-7.48274$, $c=-2.3097$. Furthermore,
+it's interesting to observe that this system has no solution with
+$c$ between $-1$ and~$+1$, even though $f(+1)$ is true and
+$f(-1)$ is false! When $c\rightarrow0$, the quantity $a^3+b^3$
+approaches $-\infty$ when $c$~is negative, $+\infty$ when $c$~is
+positive. An attempt to `"solve" $f(1,-1)$' will divide by zero and
+come up with several arithmetic overflows.
+
+\hangindent=-42mm \hangafter=-7
+Let's consider now a real application instead of a contrived example.
+\rightfig Da (34mm x 24mm) ^20pt
+We wish to find the vertices of a ^{parallelogram}
+$z_{1l}$,~$z_{1r}$, $z_{0l}$,~$z_{0r}$, such that
+\begindisplay
+$x_{1l}=a$; \ \ $y_{1r}=b$; \ \ $z_{0r}=(c,d)$;\cr
+length$(z_{1r}-z_{1l})$ $=$ length$(z_{0r}-z_{0l})$ $=$ "stem",\cr
+\enddisplay
+and such that the lines $z_{1r}\dashto z_{1l}$ and
+$z_{1r}\dashto z_{0r}$ meet at a given angle~$\phi$. We can consider
+the common angle~$\theta$ of $z_{1r}-z_{1l}$ and $z_{0r}-z_{0l}$ to be
+the ``nonlinear'' unknown, so the equations to be solved can be
+written
+\begindisplay
+$\penpos1("stem",\theta)$; \ \ $\penpos0("stem",\theta)$;\cr
+$x_{1l}=a$; \ \ $y_{1r}=b$; \ \ $z_{0r}=(c,d)$;\cr
+angle$(z_{1r}-z_{0r})\,=\,\theta+\phi$.\cr
+\enddisplay
+When $\theta$ has a given value, all but the last of these equations
+are linear; hence we can solve them by turning the crank in our general method:
+\begintt
+vardef f(expr theta) = save x,y;
+ penpos1(stem,theta); penpos0(stem,theta);
+ x1l=a; y1r=b; z0r=(c,d);
+ angle(z1r-z0r)<theta+phi enddef;
+theta=solve f(90,0);
+penpos1(stem,theta); penpos0(stem,theta);
+x1l=a; y1r=b; z0r=(c,d);
+show z1l,z1r,z0l,z0r,theta,angle(z1r-z0r).
+\endtt
+For example, if $a=1$, $b=28$, $c=14$, $d=19$, $"stem"=5$, and $\phi=80$,
+we get
+\begindisplay \def\qquad{\hskip1.5em}
+$(1,23.703)$&$(3.557,28)$&$(11.443,14.703)$&$(14,19)$&59.25&139.25
+\enddisplay
+as answers when $"tolerance"="epsilon"$, and
+\begindisplay \def\qquad{\hskip1.5em}
+$(1,23.702)$&$(3.554,28)$&$(11.446,14.702)$&$(14,19)$&59.28&139.25
+\enddisplay
+when $"tolerance"=0.1$.
+The function $f$ prescribed by the general method
+can often be simplified; for example, in this case we can remove
+redundancies and get just
+\begintt
+vardef f(expr theta) = save x,y;
+ penpos1(stem,theta); x1l=a; y1r=b;
+ angle(z1r-(c,d))<theta+phi enddef.
+\endtt
+The problem just solved can be called the ``^{d} problem,'' because it arose in
+connection with N.~N. ^{Billawala}'s meta-design of a ^{black-letter}
+`{\manual?}', and because it appears in Appendix~D.
+
+\subsection Nonlinear interpolation.
+Suppose a designer has empirically determined good values of some quantity
+$f(x)$ for several values of~$x$; for example, $f(x)$ might be a
+stroke weight or a serif length or an amount of overshoot, etc. These
+empirical values can be generalized and incorporated into a ^{meta-design}
+if we are able to ^{interpolate} between the original $x$'s, obtaining
+$f(x)$ at intermediate points.
+
+Suppose the data points are known for $x=x_1<x_2<\cdots<x_n$. We can
+represent $f(x)$ by its graph, which we can assume is well approximated
+by the \MF\ path defined by
+\begindisplay
+$F\,=\,\bigl(x_1,f(x_1)\bigr)\to\bigl(x_2,f(x_2)\bigr)\to
+\<etc.>\to\bigl(x_n,f(x_n)\bigr)$
+\enddisplay
+if $f(x)$ is a reasonable ^{function}. Therefore interpolation can be
+done by using path intersection (!):
+\begintt
+vardef interpolate expr F of x = save t; t =
+ if x < xpart point 0 of F: extrap_error 0
+ elseif x > xpart point infinity of F: extrap_error infinity
+ else: xpart(F intersectiontimes verticalline x) fi;
+ ypart point t of F enddef;
+def extrap_error = hide(errhelp "The extreme value will be used.";
+ errmessage "`interpolate' has been asked to extrapolate";
+ errhelp "") enddef;
+vardef verticalline primary x =
+ (x,-infinity)--(x,infinity) enddef;
+\endtt
+For example, if $f(1)=1$, $f(3)=2$, and $f(15)=4$, this interpolation
+scheme gives `interpolate $(1,1)\to(3,2)\to(15,4)$ of~7' the value 3.37.
+
+\subsection Drawing with ^{overlays}. Let's leave numerical computations
+now and go back into the realm of pictures. Bruce ^{Leban} has suggested
+an extension of plain \MF's `^@clearit@/^@showit@/^@shipit@' commands
+by which `^@fill@' and `^@draw@' essentially operate on imaginary sheets of
+clear plastic. A new command `^@keepit@' places a fresh sheet of plastic
+on top of whatever has already been drawn, thereby preserving the covered image
+against subsequent erasures.
+
+We can implement @keepit@ by introducing a new picture variable
+^"totalpicture", and new boolean variables ^"totalnull", ^"currentnull",
+then defining macros as follows:
+\begintt
+def clearit = currentpicture:=totalpicture:=nullpicture;
+ currentnull:=totalnull:=true; enddef;
+def keepit = cull currentpicture keeping (1,infinity);
+ addto totalpicture also currentpicture;
+ currentpicture:=nullpicture;
+ totalnull:=currentnull; currentnull:=true; enddef;
+def addto_currentpicture =
+ currentnull:=false; addto currentpicture enddef;
+def mergeit (text do) =
+ if totalnull: do currentpicture
+ elseif currentnull: do totalpicture
+ else: begingroup save v; picture v; v:=currentpicture;
+ cull v keeping (1,infinity); addto v also totalpicture;
+ do v endgroup fi enddef;
+def shipit = mergeit(shipout) enddef;
+def showit_ = mergeit(show_) enddef;
+def show_ suffix v = display v inwindow currentwindow enddef;
+\endtt
+The "totalnull" and "currentnull" bookkeeping isn't strictly necessary,
+but it contributes greatly to the efficiency of this scheme if the
+extra generality of @keepit@ is not actually being used.
+The `$v$' computations in @mergeit@ involve copying the accumulated
+picture before displaying it or shipping it out; this takes time,
+and it almost doubles the amount of memory needed, so we try to avoid it
+when possible.
+
+\subsection Filing pictures. If you want to store a picture in a file
+and read it in to some other \MF\ job, you face two problems:
+(1)~\MF's @shipout@ command implicitly culls the picture, so that only
+binary data is left. Pixel values $>0$ are distinguished from pixel
+values $<=0$, but no other information about those values will survive.
+\ (2)~The result of ^@shipout@ can be used in another \MF\ job only if
+you have an auxiliary program that converts from binary ^|gf| format
+to a \MF\ source program; \MF\ can write |gf| files, but it can't
+read them.
+
+These problems can be resolved by using \MF's ^{transcript} or ^{log file}
+as the output medium, instead of using the |gf| file. For example, let's
+consider first the use of ^"tracingedges". Suppose we say
+\begindisplay
+"tracingedges" $:=$ 1;\cr
+\<any sequence of @fill@, @draw@, or @filldraw@ commands>\cr
+@message@ |"Tracing edges completed."|; \ $"tracingedges":=0$;\cr
+\enddisplay
+then the log file will contain lines such as the following:
+\beginlines
+|Tracing edges at line 15: (weight 1)|
+|(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).|
+\smallskip
+|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).|
+\smallskip
+|Tracing edges at line 18: (weight -1)|
+|(No new edges added.)|
+\smallskip
+|Tracing edges completed.|
+\endlines
+Let us write macros so that these lines are acceptable input to \MF\!.
+\begintt
+def Tracing=begingroup save :,[,],Tracing,edges,at,weight,w;
+ delimiters []; let Tracing = endfill; interim turningcheck := 0;
+ vardef at@#(expr wt) = save (,); w := wt;
+ let ( = lp; let ) = rp; fill[gobble begingroup enddef;
+ let edges = \; let weight = \; let : = \; enddef;
+def lp = [ enddef;
+def rp = ] -- enddef;
+vardef No@# = origin enddef;
+def endfill = cycle] withweight w endgroup; enddef;
+def completed = endgroup; enddef;
+\endtt
+^^"turningcheck" ^^@save@ ^^@delimiters@
+The precise form of edge-traced output, with its limited vocabulary
+and its restricted use of parentheses and commas, has been exploited here.
+
+With slight changes to this code, you can get weird effects.
+For example, if the definition of |rp| is changed to `|]..tension 4..|',
+^^{tension} and if `|scaled|~|5pt|' is inserted before `|withweight|',
+the image will be an ``^{almost digitized}'' character:
+\displayfig Daa (18.5mm)
+(The bumps at the left here are due to the repeated points `|(1,5)|' and
+`|(3,5)|' in the original data. You can remove them by adding an extra
+pass, first tracing the edges that are output by the {\sl unmodified\/}
+|Tracing| macros.)
+
+Although the effects of @fill@ and @draw@ can be captured by
+"tracingedges", other operations like ^{culling} are not traced.
+Let us therefore consider the more general picture representation
+that \MF\ produces when ^"tracingoutput" is positive, or when you
+ask it to ^@show@ a picture (see Chapter~13). The macros on the next
+page will recreate a picture from input of the form
+\begintt
+beginpicture
+row 1: 1+ -2- || 0+ 2-
+row 0: || 0+ 2++ 5---
+row -2: 0- -2+ ||
+endpicture
+\endtt
+where the middle three lines have been copied verbatim from a transcript
+file. \ (The task would be easier if the token `|-|' didn't have
+to perform two different functions!)
+\begintt
+let neg_ = -; let colon_ = :;
+def beginpicture =
+ begingroup save row, ||, :, ---, --, +, ++, +++, v, xx, yy, done;
+ picture v; v := nullpicture; interim turningcheck := 0;
+ let --- = mmm_; let -- = mm_;
+ let + = p_; let ++ = pp_; let +++ = ppp_;
+ let row = pic_row; let || = relax; let : = pic_colon; : enddef;
+def pic_row primary y = done; yy := y; enddef;
+def pic_colon primary x =
+ if known x colon_ ; xx := x; pic_edge fi enddef;
+def pic_edge =
+ let - = m_;
+ addto v contour unitsquare xscaled xx shifted(0,yy) enddef;
+def mmm_ = withweight 3; let - = neg_; : enddef;
+def mm_ = withweight 2; let - = neg_; : enddef;
+def m_ = withweight 1; let - = neg_; : enddef;
+def p_ = withweight neg_1; let - = neg_; : enddef;
+def pp_ = withweight neg_2; let - = neg_; : enddef;
+def ppp_ = withweight neg_3; let - = neg_; : enddef;
+transform xy_swap; xy_swap = identity rotated 90 xscaled -1;
+def endpicture = done;
+ v transformed xy_swap transformed xy_swap endgroup enddef;
+\endtt
+The reader will find it instructive to study these macros closely.
+When `|done|' appears, it is an unknown primary, so |pic_colon|
+will not attempt to generate another edge. Each new edge also
+inserts a cancelling edge at $x=0$. The two applications ^^"xy\_swap"
+of |xy_swap| at the end will clear away all redundant edges. (Double
+swapping is a bit faster than the operation `|rotated-90| |rotated|~|90|'
+that was used for this purpose in Chapter~13.)
+
+\subsection Fattening a pen. Let's move on to another aspect of
+\MF\ by considering
+an operation on ^{pen} ^{polygons}: Given a @pen@ value~$p$,
+the task is to construct a pen `^@taller@~$p$' that is one pixel
+taller. For example, if $p$ is the ^{diamond} nib
+`$(0.5,0)\dashto(0,0.5)\dashto(-0.5,0)\dashto(0,-0.5)\dashto\cycle$',
+the taller nib will be
+\begindisplay
+$(0.5,0.5)\dashto(0,1)\dashto(-0.5,0.5)\dashto(-0.5,-0.5)\dashto(0,-1)
+ \dashto(0.5,-0.5)\dashto\cycle$;
+\enddisplay
+if $p$ is a tilted ^{penrazor} `$(-x,-y)\dashto(x,y)\dashto\cycle$',
+the taller nib will be
+\begindisplay
+$(-x,-y-0.5)\dashto(x,y-0.5)\dashto(x,y+0.5)\dashto(-x,-y+0.5)\dashto\cycle$,
+\enddisplay
+assuming that $x>0$. The macro itself turns out to be fairly simple, but
+it makes instructive use of ^{path} and pen operations.
+
+We want to split the pen into two parts, a ``bottom'' half and a ``top''
+half; the bottom half should be shifted down by .5~pixels, and the
+top half should be shifted up. The dividing points between halves occur
+at the leftmost and rightmost vertices of the pen. Hmmm; a potential problem
+arises if there are two or more leftmost or rightmost points; for example,
+what if we try to make `@taller@ @taller@~$p$'? Fortunately \MF\ doesn't
+mind if a pen polygon has three or more consecutive vertices that
+lie on a line, hence we can safely choose {\sl any\/} leftmost
+point and any rightmost point.
+
+The next question is, ``How should we find leftmost and rightmost
+points?'' We will, of course, use ^@makepath@ to find the set of all
+vertices; so we could simply traverse the path and find the minimum
+and maximum $x$~coordinates. However, it will be faster (and more fun)
+to use either ^{directiontime} or ^{penoffset} for this purpose.
+Let's try directiontime first:
+\begintt
+vardef taller primary p =
+ save r, n, t, T; path r;
+ r = tensepath makepath p; n = length r;
+ t = round directiontime up of r;
+ T = round directiontime down of r;
+ if t>T: t := t-n; fi
+ makepen(subpath(T-n,t) of r shifted .5down
+ --subpath(t,T) of r shifted .5up -- cycle) enddef;
+\endtt
+The result of @makepath@ has control points equal to their adjacent
+vertices, so it could not be used with directiontime.
+\ (If any key point is equal to its precontrol or postcontrol,
+the ``^{velocity}'' of the path is zero at that point; directiontime
+assumes that all directions occur whenever the velocity drops to zero.) \
+Therefore we have used `^@tensepath@'.
+This almost works, once we realize that the values
+of $t$ and~$T$ sometimes need to be rounded to integers. But it
+fails for pens like @penspeck@ that have points very close together,
+since @tensepath@ is no better than an unadulterated @makepath@ in such cases.
+Furthermore, even if we could define a nice path from~$p$ (for example
+by scaling it up), we would run into problems of
+numerical instability, in cases like @penrazor@ where
+the pen polygon takes a $180^\circ$ turn. Razor-thin pens cannot be recognized
+easily, because they might have more than two vertices; for example,
+rotations of future pens such as
+`@makepen@($"left"\to"origin"\to"right"\to\cycle$)' are problematical.
+
+We can obtain a more robust result by using penoffset, because
+this operation makes use of the convexity of the polygon. The
+``fastest'' solution looks like this:
+\begintt
+vardef taller primary p =
+ save q, r, n, t, T; pen q; q = p;
+ path r; r = makepath q; n = length r;
+ t = round xpart(r intersectiontimes penoffset up of q);
+ T = round xpart(r intersectiontimes penoffset down of q);
+ if t>T: t := t-n; fi
+ makepen(subpath(T-n,t) of r shifted .5down
+ --subpath(t,T) of r shifted .5up -- cycle) enddef;
+\endtt
+^^{intersectiontimes} ^^{subpath}
+(The argument $p$ is copied into $q$, in case it's a ^{future pen};
+this means that the conversion of future pen to pen need be
+done only once instead of three times.)
+
+\subsection ^{Bernshte{\u\i}n} polynomials. And now, for our last trick,
+let's try to extend \MF's syntax so that it will accept generalized
+^{mediation} formulas of the form `$t[u_1,\ldots,u_n]$' for all $n\ge2$.
+\ (This notation was introduced for $n=3$ and~4 in Chapter~14, when we were
+considering fractional subpaths.) \ If $n>2$, the identity
+\begindisplay
+$t[\,u_1,\ldots,u_n]\;=\;t\bigl[t[u_1,\ldots,u_{n-1}],t[u_2,\ldots,u_n]\,\bigr]$
+\enddisplay
+defines $t[u_1,\ldots,u_n]$ recursively, and it can be shown that the
+alternative definition
+\begindisplay
+$t[\,u_1,\ldots,u_n]\;=\;t\bigl[t[u_1,u_2],\ldots,t[u_{n-1},u_n]\,\bigr]$
+\enddisplay
+gives the same result. \ (Indeed, we have
+\begindisplay
+$\displaystyle t[u_1,\ldots,u_n]\;=\;\sum_{k=1}^n{n-1\choose k-1}
+ (1-t)^{n-k}t^{k-1}u_k,$
+\enddisplay
+a Bernshte{\u\i}n polynomial of order $n-1$.)
+
+Our problem is to change the meaning of \MF's ^{brackets} so that
+expressions like `$1/2[a,b,c,d]$' will evaluate to `$.125a+.375b+.375c
++.125d$' in accordance with the formulas just given, but we don't want
+to mess up the other primitive uses of brackets in contexts like
+`|x[n]|' and `|path|~|p[][]a|'. We also want to be able to use
+brackets inside of brackets.
+
+The reader is challenged to try solving this problem before looking at
+the weird solution that follows. Perhaps there is a simpler way?
+\begintt
+def lbrack = hide(delimiters []) lookahead [ enddef;
+let [[[ = [; let ]]] = ]; let [ = lbrack;
+def lookahead(text t) =
+ hide(let [ = lbrack;
+ for u=t, hide(n_ := 0; let switch_ = first_): switch_ u; endfor)
+ if n_<3: [[[t]]] else: Bernshtein n_ fi enddef;
+def first_ primary u =
+ if numeric u: numeric u_[[[]]]; store_ u
+ elseif pair u: pair u_[[[]]]; store_ u fi;
+ let switch_ = store_ enddef;
+def store_ primary u = u_[[[incr n_]]] := u enddef;
+primarydef t Bernshtein nn =
+ begingroup for n=nn downto 2:
+ for k=1 upto n-1: u_[[[k]]]:=t[[[u_[[[k]]],u_[[[k+1]]] ]]];
+ endfor endfor u_[[[1]]] endgroup enddef;
+\endtt
+The most subtle thing about this code is the way it uses the `empty'
+option of a ^\<for list> to dispense with ^{empty text arguments}.
+Since \MF\ evaluates all the expressions of a ^@for@ loop before
+reading the loop text, and since `|n_|' and `|u_|' are used here
+only when no recursion is taking place, it is unnecessary to ^{save}
+their values even when brackets are nested inside of brackets.
+
+Of course this trick slows \MF\ down tremendously, whenever brackets
+appear, so it is just of academic interest. But it seems to work
+in all cases except with respect to formulas that involve `^|]]|'
+(two consecutive brackets); the latter token, which plain \MF\ expands
+to `|]|~|]|', is not expanded when |lookahead| reads its ^{text
+argument}, hence the user must remember to insert a space between
+consecutive brackets. \looseness=-1
+
+\endchapter
+
+Their tricks an' craft hae put me daft,
+They've taen me in, an' a' that.
+\author ROBERT ^{BURNS}, {\sl The Jolly Beggar\/} (1799) % air 7
+
+\bigskip
+
+Ebery house hab him dutty carner.
+\author ^{ANDERSON} and ^{CUNDALL}, {\sl Jamaica Proverbs and Sayings\/} (1927)
+ % #755 in 2nd edition; was not in the first (1910) edition
+
+\eject
+ \beginchapter Appendix E. Examples
+
+We've seen lots of examples of individual letters or parts of letters;
+let's concentrate now on the problem of getting things all together.
+The next two pages contain the entire contents of an example file
+`|logo.mf|', which generates the letters of the \MF\ ^{logo}. The file
+is short, because only seven letters are involved, and because those letters
+were intentionally done in a style that would be easy for the system they name.
+But the file is complete, and it illustrates in simplified form all
+the essential aspects of larger fonts: Ad~hoc dimensions are
+converted to pixels; subroutines are defined; programs for
+individual letters appear; intercharacter and interword
+spacing conventions are nailed down. Furthermore, the character programs
+are careful to draw letters that will be
+well adapted to the raster, even if pixels on the output device are
+not square. % It's all there, in one short program.
+
+We've been studying the `\MF' letters off and on since Chapter~4, making
+our examples slightly more complex as more of the language has been
+encountered. Finally we're ready to pull out all the stops and look at the
+real, professional-quality |logo.mf|, which incorporates all the best
+suggestions that have appeared in the text and in answers to the exercises.
+
+It's easy to generate a font with |logo.mf|, by proceeding as explained
+in Chapter~11. For example, the |logo10| font that produces `\MF' in
+10-point size can be created for a low-resolution printer by running
+\MF\ with the ^{command line}
+\begintt
+\mode=lowres; input logo10
+\endtt
+where the ^{parameter file} |logo10.mf| appears in that chapter. Furthermore
+the slanted version `{\manual 89:;<=>:}\kern3pt' can be created by
+inputting the parameter file |logosl10.mf|, which says simply
+\begintt
+% 10-point slanted METAFONT logo
+slant := 1/4;
+input logo10
+\endtt
+The ^"slant" parameter affects ^"currenttransform" as explained in
+Chapter~15.
+
+There isn't a great deal of ``^{meta-ness}'' in the |logo.mf| design,
+because only a few forms of the \MF\ logo are needed. However, some
+interesting variations are possible; for example, if we use the
+parameter files
+\begindisplay \def\qquad{\hskip4em\relax} \advance\belowdisplayskip by 3pt
+|font_size 30pt#;|&|font_size 10pt#;|\cr
+|ht#:=25pt#;|&|ht#:=6pt#;|\cr
+|xgap#:=1.5pt#;|&|xgap#:=2pt#;|\cr
+|u#:=3/9pt#;|&|u#:=4/3pt#;|\cr
+|s#:=1/3pt#;|&|s#:=-2/3pt#;|\cr
+|o#:=2/9pt#;|&|o#:=1/9pt#;|\cr
+|px#:=1pt#;|&|px#:=1/3pt#;|\cr
+|slant:=-1/9;|\cr
+\enddisplay
+we get \kern2pt{\manual BCDGHIJD} and \kern4pt{\manual KLUVWvwU},
+\kern4pt\ respectively.
+
+\goodbreak\begingroup\obeylines\everypar{\strut}\parindent=0pt
+|% Routines for the METAFONT logo, as found in The METAFONTbook|
+|% (logo10.mf is a typical parameter file)|
+\medskip
+|mode_setup;|
+|if unknown slant: slant:=0 else: currenttransform:=|
+| identity slanted slant yscaled aspect_ratio fi;|
+\medskip
+|ygap#:=(ht#/13.5u#)*xgap#; % vertical adjustment|
+|ho#:=o#; % horizontal overshoot|
+|leftstemloc#:=2.5u#+s#; % position of left stem|
+|barheight#:=.45ht#; % height of bar lines|
+|py#:=.9px#; % vertical pen thickness|
+\medskip
+|define_pixels(s,u);|
+|define_whole_pixels(xgap);|
+|define_whole_vertical_pixels(ygap);|
+|define_blacker_pixels(px,py);|
+|pickup pencircle xscaled px yscaled py;|
+|logo_pen:=savepen;|
+|define_good_x_pixels(leftstemloc);|
+|define_good_y_pixels(barheight);|
+|define_corrected_pixels(o);|
+|define_horizontal_corrected_pixels(ho);|
+\medskip
+|def beginlogochar(expr code, unit_width) =|
+| beginchar(code,unit_width*u#+2s#,ht#,0);|
+| pickup logo_pen enddef;|
+\medskip
+|def super_half(suffix i,j,k) =|
+| draw z.i{0,y.j-y.i}|
+| ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}|
+| ... z.j{x.k-x.i,0}|
+| ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}|
+| ... z.k{0,y.k-y.j} enddef;|
+\medskip
+|beginlogochar("M",18);|
+|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;|
+\medskip
+|beginlogochar("E",14);|
+|x1=x2=x3=leftstemloc;|
+|x4=x6=w-x1+ho; x5=x4-xgap;|
+|y1=y6; y2=y5; y3=y4;|
+|bot y1=0; top y3=h; y2=barheight;|
+\nointerlineskip
+\smash{\vbox{
+ \rightline{\figbox{Eb}{224\apspix}{216\apspix}\vbox} % F
+ \kern12pt
+ \rightline{\figbox{A18a}{240\apspix}{216\apspix}\vbox} % A
+ \kern12pt
+ \rightline{\figbox{Ea}{208\apspix}{216\apspix}\vbox} % T
+ \kern3.5pt}}
+\endgroup\eject\begingroup\obeylines\everypar{\strut}\parindent=0pt
+\smash{\vtop{\kern0pt\kern6pt
+ \rightline{\figbox{18a}{240\apspix}{216\apspix}\vbox} % O
+ \kern12pt
+ \rightline{\figbox{4c}{288\apspix}{216\apspix}\vbox} % M
+ \kern12pt
+ \rightline{\figbox{11a}{224\apspix}{216\apspix}\vbox} % E
+ \kern12pt
+ \rightline{\figbox{21a}{240\apspix}{216\apspix}\vbox} % N
+ }}\nointerlineskip
+|draw z6--z1--z3--z4; draw z2--z5;|
+|labels(1,2,3,4,5,6); endchar;|
+\medskip
+|beginlogochar("T",13);|
+|italcorr ht#*slant + .5u#;|
+|if .5w<>good.x .5w: change_width; fi|
+|lft x1=-eps; 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;|
+\medskip
+|beginlogochar("A",15);|
+|x1=.5w; x2=x4=leftstemloc; x3=x5=w-x2;|
+|top y1=h+o; y2=y3=barheight;|
+|bot y4=bot y5=-o;|
+|draw z4--z2--z3--z5; super_half(2,1,3);|
+|labels(1,2,3,4,5); endchar;|
+\medskip
+|beginlogochar("F",14);|
+|x1=x2=x3=leftstemloc;|
+|x4=w-x1+ho; 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;|
+\medskip
+|beginlogochar("O",15);|
+|x1=x4=.5w; top y1=h+o; bot y4=-o;|
+|x2=w-x3=good.x(1.5u+s); y2=y3=barheight;|
+|super_half(2,1,3); super_half(2,4,3);|
+|labels(1,2,3,4); endchar;|
+\medskip
+|beginlogochar("N",15);|
+|x1=x2=leftstemloc; x3=x4=x5=w-x1;|
+|bot y1=bot y4=-o;|
+|top y2=top y5=h+o; y3=y4+ygap;|
+|draw z1--z2--z3; draw z4--z5;|
+|labels(1,2,3,4,5); endchar;|
+\medskip
+|ligtable "T": "A" kern -.5u#;|
+|ligtable "F": "O" kern -u#;|
+\medskip
+|font_quad:=18u#+2s#;|
+|font_normal_space:=6u#+2s#;|
+|font_normal_stretch:=3u#;|
+|font_normal_shrink:=2u#;|
+|font_identifier:="MFLOGO" if slant<>0: & "SL" fi;|
+|font_coding_scheme:="AEFMNOT only";|
+\endgroup\goodbreak
+
+Everything in |logo.mf| has already been explained previously in this
+book except for the very last two lines, which define a `^@font\_identifier@'
+and a `^@font\_coding\_scheme@'. These are optional bits of information
+that are discussed in Appendix~F\null. Furthermore an ^{italic correction}
+has been specified for the letter `{\manual T}', since it's the final
+letter of `\kern-1.4pt{\manual 89:;<=>:\/}'.
+
+\medskip\ninepoint
+The program for a complete typeface will differ from the program for
+this simple logo font primarily in degree; there will be lots more
+parameters, lots more subroutines, lots more characters, lots more
+ligatures and kerns and whatnot. But there will probably also be
+more administrative machinery, designed to facilitate the creation,
+testing, and modification of characters, since a large enterprise
+requires good organization. The remainder of this appendix is
+devoted to an example of how this might be done: We shall discuss
+the additional kinds of routines that the author ^^{Knuth} found
+helpful while he was developing the ^{Computer Modern} family
+of typefaces.
+
+The complete, unexpurgated programs for Computer Modern appear in {\sl
+Computers \& Typesetting}, Volume~E\null; but since they have evolved
+over a long period of time, they are rather complex. We shall simplify
+the details so that it will be easier to grasp the important issues
+without being distracted by irrelevant technicalities.
+
+The simple logo fonts discussed above are generated by two types
+of files: There are parameter files like |logo10.mf|, and there is
+a program file |logo.mf|. The Computer Modern fonts, being more
+extensive, are generated by four types of files: There are
+{\sl^{parameter files}\/} like `|cmr10.mf|', which specify the
+ad hoc dimensions for particular sizes and styles of type; there are
+{\sl^{driver files}\/} like `|roman.mf|', which serve as chief
+executives of the font-generation process; there are
+{\sl^{program files}\/} like `|punct.mf|', which contain programs
+for individual characters; and there's a {\sl^{base file}\/} called
+`|cmbase.mf|', which contains the subroutines and other macros used
+throughout the system.
+
+Our logo example could have been cast in this more general mold by moving
+the character programs into a program file `|METAFON.mf|', and by moving
+most of the opening material into a base file `|logobase.mf|'
+that looks like this:
+\beginlines
+|% Base file for the METAFONT logo|
+|logobase:=1; % when logobase is known, this file has been input|
+\smallskip
+|def font_setup =|
+| if unknown slant: slant:=0 else: currenttransform:=|
+%| identity slanted slant yscaled aspect_ratio fi;|
+\qquad\smash{\vdots}\qquad\vbox to10pt{}%
+ \raise1pt\hbox{(the previous code is unchanged)}
+| define_corrected_pixels(o);|
+| define_horizontal_corrected_pixels(ho); enddef;|
+\endlines
+followed by the definitions of |beginlogochar| and |super_half|.
+Then we're left with a driver file |logo.mf| that looks like this:
+\beginlines
+|% Driver file for the METAFONT logo|
+|if unknown logobase: input logobase fi|
+\smallskip
+|mode_setup; font_setup; % establish pixel-oriented units|
+|input METAFON % generate the characters|
+\smallskip
+|ligtable "T": "A" kern -.5u#;|
+\weakendlines
+and so on, concluding as before.
+
+In general, a parameter file calls on a driver file, which calls on
+one or more program files; the base file contains predefined macros
+shared by all. There may be several driver files, each using a
+different combination of program files; for example, Computer Modern
+has `|roman.mf|' and `|italic.mf|', % a little white lie, multiplied below
+both of which call on |punct.mf| to generate punctuation marks,
+although they use different program files to generate the lowercase
+alphabets. Characters are partitioned into program files so that
+they can be shared by different drivers.
+
+Parameter files in Computer Modern don't quite follow the conventions
+of\/ |logo10.mf|. Here, for example, are the
+opening and closing lines of ^|cmr10.mf|:
+\beginlines
+|% Computer Modern Roman 10 point|
+|if unknown cmbase: input cmbase fi|
+\smallskip
+|font_identifier "CMR"; font_size 10pt#;|
+\smallskip
+|u#:=20/36pt#; % unit width|
+|serif_fit:=0pt#; % extra sidebar near serifs|
+|letter_fit:=0pt#; % extra space added to all sidebars|
+\vskip-3pt
+\qquad\vdots
+|serifs:=true; % should serifs and bulbs be attached?|
+|monospace:=false; % should all characters have the same width?|
+\smallskip
+|generate roman % switch to the driver file|
+\endlines
+The main differences are: \ (1) There's special code at the beginning, to
+make sure that |cmbase.mf| has been loaded. The base file includes
+several things that are needed right away; for example, |cmbase| declares
+the variables `"serifs"' and `^"monospace"' to be of type @boolean@,
+so that boolean-valued parameter assignments like `$"serifs":=@true@$'
+will be legal. \ (2)~The @font\_identifier@ is defined in the parameter file,
+not in the driver file. \ (3)~The last line says `^@generate@' instead of
+`@input@'; the base file defines @generate@ to be the same as @input@,
+but other meanings are assigned by utility routines that we'll study later.
+\ (4)~The final `^@end@' is no longer present in the parameter file.
+
+The |roman.mf| driver looks like this (vastly simplified):
+^^@font\_slant@ ^^@font\_quad@ ^^@font\_normal\_space@
+^^@font\_normal\_stretch@ ^^@font\_normal\_shrink@
+\beginlines
+|% The Computer Modern Roman family of fonts|
+\smallskip
+|mode_setup; font_setup;|
+\smallskip
+|input romanu; % upper case (majuscules)|
+|input romanl; % lower case (minuscules)|
+|input romand; % numerals|
+|input punct; % punctuation marks|
+\smallskip
+|font_slant slant;|
+|if monospace: font_quad 18u#;|
+| font_normal_space 9u#; % no stretching or shrinking|
+|else: font_quad 18u#+4letter_fit#;|
+| font_normal_space 6u#+2letter_fit#; % interword spacing|
+| font_normal_stretch 3u#; % with ``glue''|
+| font_normal_shrink 2u#;|
+| input romlig; % f ligatures|
+| |^|ligtable|| "f": "i" =: oct"014", "f" =: oct"013", "l" =: oct"015",|
+| "'" kern u#, "?" kern u#, "!" kern u#;|
+| ligtable oct"013": "i" =: oct"016", "l" =: oct"017", % ffi and ffl|
+| "'" kern u#, "?" kern u#, "!" kern u#;|
+| ligtable "-": "-" =: oct"173"; % en dash|
+| ligtable oct"173": "-" =: oct"174"; % em dash|
+| ligtable "`": "`" =: oct"134"; % open quotes|
+| ligtable "'": "'" =: oct"042", % close quotes|
+| "?" kern 2u#, "!" kern 2u#;|
+|fi; |^|bye.|
+\endlines
+In a ^{monospaced} font like ^|cmtt10|, all characters will be exactly
+$9u\0$ wide. Both |cmr10| and~|cmtt10| use the |roman| driver, but
+|roman| omits the ligatures and changes the interword spacing
+when it is producing monospaced fonts.
+
+The program files of Computer Modern have slightly different conventions
+from those of plain \MF\!\null. Here, for example, are the ^^{.} ^^{em dash}
+programs for two of the simplest ^{punctuation marks}:
+\beginlines
+|cmchar "Period";|
+|numeric dot_diam#; dot_diam# = if monospace: 5/4 fi dot_size#;|
+|define_whole_blacker_pixels(dot_diam);|
+|beginchar(".",5u#,dot_diam#,0);|
+|adjust_fit(0,0); pickup fine.nib;|
+|pos1(dot_diam,0); pos2(dot_diam,90);|
+|x1l=good.x(x1l+.5w-x1); bot y2l=0; z1=z2; dot(1,2); % dot|
+|penlabels(1,2); endchar;|
+\medskip
+\leftline{\hskip3pc\figbox{Ec\&Ed}{3in}{360\apspix}\vbox}
+\smallskip
+|iff not monospace: cmchar "Em dash";|
+|beginchar(oct"174",18u#,x_height#,0);|
+|italcorr .61803x_height#*slant + .5u#;|
+|adjust_fit(letter_fit#,letter_fit#);|
+|pickup crisp.nib; pos1(vair,90); pos2(vair,90);|
+|y1r=y2r=good.y(y1r+.61803h-y1); lft x1=-eps; rt x2=w+eps;|
+|filldraw stroke z1e--z2e; % crossbar|
+|penlabels(1,2); endchar;|
+\endlines
+The new structural features in these programs are: (1)~`^@cmchar@',
+which appears at the very beginning of each character program;
+(2)~`^@iff@~\<boolean expression>:', which precedes @cmchar@ if
+the character is to be generated only when the boolean expression
+is true; (3)~`^@adjust\_fit@', which can change the amount of white space
+at the character's left and/or right; (4)~pens called `"fine.nib"' and
+`"crisp.nib"'; (5)~new macros `"pos"', `"dot"', and `"stroke"',
+discussed further below.
+
+The base file |cmbase.mf| begins as follows:
+\beginlines
+|% The base file for Computer Modern (a supplement to plain.mf)|
+\smallskip
+|cmbase:=1; % when cmbase is known, this file has been input|
+\smallskip
+|let cmchar = relax; % `cmchar' should precede each character|
+|let generate = input; % `generate' should follow the parameters|
+\smallskip
+|newinternal slant, superness,| $\cdots$ | % purely numeric parameters|
+|boolean serifs, monospace,| $\cdots$ | % boolean parameters|
+\endlines
+These few lines are straightforward enough. Although |cmchar| is defined
+to be the same as ^|relax|, which does nothing, the definition of
+|cmchar| will be changed by certain utility programs below; this will
+prove to be a convenience when characters are designed, tested, and maintained.
+
+The next few lines of |cmbase| are trickier. They implement the `@iff@\kern1pt'
+feature, which bypasses unwanted characters at high speed.
+\beginlines
+|let semi_ = ;; let colon_ = :; let endchar_ = endchar;|
+|def iff expr b =|
+| if b: let next_ = use_it else: let next_ = lose_it fi;|
+| next_ enddef;|
+|def use_it = let : = restore_colon; enddef;|
+|def restore_colon = let : = colon_; enddef;|
+|def lose_it = let endchar = fi; inner cmchar; let ; = fix_ semi_|
+| if false enddef;|
+|def fix_ = let ; = semi_; let endchar = endchar_; outer cmchar; enddef;|
+|def always_iff = let : = endgroup; killboolean enddef;|
+|def killboolean text t = use_it enddef;|
+|outer cmchar;|
+\weakendlines
+^^@always\_if@ ^^@inner@ ^^@outer@
+(The |lose_it| routine assumes that every character program will end
+with `|endchar;|'.)
+
+The most interesting part of |cmbase| is probably the way it allows the
+``^{side\-bearings}'' of each character to be fine-tuned. The amount of
+space at the left and right edges of the character's ``^{bounding box}''
+can be adjusted without actually shifting the picture, and without
+changing the width that was specified in @beginchar@. Here's how it works:
+After a @beginchar@ command and an optional @italcorr@, each Computer
+Modern character program is supposed to say
+\begindisplay
+@adjust\_fit@(\<left sidebearing adjustment>,\thinspace
+ \<right sidebearing adjustment>);
+\enddisplay
+sidebearing adjustments are given in true, ``sharped'' units.
+The ^@adjust\_fit@ routine essentially adds extra space at the left
+and right, corresponding to the sidebearing adjustments. An ad-hoc
+dimension called ``^"letter\_fit"$\0$'' is also added to all sidebearings,
+behind the scenes.
+
+Our example program for the |"."|\ says simply `@adjust\_fit@$(0,0)$';
+this means that only "letter\_fit" is added. The program for em-dash
+says `@adjust\_fit@$("letter\_fit"\0,\allowbreak"letter\_fit"\0)$', hence
+the sidebearings are increased by 2"letter\_fit" at each side.
+The total character width of the em-dash comes to $18u\0+
+4"letter\_fit"\0$ (which is indeed one em, the value of ^@font\_quad@
+specified in the |roman| driver file).
+
+The program for lowercase `^{b}' in file |romanl.mf| says
+`@adjust\_fit@$("serif\_fit"\0,0)$'; this adds the ^"serif\_fit"
+parameter at the left, to compensate for the possible appearance
+of a serif at the left of this character. The "serif\_fit" is
+zero in |cmr10|, but it has a negative value in a ^{sans-serif} font,
+and a positive value when serifs are extralong.
+
+The nice thing about @adjust\_fit@ is that it's an ``add-on''
+specification that doesn't affect the rest of the character design.
+The program can still be written as if 0~were the left edge and
+$w$~were the right edge; afterwards the fit can be adjusted without
+changing the program or the shapes.
+
+There are two versions of @adjust\_fit@, one for normal fonts
+and one for ^{mono\-space} fonts. Both of them are slightly complicated
+by something called ^"shrink\_fit", which will be explained later;
+for the moment, let's just imagine that $"shrink\_fit"=0$. Here is the
+routine for the normal case:
+\beginlines
+|def normal_adjust_fit(expr left_adjustment,right_adjustment) =|
+| l := -hround(left_adjustment*hppp)-letter_fit;|
+| interim xoffset := -l;|
+| charwd := charwd+2letter_fit#+left_adjustment+right_adjustment;|
+| r := l+hround(charwd*hppp)-shrink_fit;|
+| w := r-hround(right_adjustment*hppp)-letter_fit;|
+| enddef;|
+\endlines
+Variables ^"l" and ^"r" are set to the actual pixel boundaries of the
+character; thus, plain \MF's bounding box has $0\le x\le w$, but
+Computer Modern's has $l\le x\le r$. ^{Rounding} has been done
+very carefully so that the sidebearings will have consistent
+relationships across an entire font. Notice that ^"w"~has been
+recalculated; this means that @adjust\_fit@ can affect the digitization,
+but---we hope---in a beneficial way.
+
+In a monospaced font, the @adjust\_fit@ routine changes the
+unit-width parameter, ^"u", so that the total width after adjustment
+comes out to be constant. Similar adjustments are made to parameters
+like ^"jut", the nominal serif length. The width of all characters
+in a monospaced font will be $"mono\_charwd"\0$ in true units,
+^"mono\_charwd" in pixels. The italic correction of all
+characters will be $"mono\_charic"\0$.
+\beginlines
+|def mono_adjust_fit(expr left_adjustment,right_adjustment) =|
+| numeric expansion_factor; mono_charwd# = 2letter_fit#|
+| + expansion_factor*(charwd+left_adjustment+right_adjustment);|
+| forsuffixes $=u,jut,| $\cdots$ |:|
+| $ := $.#*expansion_factor*hppp; endfor|
+| l := -hround(left_adjustment*expansion_factor*hppp)-letter_fit;|
+| interim xoffset := -l;|
+| r := l+mono_charwd-shrink_fit;|
+| w := r-hround(right_adjustment*expansion_factor*hppp)-letter_fit;|
+| charwd := mono_charwd#; charic := mono_charic#;|
+| enddef;|
+\weakendlines
+It took the author ^^{Knuth} umpteen trials to get this routine right.
+
+The ^"xoffset" calculations in @adjust\_fit@ are enough to shift the
+character by the proper amount when it's being ^{shipped out}. We just
+have to take care of getting the correct character width in pixels,
+and |cmbase| does this by setting
+^^"extra\_endchar"
+\beginlines
+|extra_endchar := extra_endchar&"r:=r+shrink_fit;w:=r-l;";|
+\endlines
+
+No other changes to plain \MF's ^@endchar@ routine are needed; but we do
+need to redefine ^|makebox| and ^|maketicks|, in order to show the
+adjusted bounding box. It's convenient to change |makebox| so that it also
+slants the box, in a slanted font, and so that it draws vertical lines
+one unit apart as aids to the designer; several more horizontal lines
+are also drawn:
+\beginlines
+|def makebox(text rule) =|
+| for y=0,asc_height,body_height,x_height,bar_height,|
+| -desc_depth,-body_depth: rule((l,y)t_,(r,y)t_); endfor % horizontals|
+| for x=l,r: rule((x,-body_depth)t_,(x,body_height)t_); endfor % verticals|
+| for x=u*(1+floor(l/u)) step u until r-1:|
+| rule((x,-body_depth)t_,(x,body_height)t_); endfor % more verticals|
+| if charic<>0:|
+| rule((r+charic*pt,h.o_),(r+charic*pt,.5h.o_)); fi % italic correction|
+| enddef;|
+\smallskip
+|def maketicks(text rule) =|
+| for y=0,h.o_,-d.o_:|
+| rule((l,y),(l+10,y)); rule((r-10,y),(r,y)); endfor % horizontals|
+| for x=l,r: rule((x,10-d.o_),(x,-d.o_));|
+| rule((x,h.o_-10),(x,h.o_)); endfor % verticals|
+| if charic<>0:|
+| rule((r+charic*pt,h.o_-10),(r+charic*pt,h.o_)); fi % italic correction|
+| enddef;|
+\weakendlines
+(Examples of the new |makebox| routine appear in the illustrations for
+period and em-dash earlier in this appendix, and also in Chapter~23.)
+
+\smallskip
+Plain \MF's ^@change\_width@ routine must also be generalized:
+\beginlines
+|def change_width = if not monospace: % change width by +1 or -1|
+| if r+shrink_fit-l = floor(charwd*hppp): w := w+1; r := r+1;|
+| else: w := w-1; r := r-1; fi fi enddef;|
+\endlines
+
+The Computer Modern ^@font\_setup@ routine is invoked at the beginning of
+each driver file. This is what converts sharped units to pixels;
+@font\_setup@ also computes additional quantities that are important to the
+font as a whole. It's a long macro, but here are its important features:
+\beginlines
+|def font_setup =|
+| define_pixels(u,jut,| $\cdots$ |);|
+| define_whole_pixels(letter_fit,fine,crisp,| $\cdots$ |);|
+| define_whole_vertical_pixels(body_height,cap_height,| $\cdots$ |);|
+| define_whole_blacker_pixels(hair,stem,curve,| $\cdots$ |);|
+| define_whole_vertical_blacker_pixels(vair,slab,| $\cdots$ |);|
+| define_corrected_pixels(o,| $\cdots$ |);|
+\smallbreak
+| if monospace: mono_charwd# := 9u#; define_whole_pixels(mono_charwd);|
+| mono_charic# := max(0,body_height#*slant);|
+| let adjust_fit = mono_adjust_fit;|
+| else: let adjust_fit = normal_adjust_fit; fi|
+| lowres_fix(stem,curve) 1.2;|
+^^@lowres\_fix@ \smallbreak
+| |\<Initialize pen nibs, see below>
+\smallbreak
+| |^|currenttransform||:=identity slanted slant|
+| yscaled aspect_ratio scaled |^|granularity||;|
+| shrink_fit := 1+hround(2letter_fit#*hppp)-2letter_fit;|
+| if not string mode: if mode <= smoke: shrink_fit := 0; fi fi|
+| enddef;|
+\endlines
+If $"letter\_fit"\0=0$, the `^"shrink\_fit"' is set to~1; otherwise
+"shrink\_fit" is 0, 1, or~2, depending on how "letter\_fit" has
+rounded to an integer. This amount is essentially subtracted from~^"w"
+before each character in the font has been drawn. Experience shows that
+this trick greatly improves the readability of fonts at ^{medium}
+and ^{low resolutions}.
+
+Many of the Computer Modern characters are drawn with ^@filldraw@, which
+is a mixture of outline-filling and fixed-pen drawing. Several macros
+are included in |cmbase| to facilitate filldrawing, especially
+`^"pos"' and `^"stroke"':
+\beginlines
+|vardef pos@#(expr b,d) =|
+| (x@#r-x@#l,y@#r-y@#l)=(b-currentbreadth,0) rotated d;|
+| x@#=.5(x@#l+x@#r); y@#=.5(y@#l+y@#r) enddef;|
+\smallbreak
+|vardef stroke text t =|
+| forsuffixes e=l,r: path_.e:=t; endfor|
+| path_.l -- reverse path_.r -- cycle enddef;|
+\endlines
+Thus "pos" is like ^"penpos", except that it subtracts ^"currentbreadth"
+from the overall breadth. \ (Cf.~the program for left parentheses in
+Chapter~12.) \ The "stroke" routine is a simplified alternative to
+@penstroke@, such that @penstroke@ is equivalent to `@fill@~"stroke"'
+if the specified path isn't a cycle.
+
+The value of "currentbreadth" is maintained by redefining plain \MF's
+`^"numeric\_pickup\_"' macro so that it includes the new line
+\beginlines
+| if known breadth_[q]: currentbreadth:=breadth_[q]; fi|
+\endlines
+The ^@clear\_pen\_memory@ macro is redefined so that its second line now says
+\beginlines
+| numeric pen_lft_[],pen_rt_[],pen_top_[],pen_bot_[],breadth_[];|
+\endlines
+relevant entries of the "breadth\_" array will be defined by @font\_setup@,
+as we'll see soon.
+
+The example programs for period and em-dash say `@pickup@ "fine.nib"' and
+`@pickup@ "crisp.nib"'. These nibs are initialized by @font\_setup@ in
+the following way:
+\beginlines
+| clear_pen_memory;|
+| forsuffixes $ = fine,crisp,| $\cdots$ |:|
+| $.breadth := $;|
+| pickup if $=0: nullpen else: pencircle scaled $; $ := $-eps fi;|
+| $.nib := |^|savepen||; breadth_[$.nib] := $;|
+| forsuffixes $$ = lft,rt,top,bot: shiftdef($.$$,$$ 0); endfor endfor|
+\weakendlines
+If, for example, we have $"fine"=4$, this code sets $"fine.breadth":=4$,
+$"fine.nib":=1$, $"fine":=4-"eps"$, and $"breadth\_"[1]:=4-"eps"$.
+\ (A small amount~^"eps" has been subtracted so that "pos" will
+usually find $b-"currentbreadth">0$.) \ Furthermore, four subroutines
+^"fine.lft", "fine.rt", "fine.top", and "fine.bot" are defined, so
+that it's easy to refer to the edges of "fine.nib" when it has not been
+picked up. These four subroutines are created by a slightly
+tricky ^|shiftdef| macro:
+\beginlines
+|def shiftdef(suffix $)(expr delta) =|
+| vardef $ primary x = x+delta enddef enddef;|
+\endlines
+
+OK, we've just about covered everything in |cmbase| that handles the
+extra administrative complexity inherent in a large-scale design.
+The rest of the base file simply contains subroutines like
+^"serif" and ^"dot", for recurring features of the characters themselves.
+Such subroutines needn't be shown here.
+
+To make a binary file called ^|cm.base|, there's a trivial file `|cm.mf|':
+\beginlines
+|% This file creates `cm.base', assuming that plain.base is preloaded|
+|input cmbase; |^|dump.|
+\endlines
+
+\medbreak
+Besides parameter files, driver files, program files, and the base file,
+the Computer Modern routines also include a number of {\sl^{utility files}\/}
+that provide a convenient environment for designing new characters and
+improving old ones. We'll conclude this appendix by studying the contents
+of those utility files.
+
+Let's suppose, for example, that test proofs have revealed problems
+with the characters `k' and `S', so we want to fix them. Instead of
+working with the font as a whole, we can copy the programs for those two
+characters (and only those two) into a temporary file called `^|test.mf|'.
+Then we can run \MF\ on the file `^|rtest.mf|', which says the following:
+\beginlines
+|% try all characters on `test.mf' using the parameters of cmr10|
+|if unknown cmbase: input cmbase fi|
+|mode_setup;|
+\smallskip
+|def generate suffix t = enddef;|
+|input cmr10; font_setup;|
+\smallbreak
+|let echar = endchar;|
+|def endchar = echar; stop "done with char "&decimal charcode&". " enddef;|
+|let iff = always_iff;|
+\smallskip
+|input test; bye|
+\endlines
+This will produce proofs of `k' and `S', using the |cmr10| parameters.
+Notice the simple trick by which |rtest| is able to stay in charge
+after inputting |cmr10|, without letting the |roman| driver come into
+action: `|generate|' is redefined so that it becomes innocuous.
+Furthermore |rtest| changes ^|endchar| so that \MF\ will ^{stop} and
+display each character before moving~on to the next. The `^|iff|'
+convention is changed to `|always_iff|', so that every test character will
+^^@always\_iff@ be tested even if the boolean expression is undefined;
+this makes it easier to copy from program files
+into the test file and back again, since the |iff| indications do not
+have to be touched.
+
+If you invoke \MF\ with `|\mode=lowres;| |input| |rtest|', you'll generate
+a low-resolution font called |rtest| with the parameters of |cmr10|,
+but containing only the characters in the test file. If you leave out
+the mode, you get proof mode as usual.
+
+There are similar pseudo-drivers |ttest.mf| (for |cmtt10| instead of |cmr10|),
+|btest.mf| (for |cmbx10|), etc.; these make it possible to try the
+test characters with many different parameter settings. There's also
+|ztest.mf|, which inputs parameters from a temporary file `|z.mf|' that
+contains special parameters of interest at the moment. \ (If file
+|z.mf| does not exist, you'll get a chance to specify another
+parameter file, online.) \looseness=-1
+
+A more elaborate ^{pseudo-driver file} called `|6test.mf|' allows you
+to test up to six parameter settings simultaneously, and to see the
+results all at once on your screen, as illustrated in Chapter~23.
+Here is the program that does the necessary magic:
+\beginlines
+|% try all characters on `test.mf' using six different sets of parameters|
+|if unknown cmbase: input cmbase fi|
+|mag=.5; % the user can override this equation|
+|mode_setup; let mode_setup=\;|
+\smallskip
+|boolean running;|
+|def abort = hide(scrollmode; running := false) enddef;|
+|def pause = stop "done with char "&decimal charcode&". " enddef;|
+|let iff = always_iff;|
+|def ligtable text t=enddef;|
+|def charlist text t=enddef;|
+|def extensible text t=enddef;|
+\smallbreak
+|string currenttitle;|
+|let semi = ;; let echar = endchar; let endchar = enddef;|
+|def cmchar expr s = currenttitle := s;|
+| let ; = testchar semi quote def chartext = enddef;|
+|def testchar = semi let ; = semi;|
+| running := true; errorstopmode;|
+| for k=1 upto 6:|
+| if running: if known params[k]: scantokens params[k]; font_setup;|
+| currentwindow:=k;|
+| currenttitle & ", " & fontname[k];|
+| chartext echar; fi fi endfor|
+| pause; enddef;|
+\smallbreak
+|string params[],fontname[];|
+|params[1] = "roman_params"; fontname[1] = "cmr10";|
+|params[2] = "sans_params"; fontname[2] = "cmssbx10";|
+|params[3] = "ital_params"; fontname[3] = "cmti10";|
+|params[4] = "tt_params"; fontname[4] = "cmtt10";|
+|params[5] = "bold_params"; fontname[5] = "cmb10";|
+|params[6] = "quote_params"; fontname[6] = "cmssqi8";|
+\smallbreak
+|w_rows = floor 1/2 screen_rows; w_cols = floor 1/3 screen_cols;|
+|def open(expr k,i,j)=|
+| openwindow k from ((i-1)*w_rows,(j-1)*w_cols) to (i*w_rows,j*w_cols)|
+| at (-10,140) enddef;|
+|def openit =|
+| open(1,1,1); open(2,1,2); open(3,1,3);|
+| open(4,2,1); open(5,2,2); open(6,2,3); enddef;|
+\smallbreak
+|begingroup delimiters begintext generate;|
+| def makedef(expr s)(text t) =|
+| expandafter def scantokens s = t enddef; flushtext enddef;|
+| def flushtext suffix t = enddef;|
+| for k=1 upto 6: if known params[k]:|
+| makedef(params[k])|
+| expandafter expandafter expandafter begintext|
+| scantokens ("input "&fontname[k]); fi endfor|
+|endgroup;|
+\smallskip
+|input test; bye|
+\endlines
+^^@errorstopmode@ ^^@scrollmode@ ^^@quote@ ^^@openwindow@ ^^@openit@
+^^"currentwindow" ^^@expandafter@ ^^@scantokens@
+Parameters are moved from parameter files into macros, using a trick
+discussed near the beginning of Appendix~D\null. Then ^@cmchar@ is redefined
+so that the entire text of each character-to-be-tested will be embedded
+in another macro called "chartext". Each instance of "chartext" is
+repeatedly applied to each of the six font setups.
+
+An error that occurs with the first or second set of parameters may be
+so bad that you won't want to see what happens with the third, fourth,
+fifth, and sixth sets. For example, when |test.mf| contains characters
+that are being newly designed, some equations might have been omitted
+or mistyped, so the results will be ludicrous. In this case you can
+^{interrupt} the program and type `|I|~^|abort|'. The |6test| routine
+has an |abort| macro that will stop at the end of the current font setup
+and move directly to the next character, without trying any of the
+remaining parameter combinations.
+
+It's possible to include material in |test.mf| that isn't part of
+a character program. For example, you might want to redefine a subroutine
+in the base file. Only the character programs themselves (i.e., the
+sequences of tokens between `@cmchar@' and `@endchar@;') are subject to
+six-fold repetition.
+
+Some large characters may not appear in full, because there might not be
+room for them on the screen at the stated magnification. You can make
+everything smaller by running \MF\ with, say, `|\mag=1/3; input 6test|'.
+The computer will stop with an error message, saying that the equation
+`|mag=.5|' is ^{inconsistent}; but you can safely proceed, because you
+will have the magnification you want.
+
+\endchapter
+
+An ensampull yn doyng ys more commendabull
+\indent{\cmman\char'15}en ys techyng o{\cmman\char'15}er prechyng.
+\author JOHN ^{MIRK}, {\sl The Festyuall\/} (c.\thinspace1400)
+ % from MS page 123b; p216 in Erbe's transcription
+
+\bigskip
+
+Old people love to give good advice,
+% Les vieillards aiment \`a donner de bons pr\'eceptes,
+to console themselves for no longer being able to give bad examples.
+% pour se consoler de n'\^etre plus en \'etat de donner de mauvais exemples.
+\author ^{LA ROCHEFOUCAULD}, {\sl Maximes\/} (1665)
+
+\eject
+ \beginchapter Appendix F. Font Metric\\Information
+
+The \TeX\ typesetting system assumes that some ``intelligence'' has been
+built into the fonts it uses. In other words, information stored with
+^^{TeX} \TeX's fonts will have important effects on \TeX's behavior. This
+has two consequences: (a)~Typesetting is quite flexible, since few
+conventions are frozen into \TeX\ itself. (b)~Font designers must work
+a little harder, since they have to tell \TeX\ what to do. The purpose
+of this appendix is to explain how you, as a font designer, can cope
+with~(b) in order to achieve spectacular successes with~(a).
+
+The information used by \TeX\ is embedded in compact binary files called
+\TeX\ Font Metric (^|tfm|) files. Although the `|t|' in `|tfm|' stands
+for \TeX, this is an artifact of history, because other formatting systems
+can work with |tfm| files too. The files should have been called just `|fm|',
+but it's too late now.
+
+\MF\ is able to produce two different kinds of binary output files.
+One, a `|gf|' file, contains digitized character shapes and some additional
+information needed by programs that drive printing devices; such files
+are discussed in Appendix~G\null. The other type of output is a |tfm| file,
+which contains font information used by formatting routines like \TeX;
+such files are our present concern. You get a |tfm| file if and only
+if \MF's internal quantity `^"fontmaking"' is positive at the end
+of your job. \ (Plain \MF's @mode\_setup@ routine usually sets
+"fontmaking" to an appropriate value automatically.)
+
+\medskip\ninepoint
+The |tfm| file contains some information about each character, some
+information about combinations of characters, and some information
+about the font as a whole. We shall consider these three kinds
+of information in turn. All of the font metric data that refers to
+physical dimensions should be expressed in device-independent,
+``^{sharp}'' units; when a particular font is produced with different
+modes or magnifications, all its |tfm| files should be identical.
+
+A formatting program like \TeX\ needs to know the size of each character's
+``^{bounding} ^{box}.'' For example, when \TeX\ typesets a word like
+`box', it places the first letter `b' into a little box in such a way that
+the \MF\ pixel whose lower left corner is at $(0,0)$ will appear
+on the baseline of the current line being typeset, at the left edge
+of the box. \ (We assume for simplicity that ^"xoffset" and ^"yoffset"
+were zero when `b' was shipped out.) \ The second letter,~`o', is placed
+in a second little box adjacent to the first one, so we obviously must tell
+\TeX\ how wide to make the `b'.
+
+In fact, \TeX\ also wants to know the height and depth of each letter.
+This affects the placing of ^{accents}, if you wish to typeset
+`\d{\~b}\kern.28pt\d{\~o}\kern-.28pt\d{\~x}\d{\~y}', and it also
+avoids overlap when adjacent lines contain boxes that go unusually
+far above or below the baselines.
+
+A total of four dimensions is given for each character, in sharp
+units (i.e., in units of printer's points):
+
+\smallskip
+\item\bull ^"charwd", the width of the bounding box.
+\item\bull ^"charht", the height (above the baseline) of the bounding box.
+\item\bull ^"chardp", the depth (below the baseline) of the bounding box.
+This is a {\sl positive\/} number if the character descends below the
+baseline, even though the corresponding $y$ values are negative.
+\item\bull ^"charic", the character's ``^{italic correction}.'' \TeX\
+adds this amount to the width of the box (at the right-hand side)
+in two cases: (a)~When the user specifies an italic correction explicitly,
+by typing |\/| immediately after the character. (b)~When an ^{isolated}
+character is used in math mode, unless it has a subscript but no
+superscript. For example, the italic correction is applied to `$P$' in
+the formulas `$P(x)$' and `$P^2$', but not in the formula `$P_n$';
+it is applied to position the superscript but not the subscript
+in `$P_n^2$'.
+\smallskip\noindent
+In plain \MF\ programs, you specify "charwd", "charht", and "chardp"
+in a ^@beginchar@ command, and you specify "charic" (if it's positive)
+in an ^@italcorr@ command. But @beginchar@ and @italcorr@ are macros,
+not primitives of \MF\!\null. What really happens is that \MF\ records the
+value of its internal quantities "charwd", "charht", "chardp", and "charic"
+at the time of a ^@shipout@ command. These values (and all other
+dimensions to be mentioned below) must be less than $2048"pt"\0$ in
+absolute value.
+
+A font contains at most 256 character codes; the ^{charexists} operator
+can be used to tell which codes have already appeared. If two or more
+characters are shipped out with the same code number (possibly with
+different ^"charext" values), the "charwd",
+"charht", "chardp", and "charic" of the final one are assumed to
+apply to them all.
+
+At most 15 different nonzero heights, 15 different nonzero depths,
+and 63 different nonzero italic corrections may appear in a single
+font. If these limits are exceeded, \MF\ will change one or more
+values, by as little as possible, until the restriction holds.
+A warning message is issued if such changes are necessary; for example,
+^^|some char values|
+`|(some| |charht| |values| |had| |to| |be| |adjusted| |by| |as| |much|
+|as| |0.12pt)|' means that~you had too many different nonzero heights, but
+\MF\ found a way to reduce the number to at most~15 by changing some of
+them; none of them had to be changed by more than 0.12 points. No warning
+is actually given unless the maximum amount of perturbation exceeds
+${1\over16}\pt$.
+
+\medbreak
+The next kind of information that \TeX\ wants is concerned with
+pairs of adjacent characters that are typeset from the same font.
+For example, \TeX\ moves the~`x' slightly closer to the~`o' in the
+word `box', and it moves the~`o' slightly away from the~`b', because
+of information stored in the |tfm| file for the font you're now reading.
+This space adjustment is called {\sl^{kerning}}. Otherwise (if the
+three characters had simply been placed next to each other according
+to their "charwd" values) the word would have been `b{}o{}x', which
+looks slightly worse. Similarly, there's a difference between
+`difference' and `dif{\null}ference', because the |tfm| file tells \TeX\
+to substitute the ligature `ff' when there are two f's in a row.
+
+Ligature information and kerning information is specified in short
+``^{ligtable programs}'' of a particularly simple form. Here's an example
+that illustrates most of the features (although it is not a serious
+example of typographic practice):
+\beginlines
+^|ligtable|| "f": "f" =: oct"013", "i" |\||=: oct"020", skipto 1;|
+|ligtable "o": "b": "p": "e" kern .5u#, "o" kern .5u#, "x" kern-.5u#,|
+| 1:: "!" kern u#;|
+\endlines
+This sequence of instructions can be paraphrased as follows:
+\smallskip
+\hangindent 3pc
+Dear \TeX, when you're typesetting an~`f' with this font, and when the
+following character also belongs to this font, look at it closely because
+you might need to do something special: If that following character is
+another~`f', replace the two f's by character code |oct"013"|
+[namely `\char'13'\kern.5pt];
+if it's an `i', retain the `f' but replace the `i' by character code
+|oct"020"| [a dotless `\char'20'\kern.5pt];
+otherwise skip down to label `|1::|' for further instructions.
+When you're typesetting an `o' or~`b' or~`p', if the next input to \TeX\ is
+`e' or~`o', add a half unit
+of space between the letters; if it's an `x', subtract a half unit; if it's an
+exclamation point, add a full unit. The last instruction applies also
+to exclamation points following~`f' (because of the label `|1::|').
+\smallskip\noindent
+When a character code appears in front of a colon, the colon ``labels''
+the starting place for that character's ligature and kerning program,
+which continues to the end of the ligtable statement. A double colon denotes
+a ``local label''; a |skipto| instruction advances to the next matching local
+label, which must appear before 128 ligtable steps intervene. The special
+label \|\||:| can be used to initiate ligtable instructions for an invisible
+``left boundary character'' that is implicitly present just before every
+word; an invisible ``right boundary character'' equal to ^"boundarychar" is
+also implicitly present just after every word, if "boundarychar" lies between
+0 and~255.
+
+The general syntax for ligtable programs is pretty easy to guess from
+these examples, but we ought to exhibit it for completeness:
+\beginsyntax \chardef\\=`\|
+<ligtable command>\is[ligtable]<ligtable program><optional skip>
+<ligtable program>\is<ligtable step>\alt<ligtable program>[,]<ligtable step>
+<optional skip>\is[,] [skipto]<code>\alt<empty>
+<ligtable step>\is<code><ligature op><code>
+ \alt<code>[kern]<numeric expression>
+ \alt<label><ligtable step>
+<ligature op>\is[=:]\alt[\\=:]\alt[\\=:>]\alt[=:\\]\alt[=:\\>]%
+ \alt[\\=:\\]\alt[\\=:\\>]\alt[\\=:\\>>]
+<label>\is<code>[:]\alt<code>[::]\alt[\\\\:]
+<code>\is<numeric expression>\alt<string expression>
+\endsyntax
+A \<code> should have a numeric value between 0 and 255, inclusive,
+after having been rounded to the nearest integer; or it should be a
+string of length~1, in which case it denotes the corresponding
+^{ASCII} code (Appendix~C\null). For example, |"A"| and |64.61| both
+specify the code value 65. Vertical bars to the left or right of `|=:|'
+tell \TeX\ to retain the original left and/or right character that invoked a
+ligature. Additional `|>|' signs tell \TeX\ to advance its focus of attention
+instead of doing any further ligtable operations at the current
+character position.
+
+{\sl Caution:\/} Novices often go overboard on kerning. Things usually
+work out best if you kern by at most half of what looks right to you
+at first, since kerning should not be noticeable by its presence
+(only by its absence). Kerning that looks right in a logo or in a
+headline display often interrupts the rhythm of reading when it appears
+in ordinary textual material.
+
+You can improve \TeX's efficiency by ordering the steps of a ligtable
+program so that the most frequent alternatives come first.
+\TeX\ will stop reading the program when it finds the first ``hit.''
+
+\medbreak
+Several characters of a font can be linked together in a series
+by means of a ^@charlist@ command. For example,
+\begintt
+charlist oct"000": oct"020": oct"022": oct"040": oct"060"
+\endtt
+is used in the font ^|cmex10| to specify the left parentheses that
+\TeX\ uses in displayed math formulas, in increasing order of size.
+\TeX\ follows charlists to make variable-size delimiters and
+variable-width ^{accents}, as well as to link text-size operators
+like `$\sum$' to the display-size `$\displaystyle\sum$'.
+
+\TeX\ builds up large delimiters by using ``^{extensible}'' characters,
+which are specified by giving top, middle, bottom, and repeatable
+characters in an ^@extensible@ command. For example, the extensible
+left ^{parentheses} in |cmex10| are defined by
+\begintt
+extensible oct"060": oct"060", 0, oct"100", oct"102";
+\endtt
+this says that character code |oct"060"| specifies an extensible
+delimiter constructed from itself as the top piece, from character number
+|oct"100"| as the bottom piece, and from character number |oct"102"| as
+the piece that should be repeated as often as necessary to reach
+a desired size. In this particular example there is no middle
+piece, but characters like curly braces have a middle piece as well.
+A zero value in the top, middle, or bottom position means that
+no character should be used in that part of the construction;
+but a zero value in the final position means that character number zero
+is the repeater. The width of an extensible character is taken to
+be the width of the repeater. \looseness=-1
+
+The first eight different sizes of parentheses available to \TeX\ in
+|cmex10|, when the user asks for `|\left(|', look like this:
+\begindisplay
+$\bigl(\quad\Bigl(\quad\biggl(\quad\Biggl(\quad
+\mathopen{\hbox{$\left(\vbox to20.5pt{}\right.\nulldelimiterspace=0pt$}}\quad
+\mathopen{\hbox{$\left(\vbox to23.5pt{}\right.\nulldelimiterspace=0pt$}}\quad
+\mathopen{\hbox{$\left(\vbox to26.5pt{}\right.\nulldelimiterspace=0pt$}}\quad
+\mathopen{\hbox{$\left(\vbox to29.5pt{}\right.\nulldelimiterspace=0pt$}}$
+\enddisplay
+According to what we know from the examples of @charlist@ and @extensible@
+above, the first four of these are the characters in positions
+|oct"000"|, |oct"020"|, |oct"022"|, and |oct"040"|. The other four have
+character |oct"060"| on top; character |oct"100"| is at the bottom;
+and there are respectively zero, one, two, and three occurrences
+of character |oct"102"| in the middle.
+
+Here is the formal syntax:
+\beginsyntax
+<charlist command>\is[charlist]<labeled code>
+<labeled code>\is<code>
+ \alt<label><labeled code>
+<extensible command>\is[extensible]<label><four codes>
+<four codes>\is<code>[,]<code>[,]<code>[,]<code>
+\endsyntax
+Notice that a \<label> can appear in a ligtable, charlist, or extensible
+command. These appearances are mutually exclusive: No code may be used
+more than once as a label. Thus, for example, a character with a
+ligature/kerning program cannot also be extensible, nor can it be
+in a charlist (except as the final item).
+
+\medbreak
+The last type of information that appears in a |tfm| file applies to
+the font as a whole. Two kinds of data are involved, bytes and
+numerics; and they are specified in ``headerbyte'' and ``fontdimen''
+commands, according to the following general syntax:
+\beginsyntax
+<headerbyte command>\is[headerbyte]<numeric expression>[:]<byte list>
+<fontdimen command>\is[fontdimen]<numeric expression>[:]<numeric list>
+<byte list>\is<code>
+ \alt<byte list>[,]<code>
+<numeric list>\is<numeric expression>
+ \alt<numeric list>[,]<numeric expression>
+\endsyntax
+We shall defer discussion of header bytes until later, because they
+are usually unnecessary. But @fontdimen@ commands are important.
+Numeric parameters of a font can be specified by saying, e.g.,
+\begintt
+fontdimen 3: 2.5, 6.5, 0, 4x
+\endtt
+which means that parameters 3--6 are to be 2.5, 6.5, 0, and $4x$,
+respectively. These are the parameters that \TeX\ calls |\fontdimen3|
+thru |\fontdimen6|. \ (Parameter numbering is old-fashioned:
+There is no |\fontdimen0|.)
+
+The first seven fontdimen parameters have special significance, so plain
+\MF\ has seven macros to specify them symbolically, one at a time:
+
+\smallskip
+\item\bull^@font\_slant@ (|\fontdimen1|) is the amount of ^{slant}
+per point; \TeX\ uses this information when raising or lowering an
+accent character.
+\item\bull^@font\_normal\_space@ (|\fontdimen2|) is the interword spacing.
+If the value is zero, all characters of this
+font will be considered to be ``^{isolated}'' in math mode, so the
+^{italic correction} will be added more often than otherwise.
+\item\bull^@font\_normal\_stretch@ (|\fontdimen3|) is the ^{stretchability}
+of interword spacing, as explained in {\sl The \TeX book}.
+\item\bull^@font\_normal\_shrink@ (|\fontdimen4|) is the ^{shrinkability}
+of interword spacing, as explained in {\sl The \TeX book}.
+\item\bull^@font\_x\_height@ (|\fontdimen5|) is the height of characters
+for which accents are correctly positioned. An accent over a character
+will be raised by the difference between the character's "charht"
+and this value. The ^{x-height} is also the unit of height that
+\TeX\ calls one `|ex|'.
+\item\bull^@font\_quad@ (|\fontdimen6|) is the unit of width that
+\TeX\ calls one `|em|'.
+\item\bull^@font\_extra\_space@ (|\fontdimen7|) is the additional amount
+added to the normal interword space between sentences, depending
+on the ``spacefactor'' as defined in {\sl The \TeX book}.
+\smallskip\noindent
+Parameters are zero unless otherwise specified.
+
+Math symbol fonts for \TeX\ are required to have at least 22 fontdimen
+parameters, instead of the usual seven; math extension fonts need at least~13.
+Appendix~G of {\sl The \TeX book\/} explains the precise significance
+of these additional parameters, which control such things as the
+placement of superscripts and subscripts.
+
+\medbreak
+The {\sl^{design size}\/} of a font is not one of the fontdimen
+parameters; it's an internal quantity of \MF\ that is actually output
+among the header bytes as explained below. When a \TeX\ user asks
+for a font `|at|' a certain size, the font is scaled by the ratio
+between the ``^{at size}'' and the design size. For example,
+|cmr10| has a design size of $10\pt$; if a \TeX\ user requests
+`|cmr10|~|at|~|15pt|', the result is the same as `|cmr10|~|scaled|~|1500|'
+(or, in plain \MF\ terms, |cmr10| with |mag=1.5|).
+
+What does the design size really mean? It's an imprecise notion,
+because there need be no connection between the design size and any specific
+measurement in a font. Typographers have always been vague when
+they speak about ``10~point'' fonts, because some fonts look larger
+than others even though the horizontal and vertical dimensions are the same.
+It's something like dress sizes or shoe sizes.
+
+In general, the design size is a statement about the approximate size
+of the type. Type with a larger design size generally looks bigger
+than type with a smaller design size. Two fonts with the same design
+size are supposed to work well together; for example, |cmr9| and
+|cmtt9| both have $9\pt$ design size, although the uppercase letters of
+|cmtt9| are quite a bit smaller (`|A|' versus `A').
+
+The "designsize" must be at least $1"pt"\0$. And, as with all |tfm|
+dimensions, it must be less than $2048"pt"\0$. Any other value is
+changed to $128"pt"\0$.
+
+\MF\ looks at the value of ^"designsize" only when the job ends, so you
+needn't set it before characters are shipped out. At the end of a job,
+when the |tfm| file is being written, \MF\ checks to make sure that every
+dimension of the font is less than 16 times the design size in absolute
+value, because this limitation is imposed by the |tfm| file format. Thus,
+for example, if the design size is $10\pt$, you cannot have a character
+whose width or height is $160\pt$ or more. If one or more dimensions prove
+to be too big, \MF\ will tell you how many of them had to be changed.
+
+\medbreak
+The ^@headerbyte@ command is similar to @fontdimen@, but it gives
+8-bit \<code> data instead of numeric information. For example,
+\begintt
+headerbyte 33: 0, 214, 0, "c"
+\endtt
+says that bytes 33--36 of the |tfm| file header will be 0, 214,
+0, and~99. The first four header bytes (numbers 1--4) are automatically
+set to a ^{check sum}, unless you have specified other values for
+at least one of those bytes. \ (This check sum will match a similar
+value in the |gf|~file, so that other typesetting software can check
+the consistency of the different files they use.) \ Similarly,
+the next four header bytes (numbers 5--8) are set automatically to
+the design size times $2^{20}$, unless you have specified something
+else. \looseness=-1
+
+\TeX\ looks only at the first eight header bytes, so you needn't use the
+header\-byte command if you are simply producing a font for
+standard \TeX. But other software that reads |tfm| files may have
+a need for more header information. For example, the original
+|tfm| format (developed by Lyle ^{Ramshaw} at ^{Xerox} Palo Alto
+Research Center) included ^@font\_coding\_scheme@ information
+in bytes 9--48 of the header, and ^@font\_identifier@ information in
+bytes 49--68. The design size of certain fonts was also packed into
+byte~72. Each font in the ``Xerox world'' is uniquely identified by
+its font identifier and its design size, rather than by its font file name.
+
+The ``font coding scheme'' is merely a comment that can be used
+to help understand large collections of fonts; it's usually a nice thing
+to know. Some of the coding scheme names in common use are
+\begindisplay
+|TeX text|&|TeX math italic|\cr
+|TeX typewriter text|&|TeX math symbols|\cr
+|XEROX text|&|TeX math extension|\cr
+|ASCII|&|TeX extended ASCII|\cr
+|PI|&|GRAPHIC|\cr
+\enddisplay
+The coding-scheme string should not include parentheses.
+
+Here are macros that can be used, if desired, to convert plain
+\MF's @font\_identifier@ and @font\_coding\_scheme@ into the format
+^^{substring} ^^{BCPL strings}
+required by Ramshaw's original |tfm| files:
+\beginlines
+|def BCPL_string(expr s,n) = % string s becomes an n-byte BCPL string|
+| for l:=if length(s)>=n: n-1 else: length(s) fi: l|
+| for k:=1 upto l: , substring (k-1,k) of s endfor|
+| for k:=l+2 upto n: , 0 endfor endfor enddef;|
+\smallskip
+^|inner|| end;|
+|def bye = if fontmaking>0:|
+| headerbyte 9: BCPL_string(font_coding_scheme_,40);|
+| special "codingscheme " & font_coding_scheme_;|
+| headerbyte 49: BCPL_string(font_identifier_,20);|
+| special "identifier " & font_identifier_;|
+| headerbyte 72: max(0, 254 - round 2designsize); fi|
+| end enddef;|
+^|outer|| bye,end;|
+\endlines
+These macros could be included among the ^|local.mf| extensions to
+|plain.mf| at particular installations. When a user says `^@bye@' instead
+of `^@end@', the additional headerbyte documentation will then be
+automatically inserted into the |tfm| file.
+
+\medbreak
+Let us now conclude this appendix by summarizing what we've learned.
+A \MF\ programmer can provide various types of information about how
+to typeset with a font, by using font metric commands. Simple versions
+of these commands, sufficient for simple fonts, are standard operations
+in plain \MF; examples have appeared in Chapter~11
+and the beginning of Appendix~E\null. The general cases are handled by
+five types of font metric commands:
+\beginsyntax
+<font metric command>\is<ligtable command>
+ \alt<charlist command>
+ \alt<extensible command>
+ \alt<fontdimen command>
+ \alt<headerbyte command>
+\endsyntax
+This completes the syntax of \MF\ that was left slightly unfinished
+in Chapter~26.
+
+\endchapter
+
+Such things induced me to untangle the chaos
+% Voil\`a ce qui m'a engag\'e \`a d\'ebrouiller ce chaos,
+by introducing order where it had never been before:
+% en mettant dans cette partie un ordre qui n'y avoit jamais r\'egn\'e :
+I think I may say I have had the good fortune to succeed
+% je crois avoir eu le bonheur d'y r\'eussir
+with an exactness \& a precision leaving nothing more to be desired,
+% avec une justesse \& une pr\'ecision qui ne laissent rien \`a desirer,
+by the invention of\/ {\rm Typographic points}.
+% par l'invention des \it Points typographiques.
+\author PIERRE ^{FOURNIER}, {\sl Manuel Typographique\/} (1764) % p129
+
+\bigskip
+
+One should absorb the color of life,
+but one should never remember its details.
+Details are always vulgar.
+\author OSCAR ^{WILDE}, {\sl The Picture of Dorian Gray\/} (1890)
+ % middle of ch6 in original edition [Lippincott's vol 46]; ch8 subsequently
+
+\eject
+ \beginchapter Appendix G. Generic\\Font\\Files
+
+\MF's main output goes into a ^|gf| or ``Generic Font'' file, so-called
+because it can easily be translated into any other digital font format,
+although it does not match the specifications of any ``name brand''
+manufacturer. The purpose of this appendix is to explain exactly what
+kinds of information go into the |gf| file, and under what circumstances
+\MF\ puts things there.
+
+\ninepoint\medskip
+A |gf| file is a compact binary representation of a digitized font,
+containing all the information needed by ``^{device driver}''
+software that produces printed documents from \TeX's ^|dvi| files. The
+exact internal representation scheme of |gf| files doesn't concern us
+here, but we ought to know what type of data is encoded.
+
+\smallskip
+The first thing in a |gf| file is a string that explains its origin.
+\MF\ writes strings of the form
+\begintt
+METAFONT output 1986.06.24:1635
+\endtt
+based on the values of the internal quantities ^"day", ^"month",
+^"year", and ^"time" when the |gf| file was started. \ (In this case
+$"day"=24$, $"month"=6$, $"year"=1986$, % my 25th wedding anniversary
+and $"time"=16\times60+35=995$.)
+
+After the opening string, the |gf| file contains a sequence of
+``special'' commands interspersed with shipped-out character images.
+^{Special commands} are intended to provide a loophole for future
+extensions to \MF's set of primitives, so that \MF\ itself will not
+have to change. Some specials are predefined, but others will
+undoubtedly be created in years to come. \ (\TeX\ has an analogous
+|\special| command, which puts an arbitrary string into a |dvi| file.)
+
+A special command gets into the |gf| file when you say `^@special@
+\<string>' or `^@numspecial@ \<numeric>' at a time when
+^"proofing"$\null\ge0$. A @special@ string should come before
+@numspecial@, and it
+should either be a keyword all by itself or it should consist of a keyword
+followed by a space followed by additional information. Keywords that
+specify operations requiring numeric arguments should be followed by
+numbers produced by @numspecial@. For example, the `^@proofrule@' macro
+in Appendix~B expands into a sequence of five special commands,
+\begindisplay
+@special@ |"rule"|;\cr
+@numspecial@ $x_1$; \ @numspecial@ $y_1$;\cr
+@numspecial@ $x_2$; \ @numspecial@ $y_2$;\cr
+\enddisplay
+this represents a rule on the proofsheet that runs from point $(x_1,y_1)$
+to point $(x_2,y_2)$. If you say `|grayfont gray5|', the ^@grayfont@
+macro in Appendix~B expands to `@special@ |"grayfont gray5"|'.
+Software that reads |gf| files will examine all of the special strings,
+until coming to a space or to the end of the string. If the resulting
+keyword isn't known to the program, the special string will be ignored,
+together with all numspecials that immediately follow. But when the
+keyword is known, the program will be able to determine the corresponding
+arguments. For example, the |GFtoDVI| program described in Appendix~H
+knows about the plain \MF\ keywords `|rule|' and `|grayfont|'.
+
+\MF\ might also create @special@ commands on its own initiative, but only
+when "proofing" is strictly greater than zero. There are
+two cases: (1)~When a ^\<title> statement occurs,
+the special string `|"title "|\thinspace\&\thinspace\<title>'
+is output. \ (This is how the phrase `|The letter O|' got onto your
+proofsheets in the experiments of Chapter~5.) \ (2)~Just before a
+character image is shipped out, \MF\ implicitly executes the following
+sequence of instructions:
+\begindisplay
+@if@ round $"xoffset"\ne0$: \ @special@ |"xoffset"|; \
+ @numspecial@ round ^"xoffset"; @fi@\cr
+@if@ round $"yoffset"\ne0$: \ @special@ |"yoffset"|; \
+ @numspecial@ round ^"yoffset"; @fi@\cr
+\enddisplay
+
+A ^@shipout@ command sends a digitized picture to the |gf|
+file, if $"proofing"\ge0$, but nothing is output if $"proofing"<0$.
+Furthermore the current values of ^"charwd", ^"charht", ^"chardp", ^"charic",
+^"chardx", and ^"chardy" are stored away for the current ^"charcode";
+these values are stored in all cases, regardless of the value of "proofing".
+The current character code is henceforth said to ``exist.'' ^^@charexists@
+
+When a ^{picture} is shipped out, its pixels of positive value are
+considered to be ``black,'' and all other pixels are considered to be
+``white.'' The pattern of blacks and whites is encoded in such a way
+that doubling the resolution approximately doubles the length of the
+|gf| output, in most cases.
+
+\MF\ reports its progress by typing `|[|$c$|]|' on the terminal
+when character code~$c$ is being shipped out. \ (The `^|[|' is typed
+before output conversion begins, and the `^|]|' is typed after; hence you
+can see how much time output takes.) \ If "charext" is nonzero, after
+being rounded to an integer, the typed message is `|[|$c.x$|]|' instead;
+for example, `[65.3]' refers to character~65 with extension code~3.
+
+\TeX\ allows only 256 characters per font, but extensions of \TeX\
+intended for ^{oriental} languages will presumably use the "charext"
+feature. All characters with the same code share the same width,
+height, and depth, but they can correspond to distinct graphics if they have
+different extension codes.
+
+\medbreak
+A @special@ command generally refers to the picture that follows it,
+rather than the picture that precedes~it. Special commands before the
+first digitized picture might, however, give instructions about
+the font as a whole. Special commands that follow the final picture
+invariably refer to the font as a whole. \ (For example, the
+`^@bye@' macro at the end of Appendix~F creates two special
+strings that will appear after the final character of a font.)
+
+\medbreak
+No |gf| file will be written unless a character is shipped out or a
+special command is performed at a time when $"proofing"\ge0$, or unless a
+title statement is encountered at a time when $"proofing">0$. When one of
+these things first happens, the |gf| file receives its name. If no
+^@input@ commands have yet occurred, \MF\ will set the job name to
+`^|mfput|'; otherwise the job name will already have been determined. The
+full name of the |gf| file will be
+`\<jobname>|.|\<resolution>\thinspace|gf|', where the \<resolution> is
+based on the current value of~^"hppp". \ (If $"hppp"\le0$, the resolution
+will be omitted; otherwise it will be converted to an equivalent number of
+pixels per inch, in the horizontal dimension.) \ Subsequent @input@
+operations or changes to~"hppp" will not change the ^^{file name}
+name of the |gf| file.
+
+\medbreak
+The end of a |gf| file contains a bunch of numeric data needed for
+typesetting. First come the ^{design size} and the ^{check sum};
+these match precisely the data in the |tfm| file, unless the header
+bytes of the |tfm| have explicitly been set to something else.
+Then come the values of "hppp" and "vppp". \ (These are the values
+at the end of the job, so "hppp" might not agree with the \<resolution>
+value in the |gf| file name.)
+
+Finally, the |gf| file gets the ^"charwd", ^"chardx", and ^"chardy"
+of each existing character code. The values of "chardx" and "chardy"
+represent desired ``escapements'' when characters are typeset on a
+particular device (cf.\ Chapter~12). The "charwd" values are identical to
+the widths in the |tfm| file.
+
+\medbreak
+The check sum is based entirely on the "charwd" data; two fonts
+with the same character widths will have the same check sum, but
+two fonts with different character widths will almost never have
+the same check sum.
+
+The purpose of check sums can be understood by considering the following
+scenario: A font named |cmr10| might be generated by \MF\ at any time,
+producing a |tfm| file called |cmr10.tfm| and a |gf| file called,
+say, |cmr10.300gf|. A document named |doc|, which uses |cmr10|,
+might be generated by \TeX\ at any time, producing a |dvi| file
+called |doc.dvi|; \TeX\ had to read |cmr10.tfm| in order to
+produce this |dvi| file. Now on some future date, a ``^{device driver}''
+program will be used to print |doc.dvi|, using the font
+|cmr10.300gf|. Meanwhile, the font may have changed.
+If the current |gf| file doesn't match the |tfm| file that was assumed
+by \TeX, mysterious glitches will probably occur in the printed document,
+because |dvi| information is kept concise by the assumption that the
+device driver knows the |tfm| widths of all characters. Potential
+problems are kept to a minimum if \TeX\ puts the assumed design size
+and check sum of each font into the |dvi| files it produces;
+a device driver can then issue a warning message when it finds a
+|gf| file that is inconsistent with \TeX's assumptions.
+
+\endchapter
+
+But if our\/ {\rm Letter-Cutter} \kern-1pt will have no Forge,
+yet he must of necessity accommodate himself %
+with a\/ {\rm Vice, Hand-Vice, Hammers,}
+\leavevmode{\rm Files, Small} \kern-1pt and\/ {\rm Fine Files} (commonly %
+ called\/ \kern1pt{\rm Watch-makers Files})
+of these he saves all, as they wear out.
+\author JOSEPH ^{MOXON}, {\sl Mechanick Exercises\/} (1683)
+ % part 12, section 1
+
+\bigskip
+
+The natural definition lists all possible generic characters.
+% 189. NATURALIS Character (186) notas omnes (92--113)
+% genericas possibiles (167) allegat;
+\author CAROLUS ^{LINN\AE US}, {\sl Philosophia Botanica\/} (1751)
+ % this translation due to Frans A. Stafleu
+
+\eject
+ \beginchapter Appendix H. Hardcopy Proofs
+
+A font cannot be proved correct like a mathematical theorem; a font must
+be seen to be believed. Moreover, if some characters of a font are faulty,
+the best way to fix them is to look at diagrams that indicate what went wrong.
+Therefore \MF\ is incomplete by itself; additional programs are needed to
+convert the output of \MF\ into graphic form.
+
+The purpose of this appendix is to discuss two such auxiliary programs,
+which serve as examples of many others that could be devised. The first
+of these, called ^|GFtoDVI|\null, takes |gf| files and converts them into
+^|dvi| files, which can be printed just like the output of \TeX. Each
+character image in the |gf| file will have a printed page to itself, with
+labeled points and with bounding boxes just as in the illustrations
+we have seen throughout this book. \ (Indeed, the illustrations in this
+book were produced by |GFtoDVI|\null.) \ The second auxiliary program to
+be discussed below is \TeX\ itself; we shall look at a set of \TeX\ macros
+designed to facilitate font testing.
+
+\ninepoint
+\subsection Large scale proofs. The |gf| files produced by plain \MF\
+when it is in ^"proof" mode or ^"smoke" mode can be converted to
+annotated diagrams by running them through |GFtoDVI|\null, as we know from
+the experiments in Chapter~5. It's also possible to study low-resolution
+characters with |GFtoDVI|\null, especially if plain \MF's
+`^|gfcorners|' feature has been used. ^^{low resolution proofs}
+We shall now take a thorough look at what |GFtoDVI| can do.
+
+All communication from \MF\ to |GFtoDVI| comes through the |gf| file and
+from options that you might type when you run |GFtoDVI|\null. If there are
+no ``^{special}'' commands in the |gf| file (cf.~Appendix~G\null), each page
+of |GFtoDVI|'s output will show just the ``black'' pixels of a character;
+furthermore there will be a title line at the top of the page, showing
+the date and time of the \MF\ run, together with the character code
+number and extension code (if they are nonzero). The black pixels are
+typeset via characters of a so-called ``^{gray font},'' described in
+detail below; by changing the gray font you can produce a variety of
+different outputs from a single |gf| file.
+
+To get other things on your proof sheets, ``special'' commands must
+appear in the |gf| file. For example, \MF\ will automatically output
+a |title| command, if $"proofing">0$, as explained in Appendix~G\null;
+|GFtoDVI| will typeset this title on the title line of the next character
+image that follows the command. If there are several title statements,
+they all will appear; they are supposed to fit on a single line.
+
+The most important special commands tell |GFtoDVI| to create labeled
+points on the character diagram. When you say, for example,
+`^@labels@$(1,2)$' in a plain \MF\ program, at a time when
+^"proofing"$\null>1$, the macros of Appendix~B will convert this to the
+special commands
+\begindisplay
+@special@ |" 01"|; \ ^@numspecial@ $x_1$; \ @numspecial@ $y_1$;\cr
+@special@ |" 02"|; \ @numspecial@ $x_2$; \ @numspecial@ $y_2$;\cr
+\enddisplay
+|GFtoDVI| will then put a dot labeled `|1|' at point $(x_1,y_1)$
+and a dot labeled `|2|' at~$(x_2,y_2)$.
+
+Labels are placed in one of four positions relative to their dots---%
+either at the top, the left, the right, or the bottom. |GFtoDVI| will
+ordinarily try to place all labels so that they don't interfere with
+each other, and so that they stay clear of other dots.
+ But if you want to exercise fine control over the placement
+yourself, you can say, for example, `@labels@."top"$(1a,2a)$'; in this
+case the specified labels will appear above their dots, regardless of whether or
+not other labels and/or dots are thereby overprinted. The |gf| file
+^^{labels.top} in this case will contain
+\begindisplay
+@special@ |" 11a"|; \ @numspecial@ $x_{1a}$; \ @numspecial@ $y_{1a}$;\cr
+@special@ |" 12a"|; \ @numspecial@ $x_{2a}$; \ @numspecial@ $y_{2a}$.\cr
+\enddisplay
+|GFtoDVI| looks at the character following a leading blank space to
+determine what sort of labeling convention is desired; the subsequent
+characters are the text of the label.
+
+The command `@labels@."top"$(1a,2a)$' in plain \MF\ is just an
+abbreviation for `^@makelabel@."top"(|"1a"|$,z_{1a}$);
+@makelabel@."top"(|"2a"|$,z_{2a}$)', when $"proofing">1$; the @makelabel@
+macro is really the fundamental one, and you should use it directly if you
+want more unusual effects. Suppose, for example, you just want to
+put a dot but no label at point~$z_5$; then you can say
+`@makelabel@(|""|$,z_5$)'. And suppose you want to put a label to the
+left of point~$z_5$ but with no dot; you can say
+`@makelabel@."lft".^"nodot"(|"5"|$,z_5$)'. Furthermore you could say
+`@makelabel@."lft".^"nodot"(|"5"|$,z_5-(2,3)$)' to move that label left
+by~2 pixels and down by~3 pixels, thereby getting the effect of a label
+that is diagonally adjacent to its dot. Labels without dots can also
+be used to put words on a diagram.
+
+|GFtoDVI| recognizes nine varieties of labels in all, based on the
+first two characters of the special string command:
+\smallskip
+\item\bull@makelabel@ (special |" 0"|): choose the label position automatically.
+\item\bull@makelabel@."top" (special |" 1"|): center the label just above
+the dot.
+\item\bull@makelabel@."lft" (special |" 2"|): place the label just left of
+the dot.
+\item\bull@makelabel@."rt" (special |" 3"|): place the label just right of
+the dot.
+\item\bull@makelabel@."bot" (special |" 4"|): center the label just below
+the dot.
+\item\bull@makelabel@."top"."nodot" (special |" 5"|): like "top", but omit
+the dot.
+\item\bull@makelabel@."lft"."nodot" (special |" 6"|): like "lft", but omit
+the dot.
+\item\bull@makelabel@."rt"."nodot" (special |" 7"|): like "rt", but omit
+the dot.
+\item\bull@makelabel@."bot"."nodot" (special |" 8"|): like "bot", but omit
+the dot.
+\smallskip\noindent
+The first case is called {\sl autolabeling\/}; this is the normal command.
+Autolabeling always places a dot, whether or not that dot overlaps other dots,
+but you don't always get a label. Autolabels are typeset only after
+all explicit labels have been established; then |GFtoDVI| tries to
+place as many of the remaining labels as possible.
+
+If there's no place to put an autolabel, an ``^{overflow equation}'' is
+put in the upper right corner of the proofsheet. For example, the
+overflow equation `|5 = 5r + (-4.9,0)|' means that there was no room
+for label~|5|, whose dot is 4.9 pixels to the left of the dot for~|5r|
+(which is labeled).
+
+You can avoid overflow equations by sending |GFtoDVI| the special command
+|" /"| instead of |" 0"|; ^^{/} this is a variant of autolabeling that
+does everything as usual except that the label will simply be forgotten if
+it can't be placed. To do this with plain \MF\!, set
+`$"lcode\_":=\null$|" /"|' near the beginning of your program; ^"lcode\_"
+is the string that @makelabel@ uses to specify autolabeling.
+
+The next most important kind of annotation for proofs is a straight line
+or ``^{rule}.'' Plain \MF's command for this is `^@proofrule@$(z_1,z_2)$',
+which expands to
+\begindisplay
+@special@ |"rule"|; \ @numspecial@ $x_1$; \ @numspecial@ $y_1$;\cr
+\qquad @numspecial@ $x_2$; \ @numspecial@ $y_2$.\cr
+\enddisplay
+|GFtoDVI| has trouble drawing diagonal rules, because standard ^|dvi|
+format includes no provision for drawing straight lines unless they are
+vertical or horizontal. Therefore you might get an error message
+unless $x_1=x_2$ (vertical rule) or $y_1=y_2$ (horizontal rule).
+However, a limited escape from this restriction is available via a
+``^{slant font},'' by which |GFtoDVI| is able to typeset diagonal lines
+as sequences of characters. Only one slope is permitted per job,
+but this is better than nothing (see below).
+
+To control the weight of proof rules, you say, e.g., `^@proofrulethickness@
+1.5$"mm"\0$' in a plain \MF\ program; this expands to
+\begindisplay
+@special@ |"rulethickness"|; \ @numspecial@ $1.5"mm"\0$.
+\enddisplay
+Each horizontal or vertical rule is drawn as if by a pen of the current
+rulethickness, hence you can get different weights of lines in a single
+diagram. If the current rulethickness is negative, no rule will appear; if
+it is zero, a default rulethickness based on a parameter of the gray font
+will be used; if it is positive, the stated thickness will be increased if
+necessary until it equals an integer number of pixels, and that value will
+be used to draw the rule. At the beginning of each character the current
+rulethickness is zero.
+
+You can reposition an entire diagram on its page by saying `^@proofoffset@
+$(x,y)$'; this expands to
+\begindisplay
+@special@ |"offset"|; \ @numspecial@ $x$; \ @numspecial@ $y$
+\enddisplay
+and it tells |GFtoDVI| to shift everything except the title line on the
+next character image, $x$~pixels to the right and $y$~pixels upward.
+
+|GFtoDVI| uses four fonts to typeset its output: (1) The {\sl
+{title font}\/} is used for the top line on each page.
+(2)~The {\sl{label font}\/} is used for all labels.
+(3)~The {\sl{gray font}\/} is used for dots and for black pixels.
+(4)~The {\sl{slant font}\/} is used for diagonal rules.
+Appropriate default fonts will be used at each installation unless
+you substitute specific fonts yourself, by using the @special@ commands
+^@titlefont@, ^@labelfont@, ^@grayfont@, or ^@slantfont@.
+|GFtoDVI| also understands special strings like `|"grayfontarea
+/usr/dek"|', which can be used to specify a nonstandard file area
+or directory name for the gray font. Furthermore the |gf| file might
+^^{grayfontarea} ^^{labelfontat}
+say, e.g.,
+\begindisplay
+@special@ |"labelfontat"|; @numspecial@ 20
+\enddisplay
+if you want the label font to be loaded at $20\pt$ instead of its ^{design
+size}. The area name and the at size must be given after the font name
+itself; in other words, `|"grayfont"|' cancels a previous
+`|"grayfontarea"|'.
+
+The four fonts used by |GFtoDVI| must be established before the first
+character bitmap appears in the |gf| file. This means that the special font
+commands must be given before the first ^@shipout@ or ^@endchar@ in your
+program; but they shouldn't appear until after ^@mode\_setup@, so that
+your |gf| file will have the correct name. If it's inconvenient to
+specify the fonts that way, you can change them at run time when
+you use |GFtoDVI|\null: Just type `^|/|' following the name of the |gf| file
+that's being input, and you will be asked to type special strings online.
+For example, the run-time dialog might look like this:
+\begintt
+This is GFtoDVI, Version 2.0
+GF file name: io.2602gf/
+Special font substitution: labelfont cmbx10
+OK; any more? grayfont black
+OK; any more?
+\endtt
+After the final carriage return, |GFtoDVI| does its normal thing,
+ignoring font specifications in the file that conflict with those
+just given.
+
+\subsection ^{Gray fonts}. A proof diagram constructed by |GFtoDVI| can
+be regarded as an array of rectangles, where each rectangle is either
+blank or filled with a special symbol that we shall call `{\manual R}'. A
+blank rectangle represents a white pixel, while {\manual R} represents a
+black pixel. Additional labels and reference lines are often superimposed
+on this array of rectangles; hence it is usually best to choose a symbol
+{\manual R} that has a somewhat gray appearance, although any symbol can
+actually be used.
+
+In order to construct such proofs, |GFtoDVI| needs to work with
+a special type of font known as a ``gray font''; it's possible to
+obtain a wide variety of different sorts of proofs by using different
+sorts of gray fonts. The next few paragraphs explain exactly what gray
+fonts are supposed to contain, in case you want to design your own.
+
+The simplest gray font contains only two characters, namely {\manual R}
+and another symbol that is used for dots that identify key points.
+If proofs with relatively large pixels are desired, a two-character
+gray font is all that's needed. However, if the pixel size is to be
+relatively small, practical considerations make a two-character
+font too inefficient, since it requires the typesetting of tens
+of thousands of tiny little characters; printing-device drivers
+rarely work very well when they are presented with data that is
+so different from ordinary text. Therefore a gray font with small
+pixels usually has a number of characters that replicate {\manual R} in
+such a way that comparatively few characters actually need to be
+typeset.
+
+Since many printing devices are not able to cope with
+arbitrarily large or complex characters, it is not possible for a
+single gray font to work well on all machines. In fact,
+{\manual R} must have a width that is an integer multiple of the printing
+device's units of horizontal and vertical positioning,
+since rounding the positions of grey
+characters would otherwise produce unsightly streaks on proof output.
+Thus, there is no way to make the gray font as device-independent as
+normal fonts of type can be.
+
+This understood, we can now take a look at what |GFtoDVI| expects to
+see in a gray font. The character~{\manual R} always appears in position~1. It
+must have positive height~$h$ and positive width~$w$; its depth
+and italic correction are ignored.
+
+Positions 2--120 of a gray font are reserved for special combinations of\/
+{\manual R}'s and blanks, stacked on top of each other. None of these
+character codes need be present in the font; but if they are, the slots
+must be occupied by characters of width~$w$ that have certain
+configurations of\/ {\manual R}'s and blanks, prescribed for each character
+position. For example, position~3 of the font should either contain no
+character at all, or it should contain a character consisting of two
+{\manual R}'s, one above the other; one of these {\manual R}'s should rest
+on the baseline, and the other should appear immediately below.
+
+It will be convenient to use a horizontal notation like `{\manual RSRRS}'
+to stand for a vertical stack of\/ {\manual R}'s and blanks. The convention
+will be that the stack is built from bottom to top, and the topmost
+rectangle should sit on the baseline. Thus, `{\manual RSRRS}' stands
+actually for a character of height~$h$ and depth~$4h$ that looks like this:
+\begindisplay
+\vbox{\offinterlineskip\halign{\manual#\hfil\cr
+\phantom{R}\cr
+R\rm\smash{\hbox{\raise.5pt\hbox{$\longleftarrow$ baseline}}}\cr
+R\cr
+\phantom{R}\cr
+R\cr
+}}
+\enddisplay
+We use a horizontal notation in this discussion instead of a vertical one
+because column vectors take too much space, and because the horizontal
+notation corresponds to binary numbers in a convenient way.
+
+Positions 1--63 of a gray font are reserved for the patterns {\manual R},
+{\manual RS}, {\manual RR}, {\manual RSS}, {\manual RSR}, and so~on up to
+{\manual RRRRRR}, just as in the normal binary notation of the numbers
+1--63, with {\manual R}'s substituted for 1's and blanks for 0's.
+Positions 64--70 are reserved for the special patterns {\manual RSSSSSS},
+{\manual RRSSSSS}, {\manual RRRSSSS}, {\manual RRRRSSS}, {\manual
+RRRRRSS}, {\manual RRRRRRS}, {\manual RRRRRRR} of length seven; positions
+71--78 are, similarly, reserved for the length-eight patterns {\manual
+RSSSSSSS} through {\manual RRRRRRRR}. The length-nine patterns {\manual
+RSSSSSSSS} through {\manual RRRRRRRRR} are assigned to positions 79--87,
+the length-ten patterns to positions 88--97, the length-eleven patterns to
+positions 98--108, and the length-twelve patterns to positions 109--120.
+
+Position 0 of a gray font is reserved for the ``dot'' character, which
+should have positive height~$h'$ and positive width~$w'$. When |GFtoDVI|
+wants to put a dot at some place $(x,y)$ on the figure, it positions
+the dot character so that its reference point is at $(x,y)$. The
+dot will be considered to occupy a rectangle whose corners are at
+$(x\pm w',y\pm h')$; the rectangular
+box for a label will butt up against the rectangle enclosing the dot.
+
+All other character positions of a gray font (namely, positions 121--255)
+are unreserved, in the sense that they have no predefined meaning.
+But |GFtoDVI| may access them via the ^@charlist@ feature of
+|tfm| files, starting with any of the characters in positions
+1--120. In such a case each succeeding character in a list should be
+equivalent to two of its predecessors, horizontally adjacent to each other.
+For example, in
+\begindisplay
+@charlist@ 53: 121: 122: 123
+\enddisplay
+character 121 will stand for two 53's, character 122 for two 121's (i.e.,
+four 53's), and character 123 for two 122's (i.e., eight 53's). Since
+position~53 contains the pattern {\manual RRSRSR}, character~123 in this example
+would have height~$h$, depth~$5h$, and width~$8w$, and it would stand for
+the pattern
+\begindisplay
+\vbox{\offinterlineskip\halign{\manual#\hfil\cr
+RRRRRRRR\cr
+\phantom{SSSSSSSS}\rm
+ \smash{\hbox{\raise.5pt\hbox{$\longleftarrow$ baseline}}}\cr
+RRRRRRRR\cr
+\phantom{SSSSSSSS}\cr
+RRRRRRRR\cr
+RRRRRRRR\cr
+}}
+\enddisplay
+Such a pattern is, of course, rather unlikely to occur in a |gf| file,
+but |GFtoDVI| would be able to use if it were present. Designers
+of gray fonts should provide characters only for patterns that they think
+will occur often enough to make the doubling worthwhile. For example,
+the character in position 120 ({\manual RRRRRRRRRRRR}), or whatever is the
+tallest stack of\/ {\manual R}'s present in the font, is a natural candidate for
+repeated doubling.
+
+Here's how |GFtoDVI| decides what characters of the gray font will be used,
+given a configuration of black and white pixels: If there are no black
+pixels, stop. Otherwise look at the top row that contains at least one
+black pixel, and the eleven rows that follow. For each such column,
+find the largest~$k$ such that $1\leq k\leq120$ and the gray font contains
+character~$k$ and the pattern assigned to position~$k$ appears in the
+given column. Typeset character $k$ (unless no such character exists)
+and erase the corresponding black pixels; use doubled characters,
+if they are present in the gray font, if two or more consecutive equal
+characters need to be typeset. Repeat the same process on the remaining
+configuration, until all the black pixels have been erased.
+
+If all characters in positions 1--63 are present, this process is guaranteed to
+take care of at least six rows each time; and with characters 64--120 as well,
+it usually takes care of twelve, since all patterns that contain at most
+one ``run'' of\/ {\manual R}'s are present.
+
+Some of the ^@fontdimen@ parameters discussed in Appendix~F are important
+in gray fonts. The ^@font\_slant@ value~$s$, if nonzero, will cause
+|GFtoDVI| to skew its output; in this case the character {\manual R} will
+presumably be a parallelogram with a corresponding slant, rather than the
+usual rectangle. \MF's coordinate $(x,y)$ will appear in physical position
+$(xw+yhs,yh)$ on the proofsheets. \ (This is appropriate for proofing unslanted
+fonts whose pixels will become slanted by mechanical obliquing.)
+
+Parameter @fontdimen@ 8 of a gray font specifies the thickness of rules
+that go on the proofs. If this parameter is zero, \TeX's default
+rule thickness (0.4\thinspace pt) will be used.
+The other parameters of a gray font are ignored by |GFtoDVI|\null, but
+it is conventional to set ^@font\_normal\_space@ and ^@font\_quad@ to~$w$,
+^@font\_x\_height@ to~$h$.
+
+For best results the designer of a gray font should choose $w$ and~$h$
+so that the user's |dvi|-to-hardcopy software will not make any
+rounding errors. Furthermore, the dot should be an even number~$2m$ of
+pixels in diameter, and the rule thickness should work out to an
+even number~$2n$ of pixels; then the dots and rules will be centered on
+the correct positions, in the common case of integer coordinates. Gray fonts
+are almost always intended for particular output devices, even though
+`|dvi|' stands for ``device independent''; we use |dvi| files for \MF\
+proofs chiefly because software to print |dvi| files is already in place.
+
+The \MF\ program for a fairly versatile gray font generator, called
+`^|grayf.mf|', appears on the next few pages. It should be invoked by a
+parameter file that establishes values of several quantities:
+\smallskip
+\item\bull If ^"large\_pixels" is of type @boolean@, only 15 characters
+will be generated; otherwise there will be 123.
+\item\bull If ^"pix\_picture" is of type @picture@, it should be the
+desired pixel image `{\manual R}', and in this case ^"pix\_wd" and
+^"pix\_ht" should be the width and height in pixels. Otherwise a default
+gray pixel pattern will be used.
+\item\bull If ^"rep" is known, it should be a positive integer; the default
+pixel pattern will be replicated so that the final
+proofs will be "rep" times bigger than usual, and the pattern will be clipped
+slightly at the edges so that discrete pixels can be seen plainly.
+\item\bull If ^"lightweight" is of type @boolean@, the default pixel
+pattern will be only half as dark as usual.
+\item\bull If ^"dotsize" is known, it should be the diameter of the
+special dot character, in pixel units.
+\item\bull The ^@font\_identifier@ should be specified.
+\smallskip\noindent
+(The "rep" and "lightweight" options are ignored if "pix\_picture" is
+explicitly given.) \
+Since gray fonts are inherently device-dependent, we do not start
+with ``sharp'' dimensions as in normal fonts; we go backwards and
+compute the sharp units from pixel units.
+
+The name of each gray font should include the name of the device for
+which it is intended. \ (A ``favorite'' proof device can also be chosen
+at each installation, for which the alternate font names `^|gray|'
+and `^|black|' are valid; these installation-dependent fonts are the
+defaults for "proof" mode and "smoke" mode.)
+
+Here, for example, is a suitable parameter file `|graycheap.mf|', which
+generates a vanilla-flavored gray font for the hypothetical "cheapo"
+printer:
+\beginlines
+|% Gray font for Cheapo with proofsheet resolution 50 pixels per inch|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|font_identifier "GRAYCHEAP";|
+|input grayf|
+\endlines
+(The proofsheet resolution will be 50 pixels per inch, because "cheapo" has
+200 pixels per inch, and the default "pix\_picture" in |grayf|
+will be four pixels square in this case.) \ If the default pixel pattern
+turns out to be such a dark gray that the labels and rules are obscured,
+the statement `|boolean lightweight|' should be added. A solid black font
+with slightly higher-resolution images can be generated by the following
+file `|blackcheap.mf|':
+\beginlines
+|% Black font for Cheapo with proofsheet resolution 66.7 pixels per inch|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|picture pix_picture; pix_wd := pix_ht := 3;|
+|pix_picture := unitpixel scaled 3;|
+\smallskip
+|font_identifier "BLACKCHEAP";|
+|input grayf|
+\endlines
+And here is a file `|graycheap5.mf|' that generates a gray font suitable
+for studying large proofs of low-resolution characters:
+\beginlines
+|% Gray font for Cheapo with proofsheet resolution 10 pixels per inch|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|rep=5; boolean large_pixels;|
+\smallskip
+|font_identifier "GRAYCHEAP";|
+|input grayf|
+\endlines
+
+Now let's look at the program file `|grayf.mf|' itself. It begins with
+a simple test to ensure that "mag" and "rep" are positive integers, if
+they're known; then comes some less obvious code that handles
+magnification in a nonstandard way:
+\beginlines
+|% More-or-less general gray font generator|
+|% See Appendix H of The METAFONTbook for how to use it|
+\smallskip
+|forsuffixes m = mag,rep:|
+| if unknown m: m := 1;|
+| elseif (m<1) or (m<>floor m):|
+| errmessage "Sorry, " & str m & " must be a positive integer";|
+| m := 1; fi endfor|
+\smallbreak
+|mg := mag; mag := 1; mode_setup;|
+|if mg>1: hppp := hppp*mg; vppp := vppp*mg;|
+| extra_endchar:=|
+| "if charcode>0:currentpicture:=currentpicture scaled mg;fi"|
+| & extra_endchar; fi;|
+\endlines
+This circumlocution is the easiest way to guarantee that the ^|tfm| file
+will be completely unaffected by magnification.
+
+The next part of |grayf| computes the pixel representation, "pix\_picture".
+\beginlines
+|if picture pix_picture: rep := 1;|
+| cull pix_picture keeping (1,infinity);|
+|else: for z=(0,2),(1,0),(2,3),(3,1):|
+| fill unitsquare shifted z; endfor|
+| if not boolean lightweight:|
+| addto currentpicture also|
+| currentpicture rotated 90 xscaled -1; fi|
+| if unknown scale: scale := max(1,round(pixels_per_inch/300)); fi|
+| pix_wd := pix_ht := 4scale;|
+| if rep>1: picture pix;|
+| currentpicture := currentpicture shifted-(1,1); pix := currentpicture;|
+| for r=1 upto rep-1: addto currentpicture also pix shifted(4r,0); endfor|
+| cullit; pix := currentpicture;|
+| for r=1 upto rep-1: addto currentpicture also pix shifted(0,4r); endfor|
+| unfill unitsquare xscaled 4rep yscaled 2 shifted-(1,1);|
+| unfill unitsquare yscaled 4rep xscaled 2 shifted-(1,1); cullit; fi|
+| picture pix_picture; pix_picture := currentpicture scaled scale;|
+| pix_wd := pix_ht := 4scale*rep; fi|
+\weakendlines
+The lightweight pattern has 4 of every 16 pixels turned on; the normal
+pattern has twice as many.
+
+Character 0 is the dot, which is quite simple:
+\beginlines
+|def # = *72.27/pixels_per_inch enddef;|
+|if unknown dotsize: dotsize := 2.5pix_wd/rep; fi|
+\smallskip
+|beginchar(0,1.2dotsize#,1.2dotsize#,0);|
+|fill fullcircle scaled dotsize scaled mg; endchar;|
+\endlines
+
+The special coding scheme of gray fonts is implemented next:
+\beginlines
+|numeric a[]; newinternal b,k;|
+|def next_binary =|
+| k := 0; forever: if k>b: a[incr b] := 0; fi|
+| exitif a[k]=0; a[k] := 0; k := k+1; endfor|
+| a[k] := 1 enddef;|
+|def next_special_binary =|
+| if a[0]=1: for k=0 upto b: a[k] := 0; endfor a[incr b]|
+| else: k := 0; forever: exitif a[incr k]=1; endfor|
+| a[k-1] fi := 1 enddef;|
+\smallbreak
+|def make_char =|
+| clearit; next_binary;|
+| for k=0 upto b: if a[k]=1:|
+| addto currentpicture also pix_picture shifted(0,-k*pix_ht); fi endfor|
+| charcode := charcode+1; chardp := b*charht;|
+| scantokens extra_endchar; shipout currentpicture enddef;|
+\endlines
+
+Now we are ready to generate all the pixel characters.
+^^@charlist@^^"chardx"^^"charwd"^^"charht"
+\beginlines
+|charwd := pix_wd#; charht := pix_ht#; chardx := pix_wd*mg;|
+|b := -1;|
+\smallskip
+|if boolean large_pixels:|
+| for k=1 upto 7: make_char; charlist k:k+120; endfor|
+| charcode := 120; b := -1;|
+| addto pix_picture also pix_picture shifted (chardx,0);|
+| charwd := 2charwd; chardx := 2chardx;|
+| for k=1 upto 7: make_char; endfor|
+|else: for k=1 upto 63: make_char; endfor|
+| let next_binary = next_special_binary;|
+| for k=64 upto 120: make_char; endfor|
+| for k=121,122: charcode := k;|
+| addto currentpicture also currentpicture shifted (chardx,0);|
+| charwd := 2charwd; chardx := 2chardx;|
+| scantokens extra_endchar; shipout currentpicture; endfor|
+| charlist 120:121:122; fi|
+\endlines
+
+The program closes by establishing fontwide parameters:
+\beginlines
+|font_coding_scheme "GFGRAY";|
+|font_size 8(pix_wd#);|
+|font_normal_space pix_wd#;|
+|font_x_height pix_ht#;|
+|font_quad pix_wd#;|
+|fontdimen 8: if known rulethickness: rulethickness|
+| else: pix_wd#/(2rep) fi;|
+|bye.|
+\weakendlines
+(The extra complications of an ^"aspect\_ratio" or a slant have not
+been addressed.)
+
+\subsection ^{Slant fonts}. |GFtoDVI| also makes use of another special
+type of font, if it is necessary to typeset slanted rules. The format of
+such so-called ``slant fonts'' is quite a bit simpler than the format of
+gray fonts.
+
+A slant font contains exactly $n$ characters, in positions 1 to~$n$,
+for some positive integer~$n$.
+The character in position~$k$ represents a slanted line $k$ units
+tall, starting at the baseline. These lines all have a fixed slant ratio~$s$.
+The vertical ``unit'' is usually chosen to be an integral number of pixels,
+small enough so that it suffices to draw rules that are an
+integer number of units high; in fact, it should probably be no larger
+than the thickness of the rules being drawn.
+
+The following simple algorithm is used to typeset a rule that is $m$ units
+high: Compute $q=\lceil m/n\rceil$; then typeset $q$~characters of
+approximately equal size, namely $(m\bmod q)$ copies of character number
+$\lceil m/q\rceil$ and $q-(m\bmod q)$ copies of character number
+$\lfloor m/q\rfloor$. For example, if $n=15$ and $m=100$, we have $q=7$;
+a 100-unit-high rule will be composed of 7~pieces, using characters
+14,~14, 14, 14, 14, 15,~15.
+
+|GFtoDVI| looks at the ^"charht" of character $n$ only, so the |tfm| file
+need not be accurate about the heights of the other characters. \ (This is
+fortunate, since ^|tfm| format allows at most 15 different nonzero
+heights per font.)
+
+The ^"charwd" of character~$k$ should be $k/n$ times $s$ times the "charht"
+of~$n$.
+
+The ^@font\_slant@ parameter should be $s$. It is customary to
+set the parameter @fontdimen@~8 to the thickness of
+the slanted rules, but |GFtoDVI| doesn't look at it.
+
+Here's an example of a slant-font parameter file, `|slantcheap6|',
+for the "cheapo" printer and a slant of 1/6:
+\beginlines
+|% Slant font for Cheapo with slope 1/6|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|s=1/6; % the slant ratio|
+|n=30; % the number of characters|
+|r#=.4pt#; % thickness of the rules|
+|u=1; % vertical unit|
+\smallskip
+|font_identifier "SLANTCHEAP6";|
+|input slant|
+\endlines
+The corresponding program file `|slant.mf|' looks like this:
+\beginlines
+|% More-or-less general slant font generator for GFtoDVI|
+|% The calling file should set the font_identifier and|
+|% n = number of characters|
+|% s = slant ratio|
+|% r# = rule thickness (in sharp units)|
+|% u = vertical unit (in pixels)|
+\smallskip
+|if unknown mag: mag := 1;|
+|elseif (mag<1) or (mag<>floor mag):|
+| errmessage "Sorry, mag must be a positive integer"; mag := 1; fi|
+\smallbreak
+|mg := mag; mag := 1; mode_setup; u# := u*72.27/pixels_per_inch;|
+|pixels_per_inch := pixels_per_inch*mg; fix_units;|
+\smallbreak
+|define_whole_pixels(u); define_blacker_pixels(r);|
+|pickup pencircle scaled r; ruler := savepen;|
+\smallbreak
+|for k=1 upto n:|
+| beginchar(k,k*u#*s,n*u#,0);|
+| pickup ruler; draw origin--(k*u*s,k*u); % draw the line|
+| unfill (lft-1,bot -1)--(rt 1,bot -1)|
+| --(rt 1,0)--(lft-1,0)--cycle; % clip the ends|
+| unfill ((lft -1,0)--(rt 1,0)|
+| --(rt 1,top 1)--(lft -1,top 1)--cycle) shifted (k*u*s,k*u);|
+| endchar; endfor|
+\smallbreak
+|font_size 16pt#;|
+|font_slant s;|
+|fontdimen 8: r#;|
+|font_coding_scheme "GFSLANT";|
+|bye.|
+\endlines
+
+\subsection Font samples. The real test of a font is its appearance
+at the final size, after it has actually been typeset. The \TeX\
+typesetting system can be used with the following example macro file
+`^|testfont.tex|' (in addition to plain \TeX\ format) to
+put a new font through its paces.
+
+We shall comment on typical uses of |testfont| as we examine its parts.
+At the beginning, |testfont.tex| turns off several of \TeX's normal features.
+\beginlines
+|% A testbed for font evaluation|
+\smallskip
+|\tracinglostchars=0 % missing characters are OK|
+|\tolerance=1000 % and so are loose lines|
+|\raggedbottom % pages can be short|
+|\nopagenumbers % and they won't be numbered|
+|\parindent=0pt % nor will paragraphs be indented|
+|\hyphenpenalty=200 % hyphens are discouraged|
+|\doublehyphendemerits=30000 % and two in a row are terrible|
+\smallskip
+|\newlinechar=`@ % we want to type multiline messages|
+|\chardef\other=12 % and redefine "catcodes"|
+\smallskip
+|\newcount\m \newcount\n \newcount\p \newdimen\dim % temporary variables|
+\endlines
+
+Then there are macros to print the time and date---an extremely valuable
+thing to have on any proofsheet.
+\beginlines
+|\def\today{\ifcase\month\or|
+| January\or February\or March\or April\or May\or June\or|
+| July\or August\or September\or October\or November\or December\fi|
+| \space\number\day, \number\year}|
+|\def\hours{\n=\time \divide\n 60|
+| \m=-\n \multiply\m 60 \advance\m \time|
+| \twodigits\n\twodigits\m}|
+|\def\twodigits#1{\ifnum #1<10 0\fi \number#1}|
+\endlines
+
+An online ``menu'' of the available test routines will be typed at your
+terminal if you request |\help|.
+\beginlines
+|{\catcode`\|\||=0 \catcode`\\=\other % use |\|| as the escape, temporarily|
+\||gdef|\||help{|\||message{%|
+|\init switches to another font;@%|
+|\end or \bye finishes the run;@%|
+|\table prints the font layout in tabular format;@%|
+|\text prints a sample text, assuming TeX text font conventions;@%|
+|\sample combines \table and \text;@%|
+|\mixture mixes a background character with a series of others;@%|
+|\alternation interleaves a background character with a series;@%|
+|\alphabet prints all lowercase letters within a given background;@%|
+|\ALPHABET prints all uppercase letters within a given background;@%|
+|\series prints a series of letters within a given background;@%|
+|\lowers prints a comprehensive test of lowercase;@%|
+|\uppers prints a comprehensive test of uppercase;@%|
+|\digits prints a comprehensive test of numerals;@%|
+|\math prints a comprehensive test of TeX math italic;@%|
+|\names prints a text that mixes upper and lower case;@%|
+|\punct prints a punctuation test;@%|
+|\bigtest combines many of the above routines;@%|
+|\help repeats this message;@%|
+|and you can use ordinary TeX commands (e.g., to \input a file).}}}|
+\endlines
+
+The program prompts you for a font name. If the font is in your local
+directory instead of a system directory, you might have to
+specify the directory name as part of the font name. You should
+also specify scaling if the font has been magnified, as in the example
+of Chapter~5. Several fonts can be tested during a single run, if you
+say `|\init|' before `|\end|'.
+\beginlines
+|\def\init{\message{@Name of the font to test = }|
+| \read-1 to\fontname \startfont|
+| \message{Now type a test command (\string\help\space for help):}}|
+|\def\startfont{\font\testfont=\fontname \spaceskip=0pt|
+| \leftline{\sevenrm Test of \fontname\unskip\ on \today\ at \hours}|
+| \medskip|
+| \testfont \setbaselineskip|
+| \ifdim\fontdimen6\testfont<10pt \rightskip=0pt plus 20pt|
+| \else\rightskip=0pt plus 2em \fi|
+| \spaceskip=\fontdimen2\testfont % space between words (\raggedright)|
+| \xspaceskip=\fontdimen2\testfont|
+| \advance\xspaceskip by\fontdimen7\testfont}|
+\endlines
+The specified font will be called |\testfont|. As soon as you have
+specified it, |\init| calls on |\startfont|, which puts a title line
+on the page; then it chooses what it hopes will be a good distance between
+baselines, and gets ready to typeset text with ``^{ragged right}'' margins.
+\ (The code above improves on plain \TeX's ^|\raggedright|.)
+
+The baselineskip distance is taken to be $6\pt$ plus the height of the
+tallest character plus the depth of the deepest character. This is the
+distance between baselines for ``series'' tests, but it is decreased
+by $4\pt$ when the sample text is set. If you
+want to change the baseline distance chosen by |testfont|,
+you can just say, e.g., `|\baselineskip=11pt|'.
+\beginlines
+|\def\setbaselineskip{\setbox0=\hbox{\n=0|
+|\loop\char\n \ifnum \n<255 \advance\n 1 \repeat} % 256 chars in \box0|
+|\baselineskip=6pt \advance\baselineskip\ht0 \advance\baselineskip\dp0 }|
+\endlines
+
+When |testfont| prompts you for a ``^{background character}''
+or a ``^{starting character}'' or an ``^{ending character},'' you
+can type the character you want (assuming ASCII code);
+or you can say, e.g., `|#35|' to get character code number 35.
+Codes 0--32 and 127--255 have to be specified with the `|#|' option,
+on non-fancy installations of \TeX,
+and so does code 35 (which is the ASCII code of `|#|' itself).
+\beginlines
+|\def\setchar#1{{\escapechar-1\message{\string#1 character = }%|
+| \def\do##1{\catcode`##1=\other}\dospecials|
+| \read-1 to\next|
+| \expandafter\finsetchar\next\next#1}}|
+|\def\finsetchar#1#2\next#3{\global\chardef#3=`#1|
+| \ifnum #3=`\# \global\chardef#3=#2 \fi}|
+|\def\promptthree{\setchar\background|
+| \setchar\starting \setchar\ending}|
+\endlines
+(The \TeX\ hackery here is a bit subtle, because special characters
+like `|\|' and `|$|' must temporarily lose their special significance.)
+
+Suppose the background character is `|o|' and the starting and ending
+characters are respectively `|p|' and~`|q|'. Then the ^|\mixture|
+operation will typeset `|opooppooopppop|' and `|oqooqqoooqqqoq|';
+the ^|\alternation| operation will typeset `|opopopopopopopopo|' and
+`|oqoqoqoqoqoqoqoqo|'. Other patterns could be added in a similar way.
+\beginlines
+|\def\mixture{\promptthree \domix\mixpattern}|
+|\def\alternation{\promptthree \domix\altpattern}|
+|\def\mixpattern{\0\1\0\0\1\1\0\0\0\1\1\1\0\1}|
+|\def\altpattern{\0\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\0}|
+|\def\domix#1{\par\chardef\0=\background \n=\starting|
+| \loop \chardef\1=\n #1\endgraf|
+| \ifnum \n<\ending \advance\n 1 \repeat}|
+\endlines
+
+The |\series| operation puts the background character between all the
+others (e.g., `|opoqo|'). Special series containing the lowercase
+letters of \TeX\ text fonts (including `\char'31', `\char'32',
+`\char'33', and ~\char'34') and the uppercase letters (including
+`\char'35', `\char'36', and~`\char'37') are provided.
+Although |\mixture| and |\alternation| show you the effects of
+ligatures and kerning, |\series| does not.
+\beginlines
+|\def\!{\discretionary{\background}{\background}{\background}}|
+|\def\series{\promptthree \!\doseries\starting\ending\par}|
+|\def\doseries#1#2{\n=#1\loop\char\n\!\ifnum\n<#2\advance\n 1 \repeat}|
+|\def\complower{\!\doseries{`a}{`z}\doseries{'31}{'34}\par}|
+|\def\compupper{\!\doseries{`A}{`Z}\doseries{'35}{'37}\par}|
+|\def\compdigs{\!\doseries{`0}{`9}\par}|
+|\def\alphabet{\setchar\background\complower}|
+|\def\ALPHABET{\setchar\background\compupper}|
+\endlines
+(A long series might fill more than one line; \TeX's |\discretionary|
+break operation is used here so that the background character will end
+the line and be repeated at the beginning of the next.)
+
+A ``comprehensive'' test uses a series of background characters
+against a series of others. The series will consist of lowercase
+letters (`^|\lowers|'), uppercase letters (`^|\uppers|'), or
+numerals (`^|\digits|').
+\beginlines
+|\def\lowers{\docomprehensive\complower{`a}{`z}{'31}{'34}}|
+|\def\uppers{\docomprehensive\compupper{`A}{`Z}{'35}{'37}}|
+|\def\digits{\docomprehensive\compdigs{`0}{`4}{`5}{`9}}|
+|\def\docomprehensive#1#2#3#4#5{\par\chardef\background=#2|
+| \loop{#1} \ifnum\background<#3\m=\background\advance\m 1|
+| \chardef\background=\m \repeat \chardef\background=#4|
+| \loop{#1} \ifnum\background<#5\m=\background\advance\m 1|
+| \chardef\background=\m \repeat}|
+\endlines
+
+The ^|\names| test puts uppercase letters and accents
+together with lowercase letters. The accents will look funny
+if the test font doesn't have them in plain \TeX's favorite positions.
+\beginlines
+|\def\names{ {\AA}ngel\aa\ Beatrice Claire|
+| Diana \'Erica Fran\c{c}oise Ginette H\'el\`ene Iris|
+| Jackie K\=aren {\L}au\.ra Mar{\'\i}a N\H{a}ta{\l}{\u\i}e {\O}ctave|
+| Pauline Qu\^eneau Roxanne Sabine T\~a{\'\j}a Ur\v{s}ula|
+| Vivian Wendy Xanthippe Yv{\o}nne Z\"azilie\par}|
+\endlines
+
+Punctuation marks are tested in juxtaposition with different
+sorts of letters, by the `^|\punct|' macro:
+\beginlines
+|\def\punct{\par\dopunct{min}\dopunct{pig}\dopunct{hid}|
+| \dopunct{HIE}\dopunct{TIP}\dopunct{fluff}|
+| \$1,234.56 + 7/8 = 9\% @ \#0\par}|
+|\def\dopunct#1{#1,\ #1:\ #1;\ `#1'\|
+| ?||`#1?\ !||`#1!\ (#1)\ [#1]\ #1*\ #1.\par}|
+\endlines
+
+Mixtures and alternations and series are excellent ways to discover
+that letters are too dark, too light, or too tightly spaced. But
+a font also has to be readable; in fact, this is the number one
+objective. So |testfont| provides a sample `^|\text|'. One of the sentences
+is optional, because it contains lots of accents and unusual letters;
+you can omit it from the text by saying `^|\omitaccents|'.
+Furthermore, you can type your own text, online, or you can input one from
+^^{Stanfords} ^^{Kafka} ^^{AEsop}
+a file, instead of using this canned example.
+\beginlines
+|\def\text{{\advance\baselineskip-4pt|
+|\setbox0=\hbox{abcdefghijklmnopqrstuvwxyz}|
+|\ifdim\hsize>2\wd0 \ifdim 15pc>2\wd0 \hsize=15pc \else\hsize=2\wd0 \fi\fi|
+|On November 14, 1885, Senator \& Mrs.~Leland Stanford called together at|
+|their San Francisco mansion the 24~prominent men who had been chosen as|
+|the first trustees of The Leland Stanford Junior University. They|
+|handed to the board the Founding Grant of the University, which they had|
+|executed three days before. This document---with various amendments,|
+|legislative acts, and court decrees---remains as the University's|
+|charter. In bold, sweeping language it stipulates that the objectives of|
+|the University are ``to qualify students for personal success and direct|
+|usefulness in life; and to promote the publick welfare by exercising an|
+|influence in behalf of humanity and civilization, teaching the blessings|
+|of liberty regulated by law, and inculcating love and reverence for the|
+|great principles of government as derived from the inalienable rights of|
+|man to life, liberty, and the pursuit of happiness.'' \moretext|
+|(!||`THE DAZED BROWN FOX QUICKLY GAVE 12345--67890 JUMPS!)\par}}|
+|\def\moretext{?||`But aren't Kafka's Schlo{\ss} and {\AE}sop's {\OE}uvres|
+|often na{\"\i}ve vis-\`a-vis the d{\ae}monic ph{\oe}nix's official|
+|r\^ole in fluffy souffl\'es? }|
+|\def\omitaccents{\let\moretext=\relax}|
+\endlines
+
+Now comes one of the hardest parts of the file, from the \TeX\
+standpoint: The |\table| macro prints a font diagram, omitting
+groups of sixteen characters that are entirely absent from the font.
+The format of this table is the same as that used in Appendix~F
+of {\sl The \TeX book}. When the font contains unusually large characters
+that ought to be vertically centered, you should say `^|\centerlargechars|'
+before `|\table|'. \ (A \TeX\ math symbol font or math extension font
+would use this feature.)
+\beginlines
+|\def\oct#1{\hbox{\rm\'{}\kern-.2em\it#1\/\kern.05em}} % octal constant|
+|\def\hex#1{\hbox{\rm\H{}\tt#1}} % hexadecimal constant|
+|\def\setdigs#1"#2{\gdef\h{#2}% \h=hex prefix; \0\1=corresponding octal|
+| \m=\n \divide\m by 64 \xdef\0{\the\m}%|
+| \multiply\m by-64 \advance\m by\n \divide\m by 8 \xdef\1{\the\m}}|
+|\def\testrow{\setbox0=\hbox{\penalty 1\def\\{\char"\h}%|
+| \\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\A\\B\\C\\D\\E\\F%|
+| \global\p=\lastpenalty}} % \p=1 if none of the characters exist|
+|\def\oddline{\cr|
+| \noalign{\nointerlineskip}|
+| \multispan{19}\hrulefill&|
+| \setbox0=\hbox{\lower 2.3pt\hbox{\hex{\h x}}}\smash{\box0}\cr|
+| \noalign{\nointerlineskip}}|
+|\newif\ifskipping|
+|\def\evenline{\loop\skippingfalse|
+| \ifnum\n<256 \m=\n \divide\m 16 \chardef\next=\m|
+| \expandafter\setdigs\meaning\next \testrow|
+| \ifnum\p=1 \skippingtrue \fi\fi|
+| \ifskipping \global\advance\n 16 \repeat|
+| \ifnum\n=256 \let\next=\endchart\else\let\next=\morechart\fi|
+| \next}|
+|\def\morechart{\cr\noalign{\hrule\penalty5000}|
+| \chartline \oddline \m=\1 \advance\m 1 \xdef\1{\the\m}|
+| \chartline \evenline}|
+|\def\chartline{&\oct{\0\1x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&}|
+|\def\chartstrut{\lower4.5pt\vbox to14pt{}}|
+|\def\table{$$\global\n=0|
+| \halign to\hsize\bgroup|
+| \chartstrut##\tabskip0pt plus10pt&|
+| &\hfil##\hfil&\vrule##\cr|
+| \lower6.5pt\null|
+| &&&\oct0&&\oct1&&\oct2&&\oct3&&\oct4&&\oct5&&\oct6&&\oct7&\evenline}|
+|\def\endchart{\cr\noalign{\hrule}|
+| \raise11.5pt\null&&&\hex 8&&\hex 9&&\hex A&&\hex B&|
+| &\hex C&&\hex D&&\hex E&&\hex F&\cr\egroup$$\par}|
+|\def\:{\setbox0=\hbox{\noboundary\char\n\noboundary}%|
+| \ifdim\ht0>7.5pt\reposition|
+| \else\ifdim\dp0>2.5pt\reposition\fi\fi|
+| \box0\global\advance\n 1 }|
+|\def\reposition{\setbox0=\vbox{\kern2pt\box0}\dim=\dp0|
+| \advance\dim 2pt \dp0=\dim}|
+|\def\centerlargechars{|
+| \def\reposition{\setbox0=\hbox{$\vcenter{\kern2pt\box0\kern2pt}$}}}|
+\endlines
+
+Two of the most important combinations of tests are treated now:
+^|\sample| prints the |\table| and the |text|; ^|\bigtest| gives
+you the works, plus a mysterious word that is traditional in type
+specimens: ^^{hamburgefonstiv}
+\beginlines
+|\def\sample{\table\text}|
+\smallskip
+|\def\bigtest{\sample|
+| hamburgefonstiv HAMBURGEFONSTIV\par|
+| \names \punct \lowers \uppers \digits}|
+\endlines
+
+Finally, there's a |\math|
+routine useful for checking out the spacing in the ^{math} ^{italic}
+fonts used by plain \TeX; |\mathsy| does a similar thing for the
+uppercase letters in a math symbols font.
+\beginlines
+|\def\math{\textfont1=\testfont \skewchar\testfont=\skewtrial|
+| \mathchardef\Gamma="100 \mathchardef\Delta="101|
+| \mathchardef\Theta="102 \mathchardef\Lambda="103 \mathchardef\Xi="104|
+| \mathchardef\Pi="105 \mathchardef\Sigma="106 \mathchardef\Upsilon="107|
+| \mathchardef\Phi="108 \mathchardef\Psi="109 \mathchardef\Omega="10A|
+| \def\ii{i} \def\jj{j}|
+| \def\\##1{|\||##1|\||+}\mathtrial|
+| \def\\##1{##1_2+}\mathtrial|
+| \def\\##1{##1^2+}\mathtrial|
+| \def\\##1{##1/2+}\mathtrial|
+| \def\\##1{2/##1+}\mathtrial|
+| \def\\##1{##1,{}+}\mathtrial|
+| \def\\##1{d##1+}\mathtrial|
+| \let\ii=\imath \let\jj=\jmath \def\\##1{\hat##1+}\mathtrial}|
+|\newcount\skewtrial \skewtrial='177|
+|\def\mathtrial{$\\A \\B \\C \\D \\E \\F \\G \\H \\I \\J \\K \\L \\M \\N|
+| \\O \\P \\Q \\R \\S \\T \\U \\V \\W \\X \\Y \\Z \\a \\b \\c \\d \\e \\f|
+| \\g \\h \\\ii \\\jj \\k \\l \\m \\n \\o \\p \\q \\r \\s \\t \\u \\v \\w|
+| \\x \\y \\z \\\alpha \\\beta \\\gamma \\\delta \\\epsilon \\\zeta|
+| \\\eta \\\theta \\\iota \\\kappa \\\lambda \\\mu \\\nu \\\xi \\\pi|
+| \\\rho \\\sigma \\\tau \\\upsilon \\\phi \\\chi \\\psi \\\omega|
+| \\\vartheta \\\varpi \\\varphi \\\Gamma \\\Delta \\\Theta \\\Lambda|
+| \\\Xi \\\Pi \\\Sigma \\\Upsilon \\\Phi \\\Psi \\\Omega|
+| \\\partial \\\ell \\\wp$\par}|
+|\def\mathsy{\begingroup\skewtrial='060 % for math symbol font tests|
+| \def\mathtrial{$\\A \\B \\C \\D \\E \\F \\G \\H \\I \\J \\K \\L|
+| \\M \\N \\O \\P \\Q \\R \\S \\T \\U \\V \\W \\X \\Y \\Z$\par}|
+| \math\endgroup}|
+\endlines
+
+The last line of |testfont| is
+\beginlines
+|\ifx\noinit!\else\init\fi|
+\endlines
+and it means ``automatically call `^|\init|' unless `|\noinit|' is
+an exclamation point.'' Why this? Well,
+you might have your own test file from which you'd like to use the
+facilities of |testfont|, without typing commands online.
+If your file says `|\let\noinit!| |\input testfont|' \TeX\ will
+read in |testfont| but the routine will not prompt you for a file name.
+The file can then continue to test one or more fonts by saying, e.g.,
+\beginlines
+|\def\fontname{cmbx10 }\startfont\sample\vfill\eject|
+|\def\fontname{cmti10 scaled \magstep3}\startfont\sample\vfill\eject|
+\endlines
+thereby defining ^|\fontname| directly, and using ^|\startfont|
+to do the initialization instead of\/ |\init|.
+
+\medbreak
+To conclude this appendix, let's look at the listing of a file
+that can be used to test special constructions in math fonts
+with the conventions of plain \TeX:
+\beginlines
+|\raggedright \rightskip=2em plus 5em minus 2em|
+\smallbreak
+|$\hbar \not\equiv B$, but $\sqrt C \mapsto \sqrt x$,|
+|$Z \hookrightarrow W$, $Z \hookleftarrow W$,|
+|$Z \longmapsto W$, $Z \bowtie W$, $Z \models W$,|
+|$Z \Longrightarrow W$, $Z \longrightarrow W$,|
+|$Z \longleftarrow W$, $Z \Longleftarrow W$,|
+|$Z \longleftrightarrow W$, $Z \Longleftrightarrow W$,|
+|$\overbrace{\hbox{very long things for testing}}$,|
+|$\underbrace{\hbox{very long things for testing}}$,|
+|$Z \choose W$, $Z \brack W$, $Z \brace W$, $Z \sqrt W$,|
+|$Z \cong W$, $Z \notin W$, $Z \rightleftharpoons W$,|
+|$\widehat Z$, $\widehat{ZW}$, $\widehat{Z+W}$,|
+|$\widetilde Z$, $\widetilde{ZW}$, $\widetilde{Z+W}$.|
+\smallbreak
+|\def\sizetest#1#2{$$|
+| \Bigggl{#1}\bigggl{#1}\Biggl{#1}\biggl{#1}\Bigl{#1}\bigl{#1}\left#1|
+| \bullet|
+| \right#2\bigr{#2}\Bigr{#2}\biggr{#2}\Biggr{#2}\bigggr{#2}\Bigggr{#2}$$}|
+|\def\biggg#1{{\hbox{$\left#1\vbox to20.5pt{}\right.$}}}|
+|\def\bigggl{\mathopen\biggg} \def\bigggr{\mathclose\biggg}|
+|\def\Biggg#1{{\hbox{$\left#1\vbox to23.5pt{}\right.$}}}|
+|\def\Bigggl{\mathopen\Biggg} \def\Bigggr{\mathclose\Biggg}|
+\smallbreak
+|\sizetest () \sizetest [] \sizetest \lgroup\rgroup|
+|\sizetest \lmoustache\rmoustache \sizetest \vert\Vert|
+|\sizetest \arrowvert\Arrowvert \sizetest \uparrow\downarrow|
+|\sizetest \updownarrow\Updownarrow \sizetest \Uparrow\Downarrow|
+|\sizetest \bracevert{\delimiter"342} \sizetest \backslash/|
+|\sizetest \langle\rangle \sizetest \lbrace\rbrace|
+|\sizetest \lceil\rceil \sizetest \lfloor\rfloor|
+\smallbreak
+|$$\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{-1}}}}}}}}}$$|
+\smallbreak
+|\def\dobig{\do\bigvee \do\bigwedge \do\bigotimes \do\bigoplus \do\bigodot|
+| \do\bigcap \do\bigcup \do\biguplus \do\bigsqcup|
+| \do\int \do\ointop \do\smallint \do\prod \do\coprod \do\sum}|
+|\def\do#1{#1_a^b A} $\dobig$ $$\dobig$$|
+\smallbreak
+|\bye|
+\endlines
+
+\endchapter
+
+Be sure of it: Giue me the Occular proofe.
+\author WILLIAM ^{SHAKESPEARE}, {\sl Othello\/} (1604) % act 3 sc 3 l 360
+
+\bigskip
+
+The figure itself appears here
+as a very necessary adjunct to the verbalization.
+In Euclid's presentation we cannot wholly follow the argumentation
+without the figure, and unless we are strong enough
+to imagine the figure in our mind's eye, we would also be reduced
+to supplying our own figure if the author had not done it for us.
+Notice also that the language of the proof has a
+formal and severely restricted quality about it.
+This is not the language of history, nor of drama,
+nor of day to day life;
+this is language that has been sharpened and refined so as to serve
+the precise needs of a precise but limited intellectual goal.
+\author P. J. ^{DAVIS} and R. ^{HERSH}, {\sl Proof\/} (1981)
+ % The Mathematical Experience (Birkh\"auser), near p150
+
+\eject
+ \beginchapter Appendix I. Index
+
+\maxdepth=4pt
+The author has tried to provide as complete an index as possible, so that
+people will be able to find things that are tucked away in obscure
+corners of this long book. Therefore the index itself is rather long. A
+short summary of the simpler aspects of \MF\ appears at the beginning of
+Appendix~B; a summary of the standard character classes for tokens
+can be found at the end of Chapter~6; a summary of other special things
+appears under `tables' below.
+
+\medskip\ninepoint
+Page numbers are \underbar{underlined} in the index when they represent
+the definition or the main source of information about whatever is being
+indexed. \ (Underlined entries are the most definitive, but not
+necessarily the easiest for a beginner to understand.) \ A page number is
+given in italics (e.g., `{\it123\/}') when that page contains an instructive
+example of how the concept in question might be used. Sometimes both
+underlining and italics are appropriate. When an index entry refers to a
+page containing a relevant exercise, the answer to that exercise (in
+Appendix~A) might divulge further information; an answer page is not
+indexed here unless it refers to a topic that isn't included in the
+statement of the relevant exercise.
+
+\smallskip
+Index entries for quoted symbols like `T' refer to example programs
+that draw the symbols in question.
+
+\smallskip
+Symbolic tokens that are preceded by an asterisk (*) in this index are
+primitives of \MF; i.e., they are built in. It may be dangerous to
+redefine them.
+
+\begindoublecolumns
+\eightpoint \baselineskip=9.9pt % shooting for 53 lines/page
+\parskip=0pt plus .8pt
+\newdimen\sqht \sqht=2.4pt % \square
+\newbox\astbox \setbox\astbox=\hbox to0pt{\hss\lower1pt\hbox{*}}
+\raggedright \tolerance=5000 \hbadness=5000 \parfillskip 0pt plus 3em
+\ttglue=.4em
+\def\<#1>{\leavevmode\hbox{$\mkern-2mu\langle$#1\/$\rangle$}}
+\let\oldcstok=\cstok
+\def\cstok{\leavevmode\kern-2pt\oldcstok}
+\def\vdots{\vbox{\baselineskip 3pt\kern2pt\hbox{.}\hbox{.}\hbox{.}}}
+\def\ddots{\mathinner{\mskip1mu\raise5pt\vbox{\kern2pt\hbox{.}}\mskip2mu
+ \raise3pt\hbox{.}\mskip2mu\raise1pt\hbox{.}\mskip1mu}}
+\def\see{{\sl see\/}~\ignorespaces}
+\def\also{\hfil\penalty50\hfilneg{\sl see~also\/}~\ignorespaces}
+\let\oldttv=\ttverbatim
+\let\+=\relax
+\def\sub{\penalty100 \vskip -\parskip \quad}
+\def\LaTeX{L\kern -.36em\raise.6ex\hbox{\sixrm A}\kern-.15em\TeX}
+\def\MF{{\manual opqrstuq}}
+\def\ttverbatim{\oldttv \catcode`\*=\other \catcode`\,=\other
+ \catcode`\.=\other \catcode`\;=\other \catcode`\@=\other \catcode`\+=\other}
+\let\comma=, \let\period=. \let\asterisk=* \let\semicolon=;
+\def\backup{\leavevmode\kern-1pt}\catcode`\-=11
+\catcode`\*=\active \def*{\leavevmode\copy\astbox}
+\catcode`\,=\active \def,{\eightrm\comma}
+\catcode`\;=\active \def;{\/\eightrm\semicolon}
+\catcode`\.=\active \def.{\eightrm\period\par\hangindent 2em }
+\catcode`\+=\active \def+#1{\ifcat0\noexpand#1$\setbox0\hbox{#1}\dp0=0pt
+ \underline{\box0}$\let\next=+%
+ \else\let\next=#1\fi \next}
+\catcode`\@=\active \let@=\eightit
+\parindent=0pt
+\hyphenpenalty=10000 \exhyphenpenalty=10000
+\def\newletter{\medbreak\hangindent 2em}
+\hangindent 2em
+|6test.mf|, 312--313.
+|#| (hash mark), \see sharped dimensions.
+`\#', 200--201.
+|##| (traced equation), 80--83, 239.
+|###| (removed independent variable), 83.
+|####| (deduced equation), 81.
+*|#@| (prefix of at point), +177, @251.
+|%| (percent sign), 43, +50.
+*|&| (ampersand), 213--214, \see concatenation.
+\sub for preloaded bases, +35, 279.
+|'| (apostrophe or prime), @25, @55, @81.
+|"| (double-quote mark), +50--+51.
+|""| (empty string), +188, 236, 254, @276, @294, @328.
+`(', 103--105, 128, 318.
+|(| (left parenthesis), 59, +60, 61, @62--@63, 71--73, 165, 210--215.
+|((|, 51.
+|)| (right parenthesis), 59, +60, 61, @62--@63, 71--73, 165, 210--215.
+|))|, 51.
+*|[| (left bracket), 9--10, +54, 55, 60, +72, 80, 211--212, 298--299, 324.
+|[[|, 61.
+|[]| (collective subscript), +56, 177, @273.
+|[1]| (progress report), 37, +324.
+*|]| (right bracket), 9--10, +54, 55, 60, +72, 80, 211--212, 298--299, 324.
+|]]|, 61, @162, +262, 299.
+*|{| (left brace), 16--18, 60, +129, 213.
+|{{|, 61, 289.
+*|}| (right brace), 16--18, 60, +129, 213.
+|}}|, 61, 289.
+*|+| (plus sign), @62, @63, +72, 80, 211.
+*|++| (Pythagorean addition), @+66, @67, 72, 211.
+\sub (double edge), 117, 296--297.
+|+++| (triple edge), 296--297.
+*|+-+| (Pythagorean subtraction), @+66, 72, 211, @238.
+*|-| (minus sign), @62, @63, +72, 80, 211, 297.
+|--| (straight join), @24--@26, 127--129, @234, +262.
+\sub (double edge), 117, 296--297.
+|---| (tense join), @107, 127--129, +262.
+\sub (triple edge), 296--297.
+|->| (macro expansion), 44, 160, 249, 251.
+`---' (em dash), 306.
+|_| (underline), 49, +51, 173, 265, 270.
+|*| (asterisk), 285--286.
+\sub as prompt character, 31, 37, 279.
+\sub \llap{\char`\*}as times sign, @59, @62--@64, +72, +73, 80, 211--212.
+|**|, as command-line prompt, 31--32, 35--40, 187, 269, 279.
+\sub as exponentiation sign, @59, @64, 72, @237, @251, +265.
+|/| (slash), 328, 329.
+\sub \llap{\char`\*}as divided-by sign, @59, @62, @63, +72, 80, 82, 210--211.
+\| (vertical line), 117, 297.
+*|\| (backslash), +179, @236, @262.
+\sub at beginning of command line, @31, @38, 40.
+|\\|, +262.
+*|<| (less than sign), @64, @65, +170, 210, 237.
+*|<=| (less than or equal to), @64, 65, +170, 210, 282.
+|<-| (argument value), 160.
+*|<>| (unequal to), @64, 65, +170, 210, 282.
+\<> (angle brackets), 49--50.
+*|=| (equals sign), @5, @6, @23, @64, @75--@85, +88, 97, 165, 167, +170,
+ 171, 210, 218.
+|==|, 292.
+*|=:| (ligature replacement), @305, @306, @316, +317.
+*\||=:|, @316, +317.
+*\||=:>|, +317.
+*|=:|\|, +317.
+*|=:|\||>|, +317.
+*\||=:|\|, +317.
+*\||=:|\||>|, +317.
+*\||=:|\||>>|, +317.
+\leavevmode{\tt \rlap/=} (unequals sign), 282.
+*|>| (greater than sign), @64, +170, 210, 237.
+|>>| (shown value), 41, 62.
+*|>=| (greater than or equal to), @64, 65, +170, 210, 282.
+*|,| (comma), 57, 72, 73, 129, 155, 165--167, 171, 317, 318.
+|,,|\thinspace, 51.
+|.|~(period), 43, +50, 51.
+`\char`\.', 306.
+*|..| (free join), @7, @15--@19, @24, 127--133, 213.
+|...| (bounded join), 18--19, 127, 248, +262.
+|...| (truncation of displayed context), 44.
+*|;| (semicolon), 155, 169, 171, 172, 187, 217, 223--224, 263, 312.
+|;;|\thinspace, 51.
+*|:| (colon), 169, 171, 317--319.
+*|::| (local label), +317.
+*\|\||:| (left boundary label), +317.
+*|:=| (gets), @28, @33, 87, +88, 97, @98, 155--156, 159, 165, 167, 171,
+ 176, 218, 282.
+|?|, @41, +42--+43.
+|???|, @224, +262.
+|!| (exclamation point), 41, 189.
+*|@| (at point), +177, @251.
+*|@#| (suffix of at point), @176, +177, +178, 251, @273--@274.
+\newletter
+`a', 192.
+`A', 163, 164, 248, 302--303.
+|abort|, 312--313.
+|abs| (absolute value), @66, 82, @238, +264.
+accents, 315, 317.
+accuracy, 50, @62--@69, @143, 237.
+ad hoc dimensions, 92, @95.
+Adams, John, 359.
+addition of pictures, 115, @117, @245.
+addition of vectors, 9, @68.
+*|addto|, +118--+119, @144, @151, @242--@245.
+\<addto command>, +220.
+|adjust_fit|, 306--308.
+{\AE}schylus, 47.
+{\AE}sopus, 340.
+affine transformations, 247.
+algebraic operations, 59--73, 209--215, 230.
+Algol, 57, 89.
+Alingham, William, 189.
+Allen, Fred (= Sullivan, John Florence), 85.
+almost digitized character, 296.
+*|also|, +118, 220, @242--@245.
+|\alternation|, 338.
+alternatives, 169.
+|always_iff|, +307, @311--@312.
+ambiguous points, 150, 198--200, 204.
+American Mathematical Society, ii, ix.
+anatomy of \MF, 169, 179, 217, 285, +344.
+*|and|, @65, +129, +170, 210, 213, 288--289.
+Anderson, Izett William, 299.
+*|angle|, @29, @67, +72, @107, @135, 211, @238.
+angle brackets, 49--50.
+angle of pen, 21--22, 26--28, 152, 164.
+arccosine, arcsine, arctangent, \see |angle|.
+arguments, 159--160, +166--+167, 210, 288.
+arithmetic, 59--63.
+arrays, 54--57.
+ASCII, 49, 188, 281--283, 317.
+*|ASCII|, 72, +188, 211.
+|aspect_ratio|, 94, 145, 204, 269, 335.
+\<assignment>, +88.
+assignments, @28, @33, 87--89, @98, 159.
+*|at|, +191, 220, @252, @277, @312.
+at size, 96, 319.
+*|atleast|, 129, +132, 213, @262.
+*|autorounding|, 127, 195, +204--+205, @206, 212, @262, @264, 271--272.
+axis, 103.
+\newletter
+`b', 308.
+background character, @40, 338--339.
+Backus, John Warner, 49.
+backwards path, 119.
+|badio.mf|, 41, 223.
+|barheight|, 96, 161, 199, 302--303.
+base file, 34--35, 261, 278--279, +304, 307.
+baseline, 75--77, +101.
+\<basic path join>, 129, +213.
+*|batchmode|, +219, 226.
+BCPL strings, 320.
+bean-like shape, 15--16, 21--22, 24--25.
+beauty, v, 185.
+Beethoven, Ludwig van, 185.
+|beginchar|, 35, 76, @96, 102--103, 107, 115, 148, 156, 197, 199, 204,
+ +275, 316.
+*|begingroup|, +155--+157, 175, 178, 210--215, 217, @236, @243, @275, @289.
+|beginlogochar|, 160, 302.
+Bell, Eric Temple, 11.
+bell-shaped distribution, 251.
+Bernshte{\u\i}n, Serge{\u\i} \thinspace Natanovich, 14.
+\sub polynomials, 14, 133, 152, 246, 298--299.
+B\'ezier, Pierre Etienne, 14.
+Bibby, Duane Robert, i.
+Bierce, Ambrose Gwinnett, ix.
+|\bigtest|, +341.
+Billawala, Nazneen Noorudin, 266, 294.
+binary search, 176--177, @293--@294.
+black, 270, 332--333.
+black-letter, 294.
+black/white reversal, 115.
+|blacker|, 93--94, 268, +270--+271.
+|blankpicture|, 192, +263.
+Boole, George, 170.
+*|boolean|, 55, +56.
+\<boolean expression>, 170, +210.
+Boolean expressions, 170, 257.
+\<boolean primary>, 170, +210.
+\<boolean secondary>, 170, +210.
+\<boolean tertiary>, 170, +210.
+|bot|, @23, 80, 147, 151, 204, +273.
+boundaries, 24--29, 123--125.
+*|boundarychar|, 212, 317.
+bounded curves, 19, 132.
+bounding box, 22, 35, 76, +101--+107, 276, 307, 315.
+bounding triangle, 19, 132.
+box, \see bounding box.
+|bp| (big point), 92, +267, 268.
+braces, 16--18, 60, +129, 213.
+bracket notation, \see mediation.
+brackets, 9--10, +54, 55, 60, +72, 80, 211--212, 298--299, 324.
+broad-edge pens, 26--29, 151--152, 162--165.
+Bront\"e, Emily Jane, 73.
+Bruck, Richard Hubert, 29.
+buffer size, 226, 286.
+built-up symbols, 318.
+Burkitt, William, 99.
+Burns, Robert, 299.
+|bye|, +278, 279, @306, +321, 324.
+|byte|, +264, @275.
+\<byte list>, +318.
+\newletter
+$c$ code, 106, 324.
+Camden, William, 51.
+Campbell, John Campbell, 359.
+|cand|, 288--289.
+|CAPSULE|, 239.
+|capsule_def|, 264.
+capsules, 159, 166, 172, 210, 239, 247, 254, 264.
+Carter, Matthew, 207.
+Cartesian coordinates, 5--6, 191.
+|cc| (cicero), 92, +267, 268.
+|ceiling|, @65, 66, 72, +264.
+|\centerlargechars|, 340, +341.
+chance, 183--185.
+|change_width|, @199, +276, +309.
+*|char|, 187, +188, 214, @263.
+*|charcode|, 106, 210, 212, +220, @275, 324.
+*|chardp|, 106, 212, 220, @275, +315--+316, 324.
+*|chardx|, 106, 212, 220, @276, +324, @334.
+*|chardy|, 212, +324.
+*|charexists|, +106, 210, 316, 324.
+*|charext|, 106, 212, +220, 316, 324.
+*|charht|, 106, 212, 220, @275, +315--+316, 324, @334, @335.
+*|charic|, 106, 212, 220, @275, +315--+316, 324.
+*|charlist|, @317, +318, 331, @334, @335.
+\<charlist command>, +318.
+*|charwd|, 106, 212, 220, @275, +315--+316, 324, @334, @335.
+|cheapo|, 91--93, 99, 278--279, 332--333.
+check sums, 320, 324, +325.
+Chinese characters, 3, 106, 324.
+circles, 123--124, 148.
+|clear_pen_memory|, 147, +273, @278, @310.
+|clearit|, 115, @242, @275, +277, 295.
+|clearpen|, +272, @275.
+|clearxy|, @275, +277.
+|cm| (centimeter), @18, 92, +267, 268.
+|cm.base|, 35, 279, 311.
+|cmchar|, @306, +307, 312--313.
+|cmex10|, 317--318.
+|cmmf|, 35, 279.
+|cmr9|, 203, 320.
+|cmr10|, 101, 305--306, 319.
+|cmr10.mf|, 305.
+|cmsl10|, 101.
+|cmtt10|, 306.
+\<code>, +317.
+codes, 281--283.
+Colburn, Dorothy, 107.
+collective subscripts, 56, 177.
+\<command>, +217.
+command line, 38, 187, 269, 277, 301.
+commands, 155, 217--220, 230, 321.
+comments, 43, 50--51.
+commutativity, 247.
+comparison, @65--@66, 80, 170.
+compass directions, 26, 119, 206--207, 228--229.
+complex numbers, 69.
+\<compound>, +217.
+compound statement, +155, 217.
+Computer Modern, 35, 103--105, 203, 206, 279, 304--313.
+concatenation, of paths, @70--@71, @123, 127, 129, +130, @245.
+\sub of strings, @69, 84--85, +187, @278, @286, @312.
+\<condition>, +169.
+conditional and/or, 288--289.
+conditions, 169--171, 179, 219, 259.
+constants, 59, @62, 263--264.
+contents of this manual, table, x--xi.
+*|contour|, +118--+119, 220.
+control points, @13--@19, 70--71, 133, 229.
+*|controls|, @19, 70--71, +129--+130, 133, @152, 213.
+\<controls>, 129, +213.
+conversion to pixel units, 259, +268.
+convex polygons, @119, 147, 297--298.
+Conway, John Horton, 121.
+coordinates, 5--11, 23, 109, 191, 193.
+|cor|, 288--289.
+corner pixels, 93--94.
+*|cosd|, @67, 72, 211.
+cosines, 67, 69.
+counterclockwise, 111, 119, 229, 255.
+|counterclockwise|, +264.
+Cowper, William, 51.
+|craziness|, 184--185.
+crispness, 103--104.
+cube roots, 177.
+cubes, 113.
+*|cull|, 118, +120, @151, @243--@245.
+\<cull command>, 118, +220.
+|culldraw|, @271, +272.
+culling, 113, 120, @151, @242--@245, 296.
+|cullit|, @113, 120, @242, @243, +277.
+Cundall, Frank, 299.
+*|curl|, @17, +128--+131, 213, 234.
+|currentbreadth|, 310--311.
+|currentnull|, 295.
+|currentpen|, 118, 147, 150, 204, +271--+272.
+|currentpicture|, 114, @115, @116, 118, 120, 191, +271--+272, 295.
+|currenttransform|, 94, @145, 204, +269, 271, 301, 310.
+|currentwindow|, 192, @312.
+curves, 13--19, \see paths.
+cusps, 136.
+|cutdraw|, @151, +271--+272.
+|cutoff|, @150, +272.
+*|cycle|, @15, @16, @24--@28, @69, +129--+131, 170, 171, 210, 213.
+\newletter
+|d|, 35, @76, 102, 204, +275.
+`d', 294.
+da Vinci, Leonardo, 19.
+dangerous bend, vii, 11, 106--107, 115, 143.
+Darwin, Charles Robert, 57.
+data structures, 53--57.
+Davis, Philip Jacob, 343.
+*|day|, +212, 218, 323.
+|dd| (didot point), 92, +267, 268.
+de Casteljau, Paul de Faget, 14.
+debugging tricks, 229--231, 286.
+*|decimal|, +187--+188, 214.
+\<decimal digit>, +50.
+decimal point, 50--51.
+decimal representation, 188.
+\<declaration>, 56, +171.
+\<declaration list>, +57.
+declarations, 56--57.
+declarative versus imperative, 87.
+\<declared suffix>, +57.
+\<declared variable>, +57, 175.
+|decr|, +266.
+*|def|, @36, @159--@162, +165--+167.
+|default_wt_|, 271--272.
+|define_blacker_pixels|, @33, 92--93, @106, +268, 302.
+|define_corrected_pixels|, 93, 197, +268, 302.
+|define_good_x_pixels|, 199, +268, 302.
+|define_good_y_pixels|, 199, +268, 302.
+|define_horizontal_corrected_pixels|, @204, +268, 302.
+|define_pixels|, @33, 92, @106, 199, +268, 302.
+|define_whole_blacker_pixels|, 202, +268.
+|define_whole_pixels|, 199, +268, 302.
+|define_whole_vertical_blacker_pixels|, +268.
+|define_whole_vertical_pixels|, @204, +268, 302.
+\<definition>, +165.
+\<definition heading>, +165.
+definitions, 159--167, 175--180.
+deleting tokens, 42--43, 225.
+\<delimited parameters>, +165.
+delimiters, 61, 167, 210, 254, 288--289.
+*|delimiters|, 61, 180, 210, +218, @221, @262, @296, @299, @313.
+\<delimiters command>, +218.
+dependent variables, +81--+83, 88, 224.
+depth, 101.
+Derek, Bo, 287.
+Descartes, Ren\'e, 6, 11, 19.
+design size, 96, +319--+320, 324, 329.
+*|designsize|, 212, 320.
+device drivers, 323, 325.
+diagnostic aids, 229--231, 259, 286.
+diamond-shaped nib, 148--149, 297.
+Dickens, Charles John Huffam, 145.
+difference of pictures, 115, @244.
+digestion process, 179, 217--221.
+\<digit string>, +50.
+digitization, 111, 149, 195--207, 230.
+|\digits|, 339.
+dimensions, 92, +267.
+|dir|, @18, @67, @68, @83--@84, @135, @163--@164, 175, @233, +264.
+|direction|, @69, 70, @135, @235, +265.
+\<direction specifier>, 129, +213.
+|directionpoint|, @135, +265.
+*|directiontime|, @135, @+136, 211, 245, 265, @295.
+|dishing|, 152, 164.
+*|display|, +191--+192, 220.
+\<display command>, +220.
+|displaying|, 269, 276, 278.
+distance, 76, 84.
+|ditto|, @187, +263.
+|div|, +265.
+division, @59, @62, @63, 80, 82.
+\sub of numeric tokens, 61, 73.
+Dopping, Olle, 181.
+|dot|, 306, 311.
+dot product, 69.
+|dotprod|, @69, 178, @238, 265.
+|dotsize|, 332, 334.
+double-quote mark, 50--51, 187.
+*|doublepath|, 118, +119, @151, 220.
+doubly filled pixels, 110--112.
+|down|, @32, +263.
+|downto|, 172, +262.
+|draw|, @7, @15--@19, 21, 112, 118--120, 145, 147, 150, 198, 230, +271, 295.
+\sub one point, 22, 150, 200, 253.
+|drawdot|, @31, 113, 147, +150, 234, +271.
+Drayton, Michael, 279.
+drift, 102, 106.
+driver files, 304--306.
+*|dropping|, 118, +120, 220.
+D\"urer, Albrecht, 13, 19.
+*|dump|, 217, +221, 262, @279, @311.
+|.dvi|, 32, 40, 103, 106, 323, 327, 328.
+\newletter
+|e|, 27--29, 273.
+`E', 96--97, 204, 302--303.
+edge structure, 116--117, 296--297.
+edges, 116.
+editing, 46.
+efficiency, 39, 99, 141, 144, 147, 228, 230, 234, 244, 264, 265, 277,
+ 291, 297, 298.
+El Palo Alto, 124--126, 139, 228--229.
+ellipses, 123, 126.
+Ellis, Henry Havelock, 11.
+*|else|, +169--+170, 179.
+*|elseif|, +169--+170, 179.
+em dash, 306.
+emergency stops, 226.
+empty option in {\bf for\/} list, 171, @299.
+empty statement, 155, 217.
+empty text argument, 299.
+*|end|, @31, @37, 155, 167, 217, 221, 226, 278, 287, 305, @321.
+end of a file, 287.
+|endchar|, @36, 102, 156, 191, +276, 309, 311, 329.
+*|enddef|, @94, @159--@164, 165, @175--@178.
+*|endfor|, @18, @39, +171--+172, @173, 250, @290.
+|ENDFOR|, 45, 286, 290.
+*|endgroup|, +155--+157, 167, 175, 178, 210--215, 217, @236, @243,
+ @276, @289, @290.
+ending character, @40, 338--339.
+*|endinput|, +179, @287--@288.
+endpoints, 128, 150--151.
+|ENE|, 119, 206--207, 228.
+enormous number, 63, 236.
+envelopes, 118--119, 150, 230.
+|eps|, 93, @199--@200, 229, +263, @310--@311.
+|epsilon|, @62--@69, 115, @135, 152, 229, +263.
+equality test, general, 292.
+equality versus equation, 171.
+|equally_spaced|, 290.
+\<equation>, +88.
+equations, @5, @6, @23, @75--@85, 88, @141, 171.
+\sub nonlinear, 84--85, 176--177, @292--@294.
+equilateral triangle, 25, 203.
+|erase|, @113, 120, 167, +271, 272.
+*|errhelp|, +189, 219, @294.
+*|errmessage|, @178, +189, 219, @294.
+error messages, 41--46, 223--228.
+*|errorstopmode|, +219, 227, @313.
+|ESE|, 206--207, 228--229.
+*|everyjob|, 180, +219.
+\<everyjob command>, +219.
+Evetts, Leonard Charles, 153.
+exercises, viii, 5--231.
+\<exit clause>, +171.
+*|exitif|, 171, +173, @176, 179, @262.
+|exitunless|, 173, +262.
+expandable tokens, 179, 230.
+*|expandafter|, +179, 180, @270, @286--@290, @313.
+expansion process, +179--+180, @285--@291.
+exponential, \see |mexp|.
+*|expr|, @160, @162, 165, @166, 167, @176, 210.
+|(EXPR|$_n$|)|, 44, 160, 249, 251.
+|expr.mf|, +61, 62--71, 116--117, 132, 135--137, 142--143, 150, 173.
+\<expression>, 167, +209.
+expressions, 59--73, 209--215.
+*|extensible|, 318.
+\<extensible command>, +318.
+external tags, 55, 218.
+|extra_beginchar|, 275--276, @278.
+|extra_endchar|, 276, @277, @309.
+|extra_setup|, 269, @270, @278.
+|!| |Extra| |tokens| |will| |be| |flushed|, 43--44, 224--225.
+\newletter
+`F', 97, 204, 302--303.
+*|false|, 55, @64--@65, 170, 210.
+faster operation, 39, 99, 141, 144, 147, 228, 230, 234, 244, 264, 265, 277,
+ 291, 297, 298.
+|Fatal| |base| |file| |error|, 226.
+fatter pens, 297--298.
+*|fi|, +169--+170, 179.
+|!| |File| |ended...|, 287.
+file names, 36, 39, +180, 324, 329.
+\<filename>, 179--180.
+|fill|, @24--@27, 109--112, @116, 118--121, 145, 167, +271, 295.
+|filldraw|, @103--@105, 112--113, 118--119, 147, 148, @152, @164,
+ 230, +271, @306, 310.
+*|fillin|, +93--+94, 150, 212, 247, 268, 278--279.
+|fine|, 103--104, 306--307, 310--311.
+|fine.lft|, 311.
+|fix_units|, +267.
+flat spots, 196--197.
+|flex|, @124--@125, 127, @152, 173, 228--229, +267.
+*|floor|, @65, 66, 72, 83, 211, @253.
+flushing, 43--44, 219, 224--225.
+Font, Fray Pedro, 139, 231.
+\<font metric command>, +321.
+font metric information, 39, 220, 315--321.
+|font_coding_scheme|, +277, @303, 304, +320--+321.
+|font_extra_space|, +277, 319.
+|font_identifier|, +277, @303, 304, @305, 320, @332--@333.
+|font_normal_shrink|, @97, +276, @305, 319.
+|font_normal_space|, @97, +276, @305, 319, 332.
+|font_normal_stretch|, @97, +276, @305, 319.
+|font_quad|, @97, +277, 308, 319, 332.
+|font_setup|, 203, 305, 309--312.
+|font_size|, @95, 96, +276.
+|font_slant|, +276, @305, 319, 331, @335--@336.
+|font_x_height|, +277, 319, 332.
+*|fontdimen|, @276--@277, +318--+319, 331--332, @335.
+\<fontdimen command>, +318.
+*|fontmaking|, 54, @94, 211, @270, +315.
+|\fontname|, 342.
+*|for|, @18, @39, @113, +171--+173, 179, 228, @285--@291, @299.
+\<for list>, +171, 299.
+forbidden tokens, +173, 218--219, 286.
+*|forever|, @61, +171--+173, @176, 179.
+*|forsuffixes|, +171--+172.
+{\sevenrm FORTRAN} language, 237.
+\<four codes>, +318.
+four-point method for curves, 13--14, 133.
+Fournier, Simon Pierre, 321. % Harry Carter says S.P. is right, not P.S.!
+fractions, 61, @62--@63, +72, 73.
+*|from| 191, 220, @252, @277, @312.
+|fullcircle|, @114, 123--124, 126, @135--@137, +263, @266.
+Fulton, A\period\ G\period, 157.
+function values by interpolation, 294--295.
+\<future pen primary>, 148, +214.
+\<future pen secondary>, 148, +214.
+future pens, 148--149, 170, 249, 264, 298.
+\newletter
+Galsworthy, John, 215.
+Gardner, Martin, 126.
+|generate|, 305, 307, 311, 313.
+|gf|, 32, 241, 295, 323--325.
+|gfcorners|, 277, +278, 327.
+|GFtoDVI|, 32, 37, 187, 327--336.
+|gimme|, 61--62.
+Giotto de Bondone, 139.
+|gobble|, @167, +262, @289.
+|gobbled|, +262, @289--@290.
+golden ratio, 11.
+|good.bot|, 204, +273.
+|good.lft|, 204, +273.
+|good.rt|, 204, +273.
+|good.top|, 204, +273.
+|good.x|, @198, @268, +273.
+|good.y|, @198, 204, @268, +273.
+Goudy, Frederic William, 19.
+grammatical rules, 49--50.
+*|granularity|, +205, 212, 262, 310.
+graph paper, 5, 102, 109, 188.
+|gray|, 332.
+gray fonts, 327, 330--335.
+|grayf.mf|, 332--335.
+|grayfont|, 270, +275, 323, 329.
+|grayfontarea|, 329.
+|grayfontat|, 329.
+greater than or equal to, 65.
+greatest integer function, \see floor.
+grid, 5, 109, 275.
+Grimm, Jakob Ludwig Karl, 73.
+Grimm, Wilhelm Karl, 73.
+group delimiters, 289.
+group expressions, 157, 160.
+groups, 155--157, 167.
+Gu Guoan, 3.
+\newletter
+|h|, @22--@25, 35--36, @76--@78, 102, 204, +275.
+`H', 163, 165.
+Haggard, Sir Henry Rider, 107.
+hairlines, 104--105.
+|halfcircle|, 123, @136, +263.
+hamburgefonstiv, 341.
+hand tuning, 195.
+*|headerbyte|, 318, +320--+321.
+\<headerbyte command>, +318.
+hearts, 134.
+height, 101.
+Hein, Piet, 126, 231.
+help messages, 43--45, 189, 224--225.
+Herbin, Auguste, 3.
+Hersh, Reuben, 343.
+*|hex|, +188, 211, 281.
+hex symbol, 7--8, 28--29.
+hexadecimal notation, 188.
+|hide|, @116, @143, 167, @173, @227, +262.
+hierarchy of operators, 60--61, 71--73, 137, 209, 289.
+histogram, 251.
+Hobby, John Douglas, viii, 3, 130, 131, 149, 252, 285.
+holes, 110.
+Holland, Philemon, 51.
+Homerus, 51.
+homogeneous transforms, 247.
+*|hppp|, 92--93, 212, 267, 268, 324.
+|hround|, +264, @268.
+Hult\'en, Karl Gunnar Pontus, 3.
+\newletter
+`I', 28, 32, 39, 163, 164.
+|!| |I| |can't| |go| |on|, 226.
+IBM Corporation, ix.
+|identity|, @141--@145, 215, +263.
+*|if|, +169--+170, 179, 289.
+|iff|, @306, +307, 311.
+|imagerules|, 277, +278.
+imperative versus declarative, 87.
+impossible cube, 113.
+|in| (inch), 92, +267, 268.
+inaccessible token, 286.
+incomplete string, 50--51.
+inconsistent equations, 82, 313.
+|incr|, @39, 176--177, +266.
+independent variables, +81--+83, 88, 224, 226.
+infinite loops, 172, 226--227.
+|infinity|, @62--@69, +263, @266.
+inflection points, 18--19.
+|INIMF|, 221, 262, 279.
+|\init|, 342.
+\<initial value>, +171.
+*|inner|, 180, +218--+219, 286--287, @307, @321.
+|inorder|, 290.
+*|input|, +179, 180, @269, @287--@288, 324.
+input stack size, 226, 287.
+inserting text online, 42, 45, 61, 188, 223--225.
+integers, 65--66.
+|interact|, 230, +262.
+interacting with \MF, 42--45, 61, 188--189, 191--193, 219, 223--225.
+*|interim|, +155--+156, 230, @243, @244, @271, @272.
+\<interim command>, 155, +218.
+internal quantities, 54--55, 88, 218, 262.
+\sub table, 211--212.
+\<internal quantity>, 156, 218, 265.
+|interpath|, 134, +267.
+interpolation, 2, 134, 294--295.
+interrupting \MF, 219, 227--228, 313.
+intersection, of lines, 84.
+\sub of paths, 136--137.
+\sub of pictures, 120.
+|intersectionpoint|, @107, @137, @138, 178, +265.
+*|intersectiontimes|, +136, @178, 213, @265, @294, @298.
+|inverse|, @143, +264.
+inverse video, 115, 118.
+*|inwindow|, +191, @277.
+Io, 33, 40, 47.
+\<is>, 165, 171, +218.
+Isis, 40.
+|!| |Isolated expression|, 223.
+isolated math characters, 316, 319.
+|italcorr|, @103--@105, +275, @303, @306, @316.
+italic corrections, 102, 105, 275, 276, 304, 315--316, 319.
+italic type, 55, 206, 341.
+\newletter
+jaggies, 201.
+*|jobname|, +187, 214, 324.
+Johnson, Samuel, 167.
+Johnston, Edward, 29.
+|join_radius|, 266.
+jokes, viii, 231.
+Journal of Algorithms, 137--139.
+|jut|, 162, 308.
+\newletter
+Kafka, Franz, 340.
+Kandinski\u\i, Vasili\u\i\ Vasil'evich, 3.
+\<keep or drop>, +118, 120.
+*|keeping|, 118, +120, 220.
+|keepit|, 295.
+*|kern|, @97, @316, +317.
+kerning, 97, 316--317.
+|killtext|, +262, @272.
+knife, 24.
+*|known|, @65, 79--82, 143, +170, 210.
+Knuth, Donald Ervin, i, ii, ix, 3, 134, 192, 206, 255, 282, 291, 304, 308, 345,
+ 361.
+Knuth, Nancy Jill Carter, ix, 134, 137.
+\newletter
+|l|, 308--309.
+La Rochefoucauld, Fran\c cois VI, 313.
+\<label>, +317.
+\<labeled code>, +318.
+|labelfont|, +275, 329.
+|labelfontarea|, 329.
+|labelfontat|, 329.
+|labels|, @107, +274--+275, 327--328.
+labels in font metric information, 317--318.
+labels on "proofmode" output, 37, 187, 274--275.
+|labels.top|, 328.
+Lam\'e, Gabriel, 126.
+|large_pixels|, 332.
+|lcode_|, 274, 328.
+le B\'e, Pierre, 207.
+least integer function, \see ceiling.
+Leban, Bruce Philip, 242, 243, 270, 295.
+|left|, @16, +263.
+left-handed dangerous bend, 143.
+|leftstemloc|, 96, 199, 302.
+*|length|, @66, @69, 72, 211, 238.
+less than or equal to, 65.
+*|let|, 53, 180, +218, @287--@289, @299, @311.
+\<let command>, +218.
+|letter_fit|, 307--308.
+\<leveldef>, +178.
+\<leveldef heading>, 165, +178.
+|lft|, @23, @77, 80, 147, 151, +273.
+lies, viii, 231.
+Life, 121.
+\<ligature op>, +317.
+ligatures, 305--306, 315--317.
+|lightweight|, 332.
+*|ligtable|, @97, @305, +316--+317.
+\<ligtable command>, +317.
+\<ligtable program>, +317.
+\<ligtable step>, +317.
+\<limit value>, +171.
+line, point to be on, 83--84.
+linear dependencies, 82--83.
+linear forms, 64, 82.
+Linn\'e, Carl von (= Linn\ae us, Carolus), 325.
+|local.mf|, 278--279, 321.
+|localfont|, 39, 271, 278, @279.
+locations of characters within a font, 106--107, 281--283, 320.
+Lockyer, Sir Joseph Norman, 57.
+log file, 42, 46, 62, 230, 295--297.
+logarithm, \see |mlog|.
+|loggingall|, 230, +263.
+logo of \MF, ii, 22--23, 95--99, 160--161, 184--185, 199--200, 204, 301--304.
+|logo.mf|, 95--98, 199, 302--303.
+logos, {\it i}, 97, @114, @137--@139.
+|logo10.mf|, 95, 287, 301, 304.
+\<loop>, +171.
+\<loop header>, +171.
+loop text, 171--172, 219, 286.
+loops, 169, 179, 226--227, 259, 290--291, 299.
+low-resolution proofs, 99, 327.
+|\lowers|, 339.
+|lowres|, 196, 201, 230, +270.
+|lowres_fix|, 203, +268, 310.
+|luxo|, 91--94, 99, 195, 278--279.
+\newletter
+`M', 23, 97, 200, 302--303.
+macros, @36--@37, 53, 114, 159--167, 175--179, 285--299.
+|mag|, @39, +91--+93, 98, 169, 230, 269, 278, 333--334.
+magnets, 60--61.
+magnification, 38--40, 91--99.
+|magstep|, 98, +270.
+|makebox|, 270, +276, 309.
+|makegrid|, +275.
+|makelabel|, +274, 328.
+*|makepath|, +150, 213, 247, @298.
+*|makepen|, +147--+148, 214, @264.
+|maketicks|, 270, +276, 309.
+mastication, 169, 179, 285.
+|\math|, 341.
+Matthew, Saint, 173.
+|max|, @65, +266, 290--291.
+maximum, 65.
+mediation, 9--11, 14, @63, @68, 72, 80, 133, 298--299.
+memory usage, 226--227.
+*|message|, @61, +189, @262.
+\<message command>, 189, +219.
+\<message op>, 189, +219.
+meta-design, 1--3, 103--105, 294.
+meta-font, 1--3, 98, 192, 301--304.
+meta-ness, 3, 301.
+\MF, the logo, ii, 22--23, 95--99, 160--161, 184--185, 199--200,
+ 204, 301--304.
+\sub the name, 1--3.
+|METAFONT| |capacity| |exceeded|, 226--227.
+\MF\kern1pt79, viii.
+*|mexp|, @+67, 72, 211, @265, @270.
+|mf|, 31, 35.
+|.mf|, 36.
+|mfput|, 31--32, 187, 324.
+|MFT|, 262.
+midpoints, 9, 13.
+Mies van der Rohe, Ludwig, 185.
+|min|, @65, +266, 290--291.
+minimum, 65.
+Mirk, John, 313.
+|!| |Missing| |`)'| |has| |been| |inserted|, 254.
+misspelling, 45, 224.
+|\mixture|, @40, +338.
+*|mlog|, @+67, 72, 211, @265.
+|mm| (millimeter), @76, 91--92, +267, 268.
+M\"obius, August Ferdinand, 114.
+mock curvature, 131.
+|mod|, @66, +265.
+|mode|, @38--@39, @75, 91--94, 269, 278.
+\<mode command>, +219.
+|mode_def|, 94, 189, @+270, @278--@279.
+|mode_name|, 269.
+|mode_setup|, @32--@34, 75, 76, 91--94, @96, 115, 169, +269, 278, @304, @305,
+ 329.
+|mono_charwd|, 308.
+|monospace|, 305--308.
+*|month|, +212, 323.
+More, Sir Thomas, 215.
+Morison, Stanley, ix, 283.
+mouth, 169, 179, 285.
+Moxon, Joseph, 325.
+Mulford, Clarence Edward, 89.
+multiplication, @59, @62--@64, 69, 79--80, 82.
+\sub of vector by scalar, 9.
+music, 183, 185.
+\newletter
+`n', 201--203.
+`N', 184--185, 303.
+|\names|, 339.
+National Science Foundation, ix.
+Naur, Peter, 49, 89.
+negation, of pictures, 115.
+\sub of vectors, 9.
+|new_window|, 193.
+*|newinternal|, 180, +218.
+\<newinternal command>, +218.
+nice tangent points, 177.
+|NNE|, 119, 228.
+|NNW|, 26, 119, 228--229.
+|nodisplays|, 277, +278.
+|nodot|, 274, 328.
+nonlinear equations, 84--85, 176--177, @292--@294.
+nonsquare pixels, 94, 145, 204.
+*|nonstopmode|, +219, 226.
+*|normaldeviate|, @68, 72, @183--@185, 210.
+*|not|, @65, +170, 210.
+|notransforms|, 277, +278.
+*|nullpen|, +148, 214, @272.
+*|nullpicture|, +115, 192, 214, @272, @277.
+*|numeric|, 55, +56, @65, 88.
+\<numeric atom>, 72, +210.
+\<numeric expression>, 72, +211.
+numeric expressions, 72--73, 257.
+\<numeric list>, +318.
+\<numeric operator>, 72, +211.
+\<numeric primary>, 72, +211.
+\<numeric secondary>, 72, 178, +211.
+\<numeric tertiary>, 72, +211.
+\<numeric token>, +50, 236.
+\<numeric token primary>, 72, +210.
+numeric tokens, 49--50, 166.
+\sub maximum value, 50.
+\sub rounded fractional values, 50.
+|numeric_pickup_|, +272, 310.
+*|numspecial|, 220, @274, +323--+324, @327--@329.
+|numtok|, @+274.
+\newletter
+|o|, @23, @34, +93, 197, 200, 204, 302.
+`o', 203.
+`O', 32--37, 199, 303.
+|o_correction|, 93--94, 268.
+*|oct|, +188, 211, 281.
+octal notation, 188.
+octants, 119, 206--207, 228--230.
+*|odd|, +170, 210, 250.
+*|of|, 73, 129, 165--167, 187, 211--214.
+of-the-way function, \see mediation.
+off by $x$, 82.
+Office of Naval Research, ix.
+|offset|, 275, 379.
+|!| |OK|, 219, 224.
+|\omitaccents|, 340.
+one-point {\bf draw}, 22, 150, 200, 253.
+online interaction, 42--45, 61, 188--189, 191--193, 219, 223--225.
+|openit|, +277, 312.
+*|openwindow|, +191--+193, 220, @277, @312--@313.
+\<openwindow command>, 191, +220.
+operands, 59.
+operators, 59, 230.
+\<optional skip>, +317.
+*|or|, @65, +170, 210, 237, 288--289.
+order of operations, 60--61, 137, 247, 289.
+oriental characters, 3, 106, 324.
+|origin|, @77--@78, @243, @251, +263.
+ornament, 144--145.
+Orwell, George (= Blair, Eric Arthur), 85.
+*|outer|, 180, +218--+219, 221, 286--287, @307, @321.
+outlines, 121.
+output of \MF, 39, 42, 315--325.
+|overdraw|, 114, 243.
+overflow labels, 37, 328.
+overlays, 295.
+overshoot, 23, 34, 93, 197, 200, 204.
+\newletter
+`P', 207.
+Paget, Francis Edward, 279.
+*|pair|, 55, +56, 65.
+\<pair expression>, 73, +213.
+pair expressions, 73, 171, 258.
+\<pair part>, +211.
+\<pair primary>, 73, +212.
+\<pair secondary>, 73, +212.
+\<pair tertiary>, 73, +213.
+Palais, Richard Sheldon, ii.
+parallel lines, 84.
+parallelogram, 293--294.
+\<parameter>, +178.
+parameter files, 301, 304.
+\<parameter heading>, +165.
+\<parameter tokens>, +165.
+\<parameter type>, +165.
+parameters, v, 1--3.
+\sub to fonts, 95, 103--104, 305.
+\sub to macros, 159--167, 175--178.
+parentheses, 51, 59, +60, 61, 71, 128, 210--215, 247.
+Pascal language, 54.
+*|path|, 55, +56, 171.
+\<path expression>, 129, +213.
+path expressions, 129--134, 258.
+\<path join>, 129--130, 171, +213.
+\<path primary>, 129, +213.
+\<path secondary>, 129, +213.
+\<path tertiary>, 129, +213.
+paths, 13--19, 123--139.
+*|pausing|, 211, +231.
+|pc| (pica), 92, +267, 268.
+pels, \see pixels.
+*|pen|, 55, +56, @65, 170.
+\<pen expression>, 147, 148, +214.
+pen expressions, 147--148, 258, 298.
+\<pen primary>, 148, +214.
+\<pen secondary>, 148, +214.
+\<pen tertiary>, 148, +214.
+|pen_bot|, 151, +272.
+|pen_lft|, 151, +272.
+|pen_rt|, 151, +272.
+|pen_top|, 151, +272.
+*|pencircle|, @21--@23, @28, @29, +147--+149, @150--@152, 198, 200, 214.
+|penlabels|, 36, +274.
+*|penoffset|, +150, 212, 230, @298.
+|penpos|, @26--@29, 37, @103, @162, +273, 310.
+|penrazor|, @107, @112, 147, 150, +264, 297.
+pens, 21--39, 147--152, 297--298.
+|penspeck|, +264, @271.
+|pensquare|, 147, 152, +264, 275.
+|penstroke|, 27--29, @138, +273.
+perpendicular, 29, 69, 84, 235.
+|pickup|, @21--@23, 145, 147, +272.
+*|picture|, 55, +56, @114.
+\<picture command>, 118, +220.
+\<picture expression>, 115, +214.
+picture expressions, 115, 258.
+\sub transformation of, 144, 297.
+\<picture primary>, 115, +214.
+\<picture secondary>, 115, +214.
+\<picture tertiary>, 115, +214.
+pictures, 109--121.
+pimples, 196--197, 204.
+|pix_ht|, 332, @333.
+|pix_picture|, 332, @333.
+|pix_wd|, 332, @333.
+pixels, 5, 109, 259, 324.
+|pixels_per_inch|, 267, 268.
+plain \MF\ base, 34, +257--279.
+|plain.mf|, 261--278.
+\<plus or minus>, 72, +211.
+*|point|, @69--@70, 73, @114, +133, 212, @267.
+polygonal path, 24, 297.
+pool size, 226, 286.
+|pos|, 310.
+*|postcontrol|, +134, 212, @267.
+|posttension|, 136.
+precedence, 60--61, 71--73, 137, 289.
+*|precontrol|, +134, 212, @267.
+|pretension|, 136.
+pretty-printed \MF\ programs, 262.
+*|primary|, 165, 167.
+\<primary>, 71, 170, +209.
+*|primarydef|, 166, @+178.
+prime numbers, 173.
+primitives, 53, 209, 345.
+private tokens, 173, 265, 270.
+product, @59, @62--@64, 69, 79--80, 82.
+\sub of vector by scalar, 9.
+\<program>, 155, +217.
+program files, 304, 306.
+\<progression>, +171.
+|proof| mode, 92, 93, 104, +270, 327.
+*|proofing|, @94, 187, 211, 220, @270, 274, +323--+324, 327.
+|proofoffset|, +275, 329.
+|proofrule|, +274, 323, 328--329.
+|proofrulethickness|, +275, 329.
+proofsheets, 37, 261, 327--343.
+\<protection command>, +218.
+pseudo-driver files, 311--313.
+|pt| (printer's point), @21--@23, @33, 91--92, +267, 268.
+|\punct|, 339.
+punctuation marks, 306.
+Pythagorean addition, @+66, @67, 72, 211.
+\<Pythagorean plus or minus>, 72, +211.
+Pythagorean subtraction, @+66, 72, 211, @238.
+\newletter
+`Q', 207.
+|quartercircle|, 123, +263.
+Quick, Jonathan Horatio, 54, 137.
+*|quote|, +166, 172, @270, @286, @312.
+\newletter
+|r|, 308--309.
+`R', 207.
+|\raggedright|, 338.
+Ramshaw, Lyle Harold, 320.
+random numbers, 183--185.
+*|randomseed|, 185, 218.
+\<randomseed command>, +218.
+|range|, @107, @138, @200, +274.
+raster, 5, 91, 109, 195.
+*|readstring|, @61, +187--+188, 214.
+recipes, 2.
+recursion, 227.
+redundant equations, 82.
+reference point, 77, +101.
+|reflectedabout|, @138, 141, @142, 160, +266.
+reinitializing a variable, 88, 157.
+\<relation>, 170, +210.
+relations, @64--@65, 170--171.
+|relax|, @31, +262, @307.
+remainder, 66.
+|rep|, 332, 335.
+replacement text, 159, +166, 219.
+resolution, 6, 38--39, 91--99, 116.
+\<return> key, 31.
+*|reverse|, 129, +132, 213.
+reverse video, 115, 118.
+Reynolds, Lloyd Jay, 153.
+|right|, @26, @68, +263.
+\<right-hand side>, +88, 171.
+*|rotated|, @21--@22, @25, 27, 44, @68, 73, @107, @114, @117, +141, 212, @238.
+|rotatedabout|, +266.
+|rotatedaround|, @138, 141, @142, @144, 159--160, +266.
+|round|, @66, 196, 202, +264, @273.
+rounding, 34--35, 50, 195--207, 308.
+|rt|, @23, @77, 80, @103, 147, 151, +273.
+|rtest.mf|, 311.
+|rule|, 234, 328.
+|rulepen|, +274, 275.
+rules on proofsheets, 328--329.
+|rulethickness|, 275, 329.
+runaway, 287.
+Running, Theodore Rudolph, 47.
+Ruskin, John, 139.
+\newletter
+`S', 40, 114.
+|safefill|, 121.
+|\sample|, 341.
+sans-serif, 105, 305, 308.
+*|save|, +155--+156, @160, 173, @178, 180, 218, @236, @244, @296, 299.
+\<save command>, 155, +218.
+|savepen|, @96, 147, +272, @310.
+\<scalar multiplication operator>, 72, +211.
+*|scaled|, @21--@23, @68, 73, +141, 212, 244, 291.
+*|scantokens|, @61, +179, @180, 189, 251, @269, @270, @286--@288, @313.
+scatter plots, 183.
+\<screen coordinates>, 191, +220.
+\<screen place>, 191, +220.
+|screen_cols|, 193, 277, @278.
+|screen_rows|, 277, @278.
+|screenchars|, 191, +277.
+|screenrule|, 274, 278.
+|screenstrokes|, 191, +277.
+*|scrollmode|, @61, +219, @313.
+*|secondary|, 165, 167.
+\<secondary>, 71, +209.
+*|secondarydef|, 166, @178.
+selective complement, 120.
+semantics, 50.
+semicolons, 155, 169, 171, 172, 187, 217, 223--224, 263, 312.
+|serif_fit|, 308.
+serifs, 152, 162--165, 308.
+Serlio, Sebastiano, 19.
+|setu_|, 266, 291.
+Shakespeare, William, 173, 255, 343.
+sharped dimensions, @32--@35, 91--99, 102--103, 268, 315.
+|shiftdef|, 311.
+*|shifted|, @68, 73, @117, +141, 213.
+|shipit|, @31, @276, +277, 295.
+*|shipout|, 106, 210, +220, @277, @295, 316, 324, 329.
+\<shipout command>, +220.
+*|show|, 142, +219, @227, 230, @250, 296.
+\<show command>, +219.
+*|showdependencies|, 81, 83, +219, @262.
+|showit|, @31, 191, @276, +277, 295.
+*|showstats|, +219.
+*|showstopping|, 211, @227, 230, @262.
+*|showtoken|, 180, +219, @221.
+*|showvariable|, 175, 177, 180, +219.
+|shrink_fit|, 308--310.
+shrinkability, 319.
+shuffled binary numbers, 137.
+sidebearings, 10, 34--35, 307--308.
+{\sevenrm SIMULA67} language, 175.
+*|sind|, @67, 72, 211.
+*|skipto|, @316, +317.
+skyline, 251.
+|slant|, 105, 206, 301--303, 310, 319.
+slant fonts, 329, 335--336.
+*|slanted|, @68, 73, 105, +141, 213.
+|slantfont|, +275, 329.
+|slantfontarea|, 329.
+|slantfontat|, 329.
+|smode|, 269.
+|smoke| mode, 38, 75, 93, +270, 327.
+*|smoothing|, 55, 195, 205--206, 212, @262.
+|softjoin|, 262, +266.
+|solve|, 176--177, +267, @292--@294.
+|(some| |charht| |values...)|, 316.
+Southall, Richard Francis, 176.
+spaces, 43, 50, 236.
+sparks, +53--+55, 156, 175, 215, 219, 289.
+*|special|, 220, @240--@241, @274, +323--+324, @327--@329.
+\<special command>, +220.
+special-purpose macros, 160, 248.
+*|sqrt|, @59, @64, 72, 211.
+square roots, 66, \also |sqrt|.
+|SSE|, 206--207, 228--229.
+|SSW|, 119, 228--229.
+stack positions, 227.
+Stanford, Amasa Leland, 340.
+Stanford, Jane Elizabeth Lathrop, 340.
+Stanford University, 125, 340.
+star, 114.
+|\startfont|, +337, 338, @342.
+starting a job, 39, 95, 259, 277.
+starting character, @40, 338--339.
+\<statement>, 155, 171, +217.
+\<statement list>, 155, +217.
+statements, 155, 217--221.
+\sub summary, 260--261.
+stems, 201--203.
+*|step|, @18, 171.
+\<step size>, +171.
+stomach, 169, 217, 285.
+|stop|, +262, @311--@312.
+stopping \MF, \see |end|.
+*|str|, +187--+188, 214, @250, @251.
+strange paths, 110--111, 119, 121, 136, 152, 228--229.
+Stravinski{\u\i}, Igor' F\"edorovich, 193.
+stretchability, 319.
+Strindberg, Johan August, 185.
+*|string|, 55, +56, 69.
+\<string expression>, 73, 187, +214.
+string expressions, 187--189, 258, 286.
+\<string primary>, 187, +214.
+\<string secondary>, 187, +214.
+\<string tertiary>, 187, +214.
+string tokens, 49--51.
+|stroke|, @306, 310.
+*|subpath|, @70, @71, 114, 129, +133, 134, 188, 213, @298.
+subroutines, \see macros.
+\<subscript>, +54.
+subscripts, 54--57.
+*|substring|, @69, 187, +188, 214, @320.
+subtraction, of pictures, 115, @244.
+\sub of vectors, 9.
+Suetonius Tranquillus, Gaius, 181.
+*|suffix|, @161, 165, @176.
+\<suffix>, +54, 161, 176, 188.
+\<suffix list>, +171, 236.
+|(SUFFIX|$_n$|)|, 44, 251.
+sum, of pictures, 115, @117, @245.
+\sub of transforms, 178.
+\sub of vectors, 9.
+|superellipse|, @126, @138, +267.
+superellipses, 126, 161.
+|superness|, 126.
+Sutherland, Ivan Edward, 121.
+Swift, Jonathan, 99, 121.
+\<symbolic token list>, 155, +218.
+symbolic tokens, 49--51.
+symmetric difference, 120.
+syntax rules, 49--50.
+System Development Foundation, ix.
+\newletter
+`T', 22--23, 97, 151, 199--200, 302--303.
+tables of \MF\ trivia:
+\sub character classes, 51.
+\sub character codes, 281--282.
+\sub expandable tokens, 179--180.
+\sub |fontdimen| parameters, 319.
+\sub internal quantities, 211--212.
+\sub language features, 257--261.
+\sub proof\/ label options, 328.
+\sub types, 55.
+\sub units of measure, 92.
+tags, +53--+55, 156, 175, 218--219.
+|takepower|, +265.
+taller pens, 297--298.
+tapered stroke, 28.
+|tensepath|, 128, +264, @298.
+*|tension|, @15--@16, @114, +129--+132, 136, @296.
+\<tension>, 129, +213.
+\<tension amount>, 129, +213.
+*|tertiary|, 165, 167.
+\<tertiary>, 71, @137, +209.
+*|tertiarydef|, 166, +178, @266.
+|test.mf|, 311--312.
+|testfont.tex|, 40, 336--342.
+\TeX, 1, 34, 40, 91, 96, 98, 101--102, 315, 336--343, 361.
+*|text|, @161, +165--+167.
+|\text|, 340.
+|(TEXT|$_n$|)|, 45, 249, 251.
+text arguments, 219, 288--290, 299.
+|.tfm|, 39, 315--317, 333, 335.
+|!| |This| |can't| |happen|, 226.
+Thomson, James, 189.
+Thoreau, Henry David, 221.
+|thru|, @107, @138, @200, +274.
+tilde, 152.
+*|time|, +212, 218, 323.
+time in paths, 119, 133--137.
+\<times or over>, 72, +211.
+Tinguely, Jean, 3.
+\<title>, +187, 217--218, 323.
+|title|, 323, 327.
+|titlefont|, +275, 329.
+|titlefontarea|, 329.
+|titlefontat|, 329.
+*|to|, 191, 220, @252, @277, @312.
+|<to| |be| |read| |again>|, 223.
+Tobin, Georgia Kay Mase, ii, 240.
+tokens, 42--43, +49--+51, 210.
+|tolerance|, 176, 251, 267, 293.
+|top|, @23, @77, 80, @103, 147, 151, 204, +273.
+Tory, Geoffroy, 19.
+|totalnull|, 295.
+*|totalweight|, +115, 211, @292.
+|tracingall|, 230, +263, 288.
+*|tracingcapsules|, 211, 219, 239.
+*|tracingchoices|, 211, +229.
+*|tracingcommands|, 211, +230.
+*|tracingedges|, 211, +230, @295--@296.
+*|tracingequations|, 80--83, 211, 229.
+*|tracingmacros|, +160, 211, 229.
+|tracingnone|, 230, +263.
+*|tracingonline|, @61, 80, 211, 219, +230.
+*|tracingoutput|, 211, +229--+230, 296.
+*|tracingpens|, 211, +229, 230.
+*|tracingrestores|, +156, 211, 229.
+*|tracingspecs|, 206--207, 211, +229.
+*|tracingstats|, 211, +227, 230.
+*|tracingtitles|, 55, @94, +187, 211, 229.
+Trajanus, 153.
+trajectories, \see paths.
+transcript file, 42, 46, 62, 230, 295--297.
+*|transform|, 55, +56, 57, 141--143, @160, 266.
+\<transform expression>, +215.
+transform expressions, 141--143, 170, 178, 258.
+\<transform part>, +211.
+\<transform primary>, +215.
+\<transform secondary>, +215.
+\<transform tertiary>, +215.
+transformations, 44, 141--145.
+*|transformed|, 73, 141--145, 213.
+\<transformer>, 73, +213.
+transition lines, 230.
+|transum|, 178.
+trial path, 235.
+triangle, 24--25, 203.
+trigonometric functions, 67, 69, 131, 177.
+*|true|, 55, @64--@65, 170, 210.
+truth, viii, 217, 221.
+{\sl TUGboat}, ix, 361.
+turning numbers, 110, +111, 112, 119, 136, 147.
+*|turningcheck|, 112, +119, 212, 229, @244, 262, 296.
+*|turningnumber|, 111, 211, 257, @264.
+Twain, Mark (= Clemens, Samuel Langhorne), 145.
+\<type>, +56, 171.
+type declarations, 56.
+types, 55.
+typewriter type, 55, 105.
+typographic errors, 45, 224.
+\newletter
+|u|, 103--104, 305--308.
+|!| |Undefined| |coordinate|, 224.
+undelimited arguments, +167.
+\<undelimited parameters>, +165.
+undelimited suffix parameters, +167, 176, 265, 270.
+underline characters, 49, +51, 173, 265, 270.
+|undraw|, 113, 118, 120, 242, +271.
+|undrawdot|, 113, +271.
+unequal to, 65.
+|unfill|, @25, 27, 109--110, 118, @126, +271.
+|unfilldraw|, 113, 118, +271.
+*|uniformdeviate|, @68, 72, +183, 184, 211.
+union, 120.
+Union Jack, 7.
+|unitpixel|, +263, @333.
+units of measure, 33, 91--99, 267--268.
+\sub table, 92.
+|unitsquare|, @116, 128, 132, 136, +263.
+|unitvector|, @238, +264.
+*|unknown|, 79--82, 143, +170, 210.
+unknown quantities, nonnumeric, 84--85.
+\sub numeric, 79--83.
+*|until|, @18, 171.
+|up|, @32, @129, +263.
+|\uppers|, 339.
+|upto|, @39, 172, +262.
+utility files, 311--313.
+\newletter
+\<vacuous expression>, +215.
+vacuous expressions, 209, +215, 250, 262, 289, 292.
+\<vacuous primary>, +215.
+\<vacuous secondary>, +215.
+\<vacuous tertiary>, +215.
+valentine, 134.
+values, disappearance of, 56, 88, 156--157.
+*|vardef|, 166, @175--@178, 289.
+\<vardef heading>, +178.
+\<variable>, 54, +55, 210.
+variables, 53--57, 59.
+\sub reinitializing, 88, 157.
+vector subtraction principle, 9.
+vectors, 9--10, 77.
+velocity zero, 136, 298.
+Venezky, Richard Lawrence, 193.
+*|vppp|, 212, 267, 324.
+|vround|, @204, @+264, @268.
+\newletter
+|w|, @22--@25, 35--36, @76--@78, 102--103, 106, +275--+276, 308--310.
+`w', 202.
+*|warningcheck|, 212, @269, 270.
+Warren, Mercy Otis, 359.
+Webster, Noah, 167.
+|whatever|, @83--@84, @138, 157, @233, 239, +264, @290.
+width, 101.
+Wilde, Oscar Fingal O'Flahertie Wills, 321.
+Wilkins, John, ii, 283.
+Willis, Ellen Jane, 157.
+\<window>, 191, +220.
+\<window spec>, 191, +220.
+\<with clause>, +118, 120.
+*|withpen|, 118, 220, @242.
+*|withweight|, 118, 220, @242, @297.
+|WNW|, 119, 228--229.
+|WSW|, 119, 228--229.
+\newletter
+$x$ coordinates, @5--@7.
+x-height, 319.
+Xerox Corporation, 320.
+|xgap|, 95--96, 199.
+*|xoffset|, 212, +220, @309, 315, 324.
+xor, 120.
+*|xpart|, @68, 72, @138, 142, 211.
+*|xscaled|, @21--@23, @68, 73, +141, 213, 244, 291.
+*|xxpart|, 72, 142, @160, 211.
+|xy_swap|, 297.
+*|xypart|, 142, @160, 211.
+\newletter
+$y$ coordinates, @5--@7.
+*|year|, +212, 323.
+|ygap|, 96, 199.
+*|yoffset|, 212, +220, 315, 324.
+*|ypart|, @68, 72, 142, 211, 238.
+*|yscaled|, @21--@23, @68, 73, +141, 213, 244, 291.
+*|yxpart|, 142, @160, 211.
+*|yypart|, 142, @160, 211.
+\newletter
+|z| convention, 7, @68, 69, 251, +277.
+Zapf, Hermann, iii, 221.
+zero, 236.
+*|zscaled|, @68--@69, 73, +141, 213.
+|ztest.mf|, 312.
+\enddoublecolumns
+\endchapter
+
+The more we search,
+the More are we Deceived.
+\author MERCY OTIS ^{WARREN}, ^^{Adams} {\sl To Mr.\thinspace Adams\/} (1773)
+ % in Mass. Historical Soc. Collections, vol73 (1917), p402; line 32
+
+\bigskip
+
+A heavy weight is now to be removed from my conscience.
+So essential did I consider an Index to be to every book,
+that I proposed to bring a Bill into Parliament
+to deprive an author who publishes a book without an Index
+of the privilege of copyright; and, moreover,
+to subject him, for his offence, to a pecuniary penalty.
+Yet, from difficulties started by my printers,
+my own books have hitherto been without an Index.
+\author LORD ^{CAMPBELL}, {\sl Lives of the Chief Justices %
+ of England}, vol.\thinspace 3 (1857) % end of the preface
+
+\eject
+ \beginchapter Appendix J. Joining the\\\TeX\ Community
+
+This appendix is about grouping of another kind: \TeX\ and \MF\ users from
+around the world have banded together to form the \TeX\ Users Group (TUG),
+in order to exchange information about common problems and solutions.
+
+A newsletter/journal called {\sl TUGboat\/} has been published
+since 1980, featuring articles about all aspects of \TeX\ and \MF\!\null.
+^^{TeX} TUG has a network of ``site coordinators'' who serve as focal points of
+communication for people with the same computer configurations.
+Occasional short courses are given,
+to provide concentrated training in special topics; videotapes of
+these courses are available for rental.
+Meetings of the entire TUG membership are held at least once a year.
+You can buy \MF\ T-shirts at these meetings.
+
+Information about membership in TUG and subscription to {\sl TUGboat\/}
+is available from
+
+\smallskip
+{\obeylines
+\TeX\ Users Group
+|email: TUG@tug.org|
+|internet: http://www.tug.org|
+}
+
+\endchapter
+
+TUG is established to serve members having a common interest
+in \TeX, a system for typesetting technical text,
+and in {\manual \char`\\]\char`\^\char`\_efg\char`\^}\!, %
+ a system for font design.
+\author T\kern-.15em\lower.5ex\hbox{E}\kern-.005em X %
+ USERS GROUP, {\sl Bylaws, Article II\/} (1983) % TUGboat 4 (1983) p60
+
+\bigskip
+
+Don't delay, subscribe today! That address again is
+\TeX\ Users Group
+email: {\eighttt TUG\char`\@ tug.org}
+internet: {\eighttt http://www.tug.org/}
+\author DONALD E. ^{KNUTH}, {\sl The \TeX book\/} (1996) % Appendix J
+
+\eject
+ \end
diff --git a/systems/knuth/dist/mf/trap.fot b/systems/knuth/dist/mf/trap.fot
new file mode 100644
index 0000000000..6bceb9f67e
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.fot
@@ -0,0 +1,59 @@
+This is METAFONT, Version 2.7182818 (INIMF)
+** &trap trap
+(trap.mf
+\*//*\trap\pass2!
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+l.21 ...t next=\; delimiters ^~7
+ ! fi
+! Extra tokens will be flushed.
+<to be read again>
+ !
+l.21 ... next=\; delimiters ^~7!
+ fi
+! Forbidden token found while scanning to the end of the statement.
+<inserted text>
+ ;
+<to be read again>
+ \
+l.22 next\
+ ; % the second pass will now compute silently; the ...
+
+hello again^^_
+>> pen (see the transcript file)
+>> path (see the transcript file)
+>> path (see the transcript file)
+>> path (see the transcript file)
+f<expr>->let)=];let[=(;show._
+(EXPR0)<-xx
+{let}
+{let}
+{show}
+>> _1
+{showdependencies}
+xpart '=-ypart '-0.66667
+{qq:=pen}
+{showstats}
+Memory usage 1462&588 (104 still untouched)
+String usage 41&161 (815&7634 still untouched)
+{[repeat the loop]}
+! A group begun on line 163 never ended.
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+{endgroup}
+ )
+(end occurred when else on line 93 was incomplete)
+(end occurred when if on line 36 was incomplete)
+(end occurred when elseif on line 21 was incomplete)
+(see the transcript file for additional information)
+(illegal design size has been changed to 128pt)
+(some chardp values had to be adjusted by as much as 0.5pt)
+(local label 0:: was missing)
+(local label 5:: was missing)
+(4 font metric dimensions had to be decreased)
+Font metrics written on trap.tfm.
+Output written on trap.72270gf (5 characters, 1028 bytes).
+Transcript written on trap.log.
diff --git a/systems/knuth/dist/mf/trap.log b/systems/knuth/dist/mf/trap.log
new file mode 100644
index 0000000000..611ce589e2
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.log
@@ -0,0 +1,4266 @@
+This is METAFONT, Version 2.7182818 (preloaded base=trap 2014.1.7) 7 JAN 2014 18:11
+** &trap trap
+(trap.mf
+{if}
+{known(0)}
+{not(true)}
+{false}
+{known("")}
+{true}
+
+/*\`'@#->begingroup.message(SUFFIX1)&str(SUFFIX0)&jobname&char.ASCII'`&s
+tr(SUFFIX2)!endgroup
+(SUFFIX0)<-/*\
+(SUFFIX1)<-`'
+(SUFFIX2)<-pass2
+{begingroup}
+{message}
+
+`'->begingroup'`endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-`'
+{begingroup}
+
+'`->"\*/"
+{endgroup}
+{("\*/")&("/*\")}
+{jobname}
+{("\*//*\")&("trap")}
+
+'`->"\*/"
+{ASCII("\*/")}
+{char(92)}
+{("\*//*\trap")&("\")}
+{("\*//*\trap\")&("pass2!")}
+
+\*//*\trap\pass2!
+{endgroup}
+{outer}
+{let}
+{delimiters}
+
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+l.21 ...t next=\; delimiters ^~7
+ ! fi
+Sorry: You can't redefine a number, string, or expr.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+! Extra tokens will be flushed.
+<to be read again>
+ !
+l.21 ... next=\; delimiters ^~7!
+ fi
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+! Forbidden token found while scanning to the end of the statement.
+<inserted text>
+ ;
+<to be read again>
+ \
+l.22 next\
+ ; % the second pass will now compute silently; the ...
+A previous error seems to have propagated,
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{\}
+{batchmode}
+
+! An expression can't begin with `endgroup'.
+<inserted text>
+ 0
+<to be read again>
+ endgroup
+l.23 batchmode; ^~7,endgroup
+ pausing:=1; exitif p exitif bool...
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+! Missing ` INACCESSIBLE' has been inserted.
+<to be read again>
+ endgroup
+l.23 batchmode; ^~7,endgroup
+ pausing:=1; exitif p exitif bool...
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+! Extra `endgroup'.
+<recently read> endgroup
+
+l.23 batchmode; ^~7,endgroup
+ pausing:=1; exitif p exitif bool...
+I'm not currently working on a `begingroup',
+so I had better not try to end anything.
+
+{pausing:=1}
+{exitif}
+{exitif}
+{pencircle}
+{endfor}
+! Extra `endfor'.
+l.23 ...ean pen pencircle endfor
+
+I'm not currently working on a for loop,
+so I had better not try to end anything.
+
+{scantokens}
+{begingroup}
+{message}
+{char(0)}
+{("^^@")&("watch this")}
+^^@watch this
+{-(1)}
+{char(-1)}
+{("pair p[],';")&("^^ff")}
+{endgroup}
+{pen(future pen)}
+{boolean(true)}
+{true}
+! No loop is in progress.
+<to be read again>
+ pair
+<scantokens> pair
+ p[],';^^ff
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+Why say `exitif' when there's nothing to exit from?
+
+>> p
+! Undefined condition will be treated as `false'.
+<to be read again>
+ pair
+<scantokens> pair
+ p[],';^^ff
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+The expression shown above should have had a definite
+true-or-false value. I'm changing it to `false'.
+
+{false}
+! Missing `;' has been inserted.
+<to be read again>
+ pair
+<scantokens> pair
+ p[],';^^ff
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+After `exitif <boolean exp>' I expect to see a semicolon.
+I shall pretend that one was there.
+
+{pair}
+! Text line contains an invalid character.
+<scantokens> pair p[],';^^ff
+
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+A funny symbol that I can't read has just been input.
+Continue, and I'll forget that it ever happened.
+
+{path}
+{-(2)}
+{-(1)}
+{turningnumber((xpart ',ypart '))}
+Path at line 25, before choices:
+(1,-2){curl 1}
+ ..(2,-1)
+ ..(2.5,0.5)
+ ..(1,2)
+ ..{curl 1}(0,2.5)
+
+Path at line 25, after choices:
+(1,-2)..controls (1.37755,-1.71404) and (1.71404,-1.37755)
+ ..(2,-1)..controls (2.33353,-0.55965) and (2.59729,-0.04124)
+ ..(2.5,0.5)..controls (2.36812,1.23369) and (1.6712,1.65662)
+ ..(1,2)..controls (0.66821,2.16974) and (0.33485,2.33641)
+ ..(0,2.5)
+
+{(unknown path qw)=(path)}
+{numeric}
+! Enormous number has been reduced.
+l.26 ...[$] ]]=10000000000000000
+ ; "this string constant is in...
+I can't handle numbers bigger than about 4095.99998;
+so I've changed your constant to that maximum amount.
+
+{(p[[ [-1] ]])=(4095.99998)}
+## p[[ [-1] ]]=4095.99998
+! Incomplete string token has been flushed.
+l.26 ...g constant is incomplete
+
+Strings should finish on the same line as they began.
+I've deleted the partial string; you might want to
+insert another by typing, e.g., `I"new string"'.
+
+{string}
+! Declared variable conflicts with previous vardef.
+<to be read again>
+ ,
+l.27 string foo[]p,
+ p~if true:[]; p~000=char34&char200&char34;
+You can't use, e.g., `numeric foo[]' after `vardef foo'.
+Proceed, and I'll ignore the illegal redeclaration.
+
+{if}
+{true}
+{true}
+{char(34)}
+{char(200)}
+{(""")&("^^c8")}
+{char(34)}
+{(""^^c8")&(""")}
+{(unknown string p~0)=(""^^c8"")}
+{boolean}
+{fi}
+{showvariable}
+boolean.boolean=unknown boolean
+! OK.
+l.28 ...n; showvariable boolean;
+ def\\= =end enddef;
+
+{def}
+{picture}
+{show}
+{scantokens}
+{length("^^c8")}
+>> 1
+! OK.
+<recently read> ;
+
+l.29 ...w length scantokens p~0;
+
+
+{pen}
+! Illegal suffix of declared variable will be flushed.
+<to be read again>
+ [
+<to be read again>
+ "a"
+l.30 pen p~[]~,q["a"
+ ,qq; p~1~=q=pencircle scaled mexp(-3016.5...
+Variables in declarations must consist entirely of
+names and collective subscripts, e.g., `x[]a'.
+Are you trying to use a reserved word in a variable name?
+I'm going to discard the junk I found here,
+up to the next comma or the end of the declaration.
+
+{pencircle}
+{-(3016.57654)}
+{mexp(-3016.57654)}
+{(future pen)scaled(0)}
+Pen polygon at line 30 (newly created):
+(0.5,0)
+ .. (0,0.5)
+ .. (-0.5,0)
+ .. (0,-0.5)
+ .. cycle
+
+{(unknown pen q)=(pen)}
+{(unknown pen p~1~)=(pen)}
+{transform}
+! Illegal suffix of declared variable will be flushed.
+<to be read again>
+ 0
+l.31 transform p,pp0
+ ; if p=p:qq=makepen((1,0)..cycle) xscaled...
+Variables in declarations must consist entirely of
+names and collective subscripts, e.g., `x[]a'.
+Explicit subscripts like `x15a' aren't permitted.
+I'm going to discard the junk I found here,
+up to the next comma or the end of the declaration.
+
+{if}
+{((xpart p,ypart p,xxpart p,xypart p,yxpart p,yypart p))=((xpart p,ypart
+ p,xxpart p,xypart p,yxpart p,yypart p))}
+{true}
+Path at line 31, before choices:
+(1,0)
+ ..cycle
+
+Path at line 31, after choices:
+(1,0)..controls (1,0) and (1,0)
+ ..cycle
+
+{makepen(path)}
+{hex("1000")}
+! Number too large (4096).
+<to be read again>
+ ;
+l.31 ...cle) xscaled hex "1000";
+ fi
+I have trouble with numbers greater than 4095; watch out.
+
+{(future pen)xscaled(4096)}
+! Pen too large.
+<to be read again>
+ ;
+l.31 ...cle) xscaled hex "1000";
+ fi
+The cycle you specified has a coordinate of 4095.5 or more.
+So I've replaced it by the trivial path `(0,0)..cycle'.
+
+Pen polygon at line 31 (newly created):
+(0,0)
+ .. cycle
+
+{(unknown pen qq)=(pen)}
+{fi}
+Path at line 32, before choices:
+(0,0)
+ ..(1,0)
+ ..(0,1)
+ ..(0,0)
+ ..(1,0)
+ ..(0,1)
+ ..cycle
+
+Path at line 32, after choices:
+(0,0)..controls (0.29056,-0.29056) and (0.75859,-0.30772)
+ ..(1,0)..controls (1.51964,0.66237) and (0.66237,1.51964)
+ ..(0,1)..controls (-0.30772,0.75859) and (-0.29056,0.29056)
+ ..(0,0)..controls (0.29056,-0.29056) and (0.75859,-0.30772)
+ ..(1,0)..controls (1.51964,0.66237) and (0.66237,1.51964)
+ ..(0,1)..controls (-0.30772,0.75859) and (-0.29056,0.29056)
+ ..cycle
+
+{makepen(path)}
+! Pen cycle must be convex.
+<to be read again>
+ ;
+l.32 ...)..(1,0)..(0,1)..cycle);
+
+The cycle you specified either has consecutive equal points
+or turns right or turns through more than 360 degrees.
+So I've replaced it by the trivial path `(0,0)..cycle'.
+
+Pen polygon at line 32 (newly created):
+(0,0)
+ .. cycle
+
+{qq:=pen}
+{vardef}
+! Missing parameter type; `expr' will be assumed.
+<to be read again>
+ )
+l.33 ...ext suffix a,b endtext()
+ )suffix@=show #@; p.a.b() end...
+You should've had `expr' or `suffix' or `text' here.
+
+{expandafter}
+{\}
+{let}
+
+\\->=end
+{outer}
+{pencircle}
+{(future pen)scaled(4.5)}
+{(future pen)yscaled(2)}
+Pen polygon at line 34 (newly created):
+(0.5,-4.5)
+ .. (1,-4)
+ .. (2,-2.5)
+ .. (2.5,0)
+ .. (2,2.5)
+ .. (1,4)
+ .. (0.5,4.5)
+ .. (-0.5,4.5)
+ .. (-1,4)
+ .. (-2,2.5)
+ .. (-2.5,0)
+ .. (-2,-2.5)
+ .. (-1,-4)
+ .. (-0.5,-4.5)
+ .. cycle
+
+{qq:=pen}
+{((6,12))-((xpart p7,ypart p7))}
+{((0,1))transformed((xpart p,ypart p,xxpart p,xypart p,yxpart p,yypart p
+))}
+{(x)-(x)}
+{(2)/(0)}
+>> 2
+! Division by zero.
+<to be read again>
+ ,
+l.35 ...)transformed p=(2/(x-x),
+ 3/0)transformed p;
+You're trying to divide the quantity shown above the error
+message by zero. I'm going to divide it by one instead.
+
+! Division by zero.
+l.35 ...ansformed p=(2/(x-x),3/0
+ )transformed p;
+I'll pretend that you meant to divide by 1.
+
+{((2,3))transformed((xpart p,ypart p,xxpart p,xypart p,yxpart p,yypart p
+))}
+{((linearform,linearform))=((linearform,linearform))}
+## yxpart p=-yypart p
+## xxpart p=-xypart p
+{((-xpart p7+6,-ypart p7+12))=((linearform,linearform))}
+## ypart p7=-ypart p-yypart p+12
+## xpart p7=-xpart p-xypart p+6
+{\}
+{if}
+{string(unknown string p~[-1])}
+{true}
+{(p0.1 0.2)-(p0.1 0.2)}
+! The token `endtext' is no longer a right delimiter.
+l.36 ...1.2-p.1.199999,1 endtext
+ transformed p;
+Strange: This token has lost its former meaning!
+I'll read it as a right delimiter this time;
+but watch out, I'll probably miss it later.
+
+{((0,1))transformed((xpart p,ypart p,-xypart p,xypart p,-yypart p,yypart
+ p))}
+{((linearform,linearform))=((linearform,linearform))}
+## ypart p=-yypart p+6
+#### ypart p7=6
+## xpart p=-xypart p+3
+#### xpart p7=3
+{(unknown path p1 2p)=((3,6))}
+{showstopping:=0}
+{showvariable}
+p=(-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p)
+p[]=pair
+p[][]=numeric
+p[][]p=unknown path
+p[][]p~=macro:(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup ETC.
+p[]~=unknown boolean
+p~=path
+p~[]=unknown string
+p~[]~=unknown pen
+p~[]~[][]=unknown picture
+p~[-1]=unknown string p~[-1]
+p~0=""^^c8""
+p~1~=pen
+p[[ [] ]]=numeric
+p[[ [-1] ]]=4095.99998
+p0.1 0.2=p0.1 0.2
+p1 2p=path
+p7=(3,6)
+{((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))=((-xy
+part p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))}
+{let}
+{let}
+{xxpart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{(-xypart p)+(0.002)}
+{yxpart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{((xpart p2,ypart p2))-((xpart p1,ypart p1))}
+{(1)*((linearform,linearform))}
+{((xpart p1,ypart p1))+((linearform,linearform))}
+{(y)+(0.00002)}
+{yypart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{xypart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{((5,y))=((yypart p,xypart p))}
+## y=xypart p
+## yypart p=5
+#### ypart p=1
+#### yxpart p=-5
+{((5,xypart p+0.00002))=((5,xypart p))}
+{((xpart p2,ypart p2))=((5,xypart p))}
+## ypart p2=xypart p
+## xpart p2=5
+{((-xypart p+0.002,-5))=((5,xypart p))}
+## xypart p=-5
+#### ypart p2=-5
+#### y=-5
+#### xpart p=8
+#### xxpart p=5
+! Inconsistent equation (off by -0.002).
+<to be read again>
+ ;
+l.38 ...,y)=(yypart p,xypart p);
+
+The equation I just read contradicts what was said before.
+But don't worry; continue and I'll just ignore it.
+
+Path at line 39, before choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+ ..cycle
+
+Path at line 39, after choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)..controls (17.52783,8.54388) and (-15.45978,-4.12262)
+ ..cycle
+
+{reverse(path)}
+{(path)transformed((8,1,5,-5,-5,5))}
+{(path)=(unknown path p2 3p)}
+
+p1 2p~(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup.show(SUFFIX0);p(SUFF
+IX2)(SUFFIX3)((EXPR4)endgroup
+(SUFFIX0)<-p1 2p
+(SUFFIX1)<-~
+(SUFFIX2)<-
+(SUFFIX3)<-2 3p~
+! A primary expression can't begin with `right delimiter that matches ('
+.
+<inserted text>
+ 0
+<to be read again>
+ )
+l.40 ...000000001]2p~(,[2]3p~,-)
+ =p~1~2[pausing];
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+{-(0)}
+(EXPR4)<-0
+(SUFFIX5)<-
+{begingroup}
+{show}
+>> Path at line 40:
+(3,6)
+
+
+p2 3p~(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup.show(SUFFIX0);p(SUFF
+IX2)(SUFFIX3)((EXPR4)endgroup
+(SUFFIX0)<-p2 3p
+(SUFFIX1)<-~
+! Missing `,' has been inserted.
+<to be read again>
+ (0)
+p1 2p~->...IX2)(SUFFIX3)((EXPR4)
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've finished reading a macro argument and am about to
+read another; the arguments weren't delimited correctly.
+You might want to delete some tokens before continuing.
+
+(SUFFIX2)<-
+! Missing `,' has been inserted.
+<to be read again>
+ (0)
+p1 2p~->...IX2)(SUFFIX3)((EXPR4)
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've finished reading a macro argument and am about to
+read another; the arguments weren't delimited correctly.
+You might want to delete some tokens before continuing.
+
+(SUFFIX3)<-
+! Missing `)' has been inserted.
+<to be read again>
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've gotten to the end of the macro parameter list.
+You might want to delete some tokens before continuing.
+
+(EXPR4)<-0
+(SUFFIX5)<-
+{begingroup}
+{show}
+>> Path at line 40:
+(8,1)..controls (-48.68579,57.68579) and (52.91974,-43.91974)
+ ..(28,-19)..controls (-7,16) and (63,-54)
+ ..cycle
+
+>> (8,1,5,-5,-5,5)
+! Isolated expression.
+<to be read again>
+ (
+p2 3p~->...;p(SUFFIX2)(SUFFIX3)(
+ (EXPR4)endgroup
+<to be read again>
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I couldn't find an `=' or `:=' after the
+expression that is shown above this error message,
+so I guess I'll just ignore it and carry on.
+
+! Extra tokens will be flushed.
+<to be read again>
+ (
+p2 3p~->...;p(SUFFIX2)(SUFFIX3)(
+ (EXPR4)endgroup
+<to be read again>
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+{endgroup}
+{endgroup}
+{(vacuous)=(unknown picture p~1~2 1)}
+>> vacuous
+>> unknown picture p~1~2 1
+! Equation cannot be performed (vacuous=unknown picture).
+<to be read again>
+ ;
+l.40 ...2]3p~,-)=p~1~2[pausing];
+
+I'm sorry, but I don't know how to make such things equal.
+(See the two expressions just above the error message.)
+
+{vardef}
+Runaway definition?
+if.p(SUFFIX1)(SUFFIX1)=(SUFFIX1)(SUFFIX1)p.fi
+! Forbidden token found while scanning the definition of p~[].
+<inserted text>
+ enddef
+<to be read again>
+ ;
+l.41 ...iary t:=if p@ @=@ @p fi;
+ vardef p[][]p~[]=BAD; inner ;;
+I suspect you have forgotten an `enddef',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{vardef}
+! This variable already starts with a macro.
+l.41 ...@p fi; vardef p[][]p~[]=
+ BAD; inner ;;
+After `vardef a' you can't say `vardef a.b'.
+So I'll have to discard this definition.
+
+Runaway definition?
+BAD
+! Forbidden token found while scanning the definition of a bad variable.
+<inserted text>
+ enddef
+<to be read again>
+ ;
+l.41 ...i; vardef p[][]p~[]=BAD;
+ inner ;;
+I suspect you have forgotten an `enddef',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{inner}
+{show}
+{-(2)}
+
+p~[-2]@#<tertiary>->begingroup.if.p(SUFFIX1)(SUFFIX1)=(SUFFIX1)(SUFFIX1)
+p.fi.endgroup
+(SUFFIX0)<-p~
+(SUFFIX1)<-[-2]
+(SUFFIX2)<-~
+! A tertiary expression can't begin with `['.
+<inserted text>
+ 0
+<to be read again>
+ [
+<to be read again>
+ (3000)
+<to be read again>
+ ,
+l.42 show p~[-2]~[3000,
+ x]++4000>path p3; showvariable p,P;
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+{(x)-(3000)}
+{(0)*(x-3000)}
+{(3000)+(0)}
+{(3000)++(4000)}
+(EXPR3)<-4999.99998
+{begingroup}
+{if}
+{(p[-2][-2])=(-2)}
+>> p[-2][-2]+2
+! Unknown relation will be considered false.
+<to be read again>
+ [-2]
+p~[-2]->...1)=(SUFFIX1)(SUFFIX1)
+ p.fi.endgroup
+<to be read again>
+ >
+l.42 show p~[-2]~[3000,x]++4000>
+ path p3; showvariable p,P;
+Oh dear. I can't decide if the expression above is positive,
+negative, or zero. So this comparison test won't be `true'.
+
+{false}
+! Missing `:' has been inserted.
+<to be read again>
+ [-2]
+p~[-2]->...1)=(SUFFIX1)(SUFFIX1)
+ p.fi.endgroup
+<to be read again>
+ >
+l.42 show p~[-2]~[3000,x]++4000>
+ path p3; showvariable p,P;
+There should've been a colon after the condition.
+I shall pretend that one was there.
+
+{endgroup}
+{path((xpart p3,ypart p3))}
+{(vacuous)>(false)}
+>> vacuous
+>> false
+! Not implemented: (vacuous)>(boolean).
+<to be read again>
+ ;
+l.42 ...~[3000,x]++4000>path p3;
+ showvariable p,P;
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+>> false
+{showvariable}
+p=(8,1,5,-5,-5,5)
+p[]=pair
+p[][]=numeric
+p[][]p=unknown path
+p[][]p~=macro:(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup ETC.
+p[]~=unknown boolean
+p~=path
+p~[]@#=macro:<tertiary>->begingroup.if.p(SUFFIX1)(SUFFIX1) ETC.
+p[[ [] ]]=numeric
+p[[ [-1] ]]=4095.99998
+p[-2][-2]=p[-2][-2]
+p0.1 0.2=p0.1 0.2
+p1=(xpart p1,ypart p1)
+p1 2p=path
+p2=(5,-5)
+p2 3p=path
+p3=(xpart p3,ypart p3)
+p7=(3,6)
+> P=tag
+{numeric}
+{(2)*(alpha)}
+{(p3~)=(2alpha)}
+## alpha=0.5p3~
+{(1)/(-1)}
+{(3)*(beta)}
+{(p[-1]~)=(3beta)}
+## beta=0.33333p[-1]~
+{begingroup}
+{save}
+{showvariable}
+> p=tag
+{(3)*(0.33333(SAVED)p[-1]~)}
+{((SAVED)p[-1]~)=(1)}
+## (SAVED)p[-1]~=1
+#### beta=0.33333
+{restoring p}
+{endgroup}
+{showvariable}
+p=(8,1,5,-5,-5,5)
+p[]=pair
+p[][]=numeric
+p[][]p=unknown path
+p[][]p~=macro:(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup ETC.
+p[]~=numeric
+p~=path
+p~[]@#=macro:<tertiary>->begingroup.if.p(SUFFIX1)(SUFFIX1) ETC.
+p[[ [] ]]=numeric
+p[[ [-1] ]]=4095.99998
+p[-2][-2]=p[-2][-2]
+p[-1]~=1
+p0.1 0.2=p0.1 0.2
+p1=(xpart p1,ypart p1)
+p1 2p=path
+p2=(5,-5)
+p2 3p=path
+p3=(xpart p3,ypart p3)
+p3~=p3~
+p7=(3,6)
+{def}
+{def}
+! Missing `=' has been inserted.
+<to be read again>
+ false
+l.45 ...enddef;def!primary!false
+ ):!fi enddef;
+The next thing in this `def' should have been `=',
+because I've already looked at the definition heading.
+But don't worry; I'll pretend that an equals sign
+was present. Everything from here to `enddef'
+will be the replacement text of this macro.
+
+{def}
+{(path)scaled(-1)}
+Path at line 46, before choices:
+(1,-2)..controls (1.37755,-1.71404) and (1.71404,-1.37755)
+ ..(2,-1)..controls (2.33353,-0.55965) and (2.59729,-0.04124)
+ ..(2.5,0.5)..controls (2.36812,1.23369) and (1.6712,1.65662)
+ ..(1,2)..controls (0.66821,2.16974) and (0.33485,2.33641)
+ ..(0,2.5)
+ ..(-1,2)..controls (-1.37755,1.71404) and (-1.71404,1.37755)
+ ..(-2,1)..controls (-2.33353,0.55965) and (-2.59729,0.04124)
+ ..(-2.5,-0.5)..controls (-2.36812,-1.23369) and (-1.6712,-1.65662)
+ ..(-1,-2)..controls (-0.66821,-2.16974) and (-0.33485,-2.33641)
+ ..(0,-2.5)
+ ..cycle
+
+Path at line 46, after choices:
+(1,-2)..controls (1.37755,-1.71404) and (1.71404,-1.37755)
+ ..(2,-1)..controls (2.33353,-0.55965) and (2.59729,-0.04124)
+ ..(2.5,0.5)..controls (2.36812,1.23369) and (1.6712,1.65662)
+ ..(1,2)..controls (0.66821,2.16974) and (0.33485,2.33641)
+ ..(0,2.5)..controls (-0.37186,2.68167) and (-0.668,2.25146)
+ ..(-1,2)..controls (-1.37755,1.71404) and (-1.71404,1.37755)
+ ..(-2,1)..controls (-2.33353,0.55965) and (-2.59729,0.04124)
+ ..(-2.5,-0.5)..controls (-2.36812,-1.23369) and (-1.6712,-1.65662)
+ ..(-1,-2)..controls (-0.66821,-2.16974) and (-0.33485,-2.33641)
+ ..(0,-2.5)..controls (0.37186,-2.68167) and (0.668,-2.25146)
+ ..cycle
+
+{makepen(path)}
+Pen polygon at line 46 (newly created):
+(1,-2)
+ .. (2,-1)
+ .. (2.5,0.5)
+ .. (1,2)
+ .. (0,2.5)
+ .. (-1,2)
+ .. (-2,1)
+ .. (-2.5,-0.5)
+ .. (-1,-2)
+ .. (0,-2.5)
+ .. cycle
+
+{qq:=pen}
+{primarydef}
+{secondarydef}
+
+//<expr>->
+
+//<expr>->
+{pencircle}
+{length(path)}
+{(future pen)slanted(1)}
+{((3,6))-((5,-5))}
+
+_aa__<secondary>->if(true
+{(0.1)*(15)}
+{odd(1.50009)}
+{not(false)}
+{known((8,1,5,-5,-5,5))}
+{(true)and(true)}
+(EXPR0)<-true
+{if}
+{true}
+
+!<primary>->false):(EXPR0)fi
+(EXPR0)<-(5,-5)
+{false}
+{(true)or(false)}
+{true}
+{fi}
+{-((5,-5))}
+{-((5,-5))}
+{+(1)}
+{-(1)}
+>> -1
+! Improper curl has been replaced by 1.
+<to be read again>
+ )
+l.51 {curl- +1)
+ ..tension atleast1..cycle sqrt2++sqrt2***[[]];
+A curl must be a known, nonnegative number.
+
+! Missing `}' has been inserted.
+<to be read again>
+ )
+l.51 {curl- +1)
+ ..tension atleast1..cycle sqrt2++sqrt2***[[]];
+I've scanned a direction spec for part of a path,
+so a right brace should have come next.
+I shall pretend that one was there.
+
+Path at line 51, before choices:
+(-5,5)
+
+Path at line 51, after choices:
+(-5,5)
+
+Path at line 51, before choices:
+(3,6)..controls (5,-5) and (-5,5)
+ ..(-5,5)..tension atleast1
+ ..{2896.30943,-2896.30934}cycle
+
+Path at line 51, after choices:
+(3,6)..controls (5,-5) and (-5,5)
+ ..(-5,5)..controls (-3.29726,7.86205) and (0.64516,8.35484)
+ ..cycle
+
+! Missing `)' has been inserted.
+<to be read again>
+ sqrt
+l.51 ...ion atleast1..cycle sqrt
+ 2++sqrt2***[[]];
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+{((-2,11))subpath(path)}
+{reverse(path)}
+{makepen(path)}
+
+**->[[show(EXPR0)*(EXPR1)]]
+(EXPR0)<-future pen
+(EXPR1)<-future pen
+{begingroup}
+{show}
+{(future pen)*(future pen)}
+>> future pen
+>> future pen
+! Not implemented: (future pen)*(future pen).
+<to be read again>
+ ]]
+<to be read again>
+ sqrt
+l.51 ...ion atleast1..cycle sqrt
+ 2++sqrt2***[[]];
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+! Pen path must be a cycle.
+<to be read again>
+ ]]
+<to be read again>
+ sqrt
+l.51 ...ion atleast1..cycle sqrt
+ 2++sqrt2***[[]];
+I can't make a pen from the given path.
+So I've replaced it by the trivial path `(0,0)..cycle'.
+
+>> Pen polygon at line 51:
+(0,0)
+ .. cycle
+
+{endgroup}
+(EXPR0)<-vacuous
+{sqrt(2)}
+{sqrt(2)}
+{(1.41422)++(1.41422)}
+{begingroup}
+{endgroup}
+
+***->expandafter(EXPR1)scantokens"**oct"(EXPR0)
+(EXPR0)<-2
+(EXPR1)<-vacuous
+{expandafter}
+{scantokens}
+{oct(2)}
+>> 2
+! Not implemented: oct(known numeric).
+<to be read again>
+ ;
+l.51 ...cle sqrt2++sqrt2***[[]];
+
+I'm afraid I don't know how to apply that operation to that
+particular type. Continue, and I'll simply return the
+argument (shown above) as the result of the operation.
+
+
+**->[[show(EXPR0)*(EXPR1)]]
+(EXPR0)<-vacuous
+(EXPR1)<-2
+{begingroup}
+{show}
+{(vacuous)*(2)}
+>> vacuous
+>> 2
+! Not implemented: (vacuous)*(known numeric).
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.51 ...cle sqrt2++sqrt2***[[]];
+
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+>> 2
+{endgroup}
+(EXPR0)<-vacuous
+{begingroup}
+{interim}
+{-(20.5)}
+{charcode:=-20.5}
+{proofing:=-20.5}
+{-(2048)}
+{chardp:=-2048}
+{shipout}
+{nullpicture}
+! Enormous chardp has been reduced.
+<to be read again>
+ ]]
+l.52 ...48;shipout nullpicture]]
+ ;
+Font metric dimensions must be less than 2048pt.
+
+{restoring proofing=0}
+{endgroup}
+{if}
+{-(275.50002)}
+{charexists(-275.50002)}
+{known(unknown path p0 0p)}
+{(true)>(false)}
+{known(path)}
+{(true)=(true)}
+{true}
+{randomseed}
+! Missing `:=' has been inserted.
+<to be read again>
+ charcode
+l.53 ... p~: randomseed charcode
+ ; fi
+Always say `randomseed:=<numeric expression>'.
+
+{randomseed:=-20.5}
+{fi}
+{randomseed}
+>> "goof"
+! Unknown value will be ignored.
+<to be read again>
+ ;
+l.54 randomseed:="goof";
+ a[($,18++1+-+18),(2,3)]=b[(3,2),(1,$);
+Your expression was too random for me to handle,
+so I won't change the random seed just now.
+
+{(18)++(1)}
+{(18.02776)+-+(18)}
+{((2,3))-((-1,1))}
+{(a)*((3,2))}
+{((-1,1))+((3a,2a))}
+! Missing `]' has been inserted.
+<to be read again>
+ ;
+l.54 ...8),(2,3)]=b[(3,2),(1,$);
+
+I've scanned an expression of the form `a[b,c',
+so a right bracket should have come next.
+I shall pretend that one was there.
+
+{((1,-1))-((3,2))}
+{(b)*((-2,-3))}
+{((3,2))+((-2b,-3b))}
+{((3a-1,2a+1))=((-2b+3,-3b+2))}
+## b=-0.66667a+0.33333
+## a=2
+#### b=-1
+{show}
+{(^)+(1)}
+{(~)+(2)}
+{-(1)}
+{((^+1,~+2))slanted(-1)}
+{-(2)}
+{((linearform,~+2))yscaled(-2)}
+{-((3,4))}
+{((linearform,-2~-4))zscaled((-3,-4))}
+>> (-5~-3^-13,10~-4^+16)
+{((xpart pp,ypart pp,xxpart pp,xypart pp,yxpart pp,yypart pp))xscaled(9)
+}
+{((8,1,5,-5,-5,5))transformed((9xpart pp,ypart pp,9xxpart pp,9xypart pp,
+yxpart pp,yypart pp))}
+>> (9xpart pp+72xxpart pp+9xypart pp,ypart pp+8yxpart pp+yypart pp,45xxp
+art pp-45xypart pp,-45xxpart pp+45xypart pp,5yxpart pp-5yypart pp,-5yxpa
+rt pp+5yypart pp)
+{((xpart pp,ypart pp,xxpart pp,xypart pp,yxpart pp,yypart pp))shifted((1
+,2))}
+{((8,1,5,-5,-5,5))transformed((8,1,5,-5,-5,5))}
+{((xpart pp+1,ypart pp+2,xxpart pp,xypart pp,yxpart pp,yypart pp))transf
+ormed((43,-34,50,-50,-50,50))}
+>> (50xpart pp-50ypart pp-7,-50xpart pp+50ypart pp+16,50xxpart pp-50yxpa
+rt pp,50xypart pp-50yypart pp,-50xxpart pp+50yxpart pp,-50xypart pp+50yy
+part pp)
+{(2)-(1)}
+{(_0)*(1)}
+{(1)+(_0)}
+{-(_0+1)}
+>> -_0-1
+{show}
+{normaldeviate}
+{-(-2048)}
+{uniformdeviate(2048)}
+{angle((-0.41438,761.67789))}
+{cosd(90.03117)}
+{(200)*(-0.00055)}
+{mexp(-0.10986)}
+{sqrt(0.99957)}
+{mlog(0.99979)}
+{sind(-0.05469)}
+{floor(-0.00096)}
+>> -1
+{string}
+{(unknown string s2)=(unknown string s4)}
+{(unknown string s1)=(unknown string s2)}
+{(unknown string s3)=(unknown string s5)}
+{(unknown string s2)=(unknown string s4)}
+! Redundant equation.
+<to be read again>
+ ;
+l.58 ... s1=s2=s4; s3=s5; s1=s2;
+ if s1<=s4<>(s1<>s3):show[[ch...
+I already knew that this equation was true.
+But perhaps no harm has been done; let's continue.
+
+{if}
+{(unknown string s2)<=(unknown string s1)}
+{(unknown string s2)<>(unknown string s5)}
+>> unknown string s2
+>> unknown string s5
+! Unknown relation will be considered false.
+<to be read again>
+ )
+l.58 ...=s2; if s1<=s4<>(s1<>s3)
+ :show[[char34=s2:=s3]]fi;
+The quantities shown above have not been equated.
+
+{(true)<>(false)}
+{true}
+{show}
+{begingroup}
+{char(34)}
+{s2:=unknown string s5}
+{(""")=(unknown string s2)}
+{endgroup}
+{fi}
+>> vacuous
+{(0.1)point(path)}
+{begingroup}
+{pencircle}
+Pen polygon at line 59 (newly created):
+(0.5,0)
+ .. (0,0.5)
+ .. (-0.5,0)
+ .. (0,-0.5)
+ .. cycle
+
+{endgroup}
+{((3.24413,0.64801))penoffset(pen)}
+{((0,-0.5))rotated(540)}
+{((0,0.5))/(0.33333)}
+{-(1.5)}
+! Paths don't touch; `&' will be changed to `..'.
+<to be read again>
+ )
+l.60 ...tcontrol-1.5of(p~&cycle)
+ -precontrol1/2of p~(p~)=s1:=s...
+When you join paths `p&q', the ending point of p
+must be exactly equal to the starting point of q.
+So I'm going to pretend that you said `p..q' instead.
+
+Path at line 60, before choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+ ..cycle
+
+Path at line 60, after choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)..controls (17.52783,8.54388) and (-15.45978,-4.12262)
+ ..cycle
+
+{(-1.5)postcontrol(path)}
+{(0.5)precontrol(path)}
+{((-2.75,-5))-((3.75,-1))}
+! Missing `of' has been inserted for directiontime.
+<to be read again>
+ (
+l.60 ...cle)-precontrol1/2of p~(
+ p~)=s1:=s4:=s4;
+I've got the first argument; will look now for the other.
+
+{((-6.5,-4))directiontime(path)}
+{decimal(0.5)}
+{((0,1.50003))substring("0.5")}
+{s4:=unknown string s1}
+{s1:=unknown string s4}
+{("0.")=(unknown string s1)}
+{path}
+{length(" ")}
+! Missing `,' has been inserted.
+<to be read again>
+ }
+l.61 ...p~[]; p~1=p2{length" "}
+ &cycle; p~1=p2=p~0; p2..contr...
+I've got the x coordinate of a path direction;
+will look for the y coordinate next.
+
+! An expression can't begin with `}'.
+<inserted text>
+ 0
+<to be read again>
+ }
+l.61 ...p~[]; p~1=p2{length" "}
+ &cycle; p~1=p2=p~0; p2..contr...
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+Path at line 61, before choices:
+(5,-5){4096,0}
+ ..{4096,0}cycle
+
+Path at line 61, after choices:
+(5,-5)..controls (5,-5) and (5,-5)
+ ..cycle
+
+{(unknown path p~1)=(path)}
+{((5,-5))=(unknown path p~0)}
+{(path)=(path)}
+! Redundant or inconsistent equation.
+<to be read again>
+ ;
+l.61 ..." "}&cycle; p~1=p2=p~0;
+ p2..controls-p2..cycle=p~2;(p7
+An equation between already-known quantities can't help.
+But don't worry; continue and I'll just ignore it.
+
+{-((5,-5))}
+Path at line 61, before choices:
+(5,-5)..controls (-5,5) and (-5,5)
+ ..cycle
+
+Path at line 61, after choices:
+(5,-5)..controls (-5,5) and (-5,5)
+ ..cycle
+
+{(path)=(unknown path p~2)}
+{length(path)}
+Path at line 62, before choices:
+(3,6)..tension 1.2
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5){0,4096}..tension 1 and atleast1
+ ..cycle
+
+Path at line 62, after choices:
+(3,6)..controls (-3.01212,4.82085) and (-1.0424,-6.01257)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..cycle
+
+>> x
+! Improper tension has been set to 1.
+<to be read again>
+ ..
+l.63 x..
+ {curl1}-p7{curl hex "IsBad"}..tension.75and.74999..p...
+The expression above should have been a number >=3/4.
+
+{-((3,6))}
+{hex("IsBad")}
+>> "IsBad"
+! String contains illegal digits.
+<to be read again>
+ }
+l.63 ...l1}-p7{curl hex "IsBad"}
+ ..tension.75and.74999..p2{0,1...
+I zeroed out characters that weren't hex digits.
+
+>> 0.74998
+! Improper tension has been set to 1.
+<to be read again>
+ ..
+l.63 ...}..tension.75and.74999..
+ p2{0,1}&p2{_,'}..cycle:=p
+The expression above should have been a number >=3/4.
+
+>> _
+! Undefined x coordinate has been replaced by 0.
+<to be read again>
+ ,
+l.63 ...and.74999..p2{0,1}&p2{_,
+ '}..cycle:=p
+I need a `known' x value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+>> (xpart ',ypart ')
+! Undefined y coordinate has been replaced by 0.
+<to be read again>
+ }
+l.63 ...d.74999..p2{0,1}&p2{_,'}
+ ..cycle:=p
+I need a `known' y value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+Path at line 63, before choices:
+(3,6)..controls (-3.01212,4.82085) and (-1.0424,-6.01257)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..(3,6)
+ ..{curl 1}(-3,-6){curl 2989}..tension 0.75 and 1
+ ..{0,4096}(5,-5){curl 1}
+ ..cycle
+
+Path at line 63, after choices:
+(3,6)..controls (-3.01212,4.82085) and (-1.0424,-6.01257)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..(3,6)..controls (-2.44821,4.93144) and (-5.41408,-1.00029)
+ ..(-3,-6)..controls (-3.70045,-12.19485) and (5,-11.61679)
+ ..(5,-5)..controls (12.45406,-3.75087) and (10.41669,7.45464)
+ ..cycle
+
+>> path
+! Improper `:=' will be changed to `='.
+l.63 ...p2{0,1}&p2{_,'}..cycle:=
+ p
+I didn't find a variable name at the left of the `:=',
+so I'm going to pretend that you said `=' instead.
+
+{(path)=(unknown path p~4)}
+{-(9)}
+{((3.5001,7.00002))subpath(path)}
+{((-9,9))subpath(path)}
+{(path)=(unknown path p~6)}
+{show}
+>> Path at line 65:
+(-2.5,2.5)..controls (-2.49893,2.49893) and (0.00107,-0.00107)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..(3,6)..controls (-2.44821,4.93144) and (-5.41408,-1.00029)
+ ..(-3,-6)..controls (-3.70045,-12.19485) and (5,-11.61679)
+ ..(5,-5)..controls (5.0001,-4.99998) and (5.00021,-4.99997)
+ ..(5.00032,-4.99995)
+
+{((1,2))directiontime(path)}
+>> 1
+{-(1.00002)}
+{((1,-1.00002))directiontime(path)}
+>> 3.24937
+Path at line 66, before choices:
+(0,0)..controls (1,1) and (0,1)
+ ..(1,0)
+
+Path at line 66, after choices:
+(0,0)..controls (1,1) and (0,1)
+ ..(1,0)
+
+{(unknown path p~3)=(path)}
+{show}
+Path at line 66, before choices:
+(5,-5){-2896.30943,-2896.30934}
+ ..{1831.78674,3663.57385}cycle
+
+Path at line 66, after choices:
+(5,-5)..controls (5,-5) and (5,-5)
+ ..cycle
+
+>> Path at line 66:
+(5,-5)..controls (5,-5) and (5,-5)
+ ..cycle
+
+{((0.31416,1))subpath(path)}
+{((1,1))directiontime(path)}
+{(1)-(0.31416)}
+{(0.27097)*(0.68584)}
+{(0.31416)+(0.18584)}
+>> 0.5
+{((0,0.25))subpath(path)}
+{((0.25,1))subpath(path)}
+Path at line 68, before choices:
+(0,0)..controls (0.25,0.25) and (0.375,0.4375)
+ ..(0.4375,0.5625)..controls (0.625,0.9375) and (0.25,0.75)
+ ..(1,0)
+
+Path at line 68, after choices:
+(0,0)..controls (0.25,0.25) and (0.375,0.4375)
+ ..(0.4375,0.5625)..controls (0.625,0.9375) and (0.25,0.75)
+ ..(1,0)
+
+! Missing `endtext' has been inserted.
+<to be read again>
+ ;
+l.68 ...~3)shifted begintext1,0;
+
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+{(path)shifted((1,0))}
+{(unknown path p~5)=(path)}
+{(2/3)*((xpart ',ypart '))}
+{((0.66667xpart ',0.66667ypart '))zscaled((xpart ',ypart '))}
+>> (0,0,xpart ',-ypart ',ypart ',xpart ')
+! Transform components aren't all known.
+<to be read again>
+ {
+l.69 p~3:=2/3'zscaled'{
+ p~3}..controls(2,2/3(3))and penoffset(...
+I'm unable to apply a partially specified transformation
+except to a fully known pair or transform.
+Proceed, and I'll omit the transformation.
+
+>> 0.66667xpart '
+! Undefined x coordinate has been replaced by 0.
+<to be read again>
+ {
+l.69 p~3:=2/3'zscaled'{
+ p~3}..controls(2,2/3(3))and penoffset(...
+I need a `known' x value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+>> 0.66667ypart '
+! Undefined y coordinate has been replaced by 0.
+<to be read again>
+ {
+l.69 p~3:=2/3'zscaled'{
+ p~3}..controls(2,2/3(3))and penoffset(...
+I need a `known' y value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+>> path
+! Undefined coordinates have been replaced by (0,0).
+<to be read again>
+ }
+l.69 p~3:=2/3'zscaled'{p~3}
+ ..controls(2,2/3(3))and penoffset(...
+I need x and y numbers for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+{(2/3)*(3)}
+{(1/2)*(x)}
+{((0.5x,-5))penoffset((0,1))}
+>> (0.5x,-5)
+>> (0,1)
+! Not implemented: penoffset(unknown pair)of(pair).
+<to be read again>
+ (
+l.69 ...enoffset(1/2x,y)of(0,1)(
+ 1,0);
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+! Missing `..' has been inserted.
+<to be read again>
+ (
+l.69 ...enoffset(1/2x,y)of(0,1)(
+ 1,0);
+A path join command should end with two dots.
+
+Path at line 69, before choices:
+(0,0)..controls (2,2) and (0,1)
+ ..(1,0)
+
+Path at line 69, after choices:
+(0,0)..controls (2,2) and (0,1)
+ ..(1,0)
+
+{p~3:=path}
+{show}
+{reverse(path)}
+{(path)intersectiontimes(path)}
+>> (0.17227,0.28339)
+{(0.17227)point(path)}
+>> (0.71329,0.78188)
+{(1)-(0.28339)}
+{(0.71661)point(path)}
+>> (0.7133,0.78189)
+{show}
+{(path)shifted((0.01,0))}
+{(path)intersectiontimes(path)}
+{xpart((1.47693,1.18973))}
+{(1.47693)point(path)}
+{(path)shifted((0.01,0))}
+{(path)intersectiontimes(path)}
+{ypart((1.47693,1.18973))}
+{(1.18973)point(path)}
+{((1.50499,0.71521))-((1.495,0.71521))}
+>> (0.01,0)
+{begingroup}
+{interim}
+{tracingedges:=1}
+{-(1)}
+{-(1.00002)}
+{(-1)+-+(-1.00002)}
+! Pythagorean subtraction 1+-+1.00002 has been replaced by 0.
+l.73 ...es:=1; e[-1+-+ -1.00001]
+ =nullpicture; addto e1 also[[
+Since I don't take square roots of negative numbers,
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{nullpicture}
+{(unknown picture e0)=(picture)}
+{addto}
+{begingroup}
+{addto}
+{(path)scaled(3)}
+Path at line 74, before subdivision into octants:
+(3,0)..controls (3.75,0.75) and (4.125,1.3125)
+ ..(4.3125,1.6875)..controls (4.875,2.8125) and (3.75,2.25)
+ ..(6,0)..controls (3.75,2.25) and (4.875,2.8125)
+ ..(4.3125,1.6875)..controls (4.125,1.3125) and (3.75,0.75)
+ ..cycle
+
+Cycle spec at line 74, after subdivision:
+(3,0) % beginning in octant `NNE'
+ ..controls (3.75,0.75) and (4.125,1.3125)
+ ..(4.3125,1.6875) % segment 0
+ ..controls (4.5,2.0625) and (4.5,2.25)
+ ..(4.5,2.25) % segment 1
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+% entering octant `SSW'
+% entering octant `SSE'
+ ..controls (4.5,2.25) and (4.5,1.5)
+ ..(6,0) % segment 1
+% entering octant `ESE'
+% entering octant `ENE'
+% entering octant `NNE'
+% entering octant `NNW'
+ ..controls (4.5,1.5) and (4.5,2.25)
+ ..(4.5,2.25) % segment 2
+% entering octant `WNW'
+% entering octant `WSW'
+% entering octant `SSW'
+ ..controls (4.5,2.25) and (4.5,2.0625)
+ ..(4.3125,1.6875) % segment 2
+ ..controls (4.125,1.3125) and (3.75,0.75)
+ ..(3,0) % segment 3
+% entering octant `SSE'
+% entering octant `ESE'
+% entering octant `ENE'
+ & cycle
+
+Tracing edges at line 74: (weight 1)
+(4,0)(4,1)(5,1)(5,3)(4,3)(4,1)(5,1)(5,0)(6,0)(6,1)(5,1)(5,3)
+(4,3)(4,1)(3,1)(3,0).
+
+{(unknown picture e1)=(unknown picture e2)}
+{(picture)=(unknown picture e1)}
+{cull}
+{nullpicture}
+{endgroup}
+{show}
+{-(4095)}
+{(picture)shifted((4089,-4095))}
+>> Edge structure at line 75:
+row -4093: | 4093+ 4094-
+row -4094: | 4093+ 4094-
+row -4095: | 4092+ 4093- 4094+ 4095-
+
+{-(4095)}
+{(picture)shifted((-4095,4092))}
+{-(3)}
+{(picture)shifted((-3,0))}
+>> Edge structure at line 75:
+row 4094: -4094+ -4093- -4094+ -4093- |
+row 4093: -4094+ -4093- -4094+ -4093- |
+row 4092: -4095+ -4092- -4093+ -4094- |
+
+{-(4095)}
+{(picture)shifted((4089,-4095))}
+! Too far to shift.
+<to be read again>
+ ]]
+l.76 e2 shifted(4089,-4095)]]
+ ;addto e1 also e2 shifted(-2,$)...
+I can't shift the picture as requested---it would
+make some coordinates too large or too small.
+Proceed, and I'll omit the transformation.
+
+>> Edge structure at line 76:
+row 2: 4+ 5- 4+ 5- |
+row 1: 4+ 5- 4+ 5- |
+row 0: 3+ 6- 5+ 4- |
+
+{restoring tracingedges=32767.99998}
+{endgroup}
+{addto}
+{-(2)}
+{(picture)shifted((-2,-1))}
+{-(4)}
+{(picture)shifted((-4,-1))}
+{e1:=picture}
+{addto}
+{(picture)rotated(89.999)}
+! That transformation is too hard.
+<to be read again>
+ +
+l.77 ...e0 also e1rotated89.999+
+ e1scaled$; show e0 xscaled-10...
+I can apply complicated transformations to paths,
+but I can only do integer operations on pictures.
+Proceed, and I'll omit the transformation.
+
+{(picture)scaled(-1)}
+{(picture)+(picture)}
+{show}
+{-(10)}
+{(picture)xscaled(-10)}
+{(picture)yscaled(2)}
+{(picture)xscaled(82)}
+! Scaled picture would be too big.
+<to be read again>
+ yscaled
+l.78 yscaled
+ 683;addto e1 doublepath (0,9) withweight-3 withwe...
+I can't xscale the picture as requested---it would
+make some coordinates too large or too small.
+Proceed, and I'll omit the transformation.
+
+{(picture)yscaled(683)}
+! Scaled picture would be too big.
+<to be read again>
+ ;
+l.78 yscaled683;
+ addto e1 doublepath (0,9) withweight-3 withwe...
+I can't yscale the picture as requested---it would
+make some coordinates too large or too small.
+Proceed, and I'll omit the transformation.
+
+>> Edge structure at line 78:
+row 5: -40- -50+ -40- -50+ |
+row 4: -40- -50+ -40- -50+ |
+row 3: -30+ 0- -10+ -20- -40- -50+ -40- -50+ | -10+ 0-
+row 2: -30+ 0- -10+ -20- -40- -50+ -40- -50+ | -10+ 0-
+row 1: 20- 10+ 20- 10+ -20+ -10- -20+ -10- -30- -60+ -50- -40+ |
+ -10+ -10+ 0- 0- 10+ 20-
+row 0: 20- 10+ 20- 10+ -20+ -10- -20+ -10- -30- -60+ -50- -40+ |
+ -10+ -10+ 0- 0- 10+ 20-
+row -1: 20- 10+ 20- 10+ -20+ -10- -20+ -10- | -20+ -10- 0+ 0+ 10-
+ 10-
+row -2: 20- 10+ 20- 10+ -20+ -10- -20+ -10- | -20+ -10- 0+ 0+ 10-
+ 10-
+row -3: 30- 0+ 10- 20+ | 0+ 10-
+row -4: 30- 0+ 10- 20+ | 0+ 10-
+
+{addto}
+{-(3)}
+{turningnumber(path)}
+! Weight must be -3, -2, -1, +1, +2, or +3.
+<to be read again>
+ withpen
+l.79 withpen
+ pencircle xscaled(oct"180"++1) rotated-angle(64...
+I'll ignore the bad `with' clause and look for another.
+
+{pencircle}
+{oct("180")}
+>> "180"
+! String contains illegal digits.
+<to be read again>
+ ++
+l.79 ...ircle xscaled(oct"180"++
+ 1) rotated-angle(64,$) shifte...
+I zeroed out characters that weren't in the range 0..7.
+
+{(64)++(1)}
+{(future pen)xscaled(64.00781)}
+{angle((64,-1))}
+{-(-0.89517)}
+{(future pen)rotated(0.89517)}
+{(future pen)shifted((9,8))}
+Pen polygon at line 80 (newly created):
+(9,7.5)
+ .. (37.5,8)
+ .. (41,8.5)
+ .. (9,8.5)
+ .. (-19.5,8)
+ .. (-23,7.5)
+ .. cycle
+
+Path at line 80, before choices:
+(-1,-1){curl 1}
+ ..(1,0)
+ ..(1,1)
+ ..(-1,0)
+ ..{curl 1}cycle
+
+Path at line 80, after choices:
+(-1,-1)..controls (-0.18568,-1.14464) and (0.62712,-0.73824)
+ ..(1,0)..controls (1.16457,0.32584) and (1.21637,0.71506)
+ ..(1,1)..controls (0.46898,1.69931) and (-0.7587,1.2167)
+ ..(-1,0)..controls (-1.06546,-0.33012) and (-1.06546,-0.66988)
+ ..cycle
+
+{makepen(path)}
+{(future pen)xscaled(4095.49998)}
+Pen polygon at line 80 (newly created):
+(4095.49998,0)
+ .. (4095.49998,1)
+ .. (-4095.49998,0)
+ .. (-4095.49998,-1)
+ .. cycle
+
+Path at line 80, before subdivision into octants:
+(0,9)..controls (0,9) and (0,9)
+ ..cycle
+
+! Curve out of range.
+<to be read again>
+ ;
+l.80 ...cycle)xscaled4095.49999;
+
+At least one of the coordinates in the path I'm about to
+digitize was really huge (potentially bigger than 4095).
+So I've cut it back to the maximum size.
+The results will probably be pretty wild.
+
+Cycle spec at line 80, after subdivision:
+(0,0) % beginning in octant `ENE'
+% entering octant `NNE'
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ & cycle
+
+Tracing edges at line 80: (weight -3)
+@ Octant ENE (2 offsets), from (-4095.49998,-1) to (4095.49998,0)
+@ transition line 0, from (-4095.49998,-1) to (-4095.49998,-1)
+@ transition line 1, from (-4095.49998,-1) to (4095.49998,0)
+@ transition line 2, from (4095.49998,0) to (4095.49998,0)
+(0,-1)
+@ Octant NNE (1 offset), from (4095.49998,0) to (4095.49998,1)
+@ transition line 2, from (4095.49998,0) to (4095.49998,0)
+@ transition line 1, from (4095.49998,0) to (4095.49998,1)
+(0,0)(4095,0)
+@ Octant NNW (0 offsets), from (4095.49998,1) to (4095.49998,1)
+@ transition line 0, from (4095.49998,1) to (4095.49998,1)
+@ Octant WNW (0 offsets), from (4095.49998,1) to (4095.49998,1)
+@ transition line 1, from (4095.49998,1) to (4095.49998,1)
+@ Octant WSW (2 offsets), from (4095.49998,1) to (-4095.49998,0)
+@ transition line 0, from (4095.49998,1) to (4095.49998,1)
+@ transition line 1, from (4095.49998,1) to (-4095.49998,0)
+@ transition line 2, from (-4095.49998,0) to (-4095.49998,0)
+(4095,1)(0,1)
+@ Octant SSW (1 offset), from (-4095.49998,0) to (-4095.49998,-1)
+@ transition line 2, from (-4095.49998,0) to (-4095.49998,0)
+@ transition line 1, from (-4095.49998,0) to (-4095.49998,-1)
+(0,0)(-4095,0)
+@ Octant SSE (0 offsets), from (-4095.49998,-1) to (-4095.49998,-1)
+@ transition line 0, from (-4095.49998,-1) to (-4095.49998,-1)
+@ Octant ESE (0 offsets), from (-4095.49998,-1) to (-4095.49998,-1)
+@ transition line 1, from (-4095.49998,-1) to (-4095.49998,-1)
+(-4095,-1).
+
+{show}
+>> Edge structure at line 81:
+row 1: | 0+ 1-
+row 0: 0--- 4095+++ -2+ -1- -2+ -1- | 0+ 1-
+row -1: -4095--- 0+++ -2+ -1- -2+ -1- | -1+ 0- 1+ 2-
+row -2: -3+ 0- -1+ -2- |
+
+{totalweight(picture)}
+>> -0.37476
+{charcode:=5}
+{chardp:=5}
+{-(1.5)}
+{xoffset:=-1.5}
+{shipout}
+[5]
+Edge structure at line 81 (just shipped out):
+row 1: | -1+ 0-
+row 0: | -3+ -3+ -2- -2- -1--- -1+ 0- 4094+++
+row -1: | -4096--- -3+ -3+ -2- -2- -2+ -1- -1+++ 0+ 1-
+row -2: | -4+ -3- -2+ -1-
+
+{showstats}
+Memory usage 1084&202 (749 still untouched)
+String usage 24&92 (858&11309 still untouched)
+{addto}
+{(path)yscaled(0.01666)}
+Path at line 82, before subdivision into octants:
+(0,0)..controls (15,0.06665) and (-15,-0.19995)
+ ..(4,0)..controls (-15,-0.19995) and (15,0.06665)
+ ..cycle
+
+Cycle spec at line 82, after subdivision:
+(0,0) % beginning in octant `ENE'
+ ..controls (1.74272,0.00775) and (2.87802,0.01099)
+ ..(3.55333,0.01099) % segment 0
+% entering octant `ESE'
+ ..controls (4.12251,0.01099) and (4.36491,0.00868)
+ ..(4.36877,0.00482) % segment 0
+% entering octant `SSE'
+ ..controls (4.3688,0.00479) and (4.36882,0.00476)
+ ..(4.36882,0.00473) % segment 0
+% entering octant `SSW'
+ ..controls (4.36882,0.0047) and (4.3688,0.00467)
+ ..(4.36877,0.00464) % segment 0
+% entering octant `WSW'
+ ..controls (4.34875,-0.01538) and (-1.88759,-0.07579)
+ ..(-2.48894,-0.07579) % segment 0
+% entering octant `WNW'
+ ..controls (-2.5191,-0.07579) and (-2.5351,-0.07564)
+ ..(-2.53542,-0.07532) % segment 0
+% entering octant `NNW'
+% entering octant `NNE'
+% entering octant `ENE'
+ ..controls (-2.53209,-0.07199) and (-0.88672,-0.05144)
+ ..(4,0) % segment 0
+% entering octant `NNE'
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+ ..controls (-0.88672,-0.05144) and (-2.5321,-0.072)
+ ..(-2.53542,-0.07532) % segment 1
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ ..controls (-2.53511,-0.07562) and (-2.5191,-0.07579)
+ ..(-2.48894,-0.07579) % segment 1
+% entering octant `ENE'
+ ..controls (-1.88759,-0.07579) and (4.34877,-0.01537)
+ ..(4.36877,0.00464) % segment 1
+% entering octant `NNE'
+ ..controls (4.3688,0.00467) and (4.36882,0.0047)
+ ..(4.36882,0.00473) % segment 1
+% entering octant `NNW'
+ ..controls (4.36882,0.00476) and (4.3688,0.00479)
+ ..(4.36877,0.00482) % segment 1
+% entering octant `WNW'
+ ..controls (4.36491,0.00868) and (4.12251,0.01099)
+ ..(3.55333,0.01099) % segment 1
+% entering octant `WSW'
+ ..controls (2.87802,0.01099) and (1.74272,0.00775)
+ ..(0,0) % segment 1
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ & cycle
+
+Tracing edges at line 82: (weight 1)
+(No new edges added.)
+
+{(picture)yscaled(0)}
+{e3:=picture}
+{autorounding:=2}
+{addto}
+Path at line 83, before choices:
+(0.5,0){curl 1}
+ ..{curl 1}(3.5,1.5)
+
+Path at line 83, after choices:
+(0.5,0)..controls (1.5,0.5) and (2.5,1)
+ ..(3.5,1.5)
+
+Path at line 83, before subdivision into octants:
+(0.5,0)..controls (1.5,0.5) and (2.5,1)
+ ..(3.5,1.5)..controls (2.5,1) and (1.5,0.5)
+ ..cycle
+
+Cycle spec at line 83, after subdivision and double autorounding:
+(0.5,0) % beginning in octant `ENE'
+ ..controls (1.5,0.5) and (2.5,1)
+ ..(3.5,1.5) % segment 0
+% entering octant `NNE'
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+ ..controls (2.5,1) and (1.5,0.5)
+ ..(0.5,0) % segment 1
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ & cycle
+
+Tracing edges at line 83: (weight 2)
+(2,0)(2,1)(4,1)(4,2)(4,1)(2,1)(2,0).
+
+{tracingspecs:=0}
+Path at line 84, before choices:
+(1,1)
+ ..cycle
+
+Path at line 84, after choices:
+(1,1)..controls (1,1) and (1,1)
+ ..cycle
+
+{makepen(path)}
+{(future pen)yscaled(1.5)}
+Pen polygon at line 84 (newly created):
+(1,1.5)
+ .. cycle
+
+{q:=pen}
+{((-1,yy))rotated((8,1,5,-5,-5,5))}
+>> (8,1,5,-5,-5,5)
+! Improper transformation argument.
+<to be read again>
+ {
+l.85 p~8=(($,yy)rotated p{
+ 0,1}..{0,$}(1,0){0,$}..cycle)scaled...
+The expression shown above has the wrong type,
+so I can't transform anything using it.
+Proceed, and I'll omit the transformation.
+
+>> yy
+! Undefined y coordinate has been replaced by 0.
+<to be read again>
+ {
+l.85 p~8=(($,yy)rotated p{
+ 0,1}..{0,$}(1,0){0,$}..cycle)scaled...
+I need a `known' y value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+Path at line 85, before choices:
+(-1,0){0,4096}
+ ..{0,-4096}(1,0){0,-4096}
+ ..{0,4096}cycle
+
+Path at line 85, after choices:
+(-1,0)..controls (-1,1.33333) and (1,1.33333)
+ ..(1,0)..controls (1,-1.33333) and (-1,-1.33333)
+ ..cycle
+
+{(path)scaled(2)}
+{(path)shifted((1000.49,9))}
+{(unknown path p~8)=(path)}
+{turningcheck:=1}
+{addto}
+>> (8,1,5,-5,-5,5)
+! Improper type.
+<to be read again>
+ withpen
+l.86 ...n q withweight p withpen
+ cycle p;
+Next time say `withweight <known numeric expression>';
+I'll ignore the bad `with' clause and look for another.
+
+{cycle((8,1,5,-5,-5,5))}
+>> false
+! Improper type.
+<to be read again>
+ ;
+l.86 ...eight p withpen cycle p;
+
+Next time say `withpen <known pen expression>';
+I'll ignore the bad `with' clause and look for another.
+
+Tracing edges at line 86: (weight 1)
+@ Octant NNE (0 offsets), from (999,10.5) to (999.15451,11.65451)
+@ transition line 1, from (999.15451,11.65451) to (999.15451,11.65451)
+(999,11)
+@ Octant ENE (0 offsets), from (999.15451,11.65451) to (1001,12)
+@ transition line 0, from (999.15451,11.65451) to (999.15451,11.65451)
+@ Octant ESE (0 offsets), from (1001,12) to (1002.8455,11.6545)
+@ transition line 1, from (1002.8455,11.6545) to (1002.8455,11.6545)
+@ Octant SSE (0 offsets), from (1002.8455,11.6545) to (1003,10.5)
+@ transition line 0, from (1002.8455,11.6545) to (1002.8455,11.6545)
+(999,12)(1003,12)
+@ Octant SSW (0 offsets), from (1003,10.5) to (1002.84549,9.34549)
+@ transition line 1, from (1002.84549,9.34549) to (1002.84549,9.34549)
+@ Octant WSW (0 offsets), from (1002.84549,9.34549) to (1001,9)
+@ transition line 0, from (1002.84549,9.34549) to (1002.84549,9.34549)
+@ Octant WNW (0 offsets), from (1001,9) to (999.1545,9.3455)
+@ transition line 1, from (999.1545,9.3455) to (999.1545,9.3455)
+@ Octant NNW (0 offsets), from (999.1545,9.3455) to (999,10.5)
+@ transition line 0, from (999.1545,9.3455) to (999.1545,9.3455)
+(1003,9)(999,9)(999,11).
+
+Tracing edges at line 86: (weight 1)
+@ Octant SSE (0 offsets), from (999,10.5) to (999.15451,9.34549)
+@ transition line 0, from (999,10.5) to (999,10.5)
+(999,11)
+@ Octant ESE (0 offsets), from (999.15451,9.34549) to (1001,9)
+@ transition line 1, from (1001,9) to (1001,9)
+@ Octant ENE (0 offsets), from (1001,9) to (1002.8455,9.3455)
+@ transition line 0, from (1001,9) to (1001,9)
+@ Octant NNE (0 offsets), from (1002.8455,9.3455) to (1003,10.5)
+@ transition line 1, from (1003,10.5) to (1003,10.5)
+(999,9)(1003,9)
+@ Octant NNW (0 offsets), from (1003,10.5) to (1002.84549,11.65451)
+@ transition line 0, from (1003,10.5) to (1003,10.5)
+@ Octant WNW (0 offsets), from (1002.84549,11.65451) to (1001,12)
+@ transition line 1, from (1001,12) to (1001,12)
+@ Octant WSW (0 offsets), from (1001,12) to (999.1545,11.6545)
+@ transition line 0, from (1001,12) to (1001,12)
+@ Octant SSW (0 offsets), from (999.1545,11.6545) to (999,10.5)
+@ transition line 1, from (999,10.5) to (999,10.5)
+(1003,12)(999,12)(999,11).
+
+{begingroup}
+{interim}
+{(xx)=(0.1)}
+## xx=0.1
+{autorounding:=0.1}
+{addto}
+Tracing edges at line 87: (weight -2)
+@ Octant NNE (0 offsets), from (999,10.5) to (999.38197,11.42706)
+@ transition line 1, from (999.38197,11.42706) to (999.38197,11.42706)
+@ Octant ENE (0 offsets), from (999.38197,11.42706) to (1001,12)
+@ transition line 0, from (999.38197,11.42706) to (999.38197,11.42706)
+(999,11)
+@ Octant ESE (0 offsets), from (1001,12) to (1002.61804,11.42705)
+@ transition line 1, from (1002.61804,11.42705) to (1002.61804,11.42705)
+(999,12)(1003,12)
+@ Octant SSE (0 offsets), from (1002.61804,11.42705) to (1003,10.5)
+@ transition line 0, from (1002.61804,11.42705) to (1002.61804,11.42705)
+@ Octant SSW (0 offsets), from (1003,10.5) to (1002.61803,9.57294)
+@ transition line 1, from (1002.61803,9.57294) to (1002.61803,9.57294)
+@ Octant WSW (0 offsets), from (1002.61803,9.57294) to (1001,9)
+@ transition line 0, from (1002.61803,9.57294) to (1002.61803,9.57294)
+@ Octant WNW (0 offsets), from (1001,9) to (999.38196,9.57295)
+@ transition line 1, from (999.38196,9.57295) to (999.38196,9.57295)
+(1003,9)(999,9)
+@ Octant NNW (0 offsets), from (999.38196,9.57295) to (999,10.5)
+@ transition line 0, from (999.38196,9.57295) to (999.38196,9.57295)
+(999,11).
+
+{restoring autorounding=2}
+{endgroup}
+{-(14.5)}
+{charext:=-14.5}
+{chardp:=-14.5}
+{chardx:=-14.5}
+{shipout}
+{-(picture)}
+{(picture)-(picture)}
+{-(picture)}
+{turningnumber(path)}
+{(6)*(-1)}
+{(picture)shifted((0,-6))}
+{(picture)+(picture)}
+[5.-14]
+Edge structure at line 88 (just shipped out):
+row 11: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 10: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 9: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 5: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 4: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 3: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 2: | 3+ 3+ 3+ 3+ 4- 4- 4- 4-
+row 1: | -1+ -1+ 0- 0- 1+ 2- 3+ 3+ 3+ 3+ 4- 4- 4- 4-
+row 0: | -3+ -3+ -3+ -2- -2- -2- -1+ -1+ 0- 0- 0+ 0+ 1- 1- 2+ 2+
+ 3- 3- 4+ 4+ 5- 5-
+row -1: | -3+ -3+ -2- -2- -2+ -2+ -1- -1- 0+ 0+ 0+ 1- 1- 1-
+row -2: | -4+ -3- -2+ -2+ -1- -1-
+row -4: | 3+ 3+ 4- 4-
+row -5: | 3+ 3+ 4- 4-
+row -6: | 2+ 3- 4+ 5-
+
+Path at line 89, before choices:
+(0,0){curl 1}
+ ..(1,0.5)
+ ..(5,1.5)
+ ..(7,2.5)
+ ..(12,3.5)
+ ..{curl 1}(13,4)
+
+Path at line 89, after choices:
+(0,0)..controls (0.3153,0.20053) and (0.6504,0.36807)
+ ..(1,0.5)..controls (2.29114,0.98723) and (3.72412,0.97192)
+ ..(5,1.5)..controls (5.68967,1.78545) and (6.30771,2.22081)
+ ..(7,2.5)..controls (8.58867,3.1407) and (10.3946,2.90645)
+ ..(12,3.5)..controls (12.3506,3.62962) and (12.68594,3.7973)
+ ..(13,4)
+
+{(unknown path p~9)=(path)}
+{addto}
+Tracing edges at line 89: (weight 1)
+(1,0)(1,1)(5,1)(5,2)(7,2)(7,3)(12,3)(12,4)(12,3)(7,3)(7,2)(5,2)
+(5,1)(1,1)(1,0).
+
+{smoothing:=1}
+{addto}
+Tracing edges at line 90: (weight 1)
+(1,0)(1,1)(4,1)(4,2)(8,2)(8,3)(12,3)(12,4)(12,3)(8,3)(8,2)(4,2)
+(4,1)(1,1)(1,0).
+
+{addto}
+{-(4095)}
+Path at line 91, before choices:
+(-4095,0){curl 1}..tension 0.75 and 999
+ ..{curl 1}(0,2)
+
+Path at line 91, after choices:
+(-4095,0)..controls (-2275,0.88889) and (-1.36636,1.99933)
+ ..(0,2)
+
+Tracing edges at line 91: (weight 1)
+(-3071,0)(-3071,1)(-1024,1)(-1024,2)(-1024,1)(-3071,1)(-3071,0).
+
+{show}
+{-(90)}
+{(picture)rotated(-90)}
+>> Edge structure at line 91:
+
+{(picture)+(picture)}
+{(90)*(-1)}
+{(picture)rotated(-90)}
+{(picture)rotated(90)}
+>> Edge structure at line 91:
+row 2: | 4+++ 4+ 5--- 5-
+row 1: | 0+++ 0+ 1--- 1- 2++ 3-- 4+++ 4+ 5--- 5-
+row 0: | -2+++ -2+++ -1--- -1--- 0+++ 0+ 2--- 2- 3++ 4-- 5++ 6--
+row -1: | -2+++ -2+ 0--- 0- 1+++ 1+++ 2--- 2---
+row -2: | -3++ -2-- -1+++ -1+ 0--- 0-
+
+{if}
+{if}
+{elseif}
+! Missing `:' has been inserted.
+<inserted text>
+ :
+<to be read again>
+ elseif
+l.92 if "a" if "ab">"b" elseif
+ path reverse (3,4): >="aa":foo...
+
+{("ab")>("b")}
+{false}
+{reverse((3,4))}
+{path(path)}
+{true}
+{("a")>=("aa")}
+{false}
+{if}
+{((xpart ',ypart '))-((1,yy))}
+{((xpart '-1,linearform))<((xpart ',ypart '))}
+{true}
+{fi}
+{else}
+! Extra else.
+l.93 ... if '-(1,yy)<': :fi else
+ def dup text t=[[t;save endd...
+I'm ignoring this; it doesn't match any if.
+
+{def}
+{def}
+{def}
+
+||<tertiary>->show.substring(EXPR0)of("a"
+(EXPR0)<-(2,-1)
+{show}
+{("a")&("bc")}
+{((2,-1))substring("abc")}
+>> "ba"
+{tertiarydef}
+{def}
+{show}
+{((23.3,4.5))subpath(path)}
+Path at line 100, before choices:
+(0,0){curl 2}
+ ..(13,4)..controls (12.84297,3.89865) and (12.68063,3.80606)
+ ..(12.51372,3.72261){curl 3}
+ ..{curl 4}(-1,-1){curl 4}
+ ..{curl 2}cycle
+
+Path at line 100, after choices:
+(0,0)..controls (-40.15552,-36.7088) and (58.71173,33.50317)
+ ..(13,4)..controls (12.84297,3.89865) and (12.68063,3.80606)
+ ..(12.51372,3.72261)..controls (8.00914,2.1484) and (3.50458,0.5742)
+ ..(-1,-1)..controls (-0.66667,-0.66667) and (-0.33333,-0.33333)
+ ..cycle
+
+>> Path at line 100:
+(0,0)..controls (-40.15552,-36.7088) and (58.71173,33.50317)
+ ..(13,4)..controls (12.84297,3.89865) and (12.68063,3.80606)
+ ..(12.51372,3.72261)..controls (8.00914,2.1484) and (3.50458,0.5742)
+ ..(-1,-1)..controls (-0.66667,-0.66667) and (-0.33333,-0.33333)
+ ..cycle
+
+{numspecial}
+{(2)+(3)}
+
+++->[[dup.showtoken(EXPR0);]];def.x.expr.z.of(EXPR1)=z.enddef;texts(x=((
+EXPR1)+0)(EXPR1)+(EXPR1))("xx",foo((EXPR0)))=0]]
+(EXPR0)<-path
+(EXPR1)<-5
+{begingroup}
+
+dup<text>->[[(TEXT0);save
+(TEXT0)<-showtoken(path)
+{begingroup}
+{showtoken}
+> (path)
+{save}
+{restoring ;}
+{endgroup}
+{def}
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+++->...]];def.x.expr.z.of(EXPR1)
+ =z.enddef;texts(x=((EXPR1)+0)...
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+Sorry: You can't redefine a number, string, or expr.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+
+texts(TEXT0)(TEXT1)<expr>->for.n:=,for.n"yy":n,length.if.false:endfor(TE
+XT1),(TEXT0),:if.string.n:forsuffixes.n=foo1,[foo(n)],':show(TEXT0),(TEX
+T1)|(n;exitif.not('<='+((EXPR2),yy))endfor.for.m= :+endfor.for.m=alpha.s
+tep-1.1 3$:+m.endfor.fi.endfor
+(TEXT0)<-x=((5)+0)(5)+(5)
+(TEXT1)<-"xx",foo((path))
+(EXPR2)<-0
+{for}
+{for}
+! Missing `=' has been inserted.
+<to be read again>
+ "yy"
+texts->for.n:=,for.n"yy"
+ :n,length.if.false:endfor(TEXT1),(TEX...
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+The next thing in this loop should have been `=' or `:='.
+But don't worry; I'll pretend that an equals sign
+was present, and I'll look for the values next.
+
+{loop value="yy"}
+{if}
+{false}
+{false}
+! Incomplete if; all text was ignored after line 100.
+<inserted text>
+ fi
+<to be read again>
+ ENDFOR
+<for("yy")> ....if.false: ENDFOR
+
+texts->...length.if.false:endfor
+ (TEXT1),(TEXT0),:if.string.n:...
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+A forbidden `outer' token occurred in skipped text.
+This kind of error happens when you say `if...' and forget
+the matching `fi'. I've inserted a `fi'; this might work.
+
+{[repeat the loop]}
+{length("xx")}
+
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-(path)
+{begingroup}
+{endgroup}
+
+x<expr>of<primary>->(EXPR0)
+{(5)+(0)}
+(EXPR0)<-5
+! Missing `of' has been inserted for x.
+<to be read again>
+ (5)
+<argument> x=((5)+0)(5)
+ +(5)
+texts->...:endfor(TEXT1),(TEXT0)
+ ,:if.string.n:forsuffixes.n=f...
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I've got the first argument; will look now for the other.
+
+(EXPR1)<-5
+{(5)+(5)}
+{loop value="yy"}
+{if}
+{string("yy")}
+{true}
+{forsuffixes}
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+<for("yy")> ...orsuffixes(EXPR0)
+ =foo1,[foo((EXPR0))],':show.x...
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+Sorry: You can't redefine a number, string, or expr.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-("yy")
+{begingroup}
+{endgroup}
+>> "yy"
+! Improper subscript has been replaced by zero.
+<for("yy")> ...o1,[foo((EXPR0))]
+ ,':show.x=((5)+0)(5)+(5),"xx"...
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+A bracketed subscript must have a known numeric value;
+unfortunately, what I found was the value that appears just
+above this error message. So I'll try a zero subscript.
+
+{loop value=foo1}
+{show}
+
+x<expr>of<primary>->(EXPR0)
+{(5)+(0)}
+(EXPR0)<-5
+! Missing `of' has been inserted for x.
+<to be read again>
+ (5)
+<for(foo1)> show.x=((5)+0)(5)
+ +(5),"xx",foo((path))|(("yy");ex...
+<for("yy")> ...'+((0),yy))endfor
+ .for.m= :+endfor.for.m=alpha....
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I've got the first argument; will look now for the other.
+
+(EXPR1)<-5
+{(5)+(5)}
+>> 10
+>> "xx"
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-(path)
+{begingroup}
+{endgroup}
+
+|<suffix>->,(SUFFIX0)
+! Missing `)' has been inserted.
+<to be read again>
+ ("yy")
+<for(foo1)> ...o((path))|(("yy")
+ ;exitif.not('<='+((0),yy)) EN...
+<for("yy")> ...'+((0),yy))endfor
+ .for.m= :+endfor.for.m=alpha....
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I've gotten to the end of the macro parameter list.
+You might want to delete some tokens before continuing.
+
+(SUFFIX0)<-
+>> Path at line 100:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+
+>> "yy"
+{exitif}
+{((xpart ',ypart '))+((0,yy))}
+{((xpart ',ypart '))<=((xpart ',linearform))}
+>> -yy
+! Unknown relation will be considered false.
+<to be read again>
+ )
+<for(foo1)> ...ot('<='+((0),yy))
+ ENDFOR
+<for("yy")> ...'+((0),yy))endfor
+ .for.m= :+endfor.for.m=alpha....
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+Oh dear. I can't decide if the expression above is positive,
+negative, or zero. So this comparison test won't be `true'.
+
+{[repeat the loop]}
+{loop value=0}
+{not(false)}
+{true}
+{for}
+{for}
+>> 0.5p3~
+! Improper initial value has been replaced by 0.
+<to be read again>
+ step
+<for("yy")> ....for.m=alpha.step
+ -1.1 3$:+m.endfor.fi ENDFOR
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+When you say `for x=a step b until c',
+the initial value `a' and the step size `b'
+and the final value `c' must have known numeric values.
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{-(1.1)}
+! Missing `until' has been inserted.
+<to be read again>
+ 3
+<for("yy")> ...=alpha.step-1.1 3
+ $:+m.endfor.fi ENDFOR
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I assume you meant to say `until' after `step'.
+So I'll look for the final value and colon next.
+
+{(3)*(-1)}
+{loop value=0}
+{[repeat the loop]}
+{loop value=-1.1}
+{+(0)}
+{[repeat the loop]}
+{loop value=-2.20001}
+{(0)+(-1.1)}
+{[repeat the loop]}
+{fi}
+{[repeat the loop]}
+{loop value=2}
+{if}
+{string(2)}
+{false}
+{[repeat the loop]}
+{loop value=path}
+{if}
+{string(path)}
+{false}
+{[repeat the loop]}
+{loop value=10}
+{if}
+{string(10)}
+{false}
+{[repeat the loop]}
+{(-1.1)+(-2.20001)}
+{endgroup}
+{begingroup}
+{let}
+{save}
+{def}
+{def}
+
+texts->input
+{input}
+(trap.mf
+if->endinput?
+{endinput}
+{if}
+{known(0)}
+{not(true)}
+{false}
+)
+! Incomplete if; all text was ignored after line 2.
+<inserted text>
+ fi
+l.101 ...input enddef;texts trap
+ ]];
+The file ended while I was skipping conditional text.
+This kind of error happens when you say `if...' and forget
+the matching `fi'. I've inserted a `fi'; this might work.
+
+{restoring \}
+{restoring if}
+{endgroup}
+
+dup<text>->[[(TEXT0);save
+Runaway text?
+[[def.texts.secondary.x=primarydef.y++y=x@y.enddef;showtoken ETC.
+! Forbidden token found while scanning a text argument.
+<inserted text>
+ endgroup
+<to be read again>
+ \
+l.102 ...showtoken++;x enddef]]\
+ ;
+It seems that a right delimiter was left out,
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+(TEXT0)<-[[def.texts.secondary.x=primarydef.y++y=x@y.enddef;showtoken++;
+x.enddef]]
+{begingroup}
+{begingroup}
+{def}
+{endgroup}
+{save}
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+<to be read again>
+ \
+l.102 ...showtoken++;x enddef]]\
+ ;
+Sorry: You can't redefine my error-recovery tokens.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+{\}
+{proofing:=1}
+
+texts<secondary>->primarydef.y++y=(EXPR0)@y.enddef;showtoken++;(EXPR0)
+(EXPR0)<-"a"
+{primarydef}
+{showtoken}
+> ++=primarydef'd macro:
+("a")@(EXPR1)
+{("a")&("b")}
+ab
+texts<secondary>->primarydef.y++y=(EXPR0)@y.enddef;showtoken++;(EXPR0)
+{(1.00002)*(a1)}
+{-(1.00002a1)}
+(EXPR0)<--1.00002a1
+{primarydef}
+{showtoken}
+> ++=primarydef'd macro:
+(-1.00002a1)@(EXPR1)
+{(a2)+(a3)}
+{(linearform)+(a4)}
+{(linearform)+(a5)}
+{(linearform)+(a6)}
+{-(linearform)}
+{(-1.00002a1)=(linearform)}
+## a1=0.99998a6+0.99998a5+0.99998a4+0.99998a3+0.99998a2
+{-((a3,a2))}
+{((-a3,-a2))/(0.99998)}
+{(a4)+(a5)}
+{begingroup}
+{showdependencies}
+a1=0.99998a6+0.99998a5+0.99998a4+0.99998a3+0.99998a2
+alpha=0.5p3~
+{endgroup}
+{(linearform)+(a6)}
+{(a3)+(a4)}
+{(linearform)+(a5)}
+{(linearform)+(a6)}
+{-((linearform,linearform))}
+{((-1.00002a3,-1.00002a2))=((linearform,linearform))}
+## a2=0.99998a6+0.99998a5+0.99998a4+0.99998a3
+## a3=0.99998a6+0.99998a5+0.99998a4
+{restoring INACCESSIBLE}
+{endgroup}
+{(1.00002)*(0.25a4*4)}
+{(0.25a4*4)+(1)}
+{(0.25a5*4)+(0.25a6*4)}
+{(0.25a4*4+1)=(linearform)}
+## a4*4=0.99998a6*4+0.99998a5*4-3.99994
+{(0.9)*(0.5p3~)}
+{(0.45p3~)+(7)}
+{alpha:=0.45p3~+7}
+## alpha=0.45p3~+7
+{showdependencies}
+alpha=0.45p3~+7
+a4=0.25a6*4+0.25a5*4-0.99998
+a3=0.49998a6*4+0.49998a5*4-0.99997
+a2=0.99997a6*4+0.99997a5*4-1.99992
+a1=1.99992a6*4+1.99992a5*4-3.99982
+{(0.66667)-(0.25a6*4)}
+{(0.25a6*4)=(-0.25a6*4+0.66667)}
+## a6*4=1.33334
+{(0.25a5*4)=(0.33333)}
+## a5*4=1.33331
+#### a4=-0.33333
+#### a3=0.33333
+#### a2=0.66664
+#### a1=1.33328
+{cull}
+{(4)*(0.33333)}
+{chardp:=27}
+{charcode:=27}
+{openwindow}
+! Improper `openwindow'.
+<to be read again>
+ ;
+l.107 ...) to (0,0) at "whoops";
+ addto p; shipout p; cull p;
+Say `openwindow k from (r0,c0) to (r1,c1) at (x,y)',
+where all quantities are known and k is between 0 and 15.
+
+{addto}
+>> (8,1,5,-5,-5,5)
+! Not a suitable variable.
+<to be read again>
+ ;
+l.107 ...) at "whoops"; addto p;
+ shipout p; cull p;
+At this point I needed to see the name of a picture variable.
+(Or perhaps you have indeed presented me with one; I might
+have missed it, if it wasn't followed by the proper token.)
+So I'll not change anything just now.
+
+{shipout}
+! Variable p is the wrong type (transform).
+<to be read again>
+ ;
+l.107 ...s"; addto p; shipout p;
+ cull p;
+I was looking for a "known" picture variable.
+So I'll not change anything just now.
+
+{cull}
+>> (8,1,5,-5,-5,5)
+! Not a suitable variable.
+<to be read again>
+ ;
+l.107 ...o p; shipout p; cull p;
+
+At this point I needed to see the name of a picture variable.
+(Or perhaps you have indeed presented me with one; I might
+have missed it, if it wasn't followed by the proper token.)
+So I'll not change anything just now.
+
+{openwindow}
+{-(0.5)}
+
+Calling BLANKRECTANGLE(0,0,0,0)
+Calling UPDATESCREEN
+{special}
+>> (8,1,5,-5,-5,5)
+! Unsuitable expression.
+<to be read again>
+ ;
+l.108 ...$) at (0,0); special p;
+ numspecial "p";
+The expression shown above has the wrong type to be output.
+
+{numspecial}
+>> "p"
+! Unsuitable expression.
+<to be read again>
+ ;
+l.108 ...cial p; numspecial "p";
+
+The expression shown above has the wrong type to be output.
+
+{openwindow}
+{(32)*(1024)}
+! Arithmetic overflow.
+l.109 ....49999 from (0,32*1024)
+ to (1,31*1057) at (0,0); shi...
+Uh, oh. A little while ago one of the quantities that I was
+computing got too large, so I'm afraid your answers will be
+somewhat askew. You'll probably have to adopt different
+tactics next time. But I shall try to carry on anyway.
+
+{(31)*(1057)}
+
+Calling BLANKRECTANGLE(100,100,0,1)
+Calling UPDATESCREEN
+{shipout}
+[27.-14]
+Edge structure at line 109 (just shipped out):
+row 0: | -3++ -2-- -1++ 4094--
+row -1: | -4096++ -3-- -2++ -1--
+
+{openwindow}
+Path at line 110, before choices:
+(0,0)
+
+Path at line 110, after choices:
+(0,0)
+
+{length(path)}
+
+Calling BLANKRECTANGLE(0,2,0,4)
+Calling UPDATESCREEN
+{openwindow}
+{length((-1,0))}
+{-(5)}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling UPDATESCREEN
+{display}
+{nullpicture}
+>> picture
+! Not a suitable variable.
+<to be read again>
+ ;
+l.112 display nullpicture;
+ display p inwindow 3; display p in...
+At this point I needed to see the name of a picture variable.
+(Or perhaps you have indeed presented me with one; I might
+have missed it, if it wasn't followed by the proper token.)
+So I'll not change anything just now.
+
+{display}
+>> 3
+! Bad window number.
+<to be read again>
+ ;
+l.112 ...; display p inwindow 3;
+ display p inwindow 6;
+It should be the number of an open window.
+
+{display}
+! Variable p is the wrong type (transform).
+<to be read again>
+ ;
+l.112 ...; display p inwindow 6;
+
+I was looking for a "known" picture variable.
+So I'll not change anything just now.
+
+{display}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling PAINTROW(2,1;4,5,6,10)
+Calling UPDATESCREEN
+{cull}
+! Enormous number has been reduced.
+l.113 ...opping (0.1,4095.999999
+ ) withweight 3.5
+I can't handle numbers bigger than about 4095.99998;
+so I've changed your constant to that maximum amount.
+
+! Weight must be -3, -2, -1, +1, +2, or +3.
+<to be read again>
+ withweight
+l.114 withweight
+ -3.5; display e0 inwindow 5.5; addto e0 also ...
+I'll ignore the bad `with' clause and look for another.
+
+{-(3.5)}
+! Bad culling amounts.
+<to be read again>
+ ;
+l.114 withweight-3.5;
+ display e0 inwindow 5.5; addto e0 also ...
+Always cull by known amounts that exclude 0.
+
+{display}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling PAINTROW(2,1;4,5,6,8,9,10)
+Calling PAINTROW(1,1;6,7,8,9)
+Calling UPDATESCREEN
+{addto}
+>> (8,1,5,-5,-5,5)
+! Improper `addto'.
+<to be read again>
+ ;
+l.114 ...w 5.5; addto e0 also p;
+ addto e0 contour 0;
+This expression should have specified a known picture.
+So I'll not change anything just now.
+
+{addto}
+>> 0
+! Improper `addto'.
+<to be read again>
+ ;
+l.114 ... p; addto e0 contour 0;
+
+This expression should have been a known path.
+So I'll not change anything just now.
+
+{display}
+
+Calling BLANKRECTANGLE(0,2,0,4)
+Calling PAINTROW(2,1;0,1)
+Calling PAINTROW(1,1;0,1)
+Calling PAINTROW(0,1;1,2)
+Calling UPDATESCREEN
+{addto}
+! Not a cycle.
+<to be read again>
+ ;
+l.115 ...; addto e0 contour p~9;
+
+That contour should have ended with `..cycle' or `&cycle'.
+So I'll not change anything just now.
+
+{display}
+{(3)+(3)}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling PAINTROW(2,1;4,5,6,10)
+Calling UPDATESCREEN
+{display}
+{begingroup}
+{vardef}
+{endgroup}
+! Variable e0e0 has been obliterated.
+<to be read again>
+ ;
+l.116 ...vardef e[]e=enddef;6]];
+
+It seems you did a nasty thing---probably by accident,
+but nevertheless you nearly hornswoggled me...
+While I was evaluating the right-hand side of this
+command, something happened, and the left-hand side
+is no longer a variable! So I won't change anything.
+
+{addto}
+{display}
+Calling PAINTROW(1,1;0,1,2)
+Calling PAINTROW(0,0;0,1,2)
+Calling UPDATESCREEN
+{ligtable}
+{display}
+{display}
+Calling UPDATESCREEN
+{show}
+{begingroup}
+{interim}
+{tracingcommands:=0}
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"g"
+(TEXT1)<-=:|
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"h":"i"
+(TEXT1)<-|=:
+! Character h is already in a ligtable.
+<to be read again>
+ :
+<argument> "h":
+ "i"
+lig->...;ligtable(TEXT0):(TEXT0)
+ (TEXT1)0,skipto255;boundarych...
+l.119 ...=:|); lig("h":"i")(|=:)
+ ;
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"j"
+(TEXT1)<-|=:>
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"k"
+(TEXT1)<-=:|>
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"l"
+(TEXT1)<-|=:|>
+{restoring tracingcommands=2.1}
+{endgroup}
+>> vacuous
+{begingroup}
+{let}
+{vardef}
+{endgroup}
+! Variable c.a1 has been obliterated.
+<to be read again>
+ ;
+l.121 ...;vardef b=enddef;1]] ];
+ ligtable"m":0=:0,skipto5;
+It seems you did a nasty thing---probably by accident,
+but nevertheless you nearly hornswoggled me...
+While I was evaluating the suffix of this variable,
+something was redefined, and it's no longer a variable!
+In order to get back on my feet, I've inserted `0' instead.
+
+{b1:=0}
+! Variable b1 has been obliterated.
+<to be read again>
+ ;
+l.121 ...;vardef b=enddef;1]] ];
+ ligtable"m":0=:0,skipto5;
+It seems you did a nasty thing---probably by accident,
+but nevertheless you nearly hornswoggled me...
+While I was evaluating the right-hand side of this
+command, something happened, and the left-hand side
+is no longer a variable! So I won't change anything.
+
+{ligtable}
+! A statement can't begin with `skipto'.
+<to be read again>
+ !!
+l.122 !!
+ ; errhelp 0; errmessage "Be like Jane";
+I was looking for the beginning of a new statement.
+If you just proceed without changing anything, I'll ignore
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+! Extra tokens will be flushed.
+<to be read again>
+ !!
+l.122 !!
+ ; errhelp 0; errmessage "Be like Jane";
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+{errhelp}
+>> 0
+! Not a string.
+<to be read again>
+ ;
+l.122 !!; errhelp 0;
+ errmessage "Be like Jane";
+A message should be a known string expression.
+
+{errmessage}
+! Be like Jane.
+<to be read again>
+ ;
+l.122 ...message "Be like Jane";
+
+This error message was generated by an `errmessage'
+command, so I can't give any explicit help.
+Pretend that you're Miss Marple: Examine all clues,
+and deduce the truth by inspired guesses.
+
+{errhelp}
+{errmessage}
+! .
+<to be read again>
+ ;
+l.123 ...e%%%lp%"; errmessage"";
+ errhelp ""; errmessage "Anot...
+He%
+lp
+
+
+{errhelp}
+{errmessage}
+! Another.
+<to be read again>
+ ;
+l.123 ...; errmessage "Another";
+
+(That was another `errmessage'.)
+
+{headerbyte}
+>> 0
+! Improper location.
+<to be read again>
+ ;
+l.124 headerbyte 0;
+ headerbyte(48.5)substring(-9,9)of"long"; ...
+I was looking for a known, positive number.
+For safety's sake I'll ignore the present command.
+
+{headerbyte}
+! Missing `:' has been inserted.
+<to be read again>
+ substring
+l.124 ...aderbyte(48.5)substring
+ (-9,9)of"long"; for\=0:\
+A colon should follow a headerbyte or fontinfo location.
+
+{-(9)}
+{((-9,9))substring("long")}
+>> "long"
+! Invalid code has been replaced by 0.
+<to be read again>
+ ;
+l.124 ...ubstring(-9,9)of"long";
+ for\=0:\
+I was looking for a number between 0 and 255, or for a
+string of length 1. Didn't find it; will use 0 instead.
+
+{for}
+Runaway loop?
+! Forbidden token found while scanning the text of a for loop.
+<inserted text>
+ endfor
+<to be read again>
+ \
+l.124 ...-9,9)of"long"; for\=0:\
+
+I suspect you have forgotten an `endfor',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{loop value=0}
+{[repeat the loop]}
+{\}
+{headerbyte}
+{(2)*(0.33333)}
+{fontdimen}
+{(2)*(0.33333)}
+>> "q"
+! Improper font parameter.
+<to be read again>
+ ;
+l.125 ..."; fontdimen 9:2a6,"q";
+ fontdimen 1:2048;
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{fontdimen}
+{fontmaking:=1}
+{extensible}
+! Missing `:' has been inserted.
+<to be read again>
+ 5
+l.126 ...king:=1; extensible 5 5
+ ,"c"255.5,"d"; charlist 0:5:"...
+I'm processing `extensible c: t,m,b,r'.
+
+! Missing `,' has been inserted.
+<to be read again>
+ 255.5
+l.126 ...extensible 5 5,"c"255.5
+ ,"d"; charlist 0:5:"a":"d";
+I'm processing `extensible c: t,m,b,r'.
+
+>> 255.5
+! Invalid code has been replaced by 0.
+<to be read again>
+ ,
+l.126 ...xtensible 5 5,"c"255.5,
+ "d"; charlist 0:5:"a":"d";
+I was looking for a number between 0 and 255, or for a
+string of length 1. Didn't find it; will use 0 instead.
+
+{charlist}
+! Character code 5 is already extensible.
+<to be read again>
+ :
+l.126 ...,"d"; charlist 0:5:"a":
+ "d";
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+{ligtable}
+! Too far to skip.
+l.127 ligtable255:255::
+ "a"=:"b","d" kern -2048,"c":0:99.5:"e"...
+At most 127 lig/kern steps can separate skipto1 from 1::.
+
+{-(2048)}
+! Character code 0 is already in a charlist.
+<to be read again>
+ :
+l.127 ...","d" kern -2048,"c":0:
+ 99.5:"e"|=:|"f",0kern';
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+>> (xpart ',ypart ')
+! Improper kern.
+<to be read again>
+ ;
+l.127 ...99.5:"e"|=:|"f",0kern';
+
+The amount of kern should be a known numeric value.
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{ligtable}
+! Character code 5 is already extensible.
+<to be read again>
+ :
+l.128 ligtable 5:
+ 0; def clear(text x)=interim x:=$ enddef; cl...
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+! Illegal ligtable step.
+<to be read again>
+ ;
+l.128 ligtable 5:0;
+ def clear(text x)=interim x:=$ enddef; cl...
+I was looking for `=:' or `kern' here.
+
+{def}
+
+clear(TEXT0)->interim(TEXT0):=$
+(TEXT0)<-hppp
+{interim}
+{hppp:=-1}
+{(0)=(0)}
+! Redundant equation.
+<to be read again>
+ ;
+l.128 ...f; clear(hppp); vppp=0;
+
+I already knew that this equation was true.
+But perhaps no harm has been done; let's continue.
+
+{begingroup}
+
+clear(TEXT0)->interim(TEXT0):=$
+(TEXT0)<-tracingmacros
+{interim}
+{tracingmacros:=-1}
+{interim}
+{tracingcommands:=-1}
+Runaway text?
+tracingoutput
+! Forbidden token found while scanning a text argument.
+<inserted text>
+ )
+<to be read again>
+ \
+l.129 ...); clear(tracingoutput\
+ ;
+It seems that a right delimiter was left out,
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{restoring proofing=1}
+{restoring tracingoutput=1}
+{restoring tracingcommands=2.1}
+{restoring tracingmacros=1}
+{endgroup}
+{def}
+{def}
+{addto}
+Path at line 139, before choices:
+(0,0){4096,0}
+ ..{4096,0}(1,0){0,-4096}
+ ..{0,4096}cycle
+
+Path at line 139, after choices:
+(0,0)..controls (0.33333,0) and (0.66667,0)
+ ..(1,0)..controls (1,-0.66667) and (0,-0.66667)
+ ..cycle
+
+> 0 ENE 1 (ESE SSE) SSW WSW WNW NNW 0 (NNE)
+! Backwards path (turning number is negative).
+<to be read again>
+ ;
+l.139 ....{0,1}cycle withpen qq;
+
+The path doesn't have a counterclockwise orientation,
+so I'll probably have trouble drawing it.
+(See Chapter 27 of The METAFONTbook for more help.)
+
+Tracing edges at line 139: (weight 1)
+@ Octant ENE (2 offsets), from (2.5,-0.5) to (1.5,-2)
+@ retrograde line from (2.5,-0.5) to (0.5,-2)
+(3,0)(3,-1)(1,-1)
+@ transition line 0, from (0.5,-2) to (0.5,-2)
+@ transition line 1, from (1.5,-2) to (2.5,-1.5)
+@ transition line 2, from (2.5,-1.5) to (3.5,-0.5)
+(1,-2)(3,-2)(3,-1)(4,-1)
+@ retrograde line from (3.5,-0.5) to (1.5,-2)
+(4,0)(4,-1)(2,-1)
+@ Octant ESE (2 offsets), from (1.5,-2) to (-1,0)
+@ retrograde line from (1.5,-2) to (-1,0)
+(2,-2)(1,-2)(1,-1)(0,-1)
+@ transition line 3, from (-1,0) to (0.5,-1.5)
+@ transition line 2, from (0.5,-1.5) to (1.5,-2)
+@ transition line 1, from (1.5,-2) to (1.5,-2)
+(0,0)(0,-1)(1,-1)
+@ retrograde line from (1.5,-2) to (-1,0)
+(1,-2)(1,-1)(0,-1)
+@ Octant SSE (0 offsets), from (-1,0) to (-1,0)
+@ transition line 0, from (-1,0) to (-1,0)
+@ Octant SSW (2 offsets), from (-1,0) to (-0.82407,0.67593)
+@ retrograde line from (-1,0) to (-0.5,1.5)
+(0,0)(-1,0)(-1,1)(0,1)
+@ transition line 3, from (-0.5,1.5) to (-0.5,1.5)
+@ transition line 2, from (-0.5,1.5) to (-1,0)
+@ transition line 1, from (-1.06265,-0.39499) to (-0.56265,1.10501)
+@ transition line 2, from (-0.82407,0.67593) to (-1.32407,-0.82407)
+@ transition line 1, from (-1.32407,-0.82407) to (-1.32407,-0.82407)
+(0,2)(0,1)(-1,1)
+@ retrograde line from (-1.32407,-0.82407) to (-0.82407,0.67593)
+(-1,-1)
+@ Octant WSW (2 offsets), from (-0.82407,0.67593) to (1,2)
+@ retrograde line from (-0.82407,0.67593) to (1.17593,2.17593)
+(-1,1)(0,1)
+@ transition line 0, from (1.17593,2.17593) to (1.17593,2.17593)
+@ transition line 1, from (1.17593,2.17593) to (0.17593,1.67593)
+@ transition line 2, from (0,1.5) to (-1,0.5)
+(0,2)
+@ retrograde line from (-1,0.5) to (1,2)
+(0,1)
+@ Octant WNW (2 offsets), from (1,2) to (3.32407,0.17593)
+@ retrograde line from (1,2) to (3.5,0)
+(0,2)(2,2)(2,1)(3,1)
+@ transition line 3, from (3.5,0) to (2,1.5)
+@ transition line 2, from (1.82407,1.67593) to (0.82407,2.17593)
+@ transition line 1, from (0.82407,2.17593) to (0.82407,2.17593)
+(3,0)(3,1)(2,1)
+@ retrograde line from (0.82407,2.17593) to (3.32407,0.17593)
+(2,2)(2,1)(3,1)
+@ Octant NNW (0 offsets), from (3.32407,0.17593) to (3,1)
+@ transition line 0, from (3.32407,0.17593) to (3.32407,0.17593)
+(3,0)
+@ Octant NNE (2 offsets), from (3,1) to (2.5,-0.5)
+@ retrograde line from (3,1) to (2.5,-0.5)
+(3,1)
+@ transition line 3, from (2.5,-0.5) to (2.5,-0.5)
+@ transition line 2, from (2.5,-0.5) to (3,1)
+@ transition line 1, from (3,1) to (3,1)
+(3,0)
+@ retrograde line from (3,1) to (2.5,-0.5)
+(3,1)(3,0).
+
+
+f(SUFFIX0)(EXPR1)(EXPR2)(TEXT3)->numeric.w;show(EXPR1);addto(SUFFIX0)con
+tour(0,0)..(2,0)..(1,$)..(1,1)..cycle.withpen.qq;addto(SUFFIX0)doublepat
+h(0,0){1,1}..{2,1}(2,1)withpen.qq;addto(SUFFIX0)doublepath(($,$){1,0}..(
+1,1){1,0})scaled0.5withpen.nullpen;cull(SUFFIX0)keeping(4,4)withweight1.
+5;
+! Missing `]' has been inserted.
+<to be read again>
+ ,
+l.140 f(e[3,
+ w); g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+I've seen a `[' and a subscript value, in a suffix,
+so a right bracket should have come next.
+I shall pretend that one was there.
+
+(SUFFIX0)<-e3
+(EXPR1)<-unknown path w
+! Missing argument to f.
+<to be read again>
+ ;
+l.140 f(e[3,w);
+ g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+That macro has more parameters than you thought.
+I'll continue by pretending that each missing argument
+is either zero or null.
+
+(EXPR2)<-0
+! Missing argument to f.
+<to be read again>
+ ;
+l.140 f(e[3,w);
+ g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+That macro has more parameters than you thought.
+I'll continue by pretending that each missing argument
+is either zero or null.
+
+(TEXT3)<-
+{numeric}
+{show}
+>> unknown path %CAPSULE1615
+{addto}
+Path at line 140, before choices:
+(0,0)
+ ..(2,0)
+ ..(1,-1)
+ ..(1,1)
+ ..cycle
+
+Path at line 140, after choices:
+(0,0)..controls (0.56189,-0.5286) and (1.43811,0.5286)
+ ..(2,0)..controls (2.70831,-0.66634) and (1.66634,-1.70831)
+ ..(1,-1)..controls (0.4714,-0.43811) and (1.5286,0.43811)
+ ..(1,1)..controls (0.33366,1.70831) and (-0.70831,0.66634)
+ ..cycle
+
+> 0 ESE ENE ESE 1 SSE SSW WSW WNW NNW 2 NNE NNW 3 WNW WSW SSW SSE ESE 0
+! Strange path (turning number is zero).
+<to be read again>
+ ;
+f->.....(1,1)..cycle.withpen.qq;
+ addto(SUFFIX0)doublepath(0,0)...
+<to be read again>
+ ;
+l.140 f(e[3,w);
+ g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+The path doesn't have a counterclockwise orientation,
+so I'll probably have trouble drawing it.
+(See Chapter 27 of The METAFONTbook for more help.)
+
+Tracing edges at line 140: (weight 1)
+@ Octant ENE (2 offsets), from (0.26321,-2.6526) to (1.73679,-2.34741)
+@ transition line 0, from (0.26321,-2.6526) to (0.26321,-2.6526)
+@ transition line 1, from (1.73679,-2.34741) to (2.73679,-1.84741)
+@ transition line 2, from (2.73679,-1.84741) to (3.73679,-0.84741)
+(1,-3)(1,-2)(3,-2)
+@ retrograde line from (3.73679,-0.84741) to (1.73679,-2.34741)
+(3,-1)
+@ Octant ESE (2 offsets), from (1.73679,-2.34741) to (-0.25465,-0.56029)
+@ retrograde line from (1.73679,-2.34741) to (-0.76321,-0.34741)
+(3,-2)(1,-2)(1,-1)(-1,-1)
+@ transition line 3, from (-0.76321,-0.34741) to (0.73679,-1.84741)
+@ transition line 2, from (0.73679,-1.84741) to (1.73679,-2.34741)
+@ transition line 1, from (2.05333,-2.41772) to (1.05333,-1.91772)
+@ transition line 2, from (1.24535,-2.06029) to (2.24535,-2.56029)
+@ transition line 1, from (2.24535,-2.56029) to (2.24535,-2.56029)
+(-1,0)(-1,-1)(0,-1)(0,-2)(2,-2)
+@ retrograde line from (2.24535,-2.56029) to (-0.25465,-0.56029)
+(2,-3)(2,-2)(1,-2)
+@ Octant SSE (0 offsets), from (-0.25465,-0.56029) to (0,-1.13196)
+@ transition line 0, from (-0.25465,-0.56029) to (-0.25465,-0.56029)
+@ Octant SSW (2 offsets), from (0,-1.13196) to (0.25,-0.25)
+@ retrograde line from (0,-1.13196) to (0.5,0.36804)
+(1,-1)(0,-1)
+@ transition line 3, from (0.5,0.36804) to (0.5,0.36804)
+@ transition line 2, from (0.5,0.36804) to (0,-1.13196)
+@ transition line 1, from (-0.04594,-1.41693) to (0.45406,0.08307)
+@ transition line 2, from (0.25,-0.25) to (-0.25,-1.75)
+@ transition line 1, from (-0.25,-1.75) to (-0.25,-1.75)
+(0,0)
+@ retrograde line from (-0.25,-1.75) to (0.25,-0.25)
+(0,-2)
+@ Octant WSW (2 offsets), from (0.25,-0.25) to (1.63197,1)
+@ retrograde line from (0.25,-0.25) to (2.25,1.25)
+(0,0)(1,0)
+@ transition line 0, from (2.25,1.25) to (2.25,1.25)
+@ transition line 1, from (2.25,1.25) to (1.25,0.75)
+@ transition line 2, from (1.03186,0.59352) to (2.03186,1.09352)
+@ transition line 1, from (1.63197,1) to (0.63197,0.5)
+@ transition line 2, from (0.63197,0.5) to (-0.36803,-0.5)
+(1,1)
+@ retrograde line from (-0.36803,-0.5) to (1.63197,1)
+(1,0)
+@ Octant WNW (2 offsets), from (1.63197,1) to (3.56024,-0.74529)
+@ retrograde line from (1.63197,1) to (4.13197,-1)
+(1,1)(2,1)(2,0)(4,0)
+@ transition line 3, from (4.13197,-1) to (2.63197,0.5)
+@ transition line 2, from (2.63197,0.5) to (1.63197,1)
+@ transition line 1, from (1.29863,1.07983) to (2.29863,0.57983)
+@ transition line 2, from (2.06024,0.75471) to (1.06024,1.25471)
+@ transition line 1, from (1.06024,1.25471) to (1.06024,1.25471)
+(4,-1)(4,0)(3,0)
+@ retrograde line from (1.06024,1.25471) to (3.56024,-0.74529)
+(3,1)(2,1)(2,0)(3,0)
+@ Octant NNW (0 offsets), from (3.56024,-0.74529) to (3.3474,-0.23679)
+@ transition line 0, from (3.56024,-0.74529) to (3.56024,-0.74529)
+(3,-1)
+@ Octant NNE (2 offsets), from (3.3474,-0.23679) to (3.65259,1.23679)
+@ retrograde line from (3.3474,-0.23679) to (2.8474,-1.73679)
+(3,0)
+@ transition line 3, from (2.8474,-1.73679) to (2.8474,-1.73679)
+@ transition line 2, from (2.8474,-1.73679) to (3.3474,-0.23679)
+@ transition line 1, from (3.65259,1.23679) to (3.65259,1.23679)
+(3,-2)
+@ Octant NNW (0 offsets), from (3.65259,1.23679) to (3.43976,1.74529)
+@ transition line 0, from (3.65259,1.23679) to (3.65259,1.23679)
+(3,1)(4,1)
+@ Octant WNW (2 offsets), from (3.43976,1.74529) to (0.36803,4)
+@ transition line 3, from (3.43976,1.74529) to (1.93976,3.24529)
+@ transition line 2, from (1.70137,3.42017) to (0.70137,3.92017)
+@ transition line 1, from (0.36803,4) to (0.36803,4)
+(4,2)(3,2)(3,3)(2,3)
+@ Octant WSW (2 offsets), from (0.36803,4) to (-2.25,2.25)
+@ transition line 0, from (0.36803,4) to (0.36803,4)
+@ transition line 1, from (-0.03186,3.90648) to (-1.03186,3.40648)
+@ transition line 2, from (-1.25,3.25) to (-2.25,2.25)
+(2,4)(-1,4)(-1,3)(-2,3)
+@ Octant SSW (2 offsets), from (-2.25,2.25) to (-3,0.13196)
+@ transition line 3, from (-2.25,2.25) to (-2.25,2.25)
+@ transition line 2, from (-2.45406,1.91695) to (-2.95406,0.41695)
+@ transition line 1, from (-3,0.13196) to (-3,0.13196)
+(-2,2)(-3,2)
+@ Octant SSE (0 offsets), from (-3,0.13196) to (-2.74533,-0.43973)
+@ transition line 0, from (-3,0.13196) to (-3,0.13196)
+@ Octant ESE (2 offsets), from (-2.74533,-0.43973) to (0.26321,-2.6526)
+@ transition line 3, from (-2.74533,-0.43973) to (-1.24533,-1.93973)
+@ transition line 2, from (-1.05331,-2.08229) to (-0.05331,-2.58229)
+@ transition line 1, from (0.26321,-2.6526) to (0.26321,-2.6526)
+(-3,-1)(-2,-1)(-2,-2)(0,-2)(0,-3).
+
+{addto}
+Path at line 140, before choices:
+(0,0){2896.30943,2896.30934}
+ ..{3663.57385,1831.78674}(2,1)
+
+Path at line 140, after choices:
+(0,0)..controls (0.53566,0.53566) and (1.32689,0.66344)
+ ..(2,1)
+
+Tracing edges at line 140: (weight 1)
+@ Octant ENE (2 offsets), from (0,-2.5) to (4,0)
+@ transition line 0, from (0,-2.5) to (0,-2.5)
+@ transition line 1, from (0,-2.5) to (1,-2)
+@ transition line 2, from (1.607,-1.57747) to (0.607,-2.07747)
+@ transition line 1, from (2,-1.5) to (3,-1)
+@ transition line 2, from (3,-1) to (4,0)
+(2,-2)(2,-1)(4,-1)
+@ Octant NNE (2 offsets), from (4,0) to (4.5,1.5)
+@ transition line 3, from (4,0) to (4,0)
+@ transition line 2, from (4,0) to (4.5,1.5)
+@ transition line 1, from (4.5,1.5) to (4.5,1.5)
+(4,1)(5,1)
+@ Octant NNW (0 offsets), from (4.5,1.5) to (4.5,1.5)
+@ transition line 0, from (4.5,1.5) to (4.5,1.5)
+@ Octant WNW (2 offsets), from (4.5,1.5) to (2,3.5)
+@ transition line 3, from (4.5,1.5) to (3,3)
+@ transition line 2, from (3,3) to (2,3.5)
+@ transition line 1, from (2,3.5) to (2,3.5)
+(5,2)(4,2)(4,3)(2,3)
+@ Octant WSW (2 offsets), from (2,3.5) to (-2,1)
+@ transition line 0, from (2,3.5) to (2,3.5)
+@ transition line 1, from (0.607,2.92253) to (-0.393,2.42253)
+@ transition line 2, from (-1,2) to (-2,1)
+(2,4)(2,3)(0,3)(0,2)(-1,2)
+@ Octant SSW (2 offsets), from (-2,1) to (-2.5,-0.5)
+@ transition line 3, from (-2,1) to (-2,1)
+@ transition line 2, from (-2,1) to (-2.5,-0.5)
+@ transition line 1, from (-2.5,-0.5) to (-2.5,-0.5)
+(-1,1)(-2,1)
+@ Octant SSE (0 offsets), from (-2.5,-0.5) to (-2.5,-0.5)
+@ transition line 0, from (-2.5,-0.5) to (-2.5,-0.5)
+@ Octant ESE (2 offsets), from (-2.5,-0.5) to (0,-2.5)
+@ transition line 3, from (-2.5,-0.5) to (-1,-2)
+@ transition line 2, from (-1,-2) to (0,-2.5)
+@ transition line 1, from (0,-2.5) to (0,-2.5)
+(-2,-1)(-1,-1)(-1,-2).
+
+{addto}
+Path at line 140, before choices:
+(-1,-1){4096,0}
+ ..{4096,0}(1,1)
+
+Path at line 140, after choices:
+(-1,-1)..controls (0.10457,-1) and (-0.10457,1)
+ ..(1,1)
+
+{(path)scaled(0.5)}
+{nullpen}
+Tracing edges at line 140: (weight 1)
+(0,0)(0,1)(0,0).
+
+{cull}
+
+g(SUFFIX0)->show(SUFFIX0)
+(SUFFIX0)<-e3
+! Too many arguments to g;
+ Missing `)' has been inserted.
+l.140 f(e[3,w); g(e3,
+ transformed p,penoffset-(1,1.3)of(pencir...
+I'm going to assume that the comma I just read was a
+right delimiter, and then I'll begin expanding the macro.
+You might want to delete some tokens before continuing.
+
+{show}
+{(picture)transformed((8,1,5,-5,-5,5))}
+>> Edge structure at line 140:
+
+{-((1,1.3))}
+{pencircle}
+{(future pen)scaled(20)}
+{-(0.5)}
+{(future pen)yscaled(-0.5)}
+Pen polygon at line 140 (newly created):
+(1.5,-5)
+ .. (4.5,-4.5)
+ .. (6,-4)
+ .. (8,-3)
+ .. (9.5,-1.5)
+ .. (10,-0.5)
+ .. (10,0.5)
+ .. (9.5,1.5)
+ .. (8,3)
+ .. (6,4)
+ .. (4.5,4.5)
+ .. (1.5,5)
+ .. (-1.5,5)
+ .. (-4.5,4.5)
+ .. (-6,4)
+ .. (-8,3)
+ .. (-9.5,1.5)
+ .. (-10,0.5)
+ .. (-10,-0.5)
+ .. (-9.5,-1.5)
+ .. (-8,-3)
+ .. (-6,-4)
+ .. (-4.5,-4.5)
+ .. (-1.5,-5)
+ .. cycle
+
+{((-1,-1.3))penoffset(pen)}
+>> (-9.5,1.5)
+Path at line 141, before choices:
+(0,0)..controls (1,1) and (-1,1)
+ ..(3,0)
+
+Path at line 141, after choices:
+(0,0)..controls (1,1) and (-1,1)
+ ..(3,0)
+
+{((0,1))directiontime(path)}
+>> 0.33333
+{(3.14159)point(path)}
+{((3.14159,4))subpath(path)}
+{((7.68668,2.72437))intersectiontimes(path)}
+>> (0,0)
+Path at line 143, before choices:
+(-1,1.1){curl 1}
+ ..{curl 1}(1,-1)
+
+Path at line 143, after choices:
+(-1,1.1)..controls (-0.33333,0.40001) and (0.33333,-0.3)
+ ..(1,-1)
+
+{(-1)precontrol((0,0))}
+{(path)intersectiontimes((0,0))}
+>> (-1,-1)
+{addto}
+{-(4094.99998)}
+{-(0.00002)}
+Path at line 144, before choices:
+(-4094.99998,0){curl 1}
+ ..{curl 1}(4094.99998,-0.00002)
+
+Path at line 144, after choices:
+(-4094.99998,0)..controls (-1365,0) and (1365,-0.00002)
+ ..(4094.99998,-0.00002)
+
+{pencircle}
+{(0)penoffset(future pen)}
+Pen polygon at line 145 (newly created):
+(0.5,0)
+ .. (0,0.5)
+ .. (-0.5,0)
+ .. (0,-0.5)
+ .. cycle
+
+>> 0
+>> pen
+! Not implemented: penoffset(known numeric)of(pen).
+<to be read again>
+ ;
+l.145 pencircle;
+ addto e3 also e3 shifted (0,257); ,"flushed ...
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+Tracing edges at line 145: (weight 1)
+@ Octant ESE (1 offset), from (-4095.49998,0) to (4094.99998,-0.50002)
+@ transition line 2, from (-4095.49998,0) to (-4094.99998,-0.5)
+@ transition line 1, from (4094.99998,-0.50002) to (4094.99998,-0.50002)
+(-4095,0)
+@ Octant ENE (1 offset), from (4094.99998,-0.50002) to (4095.49998,-0.00
+002)
+@ transition line 0, from (4094.99998,-0.50002) to (4094.99998,-0.50002)
+@ transition line 1, from (4094.99998,-0.50002) to (4095.49998,-0.00002)
+(-4095,-1)(4095,-1)
+@ Octant NNE (0 offsets), from (4095.49998,-0.00002) to (4095.49998,-0.0
+0002)
+@ transition line 1, from (4095.49998,-0.00002) to (4095.49998,-0.00002)
+@ Octant NNW (0 offsets), from (4095.49998,-0.00002) to (4095.49998,-0.0
+0002)
+@ transition line 0, from (4095.49998,-0.00002) to (4095.49998,-0.00002)
+@ Octant WNW (1 offset), from (4095.49998,-0.00002) to (-4094.99998,0.5)
+@ transition line 2, from (4095.49998,-0.00002) to (4094.99998,0.49998)
+@ transition line 1, from (-4094.99998,0.5) to (-4094.99998,0.5)
+(4095,0)(-4095,0)
+@ Octant WSW (1 offset), from (-4094.99998,0.5) to (-4095.49998,0)
+@ transition line 0, from (-4094.99998,0.5) to (-4094.99998,0.5)
+@ transition line 1, from (-4094.99998,0.5) to (-4095.49998,0)
+(-4095,1)
+@ Octant SSW (0 offsets), from (-4095.49998,0) to (-4095.49998,0)
+@ transition line 1, from (-4095.49998,0) to (-4095.49998,0)
+@ Octant SSE (0 offsets), from (-4095.49998,0) to (-4095.49998,0)
+@ transition line 0, from (-4095.49998,0) to (-4095.49998,0)
+(-4095,0).
+
+{addto}
+{(picture)shifted((0,257))}
+! A statement can't begin with `,'.
+<to be read again>
+ ,
+l.145 ...o e3 shifted (0,257); ,
+ "flushed with pride"; numeric...
+I was looking for the beginning of a new statement.
+If you just proceed without changing anything, I'll ignore
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+! Extra tokens will be flushed.
+<to be read again>
+ ,
+l.145 ...o e3 shifted (0,257); ,
+ "flushed with pride"; numeric...
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+{numeric}
+{def}
+
+f(EXPR0)(EXPR1)(EXPR2)->showdependencies;tracingcapsules:=1;showdependen
+cies;show1/3(3,6)*(((EXPR0)+(EXPR1))+((EXPR1)-(EXPR0))),(1,1)/sqrt2zscal
+ed((EXPR0)+1,(EXPR0)+2)-((EXPR0)+1,(EXPR0)+2)rotated45,(0,1)zscaled(1,(E
+XPR1)+2)-(1,(EXPR1)+2)rotated90
+{(xx)+(1)}
+{(xx+1)/(0.3)}
+(EXPR0)<-3.3333xx+3.3333
+{(yy)-(1)}
+{(yy-1)/(0.5)}
+(EXPR1)<-2yy-2
+(EXPR2)<-(xx,0)
+{showdependencies}
+alpha=0.45p3~+7
+{tracingcapsules:=1}
+{showdependencies}
+xpart %CAPSULE1359=xx
+%CAPSULE1367=2yy-2
+%CAPSULE1375 = 3.3333xx+3.3333
+alpha=0.45p3~+7
+{show}
+{(1/3)*((3,6))}
+{(3.3333xx+3.3333)+(2yy-2)}
+{(2yy-2)-(3.3333xx+3.3333)}
+{(linearform)+(linearform)}
+{((1,2))*(4yy-4)}
+>> (4yy-4,8yy-8)
+{sqrt(2)}
+{((1,1))/(1.41422)}
+{(3.3333xx+3.3333)+(1)}
+{(3.3333xx+3.3333)+(2)}
+{((0.7071,0.7071))zscaled((3.3333xx+4.3333,3.3333xx+5.3333))}
+{(3.3333xx+3.3333)+(1)}
+{(3.3333xx+3.3333)+(2)}
+{((3.3333xx+4.3333,3.3333xx+5.3333))rotated(45)}
+{((-0.7071,4.71399xx+6.83531))-((-0.7071,4.71399xx+6.83531))}
+>> (0,0)
+{(2yy-2)+(2)}
+{((0,1))zscaled((1,2yy))}
+{(2yy-2)+(2)}
+{((1,2yy))rotated(90)}
+{((-2yy,1))-((-2yy,1))}
+>> (0,0)
+{(1000)*(o3)}
+{(o1)-(o2)}
+{(4000)*(linearform)}
+{(1000o3)-(linearform)}
+{(4000)*(o2)}
+{(linearform)+(4000o2)}
+{(linearform)+(9)}
+{(0.01)*(o3)}
+{-(0.01o3)}
+{(3)*(ooo)}
+{(-0.01o3)+(3ooo)}
+{begingroup}
+{(o2)+(o4)}
+{(linearform)+(o5)}
+{(linearform)-(20)}
+{(9/10)*(linearform)}
+{(oo)=(linearform)}
+## oo=0.9o5+0.9o4+0.9o2-18
+{(0.5)*(o2)}
+{(o1)+(0.5o2)}
+{(8/9)*(linearform)}
+{(o5)=(linearform)}
+## o5=0.44444o2+0.88889o1
+{(o4)=(linearform)}
+## o4=0.44444o2+0.88889o1
+{(0.0001)*(o2)}
+{-(0.0001o2)}
+{(o6)=(-0.0001o2)}
+## o6=-0.0001o2
+{showdependencies}
+o6=-0.0001o2
+o4=0.44444o2+0.88889o1
+o5=0.44444o2+0.88889o1
+oo=1.7o2+1.6o1-18
+%CAPSULE382 = 3ooo-0.01o3
+xpart %CAPSULE1049 = 8000o2-4000o1+1000o3+9
+xpart %CAPSULE1383=xpart '
+ypart %CAPSULE1383=ypart '
+alpha=0.45p3~+7
+{numeric}
+### 4000o1 = -xpart %CAPSULE1049+8000o2+1000o3+9
+### -4.87383o2 = -oo-0.0004xpart %CAPSULE1049+0.39673o3-17.99643
+#### o6=-0.00027
+### -0.04366o3 = -o4+0.46689oo+8.40439
+### -o4 = -o5
+### 0.22894o5 = -%CAPSULE382+0.10689oo+3ooo+1.92412
+{begingroup}
+{pair}
+### -alfa=-xpart %CAPSULE1494
+{endgroup}
+{xpart((xpart %CAPSULE1494,0))}
+### -xpart %CAPSULE17=-%CAPSULE1176
+{endgroup}
+{(%CAPSULE382)+(%CAPSULE1176)}
+### -%CAPSULE382=-%CAPSULE1893+%CAPSULE1176
+### -%CAPSULE1893=-ypart %CAPSULE1049
+{begingroup}
+{save}
+{(1)-(p$)}
+{restoring p}
+### p$=-ypart %CAPSULE604+1
+{endgroup}
+{(2/3)*((-ypart %CAPSULE604+1,ypart %CAPSULE604))}
+### ypart %CAPSULE604=-xpart %CAPSULE604+1
+### -0.66667xpart %CAPSULE604=-xpart %CAPSULE1889
+{-((xpart %CAPSULE1889,-xpart %CAPSULE1889+0.66667))}
+### xpart %CAPSULE1889=-xpart %CAPSULE1172
+{((xpart %CAPSULE1049,ypart %CAPSULE1049))=((xpart %CAPSULE1172,-xpart %
+CAPSULE1172-0.66667))}
+## xpart %CAPSULE1172=-ypart %CAPSULE1049-0.66667
+## ypart %CAPSULE1049=-xpart %CAPSULE1049-0.66667
+### -xpart %CAPSULE1049=-xpart %CAPSULE1172
+{((xpart ',ypart '))=((xpart %CAPSULE1172,-xpart %CAPSULE1172-0.66667))}
+## xpart %CAPSULE1172=-ypart '-0.66667
+## xpart '=-ypart '-0.66667
+{begingroup}
+{(0.5)*(ooo)}
+{begingroup}
+{numeric}
+### -ooo=-%CAPSULE1494
+{endgroup}
+{(%CAPSULE1494)+(1)}
+### -%CAPSULE1494=-%CAPSULE1350+1
+{(1/2)*(%CAPSULE1350)}
+### -0.5%CAPSULE1350=-%CAPSULE1367
+{-(%CAPSULE1367)}
+### %CAPSULE1367=-%CAPSULE1893
+{(2)*(%CAPSULE1893)}
+### -2%CAPSULE1893=-%CAPSULE1885
+{(-0.5%CAPSULE1885-0.5)=(%CAPSULE1885)}
+## %CAPSULE1885=-0.33333
+{(oo)=(-0.33333)}
+## oo=-0.33333
+{endgroup}
+{(4000)*(-0.33333)}
+{yoffset:=-1333.31299}
+{xoffset:=-1333.31299}
+{for}
+{sqrt(-1)}
+! Square root of -1 has been replaced by 0.
+l.153 for @=angle(sqrt$,
+ mlog$):charext:=uniformdeviate$;charh...
+Since I don't take square roots of negative numbers,
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{mlog(-1)}
+! Logarithm of -1 has been replaced by 0.
+l.153 for @=angle(sqrt$,mlog$)
+ :charext:=uniformdeviate$;charh...
+Since I don't take logs of non-positive numbers,
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{angle((0,0))}
+! angle(0,0) is taken as zero.
+l.153 for @=angle(sqrt$,mlog$):
+ charext:=uniformdeviate$;charh...
+The `angle' between two identical points is undefined.
+I'm zeroing this one. Proceed, with fingers crossed.
+
+Runaway loop?
+charext:=uniformdeviate$;charht:=2048;granularity:=-8; ETC.
+! Forbidden token found while scanning the text of a for loop.
+<inserted text>
+ endfor
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+I suspect you have forgotten an `endfor',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{loop value=0}
+{uniformdeviate(-1)}
+{charext:=-0.00507}
+{charht:=2048}
+{-(8)}
+{granularity:=-8}
+{addto}
+{-(100)}
+{-(99)}
+Path at line 163, before choices:
+(0,-100)..tension 500
+ ..(100,-99)..tension 3000
+ ..cycle
+
+Path at line 163, after choices:
+(0,-100)..controls (-36.92659,-103.51175) and (136.98943,-101.7726)
+ ..(100,-99)..controls (99.9889,-98.99916) and (0.01108,-99.99895)
+ ..cycle
+
+Tracing edges at line 163: (weight 1)
+(-3,-101)(-3,-102)(71,-102)(71,-101)(99,-101)(99,-100)(103,-100)
+(103,-99)(48,-99)(48,-100)(-7,-100)(-7,-101).
+
+{tracingoutput:=0}
+{shipout}
+! Enormous charht has been reduced.
+<to be read again>
+ ;
+<for(0)> ...=(EXPR0);shipout.e3;
+ special"bye";interim.char99="...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+Font metric dimensions must be less than 2048pt.
+
+[109]
+{special}
+{interim}
+! The token `char' isn't an internal quantity.
+<to be read again>
+ char
+<for(0)> ...al"bye";interim.char
+ 99="c";true=false;[[clear(tra...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+Something like `tracingonline' should follow `interim'.
+
+{char(99)}
+{("c")=("c")}
+! Redundant equation.
+<to be read again>
+ ;
+<for(0)> ...;interim.char99="c";
+ true=false;[[clear(tracingcom...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+I already knew that this equation was true.
+But perhaps no harm has been done; let's continue.
+
+{true}
+{false}
+{(true)=(false)}
+! Inconsistent equation.
+<to be read again>
+ ;
+<for(0)> ...ar99="c";true=false;
+ [[clear(tracingcommands);char...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+The equation I just read contradicts what was said before.
+But don't worry; continue and I'll just ignore it.
+
+{begingroup}
+
+clear(TEXT0)->interim(TEXT0):=$
+(TEXT0)<-tracingcommands
+{interim}
+{tracingcommands:=-1}
+! Enormous charht has been reduced.
+<to be read again>
+ ;
+<for(0)> ...shipout+nullpicture;
+ "careful"for.for=(EXPR0)step2...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+Font metric dimensions must be less than 2048pt.
+
+[0]
+careful METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT M
+ETAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT M
+ETAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT M
+ETAFONT METAFONT METAFONT METAFONT
+{restoring tracingcommands=2.1}
+{endgroup}
+{scrollmode}
+
+{char(31)}
+{("hello again")&("^^_")}
+hello again^^_
+{save}
+### -0.45p3~=-alpha+7
+{-(0.043)}
+{fillin:=-0.043}
+{def}
+{begingroup}
+{tracingspecs:=1}
+{show}
+{nullpen}
+
+>> Pen polygon at line 163:
+(0,0)
+ .. cycle
+
+{makepath(pen)}
+
+>> Path at line 163:
+(1,-2)..controls (1,-2) and (2,-1)
+ ..(2,-1)..controls (2,-1) and (2.5,0.5)
+ ..(2.5,0.5)..controls (2.5,0.5) and (1,2)
+ ..(1,2)..controls (1,2) and (0,2.5)
+ ..(0,2.5)..controls (0,2.5) and (-1,2)
+ ..(-1,2)..controls (-1,2) and (-2,1)
+ ..(-2,1)..controls (-2,1) and (-2.5,-0.5)
+ ..(-2.5,-0.5)..controls (-2.5,-0.5) and (-1,-2)
+ ..(-1,-2)..controls (-1,-2) and (0,-2.5)
+ ..(0,-2.5)..controls (0,-2.5) and (1,-2)
+ ..cycle
+
+{(pen)rotated(1)}
+Pen polygon at line 163 (newly created):
+(0.97366,1.51723)
+ .. cycle
+
+{makepath(pen)}
+
+>> Path at line 163:
+(0.97366,1.51723)..controls (0.97366,1.51723) and (0.97366,1.51723)
+ ..cycle
+
+{pencircle}
+{makepath(future pen)}
+Pen polygon at line 163 (newly created):
+(0.5,-0.5)
+ .. (0.5,0.5)
+ .. (-0.5,0.5)
+ .. (-0.5,-0.5)
+ .. cycle
+
+{(path)rotated(-1)}
+
+>> Path at line 163:
+(0.4912,-0.50865)..controls (0.4912,-0.50865) and (0.50865,0.4912)
+ ..(0.50865,0.4912)..controls (0.50865,0.4912) and (-0.4912,0.50865)
+ ..(-0.4912,0.50865)..controls (-0.4912,0.50865) and (-0.50865,-0.4912)
+ ..(-0.50865,-0.4912)..controls (-0.50865,-0.4912) and (0.4912,-0.50865)
+ ..cycle
+
+{addto}
+Path at line 163, before choices:
+(0,2){0,-4096}
+ ..{0,-4096}(0,1){0,-4096}
+ ..{4096,0}(3,0){4096,0}
+ ..{4096,0}(4,0){4096,0}
+ ..{0,-4096}cycle
+
+Path at line 163, after choices:
+(0,2)..controls (0,1.66667) and (0,1.33333)
+ ..(0,1)..controls (0,-0.20886) and (1.63324,0)
+ ..(3,0)..controls (3.33333,0) and (3.66667,0)
+ ..(4,0)..controls (13.63031,0) and (0,10.64238)
+ ..cycle
+
+Path at line 163, before choices:
+(0,0)
+ ..(5,2.9)
+ ..(4,3)
+ ..cycle
+
+Path at line 163, after choices:
+(0,0)..controls (0.9363,-8.0181) and (11.81119,0.07109)
+ ..(5,2.9)..controls (4.68596,3.03043) and (4.33879,3.02028)
+ ..(4,3)..controls (1.86353,2.87216) and (-0.21431,1.83528)
+ ..cycle
+
+{makepen(path)}
+Pen polygon at line 163 (newly created):
+(5,2.9)
+ .. (4,3)
+ .. (0,0)
+ .. cycle
+
+Path at line 163, before subdivision into octants:
+(0,2)..controls (0,1.66667) and (0,1.33333)
+ ..(0,1)..controls (0,-0.20886) and (1.63324,0)
+ ..(3,0)..controls (3.33333,0) and (3.66667,0)
+ ..(4,0)..controls (13.63031,0) and (0,10.64238)
+ ..cycle
+
+Cycle spec at line 163, after subdivision and double autorounding:
+(1.5,0.00272) % beginning in octant `SSE'
+ ..controls (1.5,-0.49254) and (1.63411,-0.82727)
+ ..(1.8592,-1.05237) % segment 1
+% entering octant `ESE'
+ ..controls (2.23936,-1.43253) and (2.87909,-1.5)
+ ..(3.57066,-1.5) % segment 1
+% entering octant `ENE'
+ ..controls (3.95114,-1.5) and (4.3473,-1.47957)
+ ..(4.72458,-1.47957) % segment 1
+ ..controls (5.08287,-1.47957) and (5.44116,-1.47957)
+ ..(5.79944,-1.47957) % segment 2
+ ..controls (7.35368,-1.47957) and (8.34424,-1.12393)
+ ..(8.90538,-0.56279) % segment 3
+% entering octant `NNE'
+ ..controls (9.31982,-0.14835) and (9.5,0.37819)
+ ..(9.5,0.95631) % segment 3
+% entering octant `NNW'
+ ..controls (9.5,2.00116) and (8.61821,3.4318)
+ ..(7.41951,4.6305) % segment 3
+% entering octant `WNW'
+ ..controls (6.32909,5.72092) and (5.11603,6.5)
+ ..(4.2104,6.5) % segment 3
+% entering octant `WSW'
+ ..controls (3.64668,6.5) and (3.12122,6.31134)
+ ..(2.68588,5.876) % segment 3
+% entering octant `SSW'
+ ..controls (1.97163,5.16176) and (1.5,3.78351)
+ ..(1.5,1.485) % segment 3
+ ..controls (1.5,0.99092) and (1.5,0.49681)
+ ..(1.5,0.00272) % segment 0
+ & cycle
+
+Tracing edges at line 163: (weight 1)
+@ Octant SSE (0 offsets), from (1.5,0.00272) to (1.8592,-1.05237)
+@ transition line 0, from (1.5,0.00272) to (1.5,0.00272)
+(2,0)
+@ Octant ESE (0 offsets), from (1.8592,-1.05237) to (3.57066,-1.5)
+@ transition line 1, from (3.57066,-1.5) to (3.57066,-1.5)
+@ Octant ENE (2 offsets), from (3.57066,-1.5) to (13.90538,2.3372)
+@ transition line 0, from (3.57066,-1.5) to (3.57066,-1.5)
+@ transition line 1, from (8.41534,-0.93503) to (13.41534,1.96497)
+@ transition line 2, from (13.90538,2.3372) to (13.90538,2.3372)
+(2,-1)(9,-1)(9,0)(11,0)(11,1)(13,1)
+@ Octant NNE (0 offsets), from (13.90538,2.3372) to (14.5,3.85631)
+@ transition line 1, from (14.5,3.85631) to (14.5,3.85631)
+(13,2)(14,2)
+@ Octant NNW (0 offsets), from (14.5,3.85631) to (12.41951,7.53049)
+@ transition line 0, from (14.5,3.85631) to (14.5,3.85631)
+(14,6)(13,6)(13,7)(12,7)
+@ Octant WNW (2 offsets), from (12.41951,7.53049) to (8.2104,9.5)
+@ transition line 3, from (12.41951,7.53049) to (12.41951,7.53049)
+@ transition line 2, from (9.38113,9.39124) to (8.38113,9.49124)
+@ transition line 1, from (8.2104,9.5) to (8.2104,9.5)
+(12,8)(11,8)(11,9)(8,9)
+@ Octant WSW (2 offsets), from (8.2104,9.5) to (2.68588,5.876)
+@ transition line 0, from (8.2104,9.5) to (8.2104,9.5)
+@ transition line 1, from (6.92668,9.08539) to (2.92668,6.08539)
+@ transition line 2, from (2.68588,5.876) to (2.68588,5.876)
+(8,10)(8,9)(6,9)(6,8)(5,8)(5,7)(3,7)
+@ Octant SSW (0 offsets), from (2.68588,5.876) to (1.5,0.00272)
+@ transition line 1, from (1.5,0.00272) to (1.5,0.00272)
+(3,6)(2,6)(2,0).
+
+Path at line 163, before subdivision into octants:
+(0,2)..controls (0,10.64238) and (13.63031,0)
+ ..(4,0)..controls (3.66667,0) and (3.33333,0)
+ ..(3,0)..controls (1.63324,0) and (0,-0.20886)
+ ..(0,1)..controls (0,1.33333) and (0,1.66667)
+ ..cycle
+
+Cycle spec at line 163, after subdivision and double autorounding:
+(2.68588,5.876) % beginning in octant `ENE'
+ ..controls (3.12122,6.31134) and (3.64668,6.5)
+ ..(4.2104,6.5) % segment 0
+% entering octant `ESE'
+ ..controls (5.11603,6.5) and (6.32909,5.72092)
+ ..(7.41953,4.63048) % segment 0
+% entering octant `SSE'
+ ..controls (8.61824,3.43176) and (9.5,2.00116)
+ ..(9.5,0.95631) % segment 0
+% entering octant `SSW'
+ ..controls (9.5,0.37819) and (9.31982,-0.14835)
+ ..(8.90538,-0.56279) % segment 0
+% entering octant `WSW'
+ ..controls (8.34425,-1.12392) and (7.35368,-1.47957)
+ ..(5.79944,-1.47957) % segment 0
+ ..controls (5.44116,-1.47957) and (5.08287,-1.47957)
+ ..(4.72458,-1.47957) % segment 1
+ ..controls (4.3473,-1.47957) and (3.95114,-1.5)
+ ..(3.57066,-1.5) % segment 2
+% entering octant `WNW'
+ ..controls (2.87909,-1.5) and (2.23936,-1.43253)
+ ..(1.85919,-1.05235) % segment 2
+% entering octant `NNW'
+ ..controls (1.6341,-0.82726) and (1.5,-0.49254)
+ ..(1.5,0.00272) % segment 2
+% entering octant `NNE'
+ ..controls (1.5,0.49681) and (1.5,0.99092)
+ ..(1.5,1.485) % segment 3
+ ..controls (1.5,3.78351) and (1.97165,5.16177)
+ ..(2.68588,5.876) % segment 0
+ & cycle
+
+Tracing edges at line 163: (weight 1)
+@ Octant ENE (2 offsets), from (7.68588,8.776) to (4.2104,6.5)
+@ retrograde line from (7.68588,8.776) to (2.68588,5.876)
+(7,9)(7,8)(5,8)(5,7)(4,7)
+@ transition line 0, from (2.68588,5.876) to (2.68588,5.876)
+@ transition line 1, from (2.68588,5.876) to (7.68588,8.776)
+@ transition line 2, from (8.13123,9.12088) to (3.13123,6.22089)
+@ transition line 1, from (4.2104,6.5) to (9.2104,9.4)
+@ transition line 2, from (9.2104,9.4) to (9.2104,9.4)
+(4,6)(4,7)(6,7)(6,8)(8,8)
+@ retrograde line from (9.2104,9.4) to (4.2104,6.5)
+(8,9)(8,8)(6,8)
+@ Octant ESE (0 offsets), from (4.2104,6.5) to (7.41953,4.63048)
+@ transition line 1, from (7.41953,4.63048) to (7.41953,4.63048)
+(6,7)(4,7)(4,6)(6,6)
+@ Octant SSE (0 offsets), from (7.41953,4.63048) to (9.5,0.95631)
+@ transition line 0, from (7.41953,4.63048) to (7.41953,4.63048)
+(6,5)(8,5)(8,3)(9,3)
+@ Octant SSW (0 offsets), from (9.5,0.95631) to (8.90538,-0.56279)
+@ transition line 1, from (8.90538,-0.56279) to (8.90538,-0.56279)
+@ Octant WSW (2 offsets), from (8.90538,-0.56279) to (7.57066,1.5)
+@ retrograde line from (8.90538,-0.56279) to (12.90538,2.43721)
+(9,-1)(9,0)(10,0)(10,1)(12,1)
+@ transition line 0, from (12.90538,2.43721) to (12.90538,2.43721)
+@ transition line 1, from (12.90538,2.43721) to (8.90538,-0.56279)
+@ transition line 2, from (8.6665,-0.76936) to (12.6665,2.23064)
+@ transition line 1, from (7.57066,1.5) to (3.57066,-1.5)
+@ transition line 2, from (3.57066,-1.5) to (3.57066,-1.5)
+(12,2)(8,2)(8,1)(6,1)(6,0)(5,0)
+@ retrograde line from (3.57066,-1.5) to (7.57066,1.5)
+(5,-1)(5,0)(6,0)(6,1)(8,1)
+@ Octant WNW (2 offsets), from (7.57066,1.5) to (6.85919,1.84764)
+@ retrograde line from (7.57066,1.5) to (8.57066,1.4)
+(8,2)
+@ transition line 3, from (8.57066,1.4) to (8.57066,1.4)
+@ transition line 2, from (8.57066,1.4) to (7.57066,1.5)
+@ transition line 1, from (6.95764,1.52585) to (7.95764,1.42584)
+@ transition line 2, from (6.85919,1.84764) to (5.85919,1.94765)
+@ transition line 1, from (5.85919,1.94765) to (5.85919,1.94765)
+(8,1)
+@ retrograde line from (5.85919,1.94765) to (6.85919,1.84764)
+@ Octant NNW (0 offsets), from (6.85919,1.84764) to (6.5,2.90271)
+@ transition line 0, from (6.85919,1.84764) to (6.85919,1.84764)
+(8,2)(7,2)
+@ Octant NNE (0 offsets), from (6.5,2.90271) to (7.68588,8.776)
+@ transition line 1, from (7.68588,8.776) to (7.68588,8.776)
+(7,9).
+
+{tracingonline:=1}
+
+f<expr>->let)=];let[=(;show._
+(EXPR0)<-xx
+{let}
+{let}
+{show}
+>> _1
+{showdependencies}
+xpart '=-ypart '-0.66667
+{qq:=pen}
+{showstats}
+Memory usage 1462&588 (104 still untouched)
+String usage 41&161 (815&7634 still untouched)
+{[repeat the loop]}
+! A group begun on line 163 never ended.
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+I saw a `begingroup' back there that hasn't been matched
+by `endgroup'. So I've inserted `endgroup' now.
+
+{endgroup}
+ )
+(end occurred when else on line 93 was incomplete)
+(end occurred when if on line 36 was incomplete)
+(end occurred when elseif on line 21 was incomplete)
+Here is how much of METAFONT's memory you used:
+ 68 strings out of 883
+ 3752 string characters out of 11386
+ 2897 words of memory out of 3001
+ 289 symbolic tokens out of 2100
+ 8i,43n,14r,8p,167b stack positions out of 30i,100n,300r,150p,500b
+
+(illegal design size has been changed to 128pt)
+(some chardp values had to be adjusted by as much as 0.5pt)
+(local label 0:: was missing)
+(local label 5:: was missing)
+(4 font metric dimensions had to be decreased)
+(You used 3w,2h,16d,2i,312l,301k,1e,10p metric file positions
+ out of 256w,16h,16d,64i,5000l,500k,256e,50p)
+
+Font metrics written on trap.tfm.
+Output written on trap.72270gf (5 characters, 1028 bytes).
diff --git a/systems/knuth/dist/mf/trap.mf b/systems/knuth/dist/mf/trap.mf
new file mode 100644
index 0000000000..adf4d7bd48
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.mf
@@ -0,0 +1,170 @@
+% This is a diabolical test file for MF84. Don't get stuck.
+if not known inimf: .inimf.=.0. % next lines are skipped if format loaded
+ inimf; nonstopmode; tracingtitles:=day; showstopping:=year; hppp:=1000;
+ << == >> ::: || `` '' -- !! ?? ## && @@ $$ [[ ]] {{ }} . (( 5.5.5 )) ++ "..";
+ begingroup save =; let=,; save,; newinternal $=,; let ):=, endgroup;
+ let year=month; showvariable errorstopmode,readstring,2,"2",,,(,),<<,year;
+ tracingrestores:=tracingcommands:=.00000762939453125; % that's 2^(-17)
+ if tracingcommands>0:tracingcommands:=if not cycle "":1.1 forever;fi;
+ tracingcommands:=2.1 exitif tracingcommands>2 endfor; showtoken |=:|>;
+ tracingedges:=1/.00001; tracingequations:=$+1; p~=tracingedges+.00001;
+ interim tracingspecs:=tracingpens:=tracingchoices:=tracingstats:=
+ warningcheck:=tracingoutput:=tracingmacros:=1; $:=ASCII""; $:=x; p~:=p~;
+ delimiters (); delimiters begintext endtext; vardef foo(text t)=t enddef;
+ def lig(text t,|)=ligtable0::for *=1step1until60:0kern boundarychar+*,endfor
+ skipto0;ligtable t:t|0,skipto255;boundarychar:=boundarychar+51.29999enddef;
+ foo begintext interim proofing:=(-.5; shipout nullpicture; special"3" endtext;
+ for n=tracingpens step 1 until proofing:fi endfor showstats; let!!=skipto;
+ path p~; p~=(0,0)..controls (15,4) and (-15,-12)..(4,0); everyjob /*\;;
+ vardef /*\`'@#=message @ & str#@ &jobname&char ASCII'`&str@#!enddef;
+ let next=dump; vardef `'='` enddef; def '`="\*/" enddef;
+elseif known"": `'pass[2.]; outer\; let next=\; delimiters ^~7! fi
+next\; % the second pass will now compute silently; the first pass will halt
+batchmode; ^~7,endgroup pausing:=1; exitif p exitif boolean pen pencircle endfor
+scantokens begingroup message char0&"watch this"; "pair p[],';"&char-1endgroup
+path p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(turningnumber',2.5);
+numeric p[][]; p[[ [$] ]]=10000000000000000; "this string constant is incomplete
+string foo[]p,p~if true:[]; p~000=char34&char200&char34;
+boolean p[]~,boolean fi.boolean; showvariable boolean; def\\= =end enddef;
+picture e[]e[], e[], p~[]~[][]; show length scantokens p~0;
+pen p~[]~,q["a",qq; p~1~=q=pencircle scaled mexp(-3016.57654);
+transform p,pp0; if p=p:qq=makepen((1,0)..cycle) xscaled hex "1000";fi
+qq:=makepen((0,0)..(1,0)..(0,1)..(0,0)..(1,0)..(0,1)..cycle);
+vardef p[][]p~ begintext suffix a,b endtext())suffix@=show #@; p.a.b() enddef;
+expandafter let\endtext\\; outer endtext,\,;;qq:=pencircle scaled 4.5 yscaled 2;
+(6,12)-p7=(0,1)transformed p=(2/(x-x),3/0)transformed p;
+p1\2p=p007=begintext if string p~[$]: p.1.2-p.1.199999,1 endtext transformed p;
+showstopping:=0;showvariable p; p=p; let [[=begingroup; let ]]=endgroup;
+(xxpart p+.002,yxpart p)=1[p1,p2]=(5,y+.00001)=(5,y)=(yypart p,xypart p);
+reverse(p~..cycle) transformed p=p2. 3.p;
+p[000000000001]2p~(,[2]3p~,-)=p~1~2[pausing];
+vardef p~[]@# tertiary t:=if p@ @=@ @p fi; vardef p[][]p~[]=BAD; inner ;;
+show p~[-2]~[3000,x]++4000>path p3; showvariable p,P;
+numeric p[]~; p3~=2alpha; p[1/$]~=3beta;
+begingroup save p; showvariable p; 3beta=1]]; showvariable p;
+def//expr;=enddef;def!primary!false):!fi enddef;
+def _aa__ secondary _a_=if(true enddef; qq:=makepen(qw..(qw scaled$)..cycle);
+primarydef _**__=[[show _*__]] enddef;
+secondarydef _***__=expandafter __ scantokens"**oct"_ enddef;
+// //pencircle slanted length p~**makepen reverse subpath p7-p2 of
+ (p7{p2}..controls _aa__ not odd.1(15) and known p or !p2and-p2..{1,1}(-p2
+ {curl- +1)..tension atleast1..cycle sqrt2++sqrt2***[[]];
+[[interim proofing:=charcode:=-20.5;chardp:=-2048;shipout nullpicture]];
+if charexists -275.50002>known p0 0p=known p~: randomseed charcode; fi
+randomseed:="goof"; a[($,18++1+-+18),(2,3)]=b[(3,2),(1,$);
+show (^+1,~+2) slanted-1 yscaled-2 zscaled-(3,4), p transformed(pp xscaled 9),
+ pp shifted (1,2) transformed(p transformed p), -_[0][1,2]; show
+floor sind mlog sqrt mexp200cosd angle(normaldeviate,uniformdeviate-chardp);
+string s[]; s1=s2=s4; s3=s5; s1=s2; if s1<=s4<>(s1<>s3):show[[char34=s2:=s3]]fi;
+substring penoffset point.1of.p~of[[pencircle]]rotated1080/2/1/3of decimal
+ directiontime postcontrol-1.5of(p~&cycle)-precontrol1/2of p~(p~)=s1:=s4:=s4;
+path p~[]; p~1=p2{length" "}&cycle; p~1=p2=p~0; p2..controls-p2..cycle=p~2;(p7
+..tension1.2..p~[length p~2]..p~2&{0,1}p2..tension1and atleast1..cycle)..tension
+ x..{curl1}-p7{curl hex "IsBad"}..tension.75and.74999..p2{0,1}&p2{_,'}..cycle:=p
+ ~4; subpath(-9,9)of subpath(3.5001,7.00001)of p~4=p~6;
+show p~6, directiontime(1,2) of p~6, directiontime(1,-1.00001) of p~6;
+p~3=(0,0)..controls (1,1) and (0,1)..(1,0); show p2..p2{p7}&{$,$}cycle,
+ (directiontime(1,1) of subpath(.314159,1) of p~3)[.314159,1];
+p~5=(subpath(0,.25)of p~3&subpath(.25,1)of p~3)shifted begintext1,0;
+p~3:=2/3'zscaled'{p~3}..controls(2,2/3(3))and penoffset(1/2x,y)of(0,1)(1,0);
+show p~3 intersectiontimes reverse p~3, point.17227 of p~3, point1-.28339of p~3;
+show point xpart(p~5 intersectiontimes p~5 shifted (.01,0))of p~5-
+ point ypart(p~5 intersectiontimes p~5 shifted (.01,0))of p~5;
+[[interim tracingedges:=1; e[-1+-+ -1.00001]=nullpicture; addto e1 also[[
+addto e0 doublepath p~5 scaled 3 withpen q; e0=e1=e2; cull e1 dropping (0,.1);
+nullpicture]];show e1 shifted(4089,-4095), e2 shifted(-4095,4092)shifted (-3,0),
+e2 shifted(4089,-4095)]];addto e1 also e2 shifted(-2,$); e1:=e1 shifted(-4,$);
+addto e0 also e1rotated89.999+e1scaled$; show e0 xscaled-10 yscaled2 xscaled82
+yscaled683;addto e1 doublepath (0,9) withweight-3 withweight turningnumber p~6
+ withpen pencircle xscaled(oct"180"++1) rotated-angle(64,$) shifted (9,8)
+ withpen makepen(($,$)..(1,0)..(1,1)..($,0)..($,$)&cycle)xscaled4095.49999;
+show e1, totalweight e1; chardp:=charcode:=5; xoffset:=-1.5; shipout e1;
+showstats; addto e2doublepath p~ yscaled1/60; e3:=e2 yscaled 0;
+autorounding:=2; addto e3doublepath(.5,0)..(3.5,1.5)withweight2;
+tracingspecs:=0; q:=makepen((1,1)..cycle) yscaled 1.5;
+p~8=(($,yy)rotated p{0,1}..{0,$}(1,0){0,$}..cycle)scaled2shifted(1000.49,9);
+turningcheck:=1;addto e2doublepath p~8 withpen q withweight p withpen cycle p;
+[[interim autorounding:=xx=.1; addto e2 contour p~8 withpen q withweight2]];
+chardx:=chardp:=charext:=-14.5;shipout-(-e0-e2)+e2shifted(0,6turningnumber p~8);
+p~9=(0,0)..(1,.5)..(5,1.5)..(7,2.5)..(12,3.5)..(13,4);addto e3 doublepath p~9;
+smoothing:=1; addto e3 doublepath p~9; addto e3 doublepath (-4095,0)..tension
+3/4 and 999..(0,2); show e3 rotated-90, (e0+e0) rotated90$ rotated90;
+if "a" if "ab">"b" elseif path reverse (3,4): >="aa":foo elseif fi "bar"
+else if '-(1,yy)<': :fi else def dup text t=[[t;save enddef;def|suffix$=,$
+enddef; def||tertiary p=show substring p of("a" enddef;||(2,$)&"bc");
+tertiarydef x++y=[[dup showtoken x;]];def quote x expr z of y=z enddef;
+ texts(quote x=(y+0)y+y)("xx",foo(x))=0]] enddef; def texts(text t,tt)expr?=
+ for n:=,for n"yy":n,length if false:endfor tt,t,:if string n:forsuffixes n=
+ foo1,[foo(n)],':show t,tt|(n;exitif not('<='+(?,yy)) endfor for m= :+endfor
+ for m=alpha step-1.1 3$: +m endfor fi endfor enddef; show (0,0){curl2}..
+ subpath(23.3,4.5)of p~9{curl3}..($,$){curl4}..cycle;numspecial p~++2+3;[[
+let?=if;save if,\;def if=endinput?enddef;def texts=input enddef;texts trap ]];
+dup[[def texts secondary x=primarydef y++y=x@y enddef; showtoken++;x enddef]]\;
+proofing:=1;texts:="a"&"b";% strings "yy" and "ab" no longer appear in memory
+texts-1.00001a1=-(a2+a3+a4+a5+a6);-(a3,a2)/.99999=-(a4+a5+[[showdependencies;
+a6]],a3+a4+a5+a6)]]; 1.00001a4+1=a5+a6; alpha:=.9alpha+7; showdependencies;
+a5=a6=2/3-a6; cull e1 dropping($,4a5)withweight1.5; charcode:=chardp:=27;
+openwindow 3 from (0,0) to (0,0) at "whoops"; addto p; shipout p; cull p;
+openwindow -.5 from ($,$) to ($,$) at (0,0); special p; numspecial "p";
+openwindow 15.49999 from (0,32*1024) to (1,31*1057) at (0,0); shipout e1;
+openwindow 5 from (0,length((0,0){0,0})) to (4,2) at ($,1);
+openwindow 6 from (length($,0),1) to (3,10) at (-5,2);
+display nullpicture; display p inwindow 3; display p inwindow 6;
+display e1 inwindow 6; cull e0 dropping (0.1,4095.999999) withweight 3.5
+withweight-3.5; display e0 inwindow 5.5; addto e0 also p; addto e0 contour 0;
+display e0 inwindow 5.49999; addto e0 contour p~9;
+display e1 inwindow 3+3; display e0e0 inwindow[[vardef e[]e=enddef;6]];
+addto e0 also e1; display e0 inwindow 5; ligtable||:255|=:|>>0,skipto0;
+display e1 inwindow 15; display e1 inwindow 6;
+show [[interim tracingcommands:=0; lig("g")(=:|); lig("h":"i")(|=:);
+lig("j")(|=:>);lig("k")(=:|>); lig("l")(|=:|>)]];
+b1:=c.a[ [[let c=++;vardef b=enddef;1]] ]; ligtable"m":0=:0,skipto5;
+!!; errhelp 0; errmessage "Be like Jane";
+errhelp "He%%%lp%"; errmessage""; errhelp ""; errmessage "Another";
+headerbyte 0; headerbyte(48.5)substring(-9,9)of"long"; for\=0:\
+headerbyte 9:2a6,"q"; fontdimen 9:2a6,"q"; fontdimen 1:2048;
+fontmaking:=1; extensible 5 5,"c"255.5,"d"; charlist 0:5:"a":"d";
+ligtable255:255::"a"=:"b","d" kern -2048,"c":0:99.5:"e"|=:|"f",0kern';
+ligtable 5:0; def clear(text x)=interim x:=$ enddef; clear(hppp); vppp=0;
+[[clear(tracingmacros); clear(tracingcommands); clear(tracingoutput\;
+ clear(proofing);designsize:=.99999;charcode:=ASCII char-418.5;vppp:=designsize;
+ def dp expr d = charcode:=charcode+1; chardp:=d; shipout nullpicture enddef;
+ dp 13; dp 12; dp 0; dp 21; dp -2; dp 17; dp 11; dp 3; charic:=-1000; dp -1;
+ dp 25; dp 31; dp 19; dp 7; charwd:=256; chardy:=6; dp 23; dp 30]];
+def f(suffix@@)(expr a,b)(text t)=numeric w; show a; % wipes out the old w
+addto @@ contour (0,0)..(2,0)..(1,$)..(1,1)..cycle withpen qq; % strange path
+addto @@ doublepath (0,0){1,1}..{2,1}(2,1) withpen qq; % carefully chosen
+addto @@ doublepath(($,$){1,0}..(1,1){1,0})scaled.5 withpen nullpen;
+cull @@ keeping (4,4) withweight1.5; enddef; def g(suffix$)=show $ enddef;
+addto e0 contour (0,0){1,0}..{1,0}(1,0){0,$}..{0,1}cycle withpen qq;
+f(e[3,w); g(e3,transformed p,penoffset-(1,1.3)of(pencircle scaled20 yscaled-.5),
+ directiontime (0,1) of ((0,0)..controls(1,1)and($,1)..(3,0)),
+ point 3.14159 of p~9 intersectiontimes subpath (3.14159,4) of p~9,
+ (($,1.1)..(1,$)) intersectiontimes precontrol$ of (0,0);
+addto e3 doublepath(-4094.99998,0)..(4094.99998,-.00001) withpen penoffset 0 of
+pencircle; addto e3 also e3 shifted (0,257); ,"flushed with pride"; numeric xx;
+def f(expr x,y,z)=showdependencies;tracingcapsules:=1;showdependencies;show
+ 1/3(3,6)*((x+y)+(y-x)), (1,1)/sqrt2 zscaled (x+1,x+2) - (x+1,x+2) rotated 45,
+ (0,1) zscaled (1,y+2)-(1,y+2) rotated 90 enddef; f((xx+1)/.3,(yy-1)/.5,(xx,0));
+'=(1000o3-4000(o1-o2)+4000o2+9,-.01o3+3ooo+
+ [[oo=9/10(o2+o4+o5-20);o4=o5=8/9(o1+.5o2); o6=-.0001o2;showdependencies;
+ numeric o[];xpart(alfa,[[pair alfa;0]])]])=-2/3[[save p;(p$,1-p$)]];
+xoffset:=yoffset:=4000[[oo=.5ooo=2*-1/2(ooo+[[numeric ooo;1]]);oo]];
+for @=angle(sqrt$,mlog$):charext:=uniformdeviate$;charht:=2048;granularity:=-8;
+addto e3 contour (0,-100)..tension 500..(100,-99)..tension 3000..cycle;
+tracingoutput:=@; shipout e3; special "bye"; interim char 99 = "c"; true=false;
+[[clear(tracingcommands); charcode:=ASCII char 269-13; shipout+nullpicture;
+"careful" quote for for = @ step 200 until 2*2600: &" METAFONT" endfor;]];
+scrollmode; "hello again"&char31; save p; fillin:=-.043;
+def f expr x=let )=]; let [=(; show _ enddef; begingroup tracingspecs:=1;
+show nullpen, makepath.qq, makepath(q rotated1), makepath pencircle rotated $;
+addto e0 doublepath (0,2){0,$}..{0,$}(0,1)..{1,0}(3,0)..(4,0){1,0}..cycle
+ withpen makepen((0,0)..(5,2.9)..(4,3)..cycle); tracingonline:=1; f xx[1);
+showdependencies; qq:=q; showstats; bye endtext
+% things not tested:
+% interaction (error insertion/deletion, interrupts, \pausing, files not there)
+% date, time; initialization of random number generator without randomseed
+% system-dependent parsing of file names, areas, extensions
+% certain error messages, especially fatal ones
+% things that can't happen in INIMF
+% unusual cases of fixed-point arithmetic
diff --git a/systems/knuth/dist/mf/trap.pl b/systems/knuth/dist/mf/trap.pl
new file mode 100644
index 0000000000..bae32a41fb
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.pl
@@ -0,0 +1,490 @@
+(CODINGSCHEME Q)
+(DESIGNSIZE R 128.0)
+(COMMENT DESIGNSIZE IS IN POINTS)
+(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)
+(CHECKSUM O 5546464252)
+(FONTDIMEN
+ (SLANT R 2047.999999)
+ (SPACE R 0.0)
+ (STRETCH R 0.0)
+ (SHRINK R 0.0)
+ (XHEIGHT R 0.0)
+ (QUAD R 0.0)
+ (EXTRASPACE R 0.0)
+ (PARAMETER D 8 R 0.0)
+ (PARAMETER D 9 R 0.005208)
+ (PARAMETER D 10 R 0.0)
+ )
+(BOUNDARYCHAR O 377)
+(LIGTABLE
+ (COMMENT THIS PART OF THE PROGRAM IS NEVER USED!
+ )
+ (LABEL BOUNDARYCHAR)
+ (/LIG/>> O 377 O 0)
+ (KRN O 0 R 0.0)
+ (KRN O 0 R 0.0078125)
+ (KRN O 0 R 0.015625)
+ (KRN O 0 R 0.0234375)
+ (KRN O 0 R 0.03125)
+ (KRN O 0 R 0.0390625)
+ (KRN O 0 R 0.046875)
+ (KRN O 0 R 0.0546875)
+ (KRN O 0 R 0.0625)
+ (KRN O 0 R 0.0703125)
+ (KRN O 0 R 0.078125)
+ (KRN O 0 R 0.0859375)
+ (KRN O 0 R 0.09375)
+ (KRN O 0 R 0.1015625)
+ (KRN O 0 R 0.109375)
+ (KRN O 0 R 0.1171875)
+ (KRN O 0 R 0.125)
+ (KRN O 0 R 0.1328125)
+ (KRN O 0 R 0.140625)
+ (KRN O 0 R 0.1484375)
+ (KRN O 0 R 0.15625)
+ (KRN O 0 R 0.1640625)
+ (KRN O 0 R 0.171875)
+ (KRN O 0 R 0.1796875)
+ (KRN O 0 R 0.1875)
+ (KRN O 0 R 0.1953125)
+ (KRN O 0 R 0.203125)
+ (KRN O 0 R 0.2109375)
+ (KRN O 0 R 0.21875)
+ (KRN O 0 R 0.2265625)
+ (KRN O 0 R 0.234375)
+ (KRN O 0 R 0.2421875)
+ (KRN O 0 R 0.25)
+ (KRN O 0 R 0.2578125)
+ (KRN O 0 R 0.265625)
+ (KRN O 0 R 0.2734375)
+ (KRN O 0 R 0.28125)
+ (KRN O 0 R 0.2890625)
+ (KRN O 0 R 0.296875)
+ (KRN O 0 R 0.3046875)
+ (KRN O 0 R 0.3125)
+ (KRN O 0 R 0.3203125)
+ (KRN O 0 R 0.328125)
+ (KRN O 0 R 0.3359375)
+ (KRN O 0 R 0.34375)
+ (KRN O 0 R 0.3515625)
+ (KRN O 0 R 0.359375)
+ (KRN O 0 R 0.3671875)
+ (KRN O 0 R 0.375)
+ (KRN O 0 R 0.3828125)
+ (KRN O 0 R 0.390625)
+ (KRN O 0 R 0.3984375)
+ (KRN O 0 R 0.40625)
+ (KRN O 0 R 0.4140625)
+ (KRN O 0 R 0.421875)
+ (KRN O 0 R 0.4296875)
+ (KRN O 0 R 0.4375)
+ (KRN O 0 R 0.4453125)
+ (KRN O 0 R 0.453125)
+ (KRN O 0 R 0.4609375)
+ (SKIP D 1)
+ (LABEL C g)
+ (LIG/ C g O 0)
+ (STOP)
+ (KRN O 0 R 0.400782)
+ (KRN O 0 R 0.408594)
+ (KRN O 0 R 0.416407)
+ (KRN O 0 R 0.424219)
+ (KRN O 0 R 0.432032)
+ (KRN O 0 R 0.439844)
+ (KRN O 0 R 0.447657)
+ (KRN O 0 R 0.455469)
+ (KRN O 0 R 0.463282)
+ (KRN O 0 R 0.471094)
+ (KRN O 0 R 0.478907)
+ (KRN O 0 R 0.486719)
+ (KRN O 0 R 0.494532)
+ (KRN O 0 R 0.502344)
+ (KRN O 0 R 0.510157)
+ (KRN O 0 R 0.517969)
+ (KRN O 0 R 0.525782)
+ (KRN O 0 R 0.533594)
+ (KRN O 0 R 0.541407)
+ (KRN O 0 R 0.549219)
+ (KRN O 0 R 0.557032)
+ (KRN O 0 R 0.564844)
+ (KRN O 0 R 0.572657)
+ (KRN O 0 R 0.580469)
+ (KRN O 0 R 0.588282)
+ (KRN O 0 R 0.596094)
+ (KRN O 0 R 0.603907)
+ (KRN O 0 R 0.611719)
+ (KRN O 0 R 0.619532)
+ (KRN O 0 R 0.627344)
+ (KRN O 0 R 0.635157)
+ (KRN O 0 R 0.642969)
+ (KRN O 0 R 0.650782)
+ (KRN O 0 R 0.658594)
+ (KRN O 0 R 0.666407)
+ (KRN O 0 R 0.674219)
+ (KRN O 0 R 0.682032)
+ (KRN O 0 R 0.689844)
+ (KRN O 0 R 0.697657)
+ (KRN O 0 R 0.705469)
+ (KRN O 0 R 0.713282)
+ (KRN O 0 R 0.721094)
+ (KRN O 0 R 0.728907)
+ (KRN O 0 R 0.736719)
+ (KRN O 0 R 0.744532)
+ (KRN O 0 R 0.752344)
+ (KRN O 0 R 0.760157)
+ (KRN O 0 R 0.767969)
+ (KRN O 0 R 0.775782)
+ (KRN O 0 R 0.783594)
+ (KRN O 0 R 0.791407)
+ (KRN O 0 R 0.799219)
+ (KRN O 0 R 0.807032)
+ (KRN O 0 R 0.814844)
+ (KRN O 0 R 0.822657)
+ (KRN O 0 R 0.830469)
+ (KRN O 0 R 0.838282)
+ (KRN O 0 R 0.846094)
+ (KRN O 0 R 0.853907)
+ (KRN O 0 R 0.861719)
+ (SKIP D 1)
+ (LABEL C h)
+ (LABEL C i)
+ (/LIG C i O 0)
+ (STOP)
+ (KRN O 0 R 0.801562)
+ (KRN O 0 R 0.809375)
+ (KRN O 0 R 0.817187)
+ (KRN O 0 R 0.825)
+ (KRN O 0 R 0.832812)
+ (KRN O 0 R 0.840625)
+ (KRN O 0 R 0.848437)
+ (KRN O 0 R 0.85625)
+ (KRN O 0 R 0.864062)
+ (KRN O 0 R 0.871875)
+ (KRN O 0 R 0.879687)
+ (KRN O 0 R 0.8875)
+ (KRN O 0 R 0.895312)
+ (KRN O 0 R 0.903125)
+ (KRN O 0 R 0.910937)
+ (KRN O 0 R 0.91875)
+ (KRN O 0 R 0.926562)
+ (KRN O 0 R 0.934375)
+ (KRN O 0 R 0.942187)
+ (KRN O 0 R 0.95)
+ (KRN O 0 R 0.957812)
+ (KRN O 0 R 0.965625)
+ (KRN O 0 R 0.973437)
+ (KRN O 0 R 0.98125)
+ (KRN O 0 R 0.989062)
+ (KRN O 0 R 0.996875)
+ (KRN O 0 R 1.004687)
+ (KRN O 0 R 1.0125)
+ (KRN O 0 R 1.020312)
+ (KRN O 0 R 1.028125)
+ (KRN O 0 R 1.035937)
+ (KRN O 0 R 1.04375)
+ (KRN O 0 R 1.051562)
+ (KRN O 0 R 1.059375)
+ (KRN O 0 R 1.067187)
+ (KRN O 0 R 1.075)
+ (KRN O 0 R 1.082812)
+ (KRN O 0 R 1.090625)
+ (KRN O 0 R 1.098437)
+ (KRN O 0 R 1.10625)
+ (KRN O 0 R 1.114062)
+ (KRN O 0 R 1.121875)
+ (KRN O 0 R 1.129687)
+ (KRN O 0 R 1.1375)
+ (KRN O 0 R 1.145312)
+ (KRN O 0 R 1.153125)
+ (KRN O 0 R 1.160937)
+ (KRN O 0 R 1.16875)
+ (KRN O 0 R 1.176562)
+ (KRN O 0 R 1.184375)
+ (KRN O 0 R 1.192187)
+ (KRN O 0 R 1.2)
+ (KRN O 0 R 1.207812)
+ (KRN O 0 R 1.215625)
+ (KRN O 0 R 1.223437)
+ (KRN O 0 R 1.23125)
+ (KRN O 0 R 1.239062)
+ (KRN O 0 R 1.246875)
+ (KRN O 0 R 1.254687)
+ (KRN O 0 R 1.2625)
+ (SKIP D 1)
+ (LABEL C j)
+ (/LIG> C j O 0)
+ (SKIP D 123)
+ (KRN O 0 R 1.202344)
+ (KRN O 0 R 1.210156)
+ (KRN O 0 R 1.217969)
+ (KRN O 0 R 1.225781)
+ (KRN O 0 R 1.233594)
+ (KRN O 0 R 1.241406)
+ (KRN O 0 R 1.249219)
+ (KRN O 0 R 1.257031)
+ (KRN O 0 R 1.264844)
+ (KRN O 0 R 1.272656)
+ (KRN O 0 R 1.280469)
+ (KRN O 0 R 1.288281)
+ (KRN O 0 R 1.296094)
+ (KRN O 0 R 1.303906)
+ (KRN O 0 R 1.311719)
+ (KRN O 0 R 1.319531)
+ (KRN O 0 R 1.327344)
+ (KRN O 0 R 1.335156)
+ (KRN O 0 R 1.342969)
+ (KRN O 0 R 1.350781)
+ (KRN O 0 R 1.358594)
+ (KRN O 0 R 1.366406)
+ (KRN O 0 R 1.374219)
+ (KRN O 0 R 1.382031)
+ (KRN O 0 R 1.389844)
+ (KRN O 0 R 1.397656)
+ (KRN O 0 R 1.405469)
+ (KRN O 0 R 1.413281)
+ (KRN O 0 R 1.421094)
+ (KRN O 0 R 1.428906)
+ (KRN O 0 R 1.436719)
+ (KRN O 0 R 1.444531)
+ (KRN O 0 R 1.452344)
+ (KRN O 0 R 1.460156)
+ (KRN O 0 R 1.467969)
+ (KRN O 0 R 1.475781)
+ (KRN O 0 R 1.483594)
+ (KRN O 0 R 1.491406)
+ (KRN O 0 R 1.499219)
+ (KRN O 0 R 1.507031)
+ (KRN O 0 R 1.514844)
+ (KRN O 0 R 1.522656)
+ (KRN O 0 R 1.530469)
+ (KRN O 0 R 1.538281)
+ (KRN O 0 R 1.546094)
+ (KRN O 0 R 1.553906)
+ (KRN O 0 R 1.561719)
+ (KRN O 0 R 1.569531)
+ (KRN O 0 R 1.577344)
+ (KRN O 0 R 1.585156)
+ (KRN O 0 R 1.592969)
+ (KRN O 0 R 1.600781)
+ (KRN O 0 R 1.608594)
+ (KRN O 0 R 1.616406)
+ (KRN O 0 R 1.624219)
+ (KRN O 0 R 1.632031)
+ (KRN O 0 R 1.639844)
+ (KRN O 0 R 1.647656)
+ (KRN O 0 R 1.655469)
+ (KRN O 0 R 1.663281)
+ (SKIP D 1)
+ (LABEL C k)
+ (LIG/> C k O 0)
+ (SKIP D 62)
+ (KRN O 0 R 1.603125)
+ (KRN O 0 R 1.610937)
+ (KRN O 0 R 1.61875)
+ (KRN O 0 R 1.626562)
+ (KRN O 0 R 1.634375)
+ (KRN O 0 R 1.642187)
+ (KRN O 0 R 1.65)
+ (KRN O 0 R 1.657812)
+ (KRN O 0 R 1.665625)
+ (KRN O 0 R 1.673437)
+ (KRN O 0 R 1.68125)
+ (KRN O 0 R 1.689062)
+ (KRN O 0 R 1.696875)
+ (KRN O 0 R 1.704687)
+ (KRN O 0 R 1.7125)
+ (KRN O 0 R 1.720312)
+ (KRN O 0 R 1.728125)
+ (KRN O 0 R 1.735937)
+ (KRN O 0 R 1.74375)
+ (KRN O 0 R 1.751562)
+ (KRN O 0 R 1.759375)
+ (KRN O 0 R 1.767187)
+ (KRN O 0 R 1.775)
+ (KRN O 0 R 1.782812)
+ (KRN O 0 R 1.790625)
+ (KRN O 0 R 1.798437)
+ (KRN O 0 R 1.80625)
+ (KRN O 0 R 1.814062)
+ (KRN O 0 R 1.821875)
+ (KRN O 0 R 1.829687)
+ (KRN O 0 R 1.8375)
+ (KRN O 0 R 1.845312)
+ (KRN O 0 R 1.853125)
+ (KRN O 0 R 1.860937)
+ (KRN O 0 R 1.86875)
+ (KRN O 0 R 1.876562)
+ (KRN O 0 R 1.884375)
+ (KRN O 0 R 1.892187)
+ (KRN O 0 R 1.9)
+ (KRN O 0 R 1.907812)
+ (KRN O 0 R 1.915625)
+ (KRN O 0 R 1.923437)
+ (KRN O 0 R 1.93125)
+ (KRN O 0 R 1.939062)
+ (KRN O 0 R 1.946875)
+ (KRN O 0 R 1.954687)
+ (KRN O 0 R 1.9625)
+ (KRN O 0 R 1.970312)
+ (KRN O 0 R 1.978125)
+ (KRN O 0 R 1.985937)
+ (KRN O 0 R 1.99375)
+ (KRN O 0 R 2.001562)
+ (KRN O 0 R 2.009375)
+ (KRN O 0 R 2.017187)
+ (KRN O 0 R 2.025)
+ (KRN O 0 R 2.032812)
+ (KRN O 0 R 2.040625)
+ (KRN O 0 R 2.048437)
+ (KRN O 0 R 2.05625)
+ (KRN O 0 R 2.064062)
+ (STOP)
+ (LABEL C l)
+ (/LIG/> C l O 0)
+ (SKIP D 1)
+ (LABEL C m)
+ (LIG O 0 O 0)
+ (STOP)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (LABEL C c)
+ (LABEL C d)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ (STOP)
+ (COMMENT THIS PART OF THE PROGRAM IS NEVER USED!
+ )
+ )
+(CHARACTER O 0
+ (CHARWD R 2.0)
+ (CHARHT R 15.999999)
+ (CHARDP R 0.234375)
+ (CHARIC R -7.8125)
+ (NEXTLARGER O 5)
+ )
+(CHARACTER O 5
+ (CHARWD R 0.0)
+ (CHARDP R -0.113281)
+ (VARCHAR
+ (TOP O 5)
+ (MID C c)
+ (REP C d)
+ )
+ )
+(CHARACTER O 33
+ (CHARWD R 0.0)
+ (CHARDP R 0.2109375)
+ )
+(CHARACTER O 137
+ (CHARWD R 0.0)
+ (CHARDP R 0.1015625)
+ )
+(CHARACTER O 140
+ (CHARWD R 0.0)
+ (CHARDP R 0.089844)
+ )
+(CHARACTER C a
+ (CHARWD R 0.0)
+ (NEXTLARGER C d)
+ )
+(CHARACTER C b
+ (CHARWD R 0.0)
+ (CHARDP R 0.1640625)
+ )
+(CHARACTER C c
+ (CHARWD R 0.0)
+ (CHARDP R -0.011719)
+ (COMMENT
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C d
+ (CHARWD R 0.0)
+ (CHARDP R 0.1328125)
+ (COMMENT
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C e
+ (CHARWD R 0.0)
+ (CHARDP R 0.089844)
+ )
+(CHARACTER C f
+ (CHARWD R 0.0)
+ (CHARDP R 0.0234375)
+ )
+(CHARACTER C g
+ (CHARWD R 0.0)
+ (CHARDP R -0.011719)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (LIG/ C g O 0)
+ )
+ )
+(CHARACTER C h
+ (CHARWD R 0.0)
+ (CHARDP R 0.1953125)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG C i O 0)
+ )
+ )
+(CHARACTER C i
+ (CHARWD R 0.0)
+ (CHARDP R 0.2421875)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG C i O 0)
+ )
+ )
+(CHARACTER C j
+ (CHARWD R 0.0)
+ (CHARDP R 0.1484375)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG> C j O 0)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C k
+ (CHARWD R 0.0)
+ (CHARDP R 0.0546875)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (LIG/> C k O 0)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C l
+ (CHARWD R 2.0)
+ (CHARDP R 0.1796875)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG/> C l O 0)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C m
+ (CHARWD R 2.0)
+ (CHARHT R 15.999999)
+ (CHARDP R 0.234375)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (LIG O 0 O 0)
+ )
+ )
+(CHARACTER O 354
+ (CHARWD R 0.0)
+ (CHARDP R -15.999999)
+ )
diff --git a/systems/knuth/dist/mf/trap.typ b/systems/knuth/dist/mf/trap.typ
new file mode 100644
index 0000000000..159c5ce7fc
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.typ
@@ -0,0 +1,210 @@
+This is GFtype, Version 3.1
+Options selected: Mnemonic output = true; pixel output = true.
+' METAFONT output 2014.01.07:1811'
+
+35: beginning of char 5: -4096<=m<=4094 -2<=n<=1
+(initially n=1) paint (4095)1
+64: skip0 0 (n=0) paint (4093)1
+69: skip0 0 (n=-1) paint (4096)1
+74: skip0 0 (n=-2) paint (4092)1(1)1
+81: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-4096,2) in METAFONT coordinates
+
+
+
+
+.<--This pixel's upper left corner is at (-4096,-2) in METAFONT coordinates
+
+82: beginning of char 5 with extension -14: -4<=m<=1002 -6<=n<=11
+(previous character with the same code started at byte 35)
+(initially n=11) paint (1002)4
+111: skip0 0 (n=10) paint (1002)4
+116: skip0 0 (n=9) paint (1002)4
+121: skip1 3 (n=5) paint (1002)4
+127: skip0 0 (n=4) paint (1002)4
+132: skip0 0 (n=3) paint (1002)4
+137: newrow 7 (n=2) paint 1
+139: newrow 3 (n=1) paint 1(1)1(1)1
+145: newrow 1 (n=0) paint 1(1)2(1)1(1)1
+153: newrow 1 (n=-1) paint 2(1)1
+157: newrow 0 (n=-2) paint 1(1)1
+161: skip1 1 (n=-4) paint (7)1
+165: newrow 7 (n=-5) paint 1
+167: newrow 6 (n=-6) paint 1(1)1
+171: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-4,12) in METAFONT coordinates
+
+
+
+
+
+
+
+
+
+ *
+ * * *
+ * ** * *
+ ** *
+* *
+
+ *
+ *
+ * *
+.<--This pixel's upper left corner is at (-4,-6) in METAFONT coordinates
+
+172: yyy -216270 (-3.30002)
+177: xxx 'title ab'
+187: xxx 'xoffset'
+196: yyy -65536 (-1)
+201: beginning of char 27 with extension -14: -4096<=m<=4094 -1<=n<=0
+(initially n=0) paint (4093)1(1)4095
+234: newrow 0 (n=-1) paint 4093(1)1
+240: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-4096,1) in METAFONT coordinates
+
+********************************************************************************
+.<--This pixel's upper left corner is at (-4096,-1) in METAFONT coordinates
+
+241: xxx 'xoffset'
+250: yyy -87359488 (-1333)
+255: xxx 'yoffset'
+264: yyy -87359488 (-1333)
+269: beginning of char 109: -5428<=m<=2762 -1435<=n<=-1077
+(initially n=-1077) paint (0)8190
+298: skip2 256 (n=-1334) paint (0)8190
+305: skip1 98 (n=-1433) paint (4143)55
+311: skip0 0 (n=-1434) paint (4088)106
+317: skip0 0 (n=-1435) paint (4092)74
+323: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-5428,-1076) in METAFONT coordinates
+********************************************************************************
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.<--This pixel's upper left corner is at (-5428,-1156) in METAFONT coordinates
+
+324: xxx 'bye'
+329: xxx 'xoffset'
+338: yyy -87359488 (-1333)
+343: xxx 'yoffset'
+352: yyy -87359488 (-1333)
+357: beginning of char 0: 0<=m<=0 0<=n<=0
+(initially n=0)
+363: eoc
+(The character is entirely blank.)
+
+364: xxx 'title careful METAFONT METAFONT METAFONT METAFONT METAFONT METAF
+ONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT ME
+TAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT
+ METAFONT METAFONT METAFONT METAFONT'
+624: xxx 'title hello again?'
+624: ! non-ASCII character in xxx command!
+
+Postamble starts at byte 644, after special info at byte 364.
+design size = 134217728 (128pt)
+check sum = 765094058
+hppp = -65536 (-1)
+vppp = 65535 (0.99998)
+min m = -5428, max m = 4094
+min n = -1435, max n = 11
+Character 0: dx -950272 (-14.5), dy 393216 (6), width 2097152 (-256), loc 324
+Character 5: dx -950272 (-14.5), width 0 (0), loc 82
+Character 27: dx -950272 (-14.5), width 0 (0), loc 172
+Character 95: dx -950272 (-14.5), width 0 (0), loc -1
+Character 96: dx -950272 (-14.5), width 0 (0), loc -1
+Character 97: dx -950272 (-14.5), width 0 (0), loc -1
+Character 98: dx -950272 (-14.5), width 0 (0), loc -1
+Character 99: dx -950272 (-14.5), width 0 (0), loc -1
+Character 100: dx -950272 (-14.5), width 0 (0), loc -1
+Character 101: dx -950272 (-14.5), width 0 (0), loc -1
+Character 102: dx -950272 (-14.5), width 0 (0), loc -1
+Character 103: dx -950272 (-14.5), width 0 (0), loc -1
+Character 104: dx -950272 (-14.5), width 0 (0), loc -1
+Character 105: dx -950272 (-14.5), width 0 (0), loc -1
+Character 106: dx -950272 (-14.5), width 0 (0), loc -1
+Character 107: dx -950272 (-14.5), width 0 (0), loc -1
+Character 108: dx -950272 (-14.5), dy 393216 (6), width 2097152 (-256), loc -1
+Character 109: dx -950272 (-14.5), dy 393216 (6), width 2097152 (-256), loc 241
+Character 236: dx 0 (0), width 0 (0), loc -1
+The file had 5 characters altogether.
diff --git a/systems/knuth/dist/mf/trapin.log b/systems/knuth/dist/mf/trapin.log
new file mode 100644
index 0000000000..2fbafd0e6b
--- /dev/null
+++ b/systems/knuth/dist/mf/trapin.log
@@ -0,0 +1,179 @@
+This is METAFONT, Version 2.7182818 (INIMF) 7 JAN 2014 17:51
+**\input trap
+(trap.mf
+>> << == >> ::: ||`` ''--!! ??## && @@ $$[[]]{{ }}((5.5 0.5))
+>> ".."
+! Not implemented: (unknown numeric)++(string).
+<to be read again>
+ ;
+l.4 ...}} . (( 5.5.5 )) ++ "..";
+
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+..
+! Missing `=' has been inserted.
+<to be read again>
+ ,
+l.5 begingroup save =; let=,
+ ; save,; newinternal $=,; let )...
+You should have said `let symbol = something'.
+But don't worry; I'll pretend that an equals sign
+was present. The next token I read will be `something'.
+
+> errorstopmode=errorstopmode
+> readstring=readstring
+> 2
+> "2"
+> ,=,
+> (=tag
+> )=,
+<< == >> ::: ||`` ''--!! ??## && @@ $$[[]]{{ }}(([][]))=numeric
+<< == >> ::: ||`` ''--!! ??## && @@ $$[[]]{{ }}((5.5 0.5))=<< == >> :::
+||`` ''--!! ??## && @@ $$[[]]{{ }}((5.5 0.5))
+> year=month
+! OK.
+l.6 ...ring,2,"2",,,(,),<<,year;
+
+
+! Missing `:' has been inserted.
+<to be read again>
+ ;
+l.8 ...not cycle "":1.1 forever;
+ fi;
+The next thing in this loop should have been a `:'.
+So I'll pretend that a colon was present;
+everything from here to `endfor' will be iterated.
+
+{fi}
+{exitif}
+{[repeat the loop]}
+{false}
+{fi}
+{exitif}
+{[repeat the loop]}
+{(2.1)>(2)}
+{true}
+{tracingcommands:=2.1}
+{showtoken}
+> |=:|>=|=:|>
+! OK.
+l.9 ... endfor; showtoken |=:|>;
+
+
+! Arithmetic overflow.
+l.10 tracingedges:=1/.00001
+ ; tracingequations:=$+1; p~=trac...
+Uh, oh. A little while ago one of the quantities that I was
+computing got too large, so I'm afraid your answers will be
+somewhat askew. You'll probably have to adopt different
+tactics next time. But I shall try to carry on anyway.
+
+{tracingedges:=32767.99998}
+{(0)+(1)}
+{tracingequations:=1}
+{(32767.99998)+(0.00002)}
+! Arithmetic overflow.
+l.10 ... p~=tracingedges+.00001;
+
+Uh, oh. A little while ago one of the quantities that I was
+computing got too large, so I'm afraid your answers will be
+somewhat askew. You'll probably have to adopt different
+tactics next time. But I shall try to carry on anyway.
+
+{(p~)=(32767.99998)}
+## p~=32767.99998
+{interim}
+{tracingmacros:=1}
+{tracingoutput:=1}
+{warningcheck:=1}
+{tracingstats:=1}
+{tracingchoices:=1}
+{tracingpens:=1}
+{tracingspecs:=1}
+{ASCII("")}
+{$:=-1}
+{$:=x}
+>> x
+! Internal quantity `$' must receive a known value.
+<to be read again>
+ ;
+l.12 ...os:=1; $:=ASCII""; $:=x;
+ p~:=p~;
+I can't set an internal quantity to anything but a known
+numeric value, so I'll have to ignore this assignment.
+
+{p~:=32767.99998}
+## p~=32767.99998
+! Value is too large (32767.99998).
+l.12 ...:=ASCII""; $:=x; p~:=p~;
+
+The equation I just processed has given some variable
+a value of 4096 or more. Continue and I'll try to cope
+with that big value; but it might be dangerous.
+(Set warningcheck:=0 to suppress this message.)
+
+{delimiters}
+{delimiters}
+{vardef}
+{def}
+
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-interim.proofing:=(-0.5;shipout.nullpicture;special"3"
+{begingroup}
+{interim}
+{-(0.5)}
+! Missing `)' has been inserted.
+<to be read again>
+ ;
+<argument> ...m.proofing:=(-0.5;
+ shipout.nullpicture;special"3"
+foo->begingroup(TEXT2)
+ endgroup
+l.16 ...ture; special"3" endtext
+ ;
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+{proofing:=-0.5}
+{shipout}
+{nullpicture}
+{special}
+{restoring proofing=0}
+{endgroup}
+{for}
+{showstats}
+Memory usage 291&41 (1922 still untouched)
+String usage 26&83 (891&11438 still untouched)
+! OK.
+l.17 ...ing:fi endfor showstats;
+ let!!=skipto;
+
+{let}
+{path}
+{-(15)}
+{-(12)}
+Path at line 18, before choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+
+Path at line 18, after choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+
+{(unknown path p~)=(path)}
+{everyjob}
+{vardef}
+{let}
+{vardef}
+{def}
+{elseif}
+ )
+Beginning to dump on file trap.base
+ (preloaded base=trap 2014.1.7)
+1117 strings of total length 20614
+395 memory locations dumped; current usage is 317&67
+265 symbolic tokens
diff --git a/systems/knuth/dist/mf/trapman.tex b/systems/knuth/dist/mf/trapman.tex
new file mode 100644
index 0000000000..7968c6e44e
--- /dev/null
+++ b/systems/knuth/dist/mf/trapman.tex
@@ -0,0 +1,400 @@
+% The TRAP manual: How to validate MF --- last updated by D E Knuth on 4 Dec 89
+\font\eighttt= cmtt8
+\font\eightrm= cmr8
+\font\titlefont=cmssdc10 at 40pt
+\let\mc=\eightrm
+\font\logo=manfnt % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\rm
+\let\mainfont=\tenrm
+
+\def\.#1{\hbox{\tt#1}}
+\def\\#1{\hbox{\it#1\/\hskip.05em}} % italic type for identifiers
+
+\parskip 2pt plus 1pt
+\baselineskip 12pt plus .25pt
+
+\def\verbatim#1{\begingroup \frenchspacing
+ \def\do##1{\catcode`##1=12 } \dospecials
+ \parskip 0pt \parindent 0pt
+ \catcode`\ =\active \catcode`\^^M=\active
+ \tt \def\par{\ \endgraf} \obeylines \obeyspaces
+ \input #1 \endgroup}
+% a blank line will be typeset at the end of the file;
+% if you're unlucky it will appear on a page by itself!
+{\obeyspaces\global\let =\ }
+
+\output{\shipout\box255\global\advance\pageno by 1} % for the title page only
+\null
+\vfill
+\centerline{\titlefont A Torture Test}
+\vskip8pt
+\centerline{\titlefont for \logo ()*+,-.*}
+\vskip 24pt
+\centerline{by Donald E. Knuth}
+\centerline{Stanford University}
+\vskip 6pt
+\centerline{({\sl Version 2, January 1990\/})}
+\vfill
+\centerline{\vbox{\hsize 4in
+\noindent Programs that claim to be implementations of \MF84 are
+supposed to be able to process the test routine contained in this
+report, producing the outputs contained in this report.}}
+\vskip 24pt
+{\baselineskip 9pt
+\eightrm\noindent
+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.
+{\logo opqrstuq} is a trademark of Addison-Wesley Publishing Company.
+
+
+}\pageno=0\eject
+
+\output{\shipout\vbox{ % for subsequent pages
+ \baselineskip0pt\lineskip0pt
+ \hbox to\hsize{\strut
+ \ifodd\pageno \hfil\eightrm\firstmark\hfil
+ \mainfont\the\pageno
+ \else\mainfont\the\pageno\hfil
+ \eightrm\firstmark\hfil\fi}
+ \vskip 10pt
+ \box255}
+ \global\advance\pageno by 1}
+\let\runninghead=\mark
+\outer\def\section#1.{\noindent{\bf#1.}\quad
+ \runninghead{\uppercase{#1} }\ignorespaces}
+
+\section 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 {\mc 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 {\mc 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 \MF84, and the
+main bulk of this report is simply a presentation of the test program
+that was used to get the bugs out of \MF.
+
+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 \.{TRAP.MF}, because of my warped sense of humor:
+\MF's companion system, \TeX, has a similar test file called \.{TRIP}, and I
+couldn't help thinking about Billy Goat Gruff and the story of ``trip,
+trap, trip, trap.''
+
+The contents of this test file are so remote from what people actually
+do with \MF, I feel apologetic if I have to explain the correct
+translation of \.{TRAP.MF}; nobody really cares about most of the
+nitty-gritty rules that are involved. Yet I believe \.{TRAP} exemplifies
+the sort of test program that has outstanding diagnostic ability, as
+explained above.
+
+If somebody claims to have a correct implementation of \MF, I will not
+believe it until I see that \.{TRAP.MF} is translated properly.
+I propose, in fact, that a program must meet two criteria before it
+can justifiably be called \MF: (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 \.{TRAP.MF}.
+
+\MF\ 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 \MF\ processors, since users
+want \MF\ to produce identical results on different machines.
+Hence I am planning to do whatever I can to suppress any systems that
+call themselves \MF\ 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
+\MF\ 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 \MF, given
+a tape that contains copies of the other appendices.
+
+Appendix B is \.{TRAP.MF}, the fiendish test file that has already
+been mentioned. People who think that they understand \MF\ are challenged
+to see if they know what \MF\ is supposed to do with this file.
+People who know only a little about \MF\ might still find it
+interesting to study Appendix~B, just to get some insights into the
+methodology advocated here.
+
+Appendix C is \.{TRAPIN.LOG}, a correct transcript file \.{TRAP.LOG}
+that results if \.{INIMF} is applied to \.{TRAP.MF}. (\.{INIMF} is
+the name of a version of \MF\ that does certain initializations;
+this run of \.{INIMF} also creates a binary base file called \.{TRAP.BASE}.)
+
+Appendix D is a correct transcript file \.{TRAP.LOG} that results if
+\.{INIMF} or any other version of \MF\ is applied to \.{TRAP.MF}
+with base file \.{TRAP.BASE}.
+
+Appendix E is \.{TRAP.TYP}, the symbolic version of a correct output
+file \.{TRAP.72270GF} that was produced at the same time as the \.{TRAP.LOG}
+file of Appendix~D.
+
+Appendix F is \.{TRAP.PL}, the symbolic version of a correct output
+file \.{TRAP.TFM} that was produced at the same time as the \.{TRAP.LOG}
+file of Appendix~D.
+
+Appendix G is \.{TRAP.FOT}, an abbreviated version of Appendix D that
+appears on the user's terminal during the run that produces \.{TRAP.LOG},
+\.{TRAP.72270GF}, and \.{TRAP.TFM}.
+
+The debugging of \MF\ and the testing of the adequacy of \.{TRAP.MF}
+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 \MF\ did the right things
+to \.{TRAP.MF}. 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 D, E, and~F. However, I'm willing to pay
+$\$$81.92 to the first finder of any remaining bug in \MF, and I will
+be surprised if that bug doesn't show up also in one of these appendices.
+
+\vfill\eject
+
+\section Appendix A: How to test \MF.
+
+\item{0.} Let's assume that you have a tape containing \.{TRAP.MF},
+\.{TRAPIN.LOG}, \.{TRAP.LOG}, \.{TRAP.TYP}, \.{TRAP.PL}, and \.{TRAP.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} and \.{GFtype}, as described in the \TeX ware and \MF ware reports.
+
+\item{1.} Prepare a version of \.{INIMF}. (This means that your \.{WEB}
+change file should have {\bf init} and {\bf tini} defined to be null.)
+The {\bf debug} and {\bf gubed} macros should be null, in order to
+activate special printouts that occur when $\\{tracingedges}>1.0$.
+The {\bf stat} and {\bf tats} macros should also be null, so that
+statistics are kept. Set \\{mem\_top} and \\{mem\_max} to 3000
+(or to \\{mem\_min} plus 3000, if \\{mem\_min} isn't zero),
+for purposes of this test version.
+Also set $\\{error\_line}=64$, $\\{half\_error\_line}=32$,
+$\\{max\_print\_line}=72$, $\\{screen\_width}=100$, and
+$\\{screen\_depth}=200$; 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. Also (if possible) set
+$\\{gf\_buf\_size}=8$, since this tests more parts of the program.
+You probably should also use the ``normal'' settings of other parameters
+found in \.{MF.WEB} (e.g., $\\{max\_internal}=100$, $\\{buf\_size}=500$,
+etc.), since these show up in a few lines of the test output. Finally,
+change \MF's screen-display routines by putting the following simple lines
+in the change file:
+$$\vbox{\halign{\tt#\hfil\cr
+\char`\@x Screen routines:\cr
+begin init\char`\_screen:=false;\cr
+\char`\@y\cr
+begin init\char`\_screen:=true;
+ \char`\{screen instructions will be logged\char`\}\cr
+\char`\@z\cr}}$$
+None of the other screen routines (\\{update\_screen}, \\{blank\_rectangle},
+\\{paint\_row}) should be changed in any way; the effect will be to have
+\MF's actions recorded in the transcript files instead of on the screen,
+in a machine-independent way.
+
+\item{2.} Run the \.{INIMF} prepared in step 1. In response to the first
+`\.{**}' prompt, type carriage return (thus getting another `\.{**}').
+Then type `\.{\char`\\input trap}'. You should get an output that matches
+the file \.{TRAPIN.LOG} (Appendix~C). Don't be alarmed by the error
+messages that you see, unless they are different from those in Appendix~C.
+
+\def\sp{{\char'40}}
+\item{3.} Run \.{INIMF} again. This time type `\.{\sp\&trap\sp\sp trap\sp}'.
+(The spaces in this input help to check certain parts of \MF\ that
+aren't otherwise used.) You should get outputs \.{TRAP.LOG}, \.{TRAP.72270GF},
+and \.{TRAP.TFM}.
+Furthermore, your terminal should receive output that matches \.{TRAP.FOT}
+(Appendix~G). 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.
+
+\item{4.} Compare the \.{TRAP.LOG} file from step 3 with the ``master''
+\.{TRAP.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:
+
+\itemitem{a)} The dates and possibly the file names will
+naturally be different.
+
+\itemitem{b)} 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.
+
+\itemitem{c)} Help messages may be different; indeed, the author encourages
+non-English help messages in versions of \MF\ for people who don't
+understand English as well as some other language.
+
+\itemitem{d)} The total number and length of strings at the end and/or
+``still untouched'' may well be different.
+
+\itemitem{e)} If your \MF\ uses a different memory allocation or
+packing scheme, the memory usage statistics may change.
+
+\itemitem{f)} If you use a different storage allocation scheme, the
+capsule numbers will probably be different, but the order of variables
+should be unchanged when dependent variables are shown. \MF\ should also
+choose the same variables to be dependent.
+
+\itemitem{g)} If your computer handles integer division of negative operands
+in a nonstandard way, you may get results that are rounded differently.
+Although \TeX\ is careful to be machine-independent in this regard,
+\MF\ is not, because integer divisions are present in so many places.
+
+\item{5.} Use \.{GFtype} to convert your file \.{TRAP.72270GF} to a file
+\.{TRAP.TYP}. (Both of \.{GFtype}'s options, i.e., mnemonic output and image
+output, should be enabled so that you get the maximum amount of output.)
+The resulting file should agree with the master \.{TRAP.TYP} file of step~0,
+assuming that your \.{GFtype} has the ``normal'' values of compile-time
+constants ($\\{top\_pixel}=69$, etc.).
+
+\item{6.} Use \.{TFtoPL} to convert your file \.{TRAP.TFM} to a file
+\.{TRAP.PL}. The resulting file should agree with the master \.{TRAP.PL}
+file of step~0.
+
+\item{7.} You might also wish to test \.{TRAP} with other versions of
+\MF\ (i.e., \.{VIRMF} or a production version with another base file
+preloaded). It should work unless \MF's primitives have been redefined in
+the base file. However, this step isn't essential, since all the code of
+\.{VIRMF} appears in \.{INIMF}; you probably won't catch any more errors
+this way, unless they would already become obvious from normal use of
+the~system.
+
+\vfill\eject
+
+\section Appendix B: The \.{TRAP.MF} 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.
+\runninghead{APPENDIX B: \.{TRAP.MF} (CONTINUED)}
+
+\vskip 8pt
+\begingroup\count255=0
+\everypar{\global\advance\count255 by 1
+ \hbox to 20pt{\sevenrm\hfil\the\count255\ \ }}
+\verbatim{trap.mf}
+\endgroup
+\vfill\eject
+
+\section Appendix C: The \.{TRAPIN.LOG} file.
+When \.{INIMF} makes the \.{TRAP.BASE} file, it also creates a file called
+\.{TRAP.LOG} that looks like this.
+\runninghead{APPENDIX C: \.{TRAPIN.LOG} (CONTINUED)}
+
+\vskip8pt
+\verbatim{trapin.log}
+\vfill\eject
+
+\section Appendix D: The \.{TRAP.LOG} file.
+Here is the major output of the \.{TRAP} test; it is generated by running
+\.{INIMF} and loading \.{TRAP.BASE}, then reading \.{TRAP.MF}.
+\runninghead{APPENDIX D: \.{TRAP.LOG} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trap.log}
+}
+\vfill\eject
+
+\section Appendix E: The \.{TRAP.TYP} file.
+Here is another major component of the test. It shows the output of \.{GFtype}
+applied to the file \.{TRAP.72270GF} that is created at the same time
+Appendix D was produced.
+\runninghead{APPENDIX E: \.{TRAP.TYP} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trap.typ}
+}
+\vfill\eject
+
+\section Appendix F: The \.{TRAP.PL} file.
+In this case we have the output of \.{TFtoPL}
+applied to the file \.{TRAP.TFM} that is created at the same time
+Appendix D was produced.
+\runninghead{APPENDIX F: \.{TRAP.PL} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trap.pl}
+}
+\vfill\eject
+
+\section Appendix G: The \.{TRAP.FOT} file.
+This shows what appeared on the terminal while Appendix D was being produced.
+\runninghead{APPENDIX G: \.{TRAP.FOT} (CONTINUED)}
+
+\vskip8pt
+\verbatim{trap.fot}
+
+\vfill\end