From 8186542aa5c83c77e3aabe6d3cea54f249423472 Mon Sep 17 00:00:00 2001
From: Karl Berry <karl@freefriends.org>
Date: Mon, 23 May 2011 22:02:31 +0000
Subject: exam 2.4 (23may11)

git-svn-id: svn://tug.org/texlive/trunk@22585 c570f23f-e606-0410-a88d-b1316a301751
---
 Master/texmf-dist/tex/latex/exam/exam.cls | 2682 +++++++++++++++++++----------
 1 file changed, 1770 insertions(+), 912 deletions(-)

(limited to 'Master/texmf-dist/tex/latex/exam/exam.cls')

diff --git a/Master/texmf-dist/tex/latex/exam/exam.cls b/Master/texmf-dist/tex/latex/exam/exam.cls
index ea3516cbb46..1f4fc6358fc 100644
--- a/Master/texmf-dist/tex/latex/exam/exam.cls
+++ b/Master/texmf-dist/tex/latex/exam/exam.cls
@@ -18,7 +18,7 @@
 % This work consists of the files exam.cls and examdoc.tex.
 
 
-% The user documentation for exam.cls is in the file examdoc.tex.
+% The user's guide for exam.cls is in the file examdoc.tex.
 
 
 %%% Philip Hirschhorn
@@ -31,8 +31,8 @@
 % from my web page: http://www-math.mit.edu/~psh/
 
 
-\def\fileversion{2.3}
-\def\filedate{2008/07/19}
+\def\fileversion{2.4}
+\def\filedate{2011/05/22}
 %---------------------------------------------------------------------
 %---------------------------------------------------------------------
 % 
@@ -82,681 +82,460 @@
 %
 %--------------------------------------------------------------------
 %--------------------------------------------------------------------
-%                    Changelog since version 2.2:
+%                    Changelog since version 2.3:
 
 %--------------------------------------------------------------------
-% Version 2.3, 2008/07/16:
+% Version 2.4, 2011/05/22:
 %
-% Minor typographic corrections to comments.
+% No longer betatest.
 %
 %--------------------------------------------------------------------
-% Version 2.222$\beta$, 2008/07/13:
+% Version 2.321$\beta$, 2011/05/15:
 %
-% Corrected a typo in the error message displayed when
-% \shadedsolutions is used without having said \usepackage{color}.
+% New environments: EnvUplevel and EnvFullwidth
 %
-%--------------------------------------------------------------------
-% Version 2.221$\beta$, 2008/07/03:
+% The EnvUplevel environment is similar to the command \uplevel,
+% except that \uplevel is a command taking one argument.  The
+% EnvUplevel environment has the advantage that you can include
+% verbatim material (using, e.g., the \verb command) in the
+% environment.  (You can't include verbatim material in the argument
+% of an \uplevel command.)
 %
-% \themarginpoints used to expand to 
-%     \@points \@marginpointname
-% whether the points were bonus or non-bonus.  We fixed that, so that
-% it expands to either
-%     \@points \@marginbonuspointname
-% or
-%     \@points \@marginpointname
-% depending on whether or not the points are bonus points.
+% The EnvFullwidth environment is similar to the command \fullwidth,
+% except that \fullwidth is a command taking one argument.  The
+% EnvFullwidth environment has the advantage that you can include
+% verbatim material (using, e.g., the \verb command) in the
+% environment.  (You can't include verbatim material in the argument
+% of a \fullwidth command.)
 %
 %--------------------------------------------------------------------
-% Version 2.220$\beta$, 2008/05/24:
+% Version 2.320$\beta$, 2011/04/16:
 %
-% New command: \fillwithdottedlines 
-% New environment: solutionordottedlines
+% New document class options: cancelspace
+%                             nocancelspace
+%               New commands: \cancelspace
+%                             \nocancelspace
 %
+% Using the document class option cancelspace is equivalent to giving
+% the command \cancelspace at the beginning of the file.  The default is
+% \nocancelspace.  Thus, the document class option nocancelspace has no
+% effect, but is there to avoid errors if a user is confused.
 %
-% \fillwithdottedlines is similar to \fillwithlines except that it
-% inserts dotted lines (created by \dotfill) instead of solid lines.
+% If the user uses the document class option cancelspace and if
+% solutions are not being printed, then any space that would be added by
+% an optional argument to the solution, solutionorbox, solutionorlines,
+% or solutionordottedlines environments will not be added.
 %
-% \fillwithdottedlines takes one argument, which is either a length or
-% \fill, and it fills that much vertical space with dotted horizontal
-% lines that run the length of the current line.  That is, they extend
-% from the current left margin (which depends on whether we're in a
-% question, part, subpart, or subsubpart) to the right margin.
+% If solutions are being printed (because the user used the document
+% class option answers or gave the command \printanswers), then the
+% option cancelspace (and the command \cancelspace) will have no
+% effect.
 %
-% The distance between the lines is \dottedlinefillheight, whose
-% default value is set with the command
+%--------------------------------------------------------------------
+% Version 2.319$\beta$, 2011/04/11:
 %
-%   \setlength\dottedlinefillheight{.25in}
+% We fixed an obscure bug, in which if the user gives a \qformat
+% command, in which case they shouldn't want to give a \droppoints
+% command, but they *do* give a \droppoints command, there is an
+% error (\padded@point@block undefined).
 %
-% This value can be changed by giving a new \setlength command.
+%--------------------------------------------------------------------
+% Version 2.318$\beta$, 2011/04/05:
 %
-% The solutionordottedlines environment is almost identical to the
-% solutionorlines environment, except that when solutions are not
-% being printed and an optional argument appears specifying an amount
-% of space to be left for answers, that space is filled with dotted
-% lines (created by \dotfill), rather than filled with solid lines (as
-% it is by the solutionorlines environment).
+% \qformat and \bonusqformat are now totally independent.
+% That is: \qformat doesn't affect bonus questions, and \noqformat
+% no longer cancels \bonusqformat.
 %
-%--------------------------------------------------------------------
-% Version 2.219$\beta$, 2007/11/24:
-% 
-% There can now be partial grade tables and point tables, either bonus
-% or non-bonus.
-% 
-% New commands:
-% 
-%   \partialgradetable
-%   \partialbonusgradetable
-%   \partialpointtable
-%   \partialbonuspointtable
-%   \begingradingrange
-%   \endgradingrange
-% 
-% The user defines a grading range by saying
-% 
-%   \begingradingrange{whatever}
-% 
-% to start the range and
-% 
-%   \endgradingrange{whatever}
-% 
-% to end the range (where ``whatever'' is a name chosen by the user).
-% To create the table, the user says, e.g., 
-% 
-%   \partialgradetable{whatever}[v][questions]
-% 
-% That is, each of \partialgradetable, \partialbonusgradetable,
-% \partialpointtable, and \partialbonuspointtable takes
-% 
-%   one required argument (the name of the grading range) and
-% 
-%   two optional arguments, the first being either ``[v]'' or ``[h]'',
-%   the second being either ``[questions]'' or ``[pages]''.
-% 
-% If either or both of the optional arguments are omitted, [questions]
-% and [v] will be used.
-% 
-% If the table is indexed by questions, then it will include all
-% questions following the \begingradingrange{whatever} and preceding the
-% \endgradingrange{whatever}.
-% 
-% If the table is indexed by pages, then it will include all pages with
-% points starting with the page containing the
-% \begingradingrange{whatever} and ending with the page containing the
-% \endgradingrange{whatever}.
-% 
-% Otherwise, 
-% 
-%   \partialgradetable is similar to \gradetable
-%   \partialbonsugradetable is similar to \bonusgradetable
-%   \partialpointtable is similar to \pointtable
-%   \partialbonuspointtable is similar to \bonuspointtable
-% 
-%--------------------------------------------------------------------
-% Version 2.218$\beta$, 2007/10/31:
-% 
-% Instead of appending
-%   \the \pageinfo@commands \the \point@toks 
-% to \everypar, we insert them into the box \@labels.  This corrects the
-% problem that arose when a question (or part, etc.) begins with a list
-% environment (including verbatim, flushleft, center, flushright, and
-% possibly others that are implemented as trivlist environments).  The
-% \item command in those environments throws away the previous contents
-% of \everypar, and so the tokens
-%   \the \pageinfo@commands \the \point@toks
-% didn't get inserted where we expected.  List environments *do*
-% preserve the contents of the box \@labels, though.
-
-% This version of exam.cls is not completely backward compatible with
-% older versions.  The changes only affect documents in which the user
-% was doing something that probably should not have been done with the
-% older versions, though.  This new version does something useful in
-% situations in which older versions did something not very useful:
-
-% This change introduces a change in behavior for documents in which a
-% question with points begins with a parts environment, so that the
-% points for the first part (if any) would appear on the same line as
-% the points for the question.  In earlier versions of exam.cls, the
-% points for the question would not be printed if there are points for
-% the part that begins on the same line.  In this version of exam.cls,
-% all points will be printed.  If they're both printed in the left
-% margin, or both in the right margin, then they'll be printed directly
-% on top of one another, which is not what you want.  (You can use
-% \pointsinmargin, \pointsinrightmargin, and \nopointsinmargin to print
-% them in different places.)  If points are being printed right in the
-% text (i.e., \nopointsinmargin), then the points for the question will
-% be printed *before* the label (a) for the first part is printed.
-
-% The behavior of this new version of exam.cls seems clearly preferable
-% to the old behavior, but it is a change!
+% New command: \nobonusqformat
+% \nobonusqformat does for \bonusqformat what \noqformat does for
+% \qformat.
 %
 %--------------------------------------------------------------------
-% Version 2.217$\beta$, 2007/10/22:
-% 
-% We changed things so that we're now compatible with calc.sty
+% Version 2.317$\beta$, 2011/04/02:
 %
