*** tex.web.orig	Mon Sep 14 23:03:05 1998
--- tex.web	Sun Sep 20 10:00:49 1998
***************
*** 184,190 ****
  known as `\TeX' [cf.~Stanford Computer Science report CS1027,
  November 1984].
  
! @d banner=='This is TeX, Version 3.14159' {printed when \TeX\ starts}
  
  @ Different \PASCAL s have slightly different conventions, and the present
  @!@:PASCAL H}{\ph@>
--- 184,190 ----
  known as `\TeX' [cf.~Stanford Computer Science report CS1027,
  November 1984].
  
! @d banner=='This is *not* TeX, Version 3.14159' {printed when \TeX\ starts}
  
  @ Different \PASCAL s have slightly different conventions, and the present
  @!@:PASCAL H}{\ph@>
***************
*** 4951,4957 ****
  @d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
  @d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
  @d error_context_lines_code=54 {maximum intermediate line pairs shown}
! @d int_pars=55 {total number of integer parameters}
  @d count_base=int_base+int_pars {256 user \.{\\count} registers}
  @d del_code_base=count_base+256 {256 delimiter code mappings}
  @d dimen_base=del_code_base+256 {beginning of region 6}
--- 4951,4961 ----
  @d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
  @d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
  @d error_context_lines_code=54 {maximum intermediate line pairs shown}
! @d glue_overshrink_code=55 { 1000 times the amount by which to overshrink}
! @d extra_adj_demerits_code=56 { additional demerits for jumping > 2 }
! @d font_demerits_code=57 
! @d extra_font_demerits_code=58 
! @d int_pars=59 {total number of integer parameters}
  @d count_base=int_base+int_pars {256 user \.{\\count} registers}
  @d del_code_base=count_base+256 {256 delimiter code mappings}
  @d dimen_base=del_code_base+256 {beginning of region 6}
***************
*** 5014,5019 ****
--- 5018,5028 ----
  @d right_hyphen_min==int_par(right_hyphen_min_code)
  @d holding_inserts==int_par(holding_inserts_code)
  @d error_context_lines==int_par(error_context_lines_code)
+ @d glue_overshrink==int_par(glue_overshrink_code)
+ @d extra_adj_demerits==int_par(extra_adj_demerits_code)
+ @d font_demerits==int_par(font_demerits_code)
+ @d extra_font_demerits==int_par(extra_font_demerits_code)
+ 
  
  @<Assign the values |depth_threshold:=show_box_depth|...@>=
  depth_threshold:=show_box_depth;
***************
*** 5078,5083 ****
--- 5087,5096 ----
  right_hyphen_min_code:print_esc("righthyphenmin");
  holding_inserts_code:print_esc("holdinginserts");
  error_context_lines_code:print_esc("errorcontextlines");
+ glue_overshrink_code:print_esc("glueovershrink");
+ extra_adj_demerits_code:print_esc("extraadjdemerits");
+ font_demerits_code:print_esc("fontdemerits");
+ extra_font_demerits_code:print_esc("extrafontdemerits");
  othercases print("[unknown integer parameter!]")
  endcases;
  end;
***************
*** 5198,5203 ****
--- 5211,5224 ----
  @!@:holding_inserts_}{\.{\\holdinginserts} primitive@>
  primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/
  @!@:error_context_lines_}{\.{\\errorcontextlines} primitive@>
+ primitive("glueovershrink",assign_int,int_base+glue_overshrink_code);@/
+ @!@:glue_overshrink_}{\.{\\glueovershrink} primitive@>
+ primitive("extraadjdemerits",assign_int,int_base+extra_adj_demerits_code);@/
+ @!@:extra_adj_demerits_}{\.{\\extraadjdemerits} primitive@>
+ primitive("fontdemerits",assign_int,int_base+font_demerits_code);@/
+ @!@:font_demerits_}{\.{\\fontdemerits} primitive@>
+ primitive("extrafontdemerits",assign_int,int_base+extra_font_demerits_code);@/
+ @!@:extra_font_demerits_}{\.{\\extrafontdemerits} primitive@>
  
  @ @<Cases of |print_cmd_chr|...@>=
  assign_int: if chr_code<count_base then print_param(chr_code-int_base)
***************
*** 5658,5663 ****
--- 5679,5686 ----
  @!@:expand_after_}{\.{\\expandafter} primitive@>
  primitive("font",def_font,0);@/
  @!@:font_}{\.{\\font} primitive@>
+ primitive("fontvariant",def_font,1);@/
+ @!@:font_variant_}{\.{\\fontvariant} primitive@>
  primitive("fontdimen",assign_font_dimen,0);@/
  @!@:font_dimen_}{\.{\\fontdimen} primitive@>
  primitive("halign",halign,0);@/
***************
*** 5732,5738 ****
  break_penalty: print_esc("penalty");
  char_num: print_esc("char");
  cs_name: print_esc("csname");
! def_font: print_esc("font");
  delim_num: print_esc("delimiter");
  divide: print_esc("divide");
  end_cs_name: print_esc("endcsname");
--- 5755,5761 ----
  break_penalty: print_esc("penalty");
  char_num: print_esc("char");
  cs_name: print_esc("csname");
! def_font: if chr_code=0 then print_esc("font") else print_esc("fontvariant");
  delim_num: print_esc("delimiter");
  divide: print_esc("divide");
  end_cs_name: print_esc("endcsname");
***************
*** 8376,8384 ****
      end;
    scanned_result(equiv(m))(tok_val);
    end
! else  begin back_input; scan_font_ident;
    scanned_result(font_id_base+cur_val)(ident_val);
!   end
  
  @ Users refer to `\.{\\the\\spacefactor}' only in horizontal
  mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
--- 8399,8411 ----
      end;
    scanned_result(equiv(m))(tok_val);
    end
! else if m=0 then begin 
!   back_input; scan_font_ident;
    scanned_result(font_id_base+cur_val)(ident_val);
! end else begin
!   print_err("Improper "); print_cmd_chr(def_font, m);
!   error;
! end 
  
  @ Users refer to `\.{\\the\\spacefactor}' only in horizontal
  mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
***************
*** 10672,10677 ****
--- 10699,10706 ----
  @<Types...@>=
  @!internal_font_number=font_base..font_max; {|font| in a |char_node|}
  @!font_index=0..font_mem_size; {index into |font_info|}
+ @!four_fonts = packed array[0..3] of quarterword; 
+       {a version of |four_quarters| with addressable quarters}
  
  @ Here now is the (rather formidable) array of font arrays.
  
***************
*** 10709,10714 ****
--- 10738,10745 ----
    {right boundary character, |non_char| if there is none}
  @!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
    {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
+ @!font_variants:array[internal_font_number] of four_fonts; 
+   {we use four variants per font}
  
  @ Besides the arrays just enumerated, we have directory arrays that make it
  easy to get at the individual entries in |font_info|. For example, the
***************
*** 10760,10765 ****
--- 10791,10797 ----
  font_glue[null_font]:=null; font_params[null_font]:=7;
  param_base[null_font]:=-1;
  for k:=0 to 6 do font_info[k].sc:=0;
+ for k:=0 to 3 do font_variants[null_font][k]:=null_font;
  
  @ @<Put each...@>=
  primitive("nullfont",set_font,null_font);
***************
*** 12854,12865 ****
  
  @< Glob...@>=
  @!adjust_tail:pointer; {tail of adjustment list}
  
  @ @<Set init...@>=adjust_tail:=null; last_badness:=0;
  
  @ Here now is |hpack|, which contains few if any surprises.
  
! @p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer;
  label reswitch, common_ending, exit;
  var r:pointer; {the box node that will be returned}
  @!q:pointer; {trails behind |p|}
--- 12886,12905 ----
  
  @< Glob...@>=
  @!adjust_tail:pointer; {tail of adjustment list}
+ @!line_font_variant:small_number; {used for communication between 
+    |line_break| and |hpack|}
  
  @ @<Set init...@>=adjust_tail:=null; last_badness:=0;
  
  @ Here now is |hpack|, which contains few if any surprises.
  
! @p function effective_glue_overshrink : integer;
! begin
!   if glue_overshrink<1000 then effective_glue_overshrink:=1000
!   else effective_glue_overshrink:=glue_overshrink;
! end;
! 
! function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer;
  label reswitch, common_ending, exit;
  var r:pointer; {the box node that will be returned}
  @!q:pointer; {trails behind |p|}
***************
*** 12870,12875 ****
--- 12910,12916 ----
  @!f:internal_font_number; {the font in a |char_node|}
  @!i:four_quarters; {font information about a |char_node|}
  @!hd:eight_bits; {height and depth indices for a character}
+ @!max_shrink:scaled; 
  begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
  subtype(r):=min_quarterword; shift_amount(r):=0;
  q:=r+list_offset; link(q):=p;@/
***************
*** 12937,12945 ****
  character of text to the user's input will cause each of these instructions
  to be exercised one more time.
  @^inner loop@>
  
  @<Incorporate character dimensions into the dimensions of the hbox...@>=
! begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
  x:=x+char_width(f)(i);@/
  s:=char_height(f)(hd);@+if s>h then h:=s;
  s:=char_depth(f)(hd);@+if s>d then d:=s;
--- 12978,12998 ----
  character of text to the user's input will cause each of these instructions
  to be exercised one more time.
  @^inner loop@>
+ This is also the place where the proper font variants are selected.
+ 
+ @d very_condensed_font=0
+ @d condensed_font=1
+ @d normal_font=2
+ @d extended_font=3
+ @d very_extended_font=4
  
  @<Incorporate character dimensions into the dimensions of the hbox...@>=
! begin 
! if line_font_variant<normal_font then
!   font(p):=font_variants[font(p)][line_font_variant]
! else if line_font_variant>normal_font then
!   font(p):=font_variants[font(p)][line_font_variant-1];
! f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
  x:=x+char_width(f)(i);@/
  s:=char_height(f)(hd);@+if s>h then h:=s;
  s:=char_depth(f)(hd);@+if s>d then d:=s;
***************
*** 13036,13049 ****
  pack_begin_line:=0;
  
  @ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
! if output_active then print(") has occurred while \output is active")
  else  begin if pack_begin_line<>0 then
!     begin if pack_begin_line>0 then print(") in paragraph at lines ")
!     else print(") in alignment at lines ");
      print_int(abs(pack_begin_line));
      print("--");
      end
!   else print(") detected at line ");
    print_int(line);
    end;
  print_ln;@/
--- 13089,13109 ----
  pack_begin_line:=0;
  
  @ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
! case line_font_variant of
!      very_condensed_font: print(") (very condensed)");
!      condensed_font: print(") (condensed)");
!      normal_font: print(") (normal)");
!      extended_font: print(") (extended)");
!      very_extended_font: print(") (very extended)");
! endcases;
! if output_active then print(" has occurred while \output is active")
  else  begin if pack_begin_line<>0 then
!     begin if pack_begin_line>0 then print(" in paragraph at lines ")
!     else print(" in alignment at lines ");
      print_int(abs(pack_begin_line));
      print("--");
      end
!   else print(" detected at line ");
    print_int(line);
    end;
  print_ln;@/
***************
*** 13058,13066 ****
  else  begin glue_sign(r):=normal;
    set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
    end;
! if (total_shrink[o]<-x)and(o=normal)and(list_ptr(r)<>null) then
    begin last_badness:=1000000;
!   set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
    @<Report an overfull hbox and |goto common_ending|, if this box
      is sufficiently bad@>;
    end
--- 13118,13127 ----
  else  begin glue_sign(r):=normal;
    set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
    end;
! max_shrink:=xn_over_d(total_shrink[o],effective_glue_overshrink,1000);
! if (max_shrink<-x)and(o=normal)and(list_ptr(r)<>null) then
    begin last_badness:=1000000;
!   glue_set(r):=unfloat(effective_glue_overshrink/1000);
    @<Report an overfull hbox and |goto common_ending|, if this box
      is sufficiently bad@>;
    end
***************
*** 13077,13091 ****
  else o:=normal
  
  @ @<Report an overfull hbox and |goto common_ending|, if...@>=
! if (-x-total_shrink[normal]>hfuzz)or(hbadness<100) then
!   begin if (overfull_rule>0)and(-x-total_shrink[normal]>hfuzz) then
      begin while link(q)<>null do q:=link(q);
      link(q):=new_rule;
      width(link(q)):=overfull_rule;
      end;
    print_ln; print_nl("Overfull \hbox (");
  @.Overfull \\hbox...@>
!   print_scaled(-x-total_shrink[normal]); print("pt too wide");
    goto common_ending;
    end
  
--- 13138,13152 ----
  else o:=normal
  
  @ @<Report an overfull hbox and |goto common_ending|, if...@>=
! if (-x-max_shrink>hfuzz)or(hbadness<100) then
!   begin if (overfull_rule>0)and(-x-max_shrink>hfuzz) then
      begin while link(q)<>null do q:=link(q);
      link(q):=new_rule;
      width(link(q)):=overfull_rule;
      end;
    print_ln; print_nl("Overfull \hbox (");
  @.Overfull \\hbox...@>
!   print_scaled(-x-max_shrink); print("pt too wide");
    goto common_ending;
    end
  
***************
*** 16038,16056 ****
  @ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
  each break that is {\sl feasible}, in the sense that there is a way to end
  a line at the given place without requiring any line to stretch more than
! a given tolerance. A break node is characterized by three things: the position
  of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
  or |disc_node|); the ordinal number of the line that will follow this
! breakpoint; and the fitness classification of the line that has just
! ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
! 
! @d tight_fit=3 {fitness classification for lines shrinking 0.5 to 1.0 of their
    shrinkability}
! @d loose_fit=1 {fitness classification for lines stretching 0.5 to 1.0 of their
    stretchability}
! @d very_loose_fit=0 {fitness classification for lines stretching more than
    their stretchability}
! @d decent_fit=2 {fitness classification for all other lines}
  
  @ The algorithm essentially determines the best possible way to achieve
  each feasible combination of position, line, and fitness. Thus, it answers
--- 16099,16124 ----
  @ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
  each break that is {\sl feasible}, in the sense that there is a way to end
  a line at the given place without requiring any line to stretch more than
! a given tolerance. A break node is characterized by four things: the position
  of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
  or |disc_node|); the ordinal number of the line that will follow this
