[Tex/LaTex] How to change operator symbols in truth table

latex3luatexsymbols

I need to type truth tables for my computer architecture classes. Until now I was using a manually typed tabular but today I've found this answer from Scott H. which provides a command called truthtable to automatically typeset it.

A command like

\truthtable{a,b,c}{a+b;b*(-c);-(a+b)+(b*(-c))}

produces

enter image description here

Now I would like to change logical operators (\vee, \wedge and \neg) and use + (or), ยท (and) and \overline or \bar for not.

The function which typesets operators is:

\cs_new_protected:Npn \__tt_build_header:
    {
        \seq_set_from_clist:NN \l__tt_header_seq \l__tt_vars_clist
        \seq_concat:NNN \l__tt_header_seq \l__tt_header_seq \l__tt_exprs_seq
        \tl_set:Nx \l_tmpa_tl {\seq_use:Nnnn \l__tt_header_seq {&}{&}{&}}
        \tl_replace_all:Nnn \l_tmpa_tl {*} {\wedge}
        \tl_replace_all:Nnn \l_tmpa_tl {+} {\vee}
        \tl_replace_all:Nnn \l_tmpa_tl {->} {\to}
        \tl_replace_all:Nnn \l_tmpa_tl {-} {\neg}
        \tl_use:N \l_tmpa_tl
    }

Although it's written in LaTeX3 and I don't understand it, it's easy to replace \wedge with \cdot and \vee with +. But I don't know if it's easy to replace \neg to obtain a symbol (or expression) with an overline. As I imagine this alternative is difficult, a centred tilde ~ could be enough. Then I've tested with \sim but the result is ugly, the symbol is far away from negated variable or expression. Could you help me with a better alternative?

The complete code (needs lualatex) is

\documentclass{article}
\usepackage{xparse}

