summaryrefslogtreecommitdiff
path: root/systems/tex-extensions/clasen/glue
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/tex-extensions/clasen/glue
Initial commit
Diffstat (limited to 'systems/tex-extensions/clasen/glue')
l---------systems/tex-extensions/clasen/glue/etex.bib1
-rw-r--r--systems/tex-extensions/clasen/glue/glue-post-web2c.ch40
-rw-r--r--systems/tex-extensions/clasen/glue/glue.ch677
-rw-r--r--systems/tex-extensions/clasen/glue/glue.tex75
-rw-r--r--systems/tex-extensions/clasen/glue/glueratio.patch15
5 files changed, 808 insertions, 0 deletions
diff --git a/systems/tex-extensions/clasen/glue/etex.bib b/systems/tex-extensions/clasen/glue/etex.bib
new file mode 120000
index 0000000000..59519b0c14
--- /dev/null
+++ b/systems/tex-extensions/clasen/glue/etex.bib
@@ -0,0 +1 @@
+../etex.bib \ No newline at end of file
diff --git a/systems/tex-extensions/clasen/glue/glue-post-web2c.ch b/systems/tex-extensions/clasen/glue/glue-post-web2c.ch
new file mode 100644
index 0000000000..ffa0ca7899
--- /dev/null
+++ b/systems/tex-extensions/clasen/glue/glue-post-web2c.ch
@@ -0,0 +1,40 @@
+% This change file has to be applied after the web2c change file
+% to revert some changes which make assumtion on glue_ratio being
+% real. Apart from the following change, they are all related to
+% mltex.
+
+@x
+ { The Unix |pc| folks removed this restriction with a remark that
+ invalid bit patterns were vanishingly improbable, so we follow
+ their example without really understanding it.
+ |if abs(mem[p+glue_offset].int)<@'4000000 then print('?.?')|
+ |else| }
+ if fabs(g)>float_constant(20000) then
+ begin end;
+@y
+@z
+
+@x
+@!base_slant,@!accent_slant:real; {amount of slant}
+@y
+@!base_slant,@!accent_slant:scaled; {amount of slant}
+@z
+
+@x
+ base_slant:=slant(f)/float_constant(65536);
+@^real division@>
+@y
+ base_slant:=slant(f);
+@z
+
+@x
+ delta:=round((base_width-accent_width)/float_constant(2)+
+ base_height*base_slant-base_x_height*accent_slant);
+@^real multiplication@>
+@^real addition@>
+@y
+ delta:=x_over_n(base_width-accent_width,2)
+ +xn_over_d(base_height,base_slant,unity)
+ -xn_over_d(base_x_height,accent_slant,unity);
+@z
+
diff --git a/systems/tex-extensions/clasen/glue/glue.ch b/systems/tex-extensions/clasen/glue/glue.ch
new file mode 100644
index 0000000000..a847ca31f9
--- /dev/null
+++ b/systems/tex-extensions/clasen/glue/glue.ch
@@ -0,0 +1,677 @@
+@x
+@!interrupt:integer; {should \TeX\ pause for instructions?}
+@!OK_to_interrupt:boolean; {should interrupts be observed?}
+
+@ @<Set init...@>=
+interrupt:=0; OK_to_interrupt:=true;
+@y
+@!interrupt:integer; {should \TeX\ pause for instructions?}
+@!OK_to_interrupt:boolean; {should interrupts be observed?}
+@!two_to_the: array[0..30] of integer; {$|two_to_the|[k]=2^k$}
+
+@ @<Set init...@>=
+interrupt:=0; OK_to_interrupt:=true;
+two_to_the[0]:=1;
+for i:=1 to 30 do two_to_the[i]:=two_to_the[i-1]+two_to_the[i-1];
+@z
+
+@x
+@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
+proportionality ratio by which the glue inside the box should stretch
+or shrink. This calculation does not affect \TeX's decision making,
+so the precise details of rounding, etc., in the glue calculation are not
+of critical importance for the consistency of results on different computers.
+
+We shall use the type |glue_ratio| for such proportionality ratios.
+A glue ratio should take the same amount of memory as an
+|integer| (usually 32 bits) if it is to blend smoothly with \TeX's
+other data structures. Thus |glue_ratio| should be equivalent to
+|short_real| in some implementations of \PASCAL. Alternatively,
+it is possible to deal with glue ratios using nothing but fixed-point
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+@^system dependencies@>
+
+@d set_glue_ratio_zero(#) == #:=0.0 {store the representation of zero ratio}
+@d set_glue_ratio_one(#) == #:=1.0 {store the representation of unit ratio}
+@d float(#) == # {convert from |glue_ratio| to type |real|}
+@d unfloat(#) == # {convert from |real| to type |glue_ratio|}
+@d float_constant(#) == #.0 {convert |integer| constant to |real|}
+@y
+@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
+proportionality ratio by which the glue inside the box should stretch
+or shrink. This calculation does not affect \TeX's decision making,
+so the precise details of rounding, etc., in the glue calculation are not
+of critical importance for the consistency of results on different computers.
+
+We shall use the type |glue_ratio| for such proportionality ratios.
+A glue ratio should take the same amount of memory as an
+|integer| (usually 32 bits) if it is to blend smoothly with \TeX's
+other data structures. Thus |glue_ratio| should be equivalent to
+|short_real| in some implementations of \PASCAL. Alternatively,
+it is possible to deal with glue ratios using nothing but fixed-point
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+
+This is what we do here. Our implementation stores |a+16| in the
+low five bits, |b| in the next five bits and |c+@'100000| in the
+next 17 bits of the integer |glue_ratio|. The procedures
+|pack_glue_ratio| and |unpack_glue_ratio| are used to convert
+to and from this format. A more efficient implementation would
+inline these prodedures and use shifting.
+
+@d glue_ratio_zero == 33554480 {|pack_glue_ratio(16,1,0)=33554480|}
+@d glue_ratio_one == 33556528 {|pack_glue_ratio(16,1,2)=33556528|}
+@d set_glue_ratio_zero(#) == #:=glue_ratio_zero
+@d set_glue_ratio_one(#) == #:=glue_ratio_one
+@z
+
+@x
+@!glue_ratio=real; {one-word representation of a glue expansion factor}
+@y
+@!glue_ratio=real; {one-word representation of a glue expansion factor}
+@!glue_ratio=integer; {one-word representation of a glue expansion factor}
+
+@ Before we can state the glue-multiplication function |glue_mult|, we need
+routines for packing and unpacking |glue_ratio|s.
+
+@p @!debug procedure check_range(l,h:integer; s:str_number; var v:integer);
+begin
+ if v<l then begin
+ v:=l;
+ print_err(s); print(" too small");
+ end else if v>h then begin
+ v:=h;
+ print_err(s); print(" too large");
+ end;
+end;
+gubed
+
+function pack_glue_ratio(a,b,c:integer):glue_ratio;
+begin
+ c:=c+@'100000;
+ @!debug
+ check_range(1,31,"pack_glue_ratio: a",a);
+ check_range(0,30,"pack_glue_ratio: b",b);
+ check_range(0,@'200000,"pack_glue_ratio: c",c);
+ gubed
+ pack_glue_ratio:=a+@'40*b+@'2000*c;
+end;
+
+procedure unpack_glue_ratio(g:glue_ratio; var a,b,c:integer);
+begin
+ c:=(g mod @'1000000000) div @'2000;
+ b:=(g mod @'2000) div @'40;
+ a:=g mod @'40;
+ @!debug
+ check_range(1,31,"unpack_glue_ratio: a",a);
+ check_range(0,30,"unpack_glue_ratio: b",b);
+ check_range(0,@'200000,"unpack_glue_ratio: c",c);
+ gubed
+ c:=c-@'100000;
+end;
+
+function glue_mult(@!x:scaled;@!g:glue_ratio):integer;
+var a,b,c:integer;
+begin
+unpack_glue_ratio(g,a,b,c);
+if a>16 then x:=x div two_to_the[a-16] {right shift by |a| places}
+else x:=x*two_to_the[16-a]; {left shift by |-a| places}
+glue_mult:=(x*c) div two_to_the[b]; {right shift by |b| places}
+ end; {note that |b| may be as large as 30}
+
+@*Glue setting.
+The |glue_fix| function computes |a|, |b|, and |c| by the method
+explained in {\sl TUGboat \bf3},1 (March 1982), 10--27. This
+implementation differs from the one given there in that it treats negative
+values of |s| properly. To do so, we allow |c| to range from -@'100000 to
+@'100000 and store it shifted by @'100000.
+
+|glue_fix| returns a |glue_ratio| approximation of |t/s|.
+
+@p function glue_fix(@!s,@!t,@!y:scaled):glue_ratio;
+var @!a,@!b,@!c:integer; {components of the desired ratio}
+@!k,@!h:integer; {$30-\lfloor\lg s\rfloor$, $30-\lfloor\lg t\rfloor$}
+@!s0:integer; {original (unnormalized) value of |s|}
+@!q,@!r,@!s1:integer; {quotient, remainder, divisor}
+@!w:integer; {$2^l$, where $l=16-k$}
+@!negative:boolean;
+begin
+negative:=false;
+if s<0 then begin
+ negative:=true;
+ s:=-s;
+end;
+if y=0 then begin
+ print("glue_fix: y=0. Why?");
+ glue_fix:=pack_glue_ratio(a+16,b,c);
+end else begin
+@<Normalize |s|, |t|, and |y|, computing |a|, |k|, and |h|@>;
+if t<s then b:=15-a-k+h@+else b:=14-a-k+h;
+if (b<0) or (b>30) then
+ begin if b<0 then
+ begin
+ print_err("Excessive glue");
+@.Excessive glue@>
+ help2("I can't work with sizes bigger than about 19 feet.")@/
+ ("Proceed, with fingers crossed.");@/
+ error;
+ end;
+ b:=0; c:=0; {make |f(x)| identically zero}
+ end
+else begin if k>=16 then {easy case, $s_0<2^{15}$}
+ c:=(t div two_to_the[h-a-b]+s0-1) div s0 {here |1<=h-a-b<=k-14<=16|}
+ else @<Compute |c| by long division@>;
+ end;
+if negative then c:=-c;
+glue_fix:=pack_glue_ratio(a+16,b,c);
+end;
+end;
+
+@ @<Normalize |s|, |t|, and |y|, computing |a|, |k|, and |h|@>=
+begin a:=15; k:=0; h:=0; s0:=s;
+while y<@'10000000000 do {|y| is known to be positive}
+ begin decr(a); y:=y+y;
+ end;
+while s<@'10000000000 do {|s| is known to be positive}
+ begin incr(k); s:=s+s;
+ end;
+while t<@'10000000000 do {|t| is known to be positive}
+ begin incr(h); t:=t+t;
+ end;
+end {now $2^{30}\le t=2^ht_0<2^{31}$ and $2^{30}\le s=2^ks_0<2^{31}$,
+ hence $d=k-h$ if $t/s<1$}
+
+@ @<Compute |c| by long division@>=
+begin w:=two_to_the[16-k];
+s1:=s0 div w;
+q:=t div s1;
+r:=((t mod s1)*w)-((s0 mod w)*q);
+if r>0 then
+ begin incr(q); r:=r-s0;
+ end
+else while r<=-s0 do
+ begin decr(q); r:=r+s0;
+ end;
+if a+b+k-h=15 then c:=(q+1) div 2 @+else c:=(q+3) div 4;
+end
+@z
+
+@x
+@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(round(unity*float(w.gr))); print_ln;@/
+@^real multiplication@>
+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
+@y
+@p procedure print_gr(@!g:glue_ratio); {prints a glue multiplier}
+var @!j:-29..31; {the amount to shift |c|}
+ a,b,c:integer;
+begin
+unpack_glue_ratio(g,a,b,c);
+j:=32-a-b;
+while j>15 do
+ begin print("2x"); decr(j); {indicate multiples of 2 for BIG cases}
+ end;
+if j<0 then print_scaled(c div two_to_the[-j]) {shift right}
+else print_scaled(c*two_to_the[j]); {shift left}
+end;
+
+@!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_gr(w.gr); 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
+@z
+
+@x
+@p procedure show_node_list(@!p:integer); {prints a node list symbolically}
+label exit;
+var n:integer; {the number of items already printed at this level}
+@!g:real; {a glue ratio, as a floating point number}
+@y
+@p procedure show_node_list(@!p:integer); {prints a node list symbolically}
+label exit;
+var n:integer; {the number of items already printed at this level}
+@!g:glue_ratio; {a glue ratio, as a floating point number}
+a,b,c,j:integer;
+@z
+
+@x
+@ The code will have to change in this place if |glue_ratio| is
+a structured type instead of an ordinary |real|. Note that this routine
+should avoid arithmetic errors even if the |glue_set| field holds an
+arbitrary random value. The following code assumes that a properly
+formed nonzero |real| number has absolute value $2^{20}$ or more when
+it is regarded as an integer; this precaution was adequate to prevent
+floating point underflow on the author's computer.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g:=float(glue_set(p));
+if (g<>float_constant(0))and(glue_sign(p)<>normal) then
+ begin print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
+ else if abs(g)>float_constant(20000) then
+ begin if g>float_constant(0) then print_char(">")
+ else print("< -");
+ print_glue(20000*unity,glue_order(p),0);
+ end
+ else print_glue(round(unity*g),glue_order(p),0);
+@^real multiplication@>
+ end
+@y
+@ The code has been changed here, since |glue_ratio| is no longer |real|.
+Note that this routine should avoid arithmetic errors even if the |glue_set|
+field holds an arbitrary random value.
+
+@<Display the value of |glue_set(p)|@>=
+if (glue_ratio_zero<>glue_set(p))and(glue_sign(p)<>normal) then begin
+ print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
+ else if abs(g)>float_constant(20000) then
+ begin end;
+ print_gr(glue_set(p));
+ case glue_order(p) of
+ normal: do_nothing;
+ fil: print("fil");
+ fill: print("fill");
+ filll: print("filll");
+ othercases print("foul")
+ endcases
+end;
+@z
+
+@x
+@!glue_temp:real; {glue value before rounding}
+@y
+@!glue_temp:scaled; {glue value before rounding}
+@z
+
+@x
+@ @d vet_glue(#)== glue_temp:=#;
+ if glue_temp>float_constant(1000000000) then
+ glue_temp:=float_constant(1000000000)
+ else if glue_temp<-float_constant(1000000000) then
+ glue_temp:=-float_constant(1000000000)
+@y
+@ @d vet_glue(#)== glue_temp:=#;
+ if glue_temp>1000000000 then glue_temp:=1000000000
+ else if glue_temp<-1000000000 then glue_temp:=-1000000000
+@z
+
+@x
+@<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_wd:=rule_wd+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_wd:=rule_wd-round(glue_temp);
+ end;
+ end;
+@y
+@<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin
+ vet_glue(glue_mult(stretch(g),glue_set(this_box)));
+ rule_wd:=rule_wd+glue_temp;
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin
+ vet_glue(glue_mult(shrink(g),glue_set(this_box)));
+ rule_wd:=rule_wd-glue_temp;
+ end;
+ end;
+@z
+
+@x
+@!glue_temp:real; {glue value before rounding}
+@y
+@!glue_temp:scaled; {glue value before rounding}
+@z
+
+@x
+@ @<Move down or output leaders@>=
+begin g:=glue_ptr(p); rule_ht:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_ht:=rule_ht+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_ht:=rule_ht-round(glue_temp);
+ end;
+ end;
+@y
+@ @<Move down or output leaders@>=
+begin g:=glue_ptr(p); rule_ht:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin
+ vet_glue(glue_mult(stretch(g),glue_set(this_box)));
+ rule_ht:=rule_ht+glue_temp;
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin
+ vet_glue(glue_mult(shrink(g),glue_set(this_box)));
+ rule_ht:=rule_ht-glue_temp;
+ end;
+ end;
+@z
+
+@x
+@!total_stretch, @!total_shrink: array[glue_ord] of scaled;
+ {glue found by |hpack| or |vpack|}
+@y
+@!total_stretch, @!total_shrink, @!max_stretch, @!max_shrink:
+ array[glue_ord] of scaled; {glue found by |hpack| or |vpack|}
+@z
+
+@x
+@ @<Clear dimensions to zero@>=
+d:=0; x:=0;
+total_stretch[normal]:=0; total_shrink[normal]:=0;
+total_stretch[fil]:=0; total_shrink[fil]:=0;
+total_stretch[fill]:=0; total_shrink[fill]:=0;
+total_stretch[filll]:=0; total_shrink[filll]:=0
+@y
+@ @<Clear dimensions to zero@>=
+d:=0; x:=0;
+total_stretch[normal]:=0; total_shrink[normal]:=0;
+total_stretch[fil]:=0; total_shrink[fil]:=0;
+total_stretch[fill]:=0; total_shrink[fill]:=0;
+total_stretch[filll]:=0; total_shrink[filll]:=0;
+max_stretch[normal]:=0; max_shrink[normal]:=0;
+max_stretch[fil]:=0; max_shrink[fil]:=0;
+max_stretch[fill]:=0; max_shrink[fill]:=0;
+max_stretch[filll]:=0; max_shrink[filll]:=0
+@z
+
+@x
+@ @<Incorporate glue into the horizontal totals@>=
+begin g:=glue_ptr(p); x:=x+width(g);@/
+o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
+o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
+@y
+@ @<Incorporate glue into the horizontal totals@>=
+begin g:=glue_ptr(p); x:=x+width(g);@/
+o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
+if abs(stretch(g))>max_stretch[o] then max_stretch[o]:=abs(stretch(g));
+o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
+if abs(shrink(g))>max_shrink[o] then max_shrink[o]:=abs(shrink(g));
+@z
+
+@x
+@ @<Determine horizontal glue stretch setting...@>=
+begin @<Determine the stretch order@>;
+glue_order(r):=o; glue_sign(r):=stretching;
+if total_stretch[o]<>0 then glue_set(r):=unfloat(x/total_stretch[o])
+@^real division@>
+else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to stretch}
+ end;
+@y
+@ @<Determine horizontal glue stretch setting...@>=
+begin @<Determine the stretch order@>;
+glue_order(r):=o; glue_sign(r):=stretching;
+if total_stretch[o]<>0 then
+ glue_set(r):=glue_fix(total_stretch[o],x,max_stretch[o])
+else begin glue_sign(r):=normal;
+ glue_set(r):=glue_ratio_zero; {there's nothing to stretch}
+ end;
+@z
+
+@x
+@ @<Determine horizontal glue shrink setting...@>=
+begin @<Determine the shrink order@>;
+glue_order(r):=o; glue_sign(r):=shrinking;
+if total_shrink[o]<>0 then glue_set(r):=unfloat((-x)/total_shrink[o])
+@^real division@>
+@y
+@ @<Determine horizontal glue shrink setting...@>=
+begin @<Determine the shrink order@>;
+glue_order(r):=o; glue_sign(r):=shrinking;
+if total_shrink[o]<>0 then
+ glue_set(r):=glue_fix(total_shrink[o],-x,max_shrink[o])
+@z
+
+@x
+@ @<Incorporate glue into the vertical totals@>=
+begin x:=x+d; d:=0;@/
+g:=glue_ptr(p); x:=x+width(g);@/
+o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
+o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
+@y
+@ @<Incorporate glue into the vertical totals@>=
+begin x:=x+d; d:=0;@/
+g:=glue_ptr(p); x:=x+width(g);@/
+o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
+if abs(stretch(g))>max_stretch[o] then max_stretch[o]:=abs(stretch(g));
+o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
+if abs(shrink(g))>max_shrink[o] then max_shrink[o]:=abs(shrink(g));
+@z
+
+@x
+@ @<Determine vertical glue stretch setting...@>=
+begin @<Determine the stretch order@>;
+glue_order(r):=o; glue_sign(r):=stretching;
+if total_stretch[o]<>0 then glue_set(r):=unfloat(x/total_stretch[o])
+@^real division@>
+@y
+@ @<Determine vertical glue stretch setting...@>=
+begin @<Determine the stretch order@>;
+glue_order(r):=o; glue_sign(r):=stretching;
+if total_stretch[o]<>0 then
+ glue_set(r):=glue_fix(total_stretch[o],x,max_stretch[o])
+@z
+
+@x
+@ @<Determine vertical glue shrink setting...@>=
+begin @<Determine the shrink order@>;
+glue_order(r):=o; glue_sign(r):=shrinking;
+if total_shrink[o]<>0 then glue_set(r):=unfloat((-x)/total_shrink[o])
+@^real division@>
+@y
+@ @<Determine vertical glue shrink setting...@>=
+begin @<Determine the shrink order@>;
+glue_order(r):=o; glue_sign(r):=shrinking;
+if total_shrink[o]<>0 then
+ glue_set(r):=glue_fix(total_shrink[o],-x,max_shrink[o])
+@z
+
+@x
+@ @<Append tabskip glue and an empty box to list |u|...@>=
+s:=link(s); v:=glue_ptr(s); link(u):=new_glue(v); u:=link(u);
+subtype(u):=tab_skip_code+1; t:=t+width(v);
+if glue_sign(p)=stretching then
+ begin if stretch_order(v)=glue_order(p) then
+ t:=t+round(float(glue_set(p))*stretch(v));
+@^real multiplication@>
+ end
+else if glue_sign(p)=shrinking then
+ begin if shrink_order(v)=glue_order(p) then
+ t:=t-round(float(glue_set(p))*shrink(v));
+ end;
+@y
+@ @<Append tabskip glue and an empty box to list |u|...@>=
+s:=link(s); v:=glue_ptr(s); link(u):=new_glue(v); u:=link(u);
+subtype(u):=tab_skip_code+1; t:=t+width(v);
+if glue_sign(p)=stretching then
+ begin if stretch_order(v)=glue_order(p) then
+ t:=t+glue_mult(stretch(v),glue_set(p));
+ end
+else if glue_sign(p)=shrinking then
+ begin if shrink_order(v)=glue_order(p) then
+ t:=t-glue_mult(shrink(v),glue_set(p));
+ end;
+@z
+
+@x
+@ @<Make the unset node |r| into an |hlist_node| of width |w|...@>=
+begin height(r):=height(q); depth(r):=depth(q);
+if t=width(r) then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ set_glue_ratio_zero(glue_set(r));
+ end
+else if t>width(r) then
+ begin glue_sign(r):=stretching;
+ if glue_stretch(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else glue_set(r):=unfloat((t-width(r))/glue_stretch(r));
+@^real division@>
+ end
+else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
+ if glue_shrink(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else if (glue_order(r)=normal)and(width(r)-t>glue_shrink(r)) then
+ set_glue_ratio_one(glue_set(r))
+ else glue_set(r):=unfloat((width(r)-t)/glue_shrink(r));
+ end;
+width(r):=w; type(r):=hlist_node;
+end
+@y
+@ @<Make the unset node |r| into an |hlist_node| of width |w|...@>=
+begin height(r):=height(q); depth(r):=depth(q);
+if t=width(r) then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ glue_set(r):=glue_ratio_zero;
+ end
+else if t>width(r) then
+ begin glue_sign(r):=stretching;
+ if glue_stretch(r)=0 then glue_set(r):=glue_ratio_zero
+ else
+ glue_set(r):=glue_fix(glue_stretch(r),t-width(r),abs(glue_stretch(r)/2));
+ end
+else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
+ if glue_shrink(r)=0 then glue_set(r):=glue_ratio_zero
+ else if (glue_order(r)=normal)and(width(r)-t>glue_shrink(r)) then
+ glue_set(r):=glue_ratio_one
+ else
+ glue_set(r):=glue_fix(glue_shrink(r),width(r)-t,abs(glue_shrink(r)/2));
+ end;
+width(r):=w; type(r):=hlist_node;
+end
+@z
+
+@x
+@ @<Make the unset node |r| into a |vlist_node| of height |w|...@>=
+begin width(r):=width(q);
+if t=height(r) then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ set_glue_ratio_zero(glue_set(r));
+ end
+else if t>height(r) then
+ begin glue_sign(r):=stretching;
+ if glue_stretch(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else glue_set(r):=unfloat((t-height(r))/glue_stretch(r));
+@^real division@>
+ end
+else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
+ if glue_shrink(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else if (glue_order(r)=normal)and(height(r)-t>glue_shrink(r)) then
+ set_glue_ratio_one(glue_set(r))
+ else glue_set(r):=unfloat((height(r)-t)/glue_shrink(r));
+ end;
+height(r):=w; type(r):=vlist_node;
+end
+@y
+@ @<Make the unset node |r| into a |vlist_node| of height |w|...@>=
+begin width(r):=width(q);
+if t=height(r) then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ glue_set(r):=glue_ratio_zero;
+ end
+else if t>height(r) then
+ begin glue_sign(r):=stretching;
+ if glue_stretch(r)=0 then glue_set(r):=glue_ratio_zero
+ else
+ glue_set(r):=glue_fix(glue_stretch(r),t-height(r),abs(glue_stretch(r)/2));
+ end
+else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
+ if glue_shrink(r)=0 then glue_set(r):=glue_ratio_zero
+ else if (glue_order(r)=normal)and(height(r)-t>glue_shrink(r)) then
+ glue_set(r):=glue_ratio_one
+ else
+ glue_set(r):=glue_fix(glue_shrink(r),height(r)-t,abs(glue_shrink(r)/2));
+ end;
+height(r):=w; type(r):=vlist_node;
+end
+@z
+
+@x
+procedure make_accent;
+var s,@!t: real; {amount of slant}
+@!p,@!q,@!r:pointer; {character, box, and kern nodes}
+@!f:internal_font_number; {relevant font}
+@!a,@!h,@!x,@!w,@!delta:scaled; {heights and widths, as explained above}
+@!i:four_quarters; {character information}
+begin scan_char_num; f:=cur_font; p:=new_character(f,cur_val);
+if p<>null then
+ begin x:=x_height(f); s:=slant(f)/float_constant(65536);
+@^real division@>
+@y
+procedure make_accent;
+var @!s,@!t:scaled; {amount of slant}
+@!p,@!q,@!r:pointer; {character, box, and kern nodes}
+@!f:internal_font_number; {relevant font}
+@!a,@!h,@!x,@!w,@!delta:scaled; {heights and widths, as explained above}
+@!i:four_quarters; {character information}
+begin scan_char_num; f:=cur_font; p:=new_character(f,cur_val);
+if p<>null then
+ begin x:=x_height(f); s:=slant(f);
+@z
+
+@x
+@<Append the accent with appropriate kerns...@>=
+begin t:=slant(f)/float_constant(65536);
+@^real division@>
+i:=char_info(f)(character(q));
+w:=char_width(f)(i); h:=char_height(f)(height_depth(i));
+if h<>x then {the accent must be shifted up or down}
+ begin p:=hpack(p,natural); shift_amount(p):=x-h;
+ end;
+delta:=round((w-a)/float_constant(2)+h*t-x*s);
+@^real multiplication@>
+@^real addition@>
+@y
+@<Append the accent with appropriate kerns...@>=
+begin t:=slant(f);
+i:=char_info(f)(character(q));
+w:=char_width(f)(i); h:=char_height(f)(height_depth(i));
+if h<>x then {the accent must be shifted up or down}
+ begin p:=hpack(p,natural); shift_amount(p):=x-h;
+ end;
+delta:=x_over_n(w-a,2)+xn_over_d(h,t,unity)-xn_over_d(x,s,unity);
+@z
+
diff --git a/systems/tex-extensions/clasen/glue/glue.tex b/systems/tex-extensions/clasen/glue/glue.tex
new file mode 100644
index 0000000000..d124bf32b0
--- /dev/null
+++ b/systems/tex-extensions/clasen/glue/glue.tex
@@ -0,0 +1,75 @@
+\documentclass[preprint]{ltugboat}
+\usepackage{shortvrb}
+\usepackage{url}
+
+\providecommand{\meta}[1]{\ensuremath{\langle\textit{#1}\rangle}}
+\makeatletter
+\providecommand{\eTeX}{$\m@th\varepsilon$-\TeX}
+\makeatother
+\hyphenation{pa-ra-me-ter}
+
+\emergencystretch=4mm
+\title{Purging \TeX\ from floating-point arithmetic}
+\author{Matthias Clasen}
+%\address{}
+\netaddress{maclas@gmx.de}
+\setcounter{page}{1}
+
+\begin{document}
+\maketitle
+\MakeShortVerb{\|}
+
+\section{Introduction}
+
+\TeX\ uses machine-dependent floating-point arithmetic for its
+internal glue computations. This is the reason why a (undoubtly very useful)
+general |\lastnode| command cannot be added to \TeX\ without risking
+the portability of documents.
+
+A long time ago, Knuth demonstrated how to implement \TeX's glue computations
+in fixed-point arithmetic\cite{Knuth:82,Knuth:91}. Knuth mentions that his
+code would have to be slightly generalized before it can be used in \TeX. To
+my knowledge, this has not been done before.
+
+After applying my change file, |tex.web| no longer contains any
+variables of type |real|, proving that I was successful in removing all
+floating-point arithmetic from \TeX. I have not implemented the (now possible)
+|\lastnode| primitive.
+
+\section{Implementation}
+
+The changes to \TeX{} the program sketched above are realized in the
+change file |glue.ch|, which is a rewrite of Knuth's |glue.web|.
+
+\section{Installation}
+
+ The change files are written for \TeX~3.1459. But it should need only
+ cosmetic changes to get them to work with other versions of \TeX3.
+
+ To make the change files for \TeX{} work, you have to apply the change file
+ |pre.ch| first. I have only tried these files with |web2c|, and
+ for that you need to surround the |web2c| change file with |pre-web2c.ch| and
+ |post-web2c.ch|. You also need the additional change file
+ |glue-post-web2c.ch| and the patch file |glueratio.patch|.
+ The patch file is necessary, since |web2c| fixes the type |glueratio|
+ to be |double| in a file which is not subject to the |web| translation
+ process. The patch file changes this to the new type |glueratio| if
+ the |cpp| macro |HAVE_GLUERATIO_TYPE| is defined.
+
+ All this boils down to the command line
+
+\begin{verbatim}
+mv tex.ch tex.ch.orig
+tie -c tex.ch tex.web pre.ch glue.ch \
+ pre-web2c.ch tex.ch.orig \
+ post-web2c.ch glue-post-web2c.ch
+patch < glueratio.ch
+export XCFLAGS=-DHAVE_GLUERATIO_TYPE
+\end{verbatim}
+
+ \makesignature
+
+ \bibliographystyle{plain}
+ \bibliography{etex}
+
+\end{document}
diff --git a/systems/tex-extensions/clasen/glue/glueratio.patch b/systems/tex-extensions/clasen/glue/glueratio.patch
new file mode 100644
index 0000000000..d8064e18a7
--- /dev/null
+++ b/systems/tex-extensions/clasen/glue/glueratio.patch
@@ -0,0 +1,15 @@
+--- texmfmp.h.orig Thu Jul 23 18:36:40 1998
++++ texmfmp.h Thu Jul 23 18:37:21 1998
+@@ -86,10 +86,12 @@
+
+ This type is set automatically to `float' by configure if a small TeX
+ is built. */
++#ifndef HAVE_GLUERATIO_TYPE
+ #ifndef GLUERATIO_TYPE
+ #define GLUERATIO_TYPE double
+ #endif
+ typedef GLUERATIO_TYPE glueratio;
++#endif
+
+ extern void setupcharset P1H(void);
+