% ---------------------------------------------------------------------------
%              THIS FILE BELONGS TO THE TAP PACKAGE
% ---------------------------------------------------------------------------
% Macros for typesetting tables in a simple manner using PostScript
% for filling, hashing, etc. cells of the table.
% Authors:
%   Bogus\l{}aw Jackowski, Piotr Pianowski and Piotr Strzelczyk
%   BOP s.c., Piastowska 70, 80-363 Gda\'nsk, Poland
%   Email: B.Jackowski@GUST.ORG.PL
% History:
%   The starting point of this package was a neat package
%   by Michael J. Ferguson. With his kind permission we rebuilt
%   the package from a scratch. Some notational ``bells and whistles''
%   have been added, and---which is perhaps more important---the package
%   has been enhanced by PostScript-oriented features (painting cells with
%   an arbitrary colour, drawing diagonal strokes, etc.).
%   The package does not use tabskip glues for imposing a desired width;
%   this makes the code a bit more complex, yet gives some advantages.
% Version: 0.70, Friday, May 3rd, 1996 (BachoTeX 96 release)
% Version: 0.70a, Monday, October 20th, 1997 (setting \everymath corrected)
% Version: 0.70b, Monday, October 20th, 1997
%    * first step towards using struts implicitly in \X and \Y macros
% Version: 0.71, Tuesday, January 27th, 1998
%    * Staszek Wawrykiewicz encountered a nasty typo
%      (\edef\rightcellside{\leftcellside}) -- fixed
%    * implicit struts in \X and \Y macros (inherited from the \B macro) added
%    * \desiredwidth ignored for zero-width tables (this protect against
%      infinite loops and arithmetic overflow in empty tables)
% Version: 0.72, Wednesday, August 26th, 1998
%    * the assignment `\let\span_\sp@n' replaced by an explicit definition
%      of \span_ (used in \usecells_), as the definitions of \sp@n in plain
%      and LaTeX2e incompatibly differ
% =========================================================================
\ifx\tapmacrosareloaded\relax % anti-double loading barrier
  \expandafter\endinput\else\let\tapmacrosareloaded\relax
\fi
% =============================== Preamble ================================
\let\normalexclam!
\let\normalat@
\let\normalquotes"
\let\normalvbar|
\def\normalundscore{_}% an exception

\edef\dblquotecatcode{\the\catcode`\"}
\edef\brokenbarcatcode{\the\catcode`\|}
\edef\exclamcatcode{\the\catcode`\!}
\edef\undscorecatcode{\the\catcode`\_}
\edef\atcatcode{\the\catcode`\@}
\def\tableactive{%
  \catcode`\|13 \catcode`\@13 \catcode`\"13 \catcode`\!13\relax}

\catcode`\_=11
\catcode`\@=11 % temporarily, in a moment it will be active

% an attempt to avoid exploiting extra \count registers
% (\mscount is used by \span_):
\ifx\mscount\undefined_ \let\mscount\@multicnt \fi
\ifx\mscount\undefined_ \csname newcount\endcsname\mscount \fi

\tableactive

\def|{\ifmmode \vert\else \normalvbar \fi} % effectively undoes activeness
\let!\normalexclam
\let@\normalat
\let"\normalquotes

% ============================= Table macros ==============================
% \trth = normal table rule thickness (default .4pt)
% \TRTH = bold table rule thickness (default 1.2pt)
\newdimen\trth \trth=.4pt
\newdimen\TRTH \TRTH=1.2pt
\newcount\align_state \align_state=0 % 0 -- between lines after \cr
                                     % 1 -- beginning of line in \br
                                     % 2 -- midline
                                     % 3 -- endline in \er

\def\hssf{\hskip 0pt plus 1fill minus 1fill}
%general math form
\def\math#1{\relax $\relax#1\relax$}
\def\displaymath #1{\relax$\displaystyle #1\relax$}

\newtoks\everytable \everytable{\relax}
\newtoks\thistable \thistable{\relax}

\newdimen\desiredwidth
\def\width_accuracy_limit{.01pt}% not to be changed
\newdimen\widthaccuracy \widthaccuracy\width_accuracy_limit
\newif\iflongcalculation

\newbox\table_box

\newbox\box_tmp
\newdimen\innerht \newdimen\innerdp % to be used in \X, \x, \Y, and \y
\newdimen\normht  \newdimen\normdp  % for normal rows
\newdimen\extraht \newdimen\extradp % for vertically extended rows

\def\defaultcellmarg{0.5em} % \cellmarg is by default set to .5em of
\newdimen\cellmarg          % the current font at the beginning of a table

\newcount\cellnum
\newcount\maxcellnum
\newdimen\cellcorr
\newdimen\maxcellcorr
\newdimen\mincellcorr
\newcount\cellstate % 0 -- aligned to left
                    % 1 -- centered
                    % 2 -- aligned to right

