LaTeX \newcommand recursion gets very slow
Let's see what happens when you do \gcdab{377}{233}
. The first expansion becomes
\ifnum233=0 \else\gcdab{233}{\modab{#1}{#2}}\fi
The conditional is false, so you get
\gcdab{233}{\modab{377}{233}}\fi
that becomes
\ifnum\modab{377}{233}=0 0 \else\gcdab{\modab{377}{233}}{\modab{233}{\modab{377}{233}}}\fi\fi
and so on, always carrying over the still uncomputed numbers.
Just by way of example, I tried \gcdab{13}{8}
with \tracingmacros=1
; I report here just the lines about expansions of \gcdab
, which confirm what I claimed above. (Note: I removed the \relax
after 0
in the \ifnum
lines: it's not needed so long as you leave a blank space, or endline, after 0
; it's actually bad programming style to add it, because it leaves a bunch of unwanted \relax
tokens in the input stream).
\gcdab #1#2->\ifnum #2 = 0 #1\else \gcdab {#2}{\modab {#1}{#2}}\fi
#1<-13
#2<-8
\gcdab #1#2->\ifnum #2 = 0 #1\else \gcdab {#2}{\modab {#1}{#2}}\fi
#1<-8
#2<-\modab {13}{8}
\gcdab #1#2->\ifnum #2 = 0 #1\else \gcdab {#2}{\modab {#1}{#2}}\fi
#1<-\modab {13}{8}
#2<-\modab {8}{\modab {13}{8}}
\gcdab #1#2->\ifnum #2 = 0 #1\else \gcdab {#2}{\modab {#1}{#2}}\fi
#1<-\modab {8}{\modab {13}{8}}
#2<-\modab {\modab {13}{8}}{\modab {8}{\modab {13}{8}}}
\gcdab #1#2->\ifnum #2 = 0 #1\else \gcdab {#2}{\modab {#1}{#2}}\fi
#1<-\modab {\modab {13}{8}}{\modab {8}{\modab {13}{8}}}
#2<-\modab {\modab {8}{\modab {13}{8}}}{\modab {\modab {13}{8}}{\modab {8}{\modab {13}{8}}}}
\gcdab #1#2->\ifnum #2 = 0 #1\else \gcdab {#2}{\modab {#1}{#2}}\fi
#1<-\modab {\modab {8}{\modab {13}{8}}}{\modab {\modab {13}{8}}{\modab {8}{\modab {13}{8}}}}
#2<-\modab {\modab {\modab {13}{8}}{\modab {8}{\modab {13}{8}}}}{\modab {\modab {8}{\modab {13}{8}}}{\modab {\modab {13}{8}}{\modab {8}{\modab {13}{8}}}}}
Here's a version that always expands until getting an explicit number. And is fully expandable, contrary to David's.
\documentclass{article}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{etoolbox}
\makeatletter
\newcommand*{\programmerdiv}[2]{%
\ifnum #1 = 0
\expandafter\@secondoftwo
\else
\expandafter\expandafter\expandafter\@firstoftwo
\fi
{\the\numexpr (2*#1 - #2) / (2 * #2) \relax}%
{0}%
}
\newcommand*{\modab}[2]{%
\the\numexpr #1 - \programmerdiv{#1}{#2} * #2 \relax
}
\newcommand*{\gcdab}[2]{%
\ifnum #2 = 0
\expandafter\@secondoftwo
\else
\expandafter\expandafter\expandafter\@firstoftwo
\fi
{\expanded{\noexpand\gcdab{#2}{\modab{#1}{#2}}}}%
{#1}%
}
\makeatother
\begin{document}
\gcdab{37745585}{55555555}
\end{document}
I tried doing the computation 10000 times and the program ran for 1 second on my machine.
Here's the analogous report in the log file for \gcdab{13}{8}
with \tracingmacros=1
\gcdab #1#2->\ifnum #2 = 0 \expandafter \@secondoftwo \else \expandafter \expandafter \expandafter \@firstoftwo \fi {\expanded {\noexpand \gcdab {#2}{\modab {#1}{#2}}}}{#1}
#1<-13
#2<-8
\gcdab #1#2->\ifnum #2 = 0 \expandafter \@secondoftwo \else \expandafter \expandafter \expandafter \@firstoftwo \fi {\expanded {\noexpand \gcdab {#2}{\modab {#1}{#2}}}}{#1}
#1<-8
#2<-5
\gcdab #1#2->\ifnum #2 = 0 \expandafter \@secondoftwo \else \expandafter \expandafter \expandafter \@firstoftwo \fi {\expanded {\noexpand \gcdab {#2}{\modab {#1}{#2}}}}{#1}
#1<-5
#2<-3
\gcdab #1#2->\ifnum #2 = 0 \expandafter \@secondoftwo \else \expandafter \expandafter \expandafter \@firstoftwo \fi {\expanded {\noexpand \gcdab {#2}{\modab {#1}{#2}}}}{#1}
#1<-3
#2<-2
\gcdab #1#2->\ifnum #2 = 0 \expandafter \@secondoftwo \else \expandafter \expandafter \expandafter \@firstoftwo \fi {\expanded {\noexpand \gcdab {#2}{\modab {#1}{#2}}}}{#1}
#1<-2
#2<-1
\gcdab #1#2->\ifnum #2 = 0 \expandafter \@secondoftwo \else \expandafter \expandafter \expandafter \@firstoftwo \fi {\expanded {\noexpand \gcdab {#2}{\modab {#1}{#2}}}}{#1}
#1<-1
#2<-0
The mandatory expl3
implementation:
\documentclass{article}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\gcdab}{mm}
{
\dkozl_gcdab:nn { #1 } { #2 }
}
\cs_new:Nn \dkozl_gcdab:nn
{
\int_compare:nTF { #2 = 0 }
{ #1 }
{ \dkozl_gcdab:ne { #2 } { \int_mod:nn { #1 } { #2 } } }
}
\cs_generate_variant:Nn \dkozl_gcdab:nn { ne }
\ExplSyntaxOff
\begin{document}
\gcdab{37745585}{55555555}
\end{document}
In the first version you want to evaluate \modab
earlier
\documentclass{article}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{etoolbox}
\makeatletter
\newcommand*{\programmerdiv}[2]{%
\ifnum #1 = \z@
0%
\else
\the\numexpr (2*(#1) - (#2)) / (2 * (#2)) \expandafter\relax
\fi
}
\newcommand*{\modab}[2]{%
\the\numexpr #1 - \programmerdiv{#1}{#2} * (#2) \relax
}
\newcommand*{\gcdab}[2]{%
\ifnum #2 = \z@
#1%
\else
\edef\z{\noexpand\gcdab{#2}{\modab{#1}{#2}}}\z
\fi
}
\def\afterfi#1\fi{\fi#1}
\begin{document}
\gcdab{377}{233}
\end{document}