From 08c9009c085b5fb5cd2151a112662e3c689b67f7 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 16 Nov 2017 22:14:35 +0000 Subject: exam (16nov17) git-svn-id: svn://tug.org/texlive/trunk@45832 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/tex/latex/exam/exam.cls | 3829 +++++++++++++++++++---------- 1 file changed, 2594 insertions(+), 1235 deletions(-) (limited to 'Master/texmf-dist/tex/latex/exam') diff --git a/Master/texmf-dist/tex/latex/exam/exam.cls b/Master/texmf-dist/tex/latex/exam/exam.cls index 5ea596ca0a3..52b4e9d4f45 100644 --- a/Master/texmf-dist/tex/latex/exam/exam.cls +++ b/Master/texmf-dist/tex/latex/exam/exam.cls @@ -3,7 +3,8 @@ % A LaTeX2e document class for preparing exams. %% exam.cls -%% Copyright (c) 1994, 1997, 2000, 2004, 2008, 2011, 2015 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 @@ -31,8 +32,8 @@ % from my web page: http://www-math.mit.edu/~psh/ -\def\fileversion{2.5} -\def\filedate{2015/05/07} +\def\fileversion{2.6} +\def\filedate{2017/10/26} %--------------------------------------------------------------------- %--------------------------------------------------------------------- % @@ -84,6 +85,263 @@ %-------------------------------------------------------------------- % Changelog since version 2.4: +%-------------------------------------------------------------------- +% Version 2.6, 2017/09/19 + +% No longer betatest. + +%-------------------------------------------------------------------- +% 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.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.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: + +% \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.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.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.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.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.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. +% +% 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 + %-------------------------------------------------------------------- % Version 2.5 2015/05/07 @@ -552,6 +810,11 @@ % 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{ } @@ -567,6 +830,26 @@ % 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 @@ -584,17 +867,23 @@ % 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\@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{ } @@ -609,6 +898,26 @@ \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 @@ -626,14 +935,13 @@ % 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\@colht=\textheight +% % \global\@colroom=\textheight % \global\vsize=\textheight % \global\pagegoal=\textheight \endgroup -} - - +}% @setfootheight %--------------------------------------------------------------------- @@ -1246,7 +1554,7 @@ % the last such page with AtEndDocument. \def\numpages{\@ifundefined{exam@lastpage}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@lastpage }% numpages @@ -1255,50 +1563,50 @@ % 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\bf ??}}% + {0\mbox{\normalfont\bfseries ??}}% \exam@lastcoverpage }% numpages \def\totalnumpages{\@ifundefined{exam@totalpages}% - {\mbox{\normalfont\bf ??}}% + {\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\bf ??}}% + {\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\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\csname bonuspointsofq@\romannumeral #1\endcsname}% }% bonuspointsofquestion @@ -1311,11 +1619,11 @@ % for that last page, and one of those will be undefined. % % \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}% -% {\mbox{\normalfont\bf ??}}% +% {\mbox{\normalfont\bfseries ??}}% % {\csname pointsonpage@\romannumeral #1\endcsname}% % }% pointsonpage % \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}% -% {\mbox{\normalfont\bf ??}}% +% {\mbox{\normalfont\bfseries ??}}% % {\csname bonuspointsonpage@\romannumeral #1\endcsname}% % }% bonuspointsonpage % @@ -2125,6 +2433,18 @@ \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, @@ -3132,8 +3452,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}{% @@ -3339,10 +3659,10 @@ \def\setup@point@toks{% % 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. +% 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 @@ -3362,6 +3682,66 @@ % tokens ``\the\point@toks'' will insert an *empty* token list, which % will do no harm. % + \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 \point@toks={% \llap{\padded@point@block @@ -3392,8 +3772,6 @@ \fi }% setup@point@toks - - \def\droppoints{% \leavevmode\unskip\nobreak\hfill \rlap{\hskip\rightmargin % Defined by the list environment @@ -3404,7 +3782,6 @@ \par } - \def\droptotalpoints{% \leavevmode\unskip\nobreak\hfill \rlap{\hskip\rightmargin % Defined by the list environment @@ -3476,29 +3853,55 @@ \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\@pointstwosidedfalse \gdef\pt@name{\@marginpointname}% \gdef\bnspt@name{\@marginbonuspointname}} \def\pointsinrightmargin{\global\@pointsinrightmargintrue \global\@pointsinleftmarginfalse \global\@pointsdroppedfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@marginpointname}% \gdef\bnspt@name{\@marginbonuspointname}} \def\nopointsinmargin{\global\@pointsinleftmarginfalse \global\@pointsinrightmarginfalse \global\@pointsdroppedfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@pointname}% \gdef\bnspt@name{\@bonuspointname}} \def\pointsdroppedatright{\global\@pointsdroppedtrue \global\@pointsinleftmarginfalse \global\@pointsinrightmarginfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@marginpointname}% \gdef\bnspt@name{\@marginbonuspointname}} -\let\pointsinmargin\pointsinleftmargin -\let\nopointsinrightmargin\nopointsinmargin -\let\nopointsinleftmargin\nopointsinmargin +\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 @@ -3534,7 +3937,6 @@ % which confines the effects of anything here: \gdef\bonuspoint@block{#1}% } -\def\thebonuspoints{\@points \@bonuspointname} %Initialize: \noboxedpoints @@ -3688,6 +4090,7 @@ \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}% \def\choice{% \if@correctchoice + \color@endgroup \endgroup \fi \item @@ -3695,12 +4098,30 @@ } % choice \def\CorrectChoice{% \if@correctchoice + \color@endgroup \endgroup \fi \ifprintanswers % We can't say \choice here, because that would % insert an \endgroup: - \begingroup \@correctchoicetrue + % 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 @@ -3713,7 +4134,7 @@ \choiceshook }% }% - {\if@correctchoice \endgroup \fi \endlist} + {\if@correctchoice \color@endgroup \endgroup \fi \endlist} \newenvironment{oneparchoices}% {% @@ -3768,19 +4189,36 @@ \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}% \def\choice{% \if@correctchoice - \endgroup + \color@endgroup \endgroup \fi \item \do@choice@pageinfo } % choice \def\CorrectChoice{% \if@correctchoice - \endgroup + \color@endgroup \endgroup \fi \ifprintanswers % We can't say \choice here, because that would - % insert an \endgroup: - \begingroup \@correctchoicetrue + % 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 @@ -3795,7 +4233,7 @@ \checkboxeshook }% }% - {\if@correctchoice \endgroup \fi \endlist} + {\if@correctchoice \color@endgroup \endgroup \fi \endlist} \newenvironment{oneparcheckboxes}% {% @@ -3851,7 +4289,8 @@ %-------------------------------------------------------------------- % 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} @@ -3894,7 +4333,10 @@ \ifprintanswers \ans@l~\hbox to 0pt{\hbox to \answerlinelength{\hrulefill}\hss}% \raise \answerclearance\hbox to \answerlinelength{% - \CorrectChoice@Emphasis \hfil #1\hss}% + % 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 @@ -3961,9 +4403,11 @@ \setbox0 \hbox{\color@begingroup \CorrectChoice@Emphasis \fillin@ans \color@endgroup}% \ifdim\wd0 > #1\relax - \hbox{\CorrectChoice@Emphasis \fillin@ans}% + \hbox{\color@begingroup\CorrectChoice@Emphasis \fillin@ans + \color@endgroup}% \else - \hbox to #1{\CorrectChoice@Emphasis \hfil \fillin@ans \hfil}% + \hbox to #1{\color@begingroup\CorrectChoice@Emphasis + \hfil \fillin@ans \hfil\color@endgroup}% \fi \endgroup \else @@ -4043,18 +4487,71 @@ % \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 @@ -4069,7 +4566,7 @@ % no matter where on the page it happens to start: \cleaders \copy0 \vskip #1 \hbox{}% \endgroup -} +}% \do@fillwithlines %-------------------------------------------------------------------- % \fillwithdottedlines @@ -4093,10 +4590,64 @@ % % 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} -\def\fillwithdottedlines#1{% +\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 @@ -4111,7 +4662,7 @@ % no matter where on the page it happens to start: \cleaders \copy0 \vskip #1 \hbox{}% \endgroup -}% fillwithdottedlines +}% \do@fillwithdottedlines %-------------------------------------------------------------------- % \fillwithgrid @@ -4261,7 +4812,26 @@ \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 @@ -4476,15 +5046,44 @@ \cvsword{Score} \cvtword{Total:} - -% The only commands here accessible to the user are \gradetable, -% \bonusgradetable, \combinedgradetable, \pointtable, -% \bonuspointtable, \combinedpointtable, \partialgradetable, +% 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. +% \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 possibilities are @@ -4559,6 +5158,35 @@ % \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]'. @@ -4573,8 +5201,9 @@ % \partialcombinedpointtable: \newif\if@partial -% \@combinedtrue means we're doing \combinedgradetable -% or \combinedpointtable: +% \@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 @@ -4606,8 +5235,6 @@ }% 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: @@ -4617,49 +5244,315 @@ % points for the questions (or pages) that appear on the table: \new@hlfcntr{tbl@bonuspoints} +%-------------------------------------------------------------------- +% multirow tables, non-partial: -\def\partialgradetable#1{% +\def\multirowgradetable#1{% \@scorestrue \@bonusfalse - \@partialtrue + \@partialfalse \@combinedfalse - \def\tbl@range{#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 + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} -\def\partialbonusgradetable#1{% +\def\multirowpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowbonusgradetable#1{% \@scorestrue \@bonustrue - \@partialtrue + \@partialfalse \@combinedfalse - \def\tbl@range{#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 + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} -\def\partialcombinedgradetable#1{% +\def\multirowbonuspointtable#1{% + \@scoresfalse + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowcombinedgradetable#1{% \@scorestrue \@bonusfalse - \@partialtrue + \@partialfalse \@combinedtrue - \def\tbl@range{#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 + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} -\def\partialpointtable#1{% +\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: @@ -4672,6 +5565,12 @@ \@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: @@ -4684,6 +5583,12 @@ \@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: @@ -4691,12 +5596,19 @@ }% 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: @@ -4708,6 +5620,12 @@ \@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: @@ -4719,6 +5637,12 @@ \@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: @@ -4730,6 +5654,12 @@ \@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: @@ -4741,6 +5671,12 @@ \@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: @@ -4752,6 +5688,12 @@ \@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: @@ -4764,7 +5706,7 @@ % (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. -% \do@table then branches, depending on whether the user +% \find@p@or@q@range then branches, depending on whether the user % selected [questions] or [pages]. \def\i@gtable[#1]{% @@ -4774,10 +5716,11 @@ \@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 on to \do@table to see whether we're doing a table + % 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}% @@ -4788,7 +5731,7 @@ }% \fbox{Run \LaTeX{} again to produce the table}% }% - {\do@table{#1}{#2}}% + {\find@p@or@q@range{#1}{#2}}% \else \ClassError{exam}{% You must give the command \protect\addpoints\MessageBreak @@ -4801,46 +5744,50 @@ \fi }% ii@gtable - \def\@questionsref{questions} \def\@pagesref{pages} -\def\do@table#1#2{% - % The first argument is ``v'' or ``h''; - % the second argument is ``questions'' or ``pages''. +\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 or by pages: - \begingroup % avoid trouble from using \@temp - \def\@temp{#2}% - \ifx\@temp\@questionsref + % 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 + \ifx\exam@temp\@pagesref + \tbl@pgstrue \find@prange{#1}% \else \ClassError{exam}{% - The second optional argument to a\MessageBreak - \space\space grade table or point table command\MessageBreak - \space \space must be either `questions' or - `pages',\MessageBreak - \space\space not `#2'.\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 and point tables can be indexed by questions or pages;\MessageBreak \space\space for others, you're on your own.\MessageBreak }% - \fbox{Error: grade or point table: Invalid second - optional argument `#2'.}% + \fbox{\textbf{Error:} grade or point table: Invalid argument + `#2' must be `questions' or `pages'.}% \fi \fi \endgroup -}% do@table - +}% 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 + Grading range `\tbl@range' not defined.\MessageBreak \space\space Run LaTeX again to produce the table.\MessageBreak }% }% range@undefined @@ -4850,16 +5797,18 @@ %-------------------------------------------------------------------- % Grade and point tables indexed by question numbers: -% We get to \find@qrange if we know we're doing a table indexed -% by question numbers. The argument is either ``v'' or ``h''. -% If we're not doing a partial table, then \find@qrange sets -% \tbl@firstq to 1 and \tbl@lastq 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 \@tblquestions, passing along the argument -% that is either ``v'' or ``h''. +% 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}% @@ -4874,12 +5823,14 @@ {% \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{Error: Grading Range `\tbl@range ': + \fbox{\textbf{Error:} Grading Range `\tbl@range': Last question precedes first question.}% \ClassError{exam}{% - In grading range `\tbl@range ', + In grading range `\tbl@range', the last question\MessageBreak \space\space comes before the first question.\MessageBreak }{% @@ -4888,463 +5839,51 @@ least one question.\MessageBreak }% \else - \@tblquestions{#1}% + \tbl@v@or@h{#1}% \fi }% }% \else \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}% - \@tblquestions{#1}% + \let\last@pq@index=\tbl@lastq + \tbl@v@or@h{#1}% \fi }% find@qrange -\def\@tblquestions#1{% - % \tbl@firstq and \tbl@lastq have already been set. - % The argument is either ``v'' or ``h', and we branch accordingly. - \if v#1% - \@vtblquestions +\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 - \if h#1% - \@htblquestions + \ifx\exam@temp\@href + \check@num@rows@h \else \ClassError{exam}{% - The first optional argument to \protect\gradetable,\MessageBreak - \space\space \protect\bonusgradetable, - \protect\pointtable, or\MessageBreak - \space\space \protect\bonuspointtable - \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 and point 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 }% - \if@scores - \if@bonus - \fbox{Error: bonusgradetable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: gradetable: Invalid first optional argument - `#1'.}% - \fi - \else - \if@bonus - \fbox{Error: bonuspointtable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: pointtable: Invalid first optional argument - `#1'.}% - \fi - \fi + \fbox{\textbf{Error:} grade or point table: Invalid argument + `#1' must be `v' or `h'.}% \fi \fi -}% @tblquestions - -%-------------------------------------------------------------------- -% Vertical, indexed by question numbers: - -\def\@vtblquestions{% - % The table is vertical and indexed by question numbers. - % The question range has already been determined. - % Branch according to whether it's bonus, non-bonus, or combined, - % and gradetable or pointtable: - \begingroup - % Save the current value of question in @iterator, so that - % we can restore it after doing the table: - \setcounter{@iterator}{\value{question}}% - \renewcommand\arraystretch{\@gtblstretch}% - \if@bonus - \if@scores - \@bvgrdtblquestions - \else - \@bvpttblquestions - \fi - \else - \if@combined - \if@scores - \@cvgrdtblquestions - \else - \@cvpttblquestions - \fi - \else - \if@scores - \@vgrdtblquestions - \else - \@vpttblquestions - \fi - \fi - \fi - % Restore the saved value of question: - \setcounter{question}{\value{@iterator}}% - \endgroup -}% @vtblquestions - -\def\@cvgrdtblquestions{% - % Vertical combined gradetable, indexed by questions - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|c|c|} - \hline - {\@cvqword}& {\@cvpword}& {\@cvbpword}& {\@cvsword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@cvloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints& - \hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% -} -\def\do@cvloop{% - % \do@cvloop is used by \@cvgrdtblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}& - \bonuspointsofquestion{\arabic{question}}&\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@cvloop=\do@cvloop - \else - \let\next@cvloop=\relax - \fi - \next@cvloop -}% do@cvloop - - -\def\@cvpttblquestions{% - % Vertical combined pointtable, indexed by questions - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|c|} - \hline - {\@cvqword}& {\@cvpword}& {\@cvbpword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@cvptloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints\\ - \hline - \end{tabular}% -} -\def\do@cvptloop{% - % \do@cvptloop is used by \@cvpttblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}& - \bonuspointsofquestion{\arabic{question}}\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@cvptloop=\do@cvptloop - \else - \let\next@cvptloop=\relax - \fi - \next@cvptloop -}% do@cvptloop - - -\def\@bvgrdtblquestions{% - % Vertical bonus gradetable, indexed by questions: - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|c|} - \hline - {\@bvqword}& {\@bvpword}& {\@bvsword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bvloop - {\@bvtword}& \prt@tablebonuspoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% -}% @bvgrdtblquestions -\def\do@bvloop{% - % \do@bvloop is used by \@bvgrdtblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \bonuspointsofquestion{\arabic{question}}&\\ - \hline - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@bvloop=\do@bvloop - \else - \let\next@bvloop=\relax - \fi - \next@bvloop -}% do@bvloop - - -\def\@bvpttblquestions{% - % Vertical bonus point table, indexed by questions: - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|} - \hline - {\@bvqword}& {\@bvpword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bvptloop - {\@bvtword}& \prt@tablebonuspoints\\ - \hline - \end{tabular}% -}% @bvpttblquestions -\def\do@bvptloop{% - % \do@bvptloop is used by \@bvpttblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \bonuspointsofquestion{\arabic{question}}\\ - \hline - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@bvptloop=\do@bvptloop - \else - \let\next@bvptloop=\relax - \fi - \next@bvptloop -}% do@bvptloop - -\def\@vgrdtblquestions{% - % Vertical non-bonus grade table, indexed by questions: - \set@hlfcntr{tbl@points}{0}% - \begin{tabular}{|c|c|c|} - \hline - {\@vqword}& {\@vpword}& {\@vsword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@vloop - {\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% -}% @vgrdtblquestions -\def\do@vloop{% - % \do@vloop is used by \@vgrdtblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}&\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@vloop=\do@vloop - \else - \let\next@vloop=\relax - \fi - \next@vloop -}% do@vloop - - -\def\@vpttblquestions{% - % Vertical non-bonus point table, indexed by questions: - \set@hlfcntr{tbl@points}{0}% - \begin{tabular}{|c|c|} - \hline - {\@vqword}& {\@vpword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@vptloop - {\@vtword}& \prt@tablepoints\\ - \hline - \end{tabular}% -}% @vpttblquestions -\def\do@vptloop{% - % \do@vptloop is used by \@vpttblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@vptloop=\do@vptloop - \else - \let\next@vptloop=\relax - \fi - \next@vptloop -}% do@vptloop - - -%-------------------------------------------------------------------- -% Horizontal, indexed by question numbers: - -\def\@htblquestions{% - % We get here from \@tblquestions. - % The table is horizontal and indexed by question numbers. - % The question range has already been determined. - % Set num@cols equal to the number of questions on the table, and - % do either bonus, combined, or non-bonus table, in each case - % putting in a line for scores only if it's a gradetable: - \@ifundefined{exam@numquestions}% - {}% - {% - \setcounter{num@cols}{\tbl@lastq}% - \addtocounter{num@cols}{-\tbl@firstq}% - \addtocounter{num@cols}{1}% - }% - % Do either bonus, combined, or non-bonus table. In each case, put - % in a line for scores if it's a gradetable: - \begingroup - % Save the current value of question in @iterator, so that - % we can restore it after doing the table: - \setcounter{@iterator}{\value{question}}% - \renewcommand\arraystretch{\@gtblstretch}% - \if@bonus - % It's a horizontal bonus table, by questions: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@bhqword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@qnumloop - {\@bhtword}\\ - \hline - \set@hlfcntr{tbl@bonuspoints}{0}% - {\@bhpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, add in the score line: - \if@scores - {\@bhsword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@sloop - \\ - \hline - \fi - \end{tabular}% - \else - \if@combined - % It's a horizontal combined table, by questions: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@chqword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@qnumloop - {\@chtword}\\ - \hline - \set@hlfcntr{tbl@points}{0}% - {\@chpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@ptloop - \prt@tablepoints\\ - \hline - \set@hlfcntr{tbl@bonuspoints}{0}% - {\@chbpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, add in the score line: - \if@scores - {\@chsword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@sloop - \\ - \hline - \fi - \end{tabular}% - \else - % Horizontal non-bonus table, indexed by question number: - \set@hlfcntr{tbl@points}{0}% - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@hqword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@qnumloop - {\@htword}\\ - \hline - {\@hpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@ptloop - \prt@tablepoints\\ - \hline - % If it's a grade table, add in the score line: - \if@scores - {\@hsword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@sloop - \\ - \hline - \fi - \end{tabular}% - \fi - \fi - % Restore the saved value of question: - \setcounter{question}{\value{@iterator}}% - \endgroup -}% @htblquestions - - -% \do@qnumloop and \do@sloop are used by non-bonus, -% combined, and bonus tables: -\def\do@qnumloop{% - \addtocounter{question}{1}% - \ref{question@\arabic{question}}& - \ifnum \value{question} < \tbl@lastq\relax - \let\next@qnloop=\do@qnumloop - \else - \let\next@qnloop=\relax - \fi - \next@qnloop -}% do@qnumloop -\def\do@sloop{% - \addtocounter{question}{1}% - \hbox to \@cellwidth{\hfill}& - \ifnum \value{question} < \tbl@lastq\relax - \let\next@sloop=\do@sloop - \else - \let\next@sloop=\relax - \fi - \next@sloop -}% do@sloop - -% \do@ptloop is used by combined and non-bonus tables: -\def\do@ptloop{% - \addtocounter{question}{1}% - \pointsofquestion{\arabic{question}}& - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@ptloop=\do@ptloop - \else - \let\next@ptloop=\relax - \fi - \next@ptloop -}% do@ptloop - -% \do@bptloop is used by bonus and combined tables: -\def\do@bptloop{% - \addtocounter{question}{1}% - \bonuspointsofquestion{\arabic{question}}& - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@bptloop=\do@bptloop - \else - \let\next@bptloop=\relax - \fi - \next@bptloop -}% do@bptloop - +}% tbl@v@or@h %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -5374,18 +5913,23 @@ % \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 -% \@tblpages. +% \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''. - % After determining the first and last page of the range, we - % call \@tblpages, passing it that argument (i.e., we say - % \@tblpages{#1}). - % If not a partial table, we set \tbl@firstp to 1 and \tbl@lastp to + % 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}% {% @@ -5399,811 +5943,1524 @@ {% \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{Error: Grading Range `\tbl@range ': + \fbox{\textbf{Error:} Grading Range `\tbl@range ': Last page precedes first page.}% \ClassError{exam}{% - In grading range `\tbl@range ', the last page\MessageBreak + 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 - \@tblpages{#1}% + \check@secondrun{#1}% \fi }% }% \else + % It's not a partial table: \def\tbl@firstp{1}% - % \tbl@lastp isn't used on the first run of LaTeX, and + \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 - \@tblpages{#1}% + \check@secondrun{#1}% \fi }% find@prange - -\def\@tblpages#1{% +\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 \@whichtblpgs{#1}: - \@ifundefined{lastpage@withpoints}% - {\ClassWarning{exam}{% - You must run LaTeX twice more\MessageBreak - \space\space to produce the table.\MessageBreak}% - \fbox{Run \LaTeX{} twice more to produce the table}% - }% - {% - \@ifundefined{pointsonpage@\romannumeral + % 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\MessageBreak - \space\space to produce the table.\MessageBreak}% + You must run LaTeX again to produce the table.\MessageBreak}% \fbox{Run \LaTeX{} again to produce the table}% }% - {% - \@whchtblpgs#1 + {\tbl@v@or@h{#1}% }% }% -}% @tblpages - -\def\@whchtblpgs#1{% - % At this point, we know the table is indexed by pages. - % It can be vertical or horizontal, - % grade or point, and bonus or non-bonus. - % The argument is either ``v'' or ``h''; test it, and branch: - \if v#1% - \@vtblpages - \else - \if h#1% - \@htblpages - \else - \ClassError{exam}{% - The first optional argument to - \protect\gradetable,\MessageBreak - \space\space \protect\bonusgradetable, - \protect\pointtable, or\MessageBreak - \space\space - \protect\bonuspointtable \space must be either `h' or - `v'\MessageBreak - }{% - Grade tables and point tables can be either horizontal or - vertical;\MessageBreak - \space\space no diagonals allowed.\MessageBreak - }% - \if@scores - \if@bonus - \fbox{Error: bonusgradetable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: gradetable: Invalid first optional argument - `#1'.}% - \fi - \else - \if@bonus - \fbox{Error: bonuspointtable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: pointtable: Invalid first optional argument - `#1'.}% - \fi - \fi - \fi - \fi -}% \@whchtblpgs + {\tbl@v@or@h{#1}% + }% +}% check@secondrun %-------------------------------------------------------------------- -% Vertical, indexed by pages: - -\def\@vtblpages{% - % We get here from \@whchtblpgs. - % At this point, we know it's a vertical table indexed by pages. - % It can be grade or point, and bonus, non-bonus, or combined. - \begingroup - \renewcommand\arraystretch{\@gtblstretch}% - \if@bonus - % It's vertical, bonus, by pages, either point or grade: - \set@hlfcntr{tbl@bonuspoints}{0}% - \if@scores - \begin{tabular}{|c|c|c|} - \hline - {\@bvpgword}& {\@bvpword}& {\@bvsword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@bvloop - {\@bvtword}& \prt@tablebonuspoints& - \hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \else - \begin{tabular}{|c|c|} - \hline - {\@bvpgword}& {\@bvpword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@ptbvloop - {\@bvtword}& \prt@tablebonuspoints\\ - \hline - \end{tabular}% - \fi +% 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 - \if@combined - % It's vertical, combined, by pages, either point or grade: - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \if@scores - % Combined gradetable, vertical, by pages: - \begin{tabular}{|c|c|c|c|} - \hline - {\@cvpgword}& {\@cvpword}& {\@cvbpword}& {\@cvsword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@cvloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints& - \hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \else - % Combined pointtable, vertical, by pages: - \begin{tabular}{|c|c|c|} - \hline - {\@cvpgword}& {\@cvpword}& {\@cvbpword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@ptcvloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints\\ - \hline - \end{tabular}% - \fi - \else - % It's non-bonus, either point or grade: - \set@hlfcntr{tbl@points}{0}% - \if@scores - \begin{tabular}{|c|c|c|} - \hline - {\@vpgword}& {\@vpword}& {\@vsword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@vloop - {\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \else - \begin{tabular}{|c|c|} - \hline - {\@vpgword}& {\@vpword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@ptvloop - {\@vtword}& \prt@tablepoints\\ - \hline - \end{tabular}% - \fi - \fi + \docount@pgswpts{#1}% \fi - \endgroup -}% @vtblpages - + \fi +}% count@pgswpts -% \pg@ptvloop and \pg@ptvloopline are used only by non-bonus pointtable: -\def\pg@ptvloop{% +\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}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pg@ptvloopline + \addtocounter{#1}{1}% + \else + \check@bnsptpage{#1}% \fi \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@ptvloop=\pg@ptvloop + \def\nextdocount@pgswcpts{\docount@pgswcpts{#1}}% \else - \let\next@pg@ptvloop=\relax + \let\nextdocount@pgswcpts=\relax \fi - \next@pg@ptvloop -}% pg@ptvloop -\def\pg@ptvloopline{% - % 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. - \pageref{firstpoints@onpage@\arabic{@iterator}} & - \pointsonpage{\the@iterator}\\ - \hline -}% pg@ptvloopline + \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 -% \pg@vloop and \pg@vloopline are used only by non-bonus gradetable: -\def\pg@vloop{% +\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}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pg@vloopline + \addtocounter{#1}{1}% \fi \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@vloop=\pg@vloop + \def\nextdocount@pgswpts{\docount@pgswpts{#1}}% \else - \let\next@pg@vloop=\relax + \let\nextdocount@pgswpts=\relax \fi - \next@pg@vloop -}% 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. - \pageref{firstpoints@onpage@\arabic{@iterator}} & - \pointsonpage{\the@iterator}&\\ - \hline -}% pg@vloopline + \nextdocount@pgswpts +}% docount@pgswpts -% \pg@cvloop, \pg@noncvloopline, \check@bnspts, and -% \pg@cvloopline are used only by combined gradetable: -\def\pg@cvloop{% +\def\docount@pgswbpts#1{% + % Called by \count@pgswpts + % Count the number of pages in range with bonus points. \addtocounter{@iterator}{1}% - % Add the bonus points: \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - % Add the regular points: - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - % Print a line of the table, if there are points on this page: \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@noncvloopline - \else - \check@bnspts + \addtocounter{#1}{1}% \fi - % Do the tail recursion bit: \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@cvloop=\pg@cvloop + \def\nextdocount@pgswbpts{\docount@pgswbpts{#1}}% \else - \let\next@pg@cvloop=\relax + \let\nextdocount@pgswbpts=\relax \fi - \next@pg@cvloop -}% pg@cvloop -\def\pg@noncvloopline{% - % 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. - \pageref{firstpoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}&\\ - \hline -}% pg@noncvloopline -\def\check@bnspts{% - % 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. - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@cvloopline + \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 -}% check@bnspts -\def\pg@cvloopline{% - % 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. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}&\\ - \hline -}% pg@cvloopline +}% 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 -% \pg@ptcvloop, \pg@ptnoncvloopline, \check@ptbnspts, and -% \pg@ptcvloopline are used only by combined pointtable: -\def\pg@ptcvloop{% - \addtocounter{@iterator}{1}% - % Add the bonus points: - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - % Add the regular points: - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - % Print a line of the table, if there are points on this page: - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@ptnoncvloopline +\def\pointsof@index#1{% + \iftbl@pgs + \pointsonpage{\arabic{#1}}% \else - \check@ptbnspts + \pointsofquestion{\arabic{#1}}% \fi - % Do the tail recursion bit: - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@ptcvloop=\pg@ptcvloop +}% pointsof@index + +\def\bonuspointsof@index#1{% + \iftbl@pgs + \bonuspointsonpage{\arabic{#1}}% \else - \let\next@pg@ptcvloop=\relax + \bonuspointsofquestion{\arabic{#1}}% \fi - \next@pg@ptcvloop -}% pg@ptcvloop -\def\pg@ptnoncvloopline{% - % 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. - \pageref{firstpoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}\\ - \hline -}% pg@ptnoncvloopline -\def\check@ptbnspts{% - % 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. - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@ptcvloopline +}% 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 -}% check@ptbnspts -\def\pg@ptcvloopline{% - % 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. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}\\ - \hline -}% pg@ptcvloopline +}% refto@index -% \pg@bvloop and \pg@bvloopline are used only by bonus gradetable: -\def\pg@bvloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% +\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}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \pg@bvloopline + \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 - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@bvloop=\pg@bvloop +}% 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 - \let\next@pg@bvloop=\relax + \@computenumcols@h \fi - \next@pg@bvloop -}% pg@bvloop -\def\pg@bvloopline{% - % 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. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}} & - \bonuspointsonpage{\the@iterator}&\\ - \hline -}% pg@bvloopline +}% 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 -% \pg@ptbvloop and \pg@ptbvloopline are used only by bonus pointtable: -\def\pg@ptbvloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \pg@ptbvloopline +%-------------------------------------------------------------------- +% 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 - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@ptbvloop=\pg@ptbvloop + \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}% + \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 + % 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\next@pg@ptbvloop=\relax + \let\nextdo@sloop@h=\relax \fi - \next@pg@ptbvloop -}% pg@ptbvloop -\def\pg@ptbvloopline{% - % 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. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}} & - \bonuspointsonpage{\the@iterator}\\ - \hline -}% pg@ptbvloopline + \nextdo@sloop@h +}% do@sloop@h + %-------------------------------------------------------------------- -% Horizontal, indexed by pages: +%-------------------------------------------------------------------- +% Multicolumn tables -% For a horizontal table, before we begin we need to know how many -% pages there are with points on them: -\newcounter{num@cols} -\def\count@pgswpts{% - % We're called by \@htblpages. - % \count@pgswpts is used for horizontal - % grade tables and point tables, - % bonus, non-bonus, and combined. - \setcounter{num@cols}{0}% - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}% - \if@bonus - \docount@pgswbpts + +%-------------------------------------------------------------------- +% Here's an example of a multicolumn grade table indexed by questions, +% to show how we're getting the \cline's to link up correctly with the +% \vline's. + +% 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} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% Question% +% \hspace*{\fill}% +% & Points% +% & \hspace*{\fill}% +% Score% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% Question% +% \hspace*{\fill}% +% & Points% +% & \hspace*{\fill}% +% Score% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% 1\hspace*{\fill}% +% & 5% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline\hspace*{\tabcolsep}\hspace*{\fill}% +% 4% +% \hspace*{\fill}% +% & 20% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% 2% +% \hspace*{\fill}% +% & 10% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline\hspace*{\tabcolsep}\hspace*{\fill}% +% 5% +% \hspace*{\fill}% +% & 25% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% 3% +% \hspace*{\fill}% +% & 15% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline\hspace*{\tabcolsep}\hspace*{\fill}% +% Total:% +% \hspace*{\fill}% +% & 75% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \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 - \if@combined - \docount@pgswcpts + \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 - \docount@pgswpts + \@computenumrows@v \fi \fi -}% count@pgswpts - -\def\docount@pgswcpts{% - % Called by \count@pgswpts - % \docount@pgswcpts is used for combined gradetable. - % 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{num@cols}{1}% - \else - \check@bnsptpage - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@docount=\docount@pgswcpts +}% 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 - \let\next@docount=\relax + \setcounter{num@rows}{\last@pq@index}% + \addtocounter{num@rows}{-\first@pq@index}% + \addtocounter{num@rows}{2}% \fi - \next@docount -}% docount@pgswcpts -\def\check@bnsptpage{% - % We don't understand why we need to hide this inside of a macro: - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% + % 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 -}% check@bnsptpage + \@multicolumntable +}% @computenumrows@v -\def\docount@pgswpts{% - % Called by \count@pgswpts - % \docount@pgswpts is used for non-bonus gradetable: - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@docount=\docount@pgswpts +%-------------------------------------------------------------------- +% 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 - \let\next@docount=\relax + % 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 - \next@docount -}% docount@pgswpts +}% @multicolumntable -\def\docount@pgswbpts{% - % Called by \count@pgswpts - % \docount@pgswbpts is used for bonusgradetable: - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@docount=\docount@pgswbpts +%-------------------------------------------------------------------- +% \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 - \let\next@docount=\relax + \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 - \next@docount -}% docount@pgswbpts +}% create@cline@stuff@v %-------------------------------------------------------------------- - -\def\@htblpages{% - % We get here from \@whchtblpgs. - % Horizontal grade or point table, by pages: - \begingroup - \renewcommand\arraystretch{\@gtblstretch}% - % Set num@cols equal to the number of pages with points (of - % correct type): - \count@pgswpts - % Suppress ``extra alignment tab'' errors if \thenum@cols = 0: - \ifnum \thenum@cols = 0\relax - \setcounter{num@cols}{1}% +% The various \docolumn@heads@something@v + +\def\docolumn@heads@v{% + % Called by \@multicolumntable. + % multicolumngradetable or multicolumnbonusgradetable, possibly + % partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \if@bonus + \@bvpgword + \else + \@vpgword + \fi + \else + \if@bonus + \@bvqword + \else + \@vqword + \fi + \fi + \hspace*{\fill}% + & \if@bonus + \@bvpword + \else + \@vpword \fi - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% + & \hspace*{\fill}% \if@bonus - % Horizontal bonus grade or point table, by pages: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@bhpgword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@bpgnumloop - {\@bhtword}\\ - \hline - {\@bhpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@bpgptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, do the line for scores: - \if@scores - {\@bhsword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@bpgsloop - \\ - \hline - \fi - \end{tabular}% + \@bvsword \else - \if@combined - % Horizontal combined grade or point table, by pages: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@chpgword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cpgnumloop - {\@chtword}\\ - \hline - {\@chpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cpgptloop - \prt@tablepoints\\ - \hline - {\@chbpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cbpgptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, do the line for scores: - \if@scores - {\@chsword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cpgsloop - \\ - \hline - \fi - \end{tabular}% + \@vsword + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \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. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \if@bonus + \@bvpgword \else - % Horizontal non-bonus grade or point table, by pages: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@hpgword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@pgnumloop - {\@htword}\\ - \hline - {\@hpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@pgptloop - \prt@tablepoints\\ - \hline - % If it's a grade table, do the line for scores: - \if@scores - {\@hsword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@pgsloop - \\ - \hline - \fi - \end{tabular}% + \@vpgword + \fi + \else + \if@bonus + \@bvqword + \else + \@vqword \fi \fi - \endgroup -}% @htblpages - -%-------------------------------------------------------------------- -% \do@pgnumloop, \do@pgptloop, and \do@pgsloop -% are only for non-bonus gradetable. -% \pg@line is also for combinedgradetable. -\def\do@pgnumloop{% + \hspace*{\fill}% + & \hspace*{\fill}% + \if@bonus + \@bvpword + \else + \@vpword + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@line - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pgnumloop=\do@pgnumloop + \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\next@pgnumloop=\relax + \let\nextdocolumn@heads@noscores@v=\relax \fi - \next@pgnumloop -}% \do@pgnumloop -\def\pg@line{% - % We still don't know why we need to hide this inside of a macro: - \pageref{firstpoints@onpage@\arabic{@iterator}}& -}% \pg@line - -\def\do@pgptloop{% + \nextdocolumn@heads@noscores@v +}% docolumn@heads@noscores@v + +\def\docolumn@heads@comb@v{% + % Called by \@multicolumntable. + % multicolumncombinedgradetable, possibly partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \@cvpgword + \else + \@cvqword + \fi + \hspace*{\fill}% + & \@cvpword + & \@cvbpword + & \hspace*{\fill}% + \@cvsword + \hspace*{\tabcolsep}\hspace*{\fill}\vline \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pgpt@entry - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pgptloop=\do@pgptloop + \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\next@pgptloop=\relax + \let\nextdocolumn@heads@comb@v=\relax \fi - \next@pgptloop -}% \do@pgptloop -% \pgpt@entry is used by different macros: -\def\pgpt@entry{% - % We still don't know why we need to hide this inside of a macro: - \pointsonpage{\the@iterator}& -}% \pgpt@entry - -\def\do@pgsloop{% + \nextdocolumn@heads@comb@v +}% docolumn@heads@comb@v + +\def\docolumn@heads@comb@noscores@v{% + % Called by \@multicolumntable. + % multicolumncombinedpointtable, possibly partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \@cvpgword + \else + \@cvqword + \fi + \hspace*{\fill}% + & \@vpword + & \hspace*{\fill}% + \@bvpword + \hspace*{\tabcolsep}\hspace*{\fill}\vline \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pgsloop=\do@pgsloop + \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@pgsloop=\relax + \let\nextdocolumn@heads@comb@noscores@v=\relax \fi - \next@pgsloop -}% \do@pgsloop -% \pg@sentry is called by several different macros, -% to create different tables: -\def\pg@sentry{% - % We still don't know why we need to hide this inside of a macro: - \hbox to \@cellwidth{\hfill}& -}% \pg@sentry + \nextdocolumn@heads@comb@noscores@v +}% docolumn@heads@comb@noscores@v %-------------------------------------------------------------------- -% These things are for bonusgradetable: - -% \do@bpgnumloop, \do@bpgptloop, \do@bpgsloop, -% and \bpg@sline are only for bonusgradetable. -% \bpg@line is also used for combinedgradetable. -\def\do@bpgnumloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \bpg@line - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@bpgnumloop=\do@bpgnumloop - \else - \let\next@bpgnumloop=\relax - \fi - \next@bpgnumloop -}% do@bpgnumloop -\def\bpg@line{% - % We still don't know why we need to hide this inside of a macro: - \pageref{firstbonuspoints@onpage@\arabic{@iterator}}& -}% bpg@line - -\def\do@bpgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \bpgpt@line - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@bpgptloop=\do@bpgptloop +% \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 - \let\next@bpgptloop=\relax - \fi - \next@bpgptloop -}% do@bpgptloop -% \bpgpt@line is called by several macros: -\def\bpgpt@line{% - % We still don't know why we need to hide this inside of a macro: - \bonuspointsonpage{\the@iterator}& -}% bpgpt@line - -\def\do@bpgsloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry + \do@oneline@v \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@bpgsloop=\do@bpgsloop + \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}% + \let\nextdo@lines@v=\relax \else - \let\next@bpgsloop=\relax + \\ + \cline@stuff@v + \let\nextdo@lines@v=\do@lines@v \fi - \next@bpgsloop -}% do@bpgsloop - -%-------------------------------------------------------------------- -% These things are for combinedgradetable: - -% \do@cpgnumloop, \cpg@line, \cbpg@line, \do@cpgptloop, \bpgpt@line, -% \do@cpgsloop, \bpg@sline, and \do@cbpgptloop are only for -% combinedgradetable: -\def\do@cpgnumloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@line + \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: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \if@bonus + \@bvtword + \else + \@vtword + \fi + \hspace*{\fill}% + \hidden@ampersand + \if@scores + \if@bonus + \prt@tablebonuspoints + \else + \prt@tablepoints + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \if@bonus + \prt@tablebonuspoints + \else + \prt@tablepoints + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Not last column last row; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Not last column; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \else - \check@bpgpts + % We need to do question (or page) number pq@index: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \refto@index{pq@index}% + \hspace*{\fill}% + \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 + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \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 + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cpgnumloop=\do@cpgnumloop + \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@cpgnumloop=\relax - \fi - \next@cpgnumloop -}% do@bpgnumloop -\def\check@bpgpts{% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \bpg@line + \let\nextdo@oneline@v=\relax \fi -}% check@bpgpts - -\def\do@cpgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pgpt@entry + \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: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \@cvtword + \hspace*{\fill}% + \hidden@ampersand + \prt@tablepoints + \hidden@ampersand + \if@scores + \prt@tablebonuspoints + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \prt@tablebonuspoints + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Last column, but not last row; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Not last column; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \else - \check@cbpts + % We need to do question number pq@index: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \refto@index{pq@index}% + \hspace*{\fill}% + \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 + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cpgptloop=\do@cpgptloop + \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@cpgptloop=\relax - \fi - \next@cpgptloop -}% do@cpgptloop -\def\check@cbpts{% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pgpt@entry + \let\nextdo@oneline@comb@v=\relax \fi -}% check@cbpts + \nextdo@oneline@comb@v +}% do@oneline@comb@v -\def\do@cbpgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \bpgpt@line - \else - \check@cnonpts - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cbpgptloop=\do@cbpgptloop +%-------------------------------------------------------------------- +% \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@cbpgptloop=\relax - \fi - \next@cbpgptloop -}% do@cbpgptloop -\def\check@cnonpts{% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \bpgpt@line + \if@bonus + \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}% + \else + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}% + \fi \fi -}% check@cnonpts - -\def\do@cpgsloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry + \let\nextfind@nextpagewithpoints=\relax \else - \check@bnsline + \ifnum \value{#1} > \tbl@lastp\relax + \let\nextfind@nextpagewithpoints=\relax + \else + \def\nextfind@nextpagewithpoints{\find@nextpagewithpoints{#1}}% + \fi \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cpgsloop=\do@cpgsloop + \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@cpgsloop=\relax - \fi - \next@cpgsloop -}% do@cpgsloop -\def\check@bnsline{% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry + % 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 -}% check@bnsline + \nextdofind@nextcolumnpage@v +}% dofind@nextcolumnpage@v %-------------------------------------------------------------------- -% \pointsinrange and \bonuspointsinrange: +% \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\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\read@range}% }% pointsinrange @@ -6211,19 +7468,22 @@ \@bonustrue \def\tbl@range{#1}% \@ifundefined{exam@numpoints}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\read@range}% }% bonuspointsinrange \def\bad@range{% - {\mbox{\normalfont\bf ??}}% + % Called by \read@range, \firstqinrange, \lastqinrange, and + % \numqinrange. + {\mbox{\normalfont\bfseries ??}}% \ClassWarning{exam}{% - Grading range `\tbl@range ' not defined.\MessageBreak + 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 @@ -6238,7 +7498,7 @@ \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}% % Check that firstq precedes or equals lastq: \ifnum \tbl@firstq > \tbl@lastq\relax - \fbox{Error: Grading Range `\tbl@range ': + \fbox{\textbf{Error:} Grading Range `\tbl@range ': Last question precedes first question.}% \ClassError{exam}{% In grading range `\tbl@range ', @@ -6289,26 +7549,29 @@ % \firstqinrange, \lastqinrange, and \numqinrange. \newcommand{\firstqinrange}[1]{% - \@ifundefined{range@#1@firstq}% - {\mbox{\normalfont\bf ??}}% + \def\tbl@range{#1}% + \@ifundefined{range@\tbl@range @firstq}% + {\bad@range}% {\csname range@#1@firstq\endcsname}% }% firstqinrange \newcommand{\lastqinrange}[1]{% - \@ifundefined{range@#1@lastq}% - {\mbox{\normalfont\bf ??}}% + \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}% {% - \mbox{\normalfont\bf ??}% + \bad@range }% {% \@ifundefined{range@#1@lastq}% {% - \mbox{\normalfont\bf ??}% + \bad@range }% {% \setcounter{@iterator}{\csname range@#1@lastq\endcsname}% @@ -6319,6 +7582,7 @@ }% }% numqinrange + %-------------------------------------------------------------------- %-------------------------------------------------------------------- % @@ -6632,12 +7896,63 @@ % 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 - \@tempdima=\textwidth +% 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 @@ -6654,6 +7969,11 @@ \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 }% @@ -6673,7 +7993,8 @@ % \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: - \hbox to \textwidth{% + % 2016/02/08: Changed \textwidth to \hsize: + \hbox to \hsize{% \noindent \hskip\@totalleftmargin \hskip3\fboxsep\hskip\fboxrule @@ -6682,14 +8003,40 @@ \par\nointerlineskip \fi \endgroup % Finish confining the \Solution@Emphasis - \makeemptybox{\solutionbox@size}% - } +% 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 color.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 %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -6945,7 +8292,19 @@ \catcode`\|=\FrameRestore % Provide configuration commands: -\providecommand\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep \fbox} +%psh: Version 2.502, 2016/03/23, changed \FrameCommand so that the +% frame is printed in color if the user has said +% \colorsolutionboxes: +%\providecommand\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep \fbox} +\def\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep + \if@colorsolutionboxes + \def\box@it{\colorfbox{SolutionBoxColor}}% + \else + \def\box@it{\fbox}% + \fi + \box@it +}% \FrameCommand + \@ifundefined{FrameRule}{\newdimen\FrameRule \FrameRule=\fboxrule}{} \@ifundefined{FrameSep} {\newdimen\FrameSep \FrameSep =3\fboxsep}{} -- cgit v1.2.3