Replace general GPL rubrics with project-specific ones.
[mdwtools] / exercise.dtx
1 % \begin{meta-comment} <general public licence>
2 %%
3 %% exercise package -- useful macros for setting exercises with answers
4 %% Copyright (c) 2001--2003 Mark Wooding
5 %%
6 %% This file is part of the `mdwtools' LaTeX package collection.
7 %%
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.
17 %%
18 %% You should have received a copy of the GNU General Public License
19 %% along with `mdwtools'. If not, write to the Free Software Foundation,
20 %% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 %%
22 % \end{meta-comment}
23 %
24 % \begin{meta-comment} <Package preambles>
25 %<+package>\NeedsTeXFormat{LaTeX2e}
26 %<+package>\ProvidesPackage{exercise}
27 %<+package> [2020/09/06 1.14.0 Exercies with answers]
28 % \end{meta-comment}
29 %
30 % \CheckSum{271}
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
54 \errorcontextlines\maxdimen
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 %
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 %
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 %
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 %
210 % \implementation
211 %
212 %
213 %^^A-------------------------------------------------------------------------
214 % \section{Implementation}
215 %
216 % \begin{macrocode}
217 %<*package>
218 % \end{macrocode}
219 %
220 % \subsection{Initialization}
221 %
222 % The \textsf{within} option is handled by the \package{mdwkey} package.
223 %
224 % \begin{macrocode}
225 \RequirePackage{mdwkey}
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
235 \mkdef{exercise:opts}{within}{%
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 %
248 % The |\ex@opts| macro just runs the \package{mdwkey} kit to parse an option
249 % string.
250 %
251 % \begin{macrocode}
252 \def\ex@opts{\mkparse{exercise:opts}}
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 %
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 %
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{%
472 \@ifnextchar[{\answers@i\input}{\answers@i\@input[\ex@ansfilename]}%
473 }
474 \def\answers@i#1[#2]{%
475 \immediate\closeout\ex@ansfile%
476 \global\let\answrite\exerr@toolate%
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@%
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
513 % do various normal list things.
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%
579 a `parlist'. This isn't allowed. You're probably doomed now.%
580 }%
581 }
582 %</package>
583 % \end{macrocode}
584 %
585 % Done.
586 %
587 % \hfill Mark Wooding, \today
588 %
589 % \Finale
590 %
591 \endinput