\newif\ifignorecellstate

% \ostrut means ``open'' strut (to be followed by height <dimen> depth
% <dimen>, \istrut means inner strut to be used in \X, \x, \Y, and \y
\def\ostrut{\vrule width0mm}
\def\istrut{\ostrut height\innerht depth\innerdp\relax}

\def\defaultstrut{%
  \setbox\box_tmp\hbox{(pl}%
  \settablestrut \ht\box_tmp \dp\box_tmp \ht\box_tmp \dp\box_tmp
  \adjusttablestrut .45ex .15ex .95ex .55ex
  \setstrut \normht \normdp
}

\def\setstrut{\afterassignment\innerdp\innerht}

\def\adjuststrut{\afterassignment\adjuststrut_\advance\innerht}
\def\adjuststrut_{\advance\innerdp}

\def\settablestrut{\afterassignment\settablestrut_\normht}
\def\settablestrut_{\afterassignment\settablestrut__\normdp}
\def\settablestrut__{\afterassignment\settablestrut___\extraht}
\def\settablestrut___{\extradp}

\def\adjusttablestrut{\afterassignment\adjusttablestrut_\advance\normht}
\def\adjusttablestrut_{\afterassignment\adjusttablestrut__\advance\normdp}
\def\adjusttablestrut__{\afterassignment\adjusttablestrut___\advance\extraht}
\def\adjusttablestrut___{\advance\extradp}

\def\symcellcorr{\ignorecellstatetrue}
\def\asymcellcorr{\ignorecellstatefalse}
\symcellcorr

% \leftcellside and \rightcellside will be subjected to expansion
% using \edef in \begintableformat, hence no \ifnum-s can be involved;
% for the same reason \noexpand-s are used in \update_mincellmarg;
% \antileftcellside and \antirightcellside for the sake of symmetry
% are programmed similarly; ignoring the cell state is meant as a global
% setting, hence \ifignorecellstate can be subjected to \edef

\newdimen\mincellmarg
\def\update_mincellmarg{%
  \dim_tmp\cellmarg \advance\dim_tmp\cellcorr
  \noexpand\ifdim\dim_tmp<\mincellmarg \global\mincellmarg\dim_tmp
  \noexpand\fi}

\def\leftcellside{%
  \global\advance\cellnum1\kern\cellmarg
  \ifignorecellstate \kern\cellcorr \update_mincellmarg
  \else {\multiply\cellcorr\cellstate \kern\cellcorr \update_mincellmarg
  }\fi
}
\def\antileftcellside{\hskip-\cellmarg
  \ifignorecellstate \kern-\cellcorr
  \else {\multiply\cellcorr\cellstate \kern-\cellcorr}\fi
}
\def\rightcellside{%
  \global\advance\cellnum1\kern\cellmarg
  \ifignorecellstate \kern\cellcorr \update_mincellmarg
  \else {\cellstate-\cellstate \advance\cellstate2
    \multiply\cellcorr\cellstate \kern\cellcorr \update_mincellmarg
  }\fi
}
\def\antirightcellside{\hskip-\cellmarg
  \ifignorecellstate \kern-\cellcorr
  \else {\cellstate-\cellstate \advance\cellstate2
    \multiply\cellcorr\cellstate \kern-\cellcorr}\fi
}

\def\beginflatcell{\vbox to0mm\bgroup\vss \hbox\bgroup \istrut}
\def\endflatcell{\egroup\vss\egroup}

\newif\ifv_squash
\newbox\cell_box
\def\normcellbox{\hbox\bgroup\v_squashfalse\let\v_box_or_top\vbox\cellbox_}
\def\flatcellbox{\hbox\bgroup\v_squashtrue\let\v_box_or_top\vbox\cellbox_}
\def\normcelltop{\hbox\bgroup\v_squashfalse\let\v_box_or_top\vtop\cellbox_}
\def\flatcelltop{\hbox\bgroup\v_squashtrue\let\v_box_or_top\vtop\cellbox_}
\def\cellbox_#1#{\def\cellbox_parm{#1}%
  \ifx\cellbox_parm\empty \expandafter \cellbox_a \else
  \expandafter \cellbox_p \fi}
\def\cellbox_a#1{% aligned cell box
  \setbox\cell_box\v_box_or_top{
    \let\\\cr
    \let@\normalat
    \let!\normalexclam
    \def\L##1{\ignorespaces ##1\unskip\hssf\null}
    \def\C##1{\hssf \ignorespaces ##1\unskip\hssf\null}
    \def\R##1{\hssf \ignorespaces ##1\unskip\null}
    \everycr{}
    \halign{%
      \istrut \ifnum\cellstate>0\hss\fi ##\ifnum\cellstate<2\hss\fi \cr
      \ostrut height\curr_distender_height
      \ignorespaces #1%
      \ifhmode\ostrut depth\curr_distender_depth\fi
      \crcr
    }
  }%
  \ifv_squash\vbox to0mm{\vss\box\cell_box\vss}\else\box\cell_box\fi
  \egroup
}
\def\cellbox_p#1{% paragraph cell box
  \setbox\cell_box\v_box_or_top{\parindent0mm \parfillskip0pt
    \def\\{\unskip\break\hskip0pt\relax\ignorespaces}
    \let@\normalat
    \let!\normalexclam
    \let~\ori_tilde % restore the original meaning (natural in a ``par'' mode)
    \let\-\ori_bsdash % ditto
    \let\=\ori_bsequal % ditto
    \def\L##1{\ignorespaces ##1\unskip\hfill\null}
    \def\C##1{\hfill\ignorespaces ##1\unskip\hfill\null}
    \def\R##1{\hfill\ignorespaces ##1\unskip\null}
    \hsize\cellbox_parm
    \normalbaselines \baselineskip\innerht \advance\baselineskip\innerdp
    \ifnum\cellstate>0 \leftskip0ptplus1fil \fi
    \ifnum\cellstate<2 \rightskip0ptplus1fil \fi
    \ostrut height\curr_distender_height
    \nobreak\hskip0pt\relax % enable implicitly breaking of the first word
    \ignorespaces #1%
    \ifhmode\ostrut depth\curr_distender_depth\fi
  }%
  \ifv_squash \vbox to0mm{\vss\box\cell_box\vss}\else\box\cell_box\fi
  \egroup
}

