10

I want to create a scheme of coefficients to multiply polynomials, e.g., P(x) = x^2 + 2x + 3 and Q(x) = 4x^2 + 5x + 6 as in this picture:

enter image description here

By my hand, I tried

\documentclass[12pt,a4paper]{article}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{ polynomial}
\usepackage{polynom}
\usepackage{fourier}
\begin{document}
$P(x) = \polynomial{1,2,3}$ 

$Q(x) = \polynomial{4,5,6}$

\[\begin{matrix}
   {} & {} & 1 & 2 & 3  \\
   {} & {} & 4 & 5 & 6  \\
   {} & {} & 6 & 12 & 18  \\
   {} & 5 & 10 & 15 & {}  \\
   4 & 8 & 12 & {} & {}  \\
   4 & 13 & 28 & 32 & 18 
\end{matrix}\] 
\end{document}

How can I create the scheme automatically (e.g., by using the polynom package as with applications of polynomial division)?

MattAllegro
  • 1,546
minthao_2011
  • 4,534
  • 7
  • 36
  • 61

3 Answers3

9

Here's an expl3 version

\documentclass{article}
\usepackage{xparse,booktabs}

\ExplSyntaxOn
\NewDocumentCommand{\polymult}{mm}
 {
  \minthao_polymult_compute:nn { #1 } { #2 }
  \minthao_polymult_display:
 }

\seq_new:N \l_minthao_polymult_first_seq   % coefficients of first factor
\seq_new:N \l_minthao_polymult_second_seq  % coefficients of second factor
\seq_new:N \l_minthao_polymult_result_seq  % coefficients of result
\seq_new:N \l_minthao_polymult_temp_seq    % temporary usage
\int_new:N \l_minthao_polymult_first_int   % degree of first factor + 1
\int_new:N \l_minthao_polymult_second_int  % degree of second factor + 1
\int_new:N \l_minthao_polymult_temp_int    % temporary usage
\tl_new:N \l_minthao_polymult_tempa_tl     % current coefficient (first factor)
\tl_new:N \l_minthao_polymult_tempb_tl     % current coefficient (second factor)
\tl_new:N \l_minthao_polymult_body_tl      % table body

\cs_new_protected:Npn \minthao_polymult_compute:nn #1 #2
 {
  \seq_set_split:Nnn \l_minthao_polymult_first_seq { , } { #1 }
  \seq_set_split:Nnn \l_minthao_polymult_second_seq { , } { #2 }
  \seq_clear:N \l_minthao_polymult_result_seq
  \int_set:Nn \l_minthao_polymult_first_int
   {
    \seq_count:N \l_minthao_polymult_first_seq
   }
  \int_set:Nn \l_minthao_polymult_second_int
   {
    \seq_count:N \l_minthao_polymult_second_seq
   }
  \int_step_inline:nnnn
   { 2 } % start
   { 1 } % step
   { \l_minthao_polymult_first_int + \l_minthao_polymult_second_int } % end
   {
    \int_zero:N \l_minthao_polymult_temp_int
    % compute the coefficient of degree ##1 - 2
    \int_step_inline:nnnn { 0 } { 1 } { ##1 - 2 }
     {
      % get the coefficients, we'll use 0 if above the degree
      \tl_set:Nx \l_minthao_polymult_tempa_tl
       { \seq_item:Nn \l_minthao_polymult_first_seq { ####1 + 1 } }
      \tl_set:Nx \l_minthao_polymult_tempb_tl
       { \seq_item:Nn \l_minthao_polymult_second_seq { ##1 - ####1 - 1 } }
      % compute the coefficient of the product at the current degree
      \int_add:Nn \l_minthao_polymult_temp_int
       {
        \tl_if_empty:NTF \l_minthao_polymult_tempa_tl { 0 } { \l_minthao_polymult_tempa_tl }
        *
        \tl_if_empty:NTF \l_minthao_polymult_tempb_tl { 0 } { \l_minthao_polymult_tempb_tl }
       }
     }
    % append the coefficient
    \seq_put_right:Nx \l_minthao_polymult_result_seq
     {
      \int_to_arabic:n { \l_minthao_polymult_temp_int }
     }
   }
 }

\cs_new_protected:Npn \minthao_polymult_display:
 {
  \tl_clear:N \l_minthao_polymult_body_tl
  \__minthao_polymult_build_row:Nnn \l_minthao_polymult_first_seq { 1 } { 0 }
  \__minthao_polymult_build_row:Nnn \l_minthao_polymult_second_seq { 1 } { 0 }
  \tl_put_right:Nn \l_minthao_polymult_body_tl { \midrule }
  \int_step_inline:nnnn { 1 } { 1 } { \l_minthao_polymult_second_int }
   {
    \__minthao_polymult_build_row:Nnn \l_minthao_polymult_first_seq
     { \seq_item:Nn \l_minthao_polymult_second_seq { ##1 } } { ##1 - 1 }
   }
  \tl_put_right:Nn \l_minthao_polymult_body_tl { \midrule }
  \__minthao_polymult_build_row:Nnn \l_minthao_polymult_result_seq { 1 } { 0 }
  \begin{array}{*{\seq_count:N \l_minthao_polymult_result_seq}{c}}
  \tl_use:N \l_minthao_polymult_body_tl
  \end{array}
 }

\cs_new_protected:Npn \__minthao_polymult_build_row:Nnn #1 #2 #3
 {% #1 = sequence to use, #2 = multiplier, #3 = right padding
  \seq_clear:N \l_minthao_polymult_temp_seq
  \seq_map_inline:Nn #1
   {
    \seq_put_right:Nx \l_minthao_polymult_temp_seq
     { \int_to_arabic:n { ##1 * #2 } }
   }
  \seq_reverse:N \l_minthao_polymult_temp_seq
  % add the left padding
  \prg_replicate:nn
   {% number of repetitions:
    -1*(#3) + 
    \seq_count:N \l_minthao_polymult_result_seq -
    \seq_count:N \l_minthao_polymult_temp_seq
   }
   {% add blank entries
    \seq_put_left:Nn \l_minthao_polymult_temp_seq { }
   }
  \tl_put_right:Nx \l_minthao_polymult_body_tl
   { \seq_use:Nn \l_minthao_polymult_temp_seq { & } }
  \tl_put_right:Nn \l_minthao_polymult_body_tl { \\ }
 }

\ExplSyntaxOff

\begin{document}
\[
\polymult{3,2,1}{6,5,4}
\qquad
\polymult{1,1,1,1,1}{-1,1}
\]
\[
\polymult{-2,4,1,-3,10,-10}{-1,8,7,5,2,4}
\]
\end{document}

enter image description here

egreg
  • 1,121,712
7

You can try the macro \mulscheme. I wrote it as an responce for your question. The usage is:

\mulscheme {1 2 3} {4 5 6}
\mulscheme {3 4} {2 3 4 5}  etc..

The result of the first example is the same as you has shown.

The implementation is here:

\newcount\totA \newcount\totB  \newcount\tmpnum \newcount\spacenum
\def\mulscheme#1#2{%
   \tmpnum=0\def\tmp{A}\mulA #1 {} \totA=\tmpnum % reading first parameter
   \tmpnum=0\def\tmp{B}\mulA #2 {} \totB=\tmpnum % reading second parameter
   \advance\tmpnum by\totA \edef\totT{\the\tmpnum}% sum of totA and totB
   \def\linemul{} \tmpnum=0
   \loop \ifnum\tmpnum<\totB \advance\tmpnum by1 \mulC \repeat
   \edef\linemul{\linemul \mulB#1 {} }%
   \edef\muldata{\expandafter\mulF\linemul!\cr}% first line with first parameter
   \def\linemul{} \tmpnum=0
   \loop \ifnum\tmpnum<\totA \advance\tmpnum by1 \mulC \repeat
   \edef\linemul{\linemul \mulB#2 {} }% second line with second parameter
   \edef\muldata{\muldata\expandafter\mulF\linemul!\cr\noalign{\mulG\hrule\smallskip}}%
   \spacenum=0
   \loop \def\linemul{}%
         {\tmpnum=0 \loop \ifnum\tmpnum<\spacenum \advance\tmpnum by1 \mulC \repeat}%
         {\loop \mulD \advance\totA by-1 \ifnum\totA>0 \repeat}%
         {\tmpnum=\totB \loop \ifnum\tmpnum>1 \advance\tmpnum by-1 \mulC \repeat}%
         \edef\muldata{\muldata\linemul\cr}%
         \advance\spacenum by1 \advance\totB by-1
         \ifnum\totB>0 \repeat
   \tmpnum=\totT \advance\tmpnum by-1 \def\linemul{}%
   \loop \edef\linemul{\linemul\mulE \csname mulT:\the\tmpnum\endcsname}%
         \global\expandafter\let\csname mulT:\the\tmpnum\endcsname=\relax
         \advance\tmpnum by-1 \ifnum\tmpnum>0 \repeat
   \vbox{\halign{&\ \hfil$##$\ \cr\muldata\noalign{\smallskip\hrule\smallskip}\linemul\cr}}%
}
\def\mulA #1 {\ifx^#1^\else
   \advance\tmpnum by1
   \expandafter\def\csname mul\tmp:\the\tmpnum\endcsname{#1}%
   \expandafter\mulA\fi}
\def\mulB #1 {\ifx^#1^\else #1&\expandafter\mulB\fi}
\def\mulC{\xdef\linemul{\space\mulE\linemul}}
\def\mulD{\tmpnum=\csname mulA:\the\totA\endcsname 
   \multiply\tmpnum by\csname mulB:\the\totB\endcsname
   \xdef\linemul{\the\tmpnum \mulE\linemul}
   \advance\spacenum by1
   \advance\tmpnum by0\csname mulT:\the\spacenum\endcsname
   \expandafter\xdef\csname mulT:\the\spacenum\endcsname{\the\tmpnum}%
}
\def\mulE{\ifx\linemul\empty \else&\fi}
\def\mulF#1&!{#1}
\def\mulG{\nointerlineskip\vbox to0pt{\kern-1.1\baselineskip\hbox{$\times$}\vss}\smallskip}

\mulscheme {1 2 3} {4 5 6}
\bigskip\mulscheme {3 4} {2 3 4 5}

Only TeX primitives and \newcount macro are used. I tried this in plain TeX but I hope that it will work in LaTeX too. The main principle of this macro is that it calculates lines in \linemul and stores them to \muldata. When all calculations were done then \halign{...\muldata} is used.

wipet
  • 74,238
  • It seems it doesn't work with LaTeX... – karlkoeller Dec 27 '14 at 10:38
  • OK, I leave this correction as an simple exercise for LaTeX users. Hint: I didn't suppose that \smallskip isn't defined simply by \vskip\smallskipamount in LaTeX but this macro is defined via LaTeXs obscure macro \vspace which cannot be used in \edef. Replace \smallskip by \vskip\smallskipamount in the code. – wipet Dec 27 '14 at 12:34
  • Well done. But replace it in the answer as well... – karlkoeller Dec 27 '14 at 12:37
  • @wipet I cann't complile. – minthao_2011 Dec 27 '14 at 14:24
  • @minthao_2011 You can compile my code by saving it to file.tex, by adding \end command or \bye macro and by running pdftex file at 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
6

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}