-% calc.sty redefines \setcounter and \addtocounter in a way that
-% conflicts with a trick we were using to deal with \half points.
+% We changed the definition of \numcoverpages so that on the first run
+% of latex (when \exam@lastcoverpage isn't yet defined), \numcoverpages
+% will expand to
+%     0\mbox{\normalfont\bf ??}
+% instead of
+%     \mbox{\normalfont\bf ??}
+% so that the construction \romannumeral\numcoverpages will not generate
+% an error on the first run of latex.
 %
-% We replaced one \setcounter command with
+% We also made changes and corrections to comments.
 %
-%    \global\csname c@#1\endcsname 0#2\relax
-%
-% and one \addtocounter command with
+%--------------------------------------------------------------------
+% Version 2.316$\beta$, 2010/12/22:
 %
-%    \global\advance\csname c@#1\endcsname 0#2\relax
 %
-%--------------------------------------------------------------------
-% Version 2.216$\beta$, 2007/08/27:
-% 
-% The command \answerline now takes an optional argument.  If the
-% optional argument appears, then when \printanswers is true the
-% argument will be printed on the answer line.  When \printanswers is
-% false, the optional argument is ignored.
-% 
-% When the argument is printed, it is printed with
-% \CorrectChoiceEmphasis and it is a distance of \answerclearance above
-% the line.  The default value of \answerclearance is set with the
-% command
-% 
-% \setlength\answerclearance{0.2ex}
-% 
-% and it can be changed with a \setlength command.
-% 
-% The optional argument is centered on the answer line unless it is too
-% long, in which case it extends to the right of the answer line.
-% 
-%--------------------------------------------------------------------
-% Version 2.215$\beta$, 2007/08/24:
-% 
 % New commands:
-% 
-%   \pointtable
-%   \bonuspointtable
-% 
-% Both of these take up to two optional arguments:
-% 
-%   The first can be either [v] or [h].
-%   The second can be either [questions] or [pages].
-% 
-% These commands are similar to the commands
-% 
-%   \gradetable
-%   \bonusgradetable
-% 
-% except that the new commands print only two columns (or rows),
-% omitting the column (or row) for the score.
-% 
+%
+%   \pointsinrange{rangename}
+%   \bonuspointsinrange{rangename}
+%
+% If `rangename' is the name of a grading range defined using
+% \begingradingrange{rangename} and \endgradingrange{rangename}, then
+% \pointsinrange{rangename} expands to the number of points in that
+% grading range and \bonuspointsinrange{rangename} expands to the number
+% of bonus points in that grading range.
+
+% We also changed the checkboxes and oneparcheckboxes environments so
+% that, when solutions are being printed, a \CorrectChoice will be
+% printed with \CorrectChoiceEmphasis (along with the \checkedchar
+% that marks the \CorrectChoice).  This brings the behavior in line
+% with that of the choices and oneparchoices environments.
+%
 %--------------------------------------------------------------------
-% Version 2.214$\beta$, 2007/07/19:
+% Version 2.315$\beta$, 2010/12/21:
 %
 % New commands:
 %
-%   \questionshook
-%   \partshook
-%   \subpartshook
-%   \subsubpartshook
-%   \choiceshook
+%  \settabletotalpoints
+%  \settabletotalbonuspoints
 %
-% These are for advanced users who want to customize the list
-% parameters (\topsep, \partopsep, \itemsep, \parsep, etc.) for the
-% lists that these environments create.  They are all defined to be
-% empty, but the user can change them using \renewcommand.
+% Each of those takes one argument.  After giving the command
+% \settabletotalpoints{numpoints}, any grade or point table (partial
+% or full) will list the total points as numpoints instead of the
+% actual total number of points in the table.
 %
-%--------------------------------------------------------------------
-% Version 2.213$\beta$, 2006/11/27:
-% 
-%  New documentclass option: addpoints
-%  Using this option is equivalent to giving the command
-%    \addpoints
-%  at the beginning of the document.
-% 
-% New commands:
-% 
-%   \bonusquestion
-%   \bonustitledquestion
-%   \bonuspart
-%   \bonussubpart
-%   \bonussubsubpart
-% 
-%   \bonuspointsofquestion
-%   \bonuspointsonpage
-% 
-%   \numbonuspoints
-% 
-%   \bonuspointpoints
-%   \bonuspointformat
-%   \thebonuspoints
-%   \bonuspoints
-%   \bonuspointname
-%   \marginbonuspointname
-%
-%   \bonusgradetable
-% 
-%   \bhqword
-%   \bhpword
-%   \bhsword
-%   \bhtword
-%   \bvqword
-%   \bvpword
-%   \bvsword
-%   \bvtword
-% 
-%   \bvpgword
-%   \bhpgword
-% 
-% 
-% The commands
-% 
-%    \bonusquestion
-%    \bonustitledquestion
-%    \bonuspart
-%    \bonussubpart
-%    \bonussubsubpart
-% 
-% are the same as the commands
-% 
-%    \question
-%    \titledquestion
-%    \part
-%    \subpart
-%    \subsubpart
-% 
-% except that any points assigned become ``bonus points'', which by
-% default are typeset in the form
-% 
-%   1. (1 point (bonus))
-% 
-% or
-% 
-%   1. (2 points (bonus))
-% 
-% If no points are assigned, then these ``bonus'' versions of the
-% commands are equivalent to the non-bonus versions.
-% 
-% The command
-% 
-%   \bonusgradetable
-% 
-% is analogous to the command \gradetable; it can be used in any of the
-% four forms
-% 
-%   \bonusgradetable[v][questions]
-%   \bonusgradetable[h][questions]
-%   \bonusgradetable[v][pages]
-%   \bonusgradetable[h][pages]
-% 
-% and it counts only bonus points, while \gradetable counts only
-% non-bonus points.
-% 
-% The commands
-% 
-%   \bonuspointsofquestion
-%   \bonuspointsonpage
-%   \numbonuspoints
-% 
-% are analogous to the commands
-% 
-%   \pointsofquestion
-%   \pointsonpage
-%   \numpoints
-% 
-% The remaining new commands are for customizing the appearance of bonus
-% points and bonusgradetable.  The command
-% 
-%   \bonuspointpoints
-% 
-% is analogous to the command \pointpoints; it takes two arguments, the
-% first of which will appear after 1 or \half points the second to
-% appear after any other number of points.  The default was created by
-% the equivalent of the command
-% 
-%   \bonuspointpoints{point (bonus)}{points (bonus)}
-% 
-% If, for example, you give the command
-% 
-%   \bonuspointpoints{bonus point}{bonus points}
-% 
-% then bonus points will look like
-% 
-%   1. (1 bonus point)
-% 
-% or
-% 
-%   1. (2 bonus points)
-% 
-% (The commands \bracketedpoints, \boxedpoints, and \nobracketedpoints
-% have the same effect on bonus points that they have on non-bonus
-% points, and the command \marksnotpionts replaces ``point'' with
-% ``mark'' in all of these cases.)
-% 
-% \bonuspointname is analogous to \pointname, and the defaults were
-% created by the commands
-% 
-%   \pointname{ \points}
-%   \bonuspointname{ \bonuspoints}
-% 
-% (Note the intentional space in each of those commands.)
-% 
-% \marginbonuspointname is analogous to \marginpointname, and the
-% default was created by the command
-% 
-%   \marginbonuspointname{ (bonus)}
-% 
-% \bonuspointformat is analogous to \pointformat.
-% 
-% 
-% The commands for customizing the words used in \bonusgradetable are
-% analogous to those used for customizing the words used in \gradetable,
-% except that they begin with a ``b'':
-% 
-%   \bhqword      is analogous to     \hqword 
-%   \bhpword      is analogous to     \hpword 
-%   \bhsword      is analogous to     \hsword 
-%   \bhtword      is analogous to     \htword 
-%   \bvqword      is analogous to     \vqword 
-%   \bvpword      is analogous to     \vpword 
-%   \bvsword      is analogous to     \vsword 
-%   \bvtword      is analogous to     \vtword 
-%                 is analogous to             
-%   \bvpgword     is analogous to     \vpgword
-%   \bhpgword     is analogous to     \hpgword
-% 
+% After giving the command \settabletotalbonuspoints{numbonuspoints},
+% any grade or point table will list the total bonus points as
+% numbonuspoints instead of the actual total number of bonus points in
+% the table.
 %
-%--------------------------------------------------------------------
-% Version 2.212$\beta$, 2006/11/13:
+% These commands are intended for exams in which students are asked to
+% do some subset of the problems, but not all of them, and so the
+% total number of points possible is less than the sum of the points
+% for all of the questions.
 %
-% Made \useslantedhalf and \usehorizontalhalf global.
 %
-%--------------------------------------------------------------------
-% Version 2.211$\beta$, 2006/10/29:
+% To have one of these commands affect only a single table, confine the
+% command and the \gradetable command inside of a group, as in
 %
-% New commands:
+%   \begingroup
+%     \settabletotalpoints{100}
+%     \gradetable[v][questions]
+%   \endgroup
 %
-%   \pointformat
-%   \themarginpoints
+% or even just
 %
-% An old command that's also useful here is
+%   \begin{center}
+%     \settabletotalpoints{100}
+%     \gradetable[v][questions]
+%   \end{center}
 %
-%   \thepoints
+%--------------------------------------------------------------------
+% Version 2.314$\beta$, 2010/12/01:
 %
-% \pointformat: The \pointformat command allows you to change the
-% format used to print the points.  It takes one argument, and that
-% argument becomes the command to print the points, whether the points
-% are being printed at the beginning of the question, in the left
-% margin (because of \pointsinmargin), in the right margin (because of
-% \pointsinrightmargin), or dropped in the right margin at the end of
-% the question (because of \pointsdroppedatright).
+% New user command:
 %
-% The argument to \pointformat should contain either the command
+%   \addquestionobject
 %
-%   \thepoints
+% This is for when some of a question spills onto the next page, but
+% that page doesn't have any parts, subparts, subsubparts, or choices
+% beginning on theat page (which means that the \ifincomplete command
+% won't know that any of that question is on that page).  If you say
+% \addquestionobject at the very end of the question, it will write a
+% questionobject to the .aux file so that \ifincomplete will work
+% correctly.
 %
-% which expands to the number of points followed by the argument to
-% the last \pointname command, or the command
+% New commands used only internally:
 %
-%   \themarginpoints
+%   \first@questionobject
+%   \questionobject@pluspagecheck
 %
-% which expands to the number of points followed by the argument to
-% the last \marginpointname command.
+% These were just to clean up some of the internal code.
 %
-% For example, the command \bracketedpoints is equivalent to
+%--------------------------------------------------------------------
+% Version 2.313$\beta$, 2010/02/18:
 %
-%   \pointformat{[\thepoints]}
+% Bug fix: When solutions are not being printed, the solution,
+% solutionorlines, and solutionordottedlines environments disallow
+% pagebreaks before the environment, which can pull the last line of
+% the question (or part, or etc.) preceding the environment onto the
+% next page, even when leaving a rather large space before the
+% pagebreak.  We insert
 %
-% if points are not being printed in the margin and to
+% \penalty 0
 %
-%   \pointformat{[\themarginpoints]}
+% to allow a pagebreak before the environment.  We make no change to
+% the solutionorbox environment, since it didn't have this problem.
 %
-% if points are being printed in the margin.
+%--------------------------------------------------------------------
+% Version 2.312$\beta$, 2010/01/17:
 %
-% For another example, the command \boxedpoints is equivalent to
+% New commands:
+% 
+%   \combinedgradetable
+%   \combinedpointtable
+%   \partialcombinedgradetable
+%   \partialcombinedpointtable
+%
+% These are similar to \gradetable, \pointable, \bonusgradetable,
+% \bonuspointtable, \partialgradetable, \partialpointtable,
+% \partialbonusgradetable, and \partialbonuspointtable except that
+% these new commands list both the points and the bonus points.
+%
+% \partialcombinedgradetable and \partialcombinedpointtable take one
+% required argument, which is the name of the grading range.  All four
+% of these commands take two optional arguments, the first being either
+% ``v'' or ``h'' and the second being either ``questions'' or
+% ``pages''.  The possibilities are thus
+%
+%   \combinedgradetable[v][questions]
+%   \combinedgradetable[v][pages]
+%   \combinedgradetable[h][questions]
+%   \combinedgradetable[h][pages]
+%   \partialcombinedgradetable{rangename}[v][questions]
+%   \partialcombinedgradetable{rangename}[v][pages]
+%   \partialcombinedgradetable{rangename}[h][questions]
+%   \partialcombinedgradetable{rangename}[h][pages]
+%
+% The defaults are ``v'' and ``questions''.
+%
+% The horizontal tables have separate rows for the points and bonus
+% points.  The vertical tables have separate columns for the points and
+% bonus points.
+%
+% The default values for the words that appear in the tables are set by
+% the commands
+%
+% \chqword{Question:}
+% \chpgword{Page:}
+% \chpword{Points:}
+% \chbpword{Bonus Points:}
+% \chsword{Score:}
+% \chtword{Total}
+% \cvqword{Question}
+% \cvpgword{Page}
+% \cvpword{Points}
+% \cvbpword{Bonus Points}
+% \cvsword{Score}
+% \cvtword{Total:}
+%
+% The words in the table can be changed by giving any of these
+% commands with the desired word as argument.
 %
-%   \pointformat{\fbox{\thepoints}}
+%--------------------------------------------------------------------
+% Version 2.311$\beta$, 2010/01/10:
 %
-% if points are not being printed in the margin and to
+% New environment: oneparcheckboxes
 %
-%   \pointformat{\fbox{\themarginpoints}}
+% The oneparcheckboxes environment is to the checkboxes environment as
+% the oneparchoices environment is to the choices environment.  That is,
+% each choice is begun with either a \choice or a \correctchoice
+% command, and these will all be printed as a single paragraph.  When
+% answers are not being printed, each choice will have a label
+% consisting of a box that the student can check, created by the
+% argument to the last \checkboxchar command.  The default value is
+% created by the command
 %
-% if points are being printed in the margin.
+% \checkboxchar{$\bigcirc$}
 %
-% For another example, if you give the commands
+% (Note the dollar signs, since \bigcirc can appear only in math
+% mode.)  When answers are being printed, then any choice created
+% using the \correctchoice command will be labelled by the argument to
+% the last \checkedchar command, the default value of which is created
+% by the command
 %
-%   \pointsinmargin
-%   \marginpointname{\%}
-%   \pointformat{\slshape (\themarginpoints)}
+% \checkedchar{$\surd$}
 %
-% then the points will be printed in the left margin, followed by the
-% symbol `%', surrounded by parentheses, all in slanted type.
+%--------------------------------------------------------------------
+% Version 2.310$\beta$, 2010/01/09:
 %
-% For another example, if you give the commands
+% New command: \bonusqformat
 %
-%   \pointsinrightmargin
-%   \marginpointname{\%}
-%   \pointformat{\fbox{\bfseries \boldmath \themarginpoints}}
+% The \bonusqformat command takes one argument, with the same
+% description as for the argument of the \qformat command.
 %
-% then the points will be printed in the right margin, followed by the
-% symbol `%', all in bold and surrounded by a box.  (Note: The commands
-% \bfseries and \boldmath  are needed here because the ``one half''
-% printed when you use half points is printed in math mode while the
-% rest of the points are printed in text mode.)
+% Note added 2011/04/05: Version 2.318$\beta$ changes the behavior so
+% that \qformat and \bonusqformat are now completely independent!
 %
-%--------------------------------------------------------------------
-% Version 2.210$\beta$, 2006/09/28:
+% If the user gives a \qformat command, then the argument of that
+% command is used to format both questions and bonusquestions.  If,
+% after giving the \qformat command, the user gives a \bonusqformat
+% command, then the argument of the \bonusqformat command will be used
+% to format bonus questions.
 %
-% Fixed the bug that prevented having two consecutive \correctchoice's
-% in a choices or oneparchoices environment.
+% Note: The \bonusqformat command must be given *after* the \qformat
+% command, or else the \bonusqformat command will have no effect.
 %
-%--------------------------------------------------------------------
-% Version 2.209$\beta$, 2006/05/26:
+% Note: If the user hasn't given a \qformat command, then a
+% \bonusqformat command will have no effect.
 %
-% We corrected the problem that \ifcontinuation and \ifincomplete paid
-% attention only to parts, subparts, and subsubparts when deciding on
-% which page a question ended, but ignored choices.  We corrected this,
-% so that a question is now thought to end on the page on which its last
-% part, subpart, subsubpart, or choice begins.
+% Note: If the user gives a \qformat command and a \bonusqformat
+% command, and then later says \noqformat, then the arguments of both
+% the \qformat and \bonusqformat commands are no longer usable.  That
+% is, if the user later on once again says \qformat, and wants the bonus
+% questions to have a different format from the questions, then another
+% \bonusqformat command must be given.
 %
-%--------------------------------------------------------------------
-% Version 2.208$\beta$, 2006/01/30:
-% 
-% We corrected the description of the usage of \ifthenelse in the
-% comments describing what's new in version 2.207beta.
+% Note added 2011/04/05:: The above is changed in version 2.318$\beta$.
+% \qformat and \bonusqformat are now completely independent.
+% \noqformat cancels only \qformat, and the new command 
+% \nobonusqformat cancels \bonusqformat.
 % 
 %--------------------------------------------------------------------
-% Version 2.207$\beta$, 2006/01/29:
-% 
-% New feature: It's possible to say
-% 
-%   \ifprintanswers
-%     Some stuff
-%   \fi
-% 
-% to insert ``Some stuff'' only when answers are being printed.  It's
-% also possible to say
-% 
-%   \ifprintanswers
-%     Some stuff
-%   \else
-%     Other stuff
-%   \fi
-% 
-% to insert ``Some stuff'' when answers are being printed and ``Other
-% stuff'' when answers aren't being printed.
-% 
-% Alternatively, it's possible to accomplish the same thing by saying
-% 
-%   \ifthenelse{\boolean{printanswers}}{Some stuff}{Other stuff}
-% 
-% If you want to ``Stuff'' only when answers aren't being printed, you
-% can say either
-% 
-%   \ifprintanswers
-%   \else
-%     Stuff
-%   \fi
-% 
-% or
-% 
-%   \ifthenelse{\boolean{printanswers}}{}{Stuff}
-% 
-% We accomplished this by changing all uses of \if@printanswers,
-% \@printanswerstrue, and \@printanswersfalse to \ifprintanswers,
-% \printanswerstrue, and \printanswersfalse.
-% 
+% Version 2.309$\beta$, 2009/11/30:
+%
+% New environment: checkboxes
+% New commands: \checkboxeshook
+%               \checkboxchar
+%               \checkedchar
+%
+% The checkboxes environment is similar to the choices environment,
+% except that choices are not numbered.  Instead of numbers, each
+% choice has a label consisting of a box that the student can check,
+% created by the argument to the last \checkboxchar command.  The
+% default value is created by the command
+%
+% \checkboxchar{$\bigcirc$}
+%
+% (Note the dollar signs, since \bigcirc can appear only in math
+% mode.)  When answers are being printed, then any choice created
+% using the \correctchoice command will be labelled by the argument to
+% the last \checkedchar command, the default value of which is created
+% by the command
+%
+% \checkedchar{$\surd$}
+%
+% The list parameters used in the checkboxes environment can be
+% customized by redefining \checkboxeshook, the default value of which
+% is empty.  (This is similar to using \choiceshook to alter the
+% choices environment.)
+%
 %--------------------------------------------------------------------
-% Version 2.206$\beta$, 2005/08/24:
-% 
-% New feature: When using a grading table indexed by questions, it's
-% possible to replace the question numbers in the table with titles
-% for the questions.
+% Version 2.308$\beta$, 2009/11/26:
 %
-% 
-% There are two new commands: \titledquestion and \thequestiontitle.
-% 
-% 
-% \titledquestion is a replacement for \question; it has one required
-% argument (the title) and one optional argument (the point value), as
-% in
-% 
-%   \titledquestion{Question \thequestion: Concepts}
-% 
-% which sets the question title to ``Question 2: Concepts'' (if this
-% is the second question) and doesn't set any pointvalues, or
-% 
-%   \titledquestion{Design}[10]
-%   
-% which sets the question title to ``Design'' and sets the pointvalue
-% to ``10''.  (Note that the question title doesn't include the number
-% of the question unless you put it there using the \thequestion
-% command.)
-% 
-% The title of the question then automatically replaces the question
-% number in a grading table indexed by question numbers.
-% 
-% 
-% 
+% We fixed a bug that caused \bonussubpart to act like \subpart and
+% \bonussubsubpart to act like \subsubpart.
 %
-% 
-% \thequestiontitle: This command expands to the question title.  This
-% is for use in the argument of a \qformat command, so that you can make
-% the question title appear in the actual question.
-% 
-% If a question is begun with \question (instead of \titledquestion),
-% then the value of \thequestiontitle will automatically be set to the
-% number of the question.
-% 
-% Note: The contents of \thequestiontitle don't appear anywhere in the
-% actual question unless you do something to make it appear.  For
-% example, the command
-% 
-% \qformat{\textbf{Question \thequestion: \thequestiontitle}\dotfill\thepoints}
-% 
-% would insert the number of the question along with the title of the
-% question and the point value at the beginning of the question.
-% 
-% 
-% Note: If you use a grading table indexed by questions and you use the
-% \titledquestion command, then the number of the question won't appear
-% in the grading table unless you include \thequestion in the title of
-% the question.
-% 
+%--------------------------------------------------------------------
+% Version 2.307$\beta$, 2009/06/11:
 %
-% Note: If \thequestion appears in both the argument to \qformat and the
-% title of the question, then the question number will be printed
-% twice.
-% 
-% 
-% If the command \thequestiontitle is never used, then the only effect
-% of using the command \titledquestion is that in a grading table
-% indexed by question number, this question will be indexed by the
-% number of the question, rather than by the argument to
-% \titledquestion, and the title of the question would never appear
-% except in the grading table.
+% We fixed a bug that arose only when there were cover pages and the
+% first page of the cover pages used a different extraheadheight or
+% extrafootheight from that used on the first page of the regular pages.
+% The bug caused the first page of the regular pages to use the
+% extraheadheight and extrafootheight intended only for the first cover
+% page.
 %
-% 
 %--------------------------------------------------------------------
+% Version 2.306$\beta$, 2009/03/28:
 %
-% Version 2.205$\beta$, 2005/08/22:
+% We fixed a bug that arose only when the user used a figure or
+% table environment that floated to the top of a page.  If that
+% happened, then a full page's worth of text was placed below
+% the figure or table, makeing the text run over the footline and off
+% the bottom of the page.
 %
-% Bug fix: If a question began with a parts environment (i.e., if the
-% parts environment began before we entered horizontal mode in the
-% question), then a grading table indexed by questions would get the
-% part number instead of the question number.  That's fixed now.
+% We fixed this by commenting out several lines at the end of
+% \@setheadheight and @setfootheight.
 %
 %--------------------------------------------------------------------
+% Version 2.305$\beta$, 2009/01/30:
 %
-% Version 2.204$\beta$, 2005/08/21:
+% We fixed a bug to keep page breaks from occurring inside of
+% \makeemptybox.  We also added \begingroup \endgroup for the off
+% chance that someone was using box0 and didn't want us to change it.
 %
-% Bug fix: Hyperlinks didn't work on page numbers for horizontal
-% grading tables indexed by page numbers.  They do now.
+% We also fixed a bug in the solutionorbox environment that showed
+% itself when the user used \stretch in the optional argument.
 %
 %--------------------------------------------------------------------
+% Version 2.304$\beta$, 2009/01/22:
 %
-% Version 2.203$\beta$, 2005/08/20:
+% We rewrote the definition of \makeemptybox and the solutionorbox
+% environment (created in version 2.303).
 %
-% Eliminated \do@int@lbl, and replaced it with labels for each
-% question, part, subpart, and subsubpart the names of which
-% tell you what they're labelling.
+% The argument to \makeemptybox can now be either a length, or \fill, or
+% \stretch{number}.
 %
-% That allowed us to rewrite the grade table stuff so that, for tables
-% indexed by question numbers, the question numbers are inserted with a
-% \ref, so that if \usepackage{hyperref} and pdflatex are used, those
-% question numbers will be clickable links that take you to the relevant
-% question.
+% The optional argument to the solutionorbox environment can also be
+% either a length, or \fill, or \stretch{number}.  If the optional
+% argument is omitted, then no box is printed when printanswers is
+% false.
+%
+% The minimum box height is \minboxheight, whose value is set by the
+% command
 %
-% We also added a label for the first points to appear on each page, and
-% rewrote the grade table stuff for tables indexed by page number so
-% that the page numbers are inserted with a \pageref, so that if
-% \usepackage{hyperref} and pdflatex are used, those page numbers will
-% be clickable links that take you to the relevant page.
+%   \setlength\minboxheight{.1in}
+%
+% This can be changed by giving a new setlength command, but it's not
+% likely that anyone actually wants a box of height less than .1 inch.
 %
 %--------------------------------------------------------------------
+% Version 2.303$\beta$, 2009/01/21:
+%
+% New command: \makeemptybox
+% New environment: solutionorbox
 %
-% Version 2.202$\beta$, 2005/08/10:
+% \makeemptybox takes one argument, which is a length, and it creates
+% an empty box of width the length of the current line and of height
+% equal to the argument. That is, the box extends from the current
+% left margin (which depends on whether we're in a question, part,
+% subpart, or subsubpart) to the right margin.
 %
-% Created \correctchoice as a synonym for \CorrectChoice
+% The solutionorbox environment is similar to the solution
+% environment, except that if printanswers is false and an optional
+% argument is specified, then instead of leaving that much blank space
+% we print an empty box of that height.  The optional argument must be
+% a dimension; it can not be \fill.  If the optional argument is
+% missing or equal to 0pt, then when printanswers is false we print
+% nothing at all.
 %
-% Changed \CorrectChoiceDeclarations to \CorrectChoiceEmphasis,
-% and created \correctchoiceemphasis as a synonym for that.
+% Note: This is changed in version 2.304.  As of that version, the
+% argument can be a length, or \fill, or \stretch{number}.
 %
 %--------------------------------------------------------------------
+% Version 2.302$\beta$, 2008/10/18:
+%
+% New commands: \numcoverpages and \totalnumpages
+%
+% \numcoverpages inserts the number of pages occupied by the
+% coverpages environment.  If there is no coverpages environment,
+% it inserts 0.
 %
-% Version 2.201$\beta$, 2005/08/09:
+% \totalnumpages inserts the sum of \numcoverpages and \numpages.
+% Note that if there are pages preceding a coverpages environment, then
+% those pages will not be counted by \totalnumpages.
 %
-% We created a \CorrectChoice command for use in the choices and
-% oneparchoices environments.
+% We also added \relax after some \ifnum comparisons and commented
+% out some line endings to remove, e.g., an unwanted space inserted
+% by \ifcontinuation when the page is a continuation.
 %
-% When solutions are not being printed, \CorrectChoice is equivalent to
-% \choice.
+%--------------------------------------------------------------------
+% Version 2.301$\beta$, 2008/09/22:
 %
-% When solutions *are* being printed, the choice that was created with
-% \CorrectChoice (rather than with \choice) will be printed subject to
-% the declarations in the argument of the
+% All solution, solutionorlines, and solutionordotteslines
+% environments are now enclosed in a group which is begun
+% with the argument of the command
 %
-%   \CorrectChoiceDeclarations
+%   \SolutionEmphasis
 %
-% command.  The default is
+% the default value of which is determined by the command
 %
-%   \CorrectChoiceDeclarations{\bfseries}
+%   \SolutionEmphasis{}
 %
-% and so, when solutions are being printed, the correct choice is
-% printed in boldface by default.  If, e.g., you give the command
+% That is, the default is that nothing is inserted.
+% For example, if the command
 %
-%   \usepackage{color}
+%   \SolutionEmphasis{\itshape\small}
 %
-% after the \documentclass command and then give the command
+% is used, the solutions will be set in italics, in small type.
 %
-%  \CorrectChoiceDeclarations{\color{red}}
 %
-% then the correct choice will be printed in red.  If, e.g., you give
-% the command
+% New command: \unframedsolutions
 %
-%   \CorrectChoiceDeclarations{\color{red}\bfseries}
+% This causes solutions to be printed as normal text, without any
+% framing or shading.  Anyone using this command should use the
+% \SolutionEmphasis command so that the solutions will be
+% distinguishable from the rest of the exam.
 %
-% then the correct choice will be printed in red and in boldface.
+%--------------------------------------------------------------------
+% Version 2.3, 2008/07/16:
 %
-% Note added later: \CorrectChoiceDeclarations was changed
-% to \CorrectChoiceEmphasis in version 2.202$\beta$.
+% Minor typographic corrections to comments.
 %
 %--------------------------------------------------------------------
 %--------------------------------------------------------------------
@@ -774,6 +553,11 @@
 \DeclareOption{answers}{\printanswerstrue}
 \DeclareOption{noanswers}{\printanswersfalse}
 
+\newif\ifcancelspace
+\cancelspacefalse
+\DeclareOption{cancelspace}{\cancelspacetrue}
+\DeclareOption{nocancelspace}{\cancelspacefalse}
+
 % The following keeps track of whether the user has requested that we
 % add up the points on the exam.  We make the default false so that
 % users who put other than numbers into the points argument of a
@@ -902,7 +686,7 @@
 
 \newcommand*\adj@hdht@ftht{%
   \if@coverpages
-    \ifnum\value{page}=1
+    \ifnum\value{page}=1\relax
       \@setheadheight{\covfp@exhd}%
       \@setfootheight{\covfp@exft}%
     \else
@@ -910,7 +694,7 @@
       \@setfootheight{\covrun@exft}%
     \fi
   \else
-    \ifnum\value{page}=1
+    \ifnum\value{page}=1\relax
       \@setheadheight{\fp@exhd}%
       \@setfootheight{\fp@exft}%
     \else
@@ -991,7 +775,7 @@
 \@appendoutput{\adj@hdht@ftht}
 
 %--------------------------------------------------------------------
-%                 \setheadheight and \setfootheight:
+%                 \@setheadheight and \@setfootheight:
 
 \def\@setheadheight#1{%
   \begingroup % Avoid trouble from using \@temp and \@spaces
@@ -1018,11 +802,22 @@
     % executed only in the preamble or as we return from the output
     % routine, but we're leaving it in so that this will still work if
     % we use this at some random point in the middle of composing a
-    % page). 
+    % page).
+    % Bugfix, Version 2.306beta, 2009/03/28:
+    % We don't do this!!
+    % If the user had a figure environment that floated to the
+    % top of a page, then this would cause that page to run
+    % over the footer and off the bottom of the page, because
+    % this somehow caused a full page's worth of stuff to be
+    % placed after the figure, as if the figure wasn't taking
+    % up space on the page.
+    % We *do* need to put \@colht at the correct new value, though,
+    % apparently because \@colht is set near the end of the
+    % output routine.
     \global\@colht=\textheight
-    \global\@colroom=\textheight
-    \global\vsize=\textheight
-    \global\pagegoal=\textheight
+%     \global\@colroom=\textheight
+%     \global\vsize=\textheight
+%     \global\pagegoal=\textheight
   \endgroup
 }
 
@@ -1050,10 +845,21 @@
     % routine, but we're leaving it in so that this will still work if
     % we use this at some random point in the middle of composing a
     % page). 
+    % Bugfix, Version 2.306beta, 2009/03/28:
+    % We don't do this!!
+    % If the user had a figure environment that floated to the
+    % top of a page, then this would cause that page to run
+    % over the footer and off the bottom of the page, because
+    % this somehow caused a full page's worth of stuff to be
+    % placed after the figure, as if the figure wasn't taking
+    % up space on the page.
+    % We *do* need to put \@colht at the correct new value, though,
+    % apparently because \@colht is set near the end of the
+    % output routine.
     \global\@colht=\textheight
-    \global\@colroom=\textheight
-    \global\vsize=\textheight
-    \global\pagegoal=\textheight
+%     \global\@colroom=\textheight
+%     \global\vsize=\textheight
+%     \global\pagegoal=\textheight
   \endgroup
 }
 
@@ -1100,8 +906,12 @@
 \newif\if@coverpages
 \@coverpagesfalse
 
+\newcounter{num@coverpages}
+% We'll set this to zero in case there is no coverpages environment:
+\setcounter{num@coverpages}{0}
+
 \newenvironment{coverpages}{%
-    \ifnum \value{numquestions}>0
+    \ifnum \value{numquestions}>0\relax
       \ClassError{exam}{%
         Coverpages cannot be used after questions have begun.\MessageBreak
       }{%
@@ -1115,7 +925,16 @@
     \adj@hdht@ftht
   }{%
     \clearpage
+    \setcounter{num@coverpages}{\value{page}}%
+    \addtocounter{num@coverpages}{-1}%
     \pagenumbering{arabic}%
+    % Bugfix, Version 2.307\beta, 2009/06/11:
+    % We have to say \@coverpagesfalse before \adj@hdht@ftht
+    % because we're still inside the group created by the
+    % coverpages environment and we want to set the 
+    % extraheadheight and extrafootheight to the values correct
+    % for the first non-cover page:
+    \@coverpagesfalse
     \adj@hdht@ftht
 }
 
@@ -1132,13 +951,13 @@
 \newcommand*\@dohead{%
   \def\@oddhead{%
     \if@coverpages
-      \ifnum\value{page}=1
+      \ifnum\value{page}=1\relax
         \cov@fullhead
       \else
         \covrun@fullhead
       \fi
     \else
-      \ifnum\value{page}=1
+      \ifnum\value{page}=1\relax
         \@fullhead
       \else
         \run@fullhead
@@ -1151,13 +970,13 @@
 \newcommand*\@dofoot{%
   \def\@oddfoot{%
     \if@coverpages
-      \ifnum\value{page}=1
+      \ifnum\value{page}=1\relax
         \cov@fullfoot
       \else
         \covrun@fullfoot
       \fi
     \else
-      \ifnum\value{page}=1
+      \ifnum\value{page}=1\relax
         \@fullfoot
       \else
         \run@fullfoot
@@ -1629,6 +1448,7 @@
 %                \numpages, \iflastpage, and \oddeven
 %     Also: \numpoints, \numquestions, \numparts, and \numsubparts
 %                    Also also: \pointsofquestion
+%     Also: \numcoverpages and \totalnumpages
 
 % Make the number of pages available as the macro \numpages,
 % the number of points as \numpoints,
@@ -1660,6 +1480,20 @@
   \exam@lastpage
 }% numpages
 
+% Change 2011/04/01: We added a ``0'' in front of the
+% mbox when \exam@lastcoverpage isn't defined.  This is
+% so that the construction \romannumeral\numcoverpages
+% won't generate an error on the first run of latex.
+\def\numcoverpages{\@ifundefined{exam@lastcoverpage}%
+  {0\mbox{\normalfont\bf ??}}%
+  \exam@lastcoverpage
+}% numpages
+
+\def\totalnumpages{\@ifundefined{exam@totalpages}%
+  {\mbox{\normalfont\bf ??}}%
+  \exam@totalpages
+}% numpages
+
 \def\numpoints{\@ifundefined{exam@numpoints}%
   {\mbox{\normalfont\bf ??}}%
   \exam@numpoints
@@ -1698,12 +1532,30 @@
   {\csname bonuspointsofq@\romannumeral #1\endcsname}%
 }% bonuspointsofquestion
 
+% For use in \combinedgradetable and \combinedpointtable, we're
+% changing the defintions of \pointsonpage and \bonuspointsonpage
+% so that when, e.g.,  pointsonpage@ii is undefined, we just produce
+% 0.  This comes up because the final page of the exam may have either
+% points with no bonus point or bonus points with no points, in which
+% case the combined table will try to list both points and bonus points
+% for that last page, and one of those will be undefined.
+%
+% \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}%
+%   {\mbox{\normalfont\bf ??}}%
+%   {\csname pointsonpage@\romannumeral #1\endcsname}%
+% }% pointsonpage
+% \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}%
+%   {\mbox{\normalfont\bf ??}}%
+%   {\csname bonuspointsonpage@\romannumeral #1\endcsname}%
+% }% bonuspointsonpage
+%
+% spanish.ldf redefines \@roman, so we'll avoid using \roman:
 \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}%
-  {\mbox{\normalfont\bf ??}}%
+  {0}%
   {\csname pointsonpage@\romannumeral #1\endcsname}%
 }% pointsonpage
 \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}%
-  {\mbox{\normalfont\bf ??}}%
+  {0}%
   {\csname bonuspointsonpage@\romannumeral #1\endcsname}%
 }% bonuspointsonpage
 
@@ -1750,7 +1602,14 @@
     \advance\c@page-1
     \immediate\write\@mainaux
       {\string\gdef\string\exam@lastpage{\arabic{page}}}%
+    \immediate\write\@mainaux
+      {\string\gdef\string\exam@lastcoverpage{\arabic{num@coverpages}}}%
+    % We can now trash the value of num@coverpages:
+    \addtocounter{num@coverpages}{\value{page}}%
+    \immediate\write\@mainaux
+      {\string\gdef\string\exam@totalpages{\arabic{num@coverpages}}}%
     \advance\c@page+1 % In case some other package looks at \c@page
+    %
     \immediate\write\@mainaux
         {\string\gdef\string\exam@numpoints{%
                             \prtaux@hlfcntr{numpoints}}}%
@@ -1869,9 +1728,9 @@
 % argument.
 \def\oddeven#1#2{%
   \ifodd\value{page}%
-    #1
+    #1%
   \else
-    #2
+    #2%
   \fi
 }% oddeven
 
@@ -2018,10 +1877,10 @@
         {#1}%
         {\ifnum \thepage > \PgInfo@get{@endquestions}\relax
           % We're after a \nomorequestions:
-          #2
+          #2%
          \else
            % We actually are incomplete:
-           #1
+           #1%
          \fi
         }%
       \fi
@@ -2129,7 +1988,7 @@
   % \find@quesend, which is why it has to find the last question
   % begun on or before the current page, rather than just before
   % the current page.
-  \ifnum 1 > \arabic{question}\relax
+  \ifnum 1 > \value{question}\relax
     % Oops; probably because we're before the first question
     % Just set latest@ques to -1:
     \setcounter{latest@ques}{-1}%
@@ -2152,7 +2011,7 @@
       % then call \decr@latest@ques to recursively decrement
       % latest@ques as needed to find a question that begins on
       % or before the current page:
-      \setcounter{latest@ques}{\arabic{question}}%
+      \setcounter{latest@ques}{\value{question}}%
       \decr@latest@ques
     \fi
   \fi
@@ -2277,7 +2136,7 @@
   \else
         \@ifundefined{Pg@@endquestions}%
         {\incomp@first}%
-        {\ifnum \thepage < \PgInfo@get{@endquestions}%
+        {\ifnum \thepage < \PgInfo@get{@endquestions}\relax
            \incomp@first
          \else
            \incomp@second
@@ -2415,7 +2274,7 @@
   \csname if#1@half\endcsname
     \addtocounter{ifpos@cntr}{1}%
   \fi
-  \ifnum \value{ifpos@cntr} > 0
+  \ifnum \value{ifpos@cntr} > 0\relax
 }% ifhlfnctr@pos
 
 % \prtaux@hlfcntr is used inside the argument of a \write command for
@@ -2614,27 +2473,38 @@
 % determine if there were points specified for this question.)
 % The argument to the \qformat command *must* contain some
 % stretch, i.e., at least one \hfil or \dotfill or ...
+%
+% The command \noqformat cancels the effect of \qformat and returns us
+% to the default situation.
+%
+% The commands \bonusqformat and \nobonusformat are analogous.
 \newif\if@qformat
 \@qformatfalse
+\newif\if@bonusqformat
+\@bonusqformatfalse
 
 \def\qformat#1{%
   \global\@qformattrue
   \gdef\@questionformat{#1}%
 }% qformat
+\def\bonusqformat#1{%
+  \global\@bonusqformattrue
+  \gdef\@bonusquestionformat{#1}%
+}% bonusqformat
 
 \newcommand\noqformat{%
   \global\@qformatfalse
 }% noqformat
+\newcommand\nobonusqformat{%
+  \global\@bonusqformatfalse
+}% nobonusqformat
+
 
 % \thepoints is for use in either a \qformat command
-% or a \pointformat command.  It needs to have the
+% or a \pointformat command (or a \bonusqformat command).
+% It needs to have the
 % \if@placepoints so that if it's used in a \qformat command
 % it won't print anything if there are no points:
-%\newcommand\thepoints{%
-%  \if@placepoints
-%    \@points \@pointname
-%  \fi
-%}
 \newcommand\thepoints{%
   \if@placepoints
     \if@bonus
@@ -2647,9 +2517,6 @@
 
 % \themarginpoints is for use only in a \pointformat command,
 % and so it doesn't need the \if@placepoints bit in \thepoints:
-%\newcommand\themarginpoints{%
-%  \@points \@marginpointname
-%}% themarginpoints
 \newcommand\themarginpoints{%
   \if@bonus
     \@points \@marginbonuspointname
@@ -2673,10 +2540,54 @@
 % begun on or before the current page:
 \newcounter{ques@object}
 
+% \first@questionobject will be used by the \question command.
+% That is, it will be used only once, but we want to keep its
+% definition here, near the definitions of \addquestionobject and
+% \questionobject@pluspagecheck.
+\newcommand{\first@questionobject}{%
+  \setcounter{ques@object}{1}%
+  \edef\q@object@label{%
+    question\arabic{question}@object\arabic{ques@object}}%
+  \PgInfo@write{\q@object@label}%
+}% first@questionobject
+
+% \addquestionobject will be used by each part, subpart, and
+% subsubpart, and can also be used by the user to mark the end of a
+% question that spills over onto the next page without any part,
+% subpart, etc. starting on that page:
+\newcommand{\addquestionobject}{%
+  \addtocounter{ques@object}{1}%
+  \edef\q@object@label{%
+    question\arabic{question}@object\arabic{ques@object}}%
+  \PgInfo@write{\q@object@label}%
+}% addquestionobject
+
+% \questionobject@pluspagecheck will be used by each choice.  It uses
+% the questionobject to check if we're the first one on the current
+% page, since choices don't have labels the way that questions, parts,
+% subparts, and subsubparts do (those things use the label to check if
+% they're the first thing on the page).
+\newcommand{\questionobject@pluspagecheck}{%
+  \addtocounter{ques@object}{1}%
+  \edef\q@object@label{%
+    question\arabic{question}@object\arabic{ques@object}}%
+  \PgInfo@write{\q@object@label}%
+  \set@counter@to@pageof{Curr@Page}{\q@object@label}%
+  \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
+    % We're the first \question, \part, \subpart, \subsubpart,
+    % or choice on this page:
+    \global\expandafter\edef\csname
+    Contin@\theCurr@Page\endcsname{\arabic{question}}%
+  \fi
+}% questionobject@pluspagecheck
+
+
+
 % if@bonus will be true when we're doing a bonusquestion or bonuspart
 % or etc., and it will also be used also to distinguish between
 % \gradetable and \bonusgradetable (and between \pointtable and
-% \bonuspointtable, etc.):
+% \bonuspointtable, etc.), and also to distinguish between
+% \pointsinrange and \bonuspointsinrange:
 \newif\if@bonus
 \@bonusfalse
 
@@ -2689,6 +2600,7 @@
 \newcommand\subpartshook{}
 \newcommand\subsubpartshook{}
 \newcommand\choiceshook{}
+\newcommand\checkboxeshook{}
 
 
 \newenvironment{questions}{%
@@ -2731,10 +2643,10 @@
     \addtocounter{numquestions}{1}%
     % Write the sum of points of the previous question (if any)
     % to the .aux file.  (At this point, the question counter
-    % has not yet been incremented, so \arabic{question} has the
+    % has not yet been incremented, so \value{question} is the
     % number of the question that was just completed.)
     \if@filesw
-      \ifnum \arabic{question} > 0\relax
+      \ifnum \value{question} > 0\relax
         % First do regular points:
         \immediate\write\@mainaux
           {\string\gdef\string\pointsofq@
@@ -2779,10 +2691,7 @@
     \pageinfo@commands={%
       \edef\@queslabel{question@\arabic{question}}%
       \PgInfo@write{\@queslabel}%
-      \setcounter{ques@object}{1}%
-      \edef\q@object@label{%
-            question\arabic{question}@object\arabic{ques@object}}%
-      \PgInfo@write{\q@object@label}%
+      \first@questionobject
       % In addition to the \PgInfo@write we use an actual \label
       % command.  We do this in order to make the question numbers in
       % the grade tables into \ref's, so that if the user says
@@ -2842,7 +2751,6 @@
     \process@subpart
   }%
   \def\process@subpart{%
-    \@bonusfalse
     \if@coverpages
       \cover@question@error
     \fi
@@ -2856,10 +2764,7 @@
       \edef\@subpartlabel{subpart@\arabic{question}%
         @\arabic{partno}@\arabic{subpart}}%
       \PgInfo@write{\@subpartlabel}%
-      \addtocounter{ques@object}{1}%
-      \edef\q@object@label{%
-            question\arabic{question}@object\arabic{ques@object}}%
-      \PgInfo@write{\q@object@label}%
+      \addquestionobject
       % In addition to the \PgInfo@write we use an actual \label
       % command.  We do this in order to make the question numbers in
       % the grade tables into \ref's, so that if the user says
@@ -2908,7 +2813,6 @@
     \process@subsubpart
   }%
   \def\process@subsubpart{%
-    \@bonusfalse
     \if@coverpages
       \cover@question@error
     \fi
@@ -2922,10 +2826,7 @@
       \edef\@subsubpartlabel{subsubpart@\arabic{question}%
         @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}}%
       \PgInfo@write{\@subsubpartlabel}%
-      \addtocounter{ques@object}{1}%
-      \edef\q@object@label{%
-            question\arabic{question}@object\arabic{ques@object}}%
-      \PgInfo@write{\q@object@label}%
+      \addquestionobject
       % In addition to the \PgInfo@write we use an actual \label
       % command.  We do this in order to make the question numbers in
       % the grade tables into \ref's, so that if the user says
@@ -2981,7 +2882,7 @@
     % Write the number of points of the final question
     % to the .aux file:
     \if@filesw
-      \ifnum \arabic{question} > 0\relax
+      \ifnum \value{question} > 0\relax
         % First do the regular points:
         \immediate\write\@mainaux
           {\string\gdef\string\pointsofq@\romannumeral
@@ -3007,11 +2908,20 @@
 % \question@number is used as the label in the question list (instead
 % of \questionlabel) so that if the user uses a \qformat command,
 % we'll use the \@questionformat specified by the \qformat command:
+
 \def\question@number{%
-  \if@qformat
-    \makebox[\hsize][s]{\@questionformat}\hskip-\labelsep
+  \if@bonus
+    \if@bonusqformat
+      \makebox[\hsize][s]{\@bonusquestionformat}\hskip-\labelsep
+    \else
+      \questionlabel
+    \fi
   \else
-    \questionlabel
+    \if@qformat
+      \makebox[\hsize][s]{\@questionformat}\hskip-\labelsep
+    \else
+      \questionlabel
+    \fi
   \fi
 }
 \newcommand\questionlabel{\thequestion.}
@@ -3030,19 +2940,36 @@
   % possible points) to be combined with the label of the first
   % part.  (\if@inlabel tells us if we are still waiting to enter
   % horizontal mode after seeing a \question command.)
-  \if@qformat
-    \if@inlabel
-      \leavevmode
-      \@inlabelfalse
+  \if@bonus
+    \if@bonusqformat
+      \if@inlabel
+        \leavevmode
+        \@inlabelfalse
+      \fi
+      % The following is just in case the question had points,
+      % in which case @placepoints will still be true.
+      % (We used to do this inside of the \thepoints macro,
+      % but that allowed for an error if the user specified points for
+      % a question but had a \qformat that didn't mention \thepoints.)
+      % We also set @placepoints to be false in the \question command,
+      % in case one queation follows a previous one that had no parts.
+      \global \@placepointsfalse
+    \fi
+  \else
+    \if@qformat
+      \if@inlabel
+        \leavevmode
+        \@inlabelfalse
+      \fi
+      % The following is just in case the question had points,
+      % in which case @placepoints will still be true.
+      % (We used to do this inside of the \thepoints macro,
+      % but that allowed for an error if the user specified points for
+      % a question but had a \qformat that didn't mention \thepoints.)
+      % We also set @placepoints to be false in the \question command,
+      % in case one queation follows a previous one that had no parts.
+      \global \@placepointsfalse
     \fi
-    % The following is just in case the question had points,
-    % in which case @placepoints will still be true.
-    % (We used to do this inside of the \thepoints macro,
-    % but that allowed for an error if the user specified points for
-    % a question but had a \qformat that didn't mention \thepoints.)
-    % We also set @placepoints to be false in the \question command,
-    % in case one queation follows a previous one that had no parts.
-    \global \@placepointsfalse
   \fi
   \def\part{%
     \@bonusfalse
@@ -3065,10 +2992,7 @@
     \temp@toks={%
       \edef\@partlabel{part@\arabic{question}@\arabic{partno}}%
       \PgInfo@write{\@partlabel}%
-      \addtocounter{ques@object}{1}%
-      \edef\q@object@label{%
-            question\arabic{question}@object\arabic{ques@object}}%
-      \PgInfo@write{\q@object@label}%
+      \addquestionobject
       % In addition to the \PgInfo@write we use an actual \label
       % command.  We do this in order to make the question numbers in
       % the grade tables into \ref's, so that if the user says
@@ -3378,12 +3302,6 @@
 
 \def\item@points@pageinfo{%
   \item
-  % If @qformat is true, and if we're currently doing a question
-  % (rather than a part, subpart, or subsubpart), then we don't want
-  % to set the points (if any), since the points of a question will
-  % appear only if the user chooses to cause that by putting a
-  % \thepoints in the argument of the \qformat command.
-  % 
   % Also: We need to do this here, *after* the \item command, rather
   % than inside the macro \@readpoints, because the \item command
   % puts the result of the \qformat command into an \hbox (with the
@@ -3393,11 +3311,12 @@
   % user put a \thepoints command inside that argument it will
   % correctly expand to the number of points.  (When @placepoints is
   % false, \thepoints expands to nothing at all).
-  \if@qformat
-    \ifx\ques@ref\@queslevel
-      \global \@placepointsfalse
-    \fi
-  \fi
+  %
+  % We also want to define \padded@point@block when @placepoints is
+  % true even if qformat and bonusqformat are true just in case the
+  % user, for some deranged reason, says \droppoints immediately
+  % following a \question.
+  %
   \if@placepoints
     % Since we want the user to be able to say \thepoints in the
     % argument to a \pointformat command, we need \@placepointstrue
@@ -3435,24 +3354,60 @@
     % \setup@point@toks puts commands into \point@toks to place
     % \padded@point@block at the correct spot.  It doesn't append
     % anything to \everypar (we do that in this macro, below). 
+    %
+    % If @qformat is true, and if we're currently doing a question (or
+    % if @bonusqformat is true and we're doing a bonusquestion)
+    % (rather than a part, subpart, or subsubpart), then we don't want
+    % to set the points (if any), since the points of a question will
+    % appear only if the user chooses to cause that by putting a
+    % \thepoints in the argument of the \qformat command.
     \if@pointsdropped
       % Do nothing!
     \else
-      \setup@point@toks
+      \if@bonus
+        \if@bonusqformat
+          \ifx\ques@ref\@queslevel
+            % Do nothing
+          \else
+            \setup@point@toks
+          \fi
+        \else
+          \setup@point@toks
+        \fi
+      \else
+        \if@qformat
+          \ifx\ques@ref\@queslevel
+            % Do nothing
+          \else
+            \setup@point@toks
+          \fi
+        \else
+          \setup@point@toks
+        \fi
+      \fi
     \fi
     \global \@placepointsfalse
   \fi
-  % We *don't* use \append@toklist; see Bug fix note above
+  % We *don't* use \append@toklist; see the Bug fixnote above
+  % (Bug fix, 5 April 2004).
   % We can append the tokens ``\the\point@toks'' whether or not we're
   % setting any points because if we're not setting them, \point@toks
   % will be empty.
   % Also: It's important to do this *after* the \item command above,
   % since the \item command discards the previous contents of
   % \everypar.
-  % Version 2.217-beta changes:
-  % Instead of appending stuff to \everypar, we insert
-  % \the \pageinfo@commands and \the \point@toks 
-  % into the box \@labels:
+  % Version 2.218$\beta$, 2007/10/31 changes:
+  % Instead of appending
+  %   \the \pageinfo@commands \the \point@toks
+  % to \everypar, we insert them into the box \@labels.  This corrects
+  % the problem that arose when a question (or part, etc.) begins with
+  % a list environment (including verbatim, flushleft, center,
+  % flushright, and possibly others that are implemented as trivlist
+  % environments).  The \item command in those environments throws
+  % away the previous contents of \everypar, and so the tokens \the
+  % \pageinfo@commands \the \point@toks didn't get inserted where we
+  % expected.  List environments *do* preserve the contents of the box
+  % \@labels, though.
   \global\setbox\@labels\hbox{\unhbox\@labels
     \the \pageinfo@commands
     \the \point@toks}%
@@ -3776,7 +3731,7 @@
 
 
 %--------------------------------------------------------------------
-%                   choices (for multiple choice)
+%         choices (for multiple choice) and checkboxes
 
 
 \renewcommand\thechoice{\Alph{choice}}
@@ -3798,21 +3753,12 @@
 \CorrectChoiceEmphasis{\bfseries}
 \let\correctchoiceemphasis\CorrectChoiceEmphasis
 
-
+% Note: \do@choice@pageinfo is used in both the choices and
+% the checkboxes environments.
 \newtoks\choice@toks
 \def\do@choice@pageinfo{%
   \choice@toks={%
-    \addtocounter{ques@object}{1}%
-    \edef\q@object@label{%
-          question\arabic{question}@object\arabic{ques@object}}%
-    \PgInfo@write{\q@object@label}%
-    \set@counter@to@pageof{Curr@Page}{\q@object@label}%
-      \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
-        % We're the first \question, \part, \subpart, \subsubpart,
-        % or choice on this page:
-        \global\expandafter\edef\csname
-                       Contin@\theCurr@Page\endcsname{\arabic{question}}%
-      \fi
+    \questionobject@pluspagecheck
     \choice@toks={}%
   }%
   % Version 2.217-beta changes:
@@ -3869,25 +3815,15 @@
     \def\choice{%
       \if@correctchoice \endgroup \fi
       \refstepcounter{choice}%
-      \ifnum\value{choice}>1
+      \ifnum\value{choice}>1\relax
         \penalty -50\hskip 1em plus 1em\relax
       \fi
       \choicelabel
       % No need to put the following into a token string; we just put
       % the choicelabel onto the page, so we're at the spot whose page
       % number we want to record:
-      \addtocounter{ques@object}{1}%
-      \edef\q@object@label{%
-            question\arabic{question}@object\arabic{ques@object}}%
-      \PgInfo@write{\q@object@label}%
-      \set@counter@to@pageof{Curr@Page}{\q@object@label}%
-        \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
-          % We're the first \question, \part, \subpart, \subsubpart,
-          % or choice on this page:
-          \global\expandafter\edef\csname
-                         Contin@\theCurr@Page\endcsname{\arabic{question}}%
-        \fi
-        \nobreak\enskip
+      \questionobject@pluspagecheck
+      \nobreak\enskip
     }% choice
     \def\CorrectChoice{%
       \if@correctchoice \endgroup \fi
@@ -3896,25 +3832,15 @@
         \begingroup \@correctchoicetrue 
         \CorrectChoice@Emphasis
       \fi
-      \ifnum\value{choice}>1
+      \ifnum\value{choice}>1\relax
         \penalty -50\hskip 1em plus 1em\relax
       \fi
       \choicelabel
       % No need to put the following into a token string; we just put
       % the choicelabel onto the page, so we're at the spot whose page
       % number we want to record:
-      \addtocounter{ques@object}{1}%
-      \edef\q@object@label{%
-            question\arabic{question}@object\arabic{ques@object}}%
-      \PgInfo@write{\q@object@label}%
-      \set@counter@to@pageof{Curr@Page}{\q@object@label}%
-        \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
-          % We're the first \question, \part, \subpart, \subsubpart,
-          % or choice on this page:
-          \global\expandafter\edef\csname
-                         Contin@\theCurr@Page\endcsname{\arabic{question}}%
-        \fi
-        \nobreak\enskip
+      \questionobject@pluspagecheck
+      \nobreak\enskip
     }% CorrectChoice
     \let\correctchoice\CorrectChoice
     \let\par\@empty
@@ -3925,11 +3851,101 @@
   }%
   {\if@correctchoice \endgroup \fi}
 
+\newcommand{\checkboxchar}[1]{\def\checkbox@char{#1}}
+\newcommand{\checkedchar}[1]{\def\checked@char{#1}}
+\checkboxchar{$\bigcirc$}
+\checkedchar{$\surd$}
+
+\newenvironment{checkboxes}%
+  {\list{\checkbox@char}%
+     {%
+       \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}%
+       \def\choice{%
+         \if@correctchoice
+           \endgroup
+         \fi
+         \item
+         \do@choice@pageinfo
+       } % choice
+       \def\CorrectChoice{%
+         \if@correctchoice
+           \endgroup
+         \fi
+         \ifprintanswers
+           % We can't say \choice here, because that would
+           % insert an \endgroup:
+           \begingroup \@correctchoicetrue
+           \CorrectChoice@Emphasis
+           \item[\checked@char]
+         \else
+           \item
+         \fi
+         \do@choice@pageinfo
+       } % CorrectChoice
+       \let\correctchoice\CorrectChoice
+       \labelwidth\leftmargin\advance\labelwidth-\labelsep
+       \topsep=0pt
+       \partopsep=0pt
+       \checkboxeshook
+     }%
+  }%
+  {\if@correctchoice \endgroup \fi \endlist}
+
+\newenvironment{oneparcheckboxes}%
+  {%
+    % Although we're not printing numbers for the choices, we use the
+    % choice counter to keep track of whether a choice is the first
+    % one (in which case we don't leave any additional space) or a
+    % later one (in which case we do leave additional space):    
+    \setcounter{choice}{0}%
+    \def\choice{%
+      \if@correctchoice \endgroup \fi
+      \stepcounter{choice}%
+      \ifnum\value{choice}>1\relax
+        \penalty -50\hskip 1em plus 1em\relax
+      \fi
+      \checkbox@char
+      % No need to put the following into a token string; we just put
+      % \checkbox@char onto the page, so we're at the spot whose page
+      % number we want to record:
+      \questionobject@pluspagecheck
+      \nobreak\enskip
+    }% choice
+    \def\CorrectChoice{%
+      \if@correctchoice \endgroup \fi
+      \stepcounter{choice}%
+      \ifprintanswers
+        \begingroup \@correctchoicetrue 
+        \CorrectChoice@Emphasis
+      \fi
+      \ifnum\value{choice}>1\relax
+        \penalty -50\hskip 1em plus 1em\relax
+      \fi
+      \ifprintanswers
+        \checked@char
+      \else
+        \checkbox@char
+      \fi
+      % No need to put the following into a token string; we just put
+      % the choicelabel onto the page, so we're at the spot whose page
+      % number we want to record:
+      \questionobject@pluspagecheck
+      \nobreak\enskip
+    }% CorrectChoice
+    \let\correctchoice\CorrectChoice
+    \let\par\@empty
+    % If we're continuing the paragraph containing the question,
+    % then leave a bit of space before the first choice:
+    \ifvmode\else\enskip\fi
+    \ignorespaces
+  }%
+  {\if@correctchoice \endgroup \fi}
 
-%--------------------------------------------------------------------
-%             Answer Lines (for short answer questions)
-
-% Note: \ques@ref is also used in \item@points@pageinfo
+
+%--------------------------------------------------------------------
+%             Answer Lines (for short answer questions)
+
+% Note: \ques@ref is also used in \item@points@pageinfo
 
 \def\ques@ref{question}
 \def\part@ref{part}
@@ -4070,7 +4086,58 @@
 }
 
 %--------------------------------------------------------------------
-%                      \uplevel and \fullwidth:
+%                            \makeemptybox
+
+% \makeemptybox takes one argument, which is a length, and it creates
+% an empty box of width the length of the current line and of height
+% equal to the argument. That is, the box extends from the current
+% left margin (which depends on whether we're in a question, part,
+% subpart, or subsubpart) to the right margin.
+
+% As of version 2.304, the argument of \makeemptybox can be either
+% a length, or \fill, or \stretch{number}.
+
+% \newcommand\makeemptybox[1]{
+%   \par
+%   \begingroup
+%     \setlength{\fboxsep}{0pt}%
+%     \framebox[\linewidth]{%
+%       \vrule height 0pt depth #1 width 0pt
+%     }%
+%   \endgroup
+% }
+
+\newlength\minboxheight
+\setlength\minboxheight{.1in}
+
+\newcommand\makeemptybox[1]{%
+  \par
+  \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}%
+  \nointerlineskip
+  \begingroup
+    \setbox0=\hbox to \hsize{\hskip\@totalleftmargin
+                           \vrule height\minboxheight \hfill \vrule}%
+    % The vertical size desired may not be an exact multiple of
+    % \minboxheight, and so \cleaders might leave a gap between the
+    % vertical lines and the horizontal lines above and below it.
+    % Thus, we put a single copy of \box0 immediately below the
+    % horizontal line above and we'll also put a single copy of \box0
+    % immediately above the horizontal line below.
+    \copy0
+    \nobreak
+    \vskip -\minboxheight
+    \cleaders \copy0 \vskip #1
+    \vskip -\minboxheight
+    \nointerlineskip
+    \copy0
+  \endgroup
+  \nointerlineskip
+  \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}%
+}
+
+%--------------------------------------------------------------------
+%                      \uplevel and \fullwidth
+%           and the EnvUplevel and EnvFullwidth environments:
 
 % \uplevel is used to print text at the indentation level of the 
 % enclosing environment.  For example, to precede a question with
@@ -4079,14 +4146,26 @@
 % 
 % \fullwidth is similar, but uses the full page of text on the page.
 
+% The EnvUplevel environment is similar to the \uplevel command, but it
+% has the advantage that you can include verbatim material (using, e.g.,
+% the \verb command) in the environment.  (You can't include verbatim
+% material in the argument of an \uplevel command.)
+
+% The EnvFullwidth environment is similar to the \fullwidth command, but
+% it has the advantage that you can include verbatim material (using,
+% e.g., the \verb command) in the environment.  (You can't include
+% verbatim material in the argument of an \fullwidth command.)
+
+
 \long\def\uplevel#1{%
   \par\bigskip
   \vbox{%
+    % We entered internal vertical mode, and so we get \parshape=0.
     % We set \leftskip to provide the correct left margin for whatever
-    % text is in the argument of the \uplevel command:
+    % is in the argument of the \uplevel command:
     \leftskip=\@totalleftmargin
     \advance\leftskip-\leftmargin
-    % We adjust \@totalleftmargin (and linewidth?) in case there's a
+    % We adjust \@totalleftmargin and linewidth in case there's a
     % solution environment inside of the argument to the \uplevel:
     \advance\@totalleftmargin-\leftmargin
     \advance\linewidth\leftmargin
@@ -4095,9 +4174,24 @@
   \nobreak
 }
 
+\newenvironment{EnvUplevel}
+  {\par\bigskip\vbox\bgroup
+    % We set \leftskip to provide the correct left margin for whatever
+    % is inside of the environment:
+    \leftskip=\@totalleftmargin
+    \advance\leftskip-\leftmargin
+    % We adjust \@totalleftmargin (and linewidth?) in case there's a
+    % solution environment inside of the environment:
+    \advance\@totalleftmargin-\leftmargin
+    \advance\linewidth\leftmargin
+  }
+  {\egroup\nobreak}
+
+
 \long\def\fullwidth#1{%
   \par\bigskip
   \vbox{%
+    % We entered internal vertical mode, and so we get \parshape=0.
     \leftskip=0pt \rightskip=0pt
     \advance\linewidth\@totalleftmargin
     \@totalleftmargin=0pt
@@ -4106,6 +4200,17 @@
   \nobreak
 }
 
+\newenvironment{EnvFullwidth}
+  {\par\bigskip\vbox\bgroup
+    % We entered internal vertical mode, and so we get \parshape=0.
+    \leftskip=0pt \rightskip=0pt
+    % We adjust \@totalleftmargin (and linewidth?) in case there's a
+    % solution environment inside of the environment:
+    \advance\linewidth\@totalleftmargin
+    \@totalleftmargin=0pt
+  }
+  {\egroup\nobreak}
+
 
 %--------------------------------------------------------------------
 %--------------------------------------------------------------------
@@ -4122,69 +4227,112 @@
 \def\cellwidth#1{\@cellwidth=#1}
 \def\gradetablestretch#1{\def\@gtblstretch{#1}}
 
+% \settabletotalpoints allows the user to specify a total
+% number of points to appear in a table that may be different
+% from the sum of the points in the table:
+\newcommand\prt@tablepoints{\prt@hlfcntr{tbl@points}}
+\newcommand\settabletotalpoints[1]{%
+  \def\prt@tablepoints{#1}%
+}% \settabletotalpoints
+
+% \settabletotalbonuspoints is similar to \settabletotalpoints:
+\newcommand\prt@tablebonuspoints{\prt@hlfcntr{tbl@bonuspoints}}
+\newcommand\settabletotalbonuspoints[1]{%
+  \def\prt@tablebonuspoints{#1}%
+}% \settabletotalbonuspoints
+
 % All of the following that begin with `h' are for horizontal tables,
 % and all of them that begin with `v' are for vertical tables:
 \def\hqword#1{\def\@hqword{#1}}
 \def\hpword#1{\def\@hpword{#1}}
 \def\hsword#1{\def\@hsword{#1}}
 \def\htword#1{\def\@htword{#1}}
+\def\hpgword#1{\def\@hpgword{#1}}
+
 \def\vqword#1{\def\@vqword{#1}}
 \def\vpword#1{\def\@vpword{#1}}
 \def\vsword#1{\def\@vsword{#1}}
 \def\vtword#1{\def\@vtword{#1}}
-
 \def\vpgword#1{\def\@vpgword{#1}}
-\def\hpgword#1{\def\@hpgword{#1}}
+
 
 % The following are the versions for bonusgradetable:
 \def\bhqword#1{\def\@bhqword{#1}}
 \def\bhpword#1{\def\@bhpword{#1}}
 \def\bhsword#1{\def\@bhsword{#1}}
 \def\bhtword#1{\def\@bhtword{#1}}
+\def\bhpgword#1{\def\@bhpgword{#1}}
+
 \def\bvqword#1{\def\@bvqword{#1}}
 \def\bvpword#1{\def\@bvpword{#1}}
 \def\bvsword#1{\def\@bvsword{#1}}
 \def\bvtword#1{\def\@bvtword{#1}}
-
 \def\bvpgword#1{\def\@bvpgword{#1}}
-\def\bhpgword#1{\def\@bhpgword{#1}}
+
+% The following are the versions for combinedgradetable:
+\def\chqword#1{\def\@chqword{#1}}
+\def\chpword#1{\def\@chpword{#1}}
+\def\chbpword#1{\def\@chbpword{#1}}
+\def\chsword#1{\def\@chsword{#1}}
+\def\chtword#1{\def\@chtword{#1}}
+\def\chpgword#1{\def\@chpgword{#1}}
+
+\def\cvqword#1{\def\@cvqword{#1}}
+\def\cvpword#1{\def\@cvpword{#1}}
+\def\cvbpword#1{\def\@cvbpword{#1}}
+\def\cvsword#1{\def\@cvsword{#1}}
+\def\cvtword#1{\def\@cvtword{#1}}
+\def\cvpgword#1{\def\@cvpgword{#1}}
+
 
 
 % Initialize:
 \cellwidth{2em}
 \gradetablestretch{1.5}
+
+\hqword{Question:}
+\hpgword{Page:}
 \hpword{Points:}
 \hsword{Score:}
 \htword{Total}
 \vpword{Points}
 \vsword{Score}
 \vtword{Total:}
+\vqword{Question}
+\vpgword{Page}
 
+\bhqword{Question:}
+\bhpgword{Page:}
 \bhpword{Bonus Points:}
 \bhsword{Score:}
 \bhtword{Total}
+\bvqword{Question}
+\bvpgword{Page}
 \bvpword{Bonus Points}
 \bvsword{Score}
 \bvtword{Total:}
 
-% For tables indexed by question number:
-\vqword{Question}
-\hqword{Question:}
-\bvqword{Question}
-\bhqword{Question:}
-
-% For tables indexed by page number:
-\vpgword{Page}
-\hpgword{Page:}
-\bvpgword{Page}
-\bhpgword{Page:}
-
+\chqword{Question:}
+\chpgword{Page:}
+\chpword{Points:}
+\chbpword{Bonus Points:}
+\chsword{Score:}
+\chtword{Total}
+\cvqword{Question}
+\cvpgword{Page}
+\cvpword{Points}
+\cvbpword{Bonus Points}
+\cvsword{Score}
+\cvtword{Total:}
 
 
 % The only commands here accessible to the user are \gradetable,
-% \bonusgradetable, \pointtable, \bonuspointtable, \partialgradetable,
-% \partialbonusgradetable, \partialpointtable,
-% \partialbonuspointtable, \begingradingrange, and \endgradingrange.
+% \bonusgradetable, \combinedgradetable, \pointtable,
+% \bonuspointtable, \combinedpointtable, \partialgradetable,
+% \partialbonusgradetable, \partialcombinedtable, \partialpointtable,
+% \partialbonuspointtable, \partialcombinedpointtable,
+% \begingradingrange, \endgradingrange, \pointsinrange, and
+% \bonuspointsinrange.
 
 % The possibilities are
 
@@ -4198,6 +4346,11 @@
 %   \bonusgradetable[h][questions]
 %   \bonusgradetable[h][pages]
 
+%   \combinedgradetable[v][questions]
+%   \combinedgradetable[v][pages]
+%   \combinedgradetable[h][questions]
+%   \combinedgradetable[h][pages]
+
 %   \pointtable[v][questions]
 %   \pointtable[v][pages]
 %   \pointtable[h][questions]
@@ -4208,6 +4361,11 @@
 %   \bonuspointtable[h][questions]
 %   \bonuspointtable[h][pages]
 
+%   \combinedpointtable[v][questions]
+%   \combinedpointtable[v][pages]
+%   \combinedpointtable[h][questions]
+%   \combinedpointtable[h][pages]
+
 %   \partialgradetable{whatever}[v][questions]
 %   \partialgradetable{whatever}[v][pages]
 %   \partialgradetable{whatever}[h][questions]
@@ -4218,6 +4376,11 @@
 %   \partialbonusgradetable{whatever}[h][questions]
 %   \partialbonusgradetable{whatever}[h][pages]
 
+%   \partialcombinedgradetable{whatever}[v][questions]
+%   \partialcombinedgradetable{whatever}[v][pages]
+%   \partialcombinedgradetable{whatever}[h][questions]
+%   \partialcombinedgradetable{whatever}[h][pages]
+
 %   \partialpointtable{whatever}[v][questions]
 %   \partialpointtable{whatever}[v][pages]
 %   \partialpointtable{whatever}[h][questions]
@@ -4228,9 +4391,17 @@
 %   \partialbonuspointtable{whatever}[h][questions]
 %   \partialbonuspointtable{whatever}[h][pages]
 
+%   \partialcombinedpointtable{whatever}[v][questions]
+%   \partialcombinedpointtable{whatever}[v][pages]
+%   \partialcombinedpointtable{whatever}[h][questions]
+%   \partialcombinedpointtable{whatever}[h][pages]
+
 %   \begingradingrange{whatever}
 %   \endgradingrange{whatever}
 %
+%   \pointsinrange{whatever}
+%   \bonuspointsinrange{whatever}
+%
 % where ``whatever'' is a label chosen by the user.
 
 % If one or both optional arguments are omitted, the defaults are
@@ -4241,10 +4412,14 @@
 \newif\if@scores
 
 % \@partialtrue means we're doing \partialgradetable,
-% \partialbonusgradetable, \partialpointtable, or
-% \partialbonuspointtable:
+% \partialbonusgradetable, \partialcombinedgradetable,
+% \partialpointtable, \partialbonuspointtable, or
+% \partialcombinedpointtable:
 \newif\if@partial
 
+% \@combinedtrue means we're doing \combinedgradetable
+% or \combinedpointtable:
+\newif\if@combined
 
 % It's OK to use the counter num@cols as a scratch counter
 % in \begingradingrange and \endgradingrange because
@@ -4282,11 +4457,16 @@
 % for the questions (or pages) that appear on the table:
 \new@hlfcntr{tbl@points}
 
+% We'll use the counter tbl@bonuspoints to add up the total bonus
+% points for the questions (or pages) that appear on the table:
+\new@hlfcntr{tbl@bonuspoints}
+
 
 \def\partialgradetable#1{%
   \@scorestrue
   \@bonusfalse
   \@partialtrue
+  \@combinedfalse
   \def\tbl@range{#1}%
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
@@ -4298,6 +4478,7 @@
   \@scorestrue
   \@bonustrue
   \@partialtrue
+  \@combinedfalse
   \def\tbl@range{#1}%
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
@@ -4305,10 +4486,23 @@
   \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
 }% partialbonusgradetable
 
+\def\partialcombinedgradetable#1{%
+  \@scorestrue
+  \@bonusfalse
+  \@partialtrue
+  \@combinedtrue
+  \def\tbl@range{#1}%
+  % If the user doesn't include the optional argument
+  % choosing between vertical and horizontal,
+  % we give them vertical:
+  \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialcombinedgradetable
+
 \def\partialpointtable#1{%
   \@scoresfalse
   \@bonusfalse
   \@partialtrue
+  \@combinedfalse
   \def\tbl@range{#1}%
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
@@ -4320,6 +4514,7 @@
   \@scoresfalse
   \@bonustrue
   \@partialtrue
+  \@combinedfalse
   \def\tbl@range{#1}%
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
@@ -4327,12 +4522,25 @@
   \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
 }% partialbonuspointtable
 
+\def\partialcombinedpointtable#1{%
+  \@scoresfalse
+  \@bonusfalse
+  \@partialtrue
+  \@combinedtrue
+  \def\tbl@range{#1}%
+  % If the user doesn't include the optional argument
+  % choosing between vertical and horizontal,
+  % we give them vertical:
+  \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialcombinedpointtable
+
 %--------------------------------------------------------------------
 
 \def\gradetable{%
   \@scorestrue
   \@bonusfalse
   \@partialfalse
+  \@combinedfalse
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
   % we give them vertical:
@@ -4343,6 +4551,18 @@
   \@scorestrue
   \@bonustrue
   \@partialfalse
+  \@combinedfalse
+  % If the user doesn't include the optional argument
+  % choosing between vertical and horizontal,
+  % we give them vertical:
+  \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% bonusgradetable
+
+\def\combinedgradetable{%
+  \@scorestrue
+  \@bonusfalse
+  \@partialfalse
+  \@combinedtrue
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
   % we give them vertical:
@@ -4353,6 +4573,7 @@
   \@scoresfalse
   \@bonusfalse
   \@partialfalse
+  \@combinedfalse
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
   % we give them vertical:
@@ -4363,6 +4584,18 @@
   \@scoresfalse
   \@bonustrue
   \@partialfalse
+  \@combinedfalse
+  % If the user doesn't include the optional argument
+  % choosing between vertical and horizontal,
+  % we give them vertical:
+  \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% bonuspointtable
+
+\def\combinedpointtable{%
+  \@scoresfalse
+  \@bonusfalse
+  \@partialfalse
+  \@combinedtrue
   % If the user doesn't include the optional argument
   % choosing between vertical and horizontal,
   % we give them vertical:
@@ -4416,6 +4649,8 @@
 \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''.
   % See whether we're doing a table indexed by
   % questions or by pages:
   \begingroup % avoid trouble from using \@temp
@@ -4459,10 +4694,14 @@
 %--------------------------------------------------------------------
 % 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.
-% \@tblquestions branches, depending on whether the user
-% selected [v] or [h].
+% \find@qrange then calls \@tblquestions, passing along the argument
+% that is either ``v'' or ``h''.
 
 \def\find@qrange#1{%
   % We're doing a table indexed by question numbers.
@@ -4480,7 +4719,7 @@
             \edef\tbl@firstq{\csname tbl@\tbl@range @firstq\endcsname}%
             \edef\tbl@lastq{\csname tbl@\tbl@range @lastq\endcsname}%
             % Check that firstq precedes or equals lastq:
-            \ifnum \tbl@firstq > \tbl@lastq
+            \ifnum \tbl@firstq > \tbl@lastq\relax
               \fbox{Error: Grading Range `\tbl@range ':
                       Last question precedes first question.}%
               \ClassError{exam}{%
@@ -4509,6 +4748,8 @@
 }% 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
   \else