\begingroup
  \catcode`\%=12\relax
  \gdef\patmatch{"(%b())->(%b())","!%1||%2"}
\endgroup

\def\setimpaux#1{%
  \directlua{
    local s, _ = string.gsub("\luatexluaescapestring{#1}",\patmatch)
    tex.sprint(s)
  }
}

\ExplSyntaxOn
\int_new:N \l__tt_num_rows_int
\int_new:N \l__tt_num_cols_int
\int_new:N \l__tt_num_vars_int
\clist_new:N \l__tt_vars_clist
\seq_new:N \l__tt_exprs_seq
\seq_new:N \l__tt_header_seq

\NewDocumentCommand {\truthtable}{ m m }
    {
        \truth_table:nn {#1}{#2}
    }

\cs_new_protected:Npn \truth_table:nn #1#2
    {
        \clist_set:Nn \l__tt_vars_clist {#1}
        \seq_set_split:Nnn \l__tt_exprs_seq {;} {#2}
        \int_set:Nn \l__tt_num_vars_int {\clist_count:N \l__tt_vars_clist}
        \int_set:Nn \l__tt_num_rows_int {\fp_to_int:n {2^{\l__tt_num_vars_int}-1}}
        \int_set:Nn \l__tt_num_cols_int {\clist_count:N \l__tt_vars_clist +\seq_count:N \l__tt_exprs_seq}
        \__tt_gen_bins:
        \seq_map_function:NN \l__tt_exprs_seq \__tt_eval_bools:n
        \__tt_build_table:
    }

\cs_new_protected:Npn \__tt_build_header:
    {
        \seq_set_from_clist:NN \l__tt_header_seq \l__tt_vars_clist
        \seq_concat:NNN \l__tt_header_seq \l__tt_header_seq \l__tt_exprs_seq
        \tl_set:Nx \l_tmpa_tl {\seq_use:Nnnn \l__tt_header_seq {&}{&}{&}}
        \tl_replace_all:Nnn \l_tmpa_tl {*} {\wedge}
        \tl_replace_all:Nnn \l_tmpa_tl {+} {\vee}
        \tl_replace_all:Nnn \l_tmpa_tl {->} {\to}
        \tl_replace_all:Nnn \l_tmpa_tl {-} {\neg}
        \tl_use:N \l_tmpa_tl
    }

\cs_generate_variant:Nn \seq_use:Nnnn {cnnn}
\cs_new_protected:Npn \__tt_build_table:
    {
        \begin{array}{*{\int_use:N \l__tt_num_cols_int}{c}}
            \__tt_build_header:\\\hline
            \int_step_inline:nnnn {0}{1}{\l__tt_num_rows_int}
                {
                    \seq_use:cnnn {l__tt_row_{##1}_seq}{&}{&}{&}\\
                }
        \end{array}
    }

\cs_new_protected:Npn \__tt_set_imp:n #1
    {
        \tl_if_in:nnT {#1} {->}
            {
                \tl_set:Nx \l_tmpb_tl {\setimpaux{#1}}
                \exp_args:NV \__tt_set_imp:n \l_tmpb_tl
            }
    }
\cs_generate_variant:Nn \__tt_set_imp:n {V}

\cs_generate_variant:Nn \tl_replace_all:Nnn {Nnx}
\cs_new_protected:Npn \__tt_eval_bools:n #1
    {
        \tl_set:Nn \l_tmpa_tl {#1}
        \int_step_inline:nnnn {0}{1}{\l__tt_num_rows_int}
            {
                \int_set:Nn \l_tmpa_int {1}
                \tl_set_eq:NN \l_tmpb_tl \l_tmpa_tl
                \__tt_set_imp:V \l_tmpb_tl
                \tl_replace_all:Nnn \l_tmpb_tl {*}{&&}
                \tl_replace_all:Nnn \l_tmpb_tl {+}{||}
                \tl_replace_all:Nnn \l_tmpb_tl {-}{!}
                \clist_map_inline:Nn \l__tt_vars_clist
                    {
                        \tl_replace_all:Nnx \l_tmpb_tl {####1} {\seq_item:cn {l__tt_row_{##1}_seq} {\l_tmpa_int}}
                        \int_incr:N \l_tmpa_int
                    }
                \seq_put_right:cx {l__tt_row_{##1}_seq} {\fp_eval:n \l_tmpb_tl}
            }
    }

\cs_generate_variant:Nn \seq_set_split:Nnn {cnx}
\cs_new_protected:Npn \__tt_gen_bins:
    {
        \int_step_inline:nnnn {0}{1}{\l__tt_num_rows_int}
            {
                \seq_clear_new:c {l__tt_row_{##1}_seq}
                \seq_set_split:cnx {l__tt_row_{##1}_seq} {} {\int_to_binary:n {##1}}
                \int_while_do:nn {\seq_count:c {l__tt_row_{##1}_seq} < \l__tt_num_vars_int}
                    {
                        \seq_put_left:cn {l__tt_row_{##1}_seq} {0}
                    }
            }
    }

\ExplSyntaxOff
\begin{document}

\[
\truthtable{a,b,c}{a+b;b*(-c);-(a+b)+(b*(-c))}
\]

\end{document}

Best Answer

The undesired spacing around \sim comes from the fact that \sim is defined as a relational symbol using \mathrel; from the kernel:

\DeclareMathSymbol{\sim}{\mathrel}{symbols}{"18}

You can supress this space treating \sim as an ordinary symbol using \mathord{\sim}:

\cs_new_protected:Npn \__tt_build_header:
    {
        \seq_set_from_clist:NN \l__tt_header_seq \l__tt_vars_clist
        \seq_concat:NNN \l__tt_header_seq \l__tt_header_seq \l__tt_exprs_seq
        \tl_set:Nx \l_tmpa_tl {\seq_use:Nnnn \l__tt_header_seq {&}{&}{&}}
        \tl_replace_all:Nnn \l_tmpa_tl {*} {\cdot}
        \tl_replace_all:Nnn \l_tmpa_tl {+} {+}
        \tl_replace_all:Nnn \l_tmpa_tl {->} {\to}
        \tl_replace_all:Nnn \l_tmpa_tl {-} {\mathord{\sim}}
        \tl_use:N \l_tmpa_tl
    }

the compete code:

\documentclass{article}
\usepackage{xparse}

\begingroup
  \catcode`\%=12\relax
  \gdef\patmatch{"(%b())->(%b())","!%1||%2"}
\endgroup

\def\setimpaux#1{%
  \directlua{
    local s, _ = string.gsub("\luatexluaescapestring{#1}",\patmatch)
    tex.sprint(s)
  }
}

\ExplSyntaxOn
\int_new:N \l__tt_num_rows_int
\int_new:N \l__tt_num_cols_int
\int_new:N \l__tt_num_vars_int
\clist_new:N \l__tt_vars_clist
\seq_new:N \l__tt_exprs_seq
\seq_new:N \l__tt_header_seq

