% \subsection{Row separation (rowsep)}

% \subsubsection{Data presentation}

% A horizontal line between table rows can't appear in the output
% immediately. Its formatting should be postponed until the next
% row and its context are known. Two basic cases:
% \begin{itemize}
% \item The default cell formatting is to have a border around cells.
% For some cell \textit{A} in the middle of a table, the user has
% overridden formatting to no borders. To imlement the user's wish,
% we should not create the bottom border for the cell \textit{B}
% which is above the \textit{A}. But we don't know about the wish
% for \textit{A} while processing \textit{B}. Therefore, the border
% between two rows can be established only after processing the
% both rows.
% \item The default cell border is 1pt, but a border between
% a body and a header or a footer row is 2pt. It means that the
% border between two rows should be created only after we sure
% that there is no table break.
% \end{itemize}
% Our approach is to define the desired formatting of a rowsep
% as a set of parameters in a token list. Later we can join two
% rowseps or a rowsep and context to a final rowsep.
%
% A rowsep token list consist of several items, each
% item is a list of tokens or token groups:
% \begin{enumerate}
% \item length
% \item left-border
% \item right-border
% \item user-specified thickness
% \end{enumerate}
% Values \textit{left-border} and \textit{right-border} are
% required to get a nice rectangle border around a cell. Without length
% correction, using the cell dimension, the border could look like:
% \begin{verbatim}
%   xxxxxxxx
% xxx cell xxx
%   xxxxxxxx
% \end{verbatim}
% With length correction, we get the correct result:
% \begin{verbatim}
% xxxxxxxxxxxx
% xxx cell xxx
% xxxxxxxxxxxx
% \end{verbatim}
%
% Here is an example of a rowsep specification. It consist of three items.
% The first item: length is 5cm, width 2pt, borders are 2mm.
% The second item: length is 9cm, borders are 2mm, no rule at all.
% The third item: length 2cm, default width, borders are 2pt.
% The token list for this specification:
% \begin{verbatim}
% { {5cm} {2mm} {2mm} {2pt} }
% { {9cm} {2mm} {2mm} {0pt} }
% { {2cm} {2pt} {2pt} \relax }
% \end{verbatim}
%
% Cell length and the left and right borders should be the correct lengths,
% the rule thickness can be |\relax|, in this case the actual thickness
% will be calculated during output.
%
% \begin{macro}{\cals@rs@pack}
% Construct a rowsep fragment from the arguments 2-5 and
% put it to the macro 1.
%    \begin{macrocode}
\newcommand\cals@rs@pack[5]{%
\edef#1{\noexpand{#2\noexpand}\noexpand{#3\noexpand}\noexpand{#4\noexpand}%
 \ifx \relax#5\relax \else \noexpand{#5\noexpand}\fi }}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@unpack}
% The reverse for |\cals@rs@pack|. The first argument is a rowsep
% fragment (without enclosing curly braces), arguments 2-5 is macro
% names where to put the results.
%    \begin{macrocode}
\newcommand\cals@rs@unpack[5]{%
\def\cals@tmp##1##2##3##4{\edef#2{##1}\edef#3{##2}\edef#4{##3}%
 \ifx\relax##4\let#5=\relax \else \edef#5{##4}\fi}%
\expandafter\cals@tmp#1}
%    \end{macrocode}
% \end{macro}

%
% ^^A -----------------------------------------------------
%
% \subsubsection{From individual decorations to rowsep specification}

% The rowsep specifications are created cell-by-cell and stored
% in the macros |\cals@current@rs@above| and |\cals@current@rs@below|.
% The construction happens with delay because we don't know
% the exact value of the right border until the next cell is processed.

% \begin{macro}{\cals@rs@spec@begin}
% Initializes |\cals@current@rs@above|, |\cals@current@rs@below|
% and set the flag of a new row.
%    \begin{macrocode}
\newcommand\cals@rs@spec@begin{%
\def\cals@current@rs@above{}%
\def\cals@current@rs@below{}%
\let\cals@rs@spec@ll=\relax}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@spec@next}
% \begin{macro}{\cals@rs@spec@nextII}
% Finalizes the decorations for the previous cell by using the left border
% of the current as the right border for the previous. Then
% remembers the decorations of the current cell ---
% the left border width,
% the widths of the top and bottom borders (|\relax| is ok) ---
% in the macros |\cals@rs@spec@ll|, |...@bl|, |...@bt|, |...@bb|.
% All the arguments much be macros.
%    \begin{macrocode}
\newcommand\cals@rs@spec@next[4]{
\cals@rs@spec@nextII#2
\let\cals@rs@spec@ll=#1%
\let\cals@rs@spec@bl=#2%
\let\cals@rs@spec@bt=#3%
\let\cals@rs@spec@bb=#4%
}

\newcommand\cals@rs@spec@nextII[1]{%
\ifx \relax\cals@rs@spec@ll \else
 \cals@rs@pack\cals@tmp\cals@rs@spec@ll\cals@rs@spec@bl#1\cals@rs@spec@bt
 \llt@snoc\cals@current@rs@above\cals@tmp
 \cals@rs@pack\cals@tmp\cals@rs@spec@ll\cals@rs@spec@bl#1\cals@rs@spec@bb
 \llt@snoc\cals@current@rs@below\cals@tmp
\fi
}
%    \end{macrocode}
% \end{macro}
% \end{macro}

% \begin{macro}{\cals@rs@spec@end}
% Finishes the rowsep specification by putting the last cell to it.
% The only implicit argument (|\cals@lastLeftWidth|) is the width
% of the right border of the last cell.
%    \begin{macrocode}
\newcommand\cals@rs@spec@end[1]{}
\let\cals@rs@spec@end=\cals@rs@spec@nextII
%    \end{macrocode}
% \end{macro}

%
% ^^A -----------------------------------------------------
%
% \subsubsection{``Waiting'' rowsep}
%
% \begin{macro}{\cals@rs@sofar@length}
% \begin{macro}{\cals@rs@sofar@borderl}
% \begin{macro}{\cals@rs@sofar@borderr}
% \begin{macro}{\cals@rs@sofar@width}
% Typesetting a row separator is not an easy task, especially
% because we support border-widths. Indeed, consider the worst
% case: four cells and all the borders are different.
% Our solution is an optimizer for a good case. We do not typeset
% a fragment of the rule immediately. Instead, we remember the
% parameters. If the next fragment is of the same width,
% we increase the length of the ``waiting'' fragment.
% Otherwise, we output the waiting fragment and the new fragment
% becomes the new waiting fragment.
%    \begin{macrocode}
\newcommand\cals@rs@sofar@length{}
\newcommand\cals@rs@sofar@borderl{}
\newcommand\cals@rs@sofar@borderr{}
\newcommand\cals@rs@sofar@width{}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}

