4

Often times I have equations that are long enough to awkwardly displace the equation number, but not long enough to produce an "overfull hbox" warning. For example, see the picture below.

The document in question is hundreds of pages long, and it is exceedingly inconvenient to examine the entire document to look for such things. Ideally, something like this would throw a warning so that I could quickly jump to the problematic equations and fix them.

Is there a way to generate warnings in this case?

Related to this, it looks as if the equation is being centered* with respect to the entire text body, which results in unused space to the left which could help prevent the displaced equation number. In the example below, the equation is still too large, but there are instances when centering with respect to the left margin and the equation number (instead of the left and right margins) would prevent this from happening in the first place.

Is there a way to have the alignment of the equation take the width of the equation number into account?

I believe that both of these issues could be addressed if TeX acted as if the right margin was defined by the left end of the equation number (plus a small amount of 'padding'). Is there a way to achieve this?

(* ---- I'm actually using the fleqn option, so it isn't exactly centered.)

"Overfull hbox"

\documentclass{article}

\usepackage{mathtools}

\usepackage[paperwidth=5in,left=.9375in,right=.9375in]{geometry}

\begin{document}

    \begin{equation}
        \begin{split}
            z & =a+b+c+d+e+f+g+h+i+j+k \\
            & +l+m+n+o+p+q+r+s+t+u+v % Does not throw a warning
        \end{split}
    \end{equation}

    \begin{equation}
        \begin{split}
            z & =a+b+c+d+e+f+g+h+i+j+k+l \\
            & +m+n+o+p+q+r+s+t+u+v % Should throw a warning
        \end{split}
    \end{equation}

    \begin{equation}
        \begin{split}
            z & =a+b+c+d+e+f+g+h+i+j+k+l+m\\&+n+o+p+q+r+s+t+u+v+w % Overfull hbox
        \end{split}
    \end{equation}

\end{document}
12
  • 5.2.1.15 isn't going to fit whatever you do is it? Aug 1, 2017 at 1:02
  • It's a feature of whatever environment you're using, to put the equation number on the next line when it doesn't fit on the same one. (There are some interesting tricks that achieve this.) You could "disable that feature", by changing the code used for typesetting them. But good luck changing LaTeX code; it isn't exactly well-documented. Aug 1, 2017 at 1:07
  • 2
    @DavidCarlisle : yes, that would be even better. :-) But I'd like to see how LaTeX can be made to produce a warning....
    – JPi
    Aug 1, 2017 at 1:16
  • 2
    @JPi In fact, I'd be completely satisfied. I would consider figuring out a way to throw a warning as a complete answer to the question. Also, I'm not really worried about how to fix this particular equation, however. It's just one equation. The problem is there are hundreds of 'errors' like this and, while I will in the end ultimately have to correct each one individually, I'd rather not have to find them manually as well. Aug 1, 2017 at 1:22
  • 1
    Can you add a minimal example that produces the sort of “error” you wish you avoid? It's not obvious to me that making things work for the equation environment that @JPi helpfully added will fix things for your fleqn situation as well. Aug 1, 2017 at 1:28

2 Answers 2

5

grmmbl no test file provided..

\documentclass{article}

\usepackage{amsmath}

\makeatletter
%  \begin{macro}{\place@tag}
%    \cs{place@tag} takes care of the placment of tags in the
%    \env{align} environments.
%    \begin{macrocode}
\def\place@tag{%
    \iftagsleft@
        \kern-\tagshift@
        \if1\shift@tag\row@\relax
            \rlap{\vbox{%
                \normalbaselines
                \boxz@
                \vbox to\lineht@{}%
                \raise@tag
            }}%
        \else
            \rlap{\boxz@}%
        \fi
        \kern\displaywidth@
    \else
        \kern-\tagshift@
        \if1\shift@tag\row@\relax
%    \end{macrocode}
%    Added depth to correct vertical spacing of shifted
%    equation tags.---dmj, 1994/12/29
%    \begin{macrocode}
\typeout{===========================}%
\typeout{BEWARE: EQUATION TAG MOVING}%
\typeout{BEWARE: \theequation}%
\typeout{===========================}%
            \llap{\vtop{%
                \raise@tag
                \normalbaselines
                \setbox\@ne\null
                \dp\@ne\lineht@
                \box\@ne
                \boxz@
            }}%
        \else
            \llap{\boxz@}%
        \fi
    \fi
}
%    \end{macrocode}
%  \end{macro}
%

\begin{document}

\setcounter{equation}{123456789}
aaa
\begin{flalign}
a+b+c+d&=111+222+333+444+555\\
aaaaaaa+bbbbbbb+cccccc+dddd+eeee&=b
\end{flalign}

