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