X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=texmf%2Ftex%2Flatex%2Fexam.cls;h=ca441f5e73e476762536184e428d4166589518e3;hb=0639e71975967a0ab00418f10cd9c0e99d8b66eb;hp=d6165bf41555c864f881beca6784665b38c0658e;hpb=8170177c59dcdbd063ab091f7b04ca4a9c8d57df;p=lib.git diff --git a/texmf/tex/latex/exam.cls b/texmf/tex/latex/exam.cls index d6165bf..ca441f5 100644 --- a/texmf/tex/latex/exam.cls +++ b/texmf/tex/latex/exam.cls @@ -3,7 +3,8 @@ % A LaTeX2e document class for preparing exams. %% exam.cls -%% Copyright (c) 1994, 1997, 2000, 2004 Philip S. Hirschhorn +%% Copyright (c) 1994, 1997, 2000, 2004, 2008, 2011, +%% 2015, 2017 Philip S. Hirschhorn % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 @@ -18,7 +19,7 @@ % This work consists of the files exam.cls and examdoc.tex. -% The user documentation for exam.cls is in the file examdoc.tex. +% The user's guide for exam.cls is in the file examdoc.tex. %%% Philip Hirschhorn @@ -31,14 +32,10 @@ % from my web page: http://www-math.mit.edu/~psh/ -\def\fileversion{2.2} -\def\filedate{2004/08/14} +\def\fileversion{2.603} +\def\filedate{2017/12/17} %--------------------------------------------------------------------- %--------------------------------------------------------------------- -% PLEASE DO NOT MAKE ANY CHANGES TO THIS FILE! -% -% If you wish to make changes to this file, rename this file -% to something other than exam.cls BEFORE YOU MAKE THE CHANGES! % % If there's some feature that you'd like that this file doesn't % provide, tell me about it. @@ -86,844 +83,509 @@ % %-------------------------------------------------------------------- %-------------------------------------------------------------------- -% Changelog: +% Changelog since version 2.4: +%-------------------------------------------------------------------- +% Version 2.603, 2017/12/15 + +% No longer betatest. -% Version 2.2: -% -% Enough already with betatest. -% %-------------------------------------------------------------------- -% -% Version 2.116$\beta$: -% -% We futzed with the oneparchoices environment so that a bit more -% space will be left before the first choice when the list of choices -% continues the paragraph of the question. -% +% Version 2.602$\beta$, 2017/12/15 + +% We changed the code for multicolumn grade and point tables to remove +% the incompatibility with colortbl.sty and other packages that load +% colortbl.sty (e.g., xcolor.sty with the "table" option). + %-------------------------------------------------------------------- -% -% Version 2.115$\beta$: -% -% New environment: oneparchoices -% -% Intended for multiple choice questions (just like the choices -% environment), except that the oneparchoices prints all the choices in -% a single paragraph. This environment does *not* create a paragraph -% break at its beginning, so if you begin the environment without -% skipping a line before it, the choices will be printed as a -% continuation of the paragraph preceding the environment. -% +% Version 2.601$\beta$, 2017/09/22 + +% We changed command and environment names in the code from framed.sty +% that's included (slightly modified) in exam.cls so that the user can +% say \usepackage{framed} without creating conflicts. This also allows +% the user to use packages, such as minted.sty, that load framed.sty + %-------------------------------------------------------------------- -% -% Version 2.114$\beta$: -% -% We fixed up the code for \uplevel and \fullwidth so that they work -% correctly if a solution environment is inside the argument. -% +% Version 2.6, 2017/09/19 + +% No longer betatest. + %-------------------------------------------------------------------- -% -% Version 2.113$\beta$: -% -% Added code to warn if point totals have changed since the last run of -% LaTeX (which requires running LaTeX again to make sure that -% gradetables, pointsofquestion, and pointsonpage values are correct). -% -% -% We also added code to create a label for every question, part, -% subpart, and subsubpart. We make no use of these labels, but we put -% them there so that if a question (or part, etc.) is, e.g., moved from -% one page to another, LaTeX will notice this and warn the user that -% LaTeX must be run one more time to be sure everything is correct. -% -% We need to do this even though we've already included code to check -% when point totals change because questions (and parts, etc.) know what -% page they're on from reading the info written to the .aux file on the -% previous run. Thus, if a question (or part, etc.) is moved to a -% different page, then the pointsonpage totals won't notice until the -% *second* subsequent run of LaTeX, and so there'll be no warning to the -% user on the *first* run. Including these labels gives the user a -% warning on that first run. -% +% Version 2.510$\beta$, 2016/10/11 + +% Bugfix: We changed \@setheadheight and \@setfootheight to fix a bug +% that was introduced by the bugfix in version 2.306beta, 2009/03/28: +% If the second page has a different \textheight (because of a change +% in either headheight or footheight between pages 1 and 2), then page +% 2 would use the \textheight of page 1. Pages 3 and beyond would get +% the correct \textheight. The original version of this set \@colroom +% and \vsize to the new \textheight, but that had a bug in that if a +% float appeared at the top of a page, there would be no notice taken +% of the space lost to the float, and so the text would overrun the +% bottom of the page. The bugfix in version 2.306beta eliminated the +% changes to \@colht and \vsize. In this bugfix, we adjust \@colroom, +% \@colht, and \vsize in the same way that we adjust \textheight. + %-------------------------------------------------------------------- -% -% Version 2.112$\beta$: -% -% New environment: coverpages -% -% This is for use only *before* any questions (or parts, or subparts, or -% subsubparts). It allows you to print one or more cover pages, -% numbered in a different sequence of page numbers from the main pages. -% That is, the page number is reset to 1 at the end of a coverpages -% environment. -% -% The default is that headers and footers are empty on the coverpages, -% but if you print the page number (using \thepage in the header or -% footer) then the page number will be printed in roman numerals. When -% the coverpages environment ends, the page number is reset to 1 and the -% page numbers revert to the (standard) arabic numbers. -% -% Headers and footers on coverpages are determined by commands -% completely analogous to the relevant commands for the main pages, -% except that the names of the commands begin with the word ``cover''. -% That is, the following commands are available to define headers and -% footers, and to leave additional space for the headers and footers as -% needed: -% -% -% New commands: -% -% \coverheader -% \coverrunningheader -% \coverfirstpageheader -% -% \coverlhead -% \coverchead -% \coverrhead -% -% \coverfooter -% \coverrunningfooter -% \coverfirstpagefooter -% -% \coverlfoot -% \covercfoot -% \coverrfoot -% -% \coverextraheadheight -% \coverextrafootheight -% +% Version 2.509$\beta$, 2016/09/12 + +% Multicolumn grade and point tables, and a new syntax for multirow +% grade and point tables (which were introduced in version +% 2.508beta). + +% Multicolumn tables are vertically oriented, while multirow tables +% are horizontally oriented, and so they do not take the optional +% argument choosing between horizontal and vertical. They all take +% one required argument specifying the number of columns (for +% multicolumn) or the number of rows (for multirow). + +% The tables can be: + +% grade tables or point tables, + +% plain, bonus, or combined, + +% indexed by questions or by pages, + +% complete or partial. + +% As usual, if you omit the optional argument that chooses between +% questions and pages, you get questions. + +% The new commands are: + +% \def\multirowgradetable{numrows}[questions or pages] +% \def\multirowpointtable{numrows}[questions or pages] +% \def\multirowbonusgradetable{numrows}[questions or pages] +% \def\multirowbonuspointtable{numrows}[questions or pages] +% \def\multirowcombinedgradetable{numrows}[questions or pages] +% \def\multirowcombinedpointtable{numrows}[questions or pages] + +% \def\multirowpartialgradetable{numrows}{rangename}[questions or pages] +% \def\multirowpartialpointtable{numrows}{rangename}[questions or pages] +% \def\multirowpartialbonusgradetable{numrows}{rangename}[questions or pages] +% \def\multirowpartialbonuspointtable{numrows}{rangename}[questions or pages] +% \def\multirowpartialcombinedgradetable{numrows}{rangename}[questions or pages] +% \def\multirowpartialcombinedpointtable{numrows}{rangename}[questions or pages] + +% \def\multicolumngradetable{numcols}[questions or pages] +% \def\multicolumnpointtable{numcols}[questions or pages] +% \def\multicolumnbonusgradetable{numcols}[questions or pages] +% \def\multicolumnbonuspointtable{numcols}[questions or pages] +% \def\multicolumncombinedgradetable{numcols}[questions or pages] +% \def\multicolumncombinedpointtable{numcols}[questions or pages] + +% \def\multicolumnpartialgradetable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialpointtable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialbonusgradetable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialbonuspointtable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedgradetable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedpointtable{numcols}{rangename}[questions or pages] + +% The older grade and point table commands can still be used. For +% example, the commands + +% \gradetable[h][questions] +% \multirowgradetable{1}[questions] + +% are equivalent. + +% The distance between the rows of a multirow table and between the +% columns of a multicolumn table is \doublerulesep, the default value +% of which is 2.0pt. You can change that using a \setlength command, +% as in + +% \setlength{\doublerulesep}{0.5in} + %-------------------------------------------------------------------- -% -% Version 2.111$\beta$: -% -% Bugfix: There was an incompatibility with spanish.ldf (loaded when the -% user loads the babel package with the spanish option) (and possibly -% with other language packages as well). That file redefines \@roman, -% which is used by \roman, which I had used to define some internally -% used command names. I replaced -% -% \roman{countername} -% -% with -% -% \romannumeral \csname c@countername\endcsname -% -% for all of those command names, and now all at least seems to be -% well. -% -% -% The following stuff isn't really complete yet: -% New environment: coverpages -% +% Version 2.508$\beta$, 2016/08/06 + +% New commands: Multirow grade and point tables. + +% These are all horizontally oriented tables, and so do not take the +% optional argument choosing between horizontal and vertical. They all +% take one required argument specifying the number of columns, which is +% the number of columns used for the point values (including the total), +% but not counting the column of row headings. + +% Note: The syntax was changed in version 2.509beta, so that you now +% specify the number of *rows* rather than the number of *columns*! For +% example, the first command below should now be + +% \multirowgradetable{numrows}[questions or pages] + +% The tables can be: + +% grade tables or point tables, + +% plain, bonus, or combined, + +% indexed by questions or by pages, + +% complete or partial. + +% The new commands are: + + +% \multirowgradetable{numcols}[questions or pages] +% \multirowpointtable{numcols}[questions or pages] +% \multirowbonusgradetable{numcols}[questions or pages] +% \multirowbonuspointtable{numcols}[questions or pages] +% \multirowcombinedgradetable{numcols}[questions or pages] +% \multirowcombinedpointtable{numcols}[questions or pages] + +% \multirowpartialgradetable{numcols}{rangename}[questions or pages] +% \multirowpartialpointtable{numcols}{rangename}[questions or pages] +% \multirowpartialbonusgradetable{numcols}{rangename}[questions or pages] +% \multirowpartialbonuspointtable{numcols}{rangename}[questions or pages] +% \multirowpartialcombinedgradetable{numcols}{rangename}[questions or pages] +% \multirowpartialcombinedpointtable{numcols}{rangename}[questions or pages] + +%-------------------------------------------------------------------- +% Version 2.507$\beta$, 2016/07/14 + % New commands: -% -% \coverheader -% \coverrunningheader -% \coverfirstpageheader -% -% \coverlhead -% \coverchead -% \coverrhead -% -% \coverfooter -% \coverrunningfooter -% \coverfirstpagefooter -% -% \coverlfoot -% \covercfoot -% \coverrfoot -% -% \coverextraheadheight -% \coverextrafootheight -% + +% \pointstwosided +% \pointstwosidedreversed + +% The first causes points to be in the right margin on odd numbered +% pages and in the left margin on even numbered pages. + +% The second causes points to be in the left margin on odd numbered +% pages and in the right margin on even numbered pages. + +% Also: Some minor edits (e.g., deleting the unused \thebonuspoints). + %-------------------------------------------------------------------- -% -% Version 2.110$\beta$: -% -% We change the definition of \half to print a fraction one half that -% uses a slanted fraction bar. We also created two new commands: -% -% \newcommand*\usehorizontalhalf -% \newcommand*\useslantedhalf -% -% The first one changes the definition of \half so that it produces a -% fraction with a horizontal fraction bar (via $\frac{1}{2}$), and the -% second one returns the definition of \half to its default. -% +% Version 2.506$\beta$, 2016/05/12 + +% Fixed an obscure bug that arose only when \CorrectChoiceEmphasis +% used color and a \CorrectChoice (in a choices or checkboxes +% environment) followed a \choice whose text completely filled its +% last line, and which was not separated from the \CorrectChoice by a +% blank line, in which case an extra (blank) line was inserted by that +% \choice. We fixed this by adding an improvised "\leavehmode" +% (styled after \leavevmode) to the \CorrectChoice command in both the +% choices and checkboxes environments, which caused the text of the +% previous \choice to be broken into lines *before* the \special +% inserted by the \color command was added. + %-------------------------------------------------------------------- -% -% Version 2.109$\beta$: -% -% Point values for questions (and parts, etc.) can now include half -% points. To specify a half point, you include ``\half'' immediately -% following the integer part (or just with ``\half'' if the integer part -% is zero). For example, the following are all valid point values: -% -% 0 -% \half -% 1 -% 1\half -% 2 -% 2\half -% Etc. -% +% Version 2.505$\beta$, 2016/05/10 + +% We fixed a bug in the choices and checkboxes environments that arose +% only when \CorrectChoiceEmphasis used color. If it did, and if the +% text of a correct choice exactly filled a line, and if there was no +% blank line in the latex file separating this correct choice from the +% following choice, there would be an extra blank line inserted after +% the correct choice. We did this by inserting \color@begingroup and +% \color@endgroup as needed. (We're pretty sure the actual fix was +% the \endgraf in the expansion of \color@endgroup.) + %-------------------------------------------------------------------- -% -% Version 2.108$\beta$: -% -% New environment: solutionorlines -% -% This is almost identical to the solution environment, except that when -% solutions are not being printed and an optional argument appears -% specifying an amount of space to be left for answers, that space is -% filled with ruled lines (created by \fillwithlines), rather than being -% left blank (as it is by the solution environment). -% -% Also: Changed the shade of gray used as the default color for -% \shadedsolutions. -% -% Changed the default thickness of lines used by \fillwithlines. -% +% Version 2.504$\beta$, 2016/05/09 + +% We fixed a bug in the solutionbox environment that caused enumerate, +% itemize, or description environments to have their text stick into +% the right margin. We did this by resetting \@totalleftmargin and +% \linewidth in the box containing the solution. + %-------------------------------------------------------------------- -% -% Version 2.107$\beta$: -% -% We added an option to have solution environments use a \colorbox (from -% the color package) instead of an \fbox. Instead of surrounding the -% solution with a printed box, it prints the solution on a colored -% background. The default color is a light gray (so that it can be -% printed on any printer that can do grayscale), but the color can be -% changed. -% -% To use this option, the user must load the color package with the -% command -% -% \usepackage{color} -% -% in the preamble. (If appropriate, optional arguments can be used in -% that command.) The user can then give the command -% -% \shadedsolutions -% -% to have all solutions printed using a \colorbox. After giving that -% command, the user can change the color of the backgrounds of solutions -% by defining the color ``SolutionColor''. The default SolutionColor is -% set via the command -% -% \definecolor{SolutionColor}{gray}{0.9} -% -% and the user can change that by giving a \definecolor command *after* -% giving the command \shadedsolutions. For example, the command -% -% \definecolor{SolutionColor}{rgb}{0.8,0.9,1} -% -% sets the background color to a light blue. -% -% You can return to the default situation of having solutions printed -% inside an \fbox by giving the command -% -% \framedsolutions -% -% Note added later: The default shade of gray was changed in version -% 2.108$\beta$. -% +% Version 2.503$\beta$, 2016/03/25 + +% New commands: + +% \colorfillwithlines +% \colorfillwithdottedlines + +% The first causes the lines drawn by the \fillwithlines command to be +% drawn in color. The default color is set by the command + +% \definecolor{FillWithLinesColor}{gray}{0.8} + +% and the color can be changed by giving a new \definecolor command. +% You can return to black lines by giving the command + +% \nocolorfillwithlines + +% \colorfillwithdottedlines causes the lines drawn by the +% \fillwithdottedlines command to be drawn in color. The default +% color is set by the command + +% \definecolor{FillWithDottedLinesColor}{gray}{0.8} + +% and the color can be changed by giving a new \definecolor command. +% You can return to black dotted lines by giving the command + +% \nocolorfillwithdottedlines + %-------------------------------------------------------------------- -% -% Version 2.106$\beta$: -% -% -% We redid the solution environment to use Donald Arseneau's framed.sty -% macros, so that the solution can be broken across pages with each -% piece enclosed in a frame. -% -% We also changed the default definition of \solutiontitle; it's now -% defined by: -% -% \newcommand{\solutiontitle}{\noindent\textbf{Solution:}\enspace} -% -% +% Version 2.502$\beta$, 2016/03/23 + +% The command + +% \colorsolutionboxes + +% that was created in version 2.501beta now affects not only the boxes +% created by \solutionbox, but also by \makeemptybox, \solutionorbox, +% and all of the boxes printed by all of the various solution +% environments when solutions are being printed surrounded by a box. + %-------------------------------------------------------------------- +% Version 2.501$\beta$, 2016/02/08 + +% Changed the \solutionbox environment so that it works correctly +% inside a tabular. + +% Also: The \solutionbox frame can now be printed in color, as long as +% you load color.sty in the preamble. % -% Version 2.105$\beta$: -% -% We're making the thickness of the lines used by \fillwithlines -% changeable, and changing the default thickness. +% Usage: Say % -% We're doing this by defining a new command: +% \usepackage{color} % -% \linefill, +% in the preamble, and then give the command % -% which is similar to \hrulefill, except that \linefill uses a line of -% height \linefillthickness, whose default value is 0.2pt. -% (\fillwithlines used to use \hrulefill, which uses a line of height -% 0.4pt.) +% \colorsolutionboxes % -% The default value of \linefillthickness is set by the command +% to have the frame around a solutionbox in color. The default color +% was created by the command % -% \setlength\linefillthickness{0.2pt} +% \definecolor{SolutionBoxColor}{gray}{0.8} % -% and this can be changed by giving a new \setlength command. +% and you can change the color by giving a new \definecolor command +% (which must be done *after* the \colorsolutionboxes command). % -% Note added later: The default thickness was changed in version -% 2.108$\beta$. +% To cancel color solutionbox frames and return to black, give the +% command % +% \nocolorsolutionboxes + %-------------------------------------------------------------------- -% -% Version 2.104$\beta$: -% -% We added a new command: \fillwithlines -% -% \fillwithlines takes one argument, which is either a length or \fill, -% and it fills that much vertical space with horizontal lines that run -% the length of the current line. That is, they extend from the current -% left margin (which depends on whether we're in a question, part, -% subpart, or subsubpart) to the right margin. -% -% The distance between the lines is \linefillheight, whose default value -% is set with the command -% -% \setlength\linefillheight{.25in} -% -% This value can be changed by giving a new \setlength command. -% +% Version 2.5 2015/05/07 + +% No longer betatest. + %-------------------------------------------------------------------- +% Version 2.408$\beta$ 2013/11/17 + +% New commands: % -% Version 2.103$\beta$: -% -% We added a new command: \answerline -% -% This is intended for short answer questions. It inserts a \vskip of -% length \answerskip and then inserts a horizontal line of length -% \answerlinelength at the right margin, preceded by the number of the -% current question, part, subpart, or subsubpart. -% -% The default values are set by the commands -% -% \setlength\answerlinelength{1in} -% \setlength\answerskip{2ex} -% -% and these can be changed by giving new \setlength commands. +% \firstqinrange{whatever} +% \lastqinrange{whatever} +% \numqinrange{whatever} % -%-------------------------------------------------------------------- +% where ``whatever'' is the name of a grading range. % -% Version 2.101$\beta$: +% \firstqinrange{whatever} prints the number of the first question in +% the range. % -% We eliminated the command \marks, since it conflicts with a definition -% of that name in some package or other. We changed the definition of -% \marksnotpoints so that it still accomplishes the same thing, but now -% it works through the \pointpoints command. +% \lastqinrange{whatever} prints the number of the last question in the +% range. % -% We changed the default format for the \droptotalpoints command, so -% that it now prints +% \numqinrange{whatever} prints the number of questions in the range. % -% Total for Question 1: 25 +% We changed a couple of internal command name related to grading +% ranges. If the user defines the range `myrange', then we now use + +% \range@myrange@firstp +% \range@myrange@lastp +% \range@myrange@firstq +% \range@myrange@lastq % -% where the ``25'' is followed by \marginpointname, whose default value -% is empty. +% where we used to use % +% \tbl@myrange@firstp +% \tbl@myrange@lastp +% \tbl@myrange@firstq +% \tbl@myrange@lastq + %-------------------------------------------------------------------- +% Version 2.407$\beta$ 2012/12/19 + +% New environment: % -% Version 2.098$\beta$: -% -% We moved the definitions of \thepartno, \thesubpart, and -% \thesubsubpart outside of the associated list environments, so that -% the user can redefine them once at the beginning of the LaTeX file and -% not have their redefinition overridden in every such list. -% -% We also replaced most occurrences of \thequestion with -% \arabic{question}, so that the user can safely redefine \thequestion -% to something like \Alph{question}, etc. -% -% We rewrote the components of the \gradetable[v][questions] and -% \gradetable[h][questions] to use the question counter in place of -% @iterator, and to insert \thequestion instead of \the@iterator as the -% question number in the table (so that if the user redefines -% \thequestion, then the table will print the question ``numbers'' (or -% letters, etc.) in whatever format \thequestion prints the question -% numbers on the exam). -% -% We also created a new command: +% solutionbox % -% \totalpoints +% The solutionbox environment is different from the other solution +% environments (solution, solutionorbox, solutionorlines, +% solutionordottedlines, and solutionorgrid), in that % -% for use in the argument of a \totalformat command. \totalpoints is -% just an abbreviation for \pointsofquestion{\arabic{question}}. That -% is, it's a macro that prints the total number of points for the -% current question. +% (1) The box is always printed, whether answers are being printed +% or not. % -% We changed the longsolution environment to make it easy to change the -% amount by which the left and right margins are increased. The default -% amount is set by the command +% (2) The argument giving the size of the box is a required +% argument, not an optional argument, and so it should be enclosed +% in braces, not in brackets. It can be either a length or +% \stretch{number}. % -% \setlength{\longsolutionindent}{2em} +% (3) We make no use of the TheSolution environment; the solutionbox +% environment is completely freestanding. % -% and the user can change this by giving a new \setlength command. +% If answers are not being printed then only the box is printed, with +% nothing in it. If answers are being printed, then the solution is +% printed inside of the box. % -% Note added later: longsolution was eliminated in version -% 2.106$\beta$. +% Note: It's the user's responsibility to be sure that the box is +% large enough to hold the solution! If the solution takes up too +% much vertical space, then it will spill out of the bottom of the +% box, overwriting whatever follows the box. % %-------------------------------------------------------------------- +% Version 2.406$\beta$, 2012/12/16 % -% Version 2.097$\beta$: -% -% We've changed the default format for the total points for a question -% printed by the \droptotalpoints command. This command still prints -% the total points for the question right justified a distance of -% \rightpointsmargin from the right edge of the paper, but now the -% number of points will, by default, be surrounded by either -% -% a double box, if \boxedpoints is in effect, -% double brackets, if \bracketedpoints is in effect, or -% double parentheses, otherwise. -% -% It's still true that if you use a \totalformat command, then the -% format of the printed total points is completely controlled by the -% argument of the \totalformat command and the default doesn't matter at -% all. +% New command: % +% \noquestionsonthispage % -% Note added later: We changed the default format again in version -% 2.101$\beta$; see the notes above. +% This command tells the \ifcontinuation and \ifincomplete commands +% to assume that no part of any question is on this page. This is +% similar to the job done by the \nomorequestions command for the +% pages that follow the end of all of the questions. % -%-------------------------------------------------------------------- -% -% Version 2.096$\beta$: % -% The labels for questions, parts, subparts, and subsubparts can now be -% customized. The format now depends on the commands +% If you give the command \noquestionsonthispage on a page, then % -% \questionlabel -% \partlabel -% \subpartlabel -% \subsubpartlabel +% (1) \ifcontinuation on that page will expand to its second +% argument, +% (2) \ifincomplete on that page will expand to its second +% argument, and +% (3) an \ifincomplete on an earlier page will not assume that a +% question from that earlier page continues onto this page. % -% the default definitions of which are: +% The way that this command affects the \ifincomplete command on +% earlier pages is as follows: If there is a page with no questions or +% parts or subparts or subsubparts, then the last page before that +% with a question (or part, etc.) would normally be deemed incomplete; +% if, however, the page with no questions (or parts, etc.) (along with +% all adjacent pages with no questions or parts etc.) has a +% \noquestionsonthispage command, then that last page with a question +% (or part, etc.) will not be deemed incomplete. % -% \newcommand\questionlabel{\thequestion.} -% \newcommand\partlabel{(\thepartno)} -% \newcommand\subpartlabel{\thesubpart.} -% \newcommand\subsubpartlabel{\thesubsubpart)} -% -% These definions can be changed by using a \renewcommand command. -% Note that the definition of \partlabel used `\thepartno', and *not* -% `\thepart'. This is because `\thepart' refers to the counter for the -% standard sectioning command \part, and not the counter used in the -% parts environment. The counter used by the parts environment is -% inserted with the command `\thepartno'. +% Note that if you're tempted to use this command on a page that follows +% the end of all of the questions, then you should probably use the +% command \nomorequestions instead. % %-------------------------------------------------------------------- -% -% Version 2.095$\beta$: -% -% Added solution and longsolution environments. -% -% The command \printanswers causes both of these environments print the -% solution, and the command \noprintanswers causes both of these -% environments not to print the solution. The default is -% \noprintanswers. -% -% The commands \printanswers and \noprintanswers can be given as many -% times as desired to switch back and forth between the two. -% -% The documentclass option ``answers'' is equivalent to giving a -% \printanswers command at the beginning of the file. -% -% -% Both of these environments take an optional argument which is an -% amount of blank vertical space to be left when \noprintanswers is in -% effect. (The default value of this blank vertical space is 0pt.) -% -% -% The solution environment prints the solution inside of a box, which -% cannot be broken across pages. Thus, it is only appropriate for -% solutions that are short enough to fit on a page. -% -% The longsolution environment prints the solution with left and right -% margins slightly increased, and it can be broken across pages. -% -% When printing the solution, both environments begin with -% \solutiontitle, the default value of which is created by the command -% -% \newcommand{\solutiontitle}{\textbf{Solution:}\enspace} -% -% This can be changed with the \renewcommand command. -% -% Note added later: solution was changed and longsolution was -% eliminated in version 2.106$\beta$. +% Version 2.405$\beta$, 2012/10/21 +% +% It is now possible to use a parts, subparts, or subsubparts +% environment inside one of the solution environments (solution, +% solutionorbox, solutionorlines, solutionordottedlines, or +% solutionorgrid) without getting problems from multiply defined +% labels or having its points (if any) counted as being actual points +% on the exam. +% +% Any \part, \subpart, or \subsubpart command inside one of the +% solution environments now writes a \PgInfo command in the .aux file +% of the form question2@object3, but no labels and no other \PgInfo +% commands. In addition, if there are points assigned to any of these +% commands inside any of the solution environments, those points are +% not added to the points of the question or the points on the page, +% and do not affect any gradetables or pointtables. % %-------------------------------------------------------------------- +% Version 2.404$\beta$, 2012/09/03 % -% Version 2.094$\beta$: -% -% We've expanded the options of the \gradetable command, so that you -% can now get a grading table indexed by either question numbers or by -% page numbers, and in either case you can have either a horizontally -% oriented table or a vertically oriented table. -% -% \gradetable now takes two optional arguments: The first can be either -% `[v]' or `[h]', and the second can be either `[questions]' or -% `[pages]'. The defaults are `[v]' and `[questions]', and you must -% specify a first option if you want to specify a second option. -% -% Thus: \gradetable is equivalent to \gradetable[v][questions] +% New command: % -% `[v]' and `[h]' are as before: Vertically oriented or horizontally -% oriented. +% \fillwithgrid{length} % -% `[questions]' is the same as the earlier version: A table that lists -% the question numbers, the possible total points for each question the -% total for the entire exam, and spaces are left for the score on each -% question. -% -% `[pages]' prints a table indexed by page numbers instead of by -% question numbers. It lists only the numbers of pages that have points -% on them, and for each such page it lists the number of points possible -% on that page. It also lists the total possible points on the exam, -% and spaces are left for the score on each page. +% New environment: % -% For both tables indexed by questions and tables indexed by points, -% there are commands that allow you to change the column and row -% headings (the following are shown setting the default values:) +% solutionorgrid % -% \hpword{Points:} -% \hsword{Score:} -% \htword{Total} -% \vpword{Points} -% \vsword{Score} -% \vtword{Total:} +% These are similar to the \fillwithlines command and the +% solutionorlines environment. % -% For tables indexed by questions, the appropriate row and column titles -% are set by the following commands: +% By default, the created grids are in black. However, if you give the +% commands % -% \hqword{Question:} -% \vqword{Question} +% \usepackage{color} +% \colorgrids % -% For tables indexed by pages, the appropriate row and column titles are -% set by the following commands: +% then the grids will be in color, by default a light gray. That +% default color was defined by the command % -% \hpgword{Page:} -% \vpgword{Page} +% \definecolor{GridColor}{gray}{0.8} % +% You can change the color by redefining the color GridColor, and you +% can return to using black grids by giving the command % -% For both \gradetable[h] and \gradetable [v], the width of the blank -% cells created for filling in the grades can be changed with the -% command \cellwidth, and the arraystretch applied to the table can be -% changed with the command \gradetablestretch. The default values are -% created by the commands: +% \nocolorgrids % -% \cellwidth{2em} -% \gradetablestretch{1.5} -% -%-------------------------------------------------------------------- +% The default grid size and grid line thickness were set by the +% commands % -% Version 2.093$\beta$: +% \setlength{\gridsize}{5mm} +% \setlength{\gridlinewidth}{0.1pt} % -% If the user gives the comman \addpoints, then -% \pointsonpage{n} expands to the number of points on page n. +% You can change either or both of those by giving new \setlength +% commands. The period of the grid is \gridsize (both horizontally +% and vertically). That is, the horizontal distance from the left +% edge of one vertical line to the left edge of the next vertical line +% is \gridsize, as is the vertical distance from the top edge of one +% horizontal line to the top edge of the next horizontal line. Thus, +% each square has outer side length equal to \gridsize+\gridlinewidth. % %-------------------------------------------------------------------- +% Version 2.403$\beta$, 2012/08/29: % -% Version 2.092$\beta$: -% -% Increased the \leftmargin in the choices environment. +% We changed the code for the command \fillin (which had been modified +% in version 2.402beta) so that if only one optional argument is used, +% a space following that optional argument will not be ignored. We +% did this in such a way that the second optional argument will be +% recognized even when spaces appear in between the optional +% arguments. % %-------------------------------------------------------------------- +% Version 2.402$\beta$, 2012/08/21: % -% Version 2.091$\beta$: +% We modified the command \fillin that we had created in version +% 2.401beta. \fillin now takes two optional arguments (and no required +% arguments). % -% New commands: -% \pointsdroppedatright -% \droppoints -% \droptotalpoints -% \totalformat -% -% \pointsdroppedatright: The command \pointsdroppedatright causes points -% not to be printed until you give the command \droppoints, except that -% a \qformat command still works as expected (i.e., if you include -% ``\thepoints'' in the argument of the \qformat, it will print the -% points as usual). -% -% -% \droppoints: The command \droppoints should be given only at the end -% of a paragraph or between paragraphs; if you give it within a -% paragraph, it causes the paragraph to end. \droppoints prints the -% most recent point value in the right margin, formatted as it is when -% you give the command \pointsinrightmargin, except that the points -% appear opposite the last line of the paragraph (or, if the command -% \droppoints is given between paragraphs, then additional vertical -% space is left between the paragraphs and the points are printed -% opposite the blank space). Thus, the formatting can be changed by -% using giving the commands \bracketedpoints, \boxedpoints, or -% \marginpointname. -% -% The command \droppoints actually works this way even if one of the -% commands \pointsdroppedatright, \pointsinmargin, or -% \pointsinrightmargin, is in effect, but if you use it that way the -% points will appear twice on the page. -% -% -% \droptotalpoints: To use the command \droptotalpoints, you must first -% give the command \addpoints. The command \droptotalpoints should be -% given only at the end of a paragraph or between paragraphs; if you -% give it within a paragraph, it causes the paragraph to end. -% \droptotalpoints prints the total points for the current question -% (i.e., the sum of the points assigned to the question and to all of -% its parts, subparts, and subsubparts) in the right margin, formatted -% formatted by default inside either double brackets, double box, or -% double parentheses, depending on whether \bracketedpoints, -% \boxedpoints, or neither is in effect. -% -% \totalformat: The \totalformat command allows you to change the format -% used by the \droptotalpoints command. It takes one argument, and that -% argument becomes the command to print the total points, right -% justified a distance of \rightpointsmargin from the right edge of the -% paper. The argument should contain the expression -% ``\pointsofquestion{\arabic{question}}'' at the point at which the number -% of points should appear. For example, the command -% -% \totalformat{[\pointsofquestion{\arabic{question}}]} -% -% will produce the same appearance as the default does when the command -% \bracketedpoints is in effect and \marginpointname is empty, and the -% command +% \fillin can take two optional arguments, as in % -% \totalformat{\fbox{Total: \pointsofquestion{\arabic{question}}}} +% \fillin[Answer][Length] % -% produces a box surrounding ``Total: 25''. +% The first optional argument is the answer to be printed above the line +% when \printanswers is in effect; the default value is empty. That +% line is printed a distance of \answerclearance below the baseline. % -%-------------------------------------------------------------------- +% The second optional argument is the length of the line that we print; +% the default value is \fillinlinelength. The value of +% \fillinlinelength is set with the command % -% Version 2.087$\beta$: +% \setlength\fillinlinelength{1in} % -% We changed ``\point@toks={}'' into ``\global \point@toks={}'' when -% \point@toks is set, so that if the user somehow arranges for us to be -% inside of a group when we enter horizontal mode, the \point@toks will -% be properly set equal to null, and so the points won't accidentally be -% placed a second time at the second paragraph of the question. +% and can be changed by giving a new \setlength command. % -% Similarly, we changed ``\pageinfo@commands={}'' to -% ``\global \pageinfo@commands={}''. +% When answers are being printed, the first optional argument is +% printed subject to the declarations in the argument of the last +% \CorrectChoiceEmphasis command. It is centered on the line unless +% it is too long, in which case it extends to the right of the line. % %-------------------------------------------------------------------- +% Version 2.401$\beta$, 2012/08/20: % -% Version 2.080$\beta$: -% -% We added a new command \gradetable that produces a grading table, -% oriented either vertically or horizontally, that lists the questions, -% their point values, and the total points, and leaves spaces for -% entering the grade for each question and the total grade. We also -% added a new command \pointsofquestion that takes one argument that is -% assumed to be the number of a question and returns the total number of -% points for that question. \pointsofquestion is used by the -% \gradetable command, but the user can choose to use it separately. - -% To use either \gradetable or \pointsofquestion, the user must give the -% command \addpoints. \pointsofquestion{n} will then return the total -% number of points for question n and all of its parts, subparts, and -% subsubparts. Since all this information is stored in the .aux file -% and then read back on the next run of LaTeX, the user must run LaTeX -% twice after any changes to the questions or points. - -% \gradetable[h] produces a horizonatlly oriented grade table, and -% \gradetable[v] produces a vertically oriented grade table. The -% command \gradetable is equivalent to \gradetable[v]. - -% \gradetable[h] produces a table with three rows, titled (by default) -% ``Question:'', ``Points:'', and ``Score:'' (the titles are in the -% first column of the table). There is then one column for each -% question (with the question number and point value in the first two -% rows), plus a final column labelled ``Total'' with the total points in -% the second row. - -% \gradetable[v] produces a table with three columns, titled (by -% default) ``Question'', ``Points'', and ``Score'' (the titles are in -% the first row of the table). There is then one row for each question -% (with the question number and point value in the first two columns), -% plus a final row labelled ``Total:'' with the total points in the -% second column. - -% Note that \gradetable[h] produces row titles whose default values -% contains colons, while \gradetable[v] produces column titles whose -% default values do not contain colons. - -% The row and column titles and the word ``Total'' can be changed using -% the commands \hqword, \hpword, \hsword, \htword, \vqword, -% \vpword, \vsword, and \vtword, where the first four affect -% \gradetable[h] and the second four affect \gradetable[v]. The default -% values are created by the commands: - -% \hqword{Question:} -% \hpword{Points:} -% \hsword{Score:} -% \htword{Total} -% \vqword{Question} -% \vpword{Points} -% \vsword{Score} -% \vtword{Total:} - -% For both \gradetable[h] and \gradetable [v], the width of the blank -% cells created for filling in the grades can be changed with the -% command \cellwidth, and the arraystretch applied to the table can be -% changed with the command \gradetablestretch. The default values are -% created by the commands: - -% \cellwidth{2em} -% \gradetablestretch{1.5} - - - -% Changing the definition of \points: - -% The default definition of \points is that it expands to ``point'' if -% the number of points is 1 and to ``points'' otherwise. There is now a -% command ``\pointpoints'' that takes two arguments, after which -% \points will expand to the first argument when the number of points is -% 1 and to the second argument otherwise. For example, the default is -% the result of the command \pointpoints{point}{points}. % -%-------------------------------------------------------------------- -% -% Version 2.070$\beta$: +% New command: % -% We made this compatible with hyperref.sty. -% We did this by eliminating all use of LaTeX's -% \label, \newlabel, \ref, and \pageref commands. -% We now put information into the .aux file using the -% \PgInfo@write command, and get that info out of the aux file -% using \PgInfo and \PgInfo@get, along with a few \gdef commands -% written straight to the .aux file. +% \fillin[CorrectAnswer]{width} % -%-------------------------------------------------------------------- +% This is for use in fill in the blank questions. This command inserts +% a blank line of width ``width''. If answers are being printed and if +% the optional argument ``CorrectAnswer'' appears, then the optional +% argument is printed subject to the declarations in the argument of the +% last \CorrectChoiceEmphasis command, and it is printed a distance of +% \answerclearance above the line. It is centered on the line unless it +% is too long, in which case it extends to the right of the line. % -% Version 2.067$\beta$: +% Note: We changed this command in version 2.401beta. % -% We're adding code to enable headers and footers -% to know whether the current page continues a question started on an -% earlier page, and whether the page ends with a question still -% incomplete. - %-------------------------------------------------------------------- -% For use in headers and footers: -% -% \ifcontinuation{Text 1}{Text 2} -% Expands to ``Text 1'' if this page begins with a part or subpart or -% subsubpart that continues a question begun on an earlier page, and -% expands to ``Text 2'' if this page begins with a new question. -% -% \ContinuedQuestion expands to the number of the question that is -% being continued from an earlier page. -% -% \ifincomplete{Text 1}{Text 2} -% Expands to ``Text 1'' if the last question begun on or before this -% page has a part, subpart, or subsubpart that begins on a later page, -% and if we have not yet encountered a \nomorequestions command. -% -% \IncompleteQuestion expands to the number of the question that is -% continued on the next page. -% -% \nomorequestions is a command that you can give after the last -% question if you intend to include extra material (e.g., tables for -% use on the exam) but you don't want the pages containing the extra -% material to be labelled as continuing the last question on the exam. -% -%-------------------------------------------------------------------- -% -% -% Also: -% Changed the code for headers and footers to -% use the command \normalfont instead of \rm so that the main -% document font will appear in headers and footers even when the -% main document font is *not* a roman font. -% -% \def\marksnotpoints -% -% Changes \pointname so that it is identical to the default \pointname -% except that the work ``mark'' is used instead of the word -% ``point''. That is, if the number of points is 1, then you'll get -% ``1 mark'', and if the number of points is other than 1 then you'll -% get ``n marks'' (where n is the number of points). -% -% -% \bracketedpoints -% \nobracketedpoints -% -% The default remains to put the points inside of parentheses. -% \bracketedpoints switches to points inside of square brackets and -% \boxedpoints switches to points in side of a box. Both -% \nobracketedpoints and \noboxedpoints return to the default of -% points inside of parentheses no matter how many \boxedpoints and -% \bracketedpoints commands have been given in any order. -% -% -% \pointsinrightmargin -% \nopointsinrightmargin -% -% The default remains to put the points right after the question -% number, before the text of the question. \pointsinrightmargin -% switches to points in the right margin and \pointsinleftmargin -% switches to points in the left margin. Both \nopointsinrightmargin -% and \nopointsinleftmargin return to the default of points right -% after the question number, before the text of the question, no -% matter how many \pointsinrightmargin and \pointsinleftmargin -% commands have been given in any order. -% -% if \pointsinrightmargin is used, then the points are printed -% right justified in the right margin, with the right edge a distance -% of \rightpointsmargin from the right edge of the paper. -% The default value of \rightpointsmargin is 1 cm, but it can be set -% to any other length with the usual \setlength command -% (as in \setlength{\rightpointsmargin}{0.5cm}). -% -% -% subsubparts environment -% Numbered using lower case greek letters. -% -% -% choices environment, for multiple choice answers -% -% -% \qformat{Format line} -% -% Format line must have some stretch (e.g., at least one \hfil or -% \dotfill or something similar). -% \thequestion inserts the question number, -% \thepoints inserts the number of points followed by pointname if -% a number of points has been specified for this question, and it -% inserts nothing at all if no points have been specified. -% -% \noqformat returns to the standard question number formatting. -% -% -% -% The standard options -% a4paper -% a5paper -% b5paper -% letterpaper -% legalpaper -% executivepaper -% landscape -% now all work. -% -% -% -% Two new commands for use in \pointname, \marginpointname, and -% \qformat: -% -% \points expands to either ``point'' or ``points'', depending on -% whether the number of points is 1 or other than 1. -% -% \marks expands to either ``mark'' or ``marks'', depending on -% whether the number of points is 1 or other than 1. -% -% -%-------------------------------------------------------------------- -%-------------------------------------------------------------------- -% -% The only change from version 2.0 to version 2.01 is that this -% documentclass (and its accompanying documentation) is now -% explicitly distributed under the LaTeX Project Public License. -% %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -934,10 +596,34 @@ \RequirePackage{ifthen} -\newif\if@printanswers -\@printanswersfalse -\DeclareOption{answers}{\@printanswerstrue} -\DeclareOption{noanswers}{\@printanswersfalse} +\newif\ifprintanswers +\printanswersfalse +\DeclareOption{answers}{\printanswerstrue} +\DeclareOption{noanswers}{\printanswersfalse} + +\newif\ifcancelspace +\cancelspacefalse +\DeclareOption{cancelspace}{\cancelspacetrue} +\DeclareOption{nocancelspace}{\cancelspacefalse} + +% The following keeps track of whether the user has requested that we +% add up the points on the exam. We make the default false so that +% users who put other than numbers into the points argument of a +% question (or part, or subpart) won't get error messages. +% We use \if@printtotalpoints as a flag to signal that we are counting +% points, so that we will know to print the total on the screen (and +% in the log file). We use this separate flag so that the user can +% use both \addpoints and \noaddpoints to count some points and not +% others, but still have the total printed when we finish the file no +% matter what the state of \if@addpoints. +\newif\if@addpoints +\newif\if@printtotalpoints +\def\addpoints{\global\@addpointstrue\global\@printtotalpointstrue} +\def\noaddpoints{\global\@addpointsfalse} +\@addpointsfalse +\@printtotalpointsfalse +\DeclareOption{addpoints}{\addpoints} + \DeclareOption*{% \PassOptionsToClass{\CurrentOption}{article}% @@ -983,10 +669,6 @@ \setlength{\marginparwidth}{.5in} \setlength{\marginparsep}{5pt} - - - - %-------------------------------------------------------------------- % **************** @@ -1014,9 +696,6 @@ \advance\@rightmargin by -\@extrawidth } - - - %-------------------------------------------------------------------- %-------------------------------------------------------------------- % Making room for large headers and footers @@ -1053,10 +732,9 @@ \setlength{\covrun@exft}{0in} \setlength{\covfp@exft}{0in} - \newcommand*\adj@hdht@ftht{% \if@coverpages - \ifnum\value{page}=1 + \ifnum\value{page}=1\relax \@setheadheight{\covfp@exhd}% \@setfootheight{\covfp@exft}% \else @@ -1064,7 +742,7 @@ \@setfootheight{\covrun@exft}% \fi \else - \ifnum\value{page}=1 + \ifnum\value{page}=1\relax \@setheadheight{\fp@exhd}% \@setfootheight{\fp@exft}% \else @@ -1074,7 +752,6 @@ \fi } - \newcommand*\extraheadheight{% \@ifnextchar[{\@xtrahd}{\@ytrahd}% } @@ -1107,7 +784,6 @@ \adj@hdht@ftht } - \newcommand*\coverextraheadheight{% \@ifnextchar[{\cov@xtrahd}{\cov@ytrahd}% } @@ -1147,13 +823,18 @@ \@appendoutput{\adj@hdht@ftht} %-------------------------------------------------------------------- -% \setheadheight and \setfootheight: +% \@setheadheight and \@setfootheight: \def\@setheadheight#1{% \begingroup % Avoid trouble from using \@temp and \@spaces % Reset the effect of the most recent change: \global\advance\topmargin by -\@extrahead \global\advance\textheight by \@extrahead + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by \@extrahead + \global\advance\@colht by \@extrahead + \global\advance\vsize by \@extrahead + % % Save the newly set value: \def\@temp{#1} \def\@spaces{ } @@ -1169,23 +850,60 @@ % Set the new values: \global\advance\topmargin by \@extrahead \global\advance\textheight by -\@extrahead + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by -\@extrahead + \global\advance\@colht by -\@extrahead + \global\advance\vsize by -\@extrahead + % + % Bugfix, Version 2.510beta, 2016/10/11: + % We're fixing a bug that was introduced by the bugfix in version + % 2.306beta, 2009/03/28: If the second page has a different + % \textheight (because of a change in either headheight or + % footheight between pages 1 and 2), then page 2 would use the + % \textheight of page 1. Pages 3 and beyond would get the correct + % \textheight. + % The original version of this set \@colroom and \vsize to the new + % \textheight, but that had a bug in that if a float appeared at + % the top of a page, there would be no notice taken of the space + % lost to the float, and so the text would overrun the bottom of + % the page. + % In this bugfix, we adjust \@colroom, \@colht, and \vsize in the + % same way that we adjust \textheight. + % % Make it take effect RIGHT NOW!: % (The following stuff isn't necessary if \@setheadheight is % executed only in the preamble or as we return from the output % routine, but we're leaving it in so that this will still work if % we use this at some random point in the middle of composing a - % page). - \global\@colht=\textheight - \global\@colroom=\textheight - \global\vsize=\textheight - \global\pagegoal=\textheight + % page). + % Bugfix, Version 2.306beta, 2009/03/28: + % We don't do this!! + % If the user had a figure environment that floated to the + % top of a page, then this would cause that page to run + % over the footer and off the bottom of the page, because + % this somehow caused a full page's worth of stuff to be + % placed after the figure, as if the figure wasn't taking + % up space on the page. + % We *do* need to put \@colht at the correct new value, though, + % apparently because \@colht is set near the end of the + % output routine. +% \global\@colht=\textheight +% +% \global\@colroom=\textheight +% \global\vsize=\textheight +% \global\pagegoal=\textheight \endgroup -} +}% @setheadheight \def\@setfootheight#1{% \begingroup % Avoid trouble from using \@temp and \@spaces % Reset the effect of the most recent change: \global\advance\textheight by \@extrafoot + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by \@extrafoot + \global\advance\@colht by \@extrafoot + \global\advance\vsize by \@extrafoot + % % Save the newly set value: \def\@temp{#1} \def\@spaces{ } @@ -1200,20 +918,50 @@ \fi % Set the new values: \global\advance\textheight by -\@extrafoot + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by -\@extrafoot + \global\advance\@colht by -\@extrafoot + \global\advance\vsize by -\@extrafoot + % + % Bugfix, Version 2.510beta, 2016/10/11: + % We're fixing a bug that was introduced by the bugfix in version + % 2.306beta, 2009/03/28: If the second page has a different + % \textheight (because of a change in either headheight or + % footheight between pages 1 and 2), then page 2 would use the + % \textheight of page 1. Pages 3 and beyond would get the correct + % \textheight. + % The original version of this set \@colroom and \vsize to the new + % \textheight, but that had a bug in that if a float appeared at + % the top of a page, there would be no notice taken of the space + % lost to the float, and so the text would overrun the bottom of + % the page. + % In this bugfix, we adjust \@colroom, \@colht, and \vsize in the + % same way that we adjust \textheight. + % % Make it take effect RIGHT NOW!: % (The following stuff isn't necessary if \@setfootheight is % executed only in the preamble or as we return from the output % routine, but we're leaving it in so that this will still work if % we use this at some random point in the middle of composing a % page). - \global\@colht=\textheight - \global\@colroom=\textheight - \global\vsize=\textheight - \global\pagegoal=\textheight + % Bugfix, Version 2.306beta, 2009/03/28: + % We don't do this!! + % If the user had a figure environment that floated to the + % top of a page, then this would cause that page to run + % over the footer and off the bottom of the page, because + % this somehow caused a full page's worth of stuff to be + % placed after the figure, as if the figure wasn't taking + % up space on the page. + % We *do* need to put \@colht at the correct new value, though, + % apparently because \@colht is set near the end of the + % output routine. +% \global\@colht=\textheight +% +% \global\@colroom=\textheight +% \global\vsize=\textheight +% \global\pagegoal=\textheight \endgroup -} - - +}% @setfootheight %--------------------------------------------------------------------- @@ -1256,8 +1004,12 @@ \newif\if@coverpages \@coverpagesfalse +\newcounter{num@coverpages} +% We'll set this to zero in case there is no coverpages environment: +\setcounter{num@coverpages}{0} + \newenvironment{coverpages}{% - \ifnum \value{numquestions}>0 + \ifnum \value{numquestions}>0\relax \ClassError{exam}{% Coverpages cannot be used after questions have begun.\MessageBreak }{% @@ -1271,7 +1023,16 @@ \adj@hdht@ftht }{% \clearpage + \setcounter{num@coverpages}{\value{page}}% + \addtocounter{num@coverpages}{-1}% \pagenumbering{arabic}% + % Bugfix, Version 2.307\beta, 2009/06/11: + % We have to say \@coverpagesfalse before \adj@hdht@ftht + % because we're still inside the group created by the + % coverpages environment and we want to set the + % extraheadheight and extrafootheight to the values correct + % for the first non-cover page: + \@coverpagesfalse \adj@hdht@ftht } @@ -1285,18 +1046,16 @@ }% } - - \newcommand*\@dohead{% \def\@oddhead{% \if@coverpages - \ifnum\value{page}=1 + \ifnum\value{page}=1\relax \cov@fullhead \else \covrun@fullhead \fi \else - \ifnum\value{page}=1 + \ifnum\value{page}=1\relax \@fullhead \else \run@fullhead @@ -1309,13 +1068,13 @@ \newcommand*\@dofoot{% \def\@oddfoot{% \if@coverpages - \ifnum\value{page}=1 + \ifnum\value{page}=1\relax \cov@fullfoot \else \covrun@fullfoot \fi \else - \ifnum\value{page}=1 + \ifnum\value{page}=1\relax \@fullfoot \else \run@fullfoot @@ -1335,9 +1094,6 @@ \let\@evenfoot=\@oddfoot } - - - %-------------------------------------------------------------------- % \@fullhead, \run@fullhead, \@fullfoot, and \run@fullfoot: @@ -1358,7 +1114,6 @@ }% vbox } - \newcommand*\run@fullhead{% \vbox to \headheight{% \vss @@ -1376,8 +1131,6 @@ }% vbox } - - % We arrange it so that the very top of first line of text in the % foot is at a fixed position on the page, whether or not there's % a footrule: @@ -1400,7 +1153,6 @@ }% vbox } - \newcommand*\run@fullfoot{% \vbox to 0pt{% \ifrun@footrule @@ -1440,7 +1192,6 @@ }% vbox } - \newcommand*\covrun@fullhead{% \vbox to \headheight{% \vss @@ -1458,8 +1209,6 @@ }% vbox } - - % We arrange it so that the very top of first line of text in the % foot is at a fixed position on the page, whether or not there's % a footrule: @@ -1482,7 +1231,6 @@ }% vbox } - \newcommand*\covrun@fullfoot{% \vbox to 0pt{% \ifcovrun@footrule @@ -1501,7 +1249,6 @@ }% vbox } - %-------------------------------------------------------------------- %-------------------------------------------------------------------- % @@ -1563,7 +1310,6 @@ \runningheader{#1}{#2}{#3}% } - \def\firstpagefooter#1#2#3{% \def\@lfoot{#1}% \def\@cfoot{#2}% @@ -1581,8 +1327,6 @@ \runningfooter{#1}{#2}{#3}% } - - \def\lhead{\@ifnextchar[{\@xlhead}{\@ylhead}} \def\@xlhead[#1]#2{\def\@lhead{#1}\def\run@lhead{#2}} \def\@ylhead#1{\def\run@lhead{#1}\def\@lhead{#1}} @@ -1610,8 +1354,6 @@ % Initialize head and foot: - - \pagestyle{headandfoot} \lhead{} @@ -1679,7 +1421,6 @@ \coverrunningheader{#1}{#2}{#3}% } - \def\coverfirstpagefooter#1#2#3{% \def\cov@lfoot{#1}% \def\cov@cfoot{#2}% @@ -1697,8 +1438,6 @@ \coverrunningfooter{#1}{#2}{#3}% } - - \def\coverlhead{\@ifnextchar[{\cov@xlhead}{\cov@ylhead}} \def\cov@xlhead[#1]#2{\def\cov@lhead{#1}\def\covrun@lhead{#2}} \def\cov@ylhead#1{\def\covrun@lhead{#1}\def\cov@lhead{#1}} @@ -1726,7 +1465,6 @@ % Initialize coverpage head and foot: - \coverlhead{} \coverchead{} \coverrhead{} @@ -1754,8 +1492,6 @@ \def\headrule{\@headruletrue\run@headruletrue} \def\noheadrule{\@headrulefalse\run@headrulefalse} - - \newif\if@footrule \newif\ifrun@footrule @@ -1768,14 +1504,11 @@ \def\footrule{\@footruletrue\run@footruletrue} \def\nofootrule{\@footrulefalse\run@footrulefalse} - - % Initialize: \noheadrule \nofootrule - % Cover page headrules and footrules: \newif\ifcov@headrule @@ -1790,8 +1523,6 @@ \def\coverheadrule{\cov@headruletrue\covrun@headruletrue} \def\nocoverheadrule{\cov@headrulefalse\covrun@headrulefalse} - - \newif\ifcov@footrule \newif\ifcovrun@footrule @@ -1804,94 +1535,153 @@ \def\coverfootrule{\cov@footruletrue\covrun@footruletrue} \def\nocoverfootrule{\cov@footrulefalse\covrun@footrulefalse} - - % Initialize: \nocoverheadrule \nocoverfootrule - - %-------------------------------------------------------------------- %-------------------------------------------------------------------- % \numpages, \iflastpage, and \oddeven % Also: \numpoints, \numquestions, \numparts, and \numsubparts % Also also: \pointsofquestion +% Also: \numcoverpages and \totalnumpages % Make the number of pages available as the macro \numpages, % the number of points as \numpoints, % the number of questions as \numquestions, % the number of parts as \numparts, and % the number of subparts as \numsubparts -%\def\numpages{\pageref{@lastpage}} -%\def\numpoints{\pageref{@numpoints}} -%\def\numquestions{\pageref{@numquestions}} -%\def\numparts{\pageref{@numparts}} -%\def\numsubparts{\pageref{@numsubparts}} -%\def\numsubsubparts{\pageref{@numsubsubparts}} - -% This was previously done with \pageref commands. When I eliminated -% all \label, \ref, and \pageref commands in order to make this -% compatible with hyperref.sty, this stuff was created: - -% \gdef commands for exam@lastpage, exam@numpoints, exam@numquestions, -% exam@numparts, exam@numsubparts and exam@numsubsubparts are written to -% the .aux file via \AtEndDocument. - -% \gdef commands for pointsofq@i, pointsofq@ii, etc. are written to the -% .aux file as each question is completed (see the definition of the + +% This was previously done with \pageref commands. When I stopped +% using \pageref for this (in order to make this compatible with +% hyperref.sty), this stuff was created: + +% \gdef commands for exam@lastpage, exam@numpoints, +% exam@numbonuspoints, exam@numquestions, exam@numparts, +% exam@numsubparts and exam@numsubsubparts are written to the .aux +% file via \AtEndDocument. + +% \gdef commands for pointsofq@i, pointsofq@ii, etc. and +% bonuspointsofq@i, bonuspointsofq@ii, etc. are written to the .aux +% file as each question is completed (see the definition of the % questions environment). -% \gdef commands for pointsonpage@i, pointsonpage@ii, etc. are written -% to the .aux file as we encounter points defined for a later page, and -% for the last such page with AtEndDocument. +% \gdef commands for pointsonpage@i, pointsonpage@ii, etc. and +% bonuspointsonpage@i, bonuspointsonpage@ii, etc. are written to the +% .aux file as we encounter points defined for a later page, and for +% the last such page with AtEndDocument. \def\numpages{\@ifundefined{exam@lastpage}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@lastpage -} +}% numpages + +% Change 2011/04/01: We added a ``0'' in front of the +% mbox when \exam@lastcoverpage isn't defined. This is +% so that the construction \romannumeral\numcoverpages +% won't generate an error on the first run of latex. +\def\numcoverpages{\@ifundefined{exam@lastcoverpage}% + {0\mbox{\normalfont\bfseries ??}}% + \exam@lastcoverpage +}% numpages + +\def\totalnumpages{\@ifundefined{exam@totalpages}% + {\mbox{\normalfont\bfseries ??}}% + \exam@totalpages +}% numpages \def\numpoints{\@ifundefined{exam@numpoints}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numpoints -} +}% numpoints +\def\numbonuspoints{\@ifundefined{exam@numbonuspoints}% + {\mbox{\normalfont\bfseries ??}}% + \exam@numbonuspoints +}% numbonuspoints \def\numquestions{\@ifundefined{exam@numquestions}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numquestions -} +}% numquestions \def\numparts{\@ifundefined{exam@numparts}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numparts -} +}% numparts \def\numsubparts{\@ifundefined{exam@numsubparts}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numsubparts -} +}% numsubparts \def\numsubsubparts{\@ifundefined{exam@numsubsubparts}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numsubsubparts -} +}% numsubsubparts \def\pointsofquestion#1{\@ifundefined{pointsofq@\romannumeral #1}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\csname pointsofq@\romannumeral #1\endcsname}% -} - +}% pointsofquestion +\def\bonuspointsofquestion#1{\@ifundefined{bonuspointsofq@\romannumeral #1}% + {\mbox{\normalfont\bfseries ??}}% + {\csname bonuspointsofq@\romannumeral #1\endcsname}% +}% bonuspointsofquestion + +% For use in \combinedgradetable and \combinedpointtable, we're +% changing the defintions of \pointsonpage and \bonuspointsonpage +% so that when, e.g., pointsonpage@ii is undefined, we just produce +% 0. This comes up because the final page of the exam may have either +% points with no bonus point or bonus points with no points, in which +% case the combined table will try to list both points and bonus points +% for that last page, and one of those will be undefined. +% +% \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}% +% {\mbox{\normalfont\bfseries ??}}% +% {\csname pointsonpage@\romannumeral #1\endcsname}% +% }% pointsonpage +% \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}% +% {\mbox{\normalfont\bfseries ??}}% +% {\csname bonuspointsonpage@\romannumeral #1\endcsname}% +% }% bonuspointsonpage +% +% spanish.ldf redefines \@roman, so we'll avoid using \roman: \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}% - {\mbox{\normalfont\bf ??}}% + {0}% {\csname pointsonpage@\romannumeral #1\endcsname}% -} +}% pointsonpage +\def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}% + {0}% + {\csname bonuspointsonpage@\romannumeral #1\endcsname}% +}% bonuspointsonpage \newif\if@pointschanged \@pointschangedfalse +\newcommand*{\CheckIfChanged@hlf}[2]{% + % The first argument is the name of a half counter. + % The second argument expands to the name (without the escape + % character, and not assumed to be defined) of the control sequence + % holding the previous value. + \@ifundefined{#2}% + {\global\@pointschangedtrue}% + {% + % OK; it's defined. See if it's changed: + \begingroup + \set@hlfcntr{tmp@hlfcntr}{\csname #2\endcsname}% + \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% + \edef\pt@check{\prtaux@hlfcntr{#1}}% + \ifx \pt@check \othpt@check + % Do nothing + \else + \global\@pointschangedtrue + \fi + \endgroup + }% +}% CheckIfChanged@hlf %%%\let\@realenddocument=\enddocument @@ -1904,32 +1694,30 @@ %%% \@realenddocument %%%} - \AtEndDocument{% \clearpage \if@filesw \advance\c@page-1 \immediate\write\@mainaux {\string\gdef\string\exam@lastpage{\arabic{page}}}% + \immediate\write\@mainaux + {\string\gdef\string\exam@lastcoverpage{\arabic{num@coverpages}}}% + % We can now trash the value of num@coverpages: + \addtocounter{num@coverpages}{\value{page}}% + \immediate\write\@mainaux + {\string\gdef\string\exam@totalpages{\arabic{num@coverpages}}}% \advance\c@page+1 % In case some other package looks at \c@page + % \immediate\write\@mainaux - {\string\gdef\string\exam@numpoints{\prtaux@hlfcntr{numpoints}}}% + {\string\gdef\string\exam@numpoints{% + \prtaux@hlfcntr{numpoints}}}% % See if this has changed from the last run of LaTeX: - \@ifundefined{exam@numpoints}% - {\global\@pointschangedtrue}% - {% - % OK; it's defined. See if it's changed: - \begingroup - \set@hlfcntr{tmp@hlfcntr}{\exam@numpoints}% - \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% - \edef\pt@check{\prtaux@hlfcntr{numpoints}}% - \ifx \pt@check \othpt@check - % Do nothing - \else - \global\@pointschangedtrue - \fi - \endgroup - }% + \CheckIfChanged@hlf{numpoints}{exam@numpoints}% + \immediate\write\@mainaux + {\string\gdef\string\exam@numbonuspoints{% + \prtaux@hlfcntr{numbonuspoints}}}% + % See if this has changed from the last run of LaTeX: + \CheckIfChanged@hlf{numbonuspoints}{exam@numbonuspoints}% \immediate\write\@mainaux {\string\gdef\string\exam@numquestions{\thenumquestions}}% \immediate\write\@mainaux @@ -1944,23 +1732,16 @@ \csname c@pageof@pagepoints\endcsname {\prtaux@hlfcntr{@pagepoints}}}% % See if this has changed from the last run of LaTeX: - \@ifundefined{pointsonpage@\romannumeral + \CheckIfChanged@hlf{@pagepoints}{pointsonpage@\romannumeral \csname c@pageof@pagepoints\endcsname}% - {\global\@pointschangedtrue}% - {% - % OK; it's defined. See if it's changed: - \begingroup - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@pageof@pagepoints\endcsname\endcsname}% - \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% - \edef\pt@check{\prtaux@hlfcntr{@pagepoints}}% - \ifx \pt@check \othpt@check - % Do nothing - \else - \global\@pointschangedtrue - \fi - \endgroup - }% + \fi + \ifnum \thepageof@pagebonuspoints > 0\relax + \immediate\write\@mainaux + {\string\gdef\string\bonuspointsonpage@\romannumeral + \csname c@pageof@pagebonuspoints\endcsname + {\prtaux@hlfcntr{@pagebonuspoints}}}% + \CheckIfChanged@hlf{@pagebonuspoints}{bonuspointsonpage@\romannumeral + \csname c@pageof@pagebonuspoints\endcsname}% \fi \immediate\write\@mainaux {\string\gdef\string\lastpage@withpoints{\page@withpoints}}% @@ -1979,6 +1760,23 @@ \fi \endgroup }% + \immediate\write\@mainaux + {\string\gdef\string\lastpage@withbonuspoints{\page@withbonuspoints}}% + % See if this has changed from the last run of LaTeX: + \@ifundefined{lastpage@withbonuspoints}% + {\global\@pointschangedtrue}% + {% + % OK; it's defined. See if it's changed: + \begingroup + \edef\othpt@check{\page@withbonuspoints}% + \edef\pt@check{\lastpage@withbonuspoints}% + \ifx \pt@check \othpt@check + % Do nothing + \else + \global\@pointschangedtrue + \fi + \endgroup + }% \fi % Echo numbers of questions, parts, and subparts: \typeout{This exam contains \thenumquestions\space questions @@ -1994,13 +1792,20 @@ \fi } \typeout{This exam has a total of \typ@expnd\space points.} + \def\typ@expnd{% + \thenumbonuspoints + \ifnumbonuspoints@half + \space and a half% + \fi + } + \typeout{This exam has a total of \typ@expnd\space bonus points.} \endgroup \fi \if@pointschanged \ClassWarningNoLine{exam}{Point totals have changed. Rerun to get point totals right}% \fi -} +}% AtEndDocument % We define \iflastpage so that it can safely be used @@ -2008,12 +1813,12 @@ \def\iflastpage#1#2{% \@ifundefined{exam@lastpage}{\def\@@lastpage{-1}}% {\edef\@@lastpage{\exam@lastpage}}% - \ifnum\value{page}=\@@lastpage + \ifnum\value{page}=\@@lastpage\relax #1% \else #2% \fi -} +}% iflastpage % The macro \oddeven takes two arguments. If the page number is odd, @@ -2021,11 +1826,11 @@ % argument. \def\oddeven#1#2{% \ifodd\value{page}% - #1 + #1% \else - #2 + #2% \fi -} +}% oddeven @@ -2048,7 +1853,7 @@ % with hyperref.sty, which redefines those commands.) % We use \PgInfo, \PgInfo@write, and \PgInfo@get to know on which page -% each question, part, subpart, and subsubpart appears. +% each question, part, subpart, subsubpart, and choice appears. % We use \PgInfo@write to write \PgInfo commands to the .aux file. The % \PgInfo command takes two arguments: A question (or part, or subpart, @@ -2066,11 +1871,20 @@ % The label for a subsubpart is of the form `subsubpart@2@1@3@4' (if % it's subsubpart $\delta$ of subpart iii of part a of question 2). -% The \PgInfo command defines a control sequence of the form `Pg@label' -% that expands to the page number for the corresponding question. For -% example, \PgInfo{subsubpart@2@1@3@4}{7} defines -% \csname Pg@subsubpart@2@1@3@4\endcsname -% to expand to 7. +% Each question, part, subpart, subsubpart, and choice also gets a +% \PgInfo@write command using a label of the form question2@object3 (if +% it's the third object of the second question). + +% Inside one of the solution environments (solution, solutionorbox, +% etc.) each part, subpart, subsubpart, and choice get only the +% \PgInfo@write command using a label of the form question2@object3 +% (if it's the third object of the second question). + +% When read in from the .aux file, the \PgInfo command defines a +% control sequence of the form `Pg@label' that expands to the page +% number for the corresponding question. For example, +% \PgInfo{subsubpart@2@1@3@4}{7} defines \csname +% Pg@subsubpart@2@1@3@4\endcsname to expand to 7. % The \PgInfo@get{label} command returns the value of the macro Pg@label, % but it *doesn't* check whether that macro is defined. Thus, it's @@ -2108,15 +1922,14 @@ % *before* calling \Pginfo@get \def\PgInfo@get#1{\csname Pg@#1\endcsname} -% \set@counter@to@pageof takes two arguments: The first is the name -% of a counter, and the second (expands to) the label of a question, -% part, subpart, or subsubpart. If that label exists, then we set -% the counter equal to the page on which the question (or part, etc.) -% appears. If that label doesn't exist, we set the counter equal -% to -1. (No labels exist on the first run of LaTeX on the file, -% and if a new question (or part, etc.) was created by editing the -% file, then the label will not exist until the second run of LaTeX -% after that. +% \set@counter@to@pageof takes two arguments: The first is the name of a +% counter, and the second (expands to) the label of a question, part, +% subpart, subsubpart, or choice. If that label exists, then we set the +% counter equal to the page on which the question (or part, etc.) +% appears. If that label doesn't exist, we set the counter equal to -1. +% (No labels exist on the first run of LaTeX on the file, and if a new +% question (or part, etc.) was created by editing the file, then the +% label will not exist until the run of LaTeX after that.) \def\set@counter@to@pageof#1#2{% \@ifundefined{Pg@#2}% {\setcounter{#1}{-1}}% @@ -2126,17 +1939,35 @@ %-------------------------------------------------------------------- -% \ifcontinuation#1#2 expands to #2 if either: (1) The current page is -% before the page containing question number 1, or (2) A question begins -% on this page before any part, subpart, or subsubpart begins, or (3) -% The current page is later than a page with the \nomorequestions -% command. Otherwise, it expands to #1. +% \ifcontinuation#1#2 expands to #2 if either: +% (1) The command \noquestionsonthispage has been given on this page, +% or +% (2) The current page is before the page containing question number +% 1, or +% (3) A question begins on this page before any part, subpart, +% subsubpart, or choice begins, or +% (4) The current page is later than a page with the \nomorequestions +% command. +% Otherwise, it expands to #1. +% +% Thus, for example, if there are no questions, parts, subparts, +% subsubparts, or choices on this page, and no \noquestionsonthispage +% command, and we're not before question 1, and not after a +% \nomorequestions command, then \ifcontinuation will expand to #1. \def\ifcontinuation#1#2{% - % We need to first check whether we're on a page *before* the page on - % which the first question appears. If we don't yet know which page - % has question number 1, then we must be doing an early run of LaTeX, + % If there's a \noquestionsonthispage command on this page, then + % we assume that we're not continuing anything: + \@ifundefined{No@Questions@Pg@\thepage}% + {\chk@contin{#1}{#2}}% + {#2}% +}% \ifcontinuation + +\def\chk@contin#1#2{% + % We check whether we're on a page *before* the page on which the + % first question appears. If we don't yet know which page has + % question number 1, then we must be doing an early run of LaTeX, % and we'll assume we're not a continuation. \expandafter\ifx\csname Pg@question@1\endcsname\relax % No page info yet; assume not a continuation @@ -2155,10 +1986,10 @@ #2% \else % The current page begins a new question if Contin@\thepage - % has been defined as a macro that expands to \relax - % (Note that this is different from if Contin@\thepage - % has never been defined at all, in which case it will - % be let equal to \relax (temporarily) by the \csname command.) + % has been defined as a macro that expands to \relax (Note + % that this is different from if Contin@\thepage has never + % been defined at all, in which case it will be let equal to + % \relax (temporarily) by the \csname command.) \expandafter\ifx\csname Contin@\thepage\endcsname\ref@relax #2% \else @@ -2167,21 +1998,26 @@ {#1}% {\ifnum \thepage > \PgInfo@get{@endquestions}\relax % We're after a \nomorequestions: - #2 + #2% \else % We actually are incomplete: - #1 + #1% \fi }% \fi \fi \fi -} +}% chk@contin -\def\nomorequestions{ +\def\nomorequestions{% \PgInfo@write{@endquestions}% -} +}% nomorequestions +\def\noquestionsonthispage{% + \write\@mainaux{\string\expandafter\string\gdef + \string\csname\space No@Questions@Pg@\thepage\string\endcsname + {No questions here}}% +}% noquestionsonthispage %-------------------------------------------------------------------- @@ -2195,14 +2031,14 @@ % ACTUALLY: \ContinuedQuestion expands to a positive number if either % (1) this page doesn't contain the beginning of any question, part, -% subpart, or subsubpart, or (2) this page has a part, subpart, or -% subsubpart that appears before any question. That means that if the -% current page actually begins with space for a continuation of the -% previous question (but doesn't begin any part, subpart, or subsubpart -% of that question) and then has a question, then we'll be asserting -% that this page begins with a new question, but the actual top of the -% page will begin with some blank space that's intended for the previous -% question. +% subpart, subsubpart, or choice, or (2) this page has a part, subpart, +% subsubpart, or choice that appears before any question. That means +% that if the current page actually begins with space for a continuation +% of the previous question (but doesn't begin any part, subpart, +% subsubpart, or choice of that question) and then has a question, then +% we'll be asserting that this page begins with a new question, but the +% actual top of the page will begin with some blank space that's +% intended for the previous question. % \ContinuedQuestion works by examining the value of the macro % Contin@\thepage. If this page starts with a question (i.e., if no @@ -2213,22 +2049,22 @@ % If Contin@\thepage is undefined, then when it is used in an \ifx % command it will be temporarily set equal to \relax (which is % *different* from being a macro that expands to \relax); in this case, -% there is no question, part, subpart, or subsubpart that begins on this -% page, and so \ContinuedQuestion will be set equal to the last question -% that was begun on a page before this one. +% there is no question, part, subpart, subsubpart, or choice that begins +% on this page, and so \ContinuedQuestion will be set equal to the last +% question that was begun on a page before this one. % The last possibility is that this page begins with either a part, -% subpart, or subsubpart. In this case, Contin@\thepage is defined, and -% it expands to the number of the question that is continues onto this -% page. +% subpart, subsubpart, or choice. In this case, Contin@\thepage is +% defined, and it expands to the number of the question that is +% continued onto this page. \def\ref@relax{\relax} \def\ContinuedQuestion{% \expandafter\ifx\csname Contin@\thepage\endcsname\relax - % We get here if there's no question, part, subpart, or - % subsubpart on this page, and so Contin@\thepage has + % We get here if there's no question, part, subpart, + % subsubpart, or choice on this page, and so Contin@\thepage has % never been defined at all. In that case, this page % continues whichever question was last begun on or % before this page. @@ -2240,10 +2076,10 @@ % which is why Contin@\thepage has been defined to be % a macro that expands to \relax. % ACTUALLY: We get here if this page has a question that - % appears before any part, subpart, or subsubpart. That + % appears before any part, subpart, subsubpart, or choice. That % means that if the current page actually begins with space % for a continuation of the previous question but doesn't begin - % any part, subpart, or subsubpart of that question, then + % any part, subpart, subsubpart, or choice of that question, then % we'll be asserting that this page begins with a new question, % but the actual top of the page will begin with some space % that's intended for the previous question. @@ -2280,7 +2116,7 @@ % \find@quesend, which is why it has to find the last question % begun on or before the current page, rather than just before % the current page. - \ifnum 1 > \arabic{question}\relax + \ifnum 1 > \value{question}\relax % Oops; probably because we're before the first question % Just set latest@ques to -1: \setcounter{latest@ques}{-1}% @@ -2303,7 +2139,7 @@ % then call \decr@latest@ques to recursively decrement % latest@ques as needed to find a question that begins on % or before the current page: - \setcounter{latest@ques}{\arabic{question}}% + \setcounter{latest@ques}{\value{question}}% \decr@latest@ques \fi \fi @@ -2331,18 +2167,18 @@ \fi \next@dlq } - - %-------------------------------------------------------------------- %-------------------------------------------------------------------- %-------------------------------------------------------------------- \newcounter{ques@end} +\newcounter{last@object} + \def\find@quesend{% % We find the last question started on or before the current page % and then find the page containing the last part (or subpart, or - % subsubpart) of that question, and set the counter ques@end to that - % page number. + % subsubpart, or choice) of that question, and set the counter + % ques@end to that page number. % Set latest@ques equal to the correct question number: \find@latestques \ifnum \value{latest@ques} < 0\relax @@ -2350,192 +2186,152 @@ % \PgInfo commands in the .aux file: \setcounter{ques@end}{-1}% \else - % See if this question has any parts. (It has one or more parts - % if and only if it has a part number 1.) - \@ifundefined{Pg@part@\thelatest@ques @1}% - {% - % Nope; no parts. - \setcounter{ques@end}{\PgInfo@get{question@\thelatest@ques}}% - }% - {\find@part}% + % We now know that this question has at least one object (since + % we know that latest@ques isn't negative). + % We'll find its highest numbered object by setting last@object + % equal to 2 and then calling \find@lastobject to recursively + % test whether that object number exists and, if so, incrementing + % last@object to test for a higher numbered one: + \setcounter{last@object}{2}% + \find@lastobject + \setcounter{ques@end}{\PgInfo@get{question\thelatest@ques + @object\thelast@object}}% \fi -} - -\newcounter{last@part} -\def\find@part{% - % We now know that this question has at least one part. - % We'll find its highest numbered part by setting last@part - % equal to 2 and then calling \find@lastpart to recursively - % test whether that part number exists and, if so, incrementing - % last@part to test for a higher numbered one: - \setcounter{last@part}{2}% - \find@lastpart - % Now: See if this part has any subparts. (It has one or more - % subparts if and only if it has a subpart numbered 1.) - \@ifundefined{Pg@subpart@\thelatest@ques @\thelast@part @1}% - {% - % Nope; no subparts. - \setcounter{ques@end}{\PgInfo@get{part@\thelatest@ques - @\thelast@part}}% - }% - {\find@subpart}% -} - -\def\find@lastpart{% - % We check whether this question has a part numbered last@part - % and recursively increment last@part to find the highest - % numbered value for which the part exists: - \@ifundefined{Pg@part@\thelatest@ques @\thelast@part}% - {\addtocounter{last@part}{-1}% - \let\nextfind@lastpart=\relax - }% - {\addtocounter{last@part}{1}% - \let\nextfind@lastpart=\find@lastpart +}% find@quesend + +\def\find@lastobject{% + % We check whether this question has an object numbered last@object + % and recursively increment last@object to find the highest + % numbered value for which the object exists: + \@ifundefined{Pg@question\thelatest@ques @object\thelast@object}% + {\addtocounter{last@object}{-1}% + \let\nextfind@lastobject=\relax }% - \nextfind@lastpart -} - -\newcounter{last@subpart} -\def\find@subpart{% - % We now know that this part has at least one subpart. - % We'll find its highest numbered subpart by setting last@subpart - % equal to 2 and then calling \find@lastsubpart to recursively - % test whether that subpart number exists and, if so, incrementing - % last@subpart to test for a higher numbered one: - \setcounter{last@subpart}{2}% - \find@lastsubpart - % Now: See if this subpart has any subsubparts. (It has one or more - % subsubparts if and only if it has a subsubpart numbered 1.) - \@ifundefined{Pg@subsubpart@\thelatest@ques - @\thelast@part @\thelast@subpart @1}% - {% - % Nope; no subsubparts - \setcounter{ques@end}{\PgInfo@get{subpart@\thelatest@ques - @\thelast@part @\thelast@subpart}}% - }% - {\find@subsubpart}% -} - -\def\find@lastsubpart{% - % We check whether this part has a subpart numbered last@subpart - % and recursively increment last@subpart to find the highest - % numbered value for which the subpart exists: - \@ifundefined{Pg@subpart@\thelatest@ques - @\thelast@part @\thelast@subpart}% - {\addtocounter{last@subpart}{-1}% - \let\nextfind@lastsubpart=\relax + {\addtocounter{last@object}{1}% + \let\nextfind@lastobject=\find@lastobject }% - {\addtocounter{last@subpart}{1}% - \let\nextfind@lastsubpart=\find@lastsubpart - }% - \nextfind@lastsubpart -} + \nextfind@lastobject +}% find@lastobject -\newcounter{last@subsubpart} -\def\find@subsubpart{% - % We now know that this subpart has at least one subsubpart. - % We'll find its highest numbered subsubpart by setting last@subsubpart - % equal to 2 and then calling \find@lastsubsubpart to recursively - % test whether that subsubpart number exists and, if so, incrementing - % last@subsubpart to test for a higher numbered one: - \setcounter{last@subsubpart}{2}% - \find@lastsubsubpart - \setcounter{ques@end}{\PgInfo@get{subsubpart@\thelatest@ques - @\thelast@part @\thelast@subpart @\thelast@subsubpart}}% -} - -\def\find@lastsubsubpart{% - % We check whether this subpart has a subsubpart numbered last@subsubpart - % and recursively increment last@subsubpart to find the highest - % numbered value for which the subsubpart exists: - \@ifundefined{Pg@subsubpart@\thelatest@ques - @\thelast@part @\thelast@subpart @\thelast@subsubpart}% - {\addtocounter{last@subsubpart}{-1}% - \let\nextfind@lastsubsubpart=\relax - }% - {\addtocounter{last@subsubpart}{1}% - \let\nextfind@lastsubsubpart=\find@lastsubsubpart - }% - \nextfind@lastsubsubpart -} %-------------------------------------------------------------------- +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- + \newcounter{incmp@ques} \def\IncompleteQuestion{% \Find@Incmp@ques + % If there's no incomplete question, the counter incmp@ques will be + % set to -1: \theincmp@ques } \def\Find@Incmp@ques{% + % If we're on the last page, then there's no incomplete question: \iflastpage{\setcounter{incmp@ques}{-1}}{\chk@incomp}% -} +}% Find@Incmp@ques \newcounter{next@ques} -\newcounter{nextq@page} +\newcounter{next@page} + \def\chk@incomp{% + % If we get here, we're not on the last page. + % \find@quesend calls \find@latestques to set the counter + % latest@ques equal to the number of the last question begun on or + % before the current page, and then it sets the counter ques@end to + % the page containing the last ques@object of that question: \find@quesend \ifnum \theques@end > \thepage\relax % This question has a part (or sub...) starting on a later page \setcounter{incmp@ques}{\value{latest@ques}}% \else - \setcounter{next@ques}{\thelatest@ques}% - \addtocounter{next@ques}{1}% - \expandafter\ifx\csname Pg@question@\thenext@ques \endcsname\relax - % This isn't the last page but there is no next question: - \setcounter{incmp@ques}{\thelatest@ques}% - \else - % This isn't the last page and there is a next question: - \setcounter{nextq@page}{\PgInfo@get{question@\thenext@ques}}% - \addtocounter{nextq@page}{-1}% - \ifnum \thenextq@page > \thepage\relax - \setcounter{incmp@ques}{\thelatest@ques}% - \else - \setcounter{incmp@ques}{-1}% - \fi - \fi + \chk@incompi \fi -} - +}% chk@incomp + +\def\chk@incompi{% + % If there are any pages after the current one and before the next + % question (if there is a next question) that lack a + % \noquestionsonthispage and that aren't following the page of a + % \nomorequestions command, then question latest@ques is incomplete. + % Otherwise, there is no incomplete question: + \setcounter{next@ques}{\thelatest@ques}% + \addtocounter{next@ques}{1}% + % We use next@page as a scratch counter. We start by setting it + % to the last page we want to check for a \noquestionsonthispage + % command: + \expandafter\ifx\csname Pg@question@\thenext@ques \endcsname\relax + % This isn't the last page but there is no next question: +% + \@ifundefined{exam@lastpage}% + {\setcounter{next@page}{-1}}% + {\setcounter{next@page}{\exam@lastpage}}% +% +% \setcounter{next@page}{\exam@lastpage}% + \else + \setcounter{next@page}{\PgInfo@get{question@\thenext@ques}}% + \addtocounter{next@page}{-1}% + \fi + % See if that's after a \nomorequestions command: + \@ifundefined{Pg@@endquestions}% + {}% + {\ifnum \PgInfo@get{@endquestions} < \value{next@page}\relax + \setcounter{next@page}{\PgInfo@get{@endquestions}}% + \fi + }% + % OK, the counter next@page now contains the last page to check. + \chk@incompii +}% chk@incompi + +\def\chk@incompii{% + \ifnum \value{next@page} > \value{page}\relax + % We need to check the page next@page: + \@ifundefined{No@Questions@Pg@\arabic{next@page}}% + {\setcounter{incmp@ques}{\value{latest@ques}}% + \let\next@incompii=\relax + }% + {\addtocounter{next@page}{-1}% + \let\next@incompii = \chk@incompii + }% + \else + % There's no incomplete question: + \setcounter{incmp@ques}{-1}% + \let\next@incompii=\relax + \fi + \next@incompii +}% chk@incompii \def\ifincomplete#1#2{% + % We need to pass the arguments to \chk@ifincomp; we save them in + % macros so they won't be messed up by the call to \Find@Incmp@ques: \def\incomp@first{#1}% \def\incomp@second{#2}% + % If there's a \noquestionsonthispage command on this page, then + % we assume nothing from this page is incomplete: + \@ifundefined{No@Questions@Pg@\thepage}% + {\chk@ifincomp}% + {\incomp@second}% +}% ifincomplete + +\def\chk@ifincomp{% \Find@Incmp@ques + % If there's no incomplete question, \Find@Incmp@ques sets the + % counter incmp@ques to -1: \ifnum \theincmp@ques < 0\relax \incomp@second \else - \@ifundefined{Pg@@endquestions}% - {\incomp@first}% - {\ifnum \thepage < \PgInfo@get{@endquestions}% - \incomp@first - \else - \incomp@second - \fi - }% + % Are we after a page with \nomorequestions? + \@ifundefined{Pg@@endquestions}% + {\incomp@first}% + {\ifnum \thepage < \PgInfo@get{@endquestions}\relax + \incomp@first + \else + \incomp@second + \fi + }% \fi -} - - - - -%--------------------------------------------------------------------- -% -% *************************** -% ** QUESTION ENVIRONMENTS ** -% *************************** -% -% -% - -% We define the command \part only inside of a parts environment, so -% that we don't interfere with the meaning of the standard article -% documentclass command \part if that is used inside of a questions -% environment. The commands \question, \subpart, and \subsubpart are -% defined everywhere inside of a questions environment. If the user -% accidentally gives a \subpart command outside of a subparts -% environment, then an error will be created. - +}% chk@ifincomp %-------------------------------------------------------------------- % These are the commands for dealing with hlfcntr's, i.e., the things @@ -2549,12 +2345,14 @@ % % The commands: % +% \new@hlfcntr{countername} % \set@hlfcntr{countername}{value} % \copy@hlfcntr{tocounter}{fromcounter} % \addto@hlfcntr{countername}{value} -% \add@hlfcntrtohlfcntr +% \add@hlfcntrtohlfcntr{getsaddedto}{whatsadded} % \ifhlfcntr@pos{countername} % \prtaux@hlfcntr{countername} +% \prt@hlfcntr{countername} % % ``value'' can be either a (nonnegative) integer, an integer followed by % ``\half'', or just plain ``\half''. (Actually, ``value'' can be empty @@ -2576,52 +2374,66 @@ % \ifsomething) aren't. Thus, we need to say ``\global'' when setting % these things. +% To create a hlfcntr: +\newcommand*\new@hlfcntr[1]{% + \newcounter{#1}% + \expandafter\newif\csname if#1@half\endcsname +}% new@hlfcntr + + % A scratch hlfcntr: -\newcounter{tmp@hlfcntr} -\newif\iftmp@hlfcntr@half +\new@hlfcntr{tmp@hlfcntr} \newcommand*\horiz@half{$\frac{1}{2}$} \newcommand*\slanted@half{% $\raise0.6ex\hbox{$\scriptstyle 1$}\kern -.2em/\kern -.2em \raise-0.5ex\hbox{$\scriptstyle 2$}$% -} -\newcommand*\useslantedhalf{\let\half\slanted@half} -\newcommand*\usehorizontalhalf{\let\half\horiz@half} +}% slanted@half +\newcommand*\useslantedhalf{\global\let\half\slanted@half} +\newcommand*\usehorizontalhalf{\global\let\half\horiz@half} \newcommand*\half{\slanted@half} \newcommand*\set@hlfcntr[2]{% \begingroup - \expandafter\global\csname #1@halffalse\endcsname + \global\csname #1@halffalse\endcsname % If there as a `\half' present, it will be executed % right after the assignment of the digit part of #2 % to the counter #1. \def\half{% - \expandafter\global\csname #1@halftrue\endcsname + \global\csname #1@halftrue\endcsname }% % We insert a `0' in case there are no digits present: - \setcounter{#1}{0#2}\relax + % We avoid using \setcounter, because calc.sty redefines + % \setcounter in a way that conflicts with the \half trick + % we're using: + % \setcounter{#1}{0#2}\relax + \global\csname c@#1\endcsname 0#2\relax \endgroup -} +}% set@hlfcntr \newcommand*\copy@hlfcntr[2]{% % We set #1 to the value of #2 \setcounter{#1}{\value{#2}}% \csname if#2@half\endcsname - \expandafter\global\csname #1@halftrue\endcsname + \global\csname #1@halftrue\endcsname \else - \expandafter\global\csname #1@halffalse\endcsname + \global\csname #1@halffalse\endcsname \fi -} +}% copy@hlfcntr \newcommand*\addto@hlfcntr[2]{% % We add the valueandhalf #2 to hlfcntr #1 \begingroup \def\half{\add@half{#1}}% % We insert a `0' in case there are no digits present: - \addtocounter{#1}{0#2}\relax + % We avoid using \addtocounter, because calc.sty redefines + % \addtocounter in a way that conflicts with the \half trick + % we're using: + % \addtocounter{#1}{0#2}\relax + \global\advance\csname c@#1\endcsname 0#2\relax \endgroup -} +}% addto@hlfcntr \newcommand*\add@hlfcntrtohlfcntr[2]{% % We add the hlfcntr #2 to the hlfcntr #1 @@ -2629,18 +2441,30 @@ \csname if#2@half\endcsname \add@half{#1}% \fi -} +}% add@hlfcntrtohlfcntr \newcommand*\add@half[1]{% % We add one half to hlfcntr #1: \csname if#1@half\endcsname \addtocounter{#1}{1}% - \expandafter\global\csname #1@halffalse\endcsname + \global\csname #1@halffalse\endcsname \else - \expandafter\global\csname #1@halftrue\endcsname + \global\csname #1@halftrue\endcsname \fi -} - +}% add@half + +% Important reminder about \ifhlfcntr@pos: Do not use it inside +% another conditional! The construction +% \ifhlfcntr@pos{somecounter} +% do some stuff... +% \fi +% is perfectly fine as long as it's expanded, but: If it's inside +% another conditional, and the condition is not satisfied, then it's +% read through without expansion. In that case, TeX sees the +% \ifhlfcntr@pos but does *not* recognize it as being part of a +% conditional, but when it sees the concluding \fi it does recognize +% that, and so TeX completes the outer conditional at that \fi, which +% causes an error. \newcounter{ifpos@cntr} \def\ifhlfcntr@pos#1{% % The argument must be a hlfcntr (which, of course, @@ -2650,8 +2474,8 @@ \csname if#1@half\endcsname \addtocounter{ifpos@cntr}{1}% \fi - \ifnum \value{ifpos@cntr} > 0 -} + \ifnum \value{ifpos@cntr} > 0\relax +}% ifhlfnctr@pos % \prtaux@hlfcntr is used inside the argument of a \write command for % writing to the .aux file: @@ -2670,59 +2494,112 @@ % there's no \if visible until the \csname is expanded: \prtaux@halforblank{#1}% \fi -} +}% prtaux@hlfcntr \newcommand*\prtaux@halforzero[1]{% \csname if#1@half\endcsname \string\half \else 0% \fi -} +}% prtaux@hlforzero \newcommand*\prtaux@halforblank[1]{% \csname if#1@half\endcsname \string\half \fi -} -%-------------------------------------------------------------------- +}% prtaux@halforblank - - - -% We use the counter name `partno' for the parts environment so that -% we will not interfere with the counter `part' used by the article -% document class. - -\newcounter{question} -\newcounter{partno} -\newcounter{subpart} -\newcounter{subsubpart} -\newcounter{choice} -\newcounter{numpoints} -\newif\ifnumpoints@half -\set@hlfcntr{numpoints}{0} -\newcounter{pointsof@thisquestion} -\newif\ifpointsof@thisquestion@half -\set@hlfcntr{pointsof@thisquestion}{0} -\newcounter{numquestions} -\newcounter{numparts} +\newcommand*\prt@hlfcntr[1]{% + % We don't want a \relax after the 0 in the following + % line, because it would sometimes appear in the aux file: + \ifnum \value{#1} = 0 + % We have to make the following a macro, because if we + % don't do this part, the \fi will cause confusion, since + % there's no \if visible until the \csname is expanded: + \prt@halforzero{#1}% + \else + \arabic{#1}% + % We have to make the following a macro, because if we + % don't do this part, the \fi will cause confusion, since + % there's no \if visible until the \csname is expanded: + \prt@halforblank{#1}% + \fi +}% prt@hlfcntr +\newcommand*\prt@halforzero[1]{% + \csname if#1@half\endcsname + \half + \else + 0% + \fi +}% prt@hlforzero +\newcommand*\prt@halforblank[1]{% + \csname if#1@half\endcsname + \half + \fi +}% prt@halforblank + +% End of the commands for dealing with hlfcntr's + +%-------------------------------------------------------------------- +%--------------------------------------------------------------------- +% +% *************************** +% ** QUESTION ENVIRONMENTS ** +% *************************** +% +% +% + +% We define the command \part only inside of a parts environment, so +% that we don't interfere with the meaning of the standard article +% documentclass command \part if that is used inside of a questions +% environment. The commands \question, \subpart, and \subsubpart are +% defined everywhere inside of a questions environment. If the user +% accidentally gives a \subpart command outside of a subparts +% environment, then an error will be created. + + + + +% We use the counter name `partno' for the parts environment so that +% we will not interfere with the counter `part' used by the article +% document class. + +\newcounter{question} +\newcounter{partno} +\newcounter{subpart} +\newcounter{subsubpart} +\newcounter{choice} +\new@hlfcntr{numpoints} +\set@hlfcntr{numpoints}{0} +\new@hlfcntr{numbonuspoints} +\set@hlfcntr{numbonuspoints}{0} +\new@hlfcntr{pointsof@thisquestion} +\set@hlfcntr{pointsof@thisquestion}{0} +\new@hlfcntr{bonuspointsof@thisquestion} +\set@hlfcntr{bonuspointsof@thisquestion}{0} +\newcounter{numquestions} +\newcounter{numparts} \newcounter{numsubparts} \newcounter{numsubsubparts} \newcounter{Curr@Page} % @pagepoints accumulates the points on a single page: -\newcounter{@pagepoints} -\newif\if@pagepoints@half -%\setcounter{@pagepoints}{0} +\new@hlfcntr{@pagepoints} \set@hlfcntr{@pagepoints}{0} +\new@hlfcntr{@pagebonuspoints} +\set@hlfcntr{@pagebonuspoints}{0} \newcounter{pageof@pagepoints} \setcounter{pageof@pagepoints}{0} +\newcounter{pageof@pagebonuspoints} +\setcounter{pageof@pagebonuspoints}{0} % latest@points is a holding area for points until we know % whether they'll land on the same page as the points % currently counted in @pagepoints: -\newcounter{latest@points} -\newif\iflatest@points@half +\new@hlfcntr{latest@points} \set@hlfcntr{latest@points}{0} +\new@hlfcntr{latest@bonuspoints} +\set@hlfcntr{latest@bonuspoints}{0} % Whenever we meet a new page on which points are defined, we'll % redefine \page@withpoints to expand to that page. At the end of the @@ -2730,10 +2607,11 @@ % a \gdef\lastpage@withpoints command to the .aux file. % We initialize \page@withpoints here: \def\page@withpoints{0}% +\def\page@withbonuspoints{0}% % \pageinfo@commands is used by each question, part, subpart, and % subsubpart to insert into everypar the \PgInfo@write command to put -% its page number inot the .aux file, the \PgInfo@get command to read +% its page number into the .aux file, the \PgInfo@get command to read % the page number into the counter Curr@Page, and to test and set % \Contin@\theCurr@Page. \temp@toks is used by part, subpart, and % subsubpart to append all that to \pageinfo@commands, rather than @@ -2760,7 +2638,7 @@ $\rho$\or $\sigma$\or $\tau$\or $\upsilon$\or $\phi$\or $\chi$\or $\psi$\or $\omega$\else \@ctrerr \fi -}% +}% lc@greek % The following macros are a variation on a trick from Victor @@ -2773,43 +2651,79 @@ \def\prepend@toklist#1#2{% \edef\do@it{\noexpand#1={\the#2\the#1}}% \do@it -}% +}% prepend@toklist \def\append@toklist#1#2{% \edef\do@it{\noexpand#1={\the#1\the#2}}% \do@it -}% +}% append@toklist % The command \qformat is provided for the user who wants to % design a nonstandard question line. If this command is used, -% then the usual line containing the question numbers will be -% replaced by the line specified by the \qformat command. +% then the usual line containing the question number and the beginning +% of the question will be replaced by the line specified by the +% \qformat command, and the question will begin on the following +% line. % Within the argument of the \qformat command: % \thequestion will be replaced by the question number, and % \thepoints will be replaced by ``\@points \@pointname'' if the % number of points has been specified for this question, and otherwise % it inserts nothing at all. (The conditional @placepoints is used to -% determine if the number of points is nonzero.) +% determine if there were points specified for this question.) % The argument to the \qformat command *must* contain some % stretch, i.e., at least one \hfil or \dotfill or ... +% +% The command \noqformat cancels the effect of \qformat and returns us +% to the default situation. +% +% The commands \bonusqformat and \nobonusformat are analogous. \newif\if@qformat \@qformatfalse +\newif\if@bonusqformat +\@bonusqformatfalse \def\qformat#1{% \global\@qformattrue \gdef\@questionformat{#1}% -} +}% qformat +\def\bonusqformat#1{% + \global\@bonusqformattrue + \gdef\@bonusquestionformat{#1}% +}% bonusqformat \newcommand\noqformat{% \global\@qformatfalse -} +}% noqformat +\newcommand\nobonusqformat{% + \global\@bonusqformatfalse +}% nobonusqformat + +% \thepoints is for use in either a \qformat command +% or a \pointformat command (or a \bonusqformat command). +% It needs to have the +% \if@placepoints so that if it's used in a \qformat command +% it won't print anything if there are no points: \newcommand\thepoints{% \if@placepoints - \@points \@pointname + \if@bonus + \@points \@bonuspointname + \else + \@points \@pointname + \fi \fi -} +}% thepoints + +% \themarginpoints is for use only in a \pointformat command, +% and so it doesn't need the \if@placepoints bit in \thepoints: +\newcommand\themarginpoints{% + \if@bonus + \@points \@marginbonuspointname + \else + \@points \@marginpointname + \fi +}% themarginpoints % We define the \subpart and \subsubpart commands when we enter a % questions environment (rather than waiting until we enter a subparts @@ -2819,6 +2733,95 @@ % can use the standard sectioning \part command outside of a parts % environment.) +% The counter ques@object will count the items in each question, where +% an item is defined as either the question itself, or a part, or a +% subpart, or a subsubpart, or a choice. This will be used by +% \find@quesend to find the last page occupied by the last question +% begun on or before the current page: +\newcounter{ques@object} + +% \first@questionobject will be used by the \question command. +% That is, it will be used only once, but we want to keep its +% definition here, near the definitions of \addquestionobject and +% \questionobject@pluspagecheck. +\newcommand{\first@questionobject}{% + \setcounter{ques@object}{1}% + % \PgInfo@write expands it's argument, so we don't need edef: +% \edef\q@object@label{% +% question\arabic{question}@object\arabic{ques@object}}% +% \PgInfo@write{\q@object@label}% + \PgInfo@write{question\arabic{question}@object\arabic{ques@object}}% +}% first@questionobject + +% \addquestionobject will be used by each part, subpart, and +% subsubpart, and can also be used by the user to mark the end of a +% question that spills over onto the next page without any part, +% subpart, etc. starting on that page: +\newcommand{\addquestionobject}{% + \addtocounter{ques@object}{1}% + % \PgInfo@write expands it's argument, so we don't need edef: +% \edef\q@object@label{% +% question\arabic{question}@object\arabic{ques@object}}% +% \PgInfo@write{\q@object@label}% + \PgInfo@write{question\arabic{question}@object\arabic{ques@object}}% +}% addquestionobject + +% \questionobject@pluspagecheck will be used by each choice, as well +% as by any \part, \subpart, or \subsubpart that's inside of a +% solution. It uses the questionobject to check if we're the first +% one on the current page, since choices (and questions etc. inside of +% solution environments) don't have labels the way that questions, +% parts, subparts, and subsubparts do (those things use the label to +% check if they're the first thing on the page). +\newcommand{\questionobject@pluspagecheck}{% + % We don't want to do any of this if we're both inside a solution + % environment and not printing answers (because we want to avoid + % incrementing ques@object): + \if@insolution + \ifprintanswers + \doqobj@ppchk + \fi + \else + \doqobj@ppchk + \fi +}% questionobject@pluspagecheck +\newcommand{\doqobj@ppchk}{% + \addtocounter{ques@object}{1}% + % We need the edef because we check the page of \q@object@label: + \edef\q@object@label{% + question\arabic{question}@object\arabic{ques@object}}% + \PgInfo@write{\q@object@label}% + \set@counter@to@pageof{Curr@Page}{\q@object@label}% + \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax + % We're the first \question, \part, \subpart, \subsubpart, + % or choice on this page: + \global\expandafter\edef\csname + Contin@\theCurr@Page\endcsname{\arabic{question}}% + \fi +}% doqobj@ppchk + + + +% if@bonus will be true when we're doing a bonusquestion or bonuspart +% or etc., and it will also be used also to distinguish between +% \gradetable and \bonusgradetable (and between \pointtable and +% \bonuspointtable, etc.), and also to distinguish between +% \pointsinrange and \bonuspointsinrange: +\newif\if@bonus +\@bonusfalse + +% The following are for advanced users who want to customize the list +% parameters (\topsep, \partopsep, \itemsep, \parsep, etc.) for the +% lists that these environments create. They are all defined to be +% empty, but the user can change them using \renewcommand. +\newcommand\questionshook{} +\newcommand\partshook{} +\newcommand\subpartshook{} +\newcommand\subsubpartshook{} +\newcommand\choiceshook{} +\newcommand\checkboxeshook{} + + \newenvironment{questions}{% % \@queslevel is used for two purposes: % (1) We check that every \question, \part, \subpart, and @@ -2829,7 +2832,29 @@ % \global \point@toks={} to avoid setting the points for a % question other than via the qformat command. \def\@queslevel{question}% + \def\titledquestion##1{% + \@bonusfalse + \def\thequestiontitle{##1}% + \process@question + }% + \def\bonustitledquestion##1{% + \@bonustrue + \def\thequestiontitle{##1}% + \process@question + }% \def\question{% + \@bonusfalse + \def\thequestiontitle{\csname p@question\endcsname + \csname thequestion\endcsname}% + \process@question + }% + \def\bonusquestion{% + \@bonustrue + \def\thequestiontitle{\csname p@question\endcsname + \csname thequestion\endcsname}% + \process@question + }% + \def\process@question{% \if@coverpages \cover@question@error \fi @@ -2837,42 +2862,39 @@ \addtocounter{numquestions}{1}% % Write the sum of points of the previous question (if any) % to the .aux file. (At this point, the question counter - % has not yet been incremented, so \arabic{question} has the + % has not yet been incremented, so \value{question} is the % number of the question that was just completed.) \if@filesw - \ifnum \arabic{question} > 0\relax + \ifnum \value{question} > 0\relax + % First do regular points: \immediate\write\@mainaux {\string\gdef\string\pointsofq@ \romannumeral \csname c@question\endcsname {\prtaux@hlfcntr{pointsof@thisquestion}}}% % See if this has changed from the last run of LaTeX: - \@ifundefined{pointsofq@\romannumeral + \CheckIfChanged@hlf{pointsof@thisquestion}{pointsofq@\romannumeral \csname c@question\endcsname}% - {\global\@pointschangedtrue}% - {% - % OK; it's defined. See if it's changed: - \begingroup - \set@hlfcntr{tmp@hlfcntr}{\csname pointsofq@\romannumeral - \csname c@question\endcsname\endcsname}% - \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% - \edef\pt@check{\prtaux@hlfcntr{pointsof@thisquestion}}% - \ifx \pt@check \othpt@check - % Do nothing - \else - \global\@pointschangedtrue - \fi - \endgroup - }% + % Now do bonus points: + \immediate\write\@mainaux + {\string\gdef\string\bonuspointsofq@ + \romannumeral \csname c@question\endcsname + {\prtaux@hlfcntr{bonuspointsof@thisquestion}}}% + % See if this has changed from the last run of LaTeX: + \CheckIfChanged@hlf{bonuspointsof@thisquestion}% + {bonuspointsofq@\romannumeral + \csname c@question\endcsname}% \fi \fi \set@hlfcntr{pointsof@thisquestion}{0}% + \set@hlfcntr{bonuspointsof@thisquestion}{0}% % If there was a question with points immediately preceding % this question (i.e., there were no parts in the previous % question), then @placepoints will still be true, and we need to % cancel it. (We used to do this inside of the \thepoints macro, % but that allowed for an error if the user specified points for a % question but had a \qformat that didn't mention \thepoints.) We - % also set @placepoints to be false in the \part command. + % also set @placepoints to be false when entering a parts + % environment. \global \@placepointsfalse % point@toks will normally be empty at this point, but it might be % nonempty if there were points somewhere in the previous question @@ -2886,12 +2908,46 @@ % horizontal mode by \everypar, and so any blank lines will % cause paragraph breaks. \pageinfo@commands={% - \edef\@queslabel{question@\arabic{question}}% + \edef\@queslabel{question@\arabic{section}@\arabic{subsection}@\arabic{question}}% \PgInfo@write{\@queslabel}% + \first@questionobject + % In addition to the \PgInfo@write we use an actual \label + % command. We do this in order to make the question numbers in + % the grade tables into \ref's, so that if the user says + % \usepackage{hyperref}, those question numbers will be clickable. + % + % A further purpose of these labels (which we actually do for all + % questions, parts, subparts, and subsubparts) is that if a + % question (or part, etc.) is, e.g., moved from one page to + % another, LaTeX will notice this and warn the user that LaTeX + % must be run one more time to be sure everything is correct. + % + % We need to do this even though we've already included code to + % check when point totals change because questions (and parts, + % etc.) know what page they're on from reading the info written to + % the .aux file on the previous run. Thus, if a question (or + % part, etc.) is moved to a different page, then the pointsonpage + % totals won't notice until the *second* subsequent run of LaTeX, + % and so there'll be no warning to the user on the *first* run. + % Including these labels gives the user a warning on that first + % run. + % + % Further futzing required: Since this is being put into the + % token list \pageinfo@commands, the contents of which won't + % actually be executed until we enter horizontal mode (which + % may well be in the first part of a parts environment), we need + % to make sure that \@currentlabel is the number of the + % question: + \begingroup % to confine the change to \@currentlabel +% \def\@currentlabel{\csname p@question\endcsname +% \csname thequestion\endcsname}% + \def\@currentlabel{\thequestiontitle}% + \label{\@queslabel}% + \endgroup \set@counter@to@pageof{Curr@Page}{\@queslabel}% \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax - % We're the first \question, \part, \subpart, or \subsubpart - % on this page: + % We're the first \question, \part, \subpart, \subsubpart, + % or choice on this page: \global\expandafter\edef \csname Contin@\theCurr@Page\endcsname{\relax}% \fi @@ -2904,31 +2960,70 @@ \unskip\unskip \par \fi \@doitem - }% question + }% process@question \def\subpart{% + \@bonusfalse + \process@subpart + }% + \def\bonussubpart{% + \@bonustrue + \process@subpart + }% + \def\process@subpart{% \if@coverpages \cover@question@error \fi \@checkqueslevel{subpart}% - \addtocounter{numsubparts}{1}% - % Important: Don't leave any blank lines inside of - % \pageinfo@commands!! This token list will be dumped into - % horizontal mode by \everypar, and so any blank lines will - % cause paragraph breaks. - \temp@toks={% - \edef\@subpartlabel{subpart@\arabic{question}% - @\arabic{partno}@\arabic{subpart}}% - \PgInfo@write{\@subpartlabel}% - \set@counter@to@pageof{Curr@Page}{\@subpartlabel}% - \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax - % We're the first \question, \part, \subpart, or \subsubpart - % on this page: - \global\expandafter\edef\csname - Contin@\theCurr@Page\endcsname{\arabic{question}}% - \fi - \the\pagepoint@commands - \global \pageinfo@commands={}% - }% temp@toks + \if@insolution + % We don't count this subpart, so no addtocounter{numsubparts}. + \temp@toks={% + \questionobject@pluspagecheck + \global \pageinfo@commands={}% + % We omit the pagepoint@commands + }% temp@toks + \else + \addtocounter{numsubparts}{1}% + % Important: Don't leave any blank lines inside of + % \pageinfo@commands!! This token list will be dumped into + % horizontal mode by \everypar, and so any blank lines will + % cause paragraph breaks. + \temp@toks={% + \edef\@subpartlabel{subpart@\arabic{section}@\arabic{subsection}@\arabic{question}% + @\arabic{partno}@\arabic{subpart}}% + \PgInfo@write{\@subpartlabel}% + \addquestionobject + % In addition to the \PgInfo@write we use an actual \label + % command. We do this in order to make the question numbers in + % the grade tables into \ref's, so that if the user says + % \usepackage{hyperref}, those question numbers will be clickable. + % + % A further purpose of these labels (which we actually do for all + % questions, parts, subparts, and subsubparts) is that if a + % question (or part, etc.) is, e.g., moved from one page to + % another, LaTeX will notice this and warn the user that LaTeX + % must be run one more time to be sure everything is correct. + % + % We need to do this even though we've already included code to + % check when point totals change because questions (and parts, + % etc.) know what page they're on from reading the info written to + % the .aux file on the previous run. Thus, if a question (or + % part, etc.) is moved to a different page, then the pointsonpage + % totals won't notice until the *second* subsequent run of LaTeX, + % and so there'll be no warning to the user on the *first* run. + % Including these labels gives the user a warning on that first + % run. + \label{\@subpartlabel}% + \set@counter@to@pageof{Curr@Page}{\@subpartlabel}% + \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax + % We're the first \question, \part, \subpart, \subsubpart, + % or choice on this page: + \global\expandafter\edef\csname + Contin@\theCurr@Page\endcsname{\arabic{question}}% + \fi + \the\pagepoint@commands + \global \pageinfo@commands={}% + }% temp@toks + \fi \append@toklist \pageinfo@commands \temp@toks \ifhmode % Remove any skips at the end of the previous paragraph @@ -2936,31 +3031,70 @@ \unskip\unskip \par \fi \@doitem - }% subpart + }% process@subpart \def\subsubpart{% + \@bonusfalse + \process@subsubpart + }% + \def\bonussubsubpart{% + \@bonustrue + \process@subsubpart + }% + \def\process@subsubpart{% \if@coverpages \cover@question@error \fi \@checkqueslevel{subsubpart}% - \addtocounter{numsubsubparts}{1}% - % Important: Don't leave any blank lines inside of - % \pageinfo@commands!! This token list will be dumped into - % horizontal mode by \everypar, and so any blank lines will - % cause paragraph breaks. - \temp@toks={% - \edef\@subsubpartlabel{subsubpart@\arabic{question}% - @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}}% - \PgInfo@write{\@subsubpartlabel}% - \set@counter@to@pageof{Curr@Page}{\@subsubpartlabel}% - \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax - % We're the first \question, \part, \subpart, or \subsubpart - % on this page: - \global\expandafter\edef\csname - Contin@\theCurr@Page\endcsname{\arabic{question}}% - \fi - \the\pagepoint@commands - \global \pageinfo@commands={}% - }% temp@toks + \if@insolution + % We don't count this subsubpart, so no addtocounter{numsubsubparts}. + \temp@toks={% + \questionobject@pluspagecheck + \global \pageinfo@commands={}% + % We omit the pagepoint@commands + }% temp@toks + \else + \addtocounter{numsubsubparts}{1}% + % Important: Don't leave any blank lines inside of + % \pageinfo@commands!! This token list will be dumped into + % horizontal mode by \everypar, and so any blank lines will + % cause paragraph breaks. + \temp@toks={% + \edef\@subsubpartlabel{subsubpart@\arabic{section}@\arabic{subsection}@\arabic{question}% + @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}}% + \PgInfo@write{\@subsubpartlabel}% + \addquestionobject + % In addition to the \PgInfo@write we use an actual \label + % command. We do this in order to make the question numbers in + % the grade tables into \ref's, so that if the user says + % \usepackage{hyperref}, those question numbers will be clickable. + % + % A further purpose of these labels (which we actually do for all + % questions, parts, subparts, and subsubparts) is that if a + % question (or part, etc.) is, e.g., moved from one page to + % another, LaTeX will notice this and warn the user that LaTeX + % must be run one more time to be sure everything is correct. + % + % We need to do this even though we've already included code to + % check when point totals change because questions (and parts, + % etc.) know what page they're on from reading the info written to + % the .aux file on the previous run. Thus, if a question (or + % part, etc.) is moved to a different page, then the pointsonpage + % totals won't notice until the *second* subsequent run of LaTeX, + % and so there'll be no warning to the user on the *first* run. + % Including these labels gives the user a warning on that first + % run. + \label{\@subsubpartlabel}% + \set@counter@to@pageof{Curr@Page}{\@subsubpartlabel}% + \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax + % We're the first \question, \part, \subpart, \subsubpart, + % or choice on this page: + \global\expandafter\edef\csname + Contin@\theCurr@Page\endcsname{\arabic{question}}% + \fi + \the\pagepoint@commands + \global \pageinfo@commands={}% + }% temp@toks + \fi \append@toklist \pageinfo@commands \temp@toks \ifhmode % Remove any skips at the end of the previous paragraph @@ -2968,7 +3102,7 @@ \unskip\unskip \par \fi \@doitem - }% subsubpart + }% process@subsubpart \list{\question@number}% {\usecounter{question}% % We use the default definition of \makelabel @@ -2977,48 +3111,54 @@ \settowidth{\leftmargin}{10.\hskip\labelsep}% \labelwidth\leftmargin\advance\labelwidth-\labelsep \partopsep=0pt + \questionshook }% - }% + }% End of the first argument of \newenvironment{questions} {% \endlist % Write the number of points of the final question % to the .aux file: \if@filesw - \ifnum \arabic{question} > 0\relax + \ifnum \value{question} > 0\relax + % First do the regular points: \immediate\write\@mainaux {\string\gdef\string\pointsofq@\romannumeral \csname c@question\endcsname {\prtaux@hlfcntr{pointsof@thisquestion}}}% % See if this has changed from the last run of LaTeX: - \@ifundefined{pointsofq@\romannumeral + \CheckIfChanged@hlf{pointsof@thisquestion}% + {pointsofq@\romannumeral + \csname c@question\endcsname}% + % Now do the bonus points: + \immediate\write\@mainaux + {\string\gdef\string\bonuspointsofq@\romannumeral + \csname c@question\endcsname + {\prtaux@hlfcntr{bonuspointsof@thisquestion}}}% + % See if this has changed from the last run of LaTeX: + \CheckIfChanged@hlf{bonuspointsof@thisquestion}% + {bonuspointsofq@\romannumeral \csname c@question\endcsname}% - {\global\@pointschangedtrue}% - {% - % OK; it's defined. See if it's changed: - \begingroup - \set@hlfcntr{tmp@hlfcntr}{\csname pointsofq@\romannumeral - \csname c@question\endcsname\endcsname}% - \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% - \edef\pt@check{\prtaux@hlfcntr{pointsof@thisquestion}}% - \ifx \pt@check \othpt@check - % Do nothing - \else - \global\@pointschangedtrue - \fi - \endgroup - }% \fi \fi - } + }% End of the second argument of \newenvironment{questions} % \question@number is used as the label in the question list (instead % of \questionlabel) so that if the user uses a \qformat command, % we'll use the \@questionformat specified by the \qformat command: + \def\question@number{% - \if@qformat - \makebox[\hsize][s]{\@questionformat}\hskip-\labelsep + \if@bonus + \if@bonusqformat + \makebox[\hsize][s]{\@bonusquestionformat}\hskip-\labelsep + \else + \questionlabel + \fi \else - \questionlabel + \if@qformat + \makebox[\hsize][s]{\@questionformat}\hskip-\labelsep + \else + \questionlabel + \fi \fi } \newcommand\questionlabel{\thequestion.} @@ -3031,47 +3171,103 @@ \newenvironment{parts}{% \def\@queslevel{part}% % If the question numbers are being inserted via a \qformat, - % and if a question is beginning with a parts environmnt, then - % we need to enter horizonal mode to the the qformat printed + % and if a question is beginning with a parts environment, then + % we need to enter horizonal mode to get the qformat printed % on the page, rather than saving up the question label (and % possible points) to be combined with the label of the first % part. (\if@inlabel tells us if we are still waiting to enter % horizontal mode after seeing a \question command.) - \if@qformat - \if@inlabel - \leavevmode - \@inlabelfalse + \if@bonus + \if@bonusqformat + \if@inlabel + \leavevmode + \@inlabelfalse + \fi + % The following is just in case the question had points, + % in which case @placepoints will still be true. + % (We used to do this inside of the \thepoints macro, + % but that allowed for an error if the user specified points for + % a question but had a \qformat that didn't mention \thepoints.) + % We also set @placepoints to be false in the \question command, + % in case one queation follows a previous one that had no parts. + \global \@placepointsfalse + \fi + \else + \if@qformat + \if@inlabel + \leavevmode + \@inlabelfalse + \fi + % The following is just in case the question had points, + % in which case @placepoints will still be true. + % (We used to do this inside of the \thepoints macro, + % but that allowed for an error if the user specified points for + % a question but had a \qformat that didn't mention \thepoints.) + % We also set @placepoints to be false in the \question command, + % in case one queation follows a previous one that had no parts. + \global \@placepointsfalse \fi - % The following is just in case the question had points, - % in which case @placepoints will still be true. - % (We used to do this inside of the \thepoints macro, - % but that allowed for an error if the user specified points for - % a question but had a \qformat that didn't mention \thepoints.) - % We also set @placepoints to be false in the \question command, - % in case one queation follows a previous one that had no parts. - \global \@placepointsfalse \fi \def\part{% + \@bonusfalse + \process@part + }% + \def\bonuspart{% + \@bonustrue + \process@part + }% + \def\process@part{% \if@coverpages \cover@question@error \fi \@checkqueslevel{part}% - \addtocounter{numparts}{1}% - % Important: Don't leave any blank lines inside of - % \pageinfo@commands!! This token list will be dumped into - % horizontal mode by \everypar, and so any blank lines will - % cause paragraph breaks. - \temp@toks={% - \edef\@partlabel{part@\arabic{question}@\arabic{partno}}% - \PgInfo@write{\@partlabel}% - \set@counter@to@pageof{Curr@Page}{\@partlabel}% - \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax - \global\expandafter\edef\csname - Contin@\theCurr@Page\endcsname{\arabic{question}}% - \fi - \the\pagepoint@commands - \global \pageinfo@commands={}% - }% + \if@insolution + % We don't count this part, so no addtocounter{numparts}. + \temp@toks={% + \questionobject@pluspagecheck + \global \pageinfo@commands={}% + % We omit the pagepoint@commands + }% temp@toks + \else + \addtocounter{numparts}{1}% + % Important: Don't leave any blank lines inside of + % \pageinfo@commands!! This token list will be dumped into + % horizontal mode by \everypar, and so any blank lines will + % cause paragraph breaks. + \temp@toks={% + \edef\@partlabel{part@\arabic{question}@\arabic{partno}}% + \PgInfo@write{\@partlabel}% + \addquestionobject + % In addition to the \PgInfo@write we use an actual \label + % command. We do this in order to make the question numbers in + % the grade tables into \ref's, so that if the user says + % \usepackage{hyperref}, those question numbers will be clickable. + % + % A further purpose of these labels (which we actually do for all + % questions, parts, subparts, and subsubparts) is that if a + % question (or part, etc.) is, e.g., moved from one page to + % another, LaTeX will notice this and warn the user that LaTeX + % must be run one more time to be sure everything is correct. + % + % We need to do this even though we've already included code to + % check when point totals change because questions (and parts, + % etc.) know what page they're on from reading the info written to + % the .aux file on the previous run. Thus, if a question (or + % part, etc.) is moved to a different page, then the pointsonpage + % totals won't notice until the *second* subsequent run of LaTeX, + % and so there'll be no warning to the user on the *first* run. + % Including these labels gives the user a warning on that first + % run. + \label{\@partlabel}% + \set@counter@to@pageof{Curr@Page}{\@partlabel}% + \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax + \global\expandafter\edef\csname + Contin@\theCurr@Page\endcsname{\arabic{question}}% + \fi + \the\pagepoint@commands + \global \pageinfo@commands={}% + }% temp@toks + \fi \append@toklist \pageinfo@commands \temp@toks \ifhmode % Remove any skips at the end of the previous paragraph @@ -3079,7 +3275,7 @@ \unskip\unskip \par \fi \@doitem - }% part + }% process@part \list{\partlabel}% {% \usecounter{partno}\def\makelabel##1{\hss\llap{##1}}% @@ -3087,6 +3283,7 @@ \labelwidth\leftmargin\advance\labelwidth-\labelsep \topsep=0pt \partopsep=0pt + \partshook }% }% newenvironment{parts} {\endlist} @@ -3102,6 +3299,7 @@ \labelwidth\leftmargin\advance\labelwidth-\labelsep \topsep=0pt \partopsep=0pt + \subpartshook }% }% {\endlist} @@ -3117,6 +3315,7 @@ \labelwidth\leftmargin\advance\labelwidth-\labelsep \topsep=0pt \partopsep=0pt + \subsubpartshook }% }% {\endlist} @@ -3124,6 +3323,8 @@ \def\thesubsubpart{\greeknum{subsubpart}} + + \pagepoint@commands={% \ifhlfcntr@pos{latest@points}% % We're putting a question (or part, etc.) @@ -3139,27 +3340,17 @@ \romannumeral \csname c@pageof@pagepoints\endcsname {\prtaux@hlfcntr{@pagepoints}}}% % See if this has changed from the last run of LaTeX: - \@ifundefined{pointsonpage@\romannumeral + \CheckIfChanged@hlf{@pagepoints}{pointsonpage@\romannumeral \csname c@pageof@pagepoints\endcsname}% - {\global\@pointschangedtrue}% - {% - % OK; it's defined. See if it's changed: - \begingroup - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@pageof@pagepoints\endcsname\endcsname}% - \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% - \edef\pt@check{\prtaux@hlfcntr{@pagepoints}}% - \ifx \pt@check \othpt@check - % Do nothing - \else - \global\@pointschangedtrue - \fi - \endgroup - }% \fi % The following is a macro because \theCurr@Page and % \thepageof@pagepoints might differ by more than 1: \increment@pageof@pagepoints + % The following label is so that we can make the page + % numbers in a grade table indexed by page into \pageref's + % so that \usepackage{hyperref} and pdflatex will + % make them clickable: + \label{firstpoints@onpage@\arabic{Curr@Page}}% \else % These points go on the same page as the points % currently counted in @pagepoints: @@ -3167,7 +3358,40 @@ \set@hlfcntr{latest@points}{0}% \fi \fi -} + \ifhlfcntr@pos{latest@bonuspoints}% + % We're putting a question (or part, etc.) + % with bonus points onto this page: + \ifnum \theCurr@Page > \thepageof@pagebonuspoints\relax + % These bonus points go on a later page than + % the points currently counted in @pagebonuspoints: + \ifnum \thepageof@pagebonuspoints = 0\relax + % Do nothing... + \else + \immediate\write\@mainaux + {\string\gdef\string\bonuspointsonpage@ + \romannumeral \csname c@pageof@pagebonuspoints\endcsname + {\prtaux@hlfcntr{@pagebonuspoints}}}% + % See if this has changed from the last run of LaTeX: + \CheckIfChanged@hlf{@pagebonuspoints}% + {bonuspointsonpage@\romannumeral + \csname c@pageof@pagebonuspoints\endcsname}% + \fi + % The following is a macro because \theCurr@Page and + % \thepageof@pagebonuspoints might differ by more than 1: + \increment@pageof@pagebonuspoints + % The following label is so that we can make the page + % numbers in a bonus grade table indexed by page into \pageref's + % so that \usepackage{hyperref} and pdflatex will + % make them clickable: + \label{firstbonuspoints@onpage@\arabic{Curr@Page}}% + \else + % These points go on the same page as the points + % currently counted in @pagebonuspoints: + \add@hlfcntrtohlfcntr{@pagebonuspoints}{latest@bonuspoints}% + \set@hlfcntr{latest@bonuspoints}{0}% + \fi + \fi +}% pagepoint@commands \def\increment@pageof@pagepoints{% \addtocounter{pageof@pagepoints}{1}% \ifnum \theCurr@Page > \thepageof@pagepoints\relax @@ -3198,13 +3422,48 @@ \set@hlfcntr{latest@points}{0}% % \page@withpoints will be used to find the last % page that has points, which will be written to - % the .aux file vis \AtEndDocument: + % the .aux file via \AtEndDocument: \global\edef\page@withpoints{\thepageof@pagepoints}% \let\next@incr@pageof = \relax \fi \next@incr@pageof -} - +}% increment@pageof@pagepoints +\def\increment@pageof@pagebonuspoints{% + \addtocounter{pageof@pagebonuspoints}{1}% + \ifnum \theCurr@Page > \thepageof@pagebonuspoints\relax + \immediate\write\@mainaux + {\string\gdef\string\bonuspointsonpage@ + \romannumeral \csname c@pageof@pagebonuspoints\endcsname{0}}% + % See if this has changed from the last run of LaTeX: + \@ifundefined{bonuspointsonpage@\romannumeral + \csname c@pageof@pagebonuspoints\endcsname} + {\global\@pointschangedtrue}% + {% + % OK; it's defined. See if it's changed: + \begingroup + \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral + \csname c@pageof@pagebonuspoints\endcsname\endcsname}% + \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}% + \def\pt@check{0}% + \ifx \pt@check \othpt@check + % Do nothing + \else + \global\@pointschangedtrue + \fi + \endgroup + }% + \let\next@incr@pageof = \increment@pageof@pagebonuspoints + \else + \copy@hlfcntr{@pagebonuspoints}{latest@bonuspoints}% + \set@hlfcntr{latest@bonuspoints}{0}% + % \page@withbonuspoints will be used to find the last + % page that has bonus points, which will be written to + % the .aux file via \AtEndDocument: + \global\edef\page@withbonuspoints{\thepageof@pagebonuspoints}% + \let\next@incr@pageof = \relax + \fi + \next@incr@pageof +}% increment@pageof@pagebonuspoints @@ -3213,8 +3472,8 @@ \def\@checkqueslevel#1{% \begingroup - \def\@temp{#1}% - \ifx\@temp\@queslevel + \def\exam@temp{#1}% + \ifx\exam@temp\@queslevel % Everything's fine; do nothing. \else \ClassError{exam}{% @@ -3241,46 +3500,29 @@ \def\@points{#1}% \global \@placepointstrue \if@addpoints - \addto@hlfcntr{numpoints}{\@points}% - \addto@hlfcntr{pointsof@thisquestion}{\@points}% - % latest@points is a holding area for points to be - % added to @pagepoints after we check whether they're - % on the same page as the points currently counted - % by @pagepoints: - \addto@hlfcntr{latest@points}{\@points}% + \if@bonus + \addto@hlfcntr{numbonuspoints}{\@points}% + \addto@hlfcntr{bonuspointsof@thisquestion}{\@points}% + % latest@bonuspoints is a holding area for bonus points to be + % added to @pagepoints after we check whether they're + % on the same page as the points currently counted + % by @pagepoints: + \addto@hlfcntr{latest@bonuspoints}{\@points}% + \else + \addto@hlfcntr{numpoints}{\@points}% + \addto@hlfcntr{pointsof@thisquestion}{\@points}% + % latest@points is a holding area for points to be + % added to @pagepoints after we check whether they're + % on the same page as the points currently counted + % by @pagepoints: + \addto@hlfcntr{latest@points}{\@points}% + \fi \fi \item@points@pageinfo } -% do@int@lbl is the command used to create a label for every question, -% part, subpart, and subsubpart. We make no use of these labels, but we -% put them there so that if a question (or part, etc.) is, e.g., moved -% from one page to another, LaTeX will notice this and warn the user -% that LaTeX must be run one more time to be sure everything is correct. -% -% We need to do this even though we've already included code to check -% when point totals change because questions (and parts, etc.) know what -% page they're on from reading the info written to the .aux file on the -% previous run. Thus, if a question (or part, etc.) is moved to a -% different page, then the pointsonpage totals won't notice until the -% *second* subsequent run of LaTeX, and so there'll be no warning to the -% user on the *first* run. Including these labels gives the user a -% warning on that first run. - -\newcounter{lbl@cntr} -\def\do@int@lbl{% - \addtocounter{lbl@cntr}{1}% - % Looking at latex.ltx reveals that the argument to \label is - % expanded, so we don't really need the \expandafter's in the - % following, but I haven't seen any statement anywhere in - % documentation about it being expanded, so we'll be paranoid and - % protect against future changes in latex.ltx by using - % \expandafter's: - \expandafter\label\expandafter{\thelbl@cntr @@exam@lbl}% -} - % Bug fix, 5 April 2004: \item@points@pageinfo % Appending \point@toks and \pageinfo@commands to \everypar: @@ -3298,20 +3540,14 @@ % Since we've put the command \global \point@toks={} inside of % \point@toks and the command \pageinfo@commands={} inside of % \pageinfo@commands, when the contents of \point@toks and of -% \pageinfo@commands are executed when we enter horizontal mode and -% \everypar is dumped in, the contents of \point@toks and +% \pageinfo@commands are executed (when we enter horizontal mode and +% \everypar is dumped in), the contents of \point@toks and % \pageinfo@commands will be made empty, and so if -% the second paragraph also get \the\point@toks and +% the second paragraph also gets \the\point@toks and % \the\pageinfo@commands, it won't matter. \def\item@points@pageinfo{% \item - % If @qformat is true, and if we're currently doing a question - % (rather than a part, subpart, or subsubpart), then we don't want - % to set the points (if any), since the points of a question will - % appear only if the user chooses to cause that by putting a - % \thepoints in the argument of the \qformat command. - % % Also: We need to do this here, *after* the \item command, rather % than inside the macro \@readpoints, because the \item command % puts the result of the \qformat command into an \hbox (with the @@ -3321,37 +3557,110 @@ % user put a \thepoints command inside that argument it will % correctly expand to the number of points. (When @placepoints is % false, \thepoints expands to nothing at all). - \if@qformat - \ifx\ques@ref\@queslevel - \global \@placepointsfalse - \fi - \fi + % + % We also want to define \padded@point@block when @placepoints is + % true even if qformat and bonusqformat are true just in case the + % user, for some deranged reason, says \droppoints immediately + % following a \question. + % \if@placepoints - % \setup@point@toks defines \point@block to be a macro that prints - % the points with the correct choice of parentheses, brackets, or - % box, and \@pointname or \@marginpointname and puts commands into - % \point@toks to place \point@block at the correct spot. It - % doesn't append anything to \everypar (we do that in this macro, - % below). + % Since we want the user to be able to say \thepoints in the + % argument to a \pointformat command, we need \@placepointstrue + % when \point@block is expanded so that \thepoints will actually + % print something. (After setting up \point@toks, we do + % \@placepointsfalse, but \point@block isn't actually expanded + % until we enter horizontal mode.) Thus, we define + % \padded@point@block, and use that instead of \point@block. We + % put \begingroup and \endgroup around this to confine the + % effect of \@placepointstrue and also to confine the effect of + % any declarations like, e.g., \bfseries that the user might put + % in the argument of a \pointformat command. + % + % Note: We first tried using an \edef to expand \point@block right + % here, while @placepoints is true, but that causes problems if + % the user puts a \boldmath declaration in the argument of a + % \pointformat command. Apparently, expanding \boldmath (without + % executing anything) gives you bunches of undefined control + % sequence errors. + \if@bonus + \def\padded@point@block{% + \begingroup + \@placepointstrue + \bonuspoint@block + \endgroup + }% + \else + \def\padded@point@block{% + \begingroup + \@placepointstrue + \point@block + \endgroup + }% + \fi + % \setup@point@toks puts commands into \point@toks to place + % \padded@point@block at the correct spot. It doesn't append + % anything to \everypar (we do that in this macro, below). + % + % If @qformat is true, and if we're currently doing a question (or + % if @bonusqformat is true and we're doing a bonusquestion) + % (rather than a part, subpart, or subsubpart), then we don't want + % to set the points (if any), since the points of a question will + % appear only if the user chooses to cause that by putting a + % \thepoints in the argument of the \qformat command. \if@pointsdropped % Do nothing! \else - \setup@point@toks + \if@bonus + \if@bonusqformat + \ifx\ques@ref\@queslevel + % Do nothing + \else + \setup@point@toks + \fi + \else + \setup@point@toks + \fi + \else + \if@qformat + \ifx\ques@ref\@queslevel + % Do nothing + \else + \setup@point@toks + \fi + \else + \setup@point@toks + \fi + \fi \fi \global \@placepointsfalse \fi - % We *don't* use \append@toklist; see Bug fix note above + % We *don't* use \append@toklist; see the Bug fixnote above + % (Bug fix, 5 April 2004). % We can append the tokens ``\the\point@toks'' whether or not we're % setting any points because if we're not setting them, \point@toks % will be empty. % Also: It's important to do this *after* the \item command above, % since the \item command discards the previous contents of % \everypar. - \edef\append@everypar{\noexpand\everypar={\the\everypar - \noexpand\the \noexpand\pageinfo@commands - \noexpand\the \noexpand\point@toks}}% - \append@everypar - \do@int@lbl + % Version 2.218$\beta$, 2007/10/31 changes: + % Instead of appending + % \the \pageinfo@commands \the \point@toks + % to \everypar, we insert them into the box \@labels. This corrects + % the problem that arose when a question (or part, etc.) begins with + % a list environment (including verbatim, flushleft, center, + % flushright, and possibly others that are implemented as trivlist + % environments). The \item command in those environments throws + % away the previous contents of \everypar, and so the tokens \the + % \pageinfo@commands \the \point@toks didn't get inserted where we + % expected. List environments *do* preserve the contents of the box + % \@labels, though. + \global\setbox\@labels\hbox{\unhbox\@labels + \the \pageinfo@commands + \the \point@toks}% + % \edef\append@everypar{\noexpand\everypar={\the\everypar + % \noexpand\the \noexpand\pageinfo@commands + % \noexpand\the \noexpand\point@toks}}% + % \append@everypar } @@ -3361,20 +3670,19 @@ % Initialize \@points: -% (Now that I think of it, this seems totally unnecessary, -% but we're not deleting it because we're chicken.) +% (The only reason I think this is necessary is in case the user uses +% a \qformat command, puts \themarginpoints into the format (which is +% *not* the intended use of \themarginpoints), and then doesn't have +% any points for the first question.) \def\@points{0} \def\setup@point@toks{% -% We begin by defining \point@block so that it expands to the properly -% formatted points (including either \pointname or \marginpointname, -% enclosed in either parentheses, brackets, or a box). We then set -% the token list \point@toks equal to the sequence of commands needed -% to put \point@block at the correct location, followed by the tokens -% ``\global \point@toks={}''. The \question, \part, \subpart, or -% \subsubpart command then adds the two tokens ``\the\point@toks'' to -% \everypar. +% We set the token list \point@toks equal to the sequence of commands +% needed to put \padded@point@block at the correct location, followed +% by the tokens ``\global \point@toks={}''. The \question, \part, +% \subpart, or \subsubpart command then adds the two tokens +% ``\the\point@toks'' to \everypar. % % Note: It is not the *contents* of \point@toks that is added to % \everypar; just the two tokens ``\the\point@toks''. This difference @@ -3394,17 +3702,69 @@ % tokens ``\the\point@toks'' will insert an *empty* token list, which % will do no harm. % -% -% \point@block consists of \point@string surrounded by either -% parentheses, brackets, or an fbox. -% -% \point@string is either \@points\@pointname or -% \@points\@marginpointname. - \setup@point@block + \if@pointstwosided + % Set \csname \q@label \endcsname equal to the thing + % that expands to the page number of the current (question or + % part or subpart or subsubpar; whatever it is), but do it + % carefully because, if we don't yet have page info, then it won't + % be defined: + \ifx\@queslevel\ques@ref + \def\q@label{Pg@question@\arabic{question}} + \else + \ifx\@queslevel\part@ref + \def\q@label{Pg@part@\arabic{question}@\arabic{partno}} + \else + \ifx\@queslevel\subpart@ref + \def\q@label{Pg@subpart@\arabic{question}% + @\arabic{partno}@\arabic{subpart}} + \else + \ifx\@queslevel\subsubpart@ref + \def\q@label{Pg@subsubpart@\arabic{question}% + @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}} + \else + \ClassError{exam}{% + This can't happen in function \protect\setup@point@toks + \MessageBreak + }{% + An unexplained error occurred in exam.cls;\MessageBreak + please inform the package maintainer, and send along + \MessageBreak + the LaTeX file that shows the error.\MessageBreak + }% + \fi + \fi + \fi + \fi + % + \expandafter\ifx \csname \q@label \endcsname\relax + % No page info yet; put it into the right margin + \@pointsinrightmargintrue + \@pointsinleftmarginfalse + \else + \ifodd \csname \q@label \endcsname\relax + \if@pointsinoutsidemargin + \@pointsinrightmargintrue + \@pointsinleftmarginfalse + \else + \@pointsinrightmarginfalse + \@pointsinleftmargintrue + \fi + \else + \if@pointsinoutsidemargin + \@pointsinrightmarginfalse + \@pointsinleftmargintrue + \else + \@pointsinrightmargintrue + \@pointsinleftmarginfalse + \fi + \fi + \fi + \fi + % That ends the \if@pointstwosided. + % Now we actually setup \point@toks: \if@pointsinleftmargin - \def\point@string{\@points\@marginpointname}% \point@toks={% - \llap{\point@block + \llap{\padded@point@block \hskip\@totalleftmargin \hskip\marginpointssep }% @@ -3412,21 +3772,19 @@ }% \else \if@pointsinrightmargin - \def\point@string{\@points\@marginpointname}% \point@toks={% \rlap{\hskip-\@totalleftmargin \hskip\textwidth \hskip\@rightmargin \hskip-\rightpointsmargin - \llap{\point@block}% + \llap{\padded@point@block}% }% \global \point@toks={}% }% \else % The points just go after the question number: - \def\point@string{\@points\@pointname}% \point@toks={% - \point@block + \padded@point@block \enspace \global \point@toks={}% }% @@ -3434,48 +3792,17 @@ \fi }% setup@point@toks - -\def\setup@point@block{% - \if@boxedpoints - \def\point@block{\fbox{\point@string}}% - \else - \if@bracketedpoints - \def\point@block{[\point@string]}% - \else - % plain old parentheses: - \def\point@block{(\point@string)}% - \fi - \fi -} - \def\droppoints{% - \def\point@string{\@points\@marginpointname}% - \setup@point@block \leavevmode\unskip\nobreak\hfill \rlap{\hskip\rightmargin % Defined by the list environment \hskip\@rightmargin % Defined by exam.cls \hskip-\rightpointsmargin - \llap{\point@block}% + \llap{\padded@point@block}% }% rlap \par } -% The following is the default definition. -% It will be redefined if the user givea a \totalformat command. -\def\setup@total@block{% - \def\total@block{% - Total for Question \thequestion: \totalpoints\@marginpointname - }% -} - -\def\totalformat#1{% - \def\setup@total@block{\def\total@block{#1}}% -} -% The following is for use in the argument to a \totalformat command: -\def\totalpoints{\pointsofquestion{\arabic{question}}} - \def\droptotalpoints{% - \setup@total@block \leavevmode\unskip\nobreak\hfill \rlap{\hskip\rightmargin % Defined by the list environment \hskip\@rightmargin % Defined by exam.cls @@ -3483,20 +3810,49 @@ \llap{\total@block}% }% rlap \par -} - - -% @placepoints is set true when we encounter a question (or part, etc.) -% that has points. It is set to false (1) when we set \point@toks equal -% to the sequence of commands required to put the properly formatted +}% droptotalpoints +\def\droptotalbonuspoints{% + \leavevmode\unskip\nobreak\hfill + \rlap{\hskip\rightmargin % Defined by the list environment + \hskip\@rightmargin % Defined by exam.cls + \hskip-\rightpointsmargin + \llap{\bonustotal@block}% + }% rlap + \par +}% droptotalbonuspoints + +% The following is the default definition; +% it can be changed by a \totalformat command. +\def\total@block{% + Total for Question \thequestion: \totalpoints\@marginpointname +}% total@block +\def\bonustotal@block{% + Total for Question \thequestion: \totalbonuspoints\@marginbonuspointname +}% bonustotal@block + +\def\totalformat#1{% + \gdef\total@block{\begingroup #1\endgroup}% +}% totalformat +\def\bonustotalformat#1{% + \gdef\bonustotal@block{\begingroup #1\endgroup}% +}% bonustotalformat +% The following is for use in the argument to a \totalformat command: +\def\totalpoints{\pointsofquestion{\arabic{question}}} +\def\totalbonuspoints{\bonuspointsofquestion{\arabic{question}}} + + + +% @placepoints is set true when we encounter a question (or part, etc.) +% that has points. It is set to false (1) when we set \point@toks equal +% to the sequence of commands required to put the properly formatted % points onto the page (this happens only if @qformat is false or if % @qformat is true but we're not doing a question), or (2) by a -% \question or \part command (since if we're doing a question and -% @qformat is true, we need to leave @placepoints true so that the -% \thepoints command can tell if it should expand to points or to -% nothing, and encountering a \question or \part command tells us that -% we no longer have to deal with a possible \thepoints, since we won't -% be expanding a qformat). +% \question command or entering a parts environment (since if we're +% doing a question and @qformat is true, we need to leave @placepoints +% true so that the \thepoints command can tell if it should expand to +% points or to nothing, and encountering a \question command or parts +% environment tells us that we no longer have to deal with a possible +% \thepoints, since we won't be expanding a qformat). \newif\if@placepoints \@placepointsfalse @@ -3517,41 +3873,99 @@ \newif\if@pointsdropped \newif\if@pointsinleftmargin \newif\if@pointsinrightmargin +\newif\if@pointstwosided +\newif\if@pointsinoutsidemargin + +% If we have \@pointstwosidedtrue and \@pointsinoutsidemarginfalse, +% then the points will be printed on the inside margin (left on odd +% numbered pages, right on even numbered pages). If we have +% \@pointstwosidedfalse, then \if@pointsinoutsidemargin is ignored. + +% If we have \@pointstwosidedtrue, then both \@pointsinleftmargin and +% \@pointsinrightmargin will be flipped back and forth, as needed, in +% \setup@point@toks. + \def\pointsinleftmargin{\global\@pointsinleftmargintrue \global\@pointsinrightmarginfalse - \global\@pointsdroppedfalse} + \global\@pointsdroppedfalse + \global\@pointstwosidedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} \def\pointsinrightmargin{\global\@pointsinrightmargintrue \global\@pointsinleftmarginfalse - \global\@pointsdroppedfalse} + \global\@pointsdroppedfalse + \global\@pointstwosidedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} \def\nopointsinmargin{\global\@pointsinleftmarginfalse \global\@pointsinrightmarginfalse - \global\@pointsdroppedfalse} + \global\@pointsdroppedfalse + \global\@pointstwosidedfalse + \gdef\pt@name{\@pointname}% + \gdef\bnspt@name{\@bonuspointname}} \def\pointsdroppedatright{\global\@pointsdroppedtrue \global\@pointsinleftmarginfalse - \global\@pointsinrightmarginfalse} -\let\pointsinmargin\pointsinleftmargin -\let\nopointsinrightmargin\nopointsinmargin -\let\nopointsinleftmargin\nopointsinmargin - + \global\@pointsinrightmarginfalse + \global\@pointstwosidedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} +\def\pointstwosided{\global\@pointstwosidedtrue + \global\@pointsinoutsidemargintrue + \global\@pointsdroppedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} +\def\pointstwosidedreversed{\global\@pointstwosidedtrue + \global\@pointsinoutsidemarginfalse + \global\@pointsdroppedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} +\let\pointsinmargin=\pointsinleftmargin +\let\nopointsinrightmargin=\nopointsinmargin +\let\nopointsinleftmargin=\nopointsinmargin \nopointsinmargin % Will the points be displayed inside parentheses (the default), or -% will they be boxed or bracketed: -\newif\if@boxedpoints -\def\boxedpoints{\global\@boxedpointstrue \global\@bracketedpointsfalse} -\def\noboxedpoints{\global\@boxedpointsfalse \global\@bracketedpointsfalse} -\@boxedpointsfalse +% will they be boxed or bracketed, or customized using pointformat: +\def\boxedpoints{% + \gdef\point@block{\fbox{\@points\pt@name}}% + \gdef\bonuspoint@block{\fbox{\@points\bnspt@name}}% +} +\def\noboxedpoints{% + \gdef\point@block{(\@points\pt@name)}% + \gdef\bonuspoint@block{(\@points\bnspt@name)}% +} +\def\bracketedpoints{% + \gdef\point@block{[\@points\pt@name]}% + \gdef\bonuspoint@block{[\@points\bnspt@name]}% +} +\let\nobracketedpoints=\noboxedpoints + +\def\pointformat#1{% + % We don't have to worry about the user putting things + % like \bfseries, etc. into \point@block, because + % \padded@point@block encloses \point@block in a group, + % which confines the effects of anything here: + \gdef\point@block{#1}% +} + +\def\bonuspointformat#1{% + % We don't have to worry about the user putting things + % like \bfseries, etc. into \point@block, because + % \padded@point@block encloses \point@block in a group, + % which confines the effects of anything here: + \gdef\bonuspoint@block{#1}% +} + +%Initialize: +\noboxedpoints -\newif\if@bracketedpoints -\def\bracketedpoints{\global\@bracketedpointstrue \global\@boxedpointsfalse} -\def\nobracketedpoints{\global\@bracketedpointsfalse \global\@boxedpointsfalse} -\@bracketedpointsfalse \def\pointname#1{\gdef\@pointname{#1}} +\def\bonuspointname#1{\gdef\@bonuspointname{#1}} % Initialize to leave a space, and then the word `points': %%\pointname{ points} % The following improvement was contributed by @@ -3563,13 +3977,21 @@ % Note the space before the \points in the following; it's % intentional!) \pointname{ \points} - +\bonuspointname{ \bonuspoints} \newcommand\point@sing{point} \newcommand\point@plur{points} \newcommand\pointpoints[2]{% \renewcommand\point@sing{#1}% \renewcommand\point@plur{#2}% } +%\newcommand\bonuspoint@sing{bonus point} +%\newcommand\bonuspoint@plur{bonus points} +\newcommand\bonuspoint@sing{point (bonus)} +\newcommand\bonuspoint@plur{points (bonus)} +\newcommand\bonuspointpoints[2]{% + \renewcommand\bonuspoint@sing{#1}% + \renewcommand\bonuspoint@plur{#2}% +} @@ -3590,7 +4012,15 @@ \ifthenelse{\equal{\pt@string}{1} \or \equal{\pt@string}{\half}} {\point@sing}{\point@plur}% \endgroup -} +}% \points +\newcommand\bonuspoints{% + \begingroup + \let\half=\relax + \edef\pt@string{\@points}% + \ifthenelse{\equal{\pt@string}{1} \or \equal{\pt@string}{\half}} + {\bonuspoint@sing}{\bonuspoint@plur}% + \endgroup +}% \bonuspoints %\newcommand\points{% % \begingroup % \let\half=\relax @@ -3613,7 +4043,10 @@ % except that it expands to either ``mark'' or ``marks'', but that % conflicts with some package or other. Thus, we'll implement % \marksnotpoints using the \pointpoints command instead: -\newcommand\marksnotpoints{\pointpoints{mark}{marks}} +\newcommand\marksnotpoints{% + \pointpoints{mark}{marks}% + \bonuspointpoints{mark (bonus)}{marks (bonus)}% +}% \marksnotpoints % \@marginpointname is used in place of \@pointname if any of @@ -3621,81 +4054,280 @@ % true: \def\marginpointname#1{\gdef\@marginpointname{#1}} \marginpointname{} +\def\marginbonuspointname#1{\gdef\@marginbonuspointname{#1}} +\marginbonuspointname{ (bonus)} -% The following keeps track of whether the user has requested that we -% add up the points on the exam. We make the default false so that -% users who put other than numbers into the points argument of a -% question (or part, or subpart) won't get error messages. -% We use \if@printtotalpoints as a flag to signal that we are counting -% points, so that we will know to print the total on the screen (and -% in the log file). We use this separate flag so that the user can -% use both \addpoints and \noaddpoints to count some points and not -% others, but still have the total printed when we finish the file no -% matter what the state of \if@addpoints. -\newif\if@addpoints -\newif\if@printtotalpoints -\def\addpoints{\global\@addpointstrue\global\@printtotalpointstrue} -\def\noaddpoints{\global\@addpointsfalse} -\@addpointsfalse -\@printtotalpointsfalse - %-------------------------------------------------------------------- -% choices (for multiple choice) +% choices (for multiple choice) and checkboxes \renewcommand\thechoice{\Alph{choice}} \newcommand\choicelabel{\thechoice.} +% We will have \@correctchoicetrue when we're printing solutions +% and we're printing the correct choice of a choices or +% oneparchoices environment. +% We'll say \begingroup before saying \@correctchoicetrue +% and we'll say \endgroup at either the next \choice or \correctchoice +% or the end of the choices or oneparchoices environment. +% Thus, we'll never again need to say \@correctchoicefalse +\newif\if@correctchoice +\@correctchoicefalse + +\newcommand\CorrectChoiceEmphasis[1]{% + \def\CorrectChoice@Emphasis{#1}% +} +\CorrectChoiceEmphasis{\bfseries} +\let\correctchoiceemphasis\CorrectChoiceEmphasis + +% Note: \do@choice@pageinfo is used in both the choices and +% the checkboxes environments. +\newtoks\choice@toks +\def\do@choice@pageinfo{% + \choice@toks={% + \questionobject@pluspagecheck + \choice@toks={}% + }% + % Version 2.217-beta changes: + % Instead of appending stuff to \everypar, we insert + % \the \pageinfo@commands and \the \point@toks + % into the box \@labels: + \global\setbox\@labels\hbox{\unhbox\@labels + \the \choice@toks}% + % \edef\append@everypar{\noexpand\everypar={\the\everypar + % \noexpand\the \noexpand\choice@toks}}% + % \append@everypar +}% do@choice@pageinfo + + + % Added 22 April 2004: Increased the \leftmargin by 2.5em, % so the choices will be visibly indented. \newenvironment{choices}% {\list{\choicelabel}% {\usecounter{choice}\def\makelabel##1{\hss\llap{##1}}% \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}% - \let\choice=\item + \def\choice{% + \if@correctchoice + \color@endgroup + \endgroup + \fi + \item + \do@choice@pageinfo + } % choice + \def\CorrectChoice{% + \if@correctchoice + \color@endgroup + \endgroup + \fi + \ifprintanswers + % We can't say \choice here, because that would + % insert an \endgroup: + % 2016/05/10: We say \color@begingroup in addition to + % \begingroup in case \CorrectChoiceEmphasis involves color + % and the text exactly fills the line (which would + % otherwise create a blank line after this choice): + % 2016/05/11: We leave hmode if we're in it, + % i.e., if there's no blank line preceding this + % \CorrectChoice command. (Without this, the + % \special created by a \color{whatever} command that might + % be inserted by \CorrectChoice@Emphasis would be appended + % to the previous \choice, which could cause an extra + % (blank) line to be inserted before this \CorrectChoice.) + % Since \par and \endgraf seem to cancel \@totalleftmargin + % (for reasons I don't understand), we'll do the following: + % Motivated by the def of \leavevmode, + % \def\leavevmode{\unhbox\voidb@x} + % we will now leave hmode (if we're in hmode): + \ifhmode \unskip\unskip\unvbox\voidb@x \fi + \begingroup \color@begingroup \@correctchoicetrue + \CorrectChoice@Emphasis + \fi + \item + \do@choice@pageinfo + } % CorrectChoice + \let\correctchoice\CorrectChoice \labelwidth\leftmargin\advance\labelwidth-\labelsep \topsep=0pt \partopsep=0pt + \choiceshook }% }% - {\endlist} + {\if@correctchoice \color@endgroup \endgroup \fi \endlist} \newenvironment{oneparchoices}% {% \setcounter{choice}{0}% \def\choice{% - \refstepcounter{choice}% - \ifnum\value{choice}>1 - \penalty -50\hskip 1em plus 1em\relax - \fi - \choicelabel\nobreak\enskip - }% + \if@correctchoice \endgroup \fi + \refstepcounter{choice}% + \ifnum\value{choice}>1\relax + \penalty -50\hskip 1em plus 1em\relax + \fi + \choicelabel + % No need to put the following into a token string; we just put + % the choicelabel onto the page, so we're at the spot whose page + % number we want to record: + \questionobject@pluspagecheck + \nobreak\enskip + }% choice + \def\CorrectChoice{% + \if@correctchoice \endgroup \fi + \refstepcounter{choice}% + \ifprintanswers + \begingroup \@correctchoicetrue + \CorrectChoice@Emphasis + \fi + \ifnum\value{choice}>1\relax + \penalty -50\hskip 1em plus 1em\relax + \fi + \choicelabel + % No need to put the following into a token string; we just put + % the choicelabel onto the page, so we're at the spot whose page + % number we want to record: + \questionobject@pluspagecheck + \nobreak\enskip + }% CorrectChoice + \let\correctchoice\CorrectChoice + \let\par\@empty + % If we're continuing the paragraph containing the question, + % then leave a bit of space before the first choice: + \ifvmode\else\enskip\fi + \ignorespaces + }% + {\if@correctchoice \endgroup \fi} + +\newcommand{\checkboxchar}[1]{\def\checkbox@char{#1}} +\newcommand{\checkedchar}[1]{\def\checked@char{#1}} +\checkboxchar{$\bigcirc$} +\checkedchar{$\surd$} + +\newenvironment{checkboxes}% + {\list{\checkbox@char}% + {% + \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}% + \def\choice{% + \if@correctchoice + \color@endgroup \endgroup + \fi + \item + \do@choice@pageinfo + } % choice + \def\CorrectChoice{% + \if@correctchoice + \color@endgroup \endgroup + \fi + \ifprintanswers + % We can't say \choice here, because that would + % insert an \endgroup. + % 2016/05/10: We say \color@begingroup in addition to + % \begingroup in case \CorrectChoiceEmphasis involves color + % and the text exactly fills the line (which would + % otherwise create a blank line after this choice): + % 2016/05/11: We leave hmode if we're in it, + % i.e., if there's no blank line preceding this + % \CorrectChoice command. (Without this, the + % \special created by a \color{whatever} command that might + % be inserted by \CorrectChoice@Emphasis would be appended + % to the previous \choice, which could cause an extra + % (blank) line to be inserted before this \CorrectChoice.) + % Since \par and \endgraf seem to cancel \@totalleftmargin + % (for reasons I don't understand), we'll do the following: + % Motivated by the def of \leavevmode, + % \def\leavevmode{\unhbox\voidb@x} + % we will now leave hmode (if we're in hmode): + \ifhmode \unskip\unskip\unvbox\voidb@x \fi + \begingroup \color@begingroup \@correctchoicetrue + \CorrectChoice@Emphasis + \item[\checked@char] + \else + \item + \fi + \do@choice@pageinfo + } % CorrectChoice + \let\correctchoice\CorrectChoice + \labelwidth\leftmargin\advance\labelwidth-\labelsep + \topsep=0pt + \partopsep=0pt + \checkboxeshook + }% + }% + {\if@correctchoice \color@endgroup \endgroup \fi \endlist} + +\newenvironment{oneparcheckboxes}% + {% + % Although we're not printing numbers for the choices, we use the + % choice counter to keep track of whether a choice is the first + % one (in which case we don't leave any additional space) or a + % later one (in which case we do leave additional space): + \setcounter{choice}{0}% + \def\choice{% + \if@correctchoice \endgroup \fi + \stepcounter{choice}% + \ifnum\value{choice}>1\relax + \penalty -50\hskip 1em plus 1em\relax + \fi + \checkbox@char + % No need to put the following into a token string; we just put + % \checkbox@char onto the page, so we're at the spot whose page + % number we want to record: + \questionobject@pluspagecheck + \nobreak\enskip + }% choice + \def\CorrectChoice{% + \if@correctchoice \endgroup \fi + \stepcounter{choice}% + \ifprintanswers + \begingroup \@correctchoicetrue + \CorrectChoice@Emphasis + \fi + \ifnum\value{choice}>1\relax + \penalty -50\hskip 1em plus 1em\relax + \fi + \ifprintanswers + \checked@char + \else + \checkbox@char + \fi + % No need to put the following into a token string; we just put + % the choicelabel onto the page, so we're at the spot whose page + % number we want to record: + \questionobject@pluspagecheck + \nobreak\enskip + }% CorrectChoice + \let\correctchoice\CorrectChoice \let\par\@empty % If we're continuing the paragraph containing the question, % then leave a bit of space before the first choice: \ifvmode\else\enskip\fi \ignorespaces }% - {} + {\if@correctchoice \endgroup \fi} %-------------------------------------------------------------------- % Answer Lines (for short answer questions) -% Note: \ques@ref is also used in \item@points@pageinfo +% Note: \ques@ref is also used in \item@points@pageinfo, and all four +% of the following are used in \setup@point@toks \def\ques@ref{question} \def\part@ref{part} \def\subpart@ref{subpart} \def\subsubpart@ref{subsubpart} +% Note: \answerclearance is also used by \fillin + \newlength\answerlinelength \newlength\answerskip +\newlength\answerclearance \setlength\answerlinelength{1in} \setlength\answerskip{2ex} +\setlength\answerclearance{0.2ex} -\def\answerline{% +\newcommand\answerline[1][{}]{% + % One optional argument, the default value of which is empty. \ifx\@queslevel\ques@ref \let\ans@l=\questionlabel \else @@ -3717,19 +4349,150 @@ \fi \fi \par \nobreak \vskip \answerskip - \hfill \ans@l~\hbox to \answerlinelength{\hrulefill}% + \hfill + \ifprintanswers + \ans@l~\hbox to 0pt{\hbox to \answerlinelength{\hrulefill}\hss}% + \raise \answerclearance\hbox to \answerlinelength{% + % 2016/05/10: Added \color@begingroup and \color@endgroup: + \color@begingroup + \CorrectChoice@Emphasis \hfil #1\hss + \color@endgroup}% + \else + \ans@l~\hbox to \answerlinelength{\hrulefill}% + \fi \par +}% answerline + +%-------------------------------------------------------------------- +% \fillin, for fill-in-the-blank questions + +\newlength\fillinlinelength +\setlength\fillinlinelength{1in} + +% \fillin can take two optional arguments. +% +% The first optional argument is the answer to be printed above the line +% when \printanswers is in effect; the default value is empty. That +% line is printed a distance of \answerclearance below the baseline. +% +% The second optional argument is the length of the line that we print; +% the default value is \fillinlinelength. The value of +% \fillinlinelength is set with the command +% +% \setlength\fillinlinelength{1in} +% +% and can be changed by giving a new \setlength command. +% +% When answers are being printed, the optional argument is printed +% subject to the declarations in the argument of the last +% \CorrectChoiceEmphasis command. It is centered on the line unless it +% is too long, in which case it extends to the right of the line. +% +% \fillin eats (and ignores) space characters appearing before the +% first optional argument. It also eats (and ignores) space +% characters appearing after the first optional argument and before +% the second optional argument. However, if exactly one optional +% argument appears, and if there are one or more space characters +% following that one optional argument, then those spaces are replaced +% by a single space character, but not eaten. + +\newcommand\fillin[1][{}]{% + \def\fillin@ans{#1}% + \fillin@relay +}% fillin + +\newcommand\fillin@relay{% + % We use \exam@ifnextchar, a variation on \@ifnextchar. + % If \exam@ifnextchar encounters one or more space characters + % followed by a [, then those spaces are ignored (just as they would + % be by \@ifnextchar). However, if one or more space characters are + % followed by a non-space character other than [, then + % \exam@ifnextchar inserts a space following the + % {\@fillin@relay[\fillinlinelength]} that is the third argument to + % \exam@ifnextchar. + \exam@ifnextchar[{\@fillin@relay} + {\@fillin@relay[\fillinlinelength]}% +}% fillin@relay + +\def\@fillin@relay[#1]{% + % The first argument is in \fillin@ans, the second is #1. + \leavevmode + \ifprintanswers + \rlap{\raise -\answerclearance \hbox to #1{\hrulefill}}% + \begingroup + \setbox0 \hbox{\color@begingroup + \CorrectChoice@Emphasis \fillin@ans \color@endgroup}% + \ifdim\wd0 > #1\relax + \hbox{\color@begingroup\CorrectChoice@Emphasis \fillin@ans + \color@endgroup}% + \else + \hbox to #1{\color@begingroup\CorrectChoice@Emphasis + \hfil \fillin@ans \hfil\color@endgroup}% + \fi + \endgroup + \else + \raise -\answerclearance \hbox to #1{\hrulefill}% + \fi +}% @fillin@relay + +% \exam@ifnextchar is used by \fillin. +% \exam@ifnextchar is a variation of \@ifnextchar that does not always +% ignore space tokens. If \exam@ifnextchar encounters one or more +% space tokens, it makes note of that (with the command +% \@tempswatrue). If the first non-space character encountered +% matches argument #1, then any spaces that had been encountered are +% ignored. However, if one or more spaces are encountered and the +% first non-space character found does not match argument #1, then +% \exam@ifnextchar produces argument #3 followed by a space character. +% (This differs from the behavior of \new@ifnextchar in amsgen.sty, +% which does lookahead for any character, including a space.) This +% code (as well as the idea for it) is due to Dan Luecking and Ulrich +% Diez. +\long\def\exam@ifnextchar#1#2#3{% + \let\reserved@d=#1% + \def\reserved@a{#2}% + \def\reserved@b{#3}% + % The following says we haven't yet seen any spaces: + \@tempswafalse + \futurelet\@let@token\exam@ifnch +}% exam@ifnextchar + +\def\exam@ifnch{% + \ifx\@let@token\@sptoken + % Signal that we've found a space: + \@tempswatrue + \let\reserved@c\exam@xifnch % this gobbles the space + \else + \ifx\@let@token\reserved@d + \let\reserved@c\reserved@a + \else + \if@tempswa + \def\reserved@c{\expandafter\reserved@b\space}% + \else + \let\reserved@c\reserved@b + \fi + \fi + \fi + \reserved@c +}% exam@ifnch + +% The following defines \exam@xifnch so that it will eat a space +% following it and then call \exam@ifnch: +{% keep redefinition of \: local + \def\:{\exam@xifnch} + \expandafter\gdef\: {\futurelet\@let@token\exam@ifnch} } %-------------------------------------------------------------------- % \fillwithlines -% \fillwithlines takes one argument, which is either a length or \fill, -% and it fills that much vertical space with horizontal lines that run -% the length of the current line. That is, the extend from the current -% left margin (which depends on whether we're in a quesiton, parts, -% subpart, or subsubpart) to the right margin. +% \fillwithlines takes one argument, which is either a length or \fill +% or \stretch{number}, and it fills that much vertical space with +% horizontal lines that run the length of the current line. That is, +% they extend from the current left margin (which depends on whether +% we're in a question, part, subpart, or subsubpart) to the right +% margin. % % The distance between the lines is \linefillheight, whose default value % is set with the command @@ -3741,21 +4504,74 @@ % The thickness of the lines is \linefillthickness, whose default value % is set with the command % -% \setlength\linefillthickness{.2pt} +% \setlength\linefillthickness{.1pt} % % This value can be changed by giving a new \setlength command. - +% +% As of version 2.503, 2016/03/25, the lines drawn by the +% \fillwithlines command will be drawn in color if the user has given +% the command +% +% \colorfillwithlines. +% +% The actual drawing of the lines is now done by the command +% \do@fillwithlines, after the \fillwithlines command decides whether +% they will be in color. The default color is set by the command +% +% \definecolor{FillWithLinesColor}{gray}{0.8} +% +% and the color can be changed by giving a new \definecolor command. +% You can return to black lines by giving the command +% +% \nocolorfillwithlines \newlength\linefillheight \newlength\linefillthickness \setlength\linefillheight{.25in} \setlength\linefillthickness{0.1pt} + + + +\newif\if@colorfillwithlines +\@colorfillwithlinesfalse +\def\colorfillwithlines{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorfillwithlines + \MessageBreak + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{FillWithLinesColor}{gray}{0.8} + \@colorfillwithlinestrue + }% +}% \colorfillwithlines +\def\nocolorfillwithlines{\@colorfillwithlinesfalse} + +\newcommand\fillwithlines[1]{% + \if@colorfillwithlines + \color@begingroup + \color{FillWithLinesColor}% + \do@fillwithlines{#1}% + \color@endgroup + \else + \do@fillwithlines{#1}% + \fi +}% \fillwithlines + \newcommand\linefill{\leavevmode \leaders\hrule height \linefillthickness \hfill\kern\z@} - -\def\fillwithlines#1{% +% \do@fillwithlines is called only by \fillwithlines +\def\do@fillwithlines#1{% \begingroup \ifhmode \par @@ -3770,11 +4586,299 @@ % no matter where on the page it happens to start: \cleaders \copy0 \vskip #1 \hbox{}% \endgroup +}% \do@fillwithlines + +%-------------------------------------------------------------------- +% \fillwithdottedlines + + +% \fillwithdottedlines is similar to \fillwithlines, except that it +% fills the space with dotted lines (created by \dotfill) rather than +% with solid lines. + +% \fillwithdottedlines takes one argument, which is either a length or +% \fill or \stretch{number}, and it fills that much vertical space +% with dotted lines that run the length of the current line. That is, +% they extend from the current left margin (which depends on whether +% we're in a question, part, subpart, or subsubpart) to the right +% margin. +% +% The distance between the lines is \dottedlinefillheight, whose +% default value is set with the command +% +% \setlength\dottedlinefillheight{.25in} +% +% This value can be changed by giving a new \setlength command. + +% As of version 2.503, 2016/03/25, the dotted lines drawn by the +% \fillwithdottedlines command will be drawn in color if the user has +% given the command +% +% \colorfillwithdottedlines. +% +% The actual drawing of the lines is now done by the command +% \do@fillwithdottedlines, after the \fillwithdottedlines command +% decides whether they will be in color. The default color is set by +% the command +% +% \definecolor{FillWithDottedLinesColor}{gray}{0.8} +% +% and the color can be changed by giving a new \definecolor command. +% You can return to black lines by giving the command +% +% \nocolorfillwithdottedlines + + +\newlength\dottedlinefillheight +\setlength\dottedlinefillheight{.25in} + +\newif\if@colorfillwithdottedlines +\@colorfillwithdottedlinesfalse +\def\colorfillwithdottedlines{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorfillwithdottedlines + \MessageBreak + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{FillWithDottedLinesColor}{gray}{0.8} + \@colorfillwithdottedlinestrue + }% +}% \colorfillwithdottedlines +\def\nocolorfillwithdottedlines{\@colorfillwithdottedlinesfalse} + +\newcommand\fillwithdottedlines[1]{% + \if@colorfillwithdottedlines + \color@begingroup + \color{FillWithDottedLinesColor}% + \do@fillwithdottedlines{#1}% + \color@endgroup + \else + \do@fillwithdottedlines{#1}% + \fi +}% \fillwithdottedlines + +% \do@fillwithdottedlines is called only by \fillwithdottedlines +\def\do@fillwithdottedlines#1{% + \begingroup + \ifhmode + \par + \fi + \hrule height \z@ + \nobreak + \setbox0=\hbox to \hsize{\hskip \@totalleftmargin + \vrule height \dottedlinefillheight depth \z@ width \z@ + \dotfill}% + % We use \cleaders (rather than \leaders) so that a given + % vertical space will always produce the same number of lines + % no matter where on the page it happens to start: + \cleaders \copy0 \vskip #1 \hbox{}% + \endgroup +}% \do@fillwithdottedlines + +%-------------------------------------------------------------------- +% \fillwithgrid + + +% \fillwithgrid is similar to \fillwithlines, except that it +% fills the space with a grid. + +% \fillwithgrid takes one argument, which is either a length or \fill +% or \stretch{number}, and it fills that much vertical space with a +% grid that runs the length of the current line. That is, it extends +% from the current left margin (which depends on whether we're in a +% question, part, subpart, or subsubpart) to the right margin. +% +% The default grid size and grid line thickness were set by the +% commands +% +% \setlength{\gridsize}{5mm} +% \setlength{\gridlinewidth}{0.1pt} +% +% You can change either or both of those by giving new \setlength +% commands. The period of the grid is \gridsize (both horizontally +% and vertically). That is, the horizontal distance from the left +% edge of one vertical line to the left edge of the next vertical line +% is \gridsize, as is the vertical distance from the top edge of one +% horizontal line to the top edge of the next horizontal line. Thus, +% each square has outer side length equal to \gridsize+\gridlinewidth. + +% By default, the created grids are in black. However, if you give the +% commands +% +% \usepackage{color} +% \colorgrids +% +% then the grids will be in color, by default a light gray. That +% default color was defined by the command +% +% \definecolor{GridColor}{gray}{0.8} +% +% You can change the color by redefining the color GridColor by giving +% a new \definecolor command. + +\newif\if@colorgrids +\newcommand\colorgrids{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorgrids + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{GridColor}{gray}{0.8} + \@colorgridstrue + }% +}% \colorgrids +\newcommand\nocolorgrids{\@colorgridsfalse} +\nocolorgrids + +\newlength\gridsize +\newlength\gridlinewidth +\setlength{\gridsize}{5mm} +\setlength{\gridlinewidth}{0.1pt} + +\def\fillwithgrid#1{% + \begingroup + \ifhmode + \par + \fi + \hrule height \z@ + \nobreak + + % We first set box0 equal to an \hbox which, when printed, is a + % square with width and height equal to \gridsize+\gridlinewidth, + % but which has + % width equal to \gridsize, + % height equal to \gridsize, and + % depth equal to 0pt. + % When we put multiple copies of it together using \leaders or + % \cleaders, the right edge will coincide with the left edge of the + % next box and the bottom edge will coincide with the top edge of + % the box below it. + \setlength{\@tempdima}{\gridsize} + \addtolength{\@tempdima}{\gridlinewidth} + \setlength{\@tempdimb}{\gridsize} + \addtolength{\@tempdimb}{-\gridlinewidth} + \setbox0=\hbox{% + \rlap{\vrule height \gridsize depth \gridlinewidth width \gridlinewidth}% + \rlap{\vrule height \gridsize depth -\@tempdimb width \@tempdima}% + \vrule height 0pt depth \gridlinewidth width \@tempdima + \llap{\vrule height \gridsize depth \gridlinewidth width \gridlinewidth}% + }% + \wd0=\gridsize + \dp0=0pt + % Now we set box1 equal to an \hbox containing a single line of + % copies of box0. We use \leaders (instead of \cleaders) so that + % if we use it twice on a page, once with a question and once + % with a part, the boxes will line up vertically. We add a kern of + % \gridlinewidth at the right because the rightmost vertical line + % appears to the right of where the \leaders command thinks that it + % appears. + \setbox1=\hbox to \textwidth{% + \color@begingroup + \if@colorgrids + \color{GridColor}% + \fi + \hskip \@totalleftmargin \leaders\copy0\hfil \kern\gridlinewidth + \color@endgroup + }% + % Finally: We create the grid, using \cleaders: We use \cleaders + % (rather than \leaders) so that a given vertical space will always + % produce the same number of lines no matter where on the page it + % happens to start. We add a kern of \gridlinewidth because the + % bottommost horizontal line appears below where the \cleaders + % command thinks that it appears. + \cleaders \copy1 \vskip #1 \kern \gridlinewidth \hbox{}% + \endgroup +}% fillwithgrid + +%-------------------------------------------------------------------- +% \makeemptybox + +% \makeemptybox takes one argument, which is a length, and it creates +% an empty box of width the length of the current line and of height +% equal to the argument. That is, the box extends from the current +% left margin (which depends on whether we're in a question, part, +% subpart, or subsubpart) to the right margin. + +% As of version 2.304, the argument of \makeemptybox can be either +% a length, or \fill, or \stretch{number}. + +% \newcommand\makeemptybox[1]{ +% \par +% \begingroup +% \setlength{\fboxsep}{0pt}% +% \framebox[\linewidth]{% +% \vrule height 0pt depth #1 width 0pt +% }% +% \endgroup +% } + +\newlength\minboxheight +\setlength\minboxheight{.1in} + + +% As of version 2.502, 2016/03/23, the frame drawn by the +% \makeemptybox command will be drawn in color if the user has given +% the command \colorsolutionboxes. The actual drawing of the box is +% now done by the command \do@emptybox, after the \makeemptybox +% command decides whether it will be in color. + +\newcommand\makeemptybox[1]{% + \if@colorsolutionboxes + \color@begingroup + \color{SolutionBoxColor}% + \do@emptybox{#1}% + \color@endgroup + \else + \do@emptybox{#1}% + \fi } +% The command \do@emptybox is called only by \makeemptybox. +\newcommand\do@emptybox[1]{% + \par + \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}% + \nointerlineskip + \begingroup + \setbox0=\hbox to \hsize{\hskip\@totalleftmargin + \vrule height\minboxheight \hfill \vrule}% + % The vertical size desired may not be an exact multiple of + % \minboxheight, and so \cleaders might leave a gap between the + % vertical lines and the horizontal lines above and below it. + % Thus, we put a single copy of \box0 immediately below the + % horizontal line above and we'll also put a single copy of \box0 + % immediately above the horizontal line below. + \copy0 + \nobreak + \vskip -\minboxheight + \cleaders \copy0 \vskip #1 + \vskip -\minboxheight + \nointerlineskip + \copy0 + \endgroup + \nointerlineskip + \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}% +} %-------------------------------------------------------------------- -% \uplevel and \fullwidth: +% \uplevel and \fullwidth +% and the EnvUplevel and EnvFullwidth environments: % \uplevel is used to print text at the indentation level of the % enclosing environment. For example, to precede a question with @@ -3783,14 +4887,26 @@ % % \fullwidth is similar, but uses the full page of text on the page. +% The EnvUplevel environment is similar to the \uplevel command, but it +% has the advantage that you can include verbatim material (using, e.g., +% the \verb command) in the environment. (You can't include verbatim +% material in the argument of an \uplevel command.) + +% The EnvFullwidth environment is similar to the \fullwidth command, but +% it has the advantage that you can include verbatim material (using, +% e.g., the \verb command) in the environment. (You can't include +% verbatim material in the argument of an \fullwidth command.) + + \long\def\uplevel#1{% \par\bigskip \vbox{% + % We entered internal vertical mode, and so we get \parshape=0. % We set \leftskip to provide the correct left margin for whatever - % text is in the argument of the \uplevel command: + % is in the argument of the \uplevel command: \leftskip=\@totalleftmargin \advance\leftskip-\leftmargin - % We adjust \@totalleftmargin (and linewidth?) in case there's a + % We adjust \@totalleftmargin and linewidth in case there's a % solution environment inside of the argument to the \uplevel: \advance\@totalleftmargin-\leftmargin \advance\linewidth\leftmargin @@ -3799,9 +4915,24 @@ \nobreak } +\newenvironment{EnvUplevel} + {\par\bigskip\vbox\bgroup + % We set \leftskip to provide the correct left margin for whatever + % is inside of the environment: + \leftskip=\@totalleftmargin + \advance\leftskip-\leftmargin + % We adjust \@totalleftmargin (and linewidth?) in case there's a + % solution environment inside of the environment: + \advance\@totalleftmargin-\leftmargin + \advance\linewidth\leftmargin + } + {\egroup\nobreak} + + \long\def\fullwidth#1{% \par\bigskip \vbox{% + % We entered internal vertical mode, and so we get \parshape=0. \leftskip=0pt \rightskip=0pt \advance\linewidth\@totalleftmargin \@totalleftmargin=0pt @@ -3810,6 +4941,17 @@ \nobreak } +\newenvironment{EnvFullwidth} + {\par\bigskip\vbox\bgroup + % We entered internal vertical mode, and so we get \parshape=0. + \leftskip=0pt \rightskip=0pt + % We adjust \@totalleftmargin (and linewidth?) in case there's a + % solution environment inside of the environment: + \advance\linewidth\@totalleftmargin + \@totalleftmargin=0pt + } + {\egroup\nobreak} + %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -3826,41 +4968,143 @@ \def\cellwidth#1{\@cellwidth=#1} \def\gradetablestretch#1{\def\@gtblstretch{#1}} +% \settabletotalpoints allows the user to specify a total +% number of points to appear in a table that may be different +% from the sum of the points in the table: +\newcommand\prt@tablepoints{\prt@hlfcntr{tbl@points}} +\newcommand\settabletotalpoints[1]{% + \def\prt@tablepoints{#1}% +}% \settabletotalpoints + +% \settabletotalbonuspoints is similar to \settabletotalpoints: +\newcommand\prt@tablebonuspoints{\prt@hlfcntr{tbl@bonuspoints}} +\newcommand\settabletotalbonuspoints[1]{% + \def\prt@tablebonuspoints{#1}% +}% \settabletotalbonuspoints + % All of the following that begin with `h' are for horizontal tables, % and all of them that begin with `v' are for vertical tables: \def\hqword#1{\def\@hqword{#1}} \def\hpword#1{\def\@hpword{#1}} \def\hsword#1{\def\@hsword{#1}} \def\htword#1{\def\@htword{#1}} +\def\hpgword#1{\def\@hpgword{#1}} + \def\vqword#1{\def\@vqword{#1}} \def\vpword#1{\def\@vpword{#1}} \def\vsword#1{\def\@vsword{#1}} \def\vtword#1{\def\@vtword{#1}} - \def\vpgword#1{\def\@vpgword{#1}} -\def\hpgword#1{\def\@hpgword{#1}} + + +% The following are the versions for bonusgradetable: +\def\bhqword#1{\def\@bhqword{#1}} +\def\bhpword#1{\def\@bhpword{#1}} +\def\bhsword#1{\def\@bhsword{#1}} +\def\bhtword#1{\def\@bhtword{#1}} +\def\bhpgword#1{\def\@bhpgword{#1}} + +\def\bvqword#1{\def\@bvqword{#1}} +\def\bvpword#1{\def\@bvpword{#1}} +\def\bvsword#1{\def\@bvsword{#1}} +\def\bvtword#1{\def\@bvtword{#1}} +\def\bvpgword#1{\def\@bvpgword{#1}} + +% The following are the versions for combinedgradetable: +\def\chqword#1{\def\@chqword{#1}} +\def\chpword#1{\def\@chpword{#1}} +\def\chbpword#1{\def\@chbpword{#1}} +\def\chsword#1{\def\@chsword{#1}} +\def\chtword#1{\def\@chtword{#1}} +\def\chpgword#1{\def\@chpgword{#1}} + +\def\cvqword#1{\def\@cvqword{#1}} +\def\cvpword#1{\def\@cvpword{#1}} +\def\cvbpword#1{\def\@cvbpword{#1}} +\def\cvsword#1{\def\@cvsword{#1}} +\def\cvtword#1{\def\@cvtword{#1}} +\def\cvpgword#1{\def\@cvpgword{#1}} + + % Initialize: \cellwidth{2em} \gradetablestretch{1.5} + +\hqword{Question:} +\hpgword{Page:} \hpword{Points:} \hsword{Score:} \htword{Total} \vpword{Points} \vsword{Score} \vtword{Total:} - -% For tables indexed by question number: \vqword{Question} -\hqword{Question:} - -% For tables indexed by page number: \vpgword{Page} -\hpgword{Page:} +\bhqword{Question:} +\bhpgword{Page:} +\bhpword{Bonus Points:} +\bhsword{Score:} +\bhtword{Total} +\bvqword{Question} +\bvpgword{Page} +\bvpword{Bonus Points} +\bvsword{Score} +\bvtword{Total:} + +\chqword{Question:} +\chpgword{Page:} +\chpword{Points:} +\chbpword{Bonus Points:} +\chsword{Score:} +\chtword{Total} +\cvqword{Question} +\cvpgword{Page} +\cvpword{Points} +\cvbpword{Bonus Points} +\cvsword{Score} +\cvtword{Total:} + +% Before we created multirow and multicolumn tables, he only commands +% here accessible to the user were \gradetable, \bonusgradetable, +% \combinedgradetable, \pointtable, \bonuspointtable, +% \combinedpointtable, \partialgradetable, +% \partialbonusgradetable, \partialcombinedtable, \partialpointtable, +% \partialbonuspointtable, \partialcombinedpointtable, +% \begingradingrange, \endgradingrange, \pointsinrange, +% \bonuspointsinrange, \firstqinrange, \lastqinrange, and +% \numqinrange. The new user commands are +% +% \def\multirowgradetable +% \def\multirowpointtable +% \def\multirowbonusgradetable +% \def\multirowbonuspointtable +% \def\multirowcombinedgradetable +% \def\multirowcombinedpointtable +% +% \def\multirowpartialgradetable +% \def\multirowpartialpointtable +% \def\multirowpartialbonusgradetable +% \def\multirowpartialbonuspointtable +% \def\multirowpartialcombinedgradetable +% \def\multirowpartialcombinedpointtable +% +% \def\multicolumngradetable +% \def\multicolumnpointtable +% \def\multicolumnbonusgradetable +% \def\multicolumnbonuspointtable +% \def\multicolumncombinedgradetable +% \def\multicolumncombinedpointtable +% +% \def\multicolumnpartialgradetable +% \def\multicolumnpartialpointtable +% \def\multicolumnpartialbonusgradetable +% \def\multicolumnpartialbonuspointtable +% \def\multicolumnpartialcombinedgradetable +% \def\multicolumnpartialcombinedpointtable -% The only command here accessible to the user is \gradetable. % The possibilities are % \gradetable[v][questions] @@ -3868,385 +5112,2412 @@ % \gradetable[h][questions] % \gradetable[h][pages] -% If one or both optional arguments are omitted, the defaults are `[v]' -% and `[questions]'. +% \bonusgradetable[v][questions] +% \bonusgradetable[v][pages] +% \bonusgradetable[h][questions] +% \bonusgradetable[h][pages] + +% \combinedgradetable[v][questions] +% \combinedgradetable[v][pages] +% \combinedgradetable[h][questions] +% \combinedgradetable[h][pages] + +% \pointtable[v][questions] +% \pointtable[v][pages] +% \pointtable[h][questions] +% \pointtable[h][pages] + +% \bonuspointtable[v][questions] +% \bonuspointtable[v][pages] +% \bonuspointtable[h][questions] +% \bonuspointtable[h][pages] + +% \combinedpointtable[v][questions] +% \combinedpointtable[v][pages] +% \combinedpointtable[h][questions] +% \combinedpointtable[h][pages] + +% \partialgradetable{whatever}[v][questions] +% \partialgradetable{whatever}[v][pages] +% \partialgradetable{whatever}[h][questions] +% \partialgradetable{whatever}[h][pages] + +% \partialbonusgradetable{whatever}[v][questions] +% \partialbonusgradetable{whatever}[v][pages] +% \partialbonusgradetable{whatever}[h][questions] +% \partialbonusgradetable{whatever}[h][pages] + +% \partialcombinedgradetable{whatever}[v][questions] +% \partialcombinedgradetable{whatever}[v][pages] +% \partialcombinedgradetable{whatever}[h][questions] +% \partialcombinedgradetable{whatever}[h][pages] + +% \partialpointtable{whatever}[v][questions] +% \partialpointtable{whatever}[v][pages] +% \partialpointtable{whatever}[h][questions] +% \partialpointtable{whatever}[h][pages] + +% \partialbonuspointtable{whatever}[v][questions] +% \partialbonuspointtable{whatever}[v][pages] +% \partialbonuspointtable{whatever}[h][questions] +% \partialbonuspointtable{whatever}[h][pages] + +% \partialcombinedpointtable{whatever}[v][questions] +% \partialcombinedpointtable{whatever}[v][pages] +% \partialcombinedpointtable{whatever}[h][questions] +% \partialcombinedpointtable{whatever}[h][pages] + +% \begingradingrange{whatever} +% \endgradingrange{whatever} +% +% \pointsinrange{whatever} +% \bonuspointsinrange{whatever} +% +% \firstqinrange{whatever} +% \lastqinrange{whatever} +% \numqinrange{whatever} +% +% where ``whatever'' is a label chosen by the user. +% +% \def\multirowgradetable{numcols}[questions or pages] +% \def\multirowpointtable{numcols}[questions or pages] +% \def\multirowbonusgradetable{numcols}[questions or pages] +% \def\multirowbonuspointtable{numcols}[questions or pages] +% \def\multirowcombinedgradetable{numcols}[questions or pages] +% \def\multirowcombinedpointtable{numcols}[questions or pages] +% +% \def\multirowpartialgradetable{numcols}{rangename}[questions or pages] +% \def\multirowpartialpointtable{numcols}{rangename}[questions or pages] +% \def\multirowpartialbonusgradetable{numcols}{rangename}[questions or pages] +% \def\multirowpartialbonuspointtable{numcols}{rangename}[questions or pages] +% \def\multirowpartialcombinedgradetable{numcols}{rangename}[questions or pages] +% \def\multirowpartialcombinedpointtable{numcols}{rangename}[questions or pages] +% +% \def\multicolumngradetable{numrows}[questions or pages] +% \def\multicolumnpointtable{numrows}[questions or pages] +% \def\multicolumnbonusgradetable{numrows}[questions or pages] +% \def\multicolumnbonuspointtable{numrows}[questions or pages] +% \def\multicolumncombinedgradetable{numrows}[questions or pages] +% \def\multicolumncombinedpointtable{numrows}[questions or pages] + +% \def\multicolumnpartialgradetable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialpointtable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialbonusgradetable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialbonuspointtable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedgradetable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedpointtable{numrows}{rangename}[questions or pages] + + +% If one or both optional arguments are omitted, the defaults are +% `[v]' and `[questions]'. + +% \@scorestrue means we're doing \gradetable +% \@scoresfalse mans we're doing \pointtable +\newif\if@scores + +% \@partialtrue means we're doing \partialgradetable, +% \partialbonusgradetable, \partialcombinedgradetable, +% \partialpointtable, \partialbonuspointtable, or +% \partialcombinedpointtable: +\newif\if@partial + +% \@combinedtrue means we're doing \combinedgradetable, +% \combinedpointtable, \partialcombinedgradetable, or +% \partialcombinedpointtable: +\newif\if@combined + +% It's OK to use the counter num@cols as a scratch counter +% in \begingradingrange and \endgradingrange because +% it's only used in typesetting tables: +\def\begingradingrange#1{% + \setcounter{num@cols}{\value{question}}% + \addtocounter{num@cols}{1}% + \immediate\write\@mainaux + {\string\expandafter\string\gdef + \string\csname\space range@#1@firstq\string\endcsname + {\arabic{num@cols}}}% + \write\@mainaux + {\string\expandafter\string\gdef + \string\csname\space range@#1@firstp\string\endcsname + {\thepage}}% +}% begingradingrange + +\def\endgradingrange#1{% + \setcounter{num@cols}{\value{question}}% + \immediate\write\@mainaux + {\string\expandafter\string\gdef + \string\csname\space range@#1@lastq\string\endcsname + {\arabic{num@cols}}}% + \write\@mainaux + {\string\expandafter\string\gdef + \string\csname\space range@#1@lastp\string\endcsname + {\thepage}}% +}% endgradingrange + + +% Now that grading tables may be for only part of the exam, +% we need the counter tbl@points to add up the total points +% for the questions (or pages) that appear on the table: +\new@hlfcntr{tbl@points} + +% We'll use the counter tbl@bonuspoints to add up the total bonus +% points for the questions (or pages) that appear on the table: +\new@hlfcntr{tbl@bonuspoints} + +%-------------------------------------------------------------------- +% multirow tables, non-partial: + +\def\multirowgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowbonusgradetable#1{% + \@scorestrue + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowbonuspointtable#1{% + \@scoresfalse + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowcombinedgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowcombinedpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +%-------------------------------------------------------------------- +% multirow tables, partial: + +\def\multirowpartialgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialbonusgradetable#1#2{% + \@scorestrue + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialbonuspointtable#1#2{% + \@scoresfalse + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialcombinedgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialcombinedpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +%-------------------------------------------------------------------- +% multicolumn tables, non-partial: + +\def\multicolumngradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnbonusgradetable#1{% + \@scorestrue + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnbonuspointtable#1{% + \@scoresfalse + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumncombinedgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumncombinedpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +%-------------------------------------------------------------------- +% multicolumn tables, partial: + +\def\multicolumnpartialgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialbonusgradetable#1#2{% + \@scorestrue + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialbonuspointtable#1#2{% + \@scoresfalse + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialcombinedgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialcombinedpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +%-------------------------------------------------------------------- +% partial single row (and column) tables: + +\def\partialgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% partialgradetable + +\def\partialbonusgradetable#1{% + \@scorestrue + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% partialbonusgradetable + +\def\partialcombinedgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% partialcombinedgradetable + +\def\partialpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% partialpointtable + +\def\partialbonuspointtable#1{% + \@scoresfalse + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% partialbonuspointtable + +\def\partialcombinedpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% partialcombinedpointtable + +%-------------------------------------------------------------------- +% single row (and column) tables, non-partial: \def\gradetable{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: \@ifnextchar[{\i@gtable}{\i@gtable[v]}% -} -\def\i@gtable[#1]{% +}% gradetable + +\def\bonusgradetable{% + \@scorestrue + \@bonustrue + \@partialfalse + \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% bonusgradetable + +\def\combinedgradetable{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedtrue + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% bonusgradetable + +\def\pointtable{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% pointtable + +\def\bonuspointtable{% + \@scoresfalse + \@bonustrue + \@partialfalse + \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument - % choosing between questions and pages, + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% bonuspointtable + +\def\combinedpointtable{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedtrue + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% + % If the user doesn't include the optional argument + % choosing between vertical and horizontal, + % we give them vertical: + \@ifnextchar[{\i@gtable}{\i@gtable[v]}% +}% bonuspointtable + +%-------------------------------------------------------------------- + +% \i@gtable and \ii@gtable insert any missing optional arguments +% (the defaults being [v] and [questions]) and then make sure +% that the user said \addpoints and that this isn't the +% first run of LaTeX. +% \find@p@or@q@range then branches, depending on whether the user +% selected [questions] or [pages]. + +\def\i@gtable[#1]{% + % If the user doesn't include the second optional argument, + % which chooses between questions and pages, % we give them questions: \@ifnextchar[{\ii@gtable{#1}}{\ii@gtable{#1}[questions]}% } \def\ii@gtable#1[#2]{% + % We get here from \i@gtable. + % We make sure the user said \addpoints, and then make sure + % that this isn't the first run of LaTeX (by checking that + % \exam@numpoints is defined). If both of those are OK, + % we go to \find@p@or@q@range to see whether we're doing a table + % indexed by questions or by pages. \if@addpoints \@ifundefined{exam@numpoints}% {\ClassWarning{exam}% {% - You must run LaTeX again to produce the grade table. - \MessageBreak + You must run LaTeX again to produce the + table.\MessageBreak }% \fbox{Run \LaTeX{} again to produce the table}% }% - {\do@gtable{#1}{#2}}% + {\find@p@or@q@range{#1}{#2}}% \else \ClassError{exam}{% You must give the command \protect\addpoints\MessageBreak - \space\space in order to use the command \protect\gradetable - \MessageBreak + \space\space in order to create a grade table.\MessageBreak }{% If you don't give the command \protect\addpoints\MessageBreak \space\space then we're not keeping track of point values. \MessageBreak }% \fi -} +}% ii@gtable + \def\@questionsref{questions} \def\@pagesref{pages} -\def\do@gtable#1#2{% - \begingroup % avoid trouble from using \@temp - \def\@temp{#2}% - \ifx\@temp\@questionsref - \@grdtblquestions{#1}% +\def\find@p@or@q@range#1#2{% + % We get here from \ii@gtable. + % The first argument should be ``v'' or ``h''; + % the second argument should be ``questions'' or ``pages''. + % See whether we're doing a table indexed by + % questions (in which case we go to \find@qrange) or by pages (in + % which case we go to \find@prange): + \begingroup + % We've begun a group that will contain the construction of the + % table, to confine the effect of any \def's that we use. + \def\exam@temp{#2}% + \ifx\exam@temp\@questionsref + \tbl@pgsfalse + \find@qrange{#1}% \else - \ifx\@temp\@pagesref - \@grdtblpages{#1}% + \ifx\exam@temp\@pagesref + \tbl@pgstrue + \find@prange{#1}% \else \ClassError{exam}{% - The second optional argument to \protect\gradetable \MessageBreak - \space \space must be either `questions' or `pages'\MessageBreak + Grade and point tables can be indexed\MessageBreak + \space\space by either `questions' or `pages',\MessageBreak + \space\space but not by `#2'.\MessageBreak }{% - Grade tables can be indexed by questions or pages;\MessageBreak + Grade tables and point tables can be indexed by questions or + pages;\MessageBreak \space\space for others, you're on your own.\MessageBreak }% - \fbox{Error: Grade table: Invalid second optional argument `#1'.}% + \fbox{\textbf{Error:} grade or point table: Invalid argument + `#2' must be `questions' or `pages'.}% \fi \fi \endgroup -} +}% find@p@or@q@range + +% \range@undefined can be called from either \find@qrange or +% \find@prange +\def\range@undefined{% + \fbox{Warning: grading range `\tbl@range ' not defined; + run \LaTeX{} again.}% + \ClassWarning{exam}{% + Grading range `\tbl@range' not defined.\MessageBreak + \space\space Run LaTeX again to produce the table.\MessageBreak + }% +}% range@undefined + %-------------------------------------------------------------------- %-------------------------------------------------------------------- -% Grading tables indexed by question numbers: - -\def\@grdtblquestions#1{% - \if v#1% - \@vgrdtblquestions +% Grade and point tables indexed by question numbers: + +% When we get to \find@qrange, we know we're doing a table indexed by +% question numbers and that this is not the first run of latex. The +% argument is either ``v'' or ``h''. If we're not doing a partial +% table, then \find@qrange sets \tbl@firstq and \first@pq@index to 1 +% and \tbl@lastq and \last@pq@index to \numquestions. Otherwise, +% \find@qrange makes sure the grading range is defined and that its +% last question isn't before its first question. \find@qrange then +% calls \tbl@v@or@h, passing along the argument that is either ``v'' +% or ``h''. + +\def\find@qrange#1{% + % We get here from \find@p@or@q@range. + % We're doing a table indexed by question numbers. + \if@partial + \@ifundefined{range@\tbl@range @firstq}% + {% + \range@undefined + }% + {% + \@ifundefined{range@\tbl@range @lastq}% + {% + \range@undefined + }% + {% + \edef\tbl@firstq{\csname range@\tbl@range @firstq\endcsname}% + \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}% + \let\first@pq@index=\tbl@firstq + \let\last@pq@index=\tbl@lastq + % Check that firstq precedes or equals lastq: + \ifnum \tbl@firstq > \tbl@lastq\relax + \fbox{\textbf{Error:} Grading Range `\tbl@range': + Last question precedes first question.}% + \ClassError{exam}{% + In grading range `\tbl@range', + the last question\MessageBreak + \space\space comes before the first question.\MessageBreak + }{% + \string\begingradingrange \space must precede + \string\endgradingrange \space by at + least one question.\MessageBreak + }% + \else + \tbl@v@or@h{#1}% + \fi + }% + }% \else - \if h#1% - \@hgrdtblquestions + \def\tbl@firstq{1}% + \let\first@pq@index=\tbl@firstq + % \numquestions is always defined, even if this is the first + % run of LaTeX and \exam@numquestions isn't defined. + % If it's the first run of LaTeX, then its value isn't useful, + % but it's never used until a later run (when its value is useful). + \def\tbl@lastq{\numquestions}% + \let\last@pq@index=\tbl@lastq + \tbl@v@or@h{#1}% + \fi +}% find@qrange + +\def\@vref{v} +\def\@href{h} +\def\tbl@v@or@h#1{% + % \first@pq@index=\tbl@firstq or \tbl@firstp and + % \last@pq@index=\tbl@lastq or \tbl@lastp have already been set. + % The argument should be either `v' or `h', and we branch + % accordingly. + \def\exam@temp{#1}% + \ifx\exam@temp\@vref + \check@num@cols@v + \else + \ifx\exam@temp\@href + \check@num@rows@h \else \ClassError{exam}{% - The first optional argument to \protect\gradetable \MessageBreak - \space \space must be either `h' or `v'\MessageBreak + Grade or point table: the argument `#1'\MessageBreak + \space\space must be `v' or `h'. + \MessageBreak }{% - Grade tables can be either horizontal or vertical;\MessageBreak + Grade tables and point tables can be either vertical or + horizontal;\MessageBreak \space\space no diagonals allowed.\MessageBreak }% - \fbox{Error: Grade table: Invalid first optional argument `#1'.}% + \fbox{\textbf{Error:} grade or point table: Invalid argument + `#1' must be `v' or `h'.}% \fi \fi -} +}% tbl@v@or@h %-------------------------------------------------------------------- -% Vertical, indexed by question numbers: +%-------------------------------------------------------------------- +% Grade and point tables indexed by page numbers: -\def\@vgrdtblquestions{% - \begingroup - % Save the current value of question in @iterator, so that - % we cna restore it after doing the table: - \setcounter{@iterator}{\arabic{question}}% - \renewcommand\arraystretch{\@gtblstretch}% - \begin{tabular}{|c|c|c|} - \hline - {\@vqword}& {\@vpword}& {\@vsword}\\ - \hline - \setcounter{question}{0}\do@vloop - {\@vtword}& \numpoints&\hbox to \@cellwidth{\hfill}\\ - \hline + +% The only pages listed are those on which there are a nonzero number +% of points. We check pages \tbl@firstp through \tbl@lastp +% Once we've checked that, e.g., \lastpage@withpoints and +% \pointsonpage@\romannumeral{\lastpage@withpoints} are defined, we +% can safely (we think) check \pointsonpage@\romannumeral{n} for all n +% between \tbl@firstp and \tbl@lastp without generating errors. +% +% Actually: Since we added the notion of half points and half counters +% (a long time ago), there won't be any errors even if +% \pointsonpage@\romannumeral{n} isn't defined, since it's tested by the +% lines: +% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral +% \csname c@@iterator\endcsname\endcsname}% +% \ifhlfcntr@pos{tmp@hlfcntr}% +% and if +% \csname pointsonpage@\romannumeral +% \csname c@@iterator\endcsname\endcsname +% isn't defined, tmp@hlfcntr gets the value zero (because of the way +% that \set@hlfcntr is written). + +% \find@prange makes sure the grading range is defined and that its +% last page isn't before its first page (if it's a partial table). In +% any case, it then sets \tbl@firstp and \tbl@lastp, and calls +% \check@secondrun. + +\def\find@prange#1{% + % We get here from \find@p@or@q@range. + % We're doing a table indexed by pages. + % The argument is either ``v'' or ``h''. + % We first determine the first and last page of the range, storing + % those in \first@pq@index=\tbl@firstp and + % \last@pq@index=\tbl@lastp. If not a partial table, we set + % \first@pq@index=\tbl@firstp to 1 and \last@pq@index=\tbl@lastp to + % the last page with the appropriate points (and so if it's a + % combined table, it's the last page to have either bonus or + % non-bonus points). + % We then call \check@secondrun, passing it the argument that we + % received (i.e., we say \check@secondrun{#1}) to make sure + % we've done at least two runs of latex (so that we'll have the + % information we need about which pages have points on them). + \if@partial + \@ifundefined{range@\tbl@range @firstp}% + {% + \range@undefined + }% + {% + \@ifundefined{range@\tbl@range @lastp}% + {% + \range@undefined + }% + {% + \edef\tbl@firstp{\csname range@\tbl@range @firstp\endcsname}% + \edef\tbl@lastp{\csname range@\tbl@range @lastp\endcsname}% + \let\first@pq@index=\tbl@firstp + \let\last@pq@index=\tbl@lastp + % Check that firstp precedes or equals lastp: + \ifnum \tbl@firstp > \tbl@lastp\relax + \fbox{\textbf{Error:} Grading Range `\tbl@range ': + Last page precedes first page.}% + \ClassError{exam}{% + In grading range `\tbl@range', the last page\MessageBreak + \space\space comes before the first page.\MessageBreak + }{% + \string\begingradingrange \space must precede + \string\endgradingrange.\MessageBreak + }% + \else + \check@secondrun{#1}% + \fi + }% + }% + \else + % It's not a partial table: + \def\tbl@firstp{1}% + \let\first@pq@index=\tbl@firstp + % We never get here on the first run of LaTeX, and + % \lastpage@withbonuspoints is defined on the second and later runs. + \def\tbl@lastp{\lastpage@withpoints}% + \let\last@pq@index=\tbl@lastp + \if@bonus + \def\tbl@lastp{\lastpage@withbonuspoints}% + \let\last@pq@index=\tbl@lastp + \fi + \if@combined + \ifnum \lastpage@withbonuspoints > \lastpage@withpoints\relax + \def\tbl@lastp{\lastpage@withbonuspoints}% + \let\last@pq@index=\tbl@lastp + \fi + \fi + \check@secondrun{#1}% + \fi +}% find@prange + +\def\check@secondrun#1{% + % The function \ii@gtable already made sure that this isn't the + % first run of latex. To do a table indexed by pages, though, we + % have to also make sure it's not the second run of latex. + % We get here from \find@prange; the argument is either ``v'' or + % ``h''. + % Check that there's enough info from the .aux file to do a page + % indexed grade table. If so, call \tbl@v@or@h{#1}: + \@ifundefined{pointsonpage@\romannumeral + \csname lastpage@withpoints\endcsname}% + {\@ifundefined{bonuspointsonpage@\romannumeral + \csname lastpage@withbonuspoints\endcsname}% + {\ClassWarning{exam}{% + You must run LaTeX again to produce the table.\MessageBreak}% + \fbox{Run \LaTeX{} again to produce the table}% + }% + {\tbl@v@or@h{#1}% + }% + }% + {\tbl@v@or@h{#1}% + }% +}% check@secondrun + +%-------------------------------------------------------------------- +% Indexed by pages: + +% For a table indexed by pages, we need to know how many pages there +% are with points on them. The argument to \count@pgswpts should be +% the name of a counter; we set that counter equal to the number of +% pages with the appropriate kind of points. + +\def\count@pgswpts#1{% + % Set the counter #1 equal to the number of pages in the range with + % the appropriate type of points. + % We're called by \@computenumcols@h and \@computenumrows@v. + \setcounter{#1}{0}% + \setcounter{@iterator}{\tbl@firstp}% + \addtocounter{@iterator}{-1}% + \if@bonus + \docount@pgswbpts{#1}% + \else + \if@combined + \docount@pgswcpts{#1}% + \else + \docount@pgswpts{#1}% + \fi + \fi +}% count@pgswpts + +\def\docount@pgswcpts#1{% + % Called by \count@pgswpts + % Count the number of pages in range with any kind of point (bonus + % or non-bonus): + \addtocounter{@iterator}{1}% + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% + \ifhlfcntr@pos{tmp@hlfcntr}% + \addtocounter{#1}{1}% + \else + \check@bnsptpage{#1}% + \fi + \ifnum \the@iterator < \tbl@lastp\relax + \def\nextdocount@pgswcpts{\docount@pgswcpts{#1}}% + \else + \let\nextdocount@pgswcpts=\relax + \fi + \nextdocount@pgswcpts +}% docount@pgswcpts +\def\check@bnsptpage#1{% + % We need to hide this inside of a macro because if \ifhlfcntr@pos + % isn't expanded (because this stuff is being skipped in the outer + % conditional), then TeX doesn't see the \ifnum hidden inside the + % \ifhlfcntr@pos, but it does see the \fi, and so it get confused. + \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% + \ifhlfcntr@pos{tmp@hlfcntr}% + \addtocounter{#1}{1}% + \fi +}% check@bnsptpage + +\def\docount@pgswpts#1{% + % Called by \count@pgswpts. + % Count the number of pages in range with regular points. + \addtocounter{@iterator}{1}% + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% + \ifhlfcntr@pos{tmp@hlfcntr}% + \addtocounter{#1}{1}% + \fi + \ifnum \the@iterator < \tbl@lastp\relax + \def\nextdocount@pgswpts{\docount@pgswpts{#1}}% + \else + \let\nextdocount@pgswpts=\relax + \fi + \nextdocount@pgswpts +}% docount@pgswpts + +\def\docount@pgswbpts#1{% + % Called by \count@pgswpts + % Count the number of pages in range with bonus points. + \addtocounter{@iterator}{1}% + \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% + \ifhlfcntr@pos{tmp@hlfcntr}% + \addtocounter{#1}{1}% + \fi + \ifnum \the@iterator < \tbl@lastp\relax + \def\nextdocount@pgswbpts{\docount@pgswbpts{#1}}% + \else + \let\nextdocount@pgswbpts=\relax + \fi + \nextdocount@pgswbpts +}% docount@pgswbpts + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Multirow horizontal tables, indexed by question numbers: + +\newcounter{pq@index}% In tables indexed by page numbers, it holds a +% page number. In tables indexed by question numbers, it holds a +% question number. + +\newcounter{pq@index@pts}% In horizontal tables, this holds either the +% current page number or the current question number as we put the +% point values for that page or question number into the table. In +% vertical tables, this holds the index for the first column of the +% current row. + +\newcounter{pq@index@bpts}% used to set bonus point values in +% horizontal tables. Often used as scratch elsewhere. + +\def\hidden@ampersand{&}% Needed because an ampersand can't appear in +% the replacement text of a conditional. + +\newif\iftbl@pgs +% \tbl@pgstrue means a table indexed by page numbers +% \tbl@pgsfalse means a table indexed by question numbers + +\newcounter{num@cols} +\newcounter{num@rows} +\newcounter{current@row} +\newcounter{cols@done}% Holds the number of columns done in the +% current row. + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Stuff to unify tables indexed by questions and tables indexed by +% pages: + +% \first@pq@index and \last@pq@index will hold either \tbl@firstq and +% \tbl@lastq or \tbl@firstp and \tbl@lastp. + +\def\increment@index#1{% + % If we're doing a table indexed by question numbers, we increment + % the counter #1. + % If we're doing a table indexed by page numbers, + % we increase the counter #1 by at least 1 to either the number of the + % next page containing the appropriate kind of points, or to + % something greater than \tbl@lastp. + \iftbl@pgs + \find@nextpagewithpoints{#1}% + \else + \addtocounter{#1}{1}% + \fi +}% increment@index + +\def\nextcolumn@index@v#1{% + % Used only for multicolumn tables. + % If we're doing a table indexed by question numbers, we increase + % the counter #1 by num@cols. + % If we're doing a table indexed by page numbers, + % we use \find@nextcolumnpage@v to increment the counter #1 to either + % the (num@rows)'th page number after #1 that contains the + % appropriate kind of points or to a value greater than \tbl@lastp. + \iftbl@pgs + \find@nextcolumnpage@v{#1}% + \else + \addtocounter{#1}{\value{num@rows}}% + \fi +}% nextcolumn@index@v + +\def\pointsof@index#1{% + \iftbl@pgs + \pointsonpage{\arabic{#1}}% + \else + \pointsofquestion{\arabic{#1}}% + \fi +}% pointsof@index + +\def\bonuspointsof@index#1{% + \iftbl@pgs + \bonuspointsonpage{\arabic{#1}}% + \else + \bonuspointsofquestion{\arabic{#1}}% + \fi +}% bonuspointsof@index + +\def\refto@index#1{% + \iftbl@pgs + \if@combined + % Need to hide this inside of a macro: + \refto@comb@index{#1}% + \else + \if@bonus + \pageref{firstbonuspoints@onpage@\arabic{#1}}% + \else + \pageref{firstpoints@onpage@\arabic{#1}}% + \fi + \fi + \else + \ref{question@\arabic{#1}}% + \fi +}% refto@index + +\def\refto@comb@index#1{% + % We're called only by \refto@index. + % We can't have the \ifhlfcntr@pos...\fi inside of another + % conditional, so we're hiding it in this macro. + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\arabic{#1}}}% + \ifhlfcntr@pos{tmp@hlfcntr}% + \pageref{firstpoints@onpage@\arabic{#1}}% + \else + % In theory, there *must* be bonus points on this page, because + % there aren't any plain points, but there are allegedly *some* + % points. We're being brave and assuming that's correct, and not + % checking (which we'd have to hide inside a macro, because it + % would use \ifhlfcntr@pos): + \pageref{firstbonuspoints@onpage@\arabic{#1}}% + \fi +}% refto@comb@index + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Multirow tables: + + +%-------------------------------------------------------------------- +% Check that the number of rows is OK, and compute the number of +% columns: + +\def\check@num@rows@h{% + % We get here from \tbl@v@or@h. + % We make sure the number of rows is a positive integer. If it + % is, we go on to \@computenumcols@h + \ifnum \value{num@rows} < 1\relax + \ClassError{exam}{% + The number of rows in a table must be positive.\MessageBreak + }{% + The number of rows must be a positive integer.\MessageBreak + }% + \fbox{\textbf{Error:} Multirow table with no rows!}% + \else + \@computenumcols@h + \fi +}% check@num@rows@h + +\def\@computenumcols@h{% + % We get here from \check@num@rows@h. + % Compute the number of columns. + % First: set num@cols to one more than either (the number of pages + % with the appropriate type of points) or (the number of questions), + % to have slots for the total along with the questions: + \iftbl@pgs + \count@pgswpts{num@cols}% + \addtocounter{num@cols}{1}% + \else + \setcounter{num@cols}{\tbl@lastq}% + \addtocounter{num@cols}{-\tbl@firstq}% + \addtocounter{num@cols}{2}% + \fi + % Save the number of slots needed in pq@index (used for scratch), to + % check for truncation: + \setcounter{pq@index}{\value{num@cols}}% + % Divide the number of slots needed by num@rows: + \divide \csname c@num@cols\endcsname by + \csname c@num@rows\endcsname + % Division truncates: See if there was truncation. + % Use @iterator as a scratch counter. + \setcounter{@iterator}{\value{num@cols}}% + \multiply \csname c@@iterator\endcsname by + \csname c@num@rows\endcsname + \ifnum \value{@iterator} < \value{pq@index}\relax + % There was truncation; add a column to num@cols: + \addtocounter{num@cols}{1}% + \fi + \@multirowtable +}% @computenumcols@h + +%-------------------------------------------------------------------- +% Construct the actual table: + +\def\@multirowtable{% + % We get here from \@computenumcols@h. + % All multirow tables! + \renewcommand\arraystretch{\@gtblstretch}% + \set@hlfcntr{tbl@points}{0}% + \set@hlfcntr{tbl@bonuspoints}{0}% + \setcounter{pq@index}{\first@pq@index}% + \addtocounter{pq@index}{-1}% + \setcounter{pq@index@pts}{\value{pq@index}}% + \setcounter{pq@index@bpts}{\value{pq@index}}% + \setcounter{current@row}{0}% + \begin{tabular}{|l|*{\value{num@cols}}{c|}} + \hline + \if@combined + \do@comblines@h + \else + \do@lines@h + \fi +}% @multirowtable + + +\def\do@lines@h{% + % Called only by \@multirowtable. + % It's either bonus or regular, but not combined: + \addtocounter{current@row}{1}% Set to the number of the current row + \iftbl@pgs + \if@bonus + \@bhpgword + \else + \@hpgword + \fi + \else + \if@bonus + \@bhqword + \else + \@hqword + \fi + \fi + \setcounter{cols@done}{0}% + \do@pq@indexloop@h + % When we finish \do@pq@indexloop@h, either we've finished a + % complete row of page numbers (or questions), or we've done all + % the page numbers (or questions) through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the page or question numbers, and there's + % room remaining on the current line for \@htword (or \@bhtword): + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@htword@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + \\ + \hline + % Point values go here! + \setcounter{cols@done}{0}% + \if@bonus + \@bhpword + \do@bptloop@h + \else + \@hpword + \do@ptloop@h + \fi + % When we finish \do@ptloop@h or \do@bptloop@h, either + % we've finished a complete row of point values, or we've done all + % the question (or page) numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the point values, and there's room + % remaining on the current line for Total Points: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \if@bonus + \do@totalbpts@h + \else + \do@totalpts@h + \fi + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + % We hold off on putting in the "\\ \hline" because we may want to + % immediately follow it with either an "\end{tabular}" or another + % "\hline". + % Scores? + \if@scores + \\ + \hline + \if@bonus + \@bhsword \hidden@ampersand + \else + \@hsword \hidden@ampersand + \fi + \setcounter{cols@done}{0}% + \do@sloop@h + \fi + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last line! End the tabular: + \\ + \hline \end{tabular}% - % Restore the saved value of question: - \setcounter{question}{\arabic{@iterator}}% - \endgroup -} -\def\do@vloop{% - \addtocounter{question}{1}% - \thequestion & \pointsofquestion{\arabic{question}}&\\ + \else + % Don't end the tabular: + \\ + \hline\hline + \fi + % Check if we should repeat: + \ifnum \value{current@row} < \value{num@rows}\relax + \let\nextdo@lines@h=\do@lines@h + \else + \let\nextdo@lines@h=\relax + \fi + \nextdo@lines@h +}% do@lines@h + +\def\do@comblines@h{% + % Called only by \@multirowtable. + % Combined tables. + \addtocounter{current@row}{1}% Set to the number of the current row + \iftbl@pgs + \@chpgword + \else + \@chqword + \fi + \setcounter{cols@done}{0}% + \do@pq@indexloop@h + % When we finish \do@pq@indexloop@h, either we've finished a + % complete row of page (or question) numbers, or we've done all + % the page (or question) numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the question (or page) numbers, and there's + % room remaining on the current line for \@chtword: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@htword@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + \\ \hline - \ifnum \arabic{question} < \numquestions\relax - \let\next@vloop=\do@vloop + % Point values go here! + \@chpword + \setcounter{cols@done}{0}% + \do@ptloop@h + % When we finish \do@ptloop@h, either we've finished a complete + % row of point values, or we've done all the question (or page) + % numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the point values, and there's room + % remaining on the current line for Total Points: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@totalpts@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + \\ + \hline + % Bonus point values go here! + \@chbpword + \setcounter{cols@done}{0}% + \do@bptloop@h + % When we finish \do@bptloop@h, either + % we've finished a complete row of point values, or we've done all + % the question (or page) numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the point values, and there's room + % remaining on the current line for Total Points: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@totalbpts@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + % We hold off on putting in the "\\ \hline" because we may want to + % immediately follow it with either an "\end{tabular}" or another + % "\hline". + % Scores? + \if@scores + \\ + \hline + \@chsword \hidden@ampersand + \setcounter{cols@done}{0}% + \do@sloop@h + \fi + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last line! End the tabular: + \\ + \hline + \end{tabular}% + \else + % Don't end the tabular: + \\ + \hline\hline + \fi + % Check if we should repeat: + \ifnum \value{current@row} < \value{num@rows}\relax + \let\nextdo@comblines@h=\do@comblines@h + \else + \let\nextdo@comblines@h=\relax + \fi + \nextdo@comblines@h +}% do@comblines@h + +\def\do@pq@indexloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert at most one row of pq@index: + \increment@index{pq@index}% + \ifnum \value{pq@index} > \last@pq@index\relax + % Do nothing! + \else + \hidden@ampersand + \refto@index{pq@index}% + \addtocounter{cols@done}{1}% + \fi + \ifnum \value{pq@index} < \last@pq@index\relax + \ifnum \value{cols@done} < \value{num@cols}\relax + \let\nextdo@pq@indexloop@h=\do@pq@indexloop@h + \else + \let\nextdo@pq@indexloop@h=\relax + \fi + \else + \let\nextdo@pq@indexloop@h=\relax + \fi + \nextdo@pq@indexloop@h +}% do@pq@indexloop@h + +\def\do@ptloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert at most one row of non-bonus point values: + \increment@index{pq@index@pts}% + \ifnum \value{pq@index@pts} > \last@pq@index\relax + % Do nothing! + \else + \hidden@ampersand + \addtocounter{cols@done}{1}% + \pointsof@index{pq@index@pts}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index@pts}}% + \fi + \ifnum \value{pq@index@pts} < \last@pq@index\relax + \ifnum \value{cols@done} < \value{num@cols}\relax + \let\nextdo@ptloop@h=\do@ptloop@h + \else + \let\nextdo@ptloop@h=\relax + \fi + \else + \let\nextdo@ptloop@h=\relax + \fi + \nextdo@ptloop@h +}% do@ptloop@h + +\def\do@bptloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert at most one row of bonus point values: + \increment@index{pq@index@bpts}% + \ifnum \value{pq@index@bpts} > \last@pq@index\relax + % Do nothing! + \else + \hidden@ampersand + \addtocounter{cols@done}{1}% + \bonuspointsof@index{pq@index@bpts}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index@bpts}}% + \fi + \ifnum \value{pq@index@bpts} < \last@pq@index\relax + \ifnum \value{cols@done} < \value{num@cols}\relax + \let\nextdo@bptloop@h=\do@bptloop@h + \else + \let\nextdo@bptloop@h=\relax + \fi + \else + \let\nextdo@bptloop@h=\relax + \fi + \nextdo@bptloop@h +}% do@bptloop@h + +\def\do@htword@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert (\value{num@cols} - \value{cols@done}) ampersands, + % and then either \@htword or \@bhtword or \@chtword: + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \if@combined + \@chtword + \else + \if@bonus + \@bhtword + \else + \@htword + \fi + \fi +}% do@htword@h + +\def\do@totalpts@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert (\value{num@cols} - \value{cols@done}) ampersands + % and then the total points: + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \prt@tablepoints +}% do@totalpts@h + +\def\do@totalbpts@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert (\value{num@cols} - \value{cols@done}) ampersands, + % and then the total bonus points: + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \prt@tablebonuspoints +}% do@totalbpts@h + +\def\do@emptycols@h{% + % Called by \do@lines@h, \do@comblines@h, \do@htword@h, + % \do@totalpts@h, and \do@totalbpts@h. + % We insert \value{@iterator} ampersands: + \ifnum \value{@iterator} > 0\relax + \hidden@ampersand + \addtocounter{@iterator}{-1}% + \let\nextdo@emptycols@h=\do@emptycols@h + \else + \let\nextdo@emptycols@h=\relax + \fi + \nextdo@emptycols@h +}% do@emptycols@h + +\def\do@sloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We assume that cols@done has been set to zero. + % We insert num@cols \hbox to \@cellwidth, + % separated by ampersands. + \addtocounter{cols@done}{1}% + \hbox to \@cellwidth{\hfill}% + \ifnum \value{cols@done} < \value{num@cols}\relax + \hidden@ampersand + \let\nextdo@sloop@h=\do@sloop@h + \else + \let\nextdo@sloop@h=\relax + \fi + \nextdo@sloop@h +}% do@sloop@h + + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Multicolumn tables + + +%-------------------------------------------------------------------- +% Here's an example of a multicolumn grade table indexed by questions. + +% Every line of \cline's is followed by a +% \noalign{\vskip\arrayrulewidth} to cancel the +% \noalign{\vskip-\arrayrulewidth} that ends the definition of +% \cline. + +% \begin{tabular}{*2{|c|c|c|c}} +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% Question% +% & Points% +% & Score% +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & Question% +% & Points% +% & Score% +% \\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% 1% +% & 5% +% & \hbox to \@cellwidth{\hfill}% +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & 4% +% & 20% +% & \hbox to \@cellwidth{\hfill}% +% \\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% 2% +% & 10% +% & \hbox to \@cellwidth{\hfill}% +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & 5% +% & 25% +% & \hbox to \@cellwidth{\hfill}% +% \\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% 3% +% & 15% +% & \hbox to \@cellwidth{\hfill}% +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & Total:% +% & 75% +% & \hbox to \@cellwidth{\hfill}% +% \\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \end{tabular} +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Check that the number of cols is OK, and compute the number of rows: + +\def\check@num@cols@v{% + % We get here from \tbl@v@or@h. + % We make sure the number of cols is between 1 and 10 (since we + % can't handle more than 10 cols in a multicolumn table). + % If it is, we go on to \@computenumrows@v + \ifnum \value{num@cols} < 1\relax + \ClassError{exam}{% + The number of columns in a table must be positive.\MessageBreak + }{% + The number of columns must be a positive integer.\MessageBreak + }% + \fbox{\textbf{Error:} Multicolumn table with no columns!}% + \else + \ifnum \value{num@cols} > 10\relax + \ClassError{exam}{% + Multicolumn tables can have at most 10 columns.\MessageBreak + }{% + Multicolumn tables can have at most 10 columns.\MessageBreak + }% + \fbox{\textbf{Error:} Multicolumn table with more than 10 columns!}% + \else + \@computenumrows@v + \fi + \fi +}% check@num@cols@v + +\def\@computenumrows@v{% + % We get here from \check@num@cols@v. + % Compute the number of rows. + % First: set num@rows to one more than the number of either + % (questions) or (pages with the appropriate type of points), to + % have slots for the total along with the questions or page numbers: + \iftbl@pgs + \count@pgswpts{num@rows}% + \addtocounter{num@rows}{1}% + \else + \setcounter{num@rows}{\last@pq@index}% + \addtocounter{num@rows}{-\first@pq@index}% + \addtocounter{num@rows}{2}% + \fi + % Save the number of slots needed, using pq@index@bpts as a scratch + % counter, to check for truncation on division: + \setcounter{pq@index@bpts}{\value{num@rows}}% + % Divide the number of slots needed by num@cols: + \divide \csname c@num@rows\endcsname by + \csname c@num@cols\endcsname + % Division truncates: See if there was truncation. + % Use the counter @iterator as a scratch counter: + \setcounter{@iterator}{\value{num@rows}}% + \multiply \csname c@@iterator\endcsname by + \csname c@num@cols\endcsname + \ifnum \value{@iterator} < \value{pq@index@bpts}\relax + % There was truncation; add one to num@rows: + \addtocounter{num@rows}{1}% + \fi + \@multicolumntable +}% @computenumrows@v + +%-------------------------------------------------------------------- +% Construct the actual table: + +\def\@multicolumntable{% + % We get here from \@computenumrows@v. + % Set \cline@stuff@v equal to the line of \cline's: + \create@cline@stuff@v + \renewcommand\arraystretch{\@gtblstretch}% + \set@hlfcntr{tbl@points}{0}% + \set@hlfcntr{tbl@bonuspoints}{0}% + \if@combined + \if@scores + % combinedgradetable, possibly partial. + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{|c|c|c|c|c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@comb@v + \\ + \cline@stuff@v + \else + % combinedpointtable, possibly partial. + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{|c|c|c|c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@comb@noscores@v + \\ + \cline@stuff@v + \fi + % pq@index@pts will hold the question number (or page number) in + % the first column of the row. + \setcounter{pq@index@pts}{\first@pq@index}% + \iftbl@pgs + % If we're indexed by pages, we need to make sure there are + % points of the appropriate type on the first page listed: + \addtocounter{pq@index@pts}{-1}% + \find@nextpagewithpoints{pq@index@pts}% + \fi + \setcounter{current@row}{0}% + \do@lines@v + \else + % It's not combined: + \if@scores + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{|c|c|c|c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@v + \\ + \cline@stuff@v + \else + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{|c|c|c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@noscores@v + \\ + \cline@stuff@v + \fi + % pq@index@pts will hold the question number (or page number) in + % the first column of the row. + \setcounter{pq@index@pts}{\first@pq@index}% + \iftbl@pgs + % If we're indexed by pages, we need to make sure there are + % points of the appropriate type on the first page listed: + \addtocounter{pq@index@pts}{-1}% + \find@nextpagewithpoints{pq@index@pts}% + \fi + \setcounter{current@row}{0}% + \do@lines@v + \fi +}% @multicolumntable + +%-------------------------------------------------------------------- +% \create@cline@stuff@v + +% The function \create@cline@stuff@v defines \cline@stuff@v to be whatever's +% appropriate given the values of num@cols, \if@bonus, \if@combined, and +% \if@scores. + +% We wimped out of generating \cline@stuff@v on the fly because we didn't +% see how to get the correct expansions/nonexpansions without using a +% primitive of e-TeX. + +% \clines@ii@whatever is for tables in which a logical column consists +% of two columns; it's used for pointtable and bonuspointtable. + +\def\clines@ii@i{\cline{1-2}} +\def\clines@ii@ii{\cline{1-2} \cline{4-5}} +\def\clines@ii@iii{\cline{1-2} \cline{4-5} \cline{7-8}} +\def\clines@ii@iv{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}} +\def\clines@ii@v{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14}} +\def\clines@ii@vi{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17}} +\def\clines@ii@vii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20}} +\def\clines@ii@viii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}} +\def\clines@ii@vix{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23} + \cline{25-26}} +\def\clines@ii@x{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23} + \cline{25-26} \cline{28-29}} + +% \clines@iii@whatever is for tables in which a logical column consists +% of three columns; it's are used for gradetable, bonusgradetable, and +% combinedpointtable: + +\def\clines@iii@i{\cline{1-3}} +\def\clines@iii@ii{\cline{1-3} \cline{5-7}} +\def\clines@iii@iii{\cline{1-3} \cline{5-7} \cline{9-11}} +\def\clines@iii@iv{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}} +\def\clines@iii@v{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19}} +\def\clines@iii@vi{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23}} +\def\clines@iii@vii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27}} +\def\clines@iii@viii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}} +\def\clines@iii@ix{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31} + \cline{33-35}} +\def\clines@iii@x{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31} + \cline{33-35} \cline{37-39}} + + +% \clines@iv@whatever is for tables in which a logical column +% consists of four columns; it's used for combinedgradetable. + +\def\clines@iv@i{\cline{1-4}} +\def\clines@iv@ii{\cline{1-4} \cline{6-9}} +\def\clines@iv@iii{\cline{1-4} \cline{6-9} \cline{11-14}} +\def\clines@iv@iv{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}} +\def\clines@iv@v{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24}} +\def\clines@iv@vi{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29}} +\def\clines@iv@vii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34}} +\def\clines@iv@viii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}} +\def\clines@iv@ix{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39} + \cline{41-44}} +\def\clines@iv@x{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39} + \cline{41-44} \cline{46-49}} + +% The definition of \cline ends with \noalign{\vskip-\arrayrulewidth}, +% and so we want to throw in a \noalign{\vskip\arrayrulewidth} to +% cancel that. +\def\cline@correction{\noalign{\vskip\arrayrulewidth}} + +\def\create@cline@stuff@v{% + % Called by \@multicolumntable. + \if@combined + \if@scores + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@iv@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \else + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@iii@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \fi + \else + \if@scores + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@iii@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \else + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@ii@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \fi + \fi +}% create@cline@stuff@v + +%-------------------------------------------------------------------- +% The various \docolumn@heads@something@v + +\def\docolumn@heads@v{% + % Called by \@multicolumntable. + % multicolumngradetable or multicolumnbonusgradetable, possibly + % partial. + \iftbl@pgs + \if@bonus + \@bvpgword + \else + \@vpgword + \fi + \else + \if@bonus + \@bvqword + \else + \@vqword + \fi + \fi + & \if@bonus + \@bvpword + \else + \@vpword + \fi + & \if@bonus + \@bvsword + \else + \@vsword + \fi + \addtocounter{@iterator}{1}% + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@v=\docolumn@heads@v + \else + \let\nextdocolumn@heads@v=\relax + \fi + \nextdocolumn@heads@v +}% docolumn@heads@v + +\def\docolumn@heads@noscores@v{% + % Called by \@multicolumntable. + % multicolumnpointtable or multicolumnbonuspointtable, possibly + % partial. + \iftbl@pgs + \if@bonus + \@bvpgword + \else + \@vpgword + \fi + \else + \if@bonus + \@bvqword + \else + \@vqword + \fi + \fi + & + \if@bonus + \@bvpword + \else + \@vpword + \fi + \addtocounter{@iterator}{1}% + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@noscores@v=\docolumn@heads@noscores@v + \else + \let\nextdocolumn@heads@noscores@v=\relax + \fi + \nextdocolumn@heads@noscores@v +}% docolumn@heads@noscores@v + +\def\docolumn@heads@comb@v{% + % Called by \@multicolumntable. + % multicolumncombinedgradetable, possibly partial. + \iftbl@pgs + \@cvpgword + \else + \@cvqword + \fi + & \@cvpword + & \@cvbpword + & \@cvsword + \addtocounter{@iterator}{1}% + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@comb@v=\docolumn@heads@comb@v + \else + \let\nextdocolumn@heads@comb@v=\relax + \fi + \nextdocolumn@heads@comb@v +}% docolumn@heads@comb@v + +\def\docolumn@heads@comb@noscores@v{% + % Called by \@multicolumntable. + % multicolumncombinedpointtable, possibly partial. + \iftbl@pgs + \@cvpgword + \else + \@cvqword + \fi + & \@vpword + & \@bvpword + \addtocounter{@iterator}{1}% + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@comb@noscores@v=\docolumn@heads@comb@noscores@v \else - \let\next@vloop=\relax + \let\nextdocolumn@heads@comb@noscores@v=\relax \fi - \next@vloop -} + \nextdocolumn@heads@comb@noscores@v +}% docolumn@heads@comb@noscores@v %-------------------------------------------------------------------- -% Horizontal, indexed by question numbers: - -\def\@hgrdtblquestions{% - \begingroup - % Save the current value of question in @iterator, so that - % we can restore it after doing the table: - \setcounter{@iterator}{\arabic{question}}% - \renewcommand\arraystretch{\@gtblstretch}% - \begin{tabular}{|l|*{\numquestions}{c|}c|} - \hline - {\@hqword}& \setcounter{question}{0}\do@qnumloop - {\@htword}\\ - \hline - {\@hpword}& \setcounter{question}{0}\do@ptloop - \numpoints\\ - \hline - {\@hsword}& \setcounter{question}{0}\do@sloop - \\ - \hline +% \do@lines@v is used by *all* multicolumn tables. +% It calls \do@oneline@v for all non-combined tables and +% \do@oneline@comb@v for all combined tables. + +\def\do@lines@v{% + % We get here from \@multicolumntable. + % ALL MULTICOLUMN TABLES!!!! + % pq@index@pts holds the question number or page number in the first + % column of the current row. + \addtocounter{current@row}{1}% + \setcounter{pq@index}{\value{pq@index@pts}}% + \setcounter{cols@done}{0}% Number of columns done + % We're doing both grade tables and point tables!! + \if@combined + \do@oneline@comb@v + \else + \do@oneline@v + \fi + \increment@index{pq@index@pts}% + % We need the "\\ \cline@stuff@v" to *immediately* precede the + % \end{tabular} (i.e., with no \ifnum separating them), to avoid + % having crap after the \cline@stuff@v that + % causes there to be an extra row at the end of the table. We also + % need there to be nothing between \\ and \cline@stuff@v. + \ifnum \value{current@row} = \value{num@rows}\relax + \\ + \cline@stuff@v \end{tabular}% - % Restore the saved value of question: - \setcounter{question}{\arabic{@iterator}}% - \endgroup -} -\def\do@qnumloop{% - \addtocounter{question}{1}% - \thequestion & - \ifnum \arabic{question} < \numquestions\relax - \let\next@qnloop=\do@qnumloop + \let\nextdo@lines@v=\relax \else - \let\next@qnloop=\relax + \\ + \cline@stuff@v + \let\nextdo@lines@v=\do@lines@v \fi - \next@qnloop -} -\def\do@ptloop{% - \addtocounter{question}{1}% - \pointsofquestion{\arabic{question}}& - \ifnum \arabic{question} < \numquestions\relax - \let\next@ptloop=\do@ptloop + \nextdo@lines@v +}% do@lines@v + +\def\do@oneline@v{% + % Called by \do@lines@v. + % Used for all multicolumn non-combined tables. + % pq@index holds the question or page number we're about to do. + \ifnum \value{pq@index} > \last@pq@index\relax + % See if we're in the last column; use pq@index@bpts as a scratch + % counter: + \setcounter{pq@index@bpts}{\value{cols@done}}% + \addtocounter{pq@index@bpts}{1}% + \ifnum \value{pq@index@bpts} = \value{num@cols}\relax + % We're in the last column; are we in the last row? + \ifnum \value{current@row} = \value{num@rows}\relax + % We're in the last column, last row! + % Print the total: + \if@bonus + \@bvtword + \else + \@vtword + \fi + \hidden@ampersand + \if@scores + \if@bonus + \prt@tablebonuspoints + \else + \prt@tablepoints + \fi + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \else + \hspace*{\fill}% + \if@bonus + \prt@tablebonuspoints + \else + \prt@tablepoints + \fi + \fi + \else + % Not last column last row; insert empty space: + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \else + % Not last column; insert empty space: + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi \else - \let\next@ptloop=\relax + % We need to do question (or page) number pq@index: + \refto@index{pq@index}% + \hidden@ampersand + \if@scores + \if@bonus + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \else + \pointsof@index{pq@index}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}% + \fi + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \else + \if@bonus + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \else + \pointsof@index{pq@index}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}% + \fi + \fi \fi - \next@ptloop -} -\def\do@sloop{% - \addtocounter{question}{1}% - \hbox to \@cellwidth{\hfill}& - \ifnum \arabic{question} < \numquestions\relax - \let\next@sloop=\do@sloop + \addtocounter{cols@done}{1}% Number of columns done + \ifnum \value{cols@done} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \nextcolumn@index@v{pq@index}% + \hidden@ampersand + \let\nextdo@oneline@v=\do@oneline@v \else - \let\next@sloop=\relax + \let\nextdo@oneline@v=\relax \fi - \next@sloop -} - - -%-------------------------------------------------------------------- -%-------------------------------------------------------------------- -% Grading tables indexed by page numbers: -% -% The only pages listed are those on which there is a nonzero number -% of points. We check pages 1 through \lastpage@withpoints; this way, -% once we've checked that \lastpage@withpoints and -% \pointsonpage@\romannumeral{\lastpage@withpoints} are defined, we -% can safely (we think) check \pointsonpage@\romannumeral{n} for all n -% between 1 and \lastpage@withpoints without generating errors. - -% Check that there's enough info from the .aux file to do a page -% indexed grade table: -\def\@grdtblpages#1{% - \@ifundefined{lastpage@withpoints}% - {\ClassWarning{exam}{% - You must run LaTeX twice more\MessageBreak - \space\space to produce the grade table.\MessageBreak}% - \fbox{Run \LaTeX{} twice more to produce the grade table}% - }% - {% - \@ifundefined{pointsonpage@\romannumeral - \csname lastpage@withpoints\endcsname}% - {\ClassWarning{exam}{% - You must run LaTeX again\MessageBreak - \space\space to produce the grade table.\MessageBreak}% - \fbox{Run \LaTeX{} again to produce the grade table}% - }% - {% - \@whchtblpgs#1 - }% - }% -} - -\def\@whchtblpgs#1{% - \if v#1% - \@vgrdtblpages + \nextdo@oneline@v +}% do@oneline@v + +\def\do@oneline@comb@v{% + % Called by \do@lines@v. + % All combined multicolumn tables. + % pq@index holds the question (or page) we're about to do. + \ifnum \value{pq@index} > \last@pq@index\relax + % See if we're in the last column; use pq@index@bpts as a scratch + % counter: + \setcounter{pq@index@bpts}{\value{cols@done}}% + \addtocounter{pq@index@bpts}{1}% + \ifnum \value{pq@index@bpts} = \value{num@cols}\relax + % We're in the last column; are we in the last row? + \ifnum \value{current@row} = \value{num@rows}\relax + % We're in the last column, last row! + % Print the total: + \@cvtword + \hidden@ampersand + \prt@tablepoints + \hidden@ampersand + \if@scores + \prt@tablebonuspoints + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \else + \prt@tablebonuspoints + \fi + \else + % Last column, but not last row; insert empty space: + \hbox to \@cellwidth{\hfill}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \else + % Not last column; insert empty space: + \hbox to \@cellwidth{\hfill}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi \else - \if h#1% - \@hgrdtblpages + % We need to do question number pq@index: + \refto@index{pq@index}% + \hidden@ampersand + \pointsof@index{pq@index}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}% + \hidden@ampersand + \if@scores + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% \else - \ClassError{exam}{% - The first optional argument to \protect\gradetable \MessageBreak - \space \space must be either `h' or `v'\MessageBreak - }{% - Grade tables can be either horizontal or vertical;\MessageBreak - \space\space no diagonals allowed.\MessageBreak - }% - \fbox{Error: Grade table: Invalid first optional argument `#1'.}% + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% \fi \fi -} - -%-------------------------------------------------------------------- -% Vertical, indexed by pages: - -\def\@vgrdtblpages{% - \begingroup - \renewcommand\arraystretch{\@gtblstretch}% - \begin{tabular}{|c|c|c|} - \hline - {\@vpgword}& {\@vpword}& {\@vsword}\\ - \hline - \setcounter{@iterator}{0}\pg@vloop - {\@vtword}& \numpoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \endgroup -} -\def\pg@vloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% -% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\roman{@iterator}\endcsname}% -% spanish.ldf redefines \@roman, so we'll avoid \roman - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@vloopline - \fi - \ifnum \the@iterator < \lastpage@withpoints\relax - \let\next@pg@vloop=\pg@vloop + \addtocounter{cols@done}{1}% Number of columns done + \ifnum \value{cols@done} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \nextcolumn@index@v{pq@index}% + \hidden@ampersand + \let\nextdo@oneline@comb@v=\do@oneline@comb@v \else - \let\next@pg@vloop=\relax + \let\nextdo@oneline@comb@v=\relax \fi - \next@pg@vloop -} -\def\pg@vloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \the@iterator & \pointsonpage{\the@iterator}&\\ - \hline -} + \nextdo@oneline@comb@v +}% do@oneline@comb@v %-------------------------------------------------------------------- -% Horizontal, indexed by pages: - -% For a horizontal table, we need to know how many pages there are -% with points on them: -\newcounter{numpgs@withpts} -\def\count@pgswpts{% - \setcounter{numpgs@withpts}{0}% - \setcounter{@iterator}{0}% - \docount@pgswpts -} -\def\docount@pgswpts{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% -% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\roman{@iterator}\endcsname}% -% spanish.ldf redefines \@roman, so we'll avoid \roman - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{numpgs@withpts}{1}% - \fi - \ifnum \the@iterator < \lastpage@withpoints\relax - \let\next@docount=\docount@pgswpts +% \find@nextpagewithpoints and \find@nextcolumnpage@v: + +\def\find@nextpagewithpoints#1{% + % Called by \dofind@nextcolumnpage@v, \increment@index, and + % \@multicolumntable. + % The argument #1 should be the name of a counter with a nonnegative + % value. + % We increase #1 by at least 1 to either the number of the + % next page containing the appropriate kind of points, or to something + % greater than \tbl@lastp. + \addtocounter{#1}{1}% + \if@combined + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}% + \addto@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}% + % The sum is positive when at least one of them is positive. \else - \let\next@docount=\relax + \if@bonus + \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}% + \else + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}% + \fi \fi - \next@docount -} - - -\def\@hgrdtblpages{% - \begingroup - \renewcommand\arraystretch{\@gtblstretch}% - \count@pgswpts - \begin{tabular}{|l|*{\thenumpgs@withpts}{c|}c|} - \hline - {\@hpgword}& \setcounter{@iterator}{0}\do@pgnumloop - {\@htword}\\ - \hline - {\@hpword}& \setcounter{@iterator}{0}\do@pgptloop - \numpoints\\ - \hline - {\@hsword}& \setcounter{@iterator}{0}\do@pgsloop - \\ - \hline - \end{tabular}% - \endgroup -} - -\def\do@pgnumloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% -% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\roman{@iterator}\endcsname}% -% spanish.ldf redefines \@roman, so we'll avoid \roman \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@line - \fi - \ifnum \the@iterator < \lastpage@withpoints\relax - \let\next@pgnumloop=\do@pgnumloop + \let\nextfind@nextpagewithpoints=\relax \else - \let\next@pgnumloop=\relax - \fi - \next@pgnumloop -} -\def\pg@line{% - \the@iterator & -} - -\def\do@pgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% -% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\roman{@iterator}\endcsname}% -% spanish.ldf redefines \@roman, so we'll avoid \roman - \ifhlfcntr@pos{tmp@hlfcntr}% - \pgpt@line + \ifnum \value{#1} > \tbl@lastp\relax + \let\nextfind@nextpagewithpoints=\relax + \else + \def\nextfind@nextpagewithpoints{\find@nextpagewithpoints{#1}}% + \fi \fi - \ifnum \the@iterator < \lastpage@withpoints\relax - \let\next@pgptloop=\do@pgptloop + \nextfind@nextpagewithpoints +}% find@nextpagewithpoints + +\def\find@nextcolumnpage@v#1{% + % Called by \nextcolumn@index@v. + % This is used for all multicolumn tables that are indexed by + % pages. + % We use \find@nextpagewithpoints to increment #1 to either + % the (num@rows)'th page number after #1 that contains the + % appropriate kind of points or to a value greater than \tbl@lastp. + % We use pq@index@bpts as a scratch counter. + \setcounter{pq@index@bpts}{0}% + \dofind@nextcolumnpage@v{#1}% +}% find@nextcolumnpage@v +\def\dofind@nextcolumnpage@v#1{% + % Called only by \find@nextcolumnpage@v. + \addtocounter{pq@index@bpts}{1}% + \find@nextpagewithpoints{#1}% + \ifnum \value{pq@index@bpts} = \value{num@rows}\relax + \let\nextdofind@nextcolumnpage@v=\relax \else - \let\next@pgptloop=\relax + % The following test shouldn't be needed, in theory, because the + % computation of num@cols should prevent trouble, but we're being + % paranoid. + \ifnum \value{#1} > \tbl@lastp\relax + \let\nextdofind@nextcolumnpage@v=\relax + \else + % Note: this is a \def, and not a \let, because we need to put + % in the argument #1: + \def\nextdofind@nextcolumnpage@v{\dofind@nextcolumnpage@v{#1}}% + \fi \fi - \next@pgptloop -} -\def\pgpt@line{% - \csname pointsonpage@\romannumeral \csname c@@iterator\endcsname\endcsname & -% \csname pointsonpage@\roman{@iterator}\endcsname & -% spanish.ldf redefines \@roman, so we'll avoid \roman -} + \nextdofind@nextcolumnpage@v +}% dofind@nextcolumnpage@v -\def\do@pgsloop{% +%-------------------------------------------------------------------- +% \pointsinrange and \bonuspointsinrange, and then +% \firstqinrange, \lastqinrange, and \numqinrange. + + +% We say either \@bonusfalse or \@bonustrue, and then we check it only +% in \do@countloop: +\def\pointsinrange#1{% + \@bonusfalse + \def\tbl@range{#1}% + \@ifundefined{exam@numpoints}% + {\mbox{\normalfont\bfseries ??}}% + {\read@range}% +}% pointsinrange + +\def\bonuspointsinrange#1{% + \@bonustrue + \def\tbl@range{#1}% + \@ifundefined{exam@numpoints}% + {\mbox{\normalfont\bfseries ??}}% + {\read@range}% +}% bonuspointsinrange + +\def\bad@range{% + % Called by \read@range, \firstqinrange, \lastqinrange, and + % \numqinrange. + {\mbox{\normalfont\bfseries ??}}% + \ClassWarning{exam}{% + Grading range `\tbl@range' not defined.\MessageBreak + \space\space Run LaTeX again.\MessageBreak + }% +}% bad@range + +\def\read@range{% + % Called by \pointsinrange and \bonuspointsinrange. + \@ifundefined{range@\tbl@range @firstq}% + {% + \bad@range + }% + {% + \@ifundefined{range@\tbl@range @lastq}% + {% + \bad@range + }% + {% + \edef\tbl@firstq{\csname range@\tbl@range @firstq\endcsname}% + \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}% + % Check that firstq precedes or equals lastq: + \ifnum \tbl@firstq > \tbl@lastq\relax + \fbox{\textbf{Error:} Grading Range `\tbl@range ': + Last question precedes first question.}% + \ClassError{exam}{% + In grading range `\tbl@range ', + the last question\MessageBreak + \space\space comes before the first question.\MessageBreak + }{% + \string\begingradingrange \space must precede + \string\endgradingrange \space by at + least one question.\MessageBreak + }% + \else + \count@pointsinrange + \fi + }% + }% +}% read@range + +\def\count@pointsinrange{% + % Used for both \pointsinrange and \bonuspointsinrange: + \set@hlfcntr{tbl@points}{0}% + \setcounter{@iterator}{\tbl@firstq}% + \addtocounter{@iterator}{-1}\do@countloop + \prt@hlfcntr{tbl@points}% +}% count@pointsinrange +\def\do@countloop{% + % We check \if@bonus here when needed: \addtocounter{@iterator}{1}% -% \ifnum \csname pointsonpage@\roman{@iterator}\endcsname > 0\relax - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% -% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\roman{@iterator}\endcsname}% -% spanish.ldf redefines \@roman, so we'll avoid \roman - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sline + \if@bonus + \@ifundefined{bonuspointsofq@\romannumeral \c@@iterator}% + {}% + {\addto@hlfcntr{tbl@points} + {\csname bonuspointsofq@\romannumeral \c@@iterator\endcsname}}% + \else + \@ifundefined{pointsofq@\romannumeral \c@@iterator}% + {}% + {\addto@hlfcntr{tbl@points} + {\csname pointsofq@\romannumeral \c@@iterator\endcsname}}% \fi - \ifnum \the@iterator < \lastpage@withpoints\relax - \let\next@pgsloop=\do@pgsloop + \ifnum \value{@iterator} < \tbl@lastq\relax + \let\next@countloop=\do@countloop \else - \let\next@pgsloop=\relax + \let\next@countloop=\relax \fi - \next@pgsloop -} -\def\pg@sline{% - \hbox to \@cellwidth{\hfill}& -} + \next@countloop +}% do@countloop + +%-------------------------------------------------------------------- +% \firstqinrange, \lastqinrange, and \numqinrange. + +\newcommand{\firstqinrange}[1]{% + \def\tbl@range{#1}% + \@ifundefined{range@\tbl@range @firstq}% + {\bad@range}% + {\csname range@#1@firstq\endcsname}% +}% firstqinrange + +\newcommand{\lastqinrange}[1]{% + \def\tbl@range{#1}% + \@ifundefined{range@\tbl@range @lastq}% + {\bad@range}% + {\csname range@#1@lastq\endcsname}% +}% lastqinrange + +\newcommand{\numqinrange}[1]{% + \def\tbl@range{#1}% + \@ifundefined{range@#1@firstq}% + {% + \bad@range + }% + {% + \@ifundefined{range@#1@lastq}% + {% + \bad@range + }% + {% + \setcounter{@iterator}{\csname range@#1@lastq\endcsname}% + \addtocounter{@iterator}{-\csname range@#1@firstq\endcsname}% + \stepcounter{@iterator}% + \arabic{@iterator}% + }% + }% +}% numqinrange %-------------------------------------------------------------------- @@ -4259,63 +7530,222 @@ % If the documentclass options include ``answers'', then the command -% \@printanswerstrue is given at the beginning of the run. +% \printanswerstrue is given at the beginning of the run. % If the documentclass options include ``noanswers'', then the command -% \@printanswersfalse is given at the beginning of the run. +% \printanswersfalse is given at the beginning of the run. + +\def\printanswers{\printanswerstrue} +\def\noprintanswers{\printanswersfalse} + +% If the documentclass options include ``cancelspace'', then the +% command \cancelspacetrue is given at the beginning of the run. -\def\printanswers{\@printanswerstrue} -\def\noprintanswers{\@printanswersfalse} +% If the documentclass options include ``nocancelspace'', then the +% command \cancelspacefalse is given at the beginning of the run. +\def\cancelspace{\cancelspacetrue} +\def\nocancelspace{\cancelspacefalse} -% If @printanswers is true, we print the solution using a TheSolution -% environment. If @printanswers is false, we insert blank vertical space -% equal to the optional argument (the default value of which is 0pt). +% \if@insolution will be true while we're inside of any of the +% solution environments. This is used to supress \PgInfo@write and +% \label commands generated if there's a parts (or subparts, or +% subsubparts) environment inside of a solution. (It won't suppress +% the labels for the question objects, since a question object is +% never a label that's been used before.) +\newif\if@insolution +\@insolutionfalse + +\newcommand\SolutionEmphasis[1]{% + \def\Solution@Emphasis{#1}% +} +\SolutionEmphasis{} + +% If printanswers is true, we print the solution using a TheSolution +% environment. If printanswers is false and cancelspace is false, we +% insert blank vertical space equal to the optional argument (the +% default value of which is 0pt). \newenvironment{solution}[1][0pt]% {% - \if@printanswers + \@insolutiontrue % cancelled by the end of the environment + \@addpointsfalse % cancelled by the end of the environment + \ifprintanswers + \begingroup + \Solution@Emphasis \begin{TheSolution}% \else - \par - \vspace*{#1}% + \ifcancelspace + % Do nothing + \else + \par + \penalty 0 + \vspace*{#1}% + \fi \setbox\z@\vbox\bgroup \fi }{% - \if@printanswers + \ifprintanswers \end{TheSolution}% + \endgroup \else \egroup \fi }% -% If @printanswers is true, we print the solution using a TheSolution -% environment. If @printanswers is false, we insert lined vertical space -% equal to the optional argument (the default value of which is 0pt). +% If printanswers is true, we print the solution using a TheSolution +% environment. If printanswers is false and cancelspace is false, +% we insert an empty box of width the current line width and of +% height equal to the optional argument, which can be a length, or +% \fill, or \stretch{number}. If the optional argument is omitted, +% then the box is entirely omitted when printanswers is false. +\newenvironment{solutionorbox}[1][-1pt]% + {% + \@insolutiontrue % cancelled by the end of the environment + \@addpointsfalse % cancelled by the end of the environment + \ifprintanswers + \begingroup + \Solution@Emphasis + \begin{TheSolution}% + \else + \ifcancelspace + % Do nothing + \else + \par + % Note: It's important that the following test be + % ``\ifdim 0pt > #1'' rather than ``\ifdim #1 < 0pt'' + % That's because if the user says + % ``\begin{solutionorbox}{\stretch{1}}'' + % (or \stretch{anythingelse}), then this will expand to + % ``\ifdim 0pt > \z@ plus 1fill\relax''. + % The \ifdim will be ``\ifdim 0pt > \z@'', and we'll have + % ``plus 1fill\relax'' left over. This is OK because if the + % \ifdim is false, that leftover stuff will be ignored, + % and it will only be true if the user omitted the optional + % argument, in which case there's no \stretch and thus no + % left over part. + % If we said ``\ifdim #1 < 0pt'', then we'd get an error + % when the user used \stretch, since the leftover stuff + % would appear when TeX was looking for <, =, or >. + \ifdim 0pt > #1 + % do nothing + \else + \makeemptybox{#1}% + \fi + \fi + \setbox\z@\vbox\bgroup + \fi + }{% + \ifprintanswers + \end{TheSolution}% + \endgroup + \else + \egroup + \fi + }% + +% If printanswers is true, we print the solution using a TheSolution +% environment. If printanswers is false and cancelspace is false, +% we insert lined vertical space equal to the optional argument (the +% default value of which is 0pt). \newenvironment{solutionorlines}[1][0pt]% {% - \if@printanswers + \@insolutiontrue % cancelled by the end of the environment + \@addpointsfalse % cancelled by the end of the environment + \ifprintanswers + \begingroup + \Solution@Emphasis \begin{TheSolution}% \else - \par - \fillwithlines{#1}% + \ifcancelspace + % Do nothing + \else + \par + \penalty 0 + \fillwithlines{#1}% + \fi \setbox\z@\vbox\bgroup \fi }{% - \if@printanswers + \ifprintanswers \end{TheSolution}% + \endgroup \else \egroup \fi }% - -% The environment TheSolution is called from the solution environment -% when @printanswers is true. It uses Donald Arseneau's -% framed.sty macros (included at the end if this file) to allow the -% solution to be broken across pages and have each piece enclosed in -% an fbox (or a colorbox, if the user has given the command -% \shadedsolution). -% +% If printanswers is true, we print the solution using a TheSolution +% environment. If printanswers is false and cancelspace is false, +% we insert dotted lined vertical space equal to the optional +% argument (the default value of which is 0pt). +\newenvironment{solutionordottedlines}[1][0pt]% + {% + \@insolutiontrue % cancelled by the end of the environment + \@addpointsfalse % cancelled by the end of the environment + \ifprintanswers + \begingroup + \Solution@Emphasis + \begin{TheSolution}% + \else + \ifcancelspace + % Do nothing + \else + \par + \penalty 0 + \fillwithdottedlines{#1}% + \fi + \setbox\z@\vbox\bgroup + \fi + }{% + \ifprintanswers + \end{TheSolution}% + \endgroup + \else + \egroup + \fi + }% + +% If printanswers is true, we print the solution using a TheSolution +% environment. If printanswers is false and cancelspace is false, +% we insert a grid occupying vertically the optional argument (the +% default value of which is 0pt). +\newenvironment{solutionorgrid}[1][0pt]% + {% + \@insolutiontrue % cancelled by the end of the environment + \@addpointsfalse % cancelled by the end of the environment + \ifprintanswers + \begingroup + \Solution@Emphasis + \begin{TheSolution}% + \else + \ifcancelspace + % Do nothing + \else + \par + \penalty 0 + \fillwithgrid{#1}% + \fi + \setbox\z@\vbox\bgroup + \fi + }{% + \ifprintanswers + \end{TheSolution}% + \endgroup + \else + \egroup + \fi + }% + + +% The environment TheSolution is called from the solution, +% solutionorbox, solutionorlines, solutionordottedlines, and +% solutionorgrid environments when printanswers is true. It uses +% Donald Arseneau's framed.sty macros (included at the end of this +% file) to allow the solution to be broken across pages and have each +% piece enclosed in an fbox (or a colorbox, if the user has given the +% command \shadedsolutions), (or no box at all, if the user has given +% the command \unframedsolutions). + % Of course, the user can change TheSolution with a \renewenvironment % command. \newcommand{\solutiontitle}{\noindent\textbf{Solution:}\enspace} @@ -4327,31 +7757,42 @@ % inside of the solution box: \leftskip=0pt \rightskip=0pt + % If the user said \unframedsolutions, then both + % \if@framedsolutions and \if@shadedsolutions are false: \if@framedsolutions - % Do nothing; we'll use the default \FrameCommand + % We'll use the default \exam@FrameCommand \else - \def\FrameCommand{\colorbox{SolutionColor}}% + \if@shadedsolutions + \def\exam@FrameCommand{\colorbox{SolutionColor}}% + \else + % It's \unframedsolutions: + \def\exam@FrameCommand{}% + \fi \fi - \MakeFramed{\advance\hsize-\width}% + \exam@MakeFramed{\advance\hsize-\exam@width}% \solutiontitle \ignorespaces }% {% \unskip - \endMakeFramed + \endexam@MakeFramed }% \newif\if@framedsolutions \@framedsolutionstrue +\newif\if@shadedsolutions +\@shadedsolutionsfalse +% If the user said \unframedsolutions, then both +% \if@framedsolutions and \if@shadedsolutions are false. -\def\framedsolutions{\@framedsolutionstrue} +\def\framedsolutions{\@framedsolutionstrue\@shadedsolutionsfalse} \def\shadedsolutions{% \@ifundefined{definecolor} {% \ClassError{exam}{% You must load the color package with the command\MessageBreak \space\space\protect\usepackage{color}\MessageBreak - in order to use the command \protect\colorsolutions + in order to use the command \protect\shadedsolutions \MessageBreak }{% This command makes use of the package color.sty,\MessageBreak @@ -4361,9 +7802,178 @@ }% {% \definecolor{SolutionColor}{gray}{0.8} + \@shadedsolutionstrue \@framedsolutionsfalse }% } +\def\unframedsolutions{\@framedsolutionsfalse\@shadedsolutionsfalse} + + +% The solutionbox environment is different from the other solution +% environments (solution, solutionorbox, solutionorlines, +% solutionordottedlines, and solutionorgrid), in that +% +% (1) The box is always printed, whether answers are being printed +% or not. +% +% (2) The argument giving the size of the box is a required +% argument, not an optional argument, and so it should be enclosed +% in braces, not in brackets. It can be either a length or +% \stretch{number}. +% +% (3) We make no use of the TheSolution environment; the solutionbox +% environment is completely freestanding. +% +% If answers are not being printed then only the box is printed, with +% nothing in it. If answers are being printed, then the solution is +% printed inside of the box. +% +% Note: It's the user's responsibility to be sure that the box is +% large enough to hold the solution! If the solution takes up too +% much vertical space, then it will spill out of the bottom of the +% box, overwriting whatever follows the box. + +% 2016/02/08: The solutionbox frame can now be printed in color, as +% long as you load color.sty in the preamble. +% +% Usage: Say +% +% \usepackage{color} +% +% in the preamble, and then give the command +% +% \colorsolutionboxes +% +% to have the frame around a solutionbox in color. The default color +% was created by the command +% +% \definecolor{SolutionBoxColor}{gray}{0.8} +% +% and you can change the color by giving a new \definecolor command +% (which must be done *after* the \colorsolutionboxes command). +% +% To cancel color solutionbox frames and return to black, give the +% command +% +% \nocolorsolutionboxes + +\newif\if@colorsolutionboxes +\@colorsolutionboxesfalse +\def\colorsolutionboxes{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorsolutionboxes + \MessageBreak + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{SolutionBoxColor}{gray}{0.8} + \@colorsolutionboxestrue + }% +} +\def\nocolorsolutionboxes{\@colorsolutionboxesfalse} + +\newbox\exam@box +\newenvironment{solutionbox}[1]{% + \@insolutiontrue % cancelled by the end of the environment + \@addpointsfalse % cancelled by the end of the environment + \def\solutionbox@size{#1}% saved for end of environment +% Change, 2016/02/08: So that the solutionbox environment will work +% correctly inside of a tabular environment, we use \hsize instead of +% \textwidth: +% \@tempdima=\textwidth + \@tempdima=\hsize + \advance\@tempdima -\@totalleftmargin + \advance\@tempdima -6\fboxsep + \advance\@tempdima -2\fboxrule + % Confine the \Solution@Emphasis, as well as anything the user puts + % into the solution (e.g., \color{red}, or whatever); don't say + % \endgroup until after using \box\exam@box: + \begingroup + \Solution@Emphasis + % We save the solution in a box of the proper width. We'll either + % print it (if we're printing solutions) or throw it away by just + % not using it before the environment ends: + \setbox\exam@box=\vtop\bgroup + \hsize=\@tempdima + \leftskip=0pt + \rightskip=0pt + \vskip 2\fboxsep +% Change, 2016/05/09: We change \@totalleftmargin and \linewidth in +% case there are enumerate, itemize, or description environments +% inside the solution: + \@totalleftmargin=0pt + \linewidth=\hsize + \solutiontitle + \ignorespaces + }% + {% + \unskip + \egroup + % OK, the solution is now inside \box\exam@box. + % Set the height and depth to 0pt, so that if we use it we won't + % be advancing our position on the page: + \ht\exam@box=0pt + \dp\exam@box=0pt + \par + \vspace{\parskip} + \ifprintanswers + % We enclose the \vtop in an \hbox to avoid having the + % indentation of the enclosing list environment (implemented via + % \parshape) shift us to the right when we enter horizontal + % mode. If we don't use this \hbox, then we'd have to comment + % out the \hskip \@totalleftmargin: + % 2016/02/08: Changed \textwidth to \hsize: + \hbox to \hsize{% + \noindent + \hskip\@totalleftmargin + \hskip3\fboxsep\hskip\fboxrule + \box\exam@box\hfill + }% + \par\nointerlineskip + \fi + \endgroup % Finish confining the \Solution@Emphasis +% Starting in version 2.502, 2016/03/23,the decision of whether to +% color the box is made in the \makeemptybox command: + \makeemptybox{\solutionbox@size} + }% End of the second argument of \newenvironment{solutionbox} + + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Added in version 2.502: 2016/03/23, \colorfbox + +% The \colorfbox command is used in our modification of framed.sty +% that allows us to print the frame around the solution in color when +% the user has given the command \colorsolutionboxes. It takes two +% arguments, the first being the color for the frame, and the second +% being the stuff to be framed. + +% If we had assumed that xcolor.sty was used (instead of just +% color.sty), then the line that saves the current color in +% saved@color could have been just + +% \colorlet{saved@color}{.} + +% but we wanted to make this work even if color.sty is being used. + +% When you define a color mycolor using either color.sty or +% xcolor.sty, a macro \csname\string\color@ mycolor\endcsname is +% defined (i.e., the macro name is \\color@mycolor). +\newcommand{\colorfbox}[2]{% + % Save the current color in saved@color: + \expandafter\let\csname\string\color@saved@color\endcsname\current@color + % Create the box in color #1, with the text in saved@color + % (the braces are to confine the color change commands): + {\color{#1}\fbox{\color{saved@color}#2}}% +}% colorfbox %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -4376,28 +7986,37 @@ % provided that this notice is left intact. % % The modifications I made are marked with ``psh'' in a comment: +% +% Further modifications, 2017-09-21 +% I changed the names of many commands by prepending ``exam@'', so +% that the user can use the framed.sty package with exam.cls and not +% have conflicts. (I also renamed the framed, shaded, and leftbar +% environments to examframed, examshaded, and examleftbar.) I didn't +% mark these name changes. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Create framed or shaded regions that can break across pages using -% \begin{framed} ... \end{framed} -- ordinary frame box (box at margin) -% \begin{shaded} ... \end{shaded} -- shaded background (into margin) -% ... leftbar ... -- line on left side +% \begin{examframed} ... \end{examframed} -- ordinary frame box +% (box at margin) +% \begin{examshaded} ... \end{examshaded} -- shaded background +% (into margin) +% ... examleftbar ... -- line on left side % \begin{MakeFramed}{settings} ... \end{MakeFramed} % -- generic frame (for new environments) % -% The "framed" environment puts the text into "\fbox" with the -% settings "\fboxrule=\FrameRule" and "\fboxsep=\FrameSep". +% The "examframed" environment puts the text into "\fbox" with the +% settings "\fboxrule=\exam@FrameRule" and "\fboxsep=\exam@FrameSep". % You can change these lengths (using "\setlength") and you -% can even change the definition of "\FrameCommand" to use +% can even change the definition of "\exam@FrameCommand" to use % much fancier boxes. % -% In fact, the "shaded" environment just redefines "\FrameCommand" +% In fact, the "shaded" environment just redefines "\exam@FrameCommand" % to use "\colorbox{shadecolor}" (and you have to define the % color "shadecolor": \newcolor{shadecolor}...). % % A page break is allowed, and even encouraged, before the framed % environment. If you want to attach some text (a box title) to the -% frame, then the text should be inserted by \FrameCommand +% frame, then the text should be inserted by \exam@FrameCommand % % The contents of the framed regions are restricted: % Floats, footnotes, marginpars and head-line entries will be lost. @@ -4407,16 +8026,18 @@ % % The MakeFramed environment does the work. Its "settings" argument % should contain any adjustments to the text width (applied to \hsize, -% and using the "\width" of the frame itself) as well as a `restore' -% command -- \@parboxrestore or \FrameRestore or something similar. +% and using the "\exam@width" of the frame itself) as well as a `restore' +% command -- \@parboxrestore or \exam@FrameRestore or something similar. % % Expert commands: -% \MakeFramed, \endMakeFramed: the "MakeFramed" environment -% \FrameCommand: command to draw the frame around its argument -% \FrameRestore: restore some text settings, but fewer than \@parboxrestore -% \FrameRule: length register; \fboxrule for default "framed". -% \FrameSep: length register; \fboxsep for default "framed". -% \FrameHeightAdjust: macro; height of frame above baseline at top of page +% \exam@MakeFramed, \endexam@MakeFramed: the "MakeFramed" environment +% \exam@FrameCommand: command to draw the frame around its argument +% \exam@FrameRestore: restore some text settings, but fewer than +% \@parboxrestore +% \exam@FrameRule: length register; \fboxrule for default "framed". +% \exam@FrameSep: length register; \fboxsep for default "framed". +% \exam@frameHeightAdjust: macro; height of frame above baseline at +% top of page % % This is still a `pre-production' version because I can think of many % features/improvements that should be made. Nevertheless, starting @@ -4432,30 +8053,30 @@ \newdimen\saved@totalleftmargin \newcount\@sollistdepth -\newenvironment{framed}% using default \FrameCommand - {\MakeFramed {\advance\hsize-\width \FrameRestore}}% - {\endMakeFramed} +\newenvironment{examframed}% using default \exam@FrameCommand + {\exam@MakeFramed {\advance\hsize-\exam@width \exam@FrameRestore}}% + {\endexam@MakeFramed} -\newenvironment{shaded}{% - \def\FrameCommand{\colorbox{shadecolor}}% - \MakeFramed {\FrameRestore}}% - {\endMakeFramed} +\newenvironment{examshaded}{% + \def\exam@FrameCommand{\colorbox{shadecolor}}% + \exam@MakeFramed {\exam@FrameRestore}}% + {\endexam@MakeFramed} -\newenvironment{leftbar}{% - \def\FrameCommand{\vrule width 3pt \hspace{10pt}}% - \MakeFramed {\advance\hsize-\width \FrameRestore}}% - {\endMakeFramed} +\newenvironment{examleftbar}{% + \def\exam@FrameCommand{\vrule width 3pt \hspace{10pt}}% + \exam@MakeFramed {\advance\hsize-\exam@width \exam@FrameRestore}}% + {\endexam@MakeFramed} -\chardef\FrameRestore=\catcode`\| % for debug +\chardef\exam@FrameRestore=\catcode`\| % for debug \catcode`\|=\catcode`\% % (debug: insert space after backslash) -\def\MakeFramed#1{\par - % measure added width and height; call result \width and \height +\def\exam@MakeFramed#1{\par + % measure added width and height; call result \exam@width and \exam@height \setbox\z@\vbox{\vskip-1in \hbox{\hskip-1in - \FrameCommand{\hbox{\vrule \@height .7in \@depth.3in \@width 1in}}}% + \exam@FrameCommand{\hbox{\vrule \@height .7in \@depth.3in \@width 1in}}}% \vskip\z@skip}% - \def\width{\wd\z@}\def\height{\ht\z@}% - \edef\fb@frw{\the\width}\edef\fb@frh{\the\height}% + \def\exam@width{\wd\z@}\def\exam@height{\ht\z@}% + \edef\exam@fb@frw{\the\exam@width}\edef\exam@fb@frh{\the\exam@height}% % insert pre-penalties and skips \begingroup \skip@\lastskip @@ -4463,7 +8084,7 @@ \penalty9999 % updates \page parameters \ifdim\pagefilstretch=\z@ \ifdim\pagefillstretch=\z@ \edef\@tempa{\the\skip@}% - \ifx\@tempa\zero@glue \penalty-30 + \ifx\@tempa\exam@zero@glue \penalty-30 \else \vskip-\skip@ \penalty-30 \vskip\skip@ \fi\fi\fi \penalty\z@ @@ -4473,7 +8094,7 @@ % calculated badness is really 8192, not 10000, so the multiplier % is 0.2301. \advance\skip@ \z@ plus-.5\baselineskip - \advance\skip@ \z@ plus-.231\height + \advance\skip@ \z@ plus-.231\exam@height \advance\skip@ \z@ plus-.231\skip@ \advance\skip@ \z@ plus-.231\topsep \vskip-\skip@ \penalty 1800 \vskip\skip@ @@ -4481,11 +8102,11 @@ \addvspace{\topsep}% \endgroup % clear out pending page break - \penalty\@M \vskip 2\baselineskip \vskip\height - \penalty9999 \vskip -2\baselineskip \vskip-\height + \penalty\@M \vskip 2\baselineskip \vskip\exam@height + \penalty9999 \vskip -2\baselineskip \vskip-\exam@height \penalty9999 % updates \pagetotal |\message{After clearout, \pagetotal=\the\pagetotal, \pagegoal=\the\pagegoal. }% - \fb@adjheight + \exam@fb@adjheight %psh: Added commands: \advance\hsize-\@totalleftmargin \saved@totalleftmargin=\@totalleftmargin @@ -4496,25 +8117,26 @@ \leftmargin=0pt %psh: end of added commands \setbox\@tempboxa\vbox\bgroup - #1% Modifications to \hsize (can use \width and \height) + #1% Modifications to \hsize (can use \exam@width and \exam@height) \textwidth\hsize \columnwidth\hsize %psh: added one line: \linewidth=\hsize } -\def\endMakeFramed{\par +\def\endexam@MakeFramed{\par \kern\z@ \penalty-100 % put depth into height \egroup - \begingroup \put@frame \endgroup + \begingroup \exam@put@frame \endgroup %psh: Added one line: \@totalleftmargin=\saved@totalleftmargin } -% \put@frame takes the contents of \@tempboxa and puts all, or a piece, -% of it on the page with a frame (\FrameCommand). It recurses until -% all of \@tempboxa has been used up. (\@tempboxa must have zero depth.) +% \exam@put@frame takes the contents of \@tempboxa and puts all, or a +% piece, of it on the page with a frame (\exam@FrameCommand). It +% recurses until all of \@tempboxa has been used up. (\@tempboxa must +% have zero depth.) -\def\put@frame{\relax +\def\exam@put@frame{\relax \ifdim\pagegoal=\maxdimen \pagegoal\vsize \fi | \message{=============== Entering putframe ====================^^J | \pagegoal=\the\pagegoal, \pagetotal=\the\pagetotal. }% @@ -4522,17 +8144,17 @@ \dimen@\pagegoal \advance\dimen@-\pagetotal % natural space left on page \ifdim\dimen@<2\baselineskip | \message{Page has only \the\dimen@\space room left; eject. }% - \eject \fb@adjheight \put@frame + \eject \exam@fb@adjheight \exam@put@frame \else % there's appreciable room left on the page | \message{\string\pagetotal=\the\pagetotal, | \string\pagegoal=\the\pagegoal, | \string\pagestretch=\the\pagestretch, | \string\pageshrink=\the\pageshrink, -| \string\fb@frh=\fb@frh. \space} -| \message{Box of size \the\ht\@tempboxa\space + \fb@frh}% +| \string\exam@fb@frh=\exam@fb@frh. \space} +| \message{Box of size \the\ht\@tempboxa\space + \exam@fb@frh}% \begingroup % temporarily set \dimen@ to be... \advance\dimen@.8\pageshrink % maximum space available on page - \advance\dimen@-\fb@frh\relax % space available for frame's contents + \advance\dimen@-\exam@fb@frh\relax % space available for frame's contents \expandafter\endgroup % restore \dimen@ to real room left on page \ifdim\dimen@>\ht\@tempboxa % whole box does fit @@ -4540,10 +8162,11 @@ \else % box must be split | \message{must be split to fit in \the\dimen@. }% \setbox\@tempboxa\vbox{% simulate frame and flexiblity of the page: - \vskip \fb@frh \@plus\pagestretch \@minus.8\pageshrink + \vskip \exam@fb@frh \@plus\pagestretch \@minus.8\pageshrink \kern137sp\kern-137sp\penalty-30 \unvbox\@tempboxa}% - \edef\fb@resto@set{\boxmaxdepth\the\boxmaxdepth \splittopskip\the\splittopskip}% + \edef\exam@fb@resto@set{\boxmaxdepth\the\boxmaxdepth + \splittopskip\the\splittopskip}% \boxmaxdepth\z@ \splittopskip\z@ \setbox\tw@\vsplit\@tempboxa to\dimen@ \setbox\tw@\vbox{\unvbox\tw@}% natural-sized @@ -4557,7 +8180,7 @@ | \message{Frame is big -- Use up the full column. }% \dimen@ii\pagegoal \advance\dimen@ii -\topskip - \advance\dimen@ii \FrameHeightAdjust\relax + \advance\dimen@ii \exam@frameHeightAdjust\relax \else % suspect this is wrong: % If the split-to size > feasible room_on_page, rebox it smaller. \advance\dimen@.8\pageshrink @@ -4569,17 +8192,18 @@ \fi \fi % Re-box contents to desired size \dimen@ii - \advance\dimen@ii -\fb@frh + \advance\dimen@ii -\exam@fb@frh \setbox\tw@\vbox to\dimen@ii \bgroup % remove simulated frame and page flexibility: - \vskip -\fb@frh \@plus-\pagestretch \@minus-.8\pageshrink + \vskip -\exam@fb@frh \@plus-\pagestretch \@minus-.8\pageshrink \unvbox\tw@ \unpenalty\unpenalty \ifdim\lastkern=-137sp % whole box went to next page | \message{box split at beginning! }% - \egroup \fb@resto@set \eject % (\vskip for frame size was discarded) - \fb@adjheight + \egroup \exam@fb@resto@set \eject % (\vskip for frame size + % was discarded) + \exam@fb@adjheight \else % - \egroup \fb@resto@set + \egroup \exam@fb@resto@set \ifvoid\@tempboxa % it all fit after all | \message{box split at end! }% \setbox\@tempboxa\box\tw@ @@ -4588,49 +8212,62 @@ \ifdim\wd\tw@>\z@ %psh: Changed the command that inserts the box: % Instead of \centerline, we shift right by \saved@totalleftmargin: -% \centerline{\FrameCommand{\box\tw@}}% ??? \centerline bad idea - \hbox{\hskip \saved@totalleftmargin\FrameCommand{\box\tw@}}% +% \centerline{\exam@FrameCommand{\box\tw@}}% ??? \centerline bad idea + \hbox{\hskip \saved@totalleftmargin\exam@FrameCommand{\box\tw@}}% \else | \message{Zero width means likely blank. Don't frame it (guess)}% \box\tw@ \fi \hrule \@height\z@ \eject - \fb@adjheight - \put@frame + \exam@fb@adjheight + \exam@put@frame \fi\fi\fi\fi\fi \ifvoid\@tempboxa\else %psh: Changed the command that inserts the box: % Instead of \centerline, we shift right by \saved@totalleftmargin: -% \centerline{\FrameCommand{\box\@tempboxa}}% - \hbox{\hskip\saved@totalleftmargin\FrameCommand{\box\@tempboxa}}% +% \centerline{\exam@FrameCommand{\box\@tempboxa}}% + \hbox{\hskip\saved@totalleftmargin\exam@FrameCommand{\box\@tempboxa}}% \nointerlineskip \null %{\showoutput \showlists} \penalty-30 \vskip\topsep \fi} -\def\fb@adjheight{% - \vbox to\FrameHeightAdjust{}% get proper baseline skip from above. +\def\exam@fb@adjheight{% + \vbox to\exam@frameHeightAdjust{}% get proper baseline skip from above. \penalty\@M \nointerlineskip - \vskip-\FrameHeightAdjust + \vskip-\exam@frameHeightAdjust \penalty\@M} % useful for tops of pages -\edef\zero@glue{\the\z@skip} +\edef\exam@zero@glue{\the\z@skip} -\catcode`\|=\FrameRestore +\catcode`\|=\exam@FrameRestore % Provide configuration commands: -\providecommand\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep \fbox} -\@ifundefined{FrameRule}{\newdimen\FrameRule \FrameRule=\fboxrule}{} -\@ifundefined{FrameSep} {\newdimen\FrameSep \FrameSep =3\fboxsep}{} +%psh: Version 2.502, 2016/03/23, changed \exam@FrameCommand so that the +% frame is printed in color if the user has said +% \colorsolutionboxes: +%\providecommand\exam@FrameCommand{\fboxrule=\exam@FrameRule +%\fboxsep=\exam@FrameSep \fbox} +\def\exam@FrameCommand{\fboxrule=\exam@FrameRule \fboxsep=\exam@FrameSep + \if@colorsolutionboxes + \def\box@it{\colorfbox{SolutionBoxColor}}% + \else + \def\box@it{\fbox}% + \fi + \box@it +}% \exam@FrameCommand + +\@ifundefined{FrameRule}{\newdimen\exam@FrameRule \exam@FrameRule=\fboxrule}{} +\@ifundefined{FrameSep} {\newdimen\exam@FrameSep \exam@FrameSep =3\fboxsep}{} % Height of frame above first baseline when frame starts a page: -\providecommand\FrameHeightAdjust{6pt} +\providecommand\exam@frameHeightAdjust{6pt} -% \FrameRestore has parts of \@parboxrestore. See how it is used in the +% \exam@FrameRestore has parts of \@parboxrestore. See how it is used in the % "settings" argument of \MakeFrame. Previous behavior can be restored by % using \@parboxrestore there, or redefining: -% \makeatletter \renewcommand\FrameRestore{\@parboxrestore} \makeatother -\def\FrameRestore{% +% \makeatletter \renewcommand\exam@FrameRestore{\@parboxrestore} \makeatother +\def\exam@FrameRestore{% \let\if@nobreak\iffalse \let\if@noskipsec\iffalse % \let\par\@@par ?? @@ -4648,7 +8285,8 @@ } % Compatibility with previous versions (temporary!): -\let\fram@d=\MakeFramed \let\endfram@d=\endMakeFramed +% psh: we'll remove this 2017-09-21 +%\let\fram@d=\MakeFramed \let\endfram@d=\endMakeFramed %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%