diff options
Diffstat (limited to 'texmf-dist/source/latex/rangen/rangen.dtx')
-rw-r--r-- | texmf-dist/source/latex/rangen/rangen.dtx | 1988 |
1 files changed, 1988 insertions, 0 deletions
diff --git a/texmf-dist/source/latex/rangen/rangen.dtx b/texmf-dist/source/latex/rangen/rangen.dtx new file mode 100644 index 00000000..637775f9 --- /dev/null +++ b/texmf-dist/source/latex/rangen/rangen.dtx @@ -0,0 +1,1988 @@ +%\iffalse +%<*copyright> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Rangen.sty package, 2016-02-19 %% +%% Copyright (C) 1999-2016 D. P. Story %% +%% dpstory@uakron.edu %% +%% %% +%% This program can redistributed and/or modified under %% +%% the terms of the LaTeX Project Public License %% +%% Distributed from CTAN archives in directory %% +%% macros/latex/base/lppl.txt; either version 1 of the %% +%% License, or (at your option) any later version. %% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%</copyright> +%<package>\NeedsTeXFormat{LaTeX2e}[1997/12/01] +%<package>\ProvidesPackage{rangen} +%<package> [2014/10/17 v1.4 Rangen: Generate Random Questions (dps)] +%<*driver> +\documentclass{ltxdoc} +\usepackage[colorlinks,hyperindex]{hyperref} +%\pdfstringdefDisableCommands{\let\\\textbackslash} +%\EnableCrossrefs \CodelineIndex +\begin{document} + \GetFileInfo{rangen.sty} +% \settowidth{\oddsidemargin}{0pt}% +% \setlength{\evensidemargin}{0pt} +% \setlength{\marginparsep}{0pt} +% \setlength{\marginparwidth}{0pt} +% \setlength\textwidth{6in} +% \hoffset=.5in +% \hsize = 6in + \title{\textsf{Rangen}\texorpdfstring{\\}{:} Random Generation of Integer, Rational, and Real Numbers with + Applications to the \texttt{exercise}, \texttt{quiz}, and \texttt{shortquiz} environments of \textsf{Exerquiz}} + \author{D. P. Story\\ + Email: \texttt{dpstory@uakron.edu}} + \date{processed \today} + \maketitle + \tableofcontents + \let\Email\texttt + \DocInput{rangen.dtx} + \PrintIndex +\end{document} +%</driver> +% \fi +% \MakeShortVerb{|} +% \StopEventually{} +% +% \DoNotIndex{\def,\edef,\gdef,\xdef,\global,\long,\let} +% \DoNotIndex{\expandafter,\string,\the,\ifx,\else,\fi} +% \DoNotIndex{\csname,\endcsname,\relax,\begingroup,\endgroup} +% \DoNotIndex{\DeclareTextCommand,\DeclareTextCompositeCommand} +% \DoNotIndex{\space,\@empty,\special} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% \section{Introduction} +% +% This package provides some commands for creating randomly generated integers, rational, +% and real numbers. There are options for specifying constraints on the generation of the numbers. +% Companion JavaScript functions are developed to use these random numbers as part of a +% question in a \texttt{shortquiz} or \texttt{quiz}. The syntax of this package can be used +% to pose number-related questions, the JavaScript can be used to create the answer to the +% question based on a formula. You'll have to see it to believe it. +% +% \section{The Main Code} +% +% \subsection{Declare Options} +% +% This package has one option, other options are passed to the really nice \textsf{lcg} Package, +% by Erich Janka (\texttt{janka@utanet.at}). +% \begin{macrocode} +\newcount\seedCnt +\DeclareOption{testmode}{% + \InputIfFileExists{\jobname.seed}{}{\def\thisseed{1}}% + \PassOptionsToPackage{seed=\thisseed}{lcg}% + \AtEndOfPackage{\reseedEachRun}% +} +\def\reseedEachRun{% + \seedCnt=\thisseed + \advance\seedCnt1\relax + \newwrite \rngWrite + \immediate\openout\rngWrite \jobname.seed + \immediate\write\rngWrite{\string\def\string\thisseed{\the\seedCnt}} + \immediate\closeout\rngWrite +} +\def\RNG@Dec{.} +\DeclareOption*{\PassOptionsToPackage{\CurrentOption}{lcg}} +\ProcessOptions +\RequirePackage{lcg}[2008/09/10] +% \end{macrocode} +% Save the seed value so we can reproduce the same pseudo-random number sequence. +% \begin{macrocode} +\edef\rng@saveSeed{\the\cr@nd} +% \end{macrocode} +% There are three data types: Integer, Rational, and Real. The following macros +% gives each of these types a numerical value, 0, 1 and 2, respectively. +% \begin{macrocode} +\newcount\loopCnt +\def\maxLoopLimit{10} +\def\typeCodeForz{0} +\def\typeCodeForq{1} +\def\typeCodeForr{2} +% \end{macrocode} +% Some scratch count registers +% \begin{macrocode} +\newcount\rng@cnta +\newcount\rng@cntb +% \end{macrocode} +% A random variable is specified using a control sequence, e.g. \cs{a}. The following macro +% extracts the underlying name of the command, e.g. \verb+\@gtVarName{\a}+ expands to \texttt{a}, +% and returns the name as the expansion of the macro \cs{@varName}. +% \begin{macrocode} +\def\@getVarName#1{% + \edef\@varName{\expandafter\@gobble\string#1}% +} +% \end{macrocode} +% The command \cs{@getVarType} takes one argument, a random variable, e.g., \cs{a}. This +% command defines a macro \cs{varType} which expands to the data type the random variable is. +% \begin{macrocode} +\def\@getVarType#1{% + \@getVarName{#1}\edef\varType{\csname typeof@\@varName\endcsname}} +% \end{macrocode} +% \subsection{GCD and Rational Reduction Commands} +% \begin{macro}{\gcd} +% Here we use Euclid's Algorithm to find the greatest common divisor of two integers. +% \begin{macrocode} +\def\gcd#1#2{{% #1 = a, #2 = b + \ifnum#2=0 \edef\next{#1}\else + \@tempcnta=#1 \@tempcntb=#2 \divide\@tempcnta by\@tempcntb + \multiply\@tempcnta by\@tempcntb % q*b + \@tempcntb=#1 + \advance\@tempcntb by-\@tempcnta % remainder in \@tempcntb + \ifnum\@tempcntb=0 + \@tempcnta=#2 + \ifnum\@tempcnta < 0 \@tempcnta=-\@tempcnta\fi + \xdef\gcd@next{\noexpand% + \def\noexpand\thegcd{\the\@tempcnta}}% + \else + \xdef\gcd@next{\noexpand\gcd{#2}{\the\@tempcntb}}% + \fi + \fi}\gcd@next +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\lcm} +% Now compute the least common multiple +% \begin{macrocode} +\def\lcm#1#2{% #1 = a, #2 = b + \gcd{#1}{#2}% + {\@tempcnta=#1 + \multiply\@tempcnta by#2 + \divide\@tempcnta by\thegcd + \xdef\thelcm{\the\@tempcnta}}% +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\amodb} +% Modular arithmetic \texttt{a mod b}, returns its results +% as a macro \cs{retnmod}. +% \begin{macrocode} +\def\amodb#1#2{% #1 = a, #2 = b + {\@tempcnta=#1 + \divide\@tempcnta by#2 + \multiply\@tempcnta by#2 + \@tempcntb=#1 + \advance\@tempcntb by-\@tempcnta + \xdef\retnmod{\the\@tempcntb}}% +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\reduceFrac} +% Reduce a fraction to lowest terms. The first argument is the numerator and the second +% argument is the denominator. This command computes the \texttt{gcd} of the two integers, +% divides each by the \texttt{gcd}, and returns the results in the two scratch count +% registers \cs{@tempcnta} and \cs{@tempcntb}. +% \begin{macrocode} +\newcommand\reduceFrac[2] +{% + \gcd{#1}{#2}{\@tempcnta=#1 \divide\@tempcnta by\thegcd + \@tempcntb=#2 \divide\@tempcntb by\thegcd + \ifnum\@tempcntb<0\relax +% \end{macrocode} +% Always have the denominator as positive. +% \begin{macrocode} + \@tempcntb=-\@tempcntb + \@tempcnta=-\@tempcnta + \fi + \xdef\rfNumer{\the\@tempcnta}\xdef\rfDenom{\the\@tempcntb}}% +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\convertRatTo} +% Converts a fraction \texttt{a/b} (\texttt{\#1/\#2}) to a denominator of \texttt{\#3}. Will return +% new numerator in \cs{rnd@Cnta} register. This will be exact if +% \texttt{\#2} divides \texttt{\#3}. +% \begin{macrocode} +\def\convertRatTo#1#2#3{{% + \@tempcnta=#3 + \multiply\@tempcnta by#1 + \divide\@tempcnta by#2 + \xdef\rng@retn@num{\the\@tempcnta}% +}} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\RNGadd} +% This is the support for rational arithmetic (addition and subtraction). +% Adds two rational numbers, \texttt{\#1} and \texttt{\#2} together. These two rational numbers must have been defined +% already, possibly by \cs{defineQ}. Usage: \verb+\RNGadd\a\b+. This macro returns a rational number: +% the numerator in the \cs{rfNumer} command, and the denominator in the \cs{rfDenom} command. +% For example, +%\begin{verbatim} +%\defineQ\a{1}{3}\defineQ\b{3}{5}\RNGadd\a\b +%\makeatletter +%The sum of $\frac{\nOf\a}{\dOf\a} + \frac{\nOf\b}{\dOf\b} +% = \frac{\rfNumer}{\rfDenom}$ +%\makeatother +%\end{verbatim} +%This code typesets as $\frac{1}{3}+\frac{3}{5}=\frac{14}{15}$. +% \begin{macrocode} +\newcommand\RNGadd[2]{% + \rng@cnta=\nOf#1 \multiply\rng@cnta by\dOf#2 + \rng@cntb=\nOf#2 \multiply\rng@cntb by\dOf#1 + \advance\rng@cnta by\the\rng@cntb + \rng@cntb=\dOf#1 \multiply\rng@cntb by\dOf#2 + \reduceFrac{\the\rng@cnta}{\the\rng@cntb} +} +% \end{macrocode} +% \end{macro} +% This is a simple macro for detecting if the argument \texttt{\#1} +% is a macro or not. Used when interval definitions of the +% \cs{RandomZ/Q/R} macros. +% \begin{macrocode} +\def\rng@isControl#1{\@ifundefined{\expandafter\@gobble\string#1}% + {\let\rng@isC@ntrol=0}{\let\rng@isC@ntrol=1}} +% \end{macrocode} +% This command determines if its argument has an \texttt{*} +% prefixed or post-fixed to its argument. +% If \cs{rng@isStariii} equals \texttt{*}, then an \texttt{*} exists. +% \begin{itemize} +% \item If there is no \texttt{*}, then the argument is \texttt{\#1} +% \item \cs{rng@isStariii} equals \texttt{*}, there is \texttt{*}, +% If the argument has the form \cs{*a}, then \cs{rng@isStari} is \cs{@empty} +% and the argument, stripped of the \texttt{*}, is given as \cs{rng@isStarii} +% \item \cs{rng@isStariii} equals \texttt{*}, there is \texttt{*}, +% If the argument has the form \cs{a*}, then \cs{rng@isStarii} is \cs{@empty} +% and the argument, stripped of the \texttt{*}, is given as \cs{rng@isStari} +%\end{itemize} +% \begin{macrocode} +\def\rng@existStar#1{\rng@existSt@r#1**\@nil} +\def\rng@existSt@r#1*#2*#3\@nil{\def\rng@isStari{#1}% + \def\rng@isStarii{#2}\def\rng@isStariii{#3}% +} +\def\rng@NameEndpoint#1{% + \ifx\rng@isStari\@empty + \edef#1{\expandafter\noexpand\rng@isStarii}% + \else\ifx\rng@isStarii\@empty + \edef#1{\expandafter\noexpand\rng@isStari}% + \fi\fi +} +% \end{macrocode} +% \subsection{Define an Integer and a Rational} +% \begin{macro}{\defineZ} +% Define a integer for use in other macros. +% \begin{macrocode} +\newcommand\defineZ[2] +{% + \@getVarName#1\relax + \expandafter\def\csname typeof@\@varName\endcsname{0}% + \expandafter\edef\csname n@\@varName\endcsname{#2}% + \expandafter\edef\csname d@\@varName\endcsname{1}% + \edef\display@TeXfmt{#2}\edef\inline@TeXfmt{#2}% + \ifnum#2=1\relax\rng@makeOneFmtDefns + \else\ifnum#2=-1\relax\rng@makeMinusOneFmtDefns + \else\rng@makeOtherFmtDefns\fi\fi + \expandafter\let\csname\@varName*\endcsname\display@TeXfmt + \expandafter\let\csname\@varName!\endcsname\inline@TeXfmt + \expandafter\edef\csname\@varName\endcsname{#2}% +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\defineQ} +% The following macro defines a rational number. Usage +% \verb+\defineQ\a{1}{2}+. This defines the rational number 1/2 and +% gives it a name, \cs{a}. +% \begin{macrocode} +\newcommand\defineQ[3] +{% + \@getVarName#1\relax + \expandafter\def\csname typeof@\@varName\endcsname{1}% + \expandafter\edef\csname n@\@varName\endcsname{#2}% + \expandafter\edef\csname d@\@varName\endcsname{#3}% + \edef\display@TeXfmt{\frac{#2}{#3}}\edef\inline@TeXfmt{#2/#3}% + \ifnum#2=#3\relax\rng@makeOneFmtDefns + \else\ifnum#2=-#3\relax\rng@makeMinusOneFmtDefns + \else\rng@makeOtherFmtDefns\fi\fi + \expandafter\let\csname\@varName*\endcsname\display@TeXfmt + \expandafter\let\csname\@varName!\endcsname\inline@TeXfmt + \expandafter\edef\csname\@varName\endcsname{#2/#3}% + \simplifyCurrentQ +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\defineR} +% This macro computes: (1) \cs{rng@intpart}; (2) \cs{rng@fracpart}; +% (3) \cs{rndnDec} (the number of decimals of the fractional part); +% (4) \cs{rndPower} (the power of ten determined by \cs{rndnDec}). +% \begin{macrocode} +\newcommand{\defineR}[2]{% + \@getVarName{#1}\RNGparseDec{#2}% + \expandafter\def\csname typeof@\@varName\endcsname{2}% + \reduceFrac{\rng@intpart\rng@fracpart}{\rndPower}% + \expandafter\edef\csname n@\@varName\endcsname{\rfNumer}% + \expandafter\edef\csname d@\@varName\endcsname{\rfDenom}% + \edef\display@TeXfmt{#2}\edef\inline@TeXfmt{#2}% + \ifnum\rfNumer=1\relax\rng@makeOneFmtDefns + \else\ifnum\rfNumer=-1\relax\rng@makeMinusOneFmtDefns + \else\rng@makeOtherFmtDefns\fi\fi + \expandafter\let\csname\@varName*\endcsname\display@TeXfmt + \expandafter\let\csname\@varName!\endcsname\inline@TeXfmt + \expandafter\edef\csname\@varName\endcsname{#2}% + \simplifyCurrentR +} +\newcommand{\simplifyCurrentR}{% + \ifnum\csname d@\@varName\endcsname=1 + \expandafter\defineZ + \csname\@varName\endcsname{\csname n@\@varName\endcsname}\fi +} +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +\def\rng@makeOneFmtDefns{% +% inline + \expandafter\def\csname\@varName!e\endcsname{}% + \expandafter\def\csname\@varName!c\endcsname{}% +% display + \expandafter\def\csname\@varName*e\endcsname{}% + \expandafter\def\csname\@varName*c\endcsname{}% +} +\def\rng@makeMinusOneFmtDefns{% +% inline + \expandafter\def\csname\@varName!e\endcsname{-1}% + \expandafter\def\csname\@varName!c\endcsname{-}% +% display + \expandafter\def\csname\@varName*e\endcsname{-1}% + \expandafter\def\csname\@varName*c\endcsname{-}% +} +\def\rng@makeOtherFmtDefns{% +% inline + \expandafter\let\csname\@varName!e\endcsname\inline@TeXfmt + \expandafter\let\csname\@varName!c\endcsname\inline@TeXfmt +% display + \expandafter\let\csname\@varName*e\endcsname\display@TeXfmt + \expandafter\let\csname\@varName*c\endcsname\display@TeXfmt +} +% \end{macrocode} +% +% \subsection{Parse a Number} +% +% \subsubsection{Parsing a Rational} +% +% \begin{macro}{\RNGparseRat} +% \begin{macrocode} +\def\RNGparseRat#1{\expandafter\@chkslash#1//\@nil} +\def\@chkslash#1/#2/#3\@nil{% + \def\rng@num{#1}\def\rng@denom{#2}% + \def\rng@parseQ@iii{#3}% + \ifx\rng@denom\@empty\def\rng@denom{1}\fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Parsing a Real} +% +% \begin{macro}{\RNGparseDec} +% The argument \texttt{\#1} is a decimal number (or integer) +% This macro computes: (1) \cs{rng@intpart}; (2) \cs{rng@fracpart}; +% (3) \cs{rndnDec} (the number of decimals of the fractional part); +% (4) \cs{rndPower} (the power of ten determined by \cs{rndnDec}). +% These variables will be overwritten the next time this command +% is executed. +% \begin{macrocode} +\newcommand{\RNGparseDec}[1]{\edef\parse@argi{#1}% + \expandafter\@chkdec\parse@argi..\@nil} +\def\@chkdec#1.#2.#3\@nil{% + \def\rng@intpart{#1}\def\rng@fracpart{#2}% + \def\rng@parseR@iii{#3}\rng@getnDec} +\def\rng@getnDec{% + \begingroup + \ifx\rng@fracpart\@empty\gdef\rndnDec{0}\gdef\rndPower{1}\else + \count0=0\relax\count2=1\relax + \expandafter\cntNumDec\rng@fracpart\end\fi + \endgroup} +\def\cntNumDec#1#2\end{% + \advance\count0by1 + \def\rng@arg{#2}% + \ifx\rng@arg\@empty + \xdef\rndnDec{\the\count0}% + \xdef\rndPower{1\@nameuse{rng@tz\the\count0}}% + \let\rng@next\relax + \else + \def\rng@next{\cntNumDec#2\end}% + \fi\rng@next +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\nDivisionsPowerOfTen} +% This is a control of how many nodes to create in an interval +% of real numbers, as defined by \cs{RandomR}. The argument is +% an integer between 1 and 4 inclusive. +% \begin{macrocode} +\newcommand{\nDivisionsPowerOfTen}[1]{% + \begingroup + \count0=#1\relax + \ifnum\count0>4\relax + \PackageError{rangen}{Number of subdivisions too large}% + {Reduce the argument of \string\nDivisionsPowerOfTen.}% + \else + \ifnum\count0<1\relax + \PackageError{rangen}{Number of subdivisions too large}% + {Increase the argument of \string\nDivisionsPowerOfTen.}% + \fi\fi + \xdef\RNGpowerOfTen{1\@nameuse{rng@tz#1}}% + \endgroup +} +\nDivisionsPowerOfTen{2} +% \end{macrocode} +% \end{macro} +%\subsection{Creating Random Things} +%\subsubsection{Random Integer} +% \begin{macro}{\RandomZ} +% Randomly generates an integer in the specified range of values. +%\begin{verbatim} +%[#1] Optional parameter to modify the variable. +% #2 The random variable being defined, e.g., \a +% #3 lower limit of random integer +% #4 upper limit of random integer +%\end{verbatim} +% \begin{macrocode} +\newcommand\RandomZ[4][] +{% + \def\rng@ne@values{}% + \setkeys{rangen}{ne,#1}% +% \end{macrocode} +% Now see if there is an \texttt{*}, and get un-stripped +% argument. +% +% The \cs{rng@isControl} lets \cs{rng@isC@ntrol} to 0 if the arg is undefined, +% and lets \cs{rng@isC@ntrol} to 1 if it is defined. +% Check the left endpoint: +% \begin{macrocode} + \let\rng@CtrlLEP=0\let\rng@CtrlUEP=0% + \let\rng@makeLEPStrict=0\let\rng@makeUEPStrict=0% +% \end{macrocode} +% \paragraph*{Left endpoint.} +% \begin{macrocode} + \rng@existStar{#3}\rng@NameEndpoint{\rng@LEP}% + \if\rng@isStariii*\edef\tmp@exp{% + \noexpand\rng@isControl{\expandafter\noexpand\rng@LEP}}\tmp@exp + \if\rng@isC@ntrol1% a control sequence + \let\rng@CtrlLEP=1\let\rng@makeLEPStrict=1% +% \end{macrocode} +% The LEP is a control sequence with a star, we need to increment the value +% of \cs{rng@LEP} to the next largest integer. +% \begin{macrocode} + \edef\tmp@exp{\noexpand% + \@getVarType{\expandafter\noexpand\rng@LEP}}\tmp@exp + \ifcase\varType % integer + \rng@cnta=\rng@LEP + \or % rational + \rng@dima=\expandafter\nOf\rng@LEP pt + \divide\rng@dima by\expandafter\dOf\rng@LEP + \defineR{\rng@LEP}{\strip@pt\rng@dima}% + \RNGparseDec{\rng@LEP}% + \rng@cnta=\rng@intpart + \or % real + \defineR{\rng@LEP}{\rng@LEP}% + \RNGparseDec{\rng@LEP}% + \rng@cnta=\rng@intpart + \fi + \advance\rng@cnta by1\relax + \defineZ{\rng@LEP}{\the\rng@cnta}% + \else +% \end{macrocode} +% Not a control sequence but has a star +% \begin{macrocode} + \defineZ{\rng@LEP}{\rng@LEP}% + \fi + \else +% \end{macrocode} +% No star, control sequence or not? +% \begin{macrocode} + \rng@isControl{#3}% + \if\rng@isC@ntrol1% control sequence + \let\rng@CtrlLEP=1% + \def\rng@LEP{#3}% + \@getVarType{#3}% + \ifcase\varType % integer + \defineZ{\rng@LEP}{#3}% + \or % rational + \rng@dima=\nOf{#3}pt + \divide\rng@dima by\dOf{#3}% + \defineR{\rng@LEP}{\strip@pt\rng@dima}% + \RNGparseDec{\rng@LEP}% + \defineZ{\rng@LEP}{\rng@intpart}% + \or % real + \defineR{\rng@LEP}{\rng@LEP}% + \RNGparseDec{\rng@LEP}% + \defineZ{\rng@LEP}{\rng@intpart}% + \fi + \else +% \end{macrocode} +% A number, no star +% \begin{macrocode} + \defineZ{\rng@LEP}{#3}% + \fi + \fi +% \end{macrocode} +% \paragraph*{Right endpoint.} +% \begin{macrocode} + \rng@existStar{#4}\rng@NameEndpoint{\rng@UEP}% + \if\rng@isStariii*\edef\tmp@exp{% + \noexpand\rng@isControl{\expandafter\noexpand\rng@UEP}}\tmp@exp + \if\rng@isC@ntrol1% a control sequence + \let\rng@CtrlUEP=1\let\rng@makeUEPStrict=1% +% \end{macrocode} +% The UEP is a control sequence with a star, we need to increment the value +% of \cs{rng@UEP} to the next largest integer. +% \begin{macrocode} + \edef\tmp@exp{\noexpand% + \@getVarType{\expandafter\noexpand\rng@UEP}}\tmp@exp + \ifcase\varType % integer + \rng@cnta=\rng@UEP + \or % rational + \rng@dima=\expandafter\nOf\rng@UEP pt + \divide\rng@dima by\expandafter\dOf\rng@UEP + \defineR{\rng@UEP}{\strip@pt\rng@dima}% + \RNGparseDec{\rng@UEP}% + \rng@cnta=\rng@intpart + \or % real + \defineR{\rng@UEP}{\rng@UEP}% + \RNGparseDec{\rng@UEP}% + \rng@cnta=\rng@intpart + \fi + \advance\rng@cnta by-1\relax + \defineZ{\rng@UEP}{\the\rng@cnta}% + \else +% \end{macrocode} +% Not a control sequence but has a star +% \begin{macrocode} + \defineZ{\rng@UEP}{\rng@UEP}% + \fi + \else +% \end{macrocode} +% No star, control sequence or not? +% \begin{macrocode} + \rng@isControl{#4}% + \if\rng@isC@ntrol1% control sequence + \let\rng@CtrlUEP=1% + \def\rng@UEP{#4}% + \@getVarType{#4}% + \ifcase\varType % integer + \defineZ{\rng@UEP}{#4}% + \or % rational + \rng@dima=\nOf{#4}pt + \divide\rng@dima by\dOf{#4}% + \defineR{\rng@UEP}{\strip@pt\rng@dima}% + \RNGparseDec{\rng@UEP}% + \defineZ{\rng@UEP}{\rng@intpart}% + \or % real + \defineR{\rng@UEP}{\rng@UEP}% + \RNGparseDec{\rng@UEP}% + \defineZ{\rng@UEP}{\rng@intpart}% + \fi + \else +% \end{macrocode} +% A number, no star, assume it is an integer +% \begin{macrocode} + \defineZ{\rng@UEP}{#4}% + \fi + \fi +% \end{macrocode} +% \textbf{To Do.} Check if LEP is less than UEP, if not, notify user. +% Save the random variable, e.g., \cs{a} +% \begin{macrocode} + \def\@currentName{#2}% +% \end{macrocode} +% Record the variable type +% \begin{macrocode} + \@getVarName{#2}% + \expandafter\def\csname typeof@\@varName\endcsname{0}% +% \end{macrocode} +% Save the range of this variable +% \begin{macrocode} + \expandafter\edef\csname first@\@varName\endcsname{\rng@LEP}% + \expandafter\edef\csname last@\@varName\endcsname{\rng@UEP}% +% \end{macrocode} +% Now get a value for the variable using \cs{rand}, defined in \texttt{lcg} +% \begin{macrocode} + \rng@chgrand[first=\rng@LEP,last=\rng@UEP]\rand +% \end{macrocode} +% Now define the integer. +% \begin{macrocode} + \defineZ{#2}{\arabic{rand}}% +% \end{macrocode} +% \paragraph*{Constraints} +% We have a random Z, we now try to satisfy the \texttt{ne} condition. +% +% The macro \cs{rangen@ne} can be of the form \verb!{1,3,4,5}!. We try to +% satisfy all the conditions specified by \cs{rangen@ne} +% \begin{macrocode} + \ifx\rangen@ne\@empty\else\loopCnt=0\relax +% \end{macrocode} +% We will try a total number of \cs{maxLoopLimit} to meet the required +% conditions. +% \begin{macrocode} + \@whilenum\loopCnt<\maxLoopLimit\do{% +% \end{macrocode} +% Set \cs{rng@cnta=1}, if \cs{rng@cnta} is still 1 at the end of this +% loop, the condition is satisfied. +% conditions. +% \begin{macrocode} + \rng@cnta=1\relax +% \end{macrocode} +% We use a \cs{@for} loop to run through all the NE values +% \begin{macrocode} + \@for\ne@@tmp:=\rangen@ne\do{% +% \end{macrocode} +% If the current RV is equal to the current NE value, we fail, so we +% ``and'' a zero into the \cs{rng@cnta} register. +% \begin{macrocode} + \ifnum\value{rand}=\ne@@tmp\relax + \multiply\rng@cnta0\relax + \else +% \end{macrocode} +% \dots otherwise, we ``and'' a one. +% \begin{macrocode} + \multiply\rng@cnta1\relax + \fi + }% +% \end{macrocode} +% If \cs{rng@cnt} is still equal to 1, all conditions have been met, +% in this case we set \verb!\loopCnt=\maxLoopLimit! so we can exit the outer loop. +% \begin{macrocode} + \ifnum\rng@cnta=1\relax % all conditions met + \loopCnt=\maxLoopLimit + \else % if \rng@cnta \ne 1, try again +% \end{macrocode} +% Otherwise, we increment the loop, see if we have gone the limit, if +% not, loop back with a new random choice. +% \begin{macrocode} + \advance\loopCnt1\relax + \ifnum\loopCnt=\maxLoopLimit + \PackageWarning{rangen}{Not all conditions met + after \maxLoopLimit\space tries}% + \else + \rng@chgrand[first=\rng@LEP,last=\rng@UEP]\rand + \fi + \fi + }% + \fi +% \end{macrocode} +% Whether we fail or succeed, we'll go with the last RV. Hopefully, the +% author is aware of the log file, and re-compile, possibly with a +% wider range for the variable, or with a larger value of \cs{maxLoopLimit}. +% +% \begin{macrocode} + \defineZ{#2}{\arabic{rand}}% +} +% \end{macrocode} +% \end{macro} +% \begin{macrocode} +\def\updateZ#1#2{% + \@getVarName#1\relax + \expandafter\edef\csname\@varName\endcsname{#2}% + \expandafter\edef\csname n@\@varName\endcsname{#2}% + \expandafter\edef\csname d@\@varName\endcsname{1}% +} +% \end{macrocode} +%\subsubsection{Random Rational} +% \begin{macro}{\RandomQ} +% Randomly generate a rational number. The parameters for \cs{RandomQ} are +%\begin{verbatim} +%[#1] Optional parameter to modify the variable. +% #2 The random variable being defined, e.g., \a +%[#3] maximum denominator permitted (optional) +% #4 rational number for lower endpoint of range +% #5 rational number for upper endpoint of range +%\end{verbatim} +% Here, it is assume that the first rational number is less than the second. This macro +% will randomly generate a rational number between rat1 and rat2, with a maximum denominator +% specified in \texttt{\#3}. +% +% \medskip\noindent\textbf{Note: }To allow for random endpoints, if one or both are real numbers, we convert +% them to rational numbers in \cs{@RandomQ}. +% +% We begin by getting the first two parameters: +%\begin{verbatim} +%[#1] Optional parameter to modify the variable. +% #2 The random variable being defined, e.g., \a +%\end{verbatim} +% \begin{macrocode} +\newcommand{\RandomQ}[2][] +{% + \setkeys{rangen}{ne,#1}% + \def\rq@currentName{#2}% + \@RandomQ +} +% \end{macrocode} +% We use \cs{@RandomQ} to get the last three parameters of \cs{RandomQ}. +% If the endpoints are not rational, they are converted to rationals. +%\begin{verbatim} +%[#1] maximum denominator permitted (optional) +% #2 rational number for lower endpoint of range +% #3 rational number for upper endpoint of range +%\end{verbatim} +% \begin{macrocode} +\newcommand{\@RandomQ}[3][] +{% +% \end{macrocode} +% Now see if there is an \texttt{*}, and get un-stripped +% argument. +% +% The \cs{rng@isControl} lets \cs{rng@isC@ntrol} to 0 if the arg is undefined, +% and lets \cs{rng@isC@ntrol} to 1 if it is defined. +% Check the left endpoint: +% \begin{macrocode} + \let\rng@CtrlLEP=0\let\rng@CtrlUEP=0% + \let\rng@makeLEPStrict=0\let\rng@makeUEPStrict=0% +% \end{macrocode} +% \paragraph*{Left endpoint} +% \begin{macrocode} + \rng@existStar{#2}\rng@NameEndpoint{\rng@LEP}% + \if\rng@isStariii*\edef\tmp@exp{\noexpand% + \rng@isControl{\expandafter\noexpand\rng@LEP}}\tmp@exp + \if\rng@isC@ntrol1% a control sequence + \let\rng@CtrlLEP=1\let\rng@makeLEPStrict=1% +% \end{macrocode} +% The LEP is a control sequence we get its type and convert to rational +% \begin{macrocode} + \edef\tmp@exp{\noexpand% + \@getVarType{\expandafter\noexpand\rng@LEP}}\tmp@exp + \ifcase\varType % integer + \defineQ{\rng@LEP}{\rng@LEP}{1}% + \or % rational + \edef\tmp@exp{\noexpand% + \defineQ{\noexpand\rng@LEP}{\expandafter\nOf\rng@LEP}% + {\expandafter\dOf\rng@LEP}}\tmp@exp + \or % real + \defineR{\rng@LEP}{\rng@LEP}% + \RNGparseDec{\rng@LEP}% + \defineQ{\rng@LEP}{\rng@intpart}{\rng@fracpart}% + \fi + \else +% \end{macrocode} +% Not a control sequence but has a star, a number, we assume rational +% \begin{macrocode} + \RNGparseRat{\rng@LEP}% + \defineQ{\rng@UEP}{\rng@intpart}{\rng@fracpart}% + \fi + \else +% \end{macrocode} +% No star, is it a control sequence or not? +% \begin{macrocode} + \rng@isControl{#2}% + \if\rng@isC@ntrol1% a control sequence + \@getVarType{#2}% + \ifcase\varType % integer + \defineQ{\rng@LEP}{#2}{1}% + \or % rational + \defineQ{\rng@LEP}{\nOf{#2}}{\dOf{#2}}% + \or % real + \defineR{\rng@LEP}{#2}% + \RNGparseDec{\rng@LEP}% + \defineQ{\rng@LEP}{\rng@intpart}{\rng@fracpart}% + \fi + \else % a number, required to be rational + \RNGparseRat{#2}% + \defineQ{\rng@LEP}{\rng@num}{\rng@denom}% + \fi + \fi +% \end{macrocode} +% \paragraph*{Right endpoint} +% \begin{macrocode} + \rng@existStar{#3}\rng@NameEndpoint{\rng@UEP}%% + \if\rng@isStariii*% + \edef\tmp@exp{\noexpand% + \rng@isControl{\expandafter\noexpand\rng@UEP}}\tmp@exp + \if\rng@isC@ntrol1% a control sequence + \let\rng@CtrlUEP=1\let\rng@makeUEPStrict=1% +% \end{macrocode} +% The UEP is a control sequence we get its type and convert to rational +% \begin{macrocode} + \edef\tmp@exp{\noexpand% + \@getVarType{\expandafter\noexpand\rng@UEP}}\tmp@exp + \ifcase\varType % integer + \defineQ{\rng@UEP}{\rng@UEP}{1}% + \or % rational + \edef\tmp@exp{\noexpand% + \defineQ{\noexpand\rng@UEP}{\expandafter\nOf\rng@UEP}% + {\expandafter\dOf\rng@UEP}}\tmp@exp + \or % real + \defineR{\rng@UEP}{\rng@UEP}% + \RNGparseDec{\rng@UEP}% + \defineQ{\rng@UEP}{\rng@intpart}{\rng@fracpart}% + \fi + \else +% \end{macrocode} +% Not a control sequence but has a star, a number, we assume rational +% \begin{macrocode} + \RNGparseRat{\rng@UEP}% + \defineQ{\rng@UEP}{\rng@intpart}{\rng@fracpart}% + \fi + \else +% \end{macrocode} +% No star, is it a control sequence or not? +% \begin{macrocode} + \rng@isControl{#3}% + \if\rng@isC@ntrol1% a control sequence + \@getVarType{#3}% + \ifcase\varType % integer + \defineQ{\rng@UEP}{#3}{1}% + \or % rational + \defineQ{\rng@UEP}{\nOf{#3}}{\dOf{#3}}% + \or % real + \defineR{\rng@UEP}{#3}% + \RNGparseDec{\rng@UEP}% + \defineQ{\rng@UEP}{\rng@intpart}{\rng@fracpart}% + \fi + \else % a number, required to be rational + \RNGparseRat{#3}% + \defineQ{\rng@UEP}{\rng@num}{\rng@denom}% + \fi + \fi + \@@RandomQ{#1}{\nOf{\rng@LEP}}{\dOf{\rng@LEP}}% + {\nOf{\rng@UEP}}{\dOf{\rng@UEP}}% +} +% \end{macrocode} +% Once all the parameters have been acquired, and +% any needed conversions are made, we call \cs{@@RandomQ} which +% actually generates the random rational. +%\begin{verbatim} +% #1 maximum denominator permitted +% #2 numerator of first rational +% #3 denominator of first rational +% #4 numerator of second rational +% #5 denominator of second rational +%\end{verbatim} +% \begin{macrocode} +\newcommand{\@@RandomQ}[5] +{% +% \end{macrocode} +% Now take parameters \texttt{\#2}--\texttt{\#5}, and make into two rationals +% \begin{macrocode} + \updateQ\@rqi{#2}{#3}\updateQ\@rqii{#4}{#5}% +% \end{macrocode} +% Find least common multiple between \texttt{\#3}, \texttt{\#5} and \texttt{\#1} +% \begin{macrocode} + \lcm{#3}{#5}\edef\@thelcm{\thelcm}% + \def\@maxDenom{#1}% + \ifx\@maxDenom\@empty\edef\@maxDenom{\@thelcm}\else + \lcm{\@thelcm}{#1}\edef\@thelcm{\thelcm}\fi +% \end{macrocode} +% Now convert all rationals to have a denominator of \cs{@thelcm} +% \begin{macrocode} + \convertRatTo{\nOf\@rqi}{\dOf\@rqi}{\@thelcm}% + \updateQ\@@rqi{\rng@retn@num}{\@thelcm}% + \convertRatTo{\nOf\@rqii}{\dOf\@rqii}{\@thelcm}% + \updateQ\@@rqii{\rng@retn@num}{\@thelcm}% +% \end{macrocode} +% get divisor +% \begin{macrocode} + \rng@cnta=\@thelcm \divide\rng@cnta by\@maxDenom + \edef\@divisor{\the\rng@cnta}% +% \end{macrocode} +% Round up lower limit +% \begin{macrocode} + \rng@cnta=\nOf\@@rqi + \divide\rng@cnta by\@divisor + \advance\rng@cnta by1 +% \end{macrocode} +% Round down the upper limit +% \begin{macrocode} + \rng@cntb=\nOf\@@rqii\divide\rng@cntb by\@divisor +% \end{macrocode} +% If a strict inequality is requested, we creep in a little. +% \begin{macrocode} + \if\rng@makeLEPStrict1\advance\rng@cnta1\relax\fi + \if\rng@makeUEPStrict1\advance\rng@cntb-1\relax\fi +% \end{macrocode} +% construct numerator +% \begin{macrocode} + \expandafter\@getVarName\rq@currentName + \let\save@varName\@varName + \expandafter\edef\csname first@n@\@varName\endcsname{\the\rng@cnta}% + \expandafter\edef\csname last@n@\@varName\endcsname{\the\rng@cntb}% + \expandafter\edef\csname first@d@\@varName\endcsname{\@maxDenom}% + \expandafter\edef\csname last@d@\@varName\endcsname{\@maxDenom}% + \edef\rng@LEP{\csname first@n@\@varName\endcsname}% + \edef\rng@UEP{\csname last@n@\@varName\endcsname}% +%\typeout{\@varName: first=\rng@LEP,last=\rng@UEP}% + \rng@chgrand[first=\rng@LEP,last=\rng@UEP]\rand +% \end{macrocode} +% Record the random variable name, e.g., \cs{a}, ... +% \begin{macrocode} + \let\@currentName\rq@currentName + \expandafter\@getVarName\rq@currentName + \expandafter\defineQ\@currentName{\arabic{rand}}{\@maxDenom}% + \simplifyCurrentQ + \expandafter\defineQ\@currentName{\expandafter\nOf\@currentName}% + {\expandafter\dOf\@currentName}% +% \end{macrocode} +% +% \paragraph*{Constraints} +% +% We now attempt to satisfy the NE constraints. +% \begin{macrocode} + \ifx\rangen@ne\@empty\else\loopCnt=0\relax + \@whilenum\loopCnt<\maxLoopLimit\do{% + \rng@cnta=1\relax + \@for\ne@@tmp:=\rangen@ne\do{% +% \end{macrocode} +% Define a rational by the name of \cs{cmp@Name}, then make it have +% the same denominator as \cs{@currentName}. +% \begin{macrocode} + \let\save@currentName\rq@currentName + \RNGparseRat{\ne@@tmp}% + \defineQ{\cmp@Name}{\rng@num}{\rng@denom}% + \let\@varName\save@varName + \syncronizeQs{\@varName}% + \ifnum\csname n@\@varName\endcsname=\n@cmp@Name + \multiply\rng@cnta0\relax + \else + \multiply\rng@cnta1\relax + \fi + }% + \ifnum\rng@cnta=1\relax % all conditions met + \loopCnt=\maxLoopLimit + \else % if \rng@cnta \ne 1, try again + \advance\loopCnt1\relax + \ifnum\loopCnt=\maxLoopLimit + \PackageWarning{rangen}{Not all conditions met + after \maxLoopLimit\space tries}% + \else + \rng@chgrand[first=\rng@LEP,last=\rng@UEP]\rand + \expandafter\@getVarName\rq@currentName + \expandafter\defineQ\@currentName{\arabic{rand}}% + {\@maxDenom}% + \fi + \fi + }% + \fi + \simplifyCurrentQ + \expandafter\defineQ\@currentName{\expandafter\nOf\@currentName}% + {\expandafter\dOf\@currentName}% +% \end{macrocode} +% If the denominator is equal to 1, let's change the data type to an integer. +% \begin{macrocode} + \let\@currentName\rq@currentName + \expandafter\@getVarName\rq@currentName + \ifnum\csname d@\@varName\endcsname=1\relax\expandafter + \defineZ\@currentName{\expandafter\nOf\@currentName}% + \fi + \simplifyCurrentQ +} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\updateQ} +% Updates the value of a rational number, its numerator and denominator +% without changing any of the format macros. +% \begin{macrocode} +\newcommand\updateQ[3] +{% + \@getVarName#1\relax + \expandafter\edef\csname\@varName\endcsname{#2/#3}% + \expandafter\edef\csname n@\@varName\endcsname{#2}% + \expandafter\edef\csname d@\@varName\endcsname{#3}% +} +% \end{macrocode} +% \end{macro} +% A macro for performing routine adjustments on a rational number. +% \begin{macrocode} +\def\simplifyCurrentQ +{% +% \end{macrocode} +% Reduce fraction: Reduce the fraction to its lowest terms. +% \begin{macrocode} + \reduceFrac{\csname n@\@varName\endcsname}% + {\csname d@\@varName\endcsname}% +% \end{macrocode} +% \cs{reduceFrac} returns results in \cs{@tempcnta} and \cs{@tempcntb}, now +% update the numerator and denominator +% \begin{macrocode} + \expandafter\edef\csname n@\@varName\endcsname{\rfNumer}% + \expandafter\edef\csname d@\@varName\endcsname{\rfDenom}% +% \end{macrocode} +% If the numerator is zero, then zero out \cs{@varName} and special format +% \begin{macrocode} + \ifnum\csname n@\@varName\endcsname=0 + \expandafter\edef\csname\@varName\endcsname{0}% + \edef\display@TeXfmt{0}\edef\inline@TeXfmt{0}% + \else +% \end{macrocode} +% If numerator equals denominator, just replace by 1 +% \begin{macrocode} + \ifnum\csname n@\@varName\endcsname=\csname d@\@varName\endcsname + \expandafter\defineZ\csname\@varName\endcsname{1}% + \else +% \end{macrocode} +% If numerator equals -denominator, just replace by -1 +% \begin{macrocode} + \ifnum\csname n@\@varName\endcsname + =-\csname d@\@varName\endcsname + \expandafter\defineZ\csname\@varName\endcsname{-1}% + \else +% \end{macrocode} +% If denominator equals 1, modify value; otherwise, ok. +% \begin{macrocode} + \ifnum\csname d@\@varName\endcsname=1 + \expandafter\defineZ\csname\@varName\endcsname + {\csname n@\@varName\endcsname}% + \else + \expandafter\edef\csname \@varName\endcsname{% + \csname n@\@varName\endcsname/% + \csname d@\@varName\endcsname}% + \edef\display@TeXfmt{% + \frac{\csname n@\@varName\endcsname} + {\csname d@\@varName\endcsname}}% + \edef\inline@TeXfmt{% + \csname n@\@varName\endcsname/% + \csname d@\@varName\endcsname}% + \expandafter\let + \csname\@varName*\endcsname\display@TeXfmt + \fi + \fi + \fi + \fi +} +% \end{macrocode} +% \begin{macro}{\nOf} +% \begin{macro}{\dOf} +% \begin{macro}{\iOf} +% \begin{macro}{\typeOf} +% User access to numerator and denominator of random variables. +% \begin{macrocode} +\newcommand\nOf[1]{\csname n@\expandafter\@gobble\string#1\endcsname} +\newcommand\dOf[1]{\csname d@\expandafter\@gobble\string#1\endcsname} +% \end{macrocode} +% For a variable created by \cs{RandomL}, the index of the number chosen (1-based) +% can be accessed through the \cs{iOf} command. +% \begin{macrocode} +\newcommand{\iOf}[1]{\csname i@\expandafter\@gobble\string#1\endcsname} +% \end{macrocode} +% Get the type of a RV, \cs{ifnum}\cs{typeOf}\cs{a}=0 (integer), 1 (rational), 2 (real), +% 3 (literal, created by \cs{RandomP}). +% \begin{macrocode} +\newcommand\typeOf[1]{% + \csname typeof@\expandafter\@gobble\string#1\endcsname} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +%\subsubsection{Random Real} +% +% We attempt to generate a random real number, in a given interval of real numbers. +% +% The following are some data and switches used by \cs{RandomReal}. +% \begin{macrocode} +\newif\iftrailingzeros\trailingzerosfalse +\@namedef{rng@tz1}{0} +\@namedef{rng@tz2}{00} +\@namedef{rng@tz3}{000} +\@namedef{rng@tz4}{0000} +\@namedef{rng@tz5}{00000} +\@namedef{rng@tz6}{000000} +\@namedef{rng@tz7}{0000000} +\@namedef{rng@tz8}{00000000} +\def\rng@true{true}\def\rng@false{false} +\newdimen\rng@dima +\newdimen\rng@dimb +\newdimen\rng@dimc +% \end{macrocode} +% \begin{macro}{\RandomR} +% Create a real number at random within the given interval. For example, +%\begin{verbatim} +% \RandomR[<key-values>]{\a}{3.45}{6.45} +%\end{verbatim} +% \begin{macro}{round} +% \begin{macro}{showzeros} +% The key-value pairs recognized by \cs{RandomZ|Q|R}. +% \begin{macrocode} +\define@key{rangen}{ne}[]{\edef\rangen@ne{#1}} +\define@key{rangen}{round}[]{\def\rangen@round{#1}} +\define@key{rangen}{showzeros}[]{\def\rangen@showzeros{#1}% + \ifx\rangen@showzeros\@empty\global\trailingzerostrue\else + \ifx\rangen@showzeros\rng@true\global\trailingzerostrue\else + \global\trailingzerosfalse\fi\fi} +\define@key{rangen}{index}[]{\edef\rangen@index{#1}} +% \end{macrocode} +% \end{macro} +% \end{macro} +%\begin{verbatim} +%[#1] = options +% #2 = name of real to correct +% #3 = lower endpoint of interval +% #4 = upper endpoint of interval +%\end{verbatim} +% \begin{macrocode} +\newcommand{\RandomR}[4][]{% + \setkeys{rangen}{ne,round,showzeros=false,#1}% +% \end{macrocode} +% Now see if there is an \texttt{*}, and get un-stripped +% argument. +% +% The \cs{rng@isControl} lets \cs{rng@isC@ntrol} to 0 if the arg is undefined, +% and lets \cs{rng@isC@ntrol} to 1 if it is defined. +% Check the left endpoint: +% \begin{macrocode} + \let\rng@CtrlLEP=0\let\rng@CtrlUEP=0% + \let\rng@makeLEPStrict=0\let\rng@makeUEPStrict=0% + \def\rng@lcg@first{0}\edef\rng@lcg@last{\RNGpowerOfTen}% +% \end{macrocode} +% \paragraph{Left endpoint.} Check the left endpoint: +% \begin{macrocode} + \rng@existStar{#3}\rng@NameEndpoint{\rng@LEP}% + \if\rng@isStariii*\edef\tmp@exp{\noexpand% + \rng@isControl{\expandafter\noexpand\rng@LEP}}\tmp@exp + \if\rng@isC@ntrol1% a control sequence + \let\rng@CtrlLEP=1\let\rng@makeLEPStrict=1% + \def\rng@lcg@first{1}% +% \end{macrocode} +% The LEP is a control sequence with a star. Convert LEP to a real +% number as needed. +% \begin{macrocode} + \edef\tmp@exp{\noexpand% + \@getVarType{\expandafter\noexpand\rng@LEP}}\tmp@exp + \ifcase\varType % integer + \defineR{\rng@LEP}{\rng@LEP\RNG@Dec}% + \or % rational + \rng@dima=\expandafter\nOf\rng@LEP pt + \divide\rng@dima by\expandafter\dOf\rng@LEP + \defineR{\rng@LEP}{\strip@pt\rng@dima}% + \or % real + \defineR{\rng@LEP}{\rng@LEP}% + \fi +% \end{macrocode} +% Not a control sequence, but has a star +% \begin{macrocode} + \else + \defineR{\rng@LEP}{\rng@LEP}% + \fi + \else +% \end{macrocode} +% No star, control sequence or not? +% \begin{macrocode} + \rng@isControl{#3}% + \if\rng@isC@ntrol1% control sequence + \let\rng@CtrlLEP=1\def\rng@LEP{#3}% + \@getVarType{#3}% + \ifcase\varType % integer + \defineR{\rng@LEP}{\rng@LEP\RNG@Dec}% + \or % rational + \rng@dima=\nOf{#3}pt + \divide\rng@dima by\dOf{#3}% + \defineR{\rng@LEP}{\strip@pt\rng@dima}% + \or % real + \defineR{\rng@LEP}{\rng@LEP}% + \fi + \else +% \end{macrocode} +% A number, no star, number is required to be real +% \begin{macrocode} + \defineR{\rng@LEP}{#3}% + \fi + \fi +% \end{macrocode} +% \paragraph{Right endpoint.} Check the right endpoint: +% \begin{macrocode} + \rng@existStar{#4}\rng@NameEndpoint{\rng@UEP}% + \if\rng@isStariii*\edef\tmp@exp{\noexpand% + \rng@isControl{\expandafter\noexpand\rng@UEP}}\tmp@exp + \if\rng@isC@ntrol1% a control sequence + \let\rng@CtrlUEP=1\let\rng@makeUEPStrict=1% + \rng@cnta=\rng@lcg@last\advance\rng@cnta-1\relax + \edef\rng@lcg@last{\the\rng@cnta}% +% \end{macrocode} +% The UEP is a control sequence with a star. Convert UEP to a real +% number as needed. +% \begin{macrocode} + \edef\tmp@exp{\noexpand% + \@getVarType{\expandafter\noexpand\rng@UEP}}\tmp@exp + \ifcase\varType % integer + \defineR{\rng@UEP}{\rng@UEP\RNG@Dec}% + \or % rational + \rng@dima=\expandafter\nOf\rng@UEP pt + \divide\rng@dima by\expandafter\dOf\rng@UEP + \defineR{\rng@UEP}{\strip@pt\rng@dima}% + \or % real + \defineR{\rng@UEP}{\rng@UEP}% + \fi +% \end{macrocode} +% Not a control sequence, but has a star +% \begin{macrocode} + \else + \defineR{\rng@UEP}{\rng@UEP}% + \fi + \else +% \end{macrocode} +% No star, control sequence or not? +% \begin{macrocode} + \rng@isControl{#4}% + \if\rng@isC@ntrol1% control sequence + \let\rng@CtrlUEP=1\def\rng@UEP{#4}% + \@getVarType{#4}% + \ifcase\varType % integer + \defineR{\rng@UEP}{\rng@UEP\RNG@Dec}% + \or % rational + \rng@dima=\nOf{#4}pt + \divide\rng@dima by\dOf{#4}% + \defineR{\rng@UEP}{\strip@pt\rng@dima}% + \or % real + \defineR{\rng@UEP}{\rng@UEP}% + \fi + \else +% \end{macrocode} +% A number, no star, number is required to be real +% \begin{macrocode} + \defineR{\rng@UEP}{#4}% + \fi + \fi +% \end{macrocode} +% Prepare to generate the random real +% \begin{macrocode} + \def\@currentName{#2}\@getVarName{#2}% +% \end{macrocode} +% Save upper and lower endpoints where they are expected to be. +% \begin{macrocode} + \expandafter\edef\csname first@\@varName\endcsname{\rng@LEP}% + \expandafter\edef\csname last@\@varName\endcsname{\rng@UEP}% +% \end{macrocode} +% Get a random real, and declare it to be a real number using \cs{defineR}. +% \begin{macrocode} + \rng@getRandomR + \defineR{#2}{\strip@pt\rng@dima}% +% \end{macrocode} +% Round and remove trailing zeros. +% \begin{macrocode} + \ifx\rangen@round\@empty\else + \RNGround{#2}{#2}{\rangen@round}% + \rng@dima=#2pt\relax + \defineR{#2}{\strip@pt\rng@dima}% + \fi +% \end{macrocode} +% \paragraph{Constraints.} Let's try to apply constraints. We only allow one constraint. +% \begin{macrocode} + \ifx\rangen@ne\@empty\else\loopCnt=0\relax + \@whilenum\loopCnt<\maxLoopLimit\do{% + \rng@cnta=1\relax + \@for\ne@@tmp:=\rangen@ne\do{% + \rng@dima=#2pt + \ifdim\rng@dima=\ne@@tmp pt\relax + \multiply\rng@cnta0\relax\else + \multiply\rng@cnta1\relax\fi + }% + \ifnum\rng@cnta=1\relax % all conditions met + \loopCnt=\maxLoopLimit + \else % if \rng@cnta \ne 1, try again + \advance\loopCnt1\relax + \ifnum\loopCnt=\maxLoopLimit + \PackageWarning{rangen}{Not all conditions met + after \maxLoopLimit\space tries}% + \else + \rng@getRandomR + \defineR{#2}{\strip@pt\rng@dima}% +% \end{macrocode} +% Round and remove trailing zeros. +% \begin{macrocode} + \ifx\rangen@round\@empty\else + \RNGround{#2}{#2}{\rangen@round}% + \rng@dima=#2pt\relax + \defineR{#2}{\strip@pt\rng@dima}% + \fi + \fi + \fi + }% + \fi +% \end{macrocode} +% \paragraph{Formatting.} Begin formatting of the real, keys recognized are +% \texttt{round} and \texttt{showzeros}. +% \begin{macrocode} + \rnd@ProcessRealFormat{#2}% +% \end{macrocode} +% We declare our number. +% \begin{macrocode} + \def\@currentName{#2}% + \defineR{#2}{#2}% +} +% \end{macrocode} +% Get a new random real and return it in the \cs{rng@dima} +% \begin{macrocode} +\def\rng@getRandomR{% +% \end{macrocode} +% Put the endpoints in dimension registers so we can subtract them. +% \begin{macrocode} + \rng@dima=\rng@LEP pt + \rng@dimb=\rng@UEP pt +% \end{macrocode} +% Compute the difference between upper and lower, then strip off the \texttt{pt}, +% to make it a decimal number. +% \begin{macrocode} + \advance\rng@dimb-\rng@dima +% \edef\r@getDiff{\strip@pt\rng@dimb}% +% \end{macrocode} +% Get a random integer from the interval 0 to \cs{RNGpowerOfTen}. +% the default value of the latter command is 100, and it can be changed +% using \cs{nDivisionsPowerOfTen}. The idea is to divide the interval +% from the lower bound to the upper bound into \cs{RNGpowerOfTen} nodes, +% and we choose one of these nodes are random. +% +% If the endpoints where strict, then we changed \cs{rng@lcg@first} +% from 0 to 1 (if the lower endpoint is strict); and changed +% \cs{rng@lcg@last} from \cs{RNGpowerOfTen} to \texttt{\string\RNGpowerOfTen-1} +% (if the upper end point is strict). +% \begin{macrocode} + \rng@chgrand[first=\rng@lcg@first,last=\rng@lcg@last]\rand +% \end{macrocode} +% Divide the length of the interval by \cs{RNGpowerOfTen}, +% and store the result in \cs{rng@dimb}, then multiply +% that by \verb!\arabic{rand}!. +% \begin{macrocode} + \divide\rng@dimb by\RNGpowerOfTen\relax + \rng@dimb=\arabic{rand}\rng@dimb +% \end{macrocode} +% Finally, the left-end point is still in \cs{rng@dima} +% we add the result in \cs{rng@dimb} to \cs{rng@dima} +% to compute our random rational. +% \begin{macrocode} + \advance\rng@dima by\rng@dimb +} +\def\rnd@ProcessRealFormat#1{% + \ifx\rangen@round\@empty + \rng@dima=#1pt\relax + \defineR{#1}{\strip@pt\rng@dima}% + \else + \RNGround{#1}{#1}{\rangen@round}% + \rng@dima=#1pt\relax + \defineR{#1}{\strip@pt\rng@dima}% + \iftrailingzeros + {\RNGparseDec{#1}\count0=\decPls\relax + \advance\count0-\rndnDec\relax + \ifnum\count0>0\relax\xdef#1{% + \rng@intpart\RNG@Dec\rng@fracpart% +\@nameuse{rng@tz\the\count0}}% + \fi}% + \defineR{#1}{#1}% + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Random Sign} +% \begin{macro}{\RandomS} +% We randomly generate a \texttt{+} or \texttt{-} sign +% for addition and subtraction. The first optional argument +% is a rational number between 0 and 1. The default is \texttt{1/2}. +% This number represents the probably of a \texttt{+} sign. +% \begin{macrocode} +\newcommand{\RandomS}[2][1/2]{% + \RNGparseRat{#1}% + \ifnum\rng@num<0\relax + \PackageError{rangen}{A positive numerator is required}% + {The rational number must be between 0 and 1}\fi + \ifnum\rng@denom<0\relax + \PackageError{rangen}{A positive denominator is required}% + {The rational number must be between 0 and 1}\fi + \ifnum\rng@num>\rng@denom\relax + \PackageError{rangen}{The rational must be between 0 and 1}% + {The rational number must be between 0 and 1}\fi + \rng@chgrand[first=1,last=\rng@denom]\rand + \@getVarName{#2}% + \ifnum\value{rand}>\rng@num\relax\def#2{-}% + \rng@makeMinusOneFmtDefns + \def\display@TeXfmt{-}\def\inline@TeXfmt{-}% + \else\def#2{+}\rng@makeOneFmtDefns + \def\display@TeXfmt{}\def\inline@TeXfmt{}\fi + \expandafter\let\csname\@varName*\endcsname\display@TeXfmt + \expandafter\let\csname\@varName!\endcsname\inline@TeXfmt +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Random Number from a List} +% +% \begin{macro}{\RandomL} +% Select a number of any type from a comma-delimited list. +%\begin{verbatim} +% \RandomL[key-values]{\RV}{<comma-delimited list>} +%\end{verbatim} +% Currently, the only key recognized is the \texttt{index} key. +% If the \texttt{index} key is specified, the number whose index is specified +% is retrieved from the list. +% \begin{macrocode} +\newcommand{\RandomL}[3][]{% + \let\rangen@index\@empty + \setkeys{rangen}{#1}% + \rng@cnta=0\relax\@for\@@tmp:=#3\do{% + \advance\rng@cnta1\relax}\edef\n@rng@listItems{\the\rng@cnta}% + \ifx\rangen@index\@empty + \rng@chgrand[first=1,last=\n@rng@listItems]\rand + \else + \rng@cnta=\rangen@index + \advance\rng@cnta-1\relax + \amodb{\rng@cnta}{\n@rng@listItems}% + \rng@cnta=\retnmod + \advance\rng@cnta1\relax + \value{rand}=\rng@cnta + \fi + \@getVarName{#2}% + \expandafter\edef\csname i@\@varName\endcsname{\arabic{rand}}% + \rng@cnta=0\relax\@for\@@tmp:=#3\do{% + \advance\rng@cnta1\relax\ifnum\rng@cnta=\arabic{rand}% + \edef\rng@choice{\@@tmp}\fi}% + \def\@currentName{#2}% +% \end{macrocode} +% Now, determine the type of this choice, and make appropriate +% data type definition. +% \begin{macrocode} + \RNGparseDec{\rng@choice}% + \if\rng@parseR@iii\RNG@Dec\defineR{#2}{\rng@choice}% + \else\RNGparseRat{\rng@choice}% + \if\rng@parseQ@iii/\defineQ{#2}{\rng@num}{\rng@denom}% + \else\defineZ{#2}{\rng@choice}\fi\fi +} +% \end{macrocode} +% \end{macro} + +% \subsubsection{Random Problem from a List} +% +% \begin{macro}{\RandomP} +% Select a literal from a comma-delimited list of literals. +%\begin{verbatim} +% \RandomP[key-values]{\RV}{<comma-delimited list>} +%\end{verbatim} +% Currently, the only key recognized is the \texttt{index} key. +% If the \texttt{index} key is specified, the number whose index is specified +% is retrieved from the list. +% \begin{macrocode} +\newcommand{\RandomP}[3][]{% + \let\rangen@index\@empty + \setkeys{rangen}{#1}% + \rng@cnta=0\relax\@for\@@tmp:=#3\do{% + \advance\rng@cnta1\relax}\edef\n@rng@listItems{\the\rng@cnta}% + \ifx\rangen@index\@empty + \rng@chgrand[first=1,last=\n@rng@listItems]\rand + \else + \rng@cnta=\rangen@index + \advance\rng@cnta-1\relax + \amodb{\rng@cnta}{\n@rng@listItems}% + \rng@cnta=\retnmod + \advance\rng@cnta1\relax + \value{rand}=\rng@cnta + \fi + \@getVarName{#2}% + \expandafter\edef\csname i@\@varName\endcsname{\arabic{rand}}% + \rng@cnta=0\relax\@for\@@tmp:=#3\do{% + \advance\rng@cnta1\relax\ifnum\rng@cnta=\arabic{rand}% + \rng@toks=\expandafter{\@@tmp}\edef#2{\the\rng@toks}% + \expandafter\def\csname typeof@\@varName\endcsname{3}\fi}% +} +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Random Index} +% +% \begin{macro}{\RandomI} +% This command creates an implied list of \verb!{1, 2, 3,...,n}!, +% and randomly selects a number from this list. The result is +% defined as an integer, and held in the macro \texttt{\#1}. +%\begin{verbatim} +% \Random{\i}{n} --> select \i from {1, 2, 3,...,n} at random +%\end{verbatim} +%A random index, \cs{i}, created by \cs{RandomI}, can be used +%in the \cs{RandomL} command; for example, +%\begin{verbatim} +% \RandomL[index=\i]{\a}{17,\rPI,3/4,\rE,88,1/2} +%\end{verbatim} +%The value of \cs{a} is determined by the index \cs{i}. +% \begin{macrocode} +\newcommand{\RandomI}[2]{% + \rng@chgrand[first=1,last=#2]\rand + \defineZ{#1}{\arabic{rand}}% + \expandafter\edef\csname i@\@varName\endcsname{\arabic{rand}}% +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Some Constants} +% +% \begin{macro}{\zZero} +% \begin{macro}{\zOne} +% \begin{macro}{\zMinusOne} +% \begin{macro}{\rPI} +% \begin{macro}{\rE} +% Define three convenience integers corresponding to $0$, $1$, and $-1$. +% \begin{macrocode} +\defineZ{\zZero}{0} +\defineZ{\zOne}{1} +\defineZ{\zMinusOne}{-1} +\defineR{\rPI}{3.1415927} +\defineR{\rE}{2.7182818} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% This macro takes \cs{@varName} and \cs{cmp@Name} and converts to the same common +% denominator. This makes it easy to make comparisons between two rational numbers. +% \begin{macrocode} +\def\syncronizeQs#1{\edef\sync@arg{#1}% + \lcm{\csname d@\sync@arg\endcsname}{\d@cmp@Name}% + \edef\@thelcm{\thelcm}% + \convertRatTo{\n@cmp@Name}{\d@cmp@Name}{\@thelcm}% + \updateQ\cmp@Name{\rng@retn@num}{\@thelcm}% + \convertRatTo{\csname n@\sync@arg\endcsname}% + {\csname d@\sync@arg\endcsname}{\@thelcm}\expandafter + \defineQ\csname\sync@arg\endcsname{\rng@retn@num}{\@thelcm}% +} +% \end{macrocode} +% \subsection{Formatting Commands} +% \begin{macro}{\ds} +% \begin{macro}{\eds} +% \begin{macro}{\cds} +% Displays an alternate representation (\textbf display\textbf style) of the random variable. Usage \cs{ds}\cs{a}. +% This displays the contents of \cs{display@TeXfmt} for this variable. The value of \cs{display@TeXfmt} +% is effected by the formatting commands above. +% +% For a rational number \cs{a}, the expression \cs{ds}\cs{a} expands either to the special format representation, or +% to a rational of the form $\frac{p}{q}$. +% \begin{macrocode} +\newcommand\ds[1]{% + \expandafter\csname\expandafter\@gobble\string#1*\endcsname +} +\newcommand\eds[1]{% + \expandafter\csname\expandafter\@gobble\string#1*e\endcsname +} +\newcommand\cds[1]{% + \expandafter\csname\expandafter\@gobble\string#1*c\endcsname +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}{\fmt} +% \begin{macro}{\efmt} +% \begin{macro}{\cfmt} +% Displays a special format for the random variable. Usage \cs{ds}\cs{a}. +% This displays the contents of \cs{display@TeXfmt} for this variable. +% Same as \cs{ds}, but does not display a display style if there is not +% special formatting. +% +% For a rational number \cs{a}, the expression \cs{ds}\cs{a} expands either to the special format representation, or +% to a rational of the form $p/q$. +% \begin{macrocode} +\newcommand\fmt[1]{% + \expandafter\csname\expandafter\@gobble\string#1!\endcsname +} +\newcommand\efmt[1]{% + \expandafter\csname\expandafter\@gobble\string#1!e\endcsname +} +\newcommand\cfmt[1]{% + \expandafter\csname\expandafter\@gobble\string#1!c\endcsname +} +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}{\js} +% Used within \cs{CorrAnsButton} to get a more precise expansion of a variable. Used with variables +% that have been defined using \cs{defineDepVar}. When you say \verb+\js\m+, for example, +% the \cs{eval@JSfmt} is expanded. +% \par\medskip\noindent +% \textbf{Usage:} \verb+\CorrAnsButton*{y = \js\m\space x }+ +% \begin{macrocode} +\newcommand\js[1]{% + \expandafter\csname\expandafter\@gobble\string#1!*\endcsname +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Commands specialized to Reals} + +% \begin{macro}{\RNGround} +% Round \texttt{\#1} to \texttt{\#3} decimal places, and leave result in \texttt{\#2}. +% \begin{macrocode} +\def\RNGround#1#2#3{% + \begingroup + \def\rng@ctrlName{#1}% + \def\rng@sourceName{#2}% + \def\rng@nDecPl{#3}% + \RNGparseDec{#2}% + \ifnum\rndnDec<#3\relax + \xdef\theseDigits{\rng@fracpart}% + \let\rng@next\relax + \else + \count0=0\relax + \gdef\theseDigits{}% + \def\rng@next{\expandafter\@rng@round\rng@fracpart\end}% + \fi + \rng@next + \xdef\decPls{\@ifundefined{save@rng@nDecPl}% + {\rng@nDecPl}{\save@rng@nDecPl}}% + \ifx\theseDigits\@empty + \xdef#1{\rng@intpart}\else + \xdef#1{\rng@intpart\RNG@Dec\theseDigits}\fi + \endgroup +} +\def\@rng@round#1{% + \ifx#1\end\let\rng@next\relax + \else + \ifnum\rng@nDecPl=0\relax + \ifnum#1>4\relax + \count0=\rng@intpart\relax + \ifnum\rng@intpart<0\relax + \advance\count0by-1\relax + \else + \advance\count0by1\relax + \fi + \xdef\rng@intpart{\the\count0}% + \fi + \gdef\theseDigits{}% + \let\rng@next\rng@gobbletoend + \else + \advance\count0by1\relax + \ifnum\count0=\rng@nDecPl\relax + \def\rng@next{\@@rng@round#1}% + \else + \xdef\theseDigits{\theseDigits#1}% + \let\rng@next\@rng@round + \fi + \fi + \fi + \rng@next +} +\def\rng@gobbletoend#1\end{} +\def\@@rng@round#1#2{% + \ifx#2\end% + \xdef\theseDigits{\theseDigits#1}% + \let\rng@next\relax + \else + \ifnum#2>4\relax\count2=#1\relax + \ifnum\count2=9\relax + \count0=\rng@nDecPl\relax + \ifnum\count0=1\relax + \count0=\rng@intpart\relax + \ifnum\rng@intpart<0\relax + \advance\count0by-1\relax + \else + \advance\count0by1\relax + \fi + \xdef\rng@intpart{\the\count0}% + \let\rng@next\rng@gobbletoend + \else + \advance\count0by-1\relax\expandafter + \xdef\rng@sourceName{% + \rng@intpart\RNG@Dec\theseDigits#1}% + \edef\save@rng@nDecPl{\rng@nDecPl}% + \edef\rng@next{\noexpand\RNGround{% + \expandafter\noexpand\rng@ctrlName}% + {\expandafter\noexpand\rng@sourceName}% + {\the\count0}\noexpand\rng@gobbletoend}% + \fi + \else + \advance\count2by1\relax + \xdef\theseDigits{\theseDigits\the\count2}% + \let\rng@next\rng@gobbletoend + \fi + \else % \ifnum#2<=4 + \xdef\theseDigits{\theseDigits#1}% + \let\rng@next\rng@gobbletoend + \fi + \fi + \rng@next +} +% \end{macrocode} +% \end{macro} +% Used with \cs{CorrAnsButton} and \texttt{rngCorrAnsButton}, like so, +%\begin{verbatim} +% \CorrAnsButton{rEval(\strAns)}*{rngCorrAnsButton\RNGprintf{\%.2f}} +%\end{verbatim} +% \begin{macrocode} +\def\RNGprintf#1{("#1",\@gobble} +% \end{macrocode} +% +% \subsection{User Defined Dependent Variables for JavaScript} +% \begin{macro}{\defineDepQJS} +% Define a rational as a function of other integers. This macro defines +% \cs{fmt} and \cs{ds} for the variable, but its primary use it +% for \cs{js}. This command is aimed at the JavaScript side of things +%\begin{verbatim} +%#1 = name of rational to be defined, e.g., \a +%#2 = numerator +%#3 = denominator +%#4 = \js expression for #1 +%\end{verbatim} +% Usage: +%\begin{verbatim} +% \defineDepQJS{\m}{\d-\b}{\c-\a} +% {rFrac(rEval(\nOf\m)/rEval(\dOf\m))} +% ... +% \CorrAnsButton{y = \js\m\space x}*{rngCorrAnsButton}% +%\end{verbatim} +% The above example would calculate equation of the line passing through +% the two points \verb!P(\a,\b)! and \verb!Q(\c,\d)!. The code is used +% in the \cs{CorrAnsButton} to have the answer appear. +% \begin{macrocode} +\newcommand\defineDepQJS[4]{% + \@getVarName#1 + \expandafter\edef\csname\@varName\endcsname{(#2)/(#3)}% + \expandafter\edef\csname n@\@varName\endcsname{(#2)}% + \expandafter\edef\csname d@\@varName\endcsname{(#3)}% + \edef\display@TeXfmt{\csname\@varName\endcsname}% + \edef\inline@TeXfmt{\csname\@varName\endcsname}% + \def\dv@argiv{#4}\ifx\dv@argiv\@empty + \edef\eval@JSfmt{\csname\@varName\endcsname}\else + \edef\eval@JSfmt{#4}\fi + \expandafter\let\csname\@varName!*\endcsname\eval@JSfmt +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Writing RVs to Solution Files} +% \begin{macrocode} +\def\rng@writeCurrentSeed#1{\immediate\write#1{\string\makeatletter + \string\global\string\cr@nd=\the\cr@nd\string\relax + \string\makeatother}} +% \end{macrocode} +% Token register to hold the verbatim contents of the \texttt{writeRVsTo} environment. +% \begin{macrocode} +\newtoks\rng@toks +\def\wrv@ex@solns{exercises}% +\def\wrv@ex@quiz{quizzes}% +% \end{macrocode} +% \begin{environment}{writeRVsTo} +% This environment takes its environment contents and writes it to +% two files, one file is \cs{jobname\_rvs.cut} which is input back +% into the source file immediately. The second parameter +% accepts the string \texttt{exercises} or \texttt{quizzes}, or a write +% handle to write to an auxiliary file. The environment was designed for +% use with the \texttt{exercise}, \texttt{quiz}, and \texttt{shortquiz} environments +% of \textsf{exerquiz}. +% \begin{macrocode} +\newenvironment{writeRVsTo}[2][] +{% + \def\wrv@argii{#2}% + \ifx\wrv@argii\wrv@ex@quiz\let\wrv@out\quiz@solns + \else\ifx\wrv@argii\wrv@ex@solns\let\wrv@out\ex@solns\else + \let\wrv@out#2\fi\fi + \rng@writeCurrentSeed\wrv@out + \rng@toks={}% + \def\verbatim@processline{% + \xdef\rng@temp{\the\rng@toks\the\verbatim@line}% + \global\rng@toks=\expandafter{\rng@temp}}% + \let\do\@makeother\dospecials\catcode`\^^M\active + #1% + \verbatim@start +}{ \immediate\write\wrv@out{\the\rng@toks}% + \newwrite\rng@writeRVs + \immediate\openout\rng@writeRVs\jobname_rvs.cut + \immediate\write\rng@writeRVs{\the\rng@toks}% + \immediate\closeout\rng@writeRVs + \aftergroup\rng@Input@RVs +} +% \end{macrocode} +% \end{environment} +% After the \texttt{writeRVsTo} environment writes the RVs to +% \cs{jobname\_rvs.cut}. The environment executes +% \cs{rng@Input@RVs} to input the file back into the source file. +% \begin{macrocode} +\def\rng@Input@RVs{\InputIfFileExists{\jobname_rvs.cut}{}{}} +% \end{macrocode} +% \subsection{Redefine lcg Package Macro} +% \begin{macrocode} +\def\rng@p@stkeysr@nd{% + \@rderr@nd% last < first -> swap + \cutr@nger@nd% range too big -> cut +} % end of \def\p@stkeysr@nd +\def\rng@chgrand{\@ifnextchar[\rng@chgr@nd{\rng@chgr@nd[]}} +\def\rng@chgr@nd[#1]{% + \@tempcnta=\z@ + \@tempcntb=\z@ + \setkeys{Init}{#1}% + \rng@p@stkeysr@nd% + \@utputr@nd% +} % end of \def\rng@chgrand +% \end{macrocode} +% \subsection{DLJS Support} +% \begin{macrocode} +\begin{insDLJS}[partialExpand]{partial}{Rangen} +var partre = /rEval|rFrac/; +% \end{macrocode} +% The arguments for this function take two forms +% (1) \texttt{fieldname}, \texttt{theanswer} (the default); (2) +% \texttt{theformat}, \texttt{fieldname}, \texttt{theanswer}. The later case +% is created by using the \cs{RNGprintf} command that inserts allows the +% document author to insert a \texttt{printf} formatting template. For example, +%\begin{verbatim} +% \CorrAnsButton{rEval(\strAns)}*{rngCorrAnsButton\RNGprintf{\%.4f}} +%\end{verbatim} +% \begin{macrocode} +function rngCorrAnsButton() +{ + var theprecision,fieldname,theanswer; + if (arguments.length==4) { + var theformat=arguments[0]; + var fieldname=arguments[1]; + var theanswer=arguments[2]; + var thequiz=arguments[3]; + } else { + var fieldname=arguments[0]; + var theanswer=arguments[1]; + var thequiz=arguments[2]; + } + theanswer = partialExpand(0,theanswer); + if (arguments.length==4) + theanswer=util.printf(theformat,eval(theanswer)); + DisplayAnswer(fieldname,theanswer,thequiz); +} +% \end{macrocode} +% The JavaScript function \texttt{partialExpand} searches through \texttt{Ans} in search of +% \texttt{rEval} and \texttt{rFrac}. It calls itself recursively to search for the inner most +% appearances of these two functions. It evaluates these two functions starting with the inner +% most and working its way outward. +% \begin{macrocode} +function partialExpand(level,Ans) +{ + Ans = correctPlusMinus(Ans) + level += 1; +\db console.println("Enter level = " + level +": Ans: " + Ans);\db% + var n=0, m, bP, eP, subExp; + while ( true ) { +\db console.println("Searching a level " + level);\db% + try { m = Ans.match(partre); } + catch (e) { break; } + if ( m != null ) { + bP = m.index + m[0].length; + eP = FindBalP(Ans, bP, true); +\db console.println("bP = " + bP + " : eP = " + eP);\db% + var subExp = Ans.substring(bP+1, eP); +\db console.println("Found \'" + subExp% + + "\' at level = " + level);\db% +% subExp = partialExpand(level, subExp); + // n = beginning of "rEval", + // eP = beginning of balanced parens, + // bP = end of balanced parens + Ans = Ans.substring(0, m.index) + + eval(m[0]+"(level,subExp)") + Ans.substring(eP+1); +\db console.println("level = " + level% + +": New Ans: " + Ans);\db % + } else { + if ( level == 1 ) { +\db console.println("Level 1 break");\db % + break; + } + } + } +\db console.println("Return Ans: " + Ans);\db% + Ans = correctPlusMinus(Ans); + return Ans; +} +% \end{macrocode} +% Evaluates the value of \texttt{Ans}. +% \begin{macrocode} +function rEval(level, Ans) +{ + level += 1; +\db console.println("Enter rEval: level = "% + + level +": Ans: " + Ans);\db% + var n=0, m, bP, eP, subExp; + while ( true ) + { +\db console.println("Searching a level " + level);\db% + try { m = Ans.match(partre); } + catch (e) { break; } + if ( m != null ) { + bP = m.index + m[0].length; + eP = FindBalP(Ans, bP, true); +\db console.println("bP = " + bP + " : eP = " + eP);\db% + var subExp = Ans.substring(bP+1, eP); +\db console.println("Found \'" + subExp% + + "\' at level = " + level);\db% + // n = beginning of "rEval", + // eP = beginning of balanced parens, + // bP = end of balanced parens + Ans = Ans.substring(0, m.index) + +eval(m[0]+"(level,subExp)")+Ans.substring(eP+1); +\db console.println("level = "% + + level +": New Ans: " + Ans);\db % + } else { + Ans = ParseInput(Ans); +\db console.println("Ready to eval at level = "% + + level + ": Ans = " + Ans);\db% + with(Math) { Ans = eval( Ans ) }; +\db console.println("After eval at level = "% + + level + ": Ans = " + Ans);\db% + break; + } + } +\db console.println("Return Ans: " + Ans);\db% + return Ans; +} +% \end{macrocode} +% Evaluates an rational number by evaluating the value of the numerator and denominator separately. +% \begin{macrocode} +function rFrac(level, Ans) +{ + level += 1; +\db console.println("Enter rFrac level = "% + + level +": Ans: " + Ans);\db% + var n=0, m, bP, eP, subExp; + while ( true ) { +\db console.println("Searching a level " + level);\db% + try { m = Ans.match(partre); } + catch (e) { break; } + if ( m != null ) { + bP = m.index + m[0].length; + eP = FindBalP(Ans, bP, true); +\db console.println("bP = "% + + bP + " : eP = " + eP);\db% + var subExp = Ans.substring(bP+1, eP); +\db console.println("Found \'" + subExp% + + "\' at level = " + level);\db% + // n = beginning of "rEval", + // eP = beginning of balanced parens, + // bP = end of balanced parens + Ans = Ans.substring(0, m.index) + + eval(m[0]+"(level,subExp)") + Ans.substring(eP+1); +\db console.println("level = " + level% + +": New Ans: " + Ans);\db % + } else { + var numDenom = Ans.split("/"); + numDenom[0] = eval(numDenom[0]); + numDenom[1] = eval(numDenom[1]); + var g = gcd(numDenom[0], numDenom[1]); + numDenom[0] /= g; + numDenom[1] /= g; + if ( numDenom[1] == 1) + Ans = numDenom[0]; + else + Ans = numDenom.join("/"); +\db console.println("Reduce: " + numDenom.join("/"));\db% + break; + } + } +\db console.println("Return Ans: " + Ans);\db% + return Ans; +} +function correctPlusMinus(Ans) +{ + Ans = "" + Ans; + Ans = Ans.replace(/\s*([\+-])\s*\1\s*/g, " + "); + Ans = Ans.replace(/\s*\+\s*-\s*/g, " - "); +% Ans = Ans.replace(/\s*\+\s*\+\s*/g, " + "); +% Ans = Ans.replace(/\s*-\s*-\s*/g, " + "); + Ans = Ans.replace(/\s*-\s*\+\s*/g, " - "); + return Ans; +} +function gcd(a,b) +{ + var x = a, y = b, r; + while (true) + { + r = x \% y; + if ( r == 0 ) break; + x = y; + y = r; + } + return Math.abs(y); +} +function lcm (a,b) { return (a*b)/gcd(a,b); } +\end{insDLJS} +%</package> +% \end{macrocode} +\endinput |