4

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}
2

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).

Output of the above code

4
  • Great and "merci beaucoup". I will analyze your code as soon as possible.
    – projetmbc
    Apr 16 at 11:10
  • Indeed the code fails on `$\orexpl{A1---B1---C1 \\ C2 \\ C3}$.
    – projetmbc
    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!
    – projetmbc
    Apr 16 at 13:44
4

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[2][]{%
  \setsepchar{---/\\}
  \readlist*\myvecs{#2}%
  \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[]{%
    \ifnum\zcnt=1\relax\else\Shortunderstack[c]{\quad or\quad\\\rule
        {.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}

enter image description here

3
  • 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.
    – projetmbc
    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.
    – projetmbc
    Apr 16 at 12:00
3

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}

enter image description here

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}

enter image description here

3
  • Thanks for this. I will adapt it such as to have the keywords or in the first lines.
    – projetmbc
    Apr 16 at 10:59
  • @projetmbc I have just added it
    – egreg
    Apr 16 at 12:12
  • Thanks for this!
    – projetmbc
    Apr 16 at 12:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.