latex3 / latex2e

The LaTeX2e kernel
https://www.latex-project.org/
LaTeX Project Public License v1.3c
1.88k stars 261 forks source link

amsmath: equation numbering enhancement #231

Open nbeisert opened 4 years ago

nbeisert commented 4 years ago

Brief outline of the bug

The align environment for equation blocks in amsmath (and similar environments) number all lines with equation numbers. The numbering can be suppressed for individual lines (\nonumber, \notag) or for all lines (align*). However, it is tedious and error-prone to just number few, selected lines. For instance, it may be desirable to only assign a number to the first or the last (or a user-selected) equation within each block, and to do that consistently throughout the document. While certainly possible with existing commands, it requires many \nonumber insertions at the right places.

I would like to suggest an extension to the equation numbering mechanism in amsmath to:

The below example contains suggested patches for amsmath to implement the suggested extensions.

Minimal example showing the bug

\RequirePackage{latexbug}
\documentclass{article}

%\PassOptionsToPackage{leqno}{amsmath}
%\PassOptionsToPackage{fleqn}{amsmath}
\RequirePackage{amsmath}

% temporary setup
%\def\scheme{0} % number all lines (default)
%\def\scheme{1} % no numbering
%\def\scheme{2} % number first line
\def\scheme{3} % number last line

\usepackage{etoolbox}
\makeatletter

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% mechanism to activate equation numbers:

% \ifunst@rred: flag to activate numbering for a line
\newif\ifunst@rred
% \donumber: command to activate numbering for a line
\newcommand{\donumber}{\global\unst@rredtrue}
% \numberlabel: put a number and declare label in one command
% (suggested optional definition)
\newcommand{\numberlabel}{\donumber\label}

% gather: 
% reset \ifunst@rred
\patchcmd{\start@gather}{%
        #1%
        \ifst@rred \else \global\@eqnswtrue \fi
}{%
        #1%
        \unst@rredfalse
        \ifst@rred \else \global\@eqnswtrue \fi
}{\typeout{p01okay}}{\typeout{p01fail!}}

% align: 
% reset \ifunst@rred
\patchcmd{\align@}{%
    #1% set st@r
    \ifst@rred\else \global\@eqnswtrue \fi
}{%
    #1% set st@r
    \unst@rredfalse
    \ifst@rred\else \global\@eqnswtrue \fi
}{\typeout{p06okay}}{\typeout{p06fail!}}

% multline:
% do not invoke disruptive \nonumber too early
% reset \ifunst@rred
\patchcmd{\start@multline}{%
        \ifst@rred
            \nonumber
        \else
            \global\@eqnswtrue
        \fi
}{%
        \global\@eqnswtrue
        \unst@rredfalse
}{\typeout{p18okay}}{\typeout{p18fail!}}

% endmultline:
% suppress number if needed
\pretocmd{\endmultline}{%
  \ifst@rred\ifunst@rred\else
    \nonumber
  \fi\fi
}{\typeout{p19okay}}{\typeout{p19fail!}}

% equation*:
% reset \ifunst@rred
\expandafter\patchcmd\expandafter{\csname equation*\endcsname}{%
  \st@rredtrue \global\@eqnswfalse
}{%
  \st@rredtrue \global\@eqnswfalse
  \unst@rredfalse
}{\typeout{p10okay}}{\typeout{p10fail!}}

% endequation*:
% activate number if needed
\expandafter\pretocmd\expandafter{\csname endequation*\endcsname}{%
  \ifunst@rred
    \incr@eqnum
    \global\@eqnswtrue
  \fi
}{\typeout{p11okay}}{\typeout{p11fail!}}

% next line in gather:
% turn on equation if unst@rred
% reset \ifunst@rred
\patchcmd{\math@cr@@@gather}{%
    \ifst@rred\nonumber\fi
}{%
  \ifunst@rred
    \global\@eqnswtrue
    \global\unst@rredfalse
  \else
    \ifst@rred\nonumber\fi
  \fi
}{\typeout{p04okay}}{\typeout{p04fail!}}