@@ -4552,8 +4793,9 @@
 
 \def\@vtblquestions{%
   % The table is vertical and indexed by question numbers.
-  % See whether it's bonus or non-bonus, and
-  % gradetable or pointtable:
+  % 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:
@@ -4566,10 +4808,18 @@
         \@bvpttblquestions
       \fi
     \else
-      \if@scores
-        \@vgrdtblquestions
+      \if@combined
+        \if@scores
+          \@cvgrdtblquestions
+        \else
+          \@cvpttblquestions
+        \fi
       \else
-        \@vpttblquestions
+        \if@scores
+          \@vgrdtblquestions
+        \else
+          \@vpttblquestions
+        \fi
       \fi
     \fi
     % Restore the saved value of question:
@@ -4577,16 +4827,95 @@
   \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@points}{0}%
+  \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@hlfcntr{tbl@points}&\hbox to \@cellwidth{\hfill}\\
+    {\@bvtword}& \prt@tablebonuspoints&\hbox to \@cellwidth{\hfill}\\
     \hline
   \end{tabular}%
 }% @bvgrdtblquestions
@@ -4598,7 +4927,7 @@
   \hline
   \@ifundefined{bonuspointsofq@\romannumeral \c@question}%
     {}%
-    {\addto@hlfcntr{tbl@points}
+    {\addto@hlfcntr{tbl@bonuspoints}
        {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
   \ifnum \value{question} < \tbl@lastq\relax
     \let\next@bvloop=\do@bvloop
@@ -4608,16 +4937,17 @@
   \next@bvloop
 }% do@bvloop
 
+
 \def\@bvpttblquestions{%
   % Vertical bonus point table, indexed by questions:
-  \set@hlfcntr{tbl@points}{0}%
+  \set@hlfcntr{tbl@bonuspoints}{0}%
   \begin{tabular}{|c|c|}
     \hline
     {\@bvqword}& {\@bvpword}\\
     \hline
     \setcounter{question}{\tbl@firstq}%
     \addtocounter{question}{-1}\do@bvptloop
-    {\@bvtword}& \prt@hlfcntr{tbl@points}\\
+    {\@bvtword}& \prt@tablebonuspoints\\
     \hline
   \end{tabular}%
 }% @bvpttblquestions
@@ -4629,7 +4959,7 @@
   \hline
   \@ifundefined{bonuspointsofq@\romannumeral \c@question}%
     {}%
-    {\addto@hlfcntr{tbl@points}
+    {\addto@hlfcntr{tbl@bonuspoints}
        {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
   \ifnum \value{question} < \tbl@lastq\relax
     \let\next@bvptloop=\do@bvptloop
@@ -4648,7 +4978,7 @@
     \hline
     \setcounter{question}{\tbl@firstq}%
     \addtocounter{question}{-1}\do@vloop
-    {\@vtword}& \prt@hlfcntr{tbl@points}&\hbox to \@cellwidth{\hfill}\\
+    {\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\
     \hline
   \end{tabular}%
 }% @vgrdtblquestions
@@ -4670,6 +5000,7 @@
   \next@vloop
 }% do@vloop
 
+
 \def\@vpttblquestions{%
   % Vertical non-bonus point table, indexed by questions:
   \set@hlfcntr{tbl@points}{0}%
@@ -4679,7 +5010,7 @@
     \hline
     \setcounter{question}{\tbl@firstq}%
     \addtocounter{question}{-1}\do@vptloop
-    {\@vtword}& \prt@hlfcntr{tbl@points}\\
+    {\@vtword}& \prt@tablepoints\\
     \hline
   \end{tabular}%
 }% @vpttblquestions
@@ -4706,7 +5037,12 @@
 % 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}%
   {}%
   {%
@@ -4714,24 +5050,25 @@
     \addtocounter{num@cols}{-\tbl@firstq}%
     \addtocounter{num@cols}{1}%
   }%
-  % See whether it's bonus or non-bonus, and
-  % gradetable or pointtable:
+  % 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
-      \set@hlfcntr{tbl@points}{0}%
+      % 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@hlfcntr{tbl@points}\\
+        \prt@tablebonuspoints\\
         \hline
         % If it's a grade table, add in the score line:
         \if@scores
@@ -4742,25 +5079,54 @@
         \fi
       \end{tabular}%
     \else
-      \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@hlfcntr{tbl@points}\\
-        \hline
-        % If it's a grade table, add in the score line:
-        \if@scores
-          {\@hsword}& \setcounter{question}{\tbl@firstq}%
-          \addtocounter{question}{-1}\do@sloop
-          \\
+      \if@combined
+        % It's a horizontal combined table, by questions:
+        \begin{tabular}{|l|*{\thenum@cols}{c|}c|}
           \hline
-        \fi
-      \end{tabular}%
+          {\@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}}%
@@ -4768,11 +5134,11 @@
 }% @htblquestions
 
 
-% \do@qnumloop and \do@sloop are used both by non-bonus gradetable and
-% by bonusgradetable:
+% \do@qnumloop and \do@sloop are used by non-bonus,
+% combined, and bonus tables:
 \def\do@qnumloop{%
   \addtocounter{question}{1}%
-  \ref{question@\arabic{question}} &
+  \ref{question@\arabic{question}}&
   \ifnum \value{question} < \tbl@lastq\relax
     \let\next@qnloop=\do@qnumloop
   \else
@@ -4791,7 +5157,7 @@
   \next@sloop
 }% do@sloop
 
-%  \do@ptloop is used only by non-bonus gradetable:
+%  \do@ptloop is used by combined and non-bonus tables:
 \def\do@ptloop{%
   \addtocounter{question}{1}%
   \pointsofquestion{\arabic{question}}&
@@ -4807,13 +5173,13 @@
   \next@ptloop
 }% do@ptloop
 
-%  \do@bptloop is used only by bonusgradetable:
+%  \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@points}
+    {\addto@hlfcntr{tbl@bonuspoints}
        {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
   \ifnum \value{question} < \tbl@lastq\relax
     \let\next@bptloop=\do@bptloop
@@ -4829,7 +5195,7 @@
 % Grade and point tables indexed by page numbers:
 
 
-% The only pages listed are those on which there is a nonzero number
+% The only pages listed are those on which there are a nonzero number
 % of points.  We check pages \tbl@firstp through \tbl@lastp
 % Once we've checked that, e.g., \lastpage@withpoints and
 % \pointsonpage@\romannumeral{\lastpage@withpoints} are defined, we
@@ -4849,13 +5215,21 @@
 % isn't defined, tmp@hlfcntr gets the value zero (because of the way
 % that \set@hlfcntr is written).
 
-
-% \find@prange makes sure the grading range is defined and that
-% its last page isn't before its first page.
-
+% \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.
 
 \def\find@prange#1{%
   % 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
+  % 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).
   \if@partial
     \@ifundefined{tbl@\tbl@range @firstp}%
       {%
@@ -4870,7 +5244,7 @@
             \edef\tbl@firstp{\csname tbl@\tbl@range @firstp\endcsname}%
             \edef\tbl@lastp{\csname tbl@\tbl@range @lastp\endcsname}%
             % Check that firstp precedes or equals lastp:
-            \ifnum \tbl@firstp > \tbl@lastp
+            \ifnum \tbl@firstp > \tbl@lastp\relax
               \fbox{Error: Grading Range `\tbl@range ':
                       Last page precedes first page.}%
               \ClassError{exam}{%
@@ -4889,20 +5263,25 @@
     \def\tbl@firstp{1}%
     % \tbl@lastp isn't used on the first run of LaTeX, and
     % \lastpage@withbonuspoints is defined on the second and later runs.
+    \def\tbl@lastp{\lastpage@withpoints}%
     \if@bonus
       \def\tbl@lastp{\lastpage@withbonuspoints}%
-    \else
-      \def\tbl@lastp{\lastpage@withpoints}%
+    \fi
+    \if@combined
+      \ifnum \lastpage@withbonuspoints > \lastpage@withpoints\relax
+        \def\tbl@lastp{\lastpage@withbonuspoints}%
+      \fi        
     \fi
     \@tblpages{#1}%
   \fi
 }% find@prange
 
 
-
-% Check that there's enough info from the .aux file to do a page
-% indexed grade table:
 \def\@tblpages#1{%
+  % 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
@@ -4927,6 +5306,7 @@
   % 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
@@ -4971,12 +5351,14 @@
 % 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 or non-bonus.
+  % It can be grade or point, and bonus, non-bonus, or combined.
   \begingroup
     \renewcommand\arraystretch{\@gtblstretch}%
-    \set@hlfcntr{tbl@points}{0}%
     \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
@@ -4984,7 +5366,8 @@
           \hline
           \setcounter{@iterator}{\tbl@firstp}%
           \addtocounter{@iterator}{-1}\pg@bvloop
-          {\@bvtword}& \prt@hlfcntr{tbl@points}&\hbox to \@cellwidth{\hfill}\\
+          {\@bvtword}& \prt@tablebonuspoints&
+                              \hbox to \@cellwidth{\hfill}\\
           \hline
         \end{tabular}%
       \else
@@ -4994,31 +5377,65 @@
           \hline
           \setcounter{@iterator}{\tbl@firstp}%
           \addtocounter{@iterator}{-1}\pg@ptbvloop
-          {\@bvtword}& \prt@hlfcntr{tbl@points}\\
+          {\@bvtword}& \prt@tablebonuspoints\\
           \hline
         \end{tabular}%
       \fi
     \else
-      \if@scores
-        \begin{tabular}{|c|c|c|}
-          \hline
-          {\@vpgword}& {\@vpword}& {\@vsword}\\
-          \hline
-          \setcounter{@iterator}{\tbl@firstp}%
-          \addtocounter{@iterator}{-1}\pg@vloop
-          {\@vtword}& \prt@hlfcntr{tbl@points}&\hbox to \@cellwidth{\hfill}\\
-          \hline
-        \end{tabular}%
+      \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
-        \begin{tabular}{|c|c|}
-          \hline
-          {\@vpgword}& {\@vpword}\\
-          \hline
-          \setcounter{@iterator}{\tbl@firstp}%
-          \addtocounter{@iterator}{-1}\pg@ptvloop
-          {\@vtword}& \prt@hlfcntr{tbl@points}\\
-          \hline
-        \end{tabular}%
+        % 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
     \fi
   \endgroup
@@ -5028,9 +5445,7 @@
 % \pg@ptvloop and \pg@ptvloopline are used only by non-bonus pointtable:
 \def\pg@ptvloop{%
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
-                       \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
     \pg@ptvloopline
@@ -5055,9 +5470,7 @@
 % \pg@vloop and \pg@vloopline are used only by non-bonus gradetable:
 \def\pg@vloop{%
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
-                       \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
     \pg@vloopline
@@ -5079,14 +5492,123 @@
   \hline
 }% pg@vloopline
 
+% \pg@cvloop, \pg@noncvloopline, \check@bnspts, and
+% \pg@cvloopline are used only by combined gradetable:
+\def\pg@cvloop{%
+  \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
+  \fi
+  % Do the tail recursion bit:
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@pg@cvloop=\pg@cvloop
+  \else
+    \let\next@pg@cvloop=\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
+
+% \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
+  \else
+    \check@ptbnspts
+  \fi
+  % Do the tail recursion bit:
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@pg@ptcvloop=\pg@ptcvloop
+  \else
+    \let\next@pg@ptcvloop=\relax
+  \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
+  \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
+
 % \pg@bvloop and \pg@bvloopline are used only by bonus gradetable:
 \def\pg@bvloop{%
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
-                          \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
-    \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
+    \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
     \pg@bvloopline
   \fi
   \ifnum \the@iterator < \tbl@lastp\relax
@@ -5109,11 +5631,9 @@
 % \pg@ptbvloop and \pg@ptbvloopline are used only by bonus pointtable:
 \def\pg@ptbvloop{%
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
-                          \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
-    \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
+    \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
     \pg@ptbvloopline
   \fi
   \ifnum \the@iterator < \tbl@lastp\relax
@@ -5140,25 +5660,56 @@
 % 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, both
-  % bonus and non-bonus.
+  % grade tables and point tables,
+  % bonus, non-bonus, and combined.
   \setcounter{num@cols}{0}%
   \setcounter{@iterator}{\tbl@firstp}%
   \addtocounter{@iterator}{-1}%
   \if@bonus
     \docount@pgswbpts
   \else
-    \docount@pgswpts
+    \if@combined
+      \docount@pgswcpts
+    \else
+      \docount@pgswpts
+    \fi
   \fi
 }% count@pgswpts
 
-% \docount@pgswpts is used for non-bonus gradetable:
+\def\docount@pgswcpts{%
+  % Called by \count@pgswpts
+  % \docount@pgswcpts is used for combined gradetable.
+  % Count the number of pages in range with any kind of point (bonus
+  % or non-bonus):
+  \addtocounter{@iterator}{1}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \addtocounter{num@cols}{1}%
+  \else
+    \check@bnsptpage
+  \fi
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@docount=\docount@pgswcpts
+  \else
+    \let\next@docount=\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}%
+  \fi
+}% check@bnsptpage
+
 \def\docount@pgswpts{%
+  % Called by \count@pgswpts
+  % \docount@pgswpts is used for non-bonus gradetable:
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
-                         \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \addtocounter{num@cols}{1}%
   \fi
@@ -5170,12 +5721,11 @@
   \next@docount
 }% docount@pgswpts
 
-% \docount@pgswbpts is used for bonusgradetable:
 \def\docount@pgswbpts{%
+  % Called by \count@pgswpts
+  % \docount@pgswbpts is used for bonusgradetable:
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
-                           \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \addtocounter{num@cols}{1}%
   \fi
@@ -5190,15 +5740,21 @@
 %--------------------------------------------------------------------
 
 \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
+    \ifnum \thenum@cols = 0\relax
       \setcounter{num@cols}{1}%
     \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}%
@@ -5207,7 +5763,7 @@
         \hline
         {\@bhpword}& \setcounter{@iterator}{\tbl@firstp}%
                      \addtocounter{@iterator}{-1}\do@bpgptloop
-        \prt@hlfcntr{tbl@points}\\
+        \prt@tablebonuspoints\\
         \hline
         % If it's a grade table, do the line for scores:
         \if@scores
@@ -5218,35 +5774,62 @@
         \fi
       \end{tabular}%
     \else
-      \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@hlfcntr{tbl@points}\\
-        \hline
-        % If it's a grade table, do the line for scores:
-        \if@scores
-          {\@hsword}& \setcounter{@iterator}{\tbl@firstp}%
-                      \addtocounter{@iterator}{-1}\do@pgsloop
-          \\
+      \if@combined
+        % Horizontal combined grade or point table, by pages:
+        \begin{tabular}{|l|*{\thenum@cols}{c|}c|}
           \hline
-        \fi
-      \end{tabular}%
+          {\@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
     \fi
   \endgroup
 }% @htblpages
 
-% \do@pgnumloop, \pg@line, \do@pgptloop, \pgpt@line, \do@pgsloop, and
-% \pg@sline are only for non-bonus gradetable:
+%--------------------------------------------------------------------
+% \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}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
-                        \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \pg@line
   \fi
@@ -5258,17 +5841,16 @@
   \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}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
-                        \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
-    \pgpt@line
+    \pgpt@entry
   \fi
   \ifnum \the@iterator < \tbl@lastp\relax
     \let\next@pgptloop=\do@pgptloop
@@ -5277,20 +5859,17 @@
   \fi
   \next@pgptloop
 }% \do@pgptloop
-\def\pgpt@line{%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \csname pointsonpage@\romannumeral
-        \csname c@@iterator\endcsname\endcsname &
-}% \pgpt@line
+% \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}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
-                         \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
-    \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
-    \pg@sline
+    \pg@sentry
   \fi
   \ifnum \the@iterator < \tbl@lastp\relax
     \let\next@pgsloop=\do@pgsloop
@@ -5299,20 +5878,22 @@
   \fi
   \next@pgsloop
 }% \do@pgsloop
-\def\pg@sline{%
+% \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@sline
+}% \pg@sentry
 
 %--------------------------------------------------------------------
 % These things are for bonusgradetable:
 
-% \do@bpgnumloop, \bpg@line, \do@bpgptloop, \bpgpt@line, \do@bpgsloop,
-% and \bpg@sline are only for bonusgradetable:
+% \do@bpgnumloop, \do@bpgptloop, \do@bpgsloop,
+% and \bpg@sline are only for bonusgradetable.
+% \bpg@line is also used for combinedgradetable.
 \def\do@bpgnumloop{%
   \addtocounter{@iterator}{1}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
-                          \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
     \bpg@line
   \fi
@@ -5324,16 +5905,15 @@
   \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}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
-                            \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
-    \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
+    \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
     \bpgpt@line
   \fi
   \ifnum \the@iterator < \tbl@lastp\relax
@@ -5343,20 +5923,17 @@
   \fi
   \next@bpgptloop
 }% do@bpgptloop
+% \bpgpt@line is called by several macros:
 \def\bpgpt@line{%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \csname bonuspointsonpage@\romannumeral
-                       \csname c@@iterator\endcsname\endcsname &
+  % 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}%
-  % spanish.ldf redefines \@roman, so we'll avoid using \roman:
-  \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
-                          \csname c@@iterator\endcsname\endcsname}%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
   \ifhlfcntr@pos{tmp@hlfcntr}%
-    \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
-    \bpg@sline
+    \pg@sentry
   \fi
   \ifnum \the@iterator < \tbl@lastp\relax
     \let\next@bpgsloop=\do@bpgsloop
@@ -5365,9 +5942,193 @@
   \fi
   \next@bpgsloop
 }% do@bpgsloop
-\def\bpg@sline{%
-  \hbox to \@cellwidth{\hfill}&
-}% bpg@sline
+
+%--------------------------------------------------------------------
+% These things are for combinedgradetable:
+
+% \do@cpgnumloop, \cpg@line, \cbpg@line, \do@cpgptloop, \bpgpt@line,
+% \do@cpgsloop, \bpg@sline, and \do@cbpgptloop are only for
+% combinedgradetable:
+\def\do@cpgnumloop{%
+  \addtocounter{@iterator}{1}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \pg@line
+  \else
+    \check@bpgpts
+  \fi
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@cpgnumloop=\do@cpgnumloop
+  \else
+    \let\next@cpgnumloop=\relax
+  \fi
+  \next@cpgnumloop
+}% do@bpgnumloop
+\def\check@bpgpts{%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \bpg@line
+  \fi
+}% check@bpgpts
+
+\def\do@cpgptloop{%
+  \addtocounter{@iterator}{1}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
+    \pgpt@entry
+  \else
+    \check@cbpts
+  \fi
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@cpgptloop=\do@cpgptloop
+  \else
+    \let\next@cpgptloop=\relax
+  \fi
+  \next@cpgptloop
+}% do@cpgptloop
+\def\check@cbpts{%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \pgpt@entry
+  \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
+  \else
+    \check@cnonpts
+  \fi
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@cbpgptloop=\do@cbpgptloop
+  \else
+    \let\next@cbpgptloop=\relax
+  \fi
+  \next@cbpgptloop
+}% do@cbpgptloop
+\def\check@cnonpts{%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \bpgpt@line
+  \fi
+}% check@cnonpts
+
+\def\do@cpgsloop{%
+  \addtocounter{@iterator}{1}%
+  \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \pg@sentry
+  \else
+    \check@bnsline
+  \fi
+  \ifnum \the@iterator < \tbl@lastp\relax
+    \let\next@cpgsloop=\do@cpgsloop
+  \else
+    \let\next@cpgsloop=\relax
+  \fi
+  \next@cpgsloop
+}% do@cpgsloop
+\def\check@bnsline{%
+  \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
+  \ifhlfcntr@pos{tmp@hlfcntr}%
+    \pg@sentry
+  \fi
+}% check@bnsline
+
+%--------------------------------------------------------------------
+% \pointsinrange and \bonuspointsinrange:
+% 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 ??}}%
+    {\read@range}%
+}% pointsinrange
+
+\def\bonuspointsinrange#1{%
+  \@bonustrue
+  \def\tbl@range{#1}%
+  \@ifundefined{exam@numpoints}%
+    {\mbox{\normalfont\bf ??}}%
+    {\read@range}%
+}% bonuspointsinrange
+
+\def\bad@range{%
+  {\mbox{\normalfont\bf ??}}%
+  \ClassWarning{exam}{%
+    Grading range `\tbl@range ' not defined.\MessageBreak
+    \space\space Run LaTeX again.\MessageBreak
+  }%
+}% bad@range
+
+\def\read@range{%
+  \@ifundefined{tbl@\tbl@range @firstq}%
+  {%
+    \bad@range
+  }%
+  {%
+    \@ifundefined{tbl@\tbl@range @lastq}%
+    {%
+      \bad@range
+    }%
+    {%
+      \edef\tbl@firstq{\csname tbl@\tbl@range @firstq\endcsname}%
+      \edef\tbl@lastq{\csname tbl@\tbl@range @lastq\endcsname}%
+      % Check that firstq precedes or equals lastq:
+      \ifnum \tbl@firstq > \tbl@lastq\relax
+        \fbox{Error: Grading Range `\tbl@range ':
+          Last question precedes first question.}%
+        \ClassError{exam}{%
+          In grading range `\tbl@range ',
+          the last question\MessageBreak
+          \space\space comes before the first question.\MessageBreak
+        }{%
+          \string\begingradingrange \space must precede
+          \string\endgradingrange \space by at
+          least one question.\MessageBreak
+        }%
+      \else
+        \count@pointsinrange
+      \fi
+    }%
+  }%
+}% read@range
+
+\def\count@pointsinrange{%
+  % Used for both \pointsinrange and \bonuspointsinrange:
+  \set@hlfcntr{tbl@points}{0}%
+  \setcounter{@iterator}{\tbl@firstq}%
+  \addtocounter{@iterator}{-1}\do@countloop
+  \prt@hlfcntr{tbl@points}%
+}% count@pointsinrange
+\def\do@countloop{%
+  % We check \if@bonus here when needed:
+  \addtocounter{@iterator}{1}%
+  \if@bonus
+    \@ifundefined{bonuspointsofq@\romannumeral \c@@iterator}%
+      {}%
+      {\addto@hlfcntr{tbl@points}
+         {\csname bonuspointsofq@\romannumeral \c@@iterator\endcsname}}%
+  \else
+    \@ifundefined{pointsofq@\romannumeral \c@@iterator}%
+      {}%
+      {\addto@hlfcntr{tbl@points}
+         {\csname pointsofq@\romannumeral \c@@iterator\endcsname}}%
+  \fi
+  \ifnum \value{@iterator} < \tbl@lastq\relax
+    \let\next@countloop=\do@countloop
+  \else
+    \let\next@countloop=\relax
+  \fi
+  \next@countloop
+}% do@countloop
+
 
 
 %--------------------------------------------------------------------