% \begin{macro}{\cals@rs@sofar@reset}
% Sets a flag that a new waiting rule should be started.
%    \begin{macrocode}
\newcommand\cals@rs@sofar@reset{\let\cals@rs@sofar@width=\relax}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@sofar@end}
% Prints the waiting rule, if exists.
%    \begin{macrocode}
\newcommand\cals@rs@sofar@end{\ifx\relax\cals@rs@sofar@width
  \else\cals@rs@sofar@out\fi}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@sofar@next}
% Enlarges the current waiting rule, or typesets it and
% starts new if the widths do not match. All the parameters
% must be macro names. In order: length, left border, right
% border, width.
%    \begin{macrocode}
\newcommand\cals@rs@sofar@next[4]{%
\ifx\relax\cals@rs@sofar@width
%    \end{macrocode}
% Starts a new waiting rule.
%    \begin{macrocode}
 \let\cals@rs@sofar@length=#1%
 \let\cals@rs@sofar@borderl=#2%
 \let\cals@rs@sofar@borderr=#3%
 \let\cals@rs@sofar@width=#4%
\else
 \ifdim \cals@rs@sofar@width=#4\relax
%    \end{macrocode}
% Enlarges the waiting rule.
%    \begin{macrocode}
  \dimen0=\cals@rs@sofar@length\relax
  \advance\dimen0 by #1\relax
  \edef\cals@rs@sofar@length{\the\dimen0}%
  \let\cals@rs@sofar@borderr=#3%
 \else
%    \end{macrocode}
% Typesets the current and start a new waiting rule.
%    \begin{macrocode}
  \cals@rs@sofar@out
  \let\cals@rs@sofar@length=#1%
  \let\cals@rs@sofar@borderl=#2%
  \let\cals@rs@sofar@borderr=#3%
  \let\cals@rs@sofar@width=#4%
 \fi
\fi}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@sofar@over}
% Repeats the last rowsep fragment, probably with another settings.
% Arguments are like in |\cals@rs@sofar@next|.
%    \begin{macrocode}
\newcommand\cals@rs@sofar@over[4]{%
\ifdim 0pt=#4
  \relax
\else
  \ifdim \cals@rs@sofar@width=#4\relax
%    \end{macrocode}
% The width is not changed. We probably need to enlarge the right border
% and probably the left border too. The latter is a bit harder because
% we don't want to change it if the line continues from another cell
% (so, change only if |length|+|borderl|\textgreater |sofar@length|+|sofar@borderl|).
%    \begin{macrocode}
    \ifdim #3>\cals@rs@sofar@borderr\relax
      \edef\cals@rs@sofar@borderr{#3}%
    \fi
    \dimen0=\cals@rs@sofar@length
    \advance\dimen0 by \cals@rs@sofar@borderl\relax
    \advance\dimen0 by -#2\relax
    \ifdim #1>\dimen0 \relax
      \edef\cals@rs@sofar@borderl{#2}%
    \fi
  \else
%    \end{macrocode}
% Typesets the current and start a new waiting rule.
%    \begin{macrocode}
    \cals@rs@sofar@out
    \hskip-#1\relax
    \let\cals@rs@sofar@length=#1%
    \let\cals@rs@sofar@borderl=#2%
    \let\cals@rs@sofar@borderr=#3%
    \let\cals@rs@sofar@width=#4%
  \fi
\fi}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@sofar@out}
% Typesets the waiting rule
%    \begin{macrocode}
\newcommand\cals@rs@sofar@out{%
\ifdim 0pt=\cals@rs@sofar@width\relax
  \hskip\cals@rs@sofar@length\relax
\else
  \cals@halfWidthToDimen0\cals@rs@sofar@borderl
  \hskip-\dimen0\relax
  \cals@halfWidthToDimen2\cals@rs@sofar@borderr
  \dimen4=\cals@rs@sofar@length\relax
  \advance\dimen4 by \dimen0\relax \advance\dimen4 by \dimen2\relax
  \cals@halfWidthToDimen6\cals@rs@sofar@width
  \vrule height\dimen6 depth\dimen6 width\dimen4\relax
  \hskip-\dimen2\relax
\fi}
%    \end{macrocode}
% \end{macro}

% ^^A -----------------------------------------------------
%
% \subsubsection{From rowsep specification to typesetting}

% \begin{macro}{\cals@rs@joinTwo}
% Join and typeset two rowseps (arguments 2 and 3, must be macro names).
% The number and the lengths of the fragments in the rowseps
% should match.
% The argument 1 (also a macro name) is the default width.
% Corrupts the macros 2 and 3.
% Call this macro inside |sofar@reset|...|@end| group.
%    \begin{macrocode}
\newcommand\cals@rs@joinTwo[3]{%
%    \end{macrocode}
% The loop function.
%    \begin{macrocode}
\def\next##1{%
\ifx \eol##1\let\next=\relax
\else
  \toks0=\expandafter{##1}%
  \edef\cals@tmpII{\the\toks0}%
  \llt@decons#3%
%    \end{macrocode}
% Now |\cals@tmpII| contains a current fragment of the first rowsep,
% and |\llt@car| of the second. Unpack the individual parameters.
%    \begin{macrocode}
  \cals@rs@unpack\cals@tmpII\cals@tmpLI \cals@tmpBlI \cals@tmpBrI \cals@tmpWI
  \cals@rs@unpack\llt@car   \cals@tmpLII\cals@tmpBlII\cals@tmpBrII\cals@tmpWII
%    \end{macrocode}
% The special case is when we should not typeset a rowsep fragment.
%    \begin{macrocode}
  \let\cals@tmp=\cals@iftrue
  \cals@maxWidth\cals@tmpWI\cals@tmpWII
  \ifx \relax\cals@width\else \ifdim \cals@width=0pt %
    \cals@rs@sofar@next\cals@tmpLI\cals@tmpBlI\cals@tmpBrI\cals@width
    \let\cals@tmp=\cals@iffalse
  \fi\fi
%    \end{macrocode}
% Not the special case. Put the both definitions, and let the
% underlying functions take care of calculations.
%    \begin{macrocode}
  \cals@tmp\ifvoid
    \cals@widthII#1\cals@tmpWI
    \cals@rs@sofar@next\cals@tmpLI\cals@tmpBlI\cals@tmpBrI\cals@width
    \cals@widthII#1\cals@tmpWII
    \cals@rs@sofar@over\cals@tmpLII\cals@tmpBlII\cals@tmpBrII\cals@width
  \fi
\fi
%    \end{macrocode}
% End of |\next| definition: continue the loop.
% End of |\cals@rs@joinTwo| definition: start the loop.
%    \begin{macrocode}
\next}%
\expandafter\next#2\eol}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@rs@joinOne}
% A simplified version of the previous macro. We have only one rowsep,
% and want to join and typeset it with regard to some width,
% given as the first macro parameter. Call this macro inside
% |sofar@reset|...|@end| group.
%    \begin{macrocode}
\newcommand\cals@rs@joinOne[2]{%
\def\next##1{\ifx\eol##1\let\next=\relax\else
 \toks0=\expandafter{##1}%
 \edef\cals@tmpII{\the\toks0}%
 \cals@rs@unpack\cals@tmpII\cals@tmpL\cals@tmpBl\cals@tmpBr\cals@tmpW
 \cals@widthII#1\cals@tmpW
 \cals@rs@sofar@next\cals@tmpL\cals@tmpBl\cals@tmpBr\cals@width
\fi\next}%
\expandafter\next#2\eol}
%    \end{macrocode}
% \end{macro}
