% +--------------------------------------------------------------------+
% ]                                                                    ]
% ]                           TABLES.TEX                               ]
% ]                                                                    ]
% ]                     Ray F. Cowan  15-Feb-85                        ]
% ]                                                                    ]
% ]                       Princeton University                         ]
% ]                                                                    ]
% ]                     Last Revision: 21-Nov-85                       ]
% ]                                                                    ]
% ]   Macros I find handy for making tables.  See TABLEDOC TEX for     ]
% ]   a longer description.  The token-counting macros are straight    ]
% ]   from the TeXbook's "Dirty Tricks" appendix.                      ]
% ]                                                                    ]
% +--------------------------------------------------------------------+
%
\newbox\hdbox%
\newcount\hdrows%
\newcount\multispancount%
\newcount\ncase%
\newcount\ncols% This is the number of primary text columns in the table.
\newcount\nrows%
\newcount\nspan%
\newcount\ntemp%
\newdimen\hdsize%
\newdimen\newhdsize%
\newdimen\parasize%
\newdimen\spreadwidth%
\newdimen\thicksize%
\newdimen\thinsize%
\newdimen\tablewidth%
\newif\ifcentertables%
\newif\ifendsize%
\newif\iffirstrow%
\newif\iftableinfo%
\newtoks\dbt%
\newtoks\hdtks%
\newtoks\savetks%
\newtoks\tableLETtokens%
\newtoks\tabletokens%
\newtoks\widthspec%
%
%  Book-keeping stuff--see how often these macros are called.
%
%\immediate\write15{%
%CP SMSG GJMSINK TEXTABLE --> TABLE MACROS V. 851121 JOB = \jobname%
%}%
%
%  Turn on table diagnostics.
%
\tableinfotrue%
\catcode`\@=11%  Allows use of "@" in macro names, like PLAIN.TEX does.
\def\out#1{\immediate\write16{#1}}%  Debugging aid.  Writes #1 on the
%                                    user's terminal and in the log file.
%
%  Define the \tstrut height, depth in terms of the x_height parameter.
%
\def\tstrut{\vrule height3.1ex depth1.2ex width0pt}%
\def\and{\char`\&}%  Allows us to get an `&' in the text.  This is the
%                    same as using the PLAIN TeX macro \&.
\def\tablerule{\noalign{\hrule height\thinsize depth0pt}}%
\thicksize=1.5pt%  Default thickness for fat rules.  The user should feel
%                  free to change this to his preference.
\thinsize=0.6pt%   Default thickness for thin rules.
\def\thickrule{\noalign{\hrule height\thicksize depth0pt}}%
\def\hrulefill{\leaders\hrule\hfill}%
\def\bigrulefill{\leaders\hrule height\thicksize depth0pt \hfill}%
\def\ctr#1{\hfil\ #1\hfil}%
\def\altctr#1{\hfil #1\hfil}%
\def\vctr#1{\hfil\vbox to0pt{\vss\hbox{#1}\vss}\hfil}%
%
%  Here are things for controlling the width of the finished table.
%
\tablewidth=-\maxdimen%
\spreadwidth=-\maxdimen%
\def\tabskipglue{0pt plus 1fil minus 1fil}%
%
%  Stuff for centering or not.
%
\centertablestrue%
\def\centeredtables{%
   \centertablestrue%
}%
\def\noncenteredtables{%
   \centertablesfalse%
}%
%
%  \vctr vertically centers its argument in the row.
%
\parasize=4in%
\long\def\para#1{%  Used to make little paragraphs out of one entry.
   {%
      \vtop{%
         \hsize=\parasize%
         \baselineskip14pt%
         \lineskip1pt%
         \lineskiplimit1pt%
         \noindent #1%
         \vrule width0pt depth6pt%
      }%
   }%
}%
%
\gdef\ARGS{########}%  Produces the correct number of #'s in the preamble
%                      by the time eveything is expanded and \halign sees
%                      it.
\gdef\headerARGS{####}%  Same as \ARGS, but used in \header macros.
\def\@mpersand{&}%  Allows us to get alignment tab characters later
%                   when we have made the character "&" an active macro.
{\catcode`\|=13%  Make |'s locally active.
\gdef\letbarzero{\let|0}%  Globally define a macro that allows us to
%                          keep active |'s from being expanded in edef's.
\gdef\letbartab{\def|{&&}}%
\gdef\letvbbar{\let\vb|}%
%  This \def will cause active |'s read by
%                            \ruledtable to be converted into double
%                            alignment tabs.
}%  End of locally active |'s.
%
{\catcode`\&=4%  Make these alignment tabs.
\def\ampskip{&\omit\hfil&}%  This local macro skips a vertical rule.
\catcode`\&=13%  Now make &'s into active macros.
\let&0%  This allows us to expand \ampskip in the next \xdef without
%        attempting to expand the & and getting an "undefined control
%        sequence" error.
\xdef\letampskip{\def&{\ampskip}}%
\gdef\letnovbamp{\let\novb&\let\tab&}
%  This will cause active &'s read by
%                                   \ruledtable to be converted into
%                                   double tabs and an \omit'ted \vrule.
}%  End of locally active &'s.
%
\def\begintable{%  Here we make |'s and &'s active characters so we can
%                  interpret them as macros.  Note that this action is
%                  true only until we encounter the matching \endgroup
%                  token later at the end of the \ruledtable macro.
   \begingroup%
   \catcode`\|=13\letbartab\letvbbar%
   \catcode`\&=13\letampskip\letnovbamp%
   \def\multispan##1{%  We must redefine \multispan to count the number
%                       of primary columns, not physical columns.
      \omit \mscount##1%
      \multiply\mscount\tw@\advance\mscount\m@ne%
      \loop\ifnum\mscount>\@ne \sp@n\repeat%
   }%  End of \multispan macro.
   \def\|{%
      &\omit\widevline&%
   }%
   \ruledtable%  Now we call \ruledtable to do the real work.
}%  End of \begintable macro.
%
\long\def\ruledtable#1\endtable{%
%
%  This macro reads in the user's data entries
%  and converts them into a ruled table.
%
%  Important note:  Many macros and parameters are re-defined here, and
%  these must be kept local to the table macros to avoid conflict with
%  their use outside of tables.  This is done by the \begingroup token
%  macro \begintable and the \endgroup token at the end of
%  this macro.
%
   \offinterlineskip%  Needed to make rules touch each other.
   \tabskip 0pt%  Needed for same reason as \offinterlineskip.
   \def\widevline{\vrule width\thicksize}%  Make outer \vrule's wider.
   \def\endrow{\@mpersand\omit\hfil\crnorm\@mpersand}%
   \def\crthick{\@mpersand\crnorm\thickrule\@mpersand}%
   \def\crnorule{\@mpersand\crnorm\@mpersand}%
   \let\nr=\crnorule%  A shorter abbreviation.
   \def\endtable{\@mpersand\crnorm\thickrule}%
