# Transpose a partially filled matrix in LaTeX 3

I am working on a LaTeX 3 macro to obtain something that will look like this (I use an ASCII formatting just to ease the understanding of the output expected):

x^2 - 2 = 0    or  4 x + 3 = 0  or  x - 3 = 0
x^2 = 2         |  4 x = - 3    |   x = 3
x = +/-sqrt(2)  |  x = - 3/4    |


The API would look like that:

\orexpl{
x^2 - 2 = 0      \\
x^2 = 2          \\
x = \pm \sqrt{2}
---
4 x + 3 = 0       \\
4 x = - 3         \\
x = - \frac{3}{4}
---
x - 3 = 0 \\
x = 3
}


Here is a starting point where I split the content regarding ---, but I'm looking for advice to finish the job. The idea will be to build code that can be used with a table-like environment like the ones proposed by nicematrix for example (indeed the symbols , and ; of my tiny M(not)WE will be merely replaced by & and \\).

\documentclass{article}

\ExplSyntaxOn
\seq_new:N \l__pmbc_lines
\seq_new:N \l__pmbc_cells

\int_new:N \l__pmbc_nblines

\NewDocumentCommand{\orexpl}{+m} {
\seq_set_split:Nnn \l__pmbc_lines
{ --- }
{ #1 }

\int_set:Nn \l__pmbc_nblines
{ \seq_count:N \l__pmbc_lines }
}
\ExplSyntaxOff

\begin{document}

NO HOLE

\orexpl{
A1 \\ A2 \\ A3
---
B1 \\ B2 \\ B3
---
C1 \\ C2 \\ C3
}

Output wanted

A1 , B1 , C1 ;

A2 , B2 , C2 ;

A3 , B3 , C3 ;

\bigskip

WITH HOLES - CASE 1

\orexpl{
A1 \\ A2 \\ A3
---
B1
---
C1 \\ C2
}

Output wanted

A1 , B1 , C1 ;

A2 , , C2 ;

A3 , , ;

\bigskip

WITH HOLES - CASE 2

\orexpl{
A1
---
B1 \\ B2
---
C1 \\ C2 \\ C3
}

Output wanted

A1 , B1 , C1 ;

, B2 , C2 ;

, , C3 ;

\end{document}


Here is a solution with expl3 and {NiceMatrix} of nicematrix. You need the latest version of nicematrix (v. 5.14 of 2021-04-08).

\documentclass{article}
\usepackage{nicematrix,tikz}

\ExplSyntaxOn
\seq_new:N \l__pmbc_lines_seq
\seq_new:N \l__pmbc_cells_seq
\int_new:N \l__pmbc_lines_int
\int_new:N \l__pmbc_cols_int

\cs_generate_variant:Nn \seq_set_split:Nnn { c n v }

\NewDocumentCommand { \orexpl } { +m }
{
\seq_set_split:Nnn \l__pmbc_lines_seq { --- } { #1 }
\int_set:Nn \l__pmbc_lines_int { \seq_count:N \l__pmbc_lines_seq }
%
% We create a sequence for each line called \l__pmbc_line_1_seq, \l__pmbc_line_2_seq, etc.
% Recall that ##1 is the index of the loop: ##1 will vary from 1 to \l__pmbc_lines_int
\int_step_inline:nn \l__pmbc_lines_int
{
\tl_set:cx { l__pmbc_line_ ##1 _tl } { \seq_item:Nn \l__pmbc_lines_seq { ##1 } }
\seq_set_split:cnv { l__pmbc_line_ ##1 _seq } { \\ } { l__pmbc_line_ ##1 _tl }
}
%
% We compute in \l__pmbc_cols_int the maximum of the length of the previous sequences: it's the
% number of columns of the input (the array given by the user).
\int_step_inline:nn \l__pmbc_lines_int
{
\int_set:Nn \l_tmpa_int { \seq_count:c { l__pmbc_line_ ##1 _seq } }
\int_compare:nNnT \l_tmpa_int > \l__pmbc_cols_int
{ \int_set_eq:NN \l__pmbc_cols_int \l_tmpa_int }
}
\begin{NiceMatrix}
%
% Now, two imbricated loops. ##1 is the index of the first loop and ####1 is the index of the second loop.
\int_step_inline:nn \l__pmbc_cols_int
{
\int_compare:nNnF ##1 > { \seq_count:c { l__pmbc_line_1_seq } }
{ \seq_item:cn { l__pmbc_line_1_seq } { ##1 } }
\int_step_inline:nnn 2 \l__pmbc_lines_int
{
\int_compare:nNnTF ##1 > { \seq_count:c { l__pmbc_line_ ####1 _seq } }
{ & & }
{
&
% The words "or" will be in a standalone column (the vertical rule below will be drawn by Tikz)
\int_compare:nNnT { ##1 } = 1 { \text { or } }
&
\seq_item:cn { l__pmbc_line_ ####1 _seq } { ##1 }
}
}
\int_compare:nNnF \l__pmbc_cols_int = ##1 { \\ }
}
%
% We draw the vertical rules in the columns by using the PGF/Tikz constructed by {NiceMatrix}
\CodeAfter
\int_step_inline:nn { \l__pmbc_lines_int - 1 }
{
\tikz \draw ( 2 -| \int_eval:n { 2 * ##1 } .5 )
-- (last -| \int_eval:n { 2 * ##1 } .5 ) ;
}
\end{NiceMatrix}
}
\ExplSyntaxOff

\begin{document}

$\orexpl{ x^2 - 2 = 0 \\ x^2 = 2 \\ x = \pm \sqrt{2} --- 4 x + 3 = 0 \\ 4 x = - 3 \\ x = - \frac{3}{4} --- x - 3 = 0 \\ x = 3 \\ }$

\bigskip
$\orexpl{A1 --- B1 --- C1 \\ C2 \\ C3}$

\bigskip
$\orexpl{A1--- B1 \\ B2 --- C1 \\ C2 \\ C3}$

\end{document}


As usual with nicematrix you need several compilations (because nicematrix uses PGF/Tikz nodes under the hood). • Great and "merci beaucoup". I will analyze your code as soon as possible. Apr 16 at 11:10
• Indeed the code fails on $\orexpl{A1---B1---C1 \\ C2 \\ C3}$. Apr 16 at 11:17
• I have modified my answer. Apr 16 at 11:35
• Thanks a lot for the comments in the code. They make it is easy to follow the code. LaTeX3 is so logical! Apr 16 at 13:44

Here, I use listofitems to parse the input with a --- separator. Each of the items in the list can be directly employed as a stack. I actually do a nested parsing, to also read the number of \\ dividers in each stack, and use this information to calculate the depth of the rule between stacks.

I changed the list depth in one of the examples just to show that the rule depth is not fixed, but adjusts with the list depth.

I also change the baselineskip of the stacks, and the rule adjusts to the setting automatically.

EDITED to add an [align] option if you wish to provide tabbing (after, not before the =). Otherwise, alignment is left.

\documentclass{article}
\usepackage{listofitems,tabstackengine}
\newcommand\orexpl[]{%
\setsepchar{---/\\}
\def\listdepth{1}%
\foreachitem\z\in\myvecs[]{%
\ifnum\listlen\myvecs[\zcnt]>\listdepth\relax
\xdef\listdepth{\listlen\myvecs[\zcnt]}%
\fi
}%
\renewcommand\stackalignment{l}%
\foreachitem\z\in\myvecs[]{%
{.4pt}{\numexpr\listdepth-1\relax\dimexpr\Lstackgap\relax}}\fi
\ensurestackMath{\csname #1Longunderstack\expandafter
\endcsname\expandafter{\z}}%
}%
}
\begin{document}
\orexpl[align]{
x^2 - 2 =& 0      \\
x^2 =& 2          \\
x =& \pm \sqrt{2}
---
4 x + 3 =& 0       \\
4 x =& - 3         \\
x =& - \frac{3}{4}
---
x - 3 =& 0 \\
x =& 3
}

\bigskip
\orexpl{
A123 \\ A2 \\ A3 \\ A4
---
B1 \\ B2 \\ B3
---
C1 \\ C2 \\ C3
}

\setstackgap{L}{.8\baselineskip}
\bigskip
\orexpl{
A1 \\ A2 \\ A3
---
B1
---
C1 \\ C2
}

\setstackgap{L}{1.3\baselineskip}
\bigskip
\orexpl{
A1
---
B1 \\ B2
---
C1 \\ C2 \\ C3
}
\end{document} • Thanks a lot for this TeX solution even if I would prefer a LaTeX3 one. Can you explain a little your code? More specifically the foreach loop. Apr 16 at 6:39
• @projetmbc The \foreachitem loop is a syntax of the listofitems package in which each time through the loop, a list item (the stuff between the --- delimiters) is sequentially substituted for the loop variable (here \z). In order to get the actual item tokens inside of \z, one expansion is required. Apr 16 at 11:57
• Thanks for this clarification. Apr 16 at 12:00

This is the basis for the transposition:

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\orexpl}{m}
{
\pmbc_orexpl:n { #1 }
}

\cs_generate_variant:Nn \seq_set_split:Nnn { c }

\seq_new:N \l_pmbc_orexpl_lines_seq
\int_new:N \l_pmbc_orexpl_cols_int
\int_new:N \l_pmbc_orexpl_rows_int

% allocate at least three columns
\seq_new:c { l_pmbc_orexpl_col_1_seq }
\seq_new:c { l_pmbc_orexpl_col_2_seq }
\seq_new:c { l_pmbc_orexpl_col_3_seq }
% and three rows
\seq_new:c { l_pmbc_orexpl_row_1_seq }
\seq_new:c { l_pmbc_orexpl_row_2_seq }
\seq_new:c { l_pmbc_orexpl_row_3_seq }

\cs_new_protected:Nn \pmbc_orexpl:n
{
\seq_set_split:Nnn \l_pmbc_orexpl_lines_seq { --- } { #1 }
\int_set:Nn \l_pmbc_orexpl_cols_int { \seq_count:N \l_pmbc_orexpl_lines_seq }
\int_zero:N \l_pmbc_orexpl_rows_int
% get the column data
\seq_map_indexed_inline:Nn \l_pmbc_orexpl_lines_seq
{
\seq_clear_new:c { l_pmbc_orexpl_col_##1_seq }
\seq_set_split:cnn { l_pmbc_orexpl_col_##1_seq } { \\ } { ##2 }
\int_set:Nn \l_pmbc_orexpl_rows_int
{
\int_max:nn { \l_pmbc_orexpl_rows_int } { \seq_count:c { l_pmbc_orexpl_col_##1_seq } }
}
}
% now transpose
\int_step_inline:nn { \l_pmbc_orexpl_rows_int }
{
\seq_clear_new:c { l_pmbc_orexpl_row_##1_seq }
\int_step_inline:nn { \l_pmbc_orexpl_cols_int }
{
\seq_put_right:cx { l_pmbc_orexpl_row_##1_seq }
{
\seq_item:cn { l_pmbc_orexpl_col_####1_seq } { ##1 }
}
}
}
% and build the array
\begin{array}{ c *{ \int_eval:n { \l_pmbc_orexpl_cols_int - 1 } } { |c } }
\int_step_function:nN { \l_pmbc_orexpl_rows_int } \__pmbc_orexpl_makerow:n
\end{array}
}

\cs_new_protected:Nn \__pmbc_orexpl_makerow:n
{
\seq_use:cn { l_pmbc_orexpl_row_#1_seq } { & } \\
}

\ExplSyntaxOff

\begin{document}

$\orexpl{ x^2 - 2 = 0 \\ x^2 = 2 \\ x = \pm \sqrt{2} --- 4 x + 3 = 0 \\ 4 x = - 3 \\ x = - \frac{3}{4} --- x - 3 = 0 \\ x = 3 }$

\end{document} With the “or” separators:

\documentclass{article}
\usepackage{amsmath,array}

\ExplSyntaxOn

\NewDocumentCommand{\orexpl}{m}
{
\group_begin:
\setlength{\arraycolsep}{2em}
\pmbc_orexpl:n { #1 }
\group_end:
}

\cs_generate_variant:Nn \seq_set_split:Nnn { c }
\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { c }

\seq_new:N \l_pmbc_orexpl_lines_seq
\int_new:N \l_pmbc_orexpl_cols_int
\int_new:N \l_pmbc_orexpl_rows_int

% allocate at least three columns
\seq_new:c { l_pmbc_orexpl_col_1_seq }
\seq_new:c { l_pmbc_orexpl_col_2_seq }
\seq_new:c { l_pmbc_orexpl_col_3_seq }
% and three rows
\seq_new:c { l_pmbc_orexpl_row_1_seq }
\seq_new:c { l_pmbc_orexpl_row_2_seq }
\seq_new:c { l_pmbc_orexpl_row_3_seq }

\cs_new_protected:Nn \pmbc_orexpl:n
{
\seq_set_split:Nnn \l_pmbc_orexpl_lines_seq { --- } { #1 }
\int_set:Nn \l_pmbc_orexpl_cols_int { \seq_count:N \l_pmbc_orexpl_lines_seq }
\int_zero:N \l_pmbc_orexpl_rows_int
% get the column data
\seq_map_indexed_inline:Nn \l_pmbc_orexpl_lines_seq
{
\seq_clear_new:c { l_pmbc_orexpl_col_##1_seq }
\seq_set_split:cnn { l_pmbc_orexpl_col_##1_seq } { \\ } { ##2 }
\int_set:Nn \l_pmbc_orexpl_rows_int
{
\int_max:nn { \l_pmbc_orexpl_rows_int } { \seq_count:c { l_pmbc_orexpl_col_##1_seq } }
}
}
% now transpose
\int_step_inline:nn { \l_pmbc_orexpl_rows_int }
{
\seq_clear_new:c { l_pmbc_orexpl_row_##1_seq }
\int_step_inline:nn { \l_pmbc_orexpl_cols_int }
{
\seq_put_right:cx { l_pmbc_orexpl_row_##1_seq }
{
\seq_item:cn { l_pmbc_orexpl_col_####1_seq } { ##1 }
}
}
}
% and build the array
\__pmbc_orexpl_makerow_first:
\begin{array}{ c *{ \int_eval:n { \l_pmbc_orexpl_cols_int - 1 } } { |c } }
\tl_use:N \l_tmpa_tl \\
\int_step_function:nnN { 2 } { \l_pmbc_orexpl_rows_int } \__pmbc_orexpl_makerow:n
\end{array}
}

\cs_new_protected:Nn \__pmbc_orexpl_makerow_first:
{
\tl_clear:N \l_tmpa_tl
\seq_map_indexed_inline:cn { l_pmbc_orexpl_row_1_seq }
{
\int_compare:nTF { ##1 < \seq_count:c { l_pmbc_orexpl_row_1_seq } }
{ \tl_put_right:Nn \l_tmpa_tl { \multicolumn{1}{c!{\__pmbc_orexpl_or:}}{##2} & } }
{ \tl_put_right:Nn \l_tmpa_tl { ##2 } }
}
}

\cs_new_protected:Nn \__pmbc_orexpl_or:
{
\makebox[0pt]{\makebox[2\arraycolsep]{~or~}}
}

\cs_new_protected:Nn \__pmbc_orexpl_makerow:n
{
\seq_use:cn { l_pmbc_orexpl_row_#1_seq } { & } \\
}

\ExplSyntaxOff

\begin{document}

$\orexpl{ x^2 - 2 = 0 \\ x^2 = 2 \\ x = \pm \sqrt{2} --- 4 x + 3 = 0 \\ 4 x = - 3 \\ x = - \frac{3}{4} --- x - 3 = 0 \\ x = 3 }$

\end{document} • Thanks for this. I will adapt it such as to have the keywords or` in the first lines. Apr 16 at 10:59
• @projetmbc I have just added it Apr 16 at 12:12
• Thanks for this! Apr 16 at 12:14