% \iffalse meta-comment
%
% Copyright (C) 1993-2026
%
% The LaTeX Project and any individual authors listed elsewhere
% in this file.
%
% This file is part of the Standard LaTeX `Tools Bundle'.
% -------------------------------------------------------
%
% It may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
%    https://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% The list of all files belonging to the LaTeX `Tools Bundle' is
% given in the file `manifest.txt'.
%
% \fi
%\iffalse   % this is a METACOMMENT !
%
%% Package `array' to use with LaTeX 2e
%% Copyright (C) 1989-1998 Frank Mittelbach, all rights reserved.
%
%<+package>
% For anything before 2016-10-06 we load the 2016 version and hope for the best:
%<+package>\DeclareRelease{}{1994-06-01}{array-2016-10-06.sty}
%
%<+package>\DeclareRelease{}{2016-10-06}{array-2016-10-06.sty}
%<+package>\DeclareRelease{v2.4}{2020-02-10}{array-2020-02-10.sty}
%<+package>\DeclareRelease{v2.5}{2023-11-01}{array-2023-11-01.sty}
%<+package>\DeclareRelease{v2.6}{2024-06-01}{array-2024-06-01.sty}
%<+package>\DeclareCurrentRelease{}{2026-06-01}
%<+package>
%    \end{macrocode}
%    Current version needs a new kernel.
%    \begin{macrocode}
%<+package>\NeedsTeXFormat{LaTeX2e}[2026/06/01]
%<+package>\ProvidesPackage{array}
%<+package>         [2026/02/24 v2.7b Tabular extension package (FMi)]
%
% \fi
%
%%
%
% \providecommand\hook[1]{\texttt{#1}}
%
% \changes{v2.4g}{2018/04/07}{Renamed internal \cs{mcell@box} to
%       \cs{ar@mcellbox} and \cs{align@mcell} to \cs{ar@align@mcell}
%        to avoid conflict with makecell package}
%
% \changes{v2.4c}{2008/09/09}{(WR) Typo fix in documentation}
%
% \changes{v2.3c}{1995/11/02}{(DPC) minor doc changes}
%
% \changes{v2.3a}{1994/10/16}{Added code for \cs{firsthline} and
%                             friends}
%
% \changes{v2.2c}{1994/03/14}{removed check for \cs{@tfor} bug}
%
% \changes{v1.0b}{1987/06/04}{`@classi (faster),
%                        `@classvi (new)   A in preamble means
%                                         \&\& in `halign.}
%
% \changes{v1.1a}{1987/07/05}{New concept:
%                        preamblechar: c,l,r,C,L,R,A,p,t,{\tt !|},@,!!}
% \changes{v1.1b}{1987/09/21}{Again p like original \LaTeX{} and z for
%                        centered `parbox.}
%
% \changes{v1.2a}{1987/09/27}{Completely new implementation.}
% \changes{v1.2b}{1987/10/06}{{\tt !|} does no longer generate space at
%                        start or end of the preamble. Otherwise `hline
%                        is too long.}
% \changes{v1.2b}{1987/10/06}{Enlarged `@arstrutbox by 1pt (Test-Impl)
%                         with dimen `@strutheight.}
% \changes{v1.2c}{1987/10/22}{New dimen parameter `extrarowheight
%                        (default: 0pt).}
% \changes{v1.2c}{1987/10/22}{Enlarged `@arstrutbox by `extrarowheight.
%                        Thus you may avoid large characters to
%                        overprint a `hline.}
% \changes{v1.2c}{1987/10/22}{Introduced `m@th in `@array to allow
%                         non-zero values of `mathsurround.}
% \changes{v1.2d}{1987/11/02}{Completed the documentation.}
% \changes{v1.2e}{1987/11/03}{Bug fixed: A at start of preamble resulted
%                        in an error since `@mkpream generated
%                        `@arstrut \& ... as a preamble.}
% \changes{v1.2f}{1987/11/09}{`@testpach documented.}
%
% \changes{v1.3a}{1987/11/11}{Again a new implementation, with a new
%                        concept (cf. the documentation).}
% \changes{v1.3b}{1988/03/17}{`@decl expands now into `@empty, i.e., it
%                        disappears when the preamble is generated,
%                        except when the user specifies A\{\} or
%                        B\{\}.}
%
% \changes{v1.4a}{1988/03/18}{Test implementation of use of token
%                        registers in order to do without `protect.}
% \changes{v1.4b}{1988/03/19}{Changed erroneous class numbers:
%                        5 -!> 6
%                        6 -!> 7
%                        7 -!> 5
%                        Corresponding changes in the macros.}
% \changes{v1.4c}{1988/03/19}{Everything except p,z now works with token
%                        registers.}
%
% \changes{v1.9a}{1988/03/20}{Last (so I hope) major change: 1) Options
%                        B,A now called !>,<.  These options now point
%                        to the column they modify.}
% \changes{v1.9a}{1988/03/20}{2) `protect is no longer necessary. But
%                           still the macro `@expast needs top be
%                          modified. `multicolumn still does not work.}
% \changes{v1.9b}{1988/04/29}{inserted missing `fi in `@testpach.
%                        Corrected \LaTeX bug in `@tfor.}
% \changes{v1.9c}{1988/05/07}{Re-introduced `@endpbox.
%                        `multicolumn now works!! Version number still
%                        1.9 since the documentation is still not
%                        finished.}
% \changes{v1.9c}{1988/05/07}{1) `def `the@toks \{`the ...\} remaining
%                         only in `@mkpream.  2) Removed `@classiii and
%                        replaced by `save@decl.}
% \changes{v1.9c}{1988/05/07}{3) `insert@column contains only `@tempcnta
%                        and `count@ counters.  4) `@@startpbox and
%                        `@@endpbox now totally obsolete.}
% \changes{v1.9d}{1988/05/10}{Replaced `number by `the where the `toks
%                        registers' contents are used.}
% \changes{v1.9e}{1988/05/11}{Re-introduced `@xargarraycr and
%                        `@yargarraycr, since `endtemplate seems to
%                        be `outer.}
% \changes{v1.9f}{1988/05/20}{Small changes finally carried out:
%                        1) `par!=`@empty.
%                        2) \{..ifnum0!=!`\}...  $\to$ `bgroup and
%                           analogously `egroup.}
% \changes{v1.9g}{1988/02/24}{Inserted again \{..ifnum0!=!`\}..,
%                        c.f. Appendix D of the \protect\TeX{}book.}
% \changes{v1.9h}{1988/06/28}{No longer necessary to read in the file
%                        twice.}
% \changes{v1.9i}{1988/06/28}{Corrected typo in german version.}
% \changes{v1.9j}{1988/11/23}{In a `r' column an extra `kern`z@ is
%                        needed.}
% \changes{v1.9j}{1988/11/23}{Otherwise the `hfil on the left side
%                        will be removed by the `unskip in
%                        `insert@column if the entry is empty.}
% \changes{v1.9k}{1988/06/28}{Corrected typo in german version.}
% \changes{v1.9k}{1989/01/16}{`begin{Macro} changed to `begin{macro} in
%                        documentation.}
%
% \changes{v2.0a}{1989/05/12}{\cs{@thetoks} changed to \cs{the@toks}.}
% \changes{v2.0a}{1989/05/12}{source changed to reflect new doc.sty
%                          conventions.}
% \changes{v2.0a}{1989/05/12}{t option renamed to p to be compatible to
%                        the original.}
% \changes{v2.0a}{1989/05/12}{File renamed from arraye.sty to
%                        array.sty.}
% \changes{v2.0b}{1989/05/17}{Three forgotten end macro added.}
% \changes{v2.0b}{1989/05/17}{All lines shortened to 72 or less.}
% \changes{v2.2a}{1994/02/03}{Upgrade to \LaTeXe}
%
% \DoNotIndex{\@depth,\@ehc,\@fortmp,\@height,\@ifnextchar,\@ifstar}
% \DoNotIndex{\@ifundefined,\@ne,\@nil,\@tempa,\@tempb}
% \DoNotIndex{\@tempcnta,\@tempd,\@tempdima,\@whilenum,\@width,\\}
% \DoNotIndex{\@tforloop}
% \DoNotIndex{\advance}
% \DoNotIndex{\baselineskip,\begingroup,\bgroup}
% \DoNotIndex{\cr,\crcr,\csname}
% \DoNotIndex{\def,\do,\docdate,\dp}
% \DoNotIndex{\edef,\egroup,\else,\endcsname,\endinput,\expandafter}
% \DoNotIndex{\fi,\filedate,\fileversion}
% \DoNotIndex{\gdef}
% \DoNotIndex{\hbox,\hfil,\hsize,\hskip,\ht}
% \DoNotIndex{\if,\ifcase,\ifdim,\ifnum,\ifx,\ignorespaces}
% \DoNotIndex{\kern}
% \DoNotIndex{\leavevmode,\let,\lineskip}
% \DoNotIndex{\m@ne,\multispan}
% \DoNotIndex{\newcount,\newdimen,\noalign}
% \DoNotIndex{\or}
% \DoNotIndex{\relax}
% \DoNotIndex{\setbox,\space,\strutbox}
% \DoNotIndex{\tabskip,\thr@@,\the,\toks,\toks@,\tw@,\typeout}
% \DoNotIndex{\unhcopy,\unskip}
% \DoNotIndex{\vbox,\vcenter,\vline,\vrule,\vtop,\vskip}
% \DoNotIndex{\xdef}
% \DoNotIndex{\z@}
%
%
% \providecommand\env[1]{\texttt{#1}}
%
%
% \GetFileInfo{array.sty}
%
% \title{A new implementation of \LaTeX's \textsf{tabular}
%        and \textsf{array} environment\thanks{This file
%        has version number \fileversion, last
%        revised \filedate.}}
% \author{Frank Mittelbach
%         \and
%         David Carlisle\thanks{David kindly agreed on the inclusion
%         of the \texttt{\textbackslash{}newcolumntype} implementation,
%         formerly in
%         \texttt{newarray.sty} into this package.}}
%
% \date{Printed \today}
%
% \MaintainedByLaTeXTeam{tools}
% \maketitle
%
%
%
% \MakeShortVerb{\=}
%
% \begin{abstract}
% This article describes an extended implementation of the \LaTeX\
% \textsf{array}-- and \textsf{tabular}--environments. The special
% merits of this implementation are further options to format columns
% and the fact that fragile \LaTeX--commands don't have to be
% =\protect='ed any more within those environments.
%
% The major part of the code for this package dates back to 1988---so
% does some of its documentation.
% \end{abstract}
%
%
%
% \section{Introduction}
%
% This new implementation of the \textsf{array}-- and
% \textsf{tabular}--environments is part of a larger project in which
% we are trying to improve the \LaTeX\--code in some aspects and to
% make \LaTeX\ even easier to handle.
%
% The reader should be familiar with the general structure of the
% environments
% mentioned above. Further information can be found in
% \cite{bk:lamport} and \cite{bk:GMS94}.
% The additional options which can be used in the
% preamble as well as those which now have a slightly different meaning
% are described in table~\ref{tab:opt}.
%
% \DescribeMacro\extrarowheight
% Additionally we introduce a new
% parameter called =\extrarowheight=. If it takes a positive
% length, the value of the parameter is added to the normal height of
% every row of the table, while
% the depth will remain the same. This is important for tables
% with horizontal lines because those lines normally touch the
% capital letters.
% For example, we used =\setlength{\extrarowheight}{1pt}=
% in table~\ref{tab:opt}.
%
% \begin{table}[!t]
% \begin{center}
%    \setlength{\extrarowheight}{1pt}
%    \begin{tabular}{|>{\ttfamily}c|m{9cm}|}
%       \hline
%     \multicolumn{2}{|c|}{Unchanged options}\\
%       \hline
%       l             &  Left adjusted column. \\
%       c             &  Centered adjusted column. \\
%       r             &  Right adjusted column. \\
%       p\{width\}    &  Equivalent to =\parbox[t]{width}=. \\
%       @\{decl.\}    &  Suppresses inter-column space and inserts
%                        \texttt{decl.}\ instead. \\
%       \hline
%     \multicolumn{2}{|c|}{New options}\\
%       \hline
%       m\{width\}    &  Defines a column of width \texttt{width}.
%                        Every entry will be centered in proportion to
%                        the rest of the line. It is somewhat like
%                        =\parbox{width}=. \\
%       \hline
%       b\{width\}    &  Coincides with =\parbox[b]{width}=. \\
%       \hline
%       >\{decl.\}    &  Can be used before an \texttt{l}, \texttt{r},
%                        \texttt{c}, \texttt{p}, \texttt{m} or a
%                        \texttt{b} option. It inserts \texttt{decl.}\
%                        directly in front of the entry of the column.
%                        \\
%       \hline
%       <\{decl.\}    &  Can be used after an \texttt{l}, \texttt{r},
%                        \texttt{c}, =p{..}=, =m{..}= or a =b{..}=
%                        option.  It inserts \texttt{decl.}\ right
%                        after the entry of the column.  \\
%       \hline
%       |             &  Inserts a vertical line. The distance between
%                        two columns will be enlarged by the width of
%                        the line
%                        in contrast to the original definition of
%                        \LaTeX.  \\
%       \hline
%       !\{decl.\}    &  Can be used anywhere and corresponds with the
%                        \texttt{|} option. The difference is that
%                        \texttt{decl.} is inserted instead of a
%                        vertical line, so this option doesn't
%                        suppress the normally inserted space between
%                        columns in contrast to =@{...}=.\\
%       \hline\hline
%       w\{align\}\{width\} & Sets the cell content in a box of the
%                        specified \texttt{width} aligned according to
%                        the \texttt{align} parameter which could be
%                        either \texttt{l}, \texttt{c} or
%                        \texttt{r}. Works essentially like
%                        =\makebox[=width=][=align=]{=cell=}= so
%                        silently overprints if the cell content is
%                        wider than the specified width. If that is
%                        not desired use \texttt{W} instead.\\
%       \hline
%       W\{align\}\{width\} & Like \texttt{w} but spits out an overfull
%                        box warning (and an overfullrule marker in
%                        draft mode) when the cell content is too wide
%                        to fit. This also means that the alignment is
%                        different if there is too much material,
%                        because it then always protrudes to the right!\\
%       \hline
%    \end{tabular}
% \end{center}
% \caption{The  preamble options.} \label{tab:opt}
% \end{table}
%
%
% We will discuss a few examples using the new preamble options before
% dealing with the implementation.
% \begin{itemize}
%    \item
%       If you want to use a special font (for example =\bfseries=) in a
%       flushed left column, this can be done with =>{\bfseries}l=. You
%       do not have to begin every entry of the column with  =\bfseries=
%       any more.
%    \item
%       In columns which have been generated with \texttt{p}, \texttt{m}
%       or \texttt{b}, the default value of =\parindent= is
%       \textsf{0pt}.
%       This can be changed with \\
%       =>{\setlength{\parindent}{1cm}}p=.
%    \item
%       The \texttt{>}-- and \texttt{<}--options were originally
%       developed for the following application:
%       =>{$}c<{$}= generates a column in math
%       mode in a \textsf{tabular}--environment. If you use this type
%       of a preamble in an \textsf{array}--environment, you get a
%       column in LR mode because the additional \$'s cancel the
%       existing \$'s.
%
%       Note that the arguments should only contain declarations, see the
%       section below.
%    \item
%       Using =c!{\hspace{1cm}}c= you get space between two
%       columns which is enlarged by one centimeter, while
%       =c@{\hspace{1cm}}c= gives you exactly one centimeter
%       space between two columns.
%
%    \item
%       A declaration like =w{l}{3cm}= (or even shorter =wl{3cm}=)
%       works like an =l= column except that the width will always be
%       =3cm= regardless of the cell content. Same with =w{c}= or
%       =w{r}=. This means that it is easy to set up tables in which all
%       columns have predefined widths.
% \end{itemize}
%
%
%
% \subsection{A note on the allowed content of \texttt{>\{...\}} and
%             \texttt{<\{...\}}}
%
% These specifiers are meant to hold declarations, such as
% \verb=>{\itshape}=. They cannot end in commands that take arguments
% without providing these arguments as part of the \verb={...}=.  It
% would be a mistaken assumption that they pick up all or parts of the
% alignment entry data if their argument is not provided.  E.g.,
% \verb=>{\textbf}= would not make the whole column bold nor would it
% make the first character bold (technically it would try to
% bolden \cs{ignorespaces}). Thus, it would not fail with an error,
% but effectively the output would be wrong and not as expected.
%
% It is also not possible to use an environment that grabs its body,
% that is defining
% \verb=\NewDocumentEnvironment{foo}{b}{\textit{#1}}{}= and then
% trying \verb=>{\begin{foo}= \texttt{c} \verb=<{\end{foo}=. This
% will fail with a low-level error message.

%
% \subsection{The behavior of the \cs{\textbackslash} command}
%
% In the basic \texttt{tabular} implementation of \LaTeX{} the \cs{\textbackslash}
% command ending the rows of the \texttt{tabular} or \texttt{array} has
% a somewhat inconsistent behavior if its optional argument is used. The
% result then depends on the type of rightmost column and as remarked in
% Leslie Lamport's \LaTeX{} manual~\cite{bk:lamport} may not always produce the
% expected extra space.
%
%
% Without the \textsf{array} package the extra space requested by the
% optional argument of \cs{\textbackslash} is measured from the last baseline of
% the rightmost column (indicated by ``x'' in the following
% example). As a result, swapping the column will give different
% results:
% \begin{verbatim}
%   \begin{tabular}[t]{lp{1cm}}
%       1 & 1\newline x   \\[20pt]     2 & 2    \end{tabular}
%   \begin{tabular}[t]{p{1cm}l}
%       1\newline 1 & x   \\[20pt]     2 & 2    \end{tabular}
%   \end{verbatim}
% \pagebreak
% If you run this without the \textsf{array} package you will get the
% following result:
% \begin{center}
% \begin{tabular}[t]{lp{1cm}}
%   1 & 1\newline x \\[32pt]      2 & 2 \end{tabular}
% \begin{tabular}[t]{p{1cm}l}
%   1\newline 1 & x \\[20pt]      2 & 2 \end{tabular}
% \end{center}
% In contrast, when the \textsf{array} package is loaded, the requested
% space in the optional argument is always measured from the baseline of
% the whole row and not from the last baseline of the rightmost column, thus
% swapping columns doesn't change the spacing and we same table height
% with an effective 8pt of extra space (as the second line already takes
% up 12pt of the requested 20pt):
% \begin{center}
% \begin{tabular}[t]{lp{1cm}}
%   1 & 1\newline x \\[20pt]      2 & 2 \end{tabular}
% \begin{tabular}[t]{p{1cm}l}
%   1\newline 1 & x \\[20pt]      2 & 2 \end{tabular}
% \end{center}
%
% This correction of behavior only makes a difference if the rightmost column
% is a \texttt{p}-column. Thus if you add the \textsf{array}
% package to an existing document, you should verify the spacing in all
% tables that have this kind of structure.
%
%
% \subsection{Defining new column specifiers}
%
% \DeleteShortVerb{\=}
% \MakeShortVerb{\"}
% \DescribeMacro{\newcolumntype}
% Whilst it is handy to be able to type
% \begin{quote}
%   ">{"\meta{some declarations}"}{c}<{"\meta{some more
%   declarations}"}"
% \end{quote}
% if you have a one-off column in a table, it is rather inconvenient
% if you often use columns of this form. The new version allows you
% to define a new column specifier, say \texttt{x}, which will expand to
% the primitives column specifiers.\footnote{This command was named
% \texttt{\textbackslash{}newcolumn} in the \texttt{newarray.sty}.
% At the moment \texttt{\textbackslash{}newcolumn} is still supported
% (but gives a warning). In later releases it will vanish.} Thus we
% may define
% \begin{quote}
%   "\newcolumntype{x}{>{"\meta{some declarations}"}{c}<{"\meta{some
%   more declarations}"}}"\hspace*{-3cm} ^^A no overfull from this line
% \end{quote}
% One can then use the \texttt{x} column specifier in the preamble
% arguments of all \texttt{array} or \texttt{tabular} environments in
% which you want columns of this form.
%
% It is common  to need math-mode and LR-mode columns in the same
% alignment. If we define:
% \begin{quote}
%   "\newcolumntype{C}{>{$}c<{$}}" \\
%   "\newcolumntype{L}{>{$}l<{$}}" \\
%   "\newcolumntype{R}{>{$}r<{$}}"
% \end{quote}
% Then we can use \texttt{C} to get centered LR-mode in an
% \texttt{array}, or centered math-mode in a \texttt{tabular}.
%
% The example given above for `center decimal points' could be
% assigned to a \texttt{d} specifier with the following command.
% \begin{quote}
% "\newcolumntype{d}{>{\centerdots}c<{\endcenterdots}}"
% \end{quote}
%
% The above solution always centers the dot in the
% column. This does not look too good if the column consists of large
% numbers, but to only a few decimal places. An alternative definition
% of a \texttt{d} column is
% \begin{quote}
%   "\newcolumntype{d}[1]{>{\rightdots{#1}}r<{\endrightdots}}"
% \end{quote}
% where the appropriate macros in this case are:\footnote{The package
% \texttt{dcolumn.sty} contains more robust macros based on these
% ideas.}
% \begin{verbatim}
%   \def\coldot{.}% Or if you prefer, \def\coldot{\cdot}
%   {\catcode`\.=\active
%     \gdef.{$\egroup\setbox2=\hbox to \dimen0 \bgroup$\coldot}}
%   \def\rightdots#1{%
%     \setbox0=\hbox{$1$}\dimen0=#1\wd0
%     \setbox0=\hbox{$\coldot$}\advance\dimen0 \wd0
%     \setbox2=\hbox to \dimen0 {}%
%     \setbox0=\hbox\bgroup\mathcode`\.="8000 $}
%   \def\endrightdots{$\hfil\egroup\box0\box2}
%\end{verbatim}
% Note that "\newcolumntype" takes the same optional argument as
% "\newcommand" which declares the number of arguments of the column
% specifier being defined. Now we can specify "d{2}" in our preamble
% for a column of figures to at most two decimal places.
%
% A rather different use of the "\newcolumntype" system takes
% advantage of the fact that the replacement text in the
% "\newcolumntype" command may refer to more than one column. Suppose
% that a document contains a lot of \texttt{tabular} environments that
% require the same preamble, but you wish to experiment with different
% preambles. Lamport's original definition allowed you to do the
% following (although it was probably a mis-use of the system).
% \begin{quote}
%   "\newcommand{\X}{clr}"\\
%   "\begin{tabular}{\X}" \ldots
% \end{quote}
% \texttt{array.sty} takes great care \textbf{not} to expand the
% preamble, and so the above does not work with the new scheme. With
% the new version this functionality is returned:
% \begin{quote}
% "\newcolumntype{X}{clr}"\\
% "\begin{tabular}{X}" \ldots
% \end{quote}
%
% The replacement text in a "\newcolumntype" command may refer to any of
% the primitives of \texttt{array.sty} see table \ref{tab:opt} on page
% \pageref{tab:opt}, or to any new letters defined in other
% "\newcolumntype" commands.
%
%
% \DescribeMacro{\showcols}A list of all the currently active
% "\newcolumntype" definitions is sent to the terminal and log file if
% the "\showcols" command is given.
%
%
% \subsection{Special variations of \texttt{\textbackslash hline}}
%
% The family of \texttt{tabular} environments allows
% vertical positioning with respect to the baseline of
% the text in which the environment appears.  By default the
% environment appears centered, but this can be changed to
% align with the first or last line in the environment by
% supplying a \texttt{t} or \texttt{b} value to the
% optional position argument. However, this does not work
% when the first or last element in the environment is a
% "\hline" command---in that case the environment is
% aligned at the horizontal rule.
%
% \pagebreak[3]
%
% Here is an example:
% \begin{center}
% \begin{minipage}[t]{.4\linewidth}
% Tables
% \begin{tabular}[t]{l}
%   with no\\ hline \\ commands \\ used
% \end{tabular} versus \\ tables
% \begin{tabular}[t]{|l|}
%  \hline
%   with some \\ hline \\ commands \\
%  \hline
% \end{tabular} used.
% \end{minipage}
% \begin{minipage}[t]{.5\linewidth}
% \begin{verbatim}
% Tables
% \begin{tabular}[t]{l}
%  with no\\ hline \\ commands \\ used
% \end{tabular} versus tables
% \begin{tabular}[t]{|l|}
%  \hline
%   with some \\ hline \\ commands \\
%  \hline
% \end{tabular} used.
% \end{verbatim}
% \end{minipage}
% \end{center}
%
% \DescribeMacro\firsthline
% \DescribeMacro\lasthline
% Using "\firsthline" and "\lasthline" will
% cure the problem, and the tables will align properly as long
% as their first or last line does not contain extremely large
% objects.
% \begin{center}
% \begin{minipage}[t]{.4\linewidth}
% Tables
% \begin{tabular}[t]{l}
%   with no\\ line \\ commands \\ used
% \end{tabular} versus \\ tables
% \begin{tabular}[t]{|l|}
%  \firsthline
%   with some \\ line   \\ commands \\
%  \lasthline
% \end{tabular} used.
% \end{minipage}
% \begin{minipage}[t]{.5\linewidth}
% \begin{verbatim}
% Tables
% \begin{tabular}[t]{l}
%   with no\\ line \\ commands \\ used
% \end{tabular} versus tables
% \begin{tabular}[t]{|l|}
%  \firsthline
%   with some \\ line   \\ commands \\
%  \lasthline
% \end{tabular} used.
% \end{verbatim}
% \end{minipage}
% \end{center}
% \DescribeMacro\extratabsurround
% The implementation of these two commands contains an extra
% dimension, which is called "\extratabsurround", to add some
% additional space at the top and the bottom of such an environment.
% This is useful if such tables are nested.
%
% \section{Final Comments}
%
% \subsection{Handling of rules}
%
% There are two possible approaches to the handling of horizontal and
% vertical rules in tables:
% \begin{enumerate}
%   \item rules can be placed into the available space without
%   enlarging the table, or
%   \item rules can be placed between columns or rows thereby enlarging
%   the table.
% \end{enumerate}
% For vertical rules \texttt{array.sty} implements the second
% possibility while the default implementation in the \LaTeX{} kernel
% implements the first concept.
% Both concepts have their merits but
% one has to be aware of the individual implications.
% \begin{itemize}
% \item
%   With standard \LaTeX{} adding vertical rules to a table will
%   not affect the
%   width of the table (unless double rules are used), e.g.,
%   changing a preamble from \verb=lll= to \verb=l|l|l= does not
%   affect the document other than adding rules to the table. In
%   contrast, with \texttt{array.sty} a table that just fit the
%   \verb=\textwidth= might now produce an overfull box.
% \item
%   With standard \LaTeX{} modifying the width of rules could result
%   in ugly looking tables because without adjusting the
%   \verb=\tabcolsep=, etc.\ the space between rule and column could
%   get too small (or too large). In fact even overprinting of text is
%   possible. In contrast, with  \texttt{array.sty} modifying any such
%   length usually works well as the actual visual white space (from
%   \verb=\tabcolsep=, etc.) does not depend on the width of the
%   rules.
% \item
%   With standard \LaTeX{} boxed tabulars actually have strange
%   corners because the horizontal rules end in the middle of the
%   vertical ones. This looks very unpleasant when a large
%   \verb=\arrayrulewidth= is chosen. In that case a simple table like
%\begin{verbatim}
%\setlength{\arrayrulewidth}{5pt}
%\begin{tabular}{|l|}
%  \hline  A \\  \hline
%\end{tabular}
%\end{verbatim}
%   will produce something like
%   \begin{center}
%\setlength{\arrayrulewidth}{5pt}
%\begin{tabular}{@{}l@{}}
%  \hline  \null\hskip-.5\arrayrulewidth\vline
%  \hskip\tabcolsep
%   A\hskip\tabcolsep
%  \vline\hskip-.5\arrayrulewidth\null \\  \hline
%\end{tabular}
%   \quad
%   instead of
%   \quad
%\begin{tabular}{|l|}
%  \hline  A \\  \hline
%\end{tabular}
%   \end{center}
% \end{itemize}
%
% Horizontal rules produced with \cs{hline} add to the table height in
% both implementations but they differ in handling double \cs{hline}s.
% In contrast a \cs{cline} does not change the table
% height.\footnote{All a bit inconsistent, but nothing that can be
% changed after being 30+ years in existence.}
%
% \subsection{Comparisons with older versions of \texttt{array.sty}}
%
% There are some differences in the way version 2.1 treats incorrect
% input, even if the source file does not appear to use any of the
% extra features of the new version.
% \begin{itemize}
% \item A preamble of the form "{wx*{0}{abc}yz}" was treated by
% versions prior to 2.1 as "{wx}". Version 2.1 treats it as "{wxyz}"
% \item An incorrect positional argument such as \texttt{[Q]} was
% treated as \texttt{[c]} by \texttt{array.sty}, but is now treated as
% \texttt{[t]}.
% \item A preamble such as "{cc*{2}}" with an error in
% a $*$-form will generate different errors in the new version. In
% both cases the error message is not particularly helpful to the
% casual user.
% \item Repeated \texttt{<} or \texttt{>} constructions
% generated an error in earlier versions, but are now allowed in
% this package.  ">{"\meta{decs1}"}>{"\meta{decs2}"}" is treated the
% same as ">{"\meta{decs2}\meta{decs1}"}".
% \item The "\extracolsep"
% command does not work with the old versions of \texttt{array.sty},
% see the comments in \texttt{array.bug}. With version 2.1
% "\extracolsep" may again be used in \texttt{@}-expressions as in
% standard \LaTeX, and also in \texttt{!}-expressions (but see the
% note below).
% \end{itemize}
%
% Prior to version 2.4f the space added by the optional argument to "\\"
% was added inside an m-cell if the last column was of type
% \texttt{m}. As a result that cell was vertically centered with that
% space inside, resulting in a strange offset. Since 2.4f, this space
% is now added after centering the cell.
%
% A similar problem happened when "\extrarowheight" was used. For that
% reason m-cells now manually position the cell content which
% allows to ignore this extra space request during the vertical alignment.
%
%
% \subsection{Bugs and Features}
%
% \begin{itemize}
% \item Error messages generated when parsing the column specification
%   refer to the preamble argument \textbf{after} it has been re-written
%   by the "\newcolumntype" system, not to the preamble entered by the
%   user.  This seems inevitable with any system based on
%   pre-processing and so is classed as a \textbf{feature}.
%
% \item The treatment of multiple \texttt{<} or \texttt{>}
%   declarations may seem strange at first. Earlier implementations
%   treated ">{"\meta{decs1}"}>{"\meta{decs2}"}" the same as
%   ">{"\meta{decs1}\meta{decs2}"}". However this did not give the
%   user the opportunity of overriding the settings of a
%   "\newcolumntype" defined using these declarations. For example,
%   suppose in an \texttt{array} environment we use a \texttt{C}
%   column defined as above. The \texttt{C} specifies a centered text
%   column, however ">{\bfseries}C", which re-writes to
%   ">{\bfseries}>{$}c<{$}" would not specify a bold column as might
%   be expected, as the preamble would essentially expand to
%   "\hfil$\bfseries$#$ $\hfil" and so the column entry would not be in the
%   scope of the "\bfseries"\,! The present version switches the order
%   of repeated declarations, and so the above example now produces a
%   preamble of the form "\hfil$" "$\bfseries#$" "$\hfil", and the
%   dollars cancel each other out without limiting the scope of the
%   "\bfseries".
%
% \item The use of "\extracolsep" has been subject to the following
%   two restrictions.  There must be at most one "\extracolsep"
%   command per "@", or "!" expression and the command must be
%   directly entered into the "@" expression, not as part of a macro
%   definition. Thus "\newcommand{\ef}{\extracolsep{\fill}}" \ldots
%   "@{\ef}" does not work with this package. However you can use
%   something like
%   "\newcolumntype{e}{@{\extracolsep{\fill}}" instead.
%
% \item As noted by the \LaTeX{} book, for the purpose of
%   "\multicolumn" each column with the exception of the first one
%   consists of the entry and the \emph{following} inter-column
%   material. This means that in a tabular with the preamble
%   "|l|l|l|l|" input such as "\multicolumn{2}{|c|}" in
%   anything other than the first column is incorrect.

%   In the standard array/tabular implementation this error is not so
%   noticeable as that version contains negative spacing so that each
%   "|" takes up no horizontal space. But since in this package the
%   vertical lines take up their natural width one sees two lines if
%   two are specified.
%
% \end{itemize}
%
%
% \section{Support for tagged PDF}
%
% With version 2.6a the package is made tagging aware, which means that
% it will automatically produce tagged tables (necessary, for example, for
% accessibility) if tagging is requested via \cs{DocumentMetadata}.
%
% More granular control, e.g., explicitly deciding which cells are
% header cells, etc., is currently under development, but syntax for
% this will not appear in this package. Instead it will become
% available across all tabular-generating packages and then
% automatically apply here as well.
%
% Enabling \LaTeX{} to automatically produce tagged PDF is a long-term
% project and this is a tiny step in this puzzle. For more information
% on the project and already available functionality, see
% \url{https://latex-project.org/publications/indexbytopic/pdf} and
% \url{https://github.com/latex3/tagging-project}.
%
%
% \section{Package hooks}
%
% To support extensions made by other packages a few hooks
% have been added. There are
% \begin{description}
%
% \item[\hook{tbl/init}]
%
%    This hook is executed at the start of the \env{tabular} after the
%    preamble has been evaluated. It accepts two arguments, the number
%    of columns and the full user preamble and can be used to make
%    special initializations.
%
% \item[\hook{tbl/row/init}]
%
%    This hook is invoked just before a row is about to start or could
%    start, i.e., just before the first row and then after each row
%    including the last. At this point tagging for the row has not been
%    added and the hook can therefore only contain declarations (which
%    have to to be global as everything is executed within a
%    \cs{noalign}) and it can contain special commands such as
%    \cs{hline}, \cs{cline}, or \cs{rowcolor}, that are allowed after
%    \verb=\\=.
%    
% \item[\hook{tbl/row/begin}]
%
%    This hook is executed after the tagging for the row is done but
%    before the tagging for the cell is added, so again it is not
%    intended for adding any typeset material.  It accepts the current
%    row number as its argument.
%
%    This hook is probably of limited use, because it is in fact local
%    to the first cell due to technical restrictions. Local
%    declarations therefore do not apply to the whole row but just to
%    its first cell.
%
%    So perhaps this hook and the corresponding \texttt{/end}  hook
%    are removed again before the final release in June.
%
% \item[\hook{tbl/row/end}]
%
%    This hook is executed when \verb=\\= or the end of the tabular is
%    seen, after the tagging for the cell is done but before the
%    row-end tag is added, so again it is not
%    intended for adding any typeset material.  It accepts the current
%    row number as its argument.
%
%    It is implemented as a reversed hook.
%
% \item[\hook{tbl/cell/init}]
%
%    This hook is executed at the start of each cell (before tagging for
%    the cell is set up), it should therefore not generate any textual
%    material. It accepts the current row and column numbers and the
%    current cell type (\texttt{c}, \texttt{l}, \ldots) as its
%    arguments.
%
%    Cell type in the context of hooks means only the built-in specifiers and
%    not those that are added using \cs{newcolumntype}. When the
%    preamble for the tabular is constructed the \cs{newcolumntype}
%    specfiers are expanded until only base ones remain.  The
%    \pkg{array} package defines two specifiers with
%    \cs{newcolumntype}: \texttt{w} and \texttt{W}. By the hooks both
%    are seen as \texttt{c} type cells, because technically that's
%    what they are.
%    
% \item[\hook{tbl/cell/begin}]
%
%    This hook is executed after the start-tag for the cell is added
%    but before any code from \verb=>{...}= is looked at and can, with
%    some care, contain material that is typeset as part of the cell.
%    It accepts the current row and column numbers and the current
%    cell type as its arguments.
%
% \item[\hook{tbl/cell/end}]
%
%    This hook is executed at the end of the cell before the end-tag for
%    the cell is added and thus can theoretically contain material
%    that is typeset as part of the cell.  It accepts the current
%    row and column numbers and the
%    current cell type as its arguments.
%
%    It is implemented as a reversed hook.
%
% \end{description}
%
%
%
%
%
%
% \changes{v2.2b}{1994/02/04}{Removed interactive prompt}
%
% \MaybeStop{
% \begin{thebibliography}{1}
%    \bibitem{bk:GMS94} \textsc{M.~Goossens}, \textsc{F.~Mittelbach}
%       and \textsc{A.~Samarin}.
%       \newblock The \LaTeX{} Companion.
%       \newblock
%       Addison-Wesley, Reading, Massachusetts, 1994.
%    \bibitem{bk:knuth}  \textsc{D. E. Knuth}.
%       \newblock  The \TeX{}book (Computers \& Typesetting Volume A).
%       \newblock
%       Addison-Wesley, Reading, Massachusetts, 1986.
%    \bibitem{bk:lamport} \textsc{L. Lamport}.
%       \newblock
%       \LaTeX\ --- A Document Preparation System.
%       \newblock
%       Addison-Wesley, Reading, Massachusetts, 1986.
% \end{thebibliography}
% }   ^^A  end of \MaybeStop
%
%
%
%
% \section{The documentation driver file}
%
% The first bit of code contains the documentation driver file for
% \TeX{}, i.e., the file that will produce the documentation you are
% currently reading. It will be extracted from this file by the
% \texttt{docstrip} program.
%    \begin{macrocode}
%<*driver>
\NeedsTeXFormat{LaTeX2e}[2024/06/01]
%    \end{macrocode}
%
%    We switched from \cls{ltxdoc} to \cls{l3doc} to get support for
%    code written in the L3 programming layer. The first is that we
%    are currently missing \cs{MaintainedByLaTeXTeam}, so we have to
%    provide that for now. 
%    \begin{macrocode}
\documentclass{l3doc}

% currently missing in l3doc
\makeatletter
\def\MaintainedBy#1{\gdef\@maintainedby{#1}}
\let\@maintainedby\@empty
\def\MaintainedByLaTeXTeam#1{%
{\gdef\@maintainedby{%
This file is maintained by the \LaTeX{} Project team.\\%
Bug reports can be opened (category \texttt{#1}) at\\%
\url{https://latex-project.org/bugs.html}.}}}
\def\@maketitle{%
  \newpage
  \null
  \vskip 2em%
  \begin{center}%
  \let \footnote \thanks
    {\LARGE \@title \par}%
    \vskip 1.5em%
    {\large
      \lineskip .5em%
      \begin{tabular}[t]{c}%
        \@author
      \end{tabular}\par}%
    \vskip 1em%
    {\large \@date}%
    \ifx\@maintainedby\@empty
    \else
    \vskip 1em%
    \fbox{\fbox{\begin{tabular}{@{}l@{}}\@maintainedby\end{tabular}}}%
    \fi
  \end{center}%
  \par
  \vskip 1.5em}
\makeatother

% undo the default is not used:

\IfFormatAtLeastTF {2020/10/01}
 {\AtBeginDocument[ltxdoc]{\DeleteShortVerb{\|}} } 
 {\AtBeginDocument{\DeleteShortVerb{\|}} } 

 \usepackage{array}

 % Allow large table at bottom
 \renewcommand{\bottomfraction}{0.7}

\EnableCrossrefs
 %\DisableCrossrefs   % Say \DisableCrossrefs if index is ready

\RecordChanges                  % Gather update information

\CodelineIndex                  % Index code by line number

 %\OnlyDescription    % comment out for implementation details
 %\OldMakeindex       % use if your MakeIndex is pre-v2.9

\begin{document}
   \DocInput{array.dtx}
\end{document}
%</driver>
%    \end{macrocode}
%
%
% \section{A note on the updates done December 2023}
%
% We introduced support for tagged PDF and at the same time we added
% code to determine row and column numbers for each cell in
% preparation for supporting  formatting or type specifications for individual
% cells (or group of cells) from the outside, e.g., \enquote{rows 1,
% 2, and 10 are header rows} (syntax to be decided).
%
% This new code is already written with L3 programming layer conventions
% while most of the legacy code is still as it was before. This make the code
% currently somewhat clattered, unfortunately. Eventually this will all move to L3
% programming layer but this will take time.
%
%
%    \begin{macrocode}
%<@@=tbl>
\ExplSyntaxOn
%    \end{macrocode}
%
%
%
%
%
% \section{The construction of the preamble}
%
% \DeleteShortVerb{\"}
% \MakeShortVerb{\=}
%
% It is obvious that those environments will consist mainly of an
% =\halign=, because \TeX\ typesets tables using this primitive.
% That is why we will now take a look at the algorithm which determines
% a preamble for a =\halign= starting with a given user preamble
% using the options mentioned above.
%
%
%    The current version is defined at the top of the file looking
%    something like this
%    \begin{macrocode}
%<*package>
%\NeedsTeXFormat{LaTeX2e}[1994/05/13]
%\ProvidesPackage{array}[\filedate\space version\fileversion]
%    \end{macrocode}
%
% The most interesting macros of this implementation are without doubt
% those which are responsible for the construction of the preamble for
% the =\halign=. The underlying algorithm was developed by
% \textsc{Lamport} (resp.\ \textsc{Knuth}, see texhax V87\#??), and it
% has been extended and improved.
%
% The user preamble will be read \textsf{token} by \textsf{token}.  A
% \textsf{token} is a single character like \texttt{c} or a block
% enclosed in ={...}=. For example the preamble of
% =\begin{tabular}=\linebreak[0]={lc||c@{\hspace{1cm}}}= consists of
% the \textsf{token} \texttt{l}, \texttt{c}, \texttt{|}, \texttt{|},
% \texttt{@} and =\hspace{1cm}=.
%
% The currently used \textsf{token} and the one, used before, are needed
% to decide on how the construction of the preamble has to be
% continued.
% In the example mentioned above the \texttt{l} causes the preamble
% to begin with =\hskip\tabcolsep=. Furthermore
% =# \hfil= would be appended to define a flush left column.
% The next \textsf{token} is a \texttt{c}. Because it was preceded by an
% \texttt{l} it generates a new column. This is done with
% =\hskip \tabcolsep & \hskip \tabcolsep=. The column which is to
% be centered will be appended with =\hfil # \hfil=.
% The \textsf{token} \texttt{|} would then add a space of
% =\hskip \tabcolsep=
% and a vertical line because the last
% \textsf{tokens} was a \texttt{c}.
% The following \textsf{token} \texttt{|} would only add a space
% =\hskip \doublerulesep= because it was preceded by the
% \textsf{token} \texttt{|}. We will not discuss our example further but
%  rather take a look at the general case of constructing preambles.
%
% The example shows that the desired preamble for the
% =\halign= can be constructed as soon as the action of all
% combinations
% of the preamble \textsf{tokens} are specified. There are 18 such
% \textsf{tokens}
% so we have $19 \cdot 18 \string= 342$ combinations if we count the
% beginning of
% the preamble as a special \textsf{token}. Fortunately, there are many
% combinations which generate the same spaces, so we can define
% \textsf{token} classes. We will identify a
% \textsf{token} within a class with a number, so we can insert the
% formatting (for example of a column).
% Table~\ref{tab:Klassen} lists all \textsf{token} classes and
% their corresponding numbers.
% \begin{table}[ht]
% \begin{center}
%    \begin{tabular}[t]{>{\ttfamily}ccc}
%       \textsf{token} & =\@chclass= & =\@chnum= \\[2mm]
%       c   & 0  & 0 \\
%       l   & 0  & 1 \\
%       r   & 0  & 2 \\
%       m-arg    & 0  & 3 \\
%       p-arg    & 0  & 4 \\
%       b-arg    & 0  & 5 \\
%       |   & 1  & 0 \\
%       !-arg    & 1  & 1 \\
%       <-arg    & 2  & --- \\
%       >-arg    & 3  & ---
%    \end{tabular}
%    \kern3mm \vrule \kern3mm%
%    \begin{tabular}[t]{>{\ttfamily}ccc}
%       \textsf{token} & =\@chclass= & =\@chnum= \\[2mm]
%       Start    & 4  & --- \\
%       @-arg    & 5  & --- \\
%       !   & 6  & --- \\
%       @   & 7  & --- \\
%       <   & 8  & --- \\
%       >   & 9  & --- \\
%       m   & 10 & 3 \\
%       p   & 10 & 4 \\
%       b   & 10 & 5
%    \end{tabular}
% \end{center}
% \caption{Classes of preamble \textsf{tokens}}
% \label{tab:Klassen}
% \end{table}
%
%
% \begin{macro}[int]{\@chclass}
% \begin{macro}[int]{\@chnum}
% \begin{macro}[int]{\@lastchclass}
%    The class and the number of the current \textsf{token} are saved in
%    the
%    \textsf{count} registers =\@chclass=
%    and =\@chnum=, while the class of the previous
%    \textsf{token} is stored in the
%    \textsf{count} register =\@lastchclass=.
%    All of the mentioned registers are already allocated in
%    the \LaTeX{} format,
%    which is the reason why the following three lines of code are
%    commented out.
%    Later throughout the text I will not mention it again explicitly
%    whenever I use a =%= sign. These parts are already defined in
%    the \LaTeX{} format.
%    \begin{macrocode}
% \newcount \@chclass
% \newcount \@chnum
% \newcount \@lastchclass
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
% \begin{macro}[int]{\@addtopreamble}
%    We will save the already constructed preamble for
%    the =\halign=
%    in the global macro =\@preamble=. This will then be
%     enlarged with
%    the command =\@addtopreamble=.
%    \begin{macrocode}
\def\@addtopreamble#1{\xdef\@preamble{\@preamble #1}}
%    \end{macrocode}
% \end{macro}
%
%
%
%
%
% \subsection{The character class of a \textsf{token}}
%
% \begin{macro}[int]{\@testpach}
% \changes{v2.0a}{1989/05/12}{p option renamed to m (middle).}
% \changes{v2.0a}{1989/05/12}{t option renamed to p to be compatible to
%                         the original.}
%    With the help of =\@lastchclass= we can now define a macro
%    which determines the class and the number of a given preamble
%    \textsf{token}
%    and assigns them to the registers
%    =\@chclass= and =\@chnum=.
% \changes{v2.0f}{1992/02/29}{Argument removed since implicitly known}
%    \begin{macrocode}
\ExplSyntaxOff
\def\@testpach{\@chclass
%    \end{macrocode}
%    First we deal with the cases in which the \textsf{token}
%    (=#1=) is the argument of \texttt{!}, \texttt{@}, \texttt{<} or
%    \texttt{>}. We can see this from the value of =\@lastchclass=:
%    \begin{macrocode}
 \ifnum \@lastchclass=6 \@ne \@chnum \@ne \else
  \ifnum \@lastchclass=7 5 \else
   \ifnum \@lastchclass=8 \tw@ \else
    \ifnum \@lastchclass=9 \thr@@@@
%    \end{macrocode}
%    Otherwise we will assume that the \textsf{token} belongs to the
%    class $0$
%    and assign the corresponding number to =\@chnum= if our
%    assumption is correct.
%    \begin{macrocode}
   \else \z@
%    \end{macrocode}
%    If the last \textsf{token} was a \texttt{p}, \texttt{m} or a
%    \texttt{b}, =\@chnum= already has the right value. This is the
%    reason for the somewhat curious choice of the \textsf{token}
%    numbers in class $10$.
%    \begin{macrocode}
   \ifnum \@lastchclass = 10 \else
%    \end{macrocode}
%    Otherwise we will check if =\@nextchar= is either a \texttt{c},
%    \texttt{l} or an \texttt{r}.  Some applications change the
%    catcodes of certain characters like ``\texttt{@}'' in
%    \texttt{amstex.sty}. As a result the tests below would fail since
%    they assume non-active character tokens. Therefore we evaluate
%    =\@nextchar= once thereby turning the first token of its
%    replacement text into a char. At this point here this should have
%    been the only char present in =\@nextchar= which put into via a
%    =\def=.
% \changes{v2.0f}{1992/02/29}{Ensure to test a char which is not active}
%    \begin{macrocode}
   \edef\@nextchar{\expandafter\string\@nextchar}%
   \@chnum
   \if \@nextchar c\z@ \else
    \if \@nextchar l\@ne \else
     \if \@nextchar r\tw@ \else
%    \end{macrocode}
%    If it is a different \textsf{token}, we know that the class was
%    not $0$. We assign the value $0$ to =\@chnum= because this value
%    is needed for the \texttt{|}--\textsf{token}. Now we must check
%    the remaining classes.  Note that the value of =\@chnum= is
%    insignificant here for most classes.
%    \begin{macrocode}
   \z@ \@chclass
   \if\@nextchar |\@ne \else
    \if \@nextchar !6 \else
     \if \@nextchar @7 \else
      \if \@nextchar <8 \else
       \if \@nextchar >9 \else
%    \end{macrocode}
%    The remaining permitted \textsf{tokens} are \texttt{p},
%    \texttt{m} and \texttt{b} (class $10$).
%    \begin{macrocode}
  10
  \@chnum
  \if \@nextchar m\thr@@@@ \else
   \if \@nextchar p4 \else
    \if \@nextchar b5 \else
%    \end{macrocode}
%    Now the only remaining possibility is a forbidden \textsf{token},
%    so we choose class $0$ and number $0$ and give an error message.
%    Then we finish the macro by closing all =\if='s.
%    \begin{macrocode}
   \z@ \@chclass \z@ \@preamerr \z@ \fi \fi \fi \fi
   \fi \fi  \fi  \fi  \fi  \fi  \fi \fi \fi \fi \fi \fi}
\ExplSyntaxOn
%    \end{macrocode}
% \end{macro}
%
%
%
%
%
% \subsection{Multiple columns (\texorpdfstring{$*$}{*}--form)}
%
% \begin{macro}[int]{\@xexpast}
% \begin{macro}[int]{\the@toks}
% \begin{macro}[int]{\the@toksz}
%    \label{@xexpast} Now we discuss the macro that deletes all forms
%    of type =*{=\textit{N\/}=}{=\textit{String\/}=}= from a user
%    preamble and replaces them with \textit{N} copies of
%    \textit{String}.  Nested $*$--expressions are dealt with
%    correctly, that means $*$--expressions are not substituted if
%    they are in explicit braces, as in =@{*}=.
%
%    This macro is called via
%    =\@xexpast=\meta{preamble}=*0x\@@@@=.
%    The $*$--expression =*0x= is being used to terminate the
%    recursion,
%    as we shall see later, and =\@@@@= serves as an argument
%    delimiter. =\@xexpast= has four arguments. The first
%    one is the part of the
%    user preamble before the first $*$--expression while the second
%    and third ones are the arguments of the first $*$--expression
%    (that is \textit{N} and \textit{String} in the notation mentioned
%    above).
%    The fourth argument is the rest of the preamble.
%    \begin{macrocode}
\def\@xexpast#1*#2#3#4\@@@@{%
%    \end{macrocode}
%    The number of copies of \textit{String} (=#2=) that are to be
%    produced will be saved in a \textsf{count} register.
%    \begin{macrocode}
   \@tempcnta #2
%    \end{macrocode}
%    We save the part of the preamble which does not
%    contain a $*$--form (=#1=)
%    in a \PlainTeX\ \textsf{token} register.
%    We also save \textit{String} (=#3=) using a \LaTeX\
%    \textsf{token} register.
%    \begin{macrocode}
   \toks@={#1}\@temptokena={#3}%
%    \end{macrocode}
%    Now we have to use a little trick to produce \textit{N} copies of
%    \textit{String}.
%    We could try =\def\@tempa{#1}= and then
%    \textit{N} times =\edef\@tempa{\@tempa#3}=. This would have the
%    undesired effect that all macros within =#1= and =#3=
%    would be expanded, although, for example, constructions like
%    =@{..}= are not supposed to be changed.
%    That is why we =\let= two control sequences to
%    be equivalent to =\relax=.
%    \begin{macrocode}
   \let\the@toksz\relax \let\the@toks\relax
%    \end{macrocode}
%    Then we ensure that =\@tempa= contains
%    ={\the@toksz\the@toks...\the@toks}= (the macro
%    =\the@toks= exactly \textit{N\/} times) as substitution text.
%    \begin{macrocode}
   \def\@tempa{\the@toksz}%
   \ifnum\@tempcnta >0 \@whilenum\@tempcnta >0\do
     {\edef\@tempa{\@tempa\the@toks}\advance \@tempcnta \m@ne}%
%    \end{macrocode}
%    If \textit{N\/} was greater than zero we prepare for another call
%    of =\@xexpast=. Otherwise we assume we have reached the end of
%    the user preamble, because we had appended =*0x\@@@@= when we first
%    called =\@xexpast=.  In other words: if the user inserts
%    =*{0}{..}= in his preamble, \LaTeX\ ignores the rest of it.
%    \begin{macrocode}
       \let \@tempb \@xexpast \else
       \let \@tempb \@xexnoop \fi
%    \end{macrocode}
%    Now we will make sure that the part of the user preamble, which
%    was already dealt with, will be saved again in =\@tempa=.
%    \begin{macrocode}
   \def\the@toksz{\the\toks@}\def\the@toks{\the\@temptokena}%
   \edef\@tempa{\@tempa}%
%    \end{macrocode}
%    We have now evaluated the first $*$--expression, and the user
%    preamble up to this point
%    is saved in =\@tempa=. We will put the contents of
%    =\@tempa= and the rest of the user preamble together and work
%    on the result with =\@tempb=. This macro either corresponds
%    to =\@xexpast=, so that the next
%    $*$--expression is handled, or to the macro =\@xexnoop=,
%    which only ends the recursion by deleting its argument.
%    \begin{macrocode}
   \expandafter \@tempb \@tempa #4\@@@@}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[int]{\@xexnoop}
%    So the first big problem is solved. Now it is easy to
%    specify =\@xexnoop=.
%    Its argument is delimited by =\@@@@= and it simply expands to
%    nothing.
%    \begin{macrocode}
%  \def\@xexnoop#1\@@@@{}
%    \end{macrocode}
% \end{macro}
%
%
%
%
% \section{The insertion of declarations
%           (\texttt{>}, \texttt{<}, \texttt{!}, \texttt{@})}
%
%
% The preamble will be enlarged with the help of =\xdef=, but the
% arguments of \texttt{>}, \texttt{<},~\texttt{!}\ and \texttt{@} are
% not supposed to be expanded during the construction (we want an
% implementation that doesn't need a =\protect=). So we have to find a
% way to inhibit the expansion of those arguments.
%
% We will solve this problem with \textsf{token} registers. We need
% one register for every \texttt{!}\ and \texttt{@}, while we need two
% for every \texttt{c}, \texttt{l}, \texttt{r}, \texttt{m}, \texttt{p}
% or \texttt{b}. This limits the number of columns of a table because
% there are only 256 \textsf{token} registers. But then, who needs
% tables with more than 100 columns?
%
% One could also find a solution which only needs two or three
% \textsf{token} registers by proceeding similarly as in the macro
% =\@xexpast= (see page \pageref{@xexpast}). The advantage of our
% approach is the fact that we avoid some of the problems that arise
% with the other method\footnote{Maybe there are also historical
%  reasons.}.
%
% So how do we proceed? Let us assume that we had =!{foo}= in the
% user preamble and say we saved \texttt{foo} in
% \textsf{token} register $5$. Then we call
% =\@addtopreamble{\the@toks5}= where
% =\the@toks= is defined in a way that it does not expand
% (for example it could be equivalent to =\relax=). Every
% following call
% of =\@addtopreamble= leaves =\the@toks5= unchanged in
% =\@preamble=. If the construction of the preamble is completed
% we change the definition of =\the@toks= to
% =\the\toks= and expand =\@preamble= for the last time.
% During this process all parts of the form
%    =\the@toks=\meta{Number}
% will be substituted by the contents of the respective \textsf{token}
% registers.
%
% As we can see from this informal discussion the construction of the
% preamble has to take place within a group, so that the
% \textsf{token} registers we use will be freed later on. For that
% reason we keep all assignments to =\@preamble= global; therefore the
% replacement text of this macro will remain the same after we leave
% the group.
%
% \begin{macro}[int]{\count@}
%    We further need a \textsf{count} register to remember which
%    \textsf{token} register is to be used next. This will be
%    initialized with $-1$ if we want to begin with the \textsf{token}
%    register $0$. We use the \PlainTeX\ scratch register =\count@=
%    because everything takes place locally. All we have to do is
%    insert =\the@toks= =\the= =\count@= into the preamble.
%    =\the@toks= will remain unchanged and =\the\count@= expands into
%    the saved number.
% \end{macro}
%
% \begin{macro}[int]{\prepnext@tok}
%    The macro =\prepnext@tok= is in charge of preparing the next
%    \textsf{token} register. For that purpose we increase
%    =\count@= by $1$:
%    \begin{macrocode}
\def\prepnext@tok{\advance \count@ \@ne
%    \end{macrocode}
%    Then we locally delete any contents the
%    \textsf{token} register might have.
%    \begin{macrocode}
   \toks\count@{}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\save@decl}
%    During the construction of the preamble the current
%    \textsf{token} is always saved in the macro =\@nextchar= (see the
%    definition of =\@mkpream= on page \pageref{@mkpream}). The macro
%    =\save@decl= saves it into the next free \textsf{token} register,
%    i.e.\ in =\toks\count@=.
% \changes{v2.0c}{1990/08/14}{\cs{relax} removed and added elsewhere.}
%    \begin{macrocode}
\def\save@decl{\toks\count@ \expandafter{\@nextchar}}
%    \end{macrocode}
%    The reason for the  use of =\relax= is the following
%    hypothetical situation in the preamble:
%    \quad =..\the\toks1\the\toks2..= \quad \TeX\ expands
%    =\the\toks2= first in order to find out if the digit =1=
%    is followed by other digits. E.g.\ a =5= saved in the
%    \textsf{token} register $2$ would lead \TeX\ to insert the contents
%    of \textsf{token} register $15$ instead of $1$ later on.
%
% The example above referred to an older version of =\save@decl= which
% inserted a =\relax= inside the token register. This is now moved to
% the places where the actual token registers are inserted (look for
% =\the@toks=) because the old version would still make =@=
% expressions to moving arguments since after expanding the second
% register while looking for the end of the number the contents of the
% token register is added so that later on the whole register will be
% expanded.  This serious bug was found after nearly two years
% international use of this package  by Johannes Braams.
% \end{macro}
%
%
%
% How does the situation look like, if we want to add another column
% to the preamble, i.e.\ if we have found a \texttt{c}, \texttt{l},
% \texttt{r}, \texttt{p}, \texttt{m} or \texttt{b} in the user
% preamble?  In this case we have the problem of the \textsf{token}
% register from =>{..}= and =<{..}= having to be inserted at this
% moment because formatting instructions like =\hfil= have to be set
% around them. On the other hand it is not known yet, if any =<{..}=
% instruction will appear in the user preamble at all.
%
% We solve this problem by adding two \textsf{token} registers at a
% time.  This explains, why we have freed the \textsf{token} registers
% in =\prepnext@tok=.
%
% \begin{macro}[int]{\insert@column}
% \begin{macro}[int]{\@sharp}
% \begin{macro}[int]{\textonly@unskip}
% \begin{macro}[int]{\@protected@firstofone}
%    We now define the macro =\insert@column= which will do
%    this work for us.
%    \begin{macrocode}
\def\insert@column{%
%    \end{macrocode}
%    We start with a hook that packages can use to add code at the
%    start of the cell. It accepts 3 arguments: the current row and
%    column number and the cell type, so that the hook code can do
%    conditional processing based on the values.  The
%    \cs{insert@column} command is x-expanded during the preamble
%    construction and \cs{hook_use:nnw} is fully expandable, so we
%    have to prevent that expansion inside the \cs{xdef} of the
%    \cs{@premable} construction. \cs{@@_hook_use_protected:nVVn} does
%    just that for us.
% \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
  \@@_hook_use_protected:nVVn {tbl/cell/init}
                              \g_@@_row_int
                              \g_@@_col_int
                              { \ar@cell@type }
%    \end{macrocode}
%
%    For tagging we insert as special socket, that adds the necessary
%    PDF tag at the beginning of the cell if tagging is enabled.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
  \UseTaggingSocket{tbl/cell/begin}%
  \@@_hook_use_protected:nVVn {tbl/cell/begin}
                              \g_@@_row_int
                              \g_@@_col_int
                              { \ar@cell@type }
%    \end{macrocode}
%     Next we have to insert the toks register holding the content of
%    \verb=>{...}=.  Here, we assume that the \textsf{count} register
%    =\@tempcnta= has saved the value $=\count@= - 1$.
%
%    To keep \TeX{} happy if there is a look ahead in the tabular
%    preamble, i.e., starting in \verb=>{...}=, which uses the
%    Appendix~D trick (for example, anything with a trailing optional
%    argument defined by \pkg{ltcmd}), we wrap everything here in a
%    protected version of \cs{@firstofone}.  \TeX{} otherwise can get
%    confused about the value of the master counter, and we get some
%    strange errors. We suspected that there was an underlying issue
%    is the \TeX{} engine, but it turned out to be rather hard to get
%    to the bottom of it, because the master counter is not accessible
%    through \TeX{}'s tracing tools. Thus, all we could do was
%    producing various example documents, observing results, as well
%    as staring at a printout of the \TeX{} program. As an example,
%    without this approach, something like
%    \begin{verbatim}
% \NewDocumentCommand\foo{o}{x}
% \begin{tabular}{>{\foo}l}
%    Foo 
% \end{tabular}
%    \end{verbatim}
%    failed. That can be fixed by adding a \cs{relax} after the
%    \cs{@tempcnta}, but that then leads to issues if you are
%    collecting whole cells (tagging code or \pkg{collcell}), where
%    you can no longer alter the meaning of \cs{cr} as the master
%    counter goes wrong due to an obscure bug (or perhaps, say, an
%    undocumented feature of \TeX{}).  Eventually, we were able to pin
%    down the root cause and really understand why
%    \cs{@protected@firstofone} solves the problem, even though it
%    looks like a nonsense addition to the code that does nothing
%    useful.\footnote{So it is a \TeX{} engine bug that was in there
%    from day one, or if you like, it is a hidden feature that is not
%    explained; neither in the \TeX{}book nor in the program code. We
%    don't really expect this to change in \TeX{} after such a long
%    time, other than perhaps documenting it as a feature, so this is
%    a proper solution to the problem and not just a workaround.}
%
%    The problem is that \TeX{} tries to conserve stack space, and
%    when the last token of an existing token list is a macro, then
%    this token list is \emph{first} removed from memory (reducing the
%    stack) \emph{before} the macro replacement text (as a new
%    token list) is given to the parser adding a new stack level. This
%    is done using the routine \texttt{end\_token\_list} in the \TeX{}
%    program and ending the u-part of an \cs{halign} column with this
%    routine immediately sets the \emph{master counter} used by alignments to
%    zero (see chapter~22 and Appendix~D of the \TeX{}book). This
%    means that technically the expansion of the last token in the u-part (if it
%    is a macro) is not executed in the context of the u-part, but in
%    the context of the alignment entry in the document. That normally
%    doesn't make any difference whatsoever --- unless you play
%    around (as we sometimes have to) with tricks like those from
%    Appendix~D.
%
%    To illustrate the issue we show a bit of strange low-level plain
%    \TeX{} code.\footnote{If all of this looks mighty strange to you,
%    don't worry. You will be unlikely to need to know about it. It is
%    just there so that programmers at some point in the future do not
%    have to wonder too much why there is this odd
%    \cs{@protected@firstofone} that apparently does nothing
%    useful. It took us several nights of head scratching to come up
%    with these minimal examples and then some more time to understand
%    what the heck is going on inside \TeX{}---thanks to Bruno for the
%    right ideas on the latter.}  Below are two very special grouping
%    commands that are like \cs{bgroup} and \cs{egroup} but also
%    affect the alignment master counter when expanded (see
%    \TeX{}book p.385).  If one of them is used as the
%    last macro in the u-part of a column, then you get strange errors
%    that you shouldn't get. 
% \begin{verbatim}
% \def\bbgroup{{\ifnum0=`}\fi}
% \def\eegroup{\ifnum0=`{\fi}}
% 
% % Fails with an error message, but there should be none:
% \halign{%
%   \message{u-part^^J}%
%   \bbgroup              % <-- in the u-part
%   \eegroup              % <-- in the u-part
%   #%
%   \message{v-part^^J}%
%   \hfill\cr
%   \message{body^^J}x
%   \cr
% }
% 
% % Fails but should work, the v-part is never reached:
% \halign{%
%   \message{u-part^^J}%
%   \bbgroup              % <-- in the u-part
%   #%
%   \message{v-part^^JJ}%
%   \eegroup              % <-- in the v-part
%   \hfill\cr
%   \message{body^^J}x
%   \cr
% }
% \end{verbatim}
%
%    So the trick we use now is making \cs{@protected@firstofone} the
%    last macro in the u-part, i.e., before the \cs{@sharp}. That way
%    its argument is always fully expanded as part of the alignment
%    entry and not as part of the u-part and this way we know exactly
%    what the master counter value is at this point, regardless of the content of
%    \verb=>{...}=.
%
% \changes{v2.6f}{2024/09/13}{Stop parsing for optional argument (gh/1468)}
% \changes{v2.6g}{2024/10/12}{Further work to support optional args in preamble (gh/1468)}
%    \begin{macrocode}
   \@protected@firstofone { \the@toks \the \@tempcnta \ignorespaces }
%    \end{macrocode}
%    Next follows the =#= sign which specifies the place
%    where the text of the column shall be inserted. To avoid
%    errors during the expansions in
%    =\@addtopreamble= we hide this sign in the command
%    =\@sharp= which is temporarily occupied with
%    =\relax= during the build-up of the preamble.
%    To remove unwanted spaces before and after the column in text mode,
%    we set an =\ignorespaces=  in front (see above) and a =\unskip=
%    afterwards; in math mode, the latter is suppressed while the
%    \cs{ignorespaces} makes no difference.
% \changes{v2.0e}{1991/02/07}{Added \{\} around \cs{@sharp} for new ftsel}
% \changes{v2.0h}{1992/06/22}{Removed \{\} again in favor of
%                             \cs{d@llarbegin}}
% \changes{v2.6b}{2024/04/08}{Do not \cs{unskip} if in math mode (gh/1323)}
%    \begin{macrocode}
   \@sharp \textonly@unskip
%    \end{macrocode}
%    Then the second \textsf{token} register follows whose number should
%    be saved in =\count@=.
%    We make sure that there will be no further expansion after reading
%    the number, by finishing with =\relax=. The case above is not
%    critical since it is ended by =\ignorespaces=.
% \changes{v2.0c}{1990/08/14}{\cs{relax} added to avoid problem
%                           \cs{the}\cs{toks0}\cs{the}\cs{toks1}.}
%    \begin{macrocode}
   \the@toks \the \count@ \relax
%    \end{macrocode}
%    The corresponding hook at the end of the cell.
% \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
   \@@_hook_use_protected:nVVn {tbl/cell/end}%
                               \g_@@_row_int
                               \g_@@_col_int
                               {\ar@cell@type}%
%    \end{macrocode}
%
%    And another socket for tagging that adds the necessary closing tag
%    if enabled.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
   \UseTaggingSocket{tbl/cell/end}%
}
%    \end{macrocode}
%    
%
%    Do the unskip only if we are in hmode:
% \changes{v2.6b}{2024/04/08}{Do not \cs{unskip} if in math mode (gh/1323)}
%    \begin{macrocode}
\protected\def\textonly@unskip{\ifhmode\unskip\fi}
%    \end{macrocode}
% \changes{v2.6g}{2024/10/12}{Further work to support optional args in preamble (gh/1468)}
%  We need an engine-protected function that is just \cs{@firstofone}:
%    \begin{macrocode}
\protected\long\def\@protected@firstofone#1{#1}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
%  \begin{macro}{\@@_hook_use_protected:n,
%     \@@_hook_use_protected:nn,\@@_hook_use_protected:nnnn,
%     \@@_hook_use_protected:nV,\@@_hook_use_protected:nVVn}
%    
%    Unexpandable versions of \cs{hook_use:n} and
%    \cs{hook_use:nnw} for use within the preamble
%    construction that is done with \cs{xdef}.
%
%    For now we define them as internal commands because they are
%    really only needed because of the strange way \cs{@preamble} is
%    constructed which is really a historical artifact.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_hook_use_protected:n #1 {
  \hook_use:n {#1}
}
\cs_new_protected:Npn \@@_hook_use_protected:nn #1#2 {
  \hook_use:nnw {#1}{1}{#2}
}
\cs_new_protected:Npn \@@_hook_use_protected:nnnn #1#2#3#4 {
  \hook_use:nnw {#1}{3}{#2}{#3}{#4}
}
\cs_generate_variant:Nn \@@_hook_use_protected:nn { nV }
\cs_generate_variant:Nn \@@_hook_use_protected:nnnn { nVVn }
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}[int]{\insert@pcolumn}
%    Handling pcolumn-cells needs slightly different handling when
%    doing tagging.  Rather than changing the plugs in
%    \cs{insert@column} back and forth, we simply use a different
%    version of \cs{insert@column} that has its own sockets.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
\def\insert@pcolumn{%
%    \end{macrocode}
%    We start with the initalization hook for cells; again this one needs protection from
%    premature expansion.
% \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
   \@@_hook_use_protected:nVVn {tbl/cell/init}%
                               \g_@@_row_int
                               \g_@@_col_int
                               {\ar@cell@type}%
%    \end{macrocode}
%    Then set up the tagging.
%    \begin{macrocode}
   \UseTaggingSocket{tbl/pcell/begin}%
%    \end{macrocode}
%    If special code is wanted only for a specific type or for certain
%    rows, columns or even for a specific cell then the arguments can
%    be used to define conditional processing.
%    \begin{macrocode}
   \@@_hook_use_protected:nVVn {tbl/cell/begin}%
                               \g_@@_row_int
                               \g_@@_col_int
                               {\ar@cell@type}%
%    \end{macrocode}
% \changes{v2.6f}{2024/09/13}{Stop parsing for optional argument (gh/1468)}
%    \begin{macrocode}
   \the@toks \the \@tempcnta \relax
   \ignorespaces \@sharp \unskip
   \the@toks \the \count@ \relax
%    \end{macrocode}
%    And the corresponding closing hook and socket.
% \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
   \@@_hook_use_protected:nVVn {tbl/cell/end}%
                               \g_@@_row_int
                               \g_@@_col_int
                               {\ar@cell@type}%
   \UseTaggingSocket{tbl/pcell/end}%
}
%    \end{macrocode}
%  \end{macro}
%
%
%
% \subsection{The separation of columns}
%
% \begin{macro}[int]{\@addamp}
%    In the preamble a =&= has to be inserted between any two columns;
%    before the first column there should not be a =&=. As the user
%    preamble may start with a \texttt{|} we have to remember somehow
%    if we have already inserted a =#= (i.e.\ a column). This is done
%    with the boolean variable =\if@firstamp= that we test in
%    =\@addamp=, the macro that inserts the =&=.
%    \begin{macrocode}
%    \newif \@iffirstamp
\def\@addamp {
  \if@firstamp
    \@firstampfalse
  \else
%    \end{macrocode}
%    If we are after the first column we have to insert a \verb=&= and
%    also update the cell data.
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    \begin{macrocode}
    \edef\@preamble{\@preamble &
      \noexpand\tbl_update_cell_data: }
  \fi
}
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}[int]{\@acol}
% \begin{macro}[int]{\@acolampacol}
% \begin{macro}[int]{\col@sep}
%    We will now define some abbreviations for the extensions,
%    appearing most often in the preamble build-up.
%    Here =\col@sep= is a \textsf{dimen} register which is set
%    equivalent to =\arraycolsep= in an \textsf{array}--environment,
%    otherwise it is set equivalent to =\tabcolsep=.
%    \begin{macrocode}
\newdimen\col@sep
\def\@acol{\@addtopreamble{\hskip\col@sep}}
%    \def\@acolampacol{\@acol\@addamp\@acol}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% \subsection{The macro \texttt{\textbackslash @mkpream}}
%
% \begin{macro}[int]{\@mkpream}
%    \emph{The code below has been replaced long time ago by an
%    extended version further down but the code and its documentation
%    was left here for reference. It is now commented out to avoid
%    confusion.}
% \begin{macro}[int]{\the@toks}
%    \label{@mkpream}
%    Now we can define the macro which builds up the preamble for the
%    =\halign=.
%    First we initialize =\@preamble=, =\@lastchclass=
%    and the boolean variable =\if@firstamp=.
%    \begin{macrocode}
%\def\@mkpream#1{\gdef\@preamble{}\@lastchclass 4 \@firstamptrue
%    \end{macrocode}
%    During the build-up of the preamble we cannot directly use the
%    =#= sign; this would lead to an error message in the next
%    =\@addtopreamble= call.
%    Instead, we use the command =\@sharp= at places where later
%    a =#= will be.
%    This command is at first given the meaning =\relax=;
%    therefore it will not be expanded when the preamble
%    is extended.
%    In the macro =\@array=, shortly before the =\halign=
%    is carried out, =\@sharp= is given its final meaning.
%
%    In a similar way,
%    we deal with the commands =\@startpbox= and
%    =\@endpbox=, although the reason is different here: these
%    macros expand in many \textsf{tokens} which would delay the
%    build-up of the preamble.
%    \begin{macrocode}
%   \let\@sharp\relax\let\@startpbox\relax\let\@endpbox\relax
%    \end{macrocode}
%    Two more are needed to deal with the code that handles struts
%    for extra space after a row from =\\[<space>]=
%    (=\do@row@strut=) and code that manages m-cells depending on
%   their heights (=\ar@align@mcell=).
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
% \changes{v2.4f}{2017/11/04}{Managing m-cells without \cs{vcenter}}
%    \begin{macrocode}
%   \let\do@row@strut\relax
%   \let\ar@align@mcell\relax
%    \end{macrocode}
%    Now we remove possible  $*$-forms in the user preamble with the
%    command =\@xexpast=.  As we already know, this command saves
%    its result in the macro =\@tempa=.
%    \begin{macrocode}
%   \@xexpast #1*0x\@@@@
%    \end{macrocode}
%    Afterwards we initialize all registers and macros, that we need
%    for the build-up of the preamble.
%    Since we want to start with the \textsf{token} register $0$,
%    =\count@= has to contain the value $-1$.
%    \begin{macrocode}
%   \count@\m@ne
%   \let\the@toks\relax
%    \end{macrocode}
%    Then we call up =\prepnext@tok= in order to prepare the
%    \textsf{token} register $0$ for use.
%    \begin{macrocode}
%   \prepnext@tok
%    \end{macrocode}
%    To evaluate the user preamble (without stars) saved in
%    =\@tempa= we use the \LaTeX--macro =\@tfor=.
%    The strange appearing construction with =\expandafter= is
%    based on the fact that we have to put the replacement text of
%    =\@tempa= and not the macro =\@tempa= to this
%    \LaTeX--macro.
%    \begin{macrocode}
%   \expandafter \@tfor \expandafter \@nextchar
%    \expandafter :\expandafter =\@tempa \do
%    \end{macrocode}
%    The body of this loop (the group after the =\do=)
%    is executed for one \textsf{token} at a time, whereas
%    the current \textsf{token} is saved in =\@nextchar=.
%    At first we evaluate the current \textsf{token} with the already
%    defined macro =\@testpach=, i.e.\ we assign to
%    =\@chclass= the character class and to =\@chnum=
%    the character number of this \textsf{token}.
% \changes{v2.0f}{1992/02/29}{\cs{@testpach} now without arg}
%    \begin{macrocode}
%   {\@testpach
%    \end{macrocode}
%    Then we branch out depending on the value of =\@chclass= into
%    different macros that extend the preamble respectively.
%    \begin{macrocode}
%   \ifcase \@chclass \@classz \or \@classi \or \@classii
%     \or \save@decl \or \or \@classv \or \@classvi
%     \or \@classvii \or \@classviii  \or \@classix
%     \or \@classx \fi
%    \end{macrocode}
%    Two cases deserve our special attention: Since the current
%    \textsf{token} cannot have the character class $4$ (start) we
%    have skipped this possibility. If the character class is $3$,
%    only the content of =\@nextchar= has to be saved into the current
%    \textsf{token} register; therefore we call up =\save@decl=
%    directly and save a macro name.  After the preamble has been
%    extended we assign the value of =\@chclass= to the counter
%    =\@lastchclass= to assure that this information will be available
%    during the next run of the loop.
%    \begin{macrocode}
%   \@lastchclass\@chclass}%
%    \end{macrocode}
%    After the loop has been finished space must still be added to
%    the created preamble, depending on the last \textsf{token}.
%    Depending on the value of =\@lastchclass= we perform
%    the necessary operations.
%    \begin{macrocode}
%   \ifcase\@lastchclass
%    \end{macrocode}
%    If the last class equals $0$ we add a
%    =\hskip \col@sep=.
%    \begin{macrocode}
%   \@acol \or
%    \end{macrocode}
%    If it equals $1$ we do not add any additional space so that the
%    horizontal lines do not exceed the vertical ones.
%    \begin{macrocode}
%   \or
%    \end{macrocode}
%    Class $2$ is treated like class $0$ because a =<{...}=  can
%    only directly follow after class $0$.
%    \begin{macrocode}
%   \@acol \or
%    \end{macrocode}
%    Most of the other possibilities can only appear if the user
%    preamble was defective. Class $3$ is not allowed since after a
%    =>{..}= there must always follow a \texttt{c}, \texttt{l},
%    \texttt{r}, \texttt{p},\texttt{m} or \texttt{b}. We report an
%    error and ignore the declaration given by ={..}=.
%    \begin{macrocode}
%   \@preamerr \thr@@@@ \or
%    \end{macrocode}
%    If =\@lastchclass= is $4$ the user preamble has been empty.
%    To continue, we insert a =#= in the preamble.
%    \begin{macrocode}
%   \@preamerr \tw@ \@addtopreamble\@sharp \or
%    \end{macrocode}
%    Class $5$ is allowed again. In this case
%    (the user preamble ends with =@{..}=) we need not
%    do anything.
%    \begin{macrocode}
%   \or
%    \end{macrocode}
%    Any other case means that the arguments to =@=, \texttt{!},
%    \texttt{<}, \texttt{>}, \texttt{p}, \texttt{m} or \texttt{b} have
%    been forgotten. So we report an error and ignore the last
%    \textsf{token}.
%    \begin{macrocode}
%   \else  \@preamerr \@ne \fi
%    \end{macrocode}
%    Now that the build-up of the preamble is almost finished we can
%    insert the \textsf{token} registers and therefore redefine
%    =\the@toks=. The actual insertion, though, is performed
%    later.
%    \begin{macrocode}
%   \def\the@toks{\the\toks}}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
%  \section{The macros \texttt{\textbackslash @classz}
%           to \texttt{\textbackslash @classx}}
%
% The preamble is extended by the macros =\@classz= to
% =\@classx= which are called by =\@mkpream=
%  depending on =\@lastchclass=
% (i.e. the character class of the last \textsf{token}).
% \begin{macro}[int]{\@classx}
%    First we define =\@classx= because of its important r\^ole.
%    When it is called we find that the current
%    \textsf{token} is \texttt{p}, \texttt{m} or \texttt{b}.
%    That means that a new column has to start.
%    \begin{macrocode}
\def\@classx{%
%    \end{macrocode}
%    Depending on the value of =\@lastchclass= different actions
%    must take place:
%    \begin{macrocode}
  \ifcase \@lastchclass
%    \end{macrocode}
%    If the last character class was $0$ we separate the columns by
%    =\hskip\col@sep= followed by =&= and another
%    =\hskip\col@sep=.
%    \begin{macrocode}
  \@acolampacol \or
%    \end{macrocode}
%    If the last class was class $1$ --- that means that a vertical
%    line was
%    drawn, --- before this line a =\hskip\col@sep= was inserted.
%    Therefore there has to be only a =&= followed by
%    =\hskip\col@sep=. But this =&= may be inserted only
%    if this is not the first column. This process is controlled
%    by =\if@firstamp= in the macro =\addamp=.
%    \begin{macrocode}
  \@addamp \@acol \or
%    \end{macrocode}
%    Class $2$ is treated like class $0$ because =<{...}= can only
%    follow after class $0$.
%    \begin{macrocode}
  \@acolampacol \or
%    \end{macrocode}
%    Class $3$ requires no actions because all things necessary have
%    been done by the preamble \textsf{token} \texttt{>}.
%    \begin{macrocode}
  \or
%    \end{macrocode}
%    Class $4$ means that we are at the beginning of the preamble.
%    Therefore we start the preamble with =\hskip\col@sep= and
%    then call =\@firstampfalse=. This makes sure that a later
%    =\@addamp= inserts the character
%    =&= into the preamble.
%    \begin{macrocode}
  \@acol \@firstampfalse \or
%    \end{macrocode}
%    For class $5$ \textsf{tokens} only the character =&= is inserted
%    as a column separator. Therefore we call =\@addamp=.
%    \begin{macrocode}
  \@addamp
%    \end{macrocode}
%    Other cases are impossible. For an example
%    $=\@lastchclass= \string= 6$---as it might appear in a
%    preamble of the form =...!p...=---\texttt{p} would have
%    been taken as an  argument of \texttt{!}\ by =\@testpach=.
%    \begin{macrocode}
  \fi}
%    \end{macrocode}
% \end{macro}
%
% \changes{v2.6h}{2024/12/02}{Add tagging sockets for luamml support}
% Until the sockets are in the release we test for existence 
% and declare them.
%    \begin{macrocode}
\str_if_exist:cF { l__socket_tagsupport/math/luamml/array/save_plug_str }
 {
   \NewSocket{tagsupport/math/luamml/array/save}{0}
   \NewSocket{tagsupport/math/luamml/array/finalize}{0}
   \NewSocket{tagsupport/math/luamml/array/initcol}{0}
   \NewSocket{tagsupport/math/luamml/array/finalizecol}{1}
   \AssignSocketPlug{tagsupport/math/luamml/array/finalizecol}{noop}
 }
%    \end{macrocode}
% \begin{macro}[int]{\@classz}
%    If the character class of the last \textsf{token} is $0$ we have
%    \texttt{c}, \texttt{l}, \texttt{r} or an argument of \texttt{m},
%    \texttt{b} or\ \texttt{p}. In the first three cases the preamble
%    must be extended the same way as if we had class $10$. The
%    remaining two cases do not require any action because the space
%    needed was generated by the last \textsf{token} (i.e.\
%    \texttt{m}, \texttt{b} or \texttt{p}). Since =\@lastchclass= has
%    the value $10$ at this point nothing happens when =\@classx= is
%    called. So the macro =\@chlassz= may start like this:
%    \begin{macrocode}
\def\@classz{\@classx
%    \end{macrocode}
%    According to the definition of =\insert@column= we must store
%    the number of the \textsf{token} register in which a preceding
%    =>{..}= might have stored its argument into
%    =\@tempcnta=.
%    \begin{macrocode}
   \@tempcnta \count@
%    \end{macrocode}
%    To have $=\count@= \string= =\@tmpcnta= + 1$ we prepare
%    the next \textsf{token} register.
%    \begin{macrocode}
   \prepnext@tok
%    \end{macrocode}
%    Now the preamble must be extended with the column whose format
%    can be determined by =\@chnum=.
%    \begin{macrocode}
   \@addtopreamble{\ifcase \@chnum
%    \end{macrocode}
%    If =\@chnum= has the value $0$ a centered column has to be
%    generated.
%    So we begin with stretchable space.
%    \begin{macrocode}
      \hfil
%    \end{macrocode}
%    We also add a space of 1sp just in case the first thing in the
%    cell is a command doing an =\unskip=.
% \changes{v2.4k}{2018/12/30}{Add extra \cs{hskip} to guard against an
%   \cs{unskip} at the start of a c-column cell (gh/102)}
%    \begin{macrocode}
      \hskip1sp%
%    \end{macrocode}
%    The command =\d@llarbegin= follows expanding into =\begingroup=
%    (in the \textsf{tabular}--environment) or into =$=.  Doing this
%    (provided an appropriate setting of =\d@llarbegin=) we achieve
%    that the contents of the columns of an \textsf{array}--environment
%    are set in math mode while those of a \textsf{tabular}--environment
%    are set in LR mode.
%    \begin{macrocode}
      \d@llarbegin
%    \end{macrocode}
%    Now we insert the contents of the two \textsf{token} registers
%    and the symbol
%    for the column entry (i.e.\ =#= or
%    more precise =\@sharp=) using =\insert@column=. 
%    \begin{macrocode}
      \insert@column
%    \end{macrocode}
%    We end this case with =\d@llarend= and =\hfil= where =\d@llarend=
%    again is either =$= or =\endgroup=.
%    The strut to enforce a regular row height is placed between the two.
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
%    \begin{macrocode}
      \d@llarend 
      \do@row@strut \hfil \or
%    \end{macrocode}
%    The templates for \texttt{l} and \texttt{r} (i.e.\ =\@chnum= $1$
%    or $2$) are generated the same way. Since one  =\hfil= is
%    missing the text is moved to the relevant side.
%    The =\kern\z@= is needed in case of an empty column
%    entry. Otherwise
%    the =\unskip= in =\insert@column= removes the
%    =\hfil=. Changed to =\hskip1sp= so that it interacts better with
%    =\@bsphack=.
% \changes{v2.3f}{1996/04/22}
%     {(DPC) Extra \cs{kern} keeps tabcolsep in empty l columns
%             internal/2122}
% \changes{v2.3i}{1996/06/14}
%     {Change both \cs{kern}\cs{z@} to \cs{hskip}1sp for latex/2160}
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
%    \begin{macrocode}
      \hskip1sp\d@llarbegin\insert@column\d@llarend
      \do@row@strut \hfil \or
      \hfil\hskip1sp\d@llarbegin\insert@column\d@llarend
      \do@row@strut \or
%    \end{macrocode}
%    The templates for \texttt{p}, \texttt{m} and \texttt{b} mainly
%    consist of a \textsf{box}. In case of \texttt{m} it is generated
%    by =\vcenter=.  This command is allowed only in math
%    mode. Therefore we start with a~=$=.
% \changes{v2.4f}{2017/11/04}{Managing m-cells without \cs{vcenter}}
%    \begin{macrocode}
    \setbox\ar@mcellbox\vbox
%    \end{macrocode}
%    The part of the templates which is the same in all three cases
%    (\texttt{p}, \texttt{m} and \texttt{b}) is built by the macros
%    =\@startpbox= and =\@endpbox=. =\@startpbox= has an argument: the
%    width of the column which is stored in the current \textsf{token}
%    (i.e.\ =\@nextchar=).  Between these two macros we find the well
%    known =\insert@column= or rather the variant for tagging:
%    \cs{insert@pcolumn}.  The strut is placed after the box.
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
% \changes{v2.4f}{2017/11/04}{Managing m-cells without \cs{vcenter}}
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
    \@startpbox{\@nextchar}\insert@pcolumn \@endpbox
    \ar@align@mcell
    \do@row@strut \or
%    \end{macrocode}
%    The templates for \texttt{p} and \texttt{b} are generated in the
%    same way though we do not need the =$= characters because we use
%    =\vtop= or =\vbox=.
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
   \vtop \@startpbox{\@nextchar}\insert@pcolumn \@endpbox\do@row@strut \or
   \vbox \@startpbox{\@nextchar}\insert@pcolumn \@endpbox\do@row@strut
%    \end{macrocode}
%    Other values for =\@chnum= are impossible. Therefore we
%    end the arguments to =\@addtopreamble= and =\ifcase=.
%    Before we come to the end of =\@classz= we have to
%    prepare the next \textsf{token} register.
%    \begin{macrocode}
  \fi}\prepnext@tok}
%    \end{macrocode}
% \end{macro}
%

%
% \begin{macro}[int]{\ar@mcellbox}
%    When dealing with m-cells we need a box to measure the cell
%    height.
% \changes{v2.4f}{2017/11/04}{Managing m-cells without \cs{vcenter}}
%    \begin{macrocode}
\newbox\ar@mcellbox
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[int]{\ar@align@mcell}
%    M-cells are supposed to be vertically centered within the table
%    row. In the original implementation that was done using
%    =\vcenter= but the issue with that approach is that it centers
%    the material based on the math-axis. In most situations that
%    comes out quit right, but if, for example, an m-cell has only a
%    single line worth of material inside it will be positioned
%    differently to a =l=, =c= or =r= cell or to a =p= or =b= cell
%    with  the same content.
%
%    For that reason the new implementation does the centering
%    manually: First we check the height of the cell and if that is
%    less or equal to =\ht\strutbox= we assume that this is a
%    single line cell. In that case  we don't do any vertical maneuver
%    and simply output the box, i.e., make it behave like a single
%    line p-cell.
%
%    We use the height of \cs{strutbox} not \cs{@arstrutbox} in the comparison,
%    because \cs{box}\cs{ar@mcellbox} does not have any strut
%    incorporated and if \cs{arraystretch} is made very
%    small the test would otherwise incorrectly assume a multi-line cell.
% \changes{v2.4f}{2017/11/04}{Managing m-cells without \cs{vcenter}}
% \changes{v2.5g}{2022/09/04}{Test against \cs{strutbox} height (gh/766)}
%    \begin{macrocode}
\def\ar@align@mcell{%
  \ifdim \ht\ar@mcellbox > \ht\strutbox
%    \end{macrocode}
%    Otherwise we realign vertically by lowering the box. The question
%    is how much do we need to move down? If there is any
%    =\arraystretch= in place then the first line will have some
%    unusual height and we don't want to consider that when finding
%    the middle point. So we subtract from the cell height the height
%    of that strut. But of course we want to include the normal height
%    of the first line (which would be something like =\ht\strutbox=)
%    so we need to add that. On the other hand, when centering around
%    the mid-point of the cell, we also need to account for the depth
%    of the last line (which is nominally something like
%    =\dp\strutbox=). Both together equals =\baselineskip= so that is
%    what we add and then lower the cell by half of the resulting value.
%    \begin{macrocode}
    \begingroup
     \dimen@\ht\ar@mcellbox
     \advance\dimen@-\ht\@arstrutbox
     \advance\dimen@\baselineskip
     \lower.5\dimen@\box\ar@mcellbox
    \endgroup
  \else % assume one line and align at baseline
    \box\ar@mcellbox
  \fi}
%    \end{macrocode}
% \end{macro}
%

%
% \begin{macro}[int]{\@classix}
%    \emph{The code below has been replaced long time ago by an
%    extended version further down but the code and its documentation
%    was left here for reference. It is now commented out to avoid
%    confusion.}
%
%    In case of class $9$ (\texttt{>}--\textsf{token}) we first check
%    if the character class of the last
%    \textsf{token} was $3$. In this case we have a
%    user preamble of the form =..>{...}>{...}..= which
%    is not allowed. We only give an error message and continue.
%    So the declarations defined by the first  =>{...}=
%    are ignored.
%    \begin{macrocode}
%\def\@classix{\ifnum \@lastchclass = \thr@@@@
%       \@preamerr \thr@@@@ \fi
%    \end{macrocode}
%    Furthermore, we call up =\@class10= because afterwards always a
%    new column is started by \texttt{c}, \texttt{l}, \texttt{r},
%    \texttt{p}, \texttt{m} or \texttt{b}.
%    \begin{macrocode}
%       \@classx}
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}[int]{\@classviii}
%    \emph{The code below has been replaced long time ago by an
%    extended version further down but the code and its documentation
%    was left here for reference. It is now commented out to avoid
%    confusion.}
%
%    If the current \textsf{token} is a \texttt{<} the last character
%    class must be $0$. In this case it is not necessary to extend the
%    preamble. Otherwise we output an error message, set =\@chclass=
%    to $6$ and call =\@classvi=.  By doing this we achieve that
%    \texttt{<} is treated like \texttt{!}.
%    \begin{macrocode}
%\def\@classviii{\ifnum \@lastchclass >\z@
%      \@preamerr 4\@chclass 6 \@classvi \fi}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@arrayrule}
%    There is only one incompatibility with the original definition:
%    the definition of =\@arrayrule=. In the original a line without
%    width\footnote{So the space between \texttt{cc} and \texttt{c|c}
%    is equal.}  is created by multiple insertions of
% =\hskip .5\arrayrulewidth=.
%    We only insert a vertical line into the
%    preamble.  This is done to prevent problems with \TeX's main
%    memory when generating tables with many vertical lines in them
%    (especially in the case of \textsf{floats}).
%    \begin{macrocode}
\def\@arrayrule{\@addtopreamble \vline}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@classvii}
%    As a consequence it follows that in case of class $7$
%    (=@= \textsf{token})  the preamble need not to be extended.
%    In the original definition $=\@lastchclass= \string= 1$
%    is treated by inserting =\hskip .5\arrayrulewidth=.
%    We only check if the last \textsf{token} was of class $3$ which is
%    forbidden.
%    \begin{macrocode}
\def\@classvii{\ifnum \@lastchclass = \thr@@@@
%    \end{macrocode}
%    If this is true we output an error message and
%    ignore the declarations stored
%    by the last  =>{...}=, because these are overwritten
%    by the argument of \texttt{@}.
%    \begin{macrocode}
   \@preamerr \thr@@@@ \fi}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[int]{\@classvi}
%    If the current \textsf{token} is a regular \texttt{!}\ and the
%    last class was $0$ or $2$ we extend the preamble with
%    =\hskip\col@sep=.  If the last \textsf{token} was of class $1$
%    (for instance \texttt{|}) we extend with =\hskip \doublerulesep=
%    because the construction =!{...}= has to be treated like
%    \texttt{|}.
%    \begin{macrocode}
\def\@classvi{\ifcase \@lastchclass
      \@acol \or
      \@addtopreamble{\hskip \doublerulesep}\or
      \@acol \or
%    \end{macrocode}
%    Now =\@preamerr...= should follow because a
%    user preamble of the form =..>{..}!.= is not allowed.
%    To save memory we call =\@classvii= instead which also
%    does what we want.
%    \begin{macrocode}
      \@classvii
%    \end{macrocode}
%    If =\@lastchclass= is $4$ or $5$ nothing has to be done.
%    Class $6$ to $10$ are not possible.
%    So we finish the macro.
%    \begin{macrocode}
      \fi}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@classii}
% \begin{macro}[int]{\@classiii}
%    In the case of character classes $2$ and $3$ (i.e.\ the argument
%    of \texttt{<} or \texttt{>}) we only have to store the current
%    \textsf{token} (=\@nextchar=) into the corresponding
%    \textsf{token} register since the preparation and
%    insertion of these registers
%    are done by the macro =\@classz=.
%    This is equivalent to calling =\save@decl= in the case of
%    class $3$. To save command identifiers we do this call up
%     in the macro =\@mkpream=.
%
%    Class $2$ exhibits a more complicated situation: the
%    \textsf{token} registers have already been inserted by
%    =\@classz=. So the value of =\count@= is too high
%    by one. Therefore we decrease =\count@= by $1$.
%    \begin{macrocode}
\def\@classii{\advance \count@ \m@ne
%    \end{macrocode}
%    Next we store the current \textsf{token} into the correct
%    \textsf{token} register by calling =\save@decl= and then
%    increase the value of =\count@= again. At this point we
%    can save memory once more (at the cost of time) if we use the
%     macro =\prepnext@tok=.
%    \begin{macrocode}
   \save@decl\prepnext@tok}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
% \begin{macro}[int]{\@classv}
%    \emph{The code below has been replaced long time ago by an
%    extended version further down but the code and its documentation
%    was left here for reference. It is now commented out to avoid
%    confusion.}
%
%    If the current \textsf{token} is of class $5$ then it is an
%    argument of a \texttt{@} \textsf{token}. It must be stored into a
%    \textsf{token} register.
%    \begin{macrocode}
%\def\@classv{\save@decl
%    \end{macrocode}
%    We extend the preamble with a command which inserts this
%    \textsf{token} register into the preamble when its construction
%    is finished. The user expects that this argument is worked out in
%    math mode if it was used in an
%    \textsf{array}--environment. Therefore we surround it with
%    =\d@llar...='s.
% \changes{v2.0c}{1990/08/14}{\cs{relax} added to avoid problem
%                           `the`toks0`the`toks1.}
%    \begin{macrocode}
%   \@addtopreamble{\d@llarbegin\the@toks\the\count@\relax\d@llarend}%
%    \end{macrocode}
%    Finally we must prepare the next \textsf{token} register.
%    \begin{macrocode}
%   \prepnext@tok}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@classi}
%    In the case of class $0$ we were able to generate the necessary
%    space between columns by using the macro =\@classx=.
%    Analogously the macro =\@classvi= can be used for class $1$.
%    \begin{macrocode}
\def\@classi{\@classvi
%    \end{macrocode}
%    Depending on =\@chnum= a vertical line
%    \begin{macrocode}
   \ifcase \@chnum \@arrayrule \or
%    \end{macrocode}
%    or (in case of =!{...}=) the current \textsf{token} --- stored
%    in =\@nextchar= --- has to be inserted into the preamble.
%    This corresponds to calling =\@classv=.
%    \begin{macrocode}
      \@classv \fi}
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}[int]{\@startpbox,\@startpbox@action}
%    In =\@classz=  the macro =\@startpbox= is used.
%    The width of the \textsf{parbox} is passed as an argument.
%    =\vcenter=, =\vtop= or =\vbox= are already in the
%    preamble. So we start with the braces for the wanted box.
% \changes{v2.4i}{2018/09/13}{Add group to prevent color leak (gh/72)}
%    \begin{macrocode}
\def\@startpbox#1{\bgroup
  \color@begingroup
%    \end{macrocode}
%    The argument is the width of the box. This information has to be
%    assigned to =\hsize=.
%    Then we assign default values to several parameters used in a
%    \textsf{parbox}.
% \changes{v2.3k}{1998/05/12}{Use \cs{setlength} to set \cs{hsize},
%      so that the calc package can be applied here (pr/2793)}
%    \begin{macrocode}
  \setlength\hsize{#1}\@arrayparboxrestore
%    \end{macrocode}
%    Our main problem is to obtain the same distance between succeeding
%    lines of the \textsf{parbox}.
%    We have to remember that the distance between two \textsf{parboxes}
%    should be defined by =\@arstrut=. That means that it can be
%    greater than the distance in a \textsf{parbox}.
%    Therefore it is not enough to set a =\@arstrut= at the
%    beginning and at the end of the \textsf{parbox}. This would
%    dimension the distance
%    between first and second line and the distance between the two
%    last lines of the \textsf{parbox} wrongly.
%    To prevent this we set an invisible rule of height
%    =\@arstrutbox=
%    at the beginning of the \textsf{parbox}. This has no effect on the
%    depth of the first line. At the end of the \textsf{parbox} we set
%    analogously another invisible rule which only affects the depth
%    of the last line. It is necessary to wait inserting this strut
%    until the paragraph actually starts to allow for things like
%    =\parindent= changes via =>{...}=.
% \changes{v2.1c}{1992/12/14}{Use `everypar to insert strut}
%    \begin{macrocode}
   \everypar{%
      \vrule \@height \ht\@arstrutbox \@width \z@
      \everypar{}}%
   }
%    \end{macrocode}
%
% \changes{v2.6h}{2024/12/17}{New \cs{@startpbox@action} to do the real work
%    after the preamble is built (gh/1585)}
%    Starting with 2.6h, \cs{@startpbox} is used only during the
%    built-up of the preamble and then changed to \cs{@startpbox@action}
%    which does the real work. For an explanation of this code see
%    the documentation in \cs{@mkpream}. We keep the \cs{@startpbox}
%    definition on top-level in case there are tabular packages that
%    built the preamble differently and expect that it contains
%    \cs{@startpbox} (\pkg{longtable} at the moment).
%    \begin{macrocode}
\let\@startpbox@action\@startpbox
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@endpbox}
%    If there are any declarations defined by =>{...}=
%    and =<{...}=
%    they now follow in the macro =\@classz= --- the contents
%    of the column in between.
%    So the macro =\@endpbox= must insert the \textsf{specialstrut}
%    mentioned earlier and then close the group opened by
%    =\@startpbox=.
% \changes{v2.2d}{1994/05/16}{Use \LaTeXe \cs{@finalstrut}}
% \changes{v2.3g}{1996/05/07}{Add \cs{hfil} for tools/2120}
% \changes{v2.4i}{2018/09/13}{Add group to prevent color leak (gh/72)}
% \changes{v2.5d}{2021/02/10}{Explicitly run \cs{par} at the end of pboxes}
%    \begin{macrocode}
\def\@endpbox{\@finalstrut\@arstrutbox \par \color@endgroup \egroup\hfil}
%    \end{macrocode}
% \end{macro}
%    
%  \begin{macro}[int]{\@finalstrut}
%    We also have to adjust \cs{@finalstrut} so that it doesn't back
%    up by a \cs{baselineskip} when the the current vlist is completely empty.
%    This should go eventually to \file{ltboxes} but not in a PL.
% \changes{v2.6i}{2025/05/19}{Ensure \cs{arraystretch} is taken into
%    account inside a tabular (gh/1730)}
% \changes{v2.6j}{2025/06/08}{Fix gh/1730 correctly (gh/1765)}
%    \begin{macrocode}
\def\@finalstrut#1{%
  \unskip
  \ifhmode \nobreak
  \else
    \ifnum\lastnodetype>\m@ne 
      \vskip-\baselineskip
    \fi
  \fi
  \vrule\@width\z@\@height\z@\@depth\dp#1}
%    \end{macrocode}
%  \end{macro}
%
%
% \section{Building and calling \texttt{\textbackslash halign}}
%
% \begin{macro}[int]{\@array}
%    After we have discussed the macros needed for the evaluation
%    of the user preamble we can define the macro =\@array=
%    which uses these macros to create a =\halign=.
%    It has two arguments. The first one is a position argument
%    which can be \texttt{t}, \texttt{b} or \texttt{c}; the
%    second one describes the wanted preamble,
%    e.g.\ it has the form =|c|c|c|=.
%    \begin{macrocode}
\def\@array[#1]#2{
%    \end{macrocode}
%    First we define a \textsf{strut} whose size basically corresponds
%     to a normal \textsf{strut} multiplied by the factor
%    =\arraystretch=.
%    This \textsf{strut} is then  inserted into every row and enforces
%     a minimal distance between two rows.
%    Nevertheless, when using horizontal lines, large letters
%    (like accented capital letters) still collide with such lines.
%    Therefore at first we add to the height of a normal \textsf{strut}
%    the value of the parameter =\extrarowheight=.
%    \begin{macrocode}
  \@tempdima \ht \strutbox
  \advance \@tempdima by\extrarowheight
  \setbox \@arstrutbox \hbox{\vrule
             \@height \arraystretch \@tempdima
             \@depth \arraystretch \dp \strutbox
             \@width \z@}%
%    \end{macrocode}
%    Before starting a table we have to initialize the variables
%    holding row and column information for cells. We also have
%    locally store the information related to the current cell (if we
%    are already inside a table) so that we can restore it once the
%    inner table is finished.
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    The total number of table columns of the current table is
%    determined in \cs{tbl_count_table_cols:} but this is called in
%    a group, so local settings do not survive. Thus, to save away the
%    outer value of \cs{g_@@_table_cols_tl} we do that also
%    \cs{tbl_init_cell_data_for_table:}.
% \changes{v2.6k}{2025/07/18}{Set cs{tbl_init_cell_data_for_table} here}
%    \begin{macrocode}
  \tbl_init_cell_data_for_table:
%    \end{macrocode}
%    Then we open a group, in which the user preamble is evaluated by
%    the macro =\@mkpream=. As we know this must happen locally.
%    This macro creates a preamble for a =\halign= and saves
%    its result globally in the control sequence =\@preamble=.
%    \begin{macrocode}
  \begingroup
  \@mkpream{#2}%
%    \end{macrocode}
%    Figure out how many columns this table has:
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    \begin{macrocode}
  \tbl_count_table_cols:
%    \end{macrocode}
%    We again redefine =\@preamble= so that a call up of =\@preamble=
%    now starts the =\halign=. Thus also the arguments of \texttt{>},
%    \texttt{<}, \texttt{@} and \texttt{!}, saved in the
%    \textsf{token} registers are inserted into the preamble.  The
%    =\tabskip= at the beginning and end of the preamble is set to
%    \textsf{0pt} (in the beginning by the use of =\ialign=). Also the
%    command =\@arstrut= is build in, which inserts the
%    =\@arstrutbox=, defined above. Of course, the opening brace after
%    =\ialign= has to be implicit as it will be closed in =\endarray=
%    or another macro.
% \changes{v2.3m}{1998/12/31}{Added \cs{noexpand} in front of \cs{ialign}
%    to guard against interesting :-) changes to \cs{halign} done to support
%    text glyphs in math}
%
%    The =\noexpand= in front of =\ialign= does no harm in standard \LaTeX{}
%    and was added since some experimental support for using text glyphs in math
%    redefines =\halign= with the result that is becomes expandable with
%    disastrous results in cases like this.
%    In the kernel definition for this macro the problem does
%    not surface because there =\protect= is set (which is not necessary in this
%    implementation as there is no arbitrary user input that can get expanded) and
%    the experimental code made the redefinition robust. Whether this is the right
%    approach is open to question; consider the =\noexpand= a courtesy to allow an
%    unsupported redefinition of a \TeX{} primitive for the moment (as people rely
%    on that experimental code).
%    \begin{macrocode}
  \xdef\@preamble{
%    \end{macrocode}
%    \cs{ialign} in the original definition is replaced by
%    \cs{ar@ialign} defined below. This does what \cs{ialign} does but
%    additionally handles the tagging structure for the whole table if necessary.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
    \noexpand \ar@ialign
    \@halignto
       \bgroup \@arstrut
%    \end{macrocode}
%    What we have not explained yet is the macro =\@halignto=
%    that was just used. Depending on its replacement text the
%    =\halign= becomes a =\halign= \texttt{to} \meta{dimen}.
%    
%    Next, a tagging support socket is inserted adding the start row tag.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
       \UseTaggingSocket{tbl/row/begin}
%    \end{macrocode}
%    
%    \begin{macrocode}
       \@@_hook_use_protected:nV {tbl/row/begin} \g_@@_row_int
%    \end{macrocode}
%    At the start of the preamble for the first column we call
%    \cs{tbl_init_cell_data_for_row:} to initialize the cell index data. In
%    later columns this data is updated via \cs{tbl_update_cell_data:}.
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    \begin{macrocode}
       \tbl_init_cell_data_for_row:
%    \end{macrocode}
%    
%    \begin{macrocode}
       \@preamble
       \tabskip \z@ \cr}
%    \end{macrocode}
%    Now we close the group again. Thus
%    =\@startpbox= and =\@endpbox= as well as all
%    \textsf{token} registers get their former meaning back.
%    \begin{macrocode}
  \endgroup
%    \end{macrocode}
%    Then we run the hook \hook{tbl/init} that can be used by other
%    packages to add additional initializations. We pass it the number
%    of columns detected and the user preamble for inspection in the
%    hook code.
% \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
  \exp_args:Nnno \hook_use:nnw {tbl/init} {2}
                               \g_@@_table_cols_tl {#2}
%    \end{macrocode}
%     To support the \texttt{delarray.sty} package  we include a hook
%     into this part of the code which is a no-op in the main package.
% \changes{v2.1a}{1992/07/03}{Hook for delarray added}
%    \begin{macrocode}
  \@arrayleft
%    \end{macrocode}
%    Now we decide depending on the position argument in which
%    \textsf{box} the =\halign= is to be put. (=\vcenter= may be used
%    because we are in math mode.)
% \changes{v2.1a}{1992/07/03}{Wrong spec is now equiv to [t]}
% \changes{v2.7b}{2026/02/24}{Use text mode vertical centering}
%    \begin{macrocode}
  \if #1t\vtop \else \if#1b\vbox \else \vcenter@text \fi \fi
%    \end{macrocode}
%    Now another implicit opening brace appears; then definitions
%    which shall stay local follow. While constructing the
%    =\@preamble= in =\@mkpream= the =#= sign must be
%    hidden in the macro =\@sharp= which is =\let= to
%    =\relax= at that moment (see definition of =\@mkpream=
%    on page~\pageref{@mkpream}).
%    All these now get their actual meaning.
%    \begin{macrocode}
  \bgroup
  \let \@sharp ##\let \protect \relax
%    \end{macrocode}
%    With the above defined \textsf{struts} we fix down the distance
%    between rows by setting =\lineskip= and =\baselineskip=
%    to \textsf{0pt}. Since there have to be set =$='s
%    around every column in the \textsf{array}--environment
%     the parameter =\mathsurround= should
%    also be set to \textsf{0pt}. This prevents additional space between
%    the rows.
%    \begin{macrocode}
  \lineskip \z@
  \baselineskip \z@
%    \end{macrocode}
%    Don't use \cs{m@th} here as that signals to the math tagging
%    code that this is fake math that should not be tagged.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
  \mathsurround \z@
%    \end{macrocode}
%    Beside, we have to assign a special meaning (which we still have
%    to specify) to the line separator =\\=. We also have to
%    redefine the command =\par= in such a way that empty lines in
%    =\halign= cannot do any damage. We succeed in doing  so
%    by choosing something that will disappear when expanding.
%    After that we only have to call up =\@preamble= to
%    start the wanted =\halign=.
%    \changes{v2.3b}{1994/12/08}{add \cs{tabularnewline}}
%    \changes{v2.6m}{2025/09/24}{Check \cs{currentgrouptype} in \cs{par} (gh1864)}
%    \changes{v2.6n}{2025/09/25}{double @ for l3doc conventions (gh1864)}
%    \begin{macrocode}
  \let\\\@arraycr \let\tabularnewline\\%
  \def\par{\ifnum\currentgrouptype=6~ \else\@@@@par\fi}%
%    \end{macrocode}
%    Another socket for tagging. TODO: what about \cs{arrayleft} above?
%    \begin{macrocode}
  \UseTaggingSocket{tbl/init}
%    \end{macrocode}
%
%    \begin{macrocode}
%\show  \@preamble
  \@preamble
}
%    \end{macrocode}
% \end{macro}
%
%
%
%
%
% \begin{macro}[int]{\ar@ialign}
%    A new command that replaces \cs{ialign} used previously.  \cs{everycr} is
%    also applied to the \cs{cr} ending the preamble so we have to
%    program around that.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
\def\ar@ialign{%
%    \end{macrocode}
%    
%    \begin{macrocode}
  \everycr{%
    \noalign{%
%    \end{macrocode}
%    If this \cs{cr} was at the end of a real row (e.g., not at the
%    end of the table preamble) we run the end-row hook row and also
%    have add the socket that adds the end-row tag.
%  \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
      \tbl_if_row_was_started:T  {
%    \end{macrocode}
%    
%    \begin{macrocode}
        \@@_hook_use_protected:nV {tbl/row/end} \g_@@_row_int
        \UseTaggingSocket{tbl/row/end}
      }
%    \end{macrocode}
%    Then we prepare for the next row.
%    \begin{macrocode}
      \tbl_update_cell_data_for_next_row:
%    \end{macrocode}
%    We are now ready to start the next row and or to process any
%    material between rows, such as \cs{hline}. This is the right
%    place to initialize anything extra for the upcoming row, i.e.,
%    the place for the \texttt{tbl/row/init}.
%      
%    For a number of reasons we have to do this inside a \cs{noalign}
%    so declarations put into this hook have to be global to take any
%    effect.
%      
%    But we also want to be able to call \cs{hline} or \cs{rowcolor}
%    and similar commands from this hook. Such commands come with
%    their own \cs{noalign} and it is not allowed to nest them. We
%    therefore temporarily change \cs{noalign} into a no-op.
%      
%    At this point, \LaTeX{} does not know if there is any real row
%    following or if the next thing is \verb=\end{tabular}=. This can
%    only be figured out in a two pass system, where the last row
%    number of each tabular is recorded and passed to the next run via
%    the \texttt{.aux} file. If hook code needs this information it has
%    implement this as part of the hook code. Should it turn out that
%    is needed more often we may provided it in the end directly.
%      
% \changes{v2.6m}{2026-01-15}{Add support hook}
%    \begin{macrocode}
      \cs_set_eq:NN \noalign \prg_do_nothing:
      \@@_hook_use_protected:nV {tbl/row/init} \g_@@_row_int
    }%
  }%
  \tabskip\z@skip\halign}
%    \end{macrocode}
% \end{macro}
%
%
%
%
% \begin{macro}[no-user-doc]{\arraybackslash}
% \changes{v2.4a}{2003/12/17}{(DPC) Macro added (from tabularx)}
% Restore =\\= for use in array and tabular environment (after
% =\raggedright= etc.).
%    \begin{macrocode}
\def\arraybackslash{\let\\\tabularnewline}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\extrarowheight}
%    The \textsf{dimen} parameter used above also needs to be
%    allocated.  As a default value we use \textsf{0pt}, to ensure
%    compatibility with standard \LaTeX.
%    \begin{macrocode}
\newdimen \extrarowheight
\extrarowheight=0pt
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@arstrut}
%    Now the insertion of =\@arstrutbox= through =\@arstut=
%    is easy since we know exactly in which mode \TeX\ is while working
%    on the =\halign= preamble.
%    \begin{macrocode}
\def\@arstrut{\unhcopy\@arstrutbox}
%    \end{macrocode}
% \end{macro}
%
%
% \section{The line separator \texttt{\textbackslash\textbackslash}}
%
% \begin{macro}[int]{\@arraycr}
%    In the macro =\@array= the line separator =\\= is
%    =\let= to the command =\@arraycr=.
% \changes{v2.3c}{1995/04/23}{Avoid adding an ord atom in math}
% \changes{v2.5e}{2021/04/20}{Use \cs{protected} for \cs{\textbackslash} variant (gh/548)}
%    \begin{macrocode}
\protected\def\@arraycr {
%    \end{macrocode}
%    Add code that figures out if the current table row is incomplete
%    (not enough \verb=&=s). It can then do extra actions, such as
%    inserting missing cell tags.
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    \begin{macrocode}
  \tbl_count_missing_cells:n {@arraycr}
%    \end{macrocode}
%    TODO: maybe this is also the right place to add a socket that
%    could be used to actually enter missing cells instead of just
%    adding tagging structures for them later. This would be optional
%    but in many cases it would be the right thing to do (for example
%    if tables contain vertical lines or similar visual structures
%    that require fully specified rows.
%  
%    
%    We then start a special brace which I have directly
%    copied from the original definition. It is
%    necessary, because the =\futurelet= in =\@ifnextchar=
%    might
%    expand a  following =&= \textsf{token} in a construction like
%    =\\ &=. This would otherwise end the alignment template at a
%    wrong time. On the other hand we have to be careful to avoid
%    producing a real group, i.e.\ ={}=, because the command will also
%    be used for the array environment, i.e.\ in math mode. In that
%    case an extra ={}= would produce an ord atom which could mess up
%    the spacing. For this reason we use a combination that does not
%    really produce a group at all but modifies the master counter so
%    that a =&= will not be considered belonging to the current
%    =\halign= while we are looking for a =*= or =[=.
%    For further information see
%    \cite[Appendix~D]{bk:knuth}.
%    \begin{macrocode}
  \iffalse{\fi\ifnum 0=`}\fi
%    \end{macrocode}
%    Then we test whether the user is using the star form and ignore
%    a possible star (I also disagree with this procedure, because a
%    star does not make any sense here).
%    \begin{macrocode}
  \@ifstar \@xarraycr \@xarraycr}
%    \end{macrocode}
% \end{macro}





%
% \begin{macro}[int]{\@xarraycr}
%    In the command =\@xarraycr= we test if an optional argument
%    exists.
%    \begin{macrocode}
\def\@xarraycr{\@ifnextchar [%
%    \end{macrocode}
%    If it does, we branch out into the macro =\@argarraycr= if
%    not we close the special brace (mentioned above) and end the row
%    of the =\halign= with a =\cr=.
% \changes{v2.3c}{1995/04/23}{Avoid adding an ord atom in math}
%    \begin{macrocode}
  \@argarraycr {\ifnum 0=`{}\fi\cr}}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[int]{\@argarraycr}
%    If additional space is requested by the user this case is treated
%    in the macro =\@argarraycr=. First we close the special brace
%    and then we test if the additional space is positive.
% \changes{v2.3c}{1995/04/23}{Avoid adding an ord atom in math}
%    \begin{macrocode}
\def\@argarraycr[#1]{\ifnum0=`{}\fi\ifdim #1>\z@
%    \end{macrocode}
%    If this is the case we create an invisible vertical rule with
%    depth =\dp\@arstutbox=${}+{}$\meta{wanted\ space}.
%    Thus we achieve that all vertical lines specified
%    in the user preamble by a \texttt{|} are now
%    generally drawn.
%    Then the row ends with a =\cr=.
%
%    If the space is negative we end the row at once with a =\cr=
%    and move back up with a =\vskip=.
%
%    While testing these macros I found out that the
%    =\endtemplate=
%    created by =\cr= and =&= is something like an
%    =\outer= primitive and therefore it should not appear in
%    incomplete =\if= statements. Thus the following solution was
%    chosen which hides the =\cr= in other macros when \TeX\
%    is skipping conditional text.
% \changes{v2.3c}{1995/04/23}{Use \cs{expandafter}'s in conditional}
%    \begin{macrocode}
  \expandafter\@xargarraycr\else
  \expandafter\@yargarraycr\fi{#1}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@xargarraycr}
% \begin{macro}[int]{\@yargarraycr}
%    The following macros were already explained above.
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
%    \begin{macrocode}
\def\@xargarraycr#1{\unskip\gdef\do@row@strut
 {\@tempdima #1\advance\@tempdima \dp\@arstrutbox
   \vrule \@depth\@tempdima \@width\z@\global\let\do@row@strut\relax}%
%    \end{macrocode}
%    If the last column is a =\multicolumn= cell then we need to
%    insert the row strut now as it isn't inside the template (as that
%    got =\omit=ted).
% \changes{v2.4h}{2018/04/30}{Fixing issue 42}
%    \begin{macrocode}
   \ifnum\@multicnt >\z@ \do@row@strut \fi
   \cr}
\let\do@row@strut\relax
%    \end{macrocode}
%
%    \cs{@yargarraycr} is the same as in the \LaTeX{} kernel
%    (depending on the date of the kernel with one of the two
%    definitions below). We therefore do not define it again.
% \changes{v2.5b}{2020/04/22}{Don't define \cs{@yargarraycr} unnecessarily}
%    \begin{macrocode}
%\def\@yargarraycr#1{\cr\noalign{\@vspace@calcify{#1}}} % 2020-10-01
%\def\@yargarraycr#1{\cr\noalign{\vskip #1}}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
%
% \section{Spanning several columns}
%
% \begin{macro}[no-user-doc]{\multicolumn}
%    If several columns should be held together with a special format
%    the command =\multicolumn= must be used. It has three
%    arguments: the number of columns to be covered; the format for
%    the result column and the actual column entry.
% \changes{v2.3j}{1998/01/29}{Command made \cs{long} to match
%      kernel change for pr/2180}
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
\long\def\multicolumn#1#2#3{%
%    \end{macrocode}
%    First we combine the given number of columns into a single one;
%    then we start a new block so that the following definition is kept
%    local.
%    \begin{macrocode}
   \multispan{#1}\begingroup
%    \end{macrocode}
%    For tagging support we have to solve two problems:
%    \cs{multicolumn} must handle the row begin if it is used there,
%    and it must save the numbers of cells it spans so that we can add
%    a suitable ColSpan attribute. We do this in the next macro
%    (which in turn calls the \texttt{tbl/row/begin} socket, if
%    necessary).
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    \begin{macrocode}
   \tbl_update_multicolumn_cell_data:n {#1}    
%    \end{macrocode}
%    Since a =\multicolumn= should only describe the format of a
%    result column, we redefine =\@addamp= in such a way that one gets
%    an error message if one uses more than one \texttt{c},
%    \texttt{l}, \texttt{r}, \texttt{p}, \texttt{m} or \texttt{b} in
%    the second argument. One should consider that this definition is
%    local to the build-up of the preamble; an \textsf{array}-- or
%    \textsf{tabular}--environment in the third argument of the
%    =\multicolumn= is therefore worked through correctly as well.
%    \begin{macrocode}
   \def\@addamp{\if@firstamp \@firstampfalse \else
                \@preamerr 5\fi}%
%    \end{macrocode}
%    Then we evaluate the second argument with the help of
%    =\@mkpream=.
%    Now we still have to insert the contents of the \textsf{token}
%    register into the =\@preamble=, i.e.\ we have to say
%    =\xdef\@preamble{\@preamble}=. This is achieved shorter by
%    writing:
%    \begin{macrocode}
   \@mkpream{#2}\@addtopreamble\@empty
%    \end{macrocode}
%    After the =\@preamble= is created we forget all local
%    definitions and occupations of the \textsf{token} registers.
%    \begin{macrocode}
   \endgroup
%    \end{macrocode}
%    Now we update the colspan attribute.  This needs setting after
%    the group as it is hidden inside the plug in \cs{insert@column}.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
   \UseTaggingSocket{tbl/colspan}{#1}%
%    \end{macrocode}
%    In the special situation of  =\multicolumn= =\@preamble=
%    is not needed as preamble for a =\halign= but it is directly
%    inserted into our table. Thus instead of =\sharp=
%    there has to be the column entry (=#3=) wanted by the user.
%    \begin{macrocode}
   \def\@sharp{#3}%
%    \end{macrocode}
%    Now we can pass the =\@preamble= to \TeX\ . For safety
%    we start with an =\@arstrut=. This should usually be in the
%    template for the first column however we do not know if this
%    template was overwritten by our =\multicolumn=.
%    We also add a =\null= at the right end to prevent any following
%    =\unskip= (for example from =\\[..]=) to remove the =\tabcolsep=.
% \changes{v2.2e}{1994/06/01}{Added \cs{null}}
%    \begin{macrocode}
   \@arstrut \@preamble
   \null
   \ignorespaces}
%    \end{macrocode}
% \end{macro}
%
%
%
%   \section{The Environment Definitions}
%
% After these preparations we are able to define the environments. They
% only differ in the initializations of =\d@llar...=, =\col@sep=
%  and =\@halignto=.
%
% \begin{macro}[int]{\@halignto}
% \begin{macro}[no-user-doc]{\d@llarbegin}
% \begin{macro}[no-user-doc]{\d@llarend}
%    =\d@llar= has to be
%    locally assigned since otherwise nested \textsf{tabular} and \textsf{array}
%    environments (via =\multicolumn=) are impossible.
%    For 25 years or so =\@halignto= was set globally (to save space on the
%    save stack, but that was a mistake: if there is a tabular in the
%    output routine (e.g., in the running header) then that tabular is
%    able overwrite the =\@halignto=
%    setting of a tabular in the main text resulting in a very weird error.
% \changes{v2.4d}{2016/10/06}{\cs{@halignto} set locally (pr/4488)}
% \changes{v2.0g}{1992/06/18}{`d@llarbegin defined on top-level.}
%    When the new font selection scheme is in force we have to
%    we surround all =\halign= entries
%    with braces. See remarks in TUGboat 10\#2. Actually we are going
%    to use =\begingroup= and =\endgroup=. However, this is only
%    necessary when we are in text mode. In math the surrounding
%    dollar signs will already serve as the necessary extra grouping
%    level. Therefore we switch the settings of =\d@llarbegin= and
%    =\d@llarend= between groups and dollar signs.
%    \begin{macrocode}
\let\d@llarbegin\begingroup
\let\d@llarend\endgroup
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% \begin{macro}[no-user-doc]{\array}
%    Our new definition of =\array= then reads:
% \changes{v2.0d}{1990/08/20}{`d@llar local to preamble.}
% \changes{v2.4d}{2016/10/06}{\cs{@halignto} set locally (pr/4488)}
% In math mode we additionally add also the tagging sockets for luamml.
% \changes{v2.6h}{2024/12/02}{Add tagging sockets for luamml support}.
% \changes{v2.7b}{2026/02/24}{Move \cs{m@th} from \cs{@tabarray}}
% The =math/luamml/array/finalizecol= takes as argument a number (0, 1, 2)
% that should represent the alignment (c,l,r).   
% TODO: instead of =\the\@chnum=  a proper tl should be used.
% TODO: the w and W columntypes must be redefined - currently they
% insert stray mtext columns.
%    \begin{macrocode}
\def\array{\col@sep\arraycolsep
  \def\d@llarbegin{$\tag_socket_use:n { math/luamml/array/initcol }}
  \def\d@llarend
   {
     \tag_socket_use:nn { math/luamml/save/nn }{{}{mtd}}
     $
     \tag_socket_use:nn { math/luamml/array/finalizecol}{\the\@chnum}
   }
  \def\@halignto{}%
%    \end{macrocode}
%    Since there might be an optional argument we call another
%    macro which is also used by the other environments.
%    \begin{macrocode}
  \m@th\@tabarray}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@tabarray}
%    \emph{The code below has been replaced long time ago by an
%    extended version further down but the code and its documentation
%    was left here for reference. It is now commented out to avoid
%    confusion.}
%
%    This macro tests for a optional bracket and then calls up
%    =\@array= or =\@array[c]= (as default).
%    \begin{macrocode}
%\def\@tabarray{\@ifnextchar[{\@array}{\@array[c]}}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[no-user-doc]{\tabular}
% \begin{macro}[no-user-doc]{\tabular*}
%    The environments \textsf{tabular} and \textsf{tabular$*$} differ
%    only in the initialization of the command =\@halignto=. Therefore
%    we define
% \changes{v2.4d}{2016/10/06}{\cs{@halignto} set locally (pr/4488)}
%    \begin{macrocode}
\def\tabular{\def\@halignto{}\@tabular}
%    \end{macrocode}
%     and analogously for the star form. We evaluate the argument first
%     using =\setlength= so that users of the \texttt{calc} package can
%     write code like\\ =\begin{tabular*}{(\columnwidth-1cm)/2}...=
% \changes{v2.3l}{1998/05/13}{Use \cs{setlength} evaluate arg
%      so that the calc package can be applied here (pr/2793)}
% \changes{v2.4d}{2016/10/06}{\cs{@halignto} set locally (pr/4488)}
%     \begin{macrocode}
\expandafter\def\csname tabular*\endcsname#1{%
       \setlength\dimen@{#1}%
       \edef\@halignto{to\the\dimen@}\@tabular}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[int]{\@tabular}
%    The rest of the job is carried out by the =\@tabular= macro:
%    \begin{macrocode}
\providecommand\@kernel@tabular@init{}
\def\@tabular{%
%    \end{macrocode}
%    First of all we have to make sure that we start out in
%    \textsf{hmode}.  Otherwise we might find our table dangling by
%    itself on a line.
%    \begin{macrocode}
  \leavevmode
%    \end{macrocode}
%    Now that we know we are in hmode we can add the start tag for the
%    whole table.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
  \UseTaggingSocket{tbl/hmode/begin}%
%    \end{macrocode}
%    It should be taken into consideration that the macro =\@array=
%    must be called in math mode. Therefore we open a \textsf{box},
%    then assign the correct values to =\col@sep= and
%    =\d@llar...=.
% \changes{v2.0d}{1990/08/20}{`d@llar local to preamble.}
% \changes{v2.6h}{2025/02/18}{Surround dollar with \cs{MathCollectTrue}/\cs{MathCollectFalse}}%    
% \changes{v2.6l}{2025/09/09}{Use \cs{UseMathForPositioningText} instead (tagging/973+983)}%
% \changes{v2.7b}{2026/02/24}{Remove math mode switch}
%    \begin{macrocode}
  \hbox \bgroup 
    \col@sep\tabcolsep
    \let\d@llarbegin\begingroup
    \let\d@llarend\endgroup
%    \end{macrocode}
% \changes{v2.6h}{2025/02/18}{Add internal kernel hook to switch math collection method}%    
%    \begin{macrocode}
   \@kernel@tabular@init  
%    \end{macrocode}
%    Now everything \textsf{tabular} specific is done and we are able to
%    call the =\@tabarray= macro.
%    \begin{macrocode}
  \@tabarray}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\endarray}
%
%    When the processing of \textsf{array} is finished we have to
%    close the =\halign=
%    and afterwards the surrounding \textsf{box} selected by
%    =\@array=. To save \textsf{token} space we then redefine
%    =\@preamble=
%    because its replacement text isn't longer needed.
%
%    To handle cell indexes, we do not use \cs{crcr} but a variant
%    that also handles missing cells as necessary.
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
% \changes{v2.6h}{2024/12/02}{Add tagging sockets for luamml support}% 
%    \begin{macrocode}
\def\endarray {
  \tbl_crcr:n{endarray} 
  \tag_socket_use_expandable:n { math/luamml/array/save }
  \egroup
  \tag_socket_use:n { tbl/finalize }
%    \end{macrocode}
%    
%    If tables are nested into another then  it is necessary to
%    restore information about the cell the inner table started
%    in. Otherwise, the cell index data structures reflect the
%    status in the outer table as they
%    are globally manipulated. We restore in all cases even if we are
%    not in a nesting situation as that makes the code simpler (and
%    probably faster).
%
%    \cs{endtabular} and \cs{endtabular*} inherit from \cs{endarray}
%    so we only need to change that. \texttt{tabularx} uses a similar method.
% \changes{v2.6a}{2023/12/11}{Managing cell indexes}
%    \begin{macrocode}
  \tbl_restore_outer_cell_data:
%    \end{macrocode}
%    
%    \begin{macrocode}
  \egroup
  \tag_socket_use:n { math/luamml/array/finalize }
  \@arrayright \gdef\@preamble{}%
}
%    \end{macrocode}
% \end{macro}
%
%
%
%
% \begin{macro}[no-user-doc]{\endtabular}
% \begin{macro}[no-user-doc]{\endtabular*}
%    To end a \textsf{tabular} or \textsf{tabular$*$} environment we
%    call up =\endarray=, then the surrounding
%    =\hbox=.
% \changes{v2.5f}{2021/07/12}{Cancel any outside \cs{mathsurround} (gh/614)}
% \changes{v2.7b}{2026/02/24}{Remove math mode switch}
%    \begin{macrocode}
\def\endtabular{\endarray\egroup
%    \end{macrocode}
%    
% \changes{v2.6a}{2023/12/11}{Support for tagged PDF}
%    \begin{macrocode}
  \UseTaggingSocket{tbl/hmode/end}%
}
\expandafter\let\csname endtabular*\endcsname=\endtabular
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%
%
%   \section{Last minute definitions}
%
%
% If this file is used as a package file we should =\let= all macros
% to =\relax= that were used in the original but are no longer
%  necessary.
%    \begin{macrocode}
\let\@ampacol=\relax        \let\@expast=\relax
\let\@arrayclassiv=\relax   \let\@arrayclassz=\relax
\let\@tabclassiv=\relax     \let\@tabclassz=\relax
\let\@arrayacol=\relax      \let\@tabacol=\relax
\let\@tabularcr=\relax      \let\@@@@endpbox=\relax
\let\@argtabularcr=\relax   \let\@xtabularcr=\relax
%    \end{macrocode}
%
% \begin{macro}[int]{\@preamerr}
%   \changes{v2.6d}{2024/06/14}{Keep message sources out of L3 code (gh/1378)}
%    We also have to redefine the error routine =\@preamerr= since
%    new kind of errors are possible.
%    The code for this macro is not perfect yet;
%    it still needs too much memory.
%    \begin{macrocode}
\ExplSyntaxOff
\def\@preamerr#1{\def\@tempd{{..} at wrong position: }%
   \PackageError{array}{%
   \ifcase #1 Illegal pream-token (\@nextchar): `c' used\or %0
    Missing arg: token ignored\or                           %1
    Empty preamble: `l' used\or                             %2
    >\@tempd token ignored\or                               %3
    <\@tempd changed to !{..}\or                            %4
    Only one column-spec. allowed.\fi}\@ehc}                %5
%    \end{macrocode}
% \end{macro}
%
%
%
% \section
%   [Defining your own column specifiers]
%   {Defining your own column specifiers\footnotemark}
%
% \footnotetext{The code and the documentation in this section was
%   written by David. So far only the code from newarray was plugged
%   into array so that some parts of the documentation still claim
%   that this is newarray and even worse, some parts of the code are
%   unnecessarily doubled. This will go away in a future release. For
%   the moment we thought it would be more important to bring both
%   packages together.}
% \changes{v2.1a}{1992/07/03}{Newcolumn stuff added}
%
% \DeleteShortVerb{\=}
% \MakeShortVerb{\"}
%
%  \begin{macro}[no-user-doc]{\newcolumn}
%    In \texttt{newarray.sty} the macro for specifying new columns was
%    named "\newcolumn". When the functionality was added to
%    \texttt{array.sty} the command was renamed "\newcolumntype".
%    Initially both names were supported, but now (In versions of this
%    package distributed for \LaTeXe) the old name is not defined.
% \changes{v2.2a}{1994/02/03}{Now made `newcolumn an error}
% \changes{v2.2a}{1994/02/04}{Removed `newcolumn}
%    \begin{macrocode}
%<*ncols>
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[no-user-doc]{\newcolumntype}
% \changes{v2.1b}{1992/06/07}{Macro renamed from `newcolumn} As
% described above, the "\newcolumntype" macro gives users the chance
% to define letters, to be used in the same way as the primitive
% column specifiers, `c' `p' etc.
%    \begin{macrocode}
\def\newcolumntype#1{%
%    \end{macrocode}
% "\NC@char" was added in V2.01 so that active characters, like "@" in
% AMS\LaTeX\ may be used. This trick was stolen from \texttt{array.sty}
% 2.0h. Note that we need to use the possibly active token,
% "#1", in several places, as that is the token that actually
% appears in the preamble argument.
%    \begin{macrocode}
  \edef\NC@char{\string#1}%
%    \end{macrocode}
% First we check whether there is already a definition for this column.
% Unlike "\newcommand" we give a warning rather than an error if it is
% defined. If it is a new column, add "\NC@do" \meta{column} to
% the list "\NC@list".
%    \begin{macrocode}
  \@ifundefined{NC@find@\NC@char}%
    {\@tfor\next:=<>clrmbp@!|\do
      {%
%    \end{macrocode}
%    We use \cs{noexpand} on the tokens from the list in case one or
%    the other (typically \texttt{@}, \texttt{!} or \texttt{|}) has
%    been made active.
% \changes{v2.4l}{2019/08/31}{Add a necessary \cs{expandafter} (github/148)}
%    \begin{macrocode}
        \if\expandafter\noexpand\next\NC@char
        \PackageWarning{array}%
                       {Redefining primitive column \NC@char}\fi}%
     \NC@list\expandafter{\the\NC@list\NC@do#1}}%
    {\PackageWarning{array}{Column \NC@char\space is already defined}}%
%    \end{macrocode}
% Now we define a macro with an argument delimited by the new column
% specifier, this is used to find occurrences of this specifier in the
% user preamble.
%    \begin{macrocode}
  \@namedef{NC@find@\NC@char}##1#1{\NC@{##1}}%
%    \end{macrocode}
% If an optional argument was not given, give a default argument of 0.
%    \begin{macrocode}
  \@ifnextchar[{\newcol@{\NC@char}}{\newcol@{\NC@char}[0]}}
\ExplSyntaxOn
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\newcol@}
% We can now define the macro which does the rewriting,
% "\@reargdef" takes the same arguments as "\newcommand", but
% does not check that the command is new. For a column, say `D' with
% one argument, define a command "\NC@rewrite@D" with one
% argument, which recursively calls "\NC@find" on the user preamble
% after replacing the first token or group with the replacement text
% specified in the "\newcolumntype" command. "\NC@find" will find the
% next occurrence of `D' as it will be "\let" equal to
% "\NC@find@D" by "\NC@do".
%    \begin{macrocode}
\def\newcol@#1[#2]#3{\expandafter\@reargdef
     \csname NC@rewrite@#1\endcsname[#2]{\NC@find#3}}
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\NC@}
% Having found an occurrence of the new column, save the preamble
% before the column in "\@temptokena", then check to see if we
% are at the end of the preamble. (A dummy occurrence of the column
% specifier will be placed at the end of the preamble by "\NC@do".
%    \begin{macrocode}
\def\NC@#1{%
  \@temptokena\expandafter{\the\@temptokena#1}\futurelet\next\NC@ifend}
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\NC@ifend}
% We can tell that we are at the end as "\NC@do" will place a "\relax"
% after the dummy column.
%    \begin{macrocode}
\def\NC@ifend{%
%    \end{macrocode}
% If we are at the end, do nothing. (The whole preamble will now be in
% "\@temptokena".)
%    \begin{macrocode}
  \ifx\next\relax
%    \end{macrocode}
% Otherwise set the flag "\if@tempswa", and rewrite the column.
% "\expandafter" introduced 1n V2.01
%    \begin{macrocode}
    \else\@tempswatrue\expandafter\NC@rewrite\fi}
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\NC@do}
% If the user has specified `C' and `L' as new columns, the list of
% rewrites (in the token register "\NC@list") will look like
% "\NC@do *" "\NC@do C" "\NC@do L".
% So we need to define "\NC@do" as a one argument macro which
% initializes the rewriting of the specified column. Let us assume that
% `C' is the argument.
%    \begin{macrocode}
\def\NC@do#1{%
%    \end{macrocode}
% First we let "\NC@rewrite" and "\NC@find" be
% "\NC@rewrite@C" and "\NC@find@C" respectively.
%    \begin{macrocode}
  \expandafter\let\expandafter\NC@rewrite
    \csname NC@rewrite@\string#1\endcsname
  \expandafter\let\expandafter\NC@find
    \csname NC@find@\string#1\endcsname
%    \end{macrocode}
% Clear the token register "\@temptokena" after putting the present
% contents of the register in front of the token "\NC@find". At the
% end we place the tokens `"C\relax"' which "\NC@ifend" will use
% to detect the end of the user preamble.
%    \begin{macrocode}
  \expandafter\@temptokena\expandafter{\expandafter}%
        \expandafter\NC@find\the\@temptokena#1\relax}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\showcols}
% This macro is useful for debugging "\newcolumntype" specifications,
% it is the equivalent of the primitive "\show" command for macro
% definitions.  All we need to do is locally redefine "\NC@do" to take
% its argument (say `C') and then "\show" the (slightly modified)
% definition of "\NC@rewrite@C". Actually as the list always
% starts off with "\NC@do *" and we do not want to print the
% definition of the $*$-form, define "\NC@do" to throw away the first
% item in the list, and then redefine itself to print the rest of the
% definitions.
%    \begin{macrocode}
\def\showcols{{\def\NC@do##1{\let\NC@do\NC@show}\the\NC@list}}
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\NC@show}
% If the column `C' is defined as above, then
% "\show\NC@rewrite@C" would output\\
% "\long macro: ->\NC@find >{$}c<{$}".
% We want to strip the "long macro: ->" and the "\NC@find". So first we
% use "\meaning" and then apply the macro "\NC@strip" to the tokens so
% produced and then "\typeout" the required string.
%    \begin{macrocode}
\def\NC@show#1{%
  \typeout{Column~ #1\expandafter\expandafter\expandafter\NC@strip
  \expandafter\meaning\csname NC@rewrite@#1\endcsname\@@@@}}
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\NC@strip}
% Delimit the arguments to "\NC@strip" with `\texttt{:}', `\texttt{->}',
% a space, and "\@@@@" to pull out the required parts of the output from
% "\meaning".
%    \begin{macrocode}
\ExplSyntaxOff
\def\NC@strip#1:#2->#3 #4\@@@@{#2 -> #4}
\ExplSyntaxOn
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\NC@list}
% Allocate the token register used for the rewrite list.
%    \begin{macrocode}
\newtoks\NC@list
%    \end{macrocode}
% \end{macro}
%
% \subsection{The \texorpdfstring{$*$}{*}--form}
% We view the $*$-form as a slight generalization of the system
% described in the previous subsection. The idea is to define a $*$
% column by a command of the form:
% \begin{verbatim}
% \newcolumntype{*}[2]{%
%    \count@=#1\ifnum\count@>0
%       \advance\count@ by -1 #2*{\count@}{#2}\fi}
% \end{verbatim}
% \begin{macro}[int]{\NC@rewrite@*}\label{NC@rewrite@*}
% \changes{v2.4b}{2005/08/23}{Fix occasional spurious space (PR/3755)}
% This does not work however as "\newcolumntype" takes great care not
% to expand anything in the preamble, and so the "\if" is never
% expanded. "\newcolumntype" sets up various other parts of the
% rewrite correctly though so we can define:
%    \begin{macrocode}
\newcolumntype{*}[2]{}
%    \end{macrocode}
% Now we must correct the definition of "\NC@rewrite@*". The
% following is probably more efficient than a direct translation of
% the idea sketched above, we do not need to put a $*$ in the preamble
% and call the rewrite recursively, we can just put "#1" copies of
% "#2" into "\@temptokena". (Nested $*$ forms will be expanded
% when the whole rewrite list is expanded again, see "\@mkpream")
%    \begin{macrocode}
\long\@namedef{NC@rewrite@*}#1#2{%
%    \end{macrocode}
% Store the number.
%    \begin{macrocode}
  \count@#1\relax
%    \end{macrocode}
% Put "#1" copies of "#2" in the token register.
%    \begin{macrocode}
  \loop
  \ifnum\count@>\z@
  \advance\count@\m@ne
  \@temptokena\expandafter{\the\@temptokena#2}%
  \repeat
%    \end{macrocode}
% "\NC@do" will ensure that "\NC@find" is "\let" equal
% to "\NC@find@*".
%    \begin{macrocode}
  \NC@find}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Modifications to internal macros of \texttt{array.sty}}
%
% \begin{macro}[int]{\@xexpast}
% \begin{macro}[int]{\@xexnoop}
%    These macros are used to expand $*$-forms in
%    \texttt{array.sty}. "\let" them to "\relax" to save space.
%    \begin{macrocode}
\let\@xexpast\relax
\let\@xexnoop\relax
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[int]{\save@decl}
% We do not assume that the token register is free, we add the new
% declarations to the front of the register. This is to allow user
% preambles of the form, ">{foo}>{bar}..". Users are not encouraged to
% enter such expressions directly, but they may result from the
% rewriting of "\newcolumntype"'s.
%    \begin{macrocode}
\def\save@decl{\toks \count@ = \expandafter\expandafter\expandafter
                  {\expandafter\@nextchar\the\toks\count@}}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@mkpream}
%    The main modification to "\@mkpream" is to replace the call to
%    "\@xexpast" (which expanded $*$-forms) by a loop which expands
%    all "\newcolumntype" specifiers.
% \changes{v2.4e}{2016/10/07}{Fixing SX68732}
% \changes{v2.4f}{2017/11/04}{Managing m-cells without \cs{vcenter}}
%    \begin{macrocode}
\ExplSyntaxOff % really oldstyle using \@tfor :=
\def\@mkpream#1{\gdef\@preamble{}\@lastchclass 4 \@firstamptrue
   \let\@sharp\relax
%    \end{macrocode}
%
%    The "\@startpbox" (which is called for "p", "m" or "b" columns)
%    receives a user supplied argument: the width of the
%    paragraph-column. Normally that is something harmless like a length or a
%    simple length expression, but with the calc package involved it
%    could break under an "\edef" operation, which is how the preamble
%    is constructed. We now make use of "\unexpanded" here to prevent that. The
%    "\expandafter" gymnastics are necessary to expand the "#1" exactly
%    once (since it will get "\@nextchar" as its value and need its
%    content). However, once added to the preamble, no further
%    expansion should happen if the preamble is extended. Therefore we
%    change from \cs{@startpbox} to \cs{@startpbox@action}. The latter
%    then has a trivial definition and expands (including its
%    argument) to itself while the preamble is being built. Outside of this
%    preamble build up \cs{@startpbox@action} does the actual work.
%
% \changes{v2.4j}{2018/11/13}{Do not expand argument of
%    \cs{@startpbox} while building the tabular preamble (sx/459285)}
% \changes{v2.6h}{2024/12/17}{Expand argument of \cs{@startpbox}
%      exactly once while the preamble is being built, and use
%      \cs{@startpbox@action} later to do the real work (gh/1585)}
%    \begin{macrocode}
   \def\@startpbox##1{\unexpanded\expandafter{\expandafter
                      \@startpbox@action\expandafter{##1}}}%
   \def\@startpbox@action##1{\unexpanded{\@startpbox@action{##1}}}%
   \let\@endpbox\relax
   \let\do@row@strut\relax
   \let\ar@align@mcell\relax
%    \end{macrocode}
%    Now we remove possible  $*$-forms and user-defined column
%    specifiers in the user preamble by repeatedly executing the list
%    "\NC@list" until the re-writes have no more effect. The
%    expanded preamble will then be in the token register
%    "\@temptokena". Actually we need to know at this point that
%    this is not "\toks0".
%    \begin{macrocode}
   \@temptokena{#1}\@tempswatrue
   \@whilesw\if@tempswa\fi{\@tempswafalse\the\NC@list}%
%    \end{macrocode}
%    Afterwards we initialize all registers and macros, that we need
%    for the build-up of the preamble.
%    \begin{macrocode}
   \count@\m@ne
   \let\the@toks\relax
   \prepnext@tok
%    \end{macrocode}
% Having expanded all tokens defined using "\newcolumntype" (including
% "*"), we evaluate the remaining tokens, which are saved in
% "\@temptokena".  We use the \LaTeX--macro "\@tfor" to inspect each
% token in turn.
%    \begin{macrocode}
   \expandafter \@tfor \expandafter \@nextchar
    \expandafter :\expandafter =\the\@temptokena \do
%    \end{macrocode}
% "\@testpatch" does not take an argument since \texttt{array.sty} 2.0h.
%    \begin{macrocode}
   {\@testpach
%    \end{macrocode}
%    We provide the current cell type in \cs{ar@cell@type} to pass it
%    to the hooks.
%    \begin{macrocode}
    \edef\ar@cell@type{%
      \ifcase\@chnum c\or l\or r\or m\or p\or b\else ?\fi}%
%    \end{macrocode}
%    
%    \begin{macrocode}
   \ifcase \@chclass \@classz \or \@classi \or \@classii
     \or \save@decl \or \or \@classv \or \@classvi
     \or \@classvii \or \@classviii
%    \end{macrocode}
%    In \texttt{newarray.sty} class 9 is equivalent to class 10.
%    \begin{macrocode}
     \or \@classx
     \or \@classx \fi
   \@lastchclass\@chclass}%
   \ifcase\@lastchclass
   \@acol \or
   \or
   \@acol \or
   \@preamerr \thr@@@@ \or
   \@preamerr \tw@ \@addtopreamble\@sharp \or
   \or
   \else  \@preamerr \@ne \fi
   \def\the@toks{\the\toks}}
%    \end{macrocode}
% \end{macro}
%    
%    
%  \begin{macro}{\ar@cell@type}
%    We need a top-level definition for the current cell type, just in
%    case some package overwrites \cs{@mkpream} and the runtime
%    definition gets lost this way. We make it \texttt{?} which isn't
%    a real cell type.
%    \begin{macrocode}
\def\ar@cell@type{?}
%    \end{macrocode}
%  \end{macro}
%    
%    
%    
%    \begin{macrocode}
\ExplSyntaxOn
%    \end{macrocode}
%
% \begin{macro}[int]{\@classix}
%    \texttt{array.sty} does not allow repeated \texttt{>}
%    declarations for the same column. This is allowed in
%    \texttt{newarray.sty} as documented in the introduction. Removing
%    the test for this case makes class 9 equivalent to class 10, and
%    so this macro is redundant. It is "\let" to "\relax" to save
%    space.
%    \begin{macrocode}
\let\@classix\relax
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@classviii}
%    In \texttt{newarray.sty} explicitly allow class 2, as repeated
%    \texttt{<} expressions are accepted by this package.
%    \begin{macrocode}
\def\@classviii{\ifnum \@lastchclass >\z@\ifnum\@lastchclass=\tw@\else
      \@preamerr 4\@chclass 6 \@classvi \fi\fi}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@classv}
% Class 5 is \texttt{@}-expressions (and is also called by class 1)
% This macro was incorrect in Version~1. Now we do not expand the
% "@"-expression, but instead explicitly replace an
% "\extracolsep" command by an assignment to "\tabskip" by a
% method similar to the "\newcolumntype" system described above.
% "\d@llarbegin" "\d@llarend" were introduced in V2.01 to match
% \texttt{array.sty} 2.0h.
%    \begin{macrocode}
\def\@classv{\save@decl
   \expandafter\NC@ecs\@nextchar\extracolsep{}\extracolsep\@@@@@@
   \@addtopreamble{\d@llarbegin\the@toks\the\count@\relax\d@llarend}%
   \prepnext@tok}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\NC@ecs}
% Rewrite the first occurrence of "\extracolsep{1in}" to
% "\tabskip1in\relax". As a side effect discard any tokens after a
% second "\extracolsep", there is no point in the user entering two of
% these commands anyway, so this is not really a restriction.
%    \begin{macrocode}
\def\NC@ecs#1\extracolsep#2#3\extracolsep#4\@@@@@@{\def\@tempa{#2}%
  \ifx\@tempa\@empty\else\toks\count@={#1\tabskip#2\relax#3}\fi}
%</ncols>
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Support for the \texttt{delarray.sty}}
%
% The \texttt{delarray.sty} package  extends the array syntax by
% supporting the notation of delimiters. To this end we extend the
% array parsing mechanism to include a hook which can be used by this
% (or another) package to do some additional parsing.
%
% \begin{macro}[int]{\@tabarray}
%    This macro tests for an optional bracket and then calls up
%    "\@@@@array" or "\@@@@array[c]" (as default).
%    \begin{macrocode}
%<*package>
\def\@tabarray{\@ifnextchar[{\@@@@array}{\@@@@array[c]}}
%    \end{macrocode}
% \end{macro}
% \begin{macro}[int]{\@@@@array}
%    This macro tests could then test an optional delimiter before the
%    left brace of the main preamble argument. Here in the main package
%    it simply is let to be "\@array".
%    \begin{macrocode}
\let\@@@@array\@array
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@arrrayleft}
% \begin{macro}[int]{\@arrayright}
%    We have to declare the hook we put into "\@array" above.
%    A similar hook \cs{@arrayright} will be inserted into the
%    "\endarray" to gain control. Both defaults to empty.
%    \begin{macrocode}
\let\@arrayleft\@empty
\let\@arrayright\@empty
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Support for \texttt{\textbackslash firsthline} and
%             \texttt{\textbackslash lasthline}}
%
% The Companion~\cite[p.137]{bk:GMS94} suggests two additional
% commands to control the alignments in case of tabulars with
% horizontal lines. They are now added to this package.
%
%  \begin{macro}[no-user-doc]{\extratabsurround}
%    The extra space around a table when "\firsthline" or "\lasthline"
%    are used.
%    \begin{macrocode}
\newlength{\extratabsurround}
\setlength{\extratabsurround}{2pt}
%    \end{macrocode}
%  \end{macro}
%
% \begin{macro}[int]{\backup@length}
%    This register will be used internally by "\firsthline" and
%    "\lasthline".
%    \begin{macrocode}
\newlength{\backup@length}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\firsthline}
% \changes{v2.3h}{1996/05/25}{Complete reimplementation}
%    This code can probably be improved but for the moment it should
%    serve.
%
%    We start by producing a single tabular row without any visible
%    content that will produce the external reference point in case
%    "[t]" is used.  We need to suppress the \cs{tabcolsep} in the
%    \cs{multicolumn} in case there wasn't any in the real column.
%
% \changes{v2.5c}{2020/07/20}{Suppress all column space (gh/322)}
%    \begin{macrocode}
\newcommand{\firsthline}{%
  \multicolumn1{@{}c@{}}{%
%    \end{macrocode}
%    Within this row we calculate "\backup@length" to be the height
%    plus depth of a standard line. In addition we have to add the
%    width of the "\hline", something that was forgotten in the
%    original definition.
%    \begin{macrocode}
    \global\backup@length\ht\@arstrutbox
    \global\advance\backup@length\dp\@arstrutbox
    \global\advance\backup@length\arrayrulewidth
%    \end{macrocode}
%    Finally we do want to make the height of this first line be a bit
%    larger than usual, for this we place the standard array strut
%    into it but raised by "\extratabsurround"
%    \begin{macrocode}
     \raise\extratabsurround\copy\@arstrutbox
%    \end{macrocode}
%    And we should also cancel the guard otherwise we end up with two.
%    \begin{macrocode}
     \kern-1sp%
%    \end{macrocode}
%    Having done all this we end the line and back up by the value of
%    "\backup@length" and then finally place our "\hline". This should
%    place the line exactly at the right place but keep the reference
%    point of the whole tabular at the baseline of the first row.
%    \begin{macrocode}
    }\\[-\backup@length]\hline
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[no-user-doc]{\lasthline}
% \changes{v2.3h}{1996/05/25}{Complete reimplementation}
%    For "\lasthline" the situation is even worse and I got it
%    completely wrong initially.
%
%    The problem in this case is that if the optional argument "[b]"
%    is used we do want the reference point of the tabular be at the
%    baseline of the last row but at the same time do want the
%    depth of this last line increased by "\extratabsurround" without
%    changing the placement "\hline".
%
%    We start by placing the rule followed by an invisible row. We
%    need to suppress the \cs{tabcolsep} in the multicol in case there
%    wasn't any in the real column.
% \changes{v2.5c}{2020/07/20}{Suppress all column space (gh/322)}
%    \begin{macrocode}
\newcommand{\lasthline}{\hline\multicolumn1{@{}c@{}}{%
%    \end{macrocode}
%    We now calculate "\backup@length" to be the height and depth of
%    two lines plus the width of the rule.
%    \begin{macrocode}
    \global\backup@length2\ht\@arstrutbox
    \global\advance\backup@length2\dp\@arstrutbox
    \global\advance\backup@length\arrayrulewidth
%    \end{macrocode}
%    This will bring us back to the baseline of the second last row:
%    \begin{macrocode}
    }\\[-\backup@length]%
%    \end{macrocode}
%    Thus if we now add another invisible row the reference point of
%    that row will be at the baseline of the last row (and will be the
%    reference for the whole tabular). Since this row is invisible we
%    can enlarge its depth by the desired amount.
%    \begin{macrocode}
    \multicolumn1{@{}c@{}}{%
       \lower\extratabsurround\copy\@arstrutbox
       \kern-1sp%
       }%
}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Getting the spacing around rules right}
%
%    Beside a larger functionality \texttt{array.sty} has one
%    important difference to the standard \texttt{tabular} and
%    \texttt{array} environments: horizontal and vertical rules make a
%    table larger or wider, e.g., \verb=\doublerulesep= really denotes
%    the space between two rules and isn't measured from the middle of
%    the rules.
%
%  \begin{macro}[int]{\@xhline}
%    For vertical rules this is implemented by the definitions above,
%    for horizontal rules we have to take out the backspace.
% \changes{v2.3d}{1995/11/19}{fix space between double rules pr/1945}
%    \begin{macrocode}
\CheckCommand*\@xhline{\ifx\reserved@a\hline
               \vskip\doublerulesep
               \vskip-\arrayrulewidth
             \fi
      \ifnum0=`{\fi}}
\renewcommand*\@xhline{\ifx\reserved@a\hline
               \vskip\doublerulesep
             \fi
      \ifnum0=`{\fi}}
%</package>
%    \end{macrocode}
%  \end{macro}

% \subsection{Implementing column types \texttt{w} and \texttt{W}}
%
% In TugBoat 38/2 an extension was presented that implemented two
% additional column types \texttt{w} and \texttt{W}. These have now
% been added to the package itself.
%
%
%  \begin{macro}[int]{\ar@cellbox}
%    For \texttt{w} and \texttt{W} column types we need a box to
%    temporarily hold the cell content.
% \changes{v2.4f}{2017/11/07}{Macro added}
%    \begin{macrocode}
\newsavebox\ar@cellbox
%    \end{macrocode}
%  \end{macro}
%
%
%  \begin{macro}[no-user-doc]{\newcolumntype w}
%    The \texttt{w} column type has two arguments: the first holds the
%    alignment which is either "l", "c", or "r" and the second is the
%    nominal width of the column.
% \changes{v2.4f}{2017/11/07}{Column type added}
% \changes{v2.5a}{2020/04/06}{Use \cs{d@llarbegin} and \cs{d@llarend} so
%    that cell is typeset in math mode inside \texttt{array} (gh/297)}
%    \begin{macrocode}
\newcolumntype{w}[2]{%
%    \end{macrocode}
%    Before the cell content we start an "lrbox"-environment to
%    collect the cell material into the previously allocated box
%    "\ar@cellbox". We add \cs{d@llarbegin} (and later \cs{d@llarend})
%    so that the content is typeset in math mode if we are in an
%    \texttt{array} environment.
%    \begin{macrocode}
  >{\begin{lrbox}\ar@cellbox\d@llarbegin}%
%    \end{macrocode}
%    Then comes a specifier for the cell content.  We use "c", but
%    that doesn't matter as in the end we will always put a box of a
%    specific width ("#2") into the cells of that column, so "l" or
%    "r" would give the same result. There is only a difference if
%    there are also very wide "\multicolumn" rows overwriting the
%    setting  in which case "c" seems to be slightly better.
%    \begin{macrocode}
  c%
%    \end{macrocode}
%    At the end of the cell we end the "lrbox" environment so that all
%    of the cell content is now in box "\ar@cellbox".  As a final step we
%    put that box into a "\makebox" using the optional arguments of
%    that command to achieve the correct width and the desired
%    alignment within that width. We unbox the collected material so
%    that any stretchable glue inside can interact with the alignment.
% \changes{v2.4m}{2020/02/10}{Unbox collected material so that
%                             stretchable glue inside can act (gh/270)}
%    \begin{macrocode}
  <{\d@llarend \end{lrbox}%
    \makebox[#2][#1]{\unhbox\ar@cellbox}}}
%    \end{macrocode}
%  \end{macro}

%
%
%  \begin{macro}[no-user-doc]{\newcolumntype W}
%    The \texttt{W} is similar but in this case we want a warning if
%    the cell content is too wide.
% \changes{v2.4f}{2017/11/07}{Column type added}
% \changes{v2.5a}{2020/04/06}{Use \cs{d@llarbegin} and \cs{d@llarend} so
%    that cell is typeset in math mode inside \texttt{array} (gh/297)}
%    \begin{macrocode}
\newcolumntype{W}[2]
  {>{\begin{lrbox}\ar@cellbox\d@llarbegin}%
   c%
   <{\d@llarend\end{lrbox}%
   \let\hss\hfil
   \makebox[#2][#1]{\unhbox\ar@cellbox}}}
%    \end{macrocode}
%    This is a bit sneaky, as it temporarily disables "\hss", but
%    given that we know what goes into that box it should be
%    sufficient.
%  \end{macro}
%
%
% \subsection{Handling \cs{cline}}
%
% In the past \pkg{array} did not have to concern itself with
% \cs{cline} but simply used the definition already provided in the
% kernel. However, for tagged PDF output this definition is
% insufficient, because it causes incorrect row counting and the rules
% it generates would need to be marked as artifacts.
% We therefore update it  here.  
%
%
%  \begin{macro}[int]{\@cline}
%    Tagging support for \cs{cline}
%  \changes{v2.6e}{2024/07/13}{Support for tagging \cs{cline} (tagging/134)}
%    \begin{macrocode}
\ExplSyntaxOn
\def\@cline#1-#2\@nil{
  \omit
  \@multicnt#1
  \advance\@multispan\m@ne
  \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
  \@multicnt#2
  \advance\@multicnt-#1
  \advance\@multispan\@ne
%    \end{macrocode}
%    The rule needs artifact tagging in tagged PDF.
%    \begin{macrocode}
  \UseTaggingSocket{tbl/leaders/begin}
  \leaders\hrule\@height\arrayrulewidth\hfill
  \UseTaggingSocket{tbl/leaders/end}
%    \end{macrocode}
%    To the row counting the above appears like an extra row, so we
%    have to correct the count.
%    \begin{macrocode}
  \tbl_gdecr_row_count:
  \cr
  \noalign{\vskip-\arrayrulewidth}
}
\ExplSyntaxOff
%    \end{macrocode}
%  \end{macro}
%
%
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
%
%
% \PrintIndex
% \PrintChanges
%
% \Finale
%
\endinput
