Fix checksum.
[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%
9%----- Revision history -----------------------------------------------------
10%
11% $Log: exercise.dtx,v $
db127fce 12% Revision 1.2 2003/09/05 16:10:41 mdw
13% Non-typesetting of answers. Reading in answers from another document.
14%
86f6a31e 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
db127fce 24%% Copyright (c) 2003 Mark Wooding
86f6a31e 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}
db127fce 44%<+package> [2003/08/25 1.1 Exercies with answers]
86f6a31e 45% \end{meta-comment}
46%
db127fce 47% \CheckSum{271}
86f6a31e 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
db127fce 71\errorcontextlines\maxdimen
86f6a31e 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%
db127fce 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%
86f6a31e 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%
db127fce 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%
86f6a31e 227% \implementation
228%
229%
230%^^A-------------------------------------------------------------------------
231% \section{Implementation}
232%
233% \begin{macrocode}
234%<*package>
235% \end{macrocode}
236%
237% \subsection{Initialization}
238%
db127fce 239% The \textsf{within} option is handled by the \package{mdwkey} package.
86f6a31e 240%
241% \begin{macrocode}
db127fce 242\RequirePackage{mdwkey}
86f6a31e 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
db127fce 252\mkdef{exercise:opts}{within}{%
86f6a31e 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%
db127fce 265% The |\ex@opts| macro just runs the \package{mdwkey} kit to parse an option
86f6a31e 266% string.
267%
268% \begin{macrocode}
db127fce 269\def\ex@opts{\mkparse{exercise:opts}}
86f6a31e 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%
db127fce 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%
86f6a31e 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{%
db127fce 489 \@ifnextchar[{\answers@i\input}{\answers@i\@input[\ex@ansfilename]}%
490}
491\def\answers@i#1[#2]{%
86f6a31e 492 \immediate\closeout\ex@ansfile%
493 \global\let\answrite\exerr@toolate%
db127fce 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@%
86f6a31e 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%
db127fce 596 a `parlist'. This isn't allowed. You're probably doomed now.%
86f6a31e 597 }%
598}
599%</package>
600% \end{macrocode}
601%
602% Done.
603%
604% \hfill Mark Wooding, \today
605%
606% \Finale
607%
608\endinput