# Scalable If/Then Structure

I am trying to make some math fact equation sheets that are based on random number generation. I have quite a bit of learning to do... but this is where I think should start: Use random numbers to make LaTeX generate a string of equations for me.

Right now, the basic idea is that: I first have \pgfmathsetmacro{\A}{random(1,4)} set \A as 1,2, 3 or 4.

1. If \A=1, then I want 1+2=3 to appear.
2. If \A=2, then I want 2+1=3 to appear.
3. If \A=3, then I want 3=2+1 to appear.
4. If \A=4, then I want 3=1+2 to appear.

This is what I've got so far, but the nested \ifthen structure seems very inelegant and will basically not scale to when I have, say, 100 different equations to choose from instead of just 4.

What's a better way?

\documentclass{article}

\usepackage{pgf}
\usepackage{pgffor}
\usepackage{ifthen}

\pagestyle{empty}
\setlength{\parindent}{0pt}

% THE FOUR EQUATIONS
% 1.   1+2=3
% 2.   2+1=3
% 3.   3=2+1
% 4.   3=1+2

\pgfmathsetseed{\number\pdfrandomseed}

\newcommand{\InitVariables}
{\pgfmathsetmacro{\A}{random(1,4)}}

\newcommand*{\onefact}
{
\InitVariables
\ifthenelse
{\equal{\A}{1}}{1+2=3}
{
\ifthenelse{\equal{\A}{2}}{3=2+1}
{
\ifthenelse{\equal{\A}{3}}{3=2+1}{3=1+2}
}
}
}