@@ -5388,80 +6149,164 @@
 \def\printanswers{\printanswerstrue}
 \def\noprintanswers{\printanswersfalse}
 
+% If the documentclass options include ``cancelspace'', then the
+% command \cancelspacetrue is given at the beginning of the run.
+
+% If the documentclass options include ``nocancelspace'', then the
+% command \cancelspacefalse is given at the beginning of the run.
+
+\def\cancelspace{\cancelspacetrue}
+\def\nocancelspace{\cancelspacefalse}
+
+\newcommand\SolutionEmphasis[1]{%
+  \def\Solution@Emphasis{#1}%
+}
+\SolutionEmphasis{}
 
 % If printanswers is true, we print the solution using a TheSolution
-% environment.  If printanswers is false, we insert blank vertical
-% space equal to the optional argument (the default value of which is
-% 0pt).
+% environment.  If printanswers is false and cancelspace is false, we
+% insert blank vertical space equal to the optional argument (the
+% default value of which is 0pt).
 \newenvironment{solution}[1][0pt]%
   {%
     \ifprintanswers
+      \begingroup
+      \Solution@Emphasis
       \begin{TheSolution}%
     \else
-      \par
-      \vspace*{#1}%
+      \ifcancelspace
+        % Do nothing
+      \else
+        \par
+        \penalty 0
+        \vspace*{#1}%
+      \fi
       \setbox\z@\vbox\bgroup
     \fi
   }{%
     \ifprintanswers
       \end{TheSolution}%
+      \endgroup
     \else
       \egroup
     \fi
   }%
 
 % If printanswers is true, we print the solution using a TheSolution
-% environment.  If printanswers is false, we insert lined vertical
-% space equal to the optional argument (the default value of which is
-% 0pt).
+% environment.  If printanswers is false and cancelspace is false,
+% we insert an empty box of width the current line width and of
+% height equal to the optional argument, which can be a length, or
+% \fill, or \stretch{number}.  If the optional argument is omitted,
+% then the box is entirely omitted when printanswers is false.
+\newenvironment{solutionorbox}[1][-1pt]%
+  {%
+    \ifprintanswers
+      \begingroup
+      \Solution@Emphasis
+      \begin{TheSolution}%
+    \else
+      \ifcancelspace
+        % Do nothing
+      \else
+        \par
+        % Note: It's important that the following test be
+        % ``\ifdim 0pt > #1'' rather than ``\ifdim #1 < 0pt''
+        % That's because if the user says
+        % ``\begin{solutionorbox}{\stretch{1}}''
+        % (or \stretch{anythingelse}), then this will expand to
+        % ``\ifdim 0pt > \z@ plus 1fill\relax''.
+        % The \ifdim will be ``\ifdim 0pt > \z@'', and we'll have
+        % ``plus 1fill\relax'' left over.  This is OK because if the
+        % \ifdim is false, that leftover stuff will be ignored,
+        % and it will only be true if the user omitted the optional
+        % argument, in which case there's no \stretch and thus no
+        % left over part.
+        % If we said ``\ifdim #1 < 0pt'', then we'd get an error
+        % when the user used \stretch, since the leftover stuff
+        % would appear when TeX was looking for <, =, or >.
+        \ifdim 0pt > #1
+          % do nothing
+        \else
+          \makeemptybox{#1}%
+        \fi
+      \fi
+      \setbox\z@\vbox\bgroup
+    \fi
+  }{%
+    \ifprintanswers
+      \end{TheSolution}%
+      \endgroup
+    \else
+      \egroup
+    \fi
+  }%
+  
+% If printanswers is true, we print the solution using a TheSolution
+% environment.  If printanswers is false and cancelspace is false,
+% we insert lined vertical space equal to the optional argument (the
+% default value of which is 0pt).
 \newenvironment{solutionorlines}[1][0pt]%
   {%
     \ifprintanswers
+      \begingroup
+      \Solution@Emphasis
       \begin{TheSolution}%
     \else
-      \par
-      \fillwithlines{#1}%
+      \ifcancelspace
+        % Do nothing
+      \else
+        \par
+        \penalty 0
+        \fillwithlines{#1}%
+      \fi
       \setbox\z@\vbox\bgroup
     \fi
   }{%
     \ifprintanswers
       \end{TheSolution}%
+      \endgroup
     \else
       \egroup
     \fi
   }%
-
   
 % If printanswers is true, we print the solution using a TheSolution
-% environment.  If printanswers is false, we insert dotted lined
-% vertical space equal to the optional argument (the default value of
-% which is 0pt).
+% environment.  If printanswers is false and cancelspace is false,
+% we insert dotted lined vertical space equal to the optional
+% argument (the default value of which is 0pt).
 \newenvironment{solutionordottedlines}[1][0pt]%
   {%
     \ifprintanswers
+      \begingroup
+      \Solution@Emphasis
       \begin{TheSolution}%
     \else
-      \par
-      \fillwithdottedlines{#1}%
+      \ifcancelspace
+        % Do nothing
+      \else
+        \par
+        \penalty 0
+        \fillwithdottedlines{#1}%
+      \fi
       \setbox\z@\vbox\bgroup
     \fi
   }{%
     \ifprintanswers
       \end{TheSolution}%
+      \endgroup
     \else
       \egroup
     \fi
   }%
 
-  
 % The environment TheSolution is called from the solution,
-% solutionorlines, and solutionordottedlines environments
-% when printanswers is true.  It uses Donald Arseneau's
-% framed.sty macros (included at the end if this file) to allow the
+% solutionorbox, solutionorlines, and solutionordottedlines
+% environments when printanswers is true.  It uses Donald Arseneau's
+% framed.sty macros (included at the end of this file) to allow the
 % solution to be broken across pages and have each piece enclosed in
 % an fbox (or a colorbox, if the user has given the command
-% \shadedsolutions).
-%
+% \shadedsolutions), (or no box at all, if the user has given the
+% command \unframedsolutions).
 % Of course, the user can change TheSolution with a \renewenvironment
 % command.
 \newcommand{\solutiontitle}{\noindent\textbf{Solution:}\enspace}
@@ -5473,10 +6318,17 @@
     % inside of the solution box:
     \leftskip=0pt
     \rightskip=0pt
+    % If the user said \unframedsolutions, then both
+    % \if@framedsolutions and \if@shadedsolutions are false:
     \if@framedsolutions
-      % Do nothing; we'll use the default \FrameCommand
+      % We'll use the default \FrameCommand
     \else
-      \def\FrameCommand{\colorbox{SolutionColor}}%
+      \if@shadedsolutions
+        \def\FrameCommand{\colorbox{SolutionColor}}%
+      \else
+        % It's \unframedsolutions:
+        \def\FrameCommand{}%
+      \fi
     \fi
     \MakeFramed{\advance\hsize-\width}%
     \solutiontitle
@@ -5489,8 +6341,12 @@
 
 \newif\if@framedsolutions
 \@framedsolutionstrue
+\newif\if@shadedsolutions
+\@shadedsolutionsfalse
+% If the user said \unframedsolutions, then both
+% \if@framedsolutions and \if@shadedsolutions are false.
 
-\def\framedsolutions{\@framedsolutionstrue}
+\def\framedsolutions{\@framedsolutionstrue\@shadedsolutionsfalse}
 \def\shadedsolutions{%
   \@ifundefined{definecolor}
   {%
@@ -5507,9 +6363,11 @@
   }%
   {%
     \definecolor{SolutionColor}{gray}{0.8}
+    \@shadedsolutionstrue
     \@framedsolutionsfalse
   }%
 }
+\def\unframedsolutions{\@framedsolutionsfalse\@shadedsolutionsfalse}
 
 %--------------------------------------------------------------------
 %--------------------------------------------------------------------
-- 
cgit v1.2.3