%
   \let\crnorm=\cr%  Allows us to use \cr for our own purposes.
%
%  Cause user-typed \cr's to follow a row with a \tablerule.
%
   \edef\cr{\@mpersand\crnorm\tablerule\@mpersand}%
%
   \the\tableLETtokens%  Get the user's extra \let's, if any.
%
%  Put the data entries into a token register so we can scan through them
%  and see what the user is asking us to do.
%
   \tabletokens={&#1}%  We add an extra alignment tab to the beginning
%                       of the first row to allow for the first \vrule.
%
%  Now count how many rows are in the table and return the result in
%  count register \nrows; do the same for columns, and return that
%  in register \ncols.
%
   \countROWS\tabletokens\into\nrows%
   \countCOLS\tabletokens\into\ncols%
%
%  Now do a little arithmetic to convert the number of primary columns
%  into the number of physical columns that the alignment preamble must
%  prepare for;  similarly for rows.
%
   \advance\ncols by -1%
   \divide\ncols by 2%
   \advance\nrows by 1%
%
%  Tell the user how many rows and columns we found in his data, if he
%  wants to know.
%
   \iftableinfo %
      \immediate\write16{[Nrows=\the\nrows, Ncols=\the\ncols]}%
   \fi%
%
%  Now we actually go ahead and produce the table.
%
   \ifcentertables
      \ifhmode \par\fi%  Make sure we are in vertical mode.
      \line{%  The final table comes out as an \hbox of width the \hsize.
      \hss%  The final table will be centered left-to-right.
   \else %
      \hbox{%
   \fi
      \vbox{%
         \makePREAMBLE{\the\ncols}%  Generate the preamble.
         \edef\next{\preamble}%  This line and the next line force the
         \let\preamble=\next%    expansion of all \ARGS tokens into the
%                                appropriate number of #'s.
         \makeTABLE{\preamble}{\tabletokens}%  Go do the \halign here.
      }%  End of \vbox.
      \ifcentertables \hss}\else }\fi%  Finish the centering effect.
%                                       It is important that no spaces
%                                       follow the two `}' here.
%  }%  End of \line.
   \endgroup%  Return all local macros and parameters to their outside
%              values.
   \tablewidth=-\maxdimen%  Reset \tablewidth to normal.
   \spreadwidth=-\maxdimen% Same for \spreadwidth.
}%  End of macro \ruledtable.
%
\def\makeTABLE#1#2{%  Does an \halign for the \ruledtable macro.
   {%  Start of local parameter values.
%
   \let\ifmath0%     These macros would cause trouble if they were to be
   \let\header0%     expanded in the following \xdef; we \let them be
   \let\multispan0%  equal to a digit, because digits can't be expanded.
%
%  Set up the width specification here.
%
   \ncase=0%
   \ifdim\tablewidth>-\maxdimen \ncase=1\fi%
   \ifdim\spreadwidth>-\maxdimen \ncase=2\fi%
   \relax%  This \relax is absolutely necessary, without it the following
%           \ifcase will always take \ncase=0.
%
   \ifcase\ncase %
      \widthspec={}%
   \or %
      \widthspec=\expandafter{\expandafter t\expandafter o%
                 \the\tablewidth}%
   \else %
      \widthspec=\expandafter{\expandafter s\expandafter p\expandafter r%
                 \expandafter e\expandafter a\expandafter d%
                 \the\spreadwidth}%
   \fi %
%\out{Widthspec=O\the\widthspecE}%
%\out{Preamble=O\preambleE}%
   \xdef\next{%  We must force the preamble to be expanded BEFORE the
      \halign\the\widthspec{%
%        \halign is done;  this \edef\next{...}\next construction
%                does the trick.
      #1%  This is the preamble text.
%
      \noalign{\hrule height\thicksize depth0pt}%  Makes the top \hrule.
%
      \the#2\endtable%  This is the main body.
%
%     \noalign{\hrule height0.7pt depth0pt}%  Makes the last \hrule.
      }%  End of \halign.
   }%  End of \next.
   }%  End of local values.
   \next%  This \next must be outside of the local values, because now
%          we want those troublesome macros in the \let's above to have
%          their normal actions.
}%  End of macro \makeTABLE.
%
\def\makePREAMBLE#1{%  This macro generates the necessary preamble for a
%                      ruled table with #1 primary columns.
%                      (Primary columns means the number of columns NOT
%                       counting those used for vertical rules.)
   \ncols=#1%  Get the number of columns desired.
   \begingroup%  Start local parameter definitions.
   \let\ARGS=0%  This is the key to the whole thing; it prevents \ARGS
%                from being expanded in the following \edef's.
   \edef\xtp{\widevline\ARGS\tabskip\tabskipglue%
   &\ctr{\ARGS}\tstrut}%  A 1-column preamble.  Gets the sizing right.
   \advance\ncols by -1%  One column has been generated; decrement the
%                         counter.
   \loop%  Append as many further columns as needed to the preamble.
      \ifnum\ncols>0 %
      \advance\ncols by -1%
      \edef\xtp{\xtp&\vrule width\thinsize\ARGS&\ctr{\ARGS}}%
   \repeat
   \xdef\preamble{\xtp&\widevline\ARGS\tabskip0pt%
   \crnorm}%  Adds the last \vrule.
   \endgroup%  End of local parameters.
}%  End of macro \makePREAMBLE.
%
\def\countROWS#1\into#2{%  This counts the number of rows in #1 by
%                          looking for control sequences that end a row,
%                          e.g., \cr, \crthick, etc., and puts the result
%                          into count register #2.
   \let\countREGISTER=#2%
   \countREGISTER=0%
%  \out{In countROWS:  tokens are O\the#1E}%
   \expandafter\ROWcount\the#1\endcount%
}%
%
\def\ROWcount{%
   \afterassignment\subROWcount\let\next= %
}%
\def\subROWcount{%
%  \out{In subROWcount:  next is O\meaning\nextE}%  Debugging aid.
   \ifx\next\endcount %
      \let\next=\relax%
   \else%
      \ncase=0%
      \ifx\next\cr %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\endrow %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\crthick %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\crnorule %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\header %
%     \out{In subROWcount:  next=header, ncase set=1}%
         \ncase=1%
      \fi%
%     \out{In subROWcount:  ncase is O\the\ncaseE}%
      \relax%
      \ifcase\ncase %
         \let\next\ROWcount%
%        \out{subROWcount---> ncase=\the\ncase}%
      \or %
         \let\next\argROWskip%
%        \out{subROWcount---> ncase=\the\ncase}%
      \else %
      \fi%
   \fi%
%  \out{subROWcount---> NEXT=\meaning\next}%
   \next%
}%  End of macro \subROWcount.
%
\def\counthdROWS#1\into#2{%
\dvr{10}%
   \let\countREGISTER=#2%
   \countREGISTER=0%
\dvr{11}%
%  \out{In counthdROWS:  tokens are O\the#1E}%
\dvr{13}%
   \expandafter\hdROWcount\the#1\endcount%
\dvr{12}%
}%
%
\def\hdROWcount{%
   \afterassignment\subhdROWcount\let\next= %
}%
\def\subhdROWcount{%
%\out{In subhdROWcount:  next is O\meaning\nextE}%
   \ifx\next\endcount %
      \let\next=\relax%
   \else%
      \ncase=0%
      \ifx\next\cr %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\endrow %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\crthick %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\crnorule %
         \global\advance\countREGISTER by 1%
         \ncase=0%
      \fi%
      \ifx\next\header %
%\out{In subhdROWcount:  next=header, ncase set=1}%
         \ncase=1%
      \fi%
%\out{In subhdROWcount:  ncase is O\the\ncaseE}%
\relax%
      \ifcase\ncase %
         \let\next\hdROWcount%
%\out{subhdROWcount---> ncase=\the\ncase}%
      \or%
         \let\next\arghdROWskip%
%\out{subhdROWcount---> ncase=\the\ncase}%
      \else %
      \fi%
   \fi%
%\out{subhdROWcount---> NEXT=\meaning\next}%
   \next%
}%
%
{\catcode`\|=13\letbartab
\gdef\countCOLS#1\into#2{%
%  \out{In countCOLS:  tokens are O\the#1E}
   \let\countREGISTER=#2%
   \global\countREGISTER=0%
   \global\multispancount=0%
   \global\firstrowtrue
   \expandafter\COLcount\the#1\endcount%
   \global\advance\countREGISTER by 3%
   \global\advance\countREGISTER by -\multispancount
%  \out{countCOLS-->O\the\countREGISTERE}
}%
%
\gdef\COLcount{%
   \afterassignment\subCOLcount\let\next= %
}%
{\catcode`\&=13%
\gdef\subCOLcount{%
%\out{In subCOLcount: next is O\meaning\nextE}
   \ifx\next\endcount %
      \let\next=\relax%
   \else%
      \ncase=0%
      \iffirstrow
         \ifx\next& %
            \global\advance\countREGISTER by 2%
            \ncase=0%
         \fi%
         \ifx\next\span %
            \global\advance\countREGISTER by 1%
            \ncase=0%
         \fi%
         \ifx\next| %
            \global\advance\countREGISTER by 2%
            \ncase=0%
         \fi
         \ifx\next\|
            \global\advance\countREGISTER by 2%
            \ncase=0%
         \fi
         \ifx\next\multispan
            \ncase=1%
            \global\advance\multispancount by 1%
         \fi
         \ifx\next\header
            \ncase=2%
         \fi
         \ifx\next\cr       \global\firstrowfalse \fi
         \ifx\next\endrow   \global\firstrowfalse \fi
         \ifx\next\crthick  \global\firstrowfalse \fi
         \ifx\next\crnorule \global\firstrowfalse \fi
      \fi%  End of \iffirstrow.
\relax%\out{subCOL-->  ncase=O\the\ncaseE}
% \out{subCOL-->  next=\meaning\next}
      \ifcase\ncase %
         \let\next\COLcount%
      \or %
         \let\next\spancount%
      \or %
         \let\next\argCOLskip%
      \else %
      \fi %
   \fi%
%  \out{subCOL-->  countREGISTER=O\the\countREGISTERE}
   \next%
}%
\gdef\argROWskip#1{%
%  Deletes the next balanced, undelimited argument from a
%                 token list.
% \out{---> Entering argROWskip <---}
% \out{In argROWskip:  deleted arg is O#1E}%
   \let\next\ROWcount \next%
}%  End of macro \argskip.
\gdef\arghdROWskip#1{%
%  Deletes the next balanced, undelimited argument from a
%                 token list.
% \out{---> Entering arghdROWskip <---}
% \out{In arghdROWskip:  deleted arg is O#1E}%
   \let\next\ROWcount \next%
}%  End of macro \arghdROWskip.
\gdef\argCOLskip#1{%
%  Deletes the next balanced, undelimited argument from a
%                 token list.
% \out{---> Entering argCOLskip <---}
% \out{In argCOLskip:  deleted arg is O#1E}%
   \let\next\COLcount \next%
}%  End of macro \argskip.
}%  End of active &'s.
}%  End of active |'s.
\def\spancount#1{%\out{spancount--->\meaning#1}
   \nspan=#1\multiply\nspan by 2\advance\nspan by -1%
   \global\advance \countREGISTER by \nspan
%  \out{number spancount--->\the\nspan; \the\countREGISTER}
   \let\next\COLcount \next}%
%
%\def\dvr#1{\vrule width 1.0pt depth 0pt height 12pt$_{#1}$}
\def\dvr#1{\relax}%
% \omit\hfil%
% \parindent=0pt\hsize=1.1in\valign{%
% \vfil#\vfil&\vfil#\vfil\cr\hfil\hbox{\ Added to\ }\hfil&%
% \hfil\hbox{\ empty events\ }\hfil\cr}\hfil%
\def\header#1{%
\dvr{1}{\let\cr=\@mpersand%
\hdtks={#1}%
%\out{In header:  hdtks=O\the\hdtksE}%
\counthdROWS\hdtks\into\hdrows%
\advance\hdrows by 1%
\ifnum\hdrows=0 \hdrows=1 \fi%
%\out{In header:  Nhdrows=O\the\hdrowsE}%
\dvr{5}\makehdPREAMBLE{\the\hdrows}%
%\out{In header:  headerpreamble=O\headerpreambleE}%
\dvr{6}\getHDdimen{#1}%
%\out{In header:  hdsize=O\the\hdsizeE}%
%\striplastCR{#1}%
{\parindent=0pt\hsize=\hdsize{\let\ifmath0%
\xdef\next{\valign{\headerpreamble #1\crnorm}}}\dvr{7}\next\dvr{8}%
}%
}\dvr{2}}%  End of macro \header.
%\def\striplastCR#1\cr{\xdef\headerbody{#1}}%
\def\makehdPREAMBLE#1{%This macro generates the necessary preamble for a
\dvr{3}%
%                      ruled table with \ncols primary columns.
%                      (Primary columns means the number of columns NOT
%                       counting those used for vertical rules.
\hdrows=#1%  Get the number of columns desired.
{%  Start local parameter definitions.
\let\headerARGS=0%
%  This is the key to the whole thing; it prevents \ARGS
\let\cr=\crnorm%
%                from being expanded in the followin \edef's.
\edef\xtp{\vfil\hfil\hbox{\headerARGS}\hfil\vfil}%
\advance\hdrows by -1%  One row has been generated; decrement the
%                         counter.
\loop%  Append as many further rows as needed to the preamble.
\ifnum\hdrows>0%
\advance\hdrows by -1%
\edef\xtp{\xtp&\vfil\hfil\hbox{\headerARGS}\hfil\vfil}%
\repeat%
\xdef\headerpreamble{\xtp\crcr}%
}%  End of local parameters.
\dvr{4}}%  End of \makehdPREAMBLE.
%
\def\getHDdimen#1{%
%\out{In getHDdimen:  Arg 1=O#1E}%
\hdsize=0pt%
\getsize#1\cr\end\cr%
}%  End of macro getHDdimen.
\def\getsize#1\cr{%
%\out{In getsize:  Arg 1=O#1E}%
%  Here we have to check arg#1 and see if the first token in #1 is an
%    \end; if so, we stop, else we check the width of arg#1.
%  We recall that each arg#1 will be terminated with a \cr token.
\endsizefalse\savetks={#1}%
%\out{In getsize:  the savetks = O\the\savetksE}%
\expandafter\lookend\the\savetks\cr%
%\out{In getsize:  ifendsize = O\meaning\ifendsizeE}%
\relax \ifendsize \let\next\relax \else%
\setbox\hdbox=\hbox{#1}\newhdsize=1.0\wd\hdbox%
\ifdim\newhdsize>\hdsize \hdsize=\newhdsize \fi%
%\out{In getsize:  hdsize=O\the\hdsizeE}%
%\out{In getsize:  newhdsize=O\the\newhdsizeE}%
\let\next\getsize \fi%
\next%
}%
\def\lookend{\afterassignment\sublookend\let\looknext= }%
\def\sublookend{\relax%
%\out{In sublookend:  looknext = O\looknextE}%
\ifx\looknext\cr %
%\out{In sublooknext:  looknext=cr}%
\let\looknext\relax \else %
%\out{In sublooknext:  looknext/=cr}%
   \relax
   \ifx\looknext\end \global\endsizetrue \fi%
   \let\looknext=\lookend%
    \fi \looknext%
}%
%
%  Allow the user to make his own names for crthick, etc.
%
\def\tablelet#1{%
   \tableLETtokens=\expandafter{\the\tableLETtokens #1}%
}%
\catcode`\@=12%  Change @'s back to their normal category code.
%
