infra: Expunge revision history clutter.
[mdwtools] / exercise.dtx
CommitLineData
86f6a31e 1% \begin{meta-comment}
2%
db127fce 3% $Id: exercise.dtx,v 1.2 2003/09/05 16:10:41 mdw Exp $
86f6a31e 4%
5% Exercises
6%
db127fce 7% (c) 2003 Mark Wooding
86f6a31e 8%
86f6a31e 9% \end{meta-comment}
10%
11% \begin{meta-comment} <general public licence>
12%%
13%% exercise package -- useful macros for setting exercises with answers
db127fce 14%% Copyright (c) 2003 Mark Wooding
86f6a31e 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}
db127fce 34%<+package> [2003/08/25 1.1 Exercies with answers]
86f6a31e 35% \end{meta-comment}
36%
db127fce 37% \CheckSum{271}
86f6a31e 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
db127fce 61\errorcontextlines\maxdimen
86f6a31e 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%
db127fce 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%
86f6a31e 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%
db127fce 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%
86f6a31e 217% \implementation
218%
219%
220%^^A-------------------------------------------------------------------------
221% \section{Implementation}
222%
223% \begin{macrocode}
224%<*package>
225% \end{macrocode}
226%
227% \subsection{Initialization}
228%
db127fce 229% The \textsf{within} option is handled by the \package{mdwkey} package.
86f6a31e 230%
231% \begin{macrocode}
db127fce 232\RequirePackage{mdwkey}
86f6a31e 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
db127fce 242\mkdef{exercise:opts}{within}{%
86f6a31e 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%
db127fce 255% The |\ex@opts| macro just runs the \package{mdwkey} kit to parse an option
86f6a31e 256% string.
257%
258% \begin{macrocode}
db127fce 259\def\ex@opts{\mkparse{exercise:opts}}
86f6a31e 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%
db127fce 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%
86f6a31e 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{%
db127fce 479 \@ifnextchar[{\answers@i\input}{\answers@i\@input[\ex@ansfilename]}%
480}
481\def\answers@i#1[#2]{%
86f6a31e 482 \immediate\closeout\ex@ansfile%
483 \global\let\answrite\exerr@toolate%
db127fce 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@%
86f6a31e 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%
db127fce 586 a `parlist'. This isn't allowed. You're probably doomed now.%
86f6a31e 587 }%
588}
589%</package>
590% \end{macrocode}
591%
592% Done.
593%
594% \hfill Mark Wooding, \today
595%
596% \Finale
597%
598\endinput