bbb
\begin{flalign}
a+b+c+d&=111+222+333+444+5555\\
aaaaaaa+bbbbbbb+cccccc&=b
\end{flalign}
\end{document}

Produces

enter image description here

with (just) equation number 123456790 being moved.

The terminal and log have the subtle warning:

===========================
BEWARE: EQUATION TAG MOVING
BEWARE: 123456790
===========================
6
  • Thanks for this. I tried replacing the flaligns with a combination of split and equation (the vast majority of my problematic equations are of this form), but it didn't seem to work in that case. I will try to study the code to see if I can figure out to modify it, but I am sufficiently unfamiliar with this that it is not obvious to me how to do so. Aug 1, 2017 at 1:53
  • Unrelated to the problem, but reading your answer I noticed that amsmath’s code contains an imperfection: it uses \box1 as a temporary local box (indeed, the assignment \setbox\@ne\null is not \global); \box2, say, should be used instead!
    – GuM
    Aug 1, 2017 at 4:24
  • 2
    Asking to understand LaTeX philosophy: where is all this documented? Where should a typical user learn stuff like how to modify the formatting of the equation environment? Aug 1, 2017 at 6:15
  • 1
    @JonathanGleason you didn't mention equation in the original question:-) equation is a bit harder as it's essentially the TeX primitive $$ so there is no macro that moves the tag. You would have to do it by hand, measure the width of the split and the width of the tag and check if it fits Aug 1, 2017 at 8:57
  • 1
    @ShreevatsaR the comments in the code above are just the documented sources amsmath.dtx Aug 1, 2017 at 8:58
1

The solutions

align

First of all, the final solution I wound up going with (essentially due to David Carlisle) for the align environment is as follows.

\apptocmd{\place@tag}{%
    \if1\shift@tag\row@%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \fi 
}{}{}

equation

For the equation environment, I wound up going with the following solution (explanation below).

\tikzcdset{ampersand replacement=\&} % This makes it so that "\&", not "&", is treated as the column separator in tikzcd environments.  (We do this so that it these environments can be passed as arguments (via "\BODY") to our redefinition of the equation environment.)  (See https://tex.stackexchange.com/questions/15093/.)
\let\@ldtikzcd\tikzcd % So that we don't have to manually write "\&", we update the "tikzcd" environment so that it automatically replaces the "&"s with "\&"s.
\let\@ldtikzcd\tikzcd
\let\@ldendtikzcd\endtikzcd
\RenewEnviron{tikzcd}[1][]{%
    \xpatchcmd*{\BODY}{&}{\&}{}{}% For compatibility with tikz.  (Also see \tikzcdset{ampersand replacement=\&} above.)
    \@ldtikzcd[#1]%
    \BODY%
    \@ldendtikzcd%
}

\newbox\tempb@x
\newdimen\stest
\newdimen\smin
\newdimen\smax
\def\find#1#2#3{% Used for \getstretch and \getshrink below.  (From https://tex.stackexchange.com/questions/191140/measure-total-glue-in-a-box)
    \let\@ldhbadness\hbadness% Need to temporarily redefine \hbadness and \hfuzz so don't get a ridiculously number of warnings as we test for stretchability/shrinkability.
    \hbadness=1000000%
    \let\@ldhfuzz\hfuzz%
    \hfuzz=\maxdimen%
    \smin = -\maxdimen
    \smax = \maxdimen
    \loop
    \stest = \smin
    \advance \stest by \smax
    \divide \stest by 2 % now \stest=(\smin+\smax)/2 truncated to 0
    \ifdim \stest = \smax
    \advance \stest by -1sp % ensure that \smin<=\stest<\smax.
    \fi
    \setbox 0 = \hbox spread #2 1sp {%
        \unhcopy\tempb@x
        \hskip 0pt #3-\stest
    }%
    \ifnum \badness > 100 % (shrink/stretch in \tempb@x) <= \stest
    \smax = \stest
    \else
    \smin = \stest
    \advance \smin by 1sp
    % since ">\stest" implies ">=\stest+1sp"
    \fi
    % In both cases, the interval [\smin,\smax] becomes smaller.
    \ifdim\smin<\smax
    \repeat
    \ifdim\smin>\smax\BOOM\fi% cannot happen, I think
    \hfuzz=\@ldhfuzz%
    \hbadness=\@ldhbadness%
}
\def\getstretch#1{% Returns the amount of stretchable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{stretch}{}{plus}%
}
\def\getshrink#1{% Returns the amount of shrinkable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{shrink}{-}{minus}%    
}