! breakpoint, the fitness classification of the line that has just
! ended, i.e., |very_tight_fit|, |tight_fit|, |decent_fit|, 
! |loose_fit|, |very_loose_fit|, or |extremely_loose_fit|, and the font variant
! to be used, |very_condensed_font|, |condensed_font|, |normal_font|,
! |extended_font| or |very_extended_font|.
! 
! @d very_tight_fit=5 {fitness classification for lines shrinking more than their
!   shrinkability} 
! @d tight_fit=4 {fitness classification for lines shrinking 0.5 to 1.0 of their
    shrinkability}
! @d decent_fit=3 {fitness classification for all other lines}
! @d loose_fit=2 {fitness classification for lines stretching 0.5 to 1.0 of their
    stretchability}
! @d very_loose_fit=1 {fitness classification for lines stretching 1.0 to 2.0 of
    their stretchability}
! @d extremely_loose_fit=0 {fitness classification for lines stretching more 
!   than 2.0 of their stretchability}
  
  @ The algorithm essentially determines the best possible way to achieve
  each feasible combination of position, line, and fitness. Thus, it answers
***************
*** 16063,16069 ****
  
  An ``active node'' and a ``passive node'' are created in |mem| for each
  feasible breakpoint that needs to be considered. Active nodes are three
! words long and passive nodes are two words long. We need active nodes only
  for breakpoints near the place in the paragraph that is currently being
  examined, so they are recycled within a comparatively short time after
  they are created.
