@@@ reinstate build-depends on mdwfonts, strayman
[mdwtools] / exercise.dtx
CommitLineData
86f6a31e 1% \begin{meta-comment} <general public licence>
2%%
3%% exercise package -- useful macros for setting exercises with answers
8bc5bdd2 4%% Copyright (c) 2001--2003 Mark Wooding
86f6a31e 5%%
3d509049 6%% This file is part of the `mdwtools' LaTeX package collection.
e8e9e5d8 7%%
3d509049
MW
8%% `mdwtools' is free software: you can redistribute it and/or modify it
9%% under the terms of the GNU General Public License as published by the
10%% Free Software Foundation; either version 2 of the License, or (at your
11%% option) any later version.
12%%
13%% `mdwtools' is distributed in the hope that it will be useful, but
14%% WITHOUT ANY WARRANTY; without even the implied warranty of
15%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16%% General Public License for more details.
e8e9e5d8 17%%
86f6a31e 18%% You should have received a copy of the GNU General Public License
3d509049 19%% along with `mdwtools'. If not, write to the Free Software Foundation,
86f6a31e 20%% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
3d509049 21%%
86f6a31e 22% \end{meta-comment}
23%
24% \begin{meta-comment} <Package preambles>
25%<+package>\NeedsTeXFormat{LaTeX2e}
26%<+package>\ProvidesPackage{exercise}
af8af7eb 27%<+package> [2020/09/06 1.14.0 Exercies with answers]
86f6a31e 28% \end{meta-comment}
29%
db127fce 30% \CheckSum{271}
86f6a31e 31%% \CharacterTable
32%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
33%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
34%% Digits \0\1\2\3\4\5\6\7\8\9
35%% Exclamation \! Double quote \" Hash (number) \#
36%% Dollar \$ Percent \% Ampersand \&
37%% Acute accent \' Left paren \( Right paren \)
38%% Asterisk \* Plus \+ Comma \,
39%% Minus \- Point \. Solidus \/
40%% Colon \: Semicolon \; Less than \<
41%% Equals \= Greater than \> Question mark \?
42%% Commercial at \@ Left bracket \[ Backslash \\
43%% Right bracket \] Circumflex \^ Underscore \_
44%% Grave accent \` Left brace \{ Vertical bar \|
45%% Right brace \} Tilde \~}
46%%
47%
48% \begin{meta-comment}
49%
50%<*driver>
51\input{mdwtools}
52\describespackage{exercise}
53\let\epsilon\varepsilon
db127fce 54\errorcontextlines\maxdimen
86f6a31e 55\mdwdoc
56%</driver>
57%
58% \end{meta-comment}
59%
60%^^A-------------------------------------------------------------------------
61% \section{User guide}
62%
63% The \package{exercise} package allows you to typeset exercises and keep the
64% answers together with the questions in your source (so they don't get
65% lost) but typeset them all together at the end of your document.
66%
67% \subsection{Exercises and answers}
68%
69% \DescribeEnv{exercise}
70% Exercises are typset in an \env{exercise} environment. This takes no
71% arguments. There's a counter for exercises, named |exercise|, and you can
72% cross-reference exercises in the usual way.
73%
74% \DescribeMacro\answer
75% If you want to include an answer for your exercise, just say |\answer|
76% followed by your answer. The rest of the text up to the end of the
77% \env{exercise} environment is considered to be the answer, and is stored
78% away until asked for. This material can contain anything you like. It
79% \emph{isn't} a moving argument.
80%
81% \begin{figure}
82%\begin{demo}[w]{The \env{exercise} environment}
83%\begin{exercise}
84%Show that if $F\colon \{0, 1\}^k \times \{0, 1\}^* \to \{0, 1\}^L$
85%is a $(t, q + 1, \epsilon)$-secure PRF, then $F$~is also a
86%$(t, q, \epsilon + 2^{-L})$-secure MAC.
87%\answer
88%Let~$A$ be an adversary attacking~$F$ as a MAC. Consider the
89%adversary~$B$ \ldots
90%\end{exercise}
91%\end{demo}
92% \end{figure}
93%
db127fce 94% \DescribeMacro\skipanswer
95% The |\skipanswer| command is similar, but just skips over the answer rather
96% than writing it to the output file. It does nothing at all with counters
97% -- it's as if there was no answer given at all.
98%
99% It's OK to use commands like |\answer| and |\skipanswer| in your own
100% macros as long as they're the \emph{last} token. You can therefore say
101% something like
102%\begin{verbatim}
103%\newcommand{\evenanswer}{%
104% \ifthenelse%
105% {\isodd{\value{exercise}}}%
106% {\answer}%
107% {\skipanswer}%
108%}
109%\end{verbatim}
110% to get just the answers to the odd-numbered problems. (If you don't like
111% \package{ifthen} then you'll need to play with |\expandafter| for a bit.)
112%
86f6a31e 113% \subsection{The answers file}
114%
115% \DescribeMacro\answrite
116% Answers are accumulated into a file, to be read later. Additional material
117% can be added to the file using the |\answrite| macro, which just writes its
118% argument. Note that this is a \emph{moving} argument, so fragile commands
119% need |\protect|ing.
120%
121% \DescribeMacro\exctrcheck
122% It's common to divide up the answers by section. You can tell the package
123% to check a collection of counters and perform actions if they've changed
124% since last time, giving you a chance to write the correct decorations to
125% the answers file. This is done by saying
126% \syntax{"\\exctrcheck{"<counter>"}{"<action>"}"}. Then, each |\answer|
127% command checks to see if \<counter> has changed since last time, and if it
128% has, it does \<action>, For example,
129%\begin{verbatim}
130%\exctrcheck{section}
131% {\answrite{\protect\subsection*{Section \thesection}}}
132%\end{verbatim}
133% starts a new (unnumbered) subsection in the answers for each section in the
134% main document.
135%
136% \subsection{Style tweaks}
137%
138% The \env{exercise} environment is very simple, and can be easily rewritten
139% to fit in with your style preferences. If you like exercises to look like
140% theorems, the easiest thing to do is say something like
141%\begin{verbatim}
142%\newtheorem{doexercise}[exercise]{\exercisename}
143%\renewenvironment{exercise}{\exfix\doexercise}{\enddoexercise}
144%\end{verbatim}
145% This makes the environment use the existing exercise counter. If you don't
146% want that, say
147%\begin{verbatim}
148%\newtheorem{doexercise}[othercounter]{\exercisename}
149%\renewenvironment{exercise}{\exfix\doexercise}{\enddoexercise}
150%\let\theexercise\theothercounter
151%\end{verbatim}
152% and all will be well.
153%
154% \DescribeEnv{doanswer}
155% Answers are typeset in a \env{doanswer} environment, which is given one
156% argument: the exercise number (as set by |\theexercise|). This can be
157% modified to do whatever you like.
158%
159% \DescribeMacro\exfix
160% The |\exfix| is a convenient hook which is run both in the \env{exercise}
161% and \env{doanswer} environments by default. The current implementation
162% just skips a level of \env{enumerate} depth, which usually means that
163% \env{enumerate} lists will go (a), (b), (c), \ldots\ rather than 1, 2, 3,
164% \ldots
165%
166% \subsection{Lists in paragraphs}
167%
168% \DescribeEnv{parenum}
169% Answers to subparts tend to be compressed together into a single
170% paragraph. It's nice, when you do this, not to have to worry about losing
171% your numbering of subparts. The \env{parenum} environment provides an
172% enumerated list in a paragraph. So, for example, you can say something
173% like this.
174%\begin{demo}[w]{Example of \env{parenum}}
175%\begin{exercise}
176%A PRG $g\colon \{0, 1\}^k \to \{0, 1\}^L$ is \emph{trivial} if
177%$k \ge L$.
178%\begin{enumerate}
179%\item Show that trivial PRGs exist.
180%\item Show that a non-trivial $(t, \epsilon)$-secure PRG is a
181% $(t, \epsilon + 2^{k-L})$-secure one-way function.
182%\end{enumerate}
183%\answer
184%\begin{parenum}
185%\item The identity function is a trivial PRG: the real and random
186% games are identically distributed.
187%\item Let~$A$ be an adversary attempting to invert~$g$: then we
188% can construct a distinguisher~$B$ as follows \ldots
189%\end{parenum}
190%\end{exercise}
191%\end{demo}
192%
193% \subsection{And finally}
194%
195% \DescribeMacro\answers
196% In order to extract your answers, say |\answers|.
197%\begin{demo}[w]{The \texttt{\string\answers} command}
198%\section*{Answers}
199%\answers
200%\end{demo}
201%
db127fce 202% The |\answers| command has an optional argument, which is the file to read
203% in. This allows you to make `answer booklet' documents, by saying
204% something like
205%\begin{verbatim}
206%\answers[otherdoc]
207%\end{verbatim}
208% If you don't give a file extension, then |.ans| is appended automatically.
209%
86f6a31e 210% \implementation
e8e9e5d8 211%
86f6a31e 212%
213%^^A-------------------------------------------------------------------------
214% \section{Implementation}
215%
216% \begin{macrocode}
217%<*package>
218% \end{macrocode}
219%
220% \subsection{Initialization}
221%
db127fce 222% The \textsf{within} option is handled by the \package{mdwkey} package.
86f6a31e 223%
224% \begin{macrocode}
db127fce 225\RequirePackage{mdwkey}
86f6a31e 226% \end{macrocode}
227%
228% \begin{macro}{\ex@within}
229%
230% When the \textsf{within} option is seen, we set a command |\ex@within| to
231% the correct code, to be executed later when we've made our minds up.
232%
233% \begin{macrocode}
234\let\ex@within\relax
db127fce 235\mkdef{exercise:opts}{within}{%
86f6a31e 236 \def\ex@within{%
237 \@addtoreset{exercise}{#1}%
238 \toks@\expandafter{\csname the#1\expandafter\endcsname%
239 \expandafter.\theexercise}%
240 \edef\theexercise{\the\toks@}%
241 }%
242}
243% \end{macrocode}
244% \end{macro}
245%
246% \begin{macro}{\ex@opts}
247%
db127fce 248% The |\ex@opts| macro just runs the \package{mdwkey} kit to parse an option
86f6a31e 249% string.
250%
251% \begin{macrocode}
db127fce 252\def\ex@opts{\mkparse{exercise:opts}}
86f6a31e 253% \end{macrocode}
254% \end{macro}
255%
256% Now do the options thing.
257%
258% \begin{macrocode}
259\DeclareOption*{\expandafter\ex@opts\expandafter{\CurrentOption}}
260\ProcessOptions*
261% \end{macrocode}
262%
263% Set up the |exercise| counter, and number it within some other sort of
264% counter as appropriate.
265%
266% \begin{macrocode}
267\newcounter{exercise}\ex@within
268% \end{macrocode}
269%
270% We also need the \package{sverb} package in order to do the delaying of the
271% answers.
272%
273% \begin{macrocode}
274\RequirePackage{sverb}
275% \end{macrocode}
276%
277% \subsection{Checking for counter changes}
278%
279% \begin{macro}{\ex@ctrcheck}
280%
281% The counter checking state is stored here. It's initially empty.
282%
283% \begin{macrocode}
284\def\ex@ctrcheck{}
285% \end{macrocode}
286% \end{macro}
287%
288% \begin{macro}{\exctrcheck}
289%
290% Adding a counter to the check list is relatively easy. We expand the
291% current list into a token register, add the new material to the end, and
292% put the list back in our macro using |\edef|. The `last' value of the
293% counter is set to |\relax|, to force out the change on the next |\answer|.
294%
295% \begin{macrocode}
296\def\exctrcheck#1#2{%
297 \toks@\expandafter{\ex@ctrcheck\ex@ctrdo{#1}{#2}}%
298 \edef\ex@ctrcheck{\the\toks@}%
299 \global\expandafter\let\csname ex@ctrlast@#1\endcsname\relax%
300}
301% \end{macrocode}
302% \end{macro}
303%
304% \begin{macro}{\ex@ctrdo}
305%
306% Here we actually check to see whether a counter has changed and execute the
307% appropriate code.
308%
309% \begin{macrocode}
310\def\ex@ctrdo#1#2{%
311 \edef\@tempa{\csname the#1\endcsname}%
312 \expandafter\ifx\csname ex@ctrlast@#1\endcsname\@tempa\else%
313 #2%
314 \global\expandafter\let\csname ex@ctrlast@#1\endcsname\@tempa%
315 \fi%
316}
317% \end{macrocode}
318% \end{macro}
319%
320% \subsection{The \env{exercise} environment}
321%
322% \begin{macro}{\exercisename}
323%
324% We store the string to print for each exercise in |\exercisename| as a
325% half-hearted attempt at internationalization.
326%
327% \begin{macrocode}
328\providecommand\exercisename{Exercise}
329% \end{macrocode}
330% \end{macro}
331%
332% \begin{macro}{\exfix}
333%
334% This is a dumping ground for style fixing common to both exercises and
335% answers. Here, we bump on the \env{enum} depth counter, so that it skips
336% labelling with digits.
337%
338% \begin{macrocode}
339\def\exfix{\advance\@enumdepth\@ne}
340% \end{macrocode}
341% \end{macro}
342%
343% \begin{environment}{exercise}
344%
345% This is pretty simple. The environment is list-based, with the number set
346% in bold in a label.
347%
348% \begin{macrocode}
349\def\exercise{%
350 \refstepcounter{exercise}%
351 \exfix%
352 \trivlist\advance\itemindent\labelsep%
353 \item[\textbf{\exercisename\ \theexercise}]%
354}
355\let\endexercise\endtrivlist
356% \end{macrocode}
357% \end{environment}
358%
359% \subsection{Answers}
360%
361% We need a file in which to store our answers.
362%
363% \begin{macrocode}
364\newwrite\ex@ansfile
365% \end{macrocode}
366%
367% \begin{macro}{\ex@ansfilename}
368%
369% In case anyone has a better idea for a filename than our default, we
370% provide a hook.
371%
372% \begin{macrocode}
373\def\ex@ansfilename{\jobname.ans}
374% \end{macrocode}
375% \end{macro}
376%
377% We open the file at the end of the preamble, to give the user a chance to
378% say |\nofiles|, or change |\ex@ansfilename|.
379%
380% \begin{macrocode}
381\AtBeginDocument{%
382 \if@filesw%
383 \immediate\openout\ex@ansfile=\ex@ansfilename\relax%
384 \answrite\relax%
385 \fi%
386}
387% \end{macrocode}
388%
389% \begin{macro}{\answrite}
390%
391% This writes stuff to the answers file. We make sure that it's
392% appropriately protected, so that you can insert section headings and so on.
393%
394% \begin{macrocode}
395\def\answrite#1{%
396 \if@filesw%
397 \begingroup%
398 \let\protect\@unexpandable@protect%
399 \immediate\write\ex@ansfile{#1}%
400 \endgroup%
401 \fi%
402}
403% \end{macrocode}
404% \end{macro}
405%
406% \begin{macro}{\answer}
407%
408% The |\answer| macro needs to read until the end of the enclosing
409% \env{exercise} environment (or whatever).
410%
411% \begin{macrocode}
412\def\answer{\sv@readenv\ex@answer}
413% \end{macrocode}
414%
415% Now for the main guts.
416%
417% \begin{macrocode}
418\def\ex@answer#1#2{%
419 \begingroup%
420 \@bsphack%
421% \end{macrocode}
422%
423% First of all, check to see whether any counters have changed.
424%
425% \begin{macrocode}
426 \ex@ctrcheck%
427% \end{macrocode}
428%
429% Start a \env{doanswer} environment in the file.
430%
431% \begin{macrocode}
432 \answrite{\noexpand\begin{doanswer}{\theexercise}}%
433% \end{macrocode}
434%
435% Set catcodes to be strange, and read lines one-at-a-time, writing them to
436% the file. When finished, continue at |\ex@endanswer|.
437%
438% \begin{macrocode}
439 \let\do\@makeother\dospecials%
440 \sv@safespc%
441 \sv@read{#1}\answrite{\ex@endanswer#2}%
442}
443% \end{macrocode}
444%
445% When that's done, we wind up here.
446%
447% \begin{macrocode}
448\def\ex@endanswer{%
449 \@esphack%
450 \answrite{\noexpand\end{doanswer}}%
451 \endgroup%
452}
453% \end{macrocode}
454% \end{macro}
455%
db127fce 456% \begin{macro}{\skipanswer}
457%
458% This is much simpler.
459%
460% \begin{macrocode}
461\let\skipanswer\ignore
462% \end{macrocode}
463% \end{macro}
464%
86f6a31e 465% \begin{macro}{\answers}
466%
467% The |\answers| macro closes the file, makes sure that future
468% \env{exercise}s with answers cause an error, and reads in the file.
469%
470% \begin{macrocode}
471\def\answers{%
db127fce 472 \@ifnextchar[{\answers@i\input}{\answers@i\@input[\ex@ansfilename]}%
473}
474\def\answers@i#1[#2]{%
86f6a31e 475 \immediate\closeout\ex@ansfile%
476 \global\let\answrite\exerr@toolate%
db127fce 477 \ex@withext{#1}{#2}{ans}%
478}
479\def\q@delim{\q@delim}
480\def\ex@withext#1#2#3{%
481 \edef\next@##1{\noexpand\ex@ext@i{##1}{#2}{#3}#2.\noexpand\q@delim}%
482 \next@{#1}%
483}
484\def\ex@ext@i#1#2#3#4.#5\q@delim{%
485 \ifx\q@delim#5\q@delim\def\next@{#1{#2.#3}}%
486 \else\def\next@{#1{#2}}\fi%
487 \next@%
86f6a31e 488}
489% \end{macrocode}
490% \end{macro}
491%
492% \begin{environment}{doanswer}
493%
494% A very simple environment. We set the exercise number in bold and then
495% just write the text.
496%
497% \begin{macrocode}
498\def\doanswer#1{%
499 \exfix%
500 \trivlist\advance\itemindent\labelsep%
501 \item[\textbf{#1}]%
502}
503\let\enddoanswer\endtrivlist
504% \end{macrocode}
505% \end{environment}
506%
507% \subsection{Lists inside paragraphs}
508%
509% \begin{environment}{parlist}
510%
511% The \env{parlist} environment is a trimmed-down version of a normal list.
512% We todge the |\item| command, make |\list| and |\trivlist| make errors, and
e8e9e5d8 513% do various normal list things.
86f6a31e 514%
515% \begin{macrocode}
516\def\parlist#1#2{%
517 \let\@trivlist\exerr@parlist%
518 \def\@itemlabel{#1}%
519 \let\makelabel\relax%
520 \@nmbrlistfalse%
521 #2%
522 \let\item\pl@item%
523 \ignorespaces%
524}
525% \end{macrocode}
526% \end{environment}
527%
528% \begin{macro}{\pl@item}
529%
530% This is the implementation of |\item| within a \env{parlist}. The main
531% interesting point is the game with boxes, which has the objective of
532% extracting the text of the item, together with any style changes set by
533% |\makelabel|, but without any stupid bits of glue, or |\llap| or anything
534% like that.
535%
536% \begin{macrocode}
537\def\pl@item{\@ifnextchar[\pl@item@i{\pl@item@i[\@itemlabel]}}
538\def\pl@item@i[#1]{%
539 \if@nmbrlist\refstepcounter{\@listctr}\fi%
540 \setbox\z@\hbox{\makelabel{\global\setbox\@ne\hbox{#1}}}%
541 \ifvmode\leavevmode\else\unskip\hskip1em\fi\box\@ne~\ignorespaces%
542}
543% \end{macrocode}
544% \end{macro}
545%
546% \begin{macro}{\useparlist}
547%
548% We just set the \env{list} environment to use \env{parlist}.
549%
550% \begin{macrocode}
551\def\useparlist{\let\list\parlist\let\endlist\relax}
552% \end{macrocode}
553% \end{macro}
554%
555% \begin{environment}{parenum}
556%
557% Very simple, this. Note that we don't run |\endenumerate|, because that's
558% |\let| to |\endlist|.
559%
560% \begin{macrocode}
561\def\parenum{\useparlist\enumerate}
562\let\endparenum\endparlist
563% \end{macrocode}
564% \end{environment}
565%
566% \subsection{Errors}
567%
568% \begin{macrocode}
569\def\exerr@toolate{%
570 \PackageError{exercise}{Too late now for \string\answrite}{%
571 You can't write answers after you've read the file in. I've^^J%
572 ignored the text you attempted to write. This is why answers^^J%
573 go at the end of a book!%
574 }%
575}
576\def\exerr@parlist{%
577 \PackageError{exercise}{You can't nest a `list' inside a `parlist'.}{%
578 I've found a `list' or `trivlist' environment nested inside^^J%
db127fce 579 a `parlist'. This isn't allowed. You're probably doomed now.%
86f6a31e 580 }%
581}
582%</package>
583% \end{macrocode}
584%
585% Done.
586%
587% \hfill Mark Wooding, \today
588%
589% \Finale
590%
591\endinput