% Modifies "equation" environment to test if equation number has been displaced.
\ifdef{\mathindent}{}{\newlength{\mathindent}}% In case \mathindent hasn't been defined, define it. (Will be used in redefinition of equation environment.)
% Modifies "equation" environment to test if equation number has been displaced.
\newlength{\widthofequ@tion}
\newlength{\widthofnumber}
\newlength{\tempshrink@bility}
\newlength{\halfqu@d}
\def\initializelengthsdispl@ced{%
    \setlength{\halfqu@d}{\widthof{$\displaystyle \hspace{.5em}$}}%
    \getshrink{\ensuremath{\BODY}}% Stores shrink in \smin.
    \setlength{\tempshrink@bility}{\smin}%
    \setlength{\widthofequ@tion}{\widthof{\ensuremath{\displaystyle\BODY}}}%
    \setlength{\widthofnumber}{\widthof{\ensuremath{(\displaystyle\theequation )}}}%
}

\def\istagdispl@ced{% The Lua code expands to 1 if the equation number will be moved, and 0 otherwise.
    \directlua{% See pg. 188 of the TeXBook for the notation and algorithm.
        w0 = \strip@pt\widthofequ@tion;%
        e = \strip@pt\widthofnumber;%
        halfquad = \strip@pt\halfqu@d;%
        q = e+halfquad;% Knuth says that this should be a quad, not a half quad, but that doesn't seem to work (half a quad seems to work perfectly though).
        z = \strip@pt\displaywidth;%
        shrinkability = \strip@pt\tempshrink@bility;%.
        %
        if w0-shrinkability+q > z then
        tex.print("1");
        else
        tex.print("0");
        end
    }%
}
\def\settagdispl@ced{%
    \initializelengthsdispl@ced%
    \edef\tagdispl@ced{\istagdispl@ced}%
}
\let\@ldequation\equation
\let\@ldendequation\endequation
\RenewEnviron{equation}{%
    \@ldequation% Start equation environment.
    %
    \BODY% Display actual equation.
    % Don't want to "label" twice, so we disable labeling here (will be "label"ed below when defining \@tempequationlength, and need the label to appear there because we want it to be measured when it comes to the legnth).
    \let\@ldlabel\label%
    \renewcommand{\label}[1]{}%
    \let\@ldfootnote\footnote% Similarly for footnotes.
    \renewcommand{\footnote}[1]{}%
    % Actually test if the equation number has been displaced, and if so, give a warning.
    \settagdispl@ced%
    \if1\tagdispl@ced%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \getshrink{\hspace{\mathindent}}%
    \hspace{\smin}% Sometimes there are cases where the equation number should be displaced, but instead the equation has been shifted into the left margin.  If this should be the case, we add the extra space to actually displace the equation number in order to avoid confusion.
    \fi%
    % Restore \footnote and \label commands.
    \let\footnote\@ldfootnote%
    \let\label\@ldlabel%
    %
    \@ldendequation% Close equation environment.
}

This was actually my second solution to the problem of obtaining similar behavior for the equation environment as David found for the align environment.