\NewDocumentCommand {\truthtable}{ m m }
    {
        \truth_table:nn {#1}{#2}
    }

\cs_new_protected:Npn \truth_table:nn #1#2
    {
        \clist_set:Nn \l__tt_vars_clist {#1}
        \seq_set_split:Nnn \l__tt_exprs_seq {;} {#2}
        \int_set:Nn \l__tt_num_vars_int {\clist_count:N \l__tt_vars_clist}
        \int_set:Nn \l__tt_num_rows_int {\fp_to_int:n {2^{\l__tt_num_vars_int}-1}}
        \int_set:Nn \l__tt_num_cols_int {\clist_count:N \l__tt_vars_clist +\seq_count:N \l__tt_exprs_seq}
        \__tt_gen_bins:
        \seq_map_function:NN \l__tt_exprs_seq \__tt_eval_bools:n
        \__tt_build_table:
    }

\cs_new_protected:Npn \__tt_build_header:
    {
        \seq_set_from_clist:NN \l__tt_header_seq \l__tt_vars_clist
        \seq_concat:NNN \l__tt_header_seq \l__tt_header_seq \l__tt_exprs_seq
        \tl_set:Nx \l_tmpa_tl {\seq_use:Nnnn \l__tt_header_seq {&}{&}{&}}
        \tl_replace_all:Nnn \l_tmpa_tl {*} {\cdot}
        \tl_replace_all:Nnn \l_tmpa_tl {+} {+}
        \tl_replace_all:Nnn \l_tmpa_tl {->} {\to}
        \tl_replace_all:Nnn \l_tmpa_tl {-} {\mathord{\sim}}
        \tl_use:N \l_tmpa_tl
    }

\cs_generate_variant:Nn \seq_use:Nnnn {cnnn}
\cs_new_protected:Npn \__tt_build_table:
    {
        \begin{array}{*{\int_use:N \l__tt_num_cols_int}{c}}
            \__tt_build_header:\\\hline
            \int_step_inline:nnnn {0}{1}{\l__tt_num_rows_int}
                {
                    \seq_use:cnnn {l__tt_row_{##1}_seq}{&}{&}{&}\\
                }
        \end{array}
    }

\cs_new_protected:Npn \__tt_set_imp:n #1
    {
        \tl_if_in:nnT {#1} {->}
            {
                \tl_set:Nx \l_tmpb_tl {\setimpaux{#1}}
                \exp_args:NV \__tt_set_imp:n \l_tmpb_tl
            }
    }
\cs_generate_variant:Nn \__tt_set_imp:n {V}

\cs_generate_variant:Nn \tl_replace_all:Nnn {Nnx}
\cs_new_protected:Npn \__tt_eval_bools:n #1
    {
        \tl_set:Nn \l_tmpa_tl {#1}
        \int_step_inline:nnnn {0}{1}{\l__tt_num_rows_int}
            {
                \int_set:Nn \l_tmpa_int {1}
                \tl_set_eq:NN \l_tmpb_tl \l_tmpa_tl
                \__tt_set_imp:V \l_tmpb_tl
                \tl_replace_all:Nnn \l_tmpb_tl {*}{&&}
                \tl_replace_all:Nnn \l_tmpb_tl {+}{||}
                \tl_replace_all:Nnn \l_tmpb_tl {-}{!}
                \clist_map_inline:Nn \l__tt_vars_clist
                    {
                        \tl_replace_all:Nnx \l_tmpb_tl {####1} {\seq_item:cn {l__tt_row_{##1}_seq} {\l_tmpa_int}}
                        \int_incr:N \l_tmpa_int
                    }
                \seq_put_right:cx {l__tt_row_{##1}_seq} {\fp_eval:n \l_tmpb_tl}
            }
    }

\cs_generate_variant:Nn \seq_set_split:Nnn {cnx}
\cs_new_protected:Npn \__tt_gen_bins:
    {
        \int_step_inline:nnnn {0}{1}{\l__tt_num_rows_int}
            {
                \seq_clear_new:c {l__tt_row_{##1}_seq}
                \seq_set_split:cnx {l__tt_row_{##1}_seq} {} {\int_to_binary:n {##1}}
                \int_while_do:nn {\seq_count:c {l__tt_row_{##1}_seq} < \l__tt_num_vars_int}
                    {
                        \seq_put_left:cn {l__tt_row_{##1}_seq} {0}
                    }
            }
    }

\ExplSyntaxOff
\begin{document}

\[
\truthtable{a,b,c}{a+b;b*(-c);-(a+b)+(b*(-c))}
\]

\end{document}

enter image description here

An explanation of \mathord, \mathrel, \mathbin and similars can be found in TeX by Topic or in the TeXbook.

Related Question