\newcommand{\myequations}[1]
{
\foreach \x in {1,...,#1}
{\onefact\\}
}

\begin{document}

\myequations{10}

\end{document}

• you are looking for cases Sep 19, 2016 at 22:08
• @percusse What are cases? Sep 19, 2016 at 22:14

If you have 100 different cases, then your code logic is wrong at some point. But for not too many cases you can do \ifcase

\documentclass{article}
\usepackage{pgfmath}

\pgfmathsetseed{\number\pdfrandomseed}
\pgfmathtruncatemacro{\A}{random(1,4)}

\begin{document}

\ifcase\A\relax%
\or 1+2=3% Because \A starts from 1
\or 2+1=3%
\or 3=2+1%
\or 3=1+2%
\fi

\end{document}

• How does 100 cases imply something is wrong with the code logic? And what does \ifcase\A\relax mean? I can't make sense of it. Sep 19, 2016 at 22:26
• @WeCanLearnAnything It means that you are doing a lookup so it shouldn't be a if or switch it should get the index and pick it up from some array/table/database whatever. It shouldn't list all the possibilities inline. Sep 19, 2016 at 22:32
• \ifcase works with integers. \A\relax means roughly after finishing \A stop looking for more numbers from this point and carry on with the code. Sep 19, 2016 at 22:32
• Hmm. How would I enter that array/table/database into LaTeX? Does that have to be a separate document or separate file?? Sep 19, 2016 at 22:37
• search for datatools or pgfplotstable on this site Sep 19, 2016 at 22:44

An ideal usecase for luatex:

\documentclass[margin=1mm, varwidth=true]{standalone}
\usepackage{luacode}

\begin{luacode}
userdata = userdata or {}

local questions = {
"1 + 2 = 3" ,
"2 + 1 = 3" ,
"3 = 2 + 1" ,
"3 = 1 + 2" ,
}

function userdata.fact()
local A = math.random(#questions)
tex.print(questions[A])
end
\end{luacode}

\begin{document}

\begin{itemize}
\item $\Fact$
\item $\Fact$
\item $\Fact$
\item $\Fact$
\item $\Fact$
\end{itemize}

\end{document}


which gives

I think it's easy to define essentially an array of options so you can define or redefine them as needed without having to define them all together in a nested structure.

\documentclass{article}

\usepackage{pgffor}

\usepackage{ifthen}

\pagestyle{empty}
\setlength{\parindent}{0pt}

% THE FOUR EQUATIONS
% 1.   1+2=3
% 2.   2+1=3
% 3.   3=2+1
% 4.   3=1+2

\def\defchoice#1#2{\expandafter\def\csname X-#1\endcsname{#2}}
\defchoice{1}{$1+2=3$}
\defchoice{2}{$2+1=3$}
\defchoice{3}{$3=2+1$}
\defchoice{4}{$3=1+2$}

\pgfmathsetseed{\number\pdfrandomseed}

\newcommand{\InitVariables}
{\pgfmathsetmacro{\A}{random(1,4)}}

\newcommand*{\onefact}
{%%%%%%%%%% dont forget % at end of line
\InitVariables
\csname X-\A\endcsname}

\newcommand{\myequations}[1]
{%
\foreach \x in {1,...,#1}
{\onefact\par}% don't end a paragraph with \\
}

\begin{document}

\myequations{10}

\end{document}

• This seems to look good. What does \def\defchoice#1#2{\expandafter\def\csname X-#1\endcsname{#2}} mean? I can't make sense of this... Sep 19, 2016 at 22:28
• @WeCanLearnAnything: \def\defchoice#1#2{...} creates a macro called \defchoices{#1}{#2} where you have to supply it with [2] arguments (both are mandatory). It creates a macro called \X-#1 that contains the definition #2. However, creating a macro with numbers in its name is not easy. You have to do this using a \csname ... \endcsname construction, so we use \expandafter\def\csname ... \endcsname to first create the control sequence, then \define it.
– Werner
Sep 19, 2016 at 22:41
• Ok, I understand the \def\defchoice#1#2{...} part. But then there's another macro created within that? And why would it have the name \X- #1? Sep 19, 2016 at 23:56
• @WeCanLearnAnything you need a macro name to store each choice, the prefix is arbitrary, I just chose \X-1, \X-2, \X-3 ... Sep 20, 2016 at 0:02
• @WeCanLearnAnything tex.stackexchange.com/questions/39380/… Sep 20, 2016 at 0:04

LaTeX3 provides a fairly intuitive switch or case-like structure that allows you to define a case and its corresponding resolution. For your implementation, you can use:

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\onefact}{m}{
\int_case:nnF {#1}
{
{ 1 }{ 1 + 2 = 3 }
{ 2 }{ 2 + 1 = 3 }
{ 3 }{ 3 = 2 + 1 }
{ 4 }{ 3 = 1 + 2 }
}
{ ? }
}
\ExplSyntaxOff


This will perform an integer comparison where the "else clause" (no match is found) results in ?. The use will be something like \onecase{2}, or \onecase{\A} where \A is defined as your random value between 1 and 4 (inclusive).

Scalable means “good interface”. We define a set of possible outcomes with

\defineset{<name>}{<a>,<b>,...}


and then \printfromset{<name>} will choose one of the outcomes at random; the random number will be computed from the number of possible outcomes associated with the particular set.

\documentclass{article}
\usepackage{xparse}

\input{random} % for random numbers
\randomi=\pdfrandomseed % initialize the seed

\ExplSyntaxOn

\cs_new_eq:NN \wcla_get_random:Nnn \setrannum
\int_new:N \l_wcla_random_int

\NewDocumentCommand{\defineset}{mm}
{
\tl_new:c { g_wcla_set_#1_tl }
\int_zero:N \l_tmpa_int
\clist_map_inline:nn { #2 }
{
\int_incr:N \l_tmpa_int
\tl_gput_right:cx { g_wcla_set_#1_tl }
{
{ \int_to_arabic:n { \l_tmpa_int } }
{ \exp_not:n { ##1 } }
}
}
}

\NewDocumentCommand{\printfromset}{m}
{
\wcla_get_random:Nnn \l_wcla_random_int
{ 1 }
{ \int_eval:n { \tl_count:c { g_wcla_set_#1_tl } /2 } }
\int_case:nv { \l_wcla_random_int } { g_wcla_set_#1_tl }
}
\cs_generate_variant:Nn \int_case:nn { nv }
\ExplSyntaxOff

\defineset{equations}{
$1+2=3$,
$2+1=3$,
$3=2+1$,
$3=1+2$
}
\defineset{letters}{A,B,C,D,E,F,G,H,I}

\begin{document}

\printfromset{equations}

\printfromset{equations}

\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}
\printfromset{letters}

\end{document}


The \defineset macro allocates a token list variable that's then filled in the format

{1}{<a>}{2}{<b>}...


which is good for \int_case:nn, provided we use the \int_case:nv variant. Note that the number of items is twice the number of outcomes.

The macro \printfromset generates a random number between 1 and half the length of the token list variable (we divide it by 2).

Here's a possible output

Note about random.tex, a set of macros by Donald Arsenau. The syntax is \setrannum<counter>{<low>}{<high>} which stores in the <counter> a random number between the integers <low> and <high> (extremes included). The seed is stored in the counter \randomi.