For both my first and second attempt, the basic idea was to measure the width of the equation and the equation number, and compare the sum of those widths with the \displaywidth. Naively, the first time around, this is all I took into account, and I found experimentally that comparing instead with .95572663\displaywidth produced better results. This worked okay, but if the font or margins were changed, or even just for certain equations, this produced the wrong result (the effective coefficient here only worked perfectly using the settings I first tested with). There were two reasons for this. (Incidentally, I figured this out by looking at TeX's algorithm for displaying equations as explained on pg. 188 of The TeXbook.)

The obvious thing I forgot to consider was the space between the equation and equation number---Knuth says this is one quad, but it only worked perfectly if I used half a quad instead of a quad. The not-as-obvious thing I forgot to consider was the shrinkability of the equation. TeX first tries to display the equation at it's natural width, and if that were to displace the equation number, it tries to shrink the equation enough to get the equation number to fit. Only if there is not enough shrinkability does TeX displace the equation number. Thus, what I really wanted to compare was

\widthof{EQUATION}+\widthof{NUMBER}+.5em-\shrinkability{EQUATION}

width \displaywidth.

To compute the shrinkability, I used the solution to this question. Ultimately, we make use of the macro \getshrink (defined above) which takes in one argument, puts it in an \hbox, and stores the shrinkability of that \hbox in the dimension register \smin.

Unfortunately, however, things were still not quite as simple as computing the length above, comparing it with \displaywidth, and then issuing a warning if necessary.

First of all, using \BODY to actually display the equation and \BODY to measure the size of the equation results in TeX complaining about double labeling, so I had to manually disable \label for the one that did the measuring. Similarly for footnotes that might appear in equations.

The third 'hack' needed was to account for equations which contained commutative diagrams. Evidently, tikz changes the catcode of & to an active character, which doesn't play nice with being passed to other macros. So I had to tell tikzcd not to do that (and instead use \& for that purpose); then I "patched" the tikzcd environment to automatically replace every & with a \&.

Comments

As written, this requires LuaLaTeX. I'm confident you can accomplish the same with just pdfLaTeX, but in this I found writing Lua code for the main part of the algorithm was easier.

I highly doubt something like this is a final solution to things. In my single document alone I ran into two obstacles I needed to fix, so I would imagine that this current solution would break in other scenarios I didn't myself test.

Furthermore, I'm not particularly experienced with this sort of thing, and so there are probably ways my solution can be improved. If you see any such way, I would be happy to hear it.

The complete minimal working example

The final MWE is given below (together with the output and relevant lines from the .log file).

Log:

Package custom Warning: Equation number (2) has been displaced on input line 151.
Package custom Warning: Equation number (3) has been displaced on input line 157.
Overfull \hbox (5.49112pt too wide) in paragraph at lines 157--157
Package custom Warning: Equation number (123456790) has been displaced on input line 164.
Package custom Warning: Equation number (123456791) has been displaced on input line 164.
Overfull \hbox (92.71948pt too wide) in paragraph at lines 164--164
Package custom Warning: Equation number (123456792) has been displaced on input line 170.
Overfull \hbox (33.8307pt too wide) in paragraph at lines 170--170

Output: Output

Code:

\makeatletter

\documentclass{article}

\usepackage{etoolbox}
\usepackage{regexpatch}
\usepackage{xparse}
\usepackage{calc}
\usepackage{environ}
\usepackage[paperwidth=5in,left=.9375in,right=.9375in]{geometry}
\usepackage{mathtools}
\usepackage{tikz}
\usetikzlibrary{cd}
\tikzcdset{ampersand replacement=\&} % This makes it so that "\&", not "&", is treated as the column separator in tikzcd environments.  (We do this so that it these environments can be passed as arguments (via "\BODY") to our redefinition of the equation environment.)  (See https://tex.stackexchange.com/questions/15093/.)
\let\@ldtikzcd\tikzcd % So that we don't have to manually write "\&", we update the "tikzcd" environment so that it automatically replaces the "&"s with "\&"s.
\let\@ldtikzcd\tikzcd
\let\@ldendtikzcd\endtikzcd
\RenewEnviron{tikzcd}[1][]{%
    \xpatchcmd*{\BODY}{&}{\&}{}{}% For compatibility with tikz.  (Also see \tikzcdset{ampersand replacement=\&} above.)
    \@ldtikzcd[#1]%
    \BODY%
    \@ldendtikzcd%
}

\newbox\tempb@x
\newdimen\stest
\newdimen\smin
\newdimen\smax
\def\find#1#2#3{% Used for \getstretch and \getshrink below.  (From https://tex.stackexchange.com/questions/191140/measure-total-glue-in-a-box)
    \let\@ldhbadness\hbadness% Need to temporarily redefine \hbadness and \hfuzz so don't get a ridiculously number of warnings as we test for stretchability/shrinkability.
    \hbadness=1000000%
    \let\@ldhfuzz\hfuzz%
    \hfuzz=\maxdimen%
    \smin = -\maxdimen
    \smax = \maxdimen
    \loop
    \stest = \smin
    \advance \stest by \smax
    \divide \stest by 2 % now \stest=(\smin+\smax)/2 truncated to 0
    \ifdim \stest = \smax
    \advance \stest by -1sp % ensure that \smin<=\stest<\smax.
    \fi
    \setbox 0 = \hbox spread #2 1sp {%
        \unhcopy\tempb@x
        \hskip 0pt #3-\stest
    }%
    \ifnum \badness > 100 % (shrink/stretch in \tempb@x) <= \stest
    \smax = \stest
    \else
    \smin = \stest
    \advance \smin by 1sp
    % since ">\stest" implies ">=\stest+1sp"
    \fi
    % In both cases, the interval [\smin,\smax] becomes smaller.
    \ifdim\smin<\smax
    \repeat
    \ifdim\smin>\smax\BOOM\fi% cannot happen, I think
    \hfuzz=\@ldhfuzz%
    \hbadness=\@ldhbadness%
}
\def\getstretch#1{% Returns the amount of stretchable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{stretch}{}{plus}%
}
\def\getshrink#1{% Returns the amount of shrinkable glue in the \smin register.
    \setbox\tempb@x\hbox{#1}%
    \find{shrink}{-}{minus}%    
}

