%\iffalse % makeindex -s gglo.ist -o ran_toks.gls ran_toks.glo % makeindex -s gind.ist -o ran_toks.ind ran_toks.idx %<*copyright> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% ran_toks package, %% %% Copyright (C) 1999-2021 D. P. Story %% %% dpstory@uakron.edu, dpstory@acrotex.net %% %% %% %% 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. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %\NeedsTeXFormat{LaTeX2e}[1997/12/01] %\ProvidesPackage{ran_toks} % [2021/06/06 v1.4 Randomizing tokens (dps)] %<*driver> \documentclass{ltxdoc} \usepackage[colorlinks,hyperindex=false]{hyperref} \usepackage{fancyvrb} %\def\texorpdfstring#1#2{#1} %\pdfstringdefDisableCommands{\let\\\textbackslash} \OnlyDescription % comment out for implementation details \EnableCrossrefs \CodelineIndex \RecordChanges \bgroup\ttfamily \gdef\brpr#1{\char123\relax#1\char125\relax}\egroup \let\darg\brpr \let\env\texttt \let\opt\texttt \let\app\textsf \def\visispace{\symbol{32}} \def\ameta#1{\ensuremath{\langle\textit{\texttt{#1}}\rangle}} \def\meta#1{\textsl{\texttt{#1}}} \def\SUB#1{\ensuremath{{}_{\mbox{\scriptsize\ttfamily#1}}}} \def\cs#1{\texttt{\bslash#1}} \makeatletter \let\@latex@warning@no@line\@gobble \makeatother \InputIfFileExists{aebdocfmt.def}{\PackageInfo{ran_toks}{Inputting aebdocfmt.def}} {\def\IndexOpt{\DescribeMacro}\def\IndexKey{\DescribeMacro}\let\setupFullwidth\relax \PackageInfo{ran_toks}{aebdocfmt.def cannot be found}} \begin{document} \def\CMD#1{\textbackslash#1} \let\pkg\textsf \let\opt\texttt \addtolength{\marginparwidth}{3pt} \GetFileInfo{ran_toks.sty} \title{The \textsf{ran\_toks} Package} \author{D. P. Story\\ Email: \texttt{dpstory@uakron.edu}} \date{processed \today} \maketitle \tableofcontents \DocInput{ran_toks.dtx} \IfFileExists{\jobname.ind}{\newpage\setupFullwidth\par\PrintIndex}{\paragraph*{Index} The index goes here.\\Execute \texttt{makeindex -s gind.ist -o ran\_toks.ind ran\_toks.idx} on the command line and recompile \texttt{ran\_toks.dtx}.} \IfFileExists{\jobname.gls}{\PrintChanges}{\paragraph*{Change History} The list of changes goes here.\\Execute \texttt{makeindex -s gglo.ist -o ran\_toks.gls ran\_toks.glo} on the command line and recompile \texttt{ran\_toks.dtx}.} \end{document} % % \fi % \MakeShortVerb{|} % \InputIfFileExists{aebdonotindex.def}{\PackageInfo{ran_toks}{Inputting aebdonotindex.def}} % {\PackageInfo{ran_toks}{cannot find aebdonotindex.def}} % % \begin{macrocode} %<*package> % \end{macrocode} % \paragraph*{Description.} % This short package randomizes a list of tokens. The command, \cs{ranToks}, % takes one argument, which is a list of tokens: %\begin{Verbatim}[xleftmargin=\parindent,fontsize=\small, %commandchars={!()},codes={\catcode`\%=9}] %\ranToks{!ameta(name)}{!% %!quad{!ameta(tok!SUB1)}{!ameta(tok!SUB2)}...{!ameta(tok!SUB(n))} %} %\end{Verbatim} % The command defines a series of $n$ internal commands, one for each of the tokens. % The definitions are essentially randomized. The randomized tokens are accessed through % the command \cs{useRanTok}. For example, %\begin{Verbatim}[xleftmargin=\parindent,fontsize=\small, %commandchars={!()},codes={\catcode`\%=9}] %\useRanTok{1}, \useRanTok{2},..., \useRanTok{n} %\end{Verbatim} % gives a random listing of the $n$ tokens. These can be arranged on the page % as desired. % % There is a second construct, designed for more elaborate randomization. %\begin{Verbatim}[xleftmargin=\parindent,fontsize=\small, %commandchars={!()},codes={\catcode`\%=9}] %\bRTVToks{!ameta(name)} %\begin{rtVW} %!quad!ameta(some content) %\end{rtVW} %... %... %\begin{rtVW} %!quad!ameta(some content) %\end{rtVW} %\eRTVToks %\end{Verbatim} % The contents of each of the \texttt{rtVW} environments are written to the computers hard drive, then input % back in random order, using \cs{useRanTok}, eg, %\begin{Verbatim}[xleftmargin=\parindent,fontsize=\small, %commandchars={!()},codes={\catcode`\%=9}] %\useRanTok{1}, \useRanTok{2},..., \useRanTok{n} %\end{Verbatim} % Other details are left to the readers' imagination. % \changes{v1.4}{2021/06/06}{Rounded up v1.3.3 to v1.4 for publication} % \paragraph{Requirements.} As of this writing, we require only the \texttt{verbatim} package % and \texttt{random.tex}, the package was written by Donald Arseneau. % \begin{macrocode} \RequirePackage{verbatim} % \end{macrocode} % \paragraph*{Input \texttt{random.tex}.} Input \texttt{random.tex} if not already input. %\changes{v1.0d}{2013/08/03}{Added conditional input of random.tex} % \begin{macrocode} \@ifundefined{nextrandom}{\input{random.tex}}{} % \end{macrocode} % We redefine \cs{nextrandom} from \texttt{random.tex} to save the initializing seed. % \begin{macrocode} \def\nextrandom{\begingroup \ifnum\randomi<\@ne % then initialize with time \global\randomi\time \global\multiply\randomi388 \global\advance\randomi\year \global\multiply\randomi31 \global\advance\randomi\day \global\multiply\randomi97 \global\advance\randomi\month \message{Randomizer initialized to \the\randomi.}% \nextrandom \nextrandom \nextrandom % \end{macrocode} % Save the initial seed value to \cs{rtInitSeedValue}. % \changes{v1.0c}{2013/08/03}{Save the initial seed value to \string\cs{rtInitSeedValue}.} % \begin{macrocode} \xdef\InitSeedValue{\the\randomi}% \fi \count@ii\randomi \divide\count@ii 127773 % modulus = multiplier * 127773 + 2836 \count@\count@ii \multiply\count@ii 127773 \global\advance\randomi-\count@ii % random mod 127773 \global\multiply\randomi 16807 \multiply\count@ 2836 \global\advance\randomi-\count@ \ifnum\randomi<\z@ \global\advance\randomi 2147483647\relax\fi \endgroup } % \end{macrocode} % The code for this package was taken from the \textsf{dps} package, and modified % suitably. We use several token registers and count registers. This can probably % be optimized. % \begin{macrocode} \newtoks\rt@listIn \rt@listIn={} \newtoks\rt@newListIn \rt@newListIn={} \newtoks\rt@listOut \rt@listOut={} \newcount\rt@nMax \newcount\rt@nCnt \newcount\rt@getRanNum \newif\ifrtdebug \rtdebugfalse \newif\ifwerandomize \werandomizetrue \newif\ifsaveseed\saveseedtrue \newif\ifrt@InputUsedIDs\rt@InputUsedIDsfalse \newwrite\rt@Verb@write % \end{macrocode} % Convenience commands. % \begin{macrocode} \def\rtcsarg#1#2{\expandafter#1\csname#2\endcsname} \def\rt@nameedef#1{\expandafter\edef\csname #1\endcsname} % \end{macrocode} % \leavevmode\IndexOpt{usedbapp}^^A % The code to support a DB application has grown, so much so, it desirves % a option so as to include the code only if needed. % \changes{v1.3}{2021/01/14}{Added \string\opt{usedbapp} option} % \begin{macrocode} \DeclareOption{usedbapp}{\let\rtPkgInpt\rt@PkgInpt} \def\rt@PkgInpt{\InputIfFileExists{rt-dbapp.def} {\PackageInfo{ran_toks}{Inputting rt-dbapp.def}} {\PackageInfo{ran_toks}{Cannot find rt-dbapp.def}} } \let\rtPkgInpt\relax \AtEndOfPackage{\rtPkgInpt} \ProcessOptions\relax % \end{macrocode} % \begin{macrocode} % %<*altpkgname> % \end{macrocode} % % \section{Alternate package name: \texorpdfstring{\protect\pkg{ran-toks}}{ran-toks}} % CTAN lists this package (\pkg{ran\_toks}) as \pkg{ran-toks}, so we'll create % a dummy package by that name. % \changes{v1.2}{2019/12/28}{Added dummy package \string\pkg{ran-toks}} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{ran-toks} [2019/12/28 v1.0 ran-toks Alt-name (dps)] \DeclareOption*{\PassOptionsToPackage{\CurrentOption}{ran_toks}} \ProcessOptions \RequirePackage{ran_toks}[2019/12/28] % \end{macrocode} % \begin{macrocode} % %<*package> % \end{macrocode} % \section{Commands for controlling the process} % % \DescribeMacro{\ranToksOn}\DescribeMacro{\ranToksOff} These two turn on and turn off % randomization. % \begin{macrocode} \def\ranToksOn{\werandomizetrue} \def\ranToksOff{\werandomizefalse} % \end{macrocode} % \DescribeMacro{\useThisSeed} initializes the random number generator. Use this to reproduce % the same sequence of pseudo-random numbers from an earlier run. We also set % \cs{saveseedfalse} so we do not write the initial seed to the disk. % \begin{macrocode} \def\useThisSeed#1{\saveseedfalse\randomi=#1} \@onlypreamble\useThisSeed % \end{macrocode} % \DescribeMacro{\useLastAsSeed} initializes the random number generator using the % last random seed. If the file \cs{jobname\_rt.sav} does not exist, the generator will be % initialized using time and date data. % \begin{macrocode} \def\useLastAsSeed{\rt@useLastAsSeed} \@onlypreamble\useLastAsSeed \def\rt@useLastAsSeed{% \IfFileExists{\jobname_rt.sav}{% \PackageInfo{ran_toks}{Inputting \jobname_rt.sav}% \@ifundefined{readsavfile}{\newread\readsavfile}{}% \openin\readsavfile=\jobname_rt.sav \read\readsavfile to \InitSeedValue \read\readsavfile to \lastRandomNum \closein\readsavfile \randomi=\lastRandomNum % \end{macrocode} % When \cs{useLastAsSeed}, the last becomes the first. % \begin{macrocode} \xdef\InitSeedValue{\the\randomi} }{% \PackageInfo{ran_toks}{\jobname_rt.sav cannot be found, \MessageBreak using the random initializer}% }% } \@ifundefined{aeb@randomizeChoices}{% \let\inputRandomSeed\useLastAsSeed \let\useRandomSeed\useThisSeed}{} % \end{macrocode} % % \section{Utility commands} % % A standard \cs{verbatim} write used in exerquiz and other package in the AeB family. % \begin{macrocode} \def\verbatimwrite{\@bsphack \let\do\@makeother\dospecials \catcode`\^^M\active \catcode`\^^I=12 \def\verbatim@processline{% \immediate\write\verbatim@out {\the\verbatim@line}}% \verbatim@start} \def\endverbatimwrite{\@esphack} \def\rt@IWVO{\immediate\write\verbatim@out} % \end{macrocode} % We write only if \cs{ifsaveseed} is true. % \begin{macrocode} \def\InitSeedValue{\the\randomi} \def\rt@writeSeedData{\ifsaveseed \@ifundefined{saveseedinfo}{\newwrite\saveseedinfo}{} \immediate\openout \saveseedinfo \jobname_rt.sav \let\verbatim@out\saveseedinfo \def\rt@msgi{initializing seed value}% \def\rt@msgii{last random number used}% \uccode`c=`\%\uppercase{% \rt@IWVO{\InitSeedValue\space c \rt@msgi}% \rt@IWVO{\the\randomi\space c \rt@msgii}}\immediate \closeout\saveseedinfo\fi} % \end{macrocode} % Save the initial seed value to hard drive. % \begin{macrocode} \AtEndDocument{\rt@writeSeedData}% % \end{macrocode} % \DescribeMacro{\rt@populateList}\hskip-\marginparsep|{|\ameta{n}|}| is a utility command, % its argument \ameta{n} is a positive integer, % and it generates a list of the form \verb!\\{1}\\{2}...\\{n}! and is held in the % token register \cs{rt@listIn} This listing is later % randomly permuted by \cs{rt@RandomizeList}. % \begin{macrocode} \def\rt@populateList#1{\rt@listIn={}\rt@nCnt\z@ \@whilenum\rt@nCnt<#1\do{\advance\rt@nCnt\@ne \edef\rt@listInHold{\the\rt@listIn\noexpand\\{\the\rt@nCnt}}% \rt@listIn=\expandafter{\rt@listInHold}}} % \end{macrocode} % \DescribeMacro{\rt@RandomizeList}\hskip-\marginparsep\thinspace\texttt{\darg{\ameta{n}}} % is the command that gets the process of randomizing % the input list going. The argument is the number \ameta{n} of tokens. If % \cs{werandomize} is false, it just returns the input list; otherwise, it % calls \cs{rt@randomizeList} to actually do the work. % \begin{macrocode} \def\rt@RandomizeList#1{\global \rt@listIn={}\global\rt@newListIn={}\global\rt@listOut={}% \rt@nMax=#1\relax\rt@populateList{\the\rt@nMax}% \ifwerandomize \expandafter\rt@randomizeList\else \global\rt@listOut=\expandafter{\the\rt@listIn}\fi % \end{macrocode} % Save the list out as \cs{rt@BaseName-List} for later retrieval. This is the randomized % list of integers for this base name. % \changes{v1.1}{2017/05/04}{Save out list for later use} % \begin{macrocode} \global\rt@nameedef{\rt@BaseName-List}{\the\rt@listOut}} % \end{macrocode} % \DescribeMacro{\rt@randomizeList} randomizes the list of consecutive integers, and leaves the % results, %\begin{verbatim} % \\{k_1}\\{k_2}...\\{k_n} %\end{verbatim} % in the token register \cs{rt@listOut}. % \cs{rt@randomizeList} is a loop, looping between itself and \cs{rt@loopTest}. % \begin{macrocode} \def\rt@randomizeList{\let\\=\rt@processi \setrannum{\rt@getRanNum}{1}{\the\rt@nMax}% \ifrtdebug\typeout{\string\rt@getRanNum=\the\rt@getRanNum}\fi \rt@nCnt\z@ \ifrtdebug\typeout{LISTING: \the\rt@listIn}\fi \the\rt@listIn \rt@loopTest } \def\rt@loopTest{\advance\rt@nMax\m@ne \ifnum\rt@nMax>\z@ \def\rt@next{% \rt@listIn=\expandafter{\the\rt@newListIn}% \rt@newListIn={}\rt@randomizeList}% \else \let\rt@next\relax \global\rt@listOut=\expandafter{\the\rt@listOut}% \ifrtdebug \typeout{Final Result: \string\rt@listOut=\the\rt@listOut}\fi \fi\rt@next } % \end{macrocode} % In \cs{rt@randomizeList}, we \verb~\let\\=\rt@processi~ before dumping the % contents of \cs{rt@listIn}. We then go into a loop \cs{rt@loopTest}. \cs{rt@getRanNum} % is the random integer between 1 and \cs{rt@nMax}. % \begin{macrocode} \def\rt@processi#1{\advance\rt@nCnt\@ne \ifnum\rt@nCnt=\rt@getRanNum \edef\rt@listOutHold{\the\rt@listOut}% \global\rt@listOut=\expandafter{\rt@listOutHold\\{#1}}% \ifrtdebug\typeout{Found it: \string\\{#1}}% \typeout{New \string\rt@listOut: \the\rt@listOut}\fi \else \edef\rt@listInHold{\the\rt@newListIn}% \rt@newListIn=\expandafter{\rt@listInHold\\{#1}}% \ifrtdebug\typeout{\string\rt@newListIn: \the\rt@newListIn}\fi \fi } % \end{macrocode} % We perform modular arithmetic when the index of \cs{useRanTok} is too large. % \DescribeMacro{\rt@modarith}\cs{rt@modarith} performs modular arithmetic on its arguments (\texttt{\#1 mod \#2}) % and returns the result in the macro \cs{rt@mod}. % \begin{macrocode} \def\rt@modarith#1#2{\count\z@=#1\relax\count\tw@=#1\relax % \end{macrocode} % This macro uses \cs{dimen0} and % \cs{dimen2}, so it should be called within a group. % \begin{macrocode} \advance\count\z@\m@ne\divide\count\z@ #2\relax \multiply\count\z@ #2\relax \advance\count\tw@-\count\z@ \edef\rt@mod{\the\count\tw@}} % \end{macrocode} % Warning messages, these are \DescribeMacro{\rt@badIndex}\cs{rt@badIndex} and % \cs{rt@badTokName}\DescribeMacro{\rt@badTokName}. % \begin{macrocode} \def\rt@badIndex#1#2{\PackageWarningNoLine{ran_toks} {The argument of \string\useRanTok{#1} on line \the\inputlineno\space is\MessageBreak greater than \string\nToksFor{#2} (\nToksFor{#2}), instead will use\MessageBreak \string\useRanTok{\rt@mod}, obtained from modular arithmetic.\MessageBreak You might want to fix this}% } \def\rt@badTokName#1{% \PackageWarningNoLine{ran_toks} {The token list `#1' on line \the\inputlineno\space is undefined,\MessageBreak possibly simply mispelled; check spelling.\MessageBreak If undefined, use \string\ranToks\space or \string\bRTVToks/% \string\eRTVToks\space\MessageBreak to define a list with the name `#1'}% } \def\rt@warnTokName#1{% \PackageWarningNoLine{ran_toks} {The token list `#1' on line \the\inputlineno\space is already defined,\MessageBreak will overwrite this list}% } % \end{macrocode} % \section{The main commands} % \begin{macro}{\ranToks}\hskip-\marginparsep\,\texttt{\darg{\ameta{token-list}}} % takes one argument, \texttt{\darg{\ameta{token-list}}}, a list of tokens. It randomizes them. % The randomized listing can be accessed using \cs{useRanTok}. % \begin{macrocode} \def\ranToks#1{\begingroup \useRTName{#1}% \r@nToks } \long\def\r@nToks#1{\rt@nMax\z@\r@ndToks#1\rt@NIL} \def\rt@NIL{@nil} % \end{macrocode} % \end{macro} % \begin{macro}{\useRTName}\hskip-\marginparsep\,\texttt{\darg{\ameta{name}}} % sets the base name (use prior to the use of \cs{useRanTok}). % \begin{macrocode} \newcommand{\useRTName}[1]{\gdef\rt@BaseName{#1}}% \let\rt@BaseName\@empty % \end{macrocode} % \end{macro} % \begin{macro}{\bRTVToks}\hskip-\marginparsep\,\texttt{\darg{\ameta{name}}} % \cs{bRTVToks} and \cs{eRTVToks} enclose a series % of \texttt{rtVW} environments. The single argument is the name of this set of % verbatim write ``tokens''. % \begin{macrocode} \newcommand{\bRTVToks}[1]{\rt@nCnt\z@\useRTName{#1}} % \end{macrocode} % \end{macro} % \begin{macro}{\eRTVToks} % At the end of the \texttt{rtVW} environments, initiated by \cs{bRTVToks}, the % \cs{eRTVToks} command saves the number of tokens counted, and randomizes the % access to the contents of the \texttt{rtVW} environments, this done by % \cs{r@nVToks}. % \begin{macrocode} \newcommand{\eRTVToks}{% \global\rt@nameedef{\rt@BaseName Cnt}{\the\rt@nCnt}\expandafter \r@nVToks\expandafter{\rt@BaseName}} % \end{macrocode} % \end{macro} % \begin{environment}{rtVW} % \cs{rtVW} is a verbatim write environment. It saves its contents to the file % \verb!\jobname_\rt@BaseName-\the\rt@nCnt.cut!. The file is later input back into % the source file in a random way. % \begin{macrocode} \def\reVerbEnd{\ifhmode\unskip\fi} % \end{macrocode} % Insert the hook \cs{rtVWHook} prior to writing the verbatim content. % The default is \cs{relax}. % \changes{v1.2}{2019/12/28}{Defined \string\cs{rtVWHook}} % \begin{macrocode} \def\rtVWHook#1{\def\@rgi{#1}\ifx\@rgi\@empty \let\RTVWHook\relax\else\def\RTVWHook{#1}\fi} \rtVWHook{} \newwrite\wrtprobids \newif\ifviewIDs\viewIDsfalse \newif\ifxDBUnique\xDBUniquefalse \def\wrtProbIds#1{\immediate\write\wrtprobids{\string \rtcsarg\string\gdef{#1}{used}}} \def\rtVWId#1{\ifviewIDs\noindent#1\fi \ifxDBUnique\ifrt@InputUsedIDs\wrtProbIds{#1}\fi\fi } % \end{macrocode} % \begin{macrocode} \newenvironment{rtVW}{\global\advance\rt@nCnt\@ne \immediate\openout\rt@Verb@write \jobname_\rt@BaseName-\the\rt@nCnt.cut \let\verbatim@out\rt@Verb@write \rt@IWVO{\string\RTVWHook}% \rt@IWVO{\string \rtVWId{\rt@BaseName-\the\rt@nCnt}\string\relax}% \verbatimwrite }{% \endverbatimwrite \immediate\write\rt@Verb@write{\string\reVerbEnd}% \immediate\closeout\rt@Verb@write } % \end{macrocode} % (2021/05/29) In support of nested |\bRTVToks/\eRTVToks| constructs, we \cs{let} % \cs{rtVWi} and \cs{rtVWii} to \cs{rtVW}, and \cs{endrtVWi} and \cs{endrtVWii} to \cs{endrtVW}. % \changes{v1.3.3}{2021/05/29}{Assign \string\cs{rtVWi} and \string\cs{endrtVWi}} % \begin{macrocode} \let\rtVWi\rtVW % dps5-29 \let\endrtVWi\endrtVW \let\rtVWii\rtVW % dps5-29 \let\endrtVWii\endrtVW % \end{macrocode} % \end{environment} % \DescribeMacro{\r@nVToks} randomizes the contents of the \texttt{rtVW} % environment. % \begin{macrocode} \def\r@nVToks#1{\begingroup \gdef\rt@BaseName{#1}% \expandafter\rt@nMax\@nameuse{#1Cnt}% \rt@listIn={}\rt@nCnt=0\relax\let\rt@listInHold\@empty \@whilenum\rt@nCnt<\rt@nMax\do{\advance\rt@nCnt\@ne \edef\rt@listInHold{% \the\rt@listIn{\noexpand\rt@inputVerb{#1-\the\rt@nCnt}}}% J14 \rt@listIn=\expandafter{\rt@listInHold}}\ifrtdebug \typeout{\string\r@nVToks: \the\rt@listIn}\fi \expandafter\r@nToks\expandafter{\the\rt@listIn}} % \end{macrocode} % \DescribeMacro\rt@inputVerb\hskip-\marginparsep\texttt{\darg{\ameta{db-name}-\ameta{num}}} This % is the command that inputs a DB Test problem, it inputs it from the file named % \cs{jobname\_\darg{\ameta{db-name}-\ameta{num}}}. As we input, we make a record of the % problem we input by expanding \cs{rt@recordAsUsed\darg{\ameta{db-name}-\ameta{num}}}, which % itself expands to \cs{rtcsarg\string\gdef\darg{\ameta{db-name}-\ameta{num}}\darg{used}}. This is necessary when we are % choosing more than one item from a given DB Test file; it must be recorded immediately so that later % it cannot be used again, if possible. % \begin{macrocode} \def\rt@inputVerb#1{\rt@recordAsUsed{#1}\input{\jobname_#1.cut}} %\def\rt@recordAsUsed#1{\ifxDBUnique\rtcsarg\gdef{#1}{used}\fi} \def\rt@recordAsUsed#1{\rtcsarg\gdef{#1}{used}} % \end{macrocode} % \DescribeMacro{\r@ndToks} is main looping command for \cs{ranToks} % and \cs{eRTVToks} (through \cs{r@nVToks}). If the ending token \cs{rt@NIL} is detected, we % break off and go to \cs{rt@endToks}. % \changes{v1.0e}{2016/02/06}{Fixed a bug, when the first two tokens \string\texttt{\#1} are the same, % we get an incorrect decision} % \begin{macrocode} \def\rt@PAR{\par} \long\def\r@ndToks#1{\def\rt@rgi{#1}% % \end{macrocode} % If the current argument is \cs{par}, we skip it % \begin{macrocode} \ifx\rt@rgi\rt@PAR\def\rt@next{\r@ndToks}\else \advance\rt@nMax\@ne \global\@namedef{rtTok\the\rt@nMax\rt@BaseName}{#1}% \def\rt@next{\@ifnextchar\rt@NIL {\rt@endToks\@gobble}{\r@ndToks}}\fi\rt@next} % \end{macrocode} % \hskip-\marginparsep|{|\ameta{n}|}| The \DescribeMacro{\rt@performRanDefns}\cs{rt@performRanDefns} performs code that % is repeated in several other macros: \cs{rt@endToks}, \cs{reorderRanToks}, and % \cs{copyRanToks}. It randomizes the list {\cs{rt@RandomizeList}, then assignments % the randomized list to the definitions. % \begin{macrocode} \def\rt@performRanDefns#1{% % \end{macrocode} % Now we randomize the order of the integers 1, 2,\dots \texttt{\#1}. % \begin{macrocode} \rt@RandomizeList{#1}\rt@nCnt\z@ % \end{macrocode} % Now we randomize the definitions. We \verb!\let\\=\rt@ssign!, then % let loose the tokens! % \begin{macrocode} \let\\\rt@ssign\the\rt@listOut} % \end{macrocode} % The final destination for \DescribeMacro{\rt@endToks}\cs{r@ndToks}. % \begin{macrocode} \def\rt@endToks{\global % \end{macrocode} % Save the number of tokens counted % \begin{macrocode} \rt@nameedef{nMax4\rt@BaseName}{\the\rt@nMax}% \rt@performRanDefns{\the\rt@nMax}\endgroup} % \end{macrocode} % \begin{macro}{\reorderRanToks}\hskip-\marginparsep\,\texttt{\darg{\ameta{name}}} % The \cs{reorderRanToks} command reorders (or re-indexes) % the family with name \ameta{name} (\texttt{\#1}). % \begin{macrocode} \def\reorderRanToks#1{\begingroup\useRTName{#1}\expandafter \ifx\csname nMax4#1\endcsname\relax % \end{macrocode} % Document author has not run \cs{ranToks} yet for this basename (\texttt{\#1}) % \begin{macrocode} \rt@badTokName{#1}\else % \end{macrocode} % Good to go. We reorder this list. % \begin{macrocode} \rt@performRanDefns{\@nameuse{nMax4#1}}\fi \endgroup} % \end{macrocode} % \end{macro} % \begin{macro}{\copyRanToks}\hskip-\marginparsep % \texttt{\darg{\ameta{name1}}\darg{\ameta{name2}}} % Use this command to copy \ameta{name1} to \ameta{name2}. This gives % a randomization of the same list, without affecting the original order of \ameta{name1}. % \begin{macrocode} \newcommand\copyRanToks[2]{\begingroup \expandafter \ifx\csname nMax4#1\endcsname\relax % \end{macrocode} % Source list is not defined % \begin{macrocode} \rt@badTokName{#1}% \else % \end{macrocode} % Source list is defined % \begin{macrocode} \expandafter \ifx\csname nMax4#2\endcsname\relax % \end{macrocode} % Destination list is not defined, which is good in this instance. This is % the case we copy the list. % \begin{macrocode} \useRTName{#2}\global \rt@nameedef{nMax4#2}{\@nameuse{nMax4#1}}% \rt@nCnt=\csname nMax4#2\endcsname\relax \@whilenum\rt@nCnt>\z@\do{\global \rt@nameedef{rtTok\the\rt@nCnt#2}% {\noexpand\@nameuse{rtTok\the\rt@nCnt#1}}% \advance\rt@nCnt\m@ne }\rt@performRanDefns{\@nameuse{nMax4#2}}% \else % \end{macrocode} % Destination list is defined already, warn the user. % \begin{macrocode} \rt@warnTokName{#2}% \fi \fi \endgroup } % \end{macrocode} % \end{macro} %\DescribeMacro{\rt@ssign}\hskip-\marginparsep\texttt{\darg{\ameta{name}}} makes the assignments that are expanded by \cs{useRanTok}. % We \cs{let} the assignment \verb!\let\\=\rt@ssign! in \cs{rt@endToks}, just before we dump out the % contents of \verb!\the\rt@listOut!. % \begin{macrocode} \def\rt@ssign#1{\advance\rt@nCnt\@ne\global \rt@nameedef{rtRanTok\the\rt@nCnt\rt@BaseName}{\noexpand \@nameuse{rtTok#1\rt@BaseName}}} % \end{macrocode} % \subsection{Additional user access commands} % \DescribeMacro{\nToksFor}\hskip-\marginparsep\texttt{\darg{\ameta{name}}} % expands the the number of tokens whose name is \ameta{name} (\texttt{\#1}). % \begin{macrocode} \newcommand{\nToksFor}[1]{\expandafter \ifx\csname nMax4#1\endcsname\relax \textbf{??}\rt@badTokName{#1}\else \@nameuse{nMax4#1}\fi } % \end{macrocode} % \DescribeMacro{\rtTokByNum}\hskip-\marginparsep\texttt{[\ameta{name}]\darg{\ameta{num}}} is an internal macro, but it can be used publicly. % The argument of it is an integer, eg, \verb!\rtTokByNum{3}! is the third token, as listed in the order % given in the argument of \cs{ranToks}. % \begin{macrocode} \newcommand{\rtTokByNum}[2][\rt@BaseName]{\expandafter \ifx\csname nMax4#1\endcsname\relax \textbf{??}\rt@badTokName{#1}\else \@nameuse{rtTok#2#1}\expandafter\ignorespaces \fi } % \end{macrocode} %\DescribeMacro{\useRanTok}\hskip-\marginparsep\texttt{[\ameta{name}]\darg{\ameta{num}}} % After \cs{ranToks} has been executed, the user has access to the % randomized tokens through \cs{useRanTok}. The argument \ameta{num} is an integer 1 through max. % \begin{macrocode} % \end{macrocode} % We provide two commands to control the feature of try to select unique % choices across multiple renditions of the same source file. % \DescribeMacro\uniqueXDBChoicesOn\cs{uniqueXDBChoicesOn}, turns on this feature; % the default is \DescribeMacro\uniqueXDBChoicesOff\cs{uniqueXDBChoicesOff} make % no changes to how \cs{useRanTok} operates. One other command we define here is % \DescribeMacro\makeInfoAWarning\cs{makeInfoAWarning}; this command applies only % when \cs{uniqueXDBChoicesOn} is expanded. In the macro \cs{xdb@unique} which is % expanded when \cs{uniqueXDBChoicesOn} is expanded first, there is one line that % reports information to the log as \cs{PackageInfo}. By expanding \cs{makeInfoAWarning} % we change \cs{PackageInfo} to \cs{PackageWarning}, which gives it greater % visibility in the log and the log report. % \changes{v1.3.1}{2021/01/19}{Added \string\cs{ifrt@recording}} % \begin{macrocode} \newcommand{\uniqueXDBChoicesOn}{\xDBUniquefalse \PackageWarning{ran_toks} {The \string\uniqueXDBChoicesOn\space requires the\MessageBreak \texttt{usedbapp} option}} \newcommand{\uniqueXDBChoicesOff}{\let\xdbunique\relax\xDBUniquefalse} \let\xdbunique\relax \newcommand{\makeInfoAWarning}{\def\pkgNotifType{\PackageWarning}} \def\pkgNotifType{\PackageInfo} \newif\ifrt@recording \rt@recordingtrue % dps % \end{macrocode} % Now for the definition of \cs{useRanTok}. % \begin{macrocode} \newcommand{\useRanTok}[2][\rt@BaseName]{\bgroup\expandafter \ifx\csname nMax4#1\endcsname\relax \rt@badTokName{#1}\global\let\rt@next\relax \else \ifnum#2>\@nameuse{nMax4#1}% \rt@modarith{#2}{\@nameuse{nMax4#1}}% \rt@badIndex{#2}{#1}\edef\Indx{\rt@mod}% \else \edef\Indx{#2}% \fi \xdef\rt@orig@Indx{\Indx}% % \end{macrocode} % If \cs{xdbunique} is \cs{relax}, \cs{useRanTok} executes as it did in the past % (no change in behavior); otherwise, we expand \cs{xdb@unique} which attempts % to avoid duplicate choices based on the DBs input by \cs{useProbDBs}. % \begin{macrocode} \ifx\xdbunique\relax \ifrt@recording\rt@recordAsUsed{#1-\Indx}\fi \xdef\rt@next{\noexpand\@nameuse{rtRanTok\Indx#1}}% \else \xdb@unique{#1}% \fi \fi \egroup \rt@next } % \end{macrocode} % \DescribeMacro{\displayListRandomly}\hskip-\marginparsep\texttt{[\ameta{prior}][\ameta{post}]\darg{\ameta{name}}} % lists all items in the list as passed % by the required argument. For expanding in a list environment, use \cs{item} as % the optional argument. Designed for listing all question in an eqexam % document in random order. %\changes{v1.0b}{2013/07/29}{Added \string\cs{displayListRandomly}} %\changes{v1.0e}{2016/02/06}{Added optional argument to \string\cs{displayListRandomly}} %\changes{v1.1}{2017/05/04}{Added second optional argument to \string\cs{displayListRandomly}} %\changes{v1.3.3}{2021/05/29}{In support of nested \string\cs{bRTVToks}/\string\cs{eRTVToks}\space % constructs, replace the counter \string\cs{rt@nCnt} with \string\cs{@tempcntb}} % \begin{macrocode} \newcommand{\displayListRandomly}[1][]{\begingroup \def\rt@prior{#1}\displ@yListRandomly } \newcommand{\displ@yListRandomly}[2][]{\@tempcntb\z@ % dps5-29 \expandafter\ifx\csname nMax4#2\endcsname\relax \rt@rgi\space\textbf{??}\rt@badTokName{#2}#1% \else \rt@recordingfalse % \end{macrocode} % Within the optional arguments, we define \DescribeMacro{\i}\cs{i}, % \DescribeMacro{\first}\cs{first}, \DescribeMacro{\last}\cs{last}, and % \DescribeMacro{\lessone}\cs{lessone} to do some logic on the arguments. % These four macro are defined locally and not available outside the command % \cs{displayListRandomly}. % \begin{macrocode} \def\rt@post{#1}\useRTName{#2}\let\i\@tempcntb \def\first{1}\edef\last{\@nameuse{nMax4#2}}\@tempcnta\last \advance\@tempcnta\m@ne \edef\lessone{\the\@tempcnta}\@whilenum\@tempcntb<\last \advance\@tempcntb\@ne % \end{macrocode} % There is one example of this command getting confused and printing the wrong % number of items. Here, we pass the optional argument of \cs{useRanTok} and that % cleared up the problem. % \changes{v1.3.2}{2021/05/25}{Insert optional argument for \string\cs{useRanTok} % within \string\cs{displ@yListRandomly}} % \begin{macrocode} \do{\rt@prior\useRanTok[#2]{\the\@tempcntb}\rt@post }\fi \endgroup } % %<*dbapp> % \end{macrocode} % \section{A DB application} % This (optional) section supports an application of \pkg{ran\_toks} to the \pkg{eqexam} package; though, % conceptually, the commands of this section may be applied in other settings. In this application, % the document author has a series of DB test files (TEX files), each file contains \cs{bRTVToks}/\allowbreak % \cs{eRTVToks} constructs, which contain a series of \env{rtVW} environments of verbatim content. In this application, % the verbatim content are \env{problem}/\env{problem*} environments of \pkg{eqexam}. % % The following verbatim listing is taken from the preamble of \texttt{mc-db.tex}, which illustrates the layout % of how to apply the commands of this section. %\begin{Verbatim}[xleftmargin=\parindent,fontsize=\small, %commandchars={!()},codes={\catcode`\%=9}] %\examNum{1} %\numVersions{4} %\forVersion{a} %!%!textsf( initial seeds for each of the four versions of this document) %\vA{\useThisSeed{54356}} %\vB{\useThisSeed{577867}} %\vC{\useThisSeed{6746788}} %\vD{\useThisSeed{856785}} % %\uniqueXDBChoicesOn !%!textsf( Try to avoid duplicate questions in multi-version doc) %\InputUsedIDs !%!textsf( Input history of previous versions to current version) %\viewIDstrue !%!textsf( To view the IDs of problems used) %... %\useTheseDBs{db1,db2,db3,db4} %\end{Verbatim} % \changes{v1.3}{2021/01/14}{Added several commands and macro to continue to support % a DB application.} % If \cs{ifxDBUnique} is true and if \pkg{eqexam} is loaded, we % open \cs{wrtprobids} which is used to write the problem IDs of the problems % already chosen in earlier version of this source file. The name of this file % is \cs{jobname-ver\string\selVersion.cut}; eg, \texttt{mc-db-verA.cut}, % \texttt{mc-db-verB.cut}, etc. % \begin{macrocode} \def\rt@OpenProbIds{\@ifpackageloaded{eqexam} {\immediate\openout\wrtprobids\jobname-ver\selVersion.cut}{}} % \end{macrocode} % We open the file \cs{jobname-ver\cs{selVersion}.cut} when \cs{InputUsedIDs} is % expanded in the preamble. % \begin{macrocode} %\def\rt@ABD{\ifxDBUnique\expandafter\rt@OpenProbIds\fi} \def\rt@ABD{\@ifundefined{eq@nVersions}{} {\ifnum\eq@nVersions>\@ne\expandafter\rt@OpenProbIds\fi}} % \end{macrocode} % We begin with some utility commands to help parse the argument of \cs{useProbDBs}. % \begin{macrocode} \def\rt@gettonil#1\@nil{\def\to@nilarg{#1}} \def\rt@ifspc{\ifx\@let@token\@sptoken \let\rt@next\rt@xifspc\else \let\rt@next\rt@gettonil\fi\rt@next } \begingroup \def\:{\rt@xifspc}\expandafter \gdef\: {\futurelet\@let@token\rt@ifspc} \endgroup \def\rt@strpspcs{\futurelet\@let@token\rt@ifspc} % \end{macrocode} % \begin{macro}{\useTheseDBs}\hskip-\marginparsep\thinspace\texttt{\darg{\ameta{list}}} % Inputs any files included in the comma-delimited list. The base names need only be listed, % as the extension is assumed to be \texttt{.tex}. The command \cs{useProbDBs} can only be used % in the preamble. Refer to the demo file \texttt{mc\_db.tex} for an illustration of its intended use. % \begin{macrocode} \def\ProbDBWarningMsg#1{\filename@parse{#1} \PackageWarning{ran_toks} {The file \filename@area\filename@base.\ifx\filename@ext\relax tex\else\filename@ext\fi\space cannot be found}} \def\useTheseDBs#1{\def\rt@dblist{#1}\ifx\rt@dblist\@empty\else \let\rt@DB@List\@empty \edef\temp@expand{\noexpand\@for\noexpand\@@tmp:=\rt@dblist}% \temp@expand\do{\ifx\@@tmp\@empty\else \expandafter\rt@strpspcs\@@tmp\@nil\edef\@@tmp{\to@nilarg}% \edef\rt@nextDB{\noexpand \InputIfFileExists{\@@tmp}{}{\noexpand \ProbDBWarningMsg{\@@tmp}}}% \toks\tw@=\expandafter{\rt@DB@List}% \toks@=\expandafter{\rt@nextDB}% \edef\rt@DB@List{\the\toks\tw@\space\the\toks@}\fi }\expandafter\rt@DB@List\fi} % \end{macrocode} % \end{macro} % \begin{macro}{\useProbDBs}\hskip-\marginparsep\thinspace\texttt{\darg{\ameta{list}}} Is an alias of % \cs{useTheseDBs}. % \begin{macrocode} \let\useProbDBs\useTheseDBs % \end{macrocode} % \end{macro} % \begin{macro}{\viewDB}\hskip-\marginparsep\thinspace\texttt{\darg{\ameta{name}}} % Typeset the entire contents of a DB Test file. The argument \ameta{name} is the name % of the DB Test file (as in |\bRTVToks{DB1}|, here \texttt{DB1} is the \ameta{name}. % The DB test files should be input using \cs{useProbDBs}. % \begin{macrocode} \def\viewDB#1{\useRTName{#1}\rt@nCnt\z@ \edef\nSTOP{\@nameuse{nMax4\rt@BaseName}}% \loop\advance\rt@nCnt\@ne \rtTokByNum{\the\rt@nCnt}% \ifnum\rt@nCnt<\nSTOP\repeat } % \end{macrocode} % \end{macro} % The macro \DescribeMacro\getR@nIndx\cs{getR@nIndx} executes with each % entry of |\@nameuse{#1-List}|. % For an index value of \cs{Indx}, the macro goes through the % arguments to the \cs{Indx}'th argument and reads % the value of the argument at that point. It % returns the argument of the \cs{Indx}'th as \cs{ranIndx}; % eg, if |\Indx=1|, then |\ranIndx=3|, for the above example. % \begin{macrocode} %% uses \@tempcnta and \Indx \def\getR@nIndx#1{\def\argi{#1}% \ifx\argi\rt@STOP % no match, something is wrong \edef\ranIndex{-1}\else \advance\@tempcnta\@ne \ifnum\Indx=\@tempcnta \def\ranIndx{#1}\fi \fi } % \end{macrocode} % \DescribeMacro\xdb@unique\hskip-\marginparsep\texttt{\darg{\ameta{name}}} % An add-on command to \cs{useRanTok}. The command attempts % to create a unique choice of a problem over several versions of the same document. This may not % be possible if there are not enough choices to satisfy the number of declared versions. % % The \cs{xdb@unique} seems to work when the \pkg{eqexam} document has multiple parts (more then one % \env{exam} environments). The id files for the parts are all combined; ideally, for multiple part % exams, the second part draws form a set of DB test files different from the first part, as long as there are % enough problems to choose from. % % Note that, if there is not a unique choice for a question from the designated DB test file, % \cs{xdb@unique} reverts to the original choice so there may be duplicates across versions of the document. % \begin{macrocode} \def\rt@NoAltChoice#1#2{\PackageWarning{ran_toks} {Cannot find an alternative to #1-#2,\MessageBreak will use it but it may be a duplicate\MessageBreak question}} \def\xdb@unique#1{\@tempcnta\z@ \def\rt@STOP{\relax}% % \end{macrocode} %We use the randomized list for the \ameta{name} \begin{Verbatim}[xleftmargin=\parindent,fontsize=\small, %commandchars={!()},codes={\catcode`\%=9}] %\@nameuse{#1-List} is the randomized list: eg, %\\{3}\\{2}\\{4}\\{5}\\{1} %\end{Verbatim} % \begin{macrocode} \let\\\relax\edef\x{\@nameuse{#1-List}}% \toks@=\expandafter{\x}\let\\\getR@nIndx \the\toks@\\\rt@STOP % \end{macrocode} % We take as the default choice the original choice % \begin{macrocode} \xdef\rt@next{\noexpand \@nameuse{rtRanTok\rt@orig@Indx#1}}% % \end{macrocode} % Begin to look at the results of |\the\toks@\\\rt@STOP|, % \begin{macrocode} \ifnum\ranIndx>\m@ne % \end{macrocode} % If \cs{ranIndx} is -1, we use the original index. % \begin{macrocode} \edef\rt@orig@ranIndx{\ranIndx}% \expandafter \ifx\csname#1-\ranIndx\endcsname\relax % \end{macrocode} % This question has not been chosen earlier, so we'll use it. % \begin{macrocode} \xdef\rt@next{\noexpand \@nameuse{rtRanTok\Indx#1}}% \else % \end{macrocode} % The question has been chosen in an earlier version of the document. % Find the next higher unused one (cycle search). % \begin{macrocode} \@tempcntb\z@ \rt@nCnt\rt@orig@Indx\relax % \end{macrocode} % As we move into the \cs{@whilenum} loop, we take as the default % the original index. The loop may overwrite the definition of % \cs{rt@next}. % \begin{macrocode} \xdef\rt@next{\noexpand\rt@NoAltChoice{#1}{\rt@orig@Indx}\noexpand \@nameuse{rtRanTok\rt@orig@Indx#1}}% \@whilenum\@tempcntb<\@nameuse{nMax4#1}\do{% \advance\@tempcntb\@ne \advance\rt@nCnt\@ne % \end{macrocode} % If the count is at the \texttt{nMax4} value, we start over from % the beginning. % \begin{macrocode} \ifnum\rt@nCnt>\@nameuse{nMax4#1}\rt@nCnt\@ne\fi % \end{macrocode} % We search through \cs{toks@} again, so we have the initialize % the dependent variables: \cs{Indx}, the index to search for; \cs{@tempcnta} % the counter that is used by \cs{getR@nIndx}; nothing has changed % |\let\\\getR@nIndx| should still be in effect. % \begin{macrocode} \edef\Indx{\the\rt@nCnt}\@tempcnta\z@ \the\toks@\\\rt@STOP % \end{macrocode} % If \cs{ranIndx} is -1, we use the original index. % \begin{macrocode} \ifnum\ranIndx>\m@ne \expandafter \ifx\csname#1-\ranIndx\endcsname\relax \pkgNotifType{ran_toks}{#1-\rt@orig@ranIndx\space has already been used,\MessageBreak will use #1-\ranIndx}% % exit the \@whilenum loop \@tempcntb\@nameuse{nMax4#1}% \advance\@tempcntb\@ne \fi \fi \xdef\rt@next{\noexpand\@nameuse{rtRanTok\Indx#1}}% }% do \ifnum\@tempcntb=\@nameuse{nMax4#1}\relax \xdef\rt@next{\noexpand \rt@NoAltChoice{#1}{\rt@orig@ranIndx}\noexpand \@nameuse{rtRanTok\rt@orig@Indx#1}}% \fi \fi \fi } % \end{macrocode} % Here is the operational definition of \DescribeMacro\uniqueXDBChoicesOn % \cs{uniqueXDBChoicesOn}; when executed in the preamble, an attempt is made % the select only problems that have not already been chosen in any prior % renditions of the same source file. % \begin{macrocode} \renewcommand{\uniqueXDBChoicesOn}{\xDBUniquetrue \let\xdbunique\xdb@unique} % \end{macrocode} % \begin{macro}{\InputUsedIDs} Input the user ID (CUT) files. These are files % that document which questions were used for the various versions of the exam. % \begin{macrocode} \newif\ifrt@InputUsedIDs\rt@InputUsedIDsfalse \def\InputUsedIDs{\rt@InputUsedIDstrue \bgroup \setcounter{eq@count}{0}% \let\rt@InputUsedIDs\@empty \let\rt@InputUsedIDsFIs\@empty \@whilenum \value{eq@count}<\eq@nVersions\relax\do {% \stepcounter{eq@count}% \g@addto@macro\rt@InputUsedIDs{\if\selVersion}% \g@addto@macro\rt@InputUsedIDsFIs{\fi}% \edef\x{\Alph{eq@count}}% \edef\y{\noexpand\g@addto@macro\noexpand \rt@InputUsedIDs{\x\expandafter\noexpand \csname else\endcsname\noexpand\rt@IIFE}}\y \edef\x{{\x}}\expandafter \g@addto@macro\expandafter\rt@InputUsedIDs\expandafter{\x}% }% do \expandafter\g@addto@macro\expandafter \rt@InputUsedIDs\expandafter{\rt@InputUsedIDsFIs}% \egroup \rt@InputUsedIDs \AtBeginDocument{\rt@ABD}% } \@onlypreamble\InputUsedIDs % \end{macrocode} % \end{macro} % A convenience command used by \cs{InputUsedIDs}. % \begin{macrocode} \def\rt@IIFE#1{\InputIfFileExists{\jobname-ver#1.cut} {\PackageInfo{ran_toks}{Inputting \jobname-ver#1.cut}} {\PackageInfo{ran_toks}{Cannot find \jobname-ver#1.cut}}} % \end{macrocode} % \begin{macrocode} % %<*package> % % \end{macrocode} % \Finale \endinput