modernc.org/knuth@v0.0.4/web/testdata/ctan.org/tex-archive/systems/knuth/dist/tex/glue.web (about)

     1  % This program by D. E. Knuth is not copyrighted and can be used freely.
     2  % It was written on 18 Dec 1981 and revised on 24 May 1991.
     3  
     4  % Here is TeX material that gets inserted after \input webmac
     5  \def\PASCAL{Pascal}
     6  \font\eightrm=cmr8
     7  
     8  \def\title{GLUE}
     9  \def\topofcontents{\null
    10    \titlefalse % include headline on the contents page
    11    \def\rheader{\mainfont\hfil \contentspagenumber}
    12    \vfill
    13    \centerline{\titlefont Fixed-Point Glue Setting}
    14    \vfill}
    15  \def\botofcontents{\vfill
    16    \centerline{\hsize 6in\baselineskip9pt
    17      \vbox{\eightrm\baselineskip9pt\noindent
    18      The preparation of this report
    19      was supported in part by the National Science
    20      Foundation under grants IST-7921977 and MCS-7723728;
    21      by Office of Naval Research grant N00014-81-K-0330;
    22      and by the IBM Corporation. `\TeX' is a
    23      trademark of the American Mathematical Society.}}}
    24  
    25  @* Introduction.
    26  If \TeX\ is being implemented on a microcomputer that does 32-bit
    27  addition and subtraction, but with multiplication and division restricted to
    28  multipliers and divisors that are either powers of~2 or positive
    29  integers less than~$2^{15}$, it can still do the computations associated
    30  with the setting of glue in a suitable way. This program illustrates one
    31  solution to the problem.
    32  
    33  Another purpose of this program is to provide the first ``short'' example
    34  of the use of \.{WEB}.
    35  
    36  @ The program itself is written in standard \PASCAL. It begins with a
    37  normal program header, most of which will be filled in with other parts of this
    38  ``web'' as we are ready to introduce them.
    39  @^program header@>
    40  
    41  @p program GLUE(@!input,@!output);
    42    type @<Types in the outer block@>@;
    43    var @<Globals in the outer block@>@;
    44    procedure initialize; {this procedure gets things started}
    45      var @<Local variables for initialization@>@;
    46      begin @<Set initial values@>;
    47      end;
    48  
    49  @ Here are two macros for common programming idioms.
    50  
    51  @d incr(#) == #:=#+1 {increase a variable by unity}
    52  @d decr(#) == #:=#-1 {decrease a variable by unity}
    53  
    54  @* The problem and a solution.
    55  We are concerned here with the ``setting of glue'' that occurs when a
    56  \TeX\ box is being packaged. Let $x_1$, \dots,~$x_n$ be integers whose sum
    57  $s=x_1+\cdots+x_n$ is positive, and let $t$ be another positive integer.
    58  These $x_i$ represent scaled amounts of glue in units of sp (scaled
    59  points), where one sp is $2^{-16}$ of a printer's point. The other
    60  quantity $t$ represents the total by which the glue should stretch or
    61  shrink. Following the conventions of \TeX82, we will assume that the
    62  integers we deal with are less than $2^{31}$ in absolute value.
    63  
    64  After the glue has been set, the actual amounts of incremental glue space
    65  (in~sp) will be the integers $f(x_1)$, \dots,~$f(x_n)$, where $f$ is a
    66  function that we wish to compute. We want $f(x)$ to be nearly proportional
    67  to~$x$, and we also want the sum $f(x_1)+\cdots+f(x_n)$ to be nearly
    68  equal to~$t$. If we were using floating-point arithmetic, we would simply
    69  compute $f(x)\equiv(t/s)\cdot x$ and hope for the best; but the goal here
    70  is to compute a suitable~$f$ using only the fixed-point arithmetic operations
    71  of a typical ``16-bit microcomputer.''
    72  
    73  The solution adopted here is to determine integers $a$, $b$, $c$ such that
    74  $$f(x)=\bigl\lfloor 2^{-b}c\lfloor 2^{-a}x\rfloor\bigr\rfloor$$
    75  if $x$ is nonnegative. Thus, we take $x$ and shift it right by $a$~bits,
    76  then multiply by~$c$ (which is $2^{15}$ or less), and shift the product
    77  right by $b$~bits. The quantities $a$, $b$, and~$c$ are to be chosen
    78  so that this calculation doesn't cause overflow and so that $f(x_1)+\cdots
    79  +f(x_n)$ is reasonably close to~$t$.
    80  
    81  The following method is used to calculate $a$ and~$b$:
    82  Suppose $$y=\max_{1\le i\le n}\vert x_i\vert\,.$$
    83  Let $d$ and $e$ be the smallest integers such that $t<2^ds$ and $y<2^e$.
    84  Since $s$ and~$t$ are less than~$2^{31}$, we have $-30\le d\le31$ and
    85  $1\le e\le31$. An error message is given if $d+e\ge31$; in such a case
    86  some $x_m$ has $\vert x_m\vert\ge 2^{e-1}$ and we are trying to change
    87  $\vert x_m\vert$ to $\vert(t/s)x_m\vert\ge2^{d+e-2}\ge2^{30}$~sp, which
    88  \TeX\ does not permit. (Consider, for example, the ``worst case'' situation
    89  $x_1=2^{30}+1$, $x_2=-2^{30}$, $t=2^{31}-1$; surely we need not bother
    90  trying to accommodate such anomalous combinations of values.) On the other
    91  hand if $d+e\le31$, we set $a=e-16$ and $b=31-d-e$. Notice that this choice
    92  of~$a$ guarantees that $\lfloor2^{-a}\vert x_i\vert\rfloor<2^{16}$. We will
    93  choose~$c$ to be at most~$2^{15}$, so that the product will be less
    94  than~$2^{31}$.
    95  
    96  The computation of $c$ is the tricky part.
    97  @^hairy mathematics@>
    98  The ``ideal'' value for $c$ would be $\rho=2^{a+b}t/s$, since $f(x)$ should
    99  be approximately $(t/s)\cdot x$. Furthermore it is better to have $c$ slightly
   100  larger than~$\rho$, instead of slightly smaller, since the other operations
   101  in $f(x)$ have a downward bias. Therefore we shall compute $c=\lceil\rho\rceil$.
   102  Since $2^{a+b}t/s<2^{a+b+d}=2^{15}$, we have $c\le2^{15}$ as desired.
   103  
   104  We want to compute $c=\lceil\rho\rceil$ exactly in all cases. There is no
   105  difficulty if $s<2^{15}$, since $c$ can be computed directly using the
   106  formula $c=\bigl\lfloor(2^{a+b}t+s-1)/s\bigr\rfloor$; overflow will not
   107  occur since $2^{a+b}t<2^{15}s<2^{30}$.
   108  
   109  Otherwise let $s=s_12^l+s_2$, where $2^{14}\le s_1<2^{15}$ and $0\le s_2<2^l$.
   110  We will essentially carry out a long division. Let $t$ be ``normalized''
   111  so that $2^{30}\le2^ht<2^{31}$ for some~$h$. Then we form the quotient and
   112  remainder of $2^ht$ divided by~$s_1$,
   113  $$ 2^ht=qs_1+r_0, \qquad 0\le r_0<s_1.$$
   114  It follows that $2^{h+l}t-qs=2^lr_0-qs_2=r$, say. If $0\ge r>-s$ we have
   115  $q=\lceil2^{h+l}t/s\rceil$; otherwise we can replace $(q,r)$ by
   116  $(q\pm1,r\mp s)$ repeatedly until $r$ is in the correct range. It is not
   117  difficult to prove that $q$ needs to be increased at most once and decreased
   118  at most seven times, since $2^lr_0-qs_2<2^ls_1\le s$ and since
   119  $qs_2/s\le(2^ht/s_1)(s_2/2^ls_1)<2^{31}/s_1^2\le8$. Finally, we have
   120  $a+b-h-l=-1$ or~$-2$, since $2^{28+l}\le2^{14}s=2^{a+b+d-1}s\le2^{a+b}t<
   121  2^{a+b+d}s=2^{15}s<2^{30+l}$ and $2^{30}\le2^ht<2^{31}$. Hence
   122  $c=\lceil2^{a+b-h-l}q\rceil=\lceil{1\over2}q\rceil$ or~$\lceil{1\over4}q\rceil$.
   123  
   124  An error analysis shows that these values of $a$, $b$, and $c$ work
   125  satisfactorily, except in unusual cases where we wouldn't expect them to.
   126  @^error analysis@>
   127  When $x\ge0$ we have
   128  $$\eqalign{f(x)&=2^{-b}(2^{a+b}t/s+\theta_0)(2^{-a}x-\theta_1)-\theta_2\cr
   129  &=(t/s)x+\theta_02^{-a-b}x-\theta_12^at/s-2^{-b}\theta_0\theta_1-\theta_2\cr}$$
   130  where $0\le\theta_0,\theta_1,\theta_2<1$. Now $0\le\theta_02^{-a-b}x
   131  <2^{e-a-b}=2^{d+e-15}$ and $0\le\theta_12^at/s<2^{a+d}=2^{d+e-16}$, and
   132  the other two terms are negligible. Therefore $f(x_1)+\cdots+f(x_n)$ differs
   133  from~$t$ by at most about $2^{d+e-15}n$. Since $2^{d+e}$ is larger than
   134  $(t/s)y$, which is the largest stretching or shrinking of glue after expansion,
   135  the error is at worst about $n/32000$ times as much as this, so it is quite
   136  reasonable. For example, even if fill glue is being used to stretch
   137  20 inches, the error will still be less than $1\over1600$ of an inch.
   138  
   139  @ To sum up: Given the positive integers $s$, $t$, and $y$ as above, we
   140  set $$a\gets\lfloor\lg y\rfloor-15,\qquad b\gets29-\lfloor\lg y\rfloor-
   141  \lfloor\lg t/s\rfloor,\qquad\hbox{and}\qquad c\gets\lceil2^{a+b}t/s\rceil.$$
   142  The implementation below shows how to do the job in \PASCAL\ without using
   143  large numbers.
   144  
   145  @ \TeX\ wants to have the glue-setting information in a 32-bit data type
   146  called |glue_ratio|. The \PASCAL\ implementation of \TeX82 has |glue_ratio
   147  =real|, but alternative definitions of |glue_ratio| are explicitly allowed.
   148  
   149  For our purposes we shall let |glue_ratio| be a record that is packed with
   150  three fields: The |a_part| will hold the positive integer |a+16|, the
   151  |b_part| will hold the nonnegative integer~|b|, and the |c_part| will hold
   152  the nonnegative integer~|c|. When the formulas above tell us to take
   153  |b>30|, we might as well set |c:=0| instead, because |f(x)| will be
   154  zero in all cases when |b>30|. Note that we have only about 25 bits of
   155  information in all, so it should fit in 32 bits with ease.
   156  
   157  @<Types...@>=
   158  @!glue_ratio=packed record
   159    @!a_part: 1..31; {the quantity |e=a+16| in our derivation}
   160    @!b_part: 0..30; {the quantity |b| in our derivation}
   161    @!c_part: 0..@'100000; {the quantity |c| in our derivation}
   162    end;
   163  @!scaled = integer; {this data type is used for quantities in sp units}
   164  
   165  @ The real problem is to define the procedures that \TeX\ needs to
   166  deal with such |glue_ratio| values:
   167  (a)~Given scaled numbers |s|, |t|, and~|y| as above, to compute the
   168  corresponding |glue_ratio|.
   169  (b)~Given a nonnegative scaled number~|x| and a |glue_ratio|~|g|, to
   170  compute the scaled number~|f(x)|.
   171  (c)~Given a |glue_ratio|~|g|, to print out a decimal equivalent of
   172  |g| for diagnostic purposes.
   173  
   174  The procedures below can be incorporated into \TeX82 via a change file
   175  without great difficulty. A few modifications will be needed, because
   176  \TeX's |glue_ratio| values can be negative in unusual cases---when the
   177  amount of stretchability or shrinkability is less than zero. Negative
   178  values in the |c_part| will handle such problems, if proper care is
   179  taken.  The error message below should either become a warning message
   180  or a call to \TeX's |print_err| routine; in the latter case, an
   181  @^error message@>
   182  appropriate help message should be given, stating that glue cannot
   183  stretch to more than 18~feet long, but that it's OK to proceed with
   184  fingers crossed.
   185  
   186  @*Glue multiplication.
   187  The easiest procedure of the three just mentioned is the one that is
   188  needed most often, namely, the computation of~|f(x)|.
   189  
   190  \PASCAL\ doesn't have built-in binary shift commands or built-in exponentiation,
   191  although many computers do have this capability. Therefore our arithmetic
   192  routines use an array called `|two_to_the|', containing powers of~two.
   193  Divisions by powers of two are never done in the programs below when the
   194  dividend is negative, so the operations can safely be replaced by right
   195  shifts on machines for which this is most appropriate. (Contrary to popular
   196  opinion, the operation `|x div 2|' is not the same as shifting |x|
   197  right one binary place, on a machine with two's complement arithmetic,
   198  when |x| is a negative odd integer. But division
   199  {\it is\/} equivalent to shifting when |x| is nonnegative.)
   200  
   201  @<Globals...@>=
   202  @!two_to_the: array[0..30] of integer; {$|two_to_the|[k]=2^k$}
   203  
   204  @ @<Local variables for init...@>=
   205  @!k:1..30; {an index for initializing |two_to_the|}
   206  
   207  @ @<Set init...@>=
   208  two_to_the[0]:=1;
   209  for k:=1 to 30 do two_to_the[k]:=two_to_the[k-1]+two_to_the[k-1];
   210  
   211  @ We will use the abbreviations |ga|, |gb|, and |gc| as convenient
   212  alternatives to \PASCAL's \&{with} statement. The glue-multiplication
   213  function |f|, which replaces several occurrences of the `|float|' macro
   214  in \TeX82, is now easy to state:
   215  
   216  @d ga==g.a_part
   217  @d gb==g.b_part
   218  @d gc==g.c_part
   219  
   220  @p function glue_mult(@!x:scaled;@!g:glue_ratio):integer;
   221   {returns |f(x)| as above, assuming that |x>=0|}
   222  begin if ga>16 then x:=x div two_to_the[ga-16] {right shift by |a| places}
   223  else x:=x*two_to_the[16-ga]; {left shift by |-a| places}
   224  glue_mult:=(x*gc) div two_to_the[gb]; {right shift by |b| places}
   225  end; {note that |b| may be as large as 30}
   226  
   227  @*Glue setting.
   228  The |glue_fix| procedure computes |a|, |b|, and |c| by the method
   229  explained above. \TeX\ does not normally compute the quantity~|y|, but
   230  it could be made to do so without great difficulty.
   231  
   232  This procedure replaces several occurrences of the `|unfloat|' macro in
   233  \TeX82. It would be written as a function that returns a |glue_ratio|,
   234  if \PASCAL\ would allow functions to produce records as values.
   235  
   236  @p procedure glue_fix(@!s,@!t,@!y:scaled; var@!g:glue_ratio);
   237  var @!a,@!b,@!c:integer; {components of the desired ratio}
   238  @!k,@!h:integer; {$30-\lfloor\lg s\rfloor$, $30-\lfloor\lg t\rfloor$}
   239  @!s0:integer; {original (unnormalized) value of |s|}
   240  @!q,@!r,@!s1:integer; {quotient, remainder, divisor}
   241  @!w:integer; {$2^l$, where $l=16-k$}
   242  begin @<Normalize |s|, |t|, and |y|, computing |a|, |k|, and |h|@>;
   243  if t<s then b:=15-a-k+h@+else b:=14-a-k+h;
   244  if (b<0) or (b>30) then
   245    begin if b<0 then write_ln('! Excessive glue.'); {error message}
   246  @^error message@>
   247    b:=0; c:=0; {make |f(x)| identically zero}
   248    end
   249  else begin if k>=16 then {easy case, $s_0<2^{15}$}
   250      c:=(t div two_to_the[h-a-b]+s0-1) div s0 {here |1<=h-a-b<=k-14<=16|}
   251    else @<Compute |c| by long division@>;
   252    end;
   253  ga:=a+16; gb:=b; gc:=c;
   254  end;
   255  
   256  @ @<Normalize |s|, |t|, and |y|, computing |a|, |k|, and |h|@>=
   257  begin a:=15; k:=0; h:=0; s0:=s;
   258  while y<@'10000000000 do {|y| is known to be positive}
   259    begin decr(a); y:=y+y;
   260    end;
   261  while s<@'10000000000 do {|s| is known to be positive}
   262    begin incr(k); s:=s+s;
   263    end;
   264  while t<@'10000000000 do {|t| is known to be positive}
   265    begin incr(h); t:=t+t;
   266    end;
   267  end {now $2^{30}\le t=2^ht_0<2^{31}$ and $2^{30}\le s=2^ks_0<2^{31}$,
   268    hence $d=k-h$ if $t/s<1$}
   269  
   270  @ @<Compute |c| by long division@>=
   271  begin w:=two_to_the[16-k];
   272  s1:=s0 div w;
   273  q:=t div s1;
   274  r:=((t mod s1)*w)-((s0 mod w)*q);
   275  if r>0 then
   276    begin incr(q); r:=r-s0;
   277    end
   278  else while r<=-s0 do
   279    begin decr(q); r:=r+s0;
   280    end;
   281  if a+b+k-h=15 then c:=(q+1) div 2 @+else c:=(q+3) div 4;
   282  end
   283  
   284  @*Glue-set printing.
   285  The last of the three procedures we need is |print_gr|, which displays a
   286  |glue_ratio| in symbolic decimal form. Before constructing such a procedure,
   287  we shall consider some simpler routines, copying them from an early
   288  draft of the program \TeX82.
   289  
   290  @d unity==@'200000 {$2^{16}$, represents 1.0000}
   291  
   292  @<Glob...@>=
   293  @!dig:array[0..15] of 0..9; {for storing digits}
   294  
   295  @ An array of digits is printed out by |print_digs|.
   296  
   297  @p procedure print_digs(@!k:integer); {prints |dig[k-1]| \dots |dig[0]|}
   298  begin while k>0 do
   299    begin decr(k); write(chr(ord('0')+dig[k]));
   300    end;
   301  end;
   302  
   303  @ A nonnegative integer is printed out by |print_int|.
   304  
   305  @p procedure print_int(@!n:integer); {prints an integer in decimal form}
   306  var @!k:0..12; {index to current digit; we assume that $0\le n<10^{12}$}
   307  begin k:=0;
   308  repeat dig[k]:=n mod 10; n:=n div 10; incr(k);
   309  until n=0;
   310  print_digs(k);
   311  end;
   312  
   313  @ And here is a procedure to print a nonnegative |scaled| number.
   314  
   315  @p procedure print_scaled(s:scaled);
   316   {prints a scaled real, truncated to four digits}
   317  var k:0..3; {index to current digit of the fraction part}
   318  begin print_int(s div unity); {print the integer part}
   319  s:=((s mod unity)*10000) div unity;
   320  for k:=0 to 3 do
   321    begin dig[k]:=s mod 10; s:=s div 10;
   322    end;
   323  write('.'); print_digs(4);
   324  end;
   325  
   326  @ Now we're ready to print a |glue_ratio|. Since the effective multiplier
   327  is $2^{-a-b}c$, we will display the scaled integer $2^{16-a-b}c$, taking
   328  care to print something special if this quantity is terribly large.
   329  
   330  @p procedure print_gr(@!g:glue_ratio); {prints a glue multiplier}
   331  var @!j:-29..31; {the amount to shift |c|}
   332  begin j:=32-ga-gb;
   333  while j>15 do
   334    begin write('2x'); decr(j); {indicate multiples of 2 for BIG cases}
   335    end;
   336  if j<0 then print_scaled(gc div two_to_the[-j]) {shift right}
   337  else print_scaled(gc*two_to_the[j]); {shift left}
   338  end;
   339  
   340  @* The driver program.
   341  In order to test these routines, we will assume that the |input| file
   342  contains a sequence of test cases, where each test case consists of the
   343  integer numbers $t$, $x_1$, \dots,~$x_n$, 0. The final test case should
   344  be followed by an additional zero.
   345  
   346  @<Glob...@>=
   347  @!x:array[1..1000] of scaled; {the $x_i$}
   348  @!t:scaled; {the desired total}
   349  @!m:integer; {the test case number}
   350  
   351  @ Each case will be processed by the following routine, which assumes
   352  that |t| has already been read.
   353  
   354  @p procedure test; {processes the next data set, given |t| and~|m|}
   355  var @!n: 0..1000; {the number of items}
   356  k:0..1000; {runs through the items}
   357  y:scaled; {$\max_{1\le i\le n}\vert x_i\vert$}
   358  @!g:glue_ratio; {the computed glue multiplier}
   359  @!s:scaled; {the sum $x_1+\cdots+x_n$}
   360  @!ts:scaled; {the sum $f(x_1)+\cdots+f(x_n)$}
   361  begin write_ln('Test data set number ',m:1,':');
   362  @<Read $x_1,\ldots,x_n$@>;
   363  @<Compute |s| and |y|@>;
   364  if s<=0 then write_ln('Invalid data (nonpositive sum); this set rejected.')
   365  else begin @<Compute |g| and print it@>;
   366    @<Print the values of $x_i$, $f(x_i)$, and the totals@>;
   367    end;
   368  end;
   369  
   370  @ @<Read $x_1,\ldots,x_n$@>=
   371  begin n:=0;
   372  repeat incr(n); read(x[n]);
   373  until x[n]=0;
   374  decr(n);
   375  end
   376  
   377  @ @<Compute |s| and |y|@>=
   378  begin s:=0; y:=0;
   379  for k:=1 to n do
   380    begin s:=s+x[k];
   381    if y<abs(x[k]) then y:=abs(x[k]);
   382    end;
   383  end
   384  
   385  @ @<Compute |g| and print it@>=
   386  begin glue_fix(s,t,y,g); {set |g|, perhaps print an error message}
   387  write('  Glue ratio is '); print_gr(g);
   388  write_ln(' (',ga-16:1,',',gb:1,',',gc:1,')');
   389  end
   390  
   391  @ @<Print the values of $x_i$, $f(x_i)$, and the totals@>=
   392  begin ts:=0;
   393  for k:=1 to n do
   394    begin write(x[k]:20);
   395    if x[k]>=0 then y:=glue_mult(x[k],g)
   396    else y:=-glue_mult(-x[k],g);
   397    write_ln(y:15);
   398    ts:=ts+y;
   399    end;
   400  write_ln(' Totals',s:13,ts:15,' (versus ',t:1,')');
   401  end
   402  
   403  @ Here is the main program.
   404  @^main program@>
   405  
   406  @p begin initialize;
   407  m:=1;
   408  read(t);
   409  while t>0 do
   410    begin test;
   411    incr(m); read(t);
   412    end;
   413  end.
   414  
   415  @*Index. Here are the section numbers where various identifiers are used in the
   416  program, and where various topics are discussed.
   417  
   418  
   419  
   420  
   421