5

There already are question about how to smash math operator limits horizontally, e.g. Improving typesetting of sums with long limits, with the conclusion "use the \smashoperator command from the mathtools package".

I'm trying to understand how this macro works. Unfortunately, the implementation is expl3-obfuscated and thus hard to understand. There are some comments on the code but there's almost no description of how internal macros are to be used. What I understand so far is that \smashoperator first analyses its mandatory argument and splits it into superscript, subscript and some other, optional code.

What I don't understand is how the operator is actually typeset with scripts smashed only on the left, right or both sides. Could someone explain in TeX/LaTeX2e code how this part of the command is implemented?

  • As far as I understand it, it is put in a box of width 0 pt. – Bernard Nov 25 '19 at 19:47
  • 3
    Actually it isn't mathtools is not written in expl3, is written in a self contained dialect of what became the expl3 syntax – daleif Nov 25 '19 at 20:00
  • 2
    It uses a lot of strangeness (that I don't understand either) to figure out exactly what type of data it has been given (no limits, just bottom limit etc), then gathers up these arguments to it knows what the operator is, and the limits, then typeset this which the limits in an internal version of clap – daleif Nov 25 '19 at 20:04
  • 2
    I'm guessing it would be a lot simpler to implement these days with xparse and the e type (for handling the parsing of the arg given to smashoperator) – daleif Nov 25 '19 at 20:08
4

First off:

\newcommand*\smashoperator[2][lr]{
  \def\MT_smop_use:NNNNN {\@nameuse{MT_smop_smash_#1:NNNNN}}
  \toks@{#2}
  \expandafter\MT_smop_get_args:wwwNnNn
    \the\toks@\@nil\@nil\@nil\@nil\@nil\@nil\@@nil
}

Upon calling \smashoperator{\sum_{a}^{b}}, the macro \MT_smop_use:NNNNN is defined to expand to \MT_smop_smash_lr:NNNNN and the argument is loaded in the token register \toks@. It's not really clear why doing this intermediate step, because the token register's content is immediately delivered and we get

\MT_smop_get_args:wwwNnNn \sum_{a}^{b}\@nil\@nil\@nil\@nil\@nil\@nil\@@nil

The purpose of \MT_smop_get_args:wwwNnNn is to identify the operator, the subscript and the superscript, with very long code. Then the superscript and subscript are set in boxes with the appropriate cramped style and placed above and below, but occupying zero width.

A simpler implementation with xparse:

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

\NewDocumentCommand{\xsmashoperator}{me{_^}}{%
  \doxsmashoperator{#1}{#2}{#3}%
}

\ExplSyntaxOn

\NewDocumentCommand{\doxsmashoperator}{mmm}
 {
  #1
  \IfValueT{#2}{\sb{\mathclap{\cramped{#2}}}}
  \IfValueT{#3}{\sp{\mathclap{\cramped{#3}}}}
 }

\ExplSyntaxOff

\begin{document}

\begin{gather*}
XXXX\smashoperator{\sum_{k=12345^2}^{123456789}}YYYYYY \\
XXXX\xsmashoperator\sum_{k=12345^2}^{123456789}YYYYYY
\end{gather*}

\end{document}

enter image description here

It would be more complicated to implement also the l and r options, but the main ingredients are already there, namely how to absorb optional subscript and superscript, without even requiring additional braces (which are not allowed in the xparse version).

Note that \ExplSyntaxOn and \ExplSyntaxOff are not required; they're used in view of the implementation of l and r.

Basically one needs to do the measuring and to add the necessary spacing fore or aft. Let sp, sb and op be the widths of the superscript, subscript and the operator, respectively. The operator is then set to its normal width with super-/subscripts of zero width. If r is set, a space of width max(0pt, max(sp, sb) - op) / 2 is added before the operator; if l is set, it's added after the operator.

Here's a complete implementation:

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

\NewDocumentCommand{\xsmashoperator}{ome{_^}}{%
  \IfNoValueTF{#1}
   {%
    \doxsmashoperator{#2}{#3}{#4}%
   }
   {%
    \doxsmashoperatoropt{#1}{#2}{#3}{#4}%
   }
}

\ExplSyntaxOn

\NewDocumentCommand{\doxsmashoperator}{mmm}
 {
  #1
  \IfValueT{#2}{\sb{\mathclap{\cramped{#2}}}}
  \IfValueT{#3}{\sp{\mathclap{\cramped{#3}}}}
 }

\NewDocumentCommand{\doxsmashoperatoropt}{mmmm}
 {
  \group_begin:
  \__siracusa_xsmashoperator:nnnn { #1 } { #2 } { #3 } { #4 }
  \group_end:
 }

\box_new:N \l__siracusa_xsmashoperator_op_box
\box_new:N \l__siracusa_xsmashoperator_sb_box
\box_new:N \l__siracusa_xsmashoperator_sp_box
\dim_new:N \l__siracusa_xsmashoperator_dim

\cs_new_protected:Nn \__siracusa_xsmashoperator_math:n
 {
  $ \use:c { m@th } #1 $
 }

\cs_new_protected:Nn \__siracusa_xsmashoperator:nnnn
 {
  \hbox_set:Nn \l__siracusa_xsmashoperator_op_box
   {
    \__siracusa_xsmashoperator_math:n { \displaystyle{#2} }
   }
  \hbox_set:Nn \l__siracusa_xsmashoperator_sb_box
   {
    \tl_if_novalue:nF { #3 }{ \__siracusa_xsmashoperator_math:n {\scriptstyle\cramped{#3}} }
   }
  \hbox_set:Nn \l__siracusa_xsmashoperator_sp_box
   {
    \tl_if_novalue:nF { #4 }{ \__siracusa_xsmashoperator_math:n {\scriptstyle\cramped{#4}} }
   }
  \dim_set:Nn \l__siracusa_xsmashoperator_dim
   {
    \dim_max:nn
     { \box_wd:N \l__siracusa_xsmashoperator_sp_box }
     { \box_wd:N \l__siracusa_xsmashoperator_sb_box }
    - \box_wd:N \l__siracusa_xsmashoperator_op_box
   }
  \dim_set:Nn \l__siracusa_xsmashoperator_dim
   {
    \dim_max:nn { \l__siracusa_xsmashoperator_dim } { 0pt } / 2
   }
  % now do the typesetting
  \str_if_eq:nnT { #1 } { r } { \hspace{\l__siracusa_xsmashoperator_dim} }
  #2
  \tl_if_novalue:nF { #3 } { \sb{\mathclap{\cramped{#3}}} }
  \tl_if_novalue:nF { #4 } { \sp{\mathclap{\cramped{#4}}} }
  \str_if_eq:nnT { #1 } { l } { \hspace{\l__siracusa_xsmashoperator_dim} }
 }

\ExplSyntaxOff

\begin{document}

\begin{gather*}
XXXX\smashoperator{\sum_{k=12345^2}^{123456789}}YYYYYY \\
XXXX\xsmashoperator\sum_{k=12345^2}^{123456789}YYYYYY \\
XXXX\smashoperator[l]{\sum_{k=12345^2}^{123456789}}YYYYYY \\
XXXX\xsmashoperator[l]\sum_{k=12345^2}^{123456789}YYYYYY \\
XXXX\smashoperator[r]{\sum_{k=12345^2}^{123456789}}YYYYYY \\
XXXX\xsmashoperator[r]\sum_{k=12345^2}^{123456789}YYYYYY
\end{gather*}

\end{document}

enter image description here

|improve this answer|||||
  • 1
    Thanks, the spacing calculation isn't obvious at first sight. Why is the \cramped command used here for the limits? Does that give the same limits as in the non-smashed version? – siracusa Nov 26 '19 at 0:07
  • @siracusa I used the same method as mathtools, but actually TeX doesn't use the cramped style in the upper limit. It's easy enough to remove \cramped in two places. – egreg Nov 26 '19 at 7:42

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.