% next line in align:
% turn on equation if unst@rred
% reset \ifunst@rred
\patchcmd{\math@cr@@@align}{%
  \ifst@rred\nonumber\fi
}{%
  \ifunst@rred
    \global\@eqnswtrue
    \global\unst@rredfalse
  \else
    \ifst@rred\nonumber\fi
  \fi
}{\typeout{p08okay}}{\typeout{p08fail!}}

% next line in measure:
% turn on equation if unst@rred
% reset \ifunst@rred
\patchcmd{\math@cr@@@align@measure}{%
    \ifst@rred\nonumber\fi
}{%
    \ifunst@rred
        \global\@eqnswtrue
        \global\unst@rredfalse
    \else
        \ifst@rred\nonumber\fi
    \fi
}{\typeout{p09okay}}{\typeout{p09fail!}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% install alternative numbering schemes:

% \ifunst@rone: flag to activate a numbering on a single line
\newif\ifunst@rone
% \eqnumbering@: selected numbering scheme
\let\eqnumbering@\z@
% \numberhere: command to select present line to display number for block
\newcommand{\numberhere}{\donumber\global\unst@ronefalse}

% gather:
% reset \unst@ronefalse
% select scheme
\patchcmd{\start@gather}{%
        \unst@rredfalse
        \ifst@rred \else \global\@eqnswtrue \fi
}{%
        \unst@rredfalse
        \unst@ronefalse
        \ifst@rred\else\ifcase\eqnumbering@
        \or
            \st@rredtrue
        \or
            \st@rredtrue
            \unst@ronetrue
            \unst@rredtrue
        \or
            \st@rredtrue
            \unst@ronetrue
        \fi\fi
        \ifst@rred \else \global\@eqnswtrue \fi
}{\typeout{p02okay}}{\typeout{p02fail!}}

% align:
% reset \unst@ronefalse
% select scheme
\patchcmd{\align@}{%
    \unst@rredfalse
    \ifst@rred\else \global\@eqnswtrue \fi
}{%
    \unst@rredfalse
    \unst@ronefalse
    \ifst@rred\else\ifcase\eqnumbering@
    \or
        \st@rredtrue
    \or
        \st@rredtrue
        \unst@ronetrue
        \unst@rredtrue
    \or
        \st@rredtrue
        \unst@ronetrue
    \fi\fi
    \ifst@rred\else \global\@eqnswtrue \fi
}{\typeout{p07okay}}{\typeout{p07fail!}}

% align:
% after \measure, select first line for numbering again;
% if a different line had been chosen by \numberhere,
% \ifunst@rone will be inactive
\patchcmd{\align@}{%
    \measure@{#2}%
}{%
    \measure@{#2}%
    \ifunst@rone\ifnum\eqnumbering@=\tw@
        \unst@rredtrue
    \fi\fi
}{\typeout{p13okay}}{\typeout{p13fail!}}

% gather:
% after \gmeasure, select first line for numbering again;
% if a different line had been chosen by \numberhere,
% \ifunst@rone will be inactive
\patchcmd{\gather@}{%
    \gmeasure@{#1}%
}{%
    \gmeasure@{#1}%
    \ifunst@rone\ifnum\eqnumbering@=\tw@
        \unst@rredtrue
    \fi\fi
}{\typeout{p15okay}}{\typeout{p15fail!}}

% align \measure:
% select last line for numbering;
% if a different line had been chosen by \numberhere,
% \ifunst@rone will be inactive
\patchcmd{\measure@}{%
                #1%
}{%
                #1%
                \ifunst@rone\ifnum\eqnumbering@=\thr@@
                  \donumber
                \fi\fi
}{\typeout{p14okay}}{\typeout{p14fail!}}

% gather \gmeasure:
% select last line for numbering;
% if a different line had been chosen by \numberhere,
% \ifunst@rone will be inactive
\patchcmd{\gmeasure@}{%
                #1%
}{%
                #1%
                \ifunst@rone\ifnum\eqnumbering@=\thr@@
                  \donumber
                \fi\fi
}{\typeout{p21okay}}{\typeout{p21fail!}}

% equation:
% postpone increase counter
\patchcmd{\equation}{%
  \incr@eqnum
}{%
}{\typeout{p16okay}}{\typeout{p16fail!}}

% equation:
% suppress numbering in scheme 1
% switch numbering
\patchcmd{\equation}{%
  \st@rredfalse \global\@eqnswtrue
}{%
  \st@rredfalse
  \unst@rredfalse
  \ifnum\eqnumbering@=\@ne
    \st@rredtrue
  \fi
  \ifst@rred
    \global\@eqnswfalse
  \else 
    \incr@eqnum 
    \global\@eqnswtrue 
  \fi
}{\typeout{p12okay}}{\typeout{p12fail!}}

% endequation:
% activate number if needed
\pretocmd{\endequation}{%
  \ifst@rred\ifunst@rred
    \incr@eqnum
    \global\@eqnswtrue
  \fi\fi
}{\typeout{p17okay}}{\typeout{p17fail!}}

% endgather:
% select last line for numbering;
% if a different line had been chosen by \numberhere,
% \ifunst@rone will be inactive
\pretocmd{\endgather}{%
  \ifunst@rone\ifnum\eqnumbering@=\thr@@
    \donumber
  \fi\fi
}{\typeout{p03okay}}{\typeout{p03fail!}}

% endalign:
% select last line for numbering;
% if a different line had been chosen by \numberhere,
% \ifunst@rone will be inactive
\pretocmd{\endalign}{%
  \ifunst@rone\ifnum\eqnumbering@=\thr@@
    \donumber
  \fi\fi
}{\typeout{p05okay}}{\typeout{p05fail!}}

% multline:
% suppress numbering in scheme 1
\patchcmd{\start@multline}{%
        \unst@rredfalse
}{%
        \ifnum\eqnumbering@=\@ne
            \st@rredtrue
        \fi
        \unst@rredfalse
}{\typeout{p20okay}}{\typeout{p20fail!}}

\makeatother

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% implement temporary setup
\makeatletter
\ifdefined\scheme\let\eqnumbering@\scheme\fi
\providecommand{\numberhere}{\donumber}
\makeatother

\renewcommand{\minalignsep}{1em}

\begin{document}

\section*{unstarred environments}\noindent

equation
\begin{equation}
a^2+b^2=c^2
\end{equation}

multline
\begin{multline}
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\end{multline}

gather
\begin{gather}
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\end{gather}

align
\begin{align}
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\end{align}

gather with donumber (line 2) and numberhere (line 3)
\begin{gather}
a^2+b^2=c^2
\\
\donumber
a^2+b^2=c^2
\\
\numberhere
a^2+b^2=c^2
\\
a^2+b^2=c^2
\end{gather}

align with donumber (line 2) and numberhere (line 3)
\begin{align}
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
\donumber
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
\numberhere
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\end{align}

\newpage
\section*{starred environments}\noindent

equation*
\begin{equation*}
a^2+b^2=c^2
\end{equation*}

equation* with donumber
\begin{equation*}
\donumber
a^2+b^2=c^2
\end{equation*}

multline*
\begin{multline*}
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\end{multline*}

multline* with donumber
\begin{multline*}
a^2+b^2=c^2
\\
\donumber
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\end{multline*}

align* with donumber (line 2)
\begin{align*}
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\donumber
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\\
a^2+b^2&=c^2 & a^2+b^2&=c^2
\end{align*}

gather* with donumber (line 2)
\begin{gather*}
a^2+b^2=c^2
\\
\donumber
a^2+b^2=c^2
\\
a^2+b^2=c^2
\\
a^2+b^2=c^2
\end{gather*}

\end{document}

Some remarks

PDF and log files

amsmathnum.log amsmathnum.pdf

wspr commented 4 years ago

Just a quick comment: if you only want to number one equation in an align block, might one alternative idea here be to use the following instead?

\begin{align}\begin{split}…\end{split}\end{align}

In that case the additional options could be added to the split environment to control where its number is placed (first/last/middle, etc.).

nbeisert commented 4 years ago

@wspr: thanks for the suggestion, but that one does not break across pages on \allowdisplaybreaks (by design).

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity.