258  PART 35: SUBROUTINES FOR MATH MODE  TEX82 §699

699. Subroutines for math mode. In order to convert mlists to hlists, i.e., noads to nodes, we need several subroutines that are conveniently dealt with now.

Let us first introduce the macros that make it easy to get at the parameters and other font information. A size code, which is a multiple of 16, is added to a family number to get an index into the table of internal font numbers for each combination of family and size. (Be alert: Size codes get larger as the type gets smaller.)

define text size = 0 { size code for the largest size in a family } define script size = 16 { size code for the medium size in a family } define script script size = 32 { size code for the smallest size in a family }

⟨ Basic printing procedures 57 ⟩ +≡ procedure print size(s : integer );

begin if s = 0 then print esc( ) else if s = script size then print esc( )

else print esc( ); end;

700. Before an mlist is converted to an hlist, TEX makes sure that the fonts in family 2 have enough parameters to be math-symbol fonts, and that the fonts in family 3 have enough parameters to be mathextension fonts. The math-symbol parameters are referred to by using the following macros, which take a size code as their parameter; for example, num1 (cur size) gives the value of the num1 parameter for the current size.

define mathsy end (#) ≡ fam fnt (2 + #) ] ] .sc define mathsy (#) ≡ font info [ # + param base [ mathsy end define math x height ≡ mathsy (5) { height of ‘ ’ } define math quad ≡ mathsy (6) { } define num1 ≡ mathsy (8) { numerator shift-up in display styles } define num2 ≡ mathsy (9) { numerator shift-up in non-display, non- } define num3 ≡ mathsy (10) { numerator shift-up in non-display } define denom1 ≡ mathsy (11) { denominator shift-down in display styles } define denom2 ≡ mathsy (12) { denominator shift-down in non-display styles } define sup1 ≡ mathsy (13) { superscript shift-up in uncramped display style } define sup2 ≡ mathsy (14) { superscript shift-up in uncramped non-display } define sup3 ≡ mathsy (15) { superscript shift-up in cramped styles } define sub1 ≡ mathsy (16) { subscript shift-down if superscript is absent } define sub2 ≡ mathsy (17) { subscript shift-down if superscript is present } define sup drop ≡ mathsy (18) { superscript baseline below top of large box } define sub drop ≡ mathsy (19) { subscript baseline below bottom of large box } define delim1 ≡ mathsy (20) { size of delimiters in display styles } define delim2 ≡ mathsy (21) { size of delimiters in non-displays } define axis height ≡ mathsy (22) { height of fraction lines above the baseline } define total mathsy params = 22

701. The math-extension parameters have similar macros, but the size code is omitted (since it is always cur size when we refer to such parameters).

§702  TEX82  PART 35: SUBROUTINES FOR MATH MODE 259

define mathex (#) ≡ font info[# + param base[fam fnt (3 + cur size)]].sc define default rule thickness ≡ mathex (8) { thickness of bars } define big op spacing1 ≡ mathex (9) { minimum clearance above a displayed op } define big op spacing2 ≡ mathex (10) { minimum clearance below a displayed op } define big op spacing3 ≡ mathex (11) { minimum baselineskip above displayed op } define big op spacing4 ≡ mathex (12) { minimum baselineskip below displayed op } define big op spacing5 ≡ mathex (13) { padding above and below displayed limits } define total mathex params = 13 702. We also need to compute the change in style between mlists and their subsidiaries. The following macros define the subsidiary style for an overlined nucleus (cramped style), for a subscript or a superscript (sub style or sup style), or for a numerator or denominator (num style or denom style).

define cramped style(#) ≡ 2 ∗ (# div 2) + cramped { cramp the style } define sub style(#) ≡ 2 ∗ (# div 4) + script style + cramped { smaller and cramped } define sup style(#) ≡ 2 ∗ (# div 4) + script style + (# mod 2) { smaller } define num style(#) ≡ # + 2 − 2 ∗ (# div 6) { smaller unless already script-script } define denom style(#) ≡ 2 ∗ (# div 2) + cramped + 2 − 2 ∗ (# div 6) { smaller, cramped }

703. When the style changes, the following piece of program computes associated information: ⟨ Set up the values of cur size and cur mu, based on cur style 703 ⟩ ≡

begin if cur style < script style then cur size ← text size else cur size ← 16 ∗ ((cur style − text style) div 2); cur mu ← x over n(math quad (cur size), 18); end

This code is used in sections 720, 726, 730, 754, 760, and 763.

704. Here is a function that returns a pointer to a rule node having a given thickness t. The rule will extend horizontally to the boundary of the vlist that eventually contains it. function fraction rule(t : scaled ): pointer ; { construct the bar for a fraction }

var p: pointer ; { the new node } begin p ← new rule; height (p) ← t; depth(p) ← 0; fraction rule ← p; end;

705. The overbar function returns a pointer to a vlist box that consists of a given box b, above which has been placed a kern of height k under a fraction rule of thickness t under additional space of height t. function overbar (b : pointer ; k, t : scaled ): pointer ;

var p, q: pointer ; { nodes being constructed } begin p ← new kern(k); link (p) ← b; q ← fraction rule(t); link (q) ← p; p ← new kern(t); link (p) ← q; overbar ← vpack (p, natural ); end; 706. The var delimiter function, which finds or constructs a sufficiently large delimiter, is the most interesting of the auxiliary functions that currently concern us. Given a pointer d to a delimiter field in some noad, together with a size code s and a vertical distance v, this function returns a pointer to a box that contains the smallest variant of d whose height plus depth is v or more. (And if no variant is large enough, it returns the largest available variant.) In particular, this routine will construct arbitrarily large delimiters from extensible components, if d leads to such characters.

The value returned is a box whose shift amount has been set so that the box is vertically centered with respect to the axis in the given size. If a built-up symbol is returned, the height of the box before shifting will be the height of its topmost component. ⟨ Declare subprocedures for var delimiter 709 ⟩ function var delimiter (d : pointer ; s : small number ; v : scaled ): pointer ;

label found , continue; var b: pointer ; { the box that will be constructed }

f, g: internal font number ; { best-so-far and tentative font codes } c, x, y: quarterword ; { best-so-far and tentative character codes } m, n: integer ; { the number of extensible pieces } u: scaled ; { height-plus-depth of a tentative character } w: scaled ; { largest height-plus-depth so far } q: four quarters ; { character info } hd : eight bits ; { height-depth byte } r: four quarters ; { extensible pieces } z: small number ; { runs through font family members } large attempt : boolean; { are we trying the “large” variant? }

begin f ← null font ; w ← 0; large attempt ← false; z ← small fam(d); x ← small char (d); loop begin ⟨ Look at the variants of (z, x); set f and c whenever a better character is found; goto

found as soon as a large enough variant is encountered 707 ⟩; if large attempt then goto found ; { there were none large enough } large attempt ← true; z ← large fam(d); x ← large char (d); end;

found : if f ≠ null font then ⟨ Make variable b point to a box for (f, c) 710 ⟩

else begin b ← new null box ; width(b) ← null delimiter space;

{ use this width if no delimiter was found } end;

shift amount (b) ← half (height (b) − depth(b)) − axis height (s); var delimiter ← b; end;

707. The search process is complicated slightly by the facts that some of the characters might not be present in some of the fonts, and they might not be probed in increasing order of height. ⟨ Look at the variants of (z, x); set f and c whenever a better character is found; goto found as soon as a

large enough variant is encountered 707 ⟩ ≡ if (z ≠ 0) ∨ (x ≠ min quarterword ) then

begin z ← z + s + 16; repeat z ← z − 16; g ← fam fnt (z);

if g ≠ null font then ⟨ Look at the list of characters starting with x in font g; set f and c whenever

a better character is found; goto found as soon as a large enough variant is encountered 708 ⟩;

until z < 16; end

This code is used in section 706.

708. ⟨ Look at the list of characters starting with x in font g; set f and c whenever a better character is

found; goto found as soon as a large enough variant is encountered 708 ⟩ ≡ begin y ← x; if (qo(y) ≥ font bc[g]) ∧ (qo(y) ≤ font ec[g]) then

begin continue: q ← char info(g)(y);

if char exists (q) then

begin if char tag (q) = ext tag then begin f ← g; c ← y; goto found ; end;

hd ← height depth(q); u ← char height (g)(hd ) + char depth(g)(hd ); if u > w then

begin f ← g; c ← y; w ← u; if u ≥ v then goto found ; end;

if char tag (q) = list tag then begin y ← rem byte(q); goto continue; end; end;

end; end

This code is used in section 707.

709. Here is a subroutine that creates a new box, whose list contains a single character, and whose width includes the italic correction for that character. The height or depth of the box will be negative, if the height or depth of the character is negative; thus, this routine may deliver a slightly different result than hpack would produce. ⟨ Declare subprocedures for var delimiter 709 ⟩ ≡ function char box (f : internal font number ; c : quarterword ): pointer ;

var q: four quarters ; hd : eight bits ; { height depth byte }

b, p: pointer ; { the new box and its character node } begin q ← char info(f)(c); hd ← height depth(q); b ← new null box ; width(b) ← char width(f)(q) + char italic(f)(q); height (b) ← char height (f)(hd ); depth(b) ← char depth(f)(hd ); p ← get avail ; character (p) ← c; font (p) ← f; list ptr (b) ← p; char box ← b; end;

See also sections 711 and 712. This code is used in section 706.

710. When the following code is executed, char tag (q) will be equal to ext tag if and only if a built-up symbol is supposed to be returned. ⟨ Make variable b point to a box for (f, c) 710 ⟩ ≡

if char tag (q) = ext tag then

⟨ Construct an extensible character in a new box b, using recipe rem byte(q) and font f 713 ⟩ else b ← char box (f, c)

This code is used in section 706.

711. When we build an extensible character, it’s handy to have the following subroutine, which puts a given character on top of the characters already in box b: ⟨ Declare subprocedures for var delimiter 709 ⟩ +≡ procedure stack into box (b : pointer ; f : internal font number ; c : quarterword );

var p: pointer ; { new node placed into b } begin p ← char box (f, c); link (p) ← list ptr (b); list ptr (b) ← p; height (b) ← height (p); end;

712. Another handy subroutine computes the height plus depth of a given character: ⟨ Declare subprocedures for var delimiter 709 ⟩ +≡ function height plus depth(f : internal font number ; c : quarterword ): scaled ;

var q: four quarters ; hd : eight bits ; { height depth byte } begin q ← char info(f)(c); hd ← height depth(q); height plus depth ← char height (f)(hd ) + char depth(f)(hd ); end;

713. ⟨ Construct an extensible character in a new box b, using recipe rem byte(q) and font f 713 ⟩ ≡ begin b ← new null box ; type(b) ← vlist node; r ← font info[exten base[f] + rem byte(q)].qqqq ; ⟨ Compute the minimum suitable height, w, and the corresponding number of extension steps, n; also set

width(b) 714 ⟩; c ← ext bot (r); if c ≠ min quarterword then stack into box (b, f, c); c ← ext rep(r); for m ← 1 to n do stack into box (b, f, c); c ← ext mid (r); if c ≠ min quarterword then

begin stack into box (b, f, c); c ← ext rep(r); for m ← 1 to n do stack into box (b, f, c); end;

c ← ext top(r); if c ≠ min quarterword then stack into box (b, f, c); depth(b) ← w − height (b); end

This code is used in section 710.

714. The width of an extensible character is the width of the repeatable module. If this module does not have positive height plus depth, we don’t use any copies of it, otherwise we use as few as possible (in groups of two if there is a middle part). ⟨ Compute the minimum suitable height, w, and the corresponding number of extension steps, n; also set

width(b) 714 ⟩ ≡ c ← ext rep(r); u ← height plus depth(f, c); w ← 0; q ← char info(f)(c); width(b) ← char width(f)(q) + char italic(f)(q); c ← ext bot (r); if c ≠ min quarterword then w ← w + height plus depth(f, c); c ← ext mid (r); if c ≠ min quarterword then w ← w + height plus depth(f, c); c ← ext top(r); if c ≠ min quarterword then w ← w + height plus depth(f, c); n ← 0; if u > 0 then

while w < v do begin w ← w + u; incr (n); if ext mid (r) ≠ min quarterword then w ← w + u; end

This code is used in section 713.

715. The next subroutine is much simpler; it is used for numerators and denominators of fractions as well as for displayed operators and their limits above and below. It takes a given box b and changes it so that the new box is centered in a box of width w. The centering is done by putting glue at the left and right of the list inside b, then packaging the new box; thus, the actual box might not really be centered, if it already contains infinite glue.

The given box might contain a single character whose italic correction has been added to the width of the box; in this case a compensating kern is inserted. function rebox (b : pointer ; w : scaled ): pointer ;

var p: pointer ; { temporary register for list manipulation } f: internal font number ; { font in a one-character box } v: scaled ; { width of a character without italic correction }

begin if (width(b) ≠ w) ∧ (list ptr (b) ≠ null ) then begin if type(b) = vlist node then b ← hpack (b, natural ); p ← list ptr (b); if (is char node(p)) ∧ (link (p) = null ) then

begin f ← font (p); v ← char width(f)(char info(f)(character (p))); if v ≠ width(b) then link (p) ← new kern(width(b) − v); end;

free node(b, box node size); b ← new glue(ss glue); link (b) ← p; while link (p) ≠ null do p ← link (p); link (p) ← new glue(ss glue); rebox ← hpack (b, w, exactly ); end

else begin width(b) ← w; rebox ← b;

end; end; 716. Here is a subroutine that creates a new glue specification from another one that is expressed in ‘ ’, given the value of the math unit.

define mu mult (#) ≡ nx plus y (n, #, xn over d (#, f, ´ 200000 )) function math glue(g : pointer ; m : scaled ): pointer ;

var p: pointer ; { the new glue specification } n: integer ; { integer part of m } f: scaled ; { fraction part of m }

begin n ← x over n(m, ´ 200000 ); f ← remainder ; if f < 0 then

begin decr (n); f ← f + ´ 200000 ;

end; p ← get node(glue spec size); width(p) ← mu mult (width(g)); { convert to } stretch order (p) ← stretch order (g); if stretch order (p) = normal then stretch(p) ← mu mult (stretch(g)) else stretch(p) ← stretch(g); shrink order (p) ← shrink order (g); if shrink order (p) = normal then shrink (p) ← mu mult (shrink (g)) else shrink (p) ← shrink (g); math glue ← p; end;

717. The math kern subroutine removes mu glue from a kern node, given the value of the math unit. procedure math kern(p : pointer ; m : scaled );

var n: integer ; { integer part of m }

f: scaled ; { fraction part of m } begin if subtype(p) = mu glue then

begin n ← x over n(m, ´ 200000 ); f ← remainder ; if f < 0 then

begin decr (n); f ← f + ´ 200000 ; end;

width(p) ← mu mult (width(p)); subtype(p) ← explicit ; end;

end;

718. Sometimes it is necessary to destroy an mlist. The following subroutine empties the current list, assuming that abs (mode) = mmode. procedure flush math;

begin flush node list (link (head )); flush node list (incompleat noad ); link (head ) ← null ; tail ← head ; incompleat noad ← null ; end;