\def\cellaction#1{\ifcase\align_state \def\cellaction_{#1}\or
  \def\cellaction_{#1}\or
  \def\cellaction_{\unskip&#1&}\else
  \def\cellaction_{#1}\fi\cellaction_}

\def\tablerulecmyk#1{\def\thetablerulecmyk{#1}}
\def\beginrulecmyk{\ifx\thetablerulecmyk\empty\else
  \special{ps:gsave \thetablerulecmyk\space setcmykcolor}\fi}
\def\endrulecmyk{\ifx\thetablerulecmyk\empty\else\special{ps:grestore}\fi}
\def\tablefullrule#1{\noalign{\beginrulecmyk \hrule height #1\endrulecmyk}}
\def\tablevrule#1{\hss\beginrulecmyk\vrule width #1\endrulecmyk\hss}
\def\tablehrule#1{\antileftcellside
  \beginrulecmyk\leaders\hrule height #1\hfill\endrulecmyk
  \antirightcellside}

\def\preambleleft{\cellstate0\relax \leftcellside \currtfont
  \begincell \ignorespaces ####\unskip\endcell \hfil \rightcellside}
\def\preamblecenter{\cellstate1\relax \leftcellside \hfil \currtfont
  \begincell \ignorespaces ####\unskip\endcell \hfil \rightcellside}
\def\preambleright{\cellstate2\relax \leftcellside \hfil \currtfont
  \begincell \ignorespaces ####\unskip\endcell \rightcellside}
\def\tableleft#1{\omit\cellstate0\relax \leftcellside \currtfont
  \begincell \ignorespaces #1\endcell \hfil \rightcellside}
\def\tablecenter#1{\omit\cellstate1\relax \leftcellside \hfil \currtfont
  \begincell \ignorespaces #1\endcell \hfil \rightcellside}
\def\tableright#1{\omit\cellstate2\relax \leftcellside \hfil \currtfont
  \begincell \ignorespaces #1\endcell \rightcellside}

\def\span_{\span\omit \advance\mscount-1\relax}
\def\usecells{\omit\relax\afterassignment\usecells_\mscount}
\def\usecells_{%
  \advance\mscount-1\multiply\mscount2\relax
  \loop\ifnum\mscount>1 \span_ \repeat
  \ifnum\mscount>0 \span \else \relax \fi}

\newif\ifspec_distender \spec_distenderfalse % used by macro \B

\def\short_fix_cellcorr{%
% \setbox\table_box... is already performed
% locally \cellcorr = 1/cellmax * (desired_wd-acual_wd)
    \cellcorr\desiredwidth \advance\cellcorr-\wd\table_box
    \def\corr_sign{}%
    \ifdim\cellcorr<0mm\def\corr_sign{-}\cellcorr-\cellcorr\fi
    \resize \cellcorr{1sp}\cellcorr{\the\maxcellnum sp}%
    \cellcorr\corr_sign\cellcorr
}

\def\long_fix_cellcorr{%
  \ifdim\widthaccuracy<\width_accuracy_limit\relax
    \widthaccuracy\width_accuracy_limit\relax % moderate accuracy
  \fi
% initial settings (\setbox\table_box... is already performed):
  \cellcorr0mm
  \dim_tmp\desiredwidth \advance\dim_tmp-\wd\table_box
% each cell is ``padded'' exactly by 2\cellcorr and at least
% one cell must appear in any table, hence the coefficient 1/2:
  \ifdim\dim_tmp<0mm \mincellcorr.5\dim_tmp
  \else \maxcellcorr.5\dim_tmp \fi
% binary search:
  \loop
    \ifdim\dim_tmp<0mm \maxcellcorr\cellcorr \dim_tmp-\dim_tmp
    \else \mincellcorr\cellcorr \fi
    \ifdim\dim_tmp>\widthaccuracy
    \cellcorr\mincellcorr \advance\cellcorr\maxcellcorr
    \cellcorr.5\cellcorr
    \setbox\table_box\hbox{\curr_table}%
%   \immediate\write16{\the\wd\table_box, \the\cellcorr\space}%
    \dim_tmp\desiredwidth \advance\dim_tmp-\wd\table_box
  \repeat
}

\def\fix_cellcorr{%
  \iflongcalculation \long_fix_cellcorr \else \short_fix_cellcorr \fi}

\def\margin_warning{%
  \ifdim\cellcorr<0mm
    \ifdim\mincellmarg<\TRTH
      \immediate\write16{%
        TAPS warning: effective cell margin = \the\mincellmarg.}%
    \fi
  \fi}

\def\width_info#1{%
  \let\ \space % it's local
  \immediate\write16{%
    TAPS warning: Unsuccessful forcing of the desired width.}%
  \immediate\write16{%
    \ \ \ \ \ \ \ \ \ \ \ \ \ \ desired width - resulting width = \the#1}%
  \immediate\write16{%
    \ \ \ \ \ \ \ \ \ \ \ \ \ \ required accuracy = \the\widthaccuracy}}

\def\width_warning{%
  \ifdim\desiredwidth>0mm
% \setbox\table_box\hbox{\curr_table} is performed immediately prior to
% calling \width_warning, i.e., \table_box registers are defined
    \begingroup
      \advance\desiredwidth-\wd\table_box % temporary settings:
      \ifdim\desiredwidth>\widthaccuracy \width_info\desiredwidth \fi
      \ifdim\desiredwidth<-\widthaccuracy \width_info\desiredwidth \fi
    \endgroup
  \fi}

\def\table_setup{
  \tableactive
% protect the original meanings of important macros:
  \let\ori_tilde~
  \let\ori_bsdash\-
  \let\ori_bsequal\=
  \let\ori_bsgreater\> % in plain \def\>{\mskip\medmuskip}
  \let\ori_bsexclam\!  %    "     \def\!{\mskip-\thinmuskip}
  \everymath\expandafter{\the\everymath
    \let\>\ori_bsgreater \let\!\ori_bsexclam
    \let@\normalat \let!\normalexclam}
  \let\!\normalexclam
  \let\>\rlap
  \let\<\llap
  \let\H\hbox % useful as a ``horizontal strut'': \H to5mm{}
  \let\x\flatcellbox
  \let\X\normcellbox
  \let\y\flatcelltop
  \let\Y\normcelltop
  \def\P##1{{\phantom{##1}}}  % \phantom uses \box0, hence curly braces
  \def\V##1{{\vphantom{##1}}} % ditto
  \def\K{\noalign\bgroup\afterassignment\K_\dim_tmp}
  \def\K_{\kern\dim_tmp\egroup}
  \def\L{\endcell\hfill\begincell}
  \def\F##1{\noalign{\global\let\currtfont##1}} % sets current table font
  \def\-{\ifcase\align_state \tablefullrule{\trth}\else
         \endcell\tablehrule{\trth}\begincell\fi}
  \def\={\ifcase\align_state \tablefullrule{\TRTH}\else
         \endcell\tablehrule{\TRTH}\begincell\fi}
  \def|{\cellaction{\tablevrule\trth}}
  \def!{\cellaction{\tablevrule\TRTH}}
  \def\br##1{%
    \global\cellnum0\relax
    \global\align_state1\relax \ignorespaces ##1\unskip
    \global\align_state2\relax &%
  }
  \def\B##1##2{%
% if ##2 equals to 0 - + _ ^ or : it is interpreted in a special way;
% otherwise ##2 is treated as if it were equal to 0 and the token list
% swallowed as ##2 parameter is put after \br{...}; in the later case,
% however, surrounding curly braces may disappear; in these, hopefully, rare
% situations using either explicitly suffix 0 or double bracing may help
    \br{##1\spec_distendertrue\D{##2}}}
  \def\er##1{\global\align_state=3\unskip&##1\unskip \global\align_state=0\cr}
  \def\E{\er}
  \def\D##1{% ``distender''
% if ##1 equals to 0 - + _ ^ or : it is interpreted in a special way,
% namely a strut of an appropriate size is formed; otherwise ##1 is just
% returned to \TeX (possibly without curly braces, however); see also
% definition of \B above
    \ifvmode\leavevmode\fi
    \ifhmode\nobreak\hskip0pt\relax\fi % enable breaking of a preceding word
    \gdef\next_tok{}%
    \def\first_par{##1}%
    \def\null_suffix{0}%
    \def\flat_suffix{-}%
    \def\norm_suffix{+}%
    \edef\dn_suffix{\normalundscore}% an exception
    \def\up_suffix{^}%
    \def\updn_suffix{:}%
    \setbox\box_tmp\hbox{%
      \ostrut
      \ifx\first_par\null_suffix \else
      \ifx\first_par\norm_suffix height\normht depth\normdp \else
      \ifx\first_par\flat_suffix height0mm depth0mm
        \ifspec_distender
          \global\let\begincell\beginflatcell \global\let\endcell\endflatcell
        \fi
      \else
      \ifx\first_par\up_suffix height\extraht depth\normdp \else
      \ifx\first_par\dn_suffix height\normht depth\extradp \else
      \ifx\first_par\updn_suffix height\extraht depth\extradp
      \else \gdef\next_tok{##1}% unrecognized token(s) will recirculate
      \fi\fi\fi\fi\fi\fi \relax
    }%
    \xdef\curr_distender_depth{\the\dp\box_tmp}%
    \xdef\curr_distender_height{\the\ht\box_tmp}%
    \unhbox\box_tmp
    \ifspec_distender \expandafter
      \aftergroup % put \next_tok in a next alignment cell
    \fi
    \next_tok}
  \let@\usecells
  \def~{\leavevmode\phantom{0}}% invisible digit (usually, all digits have
                               % the same width)
%  \def\$##1{~\llap{$##1$}}% mathematical sign, with 
%                          % width of digit
%  \def\,{\leavevmode\phantom{,}}% space of coma
  \defaultstrut
  \longcalculationtrue
  \cellmarg\defaultcellmarg % freeze a default value
  \everycr{\noalign{
    \ifnum\cellnum>\maxcellnum \global\maxcellnum\cellnum\fi
    \global\let\begincell\defaultbegincell
    \global\let\endcell\defaultendcell
    \global\align_state0
  }}
  \let\defaultbegincell\relax
  \let\defaultendcell\relax
  \tablerulecmyk{}% no color is specified
  \tablebkgcmyk{0 0 0 0}% background is white by default
  \the\everytable\relax \the\thistable\relax}

\def\begintable_{\vbox\bgroup
  \global\let\begincell\defaultbegincell %  \begincell and \endcell
  \global\let\endcell\defaultendcell     %  are always set globally,
  \global\let\currtfont\relax            %  \currtfont too
% \currtfont was introduced in order to avoid global font assignments
% as well as to avoid a premature expansion
% of \currtfont in \begintableformat
  \global\maxcellnum0 \global\cellnum0 \global\mincellmarg\maxdimen
  \let\cellaction_\relax % protect against premature expansions
  \let\ori_par\par
  \let\par\empty % set to original meaning at the beginning of each vbox
  \everyvbox\expandafter{\the\everyvbox \let\par\ori_par}
}
\def\endtable_{\crcr\egroup\egroup}

\def\begintableformat #1\endtableformat{\offinterlineskip \tabskip = 0pt
  \let\left\preambleleft \let\center\preamblecenter \let\right\preambleright
  \def"{&########&}
  \edef\tbl_form{####&#1&####\cr}
  \let\left\tableleft \let\center\tablecenter \let\right\tableright
  \def"{\cellaction\relax}
  \xdef\currtfont{\the\font}
  \edef\leftcellside{\leftcellside}
  \edef\rightcellside{\rightcellside}
  \halign\bgroup\span\tbl_form}

\def\begintable{\vbox\bgroup \table_setup \midtable}
\long\def\midtable #1\endtable{%
  \def\curr_table{\begintable_#1\endtable_}\nospecanchors
  \edef\currhbadness{\the\hbadness}\edef\currvbadness{\the\vbadness}%
  \ifdim\desiredwidth>0mm \hbadness10000 \vbadness10000 \fi
  \setbox\table_box\hbox{\curr_table}%
  \ifdim\desiredwidth>0mm
    \ifdim\wd\table_box>0mm
      \fix_cellcorr
% typesetset the table for the last time with the original badnesses
      \hbadness\currhbadness \vbadness\currvbadness
      \setbox\table_box\hbox{\curr_table}%
      \iflongcalculation \else \width_warning\fi
      \margin_warning
    \else
      \immediate\write16{%
        TAPS warning: table width = \the\wd\table_box\space
        (\desiredwidth ignored).}%
    \fi
  \fi
  \xdef\tablewidth{\the\wd\table_box}%
  \xdef\tablecellcorr{\the\cellcorr}%
  \box\table_box
  \egroup
  \thistable{\relax}}

\def\deftable#1\begintable{% #1 is expected to be a valid macro header,
% preferably a csname; characters in the header have ``outer'' category
% codes, i.e., not ``table'' category codes
  \begingroup
     \def\table_name{#1}\tableactive
     \expandafter \expandafter \expandafter \gdef \expandafter \table_name
     \deftablecont}
\long\def\deftablecont#1\endtable{{#1}\endgroup}

% ====================== PostScript (anchor) macros =======================
\def\beginanchtable{\vbox\bgroup \table_setup \midanchtable}
\long\def\midanchtable #1\endanchtable{%
  \def\curr_table{%
    \gdef\useanchors{}% \useanchors is defined during typesetting a table
    \begintable_#1\endtable_}%
  \edef\currhbadness{\the\hbadness}\edef\currvbadness{\the\vbadness}%
% at least two table settings are necessary with anchors, as \urfullanchor
% needs to know the actual table width, hence the first setting is always
% temporary; if a short calculation option holds, after fixing the
% cell correction the table must be set once again, for the same reason
  \hbadness10000 \vbadness10000 \setbox\table_box\hbox{\curr_table}%
  \hbox{%
    \ifdim\desiredwidth>0mm \fix_cellcorr
      \iflongcalculation \else
        \setbox\table_box\hbox{\curr_table}% see the previous comment
      \fi
    \fi
% typesetset the table for the last time with the original badnesses
    \hbadness\currhbadness \vbadness\currvbadness
    \setbox\table_box\hbox{\curr_table}%
    \ifdim\desiredwidth>0mm
      \iflongcalculation \else \width_warning\fi
      \margin_warning
    \fi
    \xdef\tablewidth{\the\wd\table_box}%
    \xdef\tablecellcorr{\the\cellcorr}%
    \tbkg_patch\table_box
%   \useanchors is now defined, it will expand to commands that use anchors
    \hbadness\currhbadness \vbadness\currvbadness
    \useanchors \nospecanchors \llap{\curr_table}%
  }%
  \egroup
  \thistable{\relax}}
% ---
\def\tbkg_patch#1{% \tbkg_patch puts a color patch (background) on the top
% of a table given by #1; #1 is a csname of a table box; hmode is on.
 \speccastat
   {-\tablebkgcorr}{-\tablebkgcorr}{\specurcast\tbkgsuffix{\wd#1}{\ht#1}}%
 \speccastat
   {\tablebkgcorr}{\tablebkgcorr}{\specllcast\tbkgsuffix{0mm}{-\dp#1}}%
 \box#1% cast anchors while typesetting table #1
 \dotbkpatch % erase table; it seems better to use rather anchors than rules,
             % as rules spoil ``classic'' previewing; patching was introduced
             % in order to avoid the ``diffusion'' of a table contents
             % (1 pixel?). If you don't care, or you use high resolution,
             % you may wish to define \dotbkpatch as an empty command.
}
\def\tbkgsuffix{TbKg.SuF}% a suffix unlikely to be used by chance
% \tbkgsuffix, \tablebkgcorr, and \dotbkpatch can be virtualy
% exploited by a user, hence ``plain'' csnames were used; table background
% colour is most likely to be used by a user, hence it is set in \table_setup
\def\tablebkgcmyk#1{\def\thetablebkgcmyk{#1}}% set table background colour
\def\tablebkgcorr{0mm}% tiny correction of the position of the corners
                      % of the table background
\def\dotbkpatch{\specrect\tbkgsuffix\thetablebkgcmyk}
% ---
\def\nospecanchors{%
  \def\expa_expa_##1##2##3##4{\gdef\useanchors{}}%
  \def\speccastat##1##2##3{}%
%  \def\specrect##1##2{}%
%  \def\specdiagboth##1##2##3{}%
%  \def\specdiagdown##1##2##3{}%
%  \def\specdiagup##1##2##3{}%
%  \def\spectrianne##1##2{}%
%  \def\spectriannw##1##2{}%
%  \def\spectrianse##1##2{}%
%  \def\spectriansw##1##2{}%
}
% ---
\def\expa_expa_#1#2#3#4{%
% #1 -- \def or \gdef, #2, #3, #4 -- csnames
% performs #1#2{#3#4}, #3 and #4 expanded once;
% e.g., given \def\pp{ALA} \def\qq{BELA},
% \expa_expa_\def\pp\pp\qq defines \pp as a macro {ALABELA}
 \expandafter\expandafter\expandafter#1%
 \expandafter\expandafter\expandafter#2%
 \expandafter\expandafter\expandafter{%
 \expandafter#3#4}}
%
{\catcode`\p12 \catcode`\t12 \gdef\gobblePT#1pt{#1}}
\def\dimval#1{\expandafter\gobblePT\the#1}
%
\def\rectfill#1#2{%
  \noalign{
    \edef\useanchors_{\noexpand\specrect{#2}{#1}}
    \expa_expa_\gdef\useanchors\useanchors\useanchors_
  }}
\def\trianfill#1#{\noalign\bgroup\lowercase{\def\trian_dir{#1}}\trianfill_}
\def\trianfill_#1#2{%
  \edef\useanchors_{%
    \noexpand\csname pstrian\trian_dir \noexpand\endcsname{#2}{#1}}%
  \expa_expa_\gdef\useanchors\useanchors\useanchors_
  \egroup}
%
\newdimen\pen_wd
\def\diagstroke{\noalign\bgroup\afterassignment\diagstroke_\pen_wd}
\def\diagstroke_#1#{\lowercase{\def\diag_dir{#1}}\diagstroke__}
\def\diagstroke__#1#2{%
  \edef\useanchors_{%
    \noexpand\csname psdiag\diag_dir \noexpand\endcsname
      {#2}{#1}{\dimval\pen_wd}}%
  \expa_expa_\gdef\useanchors\useanchors\useanchors_
  \egroup}
%
\expandafter\def\csname pstrianne \endcsname{\spectrianne}
\expandafter\def\csname pstriannw \endcsname{\spectriannw}
\expandafter\def\csname pstrianse \endcsname{\spectrianse}
\expandafter\def\csname pstriansw \endcsname{\spectriansw}
\expandafter\def\csname psdiagboth \endcsname{\specdiagboth}
\expandafter\def\csname psdiagdown \endcsname{\specdiagdown}
\expandafter\def\csname psdiagup \endcsname{\specdiagup}
% ---
\def\anchoffset{.5\trth}% by default depends on current rule thickness
% ---
\def\speccastat#1#2#3{% cast anything (presumably a \special command)
                      % at a chosen position, not ignored in anchor tables
% #1 -- xoffset, #2 -- yoffset, #3 -- contents,
 \vbox to0mm{\vss\hbox to0mm{\kern#1\relax#3\hss}\kern#2}}
% ---
\def\castat#1#2#3{% cast anything at a chosen position, never ignored
% #1 -- xoffset, #2 -- yoffset, #3 -- contents, presumably a \special command
 \vbox to0mm{\vss\hbox to0mm{\kern#1\relax#3\hss}\kern#2}}
% ---
\def\specurcast#1#2#3{% cast upper right PostScript anchor
% #1 -- suffix, #2 -- xoffset, #3 -- yoffset
  \speccastat{#2}{#3}
    {\special{ps: currentpoint /yur.#1 exch def /xur.#1 exch def}}}
\def\specllcast#1#2#3{% cast lower left PostScript anchor
% #1 -- suffix, #2 -- xoffset, #3 -- yoffset
  \speccastat{#2}{#3}
    {\special{ps: currentpoint /yll.#1 exch def /xll.#1 exch def}}}
\def\specdiagboth#1#2#3{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
% #3 pen width in points, only the number (pt gobbled by, e.g., \dimval)
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    #3 72.27 div Resolution mul setlinewidth 1 setlinecap
    xll.#1 yll.#1 moveto xur.#1 yur.#1 lineto stroke
    xll.#1 yur.#1 moveto xur.#1 yll.#1 lineto stroke grestore}}
\def\specdiagdown#1#2#3{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
% #3 pen width in points, only the number (pt gobbled by, e.g., \dimval)
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    #3 72.27 div Resolution mul setlinewidth 1 setlinecap
    xll.#1 yur.#1 moveto xur.#1 yll.#1 lineto stroke grestore}}
\def\specdiagup#1#2#3{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
% #3 pen width in points, only the number (pt gobbled by, e.g., \dimval)
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    #3 72.27 div Resolution mul setlinewidth 1 setlinecap
    xll.#1 yll.#1 moveto xur.#1 yur.#1 lineto stroke grestore}}
\def\specrect#1#2{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    xll.#1 yll.#1 moveto
    xur.#1 yll.#1 lineto xur.#1 yur.#1 lineto xll.#1 yur.#1 lineto
    closepath fill grestore}}
\def\spectrianne#1#2{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    xll.#1 yur.#1 moveto xur.#1 yll.#1 lineto xur.#1 yur.#1 lineto
    closepath fill grestore}}
\def\spectriannw#1#2{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    xll.#1 yll.#1 moveto xur.#1 yur.#1 lineto xll.#1 yur.#1 lineto
    closepath fill grestore}}
\def\spectrianse#1#2{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    xll.#1 yll.#1 moveto xur.#1 yll.#1 lineto xur.#1 yur.#1 lineto
    closepath fill grestore}}
\def\spectriansw#1#2{%
% #1 index (suffix) of lower left and upper right corners (anchors)
% #2 cmyk coordinates
  \edef\cmyk_{#2}%
  \special{ps: gsave newpath \ifx\cmyk_\empty\else #2 setcmykcolor \fi
    xll.#1 yll.#1 moveto xur.#1 yll.#1 lineto xll.#1 yur.#1 lineto
    closepath fill grestore}}

\newcount\col_num
\newtoks\col_tok

\def\uranchor{\noalign\bgroup \afterassignment\uranchor_ \global\col_num}
\def\uranchor_{%
     \global\advance\col_num-1\relax
     \ifnum\col_num<0 \global\let\uranchor__\urfullanchor
     \else \global\let\uranchor__\urcellanchor \fi
  \egroup \uranchor__}
\def\llanchor{\noalign\bgroup \afterassignment\llanchor_ \global\col_num}
\def\llanchor_{%
     \global\advance\col_num-1\relax
     \ifnum\col_num<0 \global\let\llanchor__\llfullanchor
     \else \global\let\llanchor__\llcellanchor \fi
  \egroup \llanchor__}
\def\urfullanchor#1{% \wd\table_box is set by \midanchtable
  \br{\speccastat{\wd\table_box}{0mm}
        {\specurcast{#1}{-\anchoffset}{\anchoffset}}}%
  \er{}}
\def\llfullanchor#1{%
  \br{\speccastat{0mm}{0mm}{\specllcast{#1}{\anchoffset}{-\anchoffset}}}%
  \er{}}
\def\urcellanchor#1{%
  \br{}
  \col_tok{}%
  \loop \ifnum\col_num>0
    \global\advance\col_num-1 \col_tok\expandafter{\the\col_tok"}%
  \repeat
  \the\col_tok
  \hskip0ptplus1filll
  \speccastat
    {\ifignorecellstate 1\else\ifcase\cellstate 2\or 1\or 0\fi\fi
     \cellcorr}{0mm}
       {\speccastat
        {\cellmarg}{0mm}{\specurcast{#1}{\anchoffset}{\anchoffset}}%
  }%
  \er{}}
\def\llcellanchor#1{%
  \br{}
  \loop \ifnum\col_num>0
    \global\advance\col_num-1 \col_tok\expandafter{\the\col_tok"}%
  \repeat
  \the\col_tok
  \speccastat
    {-\ifignorecellstate 1\else\ifcase\cellstate 2\or 1\or 0\fi\fi
     \cellcorr}{0mm}
      {\speccastat
        {-\cellmarg}{0mm}{\specllcast{#1}{-\anchoffset}{-\anchoffset}}%
  }%
  \hskip0ptplus1filll\null
  \er{}}

% ====== ``floating point arithmetic'' (excerpted from T. Rokicki): =======
%
% ^  r  y
% |
% |
% |
% |
% 0-----------> t   x
%
\def\resize
  % dimen registers:
  #1% y   make y such that y/r=x/t
  #2% r
  #3% x
  #4% t
%   We have a sticky problem here:  TeX doesn't do floating point arithmetic!
%   Our goal is to compute y = rx/t. The following loop does this reasonably
%   fast, with an error of at most about 16 sp (about 1/4000 pt).
  {%
  % save parameters to the internal variables:
  \dim_r#2\relax \dim_x#3\relax \dim_t#4\relax
  \dim_tmp=\dim_r \divide\dim_tmp\dim_t
  \dim_y=\dim_x \multiply\dim_y\dim_tmp
  \multiply\dim_tmp\dim_t \advance\dim_r-\dim_tmp
  \dim_tmp=\dim_x
  \loop \advance\dim_r\dim_r \divide\dim_tmp 2
  \ifnum\dim_tmp>0
    \ifnum\dim_r<\dim_t\else
      \advance\dim_r-\dim_t \advance\dim_y\dim_tmp \fi
  \repeat
  % assign result:
  #1\dim_y\relax}
%
\newdimen\dim_x    % horizontal size after scaling
\newdimen\dim_y    % vertical size after scaling
\newdimen\dim_t    % horizontal size before scaling
\newdimen\dim_r    % vertical size before scaling
\newdimen\dim_tmp % register for arithmetic manipulation

% ============================== Postamble ================================
\catcode`\"\dblquotecatcode
\catcode`\|\brokenbarcatcode
\catcode`\!\exclamcatcode
\catcode`\_=\undscorecatcode
\catcode`\@=\atcatcode

\endinput