--- 16131,16137 ----
  
  An ``active node'' and a ``passive node'' are created in |mem| for each
  feasible breakpoint that needs to be considered. Active nodes are three
! words long and passive nodes are too. We need active nodes only
  for breakpoints near the place in the paragraph that is currently being
  examined, so they are recycled within a comparatively short time after
  they are created.
***************
*** 16095,16101 ****
  to each other.
  
  @d active_node_size=3 {number of words in active nodes}
! @d fitness==subtype {|very_loose_fit..tight_fit| on final line for this break}
  @d break_node==rlink {pointer to the corresponding passive node}
  @d line_number==llink {line that begins at this breakpoint}
  @d total_demerits(#)==mem[#+2].int {the quantity that \TeX\ minimizes}
--- 16163,16170 ----
  to each other.
  
  @d active_node_size=3 {number of words in active nodes}
! @d fitness==subtype {|extremely_loose_fit..very_tight_fit| on final line for this
! break} 
  @d break_node==rlink {pointer to the corresponding passive node}
  @d line_number==llink {line that begins at this breakpoint}
  @d total_demerits(#)==mem[#+2].int {the quantity that \TeX\ minimizes}
***************
*** 16107,16113 ****
  type(last_active):=hyphenated; line_number(last_active):=max_halfword;
  subtype(last_active):=0; {the |subtype| is never examined by the algorithm}
  
! @ The passive node for a given breakpoint contains only four fields:
  
  \yskip\hang|link| points to the passive node created just before this one,
  if any, otherwise it is |null|.
--- 16176,16182 ----
  type(last_active):=hyphenated; line_number(last_active):=max_halfword;
  subtype(last_active):=0; {the |subtype| is never examined by the algorithm}
  
! @ The passive node for a given breakpoint contains only five fields:
  
  \yskip\hang|link| points to the passive node created just before this one,
  if any, otherwise it is |null|.
***************
*** 16122,16137 ****
  one created during the current pass. (This field is used only when
  printing out detailed statistics about the line-breaking calculations.)
  
  \yskip\noindent
  There is a global variable called |passive| that points to the most
  recently created passive node. Another global variable, |printed_node|,
  is used to help print out the paragraph when detailed information about
  the line-breaking computation is being displayed.
  
! @d passive_node_size=2 {number of words in passive nodes}
  @d cur_break==rlink {in passive node, points to position of this breakpoint}
  @d prev_break==llink {points to passive node that should precede this one}
  @d serial==info {serial number for symbolic identification}
  
  @<Glob...@>=
  @!passive:pointer; {most recent node on passive list}
--- 16191,16211 ----
  one created during the current pass. (This field is used only when
  printing out detailed statistics about the line-breaking calculations.)
  
+ \yskip\hang|condensedness| is the font variant to be used. We store this
+ information in passive nodes rather than active ones, since it has to
+ stay around till the packaging, when all active nodes are long gone.
+ 
  \yskip\noindent
  There is a global variable called |passive| that points to the most
  recently created passive node. Another global variable, |printed_node|,
  is used to help print out the paragraph when detailed information about
  the line-breaking computation is being displayed.
  
! @d passive_node_size=3 {number of words in passive nodes}
  @d cur_break==rlink {in passive node, points to position of this breakpoint}
  @d prev_break==llink {points to passive node that should precede this one}
  @d serial==info {serial number for symbolic identification}
+ @d condensedness(#)==mem[#+2].int
  
  @<Glob...@>=
  @!passive:pointer; {most recent node on passive list}
***************
*** 16153,16183 ****
  pt, fil, fill, and filll appear in |mem[q+2..q+5].sc|; and the shrink difference
  appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
  
! @d delta_node_size=7 {number of words in a delta node}
  @d delta_node=2 {|type| field in a delta node}
  
! @ As the algorithm runs, it maintains a set of six delta-like registers
  for the length of the line following the first active breakpoint to the
  current position in the given hlist. When it makes a pass through the
! active list, it also maintains a similar set of six registers for the
  length following the active breakpoint of current interest. A third set
  holds the length of an empty line (namely, the sum of \.{\\leftskip} and
  \.{\\rightskip}); and a fourth set is used to create new delta nodes.
  
  When we pass a delta node we want to do operations like
  $$\hbox{\ignorespaces|for
! k:=1 to 6 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
! want to do this without the overhead of |for| loops. The |do_all_six|
! macro makes such six-tuples convenient.
  
  @d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6)
  
  @<Glo...@>=
! @!active_width:array[1..6] of scaled;
    {distance from first active node to~|cur_p|}
! @!cur_active_width:array[1..6] of scaled; {distance from current active node}
! @!background:array[1..6] of scaled; {length of an ``empty'' line}
! @!break_width:array[1..6] of scaled; {length being computed after current break}
  
  @ Let's state the principles of the delta nodes more precisely and concisely,
  so that the following programs will be less obscure. For each legal
--- 16227,16261 ----
  pt, fil, fill, and filll appear in |mem[q+2..q+5].sc|; and the shrink difference
  appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
  
! Delta nodes have four additional fields keeping track of the
! difference in natural width for the font variants.
! 
! @d delta_node_size=11 {number of words in a delta node}
  @d delta_node=2 {|type| field in a delta node}
  
! @ As the algorithm runs, it maintains a set of ten delta-like registers
  for the length of the line following the first active breakpoint to the
  current position in the given hlist. When it makes a pass through the
! active list, it also maintains a similar set of ten registers for the
  length following the active breakpoint of current interest. A third set
  holds the length of an empty line (namely, the sum of \.{\\leftskip} and
  \.{\\rightskip}); and a fourth set is used to create new delta nodes.
  
  When we pass a delta node we want to do operations like
  $$\hbox{\ignorespaces|for
! k:=1 to 10 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
! want to do this without the overhead of |for| loops. The |do_all_ten|
! macro makes such ten-tuples convenient. |do_all_six| is also needed.
  
  @d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6)
+ @d do_all_ten(#)==#(1);#(2);#(3);#(4);#(5);#(6);#(7);#(8);#(9);#(10)
  
  @<Glo...@>=
! @!active_width:array[1..10] of scaled;
    {distance from first active node to~|cur_p|}
! @!cur_active_width:array[1..10] of scaled; {distance from current active node}
! @!background:array[1..10] of scaled; {length of an ``empty'' line}
! @!break_width:array[1..10] of scaled; {length being computed after current break}
  
  @ Let's state the principles of the delta nodes more precisely and concisely,
  so that the following programs will be less obscure. For each legal
***************
*** 16258,16263 ****
--- 16336,16345 ----
  background[2+stretch_order(q)]:=stretch(q);@/
  background[2+stretch_order(r)]:=@|background[2+stretch_order(r)]+stretch(r);@/
  background[6]:=shrink(q)+shrink(r);
+ background[7]:=0;
+ background[8]:=0;
+ background[9]:=0;
+ background[10]:=0;
  
  @ A pointer variable |cur_p| runs through the given horizontal list as we look
  for breakpoints. This variable is global, since it is used both by |line_break|
***************
*** 16316,16322 ****
  @<Other local variables for |try_break|@>@;
  begin @<Make sure that |pi| is in the proper range@>;
  no_break_yet:=true; prev_r:=active; old_l:=0;
! do_all_six(copy_to_cur_active);
  loop@+  begin continue: r:=link(prev_r);
    @<If node |r| is of type |delta_node|, update |cur_active_width|,
      set |prev_r| and |prev_prev_r|, then |goto continue|@>;
--- 16398,16404 ----
  @<Other local variables for |try_break|@>@;
  begin @<Make sure that |pi| is in the proper range@>;
  no_break_yet:=true; prev_r:=active; old_l:=0;
! do_all_ten(copy_to_cur_active);
  loop@+  begin continue: r:=link(prev_r);
    @<If node |r| is of type |delta_node|, update |cur_active_width|,
      set |prev_r| and |prev_prev_r|, then |goto continue|@>;
***************
*** 16341,16348 ****
  @!f:internal_font_number; {used in character width calculation}
  @!l:halfword; {line number of current active node}
  @!node_r_stays_active:boolean; {should node |r| remain in the active list?}
  @!line_width:scaled; {the current line will be justified to this width}
! @!fit_class:very_loose_fit..tight_fit; {possible fitness class of test line}
  @!b:halfword; {badness of test line}
  @!d:integer; {demerits of test line}
  @!artificial_demerits:boolean; {has |d| been forced to zero?}
--- 16423,16435 ----
  @!f:internal_font_number; {used in character width calculation}
  @!l:halfword; {line number of current active node}
  @!node_r_stays_active:boolean; {should node |r| remain in the active list?}
+ @!do_not_consider:boolean; 
  @!line_width:scaled; {the current line will be justified to this width}
! @!fit_class:extremely_loose_fit..very_tight_fit; {possible fitness class of
! test line}
! @!condensed_class,prev_cc:very_condensed_font..very_extended_font;
! @!cw:scaled;
! @!j:small_number;
  @!b:halfword; {badness of test line}
  @!d:integer; {demerits of test line}
  @!artificial_demerits:boolean; {has |d| been forced to zero?}
***************
*** 16362,16368 ****
  @<If node |r|...@>=
  @^inner loop@>
  if type(r)=delta_node then
!   begin do_all_six(update_width);
    prev_prev_r:=prev_r; prev_r:=r; goto continue;
    end
  
--- 16449,16455 ----
  @<If node |r|...@>=
  @^inner loop@>
  if type(r)=delta_node then
!   begin do_all_ten(update_width);
    prev_prev_r:=prev_r; prev_r:=r; goto continue;
    end
  
***************
*** 16381,16401 ****
  @d awful_bad==@'7777777777 {more than a billion demerits}
  
  @<Global...@>=
! @!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total
!   demerits known for current line class and position, given the fitness}
  @!minimum_demerits:integer; {best total demerits known for current line class
    and position}
! @!best_place:array[very_loose_fit..tight_fit] of pointer; {how to achieve
    |minimal_demerits|}
! @!best_pl_line:array[very_loose_fit..tight_fit] of halfword; {corresponding
    line number}
  
  @ @<Get ready to start...@>=
  minimum_demerits:=awful_bad;
! minimal_demerits[tight_fit]:=awful_bad;
! minimal_demerits[decent_fit]:=awful_bad;
! minimal_demerits[loose_fit]:=awful_bad;
! minimal_demerits[very_loose_fit]:=awful_bad;
  
  @ The first part of the following code is part of \TeX's inner loop, so
  we don't want to waste any time. The current active node, namely node |r|,
--- 16468,16490 ----
  @d awful_bad==@'7777777777 {more than a billion demerits}
  
  @<Global...@>=
! @!minimal_demerits:array[extremely_loose_fit..very_tight_fit,
!                          very_condensed_font..very_extended_font] of integer; {best total
!   demerits known for current line class and position, given the fitness and condensedness}
  @!minimum_demerits:integer; {best total demerits known for current line class
    and position}
! @!best_place:array[extremely_loose_fit..very_tight_fit,
! 		   very_condensed_font..very_extended_font] of pointer; {how to achieve
    |minimal_demerits|}
! @!best_pl_line:array[extremely_loose_fit..very_tight_fit,
!                      very_condensed_font..very_extended_font] of halfword; {corresponding
    line number}
  
  @ @<Get ready to start...@>=
  minimum_demerits:=awful_bad;
! for l:=extremely_loose_fit to very_tight_fit do
!   for j:=very_condensed_font to very_extended_font do
!     minimal_demerits[l][j]:=awful_bad;
  
  @ The first part of the following code is part of \TeX's inner loop, so
  we don't want to waste any time. The current active node, namely node |r|,
***************
*** 16429,16439 ****
  if abs(adj_demerits)>=awful_bad-minimum_demerits then
    minimum_demerits:=awful_bad-1
  else minimum_demerits:=minimum_demerits+abs(adj_demerits);
! for fit_class:=very_loose_fit to tight_fit do
!   begin if minimal_demerits[fit_class]<=minimum_demerits then
      @<Insert a new active node
        from |best_place[fit_class]| to |cur_p|@>;
!   minimal_demerits[fit_class]:=awful_bad;
    end;
  minimum_demerits:=awful_bad;
  @<Insert a delta node to prepare for the next active node@>;
--- 16518,16529 ----
  if abs(adj_demerits)>=awful_bad-minimum_demerits then
    minimum_demerits:=awful_bad-1
  else minimum_demerits:=minimum_demerits+abs(adj_demerits);
! for fit_class:=extremely_loose_fit to very_tight_fit do
!   for condensed_class:=very_condensed_font to very_extended_font do
!     begin if minimal_demerits[fit_class][condensed_class]<=minimum_demerits then
      @<Insert a new active node
        from |best_place[fit_class]| to |cur_p|@>;
!   minimal_demerits[fit_class][condensed_class]:=awful_bad;
    end;
  minimum_demerits:=awful_bad;
  @<Insert a delta node to prepare for the next active node@>;
***************
*** 16461,16467 ****
  @d set_break_width_to_background(#)==break_width[#]:=background[#]
  
  @<Compute the values of |break...@>=
! begin no_break_yet:=false; do_all_six(set_break_width_to_background);
  s:=cur_p;
  if break_type>unhyphenated then if cur_p<>null then
    @<Compute the discretionary |break_width| values@>;
--- 16551,16557 ----
  @d set_break_width_to_background(#)==break_width[#]:=background[#]
  
  @<Compute the values of |break...@>=
! begin no_break_yet:=false; do_all_ten(set_break_width_to_background);
  s:=cur_p;
  if break_type>unhyphenated then if cur_p<>null then
    @<Compute the discretionary |break_width| values@>;
***************
*** 16524,16536 ****
  @<Subtract the width of node |v|...@>=
  if is_char_node(v) then
    begin f:=font(v);
!   break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v)));
!   end
! else  case type(v) of
    ligature_node: begin f:=font(lig_char(v));@/
!     break_width[1]:=@|break_width[1]-
!       char_width(f)(char_info(f)(character(lig_char(v))));
!     end;
    hlist_node,vlist_node,rule_node,kern_node:
      break_width[1]:=break_width[1]-width(v);
    othercases confusion("disc1")
--- 16614,16638 ----
  @<Subtract the width of node |v|...@>=
  if is_char_node(v) then
    begin f:=font(v);
!   cw:=char_width(f)(char_info(f)(character(v)));
!   break_width[1]:=break_width[1]-cw;
!   for j:=0 to 3 do 
!     break_width[7+j]:=break_width[7+j]
!                   -(char_width(font_variants[f][j])
!                               (char_info(font_variants[f][j])
!                                         (character(v)))
!                     -cw);
! end else  case type(v) of
    ligature_node: begin f:=font(lig_char(v));@/
!     cw:=char_width(f)(char_info(f)(character(lig_char(v))));
!     break_width[1]:=@|break_width[1]-cw;
!     for j:=0 to 3 do 
!       break_width[7+j]:=break_width[7+j]
!                       -(char_width(font_variants[f][j])
!                                   (char_info(font_variants[f][j])
!                                             (character(lig_char(v))))
!                         -cw);
!   end;
    hlist_node,vlist_node,rule_node,kern_node:
      break_width[1]:=break_width[1]-width(v);
    othercases confusion("disc1")
***************
*** 16540,16552 ****
  @ @<Add the width of node |s| to |b...@>=
  if is_char_node(s) then
    begin f:=font(s);
!   break_width[1]:=@|break_width[1]+char_width(f)(char_info(f)(character(s)));
!   end
! else  case type(s) of
    ligature_node: begin f:=font(lig_char(s));
!     break_width[1]:=break_width[1]+
!       char_width(f)(char_info(f)(character(lig_char(s))));
!     end;
    hlist_node,vlist_node,rule_node,kern_node:
      break_width[1]:=break_width[1]+width(s);
    othercases confusion("disc2")
--- 16642,16666 ----
  @ @<Add the width of node |s| to |b...@>=
  if is_char_node(s) then
    begin f:=font(s);
!   cw:=char_width(f)(char_info(f)(character(s)));
!   break_width[1]:=@|break_width[1]+cw;
!   for j:=0 to 3 do 
!     break_width[7+j]:=break_width[7+j]
!                   +(char_width(font_variants[f][j])
!                               (char_info(font_variants[f][j])
!                                         (character(s)))
!                     -cw);
! end else  case type(s) of
    ligature_node: begin f:=font(lig_char(s));
!     cw:=char_width(f)(char_info(f)(character(lig_char(s))));
!     break_width[1]:=break_width[1]+cw;  
!     for j:=0 to 3 do 
!       break_width[7+j]:=break_width[7+j]
!                       +(char_width(font_variants[f][j])
!                                   (char_info(font_variants[f][j])
!                                             (character(lig_char(s))))
!                         -cw);
!   end;
    hlist_node,vlist_node,rule_node,kern_node:
      break_width[1]:=break_width[1]+width(s);
    othercases confusion("disc2")
***************
*** 16564,16577 ****
  
  @<Insert a delta node to prepare for breaks at |cur_p|@>=
  if type(prev_r)=delta_node then {modify an existing delta node}
!   begin do_all_six(convert_to_break_width);
    end
  else if prev_r=active then {no delta node needed at the beginning}
!   begin do_all_six(store_break_width);
    end
  else  begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
    subtype(q):=0; {the |subtype| is not used}
!   do_all_six(new_delta_to_break_width);
    link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
    end
  
--- 16678,16691 ----
  
  @<Insert a delta node to prepare for breaks at |cur_p|@>=
  if type(prev_r)=delta_node then {modify an existing delta node}
!   begin do_all_ten(convert_to_break_width);
    end
  else if prev_r=active then {no delta node needed at the beginning}
!   begin do_all_ten(store_break_width);
    end
  else  begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
    subtype(q):=0; {the |subtype| is not used}
!   do_all_ten(new_delta_to_break_width);
    link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
    end
  
***************
*** 16585,16591 ****
  if r<>last_active then
    begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
    subtype(q):=0; {the |subtype| is not used}
!   do_all_six(new_delta_from_break_width);
    link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
    end
  
--- 16699,16705 ----
  if r<>last_active then
    begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
    subtype(q):=0; {the |subtype| is not used}
!   do_all_ten(new_delta_from_break_width);
    link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
    end
  
***************
*** 16596,16606 ****
  begin q:=get_node(passive_node_size);
  link(q):=passive; passive:=q; cur_break(q):=cur_p;
  @!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/
! prev_break(q):=best_place[fit_class];@/
  q:=get_node(active_node_size); break_node(q):=passive;
! line_number(q):=best_pl_line[fit_class]+1;
  fitness(q):=fit_class; type(q):=break_type;
! total_demerits(q):=minimal_demerits[fit_class];
  link(q):=r; link(prev_r):=q; prev_r:=q;
  @!stat if tracing_paragraphs>0 then
    @<Print a symbolic description of the new break node@>;
--- 16710,16721 ----
  begin q:=get_node(passive_node_size);
  link(q):=passive; passive:=q; cur_break(q):=cur_p;
  @!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/
! condensedness(q):=condensed_class;
! prev_break(q):=best_place[fit_class][condensed_class];@/
  q:=get_node(active_node_size); break_node(q):=passive;
! line_number(q):=best_pl_line[fit_class][condensed_class]+1;
  fitness(q):=fit_class; type(q):=break_type;
! total_demerits(q):=minimal_demerits[fit_class][condensed_class];
  link(q):=r; link(prev_r):=q; prev_r:=q;
  @!stat if tracing_paragraphs>0 then
    @<Print a symbolic description of the new break node@>;
***************
*** 16612,16617 ****
--- 16727,16733 ----
  @.\AT!\AT!@>
  print(": line "); print_int(line_number(q)-1);
  print_char("."); print_int(fit_class);
+ print_char("."); print_int(condensed_class);
  if break_type=hyphenated then print_char("-");
  print(" t="); print_int(total_demerits(q));
  print(" -> @@@@");
***************
*** 16703,16727 ****
  We also deactivate node~|r| when a break at~|cur_p| is forced, since future
  breaks must go through a forced break.
  
  @<Consider the demerits for a line from |r| to |cur_p|...@>=
  begin artificial_demerits:=false;@/
  @^inner loop@>
! shortfall:=line_width-cur_active_width[1]; {we're this much too short}
! if shortfall>0 then
!   @<Set the value of |b| to the badness for stretching the line,
!     and compute the corresponding |fit_class|@>
! else @<Set the value of |b| to the badness for shrinking the line,
!     and compute the corresponding |fit_class|@>;
! if (b>inf_bad)or(pi=eject_penalty) then
!   @<Prepare to deactivate node~|r|, and |goto deactivate| unless
!     there is a reason to consider lines of text from |r| to |cur_p|@>
! else  begin prev_r:=r;
!   if b>threshold then goto continue;
!   node_r_stays_active:=true;
!   end;
! @<Record a new feasible break@>;
! if node_r_stays_active then goto continue; {|prev_r| has been set to |r|}
! deactivate: @<Deactivate node |r|@>;
  end
  
  @ When a line must stretch, the available stretchability can be found in the
--- 16819,16859 ----
  We also deactivate node~|r| when a break at~|cur_p| is forced, since future
  breaks must go through a forced break.
  
+ The program logic in this section is really f***ed up!!!
+ 
  @<Consider the demerits for a line from |r| to |cur_p|...@>=
  begin artificial_demerits:=false;@/
  @^inner loop@>
! node_r_stays_active:=false;
! for condensed_class:=very_condensed_font to very_extended_font do begin
!   do_not_consider:=false;
!   shortfall:=line_width-cur_active_width[1]; 
!   case condensed_class of 
!   very_condensed_font:shortfall:=shortfall-cur_active_width[7]; 
!   condensed_font:     shortfall:=shortfall-cur_active_width[8]; 
!   extended_font:      shortfall:=shortfall-cur_active_width[9]; 
!   very_extended_font: shortfall:=shortfall-cur_active_width[10]; 
!   endcases;
!   if shortfall>0 then
!     @<Set the value of |b| to the badness for stretching the line,
!       and compute the corresponding |fit_class|@>
!   else 
!     @<Set the value of |b| to the badness for shrinking the line,
!       and compute the corresponding |fit_class|@>;
!   if (b>inf_bad)or(pi=eject_penalty) then begin 
!     if final_pass and (minimum_demerits=awful_bad) and@|
!       (link(r)=last_active) and
!       (prev_r=active) then
!       artificial_demerits:=true {set demerits zero, this break is forced}
!     else if b>threshold then do_not_consider:=true;
!   end else begin 
!     node_r_stays_active:=true;
!     if b>threshold then do_not_consider:=true;
!   end;
!   if not do_not_consider then @<Record a new feasible break@>
! end;
! if node_r_stays_active then prev_r:=r
! else @<Deactivate node |r|@>;
  end
  
  @ When a line must stretch, the available stretchability can be found in the
***************
*** 16738,16789 ****
    (cur_active_width[5]<>0) then
    begin b:=0; fit_class:=decent_fit; {infinite stretch}
    end
! else  begin if shortfall>7230584 then if cur_active_width[2]<1663497 then
!     begin b:=inf_bad; fit_class:=very_loose_fit; goto done1;
!     end;
    b:=badness(shortfall,cur_active_width[2]);
    if b>12 then
!     if b>99 then fit_class:=very_loose_fit
      else fit_class:=loose_fit
    else fit_class:=decent_fit;
-   done1:
    end
  
  @ Shrinkability is never infinite in a paragraph;
  we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
  
  @<Set the value of |b| to the badness for shrinking...@>=
! begin if -shortfall>cur_active_width[6] then b:=inf_bad+1
  else b:=badness(-shortfall,cur_active_width[6]);
! if b>12 then fit_class:=tight_fit@+else fit_class:=decent_fit;
! end
! 
! @ During the final pass, we dare not lose all active nodes, lest we lose
! touch with the line breaks already found. The code shown here makes sure
! that such a catastrophe does not happen, by permitting overfull boxes as
! a last resort. This particular part of \TeX\ was a source of several subtle
! bugs before the correct program logic was finally discovered; readers
! who seek to ``improve'' \TeX\ should therefore think thrice before daring
! to make any changes here.
! @^overfull boxes@>
! 
! @<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>=
! begin if final_pass and (minimum_demerits=awful_bad) and@|
!    (link(r)=last_active) and
!    (prev_r=active) then
!   artificial_demerits:=true {set demerits zero, this break is forced}
! else if b>threshold then goto deactivate;
! node_r_stays_active:=false;
  end
  
  @ When we get to this part of the code, the line from |r| to |cur_p| is
  feasible, its badness is~|b|, and its fitness classification is |fit_class|.
  We don't want to make an active node for this break yet, but we will
  compute the total demerits and record them in the |minimal_demerits| array,
  if such a break is the current champion among all ways to get to |cur_p|
  in a given line-number class and fitness class.
  
  @<Record a new feasible break@>=
  if artificial_demerits then d:=0
  else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
  @!stat if tracing_paragraphs>0 then
--- 16870,16906 ----
    (cur_active_width[5]<>0) then
    begin b:=0; fit_class:=decent_fit; {infinite stretch}
    end
! else  begin 
    b:=badness(shortfall,cur_active_width[2]);
    if b>12 then
!     if b>99 then 
!       if b>799 then fit_class:=extremely_loose_fit
!       else fit_class:=very_loose_fit
      else fit_class:=loose_fit
    else fit_class:=decent_fit;
    end
  
  @ Shrinkability is never infinite in a paragraph;
  we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
  
  @<Set the value of |b| to the badness for shrinking...@>=
! begin 
!   if xn_over_d(-shortfall,1000,effective_glue_overshrink)>cur_active_width[6] then b:=inf_bad+1
  else b:=badness(-shortfall,cur_active_width[6]);
! if b>100 then fit_class:= very_tight_fit
! else if b>12 then fit_class:=tight_fit@+else fit_class:=decent_fit;
  end
  
  @ When we get to this part of the code, the line from |r| to |cur_p| is
  feasible, its badness is~|b|, and its fitness classification is |fit_class|.
+ And its condensedness classification is |condensed_class|.
  We don't want to make an active node for this break yet, but we will
  compute the total demerits and record them in the |minimal_demerits| array,
  if such a break is the current champion among all ways to get to |cur_p|
  in a given line-number class and fitness class.
  
  @<Record a new feasible break@>=
+ begin
  if artificial_demerits then d:=0
  else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
  @!stat if tracing_paragraphs>0 then
***************
*** 16791,16801 ****
  tats@;@/
  d:=d+total_demerits(r); {this is the minimum total demerits
    from the beginning to |cur_p| via |r|}
! if d<=minimal_demerits[fit_class] then
!   begin minimal_demerits[fit_class]:=d;
!   best_place[fit_class]:=break_node(r); best_pl_line[fit_class]:=l;
    if d<minimum_demerits then minimum_demerits:=d;
    end
  
  @ @<Print a symbolic description of this feasible break@>=
  begin if printed_node<>cur_p then
--- 16908,16920 ----
  tats@;@/
  d:=d+total_demerits(r); {this is the minimum total demerits
    from the beginning to |cur_p| via |r|}
! if d<=minimal_demerits[fit_class][condensed_class] then
!   begin minimal_demerits[fit_class][condensed_class]:=d;
!   best_place[fit_class][condensed_class]:=break_node(r); 
!   best_pl_line[fit_class][condensed_class]:=l;
    if d<minimum_demerits then minimum_demerits:=d;
    end
+ end
  
  @ @<Print a symbolic description of this feasible break@>=
  begin if printed_node<>cur_p then
***************
*** 16818,16823 ****
--- 16937,16945 ----
  @.*\relax@>
  print(" p="); print_int(pi); print(" d=");
  if artificial_demerits then print_char("*")@+else print_int(d);
+ print(" "); print_int(fit_class);
+ print("."); print_int(condensed_class);
+ print(" t="); print_int(d+total_demerits(r));
  end
  
  @ @<Print the list between |printed_node| and |cur_p|...@>=
***************
*** 16852,16858 ****
  if (break_type=hyphenated)and(type(r)=hyphenated) then
    if cur_p<>null then d:=d+double_hyphen_demerits
    else d:=d+final_hyphen_demerits;
! if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
  end
  
  @ When an active node disappears, we must delete an adjacent delta node if the
--- 16974,16991 ----
  if (break_type=hyphenated)and(type(r)=hyphenated) then
    if cur_p<>null then d:=d+double_hyphen_demerits
    else d:=d+final_hyphen_demerits;
! case abs(fit_class-fitness(r)) of
!  0,1: do_nothing;
!  2: d:=d+adj_demerits;
!  othercases d:=d+adj_demerits+extra_adj_demerits;
! endcases;
! if break_node(r)=null then prev_cc:=normal_font
! else prev_cc:=condensedness(break_node(r));
! case abs(condensed_class-prev_cc) of
!   0,1: do_nothing;
!   2: d:=d+font_demerits;
! othercases d:=d+font_demerits+extra_font_demerits;
! endcases;
  end
  
  @ When an active node disappears, we must delete an adjacent delta node if the
***************
*** 16866,16887 ****
    mem[prev_r+#].sc
  
  @<Deactivate node |r|@>=
  link(prev_r):=link(r); free_node(r,active_node_size);
  if prev_r=active then @<Update the active widths, since the first active
    node has been deleted@>
  else if type(prev_r)=delta_node then
    begin r:=link(prev_r);
    if r=last_active then
!     begin do_all_six(downdate_width);
      link(prev_prev_r):=last_active;
      free_node(prev_r,delta_node_size); prev_r:=prev_prev_r;
      end
    else if type(r)=delta_node then
!     begin do_all_six(update_width);
!     do_all_six(combine_two_deltas);
      link(prev_r):=link(r); free_node(r,delta_node_size);
      end;
    end
  
  @ The following code uses the fact that |type(last_active)<>delta_node|. If the
  active list has just become empty, we do not need to update the
--- 16999,17022 ----
    mem[prev_r+#].sc
  
  @<Deactivate node |r|@>=
+ begin
  link(prev_r):=link(r); free_node(r,active_node_size);
  if prev_r=active then @<Update the active widths, since the first active
    node has been deleted@>
  else if type(prev_r)=delta_node then
    begin r:=link(prev_r);
    if r=last_active then
!     begin do_all_ten(downdate_width);
      link(prev_prev_r):=last_active;
      free_node(prev_r,delta_node_size); prev_r:=prev_prev_r;
      end
    else if type(r)=delta_node then
!     begin do_all_ten(update_width);
!     do_all_ten(combine_two_deltas);
      link(prev_r):=link(r); free_node(r,delta_node_size);
      end;
    end
+ end
  
  @ The following code uses the fact that |type(last_active)<>delta_node|. If the
  active list has just become empty, we do not need to update the
***************
*** 16893,16900 ****
  @<Update the active widths,...@>=
  begin r:=link(active);
  if type(r)=delta_node then
!   begin do_all_six(update_active);
!   do_all_six(copy_to_cur_active);
    link(active):=link(r); free_node(r,delta_node_size);
    end;
  end
--- 17028,17035 ----
  @<Update the active widths,...@>=
  begin r:=link(active);
  if type(r)=delta_node then
!   begin do_all_ten(update_active);
!   do_all_ten(copy_to_cur_active);
    link(active):=link(r); free_node(r,delta_node_size);
    end;
  end
***************
*** 16921,16929 ****
--- 17056,17066 ----
  
  @<Local variables for line breaking@>=
  @!auto_breaking:boolean; {is node |cur_p| outside a formula?}
+ @!cw:scaled;
  @!prev_p:pointer; {helps to determine when glue nodes are breakpoints}
  @!q,@!r,@!s,@!prev_s:pointer; {miscellaneous nodes of temporary interest}
  @!f:internal_font_number; {used when calculating character widths}
+ @!l:small_number;
  
  @ The `\ignorespaces|loop|\unskip' in the following code is performed at most
  thrice per call of |line_break|, since it is actually a pass over the
***************
*** 16978,16984 ****
  type(q):=unhyphenated; fitness(q):=decent_fit;
  link(q):=last_active; break_node(q):=null;
  line_number(q):=prev_graf+1; total_demerits(q):=0; link(active):=q;
! do_all_six(store_background);@/
  passive:=null; printed_node:=temp_head; pass_number:=0;
  font_in_short_display:=null_font
  
--- 17115,17121 ----
  type(q):=unhyphenated; fitness(q):=decent_fit;
  link(q):=last_active; break_node(q):=null;
  line_number(q):=prev_graf+1; total_demerits(q):=0; link(active):=q;
! do_all_ten(store_background);@/
  passive:=null; printed_node:=temp_head; pass_number:=0;
  font_in_short_display:=null_font
  
***************
*** 17024,17031 ****
  kern_node: if subtype(cur_p)=explicit then kern_break
    else act_width:=act_width+width(cur_p);
  ligature_node: begin f:=font(lig_char(cur_p));
!   act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p))));
!   end;
  disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
  math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
    end;
--- 17161,17175 ----
  kern_node: if subtype(cur_p)=explicit then kern_break
    else act_width:=act_width+width(cur_p);
  ligature_node: begin f:=font(lig_char(cur_p));
!   cw:=char_width(f)(char_info(f)(character(lig_char(cur_p))));
!   act_width:=act_width+cw;
!   for j:=0 to 3 do 
!     active_width[7+j]:=active_width[7+j]
!         +(char_width(font_variants[f][j])
!                     (char_info(font_variants[f][j])
!                               (character(lig_char(cur_p))))
!           -cw);
! end;
  disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
  math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
    end;
***************
*** 17047,17053 ****
  @<Advance \(c)|cur_p| to the node following the present string...@>=
  begin prev_p:=cur_p;
  repeat f:=font(cur_p);
! act_width:=act_width+char_width(f)(char_info(f)(character(cur_p)));
  cur_p:=link(cur_p);
  until not is_char_node(cur_p);
  end
--- 17191,17204 ----
  @<Advance \(c)|cur_p| to the node following the present string...@>=
  begin prev_p:=cur_p;
  repeat f:=font(cur_p);
! cw:=char_width(f)(char_info(f)(character(cur_p)));
! act_width:=act_width+cw;
! for j:=0 to 3 do 
!   active_width[7+j]:=active_width[7+j]
!        +(char_width(font_variants[f][j])
!                    (char_info(font_variants[f][j])
!                              (character(cur_p)))
!          -cw);
  cur_p:=link(cur_p);
  until not is_char_node(cur_p);
  end
***************
*** 17378,17384 ****
  else  begin cur_width:=mem[par_shape_ptr+2*cur_line].sc;
    cur_indent:=mem[par_shape_ptr+2*cur_line-1].sc;
    end;
! adjust_tail:=adjust_head; just_box:=hpack(q,cur_width,exactly);
  shift_amount(just_box):=cur_indent
  
  @ Penalties between the lines of a paragraph come from club and widow lines,
--- 17529,17538 ----
  else  begin cur_width:=mem[par_shape_ptr+2*cur_line].sc;
    cur_indent:=mem[par_shape_ptr+2*cur_line-1].sc;
    end;
! adjust_tail:=adjust_head; 
! line_font_variant:=condensedness(cur_p);
! just_box:=hpack(q,cur_width,exactly);
! line_font_variant:=normal_font;
  shift_amount(just_box):=cur_indent
  
  @ Penalties between the lines of a paragraph come from club and widow lines,
***************
*** 23263,23271 ****
  @ Here is where the information for a new font gets loaded.
  
  @<Assignments@>=
! def_font: new_font(a);
! 
  @ @<Declare subprocedures for |prefixed_command|@>=
  procedure new_font(@!a:small_number);
  label common_ending;
  var u:pointer; {user's font identifier}
--- 23417,23441 ----
  @ Here is where the information for a new font gets loaded.
  
  @<Assignments@>=
! def_font: if cur_chr=0 then new_font(a) else
!   set_font_variant(a);
!          
  @ @<Declare subprocedures for |prefixed_command|@>=
+ procedure set_font_variant(@!a:small_number);
+ var 
+   s:integer;
+ begin
+   scan_int; s:=cur_val;
+   if (s<0)or(s>3) then begin
+     print_err("Bad font variant number");
+     help2("A font variant number must be between 0 and 3.")@/
+        ("I changed this one to zero."); int_error(s); s:=0;
+   end;
+   scan_font_ident; f:=cur_val;
+   scan_optional_equals; scan_font_ident;
+   font_variants[f][s]:=cur_val;
+ end;
+ 
  procedure new_font(@!a:small_number);
  label common_ending;
  var u:pointer; {user's font identifier}
***************
*** 23274,23279 ****
--- 23444,23450 ----
  @!t:str_number; {name for the frozen font identifier}
  @!old_setting:0..max_selector; {holds |selector| setting}
  @!flushable_string:str_number; {string not yet referenced}
+ @!k:small_number;
  begin if job_name=0 then open_log_file;
    {avoid confusing \.{texput} with the font name}
  @.texput@>
***************
*** 23292,23297 ****
--- 23463,23469 ----
    font number and |goto common_ending|@>;
  f:=read_font_info(u,cur_name,cur_area,s);
  common_ending: equiv(u):=f; eqtb[font_id_base+f]:=eqtb[u]; font_id_text(f):=t;
+ for k:=0 to 3 do font_variants[f][k]:=f;
  end;
  
  @ @<Scan the font size specification@>=
*** tex.ch.orig	Wed Sep 16 22:54:44 1998
--- tex.ch	Sun Sep 20 10:03:52 1998
***************
*** 1200,1211 ****
  @z
  
  @x [17.236] l.4954 - MLTeX: \charsubdefmax and \tracingcharsubdef
! @d int_pars=55 {total number of integer parameters}
  @y
! @d char_sub_def_min_code=55 {smallest value in the charsubdef list}
! @d char_sub_def_max_code=56 {largest value in the charsubdef list}
! @d tracing_char_sub_def_code=57 {traces changes to a charsubdef def}
! @d int_pars=58 {total number of integer parameters}
  @z
  
  @x [17.236] l.5016 - MLTeX: \charsubdefmax and \tracingcharsubdef
--- 1200,1211 ----
  @z
  
  @x [17.236] l.4954 - MLTeX: \charsubdefmax and \tracingcharsubdef
! @d int_pars=59 {total number of integer parameters}
  @y
! @d char_sub_def_min_code=59 {smallest value in the charsubdef list}
! @d char_sub_def_max_code=60 {largest value in the charsubdef list}
! @d tracing_char_sub_def_code=61 {traces changes to a charsubdef def}
! @d int_pars=62 {total number of integer parameters}
  @z
  
  @x [17.236] l.5016 - MLTeX: \charsubdefmax and \tracingcharsubdef
***************
*** 1752,1757 ****
--- 1752,1759 ----
    {right boundary character, |non_char| if there is none}
  @!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
    {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
+ @!font_variants:array[internal_font_number] of four_fonts; 
+   {we use four variants per font}
  @y
  @!font_info: ^fmemory_word;
    {the big collection of font data}
***************
*** 1783,1788 ****
--- 1785,1791 ----
    {right boundary character, |non_char| if there is none}
  @!font_false_bchar: ^nine_bits;
    {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
+ @!font_variants: ^four_fonts;
  @z
  
  @x [30.550] l.10723 - texarray
***************
*** 1845,1850 ****
--- 1848,1854 ----
  font_glue[null_font]:=null; font_params[null_font]:=7;
  param_base[null_font]:=-1;
  for k:=0 to 6 do font_info[k].sc:=0;
+ for k:=0 to 3 do font_variants[null_font][k]:=null_font;
  @y
  @z
  
***************
*** 2176,2184 ****
  @z
  
  @x [38.859] l.16855 - Fix a casting/expression evaluation problem.
! if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
  @y
! if abs(toint(fit_class)-toint(fitness(r)))>1 then d:=d+adj_demerits;
  @z
  
  @x [39.875] l.17170 - Another casting problem.
--- 2180,2188 ----
  @z
  
  @x [38.859] l.16855 - Fix a casting/expression evaluation problem.
! case abs(fit_class-fitness(r)) of
  @y
! case abs(toint(fit_class)-toint(fitness(r))) of
  @z
  
  @x [39.875] l.17170 - Another casting problem.
***************
*** 3197,3202 ****
--- 3201,3207 ----
  dump_things(exten_base[null_font], font_ptr+1-null_font);
  dump_things(param_base[null_font], font_ptr+1-null_font);
  dump_things(font_glue[null_font], font_ptr+1-null_font);
+ dump_things(font_variants[null_font], font_ptr+1-null_font);
  dump_things(bchar_label[null_font], font_ptr+1-null_font);
  dump_things(font_bchar[null_font], font_ptr+1-null_font);
  dump_things(font_false_bchar[null_font], font_ptr+1-null_font);
***************
*** 3264,3269 ****
--- 3269,3275 ----
  xmalloc_array(kern_base, font_max);
  xmalloc_array(exten_base, font_max);
  xmalloc_array(param_base, font_max);
+ xmalloc_array(font_variants, font_max);
  
  undump_things(font_check[null_font], font_ptr+1-null_font);
  undump_things(font_size[null_font], font_ptr+1-null_font);
***************
*** 3290,3295 ****
--- 3296,3302 ----
  undump_things(param_base[null_font], font_ptr+1-null_font);
  undump_checked_things(min_halfword, lo_mem_max,
                       font_glue[null_font], font_ptr+1-null_font);
+ undump_things(font_variants[null_font], font_ptr+1-null_font);
  undump_checked_things(0, fmem_ptr-1,
                       bchar_label[null_font], font_ptr+1-null_font);
  undump_checked_things(min_quarterword, non_char,
***************
*** 3705,3710 ****
--- 3712,3718 ----
    xmalloc_array(kern_base, font_max);
    xmalloc_array(exten_base, font_max);
    xmalloc_array(param_base, font_max);
+   xmalloc_array(font_variants, font_max);
  
    font_ptr:=null_font; fmem_ptr:=7;
    font_name[null_font]:="nullfont"; font_area[null_font]:="";
***************
*** 3720,3725 ****
--- 3728,3734 ----
    font_glue[null_font]:=null; font_params[null_font]:=7;
    param_base[null_font]:=-1;
    for font_k:=0 to 6 do font_info[font_k].sc:=0;
+   for font_k:=0 to 3 do font_variants[null_font][font_k]:=null_font;
    end;
    tini@/
  
