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