2

This post is born from a very practical reason: I find that in many documents that contain a lot of mathematics there is of course a lot of cross-referencing, but sometimes it is difficult to even remember what was said in that thing that is referenced. For example: if you have a proof and you say "and then from Lemma 7.12 we have that f has a max in (a,b)", but you as a reader don't even remember what Lemma 7.12 said because you are in chapter 16 now and this lemma was 200 pages ago.

So my wish is to redefine the environment used for inserting theorems/lemma/prop and so on in such a way that if you want you can store what is typed between \begin{theorem} ... \end{theorem} and then you can use a command that uses \marginpar and the label with which you stored that theorem to later if needed print in the margin where you cited that theorem what it actually said. How difficult will it be?

2
  • 1
    Welcome to TeX SX! I think this would require very wide margins! Why not simply use hyperlinks, which will let you back and forth?
    – Bernard
    Jul 22, 2018 at 9:56
  • One could provide a short description which gets into the margin (still requires some space).
    – Skillmon
    Jul 22, 2018 at 11:46

2 Answers 2

3

You could use the restatable environment defined by the thmtools package to create a theorem that can be restated. This works as follows:

  • You should add \usepackage{thmtools} to your preamble.
  • Instead of using \begin{theorem}..\end{theorem} you need to state the theorem as

    \begin{restatable}[<name>]{theorem}{<macro name>}<thm contents>\end{restatable}

  • You can then put a copy of the theorem in your document with \<macro name>* (the last argument of the restatable environment, preceded by a \ and followed by a *), but this can only be done after the theorem is stated because the aux file is not used. You can't copy theorems that appear later in the document.

See section 1.4 of the documentation for more information.


This seemed a little unsatisfactory, so I've written some code that should make this work precisely as you describe. This still uses the restate mechanism from thmtools, but it stores the theorem contents in the aux file instead of in a macro, and you can use the label name assigned to a theorem to restate it.

Here's how it works:

  • You need to add a large block of code to your preamble (the "Definitions" bit below)
  • If a theorem has a label, say \label{thm:mythm}, you can now restate it with \restate{thm:mythm}. To put it in the margin you can use \restateinmargin{thm:mythm}, and if you want to \ref to the theorem and put a copy in the margin you can use \restateref{thm:mythm}.
  • You need to run LaTeX at least THREE TIMES.

Here's the code:

\documentclass{article}

%%---------------------%%
%%     Definitions     %%
%%---------------------%%

\usepackage{amsthm} %% <- also works without amsthm, or with ntheorem
\usepackage{thmtools}

