# Revtex's tensor macro interferes with nested amsmath split and align environments

I've encountered a strange collision between RevTeX and AMSLaTeX. If I nest a split environment inside an align environment, it works as it should, but then the next time I use the RevTeX \tensor{} macro in the document, it repeats the last line of the split equation.

Here is a minimal "working" example:

\documentclass[12pt,prd,amsmath]{revtex4}
\begin{document}
Here is a split inside an align
\begin{align}
A &= B  \\
\begin{split}
C &= D \\
&= E
\end{split}
\end{align}
And now we write a tensor $\tensor{T}$ which grabs the second line.
\end{document}


-
The problem shows up with split because, for strange reasons, amsmath.sty says \global\setbox\tw@ and doesn't use it if the ctagsplit option is selected, which is not good programming. However I remain with the opinion that \box2 in \tensor is simply wrong. –  egreg Nov 28 '12 at 23:08

The problem is shown if you

{\let\protect\show\tensor}


(which lets us see how a protected command is implemented). We get

> \tensor =\long macro:
#1->\@ontopof {#1}{\leftrightarrow }{1.15}\mathord {\box 2}


The key is \box 2. What is happening is \tensor is inserting whatever happens to be in that box. Presumably this is normally perfectly safe, but the same box is being used by align, which you can see with something like

\begin{align}
A &= B  \\
\begin{split}
C &= D \\
&= E
\end{split}
\end{align}
\copy2


This looks like a bug in REVTeX. You can empty box 2 to deal with the issue. I guess this should be done as part of \tensor, so something like

\documentclass[12pt,prd,amsmath]{revtex4}
\usepackage{letltxmacro}
\LetLtxMacro{\savedtensor}{\tensor}
\DeclareRobustCommand{\tensor}{%
\setbox2=\hbox{}%
\savedtensor
}
\begin{document}
Here is a split inside an align
\begin{align}
A &= B  \\
\begin{split}
C &= D \\
&= E
\end{split}
\end{align}
And now we write a tensor $\tensor{T}$ which grabs the second line.
\end{document}


would be a sensible workaround.

-
I believe that \mathord{\box 2} is totally misplaced. The macro \tensor relies ultimately on \@@ontopof which sets (locally) \box\tw@, but apparently does nothing with it; the setting happens in a group, so that box construction disappears anyway. \tensor is not the only macro using it. –  egreg Nov 28 '12 at 22:31

\tensor is defined in package revsymb, loaded by class revtex4:

\DeclareRobustCommand\tensor[1]{\@ontopof{#1}{\leftrightarrow}{1.15}\mathord{\box2}}


Macro \@ontopof uses \mathchoice to get the current math style and configures the math style for the double arrow that is put on top of #1:

\def\@ontopof#1#2#3{%
{%
\mathchoice
{\@@ontopof{#1}{#2}{#3}\displaystyle     \scriptstyle      }%
{\@@ontopof{#1}{#2}{#3}\textstyle        \scriptstyle      }%
{\@@ontopof{#1}{#2}{#3}\scriptstyle      \scriptscriptstyle}%
{\@@ontopof{#1}{#2}{#3}\scriptscriptstyle\scriptscriptstyle}%
}%
}%


The actual work is then done by \@@ontopof.

\def\@@ontopof#1#2#3#4#5{%
\setbox\z@\hbox{$#4#1$}%
\setbox\f@ur\hbox{$#5#2$}%


Box 0 contains the base symbol #1 and box 4 contains the double arrow that will be put on box 0.

  \setbox\tw@\null\ht\tw@\ht\z@ \dp\tw@\dp\z@


Box 2 remembers the height and depth of box 0, the base symbol.

  \@ifdim{\wd\z@>\wd\f@ur}{%
\setbox\f@ur\hb@xt@\wd\z@{\hss\box\f@ur\hss}%
\mathord{\rlap{\raise#3\ht\z@\box\f@ur}\box\z@}%
}{%
\setbox\f@ur\hb@xt@.9\wd\f@ur{\hss\box\f@ur\hss}%
\setbox\z@\hb@xt@\wd\f@ur{\hss$#4\relax#1$\hss}%
\mathord{\rlap{\copy\z@}\raise#3\ht\z@\box\f@ur}%
}%
}%


The problem is that the assigment for box 2 is done locally inside a group (BTW, even numbered registers 0, 2, 4, 6, and 8 are for local assignments, odd numbered 1, 3, 5, 7, and 9 for global assignments). Thus after \@ontopof has done its work, the setting of box 2 is lost and the previous contents of box 2 is used in \tensor.

I assume that the idea was that the composite symbol keeps the original height and depth of the base symbol. This avoids ruining the line spacing by symbols with large heights, but at the risk of clashes.

The following example implements this idea

• \m@th is added in the box settings to reset \mathsurround to avoid unwanted space additions, if \mathsurround is not zero.
• The complexity of the box structure is simplified.
• Fix for the case the base symbol's width is between 0.9 and 1 of the width of the top symbol.
• There are other macros besides \tensor that use \@ontopof with \box2.

Guessed explanation for using 90% of the width of the top symbol:
Depending on the width of the symbols the smaller symbol is horizontally centered in a box with the width of the wider symbol. Whereas the full width of the base symbol is taken, only the real width of the top symbol is taken. The full width consists of the real glyph width and the left and right side bearings. The code assumes 5% for each side and estimates the glyph width to 90% of the character box width.

\documentclass[12pt,prd,amsmath]{revtex4}

\makeatletter
\DeclareRobustCommand\REV@dddot[1]{\@ontopof{#1}{\cdots}{1.0}}
\DeclareRobustCommand\tensor[1]{\@ontopof{#1}{\leftrightarrow}{1.15}}
\DeclareRobustCommand\overstar[1]{\@ontopof{#1}{\ast}{1.15}}
\DeclareRobustCommand\loarrow[1]{\@ontopof{#1}{\leftarrow}{1.15}}
\DeclareRobustCommand\roarrow[1]{\@ontopof{#1}{\rightarrow}{1.15}}

\def\@@ontopof#1#2#3#4#5{%
\setbox\z@\hbox{$\m@th#4#1$}%
\setbox\f@ur\hbox{$\m@th#5#2$}%
\@ifdim{\wd\z@>.9\wd\f@ur}{%
\setbox\f@ur\hb@xt@\wd\z@{%
\hss
\raise#3\ht\z@\box\f@ur
\hss
}%
\wd\f@ur\z@  \ht\f@ur\z@  \dp\f@ur\z@
\setbox\z@\hbox{%
\box\f@ur
\box\z@
}%
}{%
\setbox\f@ur\hb@xt@.9\wd\f@ur{%
\hss \raise#3\ht\z@\box\f@ur \hss
}%
\setbox\z@\hbox to \wd\f@ur{%
\wd\f@ur\z@  \ht\f@ur\z@  \dp\f@ur\z@
\box\f@ur
\hss \box\z@ \hss
}%
}%
\box\z@
}%
\makeatother

\begin{document}

\setlength{\fboxsep}{0pt}
\setlength{\fboxrule}{.1pt}
\newcommand*{\test}[1]{%
\fbox{$#1{T}$}, \fbox{$#1{\mathrm{I}}$}
}
\begin{gather*}
\test\dddot\\
\test\tensor\\
\test\overstar\\
\test\loarrow\\
\test\roarrow
\end{gather*}

Here is a split inside an align
\begin{align}
A &= B  \\
\begin{split}
C &= D \\
&= E
\end{split}
\end{align}
And now we write a tensor $\tensor{T}$ which grabs the second line.
\end{document}


-