%\def\filedate{21 Mar 2004}
%\def\fileversion{2.0}
%\def\filename{probsoln.dtx}
%\def\docdate{19th Dec 2000}
%\iffalse
% Doc-Source file to use with LaTeX2e
% Copyright (C) 2000 Nicola Talbot, all rights reserved.
%\fi
%
% \newsavebox{\syntaxbox}
% \newenvironment{syntax}{\begin{lrbox}{\syntaxbox}\ttfamily}%
%{\end{lrbox}\par\noindent\fbox{\usebox{\syntaxbox}}\\}
% \title{probsoln.sty v\fileversion: \LaTeX\ Package to help create problem sheets}
% \author{N.L.C. Talbot}
% \date{\docdate}
% \maketitle
% \tableofcontents
%
% \section{Introduction}
% The package |probsoln.sty| is designed for teachers or lecturers who
% want to create problem sheets for their students.  This package was 
% designed with specifically mathematics problems in mind, but can
% be used for other subjects as well.  The idea is to create a file
% containing a large number of problems with their solutions which
% can be read in by \LaTeX, and then select a number of problems to
% typeset.  This means that once the database has been set up, each
% year you can easily create a new problem sheet that is sufficiently
% different from the previous year, thus preventing the temptation
% of current students seeking out the previous year's students,
% and checking out their answers.  There is also an option that can be
% passed to the package to determine whether or not the solutions
% should be printed.  In this way, one file can either produce
% the student's version or the teacher's version.
%
% \section{Package Options}
% The following options may be passed to this package:
%
% \centerline{%
% \begin{tabular}{@{\ttfamily}ll}
% answers & Show the answers\\
% noanswers & Don't show the answers (default)
% \end{tabular}
% }
%
% \section{Commands Provided}
% \subsection{Creating a New Problem}
% \begin{syntax}|\newproblem[|\meta{nargs}|]{|\meta{label}|}{|\meta{problem}|}{|\meta{solution}|}|\end{syntax}
% \DescribeMacro{\newproblem}
% A new problem is defined using the command |\newproblem|.  This does not print anything,
% but merely stores the problem.  The argument \meta{label} is a unique string that is
% assigned to this problem so that it can be used later.  The argument \meta{problem}
% is normal \LaTeX\ code that should be used to typeset the problem.  The argument
% \meta{solution} is normal \LaTeX\ code that should be used to typeset the solution,
% if required.  For example:
%\begin{verbatim}
%\newproblem{quaddiff}{%
%%This is the problem
%\begin{displaymath}
%f(x) = x^2 + 3x + 4
%\end{displaymath}
%}{%
%%This is the solution
%\begin{displaymath}
%f'(x) = 2x + 3
%\end{displaymath}
%}
%\end{verbatim}
%
% The optional argument \meta{nargs} specifies the number of parameters this
% problem will take.  By default this value is 0, but any value from 1 to 9
% may be used.  Each parameter is refered to by |#1|, |#2|, \ldots, |#9|.
% For example, the following problem takes one parameter:
%\begin{verbatim}
%\newproblem[1]{sindiff}{%
%\(f(x) = \sin(#1x)\)
%}{%
%\(f'(x) = #1\cos(#1x)\)
%}
%\end{verbatim}
%
% The |quaddiff| problem shown above can be made more generic by using parameters:
%\begin{verbatim}
%\newcount\ctr
%\newproblem[3]{diff:quad}{%
%\(f(x) = 
%\ifnum#1=0
%\else
%\ifnum#1=1\else#1\fi x^2 
%\fi
%\ifnum#2=0
%\else
%\ifnum#2>0 \ifnum#1=0 \else + \fi \fi
%\ifnum#2=1\else#2\fi x 
%\fi
%\ifnum#3=0
%\else
%\ifnum#3>0 \ifnum#2=0 \ifnum#1=0 \else + \fi \else + \fi\fi
%#3
%\fi\)
%}{%
%\(f'(x) = 
%\ifnum#1=0
%\else
%\ctr=2
%\multiply\ctr by #1
%\the\ctr x 
%\fi
%\ifnum#2=0
%\else
%\ifnum#2>0 \ifnum#1=0 \else + \fi \fi
%#2
%\fi
% % print 0 if both #1 and #2 are 0
%\ifnum#1=0 \ifnum#2=0 0 \fi\fi 
%\)
%}
%\end{verbatim}
% The three parameters correspond to the coefficients.  Note that they
% must all be integers since \TeX\ only performs integer arithmetic.
%
% To generate a database, simply create a |.tex| file where all the problems
% are defined using |\newproblem|, and either |\input| it at the 
% start of your document if you want to use specific problems 
% (see Section~\ref{sec:useprob}), or
% pass it to |\selectrandomly| (see Section~\ref{sec:selran}).
%
% \subsection{Displaying a Problem}
% \label{sec:useprob}
% \begin{syntax}|\useproblem{|\meta{label}|}|\end{syntax}
% \DescribeMacro{\useproblem}
% Once a problem has be defined using |\newproblem|, it can be typeset
% using the command |\useproblem|.  If the problem was defined to take
% arguments, the arguments to the problem should come after the
% label.  In the case of the |sindiff| example above, the command
% |\useproblem{sindiff}{2}| would produce the following:\\[10pt]
% \(f(x) = \sin(2x)\)
% \paragraph{Solution:} \(f'(x) = 2\cos(2x)\)\\[10pt]
% whereas the command |\useproblem{diff:quad}{3}{0}{-2}| would produce:\\[10pt]
% \(f(x) = 3x^2 - 2\)
% \paragraph{Solution:} \(f'(x) = 6x\)
%
% \vspace{10pt}
% Suppose all the above problems are defined in the
% file |probs.tex|, then the following code will create a problem sheet
% with four questions in it:
%\begin{verbatim}
%\documentclass{article}
%
%\usepackage{probsoln}
%\input{probs}
%
%\begin{document}
%Differentiate the following functions with respect to $x$:
%\begin{enumerate}
%\item \useproblem{quaddiff}
%\item \useproblem{sindiff}{4}
%\item \useproblem{diff:quad}{2}{3}{1}
%\item \useproblem{diff:quad}{0}{1}{2}
%\end{enumerate}
%\end{document}
%\end{verbatim}
%The answer sheet can then be generated by passing the option |answers|
%to the |probsoln| package.
%
% \subsection{Selecting Problems at Random}
% \label{sec:selran}
% \begin{syntax}|\selectrandomly{|\meta{filename}|}{|\meta{n}|}|\end{syntax}
% \DescribeMacro{\selectrandomly}
% The command |\selectrandomly| will select \meta{n} problems that are defined
% in the file \meta{filename}.  Each problem is preceeded by a |\item|, so the
% command |\selectrandomly| should occur within one of the list-like environments,
% such as |enumerate|.  For example:
%\begin{verbatim}
%\begin{enumerate}
%\selectrandomly{easy.tex}{4}
%\end{enumerate}
%\end{verbatim}
% will result in four numbered problems, selected at random from the
% file |easy.tex|. (The \texttt{.tex} extension may be omitted.)
%
% Multiple |\selectrandomly| commands may be used.  For example:
%\begin{verbatim}
%\begin{enumerate}
%\item Differentiate the following functions with respect to $x$:
%
%\begin{enumerate}
%\selectrandomly{samples/easy.tex}{3}
%\selectrandomly{samples/args.tex}{1}
%\end{enumerate}
%
%\selectrandomly{samples/implicit.tex}{1}
%\selectrandomly{samples/1stprncpl.tex}{1}
%
%\end{enumerate}
%\end{verbatim}
% This will result in a total of 6 problems, numbered 1(a), 1(b), 1(c), 1(d), 2 and 3.
%
% If a randomly selected problem requires arguments, a message similar to the following
% will be displayed:
%\begin{verbatim}
%Problem diff:quad requires 3 argument(s), please specify (e.g. {5}{3}):
%\end{verbatim}
% Enter the required arguments, where each argument is enclosed in braces (\{ \}).
%
% \subsection{Other Commands}
% \DescribeMacro{\PSNrandseed}
% The command |\PSNrandseed{|\meta{n}|}| specifies the seed for the random
% number generator.  For example, |\PSNrandseed{\year}| will produce a
% different set of random problems each year, whereas |\PSNrandseed{\time}|
% will produce a different set of problems each time you \LaTeX\ the
% problem (as long as you leave at least a minute between runs.)
%
% \DescribeMacro{\showanswers}
% The command |\showanswers| will show the solutions from that point
% on.  May be localised by placing within a group.
%
% \DescribeMacro{\hideanswers}
% The command |\hideanswers| will hide the solutions from that point
% on.  May be localised by placing within a group.
%
% \DescribeMacro{\solutionname}
% By default, the solution is preceeded by the text: \textbf{Solution:}. This can
% be changed by redefining the macro |\solutionname|.
%
% \DescribeMacro{showanswers}
% The boolean variable |showanswers| is defined to be true if the answers
% are shown and false otherwise.  You can therefore do something like:
%\begin{verbatim}
%\ifthenelse{\boolean{showanswers}}{\textbf{Solution Sheet}}{}
%\end{verbatim}
% and \textbf{Solution Sheet} will be printed only if the answers are displayed.
%(For more information on |\ifthenelse| and |\boolean| see the 
%documentation for the |ifthen| package by David Carlisle.)
%
% \section{Error Messages}
%\begin{ttfamily}
%! Package probsoln Error: Label \ldots\ already used.
%\end{ttfamily}\\[10pt]
% Each label identifier used in |\newproblem| must be unique.
% Check to make sure you haven't used the same label more than once.
% Also check to make sure you haven't |\input|ed or randomly selected 
% from the same file more than once. (Or |\input|ed and randomly
% selected from the same file.)
%\\[10pt]
%\begin{ttfamily}
%! Package probsoln Error: Label \ldots\ undefined.
%\end{ttfamily}\\[10pt]
% You need to define a problem before you can use it.  Check to
% make sure you haven't mis-spelt it.
%\\[10pt]
%\begin{ttfamily}
%! Package probsoln Error: Requested number too large.
%\end{ttfamily}
%\\[10pt]
% You have asked for more problems than are defined within
% the specified file.  All problems in that file will be selected.
%
%\section{Contact Details}
%Dr Nicola Talbot\\
%School of Computing Sciences\\
%University of East Anglia\\
%Norwich. NR4 7TJ\\
%\url{http://theoval.cmp.uea.ac.uk/~nlct/}
%
% \StopEventually{}
% \section{The Code}
% \iffalse
% First we have the driver to get the documentation
%    \begin{macrocode}
%<*driver>
\documentclass{ltxdoc}
\usepackage[colorlinks,bookmarks]{hyperref}
\begin{document} 
\OnlyDescription
\DocInput{probsoln.dtx} 
\end{document}
%</driver>
%    \end{macrocode}
% \fi
% At the start of the package, specify that the \LaTeX2e format is
% required.  And specify the package provided.
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{probsoln}[2004/03/21 Problems and their Solutions Package]
\RequirePackage{ifthen}
%    \end{macrocode}
% Define newif to determine whether or not to show the answers.
% (Changed to |\provideboolean| 21 Mar 2004.)
%    \begin{macrocode}
\provideboolean{showanswers}
\newcommand{\showanswers}{\showanswerstrue}
\newcommand{\hideanswers}{\showanswersfalse}
%    \end{macrocode}
% Now define the package options
%    \begin{macrocode}
\DeclareOption{answers}{\showanswerstrue}
\DeclareOption{noanswers}{\showanswersfalse}
\ExecuteOptions{noanswers}
\ProcessOptions
%    \end{macrocode}
% Create a newif to determine whether a command has already been defined
%    \begin{macrocode}
\newif\ifdefined
\def\checkdefined#1{\ifx#1\relax \definedfalse \else \definedtrue \fi}
%    \end{macrocode}
% Create newifs for selecting randomly and parsing through file
%    \begin{macrocode}
\newif\ifselectrandom \selectrandomfalse
\newif\iffirstpass
\newif\ifselected
%    \end{macrocode}
% Define the command that creates a new problem
%    \begin{macrocode}
\newcommand{\solutionname}{Solution:}
\newcommand{\newproblem}[4][0]{%
\ifselectrandom
\iffirstpass
% on the first pass, just store the label, and increment \@probN
\global\advance\@probN by 1
\expandafter\gdef\csname @problabel\romannumeral\@probN\endcsname{#2}
\else
% check to see if this problem has been selected
\checkselected{#2}
\ifselected
\expandafter\checkdefined\csname @prob@#2\endcsname
\ifdefined
\PackageError{probsoln}{Label #2 already used}%
{Each problem must have a unique label identifier}
\else
\ifnum#1=0
% this problem has no arguments
\expandafter\newcommand\csname @prob@#2\endcsname[#1]{%
#3 \ifshowanswers\paragraph{\solutionname} #4\fi}
\else
\message{Problem #2 requires #1 argument(s), please specify (e.g. {5}{3}):}
\read-1to\@tmp
\expandafter\let\csname @prob@#2@arg\endcsname=\@tmp
\expandafter\newcommand\csname @prob@#2\endcsname[#1]{%
#3 \ifshowanswers\paragraph{\solutionname} #4\fi}
\fi
\fi
\fi
\fi
\else
\expandafter\checkdefined\csname @prob@#2\endcsname
\ifdefined
\PackageError{probsoln}{Label #2 already used}%
{Each problem must have a unique label identifier}
\else
\expandafter\newcommand\csname @prob@#2\endcsname[#1]{%
#3 \ifshowanswers\paragraph{\solutionname} #4\fi}
\fi
\fi
}
%    \end{macrocode}
% Define the command that uses a problem
%    \begin{macrocode}
\newcommand{\useproblem}[1]{%
\expandafter\checkdefined\csname @prob@#1\endcsname
\ifdefined
\def\doprob{\csname @prob@#1\endcsname}
\else
\PackageError{probsoln}{Label #1 undefined}%
{Can't find problem defined with label identifier '#1'}
\def\doprob{\relax}
\fi
\doprob
}
%    \end{macrocode}
% Now deal with the stuff for random selection.  
% Syntax: |\selectrandomly{|\meta{filename}|}{|\meta{n}|}|.
%    \begin{macrocode}
\newcount\@probN \newcount\@probselN \newcount\@rndselctr

% \PSNrandom{<count>}{<n>} stores a random number from 1 to <n>
% in the TeX count register <count>
\newcount\r@ndcur
\r@ndcur=1\relax
\newcommand{\PSNrandseed}[1]{%
\ifnum#1=0
\typeout{Can't have 0 as random seed, changing to 1}
\r@ndcur=1\relax
\else
\r@ndcur=#1\relax
\fi
\typeout{Random Seed = \the\r@ndcur}
}

\newcount\@ps@randtmp
\newcommand{\PSNrand}{
\@ps@randtmp=\r@ndcur
\multiply\@ps@randtmp by 16807\relax
\r@ndcur=\@ps@randtmp
\divide\r@ndcur by 120001
\multiply\r@ndcur by 120001
\advance\@ps@randtmp by -\r@ndcur
\r@ndcur = \@ps@randtmp
\ifnum\r@ndcur=0\relax\r@ndcur=1\fi
}

\newcommand{\PSNrandom}[2]{%
% generate new random number.
\PSNrand
#1=\r@ndcur
\@ps@randtmp=\r@ndcur
% now (#1 mod #2) + 1
\divide\@ps@randtmp by #2\relax
\multiply\@ps@randtmp by #2\relax
\advance#1 by -\@ps@randtmp
\advance#1 by 1\relax
}

\newcommand{\selectrandomly}[2]{%
\global\@probselN=#2
\global\@probN=0
\selectrandomtrue
\firstpasstrue
\input{#1}
\ifnum\@probselN>\@probN
\PackageError{probsoln}{Requested number too large}%
{You have asked for \the\@probselN \space problems, but there are only
\the\@probN \space problems defined in the file #1.  I will only select \the\@probN \space
problems.}
\global\@probselN=\@probN
\fi
\shuffle{@problabel}{\@probN}
\firstpassfalse
\input{#1}
\selectrandomfalse
\@rndselctr=1\relax
\whiledo{\@rndselctr < \@probselN \TE@or \@rndselctr = \@probselN}{%
\edef\@tmp{\csname @problabel\romannumeral\@rndselctr\endcsname}
\edef\@probargs{{\@tmp}\csname @prob@\@tmp @arg\endcsname}
\item \expandafter\useproblem\@probargs
\advance\@rndselctr by 1\relax
}
}
%    \end{macrocode}
% Check to see if label has been selected
%    \begin{macrocode}
\newcount\@ckselctr
\newcommand{\checkselected}[1]{%
\selectedfalse
\@ckselctr=1\relax
\whiledo{\@ckselctr < \@probselN \TE@or \@ckselctr = \@probselN}{%
\ifthenelse{\equal{#1}{\csname @problabel\romannumeral\@ckselctr\endcsname}}%
{\global\selectedtrue \global\@ckselctr=\@probselN}{}
\advance\@ckselctr by 1\relax
}
}
%    \end{macrocode}
% Shuffle contents of pseudo-array
%    \begin{macrocode}
\newcount\@shfctr \newcount\@shfA \newcount\@shfB
\newcommand{\shuffle}[2]{%
\@shfctr=1\relax
\whiledo{\@shfctr < 101}{%
\PSNrandom{\@shfA}{#2}\PSNrandom{\@shfB}{#2}
\ifnum\@shfA=\@shfB
\else
\edef\@@tmpA{\csname#1\romannumeral\@shfA\endcsname}
\let\@tmpA=\@@tmpA
\edef\@@tmpB{\csname#1\romannumeral\@shfB\endcsname}
\let\@tmpB=\@@tmpB
\expandafter\xdef\csname#1\romannumeral\@shfA\endcsname{\@tmpB}
\expandafter\xdef\csname#1\romannumeral\@shfB\endcsname{\@tmpA}
\fi
\advance\@shfctr by 1\relax
}
}
%    \end{macrocode}
%\Finale
\endinput