\makeatletter                 %% <- change @ so that it can be used in command names
  \newif\ifrthm@restating     %% <- changes behaviour of theorem environment when we restate
  \newcounter{rthm@ctr}       %% <- generates unique ids for theorems
  \addtotheorempostheadhook{% %% <- executed at \begin{theorem}
    \ifrthm@restating\else    %% <- if this is the actual theorem
      \thmt@thisistheonetrue  %% <- tell thm-restate/thmtools this is not a copy
      \thmt@restatethistrue   %% <- pretend a restate=... key+val was passed
      \stepcounter{rthm@ctr}% %% <- unique id
      \ifdefined\rthm@originallabel\else %% <- test if \label was already redefined, to be safe
        \let\rthm@originallabel\label
        \renewcommand*\label[2][]{% %% <- %% Optional argument is just there for cleveref
          \protected@edef\rthm@lastlabelname{#2}%
          \if\relax\detokenize{#1}\relax\@xa\@firstoftwo\else\@xa\@secondoftwo\fi
            {\rthm@originallabel{#2}}{\rthm@originallabel[#1]{#2}}%
        }                     %% <- redefine label to extract its name
      % \let\rthm@lastlabelname\relax %% <- unnecessary because rthm@restating contains a \label
      \fi
      \protected@edef\tmp@a{% %% <- perform some expansions first
        \@nx\@xa\@nx\thmt@restatable\@nx\@xa[\@nx\thmt@optarg]%
          {\thmt@envname}{restate\arabic{rthm@ctr}}%
      }%
      \@xa\tmp@a              %% <- now call thmt@restatable from thm-restate/thmtools
    \fi
  }

  %% Write theorem contents and counter values to the aux file
  \addtotheoremprefoothook{% %% <- executed at \end{theorem}
    \ifrthm@restating\else   %% <- if this is the actual theorem
      \@for\rthm@temp:=\rthm@labellist\do{% %% <- loop over requested theorems
        \ifx\rthm@lastlabelname\rthm@temp   %% <- if this is one of them
          \immediate\write\@auxout{%        %% <- write it to the aux file
            \global\string\@namedef{rthmbody@\rthm@lastlabelname}%
              {\@xa\@xa\@xa\unexpanded\@xa\@xa\@xa
                {\csname thmt@stored@restate\arabic{rthm@ctr}\endcsname}}%
          }%
          \let\rthm@lastlabelname\relax     %% <- don't do this more than once
        \fi
      }%
    \fi
  }

  %% This restates a theorem
  \def\rthm@newlabellist{}
  \def\rthm@labellist{}
  \AtEndDocument{\immediate\write\@auxout{\global\def\string\rthm@labellist{\rthm@newlabellist}}}%
  \newcommand\restate[1]{\ifrthm@restating\else\begingroup
      \rthm@restatingtrue     %% <- we are restating
      \thmt@thisistheonefalse %% <- tell thm-restate/thmtools this is a copy
      \@nameuse{rthmbody@#1}% %% <- restate the theorem using macro from aux file
      \xdef\rthm@newlabellist{\rthm@newlabellist,{#1}}
    \endgroup\fi}
  %% Same thing, but in the margin
  \newcommand\restateinmargin[1]{%
    \ifrthm@restating\else
      \marginpar{\small\restate{#1}}%
    \fi
  }
  %% Combine \ref and \restateinmargin
  \newcommand\restateref{\@ifstar\restaterefstar\restaterefnostar}
  \newcommand\restaterefstar[1]{\ref*{#1}\restateinmargin{#1}}
  \newcommand\restaterefnostar[1]{\ref{#1}\restateinmargin{#1}}
\makeatother %% <- change @ back

%%---------------------%%
%%    /Definitions     %%
%%---------------------%%

\usepackage{hyperref} %% <- optional
\usepackage[hmargin={3cm,6cm},marginparwidth=4cm]{geometry} %% <- just for the margins
\usepackage{amsmath} %% <- for \numberwithin, \lvert and \rvert
\numberwithin{equation}{section}

\newtheorem{theorem}{Theorem}[section]

\begin{document}

\section{First section}

\noindent Theorem~\restateref{thm:paradox} and Theorem~\restateref{thm:isitthough?} are problematic.

\begin{theorem}[Burnside's lemma] \label{thm:burnside}
    Let $G$ be a finite group that acts on a finite set $X$, then
    \begin{equation}
        \lvert X / G \rvert = \frac1{\lvert G\rvert} \sum_{g \in G} \lvert X^g \rvert,
    \end{equation}
    where $X^g$ denotes the set of points of $X$ that are fixed by $g$.
\end{theorem}

\section{Second section}

\begin{theorem} \label{thm:isitthough?}
    This statement is true
\end{theorem}

\begin{theorem} \label{thm:paradox}
    This theorem is false.
\end{theorem}

\noindent Theorem~\restateref{thm:burnside} is pretty nice.

\end{document}

Output

Under the hood, I'm just invoking several commands from the thm-restate package (which loads and is loaded by thmtools) and writing the result to the aux file. The reason you need to run LaTeX three times is that only those theorems that are actually \restated are written to the aux file: during the first run we collect of theorems that are restated and during the second run these are written to the aux file so that they are available for restating during the third run.

Edit: answer has been significantly improved (several times):

  • It now loads the thmtools package instead of "stealing" code from it
  • it is now possible to use \restate(ref) from within a theorem environment (and restating a theorem that contains a \restate does not cause a loop),
  • only those theorems that you have \restated are written to the aux file.

Edit: to suppress equation numbers when theorems are restated in the margin, use

\newcommand\restateinmargin[1]{%
  \ifrthm@restating\else
    \marginpar{\everydisplay\expandafter{\the\everydisplay\nonumber}\small\restate{#1}}%
  \fi
}

instead of the definition for \restateinmargin provided above. (Requires amsmath.)

16
  • This is unbelievable! Thank you very much!!! Jul 24, 2018 at 20:10
  • You're welcome. If my answer has helped you, you may want to accepte and/or upvote it. Jul 25, 2018 at 11:09
  • I have one more difficulty: since the file I'm working with I ran out of the main_memory size allowed, do you think there could be a way to avoid this? (For example, write not on the .aux file but from another file?) Thanks Jul 26, 2018 at 19:14
  • If it says TeX capacity exceeded, sorry [main memory size=...] that either means your project is very large or that this is causing an infinite loop of some sort somewhere. The latter option is certainly possible if something in your code interferes with mine somehow. Jul 26, 2018 at 19:59
  • Could you try running lualatex instead of pdflatex on your document, just to see if this does work? (LuaLaTeX can access more memory.) Jul 26, 2018 at 20:06
1

The following is a short implementation of the suggestion from my comment:

I wrote a macro called \NewTheorem that is a wrapper around \newtheorem, expecting the same arguments as \newtheorem. It creates an environment which is a wrapper for the environment created by \newtheorem. This wrapper environment accepts an optional argument (as the environment created by \newtheorem does) and requires two additional arguments. The first additional argument is the label, the second a short description.

You can then reference the theorem with \TheoremRef which as an optional argument accepts the referencing macro (by default \ref) and requires the label of the theorem. It places the reference in text and (if the theorem is known) creates a \marginpar containing the short description of the theorem.

It requires two passes to work correctly and writes information to the aux file.

\documentclass[twoside]{article}

\usepackage[]{amsmath}
\usepackage{amsthm}

\newtheorem{theorem}{Theorem}

\makeatletter
\newcommand\NewTheorem[2]
  {%
    \newtheorem{#1@core}{#2}%
    \newenvironment{#1}[3][]
      {%
        \begin{#1@core}[##1]%
          \label{##2}%
          \protected@write \@auxout {}
            {%
              \string\expandafter\gdef
              \string\csname\space ##2@Theorem@ShortDescription\endcsname
                {##3}%
            }%
          \protected@write \@auxout {}
            {%
              \string\expandafter\gdef
              \string\csname\space ##2@Theorem@Type\endcsname{#2}%
            }
          \if\relax\detokenize{##1}\relax
          \else
            \protected@write \@auxout {}
              {%
                \string\expandafter\gdef
                \string\csname\space ##2@Theorem@Name\endcsname{##1}%
              }%
          \fi
      }
      {%
        \end{#1@core}%
      }%
  }
\newcommand*\TheoremRef[2][\ref]
  {%
    #1{#2}%
    \ifcsname #2@Theorem@ShortDescription\endcsname
      \marginpar
        {%
          \csname #2@Theorem@Type\endcsname\space\ref{#2}%
          \ifcsname #2@Theorem@Name\endcsname
            \space(\csname #2@Theorem@Name\endcsname)%
          \fi
          :\\
          \csname #2@Theorem@ShortDescription\endcsname
        }%
    \fi
  }
\makeatother
\NewTheorem{lem}{Lemma}

\begin{document}
\begin{lem}[foo]{lem:test}{The testing lemma}
  This lemma is just for testing purposes, to see whether everything works
\end{lem}
As shown by lemma \TheoremRef{lem:test} you can see that this works.
\end{document}

Output:

enter image description here

You must log in to answer this question.

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