Inspired by a recent question on the Mathematica Stackexchange, I revisited this one, simplifying egreg's code by making use of the fact that l3fp can manipulate tuples of numbers and multiply them by scalars. I also added some key-value setting to explicitly write powers of the variable (whose name can be chosen), avoid writing x^0, and hide terms with zero coefficient.
EDIT: note that in my code the coefficients are given from highest degree to lowest, while in egreg's code it is the opposite.
EDIT: I've added the possibility to add colors etc.
\documentclass{article}
\usepackage{xparse,array,booktabs,multirow,xcolor,colortbl}
\ExplSyntaxOn
\NewDocumentCommand{\polymult}{O{}mm}
{
\group_begin: % to localize the setting of the variable
\keys_set:nn { polymult } {#1}
\polymult_compute:nn {#2} {#3}
\polymult_display:
\group_end:
}
\fp_new:N \l_polymult_P_fp % tuple of coefs of first factor
\fp_new:N \l_polymult_Q_fp % tuple of coefs of second factor
\fp_new:N \l_polymult_PQ_fp % tuple of coefs of result
\int_new:N \l_polymult_degP_int % degree of first factor
\int_new:N \l_polymult_degQ_int % degree of second factor
\int_new:N \l_polymult_ndeg_int % tracks (degQ - current degree)
\int_new:N \l_polymult_row_int % track which row we are on (differs from ndeg if zeros are hidden)
\int_new:N \l_polymult_nrows_int % number of rows of calculation displayed
\int_new:N \l_polymult_term_int % tracks which term we are on
\seq_new:N \l_polymult_rows_seq % coefs of each intermediate row
\tl_new:N \l_polymult_tmp_tl % temporary usage
\tl_new:N \l_polymult_var_tl % variable to use (empty for a bare table)
\bool_new:N \l_polymult_first_term_bool
\bool_new:N \l_polymult_hide_zeros_bool
\bool_new:N \l_polymult_simplify_bool
\bool_new:N \l_polymult_align_signs_bool
\tl_new:N \l_polymult_start_preamble_tl
\tl_new:N \l_polymult_stop_preamble_tl
\tl_new:N \l_polymult_start_P_row_tl
\tl_new:N \l_polymult_stop_P_row_tl
\tl_new:N \l_polymult_start_Q_row_tl
\tl_new:N \l_polymult_stop_Q_row_tl
\tl_new:N \l_polymult_start_first_row_tl
\tl_new:N \l_polymult_stop_first_row_tl
\tl_new:N \l_polymult_start_typical_row_tl
\tl_new:N \l_polymult_stop_typical_row_tl
\tl_new:N \l_polymult_start_last_row_tl
\tl_new:N \l_polymult_stop_last_row_tl
\tl_new:N \l_polymult_start_PQ_row_tl
\tl_new:N \l_polymult_stop_PQ_row_tl
\keys_define:nn { polymult }
{
var .tl_set:N = \l_polymult_var_tl ,
var .initial:n = { } ,
hide-zeros .bool_set:N = \l_polymult_hide_zeros_bool ,
simplify .bool_set:N = \l_polymult_simplify_bool ,
align-signs .bool_set:N = \l_polymult_align_signs_bool ,
start-preamble .tl_set:N = \l_polymult_start_preamble_tl ,
stop-preamble .tl_set:N = \l_polymult_stop_preamble_tl ,
start-P-row .tl_set:N = \l_polymult_start_P_row_tl ,
stop-P-row .tl_set:N = \l_polymult_stop_P_row_tl ,
start-Q-row .tl_set:N = \l_polymult_start_Q_row_tl ,
stop-Q-row .tl_set:N = \l_polymult_stop_Q_row_tl ,
start-first-row .tl_set:N = \l_polymult_start_first_row_tl ,
stop-first-row .tl_set:N = \l_polymult_stop_first_row_tl ,
start-typical-row .tl_set:N = \l_polymult_start_typical_row_tl ,
stop-typical-row .tl_set:N = \l_polymult_stop_typical_row_tl ,
start-last-row .tl_set:N = \l_polymult_start_last_row_tl ,
stop-last-row .tl_set:N = \l_polymult_stop_last_row_tl ,
start-PQ-row .tl_set:N = \l_polymult_start_PQ_row_tl ,
stop-PQ-row .tl_set:N = \l_polymult_stop_PQ_row_tl ,
operations-times .tl_set:N = \l_polymult_times_tl ,
operations-times .initial:n = {\times} ,
operations-plus .tl_set:N = \l_polymult_plus_tl ,
operations-plus .initial:n = {+} ,
operations .meta:n = {
start-preamble = {@{}c@{,}} ,
start-P-row = {&} ,
start-Q-row = {\l_polymult_times_tl&} ,
start-first-row = {&} ,
start-typical-row = {\l_polymult_plus_tl&} ,
start-last-row = {\l_polymult_plus_tl&} ,
start-PQ-row = {&} ,
},
halfway-operations .meta:n = {
start-preamble = {@{}c@{}} ,
start-P-row = {\multirow{2}{1em}{$\l_polymult_times_tl$}&} ,
start-Q-row = {&} ,
start-first-row = {\multirow{\l_polymult_nrows_int}{1em}{$\l_polymult_plus_tl$}&} ,
start-typical-row = {&} ,
start-last-row = {&} ,
start-PQ-row = {&} ,
}
}
\cs_generate_variant:Nn \clist_map_inline:nn { xn }
\cs_generate_variant:Nn \clist_count:n { e }
\cs_new:Npn __polymult_tuple_to_clist:n #1
{ \exp_args:Nf __polymult_tuple_to_clist_aux:n { \fp_to_tl:n {#1} } }
\cs_new:Npn __polymult_tuple_to_clist_aux:n #1
{
\tl_if_head_eq_charcode:nNTF {#1} ( % )
{ __polymult_tuple_to_clist_aux:w #1 } {#1}
}
\cs_new:Npn __polymult_tuple_to_clist_aux:w (#1) {#1}
\cs_new_protected:Npn \polymult_compute:nn #1 #2
{
% Parse the input, evaluate the coefficients only once
\fp_set:Nn \l_polymult_P_fp {#1}
\fp_set:Nn \l_polymult_Q_fp {#2}
\int_set:Nn \l_polymult_degP_int
{ \clist_count:e { \fp_to_tl:N \l_polymult_P_fp } - 1 }
\int_set:Nn \l_polymult_degQ_int
{ \clist_count:e { \fp_to_tl:N \l_polymult_Q_fp } - 1 }
%
% Variables to track the rows, and the total sum (initially zero)
\int_zero:N \l_polymult_row_int
\int_zero:N \l_polymult_ndeg_int
\seq_clear:N \l_polymult_rows_seq
\fp_set:Nn \l_polymult_PQ_fp
{ 0 \prg_replicate:nn { \l_polymult_degP_int + \l_polymult_degQ_int } { ,0 } }
%
% Loop through terms of Q, starting from the highest degree,
% so the rows here are added from right (bottom) to left (top)
\clist_map_inline:xn
{ __polymult_tuple_to_clist:n { \l_polymult_Q_fp } }
{
\bool_if:nF { \l_polymult_hide_zeros_bool && \fp_compare_p:n { ##1 = 0 } }
{ \int_incr:N \l_polymult_row_int }
\tl_set:Nx \l_polymult_tmp_tl
{ __polymult_tuple_to_clist:n { ##1 * \l_polymult_P_fp } }
\seq_put_left:Nx \l_polymult_rows_seq { \l_polymult_tmp_tl }
\fp_add:Nn \l_polymult_PQ_fp
{
\prg_replicate:nn { \l_polymult_ndeg_int } { 0, }
\l_polymult_tmp_tl
\prg_replicate:nn { \l_polymult_degQ_int - \l_polymult_ndeg_int } { ,0 }
}
\int_incr:N \l_polymult_ndeg_int
}
\int_set_eq:NN \l_polymult_nrows_int \l_polymult_row_int
}
\cs_new_protected:Npn \polymult_display:
{
% Display P and Q, with a suitable number of empty slots before
\tl_clear:N \l_polymult_body_tl
__polymult_build_row:nfnn { P }
{ __polymult_tuple_to_clist:n { \l_polymult_P_fp } }
{ \l_polymult_degQ_int } { 0 }
__polymult_build_row:nfnn { Q }
{ __polymult_tuple_to_clist:n { \l_polymult_Q_fp } }
{ \l_polymult_degP_int } { 0 }
%
\tl_put_right:Nn \l_polymult_body_tl { \midrule }
\int_zero:N \l_polymult_row_int
\int_zero:N \l_polymult_ndeg_int
\seq_map_inline:Nn \l_polymult_rows_seq
{
% detecting zero rows
\bool_if:nF
{ \l_polymult_hide_zeros_bool && \fp_compare_p:n { (##1) = 0(##1) } }
{
\int_incr:N \l_polymult_row_int
__polymult_build_row:fnnn
{
\int_case:nnF \l_polymult_row_int
{
{ 1 } { first }
{ \l_polymult_nrows_int } { last }
}
{ typical }
}
{##1}
{ \l_polymult_degQ_int - \l_polymult_ndeg_int }
{ \l_polymult_ndeg_int }
}
\int_incr:N \l_polymult_ndeg_int
}
\tl_put_right:Nn \l_polymult_body_tl { \midrule }
\tl_set:Nx \l_polymult_tmp_tl
{ __polymult_tuple_to_clist:n { \l_polymult_PQ_fp } }
__polymult_build_row:nfnn { PQ } \l_polymult_tmp_tl { 0 } { 0 }
\exp_args:Nnx \begin{array}
{
\l_polymult_start_preamble_tl
\bool_if:NTF \l_polymult_align_signs_bool
{
{ \int_eval:n { 2 * \clist_count:N \l_polymult_tmp_tl } }
{ @{} r @{} }
}
{ * { \clist_count:N \l_polymult_tmp_tl } { r } }
\l_polymult_stop_preamble_tl
}
\tl_use:N \l_polymult_body_tl
\end{array}
}
\cs_generate_variant:Nn __polymult_build_row:nnnn { f , nf }
\cs_new_protected:Npn __polymult_build_row:nnnn #1#2#3#4
{
% #1 = row name , #2 = clist to use, #3 = left padding, #4 = right padding
\tl_put_right:Nx \l_polymult_body_tl
{
\exp_not:v { l_polymult_start_#1_row_tl }
\prg_replicate:nn { \bool_if:NT \l_polymult_align_signs_bool { 2 * } (#3) } { & }
}
\bool_set_true:N \l_polymult_first_term_bool
\int_set:Nn \l_polymult_term_int
{ \l_polymult_degP_int + \l_polymult_degQ_int - (#3) }
\clist_map_inline:nn {#2}
{
\tl_put_right:Nx \l_polymult_body_tl
{
\bool_if:NF \l_polymult_first_term_bool { & }
\bool_if:nTF % BLF here
{ \l_polymult_hide_zeros_bool && \fp_compare_p:n { ##1 = 0 } }
{ & }
{ __polymult_monomial:nN {##1} \l_polymult_term_int }
}
\bool_set_false:N \l_polymult_first_term_bool
\int_decr:N \l_polymult_term_int
}
\tl_put_right:Nx \l_polymult_body_tl
{
\prg_replicate:nn { \bool_if:NT \l_polymult_align_signs_bool { 2 * } (#4) } { & }
\exp_not:v { l_polymult_stop_#1_row_tl }
\exp_not:N \
}
}
\cs_new:Npn __polymult_monomial:nN #1#2
{
\fp_compare:nTF { #1 < 0 }
{ \bool_if:NTF \l_polymult_align_signs_bool { {} - {} } { - } }
{
\bool_if:NF \l_polymult_first_term_bool
{ \tl_if_empty:NF \l_polymult_var_tl { {} + {} } }
}
\bool_if:NT \l_polymult_align_signs_bool { & }
\bool_if:NTF \l_polymult_simplify_bool
{
\int_compare:nNnTF {#2} = 0
{ \fp_abs:n {#1} }
{
\tl_if_empty:NTF \l_polymult_var_tl
{ \fp_abs:n {#1} }
{
\fp_compare:nTF { \fp_abs:n {#1} = 1 } { } { \fp_abs:n {#1} }
\exp_not:N \l_polymult_var_tl
\int_compare:nNnF {#2} = 1 { ^ { \int_use:N #2 } }
}
}
}
{
\fp_abs:n {#1}
\tl_if_empty:NF \l_polymult_var_tl
{ \exp_not:N \l_polymult_var_tl ^ { \int_use:N #2 } }
}
}
\ExplSyntaxOff
\NewDocumentCommand{\minhthienpolymult} {O{}} {%
\polymult[align-signs,
var=x,
simplify,
hide-zeros,
halfway-operations,
operations-times = \color{orange}\times,
operations-plus = \color{orange}+,
start-last-row = \arrayrulecolor{blue}&,
#1]}
\begin{document}
[
\polymult[halfway-operations, align-signs, var=\mathcal{B}, simplify, hide-zeros]{-1,2,3}{4,5,1,0,6}
\qquad
\polymult[align-signs, var=x, hide-zeros]{1,1,1,1,1}{1,-1}
]
[
\polymult{-10,10,-3,1,4,-2}{4,2,5,7,8,-1}
]
[
\polymult[operations, align-signs, var=x, simplify, hide-zeros]{-1,0,2}{-4,5,0,6}
\quad
\polymult[align-signs, var=a ,simplify]{1,0,-2,0,-1}{1,-1}
]
[
\minhthienpolymult[var=x, hide-zeros=false]{1,1,-1}{2,-7,9}
]
\end{document}
\smallskipisn't defined simply by\vskip\smallskipamountin LaTeX but this macro is defined via LaTeXs obscure macro\vspacewhich cannot be used in\edef. Replace\smallskipby\vskip\smallskipamountin the code. – wipet Dec 27 '14 at 12:34file.tex, by adding\endcommand or\byemacro and by runningpdftex fileat command line of your operating system. Your question was not LaTeX specific, so my code works without problems in plain TeX (and after a little correction i LaTeX too, see my comment about a simple exercise for LaTeX users). – wipet Dec 29 '14 at 15:57