% Modifies "equation" environment to test if equation number has been displaced.
\ifdef{\mathindent}{}{\newlength{\mathindent}}% In case \mathindent hasn't been defined, define it. (Will be used in redefinition of equation environment.)
% Modifies "equation" environment to test if equation number has been displaced.
\newlength{\widthofequ@tion}
\newlength{\widthofnumber}
\newlength{\tempshrink@bility}
\newlength{\halfqu@d}
\def\initializelengthsdispl@ced{%
    \setlength{\halfqu@d}{\widthof{$\displaystyle \hspace{.5em}$}}%
    \getshrink{\ensuremath{\BODY}}% Stores shrink in \smin.
    \setlength{\tempshrink@bility}{\smin}%
    \setlength{\widthofequ@tion}{\widthof{\ensuremath{\displaystyle\BODY}}}%
    \setlength{\widthofnumber}{\widthof{\ensuremath{(\displaystyle\theequation )}}}%
}

\def\istagdispl@ced{% The Lua code expands to 1 if the equation number will be moved, and 0 otherwise.
    \directlua{% See pg. 188 of the TeXBook for the notation and algorithm.
        w0 = \strip@pt\widthofequ@tion;%
        e = \strip@pt\widthofnumber;%
        halfquad = \strip@pt\halfqu@d;%
        q = e+halfquad;% Knuth says that this should be a quad, not a half quad, but that doesn't seem to work (half a quad seems to work perfectly though).
        z = \strip@pt\displaywidth;%
        shrinkability = \strip@pt\tempshrink@bility;%.
        %
        if w0-shrinkability+q > z then
        tex.print("1");
        else
        tex.print("0");
        end
    }%
}
\def\settagdispl@ced{%
    \initializelengthsdispl@ced%
    \edef\tagdispl@ced{\istagdispl@ced}%
}
\let\@ldequation\equation
\let\@ldendequation\endequation
\RenewEnviron{equation}{%
    \@ldequation% Start equation environment.
    %
    \BODY% Display actual equation
    % Don't want to "label" twice, so we disable labeling here (will be "label"ed below when defining \@tempequationlength, and need the label to appear there because we want it to be measured when it comes to the legnth).
    \let\@ldlabel\label%
    \renewcommand{\label}[1]{}%
    \let\@ldfootnote\footnote% Similarly for footnotes.
    \renewcommand{\footnote}[1]{}%
    % Actually test if the equation number has been displaced, and if so, give a warning.
    \settagdispl@ced%
    \if1\tagdispl@ced%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \getshrink{\hspace{\mathindent}}%
    \hspace{\smin}% Sometimes there are cases where the equation number should be displaced, but instead the equation has been shifted into the left margin.  If this should be the case, we add the extra space to actually displace the equation number in order to avoid confusion.
    \fi%
    % Restore \footnote and \label commands.
    \let\footnote\@ldfootnote%
    \let\label\@ldlabel%
    %
    \@ldendequation% Close equation environment.
}

% Something simliar as above but for "align" environments and related.  (See https://tex.stackexchange.com/questions/384222/.)
\apptocmd{\place@tag}{%
    \if1\shift@tag\row@%
    \PackageWarning{custom}{Equation number (\theequation ) has been displaced}%
    \fi 
}{}{}

\begin{document}

    \begin{equation}
    \begin{split}
    z & =a+b+c+d+e+f+g+h+i+j+k \\
    & +l+m+n+o+p+q+r+s+t+u+v % Does not throw a warning
    \end{split}
    \end{equation}

    \begin{equation}
    \begin{split}
    z & =a+b+c+d+e+f+g+h+i+j+k+l \\
    & +m+n+o+p+q+r+s+t+u+v % Should throw a warning
    \end{split}
    \end{equation}

    \begin{equation}
    \begin{split}
    z & =a+b+c+d+e+f+g+h+i+j+k+l+m\\&+n+o+p+q+r+s+t+u+v+w % Overfull hbox
    \end{split}
    \end{equation}

    \setcounter{equation}{123456789}
    aaa
    \begin{flalign}
    a+b+c+d&=111+222+333+444+555\\
    aaaaaaa+bbbbbbb+cccccc+dddd+eeee&=b
    \end{flalign}

    bbb
    \begin{flalign}
    a+b+c+d&=111+222+333+444+5555\\
    aaaaaaa+bbbbbbb+cccccc&=b
    \end{flalign}

\end{document}

\makeatother

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .