summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/exam
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2017-11-16 22:14:35 +0000
committerKarl Berry <karl@freefriends.org>2017-11-16 22:14:35 +0000
commit08c9009c085b5fb5cd2151a112662e3c689b67f7 (patch)
tree80a04ebaddc8ad615d6c2cbcffa758c540359f70 /Master/texmf-dist/tex/latex/exam
parentd703ad4829afa66b9973031c99a5dc1e9dd0c2ba (diff)
exam (16nov17)
git-svn-id: svn://tug.org/texlive/trunk@45832 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/exam')
-rw-r--r--Master/texmf-dist/tex/latex/exam/exam.cls3757
1 files changed, 2558 insertions, 1199 deletions
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}
%---------------------------------------------------------------------
%---------------------------------------------------------------------
%
@@ -85,6 +86,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
% No longer betatest.
@@ -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,6 +5244,248 @@
% points for the questions (or pages) that appear on the table:
\new@hlfcntr{tbl@bonuspoints}
+%--------------------------------------------------------------------
+% multirow tables, non-partial:
+
+\def\multirowgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowbonusgradetable#1{%
+ \@scorestrue
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowbonuspointtable#1{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowcombinedgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowcombinedpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+%--------------------------------------------------------------------
+% multirow tables, partial:
+
+\def\multirowpartialgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialbonusgradetable#1#2{%
+ \@scorestrue
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialbonuspointtable#1#2{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialcombinedgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialcombinedpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+%--------------------------------------------------------------------
+% multicolumn tables, non-partial:
+
+\def\multicolumngradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnbonusgradetable#1{%
+ \@scorestrue
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnbonuspointtable#1{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumncombinedgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumncombinedpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+%--------------------------------------------------------------------
+% multicolumn tables, partial:
+
+\def\multicolumnpartialgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialbonusgradetable#1#2{%
+ \@scorestrue
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialbonuspointtable#1#2{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialcombinedgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialcombinedpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+%--------------------------------------------------------------------
+% partial single row (and column) tables:
\def\partialgradetable#1{%
\@scorestrue
@@ -4624,6 +5493,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:
@@ -4636,6 +5511,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:
@@ -4648,6 +5529,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:
@@ -4660,6 +5547,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:
@@ -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
- \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
+ \nextdocount@pgswbpts
+}% docount@pgswbpts
-% \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
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% 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
- \check@ptbnspts
+ \addtocounter{#1}{1}%
\fi
- % Do the tail recursion bit:
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@pg@ptcvloop=\pg@ptcvloop
+}% 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
- \let\next@pg@ptcvloop=\relax
+ \addtocounter{#1}{\value{num@rows}}%
\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
+}% nextcolumn@index@v
+
+\def\pointsof@index#1{%
+ \iftbl@pgs
+ \pointsonpage{\arabic{#1}}%
+ \else
+ \pointsofquestion{\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
+}% pointsof@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}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
- \pg@bvloopline
+\def\bonuspointsof@index#1{%
+ \iftbl@pgs
+ \bonuspointsonpage{\arabic{#1}}%
+ \else
+ \bonuspointsofquestion{\arabic{#1}}%
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@pg@bvloop=\pg@bvloop
+}% 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
- \let\next@pg@bvloop=\relax
+ \ref{question@\arabic{#1}}%
\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
+}% refto@index
-% \pg@ptbvloop and \pg@ptbvloopline are used only by bonus pointtable:
-\def\pg@ptbvloop{%
- \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@ptbvloopline
+ \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@ptbvloop=\pg@ptbvloop
+}% 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@ptbvloop=\relax
+ \@computenumcols@h
\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
+}% 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
%--------------------------------------------------------------------
-% Horizontal, indexed by pages:
+% Construct the actual table:
-% 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}%
+\def\@multirowtable{%
+ % We get here from \@computenumcols@h.
+ % All multirow tables!
+ \renewcommand\arraystretch{\@gtblstretch}%
+ \set@hlfcntr{tbl@points}{0}%
+ \set@hlfcntr{tbl@bonuspoints}{0}%
+ \setcounter{pq@index}{\first@pq@index}%
+ \addtocounter{pq@index}{-1}%
+ \setcounter{pq@index@pts}{\value{pq@index}}%
+ \setcounter{pq@index@bpts}{\value{pq@index}}%
+ \setcounter{current@row}{0}%
+ \begin{tabular}{|l|*{\value{num@cols}}{c|}}
+ \hline
+ \if@combined
+ \do@comblines@h
+ \else
+ \do@lines@h
+ \fi
+}% @multirowtable
+
+
+\def\do@lines@h{%
+ % Called only by \@multirowtable.
+ % It's either bonus or regular, but not combined:
+ \addtocounter{current@row}{1}% Set to the number of the current row
+ \iftbl@pgs
+ \if@bonus
+ \@bhpgword
+ \else
+ \@hpgword
+ \fi
+ \else
+ \if@bonus
+ \@bhqword
+ \else
+ \@hqword
+ \fi
+ \fi
+ \setcounter{cols@done}{0}%
+ \do@pq@indexloop@h
+ % When we finish \do@pq@indexloop@h, either we've finished a
+ % complete row of page numbers (or questions), or we've done all
+ % the page numbers (or questions) through \last@pq@index, or both:
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ % We've inserted all the page or question numbers, and there's
+ % room remaining on the current line for \@htword (or \@bhtword):
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last row; put in the total:
+ \do@htword@h
+ \else
+ % This isn't the last row. We insert (\value{num@cols} -
+ % \value{cols@done}) ampersands.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \fi
+ \fi
+ \\
+ \hline
+ % Point values go here!
+ \setcounter{cols@done}{0}%
\if@bonus
- \docount@pgswbpts
+ \@bhpword
+ \do@bptloop@h
\else
- \if@combined
- \docount@pgswcpts
+ \@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
- \docount@pgswpts
+ % 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
-}% 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}%
+ % 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
- \check@bnsptpage
+ % Don't end the tabular:
+ \\
+ \hline\hline
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@docount=\docount@pgswcpts
+ % Check if we should repeat:
+ \ifnum \value{current@row} < \value{num@rows}\relax
+ \let\nextdo@lines@h=\do@lines@h
\else
- \let\next@docount=\relax
+ \let\nextdo@lines@h=\relax
\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}%
+ \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
-}% check@bnsptpage
-
-\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}%
+ \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
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@docount=\docount@pgswpts
+ \\
+ \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
- \let\next@docount=\relax
+ % Don't end the tabular:
+ \\
+ \hline\hline
\fi
- \next@docount
-}% docount@pgswpts
-
-\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}%
+ % 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
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@docount=\docount@pgswbpts
+ \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
- \let\next@docount=\relax
+ \hidden@ampersand
+ \refto@index{pq@index}%
+ \addtocounter{cols@done}{1}%
\fi
- \next@docount
-}% docount@pgswbpts
-
-%--------------------------------------------------------------------
-
-\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}%
+ \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
- \set@hlfcntr{tbl@points}{0}%
- \set@hlfcntr{tbl@bonuspoints}{0}%
- \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}%
+ \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
- \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}%
- \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}%
- \fi
+ \let\nextdo@ptloop@h=\relax
\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{%
- \addtocounter{@iterator}{1}%
- \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \pg@line
+ \else
+ \let\nextdo@ptloop@h=\relax
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@pgnumloop=\do@pgnumloop
+ \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
- \let\next@pgnumloop=\relax
+ \hidden@ampersand
+ \addtocounter{cols@done}{1}%
+ \bonuspointsof@index{pq@index@bpts}%
+ \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index@bpts}}%
\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{%
- \addtocounter{@iterator}{1}%
- \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
- \pgpt@entry
+ \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
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@pgptloop=\do@pgptloop
+ \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
- \let\next@pgptloop=\relax
+ \if@bonus
+ \@bhtword
+ \else
+ \@htword
+ \fi
\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{%
- \addtocounter{@iterator}{1}%
- \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \pg@sentry
+}% 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
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@pgsloop=\do@pgsloop
+ \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@pgsloop=\relax
+ \let\nextdo@sloop@h=\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
+ \nextdo@sloop@h
+}% do@sloop@h
+
%--------------------------------------------------------------------
-% These things are for bonusgradetable:
+%--------------------------------------------------------------------
+% Multicolumn tables
-% \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
+
+%--------------------------------------------------------------------
+% 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
- \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
+ \ifnum \value{num@cols} > 10\relax
+ \ClassError{exam}{%
+ Multicolumn tables can have at most 10 columns.\MessageBreak
+ }{%
+ Multicolumn tables can have at most 10 columns.\MessageBreak
+ }%
+ \fbox{\textbf{Error:} Multicolumn table with more than 10 columns!}%
+ \else
+ \@computenumrows@v
+ \fi
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@bpgptloop=\do@bpgptloop
+}% 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@bpgptloop=\relax
+ \setcounter{num@rows}{\last@pq@index}%
+ \addtocounter{num@rows}{-\first@pq@index}%
+ \addtocounter{num@rows}{2}%
\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
+ % 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
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@bpgsloop=\do@bpgsloop
+ \@multicolumntable
+}% @computenumrows@v
+
+%--------------------------------------------------------------------
+% Construct the actual table:
+
+\def\@multicolumntable{%
+ % We get here from \@computenumrows@v.
+ % Set \cline@stuff@v equal to the line of \cline's:
+ \create@cline@stuff@v
+ \renewcommand\arraystretch{\@gtblstretch}%
+ \set@hlfcntr{tbl@points}{0}%
+ \set@hlfcntr{tbl@bonuspoints}{0}%
+ \if@combined
+ \if@scores
+ % combinedgradetable, possibly partial.
+ % Note: We'll never use the final "c" in the format of the
+ % tabular, but there's no harm in that.
+ \begin{tabular}{*{\value{num@cols}}{@{}c|c|c|c@{}c}}
+ % We need to make sure that the \cline@stuff@v commands come
+ % *immediately* following the \\ or \begin{tabular} (with no
+ % conditionals evaluated, even if those conditionals expand to
+ % the empty string)!
+ % Put in the row of column headings, with \cline@stuff@v above and
+ % below:
+ \cline@stuff@v
+ \setcounter{@iterator}{0}%
+ \docolumn@heads@comb@v
+ \\
+ \cline@stuff@v
+ \else
+ % combinedpointtable, possibly partial.
+ % Note: We'll never use the final "c" in the format of the
+ % tabular, but there's no harm in that.
+ \begin{tabular}{*{\value{num@cols}}{@{}c|c|c@{}c}}
+ % We need to make sure that the \cline@stuff@v commands come
+ % *immediately* following the \\ or \begin{tabular} (with no
+ % conditionals evaluated, even if those conditionals expand to
+ % the empty string)!
+ % Put in the row of column headings, with \cline@stuff@v above and
+ % below:
+ \cline@stuff@v
+ \setcounter{@iterator}{0}%
+ \docolumn@heads@comb@noscores@v
+ \\
+ \cline@stuff@v
+ \fi
+ % pq@index@pts will hold the question number (or page number) in
+ % the first column of the row.
+ \setcounter{pq@index@pts}{\first@pq@index}%
+ \iftbl@pgs
+ % If we're indexed by pages, we need to make sure there are
+ % points of the appropriate type on the first page listed:
+ \addtocounter{pq@index@pts}{-1}%
+ \find@nextpagewithpoints{pq@index@pts}%
+ \fi
+ \setcounter{current@row}{0}%
+ \do@lines@v
\else
- \let\next@bpgsloop=\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@bpgsloop
-}% do@bpgsloop
+}% @multicolumntable
%--------------------------------------------------------------------
-% These things are for combinedgradetable:
+% \create@cline@stuff@v
+
+% The function \create@cline@stuff@v defines \cline@stuff@v to be whatever's
+% appropriate given the values of num@cols, \if@bonus, \if@combined, and
+% \if@scores.
+
+% We wimped out of generating \cline@stuff@v on the fly because we didn't
+% see how to get the correct expansions/nonexpansions without using a
+% primitive of e-TeX.
+
+% \clines@ii@whatever is for tables in which a logical column consists
+% of two columns; it's used for pointtable and bonuspointtable.
+
+\def\clines@ii@i{\cline{1-2}}
+\def\clines@ii@ii{\cline{1-2} \cline{4-5}}
+\def\clines@ii@iii{\cline{1-2} \cline{4-5} \cline{7-8}}
+\def\clines@ii@iv{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}}
+\def\clines@ii@v{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14}}
+\def\clines@ii@vi{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17}}
+\def\clines@ii@vii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20}}
+\def\clines@ii@viii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}}
+\def\clines@ii@vix{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}
+ \cline{25-26}}
+\def\clines@ii@x{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}
+ \cline{25-26} \cline{28-29}}
+
+% \clines@iii@whatever is for tables in which a logical column consists
+% of three columns; it's are used for gradetable, bonusgradetable, and
+% combinedpointtable:
+
+\def\clines@iii@i{\cline{1-3}}
+\def\clines@iii@ii{\cline{1-3} \cline{5-7}}
+\def\clines@iii@iii{\cline{1-3} \cline{5-7} \cline{9-11}}
+\def\clines@iii@iv{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}}
+\def\clines@iii@v{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19}}
+\def\clines@iii@vi{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23}}
+\def\clines@iii@vii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27}}
+\def\clines@iii@viii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}}
+\def\clines@iii@ix{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}
+ \cline{33-35}}
+\def\clines@iii@x{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}
+ \cline{33-35} \cline{37-39}}
+
+
+% \clines@iv@whatever is for tables in which a logical column
+% consists of four columns; it's used for combinedgradetable.
+
+\def\clines@iv@i{\cline{1-4}}
+\def\clines@iv@ii{\cline{1-4} \cline{6-9}}
+\def\clines@iv@iii{\cline{1-4} \cline{6-9} \cline{11-14}}
+\def\clines@iv@iv{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}}
+\def\clines@iv@v{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24}}
+\def\clines@iv@vi{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29}}
+\def\clines@iv@vii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34}}
+\def\clines@iv@viii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}}
+\def\clines@iv@ix{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}
+ \cline{41-44}}
+\def\clines@iv@x{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}
+ \cline{41-44} \cline{46-49}}
+
+% The definition of \cline ends with \noalign{\vskip-\arrayrulewidth},
+% and so we want to throw in a \noalign{\vskip\arrayrulewidth} to
+% cancel that.
+\def\cline@correction{\noalign{\vskip\arrayrulewidth}}
+
+\def\create@cline@stuff@v{%
+ % Called by \@multicolumntable.
+ \if@combined
+ \if@scores
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@iv@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \else
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@iii@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \fi
+ \else
+ \if@scores
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@iii@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \else
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@ii@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \fi
+ \fi
+}% create@cline@stuff@v
-% \do@cpgnumloop, \cpg@line, \cbpg@line, \do@cpgptloop, \bpgpt@line,
-% \do@cpgsloop, \bpg@sline, and \do@cbpgptloop are only for
-% combinedgradetable:
-\def\do@cpgnumloop{%
+%--------------------------------------------------------------------
+% 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
+ & \hspace*{\fill}%
+ \if@bonus
+ \@bvsword
+ \else
+ \@vsword
+ \fi
+ \hspace*{\tabcolsep}\hspace*{\fill}\vline
\addtocounter{@iterator}{1}%
- \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \pg@line
+ \ifnum \value{@iterator} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \hidden@ampersand
+ \let\nextdocolumn@heads@v=\docolumn@heads@v
\else
- \check@bpgpts
+ \let\nextdocolumn@heads@v=\relax
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@cpgnumloop=\do@cpgnumloop
+ \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
+ \@vpgword
+ \fi
+ \else
+ \if@bonus
+ \@bvqword
+ \else
+ \@vqword
+ \fi
+ \fi
+ \hspace*{\fill}%
+ & \hspace*{\fill}%
+ \if@bonus
+ \@bvpword
+ \else
+ \@vpword
+ \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@noscores@v=\docolumn@heads@noscores@v
\else
- \let\next@cpgnumloop=\relax
+ \let\nextdocolumn@heads@noscores@v=\relax
\fi
- \next@cpgnumloop
-}% do@bpgnumloop
-\def\check@bpgpts{%
- \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \bpg@line
+ \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}%
+ \ifnum \value{@iterator} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \hidden@ampersand
+ \let\nextdocolumn@heads@comb@v=\docolumn@heads@comb@v
+ \else
+ \let\nextdocolumn@heads@comb@v=\relax
\fi
-}% check@bpgpts
-
-\def\do@cpgptloop{%
+ \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}%
- \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
- \pgpt@entry
+ \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
- \check@cbpts
+ \let\nextdocolumn@heads@comb@noscores@v=\relax
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@cpgptloop=\do@cpgptloop
+ \nextdocolumn@heads@comb@noscores@v
+}% docolumn@heads@comb@noscores@v
+
+%--------------------------------------------------------------------
+% \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@cpgptloop=\relax
+ \do@oneline@v
\fi
- \next@cpgptloop
-}% do@cpgptloop
-\def\check@cbpts{%
- \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \pgpt@entry
+ \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
+ \\
+ \cline@stuff@v
+ \let\nextdo@lines@v=\do@lines@v
\fi
-}% check@cbpts
-
-\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
+ \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@cnonpts
+ % 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@cbpgptloop=\do@cbpgptloop
+ \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@cbpgptloop=\relax
+ \let\nextdo@oneline@v=\relax
\fi
- \next@cbpgptloop
-}% do@cbpgptloop
-\def\check@cnonpts{%
- \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \bpgpt@line
+ \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
+ % 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
-}% check@cnonpts
-
-\def\do@cpgsloop{%
- \addtocounter{@iterator}{1}%
- \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
- \ifhlfcntr@pos{tmp@hlfcntr}%
- \pg@sentry
+ \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
- \check@bnsline
+ \let\nextdo@oneline@comb@v=\relax
\fi
- \ifnum \the@iterator < \tbl@lastp\relax
- \let\next@cpgsloop=\do@cpgsloop
+ \nextdo@oneline@comb@v
+}% do@oneline@comb@v
+
+%--------------------------------------------------------------------
+% \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@cpgsloop=\relax
+ \if@bonus
+ \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}%
+ \else
+ \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}%
+ \fi
\fi
- \next@cpgsloop
-}% do@cpgsloop
-\def\check@bnsline{%
- \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
- \pg@sentry
+ \let\nextfind@nextpagewithpoints=\relax
+ \else
+ \ifnum \value{#1} > \tbl@lastp\relax
+ \let\nextfind@nextpagewithpoints=\relax
+ \else
+ \def\nextfind@nextpagewithpoints{\find@nextpagewithpoints{#1}}%
+ \fi
\fi
-}% check@bnsline
+ \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
+ % 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
+ \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}{}